@things-factory/sales-base 4.0.23 → 4.0.27
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/ecommerce/ecommerce-controller.js +7 -4
- package/dist-server/controllers/ecommerce/ecommerce-controller.js.map +1 -1
- package/dist-server/controllers/ecommerce/sellercraft-controller.js +59 -0
- package/dist-server/controllers/ecommerce/sellercraft-controller.js.map +1 -1
- package/dist-server/controllers/order-controller.js +40 -1
- package/dist-server/controllers/order-controller.js.map +1 -1
- package/dist-server/service/arrival-notice/arrival-notice-mutation.js +180 -0
- package/dist-server/service/arrival-notice/arrival-notice-mutation.js.map +1 -1
- package/dist-server/service/arrival-notice/arrival-notice-query.js +193 -1
- package/dist-server/service/arrival-notice/arrival-notice-query.js.map +1 -1
- package/dist-server/service/arrival-notice/arrival-notice-types.js +160 -2
- package/dist-server/service/arrival-notice/arrival-notice-types.js.map +1 -1
- package/dist-server/service/delivery-order/delivery-order-types.js +1 -1
- package/dist-server/service/delivery-order/delivery-order-types.js.map +1 -1
- package/dist-server/service/goods-receival-note/goods-receival-note.js +5 -0
- package/dist-server/service/goods-receival-note/goods-receival-note.js.map +1 -1
- package/dist-server/service/job-sheet/job-sheet-query.js +2 -0
- package/dist-server/service/job-sheet/job-sheet-query.js.map +1 -1
- package/dist-server/service/order-inventory/order-inventory.js +22 -1
- package/dist-server/service/order-inventory/order-inventory.js.map +1 -1
- package/dist-server/service/release-good/release-good-mutation.js +219 -212
- package/dist-server/service/release-good/release-good-mutation.js.map +1 -1
- package/dist-server/service/release-good/release-good-query.js +135 -99
- package/dist-server/service/release-good/release-good-query.js.map +1 -1
- package/dist-server/service/release-good/release-good-types.js +38 -2
- package/dist-server/service/release-good/release-good-types.js.map +1 -1
- package/dist-server/service/release-good/release-good.js +63 -1
- package/dist-server/service/release-good/release-good.js.map +1 -1
- package/dist-server/service/return-order/return-order-mutation.js +1 -1
- package/dist-server/service/return-order/return-order-mutation.js.map +1 -1
- package/dist-server/utils/inventory-util.js +89 -1
- package/dist-server/utils/inventory-util.js.map +1 -1
- package/package.json +12 -12
- package/server/controllers/ecommerce/ecommerce-controller.ts +15 -6
- package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
- package/server/controllers/order-controller.ts +55 -2
- package/server/service/arrival-notice/arrival-notice-mutation.ts +237 -1
- package/server/service/arrival-notice/arrival-notice-query.ts +214 -4
- package/server/service/arrival-notice/arrival-notice-types.ts +120 -1
- package/server/service/delivery-order/delivery-order-types.ts +1 -1
- package/server/service/goods-receival-note/goods-receival-note.ts +4 -0
- package/server/service/job-sheet/job-sheet-query.ts +3 -1
- package/server/service/order-inventory/order-inventory.ts +17 -1
- package/server/service/release-good/release-good-mutation.ts +280 -283
- package/server/service/release-good/release-good-query.ts +158 -115
- package/server/service/release-good/release-good-types.ts +30 -2
- package/server/service/release-good/release-good.ts +46 -0
- package/server/service/return-order/return-order-mutation.ts +1 -1
- package/server/utils/index.ts +1 -1
- package/server/utils/inventory-util.ts +129 -1
|
@@ -7,7 +7,7 @@ import { User } from '@things-factory/auth-base'
|
|
|
7
7
|
import { Bizplace, getCompanyBizplace, getMyBizplace, getPermittedBizplaceIds } from '@things-factory/biz-base'
|
|
8
8
|
import { Product } from '@things-factory/product-base'
|
|
9
9
|
import { buildQuery, Domain, Filter, ListParam, Pagination, Sorting } from '@things-factory/shell'
|
|
10
|
-
import { Inventory } from '@things-factory/warehouse-base'
|
|
10
|
+
import { Inventory, LOCATION_TYPE } from '@things-factory/warehouse-base'
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
InventoryInfos,
|
|
@@ -477,120 +477,10 @@ export class ReleaseGoodQuery {
|
|
|
477
477
|
@Arg('rawReleaseGoods', type => [NewReleaseGood], { nullable: true }) rawReleaseGoods: NewReleaseGood[],
|
|
478
478
|
@Arg('bizplaceId', type => String) bizplaceId: string
|
|
479
479
|
): Promise<ReleaseGood[]> {
|
|
480
|
-
const {
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
if (!rawReleaseGoods) return
|
|
484
|
-
|
|
485
|
-
const json_oi = JSON.stringify(
|
|
486
|
-
rawReleaseGoods.map(raw => {
|
|
487
|
-
return {
|
|
488
|
-
sku: raw.sku,
|
|
489
|
-
packing_type: raw.packingType,
|
|
490
|
-
packing_size: raw.packingSize,
|
|
491
|
-
uom: raw.uom,
|
|
492
|
-
release_qty: raw.releaseQty
|
|
493
|
-
}
|
|
494
|
-
})
|
|
495
|
-
)
|
|
480
|
+
const { tx }: { tx: EntityManager } = context.state
|
|
481
|
+
const availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(rawReleaseGoods, bizplaceId, context, tx)
|
|
496
482
|
|
|
497
|
-
|
|
498
|
-
`
|
|
499
|
-
CREATE TEMP TABLE temp_order_products(
|
|
500
|
-
product_id VARCHAR(50),
|
|
501
|
-
sku VARCHAR(150),
|
|
502
|
-
packing_type VARCHAR(50),
|
|
503
|
-
packing_size INT,
|
|
504
|
-
uom VARCHAR(10),
|
|
505
|
-
release_qty INT
|
|
506
|
-
);
|
|
507
|
-
`
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
await tx.query(
|
|
511
|
-
`
|
|
512
|
-
INSERT INTO temp_order_products
|
|
513
|
-
SELECT p.id AS product_id, js.sku,
|
|
514
|
-
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
|
|
515
|
-
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
|
|
516
|
-
CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
|
|
517
|
-
js.release_qty
|
|
518
|
-
FROM JSON_POPULATE_RECORDSET(NULL::temp_order_products,'${json_oi}') js
|
|
519
|
-
LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $1
|
|
520
|
-
LEFT JOIN product_details pd ON p.id = pd.product_id AND pd.is_default = TRUE;
|
|
521
|
-
`,
|
|
522
|
-
[companyBizplaceId.id]
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
let availableItems = await tx.query(
|
|
526
|
-
`
|
|
527
|
-
SELECT i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom,
|
|
528
|
-
SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
|
|
529
|
-
(
|
|
530
|
-
SELECT SUM(oi.release_qty) FROM order_inventories oi
|
|
531
|
-
WHERE (
|
|
532
|
-
oi.status = 'PENDING'
|
|
533
|
-
OR oi.status = 'PENDING_RECEIVE'
|
|
534
|
-
OR oi.status = 'PENDING_WORKSHEET'
|
|
535
|
-
OR oi.status = 'PENDING_SPLIT'
|
|
536
|
-
)
|
|
537
|
-
AND oi.inventory_id IS null
|
|
538
|
-
AND oi.product_id = i.product_id::uuid
|
|
539
|
-
AND oi.packing_type = i.packing_type
|
|
540
|
-
AND oi.uom = i.uom
|
|
541
|
-
AND oi.domain_id = $1
|
|
542
|
-
AND oi.bizplace_id = $2
|
|
543
|
-
), 0) as "remain_qty",
|
|
544
|
-
SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
|
|
545
|
-
(
|
|
546
|
-
SELECT SUM(oi.release_uom_value) FROM order_inventories oi
|
|
547
|
-
WHERE (
|
|
548
|
-
oi.status = 'PENDING'
|
|
549
|
-
OR oi.status = 'PENDING_RECEIVE'
|
|
550
|
-
OR oi.status = 'PENDING_WORKSHEET'
|
|
551
|
-
OR oi.status = 'PENDING_SPLIT'
|
|
552
|
-
)
|
|
553
|
-
AND oi.inventory_id IS null
|
|
554
|
-
AND oi.product_id = i.product_id::uuid
|
|
555
|
-
AND oi.packing_type = i.packing_type
|
|
556
|
-
AND oi.uom = i.uom
|
|
557
|
-
AND oi.domain_id = $1
|
|
558
|
-
AND oi.bizplace_id = $2
|
|
559
|
-
), 0) as "remain_uom_value"
|
|
560
|
-
FROM inventories i
|
|
561
|
-
INNER JOIN (
|
|
562
|
-
SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
|
|
563
|
-
FROM temp_order_products top
|
|
564
|
-
GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
|
|
565
|
-
) AS foo
|
|
566
|
-
ON i.product_id = foo.product_id::uuid
|
|
567
|
-
AND i.packing_type = foo.packing_type
|
|
568
|
-
AND i.packing_size = foo.packing_size
|
|
569
|
-
AND i.uom = foo.uom
|
|
570
|
-
AND i.domain_id = $1
|
|
571
|
-
AND i.bizplace_id = $2
|
|
572
|
-
GROUP BY i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom
|
|
573
|
-
ORDER BY foo.sku, remain_qty
|
|
574
|
-
`,
|
|
575
|
-
[domain.id, bizplaceId]
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
await tx.query('DROP TABLE temp_order_products')
|
|
579
|
-
|
|
580
|
-
availableItems = availableItems.map(item => {
|
|
581
|
-
return {
|
|
582
|
-
productId: item.product_id,
|
|
583
|
-
sku: item.sku,
|
|
584
|
-
batchId: item.batch_id,
|
|
585
|
-
packingType: item.packing_type,
|
|
586
|
-
packingSize: item.packing_size,
|
|
587
|
-
uom: item.uom,
|
|
588
|
-
remainQty: item.remain_qty,
|
|
589
|
-
remainUomValue: item.remain_uom_value
|
|
590
|
-
}
|
|
591
|
-
})
|
|
592
|
-
|
|
593
|
-
return _extractData(rawReleaseGoods, availableItems)
|
|
483
|
+
return availableItems
|
|
594
484
|
}
|
|
595
485
|
|
|
596
486
|
@FieldResolver(type => Domain)
|
|
@@ -689,6 +579,134 @@ async function dropOITempTable(domain: Domain, tx: EntityManager) {
|
|
|
689
579
|
await tx.query(`DROP TABLE oi`)
|
|
690
580
|
}
|
|
691
581
|
|
|
582
|
+
export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
583
|
+
rawReleaseGoods: any[],
|
|
584
|
+
bizplaceId: string,
|
|
585
|
+
context: any,
|
|
586
|
+
tx?: EntityManager
|
|
587
|
+
): Promise<any[]> {
|
|
588
|
+
const { domain }: { domain: Domain } = context.state
|
|
589
|
+
const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
|
|
590
|
+
|
|
591
|
+
if (!rawReleaseGoods) return
|
|
592
|
+
const json_oi = JSON.stringify(
|
|
593
|
+
rawReleaseGoods.map(raw => {
|
|
594
|
+
return {
|
|
595
|
+
sku: raw.sku,
|
|
596
|
+
packing_type: raw.packingType,
|
|
597
|
+
packing_size: raw.packingSize,
|
|
598
|
+
uom: raw.uom,
|
|
599
|
+
release_qty: raw.releaseQty
|
|
600
|
+
}
|
|
601
|
+
})
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
await tx.query(
|
|
605
|
+
`
|
|
606
|
+
CREATE TEMP TABLE raw_release_goods(
|
|
607
|
+
product_id VARCHAR(50),
|
|
608
|
+
product_detail_id VARCHAR(50),
|
|
609
|
+
sku VARCHAR(150),
|
|
610
|
+
product_info VARCHAR(250),
|
|
611
|
+
packing_type VARCHAR(50),
|
|
612
|
+
packing_size INT,
|
|
613
|
+
uom VARCHAR(10),
|
|
614
|
+
release_qty INT
|
|
615
|
+
);
|
|
616
|
+
`
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
await tx.query(
|
|
620
|
+
`
|
|
621
|
+
INSERT INTO raw_release_goods
|
|
622
|
+
SELECT p.id AS product_id,
|
|
623
|
+
pd.id AS product_detail_id,
|
|
624
|
+
js.sku,
|
|
625
|
+
CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
|
|
626
|
+
ELSE p.name END AS product_info,
|
|
627
|
+
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
|
|
628
|
+
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
|
|
629
|
+
CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END
|
|
630
|
+
FROM JSON_POPULATE_RECORDSET(NULL::raw_release_goods, $1) js
|
|
631
|
+
LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $2
|
|
632
|
+
LEFT JOIN product_details pd ON p.id = pd.product_id
|
|
633
|
+
AND CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom NOTNULL
|
|
634
|
+
THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.uom = js.uom
|
|
635
|
+
WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom ISNULL
|
|
636
|
+
THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.is_default IS TRUE
|
|
637
|
+
WHEN js.packing_type ISNULL AND js.packing_size ISNULL AND js.uom NOTNULL
|
|
638
|
+
THEN pd.uom = js.uom AND pd.is_default IS TRUE ELSE pd.is_default IS TRUE
|
|
639
|
+
END;
|
|
640
|
+
`,
|
|
641
|
+
[json_oi, companyBizplaceId.id]
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
let availableItems = await tx.query(
|
|
645
|
+
`
|
|
646
|
+
WITH inv AS (
|
|
647
|
+
SELECT i.product_id, foo.product_detail_id, foo.sku, foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom,
|
|
648
|
+
SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
|
|
649
|
+
(
|
|
650
|
+
SELECT SUM(oi.release_qty) FROM order_inventories oi
|
|
651
|
+
WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
|
|
652
|
+
AND oi.inventory_id IS null
|
|
653
|
+
AND oi.product_id = i.product_id::uuid
|
|
654
|
+
AND oi.packing_type = i.packing_type
|
|
655
|
+
AND oi.uom = i.uom
|
|
656
|
+
AND oi.domain_id = $1
|
|
657
|
+
AND oi.bizplace_id = $2
|
|
658
|
+
), 0) as "remain_qty",
|
|
659
|
+
SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
|
|
660
|
+
(
|
|
661
|
+
SELECT SUM(oi.release_uom_value) FROM order_inventories oi
|
|
662
|
+
WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
|
|
663
|
+
AND oi.inventory_id IS null
|
|
664
|
+
AND oi.product_id = i.product_id::uuid
|
|
665
|
+
AND oi.packing_type = i.packing_type
|
|
666
|
+
AND oi.uom = i.uom
|
|
667
|
+
AND oi.domain_id = $1
|
|
668
|
+
AND oi.bizplace_id = $2
|
|
669
|
+
), 0) as "remain_uom_value"
|
|
670
|
+
FROM inventories i
|
|
671
|
+
LEFT JOIN locations l
|
|
672
|
+
ON i.location_id = l.id
|
|
673
|
+
INNER JOIN (
|
|
674
|
+
SELECT rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
|
|
675
|
+
FROM raw_release_goods rrg
|
|
676
|
+
GROUP BY rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
|
|
677
|
+
) AS foo
|
|
678
|
+
ON i.product_id = foo.product_id::uuid
|
|
679
|
+
AND i.packing_type = foo.packing_type
|
|
680
|
+
AND i.packing_size = foo.packing_size
|
|
681
|
+
AND i.uom = foo.uom
|
|
682
|
+
AND i.domain_id = $1
|
|
683
|
+
AND i.bizplace_id = $2
|
|
684
|
+
WHERE l.type NOT IN ($3, $4)
|
|
685
|
+
GROUP BY i.product_id, foo.product_detail_id, foo.sku,foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom
|
|
686
|
+
ORDER BY foo.sku, remain_qty
|
|
687
|
+
) SELECT * FROM inv WHERE remain_qty > 0
|
|
688
|
+
`,
|
|
689
|
+
[domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
availableItems = availableItems.map(item => {
|
|
693
|
+
return {
|
|
694
|
+
productId: item.product_id,
|
|
695
|
+
productDetailId: item.product_detail_id,
|
|
696
|
+
productInfo: item.product_info,
|
|
697
|
+
sku: item.sku,
|
|
698
|
+
batchId: item.batch_id,
|
|
699
|
+
packingType: item.packing_type,
|
|
700
|
+
packingSize: item.packing_size,
|
|
701
|
+
uom: item.uom,
|
|
702
|
+
remainQty: item.remain_qty,
|
|
703
|
+
remainUomValue: item.remain_uom_value
|
|
704
|
+
}
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
return _extractData(rawReleaseGoods, availableItems)
|
|
708
|
+
}
|
|
709
|
+
|
|
692
710
|
function _extractData(rawData, validatedData) {
|
|
693
711
|
return rawData.map(raw => {
|
|
694
712
|
const idx = validatedData.findIndex(val => {
|
|
@@ -737,6 +755,7 @@ function _extractData(rawData, validatedData) {
|
|
|
737
755
|
validatedData[idx].remainUomValue -= raw.assignedUomValue
|
|
738
756
|
|
|
739
757
|
raw.productId = validatedData[idx].productId
|
|
758
|
+
raw.productDetailId = validatedData[idx].productDetailId
|
|
740
759
|
raw.packingType = validatedData[idx].packingType
|
|
741
760
|
raw.packingSize = validatedData[idx].packingSize
|
|
742
761
|
raw.uom = validatedData[idx].uom
|
|
@@ -747,9 +766,25 @@ function _extractData(rawData, validatedData) {
|
|
|
747
766
|
raw.productId = null
|
|
748
767
|
}
|
|
749
768
|
|
|
769
|
+
let releaseDate = _getStdDateStr(new Date(raw.releaseDate || ''))
|
|
770
|
+
|
|
750
771
|
return {
|
|
751
772
|
...raw,
|
|
752
|
-
releaseUomValue
|
|
773
|
+
releaseUomValue,
|
|
774
|
+
errorMsg:
|
|
775
|
+
!raw.productId || !raw.productDetailId
|
|
776
|
+
? 'product not found'
|
|
777
|
+
: raw.releaseQty <= 0
|
|
778
|
+
? 'invalid release qty'
|
|
779
|
+
: raw.assignedQty < raw.releaseQty
|
|
780
|
+
? 'insufficient stock'
|
|
781
|
+
: raw.releaseDate == ''
|
|
782
|
+
? 'release date is empty'
|
|
783
|
+
: releaseDate < _getStdDateStr(new Date())
|
|
784
|
+
? 'backdate is not allowed'
|
|
785
|
+
: !raw.refNo
|
|
786
|
+
? 'ref no is empty'
|
|
787
|
+
: ''
|
|
753
788
|
}
|
|
754
789
|
})
|
|
755
790
|
}
|
|
@@ -772,3 +807,11 @@ function getConditionValues(
|
|
|
772
807
|
return condition
|
|
773
808
|
}, {})
|
|
774
809
|
}
|
|
810
|
+
|
|
811
|
+
function _getStdDateStr(date) {
|
|
812
|
+
if (isNaN(date.getFullYear())) return ''
|
|
813
|
+
else {
|
|
814
|
+
date.setHours(date.getHours() + 8)
|
|
815
|
+
return date.toISOString().split('T')[0]
|
|
816
|
+
}
|
|
817
|
+
}
|
|
@@ -158,6 +158,9 @@ export class NewReleaseGood {
|
|
|
158
158
|
@Field({ nullable: true })
|
|
159
159
|
name: string
|
|
160
160
|
|
|
161
|
+
@Field({ nullable: true })
|
|
162
|
+
bundleInfo: string
|
|
163
|
+
|
|
161
164
|
@Field({ nullable: true })
|
|
162
165
|
releaseDate: string
|
|
163
166
|
|
|
@@ -305,6 +308,13 @@ export class NewReleaseGood {
|
|
|
305
308
|
@Field({ nullable: true })
|
|
306
309
|
partnerBizplaceId: string
|
|
307
310
|
|
|
311
|
+
/* Mainly used for rawReleaseGood*/
|
|
312
|
+
@Field({ nullable: true })
|
|
313
|
+
productId: string
|
|
314
|
+
|
|
315
|
+
@Field({ nullable: true })
|
|
316
|
+
productDetailId: string
|
|
317
|
+
|
|
308
318
|
@Field({ nullable: true })
|
|
309
319
|
sku: string
|
|
310
320
|
|
|
@@ -312,17 +322,35 @@ export class NewReleaseGood {
|
|
|
312
322
|
packingType: string
|
|
313
323
|
|
|
314
324
|
@Field({ nullable: true })
|
|
315
|
-
packingSize:
|
|
325
|
+
packingSize: number
|
|
316
326
|
|
|
317
327
|
@Field({ nullable: true })
|
|
318
328
|
uom: string
|
|
319
329
|
|
|
320
330
|
@Field({ nullable: true })
|
|
321
|
-
releaseQty:
|
|
331
|
+
releaseQty: number
|
|
332
|
+
|
|
333
|
+
@Field({ nullable: true })
|
|
334
|
+
assignedQty: number
|
|
335
|
+
|
|
336
|
+
@Field({ nullable: true })
|
|
337
|
+
assignedUomValue: number
|
|
338
|
+
|
|
339
|
+
@Field({ nullable: true })
|
|
340
|
+
remainQty: number
|
|
341
|
+
|
|
342
|
+
@Field({ nullable: true })
|
|
343
|
+
remainUomValue: number
|
|
344
|
+
|
|
345
|
+
@Field({ nullable: true })
|
|
346
|
+
errorMsg: string
|
|
322
347
|
}
|
|
323
348
|
|
|
324
349
|
@InputType()
|
|
325
350
|
export class ReleaseGoodPatch {
|
|
351
|
+
@Field({ nullable: true })
|
|
352
|
+
id: string
|
|
353
|
+
|
|
326
354
|
@Field({ nullable: true })
|
|
327
355
|
name: string
|
|
328
356
|
|
|
@@ -289,6 +289,9 @@ export class ReleaseGood {
|
|
|
289
289
|
@Field({ nullable: true })
|
|
290
290
|
invoice: string
|
|
291
291
|
|
|
292
|
+
@Column({ nullable: true })
|
|
293
|
+
bundleInfo: string
|
|
294
|
+
|
|
292
295
|
@Column({ type: 'int', nullable: true })
|
|
293
296
|
@Field({ nullable: true })
|
|
294
297
|
noOfItems: number
|
|
@@ -341,4 +344,47 @@ export class ReleaseGood {
|
|
|
341
344
|
|
|
342
345
|
@Field(type => ShippingOrderInfo, { nullable: true })
|
|
343
346
|
shippingOrderInfo?: ShippingOrderInfo
|
|
347
|
+
|
|
348
|
+
/* Mainly used for rawReleaseGood*/
|
|
349
|
+
@Field({ nullable: true })
|
|
350
|
+
productId: string
|
|
351
|
+
|
|
352
|
+
@Field({ nullable: true })
|
|
353
|
+
productDetailId: string
|
|
354
|
+
|
|
355
|
+
@Field({ nullable: true })
|
|
356
|
+
productInfo: string
|
|
357
|
+
|
|
358
|
+
@Field({ nullable: true })
|
|
359
|
+
sku: string
|
|
360
|
+
|
|
361
|
+
@Field({ nullable: true })
|
|
362
|
+
batchId: string
|
|
363
|
+
|
|
364
|
+
@Field({ nullable: true })
|
|
365
|
+
packingType: string
|
|
366
|
+
|
|
367
|
+
@Field({ nullable: true })
|
|
368
|
+
packingSize: number
|
|
369
|
+
|
|
370
|
+
@Field({ nullable: true })
|
|
371
|
+
uom: string
|
|
372
|
+
|
|
373
|
+
@Field({ nullable: true })
|
|
374
|
+
releaseQty: number
|
|
375
|
+
|
|
376
|
+
@Field({ nullable: true })
|
|
377
|
+
assignedQty: number
|
|
378
|
+
|
|
379
|
+
@Field({ nullable: true })
|
|
380
|
+
assignedUomValue: number
|
|
381
|
+
|
|
382
|
+
@Field({ nullable: true })
|
|
383
|
+
remainQty: number
|
|
384
|
+
|
|
385
|
+
@Field({ nullable: true })
|
|
386
|
+
remainUomValue: number
|
|
387
|
+
|
|
388
|
+
@Field({ nullable: true })
|
|
389
|
+
errorMsg: string
|
|
344
390
|
}
|
|
@@ -42,7 +42,7 @@ export class ReturnOrderMutation {
|
|
|
42
42
|
@Mutation(returns => ReturnOrder)
|
|
43
43
|
async generateReturnOrder(
|
|
44
44
|
@Ctx() context: any,
|
|
45
|
-
@Arg('file', type => [GraphQLUpload]) file: FileUpload[],
|
|
45
|
+
@Arg('file', type => [GraphQLUpload], { nullable: true }) file: FileUpload[],
|
|
46
46
|
@Arg('returnOrder', type => NewReturnOrder, { nullable: true }) returnOrder: NewReturnOrder
|
|
47
47
|
): Promise<ReturnOrder> {
|
|
48
48
|
const { tx, domain, user }: { tx: EntityManager; domain: Domain; user: User } = context.state
|
package/server/utils/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './order-no-generator'
|
|
2
|
-
export * from './inventory-util'
|
|
2
|
+
export * from './inventory-util'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityManager, Equal, getRepository, In, Not, Raw, Repository } from 'typeorm'
|
|
1
|
+
import { EntityManager, Equal, getRepository, In, Not, Raw, Repository, SelectQueryBuilder } from 'typeorm'
|
|
2
2
|
|
|
3
3
|
import { User } from '@things-factory/auth-base'
|
|
4
4
|
import { Bizplace } from '@things-factory/biz-base'
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from '@things-factory/warehouse-base'
|
|
17
17
|
|
|
18
18
|
import { ValidationError } from '../errors'
|
|
19
|
+
import { ORDER_TYPES } from '../constants'
|
|
19
20
|
import {
|
|
20
21
|
ArrivalNotice,
|
|
21
22
|
DeliveryOrder,
|
|
@@ -270,6 +271,68 @@ export const InventoryUtil = {
|
|
|
270
271
|
if (duplicatedReusablePalletCnt) throw new Error(palletId + ` exists`)
|
|
271
272
|
},
|
|
272
273
|
|
|
274
|
+
/**
|
|
275
|
+
* To pre-assign inventories automatically for orderInventories in Release Goods
|
|
276
|
+
* @param product
|
|
277
|
+
* @param orderInventory
|
|
278
|
+
* @param packingType
|
|
279
|
+
* @param locationSortingRules
|
|
280
|
+
* @param customerBizplace
|
|
281
|
+
* @param domain
|
|
282
|
+
* @param trxMgr
|
|
283
|
+
* @returns orderInventories
|
|
284
|
+
*/
|
|
285
|
+
async autoAssignInventoryForRelease(
|
|
286
|
+
product: Product,
|
|
287
|
+
orderInventory: OrderInventory,
|
|
288
|
+
packingType: string,
|
|
289
|
+
locationSortingRules: any = [],
|
|
290
|
+
customerBizplace: Bizplace,
|
|
291
|
+
domain: Domain,
|
|
292
|
+
trxMgr: EntityManager
|
|
293
|
+
): Promise<OrderInventory[]> {
|
|
294
|
+
let qb: SelectQueryBuilder<Inventory> = trxMgr.getRepository(Inventory).createQueryBuilder('iv')
|
|
295
|
+
qb.leftJoinAndSelect('iv.location', 'loc')
|
|
296
|
+
.andWhere('"iv"."domain_id" = :domainId')
|
|
297
|
+
.andWhere('"iv"."bizplace_id" = :bizplaceId')
|
|
298
|
+
.andWhere('"iv"."packing_type" = :packingType')
|
|
299
|
+
.andWhere('"iv"."product_id" = :productId')
|
|
300
|
+
.andWhere('"iv"."status" = :status')
|
|
301
|
+
.andWhere('"iv"."qty" - COALESCE("iv"."locked_qty", 0) > 0')
|
|
302
|
+
.andWhere('"loc"."type" NOT IN (:...locationTypes)')
|
|
303
|
+
.setParameters({
|
|
304
|
+
domainId: domain.id,
|
|
305
|
+
bizplaceId: customerBizplace.id,
|
|
306
|
+
packingType: packingType,
|
|
307
|
+
productId: product.id,
|
|
308
|
+
status: INVENTORY_STATUS.STORED,
|
|
309
|
+
locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
if (locationSortingRules?.length) {
|
|
313
|
+
locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
|
|
314
|
+
idx === 0
|
|
315
|
+
? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
316
|
+
: qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
317
|
+
})
|
|
318
|
+
} else {
|
|
319
|
+
qb.addOrderBy('"iv"."created_at"', 'ASC')
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let inventories: Inventory[] = await qb.getMany()
|
|
323
|
+
if (!inventories?.length) throw new Error(`no inventories found for ${product.sku}`)
|
|
324
|
+
|
|
325
|
+
inventories = inventories.map(inventory => {
|
|
326
|
+
return {
|
|
327
|
+
...inventory,
|
|
328
|
+
remainQty: inventory.qty - (inventory?.lockedQty || 0),
|
|
329
|
+
remainUomValue: inventory.uomValue - (inventory?.lockedUomValue || 0)
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
return _composeTargetInventories(product, orderInventory, inventories)
|
|
334
|
+
},
|
|
335
|
+
|
|
273
336
|
/**
|
|
274
337
|
* @summary Do transaction on inventory record
|
|
275
338
|
* @description It will update inventory after set a temp (domain, updater)
|
|
@@ -617,3 +680,68 @@ export async function switchLocationStatus(
|
|
|
617
680
|
|
|
618
681
|
return location
|
|
619
682
|
}
|
|
683
|
+
|
|
684
|
+
export function _composeTargetInventories(product: Product, record: any, inventories: Inventory[]): OrderInventory[] {
|
|
685
|
+
let leftReleaseQty: number = record.releaseQty
|
|
686
|
+
let leftReleaseUomValue: number = record.releaseUomValue
|
|
687
|
+
let compReleaseQty: number = 0
|
|
688
|
+
let compReleaseUomValue: number = 0
|
|
689
|
+
let totalInventoryQty: number = inventories.reduce((total, inventory) => total + inventory.remainQty, 0)
|
|
690
|
+
|
|
691
|
+
if (totalInventoryQty < record.releaseQty) {
|
|
692
|
+
throw new Error(`invalid release qty for ${product?.sku}`)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
let orderInventories: Partial<OrderInventory[]> = []
|
|
696
|
+
let idx = 0
|
|
697
|
+
while (compReleaseQty < record.releaseQty) {
|
|
698
|
+
const inventory = inventories[idx]
|
|
699
|
+
const {
|
|
700
|
+
packingType,
|
|
701
|
+
packingSize,
|
|
702
|
+
batchId,
|
|
703
|
+
uom
|
|
704
|
+
}: { packingType: string; packingSize: number; batchId: string; uom: string } = inventory
|
|
705
|
+
|
|
706
|
+
let orderInventory: OrderInventory = new OrderInventory()
|
|
707
|
+
|
|
708
|
+
if (inventory.remainQty > leftReleaseQty) {
|
|
709
|
+
const uomValuePerQty: number = Math.round((inventory.remainUomValue / inventory.remainQty) * 100) / 100
|
|
710
|
+
|
|
711
|
+
compReleaseQty += leftReleaseQty
|
|
712
|
+
compReleaseUomValue += leftReleaseUomValue
|
|
713
|
+
|
|
714
|
+
orderInventory = {
|
|
715
|
+
...orderInventory,
|
|
716
|
+
releaseQty: leftReleaseQty,
|
|
717
|
+
releaseUomValue: Math.round(leftReleaseQty * uomValuePerQty * 100) / 100
|
|
718
|
+
}
|
|
719
|
+
} else {
|
|
720
|
+
compReleaseQty += inventory.remainQty
|
|
721
|
+
compReleaseUomValue += inventory.remainUomValue
|
|
722
|
+
leftReleaseQty -= inventory.remainQty
|
|
723
|
+
leftReleaseUomValue -= inventory.remainUomValue
|
|
724
|
+
|
|
725
|
+
orderInventory = {
|
|
726
|
+
...orderInventory,
|
|
727
|
+
releaseQty: inventory.remainQty,
|
|
728
|
+
releaseUomValue: inventory.remainUomValue
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
orderInventories.push({
|
|
733
|
+
...orderInventory,
|
|
734
|
+
inventory,
|
|
735
|
+
packingType,
|
|
736
|
+
packingSize,
|
|
737
|
+
batchId,
|
|
738
|
+
uom,
|
|
739
|
+
product,
|
|
740
|
+
type: ORDER_TYPES.RELEASE_OF_GOODS
|
|
741
|
+
})
|
|
742
|
+
|
|
743
|
+
idx++
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return orderInventories
|
|
747
|
+
}
|