@things-factory/sales-base 4.0.24 → 4.0.28

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.
Files changed (59) hide show
  1. package/dist-server/controllers/ecommerce/ecommerce-controller.js +7 -4
  2. package/dist-server/controllers/ecommerce/ecommerce-controller.js.map +1 -1
  3. package/dist-server/controllers/ecommerce/sellercraft-controller.js +59 -0
  4. package/dist-server/controllers/ecommerce/sellercraft-controller.js.map +1 -1
  5. package/dist-server/controllers/order-controller.js +40 -1
  6. package/dist-server/controllers/order-controller.js.map +1 -1
  7. package/dist-server/service/arrival-notice/arrival-notice-mutation.js +180 -0
  8. package/dist-server/service/arrival-notice/arrival-notice-mutation.js.map +1 -1
  9. package/dist-server/service/arrival-notice/arrival-notice-query.js +193 -1
  10. package/dist-server/service/arrival-notice/arrival-notice-query.js.map +1 -1
  11. package/dist-server/service/arrival-notice/arrival-notice-types.js +160 -2
  12. package/dist-server/service/arrival-notice/arrival-notice-types.js.map +1 -1
  13. package/dist-server/service/delivery-order/delivery-order-types.js +1 -1
  14. package/dist-server/service/delivery-order/delivery-order-types.js.map +1 -1
  15. package/dist-server/service/delivery-order/delivery-order.js +1 -1
  16. package/dist-server/service/delivery-order/delivery-order.js.map +1 -1
  17. package/dist-server/service/goods-receival-note/goods-receival-note.js +5 -0
  18. package/dist-server/service/goods-receival-note/goods-receival-note.js.map +1 -1
  19. package/dist-server/service/job-sheet/job-sheet-mutation.js +1 -0
  20. package/dist-server/service/job-sheet/job-sheet-mutation.js.map +1 -1
  21. package/dist-server/service/job-sheet/job-sheet-query.js +2 -0
  22. package/dist-server/service/job-sheet/job-sheet-query.js.map +1 -1
  23. package/dist-server/service/order-inventory/order-inventory.js +22 -1
  24. package/dist-server/service/order-inventory/order-inventory.js.map +1 -1
  25. package/dist-server/service/purchase-order/purchase-order.js +10 -1
  26. package/dist-server/service/purchase-order/purchase-order.js.map +1 -1
  27. package/dist-server/service/release-good/release-good-mutation.js +219 -212
  28. package/dist-server/service/release-good/release-good-mutation.js.map +1 -1
  29. package/dist-server/service/release-good/release-good-query.js +135 -99
  30. package/dist-server/service/release-good/release-good-query.js.map +1 -1
  31. package/dist-server/service/release-good/release-good-types.js +38 -2
  32. package/dist-server/service/release-good/release-good-types.js.map +1 -1
  33. package/dist-server/service/release-good/release-good.js +63 -1
  34. package/dist-server/service/release-good/release-good.js.map +1 -1
  35. package/dist-server/service/return-order/return-order-mutation.js +1 -1
  36. package/dist-server/service/return-order/return-order-mutation.js.map +1 -1
  37. package/dist-server/utils/inventory-util.js +89 -1
  38. package/dist-server/utils/inventory-util.js.map +1 -1
  39. package/package.json +12 -12
  40. package/server/controllers/ecommerce/ecommerce-controller.ts +15 -6
  41. package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
  42. package/server/controllers/order-controller.ts +55 -2
  43. package/server/service/arrival-notice/arrival-notice-mutation.ts +237 -1
  44. package/server/service/arrival-notice/arrival-notice-query.ts +214 -4
  45. package/server/service/arrival-notice/arrival-notice-types.ts +120 -1
  46. package/server/service/delivery-order/delivery-order-types.ts +1 -1
  47. package/server/service/delivery-order/delivery-order.ts +1 -1
  48. package/server/service/goods-receival-note/goods-receival-note.ts +4 -0
  49. package/server/service/job-sheet/job-sheet-mutation.ts +1 -0
  50. package/server/service/job-sheet/job-sheet-query.ts +3 -1
  51. package/server/service/order-inventory/order-inventory.ts +17 -1
  52. package/server/service/purchase-order/purchase-order.ts +8 -1
  53. package/server/service/release-good/release-good-mutation.ts +280 -283
  54. package/server/service/release-good/release-good-query.ts +158 -115
  55. package/server/service/release-good/release-good-types.ts +30 -2
  56. package/server/service/release-good/release-good.ts +46 -0
  57. package/server/service/return-order/return-order-mutation.ts +1 -1
  58. package/server/utils/index.ts +1 -1
  59. package/server/utils/inventory-util.ts +129 -1
@@ -1,12 +1,11 @@
1
1
  import { FileUpload, GraphQLUpload } from 'graphql-upload'
2
2
  import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
3
- import { EntityManager, getRepository, In, Not, Repository } from 'typeorm'
3
+ import { EntityManager, getConnection, getRepository, In, Not, Repository } from 'typeorm'
4
4
 
5
5
  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,
@@ -15,6 +14,7 @@ import {
15
14
  import { generateId } from '@things-factory/id-rule-base'
16
15
  import { MarketplaceStore } from '@things-factory/integration-marketplace'
17
16
  import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
17
+ import { MarketplaceOrder, MarketplaceProductVariation } from '@things-factory/marketplace-base'
18
18
  import { sendNotification } from '@things-factory/notification'
19
19
  import { Product, ProductBundleSetting } from '@things-factory/product-base'
20
20
  import { PartnerSetting, Setting } from '@things-factory/setting-base'
@@ -48,9 +48,100 @@ import { ValidationError } from '../../errors'
48
48
  import { InventoryUtil, OrderNoGenerator } from '../../utils'
49
49
  import { confirmArrivalNoticeFunction, deleteArrivalNotice } from '../arrival-notice/arrival-notice-mutation'
50
50
  import { ReleaseGood } from './release-good'
51
+ import { bulkReleaseGoodsAvailableItemsFunction } from './release-good-query'
51
52
 
52
53
  @Resolver(ReleaseGood)
53
54
  export class ReleaseGoodMutation {
55
+ @Directive('@privilege(category: "order_customer", privilege: "mutation")')
56
+ @Directive('@transaction')
57
+ @Mutation(returns => [ReleaseGood])
58
+ async bulkAddReleaseGoods(
59
+ @Ctx() context: any,
60
+ @Arg('rawReleaseGoods', type => [NewReleaseGood], { nullable: true }) rawReleaseGoods: NewReleaseGood[],
61
+ @Arg('bizplaceId', type => String) bizplaceId: string
62
+ ): Promise<ReleaseGood[]> {
63
+ const { domain, user, tx } = context.state
64
+
65
+ if (!bizplaceId) throw new Error('company ID is not provided')
66
+
67
+ let createdReleaseGoods: ReleaseGood[] = []
68
+ const settingRepo: Repository<Setting> = tx?.getRepository(Setting) || getRepository(Setting)
69
+
70
+ const roNoSetting: Setting = await settingRepo.findOne({
71
+ where: {
72
+ domain,
73
+ name: ORDER_NUMBER_SETTING_KEY.RO_NUMBER_RULE
74
+ }
75
+ })
76
+
77
+ let releaseGoods: Partial<ReleaseGood[]> = extractRawReleaseGoods(rawReleaseGoods)
78
+
79
+ let errorsFound: any[] = []
80
+ for (let i = 0, l = releaseGoods.length; i < l; i++) {
81
+ // generate release good by group to avoid duplication
82
+ // if this function is called simultaneously by different users
83
+ try {
84
+ await getConnection().transaction(async (childTx: EntityManager) => {
85
+ const existingReleaseGood: ReleaseGood = await childTx.getRepository(ReleaseGood).findOne({
86
+ where: {
87
+ ...Object.fromEntries(
88
+ Object.entries(releaseGoods[i]).filter(([_, val]) => !/^\s*$/.test(val) && _ != 'orderInventories')
89
+ ),
90
+ domain
91
+ }
92
+ })
93
+ if (existingReleaseGood) throw new Error('release good order is already exist in the system')
94
+
95
+ let availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(
96
+ [...releaseGoods[i].orderInventories],
97
+ bizplaceId,
98
+ context,
99
+ childTx
100
+ )
101
+
102
+ if (availableItems.some(item => !item.releaseQty || item.releaseQty > item.assignedQty))
103
+ throw new ValidationError({
104
+ ...ValidationError.ERROR_CODES.INSUFFICIENT_STOCK,
105
+ detail: { data: JSON.stringify(availableItems) }
106
+ })
107
+
108
+ // update orderInventories if availableItems are valid
109
+ releaseGoods[i].orderInventories = availableItems
110
+
111
+ const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGood(
112
+ releaseGoods[i],
113
+ bizplaceId,
114
+ roNoSetting,
115
+ domain,
116
+ user,
117
+ childTx
118
+ )
119
+
120
+ createdReleaseGoods.push(createdReleaseGood)
121
+ })
122
+ } catch (e) {
123
+ errorsFound.push(e)
124
+ }
125
+ }
126
+
127
+ let confirmedReleaseGoods: ReleaseGood[] = []
128
+ try {
129
+ confirmedReleaseGoods = await bulkConfirmReleaseGoods(
130
+ createdReleaseGoods.map(rg => rg.name),
131
+ domain,
132
+ user,
133
+ context,
134
+ tx
135
+ )
136
+ } catch (e) {
137
+ errorsFound.push(e)
138
+ }
139
+
140
+ // if (errorsFound.length) { then ?? }
141
+
142
+ return confirmedReleaseGoods
143
+ }
144
+
54
145
  @Directive('@privilege(category: "order_customer", privilege: "mutation")')
55
146
  @Directive('@transaction')
56
147
  @Mutation(returns => Boolean)
@@ -206,71 +297,6 @@ export class ReleaseGoodMutation {
206
297
 
207
298
  return receivedRO
208
299
  }
209
-
210
- @Directive('@transaction')
211
- @Mutation(returns => [ReleaseGood])
212
- async bulkAddReleaseGoods(
213
- @Ctx() context: any,
214
- @Arg('rawReleaseGoods', type => [NewReleaseGood], { nullable: true }) rawReleaseGoods: NewReleaseGood[],
215
- @Arg('bizplaceId', type => String) bizplaceId: string
216
- ): Promise<ReleaseGood[]> {
217
- const { domain, user, tx } = context.state
218
-
219
- if (!bizplaceId) throw new Error('company ID is not provided')
220
-
221
- let createdReleaseGoods: ReleaseGood[] = []
222
- const settingRepo: Repository<Setting> = tx?.getRepository(Setting) || getRepository(Setting)
223
-
224
- const roNoSetting: Setting = await settingRepo.findOne({
225
- where: {
226
- domain,
227
- name: ORDER_NUMBER_SETTING_KEY.RO_NUMBER_RULE
228
- }
229
- })
230
-
231
- let releaseGoods: Partial<ReleaseGood[]> = extractRawReleaseGoods(rawReleaseGoods)
232
-
233
- for (let i = 0, l = releaseGoods.length; i < l; i++) {
234
- // generate release good by group to avoid duplication
235
- // if this function is called simultaneously by different users
236
- let availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(
237
- [...releaseGoods[i].orderInventories],
238
- bizplaceId,
239
- context,
240
- tx
241
- )
242
-
243
- if (availableItems.some(item => !item.releaseQty || item.releaseQty > item.assignedQty))
244
- throw new ValidationError({
245
- ...ValidationError.ERROR_CODES.INSUFFICIENT_STOCK,
246
- detail: { data: JSON.stringify(availableItems) }
247
- })
248
-
249
- // update orderInventories if availableItems are valid
250
- releaseGoods[i].orderInventories = availableItems
251
-
252
- const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGoods(
253
- releaseGoods[i],
254
- bizplaceId,
255
- roNoSetting,
256
- domain,
257
- user,
258
- tx
259
- )
260
-
261
- createdReleaseGoods.push(createdReleaseGood)
262
- }
263
-
264
- let confirmedReleaseGoods: ReleaseGood[] = await bulkConfirmReleaseGoods(
265
- createdReleaseGoods.map(rg => rg.name),
266
- domain,
267
- user,
268
- context,
269
- tx
270
- )
271
-
272
- return confirmedReleaseGoods
273
- }
274
300
  }
275
301
 
276
302
  export async function deleteReleaseGood(tx: EntityManager, name: string, user: User, domain: Domain): Promise<boolean> {
@@ -433,10 +459,33 @@ export async function generateReleaseGoodFunction(
433
459
  domain: warehouseDomain,
434
460
  refNo,
435
461
  status: Not(In([ORDER_STATUS.CANCELLED, ORDER_STATUS.PENDING_CANCEL]))
436
- }
462
+ },
463
+ relations: ['bizplace', 'bizplace.domain', 'bizplace.company', 'bizplace.company.domain']
437
464
  })
438
465
 
439
- if (foundReleaseGood) throw new Error('Existing release order found')
466
+ if (foundReleaseGood) {
467
+ const customerCompanyDomain: Domain = foundReleaseGood.bizplace.company.domain
468
+ const marketplaceOrder: MarketplaceOrder = await tx.getRepository(MarketplaceOrder).findOne({
469
+ where: { orderNo: refNo, domain: customerCompanyDomain }
470
+ })
471
+
472
+ // Need to restructure the validation
473
+ if (marketplaceOrder.isSplitted) {
474
+ const refNo2: string = releaseGood.refNo2
475
+ const foundSplittedReleaseGood = await tx.getRepository(ReleaseGood).findOne({
476
+ where: {
477
+ domain: warehouseDomain,
478
+ refNo2,
479
+ status: Not(In([ORDER_STATUS.CANCELLED, ORDER_STATUS.PENDING_CANCEL]))
480
+ },
481
+ relations: ['bizplace', 'bizplace.domain', 'bizplace.company', 'bizplace.company.domain']
482
+ })
483
+
484
+ if (foundSplittedReleaseGood) throw new Error('Existing release order found')
485
+ } else {
486
+ throw new Error('Existing release order found')
487
+ }
488
+ }
440
489
  }
441
490
  }
442
491
 
@@ -503,6 +552,7 @@ export async function generateReleaseGoodFunction(
503
552
  refNo3: releaseGood.refNo3,
504
553
  releaseDate: releaseGood.releaseDate,
505
554
  truckNo: releaseGood.truckNo,
555
+ bundleInfo: releaseGood?.bundleInfo || [],
506
556
  orderInventories: releaseGood.orderInventories,
507
557
  type: releaseGood?.type ? releaseGood.type : 'b2b',
508
558
  status: ORDER_STATUS.PENDING,
@@ -521,8 +571,6 @@ export async function generateReleaseGoodFunction(
521
571
  newReleaseGood.arrivalNotice = crossDockingGAN
522
572
  }
523
573
 
524
- newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
525
-
526
574
  // Make relation with RO for cross docking
527
575
  if (newReleaseGood.crossDocking) {
528
576
  crossDockingGAN.releaseGood = newReleaseGood
@@ -572,6 +620,9 @@ export async function generateReleaseGoodFunction(
572
620
 
573
621
  orderInventories = [...singleProductOIs, ...splitBundleOIs]
574
622
 
623
+ newReleaseGood.noOfItems = orderInventories.length
624
+ newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
625
+
575
626
  for (let oi of orderInventories) {
576
627
  let newOrderInv: OrderInventory = Object.assign({}, oi)
577
628
  newOrderInv = {
@@ -683,193 +734,6 @@ export async function generateReleaseGoodFunction(
683
734
  }
684
735
  }
685
736
 
686
- export async function bulkReleaseGoodsAvailableItemsFunction(
687
- rawReleaseGoods: any[],
688
- bizplaceId: string,
689
- context: any,
690
- tx?: EntityManager
691
- ): Promise<any[]> {
692
- const { domain }: { domain: Domain } = context.state
693
- const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
694
-
695
- if (!rawReleaseGoods) return
696
-
697
- const json_oi = JSON.stringify(
698
- rawReleaseGoods.map(raw => {
699
- return {
700
- sku: raw.sku,
701
- packing_type: raw.packingType,
702
- packing_size: raw.packingSize,
703
- uom: raw.uom,
704
- release_qty: raw.releaseQty
705
- }
706
- })
707
- )
708
-
709
- await tx.query(
710
- `
711
- CREATE TEMP TABLE temp_order_products(
712
- product_id VARCHAR(50),
713
- sku VARCHAR(150),
714
- packing_type VARCHAR(50),
715
- packing_size INT,
716
- uom VARCHAR(10),
717
- release_qty INT
718
- );
719
- `
720
- )
721
-
722
- await tx.query(
723
- `
724
- INSERT INTO temp_order_products
725
- SELECT p.id AS product_id, js.sku,
726
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
727
- CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
728
- CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
729
- js.release_qty
730
- FROM JSON_POPULATE_RECORDSET(NULL::temp_order_products,'${json_oi}') js
731
- LEFT JOIN products p ON js.sku = p.sku AND p.bizplace_id = $1
732
- LEFT JOIN product_details pd ON p.id = pd.product_id AND pd.is_default = TRUE;
733
- `,
734
- [companyBizplaceId.id]
735
- )
736
-
737
- let availableItems = await tx.query(
738
- `
739
- SELECT i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom,
740
- SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
741
- (
742
- SELECT SUM(oi.release_qty) FROM order_inventories oi
743
- WHERE (
744
- oi.status = 'PENDING'
745
- OR oi.status = 'PENDING_RECEIVE'
746
- OR oi.status = 'PENDING_WORKSHEET'
747
- OR oi.status = 'PENDING_SPLIT'
748
- )
749
- AND oi.inventory_id IS null
750
- AND oi.product_id = i.product_id::uuid
751
- AND oi.packing_type = i.packing_type
752
- AND oi.uom = i.uom
753
- AND oi.domain_id = $1
754
- AND oi.bizplace_id = $2
755
- ), 0) as "remain_qty",
756
- SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
757
- (
758
- SELECT SUM(oi.release_uom_value) FROM order_inventories oi
759
- WHERE (
760
- oi.status = 'PENDING'
761
- OR oi.status = 'PENDING_RECEIVE'
762
- OR oi.status = 'PENDING_WORKSHEET'
763
- OR oi.status = 'PENDING_SPLIT'
764
- )
765
- AND oi.inventory_id IS null
766
- AND oi.product_id = i.product_id::uuid
767
- AND oi.packing_type = i.packing_type
768
- AND oi.uom = i.uom
769
- AND oi.domain_id = $1
770
- AND oi.bizplace_id = $2
771
- ), 0) as "remain_uom_value"
772
- FROM inventories i
773
- INNER JOIN (
774
- SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
775
- FROM temp_order_products top
776
- GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
777
- ) AS foo
778
- ON i.product_id = foo.product_id::uuid
779
- AND i.packing_type = foo.packing_type
780
- AND i.packing_size = foo.packing_size
781
- AND i.uom = foo.uom
782
- AND i.domain_id = $1
783
- AND i.bizplace_id = $2
784
- GROUP BY i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom
785
- ORDER BY foo.sku, remain_qty
786
- `,
787
- [domain.id, bizplaceId]
788
- )
789
-
790
- await tx.query('DROP TABLE temp_order_products')
791
-
792
- availableItems = availableItems.map(item => {
793
- return {
794
- productId: item.product_id,
795
- sku: item.sku,
796
- batchId: item.batch_id,
797
- packingType: item.packing_type,
798
- packingSize: item.packing_size,
799
- uom: item.uom,
800
- remainQty: item.remain_qty,
801
- remainUomValue: item.remain_uom_value
802
- }
803
- })
804
-
805
- return _extractData(rawReleaseGoods, availableItems)
806
- }
807
-
808
- function _extractData(rawData, validatedData) {
809
- return rawData.map(raw => {
810
- const idx = validatedData.findIndex(val => {
811
- const comparison = ['packingType', 'packingSize', 'uom']
812
-
813
- let a: any = {},
814
- b: any = {}
815
-
816
- comparison.forEach(cc => {
817
- if (raw[cc]) {
818
- a[cc] = raw[cc]
819
- b[cc] = val[cc]
820
- }
821
- })
822
-
823
- a = JSON.stringify(Object.fromEntries(Object.entries(a).sort()))
824
- b = JSON.stringify(Object.fromEntries(Object.entries(b).sort()))
825
-
826
- return val.sku == raw.sku && a === b
827
- })
828
-
829
- let releaseUomValue = 0
830
-
831
- // if sku is matched, assign qty and product id
832
- if (idx >= 0) {
833
- // assign qty to rawData as much as possible
834
- releaseUomValue =
835
- (Math.round((validatedData[idx]?.remainUomValue / validatedData[idx]?.remainQty) * 100) / 100) * raw.releaseQty
836
-
837
- raw.assignedQty =
838
- validatedData[idx].remainQty >= raw.releaseQty
839
- ? raw.releaseQty > 0
840
- ? raw.releaseQty
841
- : 0
842
- : validatedData[idx].remainQty
843
-
844
- raw.assignedUomValue =
845
- validatedData[idx].remainUomValue >= releaseUomValue
846
- ? releaseUomValue > 0
847
- ? releaseUomValue
848
- : 0
849
- : validatedData[idx].remainUomValue
850
-
851
- // deduct qty & uomValue from validateData
852
- validatedData[idx].remainQty -= raw.assignedQty
853
- validatedData[idx].remainUomValue -= raw.assignedUomValue
854
-
855
- raw.productId = validatedData[idx].productId
856
- raw.packingType = validatedData[idx].packingType
857
- raw.packingSize = validatedData[idx].packingSize
858
- raw.uom = validatedData[idx].uom
859
- raw.batchId = validatedData[idx].batchId
860
- } else {
861
- raw.assignedQty = 0
862
- raw.assignedUomValue = 0
863
- raw.productId = null
864
- }
865
-
866
- return {
867
- ...raw,
868
- releaseUomValue
869
- }
870
- })
871
- }
872
-
873
737
  export async function confirmReleaseGood(name: string, context: any, tx?: EntityManager): Promise<ReleaseGood> {
874
738
  const { user, domain }: { user: User; domain: Domain } = context.state
875
739
 
@@ -883,6 +747,7 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
883
747
  'bizplace.company.domain',
884
748
  'orderProducts',
885
749
  'orderProducts.product',
750
+ 'orderProducts.product.productDetails',
886
751
  'orderInventories',
887
752
  'orderInventories.product',
888
753
  'orderVass'
@@ -891,26 +756,77 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
891
756
 
892
757
  if (!foundReleaseGood) throw new Error(`Release good order doesn't exists.`)
893
758
  let foundOIs: OrderInventory[] = foundReleaseGood.orderInventories
759
+ let foundBundleInfo: any[] = JSON.parse(foundReleaseGood.bundleInfo)
894
760
  let foundOVs: OrderVas[] = foundReleaseGood.orderVass
761
+ let foundOPs: OrderProduct[] = foundReleaseGood.orderProducts
895
762
  let customerBizplace: Bizplace = foundReleaseGood.bizplace
896
763
  const companyDomain: Domain = customerBizplace?.company.domain
764
+ const sellercraft: Sellercraft = await tx
765
+ .getRepository(Sellercraft)
766
+ .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
897
767
 
898
768
  if (foundReleaseGood.type == 'b2c') {
899
- const sellercraft: Sellercraft = await tx
900
- .getRepository(Sellercraft)
901
- .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
902
-
903
769
  if (sellercraft) {
904
770
  const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
905
771
  foundReleaseGood = await sellercraftCtrl.packOrder(sellercraft, foundReleaseGood)
906
772
  } else {
907
773
  // find for any existing marketplace store connections
908
- const marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
909
- where: { domain: companyDomain, status: 'ACTIVE', isAutoUpdateStockQty: true },
774
+ let marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
775
+ where: { domain: companyDomain, status: 'ACTIVE' },
910
776
  relations: ['marketplaceDistributors']
911
777
  })
912
778
 
779
+ const foundMarketplaceOrder: MarketplaceOrder = await tx.getRepository(MarketplaceOrder).findOne({
780
+ where: { domain: companyDomain, orderNo: foundReleaseGood.refNo },
781
+ relations: ['marketplaceStore']
782
+ })
783
+
784
+ let matchedMarketplaceStore: MarketplaceStore
785
+ if (foundMarketplaceOrder) {
786
+ matchedMarketplaceStore = marketplaceStores.filter(
787
+ marketplaceStore => marketplaceStore.id == foundMarketplaceOrder.marketplaceStore.id
788
+ )[0]
789
+ }
790
+
791
+ let combinedOrderInventories: any[] = foundOIs.map(oi => {
792
+ return {
793
+ sku: oi.product.sku,
794
+ releaseQty: oi.releaseQty
795
+ }
796
+ })
797
+
798
+ if (foundBundleInfo?.length) {
799
+ foundBundleInfo.map(bundle => {
800
+ combinedOrderInventories.push({
801
+ sku: bundle.sku,
802
+ releaseQty: bundle.releaseQty
803
+ })
804
+ })
805
+ }
806
+
807
+ for (let oi of combinedOrderInventories) {
808
+ let foundMarketplaceProductVariations: MarketplaceProductVariation[] = await tx
809
+ .getRepository(MarketplaceProductVariation)
810
+ .find({
811
+ where: { domain: companyDomain, sku: oi.sku },
812
+ relations: ['marketplaceProduct', 'marketplaceProduct.marketplaceStore']
813
+ })
814
+
815
+ if (foundMarketplaceProductVariations) {
816
+ await Promise.all(
817
+ foundMarketplaceProductVariations.map(async variation => {
818
+ if (variation.marketplaceProduct.marketplaceStore.id != matchedMarketplaceStore.id) {
819
+ variation.reserveQty -= oi.releaseQty
820
+
821
+ await tx.getRepository(MarketplaceProductVariation).save(variation)
822
+ }
823
+ })
824
+ )
825
+ }
826
+ }
827
+
913
828
  if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
829
+ marketplaceStores = marketplaceStores.filter(marketplaceStore => marketplaceStore.isAutoUpdateStockQty)
914
830
  let productIds: string[] = foundOIs.map(oi => oi.product.id)
915
831
  productIds = Array.from(new Set([...productIds]))
916
832
  const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
@@ -919,6 +835,13 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
919
835
  }
920
836
  }
921
837
 
838
+ if (foundReleaseGood.type == 'b2b') {
839
+ if (sellercraft) {
840
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
841
+ await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'CONFIRM_ORDER')
842
+ }
843
+ }
844
+
922
845
  // 1. RO Status change (PENDING => PENDING_RECEIVE)
923
846
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).save({
924
847
  ...foundReleaseGood,
@@ -1079,7 +1002,17 @@ export async function rejectReleaseGood(
1079
1002
 
1080
1003
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
1081
1004
  where: { domain, name, status: ORDER_STATUS.PENDING_RECEIVE },
1082
- relations: ['bizplace', 'orderInventories', 'orderInventories.inventory', 'orderVass', 'shippingOrder']
1005
+ relations: [
1006
+ 'bizplace',
1007
+ 'bizplace.domain',
1008
+ 'orderProducts',
1009
+ 'orderProducts.product',
1010
+ 'orderProducts.product.productDetails',
1011
+ 'orderInventories',
1012
+ 'orderInventories.inventory',
1013
+ 'orderVass',
1014
+ 'shippingOrder'
1015
+ ]
1083
1016
  })
1084
1017
 
1085
1018
  if (!releaseGood) throw new Error(`Release good doesn't exists.`)
@@ -1087,6 +1020,7 @@ export async function rejectReleaseGood(
1087
1020
 
1088
1021
  let foundOIs: OrderInventory[] = releaseGood.orderInventories
1089
1022
  let foundOVs: OrderVas[] = releaseGood.orderVass
1023
+ let foundOPs: OrderProduct[] = releaseGood.orderProducts
1090
1024
 
1091
1025
  // 1. Update status of order products (PENDING_RECEIVE => REJECTED)
1092
1026
  if (foundOIs && foundOIs.length) {
@@ -1112,6 +1046,27 @@ export async function rejectReleaseGood(
1112
1046
  )
1113
1047
  }
1114
1048
 
1049
+ if (foundOPs && foundOPs.length) {
1050
+ const sellercraft: Sellercraft = await tx
1051
+ .getRepository(Sellercraft)
1052
+ .findOne({ domain: releaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
1053
+
1054
+ if (sellercraft) {
1055
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
1056
+ await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'REJECT_ORDER')
1057
+ }
1058
+
1059
+ await tx.getRepository(OrderProduct).save(
1060
+ await Promise.all(
1061
+ foundOPs.map(async (op: OrderProduct) => {
1062
+ op.status = ORDER_PRODUCT_STATUS.REJECTED
1063
+ op.updater = user
1064
+ return op
1065
+ })
1066
+ )
1067
+ )
1068
+ }
1069
+
1115
1070
  // 2. Update status of order vass if it exists (PENDING_RECEIVE => REJECTED)
1116
1071
  if (foundOVs && foundOVs.length) {
1117
1072
  foundOVs = foundOVs.map((ov: OrderVas) => {
@@ -1159,7 +1114,7 @@ export async function rejectReleaseGood(
1159
1114
  return releaseGood
1160
1115
  }
1161
1116
 
1162
- export async function bulkGenerateReleaseGoods(
1117
+ export async function bulkGenerateReleaseGood(
1163
1118
  releaseGood: any,
1164
1119
  bizplaceId: string,
1165
1120
  roNoSetting: any,
@@ -1184,7 +1139,6 @@ export async function bulkGenerateReleaseGoods(
1184
1139
  deliveryAddress2: releaseGood?.deliveryAddress2 || null,
1185
1140
  deliveryAddress3: releaseGood?.deliveryAddress3 || null,
1186
1141
  deliveryAddress4: releaseGood?.deliveryAddress4 || null,
1187
- deliveryAddress5: releaseGood?.deliveryAddress5 || null,
1188
1142
  attentionTo: releaseGood?.attentionTo || null,
1189
1143
  attentionCompany: releaseGood?.attentionCompany || null,
1190
1144
  city: releaseGood?.city || null,
@@ -1206,40 +1160,82 @@ export async function bulkGenerateReleaseGoods(
1206
1160
  }
1207
1161
 
1208
1162
  newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
1163
+ let finalOrderInventories: OrderInventory[] = []
1209
1164
 
1165
+ // pre assign inventory to orderInventories
1210
1166
  for (let oi of orderInventories) {
1211
- delete oi.sku
1167
+ const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
1168
+ where: { domain, name: 'rule-for-picking-product' }
1169
+ })
1212
1170
 
1213
- let newOrderInv: OrderInventory = Object.assign(new OrderInventory(), oi)
1214
- newOrderInv = {
1215
- ...newOrderInv,
1171
+ let locationSortingRules = []
1172
+ if (pickingProductSetting) {
1173
+ let locationSetting = JSON.parse(pickingProductSetting.value)
1174
+ for (const key in locationSetting) {
1175
+ locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
1176
+ }
1177
+ }
1178
+
1179
+ const product: Product = await tx.getRepository(Product).findOne(oi.productId)
1180
+
1181
+ finalOrderInventories.push(
1182
+ ...(await InventoryUtil.autoAssignInventoryForRelease(
1183
+ product,
1184
+ oi,
1185
+ oi.packingType,
1186
+ locationSortingRules,
1187
+ bizplace,
1188
+ warehouseDomain,
1189
+ tx
1190
+ ))
1191
+ )
1192
+ }
1193
+
1194
+ for (let oi of finalOrderInventories) {
1195
+ let generatedOI: OrderInventory = Object.assign(new OrderInventory(), oi)
1196
+ generatedOI = {
1197
+ ...generatedOI,
1216
1198
  domain: warehouseDomain,
1217
- bizplace: bizplace,
1199
+ bizplace,
1218
1200
  status: ORDER_INVENTORY_STATUS.PENDING,
1219
1201
  name: OrderNoGenerator.orderInventory(),
1220
- batchId: oi?.batchId || '',
1221
1202
  releaseGood: newReleaseGood,
1222
- product: await tx.getRepository(Product).findOne(oi.productId),
1223
1203
  creator: user,
1224
1204
  updater: user
1225
1205
  }
1226
1206
 
1227
- let newOrderProduct: OrderProduct = Object.assign(new OrderProduct(), newOrderInv)
1228
- newOrderProduct = {
1229
- ...newOrderProduct,
1207
+ let newOrderProduct: Partial<OrderProduct> = {
1208
+ name: generatedOI.name,
1209
+ product: generatedOI.product,
1210
+ batchId: generatedOI.batchId,
1211
+ packingType: generatedOI.packingType,
1212
+ packingSize: generatedOI.packingSize,
1213
+ uom: generatedOI.uom,
1214
+ domain: warehouseDomain,
1215
+ bizplace,
1216
+ releaseQty: generatedOI.releaseQty,
1217
+ releaseUomValue: generatedOI.releaseUomValue,
1230
1218
  packQty: 0,
1231
1219
  actualPackQty: 0,
1232
1220
  palletQty: 0,
1233
1221
  actualPalletQty: 0,
1234
- status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN
1222
+ status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN,
1223
+ releaseGood: newReleaseGood,
1224
+ creator: user,
1225
+ updater: user
1235
1226
  }
1236
1227
 
1237
1228
  newOrderProduct = await tx.getRepository(OrderProduct).save(newOrderProduct)
1238
1229
 
1239
- await tx.getRepository(OrderInventory).save({ ...newOrderInv, orderProduct: newOrderProduct })
1240
- }
1230
+ await tx.getRepository(OrderInventory).save({ ...generatedOI, orderProduct: newOrderProduct })
1241
1231
 
1242
- // Change the status to PENDING_RECEIVE
1232
+ // update inventory locked qty and uom value
1233
+ await tx.getRepository(Inventory).update(oi.inventory.id, {
1234
+ lockedQty: (oi.inventory?.lockedQty || 0) + generatedOI.releaseQty,
1235
+ lockedUomValue: (oi.inventory?.lockedUomValue || 0) + generatedOI.releaseUomValue,
1236
+ updater: user
1237
+ })
1238
+ }
1243
1239
 
1244
1240
  return newReleaseGood
1245
1241
  } catch (error) {
@@ -1270,7 +1266,7 @@ export async function bulkConfirmReleaseGoods(
1270
1266
  ]
1271
1267
  })
1272
1268
 
1273
- if (!foundReleaseGoods.length) throw new Error(`Release good order doesn't exists.`)
1269
+ if (!foundReleaseGoods.length) throw new Error(`release good order doesn't exists.`)
1274
1270
  let customerBizplace: Bizplace = foundReleaseGoods[0].bizplace
1275
1271
 
1276
1272
  let foundOrderInventories: OrderInventory[] = foundReleaseGoods
@@ -1333,7 +1329,7 @@ export async function bulkConfirmReleaseGoods(
1333
1329
  })
1334
1330
  }
1335
1331
 
1336
- export function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
1332
+ function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
1337
1333
  return rawReleaseGoods.reduce((releaseGoods, item) => {
1338
1334
  const idx: number = releaseGoods.findIndex(rg => {
1339
1335
  // consider these attributes if they are exist in "item"
@@ -1370,6 +1366,7 @@ export function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]>
1370
1366
  oi => oi.sku === item.sku && oi.packingType === item.packingType && oi.packingSize === item.packingSize
1371
1367
  )
1372
1368
 
1369
+ // if there is duplicated SKU, merge them and sum up the releaseQty
1373
1370
  if (duplicateSkuIdx >= 0) {
1374
1371
  releaseGoods[idx].orderInventories[duplicateSkuIdx].releaseQty += item.releaseQty
1375
1372
  } else {