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