@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,404 +1,498 @@
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'
13
13
  import { warehouseMiddleware } from '../../../../middlewares/warehouse-middleware'
14
14
 
15
- const debug = require('debug')('things-factory:operato-hub:restful-api:v2:add-release-order')
15
+ const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
16
16
 
17
17
  router.post(
18
18
  '/v2/warehouse/:warehouseId/add-release-order',
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.productBundleSetting', '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()
120
-
121
- if (foundProductBundle) {
122
- const bundleProductSettings: any[] = foundProductBundle.productBundleSettings
123
- bundleProductSettings.map(bundleProductSetting => {
124
- const productDetailBundle: ProductDetail = bundleProductSetting.productDetail
125
-
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]
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) {
141
+ context.throw(404, t(`error.sku ${sku} not exist in product / bundle master`))
142
+ } else if (refCode) {
143
+ context.throw(404, t(`error.refCode ${refCode} not exist in product / bundle master`))
144
+ }
162
145
  }
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
146
+ }
147
+ })
148
+ )
149
+
150
+ let finalOrderInventories: OrderInventory[] = []
151
+
152
+ // Case 1. Every order inventories has specified batch id
153
+ if (newOrderInventories.every((orderInventory: OrderInventory) => orderInventory.batchId)) {
154
+ finalOrderInventories = await Promise.all(
155
+ newOrderInventories.map(async (orderInventory: OrderInventory) => {
156
+ let filters: any = [
157
+ {
158
+ name: 'batchId',
159
+ operator: 'eq',
160
+ value: orderInventory.batchId
161
+ },
162
+ {
163
+ name: 'productName',
164
+ operator: 'eq',
165
+ value: orderInventory.product.sku
166
+ },
167
+ {
168
+ name: 'bizplace',
169
+ operator: 'in',
170
+ value: [customerBizplaceId]
171
+ }
172
+ ]
173
+
174
+ // 2. get all inventory data according to SKU and batchId
175
+ const response = await client.query({
176
+ query: gql`
177
+ query inventoriesByPallet($filters: [Filter]) {
178
+ inventoriesByPallet(filters: $filters) {
179
+ items {
180
+ product {
181
+ id
182
+ name
183
+ description
184
+ }
185
+ batchId
186
+ packingType
187
+ remainQty
188
+ remainWeight
189
+ remainUomValue
175
190
  }
176
- batchId
177
- packingType
178
- remainQty
179
- remainWeight
180
- remainUomValue
191
+ total
181
192
  }
182
- total
193
+ }
194
+ `,
195
+ variables: { filters, locationSortingRules },
196
+ context: {
197
+ ...context,
198
+ state: {
199
+ ...context.state,
200
+ bizplace: customerBizplace,
201
+ domain
183
202
  }
184
203
  }
185
- `,
186
- variables: { filters, locationSortingRules },
187
- context: {
188
- ...context,
189
- state: {
190
- ...context.state,
191
- bizplace: customerBizplace,
192
- domain
193
- }
204
+ } as any)
205
+
206
+ if (!response.errors) {
207
+ const items = response.data.inventoriesByPallet.items || []
208
+ if (!items?.length)
209
+ context.throw(
210
+ 404,
211
+ t(
212
+ `error.no inventories found for ${orderInventory.product?.sku} with batch ID ${orderInventory?.batchId}`
213
+ )
214
+ )
215
+
216
+ items.forEach(item => {
217
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
218
+ context.throw(404, t(`error.invalid release qty for ${orderInventory.product?.sku}`))
219
+ }
220
+
221
+ // 3. add more information to orderInventory
222
+ orderInventory.product.id = item.product.id
223
+ orderInventory.packingType = item.packingType
224
+ orderInventory.batchId = item.batchId
225
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
226
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
227
+ orderInventory.uom = item.uom
228
+ })
229
+
230
+ return orderInventory
231
+ } else context.throw(404, t('error.failed to fetch inventory'))
232
+ })
233
+ )
234
+ } else {
235
+ // Case 2. Some of order inventories doesn't have specified batch id
236
+ // MMS order doesn't have batch ID field.
237
+ // To cover this case first result of found inventory will be a target of RO
238
+
239
+ for (let i: number = 0; i < newOrderInventories.length; i++) {
240
+ const orderInventory: OrderInventory = newOrderInventories[i]
241
+ const product: Product = orderInventory.product
242
+ const packingType: string = orderInventory.packingType
243
+
244
+ if (isAutoAssign) {
245
+ let qb: SelectQueryBuilder<Inventory> = tx.getRepository(Inventory).createQueryBuilder('iv')
246
+ qb.leftJoinAndSelect('iv.location', 'loc')
247
+ .andWhere('"iv"."domain_id" = :domainId')
248
+ .andWhere('"iv"."bizplace_id" = :bizplaceId')
249
+ .andWhere('"iv"."packing_type" = :packingType')
250
+ .andWhere('"iv"."product_id" = :productId')
251
+ .andWhere('"iv"."status" = :status')
252
+ .andWhere('"loc"."type" NOT IN (:...locationTypes)')
253
+ .setParameters({
254
+ domainId: domain.id,
255
+ bizplaceId: customerBizplace.id,
256
+ packingType: packingType,
257
+ productId: product.id,
258
+ status: INVENTORY_STATUS.STORED,
259
+ locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
260
+ })
261
+
262
+ if (locationSortingRules?.length > 0) {
263
+ locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
264
+ idx === 0
265
+ ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
266
+ : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
267
+ })
268
+ } else {
269
+ qb.addOrderBy('"iv"."created_at"', 'ASC')
194
270
  }
195
- } as any)
196
271
 
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
- )
272
+ let inventories: Inventory[] = await qb.getMany()
273
+ if (!inventories?.length) context.throw(404, t(`error.no inventories found for ${product.sku}`))
206
274
 
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}`))
275
+ inventories = inventories.map(inventory => {
276
+ return {
277
+ ...inventory,
278
+ remainQty: inventory.qty - inventory.lockedQty,
279
+ remainUomValue: inventory.uomValue - inventory.lockedUomValue
210
280
  }
281
+ })
211
282
 
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
283
+ const targetInventories: WorksheetDetail[] = _composeTargetInventories(
284
+ context,
285
+ product,
286
+ orderInventory,
287
+ inventories
288
+ )
289
+ targetInventories.map(item => {
290
+ finalOrderInventories.push(item.targetInventory)
218
291
  })
292
+ } else {
293
+ let filters: any = [
294
+ {
295
+ name: 'productName',
296
+ operator: 'eq',
297
+ value: orderInventory.product.sku
298
+ },
299
+ {
300
+ name: 'bizplace_id',
301
+ operator: 'eq',
302
+ value: customerBizplaceId
303
+ }
304
+ ]
305
+
306
+ // 2. get all inventory data according to SKU and batchId
307
+ const response = await client.query({
308
+ query: gql`
309
+ query inventoryProductGroup($filters: [Filter]) {
310
+ inventoryProductGroup(filters: $filters) {
311
+ items {
312
+ productId
313
+ productSKU
314
+ productName
315
+ batchId
316
+ packingType
317
+ remainQty
318
+ remainWeight
319
+ remainUomValue
320
+ }
321
+ total
322
+ }
323
+ }
324
+ `,
325
+ variables: { filters },
326
+ context: {
327
+ ...context,
328
+ state: {
329
+ ...context.state,
330
+ bizplace: customerBizplace,
331
+ domain
332
+ }
333
+ }
334
+ } as any)
335
+
336
+ if (!response.errors) {
337
+ const items = response.data.inventoryProductGroup.items || []
338
+ if (!items?.length)
339
+ context.throw(
340
+ 404,
341
+ t(
342
+ `error.no inventories found for ${orderInventory.product?.sku} with batch ID ${orderInventory?.batchId}`
343
+ )
344
+ )
219
345
 
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
- })
346
+ items.forEach(item => {
347
+ if (orderInventory.releaseQty > item.remainQty || orderInventory.releaseQty <= 0) {
348
+ context.throw(404, t(`error.invalid release qty for ${orderInventory.product?.sku}`))
349
+ }
248
350
 
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')
351
+ // 3. add more information to orderInventory
352
+ orderInventory.product = { id: item.productId }
353
+ orderInventory.packingType = item.packingType
354
+ orderInventory.batchId = item.batchId
355
+ orderInventory.type = ORDER_TYPES.RELEASE_OF_GOODS
356
+ orderInventory.releaseUomValue = orderInventory.releaseQty * orderInventory.uomValue
357
+ orderInventory.uom = item.uom
358
+ })
359
+
360
+ delete orderInventory.uomValue
361
+ finalOrderInventories.push(orderInventory)
362
+ } else context.throw(404, t('error.failed to fetch inventory'))
363
+ }
257
364
  }
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
365
  }
272
- }
273
366
 
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 {
367
+ context.body = {
368
+ result: (
369
+ await client.mutate({
370
+ mutation: gql`
371
+ mutation addReleaseGood($releaseGood: NewReleaseGood, $shippingOrder: ShippingOrderPatch, $file: Upload) {
372
+ addReleaseGood(releaseGood: $releaseGood, shippingOrder: $shippingOrder, file: $file) {
373
+ id
309
374
  name
310
- }
311
- exportOption
312
- releaseDate
313
- collectionOrderNo
314
- bizplace {
315
- name
316
- }
317
- orderInventories {
318
- product {
319
- sku
375
+ refNo
376
+ status
377
+ truckNo
378
+ ownTransport
379
+ crossDocking
380
+ marketplaceOrderStatus
381
+ billingAddress
382
+ deliveryAddress1
383
+ deliveryAddress2
384
+ deliveryAddress3
385
+ deliveryAddress4
386
+ deliveryAddress5
387
+ attentionTo
388
+ attentionCompany
389
+ city
390
+ state
391
+ postalCode
392
+ country
393
+ phone1
394
+ phone2
395
+ email
396
+ transporter
397
+ trackingNo
398
+ airwayBill
399
+ invoice
400
+ type
401
+ arrivalNotice {
320
402
  name
321
- description
322
403
  }
323
- batchId
324
- packingType
325
- packingSize
326
- releaseQty
327
- releaseUomValue
328
- }
329
- orderVass {
330
- vas {
404
+ exportOption
405
+ releaseDate
406
+ collectionOrderNo
407
+ bizplace {
331
408
  name
332
- operationGuide
333
- operationGuideType
334
409
  }
335
- set
336
- targetType
337
- targetBatchId
338
- targetProduct {
339
- name
410
+ orderInventories {
411
+ product {
412
+ sku
413
+ name
414
+ description
415
+ }
416
+ batchId
417
+ packingType
418
+ packingSize
419
+ releaseQty
420
+ releaseUomValue
421
+ }
422
+ orderVass {
423
+ vas {
424
+ name
425
+ operationGuide
426
+ operationGuideType
427
+ }
428
+ set
429
+ targetType
430
+ targetBatchId
431
+ targetProduct {
432
+ name
433
+ description
434
+ }
435
+ packingType
436
+ qty
437
+ uomValue
438
+ otherTarget
340
439
  description
440
+ remark
441
+ status
442
+ operationGuide
341
443
  }
342
- packingType
343
- qty
344
- uomValue
345
- otherTarget
346
- description
347
- remark
348
- status
349
- operationGuide
350
444
  }
351
445
  }
446
+ `,
447
+ variables: {
448
+ releaseGood: {
449
+ customerBizplaceId: customerBizplace.id,
450
+ courierOption: bodyReq.courierOption,
451
+ collectionOrderNo: bodyReq.collectionOrderNo,
452
+ exportOption: bodyReq.exportOption,
453
+ orderInventories: finalOrderInventories,
454
+ ownTransport: bodyReq.ownTransport,
455
+ packingOption: bodyReq.packingOption,
456
+ refNo: bodyReq.refNo,
457
+ refNo2: bodyReq?.refNo2,
458
+ releaseDate: bodyReq.releaseDate,
459
+ type: bodyReq.type,
460
+ marketplaceOrderStatus: bodyReq?.marketplaceOrderStatus,
461
+ billingAddress: bodyReq?.billTo?.billingAddress || null,
462
+ deliveryAddress1: bodyReq?.deliverTo?.deliveryAddress1 || null,
463
+ deliveryAddress2: bodyReq?.deliverTo?.deliveryAddress2 || null,
464
+ deliveryAddress3: bodyReq?.deliverTo?.deliveryAddress3 || null,
465
+ deliveryAddress4: bodyReq?.deliverTo?.deliveryAddress4 || null,
466
+ deliveryAddress5: bodyReq?.deliverTo?.deliveryAddress5 || null,
467
+ attentionTo: bodyReq?.deliverTo?.attentionTo || null,
468
+ attentionCompany: bodyReq?.deliverTo?.attentionCompany || null,
469
+ city: bodyReq?.deliverTo?.city || null,
470
+ state: bodyReq?.deliverTo?.state || null,
471
+ postalCode: bodyReq?.deliverTo?.postalCode || null,
472
+ country: bodyReq?.deliverTo?.country || null,
473
+ phone1: bodyReq?.deliverTo?.phone1 || null,
474
+ phone2: bodyReq?.deliverTo?.phone2 || null,
475
+ email: bodyReq?.deliverTo?.email || null,
476
+ transporter: bodyReq?.transporter,
477
+ trackingNo: bodyReq?.trackingNo,
478
+ airwayBill: bodyReq?.airwayBill,
479
+ invoice: bodyReq?.invoice
480
+ }
481
+ },
482
+ context: {
483
+ ...context,
484
+ hasUpload: true,
485
+ state: {
486
+ ...context.state,
487
+ customerBizplace,
488
+ domain,
489
+ type: 'api'
490
+ }
352
491
  }
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
- }
492
+ } as any)
493
+ ).data
494
+ }
495
+ })
402
496
  }
403
497
  )
404
498
 
@@ -429,9 +523,18 @@ export async function checkMandatoryKey(context, params) {
429
523
  }
430
524
 
431
525
  if ('orderInventories' in params) {
432
- if (params.orderInventories.filter(record => !record.releaseQty || !record.product).length) {
433
- noparams.push('orderInventories')
434
- }
526
+ params.orderInventories.forEach((param, key) => {
527
+ let noSubParams = []
528
+
529
+ if (!param?.releaseQty) noSubParams.push('releaseQty')
530
+
531
+ if (!param?.product) noSubParams.push('product')
532
+
533
+ if (noSubParams?.length)
534
+ context.throw(400, context.t('error.required param x not found', { x: `${noSubParams.join(', ')}` }))
535
+ })
536
+ } else {
537
+ noparams.push('orderInventories')
435
538
  }
436
539
 
437
540
  if (noparams?.length) {
@@ -450,7 +553,7 @@ export function _composeTargetInventories(context, product, record, inventories)
450
553
  let totalInventoryQty: number = 0
451
554
 
452
555
  inventories.map(inventory => {
453
- totalInventoryQty += inventory.qty
556
+ totalInventoryQty += inventory.remainQty
454
557
  })
455
558
 
456
559
  if (totalInventoryQty < record.releaseQty) {
@@ -462,7 +565,7 @@ export function _composeTargetInventories(context, product, record, inventories)
462
565
  while (compReleaseQty < record.releaseQty) {
463
566
  const inv = inventories[idx]
464
567
 
465
- if (inv.qty > leftReleaseQty) {
568
+ if (inv.remainQty > leftReleaseQty) {
466
569
  compReleaseQty += leftReleaseQty
467
570
  compReleaseUomValue += leftReleaseUomValue
468
571
 
@@ -473,23 +576,25 @@ export function _composeTargetInventories(context, product, record, inventories)
473
576
  batchId: inv.batchId,
474
577
  releaseQty: leftReleaseQty,
475
578
  releaseUomValue: leftReleaseQty * record.uomValue,
579
+ uom: inv.uom,
476
580
  product: { id: product.id },
477
581
  type: ORDER_TYPES.RELEASE_OF_GOODS
478
582
  }
479
583
  })
480
584
  } else {
481
- compReleaseQty += inv.qty
482
- compReleaseUomValue += inv.uomValue
483
- leftReleaseQty -= inv.qty
484
- leftReleaseUomValue -= inv.uomValue
585
+ compReleaseQty += inv.remainQty
586
+ compReleaseUomValue += inv.remainUomValue
587
+ leftReleaseQty -= inv.remainQty
588
+ leftReleaseUomValue -= inv.remainUomValue
485
589
 
486
590
  targetInventories.push({
487
591
  targetInventory: {
488
592
  inventory: { id: inv.id },
489
593
  packingType: inv.packingType,
490
594
  batchId: inv.batchId,
491
- releaseQty: inv.qty,
492
- releaseUomValue: inv.uomValue,
595
+ releaseQty: inv.remainQty,
596
+ releaseUomValue: inv.remainUomValue,
597
+ uom: inv.uom,
493
598
  product: { id: product.id },
494
599
  type: ORDER_TYPES.RELEASE_OF_GOODS
495
600
  }