@things-factory/operato-hub 4.3.11 → 4.3.16

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