@things-factory/sales-base 4.0.25 → 4.0.26

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.
@@ -450,7 +450,7 @@ export async function validateBulkArrivalNoticesFunction(
450
450
  ELSE CASE WHEN tp.batch_id ISNULL THEN 'batch no. is required'
451
451
  ELSE CASE WHEN tp.ref_no ISNULL OR tp.ref_no = '' THEN 'ref no. is required'
452
452
  ELSE CASE WHEN tp.eta_date ISNULL OR tp.eta_date = '' THEN 'eta date is required'
453
- ELSE CASE WHEN tp.eta_date:: date < timeofday():: date THEN 'backdate is not allowed'
453
+ ELSE CASE WHEN tp.eta_date::date < now()::date THEN 'backdate is not allowed'
454
454
  ELSE CASE WHEN tp.container_no NOTNULL AND tp.container_size ISNULL OR tp.container_no ISNULL AND tp.container_size NOTNULL THEN 'incomplete container information'
455
455
  ELSE '' END END END END END END END END AS remark
456
456
  FROM
@@ -1,13 +1,14 @@
1
1
  import { User } from '@things-factory/auth-base'
2
2
  import { getPermittedBizplaceIds } from '@things-factory/biz-base'
3
3
  import { buildQuery, Domain, Filter, Pagination, Sorting } from '@things-factory/shell'
4
- import { Arg, Ctx, FieldResolver, Query, Resolver, Root } from 'type-graphql'
4
+ import { Arg, Ctx, FieldResolver, Query, Resolver, Root, Directive } from 'type-graphql'
5
5
  import { EntityManager, getRepository, SelectQueryBuilder } from 'typeorm'
6
6
  import { ArrivalNotice, JobSheetList } from '../'
7
7
  import { JobSheet } from './job-sheet'
8
8
 
9
9
  @Resolver(JobSheet)
10
10
  export class JobSheetQuery {
11
+ @Directive('@transaction')
11
12
  @Query(returns => JobSheetList)
12
13
  async jobSheets(
13
14
  @Ctx() context: any,
@@ -65,6 +66,7 @@ export class JobSheetQuery {
65
66
  return { items, total }
66
67
  }
67
68
 
69
+ @Directive('transaction')
68
70
  @Query(returns => JobSheet)
69
71
  async jobSheet(@Arg('arrivalNoticeRefNo') arrivalNoticeRefNo: string, @Ctx() context: any): Promise<JobSheet> {
70
72
  const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
@@ -6,7 +6,6 @@ import { Attachment, createAttachments } from '@things-factory/attachment-base'
6
6
  import { Partner, Role, User } from '@things-factory/auth-base'
7
7
  import {
8
8
  Bizplace,
9
- getCompanyBizplace,
10
9
  getDomainUsers,
11
10
  getMyBizplace,
12
11
  getOutletBizplace,
@@ -20,7 +19,7 @@ import { sendNotification } from '@things-factory/notification'
20
19
  import { Product, ProductBundleSetting } from '@things-factory/product-base'
21
20
  import { PartnerSetting, Setting } from '@things-factory/setting-base'
22
21
  import { Domain } from '@things-factory/shell'
23
- import { Inventory, LOCATION_TYPE } from '@things-factory/warehouse-base'
22
+ import { Inventory } from '@things-factory/warehouse-base'
24
23
 
25
24
  import {
26
25
  ArrivalNotice,
@@ -49,6 +48,7 @@ import { ValidationError } from '../../errors'
49
48
  import { InventoryUtil, OrderNoGenerator } from '../../utils'
50
49
  import { confirmArrivalNoticeFunction, deleteArrivalNotice } from '../arrival-notice/arrival-notice-mutation'
51
50
  import { ReleaseGood } from './release-good'
51
+ import { bulkReleaseGoodsAvailableItemsFunction } from './release-good-query'
52
52
 
53
53
  @Resolver(ReleaseGood)
54
54
  export class ReleaseGoodMutation {
@@ -108,7 +108,7 @@ export class ReleaseGoodMutation {
108
108
  // update orderInventories if availableItems are valid
109
109
  releaseGoods[i].orderInventories = availableItems
110
110
 
111
- const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGoods(
111
+ const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGood(
112
112
  releaseGoods[i],
113
113
  bizplaceId,
114
114
  roNoSetting,
@@ -734,200 +734,6 @@ export async function generateReleaseGoodFunction(
734
734
  }
735
735
  }
736
736
 
737
- export async function bulkReleaseGoodsAvailableItemsFunction(
738
- rawReleaseGoods: any[],
739
- bizplaceId: string,
740
- context: any,
741
- tx?: EntityManager
742
- ): Promise<any[]> {
743
- const { domain }: { domain: Domain } = context.state
744
- const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
745
-
746
- if (!rawReleaseGoods) return
747
-
748
- const json_oi = JSON.stringify(
749
- rawReleaseGoods.map(raw => {
750
- return {
751
- sku: raw.sku,
752
- packing_type: raw.packingType,
753
- packing_size: raw.packingSize,
754
- uom: raw.uom,
755
- release_qty: raw.releaseQty
756
- }
757
- })
758
- )
759
-
760
- await tx.query(
761
- `
762
- CREATE TEMP TABLE temp_order_products(
763
- product_id VARCHAR(50),
764
- sku VARCHAR(150),
765
- product_info VARCHAR(250),
766
- packing_type VARCHAR(50),
767
- packing_size INT,
768
- uom VARCHAR(10),
769
- release_qty INT
770
- );
771
- `
772
- )
773
-
774
- await tx.query(
775
- `
776
- INSERT INTO temp_order_products
777
- SELECT p.id AS product_id, js.sku,
778
- CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
779
- ELSE p.name END AS product_info,
780
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
781
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
782
- CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
783
- js.release_qty
784
- FROM JSON_POPULATE_RECORDSET(NULL::temp_order_products,'${json_oi}') js
785
- LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $1
786
- LEFT JOIN product_details pd ON p.id = pd.product_id AND pd.is_default = TRUE;
787
- `,
788
- [companyBizplaceId.id]
789
- )
790
-
791
- let availableItems = await tx.query(
792
- `
793
- WITH inv AS (
794
- SELECT i.product_id, foo.sku, foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom,
795
- SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
796
- (
797
- SELECT SUM(oi.release_qty) FROM order_inventories oi
798
- WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
799
- AND oi.inventory_id IS null
800
- AND oi.product_id = i.product_id::uuid
801
- AND oi.packing_type = i.packing_type
802
- AND oi.uom = i.uom
803
- AND oi.domain_id = $1
804
- AND oi.bizplace_id = $2
805
- ), 0) as "remain_qty",
806
- SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
807
- (
808
- SELECT SUM(oi.release_uom_value) FROM order_inventories oi
809
- WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
810
- AND oi.inventory_id IS null
811
- AND oi.product_id = i.product_id::uuid
812
- AND oi.packing_type = i.packing_type
813
- AND oi.uom = i.uom
814
- AND oi.domain_id = $1
815
- AND oi.bizplace_id = $2
816
- ), 0) as "remain_uom_value"
817
- FROM inventories i
818
- LEFT JOIN locations l
819
- ON i.location_id = l.id
820
- INNER JOIN (
821
- SELECT top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
822
- FROM temp_order_products top
823
- GROUP BY top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
824
- ) AS foo
825
- ON i.product_id = foo.product_id::uuid
826
- AND i.packing_type = foo.packing_type
827
- AND i.packing_size = foo.packing_size
828
- AND i.uom = foo.uom
829
- AND i.domain_id = $1
830
- AND i.bizplace_id = $2
831
- WHERE l.type NOT IN ($3, $4)
832
- GROUP BY i.product_id, foo.sku,foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom
833
- ORDER BY foo.sku, remain_qty
834
- ) SELECT * FROM inv WHERE remain_qty > 0
835
- `,
836
- [domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
837
- )
838
-
839
- await tx.query('DROP TABLE temp_order_products')
840
-
841
- availableItems = availableItems.map(item => {
842
- return {
843
- productId: item.product_id,
844
- productInfo: item.product_info,
845
- sku: item.sku,
846
- batchId: item.batch_id,
847
- packingType: item.packing_type,
848
- packingSize: item.packing_size,
849
- uom: item.uom,
850
- remainQty: item.remain_qty,
851
- remainUomValue: item.remain_uom_value
852
- }
853
- })
854
-
855
- return _extractData(rawReleaseGoods, availableItems)
856
- }
857
-
858
- function _extractData(rawData, validatedData) {
859
- return rawData.map(raw => {
860
- const idx = validatedData.findIndex(val => {
861
- const comparison = ['packingType', 'packingSize', 'uom']
862
-
863
- let a: any = {},
864
- b: any = {}
865
-
866
- comparison.forEach(cc => {
867
- if (raw[cc]) {
868
- a[cc] = raw[cc]
869
- b[cc] = val[cc]
870
- }
871
- })
872
-
873
- a = JSON.stringify(Object.fromEntries(Object.entries(a).sort()))
874
- b = JSON.stringify(Object.fromEntries(Object.entries(b).sort()))
875
-
876
- return val.sku == raw.sku && a === b
877
- })
878
-
879
- let releaseUomValue: number = 0
880
-
881
- // if sku is matched, assign qty and product id
882
- if (idx >= 0) {
883
- const uomValuePerQty: number =
884
- Math.round((validatedData[idx].remainUomValue / validatedData[idx].remainQty) * 100) / 100
885
-
886
- // use Math.round again because 4.2 * 6 = 25.200000000000003
887
- releaseUomValue = Math.round(uomValuePerQty * raw.releaseQty * 100) / 100
888
-
889
- // assign qty to rawData as much as possible
890
- raw.assignedQty =
891
- validatedData[idx].remainQty >= raw.releaseQty
892
- ? raw.releaseQty > 0
893
- ? raw.releaseQty
894
- : 0
895
- : validatedData[idx].remainQty > 0
896
- ? validatedData[idx].remainQty
897
- : 0
898
-
899
- raw.assignedUomValue =
900
- validatedData[idx].remainUomValue >= releaseUomValue
901
- ? releaseUomValue > 0
902
- ? releaseUomValue
903
- : 0
904
- : validatedData[idx].remainUomValue > 0
905
- ? validatedData[idx].remainUomValue
906
- : 0
907
-
908
- // deduct qty & uomValue from validateData
909
- validatedData[idx].remainQty -= raw.assignedQty
910
- validatedData[idx].remainUomValue -= raw.assignedUomValue
911
-
912
- raw.productId = validatedData[idx].productId
913
- raw.productInfo = validatedData[idx].productInfo
914
- raw.packingType = validatedData[idx].packingType
915
- raw.packingSize = validatedData[idx].packingSize
916
- raw.uom = validatedData[idx].uom
917
- raw.batchId = validatedData[idx].batchId
918
- } else {
919
- raw.assignedQty = 0
920
- raw.assignedUomValue = 0
921
- raw.productId = null
922
- }
923
-
924
- return {
925
- ...raw,
926
- releaseUomValue
927
- }
928
- })
929
- }
930
-
931
737
  export async function confirmReleaseGood(name: string, context: any, tx?: EntityManager): Promise<ReleaseGood> {
932
738
  const { user, domain }: { user: User; domain: Domain } = context.state
933
739
 
@@ -1308,7 +1114,7 @@ export async function rejectReleaseGood(
1308
1114
  return releaseGood
1309
1115
  }
1310
1116
 
1311
- export async function bulkGenerateReleaseGoods(
1117
+ export async function bulkGenerateReleaseGood(
1312
1118
  releaseGood: any,
1313
1119
  bizplaceId: string,
1314
1120
  roNoSetting: any,
@@ -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 { domain, tx } = context.state
481
- const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
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
- await tx.query(
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
+ }
@@ -308,6 +308,13 @@ export class NewReleaseGood {
308
308
  @Field({ nullable: true })
309
309
  partnerBizplaceId: string
310
310
 
311
+ /* Mainly used for rawReleaseGood*/
312
+ @Field({ nullable: true })
313
+ productId: string
314
+
315
+ @Field({ nullable: true })
316
+ productDetailId: string
317
+
311
318
  @Field({ nullable: true })
312
319
  sku: string
313
320
 
@@ -315,13 +322,28 @@ export class NewReleaseGood {
315
322
  packingType: string
316
323
 
317
324
  @Field({ nullable: true })
318
- packingSize: string
325
+ packingSize: number
319
326
 
320
327
  @Field({ nullable: true })
321
328
  uom: string
322
329
 
323
330
  @Field({ nullable: true })
324
- releaseQty: string
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
325
347
  }
326
348
 
327
349
  @InputType()
@@ -344,4 +344,47 @@ export class ReleaseGood {
344
344
 
345
345
  @Field(type => ShippingOrderInfo, { nullable: true })
346
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
347
390
  }