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