@things-factory/operato-hub 4.3.677 → 4.3.679
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/graphql/resolvers/reports/inbound-order-details-report.js +2 -1
- package/dist-server/graphql/resolvers/reports/inbound-order-details-report.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/utils/params.js +6 -0
- 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 +238 -53
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
- package/openapi/v1/outbound.yaml +10 -0
- package/package.json +5 -5
- package/server/graphql/resolvers/reports/inbound-order-details-report.ts +5 -4
- package/server/routers/api/restful-apis/v1/utils/params.ts +6 -0
- package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +269 -54
|
@@ -40,13 +40,19 @@ import {
|
|
|
40
40
|
} from '@things-factory/sales-base'
|
|
41
41
|
import { Setting } from '@things-factory/setting-base'
|
|
42
42
|
import { Domain } from '@things-factory/shell'
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
Inventory,
|
|
45
|
+
INVENTORY_STATUS,
|
|
46
|
+
LOCATION_TYPE,
|
|
47
|
+
ProductDetailStock,
|
|
48
|
+
Warehouse
|
|
49
|
+
} from '@things-factory/warehouse-base'
|
|
44
50
|
|
|
45
51
|
import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
|
|
46
52
|
import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
|
|
47
53
|
import { getTmsService } from '@things-factory/integration-lmd'
|
|
48
54
|
|
|
49
|
-
const debug = require('debug')('things-factory:operato-hub:restful-api:
|
|
55
|
+
const debug = require('debug')('things-factory:operato-hub:restful-api:v1:add-release-order')
|
|
50
56
|
const apiVersion = 'v1'
|
|
51
57
|
|
|
52
58
|
router.post(
|
|
@@ -202,6 +208,28 @@ router.post(
|
|
|
202
208
|
throw new ApiError('E01', 'Please provide batch id for all inventories')
|
|
203
209
|
}
|
|
204
210
|
|
|
211
|
+
// validate warehouseCode (name in warehouses table) if provided on any line
|
|
212
|
+
const providedWarehouseCodes = Array.from(
|
|
213
|
+
new Set(
|
|
214
|
+
(bodyReq.orderInventories || [])
|
|
215
|
+
.map((it: any) => it?.warehouseCode && String(it.warehouseCode).trim())
|
|
216
|
+
.filter(Boolean)
|
|
217
|
+
)
|
|
218
|
+
) as string[]
|
|
219
|
+
|
|
220
|
+
if (providedWarehouseCodes.length > 0) {
|
|
221
|
+
const warehouses: Warehouse[] = await tx.getRepository(Warehouse).find({
|
|
222
|
+
where: { domain, name: In(providedWarehouseCodes) }
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const foundNames = new Set(warehouses.map(w => w.name))
|
|
226
|
+
const missing = providedWarehouseCodes.filter(code => !foundNames.has(code))
|
|
227
|
+
|
|
228
|
+
if (missing.length > 0) {
|
|
229
|
+
throw new ApiError('E04', `warehouseCode not exist in master: ${missing.join(', ')}`)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
205
233
|
// massage data
|
|
206
234
|
massagedData = await massageOrderItems(releaseGood, bodyReq.orderInventories, context) //double check this function
|
|
207
235
|
|
|
@@ -212,36 +240,185 @@ router.post(
|
|
|
212
240
|
// validation
|
|
213
241
|
await Promise.all(
|
|
214
242
|
massagedData.combinedItems.map(async item => {
|
|
215
|
-
|
|
243
|
+
const hasWarehouse = !!item.warehouseCode
|
|
244
|
+
if (hasWarehouse) {
|
|
245
|
+
// Validate availability within the specific warehouse across pickable locations (exclude Q/R/D/STORAGE)
|
|
246
|
+
const rows = await tx.query(
|
|
247
|
+
`
|
|
248
|
+
select
|
|
249
|
+
coalesce(sum(
|
|
250
|
+
case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
|
|
251
|
+
then 0
|
|
252
|
+
else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
|
|
253
|
+
end
|
|
254
|
+
), 0) as total_available_qty,
|
|
255
|
+
coalesce(sum(
|
|
256
|
+
case when (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0)) < 0
|
|
257
|
+
then 0
|
|
258
|
+
else (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0))
|
|
259
|
+
end
|
|
260
|
+
), 0) as total_available_uom_value
|
|
261
|
+
from inventories iv
|
|
262
|
+
left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
|
|
263
|
+
inner join locations loc on loc.id = iv.location_id
|
|
264
|
+
inner join warehouses w on w.id = loc.warehouse_id
|
|
265
|
+
inner join products p on p.id = iv.product_id
|
|
266
|
+
where iv.domain_id = $1
|
|
267
|
+
and iv.bizplace_id = $2
|
|
268
|
+
and iv.product_id = $3
|
|
269
|
+
and iv.packing_type = $4
|
|
270
|
+
and iv.packing_size = $5
|
|
271
|
+
and iv.uom = $6
|
|
272
|
+
and iv.status = 'STORED'
|
|
273
|
+
and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
|
|
274
|
+
and iv.obsolete = false
|
|
275
|
+
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)
|
|
276
|
+
and w.name = $7
|
|
277
|
+
and not exists (
|
|
278
|
+
select 1 from inventories i
|
|
279
|
+
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
|
|
280
|
+
)
|
|
281
|
+
`,
|
|
282
|
+
[
|
|
283
|
+
domain.id,
|
|
284
|
+
customerBizplace.id,
|
|
285
|
+
item.product.id,
|
|
286
|
+
item.packingType,
|
|
287
|
+
item.packingSize,
|
|
288
|
+
item.uom,
|
|
289
|
+
String(item.warehouseCode).trim()
|
|
290
|
+
]
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
const totalQty = parseFloat(rows?.[0]?.total_available_qty || '0')
|
|
294
|
+
const totalUomVal = parseFloat(rows?.[0]?.total_available_uom_value || '0')
|
|
295
|
+
|
|
296
|
+
let reqQty = item.releaseQty
|
|
297
|
+
let reqUomVal = item.releaseUomValue
|
|
298
|
+
|
|
299
|
+
if (!item.product.isInventoryDecimal) {
|
|
300
|
+
reqQty = Math.round(reqQty)
|
|
301
|
+
} else {
|
|
302
|
+
reqQty = Math.round(reqQty * 1000) / 1000
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (totalQty + 1e-6 < reqQty || totalUomVal + 1e-6 < reqUomVal) {
|
|
306
|
+
throw new ApiError('E01', 'INSUFFICIENT_STOCK')
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
// Fallback to existing global view-based validation when no warehouseCode provided
|
|
310
|
+
let itemList = await tx.query(
|
|
311
|
+
`
|
|
312
|
+
select wboi.*
|
|
313
|
+
from warehouse_bizplace_onhand_inventories wboi
|
|
314
|
+
left join (
|
|
315
|
+
select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom,
|
|
316
|
+
sum(i2.qty) as storage_qty,
|
|
317
|
+
sum(i2.uom_value) as storage_uom_value
|
|
318
|
+
from inventories i2
|
|
319
|
+
inner join locations l2 on l2.id = i2.location_id
|
|
320
|
+
inner join warehouses w on w.id = l2.warehouse_id
|
|
321
|
+
where i2.domain_id = $1
|
|
322
|
+
and i2.bizplace_id = $2
|
|
323
|
+
and i2.product_id = $3
|
|
324
|
+
and i2.packing_type = $4
|
|
325
|
+
and i2.packing_size = $5
|
|
326
|
+
and i2.uom = $6
|
|
327
|
+
and i2.status = 'STORED'
|
|
328
|
+
and l2.type = 'STORAGE'
|
|
329
|
+
group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
|
|
330
|
+
) storageInv
|
|
331
|
+
on storageInv.product_id = wboi.product_id
|
|
332
|
+
and storageInv.domain_id = wboi.domain_id
|
|
333
|
+
and storageInv.bizplace_id = wboi.bizplace_id
|
|
334
|
+
and storageInv.packing_type = wboi.packing_type
|
|
335
|
+
and storageInv.packing_size = wboi.packing_size
|
|
336
|
+
and storageInv.uom = wboi.uom
|
|
337
|
+
left join (
|
|
338
|
+
select i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
|
|
339
|
+
from inventories i
|
|
340
|
+
where i.domain_id = $1
|
|
341
|
+
and i.bizplace_id = $2
|
|
342
|
+
and i.product_id = $3
|
|
343
|
+
and i.packing_type = $4
|
|
344
|
+
and i.packing_size = $5
|
|
345
|
+
and i.uom = $6
|
|
346
|
+
and i.status = 'STORED'
|
|
347
|
+
group by i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
|
|
348
|
+
) lockInv
|
|
349
|
+
on lockInv.product_id = wboi.product_id
|
|
350
|
+
and lockInv.domain_id = wboi.domain_id
|
|
351
|
+
and lockInv.bizplace_id = wboi.bizplace_id
|
|
352
|
+
and lockInv.packing_type = wboi.packing_type
|
|
353
|
+
and lockInv.packing_size = wboi.packing_size
|
|
354
|
+
and lockInv.uom = wboi.uom
|
|
355
|
+
where wboi.domain_id = $1
|
|
356
|
+
and wboi.bizplace_id = $2
|
|
357
|
+
and wboi.group_type = 'SINGLE'
|
|
358
|
+
and wboi.product_id = $3
|
|
359
|
+
and wboi.packing_type = $4
|
|
360
|
+
and wboi.packing_size = $5
|
|
361
|
+
and wboi.uom = $6
|
|
362
|
+
and lockInv.lock_inventory is not true
|
|
363
|
+
and (wboi.remain_qty - wboi.transfer_qty - coalesce(storageInv.storage_qty, 0)) >= $7
|
|
364
|
+
and (wboi.remain_uom_value - wboi.transfer_uom_value - coalesce(storageInv.storage_uom_value, 0)) >= $8
|
|
365
|
+
`,
|
|
366
|
+
[
|
|
367
|
+
domain.id,
|
|
368
|
+
customerBizplace.id,
|
|
369
|
+
item.product.id,
|
|
370
|
+
item.packingType,
|
|
371
|
+
item.packingSize,
|
|
372
|
+
item.uom,
|
|
373
|
+
item.releaseQty,
|
|
374
|
+
item.releaseUomValue
|
|
375
|
+
]
|
|
376
|
+
)
|
|
377
|
+
if (itemList.length <= 0) {
|
|
378
|
+
throw new ApiError('E01', 'INSUFFICIENT_STOCK')
|
|
379
|
+
}
|
|
380
|
+
console.log()
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// inv with batchId and assignment enabled: validate availability by batch before creating order
|
|
387
|
+
if (!invWithoutBatchId && worksheetPickingAssignment?.value === 'true') {
|
|
388
|
+
await Promise.all(
|
|
389
|
+
massagedData.combinedItems.map(async item => {
|
|
390
|
+
const batchId = item?.batchId && item.batchId !== '-' ? String(item.batchId).trim() : null
|
|
391
|
+
if (!batchId) return
|
|
392
|
+
|
|
393
|
+
const warehouseName = item?.warehouseCode ? String(item.warehouseCode).trim() : null
|
|
394
|
+
|
|
395
|
+
const rows = await tx.query(
|
|
216
396
|
`
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
(wboi."remain_qty" - wboi."transfer_qty" - coalesce(storageInv.storage_qty, 0)) >= $7 and
|
|
243
|
-
(wboi."remain_uom_value" - wboi."transfer_uom_value" - coalesce(storageInv.storage_uom_value, 0)) >= $8
|
|
244
|
-
`,
|
|
397
|
+
select
|
|
398
|
+
coalesce(sum(
|
|
399
|
+
case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
|
|
400
|
+
then 0
|
|
401
|
+
else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
|
|
402
|
+
end
|
|
403
|
+
), 0) as total_available_qty
|
|
404
|
+
from inventories iv
|
|
405
|
+
left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
|
|
406
|
+
inner join locations loc on loc.id = iv.location_id
|
|
407
|
+
inner join warehouses w on w.id = loc.warehouse_id
|
|
408
|
+
inner join products p on p.id = iv.product_id
|
|
409
|
+
where iv.domain_id = $1
|
|
410
|
+
and iv.bizplace_id = $2
|
|
411
|
+
and iv.product_id = $3
|
|
412
|
+
and iv.packing_type = $4
|
|
413
|
+
and iv.packing_size = $5
|
|
414
|
+
and iv.uom = $6
|
|
415
|
+
and iv.status = 'STORED'
|
|
416
|
+
and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
|
|
417
|
+
and iv.obsolete = false
|
|
418
|
+
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)
|
|
419
|
+
and iv.batch_id = $7
|
|
420
|
+
and ( $8::text is null or w.name = $8::text )
|
|
421
|
+
`,
|
|
245
422
|
[
|
|
246
423
|
domain.id,
|
|
247
424
|
customerBizplace.id,
|
|
@@ -249,14 +426,25 @@ router.post(
|
|
|
249
426
|
item.packingType,
|
|
250
427
|
item.packingSize,
|
|
251
428
|
item.uom,
|
|
252
|
-
|
|
253
|
-
|
|
429
|
+
batchId,
|
|
430
|
+
warehouseName
|
|
254
431
|
]
|
|
255
432
|
)
|
|
256
|
-
|
|
433
|
+
|
|
434
|
+
const totalQty = parseFloat(rows?.[0]?.total_available_qty || '0')
|
|
435
|
+
|
|
436
|
+
let reqQty = item.releaseQty
|
|
437
|
+
|
|
438
|
+
// normalize comparison for decimal vs non-decimal products
|
|
439
|
+
if (!item.product.isInventoryDecimal) {
|
|
440
|
+
reqQty = Math.round(reqQty)
|
|
441
|
+
} else {
|
|
442
|
+
reqQty = Math.round(reqQty * 1000) / 1000
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (totalQty + 1e-6 < reqQty) {
|
|
257
446
|
throw new ApiError('E01', 'INSUFFICIENT_STOCK')
|
|
258
447
|
}
|
|
259
|
-
console.log()
|
|
260
448
|
})
|
|
261
449
|
)
|
|
262
450
|
}
|
|
@@ -645,7 +833,7 @@ async function createReleaseGood(
|
|
|
645
833
|
let resultReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
|
|
646
834
|
let combinedOrderInventories: OrderInventory[] = []
|
|
647
835
|
let combinedOrderProducts: OrderProduct[] = []
|
|
648
|
-
|
|
836
|
+
await Promise.all(
|
|
649
837
|
orderProducts.map(async orderProduct => {
|
|
650
838
|
let orderInventories: OrderInventory[] = orderProduct.orderInventories
|
|
651
839
|
orderProduct = Object.assign(new OrderProduct(), {
|
|
@@ -655,7 +843,12 @@ async function createReleaseGood(
|
|
|
655
843
|
bizplace: bizplace,
|
|
656
844
|
releaseGood: resultReleaseGood,
|
|
657
845
|
type: ORDER_TYPES.RELEASE_OF_GOODS,
|
|
658
|
-
status: ORDER_PRODUCT_STATUS.ASSIGNED
|
|
846
|
+
status: ORDER_PRODUCT_STATUS.ASSIGNED,
|
|
847
|
+
// ensure batchId is persisted on order product even when assignment is enabled
|
|
848
|
+
batchId:
|
|
849
|
+
(orderProduct as any)?.batchId !== undefined && (orderProduct as any)?.batchId !== null
|
|
850
|
+
? (orderProduct as any).batchId
|
|
851
|
+
: orderInventories?.[0]?.batchId || ''
|
|
659
852
|
})
|
|
660
853
|
let newOrderProduct = await tx.getRepository(OrderProduct).save(orderProduct)
|
|
661
854
|
|
|
@@ -665,6 +858,7 @@ async function createReleaseGood(
|
|
|
665
858
|
packingSize: orderProduct.packingSize,
|
|
666
859
|
releaseQty: orderProduct.releaseQty,
|
|
667
860
|
releaseUomValue: orderProduct.releaseUomValue,
|
|
861
|
+
warehouseCode: orderProduct.warehouseCode,
|
|
668
862
|
refItemId: orderProduct.refItemId
|
|
669
863
|
})
|
|
670
864
|
|
|
@@ -688,7 +882,8 @@ async function createReleaseGood(
|
|
|
688
882
|
packingType: orderInventory.packingType,
|
|
689
883
|
packingSize: orderInventory.packingSize,
|
|
690
884
|
releaseQty: orderInventory.releaseQty,
|
|
691
|
-
releaseUomValue: orderInventory.releaseUomValue
|
|
885
|
+
releaseUomValue: orderInventory.releaseUomValue,
|
|
886
|
+
warehouseCode: orderInventory.warehouseCode
|
|
692
887
|
})
|
|
693
888
|
}
|
|
694
889
|
}
|
|
@@ -720,7 +915,7 @@ async function massageOrderItems(
|
|
|
720
915
|
inputOrderProducts = await inputOrderProducts.reduce(async (accPromise, itm, idx) => {
|
|
721
916
|
const acc = await accPromise
|
|
722
917
|
let { sku, refCode } = itm.product
|
|
723
|
-
let { packingType, packingSize, batchId, releaseQty, sellingPrice, paidAmount, uom, refItemId } = itm
|
|
918
|
+
let { packingType, packingSize, batchId, releaseQty, sellingPrice, paidAmount, uom, refItemId, warehouseCode } = itm
|
|
724
919
|
|
|
725
920
|
if (!sku && !refCode) {
|
|
726
921
|
throw new ApiError('E01', 'sku or refCode not found')
|
|
@@ -730,14 +925,16 @@ async function massageOrderItems(
|
|
|
730
925
|
throw new ApiError('E01', 'negative stock request')
|
|
731
926
|
}
|
|
732
927
|
|
|
928
|
+
const normalizedWarehouseCode = warehouseCode ? String(warehouseCode).trim() : ''
|
|
733
929
|
let existingIndex = acc.findIndex(
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
930
|
+
it =>
|
|
931
|
+
it?.sku == sku?.trim() &&
|
|
932
|
+
it?.refCode == refCode?.trim() &&
|
|
933
|
+
it?.packingType == packingType?.trim() &&
|
|
934
|
+
it?.packingSize == packingSize &&
|
|
935
|
+
it?.uom == uom &&
|
|
936
|
+
it?.batchId == (batchId?.trim() || '') &&
|
|
937
|
+
(it as any)?.warehouseCode == normalizedWarehouseCode
|
|
741
938
|
)
|
|
742
939
|
|
|
743
940
|
if (existingIndex >= 0 && releaseQty > 0) {
|
|
@@ -753,7 +950,8 @@ async function massageOrderItems(
|
|
|
753
950
|
paidAmount: paidAmount,
|
|
754
951
|
uom: uom,
|
|
755
952
|
batchId: batchId?.trim() || '',
|
|
756
|
-
refItemId: refItemId ?? ''
|
|
953
|
+
refItemId: refItemId ?? '',
|
|
954
|
+
warehouseCode: normalizedWarehouseCode
|
|
757
955
|
})
|
|
758
956
|
}
|
|
759
957
|
|
|
@@ -764,7 +962,6 @@ async function massageOrderItems(
|
|
|
764
962
|
await Promise.all(
|
|
765
963
|
inputOrderProducts.map(async inputOrderItem => {
|
|
766
964
|
const sku: string = inputOrderItem.sku
|
|
767
|
-
const uom: string = inputOrderItem?.uom
|
|
768
965
|
const refCode: string = inputOrderItem.refCode
|
|
769
966
|
const packingType: string = inputOrderItem.packingType
|
|
770
967
|
const packingSize: string = inputOrderItem.packingSize
|
|
@@ -825,6 +1022,7 @@ async function massageOrderItems(
|
|
|
825
1022
|
palletQty: 0,
|
|
826
1023
|
actualPalletQty: 0,
|
|
827
1024
|
type: 'RELEASE_OF_GOODS',
|
|
1025
|
+
warehouseCode: inputOrderItem?.warehouseCode || null,
|
|
828
1026
|
status: ORDER_PRODUCT_STATUS.ASSIGNED,
|
|
829
1027
|
batchId: inputOrderItem?.batchId || ''
|
|
830
1028
|
}
|
|
@@ -837,10 +1035,16 @@ async function massageOrderItems(
|
|
|
837
1035
|
palletQty: 0,
|
|
838
1036
|
actualPalletQty: 0,
|
|
839
1037
|
type: 'RELEASE_OF_GOODS',
|
|
1038
|
+
warehouseCode: inputOrderItem?.warehouseCode || null,
|
|
840
1039
|
status: ORDER_INVENTORY_STATUS.PENDING_WORKSHEET
|
|
841
1040
|
}
|
|
842
1041
|
]
|
|
843
1042
|
|
|
1043
|
+
// ensure warehouseCode is normalized
|
|
1044
|
+
;(newOrderProduct as any).warehouseCode = newOrderProduct?.warehouseCode
|
|
1045
|
+
? String(newOrderProduct.warehouseCode).trim()
|
|
1046
|
+
: null
|
|
1047
|
+
|
|
844
1048
|
orderProducts.push(newOrderProduct)
|
|
845
1049
|
}
|
|
846
1050
|
|
|
@@ -888,6 +1092,7 @@ async function massageOrderItems(
|
|
|
888
1092
|
actualPalletQty: 0,
|
|
889
1093
|
orderInventories: [],
|
|
890
1094
|
type: 'RELEASE_OF_GOODS',
|
|
1095
|
+
warehouseCode: inputOrderItem?.warehouseCode || null,
|
|
891
1096
|
status: ORDER_PRODUCT_STATUS.ASSIGNED,
|
|
892
1097
|
batchId: ''
|
|
893
1098
|
}
|
|
@@ -913,6 +1118,7 @@ async function massageOrderItems(
|
|
|
913
1118
|
bundleProductSetting.bundleQty *
|
|
914
1119
|
(productDetailBundle.uomValue <= 0 ? 1 : productDetailBundle.uomValue),
|
|
915
1120
|
type: 'RELEASE_OF_GOODS',
|
|
1121
|
+
warehouseCode: inputOrderItem?.warehouseCode || null,
|
|
916
1122
|
status: ORDER_INVENTORY_STATUS.PENDING_WORKSHEET
|
|
917
1123
|
}
|
|
918
1124
|
|
|
@@ -941,7 +1147,7 @@ async function massageOrderItems(
|
|
|
941
1147
|
let oi = op?.orderInventories || []
|
|
942
1148
|
oi.forEach(itm => {
|
|
943
1149
|
let { sku, refCode } = itm.product
|
|
944
|
-
let { packingType, packingSize, batchId, releaseQty, product, productDetail, uom } = itm
|
|
1150
|
+
let { packingType, packingSize, batchId, releaseQty, product, productDetail, uom, warehouseCode } = itm
|
|
945
1151
|
|
|
946
1152
|
if (!sku && !refCode) {
|
|
947
1153
|
throw new ApiError('E04', t('error.sku or refCode not found'))
|
|
@@ -953,7 +1159,8 @@ async function massageOrderItems(
|
|
|
953
1159
|
itm?.packingType == packingType?.trim() &&
|
|
954
1160
|
itm?.packingSize == packingSize &&
|
|
955
1161
|
itm?.batchId == batchId?.trim() &&
|
|
956
|
-
itm?.uom == uom?.trim()
|
|
1162
|
+
itm?.uom == uom?.trim() &&
|
|
1163
|
+
(itm as any)?.warehouseCode == (warehouseCode ? String(warehouseCode).trim() : warehouseCode)
|
|
957
1164
|
)
|
|
958
1165
|
|
|
959
1166
|
if (existingIndex >= 0 && releaseQty > 0) {
|
|
@@ -961,7 +1168,7 @@ async function massageOrderItems(
|
|
|
961
1168
|
acc[existingIndex].releaseUomValue =
|
|
962
1169
|
acc[existingIndex].releaseUomValue + releaseQty * (productDetail.uomValue <= 0 ? 1 : productDetail.uomValue)
|
|
963
1170
|
} else {
|
|
964
|
-
let updateObj = {
|
|
1171
|
+
let updateObj: any = {
|
|
965
1172
|
sku: sku?.trim(),
|
|
966
1173
|
packingType: packingType?.trim(),
|
|
967
1174
|
packingSize: packingSize,
|
|
@@ -974,6 +1181,7 @@ async function massageOrderItems(
|
|
|
974
1181
|
|
|
975
1182
|
if (refCode) updateObj['refCode'] = refCode.trim()
|
|
976
1183
|
updateObj['batchId'] = batchId ? batchId?.trim() : '-'
|
|
1184
|
+
if (warehouseCode) updateObj['warehouseCode'] = String(warehouseCode).trim()
|
|
977
1185
|
|
|
978
1186
|
acc.push(updateObj)
|
|
979
1187
|
}
|
|
@@ -1033,7 +1241,7 @@ async function assignToInventory(
|
|
|
1033
1241
|
tx,
|
|
1034
1242
|
worksheetPickingAssignment
|
|
1035
1243
|
) {
|
|
1036
|
-
const {
|
|
1244
|
+
const { domain, user } = context.state
|
|
1037
1245
|
|
|
1038
1246
|
const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
|
|
1039
1247
|
where: { domain, name: 'rule-for-picking-product' }
|
|
@@ -1158,9 +1366,14 @@ async function assignToInventory(
|
|
|
1158
1366
|
LOCATION_TYPE.STORAGE
|
|
1159
1367
|
]
|
|
1160
1368
|
let batchId = orderInventory[oiIdx].batchId
|
|
1161
|
-
if (batchId) params.push(batchId)
|
|
1369
|
+
if (batchId) params.push(String(batchId).trim())
|
|
1162
1370
|
params = [...params, ...queryStrings.values]
|
|
1163
1371
|
|
|
1372
|
+
const warehouseNameFilter = orderInventory[oiIdx]?.warehouseCode ? `AND w.name = $${params.length + 1}` : ''
|
|
1373
|
+
if (orderInventory[oiIdx]?.warehouseCode) {
|
|
1374
|
+
params.push(String(orderInventory[oiIdx].warehouseCode).trim())
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1164
1377
|
let query = `
|
|
1165
1378
|
update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
|
|
1166
1379
|
locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
|
|
@@ -1201,11 +1414,13 @@ async function assignToInventory(
|
|
|
1201
1414
|
FROM "inventories" "iv"
|
|
1202
1415
|
LEFT JOIN "product_detail_stocks" "pds" ON "pds"."product_detail_id" = "iv"."product_detail_id"
|
|
1203
1416
|
INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
|
|
1417
|
+
INNER JOIN "warehouses" "w" ON "w"."id" = "loc"."warehouse_id"
|
|
1204
1418
|
INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
|
|
1205
1419
|
INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
|
|
1206
1420
|
WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
|
|
1207
1421
|
"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)
|
|
1208
1422
|
AND ${batchId ? `"iv"."batch_id" = $11` : `1=1`}
|
|
1423
|
+
${warehouseNameFilter}
|
|
1209
1424
|
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
|
|
1210
1425
|
${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
|
|
1211
1426
|
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
@@ -1231,7 +1446,7 @@ async function assignToInventory(
|
|
|
1231
1446
|
productId: orderInventory[oiIdx].product.id || ''
|
|
1232
1447
|
},
|
|
1233
1448
|
result: updatedInventories[0] || '',
|
|
1234
|
-
location: 'add-release-order
|
|
1449
|
+
location: 'add-release-order v1 assignInventory',
|
|
1235
1450
|
time: new Date()
|
|
1236
1451
|
})
|
|
1237
1452
|
|