@things-factory/operato-hub 4.3.14 → 4.3.15

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.
@@ -3,6 +3,7 @@ import { EntityManager, getConnection, SelectQueryBuilder } from 'typeorm'
3
3
 
4
4
  import { restfulApiRouter as router } from '@things-factory/api'
5
5
  import { Bizplace, getCompanyBizplace } from '@things-factory/biz-base'
6
+ import { createPayloadLog, PayloadType } from '@things-factory/integration-base'
6
7
  import { Product, ProductBundle, ProductDetail } from '@things-factory/product-base'
7
8
  import { ORDER_TYPES, OrderInventory } from '@things-factory/sales-base'
8
9
  import { Setting } from '@things-factory/setting-base'
@@ -12,7 +13,7 @@ import { WorksheetDetail } from '@things-factory/worksheet-base'
12
13
  import { businessMiddleware } from '../../../../middlewares/business-middleware'
13
14
  import { warehouseMiddleware } from '../../../../middlewares/warehouse-middleware'
14
15
 
15
- const debug = require('debug')('things-factory:operato-hub:restful-api:v2:add-release-order')
16
+ const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
16
17
 
17
18
  router.post(
18
19
  '/v2/warehouse/:warehouseId/add-release-order',
@@ -23,258 +24,291 @@ router.post(
23
24
  const { client, domain } = context.state
24
25
  const { warehouseId: customerBizplaceId } = context.params
25
26
  const { t } = context
27
+ const bodyReq = context.request.body
28
+ try {
29
+ if (!customerBizplaceId) context.throw(404, t('error.warehouse id not found'))
26
30
 
27
- if (!customerBizplaceId) context.throw(404, t('error.warehouse id not found'))
28
-
29
- let bodyReq = context.request.body
30
- if (typeof bodyReq === undefined || !Object.keys(bodyReq)?.length) {
31
- context.throw(400, t('error.invalid body request format'))
32
- }
33
- debug('post:/add-release-order request.body', bodyReq)
34
- await checkMandatoryKey(context, bodyReq)
31
+ if (typeof bodyReq === undefined || !Object.keys(bodyReq)?.length) {
32
+ context.throw(400, t('error.invalid body request format'))
33
+ }
34
+ debug('post:/add-release-order request.body', bodyReq)
35
+ await checkMandatoryKey(context, bodyReq)
35
36
 
36
- const isAutoAssign: Boolean = bodyReq.hasOwnProperty('isAutoAssign') ? bodyReq.isAutoAssign : true
37
+ const isAutoAssign: Boolean = bodyReq.hasOwnProperty('isAutoAssign') ? bodyReq.isAutoAssign : true
38
+ const requiredDraft: Boolean = bodyReq.hasOwnProperty('requiredDraft') ? bodyReq.requiredDraft : true
37
39
 
38
- // get customer company's bizplace
39
- const custCompanyBizplace: Bizplace = await getCompanyBizplace(null, null, customerBizplaceId, tx)
40
- const customerBizplace: Bizplace = await tx.getRepository(Bizplace).findOne({
41
- where: { id: customerBizplaceId }
42
- })
43
-
44
- let locationSortingRules = []
45
- const inventoryAssignmentSetting: Setting = await tx.getRepository(Setting).findOne({
46
- where: { domain, name: 'rule-for-inventory-assignment' }
47
- })
40
+ // get customer company's bizplace
41
+ const custCompanyBizplace: Bizplace = await getCompanyBizplace(null, null, customerBizplaceId, tx)
42
+ const customerBizplace: Bizplace = await tx.getRepository(Bizplace).findOne({
43
+ where: { id: customerBizplaceId }
44
+ })
48
45
 
49
- if (inventoryAssignmentSetting) {
50
- let locationSetting = JSON.parse(inventoryAssignmentSetting.value)
51
- for (const key in locationSetting) {
52
- locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
53
- }
54
- } else {
55
- const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
56
- where: { domain, name: 'rule-for-picking-product' }
46
+ let locationSortingRules = []
47
+ const inventoryAssignmentSetting: Setting = await tx.getRepository(Setting).findOne({
48
+ where: { domain, name: 'rule-for-inventory-assignment' }
57
49
  })
58
- if (pickingProductSetting) {
59
- let locationSetting = JSON.parse(pickingProductSetting.value)
50
+
51
+ if (inventoryAssignmentSetting) {
52
+ let locationSetting = JSON.parse(inventoryAssignmentSetting.value)
60
53
  for (const key in locationSetting) {
61
54
  locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
62
55
  }
63
- }
64
- }
65
-
66
- const orderInventories: OrderInventory[] = bodyReq.orderInventories
67
- let newOrderInventories: OrderInventory[] = []
68
- let bundleInfo: any[] = []
69
-
70
- await Promise.all(
71
- orderInventories.map(async orderInventory => {
72
- const sku: string = orderInventory.product?.sku
73
- const refCode: string = orderInventory.product?.refCode
74
- const { packingType, packingSize }: { packingType: string; packingSize: number } = orderInventory
75
-
76
- const qb: SelectQueryBuilder<ProductDetail> = tx
77
- .getRepository(ProductDetail)
78
- .createQueryBuilder('PD')
79
- .innerJoinAndSelect('PD.product', 'PROD')
80
- .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
81
-
82
- if (!sku && !refCode) {
83
- context.throw(404, t('error.sku or refCode not found'))
84
- }
85
-
86
- if (refCode) {
87
- qb.andWhere('PD.ref_code = :refCode', { refCode })
88
- }
89
-
90
- if (sku) {
91
- qb.andWhere('PROD.sku = :sku', { sku })
92
- }
93
-
94
- if (packingType) {
95
- qb.andWhere('PD.packing_type = :packingType', { packingType })
96
- }
97
-
98
- if (!packingType && !refCode) {
99
- qb.andWhere('PD.is_default = :isDefault', { isDefault: true })
100
- }
101
- if (packingSize) {
102
- qb.andWhere('PD.packing_size = :packingSize', { packingSize })
56
+ } else {
57
+ const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
58
+ where: { domain, name: 'rule-for-picking-product' }
59
+ })
60
+ if (pickingProductSetting) {
61
+ let locationSetting = JSON.parse(pickingProductSetting.value)
62
+ for (const key in locationSetting) {
63
+ locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
64
+ }
103
65
  }
66
+ }
104
67
 
105
- const productDetail: ProductDetail = await qb.getOne()
106
-
107
- if (productDetail) {
108
- let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
68
+ const orderInventories: OrderInventory[] = bodyReq.orderInventories
69
+ let newOrderInventories: OrderInventory[] = []
70
+ let bundleInfo: any[] = []
109
71
 
110
- newOrderInventory.product.sku = productDetail.product.sku
111
- newOrderInventory.product = productDetail.product
112
- newOrderInventory.packingType = productDetail.packingType
113
- newOrderInventory.uomValue = productDetail.uomValue
114
- newOrderInventories.push(newOrderInventory)
115
- }
72
+ await Promise.all(
73
+ orderInventories.map(async orderInventory => {
74
+ const sku: string = orderInventory.product?.sku
75
+ const refCode: string = orderInventory.product?.refCode
76
+ const packingType: string = orderInventory.packingType
116
77
 
117
- if (!productDetail) {
118
78
  const qb: SelectQueryBuilder<ProductDetail> = tx
119
- .getRepository(ProductBundle)
120
- .createQueryBuilder('PB')
121
- .innerJoinAndSelect('PB.productBundleSettings', 'PBS')
122
- .innerJoinAndSelect('PBS.productDetail', 'PBD')
123
- .innerJoinAndSelect('PBD.product', 'PROD')
124
- .where('PB.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
79
+ .getRepository(ProductDetail)
80
+ .createQueryBuilder('PD')
81
+ .innerJoinAndSelect('PD.product', 'PROD')
82
+ .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
125
83
 
126
84
  if (!sku && !refCode) {
127
85
  context.throw(404, t('error.sku or refCode not found'))
128
86
  }
129
87
 
130
88
  if (refCode) {
131
- qb.andWhere('PB.ref_code = :refCode', { refCode })
89
+ qb.andWhere('PD.ref_code = :refCode', { refCode })
132
90
  }
133
91
 
134
92
  if (sku) {
135
- qb.andWhere('PB.sku = :sku', { sku })
93
+ qb.andWhere('PROD.sku = :sku', { sku })
136
94
  }
137
95
 
138
96
  if (packingType) {
139
- qb.andWhere('PB.packing_type = :packingType', { packingType })
97
+ qb.andWhere('PD.packing_type = :packingType', { packingType })
140
98
  }
141
99
 
142
- const foundProductBundle: ProductBundle = await qb.getOne()
100
+ if (!packingType && !refCode) {
101
+ qb.andWhere('PD.is_default = :isDefault', { isDefault: true })
102
+ }
143
103
 
144
- if (foundProductBundle) {
145
- bundleInfo.push({
146
- productBundleId: foundProductBundle.id,
147
- sku: foundProductBundle.sku,
148
- releaseQty: orderInventory.releaseQty
149
- })
150
- const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
151
- bundleProductSettings.map(bundleProductSetting => {
152
- const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
153
-
154
- let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
155
- newOrderInventory.product.sku = productDetailBundle.product.sku
156
- newOrderInventory.packingType = productDetailBundle.packingType
157
- newOrderInventory.uomValue = productDetailBundle.uomValue
158
- newOrderInventory.releaseQty = orderInventory.releaseQty * bundleProductSetting.bundleQty
159
- newOrderInventory.product = productDetailBundle.product
160
- newOrderInventories.push(newOrderInventory)
161
- })
162
- } else {
163
- if (sku && refCode) {
164
- context.throw(404, t(`error.sku ${sku} and refCode ${refCode} not exist in product / bundle master`))
104
+ const productDetail: ProductDetail = await qb.getOne()
105
+
106
+ if (productDetail) {
107
+ let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
108
+
109
+ newOrderInventory.product.sku = productDetail.product.sku
110
+ newOrderInventory.product = productDetail.product
111
+ newOrderInventory.packingType = productDetail.packingType
112
+ newOrderInventory.uomValue = productDetail.uomValue
113
+ newOrderInventories.push(newOrderInventory)
114
+ }
115
+
116
+ if (!productDetail) {
117
+ const qb: SelectQueryBuilder<ProductDetail> = tx
118
+ .getRepository(ProductBundle)
119
+ .createQueryBuilder('PB')
120
+ .innerJoinAndSelect('PB.productBundleSettings', 'PBS')
121
+ .innerJoinAndSelect('PBS.productDetail', 'PBD')
122
+ .innerJoinAndSelect('PBD.product', 'PROD')
123
+ .where('PB.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
124
+
125
+ if (!sku && !refCode) {
126
+ context.throw(404, t('error.sku or refCode not found'))
127
+ }
128
+
129
+ if (refCode) {
130
+ qb.andWhere('PB.ref_code = :refCode', { refCode })
165
131
  }
166
132
 
167
133
  if (sku) {
168
- context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
134
+ qb.andWhere('PB.sku = :sku', { sku })
169
135
  }
170
136
 
171
- if (refCode) {
172
- context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
137
+ if (packingType) {
138
+ qb.andWhere('PB.packing_type = :packingType', { packingType })
173
139
  }
174
- }
175
- }
176
- })
177
- )
178
-
179
- let finalOrderInventories: OrderInventory[] = []
180
-
181
- // Case 1. Every order inventories has specified batch id
182
- if (newOrderInventories.every((orderInventory: OrderInventory) => orderInventory.batchId)) {
183
- finalOrderInventories = await Promise.all(
184
- newOrderInventories.map(async (orderInventory: OrderInventory) => {
185
- let filters: any = [
186
- {
187
- name: 'batchId',
188
- operator: 'eq',
189
- value: orderInventory.batchId
190
- },
191
- {
192
- name: 'productName',
193
- operator: 'eq',
194
- value: orderInventory.product.sku
195
- },
196
- {
197
- name: 'bizplace',
198
- operator: 'in',
199
- value: [customerBizplaceId]
140
+
141
+ const foundProductBundle: ProductBundle = await qb.getOne()
142
+
143
+ if (foundProductBundle) {
144
+ bundleInfo.push({
145
+ productBundleId: foundProductBundle.id,
146
+ sku: foundProductBundle.sku,
147
+ releaseQty: orderInventory.releaseQty
148
+ })
149
+ const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
150
+ bundleProductSettings.map(bundleProductSetting => {
151
+ const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
152
+
153
+ let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
154
+ newOrderInventory.product.sku = productDetailBundle.product.sku
155
+ newOrderInventory.packingType = productDetailBundle.packingType
156
+ newOrderInventory.uomValue = productDetailBundle.uomValue
157
+ newOrderInventory.releaseQty = orderInventory.releaseQty * bundleProductSetting.bundleQty
158
+ newOrderInventory.product = productDetailBundle.product
159
+ newOrderInventories.push(newOrderInventory)
160
+ })
161
+ } else {
162
+ if (sku && refCode) {
163
+ context.throw(404, t(`error.sku ${sku} and refCode ${refCode} not exist in product / bundle master`))
164
+ }
165
+
166
+ if (sku) {
167
+ context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
168
+ }
169
+
170
+ if (refCode) {
171
+ context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
172
+ }
200
173
  }
201
- ]
174
+ }
175
+ })
176
+ )
202
177
 
203
- let sortings: any = []
178
+ let finalOrderInventories: OrderInventory[] = []
179
+
180
+ // Case 1. Every order inventories has specified batch id
181
+ if (newOrderInventories.every((orderInventory: OrderInventory) => orderInventory.batchId)) {
182
+ finalOrderInventories = await Promise.all(
183
+ newOrderInventories.map(async (orderInventory: OrderInventory) => {
184
+ let filters: any = [
185
+ {
186
+ name: 'batchId',
187
+ operator: 'eq',
188
+ value: orderInventory.batchId
189
+ },
190
+ {
191
+ name: 'productName',
192
+ operator: 'eq',
193
+ value: orderInventory.product.sku
194
+ },
195
+ {
196
+ name: 'bizplace',
197
+ operator: 'in',
198
+ value: [customerBizplaceId]
199
+ }
200
+ ]
204
201
 
205
- switch (orderInventory.product.pickingStrategy) {
206
- case 'FIFO':
207
- sortings.push({ name: 'created_at', desc: false })
208
- break
202
+ let sortings: any = []
209
203
 
210
- case 'LIFO':
211
- sortings.push({ name: 'created_at', desc: true })
212
- break
204
+ switch (orderInventory.product.pickingStrategy) {
205
+ case 'FIFO':
206
+ sortings.push({ name: 'created_at', desc: false })
207
+ break
213
208
 
214
- case 'FEFO':
215
- sortings.push({ name: 'expiration_date', desc: false })
216
- break
209
+ case 'LIFO':
210
+ sortings.push({ name: 'created_at', desc: true })
211
+ break
217
212
 
218
- case 'FMFO':
219
- sortings.push({ name: 'manufacture_date', desc: false })
220
- break
221
- }
213
+ case 'FEFO':
214
+ sortings.push({ name: 'expiration_date', desc: false })
215
+ break
222
216
 
223
- // 2. get all inventory data according to SKU and batchId
224
- const response = await client.query({
225
- query: gql`
226
- query inventoriesByPallet($filters: [Filter], $sortings: [Sorting], $locationSortingRules: [Sorting]) {
227
- inventoriesByPallet(
228
- filters: $filters
229
- sortings: $sortings
230
- locationSortingRules: $locationSortingRules
217
+ case 'FMFO':
218
+ sortings.push({ name: 'manufacture_date', desc: false })
219
+ break
220
+ }
221
+
222
+ // 2. get all inventory data according to SKU and batchId
223
+ const response = await client.query({
224
+ query: gql`
225
+ query inventoriesByPallet(
226
+ $filters: [Filter]
227
+ $sortings: [Sorting]
228
+ $locationSortingRules: [Sorting]
231
229
  ) {
232
- items {
233
- product {
234
- id
235
- name
236
- description
230
+ inventoriesByPallet(
231
+ filters: $filters
232
+ sortings: $sortings
233
+ locationSortingRules: $locationSortingRules
234
+ ) {
235
+ items {
236
+ product {
237
+ id
238
+ name
239
+ description
240
+ }
241
+ batchId
242
+ packingType
243
+ remainQty
244
+ remainUomValue
237
245
  }
238
- batchId
239
- packingType
240
- remainQty
241
- remainUomValue
246
+ total
242
247
  }
243
- total
248
+ }
249
+ `,
250
+ variables: { filters, sortings, locationSortingRules },
251
+ context: {
252
+ ...context,
253
+ state: {
254
+ ...context.state,
255
+ bizplace: customerBizplace,
256
+ domain
244
257
  }
245
258
  }
246
- `,
247
- variables: { filters, sortings, locationSortingRules },
248
- context: {
249
- ...context,
250
- state: {
251
- ...context.state,
252
- bizplace: customerBizplace,
253
- domain
259
+ } as any)
260
+
261
+ if (!response.errors) {
262
+ const items = response.data.inventoriesByPallet.items || []
263
+ if (requiredDraft) {
264
+ if (!items?.length) {
265
+ context.body = {
266
+ result: await createDraftOrder(
267
+ client,
268
+ customerBizplace,
269
+ bodyReq,
270
+ bundleInfo,
271
+ domain,
272
+ context,
273
+ orderInventories,
274
+ tx
275
+ )
276
+ }
277
+ return
278
+ }
254
279
  }
255
- }
256
- } as any)
257
280
 
258
- if (!response.errors) {
259
- const items = response.data.inventoriesByPallet.items || []
260
- if (!items?.length) {
261
- context.body = {
262
- result: await createDraftOrder(
263
- client,
264
- customerBizplace,
265
- bodyReq,
266
- bundleInfo,
267
- domain,
268
- context,
269
- orderInventories,
270
- tx
271
- )
272
- }
273
- return
274
- }
281
+ items.forEach(async item => {
282
+ if (requiredDraft) {
283
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
284
+ context.body = {
285
+ result: await createDraftOrder(
286
+ client,
287
+ customerBizplace,
288
+ bodyReq,
289
+ bundleInfo,
290
+ domain,
291
+ context,
292
+ orderInventories,
293
+ tx
294
+ )
295
+ }
296
+ return
297
+ }
298
+ }
275
299
 
276
- items.forEach(async item => {
277
- if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
300
+ // 3. add more information to orderInventory
301
+ orderInventory.product.id = item.product.id
302
+ orderInventory.packingType = item.packingType
303
+ orderInventory.batchId = item.batchId
304
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
305
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
306
+ orderInventory.uom = item.uom
307
+ })
308
+
309
+ return orderInventory
310
+ } else {
311
+ if (requiredDraft) {
278
312
  context.body = {
279
313
  result: await createDraftOrder(
280
314
  client,
@@ -289,228 +323,241 @@ router.post(
289
323
  }
290
324
  return
291
325
  }
292
-
293
- // 3. add more information to orderInventory
294
- orderInventory.product.id = item.product.id
295
- orderInventory.packingType = item.packingType
296
- orderInventory.batchId = item.batchId
297
- orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
298
- orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
299
- orderInventory.uom = item.uom
300
- })
301
-
302
- return orderInventory
303
- } else {
304
- context.body = {
305
- result: await createDraftOrder(
306
- client,
307
- customerBizplace,
308
- bodyReq,
309
- bundleInfo,
310
- domain,
311
- context,
312
- orderInventories,
313
- tx
314
- )
315
326
  }
316
- return
317
- }
318
- })
319
- )
320
- } else {
321
- // Case 2. Some of order inventories doesn't have specified batch id
322
- // MMS order doesn't have batch ID field.
323
- // To cover this case first result of found inventory will be a target of RO
324
-
325
- // To combine items from different bundles
326
- newOrderInventories = combineItems(newOrderInventories)
327
-
328
- for (let i: number = 0; i < newOrderInventories.length; i++) {
329
- const orderInventory: OrderInventory = newOrderInventories[i]
330
- const { product, packingType }: { product: Product; packingType: string } = orderInventory
331
- const pickingStrategy: string = product.pickingStrategy
332
-
333
- if (isAutoAssign) {
334
- let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
335
- qb.leftJoinAndSelect('iv.location', 'loc')
336
- .andWhere('"iv"."domain_id" = :domainId')
337
- .andWhere('"iv"."bizplace_id" = :bizplaceId')
338
- .andWhere('"iv"."packing_type" = :packingType')
339
- .andWhere('"iv"."product_id" = :productId')
340
- .andWhere('"iv"."status" = :status')
341
- .andWhere('"loc"."type" NOT IN (:...locationTypes)')
342
- .setParameters({
343
- domainId: domain.id,
344
- bizplaceId: customerBizplace.id,
345
- packingType: packingType,
346
- productId: product.id,
347
- status: INVENTORY_STATUS.STORED,
348
- locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
349
- })
327
+ })
328
+ )
329
+ } else {
330
+ // Case 2. Some of order inventories doesn't have specified batch id
331
+ // MMS order doesn't have batch ID field.
332
+ // To cover this case first result of found inventory will be a target of RO
333
+
334
+ // To combine items from different bundles
335
+ newOrderInventories = combineItems(newOrderInventories)
336
+
337
+ for (let i: number = 0; i < newOrderInventories.length; i++) {
338
+ const orderInventory: OrderInventory = newOrderInventories[i]
339
+ const { product, packingType }: { product: Product; packingType: string } = orderInventory
340
+ const pickingStrategy: string = product.pickingStrategy
341
+
342
+ if (isAutoAssign) {
343
+ let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
344
+ qb.leftJoinAndSelect('iv.location', 'loc')
345
+ .andWhere('"iv"."domain_id" = :domainId')
346
+ .andWhere('"iv"."bizplace_id" = :bizplaceId')
347
+ .andWhere('"iv"."packing_type" = :packingType')
348
+ .andWhere('"iv"."product_id" = :productId')
349
+ .andWhere('"iv"."status" = :status')
350
+ .andWhere('"loc"."type" NOT IN (:...locationTypes)')
351
+ .setParameters({
352
+ domainId: domain.id,
353
+ bizplaceId: customerBizplace.id,
354
+ packingType: packingType,
355
+ productId: product.id,
356
+ status: INVENTORY_STATUS.STORED,
357
+ locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
358
+ })
359
+
360
+ switch (pickingStrategy) {
361
+ case 'FIFO':
362
+ qb.orderBy('"iv"."created_at"', 'ASC')
363
+ if (locationSortingRules?.length > 0) {
364
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
365
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
366
+ })
367
+ }
368
+ break
369
+
370
+ case 'LIFO':
371
+ qb.orderBy('"iv"."created_at"', 'DESC')
372
+ if (locationSortingRules?.length > 0) {
373
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
374
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
375
+ })
376
+ }
377
+ break
378
+
379
+ case 'FEFO':
380
+ qb.orderBy('"iv"."expiration_date"', 'ASC')
381
+ if (locationSortingRules?.length > 0) {
382
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
383
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
384
+ })
385
+ }
386
+ break
387
+
388
+ case 'FMFO':
389
+ qb.orderBy('"iv"."manufacture_date"', 'ASC')
390
+ if (locationSortingRules?.length > 0) {
391
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
392
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
393
+ })
394
+ }
395
+ break
396
+
397
+ case 'LOCATION':
398
+ if (locationSortingRules?.length > 0) {
399
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
400
+ idx === 0
401
+ ? qb.orderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
402
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
403
+ })
404
+ } else {
405
+ qb.orderBy('"loc"."name"', 'DESC')
406
+ }
407
+ break
408
+ }
350
409
 
351
- switch (pickingStrategy) {
352
- case 'FIFO':
353
- qb.orderBy('"iv"."created_at"', 'ASC')
354
- if (locationSortingRules?.length > 0) {
355
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
356
- qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
357
- })
358
- }
359
- break
360
-
361
- case 'LIFO':
362
- qb.orderBy('"iv"."created_at"', 'DESC')
363
- if (locationSortingRules?.length > 0) {
364
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
365
- qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
366
- })
367
- }
368
- break
369
-
370
- case 'FEFO':
371
- qb.orderBy('"iv"."expiration_date"', 'ASC')
372
- if (locationSortingRules?.length > 0) {
373
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
374
- qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
375
- })
376
- }
377
- break
378
-
379
- case 'FMFO':
380
- qb.orderBy('"iv"."manufacture_date"', 'ASC')
381
- if (locationSortingRules?.length > 0) {
382
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
383
- qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
384
- })
385
- }
386
- break
387
-
388
- case 'LOCATION':
389
- if (locationSortingRules?.length > 0) {
390
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
391
- idx === 0
392
- ? qb.orderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
393
- : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
394
- })
395
- } else {
396
- qb.orderBy('"loc"."name"', 'DESC')
410
+ let inventories: Inventory[] = await qb.getMany()
411
+ if (requiredDraft) {
412
+ if (!inventories?.length) {
413
+ context.body = {
414
+ result: await createDraftOrder(
415
+ client,
416
+ customerBizplace,
417
+ bodyReq,
418
+ bundleInfo,
419
+ domain,
420
+ context,
421
+ orderInventories,
422
+ tx
423
+ )
424
+ }
425
+ return
397
426
  }
398
- break
399
- }
400
-
401
- let inventories: Inventory[] = await qb.getMany()
402
- if (!inventories?.length) {
403
- context.body = {
404
- result: await createDraftOrder(
405
- client,
406
- customerBizplace,
407
- bodyReq,
408
- bundleInfo,
409
- domain,
410
- context,
411
- orderInventories,
412
- tx
413
- )
414
427
  }
415
- return
416
- }
417
- inventories = inventories
418
- .map(inventory => {
419
- return {
420
- ...inventory,
421
- remainQty: inventory.qty - inventory.lockedQty,
422
- remainUomValue: inventory.uomValue - inventory.lockedUomValue
428
+ inventories = inventories
429
+ .map(inventory => {
430
+ return {
431
+ ...inventory,
432
+ remainQty: inventory.qty - inventory.lockedQty,
433
+ remainUomValue: inventory.uomValue - inventory.lockedUomValue
434
+ }
435
+ })
436
+ .filter(inventory => inventory.remainQty > 0)
437
+
438
+ let targetInventories: WorksheetDetail[]
439
+ try {
440
+ targetInventories = _composeTargetInventories(context, product, orderInventory, inventories)
441
+ } catch (e) {
442
+ if (requiredDraft) {
443
+ context.body = {
444
+ result: await createDraftOrder(
445
+ client,
446
+ customerBizplace,
447
+ bodyReq,
448
+ bundleInfo,
449
+ domain,
450
+ context,
451
+ orderInventories,
452
+ tx
453
+ )
454
+ }
455
+ return
456
+ } else {
457
+ targetInventories = _composeTargetInventories(context, product, orderInventory, inventories)
423
458
  }
424
- })
425
- .filter(inventory => inventory.remainQty > 0)
426
-
427
- let targetInventories: WorksheetDetail[]
428
- try {
429
- targetInventories = _composeTargetInventories(context, product, orderInventory, inventories)
430
- } catch (e) {
431
- context.body = {
432
- result: await createDraftOrder(
433
- client,
434
- customerBizplace,
435
- bodyReq,
436
- bundleInfo,
437
- domain,
438
- context,
439
- orderInventories,
440
- tx
441
- )
442
459
  }
443
- return
444
- }
445
- targetInventories.map(item => {
446
- finalOrderInventories.push(item.targetInventory)
447
- })
448
- targetInventories.map(item => {
449
- finalOrderInventories.push(item.targetInventory)
450
- })
451
- } else {
452
- let filters: any = [
453
- {
454
- name: 'productName',
455
- operator: 'eq',
456
- value: orderInventory.product.sku
457
- },
458
- {
459
- name: 'bizplace_id',
460
- operator: 'eq',
461
- value: customerBizplaceId
460
+ if (targetInventories?.length) {
461
+ context.throw(404, t(`error.no inventories found`))
462
462
  }
463
- ]
464
-
465
- // 2. get all inventory data according to SKU and batchId
466
- const response = await client.query({
467
- query: gql`
468
- query inventoryProductGroup($filters: [Filter]) {
469
- inventoryProductGroup(filters: $filters) {
470
- items {
471
- productId
472
- productSKU
473
- productName
474
- batchId
475
- packingType
476
- remainQty
477
- remainUomValue
463
+ targetInventories.map(item => {
464
+ finalOrderInventories.push(item.targetInventory)
465
+ })
466
+ } else {
467
+ let filters: any = [
468
+ {
469
+ name: 'productName',
470
+ operator: 'eq',
471
+ value: orderInventory.product.sku
472
+ },
473
+ {
474
+ name: 'bizplace_id',
475
+ operator: 'eq',
476
+ value: customerBizplaceId
477
+ }
478
+ ]
479
+
480
+ // 2. get all inventory data according to SKU and batchId
481
+ const response = await client.query({
482
+ query: gql`
483
+ query inventoryProductGroup($filters: [Filter]) {
484
+ inventoryProductGroup(filters: $filters) {
485
+ items {
486
+ productId
487
+ productSKU
488
+ productName
489
+ batchId
490
+ packingType
491
+ remainQty
492
+ remainUomValue
493
+ }
494
+ total
478
495
  }
479
- total
496
+ }
497
+ `,
498
+ variables: { filters },
499
+ context: {
500
+ ...context,
501
+ state: {
502
+ ...context.state,
503
+ bizplace: customerBizplace,
504
+ domain
480
505
  }
481
506
  }
482
- `,
483
- variables: { filters },
484
- context: {
485
- ...context,
486
- state: {
487
- ...context.state,
488
- bizplace: customerBizplace,
489
- domain
507
+ } as any)
508
+
509
+ if (!response.errors) {
510
+ const items = response.data.inventoryProductGroup.items || []
511
+ if (requiredDraft) {
512
+ if (!items?.length) {
513
+ context.body = {
514
+ result: await createDraftOrder(
515
+ client,
516
+ customerBizplace,
517
+ bodyReq,
518
+ bundleInfo,
519
+ domain,
520
+ context,
521
+ orderInventories,
522
+ tx
523
+ )
524
+ }
525
+ return
526
+ }
490
527
  }
491
- }
492
- } as any)
493
528
 
494
- if (!response.errors) {
495
- const items = response.data.inventoryProductGroup.items || []
496
- if (!items?.length) {
497
- context.body = {
498
- result: await createDraftOrder(
499
- client,
500
- customerBizplace,
501
- bodyReq,
502
- bundleInfo,
503
- domain,
504
- context,
505
- orderInventories,
506
- tx
507
- )
508
- }
509
- return
510
- }
529
+ items.forEach(async item => {
530
+ if (requiredDraft) {
531
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
532
+ context.body = {
533
+ result: await createDraftOrder(
534
+ client,
535
+ customerBizplace,
536
+ bodyReq,
537
+ bundleInfo,
538
+ domain,
539
+ context,
540
+ orderInventories,
541
+ tx
542
+ )
543
+ }
544
+ return
545
+ }
546
+ }
511
547
 
512
- items.forEach(async item => {
513
- if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
548
+ // 3. add more information to orderInventory
549
+ orderInventory.product = { id: item.productId }
550
+ orderInventory.packingType = item.packingType
551
+ orderInventory.batchId = item.batchId
552
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
553
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
554
+ orderInventory.uom = item.uom
555
+ })
556
+
557
+ delete orderInventory.uomValue
558
+ finalOrderInventories.push(orderInventory)
559
+ } else {
560
+ if (requiredDraft) {
514
561
  context.body = {
515
562
  result: await createDraftOrder(
516
563
  client,
@@ -525,174 +572,172 @@ router.post(
525
572
  }
526
573
  return
527
574
  }
528
-
529
- // 3. add more information to orderInventory
530
- orderInventory.product = { id: item.productId }
531
- orderInventory.packingType = item.packingType
532
- orderInventory.batchId = item.batchId
533
- orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
534
- orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
535
- orderInventory.uom = item.uom
536
- })
537
-
538
- delete orderInventory.uomValue
539
- finalOrderInventories.push(orderInventory)
540
- } else {
541
- context.body = {
542
- result: await createDraftOrder(
543
- client,
544
- customerBizplace,
545
- bodyReq,
546
- bundleInfo,
547
- domain,
548
- context,
549
- orderInventories,
550
- tx
551
- )
552
575
  }
553
- return
554
576
  }
555
577
  }
556
578
  }
557
- }
558
579
 
559
- context.body = {
560
- result: (
561
- await client.mutate({
562
- mutation: gql`
563
- mutation addReleaseGood($releaseGood: NewReleaseGood, $shippingOrder: ShippingOrderPatch, $file: Upload) {
564
- addReleaseGood(releaseGood: $releaseGood, shippingOrder: $shippingOrder, file: $file) {
565
- id
566
- name
567
- refNo
568
- status
569
- truckNo
570
- ownTransport
571
- crossDocking
572
- marketplaceOrderStatus
573
- billingAddress
574
- deliveryAddress1
575
- deliveryAddress2
576
- deliveryAddress3
577
- deliveryAddress4
578
- deliveryAddress5
579
- attentionTo
580
- attentionCompany
581
- city
582
- state
583
- postalCode
584
- country
585
- phone1
586
- phone2
587
- email
588
- transporter
589
- trackingNo
590
- airwayBill
591
- invoice
592
- type
593
- storeName
594
- storeId
595
- routeId
596
- stopId
597
- arrivalNotice {
598
- name
599
- }
600
- exportOption
601
- releaseDate
602
- collectionOrderNo
603
- bizplace {
580
+ context.body = {
581
+ result: (
582
+ await client.mutate({
583
+ mutation: gql`
584
+ mutation addReleaseGood(
585
+ $releaseGood: NewReleaseGood
586
+ $shippingOrder: ShippingOrderPatch
587
+ $file: Upload
588
+ ) {
589
+ addReleaseGood(releaseGood: $releaseGood, shippingOrder: $shippingOrder, file: $file) {
590
+ id
604
591
  name
605
- }
606
- orderInventories {
607
- product {
608
- sku
592
+ refNo
593
+ status
594
+ truckNo
595
+ ownTransport
596
+ crossDocking
597
+ marketplaceOrderStatus
598
+ billingAddress
599
+ deliveryAddress1
600
+ deliveryAddress2
601
+ deliveryAddress3
602
+ deliveryAddress4
603
+ deliveryAddress5
604
+ attentionTo
605
+ attentionCompany
606
+ city
607
+ state
608
+ postalCode
609
+ country
610
+ phone1
611
+ phone2
612
+ email
613
+ transporter
614
+ trackingNo
615
+ airwayBill
616
+ invoice
617
+ type
618
+ storeName
619
+ storeId
620
+ routeId
621
+ stopId
622
+ arrivalNotice {
609
623
  name
610
- description
611
624
  }
612
- batchId
613
- packingType
614
- packingSize
615
- releaseQty
616
- releaseUomValue
617
- }
618
- orderVass {
619
- vas {
625
+ exportOption
626
+ releaseDate
627
+ collectionOrderNo
628
+ bizplace {
620
629
  name
621
- operationGuide
622
- operationGuideType
623
630
  }
624
- set
625
- targetType
626
- targetBatchId
627
- targetProduct {
628
- name
631
+ orderInventories {
632
+ product {
633
+ sku
634
+ name
635
+ description
636
+ }
637
+ batchId
638
+ packingType
639
+ packingSize
640
+ releaseQty
641
+ releaseUomValue
642
+ }
643
+ orderVass {
644
+ vas {
645
+ name
646
+ operationGuide
647
+ operationGuideType
648
+ }
649
+ set
650
+ targetType
651
+ targetBatchId
652
+ targetProduct {
653
+ name
654
+ description
655
+ }
656
+ packingType
657
+ qty
658
+ uomValue
659
+ otherTarget
629
660
  description
661
+ remark
662
+ status
663
+ operationGuide
630
664
  }
631
- packingType
632
- qty
633
- uomValue
634
- otherTarget
635
- description
636
- remark
637
- status
638
- operationGuide
639
665
  }
640
666
  }
667
+ `,
668
+ variables: {
669
+ releaseGood: {
670
+ customerBizplaceId: customerBizplace.id,
671
+ courierOption: bodyReq.courierOption,
672
+ collectionOrderNo: bodyReq.collectionOrderNo,
673
+ exportOption: bodyReq.exportOption,
674
+ orderInventories: finalOrderInventories,
675
+ ownTransport: bodyReq.ownTransport,
676
+ packingOption: bodyReq.packingOption,
677
+ refNo: bodyReq.refNo,
678
+ refNo2: bodyReq?.refNo2,
679
+ releaseDate: bodyReq.releaseDate,
680
+ type: bodyReq.type,
681
+ marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
682
+ remark: bodyReq?.remark || null,
683
+ billingAddress: bodyReq?.billTo?.billingAddress || null,
684
+ deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
685
+ deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
686
+ deliveryAddress3: bodyReq?.deliverTo?.deliveryAddress3 || null,
687
+ deliveryAddress4: bodyReq?.deliverTo?.deliveryAddress4 || null,
688
+ deliveryAddress5: bodyReq?.deliverTo?.deliveryAddress5 || null,
689
+ attentionTo: bodyReq?.deliverTo?.attentionTo || null,
690
+ attentionCompany: bodyReq?.deliverTo?.attentionCompany || null,
691
+ city: bodyReq?.deliverTo?.city || null,
692
+ state: bodyReq?.deliverTo?.state || null,
693
+ postalCode: bodyReq?.deliverTo?.postalCode || null,
694
+ country: bodyReq?.deliverTo?.country || null,
695
+ phone1: bodyReq?.deliverTo?.phone1 || null,
696
+ phone2: bodyReq?.deliverTo?.phone2 || null,
697
+ email: bodyReq?.deliverTo?.email || null,
698
+ transporter: bodyReq?.transporter,
699
+ trackingNo: bodyReq?.trackingNo,
700
+ airwayBill: bodyReq?.airwayBill,
701
+ invoice: bodyReq?.invoice,
702
+ bundleInfo: JSON.stringify(bundleInfo),
703
+ storeName: bodyReq?.storeName,
704
+ storeId: bodyReq?.storeId,
705
+ routeId: bodyReq?.routeId,
706
+ stopId: bodyReq?.stopId
707
+ }
708
+ },
709
+ context: {
710
+ ...context,
711
+ hasUpload: true,
712
+ state: {
713
+ ...context.state,
714
+ customerBizplace,
715
+ domain,
716
+ type: 'api'
717
+ }
641
718
  }
642
- `,
643
- variables: {
644
- releaseGood: {
645
- customerBizplaceId: customerBizplace.id,
646
- courierOption: bodyReq.courierOption,
647
- collectionOrderNo: bodyReq.collectionOrderNo,
648
- exportOption: bodyReq.exportOption,
649
- orderInventories: finalOrderInventories,
650
- ownTransport: bodyReq.ownTransport,
651
- packingOption: bodyReq.packingOption,
652
- refNo: bodyReq.refNo,
653
- refNo2: bodyReq?.refNo2,
654
- releaseDate: bodyReq.releaseDate,
655
- type: bodyReq.type,
656
- marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
657
- remark: bodyReq?.remark || null,
658
- billingAddress: bodyReq?.billTo?.billingAddress || null,
659
- deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
660
- deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
661
- deliveryAddress3: bodyReq?.deliverTo?.deliveryAddress3 || null,
662
- deliveryAddress4: bodyReq?.deliverTo?.deliveryAddress4 || null,
663
- deliveryAddress5: bodyReq?.deliverTo?.deliveryAddress5 || null,
664
- attentionTo: bodyReq?.deliverTo?.attentionTo || null,
665
- attentionCompany: bodyReq?.deliverTo?.attentionCompany || null,
666
- city: bodyReq?.deliverTo?.city || null,
667
- state: bodyReq?.deliverTo?.state || null,
668
- postalCode: bodyReq?.deliverTo?.postalCode || null,
669
- country: bodyReq?.deliverTo?.country || null,
670
- phone1: bodyReq?.deliverTo?.phone1 || null,
671
- phone2: bodyReq?.deliverTo?.phone2 || null,
672
- email: bodyReq?.deliverTo?.email || null,
673
- transporter: bodyReq?.transporter,
674
- trackingNo: bodyReq?.trackingNo,
675
- airwayBill: bodyReq?.airwayBill,
676
- invoice: bodyReq?.invoice,
677
- bundleInfo: JSON.stringify(bundleInfo),
678
- storeName: bodyReq?.storeName,
679
- storeId: bodyReq?.storeId,
680
- routeId: bodyReq?.routeId,
681
- stopId: bodyReq?.stopId
682
- }
683
- },
684
- context: {
685
- ...context,
686
- hasUpload: true,
687
- state: {
688
- ...context.state,
689
- customerBizplace,
690
- domain,
691
- type: 'api'
692
- }
693
- }
694
- } as any)
695
- ).data
719
+ } as any)
720
+ ).data
721
+ }
722
+
723
+ createPayloadLog(
724
+ customerBizplaceId,
725
+ '/unstable/warehouse/:warehouseId/add-release-order',
726
+ bodyReq,
727
+ context.body,
728
+ context,
729
+ PayloadType.INGESTION
730
+ )
731
+ } catch (error) {
732
+ createPayloadLog(
733
+ customerBizplaceId,
734
+ '/unstable/warehouse/:warehouseId/add-release-order',
735
+ bodyReq,
736
+ error,
737
+ context,
738
+ PayloadType.INGESTION
739
+ )
740
+ throw error
696
741
  }
697
742
  })
698
743
  }
@@ -759,7 +804,7 @@ export function _composeTargetInventories(context, product, record, inventories)
759
804
  })
760
805
 
761
806
  if (totalInventoryQty < record.releaseQty) {
762
- context.throw(404, t(`error.invalid release qty for ${product?.sku}`))
807
+ context.throw(404, t(`error.invalid release qty for ${product?.sku}`)) //
763
808
  }
764
809
 
765
810
  let targetInventories = []