@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,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'
@@ -13,9 +14,10 @@ import { businessMiddleware } from '../../../../middlewares/business-middleware'
13
14
  import { warehouseMiddleware } from '../../../../middlewares/warehouse-middleware'
14
15
 
15
16
  const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
17
+ const apiVersion = 'unstable'
16
18
 
17
19
  router.post(
18
- '/unstable/warehouse/:warehouseId/add-release-order',
20
+ `/${apiVersion}/warehouse/:warehouseId/add-release-order`,
19
21
  businessMiddleware,
20
22
  warehouseMiddleware,
21
23
  async (context, next) => {
@@ -23,255 +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: 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 })
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
+ }
100
66
  }
67
+ }
101
68
 
102
- const productDetail: ProductDetail = await qb.getOne()
103
-
104
- if (productDetail) {
105
- let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
69
+ const orderInventories: OrderInventory[] = bodyReq.orderInventories
70
+ let newOrderInventories: OrderInventory[] = []
71
+ let bundleInfo: any[] = []
106
72
 
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
- }
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
113
78
 
114
- if (!productDetail) {
115
79
  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 })
80
+ .getRepository(ProductDetail)
81
+ .createQueryBuilder('PD')
82
+ .innerJoinAndSelect('PD.product', 'PROD')
83
+ .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
122
84
 
123
85
  if (!sku && !refCode) {
124
86
  context.throw(404, t('error.sku or refCode not found'))
125
87
  }
126
88
 
127
89
  if (refCode) {
128
- qb.andWhere('PB.ref_code = :refCode', { refCode })
90
+ qb.andWhere('PD.ref_code = :refCode', { refCode })
129
91
  }
130
92
 
131
93
  if (sku) {
132
- qb.andWhere('PB.sku = :sku', { sku })
94
+ qb.andWhere('PROD.sku = :sku', { sku })
133
95
  }
134
96
 
135
97
  if (packingType) {
136
- qb.andWhere('PB.packing_type = :packingType', { packingType })
98
+ qb.andWhere('PD.packing_type = :packingType', { packingType })
137
99
  }
138
100
 
139
- const foundProductBundle: ProductBundle = await qb.getOne()
101
+ if (!packingType && !refCode) {
102
+ qb.andWhere('PD.is_default = :isDefault', { isDefault: true })
103
+ }
140
104
 
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`))
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 })
162
132
  }
163
133
 
164
134
  if (sku) {
165
- context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
135
+ qb.andWhere('PB.sku = :sku', { sku })
166
136
  }
167
137
 
168
- if (refCode) {
169
- context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
138
+ if (packingType) {
139
+ qb.andWhere('PB.packing_type = :packingType', { packingType })
170
140
  }
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]
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
+ }
197
174
  }
198
- ]
175
+ }
176
+ })
177
+ )
199
178
 
200
- 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
+ ]
201
202
 
202
- switch (orderInventory.product.pickingStrategy) {
203
- case 'FIFO':
204
- sortings.push({ name: 'created_at', desc: false })
205
- break
203
+ let sortings: any = []
206
204
 
207
- case 'LIFO':
208
- sortings.push({ name: 'created_at', desc: true })
209
- break
205
+ switch (orderInventory.product.pickingStrategy) {
206
+ case 'FIFO':
207
+ sortings.push({ name: 'created_at', desc: false })
208
+ break
210
209
 
211
- case 'FEFO':
212
- sortings.push({ name: 'expiration_date', desc: false })
213
- break
210
+ case 'LIFO':
211
+ sortings.push({ name: 'created_at', desc: true })
212
+ break
214
213
 
215
- case 'FMFO':
216
- sortings.push({ name: 'manufacture_date', desc: false })
217
- break
218
- }
214
+ case 'FEFO':
215
+ sortings.push({ name: 'expiration_date', desc: false })
216
+ break
219
217
 
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
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]
228
230
  ) {
229
- items {
230
- product {
231
- id
232
- name
233
- 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
234
246
  }
235
- batchId
236
- packingType
237
- remainQty
238
- remainUomValue
247
+ total
239
248
  }
240
- total
249
+ }
250
+ `,
251
+ variables: { filters, sortings, locationSortingRules },
252
+ context: {
253
+ ...context,
254
+ state: {
255
+ ...context.state,
256
+ bizplace: customerBizplace,
257
+ domain
241
258
  }
242
259
  }
243
- `,
244
- variables: { filters, sortings, locationSortingRules },
245
- context: {
246
- ...context,
247
- state: {
248
- ...context.state,
249
- bizplace: customerBizplace,
250
- 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
+ }
251
280
  }
252
- }
253
- } as any)
254
281
 
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
- }
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
+ }
272
300
 
273
- items.forEach(async item => {
274
- 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) {
275
313
  context.body = {
276
314
  result: await createDraftOrder(
277
315
  client,
@@ -286,225 +324,241 @@ router.post(
286
324
  }
287
325
  return
288
326
  }
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
327
  }
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
- })
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
+ }
347
410
 
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')
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
394
427
  }
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
428
  }
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
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)
420
459
  }
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
460
  }
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
461
+ if (!targetInventories?.length) {
462
+ context.throw(404, t(`error.no inventories found`))
456
463
  }
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
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
472
496
  }
473
- total
497
+ }
498
+ `,
499
+ variables: { filters },
500
+ context: {
501
+ ...context,
502
+ state: {
503
+ ...context.state,
504
+ bizplace: customerBizplace,
505
+ domain
474
506
  }
475
507
  }
476
- `,
477
- variables: { filters },
478
- context: {
479
- ...context,
480
- state: {
481
- ...context.state,
482
- bizplace: customerBizplace,
483
- 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
+ }
484
528
  }
485
- }
486
- } as any)
487
529
 
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
- }
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
+ }
505
548
 
506
- items.forEach(async item => {
507
- 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) {
508
562
  context.body = {
509
563
  result: await createDraftOrder(
510
564
  client,
@@ -519,174 +573,172 @@ router.post(
519
573
  }
520
574
  return
521
575
  }
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
576
  }
547
- return
548
577
  }
549
578
  }
550
579
  }
551
- }
552
580
 
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 {
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
598
592
  name
599
- }
600
- orderInventories {
601
- product {
602
- 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 {
603
624
  name
604
- description
605
625
  }
606
- batchId
607
- packingType
608
- packingSize
609
- releaseQty
610
- releaseUomValue
611
- }
612
- orderVass {
613
- vas {
626
+ exportOption
627
+ releaseDate
628
+ collectionOrderNo
629
+ bizplace {
614
630
  name
615
- operationGuide
616
- operationGuideType
617
631
  }
618
- set
619
- targetType
620
- targetBatchId
621
- targetProduct {
622
- 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
623
661
  description
662
+ remark
663
+ status
664
+ operationGuide
624
665
  }
625
- packingType
626
- qty
627
- uomValue
628
- otherTarget
629
- description
630
- remark
631
- status
632
- operationGuide
633
666
  }
634
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
+ }
635
719
  }
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
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
690
742
  }
691
743
  })
692
744
  }