@things-factory/operato-hub 4.3.700 → 4.3.701
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/routers/api/restful-apis/v1/company/add-contact-points.js +0 -24
- package/dist-server/routers/api/restful-apis/v1/company/add-contact-points.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/utils/params.js +0 -9
- package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js +194 -183
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
- package/openapi/v1/contact-point.yaml +48 -37
- package/package.json +19 -19
- package/server/routers/api/restful-apis/v1/company/add-contact-points.ts +1 -29
- package/server/routers/api/restful-apis/v1/utils/params.ts +0 -9
- package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +206 -215
|
@@ -44,22 +44,6 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
44
44
|
where: { id: customerBizplaceId },
|
|
45
45
|
relations: ['domain']
|
|
46
46
|
});
|
|
47
|
-
// optional override: release shelf life from ContactPoint
|
|
48
|
-
const contactPointId = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.contactPointId;
|
|
49
|
-
let releaseShelfLifeOverride = null;
|
|
50
|
-
let deliverToContactPoint = null;
|
|
51
|
-
if (contactPointId) {
|
|
52
|
-
const contactPoint = await tx
|
|
53
|
-
.getRepository(biz_base_1.ContactPoint)
|
|
54
|
-
.findOne({ where: { id: contactPointId } });
|
|
55
|
-
if (!contactPoint) {
|
|
56
|
-
throw new error_util_1.ApiError('E04', 'contactPoint not found');
|
|
57
|
-
}
|
|
58
|
-
if ((contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.releaseShelfLife) != null && contactPoint.releaseShelfLife !== 0) {
|
|
59
|
-
releaseShelfLifeOverride = contactPoint.releaseShelfLife;
|
|
60
|
-
}
|
|
61
|
-
deliverToContactPoint = contactPoint;
|
|
62
|
-
}
|
|
63
47
|
const worksheetPickingAssignment = await tx.getRepository(setting_base_1.Setting).findOne({
|
|
64
48
|
where: { domain, category: 'id-rule', name: 'enable-worksheet-picking-activation-assignment' }
|
|
65
49
|
});
|
|
@@ -78,12 +62,7 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
78
62
|
salesType: ((_e = bodyReq.xilnexSetting) === null || _e === void 0 ? void 0 : _e.salesType) || ''
|
|
79
63
|
});
|
|
80
64
|
}
|
|
81
|
-
|
|
82
|
-
let newPhone1 = (deliverToContactPoint === null || deliverToContactPoint === void 0 ? void 0 : deliverToContactPoint.phone)
|
|
83
|
-
? String(deliverToContactPoint.phone).replace(/\D/g, '')
|
|
84
|
-
: ((_f = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _f === void 0 ? void 0 : _f.phone1)
|
|
85
|
-
? bodyReq.deliverTo.phone1.replace(/\D/g, '')
|
|
86
|
-
: null;
|
|
65
|
+
let newPhone1 = ((_f = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _f === void 0 ? void 0 : _f.phone1) ? bodyReq.deliverTo.phone1.replace(/\D/g, '') : null;
|
|
87
66
|
let newPhone2 = ((_g = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _g === void 0 ? void 0 : _g.phone2) ? bodyReq.deliverTo.phone2.replace(/\D/g, '') : null;
|
|
88
67
|
releaseGood = {
|
|
89
68
|
domain,
|
|
@@ -103,36 +82,23 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
103
82
|
type: bodyReq.type,
|
|
104
83
|
marketplaceOrderStatus: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.marketplaceOrderStatus,
|
|
105
84
|
remark: (bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.remark) || null,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
: ((_h = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.billTo) === null || _h === void 0 ? void 0 : _h.billingAddress) || null,
|
|
110
|
-
deliveryAddress1: deliverToContactPoint
|
|
111
|
-
? deliverToContactPoint.address || null
|
|
112
|
-
: ((_j = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _j === void 0 ? void 0 : _j.deliveryAddress1) || null,
|
|
113
|
-
deliveryAddress2: deliverToContactPoint
|
|
114
|
-
? deliverToContactPoint.address2 || null
|
|
115
|
-
: ((_k = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _k === void 0 ? void 0 : _k.deliveryAddress2) || null,
|
|
85
|
+
billingAddress: ((_h = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.billTo) === null || _h === void 0 ? void 0 : _h.billingAddress) || null,
|
|
86
|
+
deliveryAddress1: ((_j = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _j === void 0 ? void 0 : _j.deliveryAddress1) || null,
|
|
87
|
+
deliveryAddress2: ((_k = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _k === void 0 ? void 0 : _k.deliveryAddress2) || null,
|
|
116
88
|
deliveryAddress3: ((_l = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _l === void 0 ? void 0 : _l.deliveryAddress3) || null,
|
|
117
89
|
deliveryAddress4: ((_m = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _m === void 0 ? void 0 : _m.deliveryAddress4) || null,
|
|
118
90
|
deliveryAddress5: ((_o = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _o === void 0 ? void 0 : _o.deliveryAddress5) || null,
|
|
119
|
-
attentionTo:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
attentionCompany: deliverToContactPoint
|
|
123
|
-
? deliverToContactPoint.companyName || null
|
|
124
|
-
: ((_q = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _q === void 0 ? void 0 : _q.attentionCompany) || null,
|
|
125
|
-
city: deliverToContactPoint ? deliverToContactPoint.city || null : ((_r = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _r === void 0 ? void 0 : _r.city) || null,
|
|
91
|
+
attentionTo: ((_p = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _p === void 0 ? void 0 : _p.attentionTo) || null,
|
|
92
|
+
attentionCompany: ((_q = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _q === void 0 ? void 0 : _q.attentionCompany) || null,
|
|
93
|
+
city: ((_r = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _r === void 0 ? void 0 : _r.city) || null,
|
|
126
94
|
ward: (_s = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _s === void 0 ? void 0 : _s.ward,
|
|
127
|
-
district:
|
|
128
|
-
state:
|
|
129
|
-
postalCode:
|
|
130
|
-
|
|
131
|
-
: ((_v = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _v === void 0 ? void 0 : _v.postalCode) || null,
|
|
132
|
-
country: deliverToContactPoint ? null : ((_w = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _w === void 0 ? void 0 : _w.country) || null,
|
|
95
|
+
district: (_t = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _t === void 0 ? void 0 : _t.district,
|
|
96
|
+
state: ((_u = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _u === void 0 ? void 0 : _u.state) || null,
|
|
97
|
+
postalCode: ((_v = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _v === void 0 ? void 0 : _v.postalCode) || null,
|
|
98
|
+
country: ((_w = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _w === void 0 ? void 0 : _w.country) || null,
|
|
133
99
|
phone1: newPhone1,
|
|
134
100
|
phone2: newPhone2,
|
|
135
|
-
email:
|
|
101
|
+
email: ((_x = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _x === void 0 ? void 0 : _x.email) || null,
|
|
136
102
|
transporter: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.transporter,
|
|
137
103
|
trackingNo: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.trackingNo,
|
|
138
104
|
airwayBill: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.airwayBill,
|
|
@@ -152,7 +118,6 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
152
118
|
shippingFee: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingFee,
|
|
153
119
|
lmdOption: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.lmdOption,
|
|
154
120
|
priorityDelivery: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.priorityDelivery,
|
|
155
|
-
deliverTo: deliverToContactPoint || null,
|
|
156
121
|
shippingOrder: (bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingOrder)
|
|
157
122
|
? {
|
|
158
123
|
shipName: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingOrder.shipName,
|
|
@@ -204,149 +169,204 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
204
169
|
// massage data
|
|
205
170
|
massagedData = await massageOrderItems(releaseGood, bodyReq.orderInventories, context); //double check this function
|
|
206
171
|
const invWithoutBatchId = batchIdStates.withoutBatchId;
|
|
207
|
-
// Helper function to build inventory availability query
|
|
208
|
-
const buildInventoryAvailabilityQuery = (item, domain, customerBizplace, options) => {
|
|
209
|
-
const { batchId, warehouseName, releaseShelfLifeOverride, requireWarehouseJoin, includeLockInventoryCheck } = options;
|
|
210
|
-
const hasWarehouse = warehouseName != null;
|
|
211
|
-
const hasBatch = batchId != null;
|
|
212
|
-
// Determine warehouse join strategy
|
|
213
|
-
const warehouseJoin = requireWarehouseJoin || hasWarehouse ? 'inner join warehouses w on w.id = loc.warehouse_id' : '';
|
|
214
|
-
// Build warehouse filter
|
|
215
|
-
let warehouseFilter = '';
|
|
216
|
-
if (requireWarehouseJoin) {
|
|
217
|
-
// For batch queries: always join warehouses, filter optionally
|
|
218
|
-
warehouseFilter = 'and ($8::text is null or w.name = $8::text)';
|
|
219
|
-
}
|
|
220
|
-
else if (hasWarehouse) {
|
|
221
|
-
// For non-batch queries: only filter if warehouse is specified
|
|
222
|
-
warehouseFilter = 'and w.name = $7';
|
|
223
|
-
}
|
|
224
|
-
// Build batch filter
|
|
225
|
-
const batchFilter = hasBatch ? 'and iv.batch_id = $7' : '';
|
|
226
|
-
// Calculate parameter indices
|
|
227
|
-
const shelfLifeParamIndex = hasBatch ? '$9' : hasWarehouse ? '$8' : '$7';
|
|
228
|
-
// Build lock inventory check
|
|
229
|
-
const lockInventoryCheck = includeLockInventoryCheck
|
|
230
|
-
? `and not exists (
|
|
231
|
-
select 1 from inventories i
|
|
232
|
-
where i.domain_id = $1 and i.bizplace_id = $2 and i.product_id = $3 and i.packing_type = $4 and i.packing_size = $5 and i.uom = $6 and i.status = 'STORED' and i.lock_inventory is true
|
|
233
|
-
)`
|
|
234
|
-
: '';
|
|
235
|
-
const query = `
|
|
236
|
-
select
|
|
237
|
-
coalesce(sum(
|
|
238
|
-
case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
|
|
239
|
-
then 0
|
|
240
|
-
else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
|
|
241
|
-
end
|
|
242
|
-
), 0) as total_available_qty,
|
|
243
|
-
coalesce(sum(
|
|
244
|
-
case when (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0)) < 0
|
|
245
|
-
then 0
|
|
246
|
-
else (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0))
|
|
247
|
-
end
|
|
248
|
-
), 0) as total_available_uom_value
|
|
249
|
-
from inventories iv
|
|
250
|
-
left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
|
|
251
|
-
inner join locations loc on loc.id = iv.location_id
|
|
252
|
-
${warehouseJoin}
|
|
253
|
-
inner join products p on p.id = iv.product_id
|
|
254
|
-
where iv.domain_id = $1
|
|
255
|
-
and iv.bizplace_id = $2
|
|
256
|
-
and iv.product_id = $3
|
|
257
|
-
and iv.packing_type = $4
|
|
258
|
-
and iv.packing_size = $5
|
|
259
|
-
and iv.uom = $6
|
|
260
|
-
and iv.status = 'STORED'
|
|
261
|
-
and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
|
|
262
|
-
and iv.obsolete = false
|
|
263
|
-
and (case
|
|
264
|
-
when iv.expiration_date is not null
|
|
265
|
-
then CURRENT_DATE < iv.expiration_date - (
|
|
266
|
-
case
|
|
267
|
-
when ${shelfLifeParamIndex}::integer is not null and ${shelfLifeParamIndex}::integer > 0
|
|
268
|
-
then ${shelfLifeParamIndex}::integer
|
|
269
|
-
when p.min_outbound_shelf_life is not null
|
|
270
|
-
then p.min_outbound_shelf_life
|
|
271
|
-
else 0
|
|
272
|
-
end
|
|
273
|
-
)
|
|
274
|
-
else true
|
|
275
|
-
end)
|
|
276
|
-
${batchFilter}
|
|
277
|
-
${warehouseFilter}
|
|
278
|
-
${lockInventoryCheck}
|
|
279
|
-
`;
|
|
280
|
-
// Build parameters array
|
|
281
|
-
const params = [
|
|
282
|
-
domain.id,
|
|
283
|
-
customerBizplace.id,
|
|
284
|
-
item.product.id,
|
|
285
|
-
item.packingType,
|
|
286
|
-
item.packingSize,
|
|
287
|
-
item.uom
|
|
288
|
-
];
|
|
289
|
-
if (hasBatch) {
|
|
290
|
-
params.push(batchId);
|
|
291
|
-
}
|
|
292
|
-
if (requireWarehouseJoin || hasWarehouse) {
|
|
293
|
-
params.push(warehouseName);
|
|
294
|
-
}
|
|
295
|
-
params.push(releaseShelfLifeOverride);
|
|
296
|
-
return { query, params };
|
|
297
|
-
};
|
|
298
|
-
// Helper function to validate inventory availability
|
|
299
|
-
const validateInventoryAvailability = async (item, query, params, tx) => {
|
|
300
|
-
var _a, _b;
|
|
301
|
-
const rows = await tx.query(query, params);
|
|
302
|
-
const totalQty = parseFloat(((_a = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _a === void 0 ? void 0 : _a.total_available_qty) || '0');
|
|
303
|
-
const totalUomVal = parseFloat(((_b = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _b === void 0 ? void 0 : _b.total_available_uom_value) || '0');
|
|
304
|
-
let reqQty = item.releaseQty;
|
|
305
|
-
let reqUomVal = item.releaseUomValue;
|
|
306
|
-
if (!item.product.isInventoryDecimal) {
|
|
307
|
-
reqQty = Math.round(reqQty);
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
reqQty = Math.round(reqQty * 1000) / 1000;
|
|
311
|
-
}
|
|
312
|
-
if (totalQty + 1e-6 < reqQty || totalUomVal + 1e-6 < reqUomVal) {
|
|
313
|
-
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
172
|
//inv without batchId will check stock in wboi
|
|
317
173
|
if (invWithoutBatchId) {
|
|
318
174
|
// validation
|
|
319
175
|
await Promise.all(massagedData.combinedItems.map(async (item) => {
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
176
|
+
var _a, _b;
|
|
177
|
+
const hasWarehouse = !!item.warehouseCode;
|
|
178
|
+
if (hasWarehouse) {
|
|
179
|
+
// Validate availability within the specific warehouse across pickable locations (exclude Q/R/D/STORAGE)
|
|
180
|
+
const rows = await tx.query(`
|
|
181
|
+
select
|
|
182
|
+
coalesce(sum(
|
|
183
|
+
case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
|
|
184
|
+
then 0
|
|
185
|
+
else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
|
|
186
|
+
end
|
|
187
|
+
), 0) as total_available_qty,
|
|
188
|
+
coalesce(sum(
|
|
189
|
+
case when (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0)) < 0
|
|
190
|
+
then 0
|
|
191
|
+
else (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0))
|
|
192
|
+
end
|
|
193
|
+
), 0) as total_available_uom_value
|
|
194
|
+
from inventories iv
|
|
195
|
+
left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
|
|
196
|
+
inner join locations loc on loc.id = iv.location_id
|
|
197
|
+
inner join warehouses w on w.id = loc.warehouse_id
|
|
198
|
+
inner join products p on p.id = iv.product_id
|
|
199
|
+
where iv.domain_id = $1
|
|
200
|
+
and iv.bizplace_id = $2
|
|
201
|
+
and iv.product_id = $3
|
|
202
|
+
and iv.packing_type = $4
|
|
203
|
+
and iv.packing_size = $5
|
|
204
|
+
and iv.uom = $6
|
|
205
|
+
and iv.status = 'STORED'
|
|
206
|
+
and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
|
|
207
|
+
and iv.obsolete = false
|
|
208
|
+
and (case when iv.expiration_date is not null and p.min_outbound_shelf_life is not null then CURRENT_DATE < iv.expiration_date - p.min_outbound_shelf_life else true end)
|
|
209
|
+
and w.name = $7
|
|
210
|
+
and not exists (
|
|
211
|
+
select 1 from inventories i
|
|
212
|
+
where i.domain_id = $1 and i.bizplace_id = $2 and i.product_id = $3 and i.packing_type = $4 and i.packing_size = $5 and i.uom = $6 and i.status = 'STORED' and i.lock_inventory is true
|
|
213
|
+
)
|
|
214
|
+
`, [
|
|
215
|
+
domain.id,
|
|
216
|
+
customerBizplace.id,
|
|
217
|
+
item.product.id,
|
|
218
|
+
item.packingType,
|
|
219
|
+
item.packingSize,
|
|
220
|
+
item.uom,
|
|
221
|
+
String(item.warehouseCode).trim()
|
|
222
|
+
]);
|
|
223
|
+
const totalQty = parseFloat(((_a = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _a === void 0 ? void 0 : _a.total_available_qty) || '0');
|
|
224
|
+
const totalUomVal = parseFloat(((_b = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _b === void 0 ? void 0 : _b.total_available_uom_value) || '0');
|
|
225
|
+
let reqQty = item.releaseQty;
|
|
226
|
+
let reqUomVal = item.releaseUomValue;
|
|
227
|
+
if (!item.product.isInventoryDecimal) {
|
|
228
|
+
reqQty = Math.round(reqQty);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
reqQty = Math.round(reqQty * 1000) / 1000;
|
|
232
|
+
}
|
|
233
|
+
if (totalQty + 1e-6 < reqQty || totalUomVal + 1e-6 < reqUomVal) {
|
|
234
|
+
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// Fallback to existing global view-based validation when no warehouseCode provided
|
|
239
|
+
let itemList = await tx.query(`
|
|
240
|
+
select wboi.*
|
|
241
|
+
from warehouse_bizplace_onhand_inventories wboi
|
|
242
|
+
left join (
|
|
243
|
+
select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom,
|
|
244
|
+
sum(i2.qty) as storage_qty,
|
|
245
|
+
sum(i2.uom_value) as storage_uom_value
|
|
246
|
+
from inventories i2
|
|
247
|
+
inner join locations l2 on l2.id = i2.location_id
|
|
248
|
+
inner join warehouses w on w.id = l2.warehouse_id
|
|
249
|
+
where i2.domain_id = $1
|
|
250
|
+
and i2.bizplace_id = $2
|
|
251
|
+
and i2.product_id = $3
|
|
252
|
+
and i2.packing_type = $4
|
|
253
|
+
and i2.packing_size = $5
|
|
254
|
+
and i2.uom = $6
|
|
255
|
+
and i2.status = 'STORED'
|
|
256
|
+
and l2.type = 'STORAGE'
|
|
257
|
+
group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
|
|
258
|
+
) storageInv
|
|
259
|
+
on storageInv.product_id = wboi.product_id
|
|
260
|
+
and storageInv.domain_id = wboi.domain_id
|
|
261
|
+
and storageInv.bizplace_id = wboi.bizplace_id
|
|
262
|
+
and storageInv.packing_type = wboi.packing_type
|
|
263
|
+
and storageInv.packing_size = wboi.packing_size
|
|
264
|
+
and storageInv.uom = wboi.uom
|
|
265
|
+
left join (
|
|
266
|
+
select i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
|
|
267
|
+
from inventories i
|
|
268
|
+
where i.domain_id = $1
|
|
269
|
+
and i.bizplace_id = $2
|
|
270
|
+
and i.product_id = $3
|
|
271
|
+
and i.packing_type = $4
|
|
272
|
+
and i.packing_size = $5
|
|
273
|
+
and i.uom = $6
|
|
274
|
+
and i.status = 'STORED'
|
|
275
|
+
group by i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
|
|
276
|
+
) lockInv
|
|
277
|
+
on lockInv.product_id = wboi.product_id
|
|
278
|
+
and lockInv.domain_id = wboi.domain_id
|
|
279
|
+
and lockInv.bizplace_id = wboi.bizplace_id
|
|
280
|
+
and lockInv.packing_type = wboi.packing_type
|
|
281
|
+
and lockInv.packing_size = wboi.packing_size
|
|
282
|
+
and lockInv.uom = wboi.uom
|
|
283
|
+
where wboi.domain_id = $1
|
|
284
|
+
and wboi.bizplace_id = $2
|
|
285
|
+
and wboi.group_type = 'SINGLE'
|
|
286
|
+
and wboi.product_id = $3
|
|
287
|
+
and wboi.packing_type = $4
|
|
288
|
+
and wboi.packing_size = $5
|
|
289
|
+
and wboi.uom = $6
|
|
290
|
+
and lockInv.lock_inventory is not true
|
|
291
|
+
and (wboi.remain_qty - wboi.transfer_qty - coalesce(storageInv.storage_qty, 0)) >= $7
|
|
292
|
+
and (wboi.remain_uom_value - wboi.transfer_uom_value - coalesce(storageInv.storage_uom_value, 0)) >= $8
|
|
293
|
+
`, [
|
|
294
|
+
domain.id,
|
|
295
|
+
customerBizplace.id,
|
|
296
|
+
item.product.id,
|
|
297
|
+
item.packingType,
|
|
298
|
+
item.packingSize,
|
|
299
|
+
item.uom,
|
|
300
|
+
item.releaseQty,
|
|
301
|
+
item.releaseUomValue
|
|
302
|
+
]);
|
|
303
|
+
if (itemList.length <= 0) {
|
|
304
|
+
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
305
|
+
}
|
|
306
|
+
console.log();
|
|
307
|
+
}
|
|
328
308
|
}));
|
|
329
309
|
}
|
|
330
310
|
// inv with batchId and assignment enabled: validate availability by batch before creating order
|
|
331
311
|
if (!invWithoutBatchId && (worksheetPickingAssignment === null || worksheetPickingAssignment === void 0 ? void 0 : worksheetPickingAssignment.value) === 'true') {
|
|
332
312
|
await Promise.all(massagedData.combinedItems.map(async (item) => {
|
|
313
|
+
var _a;
|
|
333
314
|
const batchId = (item === null || item === void 0 ? void 0 : item.batchId) && item.batchId !== '-' ? String(item.batchId).trim() : null;
|
|
334
315
|
if (!batchId)
|
|
335
316
|
return;
|
|
336
317
|
const warehouseName = (item === null || item === void 0 ? void 0 : item.warehouseCode) ? String(item.warehouseCode).trim() : null;
|
|
337
|
-
const
|
|
318
|
+
const rows = await tx.query(`
|
|
319
|
+
select
|
|
320
|
+
coalesce(sum(
|
|
321
|
+
case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
|
|
322
|
+
then 0
|
|
323
|
+
else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
|
|
324
|
+
end
|
|
325
|
+
), 0) as total_available_qty
|
|
326
|
+
from inventories iv
|
|
327
|
+
left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
|
|
328
|
+
inner join locations loc on loc.id = iv.location_id
|
|
329
|
+
inner join warehouses w on w.id = loc.warehouse_id
|
|
330
|
+
inner join products p on p.id = iv.product_id
|
|
331
|
+
where iv.domain_id = $1
|
|
332
|
+
and iv.bizplace_id = $2
|
|
333
|
+
and iv.product_id = $3
|
|
334
|
+
and iv.packing_type = $4
|
|
335
|
+
and iv.packing_size = $5
|
|
336
|
+
and iv.uom = $6
|
|
337
|
+
and iv.status = 'STORED'
|
|
338
|
+
and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
|
|
339
|
+
and iv.obsolete = false
|
|
340
|
+
and (case when iv.expiration_date is not null and p.min_outbound_shelf_life is not null then CURRENT_DATE < iv.expiration_date - p.min_outbound_shelf_life else true end)
|
|
341
|
+
and iv.batch_id = $7
|
|
342
|
+
and ( $8::text is null or w.name = $8::text )
|
|
343
|
+
`, [
|
|
344
|
+
domain.id,
|
|
345
|
+
customerBizplace.id,
|
|
346
|
+
item.product.id,
|
|
347
|
+
item.packingType,
|
|
348
|
+
item.packingSize,
|
|
349
|
+
item.uom,
|
|
338
350
|
batchId,
|
|
339
|
-
warehouseName
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
351
|
+
warehouseName
|
|
352
|
+
]);
|
|
353
|
+
const totalQty = parseFloat(((_a = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _a === void 0 ? void 0 : _a.total_available_qty) || '0');
|
|
354
|
+
let reqQty = item.releaseQty;
|
|
355
|
+
// normalize comparison for decimal vs non-decimal products
|
|
356
|
+
if (!item.product.isInventoryDecimal) {
|
|
357
|
+
reqQty = Math.round(reqQty);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
reqQty = Math.round(reqQty * 1000) / 1000;
|
|
361
|
+
}
|
|
362
|
+
if (totalQty + 1e-6 < reqQty) {
|
|
363
|
+
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
364
|
+
}
|
|
345
365
|
}));
|
|
346
366
|
}
|
|
347
367
|
// assignment
|
|
348
368
|
// console.time('assign')
|
|
349
|
-
let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, context, tx, worksheetPickingAssignment
|
|
369
|
+
let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, context, tx, worksheetPickingAssignment);
|
|
350
370
|
// console.timeEnd('assign')
|
|
351
371
|
// create order
|
|
352
372
|
// console.time('save')
|
|
@@ -569,7 +589,6 @@ async function createReleaseGood(releaseGood, orderProducts, bizplace, context,
|
|
|
569
589
|
: sales_base_1.OrderNoGenerator.releaseGood(),
|
|
570
590
|
domain: domain,
|
|
571
591
|
bizplace: bizplace,
|
|
572
|
-
deliverTo: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.deliverTo) || null,
|
|
573
592
|
collectionOrderNo: releaseGood.collectionOrderNo,
|
|
574
593
|
courierOption: courierOption,
|
|
575
594
|
codOption: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.codOption,
|
|
@@ -932,7 +951,7 @@ async function massageOrderItems(releaseGood, inputOrderProducts, context) {
|
|
|
932
951
|
mapJsonData(releaseGood, orderProducts);
|
|
933
952
|
return { orderProducts, combinedItems };
|
|
934
953
|
}
|
|
935
|
-
async function assignToInventory(orderProducts, customerBizplace, context, tx, worksheetPickingAssignment
|
|
954
|
+
async function assignToInventory(orderProducts, customerBizplace, context, tx, worksheetPickingAssignment) {
|
|
936
955
|
var _a, _b;
|
|
937
956
|
const { domain, user } = context.state;
|
|
938
957
|
const pickingProductSetting = await tx.getRepository(setting_base_1.Setting).findOne({
|
|
@@ -1050,9 +1069,6 @@ async function assignToInventory(orderProducts, customerBizplace, context, tx, w
|
|
|
1050
1069
|
if ((_b = orderInventory[oiIdx]) === null || _b === void 0 ? void 0 : _b.warehouseCode) {
|
|
1051
1070
|
params.push(String(orderInventory[oiIdx].warehouseCode).trim());
|
|
1052
1071
|
}
|
|
1053
|
-
// add release shelf life override parameter
|
|
1054
|
-
const releaseShelfLifeParamIndex = params.length + 1;
|
|
1055
|
-
params.push(releaseShelfLifeOverride);
|
|
1056
1072
|
let query = `
|
|
1057
1073
|
update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
|
|
1058
1074
|
locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
|
|
@@ -1094,12 +1110,7 @@ async function assignToInventory(orderProducts, customerBizplace, context, tx, w
|
|
|
1094
1110
|
"iv"."domain_id" = $2 AND "iv"."bizplace_id" = $3 AND "iv"."packing_type" = $4 AND "iv"."packing_size" = $5 AND "iv"."product_id" = $6 AND "iv"."status" = $7 AND "loc"."type" NOT IN ($8, $9, $10)
|
|
1095
1111
|
AND ${batchId ? `"iv"."batch_id" = $11` : `1=1`}
|
|
1096
1112
|
${warehouseNameFilter}
|
|
1097
|
-
AND "iv"."obsolete" = false AND case
|
|
1098
|
-
when "iv"."expiration_date" is not null
|
|
1099
|
-
and coalesce($${releaseShelfLifeParamIndex}::integer, "p"."min_outbound_shelf_life") is not null
|
|
1100
|
-
then CURRENT_DATE < "iv"."expiration_date" - coalesce($${releaseShelfLifeParamIndex}::integer, "p"."min_outbound_shelf_life")
|
|
1101
|
-
else true
|
|
1102
|
-
end
|
|
1113
|
+
AND "iv"."obsolete" = false AND case when "iv"."expiration_date" is not null and "p"."min_outbound_shelf_life" is not null then CURRENT_DATE < "iv"."expiration_date" - "p"."min_outbound_shelf_life" else true end
|
|
1103
1114
|
${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
|
|
1104
1115
|
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
1105
1116
|
)
|