@things-factory/worksheet-base 3.7.2 → 3.7.7
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/constants/template.js +1 -0
- package/dist-server/constants/template.js.map +1 -1
- package/dist-server/controllers/index.js +1 -0
- package/dist-server/controllers/index.js.map +1 -1
- package/dist-server/controllers/outbound/loading-worksheet-controller.js +4 -0
- package/dist-server/controllers/outbound/loading-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/outbound/picking-worksheet-controller.js +158 -113
- package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/render-manifest.js +64 -0
- package/dist-server/controllers/render-manifest.js.map +1 -0
- package/dist-server/controllers/worksheet-controller.js +25 -0
- package/dist-server/controllers/worksheet-controller.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/loading-worksheet.js +23 -2
- package/dist-server/graphql/resolvers/worksheet/loading-worksheet.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/packing-worksheet.js +22 -0
- package/dist-server/graphql/resolvers/worksheet/packing-worksheet.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking/complete-picking.js +27 -5
- package/dist-server/graphql/resolvers/worksheet/picking/complete-picking.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking/fetch-and-assign-picking-task.js +72 -0
- package/dist-server/graphql/resolvers/worksheet/picking/fetch-and-assign-picking-task.js.map +1 -0
- package/dist-server/graphql/resolvers/worksheet/picking/index.js +2 -1
- package/dist-server/graphql/resolvers/worksheet/picking/index.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking/picking.js +4 -4
- package/dist-server/graphql/resolvers/worksheet/picking/picking.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking/scan-product-picking.js +4 -4
- package/dist-server/graphql/resolvers/worksheet/picking/scan-product-picking.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking-worksheet.js +86 -57
- package/dist-server/graphql/resolvers/worksheet/picking-worksheet.js.map +1 -1
- package/dist-server/graphql/types/worksheet/index.js +7 -1
- package/dist-server/graphql/types/worksheet/index.js.map +1 -1
- package/dist-server/routes.js +4 -0
- package/dist-server/routes.js.map +1 -1
- package/dist-server/utils/inventory-util.js +25 -0
- package/dist-server/utils/inventory-util.js.map +1 -1
- package/package.json +16 -16
- package/server/constants/template.ts +1 -0
- package/server/controllers/index.ts +1 -0
- package/server/controllers/outbound/loading-worksheet-controller.ts +20 -0
- package/server/controllers/outbound/picking-worksheet-controller.ts +201 -143
- package/server/controllers/render-manifest.ts +68 -0
- package/server/controllers/worksheet-controller.ts +38 -0
- package/server/graphql/resolvers/worksheet/loading-worksheet.ts +30 -4
- package/server/graphql/resolvers/worksheet/packing-worksheet.ts +28 -3
- package/server/graphql/resolvers/worksheet/picking/complete-picking.ts +37 -5
- package/server/graphql/resolvers/worksheet/picking/fetch-and-assign-picking-task.ts +78 -0
- package/server/graphql/resolvers/worksheet/picking/index.ts +3 -1
- package/server/graphql/resolvers/worksheet/picking/picking.ts +6 -4
- package/server/graphql/resolvers/worksheet/picking/scan-product-picking.ts +7 -4
- package/server/graphql/resolvers/worksheet/picking-worksheet.ts +102 -62
- package/server/graphql/types/worksheet/index.ts +8 -2
- package/server/routes.ts +5 -0
- package/server/utils/inventory-util.ts +26 -0
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
INVENTORY_STATUS,
|
|
18
18
|
INVENTORY_TRANSACTION_TYPE,
|
|
19
19
|
Location,
|
|
20
|
-
LOCATION_TYPE
|
|
20
|
+
LOCATION_TYPE,
|
|
21
|
+
LOCATION_STATUS
|
|
21
22
|
} from '@things-factory/warehouse-base'
|
|
22
23
|
|
|
23
24
|
import { TASK_NUMBER_RULE_TYPE, TASK_NUMBER_SETTING_KEY, WORKSHEET_STATUS, WORKSHEET_TYPE } from '../../constants'
|
|
@@ -190,7 +191,7 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
190
191
|
if (vasWorksheet) {
|
|
191
192
|
await this.activateVAS(vasWorksheet.name, vasWorksheet.worksheetDetails)
|
|
192
193
|
}
|
|
193
|
-
} catch (e) {}
|
|
194
|
+
} catch (e) { }
|
|
194
195
|
|
|
195
196
|
const pendingSplitOIs: OrderInventory[] = await this.trxMgr.getRepository(OrderInventory).find({
|
|
196
197
|
where: { domain: this.domain, releaseGood, status: ORDER_INVENTORY_STATUS.PENDING_SPLIT }
|
|
@@ -386,106 +387,92 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
386
387
|
worksheetType: string,
|
|
387
388
|
productBarcode: string,
|
|
388
389
|
cartonId: string,
|
|
389
|
-
binLocation?: string
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
390
|
+
binLocation?: string,
|
|
391
|
+
serialNumber?: string
|
|
392
|
+
): Promise<OrderInventory> {
|
|
393
|
+
try {
|
|
394
|
+
let worksheetDetail: WorksheetDetail = await this.findExecutableWorksheetDetailByName(
|
|
395
|
+
worksheetDetailName,
|
|
396
|
+
worksheetType,
|
|
397
|
+
[
|
|
398
|
+
'worksheet',
|
|
399
|
+
'worksheet.bizplace',
|
|
400
|
+
'targetInventory',
|
|
401
|
+
'targetInventory.releaseGood',
|
|
402
|
+
'targetInventory.inventory',
|
|
403
|
+
'targetInventory.product',
|
|
404
|
+
'targetInventory.product.productDetails',
|
|
405
|
+
'targetInventory.product.productDetails.product',
|
|
406
|
+
'targetInventory.product.productDetails.childProductDetail'
|
|
407
|
+
]
|
|
408
|
+
)
|
|
406
409
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const packingType: string = inventory.packingType
|
|
412
|
-
const packingSize: number = inventory.packingSize
|
|
410
|
+
const releaseGood: ReleaseGood = worksheetDetail.targetInventory.releaseGood
|
|
411
|
+
let targetInventory: OrderInventory = worksheetDetail.targetInventory
|
|
412
|
+
const product: Product = targetInventory.product
|
|
413
|
+
let inventory: Inventory = targetInventory.inventory
|
|
413
414
|
|
|
414
|
-
|
|
415
|
-
|
|
415
|
+
//validation to prevent duplicated picking
|
|
416
|
+
let oiValidate: OrderInventory = await this.trxMgr.getRepository(OrderInventory).findOne({
|
|
417
|
+
where: { domain: this.domain, id: targetInventory.id, status: ORDER_INVENTORY_STATUS.PICKING }
|
|
418
|
+
})
|
|
419
|
+
if (!oiValidate)
|
|
420
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `is done`))
|
|
416
421
|
|
|
417
|
-
|
|
418
|
-
|
|
422
|
+
//validation to prevent over release
|
|
423
|
+
if (inventory.qty <= 0)
|
|
424
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `over release`))
|
|
419
425
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const isMatchingBarcode: boolean = productDetails.map(detail => detail.gtin).includes(productBarcode)
|
|
423
|
-
if (!isMatchingBarcode) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(productBarcode))
|
|
426
|
+
if (inventory.cartonId !== cartonId)
|
|
427
|
+
throw new Error(this.ERROR_MSG.VALIDITY.UNEXPECTED_FIELD_VALUE('Carton ID', cartonId, inventory.cartonId))
|
|
424
428
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
(detail: ProductDetail) =>
|
|
428
|
-
detail.gtin === productBarcode &&
|
|
429
|
-
detail.packingType === packingType &&
|
|
430
|
-
detail.packingSize == packingSize &&
|
|
431
|
-
detail.product?.id === product.id
|
|
432
|
-
)
|
|
429
|
+
const packingType: string = inventory.packingType
|
|
430
|
+
const packingSize: number = inventory.packingSize
|
|
433
431
|
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
(parentDetail: ProductDetail) =>
|
|
437
|
-
parentDetail.packingType === packingType && parentDetail.packingSize == packingSize
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
pickedQty = await this.getChildQty(productDetails, productBarcode, roProductDetail)
|
|
441
|
-
if (pickedQty > releaseQty) {
|
|
442
|
-
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `over release`))
|
|
443
|
-
}
|
|
444
|
-
}
|
|
432
|
+
let pickedQty: number = 1
|
|
433
|
+
const releaseQty: number = targetInventory.releaseQty
|
|
445
434
|
|
|
446
|
-
|
|
447
|
-
if (targetInventory.pickedQty == releaseQty) {
|
|
448
|
-
const leftQty: number = inventory.qty - releaseQty
|
|
449
|
-
if (leftQty < 0) {
|
|
450
|
-
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `quantity can't exceed limitation`))
|
|
451
|
-
}
|
|
435
|
+
targetInventory = await this.checkAndSetBinPicking(targetInventory, binLocation)
|
|
452
436
|
|
|
453
|
-
|
|
437
|
+
// search for matching product barcode
|
|
438
|
+
const productDetails: ProductDetail[] = product?.productDetails.filter(detail => !detail.deletedAt)
|
|
439
|
+
const isMatchingBarcode: boolean = productDetails.map(detail => detail.gtin).includes(productBarcode)
|
|
440
|
+
if (!isMatchingBarcode) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(productBarcode))
|
|
454
441
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
442
|
+
// case for scanning parent packing type, packing size
|
|
443
|
+
const foundProductDetail: ProductDetail = productDetails.find(
|
|
444
|
+
(detail: ProductDetail) =>
|
|
445
|
+
detail.gtin === productBarcode &&
|
|
446
|
+
detail.packingType === packingType &&
|
|
447
|
+
detail.packingSize == packingSize &&
|
|
448
|
+
detail.product?.id === product.id
|
|
449
|
+
)
|
|
459
450
|
|
|
460
|
-
|
|
461
|
-
|
|
451
|
+
if (!foundProductDetail) {
|
|
452
|
+
const roProductDetail: ProductDetail = productDetails.find(
|
|
453
|
+
(parentDetail: ProductDetail) =>
|
|
454
|
+
parentDetail.packingType === packingType && parentDetail.packingSize == packingSize
|
|
455
|
+
)
|
|
462
456
|
|
|
463
|
-
|
|
457
|
+
pickedQty = await this.getChildQty(productDetails, productBarcode, roProductDetail)
|
|
458
|
+
if (pickedQty > releaseQty) {
|
|
459
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `over release`))
|
|
460
|
+
}
|
|
464
461
|
}
|
|
465
|
-
await this.updateOrderTargets([targetInventory])
|
|
466
462
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
inventory.lockedUomValue = Math.round((inventory.lockedUomValue - targetInventory.releaseUomValue) * 100) / 100
|
|
471
|
-
inventory = await this.transactionInventory(
|
|
472
|
-
inventory,
|
|
473
|
-
releaseGood,
|
|
474
|
-
-targetInventory.releaseQty,
|
|
475
|
-
-targetInventory.releaseUomValue,
|
|
476
|
-
INVENTORY_TRANSACTION_TYPE.PICKING
|
|
477
|
-
)
|
|
463
|
+
if (product?.isRequireSerialNumberScanning) {
|
|
464
|
+
if ((!serialNumber || serialNumber == ''))
|
|
465
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `require serial number`))
|
|
478
466
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (leftQty === 0) {
|
|
484
|
-
inventory.status = INVENTORY_STATUS.TERMINATED
|
|
485
|
-
await this.transactionInventory(inventory, releaseGood, 0, 0, INVENTORY_TRANSACTION_TYPE.TERMINATED)
|
|
467
|
+
if (serialNumber) {
|
|
468
|
+
targetInventory.serialNumber = JSON.stringify(targetInventory.serialNumber ? [...(JSON.parse(targetInventory.serialNumber)), serialNumber] : [serialNumber])
|
|
469
|
+
}
|
|
486
470
|
}
|
|
487
|
-
|
|
488
|
-
await this.
|
|
471
|
+
|
|
472
|
+
await this.updatePickingTransaction(releaseGood, targetInventory, worksheetDetail, inventory, pickedQty)
|
|
473
|
+
|
|
474
|
+
} catch (error) {
|
|
475
|
+
throw error
|
|
489
476
|
}
|
|
490
477
|
}
|
|
491
478
|
|
|
@@ -494,70 +481,75 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
494
481
|
worksheetType: string,
|
|
495
482
|
palletId: string,
|
|
496
483
|
locationName: string,
|
|
497
|
-
|
|
484
|
+
pickedQty: number,
|
|
485
|
+
binLocation?: string,
|
|
486
|
+
serialNumber?: string
|
|
498
487
|
): Promise<void> {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
let inventory: Inventory = targetInventory.inventory
|
|
513
|
-
if (inventory.palletId !== palletId)
|
|
514
|
-
throw new Error(this.ERROR_MSG.VALIDITY.UNEXPECTED_FIELD_VALUE('Pallet ID', palletId, inventory.palletId))
|
|
515
|
-
|
|
516
|
-
const leftQty: number = inventory.qty - releaseQty
|
|
517
|
-
if (leftQty < 0) {
|
|
518
|
-
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `quantity can't exceed limitation`))
|
|
519
|
-
}
|
|
488
|
+
try {
|
|
489
|
+
let worksheetDetail: WorksheetDetail = await this.findExecutableWorksheetDetailByName(
|
|
490
|
+
worksheetDetailName,
|
|
491
|
+
worksheetType,
|
|
492
|
+
[
|
|
493
|
+
'worksheet',
|
|
494
|
+
'targetInventory',
|
|
495
|
+
'targetInventory.releaseGood',
|
|
496
|
+
'targetInventory.inventory',
|
|
497
|
+
'targetInventory.inventory.location',
|
|
498
|
+
'targetInventory.product',
|
|
499
|
+
]
|
|
500
|
+
)
|
|
520
501
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
inventory.qty -= targetInventory.releaseQty
|
|
526
|
-
inventory.uomValue = Math.round((inventory.uomValue - targetInventory.releaseUomValue) * 100) / 100
|
|
527
|
-
inventory.lockedQty = inventory.lockedQty - targetInventory.releaseQty
|
|
528
|
-
inventory.lockedUomValue = Math.round((inventory.lockedUomValue - targetInventory.releaseUomValue) * 100) / 100
|
|
529
|
-
inventory = await this.transactionInventory(
|
|
530
|
-
inventory,
|
|
531
|
-
releaseGood,
|
|
532
|
-
-targetInventory.releaseQty,
|
|
533
|
-
-targetInventory.releaseUomValue,
|
|
534
|
-
INVENTORY_TRANSACTION_TYPE.PICKING
|
|
535
|
-
)
|
|
502
|
+
const releaseGood: ReleaseGood = worksheetDetail.targetInventory.releaseGood
|
|
503
|
+
let targetInventory: OrderInventory = worksheetDetail.targetInventory
|
|
504
|
+
const product: Product = targetInventory.product
|
|
505
|
+
let inventory: Inventory = targetInventory.inventory
|
|
536
506
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
507
|
+
//validation to prevent duplicated picking
|
|
508
|
+
let oiValidate: OrderInventory = await this.trxMgr.getRepository(OrderInventory).findOne({
|
|
509
|
+
where: { domain: this.domain, id: targetInventory.id, status: ORDER_INVENTORY_STATUS.PICKING }
|
|
510
|
+
})
|
|
511
|
+
if (!oiValidate)
|
|
512
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `is done`))
|
|
540
513
|
|
|
541
|
-
|
|
542
|
-
inventory.
|
|
543
|
-
|
|
544
|
-
}
|
|
514
|
+
//validation to prevent over release
|
|
515
|
+
if (inventory.qty <= 0)
|
|
516
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `over release`))
|
|
545
517
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const toLocation: Location = await this.trxMgr.getRepository(Location).findOne({
|
|
549
|
-
where: { domain: this.domain, name: locationName },
|
|
550
|
-
relations: ['warehouse']
|
|
551
|
-
})
|
|
518
|
+
if (inventory.palletId !== palletId)
|
|
519
|
+
throw new Error(this.ERROR_MSG.VALIDITY.UNEXPECTED_FIELD_VALUE('Pallet ID', palletId, inventory.palletId))
|
|
552
520
|
|
|
553
|
-
if (!toLocation) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(locationName))
|
|
554
521
|
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
522
|
+
if (product?.isRequireSerialNumberScanning) {
|
|
523
|
+
if ((!serialNumber || serialNumber == ''))
|
|
524
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `require serial number`))
|
|
525
|
+
|
|
526
|
+
if (serialNumber) {
|
|
527
|
+
targetInventory.serialNumber = JSON.stringify(targetInventory.serialNumber ? [...(JSON.parse(targetInventory.serialNumber)), serialNumber] : [serialNumber])
|
|
528
|
+
}
|
|
560
529
|
}
|
|
530
|
+
|
|
531
|
+
targetInventory = await this.checkAndSetBinPicking(targetInventory, binLocation)
|
|
532
|
+
|
|
533
|
+
await this.updatePickingTransaction(releaseGood, targetInventory, worksheetDetail, inventory, pickedQty)
|
|
534
|
+
|
|
535
|
+
const fromLocation: Location = targetInventory.inventory.location
|
|
536
|
+
if (locationName) {
|
|
537
|
+
const toLocation: Location = await this.trxMgr.getRepository(Location).findOne({
|
|
538
|
+
where: { domain: this.domain, name: locationName },
|
|
539
|
+
relations: ['warehouse']
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
if (!toLocation) throw new Error(this.ERROR_MSG.FIND.NO_RESULT(locationName))
|
|
543
|
+
|
|
544
|
+
if (fromLocation.id !== toLocation.id) {
|
|
545
|
+
inventory.location = toLocation
|
|
546
|
+
inventory.warehouse = toLocation.warehouse
|
|
547
|
+
inventory.zone = toLocation.zone
|
|
548
|
+
inventory = await this.transactionInventory(inventory, releaseGood, 0, 0, INVENTORY_TRANSACTION_TYPE.RELOCATE)
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
} catch (error) {
|
|
552
|
+
throw error
|
|
561
553
|
}
|
|
562
554
|
}
|
|
563
555
|
|
|
@@ -937,4 +929,70 @@ export class PickingWorksheetController extends VasWorksheetController {
|
|
|
937
929
|
|
|
938
930
|
return worksheetDetails
|
|
939
931
|
}
|
|
932
|
+
|
|
933
|
+
private async checkAndSetBinPicking(orderInventory, binLocation) {
|
|
934
|
+
// bin picking validation
|
|
935
|
+
const binPickingSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
|
|
936
|
+
where: {
|
|
937
|
+
domain: this.domain,
|
|
938
|
+
name: 'enable-bin-picking'
|
|
939
|
+
}
|
|
940
|
+
})
|
|
941
|
+
if (binPickingSetting != undefined && (binPickingSetting.value).toLowerCase() == 'true') {
|
|
942
|
+
if (binLocation) {
|
|
943
|
+
const foundBinLocation: Location = await this.trxMgr.getRepository(Location).findOne({
|
|
944
|
+
where: { domain: this.domain, name: binLocation, type: LOCATION_TYPE.BIN }
|
|
945
|
+
})
|
|
946
|
+
|
|
947
|
+
if (!foundBinLocation)
|
|
948
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `invalid bin location id`))
|
|
949
|
+
|
|
950
|
+
orderInventory.binLocation = foundBinLocation
|
|
951
|
+
} else {
|
|
952
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `invalid bin location id`))
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return orderInventory
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
private async updatePickingTransaction(releaseGood, orderInventory, worksheetDetail, inventory, pickedQty) {
|
|
960
|
+
const releaseQty: number = orderInventory.releaseQty
|
|
961
|
+
|
|
962
|
+
orderInventory.pickedQty = Boolean(orderInventory?.pickedQty) ? orderInventory.pickedQty + pickedQty : pickedQty
|
|
963
|
+
if (orderInventory.pickedQty == releaseQty) {
|
|
964
|
+
const leftQty: number = inventory.qty - releaseQty
|
|
965
|
+
if (leftQty < 0) {
|
|
966
|
+
throw new Error(this.ERROR_MSG.VALIDITY.CANT_PROCEED_STEP_BY('picking', `quantity can't exceed limitation`))
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
orderInventory.status = ORDER_INVENTORY_STATUS.PICKED
|
|
970
|
+
|
|
971
|
+
inventory.qty -= orderInventory.releaseQty
|
|
972
|
+
inventory.uomValue = Math.round((inventory.uomValue - orderInventory.releaseUomValue) * 100) / 100
|
|
973
|
+
inventory.lockedQty = inventory.lockedQty - orderInventory.releaseQty
|
|
974
|
+
inventory.lockedUomValue = Math.round((inventory.lockedUomValue - orderInventory.releaseUomValue) * 100) / 100
|
|
975
|
+
inventory = await this.transactionInventory(
|
|
976
|
+
inventory,
|
|
977
|
+
releaseGood,
|
|
978
|
+
-orderInventory.releaseQty,
|
|
979
|
+
-orderInventory.releaseUomValue,
|
|
980
|
+
INVENTORY_TRANSACTION_TYPE.PICKING
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
worksheetDetail.status = WORKSHEET_STATUS.DONE
|
|
984
|
+
worksheetDetail.updater = this.user
|
|
985
|
+
await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
|
|
986
|
+
|
|
987
|
+
if (leftQty === 0) {
|
|
988
|
+
inventory.status = INVENTORY_STATUS.TERMINATED
|
|
989
|
+
await this.transactionInventory(inventory, releaseGood, 0, 0, INVENTORY_TRANSACTION_TYPE.TERMINATED)
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
await this.updateOrderTargets([orderInventory])
|
|
994
|
+
|
|
995
|
+
return orderInventory
|
|
996
|
+
}
|
|
940
997
|
}
|
|
998
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import FormData from 'form-data'
|
|
2
|
+
import fetch from 'node-fetch'
|
|
3
|
+
import { getRepository } from 'typeorm'
|
|
4
|
+
|
|
5
|
+
import { Attachment, STORAGE } from '@things-factory/attachment-base'
|
|
6
|
+
import { config } from '@things-factory/env'
|
|
7
|
+
import { Manifest, ReleaseGood } from '@things-factory/sales-base'
|
|
8
|
+
import { Domain } from '@things-factory/shell'
|
|
9
|
+
|
|
10
|
+
import { TEMPLATE_TYPE } from '../constants'
|
|
11
|
+
import { DateTimeConverter } from '../utils/datetime-util'
|
|
12
|
+
|
|
13
|
+
const REPORT_API_URL = config.get('reportApiUrl', 'http://localhost:8888/rest/report/show_html')
|
|
14
|
+
|
|
15
|
+
export async function renderManifest({ manifestNo }, context: any) {
|
|
16
|
+
// 1. find domain
|
|
17
|
+
const domain: Domain = await getRepository(Domain).findOne({
|
|
18
|
+
where: { id: context.state.domain.id }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// 2. find manifest
|
|
22
|
+
const manifest: Manifest = await getRepository(Manifest).findOne({
|
|
23
|
+
where: { domain, name: manifestNo },
|
|
24
|
+
relations: ['domain']
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// find release good
|
|
28
|
+
const releaseGoods: ReleaseGood[] = await getRepository(ReleaseGood).find({
|
|
29
|
+
where: { domain, manifest }
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// 7. find grn template based on category
|
|
33
|
+
const foundTemplate: Attachment = await getRepository(Attachment).findOne({
|
|
34
|
+
where: { domain, category: TEMPLATE_TYPE.MANIFEST_TEMPLATE }
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const template = await STORAGE.readFile(foundTemplate.path, 'utf-8')
|
|
38
|
+
|
|
39
|
+
const data = {
|
|
40
|
+
shipping_provider: manifest?.shippingProvider,
|
|
41
|
+
manifest_no: manifest?.name,
|
|
42
|
+
manifest_date: DateTimeConverter.date(manifest?.createdAt).toString(),
|
|
43
|
+
total_list: releaseGoods?.length || 0,
|
|
44
|
+
product_list: releaseGoods.map((item, idx) => {
|
|
45
|
+
return {
|
|
46
|
+
list_no: idx + 1,
|
|
47
|
+
order_no: item?.refNo,
|
|
48
|
+
tracking_no: item?.trackingNo,
|
|
49
|
+
phone: item?.phone1,
|
|
50
|
+
delivery_date: DateTimeConverter.date(manifest?.dispatchedAt).toString(),
|
|
51
|
+
attention_to: item?.attentionTo,
|
|
52
|
+
delivery_address: item?.deliveryAddress1,
|
|
53
|
+
postal_code_city: `${item?.postalCode}, ${item?.city}`
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const formData = new FormData()
|
|
59
|
+
formData.append('template', template)
|
|
60
|
+
formData.append('jsonString', JSON.stringify(data))
|
|
61
|
+
|
|
62
|
+
const response = await fetch(REPORT_API_URL, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: formData
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return await response.text()
|
|
68
|
+
}
|
|
@@ -555,6 +555,44 @@ export class WorksheetController {
|
|
|
555
555
|
return worksheet
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
// @chernhaoee I think the naming of this function is not proper. Seems like it should be something like "updateLoadingWorksheet" since the logic targets only Loading Order Target
|
|
559
|
+
async updateWorksheet(
|
|
560
|
+
worksheetType: string,
|
|
561
|
+
refOrder: ReferenceOrderType,
|
|
562
|
+
worksheet: Worksheet,
|
|
563
|
+
orderTargets: OrderTargetTypes[],
|
|
564
|
+
refOrderStatus: string,
|
|
565
|
+
orderTargetStatus: string,
|
|
566
|
+
): Promise<Worksheet> {
|
|
567
|
+
let newOrderTarget = []
|
|
568
|
+
|
|
569
|
+
orderTargets.forEach((orderTarget: OrderTargetTypes) => {
|
|
570
|
+
orderTarget.status = orderTargetStatus
|
|
571
|
+
})
|
|
572
|
+
orderTargets = await this.updateOrderTargets(orderTargets)
|
|
573
|
+
|
|
574
|
+
for (let orderTarget of orderTargets) {
|
|
575
|
+
try {
|
|
576
|
+
await this.findWorksheetDetail({
|
|
577
|
+
targetInventory: orderTarget.id,
|
|
578
|
+
type: WORKSHEET_TYPE.LOADING
|
|
579
|
+
})
|
|
580
|
+
} catch (e) {
|
|
581
|
+
newOrderTarget.push(orderTarget)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
worksheet.worksheetDetails = await this.createWorksheetDetails(worksheet, worksheetType, newOrderTarget)
|
|
586
|
+
|
|
587
|
+
// find worksheet details that doesnt exist based on order inventory
|
|
588
|
+
//worksheet.worksheetDetails = await this.findWorksheetDetail(worksheet.id)
|
|
589
|
+
|
|
590
|
+
refOrder.status = refOrderStatus
|
|
591
|
+
await this.updateRefOrder(refOrder)
|
|
592
|
+
|
|
593
|
+
return worksheet
|
|
594
|
+
}
|
|
595
|
+
|
|
558
596
|
async generateBatchWorksheet(
|
|
559
597
|
worksheetType: string,
|
|
560
598
|
batchPickingWorksheet: Worksheet,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Equal, getRepository, In, Not, SelectQueryBuilder } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
import { ORDER_STATUS, ORDER_INVENTORY_STATUS, OrderInventory, ReleaseGood } from '@things-factory/sales-base'
|
|
2
4
|
import { Domain } from '@things-factory/shell'
|
|
3
|
-
import { Inventory } from '@things-factory/warehouse-base'
|
|
4
|
-
|
|
5
|
+
import { Inventory, Location } from '@things-factory/warehouse-base'
|
|
6
|
+
|
|
5
7
|
import { WORKSHEET_STATUS, WORKSHEET_TYPE } from '../../../constants'
|
|
6
8
|
import { Worksheet, WorksheetDetail } from '../../../entities'
|
|
7
9
|
import { fetchExecutingWorksheet } from '../../../utils'
|
|
@@ -9,11 +11,34 @@ import { fetchExecutingWorksheet } from '../../../utils'
|
|
|
9
11
|
export const loadingWorksheetResolver = {
|
|
10
12
|
async loadingWorksheet(_: any, { releaseGoodNo }, context: any) {
|
|
11
13
|
const { domain }: { domain: Domain } = context.state
|
|
12
|
-
|
|
14
|
+
let releaseGood: ReleaseGood = await getRepository(ReleaseGood).findOne({
|
|
13
15
|
where: { domain, name: releaseGoodNo /*status: ORDER_STATUS.LOADING*/ },
|
|
14
16
|
relations: ['bizplace']
|
|
15
17
|
})
|
|
16
18
|
|
|
19
|
+
// Find Release Order based on Bin
|
|
20
|
+
if (!releaseGood) {
|
|
21
|
+
const binLocation: Location = await getRepository(Location).findOne({
|
|
22
|
+
where: { domain, name: releaseGoodNo }
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const qb: SelectQueryBuilder<OrderInventory> = getRepository(OrderInventory).createQueryBuilder('orderInventory')
|
|
26
|
+
qb.innerJoinAndSelect('orderInventory.releaseGood', 'releaseGood')
|
|
27
|
+
.innerJoinAndSelect('releaseGood.bizplace', 'bizplace')
|
|
28
|
+
.innerJoinAndSelect('bizplace.domain', 'domain')
|
|
29
|
+
.where('orderInventory.domain_id = :domainId', { domainId: domain.id })
|
|
30
|
+
.andWhere('orderInventory.status IN (:...orderInventoryStatus)', { orderInventoryStatus: [ORDER_INVENTORY_STATUS.LOADING, ORDER_INVENTORY_STATUS.LOADED] })
|
|
31
|
+
.andWhere('orderInventory.bin_location_id = :locationId', { locationId: binLocation.id })
|
|
32
|
+
.andWhere('releaseGood.status = :status', { status: ORDER_STATUS.LOADING })
|
|
33
|
+
|
|
34
|
+
const orderInventoryByBin = await qb.getOne()
|
|
35
|
+
|
|
36
|
+
if (orderInventoryByBin?.releaseGood) {
|
|
37
|
+
releaseGoodNo = orderInventoryByBin.releaseGood.name
|
|
38
|
+
releaseGood = orderInventoryByBin.releaseGood
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
17
42
|
if (!releaseGood) throw new Error(`Release good doesn't exists.`)
|
|
18
43
|
|
|
19
44
|
const foundWorksheet: Worksheet = await fetchExecutingWorksheet(
|
|
@@ -40,6 +65,7 @@ export const loadingWorksheetResolver = {
|
|
|
40
65
|
|
|
41
66
|
return {
|
|
42
67
|
worksheetInfo: {
|
|
68
|
+
releaseGood,
|
|
43
69
|
bizplaceName: releaseGood.bizplace.name,
|
|
44
70
|
startedAt: foundWorksheet.startedAt,
|
|
45
71
|
refNo: releaseGood.refNo,
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { EntityManager } from 'typeorm'
|
|
1
|
+
import { EntityManager, In, SelectQueryBuilder } from 'typeorm'
|
|
2
2
|
|
|
3
3
|
import { User } from '@things-factory/auth-base'
|
|
4
4
|
import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
|
|
5
5
|
import { MarketplaceOrder, MarketplaceOrderItem, MarketplaceOrderShipping } from '@things-factory/marketplace-base'
|
|
6
|
-
import { OrderInventory, ReleaseGood } from '@things-factory/sales-base'
|
|
6
|
+
import { ORDER_INVENTORY_STATUS, OrderInventory, ReleaseGood } from '@things-factory/sales-base'
|
|
7
7
|
import { Domain } from '@things-factory/shell'
|
|
8
|
-
import { Inventory } from '@things-factory/warehouse-base'
|
|
8
|
+
import { Inventory, Location } from '@things-factory/warehouse-base'
|
|
9
9
|
|
|
10
10
|
import { WORKSHEET_TYPE } from '../../../constants'
|
|
11
11
|
import { SellercraftController } from '../../../controllers'
|
|
12
12
|
import { Worksheet, WorksheetDetail } from '../../../entities'
|
|
13
13
|
import { fetchExecutingWorksheet } from '../../../utils'
|
|
14
|
+
import { ORDER_STATUS } from '@things-factory/sales-base'
|
|
14
15
|
|
|
15
16
|
export const packingWorksheetResolver = {
|
|
16
17
|
async packingWorksheet(_: any, { releaseGoodNo }, context: any) {
|
|
@@ -23,7 +24,30 @@ export const packingWorksheetResolver = {
|
|
|
23
24
|
relations: ['bizplace', 'bizplace.domain']
|
|
24
25
|
})
|
|
25
26
|
|
|
27
|
+
// Find Release Order based on Bin
|
|
28
|
+
if (!releaseGood) {
|
|
29
|
+
const binLocation: Location = await tx.getRepository(Location).findOne({
|
|
30
|
+
where: { domain, name: releaseGoodNo }
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const qb: SelectQueryBuilder<OrderInventory> = tx.getRepository(OrderInventory).createQueryBuilder('orderInventory')
|
|
34
|
+
qb.innerJoinAndSelect('orderInventory.releaseGood', 'releaseGood')
|
|
35
|
+
.innerJoinAndSelect('releaseGood.bizplace', 'bizplace')
|
|
36
|
+
.innerJoinAndSelect('bizplace.domain', 'domain')
|
|
37
|
+
.where('orderInventory.domain_id = :domainId', { domainId: domain.id })
|
|
38
|
+
.andWhere('orderInventory.status IN (:...orderInventoryStatus)', { orderInventoryStatus: [ORDER_INVENTORY_STATUS.PACKING, ORDER_INVENTORY_STATUS.PACKED] })
|
|
39
|
+
.andWhere('orderInventory.bin_location_id = :locationId', { locationId: binLocation.id })
|
|
40
|
+
.andWhere('releaseGood.status = :status', { status: ORDER_STATUS.PACKING })
|
|
41
|
+
|
|
42
|
+
const orderInventoryByBin = await qb.getOne()
|
|
43
|
+
if (orderInventoryByBin?.releaseGood) {
|
|
44
|
+
releaseGoodNo = orderInventoryByBin.releaseGood.name
|
|
45
|
+
releaseGood = orderInventoryByBin.releaseGood
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
26
49
|
if (!releaseGood) throw new Error(`Release good dosen't exist.`)
|
|
50
|
+
|
|
27
51
|
const worksheet: Worksheet = await fetchExecutingWorksheet(
|
|
28
52
|
domain,
|
|
29
53
|
releaseGood.bizplace,
|
|
@@ -110,6 +134,7 @@ export const packingWorksheetResolver = {
|
|
|
110
134
|
|
|
111
135
|
return {
|
|
112
136
|
worksheetInfo: {
|
|
137
|
+
releaseGood,
|
|
113
138
|
bizplaceName: releaseGood.bizplace.name,
|
|
114
139
|
refNo: releaseGood.refNo,
|
|
115
140
|
refNo2: releaseGood.refNo2,
|