@things-factory/operato-hub 4.0.9 → 4.0.13

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.
Files changed (64) hide show
  1. package/config.development.js +49 -0
  2. package/dist-server/controllers/bizplace-initializer/dataset/codes.js +28 -4
  3. package/dist-server/controllers/bizplace-initializer/dataset/codes.js.map +1 -1
  4. package/dist-server/controllers/bizplace-initializer/dataset/menus.js +22 -0
  5. package/dist-server/controllers/bizplace-initializer/dataset/menus.js.map +1 -1
  6. package/dist-server/controllers/bizplace-initializer/dataset/roles-privileges.js +49 -0
  7. package/dist-server/controllers/bizplace-initializer/dataset/roles-privileges.js.map +1 -1
  8. package/dist-server/controllers/bizplace-initializer/dataset/settings.js +5 -0
  9. package/dist-server/controllers/bizplace-initializer/dataset/settings.js.map +1 -1
  10. package/dist-server/graphql/resolvers/synchronization/fetch-sync-invoices.js +1 -1
  11. package/dist-server/graphql/resolvers/synchronization/fetch-sync-invoices.js.map +1 -1
  12. package/dist-server/graphql/resolvers/synchronization/fetch-sync-purchase-orders.js +23 -10
  13. package/dist-server/graphql/resolvers/synchronization/fetch-sync-purchase-orders.js.map +1 -1
  14. package/dist-server/graphql/types/synchronization/index.js +1 -1
  15. package/dist-server/routers/api/restful-apis/unstable/add-inbound-order.js +9 -9
  16. package/dist-server/routers/api/restful-apis/unstable/add-inbound-order.js.map +1 -1
  17. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js +396 -306
  18. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js.map +1 -1
  19. package/dist-server/routers/api/restful-apis/unstable/get-marketplace-order-shipping-list.js +11 -19
  20. package/dist-server/routers/api/restful-apis/unstable/get-marketplace-order-shipping-list.js.map +1 -1
  21. package/dist-server/routers/api/restful-apis/unstable/index.js +1 -0
  22. package/dist-server/routers/api/restful-apis/unstable/index.js.map +1 -1
  23. package/dist-server/routers/api/restful-apis/unstable/update-release-order-details.js +82 -0
  24. package/dist-server/routers/api/restful-apis/unstable/update-release-order-details.js.map +1 -0
  25. package/dist-server/routers/api/restful-apis/v1/add-release-order.js +394 -307
  26. package/dist-server/routers/api/restful-apis/v1/add-release-order.js.map +1 -1
  27. package/dist-server/routers/api/restful-apis/v1/get-marketplace-order-shipping-list.js +13 -22
  28. package/dist-server/routers/api/restful-apis/v1/get-marketplace-order-shipping-list.js.map +1 -1
  29. package/dist-server/routers/api/restful-apis/v1/index.js +1 -0
  30. package/dist-server/routers/api/restful-apis/v1/index.js.map +1 -1
  31. package/dist-server/routers/api/restful-apis/v1/update-release-order-details.js +82 -0
  32. package/dist-server/routers/api/restful-apis/v1/update-release-order-details.js.map +1 -0
  33. package/dist-server/routers/api/restful-apis/v2/add-release-order.js +394 -307
  34. package/dist-server/routers/api/restful-apis/v2/add-release-order.js.map +1 -1
  35. package/dist-server/routers/api/restful-apis/v2/get-marketplace-order-shipping-list.js +13 -22
  36. package/dist-server/routers/api/restful-apis/v2/get-marketplace-order-shipping-list.js.map +1 -1
  37. package/dist-server/routers/api/restful-apis/v2/index.js +1 -0
  38. package/dist-server/routers/api/restful-apis/v2/index.js.map +1 -1
  39. package/dist-server/routers/api/restful-apis/v2/update-release-order-details.js +82 -0
  40. package/dist-server/routers/api/restful-apis/v2/update-release-order-details.js.map +1 -0
  41. package/openapi/unstable/outbound.yaml +85 -0
  42. package/openapi/v1/outbound.yaml +85 -0
  43. package/openapi/v2/outbound.yaml +85 -0
  44. package/package.json +67 -67
  45. package/server/controllers/bizplace-initializer/dataset/codes.ts +28 -4
  46. package/server/controllers/bizplace-initializer/dataset/menus.ts +22 -0
  47. package/server/controllers/bizplace-initializer/dataset/roles-privileges.ts +49 -0
  48. package/server/controllers/bizplace-initializer/dataset/settings.ts +5 -0
  49. package/server/graphql/resolvers/synchronization/fetch-sync-invoices.ts +1 -1
  50. package/server/graphql/resolvers/synchronization/fetch-sync-purchase-orders.ts +33 -9
  51. package/server/graphql/types/synchronization/index.ts +1 -1
  52. package/server/routers/api/restful-apis/unstable/add-inbound-order.ts +7 -7
  53. package/server/routers/api/restful-apis/unstable/add-release-order.ts +447 -336
  54. package/server/routers/api/restful-apis/unstable/get-marketplace-order-shipping-list.ts +12 -12
  55. package/server/routers/api/restful-apis/unstable/index.ts +1 -0
  56. package/server/routers/api/restful-apis/unstable/update-release-order-details.ts +94 -0
  57. package/server/routers/api/restful-apis/v1/add-release-order.ts +444 -339
  58. package/server/routers/api/restful-apis/v1/get-marketplace-order-shipping-list.ts +14 -15
  59. package/server/routers/api/restful-apis/v1/index.ts +1 -0
  60. package/server/routers/api/restful-apis/v1/update-release-order-details.ts +94 -0
  61. package/server/routers/api/restful-apis/v2/add-release-order.ts +444 -339
  62. package/server/routers/api/restful-apis/v2/get-marketplace-order-shipping-list.ts +14 -15
  63. package/server/routers/api/restful-apis/v2/index.ts +1 -0
  64. package/server/routers/api/restful-apis/v2/update-release-order-details.ts +94 -0
@@ -1,12 +1,12 @@
1
1
  import gql from 'graphql-tag'
2
- import { getRepository, SelectQueryBuilder } from 'typeorm'
2
+ 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
6
  import { Product, ProductBundle, ProductDetail } from '@things-factory/product-base'
7
7
  import { ORDER_TYPES, OrderInventory } from '@things-factory/sales-base'
8
8
  import { Setting } from '@things-factory/setting-base'
9
- import { Inventory, INVENTORY_STATUS } from '@things-factory/warehouse-base'
9
+ import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
10
10
  import { WorksheetDetail } from '@things-factory/worksheet-base'
11
11
 
12
12
  import { businessMiddleware } from '../../../../middlewares/business-middleware'
@@ -19,386 +19,486 @@ router.post(
19
19
  businessMiddleware,
20
20
  warehouseMiddleware,
21
21
  async (context, next) => {
22
- const { client, domain } = context.state
23
- const { warehouseId: customerBizplaceId } = context.params
24
- const { t } = context
22
+ return await getConnection().transaction(async (tx: EntityManager) => {
23
+ const { client, domain } = context.state
24
+ const { warehouseId: customerBizplaceId } = context.params
25
+ const { t } = context
25
26
 
26
- if (!customerBizplaceId) context.throw(404, t('error.warehouse id not found'))
27
+ if (!customerBizplaceId) context.throw(404, t('error.warehouse id not found'))
27
28
 
28
- let bodyReq = context.request.body
29
- if (typeof bodyReq === undefined || !Object.keys(bodyReq)?.length) {
30
- context.throw(400, t('error.invalid body request format'))
31
- }
32
- debug('post:/add-release-order request.body', bodyReq)
33
- await checkMandatoryKey(context, bodyReq)
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)
34
35
 
35
- // get customer company's bizplace
36
- const custCompanyBizplace: Bizplace = await getCompanyBizplace(null, null, customerBizplaceId)
37
- const customerBizplace: Bizplace = await getRepository(Bizplace).findOne({
38
- where: { id: customerBizplaceId }
39
- })
36
+ const isAutoAssign: Boolean = bodyReq.hasOwnProperty('isAutoAssign') ? bodyReq.isAutoAssign : true
40
37
 
41
- const pickingProductSetting: Setting = await getRepository(Setting).findOne({
42
- where: { domain, name: 'rule-for-picking-product' }
43
- })
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
+ const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
45
+ where: { domain, name: 'rule-for-picking-product' }
46
+ })
44
47
 
45
- let locationSortingRules = []
46
- if (pickingProductSetting) {
47
- let locationSetting = JSON.parse(pickingProductSetting.value)
48
- for (const key in locationSetting) {
49
- locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
48
+ let locationSortingRules = []
49
+ if (pickingProductSetting) {
50
+ let locationSetting = JSON.parse(pickingProductSetting.value)
51
+ for (const key in locationSetting) {
52
+ locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
53
+ }
50
54
  }
51
- }
52
55
 
53
- const orderInventories: OrderInventory[] = bodyReq.orderInventories
54
- let newOrderInventories: OrderInventory[] = []
56
+ const orderInventories: OrderInventory[] = bodyReq.orderInventories
57
+ let newOrderInventories: OrderInventory[] = []
55
58
 
56
- await Promise.all(
57
- orderInventories.map(async orderInventory => {
58
- const sku: string = orderInventory.product?.sku
59
- const refCode: string = orderInventory.product?.refCode
60
- const packingType: string = orderInventory.packingType
59
+ await Promise.all(
60
+ orderInventories.map(async orderInventory => {
61
+ const sku: string = orderInventory.product?.sku
62
+ const refCode: string = orderInventory.product?.refCode
63
+ const packingType: string = orderInventory.packingType
61
64
 
62
- const qb: SelectQueryBuilder<ProductDetail> = getRepository(ProductDetail)
63
- .createQueryBuilder('PD')
64
- .innerJoinAndSelect('PD.product', 'PROD')
65
- .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
65
+ const qb: SelectQueryBuilder<ProductDetail> = tx
66
+ .getRepository(ProductDetail)
67
+ .createQueryBuilder('PD')
68
+ .innerJoinAndSelect('PD.product', 'PROD')
69
+ .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
66
70
 
67
- if (sku) {
68
- qb.andWhere('PROD.sku = :sku', { sku })
71
+ if (!sku && !refCode) {
72
+ context.throw(404, t('error.sku or refCode not found'))
73
+ }
69
74
 
70
- if (packingType) {
71
- qb.andWhere('PD.packing_type = :packingType', { packingType })
75
+ if (refCode) {
76
+ qb.andWhere('PD.ref_code = :refCode', { refCode })
72
77
  }
73
- }
74
78
 
75
- if (refCode) {
76
- qb.andWhere('PD.ref_code = :refCode', { refCode })
79
+ if (sku) {
80
+ qb.andWhere('PROD.sku = :sku', { sku })
81
+ }
77
82
 
78
83
  if (packingType) {
79
84
  qb.andWhere('PD.packing_type = :packingType', { packingType })
80
85
  }
81
- }
82
-
83
- const productDetail: ProductDetail = await qb.getOne()
84
86
 
85
- if (productDetail) {
86
- let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
87
+ const productDetail: ProductDetail = await qb.getOne()
87
88
 
88
- newOrderInventory.product.sku = productDetail.product.sku
89
- newOrderInventory.product = productDetail.product
90
- newOrderInventory.packingType = productDetail.packingType
91
- newOrderInventory.uomValue = productDetail.uomValue
92
- newOrderInventories.push(newOrderInventory)
93
- }
89
+ if (productDetail) {
90
+ let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
94
91
 
95
- if (!productDetail) {
96
- const qb: SelectQueryBuilder<ProductDetail> = getRepository(ProductBundle)
97
- .createQueryBuilder('PB')
98
- .innerJoinAndSelect('PB.productBundleSettings', 'PBS')
99
- .innerJoinAndSelect('PBS.productDetail', 'PBD')
100
- .innerJoinAndSelect('PBD.product', 'PROD')
101
- .where('PB.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
92
+ newOrderInventory.product.sku = productDetail.product.sku
93
+ newOrderInventory.product = productDetail.product
94
+ newOrderInventory.packingType = productDetail.packingType
95
+ newOrderInventory.uomValue = productDetail.uomValue
96
+ newOrderInventories.push(newOrderInventory)
97
+ }
102
98
 
103
- if (sku) {
104
- qb.andWhere('PB.sku = :sku', { sku })
99
+ if (!productDetail) {
100
+ const qb: SelectQueryBuilder<ProductDetail> = tx
101
+ .getRepository(ProductBundle)
102
+ .createQueryBuilder('PB')
103
+ .innerJoinAndSelect('PB.productBundleSettings', 'PBS')
104
+ .innerJoinAndSelect('PBS.productDetail', 'PBD')
105
+ .innerJoinAndSelect('PBD.product', 'PROD')
106
+ .where('PB.bizplace_id = :bizplaceId', { bizplaceId: custCompanyBizplace.id })
107
+
108
+ if (!sku && !refCode) {
109
+ context.throw(404, t('error.sku or refCode not found'))
110
+ }
105
111
 
106
- if (packingType) {
107
- qb.andWhere('PB.packing_type = :packingType', { packingType })
112
+ if (refCode) {
113
+ qb.andWhere('PB.ref_code = :refCode', { refCode })
108
114
  }
109
- }
110
115
 
111
- if (refCode) {
112
- qb.andWhere('PB.ref_code = :refCode', { refCode })
116
+ if (sku) {
117
+ qb.andWhere('PB.sku = :sku', { sku })
118
+ }
113
119
 
114
120
  if (packingType) {
115
121
  qb.andWhere('PB.packing_type = :packingType', { packingType })
116
122
  }
117
- }
118
123
 
119
- const foundProductBundle: ProductBundle = await qb.getOne()
124
+ const foundProductBundle: ProductBundle = await qb.getOne()
125
+
126
+ if (foundProductBundle) {
127
+ const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
128
+ bundleProductSettings.map(bundleProductSetting => {
129
+ const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
130
+
131
+ let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
132
+ newOrderInventory.product.sku = productDetailBundle.product.sku
133
+ newOrderInventory.packingType = productDetailBundle.packingType
134
+ newOrderInventory.uomValue = productDetailBundle.uomValue
135
+ newOrderInventory.releaseQty = orderInventory.releaseQty * bundleProductSetting.bundleQty
136
+ newOrderInventory.product = productDetailBundle.product
137
+ newOrderInventories.push(newOrderInventory)
138
+ })
139
+ } else {
140
+ if (sku && refCode) {
141
+ context.throw(404, t(`error.sku ${sku} and refCode ${refCode} not exist in product / bundle master`))
142
+ }
120
143
 
121
- if (foundProductBundle) {
122
- const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
123
- bundleProductSettings.map(bundleProductSetting => {
124
- const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
144
+ if (sku) {
145
+ context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
146
+ }
125
147
 
126
- let newOrderInventory: OrderInventory = Object.assign({}, orderInventory)
127
- newOrderInventory.product.sku = productDetailBundle.product.sku
128
- newOrderInventory.packingType = productDetailBundle.packingType
129
- newOrderInventory.uomValue = productDetailBundle.uomValue
130
- newOrderInventory.releaseQty = orderInventory.releaseQty * bundleProductSetting.bundleQty
131
- newOrderInventory.product = productDetailBundle.product
132
- newOrderInventories.push(newOrderInventory)
133
- })
134
- } else {
135
- context.throw(404, t(`error.${orderInventory.product?.sku} not exist in product / bundle master`))
136
- }
137
- }
138
- })
139
- )
140
-
141
- let finalOrderInventories: OrderInventory[] = []
142
-
143
- // Case 1. Every order inventories has specified batch id
144
- if (newOrderInventories.every((orderInventory: OrderInventory) => orderInventory.batchId)) {
145
- finalOrderInventories = await Promise.all(
146
- newOrderInventories.map(async (orderInventory: OrderInventory) => {
147
- let filters: any = [
148
- {
149
- name: 'batchId',
150
- operator: 'eq',
151
- value: orderInventory.batchId
152
- },
153
- {
154
- name: 'productName',
155
- operator: 'eq',
156
- value: orderInventory.product.sku
157
- },
158
- {
159
- name: 'bizplace',
160
- operator: 'in',
161
- value: [customerBizplaceId]
148
+ if (refCode) {
149
+ context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
150
+ }
162
151
  }
163
- ]
164
-
165
- // 2. get all inventory data according to SKU and batchId
166
- const response = await client.query({
167
- query: gql`
168
- query inventoriesByPallet($filters: [Filter]) {
169
- inventoriesByPallet(filters: $filters) {
170
- items {
171
- product {
172
- id
173
- name
174
- description
152
+ }
153
+ })
154
+ )
155
+
156
+ let finalOrderInventories: OrderInventory[] = []
157
+
158
+ // Case 1. Every order inventories has specified batch id
159
+ if (newOrderInventories.every((orderInventory: OrderInventory) => orderInventory.batchId)) {
160
+ finalOrderInventories = await Promise.all(
161
+ newOrderInventories.map(async (orderInventory: OrderInventory) => {
162
+ let filters: any = [
163
+ {
164
+ name: 'batchId',
165
+ operator: 'eq',
166
+ value: orderInventory.batchId
167
+ },
168
+ {
169
+ name: 'productName',
170
+ operator: 'eq',
171
+ value: orderInventory.product.sku
172
+ },
173
+ {
174
+ name: 'bizplace',
175
+ operator: 'in',
176
+ value: [customerBizplaceId]
177
+ }
178
+ ]
179
+
180
+ // 2. get all inventory data according to SKU and batchId
181
+ const response = await client.query({
182
+ query: gql`
183
+ query inventoriesByPallet($filters: [Filter]) {
184
+ inventoriesByPallet(filters: $filters) {
185
+ items {
186
+ product {
187
+ id
188
+ name
189
+ description
190
+ }
191
+ batchId
192
+ packingType
193
+ remainQty
194
+ remainWeight
195
+ remainUomValue
175
196
  }
176
- batchId
177
- packingType
178
- remainQty
179
- remainWeight
180
- remainUomValue
197
+ total
181
198
  }
182
- total
199
+ }
200
+ `,
201
+ variables: { filters, locationSortingRules },
202
+ context: {
203
+ ...context,
204
+ state: {
205
+ ...context.state,
206
+ bizplace: customerBizplace,
207
+ domain
183
208
  }
184
209
  }
185
- `,
186
- variables: { filters, locationSortingRules },
187
- context: {
188
- ...context,
189
- state: {
190
- ...context.state,
191
- bizplace: customerBizplace,
192
- domain
193
- }
210
+ } as any)
211
+
212
+ if (!response.errors) {
213
+ const items = response.data.inventoriesByPallet.items || []
214
+ if (!items?.length)
215
+ context.throw(
216
+ 404,
217
+ t(
218
+ `error.no inventories found for ${orderInventory.product?.sku} with batch ID ${orderInventory?.batchId}`
219
+ )
220
+ )
221
+
222
+ items.forEach(item => {
223
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
224
+ context.throw(404, t(`error.invalid release qty for ${orderInventory.product?.sku}`))
225
+ }
226
+
227
+ // 3. add more information to orderInventory
228
+ orderInventory.product.id = item.product.id
229
+ orderInventory.packingType = item.packingType
230
+ orderInventory.batchId = item.batchId
231
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
232
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
233
+ orderInventory.uom = item.uom
234
+ })
235
+
236
+ return orderInventory
237
+ } else context.throw(404, t('error.failed to fetch inventory'))
238
+ })
239
+ )
240
+ } else {
241
+ // Case 2. Some of order inventories doesn't have specified batch id
242
+ // MMS order doesn't have batch ID field.
243
+ // To cover this case first result of found inventory will be a target of RO
244
+
245
+ for (let i: number = 0; i < newOrderInventories.length; i++) {
246
+ const orderInventory: OrderInventory = newOrderInventories[i]
247
+ const product: Product = orderInventory.product
248
+ const packingType: string = orderInventory.packingType
249
+
250
+ if (isAutoAssign) {
251
+ let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
252
+ qb.leftJoinAndSelect('iv.location', 'loc')
253
+ .andWhere('"iv"."domain_id" = :domainId')
254
+ .andWhere('"iv"."bizplace_id" = :bizplaceId')
255
+ .andWhere('"iv"."packing_type" = :packingType')
256
+ .andWhere('"iv"."product_id" = :productId')
257
+ .andWhere('"iv"."status" = :status')
258
+ .andWhere('"loc"."type" NOT IN (:...locationTypes)')
259
+ .setParameters({
260
+ domainId: domain.id,
261
+ bizplaceId: customerBizplace.id,
262
+ packingType: packingType,
263
+ productId: product.id,
264
+ status: INVENTORY_STATUS.STORED,
265
+ locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
266
+ })
267
+
268
+ if (locationSortingRules?.length > 0) {
269
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
270
+ idx === 0
271
+ ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
272
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
273
+ })
274
+ } else {
275
+ qb.addOrderBy('"iv"."created_at"', 'ASC')
194
276
  }
195
- } as any)
196
277
 
197
- if (!response.errors) {
198
- const items = response.data.inventoriesByPallet.items || []
199
- if (!items?.length)
200
- context.throw(
201
- 404,
202
- t(
203
- `error.no inventories found for ${orderInventory.product?.sku} with batch ID ${orderInventory?.batchId}`
204
- )
205
- )
278
+ let inventories: Inventory[] = await qb.getMany()
279
+ if (!inventories?.length) context.throw(404, t(`error.no inventories found for ${product.sku}`))
206
280
 
207
- items.forEach(item => {
208
- if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
209
- context.throw(404, t(`error.invalid release qty for ${orderInventory.product?.sku}`))
281
+ inventories = inventories.map(inventory => {
282
+ return {
283
+ ...inventory,
284
+ remainQty: inventory.qty - inventory.lockedQty,
285
+ remainUomValue: inventory.uomValue - inventory.lockedUomValue
210
286
  }
287
+ })
211
288
 
212
- // 3. add more information to orderInventory
213
- orderInventory.product.id = item.product.id
214
- orderInventory.packingType = item.packingType
215
- orderInventory.batchId = item.batchId
216
- orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
217
- orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
289
+ const targetInventories: WorksheetDetail[] = _composeTargetInventories(
290
+ context,
291
+ product,
292
+ orderInventory,
293
+ inventories
294
+ )
295
+ targetInventories.map(item => {
296
+ finalOrderInventories.push(item.targetInventory)
218
297
  })
298
+ } else {
299
+ let filters: any = [
300
+ {
301
+ name: 'productName',
302
+ operator: 'eq',
303
+ value: orderInventory.product.sku
304
+ },
305
+ {
306
+ name: 'bizplace_id',
307
+ operator: 'eq',
308
+ value: customerBizplaceId
309
+ }
310
+ ]
311
+
312
+ // 2. get all inventory data according to SKU and batchId
313
+ const response = await client.query({
314
+ query: gql`
315
+ query inventoryProductGroup($filters: [Filter]) {
316
+ inventoryProductGroup(filters: $filters) {
317
+ items {
318
+ productId
319
+ productSKU
320
+ productName
321
+ batchId
322
+ packingType
323
+ remainQty
324
+ remainWeight
325
+ remainUomValue
326
+ }
327
+ total
328
+ }
329
+ }
330
+ `,
331
+ variables: { filters },
332
+ context: {
333
+ ...context,
334
+ state: {
335
+ ...context.state,
336
+ bizplace: customerBizplace,
337
+ domain
338
+ }
339
+ }
340
+ } as any)
341
+
342
+ if (!response.errors) {
343
+ const items = response.data.inventoryProductGroup.items || []
344
+ if (!items?.length)
345
+ context.throw(
346
+ 404,
347
+ t(
348
+ `error.no inventories found for ${orderInventory.product?.sku} with batch ID ${orderInventory?.batchId}`
349
+ )
350
+ )
219
351
 
220
- return orderInventory
221
- } else context.throw(404, t('error.failed to fetch inventory'))
222
- })
223
- )
224
- } else {
225
- // Case 2. Some of order inventories doesn't have specified batch id
226
- // MMS order doesn't have batch ID field.
227
- // To cover this case first result of found inventory will be a target of RO
228
-
229
- for (let i: number = 0; i < newOrderInventories.length; i++) {
230
- const orderInventory: OrderInventory = newOrderInventories[i]
231
- const product: Product = orderInventory.product
232
- const packingType: string = orderInventory.packingType
233
-
234
- let qb: SelectQueryBuilder<Inventory> = getRepository(Inventory).createQueryBuilder('iv')
235
- qb.leftJoinAndSelect('iv.location', 'loc')
236
- .andWhere('"iv"."domain_id" = :domainId')
237
- .andWhere('"iv"."bizplace_id" = :bizplaceId')
238
- .andWhere('"iv"."packing_type" = :packingType')
239
- .andWhere('"iv"."product_id" = :productId')
240
- .andWhere('"iv"."status" = :status')
241
- .setParameters({
242
- domainId: domain.id,
243
- bizplaceId: customerBizplace.id,
244
- packingType: packingType,
245
- productId: product.id,
246
- status: INVENTORY_STATUS.STORED
247
- })
352
+ items.forEach(item => {
353
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
354
+ context.throw(404, t(`error.invalid release qty for ${orderInventory.product?.sku}`))
355
+ }
248
356
 
249
- if (locationSortingRules?.length > 0) {
250
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
251
- idx === 0
252
- ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
253
- : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
254
- })
255
- } else {
256
- qb.addOrderBy('"iv"."created_at"', 'ASC')
357
+ // 3. add more information to orderInventory
358
+ orderInventory.product = { id: item.productId }
359
+ orderInventory.packingType = item.packingType
360
+ orderInventory.batchId = item.batchId
361
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
362
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
363
+ orderInventory.uom = item.uom
364
+ })
365
+
366
+ delete orderInventory.uomValue
367
+ finalOrderInventories.push(orderInventory)
368
+ } else context.throw(404, t('error.failed to fetch inventory'))
369
+ }
257
370
  }
258
-
259
- const inventories: Inventory[] = await qb.getMany()
260
- if (!inventories?.length) context.throw(404, t(`error.no inventories found for ${product.sku}`))
261
-
262
- const targetInventories: WorksheetDetail[] = _composeTargetInventories(
263
- context,
264
- product,
265
- orderInventory,
266
- inventories
267
- )
268
- targetInventories.map(item => {
269
- finalOrderInventories.push(item.targetInventory)
270
- })
271
371
  }
272
- }
273
372
 
274
- context.body = {
275
- result: (
276
- await client.mutate({
277
- mutation: gql`
278
- mutation addReleaseGood($releaseGood: NewReleaseGood, $shippingOrder: ShippingOrderPatch, $file: Upload) {
279
- addReleaseGood(releaseGood: $releaseGood, shippingOrder: $shippingOrder, file: $file) {
280
- id
281
- name
282
- refNo
283
- status
284
- truckNo
285
- ownTransport
286
- crossDocking
287
- marketplaceOrderStatus
288
- billingAddress
289
- deliveryAddress1
290
- deliveryAddress2
291
- deliveryAddress3
292
- deliveryAddress4
293
- deliveryAddress5
294
- attentionTo
295
- attentionCompany
296
- city
297
- state
298
- postalCode
299
- country
300
- phone1
301
- phone2
302
- email
303
- transporter
304
- trackingNo
305
- airwayBill
306
- invoice
307
- type
308
- arrivalNotice {
373
+ context.body = {
374
+ result: (
375
+ await client.mutate({
376
+ mutation: gql`
377
+ mutation addReleaseGood($releaseGood: NewReleaseGood, $shippingOrder: ShippingOrderPatch, $file: Upload) {
378
+ addReleaseGood(releaseGood: $releaseGood, shippingOrder: $shippingOrder, file: $file) {
379
+ id
309
380
  name
310
- }
311
- exportOption
312
- releaseDate
313
- collectionOrderNo
314
- bizplace {
315
- name
316
- }
317
- orderInventories {
318
- product {
319
- sku
381
+ refNo
382
+ status
383
+ truckNo
384
+ ownTransport
385
+ crossDocking
386
+ marketplaceOrderStatus
387
+ billingAddress
388
+ deliveryAddress1
389
+ deliveryAddress2
390
+ deliveryAddress3
391
+ deliveryAddress4
392
+ deliveryAddress5
393
+ attentionTo
394
+ attentionCompany
395
+ city
396
+ state
397
+ postalCode
398
+ country
399
+ phone1
400
+ phone2
401
+ email
402
+ transporter
403
+ trackingNo
404
+ airwayBill
405
+ invoice
406
+ type
407
+ arrivalNotice {
320
408
  name
321
- description
322
409
  }
323
- batchId
324
- packingType
325
- packingSize
326
- releaseQty
327
- releaseUomValue
328
- }
329
- orderVass {
330
- vas {
410
+ exportOption
411
+ releaseDate
412
+ collectionOrderNo
413
+ bizplace {
331
414
  name
332
- operationGuide
333
- operationGuideType
334
415
  }
335
- set
336
- targetType
337
- targetBatchId
338
- targetProduct {
339
- name
416
+ orderInventories {
417
+ product {
418
+ sku
419
+ name
420
+ description
421
+ }
422
+ batchId
423
+ packingType
424
+ packingSize
425
+ releaseQty
426
+ releaseUomValue
427
+ }
428
+ orderVass {
429
+ vas {
430
+ name
431
+ operationGuide
432
+ operationGuideType
433
+ }
434
+ set
435
+ targetType
436
+ targetBatchId
437
+ targetProduct {
438
+ name
439
+ description
440
+ }
441
+ packingType
442
+ qty
443
+ uomValue
444
+ otherTarget
340
445
  description
446
+ remark
447
+ status
448
+ operationGuide
341
449
  }
342
- packingType
343
- qty
344
- uomValue
345
- otherTarget
346
- description
347
- remark
348
- status
349
- operationGuide
350
450
  }
351
451
  }
452
+ `,
453
+ variables: {
454
+ releaseGood: {
455
+ customerBizplaceId: customerBizplace.id,
456
+ courierOption: bodyReq.courierOption,
457
+ collectionOrderNo: bodyReq.collectionOrderNo,
458
+ exportOption: bodyReq.exportOption,
459
+ orderInventories: finalOrderInventories,
460
+ ownTransport: bodyReq.ownTransport,
461
+ packingOption: bodyReq.packingOption,
462
+ refNo: bodyReq.refNo,
463
+ refNo2: bodyReq?.refNo2,
464
+ releaseDate: bodyReq.releaseDate,
465
+ type: bodyReq.type,
466
+ marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
467
+ billingAddress: bodyReq?.billTo?.billingAddress || null,
468
+ deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
469
+ deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
470
+ deliveryAddress3: bodyReq?.deliverTo?.deliveryAddress3 || null,
471
+ deliveryAddress4: bodyReq?.deliverTo?.deliveryAddress4 || null,
472
+ deliveryAddress5: bodyReq?.deliverTo?.deliveryAddress5 || null,
473
+ attentionTo: bodyReq?.deliverTo?.attentionTo || null,
474
+ attentionCompany: bodyReq?.deliverTo?.attentionCompany || null,
475
+ city: bodyReq?.deliverTo?.city || null,
476
+ state: bodyReq?.deliverTo?.state || null,
477
+ postalCode: bodyReq?.deliverTo?.postalCode || null,
478
+ country: bodyReq?.deliverTo?.country || null,
479
+ phone1: bodyReq?.deliverTo?.phone1 || null,
480
+ phone2: bodyReq?.deliverTo?.phone2 || null,
481
+ email: bodyReq?.deliverTo?.email || null,
482
+ transporter: bodyReq?.transporter,
483
+ trackingNo: bodyReq?.trackingNo,
484
+ airwayBill: bodyReq?.airwayBill,
485
+ invoice: bodyReq?.invoice
486
+ }
487
+ },
488
+ context: {
489
+ ...context,
490
+ hasUpload: true,
491
+ state: {
492
+ ...context.state,
493
+ customerBizplace,
494
+ domain,
495
+ type: 'api'
496
+ }
352
497
  }
353
- `,
354
- variables: {
355
- releaseGood: {
356
- customerBizplaceId: customerBizplace.id,
357
- courierOption: bodyReq.courierOption,
358
- collectionOrderNo: bodyReq.collectionOrderNo,
359
- exportOption: bodyReq.exportOption,
360
- orderInventories: finalOrderInventories,
361
- ownTransport: bodyReq.ownTransport,
362
- packingOption: bodyReq.packingOption,
363
- refNo: bodyReq.refNo,
364
- refNo2: bodyReq?.refNo2,
365
- releaseDate: bodyReq.releaseDate,
366
- type: bodyReq.type,
367
- marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
368
- billingAddress: bodyReq?.billTo?.billingAddress || null,
369
- deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
370
- deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
371
- deliveryAddress3: bodyReq?.deliverTo?.deliveryAddress3 || null,
372
- deliveryAddress4: bodyReq?.deliverTo?.deliveryAddress4 || null,
373
- deliveryAddress5: bodyReq?.deliverTo?.deliveryAddress5 || null,
374
- attentionTo: bodyReq?.deliverTo?.attentionTo || null,
375
- attentionCompany: bodyReq?.deliverTo?.attentionCompany || null,
376
- city: bodyReq?.deliverTo?.city || null,
377
- state: bodyReq?.deliverTo?.state || null,
378
- postalCode: bodyReq?.deliverTo?.postalCode || null,
379
- country: bodyReq?.deliverTo?.country || null,
380
- phone1: bodyReq?.deliverTo?.phone1 || null,
381
- phone2: bodyReq?.deliverTo?.phone2 || null,
382
- email: bodyReq?.deliverTo?.email || null,
383
- transporter: bodyReq?.transporter,
384
- trackingNo: bodyReq?.trackingNo,
385
- airwayBill: bodyReq?.airwayBill,
386
- invoice: bodyReq?.invoice
387
- }
388
- },
389
- context: {
390
- ...context,
391
- hasUpload: true,
392
- state: {
393
- ...context.state,
394
- customerBizplace,
395
- domain,
396
- type: 'api'
397
- }
398
- }
399
- } as any)
400
- ).data
401
- }
498
+ } as any)
499
+ ).data
500
+ }
501
+ })
402
502
  }
403
503
  )
404
504
 
@@ -429,9 +529,18 @@ export async function checkMandatoryKey(context, params) {
429
529
  }
430
530
 
431
531
  if ('orderInventories' in params) {
432
- if (params.orderInventories.filter(record => !record.releaseQty || !record.product).length) {
433
- noparams.push('orderInventories')
434
- }
532
+ params.orderInventories.forEach((param, key) => {
533
+ let noSubParams = []
534
+
535
+ if (!param?.releaseQty) noSubParams.push('releaseQty')
536
+
537
+ if (!param?.product) noSubParams.push('product')
538
+
539
+ if (noSubParams?.length)
540
+ context.throw(400, context.t('error.required param x not found', { x: `${noSubParams.join(', ')}` }))
541
+ })
542
+ } else {
543
+ noparams.push('orderInventories')
435
544
  }
436
545
 
437
546
  if (noparams?.length) {
@@ -450,7 +559,7 @@ export function _composeTargetInventories(context, product, record, inventories)
450
559
  let totalInventoryQty: number = 0
451
560
 
452
561
  inventories.map(inventory => {
453
- totalInventoryQty += inventory.qty
562
+ totalInventoryQty += inventory.remainQty
454
563
  })
455
564
 
456
565
  if (totalInventoryQty < record.releaseQty) {
@@ -462,7 +571,7 @@ export function _composeTargetInventories(context, product, record, inventories)
462
571
  while (compReleaseQty < record.releaseQty) {
463
572
  const inv = inventories[idx]
464
573
 
465
- if (inv.qty > leftReleaseQty) {
574
+ if (inv.remainQty > leftReleaseQty) {
466
575
  compReleaseQty += leftReleaseQty
467
576
  compReleaseUomValue += leftReleaseUomValue
468
577
 
@@ -473,23 +582,25 @@ export function _composeTargetInventories(context, product, record, inventories)
473
582
  batchId: inv.batchId,
474
583
  releaseQty: leftReleaseQty,
475
584
  releaseUomValue: leftReleaseQty * record.uomValue,
585
+ uom: inv.uom,
476
586
  product: { id: product.id },
477
587
  type: ORDER_TYPES.RELEASE_OF_GOODS
478
588
  }
479
589
  })
480
590
  } else {
481
- compReleaseQty += inv.qty
482
- compReleaseUomValue += inv.uomValue
483
- leftReleaseQty -= inv.qty
484
- leftReleaseUomValue -= inv.uomValue
591
+ compReleaseQty += inv.remainQty
592
+ compReleaseUomValue += inv.remainUomValue
593
+ leftReleaseQty -= inv.remainQty
594
+ leftReleaseUomValue -= inv.remainUomValue
485
595
 
486
596
  targetInventories.push({
487
597
  targetInventory: {
488
598
  inventory: { id: inv.id },
489
599
  packingType: inv.packingType,
490
600
  batchId: inv.batchId,
491
- releaseQty: inv.qty,
492
- releaseUomValue: inv.uomValue,
601
+ releaseQty: inv.remainQty,
602
+ releaseUomValue: inv.remainUomValue,
603
+ uom: inv.uom,
493
604
  product: { id: product.id },
494
605
  type: ORDER_TYPES.RELEASE_OF_GOODS
495
606
  }