@things-factory/sales-base 3.8.8 → 3.8.12

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 (35) hide show
  1. package/dist-server/controllers/ecommerce/sellercraft-controller.js +59 -0
  2. package/dist-server/controllers/ecommerce/sellercraft-controller.js.map +1 -1
  3. package/dist-server/controllers/order-controller.js +41 -2
  4. package/dist-server/controllers/order-controller.js.map +1 -1
  5. package/dist-server/entities/release-good.js +4 -0
  6. package/dist-server/entities/release-good.js.map +1 -1
  7. package/dist-server/graphql/resolvers/release-good/bulk-add-release-goods.js +79 -17
  8. package/dist-server/graphql/resolvers/release-good/bulk-add-release-goods.js.map +1 -1
  9. package/dist-server/graphql/resolvers/release-good/bulk-release-goods-available-items.js +27 -22
  10. package/dist-server/graphql/resolvers/release-good/bulk-release-goods-available-items.js.map +1 -1
  11. package/dist-server/graphql/resolvers/release-good/confirm-release-good.js +54 -5
  12. package/dist-server/graphql/resolvers/release-good/confirm-release-good.js.map +1 -1
  13. package/dist-server/graphql/resolvers/release-good/generate-release-good.js +1 -1
  14. package/dist-server/graphql/resolvers/release-good/generate-release-good.js.map +1 -1
  15. package/dist-server/graphql/resolvers/release-good/reject-release-good.js +28 -1
  16. package/dist-server/graphql/resolvers/release-good/reject-release-good.js.map +1 -1
  17. package/dist-server/graphql/types/release-good/new-release-good.js +1 -0
  18. package/dist-server/graphql/types/release-good/new-release-good.js.map +1 -1
  19. package/dist-server/graphql/types/release-good/raw-release-good.js +1 -0
  20. package/dist-server/graphql/types/release-good/raw-release-good.js.map +1 -1
  21. package/dist-server/utils/inventory-util.js +89 -1
  22. package/dist-server/utils/inventory-util.js.map +1 -1
  23. package/package.json +4 -4
  24. package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
  25. package/server/controllers/order-controller.ts +57 -4
  26. package/server/entities/release-good.ts +3 -0
  27. package/server/graphql/resolvers/release-good/bulk-add-release-goods.ts +114 -46
  28. package/server/graphql/resolvers/release-good/bulk-release-goods-available-items.ts +32 -24
  29. package/server/graphql/resolvers/release-good/confirm-release-good.ts +67 -7
  30. package/server/graphql/resolvers/release-good/generate-release-good.ts +1 -0
  31. package/server/graphql/resolvers/release-good/reject-release-good.ts +40 -4
  32. package/server/graphql/types/release-good/new-release-good.ts +1 -0
  33. package/server/graphql/types/release-good/raw-release-good.ts +1 -0
  34. package/server/utils/index.ts +1 -1
  35. package/server/utils/inventory-util.ts +129 -1
@@ -1,6 +1,12 @@
1
+ import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
2
+
1
3
  import { Role, User } from '@things-factory/auth-base'
2
4
  import { Bizplace, getDomainUsers } from '@things-factory/biz-base'
3
5
  import { sendNotification } from '@things-factory/notification'
6
+ import { ProductDetail } from '@things-factory/product-base'
7
+ import { Domain } from '@things-factory/shell'
8
+ import { Inventory } from '@things-factory/warehouse-base'
9
+
4
10
  import {
5
11
  ArrivalNotice,
6
12
  DeliveryOrder,
@@ -12,9 +18,6 @@ import {
12
18
  ReturnOrder,
13
19
  VasOrder
14
20
  } from '../entities'
15
- import { Domain } from '@things-factory/shell'
16
- import { Inventory } from '@things-factory/warehouse-base'
17
- import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
18
21
 
19
22
  export type ReferenceOrderType = ArrivalNotice | ReleaseGood | VasOrder | InventoryCheck | DeliveryOrder | ReturnOrder
20
23
  export type OrderTargetTypes = OrderProduct | OrderInventory | OrderVas
@@ -48,7 +51,9 @@ export type NotificationMsgInterface = {
48
51
  export class OrderController {
49
52
  public readonly ERROR_MSG = {
50
53
  FIND: {
51
- NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}`
54
+ NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}`,
55
+ NO_CHILD_RESULT: (condition: any) => `There's no child result matched with condition ${condition}`,
56
+ NOT_MATCH: (source: any, target: any) => `Unable to find matching ${target} using ${source}`
52
57
  },
53
58
  CREATE: {
54
59
  ID_EXISTS: 'Target has ID already',
@@ -217,6 +222,54 @@ export class OrderController {
217
222
  return await this.trxMgr.getRepository(Inventory).save(inventory)
218
223
  }
219
224
 
225
+ async getChildPackingSize(
226
+ productDetails: ProductDetail[],
227
+ defaultProductDetail: ProductDetail,
228
+ unmatchingProductDetail: ProductDetail
229
+ ): Promise<number> {
230
+ let hasChildRelation: boolean = Boolean(unmatchingProductDetail?.childProductDetail)
231
+ let hasMatchingChild: boolean
232
+ let packingSize: number = 1
233
+ let currentChildProductDetail: ProductDetail
234
+
235
+ if (hasChildRelation) {
236
+ currentChildProductDetail = productDetails.find(
237
+ (productDetail: ProductDetail) => productDetail.id === unmatchingProductDetail.childProductDetail.id
238
+ )
239
+
240
+ if (!currentChildProductDetail) {
241
+ throw new Error(this.ERROR_MSG.FIND.NOT_MATCH('packing type', `GTIN (${unmatchingProductDetail.gtin})`))
242
+ }
243
+
244
+ hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
245
+ if (hasMatchingChild) {
246
+ packingSize = unmatchingProductDetail.packingSize
247
+ } else {
248
+ packingSize = unmatchingProductDetail.packingSize * packingSize
249
+ }
250
+ } else {
251
+ throw new Error(this.ERROR_MSG.FIND.NO_CHILD_RESULT(`${unmatchingProductDetail.gtin}`))
252
+ }
253
+
254
+ while (hasChildRelation && !hasMatchingChild) {
255
+ if (hasMatchingChild) {
256
+ packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
257
+ } else if (!hasMatchingChild && hasChildRelation) {
258
+ packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
259
+ currentChildProductDetail = productDetails.find(
260
+ (productDetail: ProductDetail) => productDetail.id === currentChildProductDetail.childProductDetail.id
261
+ )
262
+
263
+ hasChildRelation = Boolean(currentChildProductDetail?.childProductDetail)
264
+ hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
265
+ } else if (!hasChildRelation && !hasMatchingChild) {
266
+ throw new Error(this.ERROR_MSG.FIND.NO_RESULT(unmatchingProductDetail.gtin))
267
+ }
268
+ }
269
+
270
+ return packingSize
271
+ }
272
+
220
273
  /**
221
274
  * @summary set common stamp like domain, creator, updater
222
275
  * @description Set common stamp to passed record
@@ -219,6 +219,9 @@ export class ReleaseGood {
219
219
  @Column({ nullable: true })
220
220
  invoice: string
221
221
 
222
+ @Column({ nullable: true })
223
+ bundleInfo: string
224
+
222
225
  @Column({ type: 'int', nullable: true })
223
226
  noOfItems: number
224
227
 
@@ -1,4 +1,4 @@
1
- import { EntityManager, getRepository, In, Repository } from 'typeorm'
1
+ import { EntityManager, getConnection, getRepository, In, Repository } from 'typeorm'
2
2
 
3
3
  import { Role, User } from '@things-factory/auth-base'
4
4
  import { Bizplace } from '@things-factory/biz-base'
@@ -7,6 +7,7 @@ import { sendNotification } from '@things-factory/notification'
7
7
  import { Product } from '@things-factory/product-base'
8
8
  import { Setting } from '@things-factory/setting-base'
9
9
  import { Domain } from '@things-factory/shell'
10
+ import { Inventory } from '@things-factory/warehouse-base'
10
11
 
11
12
  import { ORDER_STATUS } from '../../../constants'
12
13
  import {
@@ -17,6 +18,7 @@ import {
17
18
  } from '../../../constants/order'
18
19
  import { OrderInventory, OrderProduct, ReleaseGood } from '../../../entities'
19
20
  import { ValidationError } from '../../../errors'
21
+ import { InventoryUtil } from '../../../utils'
20
22
  import { OrderNoGenerator } from '../../../utils/order-no-generator'
21
23
  import { bulkReleaseGoodsAvailableItemsFunction } from './bulk-release-goods-available-items'
22
24
 
@@ -38,46 +40,70 @@ export const bulkAddReleaseGoods = {
38
40
 
39
41
  let releaseGoods: Partial<ReleaseGood[]> = extractRawReleaseGoods(rawReleaseGoods)
40
42
 
43
+ let errorsFound: any[] = []
41
44
  for (let i = 0, l = releaseGoods.length; i < l; i++) {
42
45
  // generate release good by group to avoid duplication
43
46
  // if this function is called simultaneously by different users
44
- let availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(
45
- _,
46
- [...releaseGoods[i].orderInventories],
47
- bizplaceId,
48
- context,
49
- tx
50
- )
51
-
52
- if (availableItems.some(item => !item.releaseQty || item.releaseQty > item.assignedQty))
53
- throw new ValidationError({
54
- ...ValidationError.ERROR_CODES.INSUFFICIENT_STOCK,
55
- detail: { data: JSON.stringify(availableItems) }
47
+ try {
48
+ await getConnection().transaction(async (childTx: EntityManager) => {
49
+ const existingReleaseGood: ReleaseGood = await childTx.getRepository(ReleaseGood).findOne({
50
+ where: {
51
+ ...Object.fromEntries(
52
+ Object.entries(releaseGoods[i]).filter(([_, val]) => !/^\s*$/.test(val) && _ != 'orderInventories')
53
+ ),
54
+ domain
55
+ }
56
+ })
57
+ if (existingReleaseGood) throw new Error('release good order is already exist in the system')
58
+
59
+ let availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(
60
+ _,
61
+ [...releaseGoods[i].orderInventories],
62
+ bizplaceId,
63
+ context,
64
+ childTx
65
+ )
66
+
67
+ if (availableItems.some(item => !item.releaseQty || item.releaseQty > item.assignedQty))
68
+ throw new ValidationError({
69
+ ...ValidationError.ERROR_CODES.INSUFFICIENT_STOCK,
70
+ detail: { data: JSON.stringify(availableItems) }
71
+ })
72
+
73
+ // update orderInventories if availableItems are valid
74
+ releaseGoods[i].orderInventories = availableItems
75
+
76
+ const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGoods(
77
+ _,
78
+ releaseGoods[i],
79
+ bizplaceId,
80
+ roNoSetting,
81
+ domain,
82
+ user,
83
+ childTx
84
+ )
85
+
86
+ createdReleaseGoods.push(createdReleaseGood)
56
87
  })
88
+ } catch (e) {
89
+ errorsFound.push(e)
90
+ }
91
+ }
57
92
 
58
- // update orderInventories if availableItems are valid
59
- releaseGoods[i].orderInventories = availableItems
60
-
61
- const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGoods(
62
- _,
63
- releaseGoods[i],
64
- bizplaceId,
65
- roNoSetting,
93
+ let confirmedReleaseGoods: ReleaseGood[] = []
94
+ try {
95
+ confirmedReleaseGoods = await bulkConfirmReleaseGoods(
96
+ createdReleaseGoods.map(rg => rg.name),
66
97
  domain,
67
98
  user,
99
+ context,
68
100
  tx
69
101
  )
70
-
71
- createdReleaseGoods.push(createdReleaseGood)
102
+ } catch (e) {
103
+ errorsFound.push(e)
72
104
  }
73
105
 
74
- let confirmedReleaseGoods: ReleaseGood[] = await bulkConfirmReleaseGoods(
75
- createdReleaseGoods.map(rg => rg.name),
76
- domain,
77
- user,
78
- context,
79
- tx
80
- )
106
+ // if (errorsFound.length) { then ?? }
81
107
 
82
108
  return confirmedReleaseGoods
83
109
  }
@@ -109,7 +135,6 @@ async function bulkGenerateReleaseGoods(
109
135
  deliveryAddress2: releaseGood?.deliveryAddress2 || null,
110
136
  deliveryAddress3: releaseGood?.deliveryAddress3 || null,
111
137
  deliveryAddress4: releaseGood?.deliveryAddress4 || null,
112
- deliveryAddress5: releaseGood?.deliveryAddress5 || null,
113
138
  attentionTo: releaseGood?.attentionTo || null,
114
139
  attentionCompany: releaseGood?.attentionCompany || null,
115
140
  city: releaseGood?.city || null,
@@ -131,40 +156,82 @@ async function bulkGenerateReleaseGoods(
131
156
  }
132
157
 
133
158
  newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
159
+ let finalOrderInventories: OrderInventory[] = []
134
160
 
161
+ // pre assign inventory to orderInventories
135
162
  for (let oi of orderInventories) {
136
- delete oi.sku
163
+ const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
164
+ where: { domain, name: 'rule-for-picking-product' }
165
+ })
137
166
 
138
- let newOrderInv: OrderInventory = Object.assign(new OrderInventory(), oi)
139
- newOrderInv = {
140
- ...newOrderInv,
167
+ let locationSortingRules = []
168
+ if (pickingProductSetting) {
169
+ let locationSetting = JSON.parse(pickingProductSetting.value)
170
+ for (const key in locationSetting) {
171
+ locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
172
+ }
173
+ }
174
+
175
+ const product: Product = await tx.getRepository(Product).findOne(oi.productId)
176
+
177
+ finalOrderInventories.push(
178
+ ...(await InventoryUtil.autoAssignInventoryForRelease(
179
+ product,
180
+ oi,
181
+ oi.packingType,
182
+ locationSortingRules,
183
+ bizplace,
184
+ warehouseDomain,
185
+ tx
186
+ ))
187
+ )
188
+ }
189
+
190
+ for (let oi of finalOrderInventories) {
191
+ let generatedOI: OrderInventory = Object.assign(new OrderInventory(), oi)
192
+ generatedOI = {
193
+ ...generatedOI,
141
194
  domain: warehouseDomain,
142
- bizplace: bizplace,
195
+ bizplace,
143
196
  status: ORDER_INVENTORY_STATUS.PENDING,
144
197
  name: OrderNoGenerator.orderInventory(),
145
- batchId: oi?.batchId || '',
146
198
  releaseGood: newReleaseGood,
147
- product: await tx.getRepository(Product).findOne(oi.productId),
148
199
  creator: user,
149
200
  updater: user
150
201
  }
151
202
 
152
- let newOrderProduct: OrderProduct = Object.assign(new OrderProduct(), newOrderInv)
153
- newOrderProduct = {
154
- ...newOrderProduct,
203
+ let newOrderProduct: Partial<OrderProduct> = {
204
+ name: generatedOI.name,
205
+ product: generatedOI.product,
206
+ batchId: generatedOI.batchId,
207
+ packingType: generatedOI.packingType,
208
+ packingSize: generatedOI.packingSize,
209
+ uom: generatedOI.uom,
210
+ domain: warehouseDomain,
211
+ bizplace,
212
+ releaseQty: generatedOI.releaseQty,
213
+ releaseUomValue: generatedOI.releaseUomValue,
155
214
  packQty: 0,
156
215
  actualPackQty: 0,
157
216
  palletQty: 0,
158
217
  actualPalletQty: 0,
159
- status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN
218
+ status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN,
219
+ releaseGood: newReleaseGood,
220
+ creator: user,
221
+ updater: user
160
222
  }
161
223
 
162
224
  newOrderProduct = await tx.getRepository(OrderProduct).save(newOrderProduct)
163
225
 
164
- await tx.getRepository(OrderInventory).save({ ...newOrderInv, orderProduct: newOrderProduct })
165
- }
226
+ await tx.getRepository(OrderInventory).save({ ...generatedOI, orderProduct: newOrderProduct })
166
227
 
167
- // Change the status to PENDING_RECEIVE
228
+ // update inventory locked qty and uom value
229
+ await tx.getRepository(Inventory).update(oi.inventory.id, {
230
+ lockedQty: (oi.inventory?.lockedQty || 0) + generatedOI.releaseQty,
231
+ lockedUomValue: (oi.inventory?.lockedUomValue || 0) + generatedOI.releaseUomValue,
232
+ updater: user
233
+ })
234
+ }
168
235
 
169
236
  return newReleaseGood
170
237
  } catch (error) {
@@ -195,7 +262,7 @@ async function bulkConfirmReleaseGoods(
195
262
  ]
196
263
  })
197
264
 
198
- if (!foundReleaseGoods.length) throw new Error(`Release good order doesn't exists.`)
265
+ if (!foundReleaseGoods.length) throw new Error(`release good order doesn't exists.`)
199
266
  let customerBizplace: Bizplace = foundReleaseGoods[0].bizplace
200
267
 
201
268
  let foundOrderInventories: OrderInventory[] = foundReleaseGoods
@@ -295,6 +362,7 @@ function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
295
362
  oi => oi.sku === item.sku && oi.packingType === item.packingType && oi.packingSize === item.packingSize
296
363
  )
297
364
 
365
+ // if there is duplicated SKU, merge them and sum up the releaseQty
298
366
  if (duplicateSkuIdx >= 0) {
299
367
  releaseGoods[idx].orderInventories[duplicateSkuIdx].releaseQty += item.releaseQty
300
368
  } else {
@@ -2,6 +2,7 @@ import { EntityManager } from 'typeorm'
2
2
 
3
3
  import { Bizplace, getCompanyBizplace } from '@things-factory/biz-base'
4
4
  import { Domain } from '@things-factory/shell'
5
+ import { LOCATION_TYPE } from '@things-factory/warehouse-base'
5
6
 
6
7
  export const bulkReleaseGoodsAvailableItems = {
7
8
  async bulkReleaseGoodsAvailableItems(_: any, { rawReleaseGoods, bizplaceId }, context: any) {
@@ -47,6 +48,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
47
48
  CREATE TEMP TABLE temp_order_products(
48
49
  product_id VARCHAR(50),
49
50
  sku VARCHAR(150),
51
+ product_info VARCHAR(250),
50
52
  packing_type VARCHAR(50),
51
53
  packing_size INT,
52
54
  uom VARCHAR(10),
@@ -59,6 +61,8 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
59
61
  `
60
62
  INSERT INTO temp_order_products
61
63
  SELECT p.id AS product_id, js.sku,
64
+ CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
65
+ ELSE p.name END AS product_info,
62
66
  CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
63
67
  CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
64
68
  CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
@@ -72,16 +76,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
72
76
 
73
77
  let availableItems = await tx.query(
74
78
  `
75
- SELECT i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom,
79
+ WITH inv AS (
80
+ SELECT i.product_id, foo.sku, foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom,
76
81
  SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
77
82
  (
78
83
  SELECT SUM(oi.release_qty) FROM order_inventories oi
79
- WHERE (
80
- oi.status = 'PENDING'
81
- OR oi.status = 'PENDING_RECEIVE'
82
- OR oi.status = 'PENDING_WORKSHEET'
83
- OR oi.status = 'PENDING_SPLIT'
84
- )
84
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
85
85
  AND oi.inventory_id IS null
86
86
  AND oi.product_id = i.product_id::uuid
87
87
  AND oi.packing_type = i.packing_type
@@ -92,12 +92,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
92
92
  SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
93
93
  (
94
94
  SELECT SUM(oi.release_uom_value) FROM order_inventories oi
95
- WHERE (
96
- oi.status = 'PENDING'
97
- OR oi.status = 'PENDING_RECEIVE'
98
- OR oi.status = 'PENDING_WORKSHEET'
99
- OR oi.status = 'PENDING_SPLIT'
100
- )
95
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
101
96
  AND oi.inventory_id IS null
102
97
  AND oi.product_id = i.product_id::uuid
103
98
  AND oi.packing_type = i.packing_type
@@ -106,10 +101,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
106
101
  AND oi.bizplace_id = $2
107
102
  ), 0) as "remain_uom_value"
108
103
  FROM inventories i
104
+ LEFT JOIN locations l
105
+ ON i.location_id = l.id
109
106
  INNER JOIN (
110
- SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
107
+ SELECT top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
111
108
  FROM temp_order_products top
112
- GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
109
+ GROUP BY top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
113
110
  ) AS foo
114
111
  ON i.product_id = foo.product_id::uuid
115
112
  AND i.packing_type = foo.packing_type
@@ -117,10 +114,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
117
114
  AND i.uom = foo.uom
118
115
  AND i.domain_id = $1
119
116
  AND i.bizplace_id = $2
120
- GROUP BY i.product_id, foo.sku, i.batch_id, i.packing_type, i.packing_size, i.uom
117
+ WHERE l.type NOT IN ($3, $4)
118
+ GROUP BY i.product_id, foo.sku,foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom
121
119
  ORDER BY foo.sku, remain_qty
122
- `,
123
- [domain.id, bizplaceId]
120
+ ) SELECT * FROM inv WHERE remain_qty > 0
121
+ `,
122
+ [domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
124
123
  )
125
124
 
126
125
  await tx.query('DROP TABLE temp_order_products')
@@ -128,6 +127,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
128
127
  availableItems = availableItems.map(item => {
129
128
  return {
130
129
  productId: item.product_id,
130
+ productInfo: item.product_info,
131
131
  sku: item.sku,
132
132
  batchId: item.batch_id,
133
133
  packingType: item.packing_type,
@@ -162,33 +162,41 @@ function _extractData(rawData, validatedData) {
162
162
  return val.sku == raw.sku && a === b
163
163
  })
164
164
 
165
- let releaseUomValue = 0
165
+ let releaseUomValue: number = 0
166
166
 
167
167
  // if sku is matched, assign qty and product id
168
168
  if (idx >= 0) {
169
- // assign qty to rawData as much as possible
170
- releaseUomValue =
171
- (Math.round((validatedData[idx]?.remainUomValue / validatedData[idx]?.remainQty) * 100) / 100) * raw.releaseQty
169
+ const uomValuePerQty: number =
170
+ Math.round((validatedData[idx].remainUomValue / validatedData[idx].remainQty) * 100) / 100
172
171
 
172
+ // use Math.round again because 4.2 * 6 = 25.200000000000003
173
+ releaseUomValue = Math.round(uomValuePerQty * raw.releaseQty * 100) / 100
174
+
175
+ // assign qty to rawData as much as possible
173
176
  raw.assignedQty =
174
177
  validatedData[idx].remainQty >= raw.releaseQty
175
178
  ? raw.releaseQty > 0
176
179
  ? raw.releaseQty
177
180
  : 0
178
- : validatedData[idx].remainQty
181
+ : validatedData[idx].remainQty > 0
182
+ ? validatedData[idx].remainQty
183
+ : 0
179
184
 
180
185
  raw.assignedUomValue =
181
186
  validatedData[idx].remainUomValue >= releaseUomValue
182
187
  ? releaseUomValue > 0
183
188
  ? releaseUomValue
184
189
  : 0
185
- : validatedData[idx].remainUomValue
190
+ : validatedData[idx].remainUomValue > 0
191
+ ? validatedData[idx].remainUomValue
192
+ : 0
186
193
 
187
194
  // deduct qty & uomValue from validateData
188
195
  validatedData[idx].remainQty -= raw.assignedQty
189
196
  validatedData[idx].remainUomValue -= raw.assignedUomValue
190
197
 
191
198
  raw.productId = validatedData[idx].productId
199
+ raw.productInfo = validatedData[idx].productInfo
192
200
  raw.packingType = validatedData[idx].packingType
193
201
  raw.packingSize = validatedData[idx].packingSize
194
202
  raw.uom = validatedData[idx].uom
@@ -4,12 +4,13 @@ import { Role, User } from '@things-factory/auth-base'
4
4
  import { Bizplace } from '@things-factory/biz-base'
5
5
  import { MarketplaceStore } from '@things-factory/integration-marketplace'
6
6
  import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
7
+ import { MarketplaceOrder, MarketplaceProductVariation } from '@things-factory/marketplace-base'
7
8
  import { sendNotification } from '@things-factory/notification'
8
9
  import { Domain } from '@things-factory/shell'
9
10
 
10
11
  import { ORDER_INVENTORY_STATUS, ORDER_STATUS, ORDER_VAS_STATUS } from '../../../constants'
11
12
  import { EcommerceController, SellercraftController } from '../../../controllers'
12
- import { OrderInventory, OrderVas, ReleaseGood } from '../../../entities'
13
+ import { OrderInventory, OrderProduct, OrderVas, ReleaseGood } from '../../../entities'
13
14
  import { confirmArrivalNoticeFunction } from '../arrival-notice/confirm-arrival-notice'
14
15
 
15
16
  export const confirmReleaseGoodResolver = {
@@ -46,6 +47,7 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
46
47
  'bizplace.company.domain',
47
48
  'orderProducts',
48
49
  'orderProducts.product',
50
+ 'orderProducts.product.productDetails',
49
51
  'orderInventories',
50
52
  'orderInventories.product',
51
53
  'orderVass'
@@ -54,26 +56,77 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
54
56
 
55
57
  if (!foundReleaseGood) throw new Error(`Release good order doesn't exists.`)
56
58
  let foundOIs: OrderInventory[] = foundReleaseGood.orderInventories
59
+ let foundBundleInfo: any[] = JSON.parse(foundReleaseGood.bundleInfo)
57
60
  let foundOVs: OrderVas[] = foundReleaseGood.orderVass
61
+ let foundOPs: OrderProduct[] = foundReleaseGood.orderProducts
58
62
  let customerBizplace: Bizplace = foundReleaseGood.bizplace
59
63
  const companyDomain: Domain = customerBizplace?.company.domain
64
+ const sellercraft: Sellercraft = await tx
65
+ .getRepository(Sellercraft)
66
+ .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
60
67
 
61
68
  if (foundReleaseGood.type == 'b2c') {
62
- const sellercraft: Sellercraft = await tx
63
- .getRepository(Sellercraft)
64
- .findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
65
-
66
69
  if (sellercraft) {
67
70
  const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
68
71
  foundReleaseGood = await sellercraftCtrl.packOrder(sellercraft, foundReleaseGood)
69
72
  } else {
70
73
  // find for any existing marketplace store connections
71
- const marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
72
- where: { domain: companyDomain, status: 'ACTIVE', isAutoUpdateStockQty: true },
74
+ let marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
75
+ where: { domain: companyDomain, status: 'ACTIVE' },
73
76
  relations: ['marketplaceDistributors']
74
77
  })
75
78
 
79
+ const foundMarketplaceOrder: MarketplaceOrder = await tx.getRepository(MarketplaceOrder).findOne({
80
+ where: { domain: companyDomain, orderNo: foundReleaseGood.refNo },
81
+ relations: ['marketplaceStore']
82
+ })
83
+
84
+ let matchedMarketplaceStore: MarketplaceStore
85
+ if (foundMarketplaceOrder) {
86
+ matchedMarketplaceStore = marketplaceStores.filter(
87
+ marketplaceStore => marketplaceStore.id == foundMarketplaceOrder.marketplaceStore.id
88
+ )[0]
89
+ }
90
+
91
+ let combinedOrderInventories: any[] = foundOIs.map(oi => {
92
+ return {
93
+ sku: oi.product.sku,
94
+ releaseQty: oi.releaseQty
95
+ }
96
+ })
97
+
98
+ if (foundBundleInfo?.length) {
99
+ foundBundleInfo.map(bundle => {
100
+ combinedOrderInventories.push({
101
+ sku: bundle.sku,
102
+ releaseQty: bundle.releaseQty
103
+ })
104
+ })
105
+ }
106
+
107
+ for (let oi of combinedOrderInventories) {
108
+ let foundMarketplaceProductVariations: MarketplaceProductVariation[] = await tx
109
+ .getRepository(MarketplaceProductVariation)
110
+ .find({
111
+ where: { domain: companyDomain, sku: oi.sku },
112
+ relations: ['marketplaceProduct', 'marketplaceProduct.marketplaceStore']
113
+ })
114
+
115
+ if (foundMarketplaceProductVariations) {
116
+ await Promise.all(
117
+ foundMarketplaceProductVariations.map(async variation => {
118
+ if (variation.marketplaceProduct.marketplaceStore.id != matchedMarketplaceStore.id) {
119
+ variation.reserveQty -= oi.releaseQty
120
+
121
+ await tx.getRepository(MarketplaceProductVariation).save(variation)
122
+ }
123
+ })
124
+ )
125
+ }
126
+ }
127
+
76
128
  if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
129
+ marketplaceStores = marketplaceStores.filter(marketplaceStore => marketplaceStore.isAutoUpdateStockQty)
77
130
  let productIds: string[] = foundOIs.map(oi => oi.product.id)
78
131
  productIds = Array.from(new Set([...productIds]))
79
132
  const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
@@ -82,6 +135,13 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
82
135
  }
83
136
  }
84
137
 
138
+ if (foundReleaseGood.type == 'b2b') {
139
+ if (sellercraft) {
140
+ const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
141
+ await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'CONFIRM_ORDER')
142
+ }
143
+ }
144
+
85
145
  // 1. RO Status change (PENDING => PENDING_RECEIVE)
86
146
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).save({
87
147
  ...foundReleaseGood,
@@ -222,6 +222,7 @@ export async function generateReleaseGoodFunction(
222
222
  refNo3: releaseGood.refNo3,
223
223
  releaseDate: releaseGood.releaseDate,
224
224
  truckNo: releaseGood.truckNo,
225
+ bundleInfo: releaseGood?.bundleInfo || [],
225
226
  orderInventories: releaseGood.orderInventories,
226
227
  type: releaseGood?.type ? releaseGood.type : 'b2b',
227
228
  status: ORDER_STATUS.PENDING,