@things-factory/worksheet-base 4.3.770 → 4.3.772
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/controllers/inbound/putaway-returning-worksheet-controller.js +10 -5
- package/dist-server/controllers/inbound/putaway-returning-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/inbound/putaway-worksheet-controller.js +10 -5
- package/dist-server/controllers/inbound/putaway-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/inbound/unloading-worksheet-controller.js +45 -19
- package/dist-server/controllers/inbound/unloading-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/outbound/packing-worksheet-controller.js +2 -2
- package/dist-server/controllers/outbound/packing-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/outbound/picking-worksheet-controller.js +169 -44
- package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/outbound/returning-worksheet-controller.js +36 -3
- package/dist-server/controllers/outbound/returning-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/outbound/sorting-worksheet-controller.js +2 -2
- package/dist-server/controllers/outbound/sorting-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/replenishment/replenishment-worksheet-controller.js +1 -1
- package/dist-server/controllers/replenishment/replenishment-worksheet-controller.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-release-good-worksheet.js +12 -8
- package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-release-good-worksheet.js.map +1 -1
- package/package.json +4 -4
- package/server/controllers/inbound/putaway-returning-worksheet-controller.ts +26 -5
- package/server/controllers/inbound/putaway-worksheet-controller.ts +26 -5
- package/server/controllers/inbound/unloading-worksheet-controller.ts +66 -23
- package/server/controllers/outbound/packing-worksheet-controller.ts +3 -2
- package/server/controllers/outbound/picking-worksheet-controller.ts +211 -45
- package/server/controllers/outbound/returning-worksheet-controller.ts +41 -16
- package/server/controllers/outbound/sorting-worksheet-controller.ts +3 -2
- package/server/controllers/replenishment/replenishment-worksheet-controller.ts +1 -7
- package/server/graphql/resolvers/worksheet/generate-worksheet/generate-release-good-worksheet.ts +14 -16
|
@@ -440,19 +440,17 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
440
440
|
foundInventory = await this.trxMgr.getRepository(Inventory).save(newInventory)
|
|
441
441
|
|
|
442
442
|
//refer to scanUnload
|
|
443
|
-
|
|
444
|
-
|
|
443
|
+
await generateInventoryHistory(
|
|
444
|
+
foundInventory,
|
|
445
445
|
Boolean(arrivalNotice) ? arrivalNotice : returnOrder,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
446
|
+
INVENTORY_TRANSACTION_TYPE.UNLOADING,
|
|
447
|
+
foundInventory.qty,
|
|
448
|
+
foundInventory.uomValue,
|
|
449
|
+
this.user,
|
|
450
|
+
this.trxMgr
|
|
449
451
|
)
|
|
450
452
|
} else {
|
|
451
|
-
const
|
|
452
|
-
foundInventory.expirationDate = foundInventory.expirationDate ? new Date(foundInventory.expirationDate) : null
|
|
453
|
-
foundInventory.manufactureDate = foundInventory?.manufactureDate ? new Date(foundInventory?.manufactureDate) : null
|
|
454
|
-
foundInventory.qty = updatedQty
|
|
455
|
-
foundInventory.uomValue +=
|
|
453
|
+
const addUomValue: number =
|
|
456
454
|
Math.round(
|
|
457
455
|
qty *
|
|
458
456
|
(Boolean(arrivalNotice)
|
|
@@ -460,20 +458,39 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
460
458
|
: targetInventory.returnUomValue / targetInventory.returnQty) *
|
|
461
459
|
1000
|
|
462
460
|
) / 1000
|
|
463
|
-
foundInventory.conditionOfGoods = conditionOfGoods ?? foundInventory.conditionOfGoods // DEFAULT BACK TO PREVIOUS INVENTORY'S CONDITION OF GOODS
|
|
464
461
|
|
|
465
|
-
|
|
466
|
-
|
|
462
|
+
const updateFields: any = {
|
|
463
|
+
qty: () => `"qty" + :addQty::numeric`,
|
|
464
|
+
uomValue: () => `"uom_value" + :addUomValue::numeric`,
|
|
465
|
+
updater: this.user,
|
|
466
|
+
updatedAt: new Date()
|
|
467
|
+
}
|
|
468
|
+
if (conditionOfGoods) {
|
|
469
|
+
updateFields.conditionOfGoods = conditionOfGoods
|
|
470
|
+
}
|
|
471
|
+
await this.trxMgr.getRepository(Inventory).createQueryBuilder()
|
|
472
|
+
.update(Inventory).set(updateFields)
|
|
473
|
+
.setParameter('addQty', qty)
|
|
474
|
+
.setParameter('addUomValue', addUomValue)
|
|
475
|
+
.where('id = :id', { id: foundInventory.id }).execute()
|
|
476
|
+
|
|
477
|
+
// Update in-memory for downstream
|
|
478
|
+
foundInventory.qty += qty
|
|
479
|
+
foundInventory.uomValue += addUomValue
|
|
480
|
+
if (conditionOfGoods) foundInventory.conditionOfGoods = conditionOfGoods
|
|
481
|
+
|
|
482
|
+
await generateInventoryHistory(
|
|
467
483
|
foundInventory,
|
|
468
484
|
Boolean(arrivalNotice) ? arrivalNotice : returnOrder,
|
|
485
|
+
INVENTORY_TRANSACTION_TYPE.UNLOADING,
|
|
469
486
|
foundInventory.qty,
|
|
470
487
|
foundInventory.uomValue,
|
|
471
|
-
|
|
488
|
+
this.user,
|
|
489
|
+
this.trxMgr
|
|
472
490
|
)
|
|
473
491
|
|
|
474
492
|
if (arrivalNotice) targetProduct.actualPackQty = targetProduct.actualPackQty + qty
|
|
475
493
|
else if (returnOrder) targetInventory.actualPackQty = targetInventory.actualPackQty + qty
|
|
476
|
-
foundInventory = await this.trxMgr.getRepository(Inventory).save(foundInventory)
|
|
477
494
|
}
|
|
478
495
|
|
|
479
496
|
let inventoryItem: InventoryItem = new InventoryItem()
|
|
@@ -770,14 +787,20 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
770
787
|
let foundInventory: Inventory = await invQb.getOne()
|
|
771
788
|
if (!foundInventory) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(foundInventory.palletId))
|
|
772
789
|
|
|
790
|
+
await this.trxMgr.getRepository(Inventory).update(
|
|
791
|
+
{ id: foundInventory.id },
|
|
792
|
+
{ status: INVENTORY_STATUS.UNLOADED, updater: this.user }
|
|
793
|
+
)
|
|
773
794
|
foundInventory.status = INVENTORY_STATUS.UNLOADED
|
|
774
795
|
|
|
775
|
-
|
|
796
|
+
await generateInventoryHistory(
|
|
776
797
|
foundInventory,
|
|
777
798
|
Boolean(arrivalNotice) ? arrivalNotice : returnOrder,
|
|
799
|
+
INVENTORY_TRANSACTION_TYPE.UNLOADING,
|
|
778
800
|
foundInventory.qty,
|
|
779
801
|
foundInventory.uomValue,
|
|
780
|
-
|
|
802
|
+
this.user,
|
|
803
|
+
this.trxMgr
|
|
781
804
|
)
|
|
782
805
|
}
|
|
783
806
|
|
|
@@ -888,14 +911,23 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
888
911
|
await this.trxMgr.getRepository(Inventory).save(inventory)
|
|
889
912
|
|
|
890
913
|
if (inventory.qty == 0) {
|
|
914
|
+
await this.trxMgr.getRepository(Inventory).createQueryBuilder()
|
|
915
|
+
.update(Inventory).set({
|
|
916
|
+
lastSeq: () => `"last_seq" + 1`,
|
|
917
|
+
status: INVENTORY_STATUS.DELETED,
|
|
918
|
+
updater: this.user, updatedAt: new Date()
|
|
919
|
+
})
|
|
920
|
+
.where('id = :id', { id: inventory.id }).execute()
|
|
891
921
|
inventory.lastSeq++
|
|
892
922
|
inventory.status = INVENTORY_STATUS.DELETED
|
|
893
|
-
|
|
923
|
+
await generateInventoryHistory(
|
|
894
924
|
inventory,
|
|
895
925
|
Boolean(orderType === ORDER_TYPES.ARRIVAL_NOTICE) ? arrivalNotice : returnOrder,
|
|
926
|
+
INVENTORY_TRANSACTION_TYPE.UNDO_UNLOADING,
|
|
896
927
|
-inventory.qty,
|
|
897
928
|
-inventory.uomValue,
|
|
898
|
-
|
|
929
|
+
this.user,
|
|
930
|
+
this.trxMgr
|
|
899
931
|
)
|
|
900
932
|
inventory.qty = 0
|
|
901
933
|
inventory.uomValue = 0
|
|
@@ -909,14 +941,23 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
909
941
|
await this.trxMgr.getRepository(Inventory).delete({ id: inventory.id })
|
|
910
942
|
}
|
|
911
943
|
} else {
|
|
944
|
+
await this.trxMgr.getRepository(Inventory).createQueryBuilder()
|
|
945
|
+
.update(Inventory).set({
|
|
946
|
+
lastSeq: () => `"last_seq" + 1`,
|
|
947
|
+
status: INVENTORY_STATUS.DELETED,
|
|
948
|
+
updater: this.user, updatedAt: new Date()
|
|
949
|
+
})
|
|
950
|
+
.where('id = :id', { id: inventory.id }).execute()
|
|
912
951
|
inventory.lastSeq++
|
|
913
952
|
inventory.status = INVENTORY_STATUS.DELETED
|
|
914
|
-
|
|
953
|
+
await generateInventoryHistory(
|
|
915
954
|
inventory,
|
|
916
955
|
Boolean(orderType === ORDER_TYPES.ARRIVAL_NOTICE) ? arrivalNotice : returnOrder,
|
|
956
|
+
INVENTORY_TRANSACTION_TYPE.UNDO_UNLOADING,
|
|
917
957
|
-inventory.qty,
|
|
918
958
|
-inventory.uomValue,
|
|
919
|
-
|
|
959
|
+
this.user,
|
|
960
|
+
this.trxMgr
|
|
920
961
|
)
|
|
921
962
|
inventory.qty = 0
|
|
922
963
|
inventory.uomValue = 0
|
|
@@ -1096,12 +1137,14 @@ export class UnloadingWorksheetController extends VasWorksheetController {
|
|
|
1096
1137
|
}
|
|
1097
1138
|
|
|
1098
1139
|
for (const inventory of inventories) {
|
|
1099
|
-
await
|
|
1140
|
+
await generateInventoryHistory(
|
|
1100
1141
|
inventory,
|
|
1101
1142
|
arrivalNotice,
|
|
1143
|
+
INVENTORY_TRANSACTION_TYPE.UNLOADING,
|
|
1102
1144
|
inventory.qty,
|
|
1103
1145
|
inventory.uomValue,
|
|
1104
|
-
|
|
1146
|
+
this.user,
|
|
1147
|
+
this.trxMgr
|
|
1105
1148
|
)
|
|
1106
1149
|
}
|
|
1107
1150
|
} catch (e) {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '@things-factory/sales-base'
|
|
19
19
|
import { webhookHandler, WebhookEventsEnum } from '@things-factory/integration-base'
|
|
20
20
|
import {
|
|
21
|
+
generateInventoryHistory,
|
|
21
22
|
Inventory,
|
|
22
23
|
INVENTORY_ITEM_SOURCE,
|
|
23
24
|
INVENTORY_STATUS,
|
|
@@ -296,7 +297,7 @@ export class PackingWorksheetController extends VasWorksheetController {
|
|
|
296
297
|
await this.trxMgr.getRepository(InventoryItem).save(inventoryItems)
|
|
297
298
|
}
|
|
298
299
|
|
|
299
|
-
await
|
|
300
|
+
await generateInventoryHistory(inventory, releaseGood, INVENTORY_TRANSACTION_TYPE.PACKING, 0, 0, this.user, this.trxMgr)
|
|
300
301
|
|
|
301
302
|
worksheetDetail.status = WORKSHEET_STATUS.DONE
|
|
302
303
|
}
|
|
@@ -484,7 +485,7 @@ export class PackingWorksheetController extends VasWorksheetController {
|
|
|
484
485
|
|
|
485
486
|
if (orderInventory.packedQty === releaseQty) {
|
|
486
487
|
orderInventory.status = ORDER_INVENTORY_STATUS.PACKED
|
|
487
|
-
await
|
|
488
|
+
await generateInventoryHistory(inventory, releaseGood, INVENTORY_TRANSACTION_TYPE.PACKING, 0, 0, this.user, this.trxMgr)
|
|
488
489
|
await this.trxMgr.getRepository(WorksheetDetail).update(
|
|
489
490
|
{ targetInventory: { id: orderInventory.id }, type: 'PACKING' },
|
|
490
491
|
{
|
|
@@ -795,10 +795,28 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
795
795
|
newTargetInventory.updater = this.user
|
|
796
796
|
newTargetInventory = await this.trxMgr.getRepository(OrderInventory).save(newTargetInventory)
|
|
797
797
|
|
|
798
|
-
//
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
798
|
+
// Atomic update of locked qty and uomValue of inventory
|
|
799
|
+
const lockResult = await this.trxMgr
|
|
800
|
+
.getRepository(Inventory)
|
|
801
|
+
.createQueryBuilder()
|
|
802
|
+
.update(Inventory)
|
|
803
|
+
.set({
|
|
804
|
+
lockedQty: () => `COALESCE("locked_qty", 0) + :releaseQty::numeric`,
|
|
805
|
+
lockedUomValue: () => `COALESCE("locked_uom_value", 0) + :releaseUomValue::numeric`,
|
|
806
|
+
updater: this.user,
|
|
807
|
+
updatedAt: new Date()
|
|
808
|
+
})
|
|
809
|
+
.setParameter('releaseQty', targetInventory.releaseQty)
|
|
810
|
+
.setParameter('releaseUomValue', targetInventory.releaseUomValue)
|
|
811
|
+
.where('id = :id AND qty >= COALESCE(locked_qty, 0) + :newQty', {
|
|
812
|
+
id: inventory.id,
|
|
813
|
+
newQty: targetInventory.releaseQty
|
|
814
|
+
})
|
|
815
|
+
.execute()
|
|
816
|
+
|
|
817
|
+
if (lockResult.affected === 0) {
|
|
818
|
+
throw new Error(`Insufficient inventory for picking assignment`)
|
|
819
|
+
}
|
|
802
820
|
|
|
803
821
|
// Create worksheet details
|
|
804
822
|
await this.createWorksheetDetails(worksheet, WORKSHEET_TYPE.PICKING, [newTargetInventory])
|
|
@@ -837,12 +855,21 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
837
855
|
const targetInventory: OrderInventory = worksheetDetail.targetInventory
|
|
838
856
|
targetInventoryIds.push(targetInventory.id)
|
|
839
857
|
|
|
840
|
-
|
|
858
|
+
// Atomic update of locked qty and uomValue of inventory
|
|
859
|
+
await this.trxMgr
|
|
841
860
|
.getRepository(Inventory)
|
|
842
|
-
.
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
861
|
+
.createQueryBuilder()
|
|
862
|
+
.update(Inventory)
|
|
863
|
+
.set({
|
|
864
|
+
lockedQty: () => `GREATEST(COALESCE("locked_qty", 0) - :releaseQty::numeric, 0)`,
|
|
865
|
+
lockedUomValue: () => `GREATEST(COALESCE("locked_uom_value", 0) - :releaseUomValue::numeric, 0)`,
|
|
866
|
+
updater: this.user,
|
|
867
|
+
updatedAt: new Date()
|
|
868
|
+
})
|
|
869
|
+
.setParameter('releaseQty', targetInventory.releaseQty)
|
|
870
|
+
.setParameter('releaseUomValue', targetInventory.releaseUomValue)
|
|
871
|
+
.where('id = :id', { id: worksheetDetail.targetInventory.inventory.id })
|
|
872
|
+
.execute()
|
|
846
873
|
|
|
847
874
|
await this.trxMgr
|
|
848
875
|
.getRepository(OrderProduct)
|
|
@@ -1008,6 +1035,14 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1008
1035
|
pickedUomValue = matchingProduct.uomValue
|
|
1009
1036
|
}
|
|
1010
1037
|
|
|
1038
|
+
// validation to prevent decimal quantities for non-decimal products
|
|
1039
|
+
const scanPickProduct: Product = await this.trxMgr.getRepository(Product).findOne({
|
|
1040
|
+
where: { id: worksheetDetailInfos.productId }
|
|
1041
|
+
})
|
|
1042
|
+
if (pickedQty % 1 !== 0 && !scanPickProduct?.isInventoryDecimal) {
|
|
1043
|
+
throw new Error('Decimal quantities are not allowed for this product')
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1011
1046
|
// //validation to prevent over release
|
|
1012
1047
|
if (!targetInventory)
|
|
1013
1048
|
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `inventory not assigned`))
|
|
@@ -1088,14 +1123,14 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1088
1123
|
targetInventory.pickedQty = (targetInventory?.pickedQty || 0) + pickedQty
|
|
1089
1124
|
|
|
1090
1125
|
let updateOiObj = {
|
|
1091
|
-
pickedQty: () => `"picked_qty" +
|
|
1126
|
+
pickedQty: () => `"picked_qty" + :pickedQty`,
|
|
1092
1127
|
updatedAt: new Date(),
|
|
1093
1128
|
updater: this.user,
|
|
1094
1129
|
pickedBy: this.user.name,
|
|
1095
1130
|
pickedByUser: this.user,
|
|
1096
1131
|
pickedAt: new Date(),
|
|
1097
1132
|
status: () =>
|
|
1098
|
-
`case when release_qty = "picked_qty" +
|
|
1133
|
+
`case when release_qty = "picked_qty" + :pickedQty then '${ORDER_INVENTORY_STATUS.PICKED}' else status end`
|
|
1099
1134
|
}
|
|
1100
1135
|
|
|
1101
1136
|
if (targetInventory.binLocation) {
|
|
@@ -1107,6 +1142,7 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1107
1142
|
.createQueryBuilder()
|
|
1108
1143
|
.update(OrderInventory)
|
|
1109
1144
|
.set(updateOiObj)
|
|
1145
|
+
.setParameter('pickedQty', pickedQty)
|
|
1110
1146
|
.where({ id: targetInventory.id })
|
|
1111
1147
|
.andWhere(`picked_qty + :pickedQty <= release_qty`, { pickedQty })
|
|
1112
1148
|
.execute()
|
|
@@ -1130,12 +1166,12 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1130
1166
|
let releaseUomValue = Math.trunc((pickedUomValue / pickedQty) * releaseQty * 1000) / 1000
|
|
1131
1167
|
|
|
1132
1168
|
let updateInvObj = {
|
|
1133
|
-
qty: () => `"qty" -
|
|
1134
|
-
lockedQty: () => `"locked_qty" -
|
|
1135
|
-
uomValue: () => `"uom_value" -
|
|
1136
|
-
lockedUomValue: () => `"locked_uom_value" -
|
|
1169
|
+
qty: () => `"qty" - :deductQty::numeric`,
|
|
1170
|
+
lockedQty: () => `GREATEST("locked_qty" - :deductQty::numeric, 0)`,
|
|
1171
|
+
uomValue: () => `"uom_value" - :deductUomValue::numeric`,
|
|
1172
|
+
lockedUomValue: () => `GREATEST("locked_uom_value" - :deductUomValue::numeric, 0)`,
|
|
1137
1173
|
status: () =>
|
|
1138
|
-
`case when "qty" -
|
|
1174
|
+
`case when "qty" - :deductQty::numeric <= 0 then '${INVENTORY_STATUS.TERMINATED}' else status end`,
|
|
1139
1175
|
updater: this.user,
|
|
1140
1176
|
updatedAt: new Date()
|
|
1141
1177
|
}
|
|
@@ -1145,6 +1181,8 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1145
1181
|
.createQueryBuilder()
|
|
1146
1182
|
.update(Inventory)
|
|
1147
1183
|
.set(updateInvObj)
|
|
1184
|
+
.setParameter('deductQty', releaseQty)
|
|
1185
|
+
.setParameter('deductUomValue', releaseUomValue)
|
|
1148
1186
|
.where('id = :id', { id: worksheetDetailInfos.inventoryId })
|
|
1149
1187
|
.returning(['qty'])
|
|
1150
1188
|
.execute()
|
|
@@ -1254,6 +1292,11 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1254
1292
|
})
|
|
1255
1293
|
if (!oiValidate) throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `is done`))
|
|
1256
1294
|
|
|
1295
|
+
// validation to prevent decimal quantities for non-decimal products
|
|
1296
|
+
if (pickedQty % 1 !== 0 && !product.isInventoryDecimal) {
|
|
1297
|
+
throw new Error('Decimal quantities are not allowed for this product')
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1257
1300
|
//validation to prevent over release
|
|
1258
1301
|
if (inventory.qty <= 0) throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `over release`))
|
|
1259
1302
|
|
|
@@ -1371,10 +1414,31 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1371
1414
|
if (!toLocation) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(locationName))
|
|
1372
1415
|
|
|
1373
1416
|
if (fromLocation.id !== toLocation.id) {
|
|
1417
|
+
await this.trxMgr
|
|
1418
|
+
.getRepository(Inventory)
|
|
1419
|
+
.update(
|
|
1420
|
+
{ id: inventory.id },
|
|
1421
|
+
{
|
|
1422
|
+
location: toLocation,
|
|
1423
|
+
warehouse: toLocation.warehouse,
|
|
1424
|
+
zone: toLocation.zone,
|
|
1425
|
+
updater: this.user
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1374
1429
|
inventory.location = toLocation
|
|
1375
1430
|
inventory.warehouse = toLocation.warehouse
|
|
1376
1431
|
inventory.zone = toLocation.zone
|
|
1377
|
-
|
|
1432
|
+
|
|
1433
|
+
await generateInventoryHistory(
|
|
1434
|
+
inventory,
|
|
1435
|
+
releaseGood,
|
|
1436
|
+
INVENTORY_TRANSACTION_TYPE.RELOCATE,
|
|
1437
|
+
0,
|
|
1438
|
+
0,
|
|
1439
|
+
this.user,
|
|
1440
|
+
this.trxMgr
|
|
1441
|
+
)
|
|
1378
1442
|
}
|
|
1379
1443
|
}
|
|
1380
1444
|
} catch (error) {
|
|
@@ -1426,6 +1490,16 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1426
1490
|
if (sumOfReleaseQty != releaseQty)
|
|
1427
1491
|
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `insufficient picking quantity`))
|
|
1428
1492
|
|
|
1493
|
+
// validation to prevent decimal quantities for non-decimal products
|
|
1494
|
+
if (targetInventories.length > 0) {
|
|
1495
|
+
const batchPickProduct: Product = await this.trxMgr.getRepository(Product).findOne({
|
|
1496
|
+
where: { id: targetInventories[0].productId }
|
|
1497
|
+
})
|
|
1498
|
+
if (releaseQty % 1 !== 0 && !batchPickProduct?.isInventoryDecimal) {
|
|
1499
|
+
throw new Error('Decimal quantities are not allowed for this product')
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1429
1503
|
for (var i = 0; i < targetInventories.length; i++) {
|
|
1430
1504
|
let targetInventory: OrderInventory = targetInventories[i]
|
|
1431
1505
|
let inventory: Inventory = await this.trxMgr.getRepository(Inventory).findOne({
|
|
@@ -1480,10 +1554,12 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1480
1554
|
await this.updateOrderTargets([targetInventory])
|
|
1481
1555
|
|
|
1482
1556
|
let updateInvObj = {
|
|
1483
|
-
qty: () => `"qty" -
|
|
1484
|
-
lockedQty: () => `"locked_qty" -
|
|
1485
|
-
uomValue: () => `"uom_value" -
|
|
1486
|
-
lockedUomValue: () => `"locked_uom_value" -
|
|
1557
|
+
qty: () => `"qty" - :deductQty::numeric`,
|
|
1558
|
+
lockedQty: () => `GREATEST("locked_qty" - :deductQty::numeric, 0)`,
|
|
1559
|
+
uomValue: () => `"uom_value" - :deductUomValue::numeric`,
|
|
1560
|
+
lockedUomValue: () => `GREATEST("locked_uom_value" - :deductUomValue::numeric, 0)`,
|
|
1561
|
+
status: () =>
|
|
1562
|
+
`case when "qty" - :deductQty::numeric <= 0 then '${INVENTORY_STATUS.TERMINATED}' else status end`,
|
|
1487
1563
|
updater: this.user,
|
|
1488
1564
|
updatedAt: new Date()
|
|
1489
1565
|
}
|
|
@@ -1493,6 +1569,8 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1493
1569
|
.createQueryBuilder()
|
|
1494
1570
|
.update(Inventory)
|
|
1495
1571
|
.set(updateInvObj)
|
|
1572
|
+
.setParameter('deductQty', targetInventory.releaseQty)
|
|
1573
|
+
.setParameter('deductUomValue', targetInventory.releaseUomValue)
|
|
1496
1574
|
.where('id = :id', { id: targetInventory.inventory.id })
|
|
1497
1575
|
.returning(['qty'])
|
|
1498
1576
|
.execute()
|
|
@@ -1521,10 +1599,31 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1521
1599
|
if (!toLocation) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(locationName))
|
|
1522
1600
|
|
|
1523
1601
|
if (fromLocation.id !== toLocation.id) {
|
|
1602
|
+
await this.trxMgr
|
|
1603
|
+
.getRepository(Inventory)
|
|
1604
|
+
.update(
|
|
1605
|
+
{ id: inventory.id },
|
|
1606
|
+
{
|
|
1607
|
+
location: toLocation,
|
|
1608
|
+
warehouse: toLocation.warehouse,
|
|
1609
|
+
zone: toLocation.zone,
|
|
1610
|
+
updater: this.user
|
|
1611
|
+
}
|
|
1612
|
+
)
|
|
1613
|
+
|
|
1524
1614
|
inventory.location = toLocation
|
|
1525
1615
|
inventory.warehouse = toLocation.warehouse
|
|
1526
1616
|
inventory.zone = toLocation.zone
|
|
1527
|
-
|
|
1617
|
+
|
|
1618
|
+
await generateInventoryHistory(
|
|
1619
|
+
inventory,
|
|
1620
|
+
releaseGood,
|
|
1621
|
+
INVENTORY_TRANSACTION_TYPE.RELOCATE,
|
|
1622
|
+
0,
|
|
1623
|
+
0,
|
|
1624
|
+
this.user,
|
|
1625
|
+
this.trxMgr
|
|
1626
|
+
)
|
|
1528
1627
|
}
|
|
1529
1628
|
}
|
|
1530
1629
|
}
|
|
@@ -1599,6 +1698,11 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1599
1698
|
|
|
1600
1699
|
pickedQty = matchingProduct.qty
|
|
1601
1700
|
|
|
1701
|
+
// validation to prevent decimal quantities for non-decimal products
|
|
1702
|
+
if (pickedQty % 1 !== 0 && !product?.isInventoryDecimal) {
|
|
1703
|
+
throw new Error('Decimal quantities are not allowed for this product')
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1602
1706
|
const sumOfReleaseQty: number = parseFloat(
|
|
1603
1707
|
targetInventories
|
|
1604
1708
|
.map((oi: OrderInventory) => oi.releaseQty)
|
|
@@ -1678,10 +1782,12 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1678
1782
|
await this.updateOrderTargets([targetInventory])
|
|
1679
1783
|
|
|
1680
1784
|
let updateInvObj = {
|
|
1681
|
-
qty: () => `"qty" -
|
|
1682
|
-
lockedQty: () => `"locked_qty" -
|
|
1683
|
-
uomValue: () => `"uom_value" -
|
|
1684
|
-
lockedUomValue: () => `"locked_uom_value" -
|
|
1785
|
+
qty: () => `"qty" - :deductQty::numeric`,
|
|
1786
|
+
lockedQty: () => `GREATEST("locked_qty" - :deductQty::numeric, 0)`,
|
|
1787
|
+
uomValue: () => `"uom_value" - :deductUomValue::numeric`,
|
|
1788
|
+
lockedUomValue: () => `GREATEST("locked_uom_value" - :deductUomValue::numeric, 0)`,
|
|
1789
|
+
status: () =>
|
|
1790
|
+
`case when "qty" - :deductQty::numeric <= 0 then '${INVENTORY_STATUS.TERMINATED}' else status end`,
|
|
1685
1791
|
updater: this.user,
|
|
1686
1792
|
updatedAt: new Date()
|
|
1687
1793
|
}
|
|
@@ -1691,6 +1797,8 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1691
1797
|
.createQueryBuilder()
|
|
1692
1798
|
.update(Inventory)
|
|
1693
1799
|
.set(updateInvObj)
|
|
1800
|
+
.setParameter('deductQty', targetInventory.releaseQty)
|
|
1801
|
+
.setParameter('deductUomValue', targetInventory.releaseUomValue)
|
|
1694
1802
|
.where('id = :id', { id: targetInventory.inventory.id })
|
|
1695
1803
|
.returning(['qty'])
|
|
1696
1804
|
.execute()
|
|
@@ -1966,6 +2074,14 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1966
2074
|
}
|
|
1967
2075
|
|
|
1968
2076
|
private async updatePickingTransaction(releaseGood, orderInventory, worksheetDetail, inventory, pickedQty) {
|
|
2077
|
+
// validation to prevent decimal quantities for non-decimal products
|
|
2078
|
+
const pickTxProduct: Product = await this.trxMgr.getRepository(Product).findOne({
|
|
2079
|
+
where: { id: orderInventory.productId }
|
|
2080
|
+
})
|
|
2081
|
+
if (pickedQty % 1 !== 0 && !pickTxProduct?.isInventoryDecimal) {
|
|
2082
|
+
throw new Error('Decimal quantities are not allowed for this product')
|
|
2083
|
+
}
|
|
2084
|
+
|
|
1969
2085
|
const releaseQty: number = orderInventory.releaseQty
|
|
1970
2086
|
|
|
1971
2087
|
orderInventory.pickedQty = (orderInventory?.pickedQty || 0) + pickedQty
|
|
@@ -1980,18 +2096,40 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
1980
2096
|
orderInventory.pickedByUser = this.user
|
|
1981
2097
|
orderInventory.pickedAt = new Date()
|
|
1982
2098
|
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2099
|
+
// Atomic SQL update instead of stale-read pattern
|
|
2100
|
+
await this.trxMgr
|
|
2101
|
+
.getRepository(Inventory)
|
|
2102
|
+
.createQueryBuilder()
|
|
2103
|
+
.update(Inventory)
|
|
2104
|
+
.set({
|
|
2105
|
+
qty: () => `"qty" - :deductQty::numeric`,
|
|
2106
|
+
uomValue: () => `"uom_value" - :deductUomValue::numeric`,
|
|
2107
|
+
lockedQty: () => `GREATEST("locked_qty" - :deductQty::numeric, 0)`,
|
|
2108
|
+
lockedUomValue: () => `GREATEST("locked_uom_value" - :deductUomValue::numeric, 0)`,
|
|
2109
|
+
status: () =>
|
|
2110
|
+
`case when "qty" - :deductQty::numeric <= 0 then '${INVENTORY_STATUS.TERMINATED}' else status end`,
|
|
2111
|
+
updater: this.user,
|
|
2112
|
+
updatedAt: new Date()
|
|
2113
|
+
})
|
|
2114
|
+
.setParameter('deductQty', orderInventory.releaseQty)
|
|
2115
|
+
.setParameter('deductUomValue', orderInventory.releaseUomValue)
|
|
2116
|
+
.where('id = :id', { id: inventory.id })
|
|
2117
|
+
.execute()
|
|
2118
|
+
|
|
2119
|
+
// Generate inventory history separately
|
|
2120
|
+
await generateInventoryHistory(
|
|
1988
2121
|
inventory,
|
|
1989
2122
|
releaseGood,
|
|
2123
|
+
INVENTORY_TRANSACTION_TYPE.PICKING,
|
|
1990
2124
|
-orderInventory.releaseQty,
|
|
1991
2125
|
-orderInventory.releaseUomValue,
|
|
1992
|
-
|
|
2126
|
+
this.user,
|
|
2127
|
+
this.trxMgr
|
|
1993
2128
|
)
|
|
1994
2129
|
|
|
2130
|
+
// Re-read inventory for downstream use (status check for TERMINATED)
|
|
2131
|
+
inventory = await this.trxMgr.getRepository(Inventory).findOne({ where: { id: inventory.id } })
|
|
2132
|
+
|
|
1995
2133
|
worksheetDetail.status = WORKSHEET_STATUS.DONE
|
|
1996
2134
|
worksheetDetail.updater = this.user
|
|
1997
2135
|
await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
|
|
@@ -2545,20 +2683,37 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
2545
2683
|
// update inventory locked qty and uom value
|
|
2546
2684
|
oi = await transaction.getRepository(OrderInventory).save({ ...oi })
|
|
2547
2685
|
|
|
2548
|
-
await transaction
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2686
|
+
const lockResult = await transaction
|
|
2687
|
+
.getRepository(Inventory)
|
|
2688
|
+
.createQueryBuilder()
|
|
2689
|
+
.update(Inventory)
|
|
2690
|
+
.set({
|
|
2691
|
+
lockedQty: () => `COALESCE("locked_qty", 0) + :releaseQty::numeric`,
|
|
2692
|
+
lockedUomValue: () => `COALESCE("locked_uom_value", 0) + :releaseUomValue::numeric`,
|
|
2693
|
+
updater: this.user
|
|
2694
|
+
})
|
|
2695
|
+
.setParameter('releaseQty', oi.releaseQty)
|
|
2696
|
+
.setParameter('releaseUomValue', oi.releaseUomValue)
|
|
2697
|
+
.where('id = :id AND qty >= COALESCE(locked_qty, 0) + :newQty', {
|
|
2698
|
+
id: oi.inventory.id,
|
|
2699
|
+
newQty: oi.releaseQty
|
|
2700
|
+
})
|
|
2701
|
+
.execute()
|
|
2702
|
+
|
|
2703
|
+
if (lockResult.affected === 0) {
|
|
2704
|
+
throw new Error(`Insufficient inventory for picking assignment`)
|
|
2705
|
+
}
|
|
2553
2706
|
|
|
2554
2707
|
await transaction
|
|
2555
2708
|
.getRepository(ProductDetailStock)
|
|
2556
2709
|
.createQueryBuilder()
|
|
2557
2710
|
.update(ProductDetailStock)
|
|
2558
2711
|
.set({
|
|
2559
|
-
unassignedQty: () => `"unassigned_qty" -
|
|
2560
|
-
unassignedUomValue: () => `"unassigned_uom_value" -
|
|
2712
|
+
unassignedQty: () => `GREATEST("unassigned_qty" - :oiReleaseQty::numeric, 0)`,
|
|
2713
|
+
unassignedUomValue: () => `GREATEST("unassigned_uom_value" - :oiReleaseUomValue::numeric, 0)`
|
|
2561
2714
|
})
|
|
2715
|
+
.setParameter('oiReleaseQty', oi.releaseQty)
|
|
2716
|
+
.setParameter('oiReleaseUomValue', oi.releaseUomValue)
|
|
2562
2717
|
.where({ productDetail: oi.productDetail.id })
|
|
2563
2718
|
.execute()
|
|
2564
2719
|
|
|
@@ -2643,26 +2798,37 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
2643
2798
|
releaseUomValue = releaseUomValue - allocatedUomValue
|
|
2644
2799
|
|
|
2645
2800
|
//// Update inventory locked quantity
|
|
2646
|
-
await this.trxMgr
|
|
2801
|
+
const lockResult = await this.trxMgr
|
|
2647
2802
|
.getRepository(Inventory)
|
|
2648
2803
|
.createQueryBuilder('inv')
|
|
2649
2804
|
.update(Inventory)
|
|
2650
2805
|
.set({
|
|
2651
|
-
lockedUomValue: () => `COALESCE(locked_uom_value,0) +
|
|
2652
|
-
lockedQty: () => `COALESCE(locked_qty,0) +
|
|
2806
|
+
lockedUomValue: () => `COALESCE(locked_uom_value,0) + :allocatedUomValue::numeric`,
|
|
2807
|
+
lockedQty: () => `COALESCE(locked_qty,0) + :allocatedQty::numeric`
|
|
2808
|
+
})
|
|
2809
|
+
.setParameter('allocatedUomValue', allocatedUomValue)
|
|
2810
|
+
.setParameter('allocatedQty', allocatedQty)
|
|
2811
|
+
.where('id = :id AND qty >= COALESCE(locked_qty, 0) + :newQty', {
|
|
2812
|
+
id: targetInventory.id,
|
|
2813
|
+
newQty: allocatedQty
|
|
2653
2814
|
})
|
|
2654
|
-
.where('id = :id', { id: targetInventory.id })
|
|
2655
2815
|
.execute()
|
|
2656
2816
|
|
|
2817
|
+
if (lockResult.affected === 0) {
|
|
2818
|
+
throw new Error(`Insufficient inventory for picking assignment`)
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2657
2821
|
// update product detail stock deduct unassigned qty and unassigned uom value
|
|
2658
2822
|
await this.trxMgr
|
|
2659
2823
|
.getRepository(ProductDetailStock)
|
|
2660
2824
|
.createQueryBuilder()
|
|
2661
2825
|
.update(ProductDetailStock)
|
|
2662
2826
|
.set({
|
|
2663
|
-
unassignedQty: () => `"unassigned_qty" -
|
|
2664
|
-
unassignedUomValue: () => `"unassigned_uom_value" -
|
|
2827
|
+
unassignedQty: () => `GREATEST("unassigned_qty" - :deductQty::numeric, 0)`,
|
|
2828
|
+
unassignedUomValue: () => `GREATEST("unassigned_uom_value" - :deductUomValue::numeric, 0)`
|
|
2665
2829
|
})
|
|
2830
|
+
.setParameter('deductQty', allocatedQty)
|
|
2831
|
+
.setParameter('deductUomValue', allocatedUomValue)
|
|
2666
2832
|
.where({ productDetail: orderProducts[i].productDetail.id })
|
|
2667
2833
|
.execute()
|
|
2668
2834
|
|