@things-factory/operato-hub 4.1.41 → 4.2.2

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.
@@ -12,7 +12,7 @@ import { WorksheetDetail } from '@things-factory/worksheet-base'
12
12
  import { businessMiddleware } from '../../../../middlewares/business-middleware'
13
13
  import { warehouseMiddleware } from '../../../../middlewares/warehouse-middleware'
14
14
 
15
- const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
15
+ const debug = require('debug')('things-factory:operato-hub:restful-api:v1:add-release-order')
16
16
 
17
17
  router.post(
18
18
  '/v1/warehouse/:warehouseId/add-release-order',
@@ -65,12 +65,13 @@ router.post(
65
65
 
66
66
  const orderInventories: OrderInventory[] = bodyReq.orderInventories
67
67
  let newOrderInventories: OrderInventory[] = []
68
+ let bundleInfo: any[] = []
68
69
 
69
70
  await Promise.all(
70
71
  orderInventories.map(async orderInventory => {
71
72
  const sku: string = orderInventory.product?.sku
72
73
  const refCode: string = orderInventory.product?.refCode
73
- const packingType: string = orderInventory.packingType
74
+ const { packingType, packingSize }: { packingType: string; packingSize: number } = orderInventory
74
75
 
75
76
  const qb: SelectQueryBuilder<ProductDetail> = tx
76
77
  .getRepository(ProductDetail)
@@ -94,6 +95,10 @@ router.post(
94
95
  qb.andWhere('PD.packing_type = :packingType', { packingType })
95
96
  }
96
97
 
98
+ if (packingSize) {
99
+ qb.andWhere('PD.packing_size = :packingSize', { packingSize })
100
+ }
101
+
97
102
  if (!packingType && !refCode) {
98
103
  qb.andWhere('PD.is_default = :isDefault', { isDefault: true })
99
104
  }
@@ -138,6 +143,11 @@ router.post(
138
143
  const foundProductBundle: ProductBundle = await qb.getOne()
139
144
 
140
145
  if (foundProductBundle) {
146
+ bundleInfo.push({
147
+ productBundleId: foundProductBundle.id,
148
+ sku: foundProductBundle.sku,
149
+ releaseQty: orderInventory.releaseQty
150
+ })
141
151
  const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
142
152
  bundleProductSettings.map(bundleProductSetting => {
143
153
  const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
@@ -151,9 +161,15 @@ router.post(
151
161
  newOrderInventories.push(newOrderInventory)
152
162
  })
153
163
  } else {
164
+ if (sku && refCode) {
165
+ context.throw(404, t(`error.sku ${sku} and refCode ${refCode} not exist in product / bundle master`))
166
+ }
167
+
154
168
  if (sku) {
155
169
  context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
156
- } else if (refCode) {
170
+ }
171
+
172
+ if (refCode) {
157
173
  context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
158
174
  }
159
175
  }
@@ -185,11 +201,35 @@ router.post(
185
201
  }
186
202
  ]
187
203
 
204
+ let sortings: any = []
205
+
206
+ switch (orderInventory.product.pickingStrategy) {
207
+ case 'FIFO':
208
+ sortings.push({ name: 'created_at', desc: false })
209
+ break
210
+
211
+ case 'LIFO':
212
+ sortings.push({ name: 'created_at', desc: true })
213
+ break
214
+
215
+ case 'FEFO':
216
+ sortings.push({ name: 'expiration_date', desc: false })
217
+ break
218
+
219
+ case 'FMFO':
220
+ sortings.push({ name: 'manufacture_date', desc: false })
221
+ break
222
+ }
223
+
188
224
  // 2. get all inventory data according to SKU and batchId
189
225
  const response = await client.query({
190
226
  query: gql`
191
- query inventoriesByPallet($filters: [Filter]) {
192
- inventoriesByPallet(filters: $filters) {
227
+ query inventoriesByPallet($filters: [Filter], $sortings: [Sorting], $locationSortingRules: [Sorting]) {
228
+ inventoriesByPallet(
229
+ filters: $filters
230
+ sortings: $sortings
231
+ locationSortingRules: $locationSortingRules
232
+ ) {
193
233
  items {
194
234
  product {
195
235
  id
@@ -199,14 +239,13 @@ router.post(
199
239
  batchId
200
240
  packingType
201
241
  remainQty
202
- remainWeight
203
242
  remainUomValue
204
243
  }
205
244
  total
206
245
  }
207
246
  }
208
247
  `,
209
- variables: { filters, locationSortingRules },
248
+ variables: { filters, sortings, locationSortingRules },
210
249
  context: {
211
250
  ...context,
212
251
  state: {
@@ -255,8 +294,8 @@ router.post(
255
294
 
256
295
  for (let i: number = 0; i < newOrderInventories.length; i++) {
257
296
  const orderInventory: OrderInventory = newOrderInventories[i]
258
- const product: Product = orderInventory.product
259
- const packingType: string = orderInventory.packingType
297
+ const { product, packingType }: { product: Product; packingType: string } = orderInventory
298
+ const pickingStrategy: string = product.pickingStrategy
260
299
 
261
300
  if (isAutoAssign) {
262
301
  let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
@@ -276,14 +315,54 @@ router.post(
276
315
  locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
277
316
  })
278
317
 
279
- if (locationSortingRules?.length > 0) {
280
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
281
- idx === 0
282
- ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
283
- : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
284
- })
285
- } else {
286
- qb.addOrderBy('"iv"."created_at"', 'ASC')
318
+ switch (pickingStrategy) {
319
+ case 'FIFO':
320
+ qb.orderBy('"iv"."created_at"', 'ASC')
321
+ if (locationSortingRules?.length > 0) {
322
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
323
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
324
+ })
325
+ }
326
+ break
327
+
328
+ case 'LIFO':
329
+ qb.orderBy('"iv"."created_at"', 'DESC')
330
+ if (locationSortingRules?.length > 0) {
331
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
332
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
333
+ })
334
+ }
335
+ break
336
+
337
+ case 'FEFO':
338
+ qb.orderBy('"iv"."expiration_date"', 'ASC')
339
+ if (locationSortingRules?.length > 0) {
340
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
341
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
342
+ })
343
+ }
344
+ break
345
+
346
+ case 'FMFO':
347
+ qb.orderBy('"iv"."manufacture_date"', 'ASC')
348
+ if (locationSortingRules?.length > 0) {
349
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
350
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
351
+ })
352
+ }
353
+ break
354
+
355
+ case 'LOCATION':
356
+ if (locationSortingRules?.length > 0) {
357
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
358
+ idx === 0
359
+ ? qb.orderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
360
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
361
+ })
362
+ } else {
363
+ qb.orderBy('"loc"."name"', 'DESC')
364
+ }
365
+ break
287
366
  }
288
367
 
289
368
  let inventories: Inventory[] = await qb.getMany()
@@ -334,7 +413,6 @@ router.post(
334
413
  batchId
335
414
  packingType
336
415
  remainQty
337
- remainWeight
338
416
  remainUomValue
339
417
  }
340
418
  total
@@ -417,6 +495,10 @@ router.post(
417
495
  airwayBill
418
496
  invoice
419
497
  type
498
+ storeName
499
+ storeId
500
+ routeId
501
+ stopId
420
502
  arrivalNotice {
421
503
  name
422
504
  }
@@ -477,6 +559,7 @@ router.post(
477
559
  releaseDate: bodyReq.releaseDate,
478
560
  type: bodyReq.type,
479
561
  marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
562
+ remark: bodyReq?.remark || null,
480
563
  billingAddress: bodyReq?.billTo?.billingAddress || null,
481
564
  deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
482
565
  deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
@@ -495,7 +578,12 @@ router.post(
495
578
  transporter: bodyReq?.transporter,
496
579
  trackingNo: bodyReq?.trackingNo,
497
580
  airwayBill: bodyReq?.airwayBill,
498
- invoice: bodyReq?.invoice
581
+ invoice: bodyReq?.invoice,
582
+ bundleInfo: JSON.stringify(bundleInfo),
583
+ storeName: bodyReq?.storeName,
584
+ storeId: bodyReq?.storeId,
585
+ routeId: bodyReq?.routeId,
586
+ stopId: bodyReq?.stopId
499
587
  }
500
588
  },
501
589
  context: {
@@ -12,7 +12,7 @@ import { WorksheetDetail } from '@things-factory/worksheet-base'
12
12
  import { businessMiddleware } from '../../../../middlewares/business-middleware'
13
13
  import { warehouseMiddleware } from '../../../../middlewares/warehouse-middleware'
14
14
 
15
- const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
15
+ const debug = require('debug')('things-factory:operato-hub:restful-api:v2:add-release-order')
16
16
 
17
17
  router.post(
18
18
  '/v2/warehouse/:warehouseId/add-release-order',
@@ -65,12 +65,13 @@ router.post(
65
65
 
66
66
  const orderInventories: OrderInventory[] = bodyReq.orderInventories
67
67
  let newOrderInventories: OrderInventory[] = []
68
+ let bundleInfo: any[] = []
68
69
 
69
70
  await Promise.all(
70
71
  orderInventories.map(async orderInventory => {
71
72
  const sku: string = orderInventory.product?.sku
72
73
  const refCode: string = orderInventory.product?.refCode
73
- const packingType: string = orderInventory.packingType
74
+ const { packingType, packingSize }: { packingType: string; packingSize: number } = orderInventory
74
75
 
75
76
  const qb: SelectQueryBuilder<ProductDetail> = tx
76
77
  .getRepository(ProductDetail)
@@ -97,6 +98,9 @@ router.post(
97
98
  if (!packingType && !refCode) {
98
99
  qb.andWhere('PD.is_default = :isDefault', { isDefault: true })
99
100
  }
101
+ if (packingSize) {
102
+ qb.andWhere('PD.packing_size = :packingSize', { packingSize })
103
+ }
100
104
 
101
105
  const productDetail: ProductDetail = await qb.getOne()
102
106
 
@@ -138,6 +142,11 @@ router.post(
138
142
  const foundProductBundle: ProductBundle = await qb.getOne()
139
143
 
140
144
  if (foundProductBundle) {
145
+ bundleInfo.push({
146
+ productBundleId: foundProductBundle.id,
147
+ sku: foundProductBundle.sku,
148
+ releaseQty: orderInventory.releaseQty
149
+ })
141
150
  const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
142
151
  bundleProductSettings.map(bundleProductSetting => {
143
152
  const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
@@ -151,9 +160,15 @@ router.post(
151
160
  newOrderInventories.push(newOrderInventory)
152
161
  })
153
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
+
154
167
  if (sku) {
155
168
  context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
156
- } else if (refCode) {
169
+ }
170
+
171
+ if (refCode) {
157
172
  context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
158
173
  }
159
174
  }
@@ -185,11 +200,35 @@ router.post(
185
200
  }
186
201
  ]
187
202
 
203
+ let sortings: any = []
204
+
205
+ switch (orderInventory.product.pickingStrategy) {
206
+ case 'FIFO':
207
+ sortings.push({ name: 'created_at', desc: false })
208
+ break
209
+
210
+ case 'LIFO':
211
+ sortings.push({ name: 'created_at', desc: true })
212
+ break
213
+
214
+ case 'FEFO':
215
+ sortings.push({ name: 'expiration_date', desc: false })
216
+ break
217
+
218
+ case 'FMFO':
219
+ sortings.push({ name: 'manufacture_date', desc: false })
220
+ break
221
+ }
222
+
188
223
  // 2. get all inventory data according to SKU and batchId
189
224
  const response = await client.query({
190
225
  query: gql`
191
- query inventoriesByPallet($filters: [Filter]) {
192
- inventoriesByPallet(filters: $filters) {
226
+ query inventoriesByPallet($filters: [Filter], $sortings: [Sorting], $locationSortingRules: [Sorting]) {
227
+ inventoriesByPallet(
228
+ filters: $filters
229
+ sortings: $sortings
230
+ locationSortingRules: $locationSortingRules
231
+ ) {
193
232
  items {
194
233
  product {
195
234
  id
@@ -199,14 +238,13 @@ router.post(
199
238
  batchId
200
239
  packingType
201
240
  remainQty
202
- remainWeight
203
241
  remainUomValue
204
242
  }
205
243
  total
206
244
  }
207
245
  }
208
246
  `,
209
- variables: { filters, locationSortingRules },
247
+ variables: { filters, sortings, locationSortingRules },
210
248
  context: {
211
249
  ...context,
212
250
  state: {
@@ -255,8 +293,8 @@ router.post(
255
293
 
256
294
  for (let i: number = 0; i < newOrderInventories.length; i++) {
257
295
  const orderInventory: OrderInventory = newOrderInventories[i]
258
- const product: Product = orderInventory.product
259
- const packingType: string = orderInventory.packingType
296
+ const { product, packingType }: { product: Product; packingType: string } = orderInventory
297
+ const pickingStrategy: string = product.pickingStrategy
260
298
 
261
299
  if (isAutoAssign) {
262
300
  let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
@@ -276,14 +314,54 @@ router.post(
276
314
  locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
277
315
  })
278
316
 
279
- if (locationSortingRules?.length > 0) {
280
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
281
- idx === 0
282
- ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
283
- : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
284
- })
285
- } else {
286
- qb.addOrderBy('"iv"."created_at"', 'ASC')
317
+ switch (pickingStrategy) {
318
+ case 'FIFO':
319
+ qb.orderBy('"iv"."created_at"', 'ASC')
320
+ if (locationSortingRules?.length > 0) {
321
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
322
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
323
+ })
324
+ }
325
+ break
326
+
327
+ case 'LIFO':
328
+ qb.orderBy('"iv"."created_at"', 'DESC')
329
+ if (locationSortingRules?.length > 0) {
330
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
331
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
332
+ })
333
+ }
334
+ break
335
+
336
+ case 'FEFO':
337
+ qb.orderBy('"iv"."expiration_date"', 'ASC')
338
+ if (locationSortingRules?.length > 0) {
339
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
340
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
341
+ })
342
+ }
343
+ break
344
+
345
+ case 'FMFO':
346
+ qb.orderBy('"iv"."manufacture_date"', 'ASC')
347
+ if (locationSortingRules?.length > 0) {
348
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
349
+ qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
350
+ })
351
+ }
352
+ break
353
+
354
+ case 'LOCATION':
355
+ if (locationSortingRules?.length > 0) {
356
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
357
+ idx === 0
358
+ ? qb.orderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
359
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
360
+ })
361
+ } else {
362
+ qb.orderBy('"loc"."name"', 'DESC')
363
+ }
364
+ break
287
365
  }
288
366
 
289
367
  let inventories: Inventory[] = await qb.getMany()
@@ -334,7 +412,6 @@ router.post(
334
412
  batchId
335
413
  packingType
336
414
  remainQty
337
- remainWeight
338
415
  remainUomValue
339
416
  }
340
417
  total
@@ -417,6 +494,10 @@ router.post(
417
494
  airwayBill
418
495
  invoice
419
496
  type
497
+ storeName
498
+ storeId
499
+ routeId
500
+ stopId
420
501
  arrivalNotice {
421
502
  name
422
503
  }
@@ -477,6 +558,7 @@ router.post(
477
558
  releaseDate: bodyReq.releaseDate,
478
559
  type: bodyReq.type,
479
560
  marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
561
+ remark: bodyReq?.remark || null,
480
562
  billingAddress: bodyReq?.billTo?.billingAddress || null,
481
563
  deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
482
564
  deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
@@ -495,7 +577,12 @@ router.post(
495
577
  transporter: bodyReq?.transporter,
496
578
  trackingNo: bodyReq?.trackingNo,
497
579
  airwayBill: bodyReq?.airwayBill,
498
- invoice: bodyReq?.invoice
580
+ invoice: bodyReq?.invoice,
581
+ bundleInfo: JSON.stringify(bundleInfo),
582
+ storeName: bodyReq?.storeName,
583
+ storeId: bodyReq?.storeId,
584
+ routeId: bodyReq?.routeId,
585
+ stopId: bodyReq?.stopId
499
586
  }
500
587
  },
501
588
  context: {
@@ -16,6 +16,7 @@
16
16
  "field.bundle_qty": "bundle qty",
17
17
  "field.packing_type": "packing type",
18
18
  "field.parent_product_ref": "parent product ref",
19
+ "field.picking_strategy": "picking strategy",
19
20
  "field.uom": "uom",
20
21
  "field.uom_value": "uom value",
21
22
 
@@ -5,6 +5,7 @@
5
5
  "field.bundle_qty": "[ko]bundle qty",
6
6
  "field.packing_type": "[ko]packing type",
7
7
  "field.parent_product_ref": "[ko]parent product ref",
8
+ "field.picking_strategy": "[ko]picking strategy",
8
9
  "field.uom": "[ko]uom",
9
10
  "field.uom_value": "[ko]uom value",
10
11
 
@@ -14,6 +14,7 @@
14
14
  "field.bundle_qty": "[ms] bundle qty",
15
15
  "field.packing_type": "[ms] packing type",
16
16
  "field.parent_product_ref": "[ms] parent product ref",
17
+ "field.picking_strategy": "[ms]picking strategy",
17
18
  "field.uom": "[ms] uom",
18
19
  "field.uom_value": "[ms] uom value",
19
20
 
@@ -18,6 +18,7 @@
18
18
  "field.bundle_qty": "捆绑数量",
19
19
  "field.packing_type": "包装类型",
20
20
  "field.parent_product_ref": "上层产品参考",
21
+ "field.picking_strategy": "[zh]picking strategy",
21
22
  "field.uom": "度量单位",
22
23
  "field.uom_value": "度量单位值",
23
24