@things-factory/sales-base 4.0.21 → 4.0.25

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 (44) 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/goods-receival-note/goods-receival-note.js +5 -0
  16. package/dist-server/service/goods-receival-note/goods-receival-note.js.map +1 -1
  17. package/dist-server/service/order-inventory/order-inventory.js +22 -1
  18. package/dist-server/service/order-inventory/order-inventory.js.map +1 -1
  19. package/dist-server/service/release-good/release-good-mutation.js +242 -81
  20. package/dist-server/service/release-good/release-good-mutation.js.map +1 -1
  21. package/dist-server/service/release-good/release-good-types.js +8 -0
  22. package/dist-server/service/release-good/release-good-types.js.map +1 -1
  23. package/dist-server/service/release-good/release-good.js +4 -0
  24. package/dist-server/service/release-good/release-good.js.map +1 -1
  25. package/dist-server/service/return-order/return-order-mutation.js +1 -1
  26. package/dist-server/service/return-order/return-order-mutation.js.map +1 -1
  27. package/dist-server/utils/inventory-util.js +89 -1
  28. package/dist-server/utils/inventory-util.js.map +1 -1
  29. package/package.json +12 -12
  30. package/server/controllers/ecommerce/ecommerce-controller.ts +15 -6
  31. package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
  32. package/server/controllers/order-controller.ts +55 -2
  33. package/server/service/arrival-notice/arrival-notice-mutation.ts +237 -1
  34. package/server/service/arrival-notice/arrival-notice-query.ts +214 -4
  35. package/server/service/arrival-notice/arrival-notice-types.ts +120 -1
  36. package/server/service/delivery-order/delivery-order-types.ts +1 -1
  37. package/server/service/goods-receival-note/goods-receival-note.ts +4 -0
  38. package/server/service/order-inventory/order-inventory.ts +17 -1
  39. package/server/service/release-good/release-good-mutation.ts +310 -119
  40. package/server/service/release-good/release-good-types.ts +6 -0
  41. package/server/service/release-good/release-good.ts +3 -0
  42. package/server/service/return-order/return-order-mutation.ts +1 -1
  43. package/server/utils/index.ts +1 -1
  44. package/server/utils/inventory-util.ts +129 -1
@@ -1,6 +1,6 @@
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'
@@ -15,11 +15,12 @@ import {
15
15
  import { generateId } from '@things-factory/id-rule-base'
16
16
  import { MarketplaceStore } from '@things-factory/integration-marketplace'
17
17
  import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
18
+ import { MarketplaceOrder, MarketplaceProductVariation } from '@things-factory/marketplace-base'
18
19
  import { sendNotification } from '@things-factory/notification'
19
20
  import { Product, ProductBundleSetting } from '@things-factory/product-base'
20
21
  import { PartnerSetting, Setting } from '@things-factory/setting-base'
21
22
  import { Domain } from '@things-factory/shell'
22
- import { Inventory } from '@things-factory/warehouse-base'
23
+ import { Inventory, LOCATION_TYPE } from '@things-factory/warehouse-base'
23
24
 
24
25
  import {
25
26
  ArrivalNotice,
@@ -51,6 +52,96 @@ import { ReleaseGood } from './release-good'
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 bulkGenerateReleaseGoods(
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 = {
@@ -711,6 +762,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
711
762
  CREATE TEMP TABLE temp_order_products(
712
763
  product_id VARCHAR(50),
713
764
  sku VARCHAR(150),
765
+ product_info VARCHAR(250),
714
766
  packing_type VARCHAR(50),
715
767
  packing_size INT,
716
768
  uom VARCHAR(10),
@@ -723,6 +775,8 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
723
775
  `
724
776
  INSERT INTO temp_order_products
725
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,
726
780
  CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
727
781
  CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
728
782
  CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
@@ -736,16 +790,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
736
790
 
737
791
  let availableItems = await tx.query(
738
792
  `
739
- SELECT i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom,
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,
740
795
  SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
741
796
  (
742
797
  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
- )
798
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
749
799
  AND oi.inventory_id IS null
750
800
  AND oi.product_id = i.product_id::uuid
751
801
  AND oi.packing_type = i.packing_type
@@ -756,12 +806,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
756
806
  SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
757
807
  (
758
808
  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
- )
809
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
765
810
  AND oi.inventory_id IS null
766
811
  AND oi.product_id = i.product_id::uuid
767
812
  AND oi.packing_type = i.packing_type
@@ -770,10 +815,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
770
815
  AND oi.bizplace_id = $2
771
816
  ), 0) as "remain_uom_value"
772
817
  FROM inventories i
818
+ LEFT JOIN locations l
819
+ ON i.location_id = l.id
773
820
  INNER JOIN (
774
- SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
821
+ SELECT top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
775
822
  FROM temp_order_products top
776
- GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
823
+ GROUP BY top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
777
824
  ) AS foo
778
825
  ON i.product_id = foo.product_id::uuid
779
826
  AND i.packing_type = foo.packing_type
@@ -781,10 +828,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
781
828
  AND i.uom = foo.uom
782
829
  AND i.domain_id = $1
783
830
  AND i.bizplace_id = $2
784
- GROUP BY i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom
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
785
833
  ORDER BY foo.sku, remain_qty
786
- `,
787
- [domain.id, bizplaceId]
834
+ ) SELECT * FROM inv WHERE remain_qty > 0
835
+ `,
836
+ [domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
788
837
  )
789
838
 
790
839
  await tx.query('DROP TABLE temp_order_products')
@@ -792,6 +841,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
792
841
  availableItems = availableItems.map(item => {
793
842
  return {
794
843
  productId: item.product_id,
844
+ productInfo: item.product_info,
795
845
  sku: item.sku,
796
846
  batchId: item.batch_id,
797
847
  packingType: item.packing_type,
@@ -826,33 +876,41 @@ function _extractData(rawData, validatedData) {
826
876
  return val.sku == raw.sku && a === b
827
877
  })
828
878
 
829
- let releaseUomValue = 0
879
+ let releaseUomValue: number = 0
830
880
 
831
881
  // if sku is matched, assign qty and product id
832
882
  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
883
+ const uomValuePerQty: number =
884
+ Math.round((validatedData[idx].remainUomValue / validatedData[idx].remainQty) * 100) / 100
836
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
837
890
  raw.assignedQty =
838
891
  validatedData[idx].remainQty >= raw.releaseQty
839
892
  ? raw.releaseQty > 0
840
893
  ? raw.releaseQty
841
894
  : 0
842
- : validatedData[idx].remainQty
895
+ : validatedData[idx].remainQty > 0
896
+ ? validatedData[idx].remainQty
897
+ : 0
843
898
 
844
899
  raw.assignedUomValue =
845
900
  validatedData[idx].remainUomValue >= releaseUomValue
846
901
  ? releaseUomValue > 0
847
902
  ? releaseUomValue
848
903
  : 0
849
- : validatedData[idx].remainUomValue
904
+ : validatedData[idx].remainUomValue > 0
905
+ ? validatedData[idx].remainUomValue
906
+ : 0
850
907
 
851
908
  // deduct qty & uomValue from validateData
852
909
  validatedData[idx].remainQty -= raw.assignedQty
853
910
  validatedData[idx].remainUomValue -= raw.assignedUomValue
854
911
 
855
912
  raw.productId = validatedData[idx].productId
913
+ raw.productInfo = validatedData[idx].productInfo
856
914
  raw.packingType = validatedData[idx].packingType
857
915
  raw.packingSize = validatedData[idx].packingSize
858
916
  raw.uom = validatedData[idx].uom
@@ -883,6 +941,7 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
883
941
  'bizplace.company.domain',
884
942
  'orderProducts',
885
943
  'orderProducts.product',
944
+ 'orderProducts.product.productDetails',
886
945
  'orderInventories',
887
946
  'orderInventories.product',
888
947
  'orderVass'
@@ -891,26 +950,77 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
891
950
 
892
951
  if (!foundReleaseGood) throw new Error(`Release good order doesn't exists.`)
893
952
  let foundOIs: OrderInventory[] = foundReleaseGood.orderInventories
953
+ let foundBundleInfo: any[] = JSON.parse(foundReleaseGood.bundleInfo)
894
954
  let foundOVs: OrderVas[] = foundReleaseGood.orderVass
955
+ let foundOPs: OrderProduct[] = foundReleaseGood.orderProducts
895
956
  let customerBizplace: Bizplace = foundReleaseGood.bizplace
896
957
  const companyDomain: Domain = customerBizplace?.company.domain
958
+ const sellercraft: Sellercraft = await tx
959
+ .getRepository(Sellercraft)
960
+ .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
897
961
 
898
962
  if (foundReleaseGood.type == 'b2c') {
899
- const sellercraft: Sellercraft = await tx
900
- .getRepository(Sellercraft)
901
- .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
902
-
903
963
  if (sellercraft) {
904
964
  const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
905
965
  foundReleaseGood = await sellercraftCtrl.packOrder(sellercraft, foundReleaseGood)
906
966
  } else {
907
967
  // find for any existing marketplace store connections
908
- const marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
909
- where: { domain: companyDomain, status: 'ACTIVE', isAutoUpdateStockQty: true },
968
+ let marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
969
+ where: { domain: companyDomain, status: 'ACTIVE' },
910
970
  relations: ['marketplaceDistributors']
911
971
  })
912
972
 
973
+ const foundMarketplaceOrder: MarketplaceOrder = await tx.getRepository(MarketplaceOrder).findOne({
974
+ where: { domain: companyDomain, orderNo: foundReleaseGood.refNo },
975
+ relations: ['marketplaceStore']
976
+ })
977
+
978
+ let matchedMarketplaceStore: MarketplaceStore
979
+ if (foundMarketplaceOrder) {
980
+ matchedMarketplaceStore = marketplaceStores.filter(
981
+ marketplaceStore => marketplaceStore.id != foundMarketplaceOrder.marketplaceStore.id
982
+ )[0]
983
+ }
984
+
985
+ let combinedOrderInventories: any[] = foundOIs.map(oi => {
986
+ return {
987
+ sku: oi.product.sku,
988
+ releaseQty: oi.releaseQty
989
+ }
990
+ })
991
+
992
+ if (foundBundleInfo?.length) {
993
+ foundBundleInfo.map(bundle => {
994
+ combinedOrderInventories.push({
995
+ sku: bundle.sku,
996
+ releaseQty: bundle.releaseQty
997
+ })
998
+ })
999
+ }
1000
+
1001
+ for (let oi of combinedOrderInventories) {
1002
+ let foundMarketplaceProductVariations: MarketplaceProductVariation[] = await tx
1003
+ .getRepository(MarketplaceProductVariation)
1004
+ .find({
1005
+ where: { domain: companyDomain, sku: oi.sku },
1006
+ relations: ['marketplaceProduct', 'marketplaceProduct.marketplaceStore']
1007
+ })
1008
+
1009
+ if (foundMarketplaceProductVariations) {
1010
+ await Promise.all(
1011
+ foundMarketplaceProductVariations.map(async variation => {
1012
+ if (variation.marketplaceProduct.marketplaceStore.id != matchedMarketplaceStore.id) {
1013
+ variation.reserveQty -= oi.releaseQty
1014
+
1015
+ await tx.getRepository(MarketplaceProductVariation).save(variation)
1016
+ }
1017
+ })
1018
+ )
1019
+ }
1020
+ }
1021
+
913
1022
  if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
1023
+ marketplaceStores = marketplaceStores.filter(marketplaceStore => marketplaceStore.isAutoUpdateStockQty)
914
1024
  let productIds: string[] = foundOIs.map(oi => oi.product.id)
915
1025
  productIds = Array.from(new Set([...productIds]))
916
1026
  const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
@@ -919,6 +1029,13 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
919
1029
  }
920
1030
  }
921
1031
 
1032
+ if (foundReleaseGood.type == 'b2b') {
1033
+ if (sellercraft) {
1034
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
1035
+ await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'CONFIRM_ORDER')
1036
+ }
1037
+ }
1038
+
922
1039
  // 1. RO Status change (PENDING => PENDING_RECEIVE)
923
1040
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).save({
924
1041
  ...foundReleaseGood,
@@ -1079,7 +1196,17 @@ export async function rejectReleaseGood(
1079
1196
 
1080
1197
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
1081
1198
  where: { domain, name, status: ORDER_STATUS.PENDING_RECEIVE },
1082
- relations: ['bizplace', 'orderInventories', 'orderInventories.inventory', 'orderVass', 'shippingOrder']
1199
+ relations: [
1200
+ 'bizplace',
1201
+ 'bizplace.domain',
1202
+ 'orderProducts',
1203
+ 'orderProducts.product',
1204
+ 'orderProducts.product.productDetails',
1205
+ 'orderInventories',
1206
+ 'orderInventories.inventory',
1207
+ 'orderVass',
1208
+ 'shippingOrder'
1209
+ ]
1083
1210
  })
1084
1211
 
1085
1212
  if (!releaseGood) throw new Error(`Release good doesn't exists.`)
@@ -1087,6 +1214,7 @@ export async function rejectReleaseGood(
1087
1214
 
1088
1215
  let foundOIs: OrderInventory[] = releaseGood.orderInventories
1089
1216
  let foundOVs: OrderVas[] = releaseGood.orderVass
1217
+ let foundOPs: OrderProduct[] = releaseGood.orderProducts
1090
1218
 
1091
1219
  // 1. Update status of order products (PENDING_RECEIVE => REJECTED)
1092
1220
  if (foundOIs && foundOIs.length) {
@@ -1112,6 +1240,27 @@ export async function rejectReleaseGood(
1112
1240
  )
1113
1241
  }
1114
1242
 
1243
+ if (foundOPs && foundOPs.length) {
1244
+ const sellercraft: Sellercraft = await tx
1245
+ .getRepository(Sellercraft)
1246
+ .findOne({ domain: releaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
1247
+
1248
+ if (sellercraft) {
1249
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
1250
+ await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'REJECT_ORDER')
1251
+ }
1252
+
1253
+ await tx.getRepository(OrderProduct).save(
1254
+ await Promise.all(
1255
+ foundOPs.map(async (op: OrderProduct) => {
1256
+ op.status = ORDER_PRODUCT_STATUS.REJECTED
1257
+ op.updater = user
1258
+ return op
1259
+ })
1260
+ )
1261
+ )
1262
+ }
1263
+
1115
1264
  // 2. Update status of order vass if it exists (PENDING_RECEIVE => REJECTED)
1116
1265
  if (foundOVs && foundOVs.length) {
1117
1266
  foundOVs = foundOVs.map((ov: OrderVas) => {
@@ -1184,7 +1333,6 @@ export async function bulkGenerateReleaseGoods(
1184
1333
  deliveryAddress2: releaseGood?.deliveryAddress2 || null,
1185
1334
  deliveryAddress3: releaseGood?.deliveryAddress3 || null,
1186
1335
  deliveryAddress4: releaseGood?.deliveryAddress4 || null,
1187
- deliveryAddress5: releaseGood?.deliveryAddress5 || null,
1188
1336
  attentionTo: releaseGood?.attentionTo || null,
1189
1337
  attentionCompany: releaseGood?.attentionCompany || null,
1190
1338
  city: releaseGood?.city || null,
@@ -1206,40 +1354,82 @@ export async function bulkGenerateReleaseGoods(
1206
1354
  }
1207
1355
 
1208
1356
  newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
1357
+ let finalOrderInventories: OrderInventory[] = []
1209
1358
 
1359
+ // pre assign inventory to orderInventories
1210
1360
  for (let oi of orderInventories) {
1211
- delete oi.sku
1361
+ const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
1362
+ where: { domain, name: 'rule-for-picking-product' }
1363
+ })
1212
1364
 
1213
- let newOrderInv: OrderInventory = Object.assign(new OrderInventory(), oi)
1214
- newOrderInv = {
1215
- ...newOrderInv,
1365
+ let locationSortingRules = []
1366
+ if (pickingProductSetting) {
1367
+ let locationSetting = JSON.parse(pickingProductSetting.value)
1368
+ for (const key in locationSetting) {
1369
+ locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
1370
+ }
1371
+ }
1372
+
1373
+ const product: Product = await tx.getRepository(Product).findOne(oi.productId)
1374
+
1375
+ finalOrderInventories.push(
1376
+ ...(await InventoryUtil.autoAssignInventoryForRelease(
1377
+ product,
1378
+ oi,
1379
+ oi.packingType,
1380
+ locationSortingRules,
1381
+ bizplace,
1382
+ warehouseDomain,
1383
+ tx
1384
+ ))
1385
+ )
1386
+ }
1387
+
1388
+ for (let oi of finalOrderInventories) {
1389
+ let generatedOI: OrderInventory = Object.assign(new OrderInventory(), oi)
1390
+ generatedOI = {
1391
+ ...generatedOI,
1216
1392
  domain: warehouseDomain,
1217
- bizplace: bizplace,
1393
+ bizplace,
1218
1394
  status: ORDER_INVENTORY_STATUS.PENDING,
1219
1395
  name: OrderNoGenerator.orderInventory(),
1220
- batchId: oi?.batchId || '',
1221
1396
  releaseGood: newReleaseGood,
1222
- product: await tx.getRepository(Product).findOne(oi.productId),
1223
1397
  creator: user,
1224
1398
  updater: user
1225
1399
  }
1226
1400
 
1227
- let newOrderProduct: OrderProduct = Object.assign(new OrderProduct(), newOrderInv)
1228
- newOrderProduct = {
1229
- ...newOrderProduct,
1401
+ let newOrderProduct: Partial<OrderProduct> = {
1402
+ name: generatedOI.name,
1403
+ product: generatedOI.product,
1404
+ batchId: generatedOI.batchId,
1405
+ packingType: generatedOI.packingType,
1406
+ packingSize: generatedOI.packingSize,
1407
+ uom: generatedOI.uom,
1408
+ domain: warehouseDomain,
1409
+ bizplace,
1410
+ releaseQty: generatedOI.releaseQty,
1411
+ releaseUomValue: generatedOI.releaseUomValue,
1230
1412
  packQty: 0,
1231
1413
  actualPackQty: 0,
1232
1414
  palletQty: 0,
1233
1415
  actualPalletQty: 0,
1234
- status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN
1416
+ status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN,
1417
+ releaseGood: newReleaseGood,
1418
+ creator: user,
1419
+ updater: user
1235
1420
  }
1236
1421
 
1237
1422
  newOrderProduct = await tx.getRepository(OrderProduct).save(newOrderProduct)
1238
1423
 
1239
- await tx.getRepository(OrderInventory).save({ ...newOrderInv, orderProduct: newOrderProduct })
1240
- }
1424
+ await tx.getRepository(OrderInventory).save({ ...generatedOI, orderProduct: newOrderProduct })
1241
1425
 
1242
- // Change the status to PENDING_RECEIVE
1426
+ // update inventory locked qty and uom value
1427
+ await tx.getRepository(Inventory).update(oi.inventory.id, {
1428
+ lockedQty: (oi.inventory?.lockedQty || 0) + generatedOI.releaseQty,
1429
+ lockedUomValue: (oi.inventory?.lockedUomValue || 0) + generatedOI.releaseUomValue,
1430
+ updater: user
1431
+ })
1432
+ }
1243
1433
 
1244
1434
  return newReleaseGood
1245
1435
  } catch (error) {
@@ -1270,7 +1460,7 @@ export async function bulkConfirmReleaseGoods(
1270
1460
  ]
1271
1461
  })
1272
1462
 
1273
- if (!foundReleaseGoods.length) throw new Error(`Release good order doesn't exists.`)
1463
+ if (!foundReleaseGoods.length) throw new Error(`release good order doesn't exists.`)
1274
1464
  let customerBizplace: Bizplace = foundReleaseGoods[0].bizplace
1275
1465
 
1276
1466
  let foundOrderInventories: OrderInventory[] = foundReleaseGoods
@@ -1333,7 +1523,7 @@ export async function bulkConfirmReleaseGoods(
1333
1523
  })
1334
1524
  }
1335
1525
 
1336
- export function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
1526
+ function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
1337
1527
  return rawReleaseGoods.reduce((releaseGoods, item) => {
1338
1528
  const idx: number = releaseGoods.findIndex(rg => {
1339
1529
  // consider these attributes if they are exist in "item"
@@ -1370,6 +1560,7 @@ export function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]>
1370
1560
  oi => oi.sku === item.sku && oi.packingType === item.packingType && oi.packingSize === item.packingSize
1371
1561
  )
1372
1562
 
1563
+ // if there is duplicated SKU, merge them and sum up the releaseQty
1373
1564
  if (duplicateSkuIdx >= 0) {
1374
1565
  releaseGoods[idx].orderInventories[duplicateSkuIdx].releaseQty += item.releaseQty
1375
1566
  } else {
@@ -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
 
@@ -323,6 +326,9 @@ export class NewReleaseGood {
323
326
 
324
327
  @InputType()
325
328
  export class ReleaseGoodPatch {
329
+ @Field({ nullable: true })
330
+ id: string
331
+
326
332
  @Field({ nullable: true })
327
333
  name: string
328
334