@things-factory/operato-hub 4.3.701 → 4.3.708
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 +24 -0
- 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 +10 -1
- 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 +188 -194
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js +18 -5
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js.map +1 -1
- package/openapi/v1/contact-point.yaml +37 -48
- package/package.json +58 -58
- package/server/routers/api/restful-apis/v1/company/add-contact-points.ts +29 -1
- package/server/routers/api/restful-apis/v1/utils/params.ts +10 -1
- package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +220 -206
- package/server/routers/api/restful-apis/v1/warehouse/update-release-order-details.ts +19 -5
|
@@ -44,6 +44,22 @@ 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
|
+
}
|
|
47
63
|
const worksheetPickingAssignment = await tx.getRepository(setting_base_1.Setting).findOne({
|
|
48
64
|
where: { domain, category: 'id-rule', name: 'enable-worksheet-picking-activation-assignment' }
|
|
49
65
|
});
|
|
@@ -62,7 +78,12 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
62
78
|
salesType: ((_e = bodyReq.xilnexSetting) === null || _e === void 0 ? void 0 : _e.salesType) || ''
|
|
63
79
|
});
|
|
64
80
|
}
|
|
65
|
-
|
|
81
|
+
// derive phone values: prefer contact point when provided, else use body
|
|
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;
|
|
66
87
|
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;
|
|
67
88
|
releaseGood = {
|
|
68
89
|
domain,
|
|
@@ -82,23 +103,36 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
82
103
|
type: bodyReq.type,
|
|
83
104
|
marketplaceOrderStatus: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.marketplaceOrderStatus,
|
|
84
105
|
remark: (bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.remark) || null,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
106
|
+
// when contactPoint exists, override target fields from contactPoint, else use request body
|
|
107
|
+
billingAddress: deliverToContactPoint
|
|
108
|
+
? deliverToContactPoint.billingAddress || null
|
|
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,
|
|
88
116
|
deliveryAddress3: ((_l = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _l === void 0 ? void 0 : _l.deliveryAddress3) || null,
|
|
89
117
|
deliveryAddress4: ((_m = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _m === void 0 ? void 0 : _m.deliveryAddress4) || null,
|
|
90
118
|
deliveryAddress5: ((_o = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _o === void 0 ? void 0 : _o.deliveryAddress5) || null,
|
|
91
|
-
attentionTo:
|
|
92
|
-
|
|
93
|
-
|
|
119
|
+
attentionTo: deliverToContactPoint
|
|
120
|
+
? deliverToContactPoint.name || null
|
|
121
|
+
: ((_p = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _p === void 0 ? void 0 : _p.attentionTo) || null,
|
|
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,
|
|
94
126
|
ward: (_s = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _s === void 0 ? void 0 : _s.ward,
|
|
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:
|
|
98
|
-
|
|
127
|
+
district: deliverToContactPoint ? null : (_t = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _t === void 0 ? void 0 : _t.district,
|
|
128
|
+
state: deliverToContactPoint ? deliverToContactPoint.state || null : ((_u = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _u === void 0 ? void 0 : _u.state) || null,
|
|
129
|
+
postalCode: deliverToContactPoint
|
|
130
|
+
? deliverToContactPoint.postCode || null
|
|
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,
|
|
99
133
|
phone1: newPhone1,
|
|
100
134
|
phone2: newPhone2,
|
|
101
|
-
email: ((_x = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _x === void 0 ? void 0 : _x.email) || null,
|
|
135
|
+
email: deliverToContactPoint ? deliverToContactPoint.email || null : ((_x = bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.deliverTo) === null || _x === void 0 ? void 0 : _x.email) || null,
|
|
102
136
|
transporter: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.transporter,
|
|
103
137
|
trackingNo: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.trackingNo,
|
|
104
138
|
airwayBill: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.airwayBill,
|
|
@@ -118,6 +152,7 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
118
152
|
shippingFee: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingFee,
|
|
119
153
|
lmdOption: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.lmdOption,
|
|
120
154
|
priorityDelivery: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.priorityDelivery,
|
|
155
|
+
deliverTo: deliverToContactPoint || null,
|
|
121
156
|
shippingOrder: (bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingOrder)
|
|
122
157
|
? {
|
|
123
158
|
shipName: bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingOrder.shipName,
|
|
@@ -169,204 +204,148 @@ api_1.restfulApiRouter.post(`/${apiVersion}/warehouse/add-release-order`, middle
|
|
|
169
204
|
// massage data
|
|
170
205
|
massagedData = await massageOrderItems(releaseGood, bodyReq.orderInventories, context); //double check this function
|
|
171
206
|
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 (
|
|
264
|
+
iv.expiration_date is null
|
|
265
|
+
or
|
|
266
|
+
case
|
|
267
|
+
when ${shelfLifeParamIndex}::integer is not null and ${shelfLifeParamIndex}::integer > 0 then
|
|
268
|
+
CURRENT_DATE < iv.expiration_date - ${shelfLifeParamIndex}::integer
|
|
269
|
+
when p.min_outbound_shelf_life is not null and p.min_outbound_shelf_life > 0 then
|
|
270
|
+
CURRENT_DATE < iv.expiration_date - p.min_outbound_shelf_life
|
|
271
|
+
else
|
|
272
|
+
true
|
|
273
|
+
end
|
|
274
|
+
)
|
|
275
|
+
${batchFilter}
|
|
276
|
+
${warehouseFilter}
|
|
277
|
+
${lockInventoryCheck}
|
|
278
|
+
`;
|
|
279
|
+
// Build parameters array
|
|
280
|
+
const params = [
|
|
281
|
+
domain.id,
|
|
282
|
+
customerBizplace.id,
|
|
283
|
+
item.product.id,
|
|
284
|
+
item.packingType,
|
|
285
|
+
item.packingSize,
|
|
286
|
+
item.uom
|
|
287
|
+
];
|
|
288
|
+
if (hasBatch) {
|
|
289
|
+
params.push(batchId);
|
|
290
|
+
}
|
|
291
|
+
if (requireWarehouseJoin || hasWarehouse) {
|
|
292
|
+
params.push(warehouseName);
|
|
293
|
+
}
|
|
294
|
+
params.push(releaseShelfLifeOverride);
|
|
295
|
+
return { query, params };
|
|
296
|
+
};
|
|
297
|
+
// Helper function to validate inventory availability
|
|
298
|
+
const validateInventoryAvailability = async (item, query, params, tx) => {
|
|
299
|
+
var _a, _b;
|
|
300
|
+
const rows = await tx.query(query, params);
|
|
301
|
+
const totalQty = parseFloat(((_a = rows === null || rows === void 0 ? void 0 : rows[0]) === null || _a === void 0 ? void 0 : _a.total_available_qty) || '0');
|
|
302
|
+
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');
|
|
303
|
+
let reqQty = item.releaseQty;
|
|
304
|
+
let reqUomVal = item.releaseUomValue;
|
|
305
|
+
if (!item.product.isInventoryDecimal) {
|
|
306
|
+
reqQty = Math.round(reqQty);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
reqQty = Math.round(reqQty * 1000) / 1000;
|
|
310
|
+
}
|
|
311
|
+
if (totalQty + 1e-6 < reqQty || totalUomVal + 1e-6 < reqUomVal) {
|
|
312
|
+
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
313
|
+
}
|
|
314
|
+
};
|
|
172
315
|
//inv without batchId will check stock in wboi
|
|
173
316
|
if (invWithoutBatchId) {
|
|
174
317
|
// validation
|
|
175
318
|
await Promise.all(massagedData.combinedItems.map(async (item) => {
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
}
|
|
319
|
+
const warehouseName = item.warehouseCode ? String(item.warehouseCode).trim() : null;
|
|
320
|
+
const { query, params } = buildInventoryAvailabilityQuery(item, domain, customerBizplace, {
|
|
321
|
+
warehouseName,
|
|
322
|
+
releaseShelfLifeOverride,
|
|
323
|
+
requireWarehouseJoin: false,
|
|
324
|
+
includeLockInventoryCheck: true
|
|
325
|
+
});
|
|
326
|
+
await validateInventoryAvailability(item, query, params, tx);
|
|
308
327
|
}));
|
|
309
328
|
}
|
|
310
329
|
// inv with batchId and assignment enabled: validate availability by batch before creating order
|
|
311
330
|
if (!invWithoutBatchId && (worksheetPickingAssignment === null || worksheetPickingAssignment === void 0 ? void 0 : worksheetPickingAssignment.value) === 'true') {
|
|
312
331
|
await Promise.all(massagedData.combinedItems.map(async (item) => {
|
|
313
|
-
var _a;
|
|
314
332
|
const batchId = (item === null || item === void 0 ? void 0 : item.batchId) && item.batchId !== '-' ? String(item.batchId).trim() : null;
|
|
315
333
|
if (!batchId)
|
|
316
334
|
return;
|
|
317
335
|
const warehouseName = (item === null || item === void 0 ? void 0 : item.warehouseCode) ? String(item.warehouseCode).trim() : null;
|
|
318
|
-
const
|
|
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,
|
|
336
|
+
const { query, params } = buildInventoryAvailabilityQuery(item, domain, customerBizplace, {
|
|
350
337
|
batchId,
|
|
351
|
-
warehouseName
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
}
|
|
338
|
+
warehouseName,
|
|
339
|
+
releaseShelfLifeOverride,
|
|
340
|
+
requireWarehouseJoin: true,
|
|
341
|
+
includeLockInventoryCheck: false
|
|
342
|
+
});
|
|
343
|
+
await validateInventoryAvailability(item, query, params, tx);
|
|
365
344
|
}));
|
|
366
345
|
}
|
|
367
346
|
// assignment
|
|
368
347
|
// console.time('assign')
|
|
369
|
-
let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, context, tx, worksheetPickingAssignment);
|
|
348
|
+
let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, context, tx, worksheetPickingAssignment, releaseShelfLifeOverride);
|
|
370
349
|
// console.timeEnd('assign')
|
|
371
350
|
// create order
|
|
372
351
|
// console.time('save')
|
|
@@ -589,6 +568,7 @@ async function createReleaseGood(releaseGood, orderProducts, bizplace, context,
|
|
|
589
568
|
: sales_base_1.OrderNoGenerator.releaseGood(),
|
|
590
569
|
domain: domain,
|
|
591
570
|
bizplace: bizplace,
|
|
571
|
+
deliverTo: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.deliverTo) || null,
|
|
592
572
|
collectionOrderNo: releaseGood.collectionOrderNo,
|
|
593
573
|
courierOption: courierOption,
|
|
594
574
|
codOption: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.codOption,
|
|
@@ -951,7 +931,7 @@ async function massageOrderItems(releaseGood, inputOrderProducts, context) {
|
|
|
951
931
|
mapJsonData(releaseGood, orderProducts);
|
|
952
932
|
return { orderProducts, combinedItems };
|
|
953
933
|
}
|
|
954
|
-
async function assignToInventory(orderProducts, customerBizplace, context, tx, worksheetPickingAssignment) {
|
|
934
|
+
async function assignToInventory(orderProducts, customerBizplace, context, tx, worksheetPickingAssignment, releaseShelfLifeOverride) {
|
|
955
935
|
var _a, _b;
|
|
956
936
|
const { domain, user } = context.state;
|
|
957
937
|
const pickingProductSetting = await tx.getRepository(setting_base_1.Setting).findOne({
|
|
@@ -1069,6 +1049,9 @@ async function assignToInventory(orderProducts, customerBizplace, context, tx, w
|
|
|
1069
1049
|
if ((_b = orderInventory[oiIdx]) === null || _b === void 0 ? void 0 : _b.warehouseCode) {
|
|
1070
1050
|
params.push(String(orderInventory[oiIdx].warehouseCode).trim());
|
|
1071
1051
|
}
|
|
1052
|
+
// add release shelf life override parameter
|
|
1053
|
+
const releaseShelfLifeParamIndex = params.length + 1;
|
|
1054
|
+
params.push(releaseShelfLifeOverride);
|
|
1072
1055
|
let query = `
|
|
1073
1056
|
update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
|
|
1074
1057
|
locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
|
|
@@ -1110,7 +1093,18 @@ async function assignToInventory(orderProducts, customerBizplace, context, tx, w
|
|
|
1110
1093
|
"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)
|
|
1111
1094
|
AND ${batchId ? `"iv"."batch_id" = $11` : `1=1`}
|
|
1112
1095
|
${warehouseNameFilter}
|
|
1113
|
-
AND "iv"."obsolete" = false AND
|
|
1096
|
+
AND "iv"."obsolete" = false AND (
|
|
1097
|
+
"iv"."expiration_date" IS NULL
|
|
1098
|
+
OR
|
|
1099
|
+
CASE
|
|
1100
|
+
WHEN $${releaseShelfLifeParamIndex}::integer IS NOT NULL AND $${releaseShelfLifeParamIndex}::integer > 0 THEN
|
|
1101
|
+
CURRENT_DATE < ("iv"."expiration_date" - $${releaseShelfLifeParamIndex}::integer)
|
|
1102
|
+
WHEN "p"."min_outbound_shelf_life" IS NOT NULL AND "p"."min_outbound_shelf_life" > 0 THEN
|
|
1103
|
+
CURRENT_DATE < ("iv"."expiration_date" - "p"."min_outbound_shelf_life")
|
|
1104
|
+
ELSE
|
|
1105
|
+
TRUE
|
|
1106
|
+
END
|
|
1107
|
+
)
|
|
1114
1108
|
${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
|
|
1115
1109
|
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
1116
1110
|
)
|