@things-factory/sales-base 8.0.2 → 8.0.5

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 (178) hide show
  1. package/dist-server/tsconfig.tsbuildinfo +1 -1
  2. package/package.json +12 -12
  3. package/server/constants/attachment-type.ts +0 -9
  4. package/server/constants/index.ts +0 -7
  5. package/server/constants/load-type.ts +0 -4
  6. package/server/constants/order.ts +0 -203
  7. package/server/constants/product-group-type.ts +0 -4
  8. package/server/constants/release-good.ts +0 -9
  9. package/server/constants/transfer-order-type.ts +0 -6
  10. package/server/constants/validation-error-code.ts +0 -3
  11. package/server/constants/vas-target-type.ts +0 -25
  12. package/server/controllers/ecommerce/ecommerce-controller.ts +0 -122
  13. package/server/controllers/ecommerce/index.ts +0 -2
  14. package/server/controllers/ecommerce/sellercraft-controller.ts +0 -182
  15. package/server/controllers/index.ts +0 -2
  16. package/server/controllers/order-controller.ts +0 -296
  17. package/server/errors/index.ts +0 -1
  18. package/server/errors/validation-error.ts +0 -25
  19. package/server/index.ts +0 -5
  20. package/server/migrations/index.ts +0 -9
  21. package/server/service/arrival-notice/arrival-notice-mutation.ts +0 -1152
  22. package/server/service/arrival-notice/arrival-notice-query.ts +0 -549
  23. package/server/service/arrival-notice/arrival-notice-types.ts +0 -310
  24. package/server/service/arrival-notice/arrival-notice.ts +0 -202
  25. package/server/service/arrival-notice/index.ts +0 -9
  26. package/server/service/claim/claim-mutation.ts +0 -308
  27. package/server/service/claim/claim-query.ts +0 -122
  28. package/server/service/claim/claim-types.ts +0 -130
  29. package/server/service/claim/claim.ts +0 -140
  30. package/server/service/claim/index.ts +0 -9
  31. package/server/service/claim-detail/claim-detail-mutation.ts +0 -102
  32. package/server/service/claim-detail/claim-detail-query.ts +0 -55
  33. package/server/service/claim-detail/claim-detail-types.ts +0 -47
  34. package/server/service/claim-detail/claim-detail.ts +0 -69
  35. package/server/service/claim-detail/index.ts +0 -9
  36. package/server/service/claim-order/claim-order-mutation.ts +0 -101
  37. package/server/service/claim-order/claim-order-query.ts +0 -47
  38. package/server/service/claim-order/claim-order-types.ts +0 -35
  39. package/server/service/claim-order/claim-order.ts +0 -81
  40. package/server/service/claim-order/index.ts +0 -9
  41. package/server/service/collection-order/collection-order-mutation.ts +0 -245
  42. package/server/service/collection-order/collection-order-query.ts +0 -97
  43. package/server/service/collection-order/collection-order-types.ts +0 -165
  44. package/server/service/collection-order/collection-order.ts +0 -135
  45. package/server/service/collection-order/index.ts +0 -9
  46. package/server/service/delivery-order/delivery-order-mutation.ts +0 -967
  47. package/server/service/delivery-order/delivery-order-query.ts +0 -631
  48. package/server/service/delivery-order/delivery-order-types.ts +0 -268
  49. package/server/service/delivery-order/delivery-order.ts +0 -258
  50. package/server/service/delivery-order/index.ts +0 -9
  51. package/server/service/draft-release-good/draft-release-good-mutation.ts +0 -765
  52. package/server/service/draft-release-good/draft-release-good-query.ts +0 -354
  53. package/server/service/draft-release-good/draft-release-good-type.ts +0 -261
  54. package/server/service/draft-release-good/draft-release-good.ts +0 -284
  55. package/server/service/draft-release-good/index.ts +0 -9
  56. package/server/service/goods-receival-note/goods-receival-note-mutation.ts +0 -129
  57. package/server/service/goods-receival-note/goods-receival-note-query.ts +0 -280
  58. package/server/service/goods-receival-note/goods-receival-note-types.ts +0 -105
  59. package/server/service/goods-receival-note/goods-receival-note.ts +0 -127
  60. package/server/service/goods-receival-note/index.ts +0 -9
  61. package/server/service/index.ts +0 -238
  62. package/server/service/inventory-check/index.ts +0 -9
  63. package/server/service/inventory-check/inventory-check-mutation.ts +0 -149
  64. package/server/service/inventory-check/inventory-check-query.ts +0 -48
  65. package/server/service/inventory-check/inventory-check-types.ts +0 -48
  66. package/server/service/inventory-check/inventory-check.ts +0 -90
  67. package/server/service/invoice/index.ts +0 -9
  68. package/server/service/invoice/invoice-mutation.ts +0 -95
  69. package/server/service/invoice/invoice-query.ts +0 -53
  70. package/server/service/invoice/invoice-types.ts +0 -279
  71. package/server/service/invoice/invoice.ts +0 -230
  72. package/server/service/invoice-product/index.ts +0 -9
  73. package/server/service/invoice-product/invoice-product-mutation.ts +0 -54
  74. package/server/service/invoice-product/invoice-product-query.ts +0 -54
  75. package/server/service/invoice-product/invoice-product-types.ts +0 -84
  76. package/server/service/invoice-product/invoice-product.ts +0 -92
  77. package/server/service/job-sheet/index.ts +0 -9
  78. package/server/service/job-sheet/job-sheet-mutation.ts +0 -92
  79. package/server/service/job-sheet/job-sheet-query.ts +0 -112
  80. package/server/service/job-sheet/job-sheet-types.ts +0 -78
  81. package/server/service/job-sheet/job-sheet.ts +0 -102
  82. package/server/service/manifest/index.ts +0 -6
  83. package/server/service/manifest/manifest-mutation.ts +0 -190
  84. package/server/service/manifest/manifest-query.ts +0 -149
  85. package/server/service/manifest/manifest-type.ts +0 -84
  86. package/server/service/manifest/manifest.ts +0 -114
  87. package/server/service/order-inventory/index.ts +0 -9
  88. package/server/service/order-inventory/order-inventory-mutation.ts +0 -54
  89. package/server/service/order-inventory/order-inventory-query.ts +0 -722
  90. package/server/service/order-inventory/order-inventory-types.ts +0 -238
  91. package/server/service/order-inventory/order-inventory.ts +0 -401
  92. package/server/service/order-product/index.ts +0 -9
  93. package/server/service/order-product/order-product-mutation.ts +0 -48
  94. package/server/service/order-product/order-product-query.ts +0 -89
  95. package/server/service/order-product/order-product-types.ts +0 -335
  96. package/server/service/order-product/order-product.ts +0 -362
  97. package/server/service/order-tote/index.ts +0 -9
  98. package/server/service/order-tote/order-tote-mutation.ts +0 -31
  99. package/server/service/order-tote/order-tote-query.ts +0 -112
  100. package/server/service/order-tote/order-tote-types.ts +0 -47
  101. package/server/service/order-tote/order-tote.ts +0 -73
  102. package/server/service/order-tote-item/index.ts +0 -9
  103. package/server/service/order-tote-item/order-tote-item-mutation.ts +0 -31
  104. package/server/service/order-tote-item/order-tote-item-query.ts +0 -82
  105. package/server/service/order-tote-item/order-tote-item-types.ts +0 -56
  106. package/server/service/order-tote-item/order-tote-item.ts +0 -72
  107. package/server/service/order-tote-seal/index.ts +0 -9
  108. package/server/service/order-tote-seal/order-tote-seal-mutation.ts +0 -31
  109. package/server/service/order-tote-seal/order-tote-seal-query.ts +0 -59
  110. package/server/service/order-tote-seal/order-tote-seal-types.ts +0 -41
  111. package/server/service/order-tote-seal/order-tote-seal.ts +0 -46
  112. package/server/service/order-vas/index.ts +0 -9
  113. package/server/service/order-vas/order-vas-mutation.ts +0 -20
  114. package/server/service/order-vas/order-vas-query.ts +0 -72
  115. package/server/service/order-vas/order-vas-types.ts +0 -159
  116. package/server/service/order-vas/order-vas.ts +0 -207
  117. package/server/service/others/index.ts +0 -5
  118. package/server/service/others/other-query.ts +0 -563
  119. package/server/service/others/other-types.ts +0 -115
  120. package/server/service/purchase-order/index.ts +0 -9
  121. package/server/service/purchase-order/purchase-order-mutation.ts +0 -458
  122. package/server/service/purchase-order/purchase-order-query.ts +0 -90
  123. package/server/service/purchase-order/purchase-order-types.ts +0 -154
  124. package/server/service/purchase-order/purchase-order.ts +0 -172
  125. package/server/service/purchase-order-other-charge/index.ts +0 -9
  126. package/server/service/purchase-order-other-charge/purchase-order-other-charge-mutation.ts +0 -31
  127. package/server/service/purchase-order-other-charge/purchase-order-other-charge-query.ts +0 -52
  128. package/server/service/purchase-order-other-charge/purchase-order-other-charge-types.ts +0 -44
  129. package/server/service/purchase-order-other-charge/purchase-order-other-charge.ts +0 -68
  130. package/server/service/release-good/index.ts +0 -9
  131. package/server/service/release-good/release-good-mutation.ts +0 -1686
  132. package/server/service/release-good/release-good-query.ts +0 -980
  133. package/server/service/release-good/release-good-types.ts +0 -662
  134. package/server/service/release-good/release-good.ts +0 -490
  135. package/server/service/retail-replenishment-order/index.ts +0 -9
  136. package/server/service/retail-replenishment-order/retail-replenishment-order-mutation.ts +0 -382
  137. package/server/service/retail-replenishment-order/retail-replenishment-order-query.ts +0 -54
  138. package/server/service/retail-replenishment-order/retail-replenishment-order-types.ts +0 -101
  139. package/server/service/retail-replenishment-order/retail-replenishment-order.ts +0 -115
  140. package/server/service/return-order/index.ts +0 -9
  141. package/server/service/return-order/return-order-mutation.ts +0 -516
  142. package/server/service/return-order/return-order-query.ts +0 -226
  143. package/server/service/return-order/return-order-types.ts +0 -196
  144. package/server/service/return-order/return-order.ts +0 -127
  145. package/server/service/reverse-kitting-order/index.ts +0 -9
  146. package/server/service/reverse-kitting-order/reverse-kitting-order-mutation.ts +0 -500
  147. package/server/service/reverse-kitting-order/reverse-kitting-order-query.ts +0 -197
  148. package/server/service/reverse-kitting-order/reverse-kitting-order-type.ts +0 -173
  149. package/server/service/reverse-kitting-order/reverse-kitting-order.ts +0 -121
  150. package/server/service/reverse-kitting-order-inventory/index.ts +0 -9
  151. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-mutation.ts +0 -129
  152. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-query.ts +0 -52
  153. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-type.ts +0 -95
  154. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory.ts +0 -143
  155. package/server/service/shipping-order/index.ts +0 -9
  156. package/server/service/shipping-order/shipping-order-mutation.ts +0 -61
  157. package/server/service/shipping-order/shipping-order-query.ts +0 -61
  158. package/server/service/shipping-order/shipping-order-types.ts +0 -89
  159. package/server/service/shipping-order/shipping-order.ts +0 -129
  160. package/server/service/transfer-order/index.ts +0 -9
  161. package/server/service/transfer-order/transfer-order-mutation.ts +0 -309
  162. package/server/service/transfer-order/transfer-order-query.ts +0 -66
  163. package/server/service/transfer-order/transfer-order-types.ts +0 -97
  164. package/server/service/transfer-order/transfer-order.ts +0 -117
  165. package/server/service/vas/index.ts +0 -9
  166. package/server/service/vas/vas-mutation.ts +0 -106
  167. package/server/service/vas/vas-query.ts +0 -60
  168. package/server/service/vas/vas-types.ts +0 -71
  169. package/server/service/vas/vas.ts +0 -77
  170. package/server/service/vas-order/index.ts +0 -9
  171. package/server/service/vas-order/vas-order-mutation.ts +0 -259
  172. package/server/service/vas-order/vas-order-query.ts +0 -119
  173. package/server/service/vas-order/vas-order-types.ts +0 -49
  174. package/server/service/vas-order/vas-order.ts +0 -81
  175. package/server/utils/datetime-util.ts +0 -54
  176. package/server/utils/index.ts +0 -3
  177. package/server/utils/inventory-util.ts +0 -1155
  178. package/server/utils/order-no-generator.ts +0 -146
@@ -1,1155 +0,0 @@
1
- import { EntityManager, Equal, In, Not, Raw, Repository, SelectQueryBuilder } from 'typeorm'
2
-
3
- import { User } from '@things-factory/auth-base'
4
- import { Bizplace } from '@things-factory/biz-base'
5
- import { Product, ProductBundle } from '@things-factory/product-base'
6
- import { Domain, Filter, getRepository, ListParam } from '@things-factory/shell'
7
- import {
8
- Inventory,
9
- INVENTORY_STATUS,
10
- InventoryHistory,
11
- InventoryNoGenerator,
12
- Location,
13
- LOCATION_STATUS,
14
- LOCATION_TYPE,
15
- Pallet
16
- } from '@things-factory/warehouse-base'
17
-
18
- import { ORDER_TYPES } from '../constants'
19
- import { ValidationError } from '../errors'
20
- import {
21
- ArrivalNotice,
22
- DeliveryOrder,
23
- InventoryCheck,
24
- OrderInventory,
25
- ReleaseGood,
26
- ReturnOrder,
27
- ReverseKittingOrder,
28
- VasOrder
29
- } from '../service'
30
-
31
- export type ReferenceOrderType =
32
- | ArrivalNotice
33
- | ReleaseGood
34
- | VasOrder
35
- | InventoryCheck
36
- | DeliveryOrder
37
- | ReturnOrder
38
- | ReverseKittingOrder
39
-
40
- export const InventoryUtil = {
41
- /**
42
- * Get all available product belonging to list of bizplaces
43
- * @param {[Object]} bizplaces
44
- * @param {Object} params
45
- * @param {Object} context
46
- * @param {Object} trxMgr
47
- * @returns { object } {items, total}
48
- */
49
- async bizplaceProductInventory(
50
- bizplaces: Bizplace[],
51
- params: ListParam,
52
- context: ResolverContext,
53
- trxMgr: EntityManager
54
- ) {
55
- try {
56
- let filters = params.filters
57
-
58
- const { domain } = context.state
59
-
60
- const { bizplaceIds, productWhereClause, bundleWhereClause, whereClause, batchBundle, productDetailWhereClause } =
61
- await getConditions(bizplaces, filters, trxMgr, false)
62
-
63
- let productFilter = filters.find(itm => itm.name == 'productName')
64
- let inventoryBizplaceFilter = filters.find(itm => itm.name == 'bizplaceId')
65
-
66
- let queryStrings = `
67
- CREATE TEMP TABLE temp_inventory_product_group ON COMMIT DROP AS (
68
- SELECT * FROM (
69
- WITH oi as (
70
- SELECT
71
- SUM(oi.release_qty) AS release_qty,
72
- SUM(oi.release_uom_value) AS release_uom_value,
73
- oi.batch_id,
74
- oi.batch_id_ref,
75
- oi.product_id,
76
- p.name AS product_name,
77
- oi.packing_type,
78
- oi.packing_size,
79
- oi.uom
80
- FROM
81
- order_inventories oi
82
- LEFT JOIN
83
- products p
84
- ON
85
- oi.product_id = p.id
86
- WHERE
87
- (oi.status = 'PENDING' or oi.status = 'PENDING_RECEIVE' or oi.status = 'PENDING_WORKSHEET' or oi.status = 'PENDING_SPLIT')
88
- AND oi.batch_id NOTNULL
89
- AND oi.product_id NOTNULL
90
- AND oi.packing_type NOTNULL
91
- AND oi.packing_size NOTNULL
92
- AND oi.inventory_id IS NULL
93
- GROUP BY
94
- oi.batch_id,
95
- oi.batch_id_ref,
96
- oi.product_id,
97
- oi.packing_type,
98
- oi.packing_size,
99
- oi.uom,
100
- p.name
101
- )
102
- SELECT
103
- pd.packing_type AS "packingType",
104
- pd.packing_size AS "packingSize",
105
- pd.uom AS "uom",
106
- concat(p.name, ' (', p.description, ')') AS "productName",
107
- coalesce(p.sku, '') AS "productSKU",
108
- coalesce(p.brand, '') AS "productBrand",
109
- p.id AS "productId",
110
- COALESCE(SUM(COALESCE(i.qty, 0)) - SUM(COALESCE(i.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0)) - MAX(COALESCE(bp.bundle_product_release_qty, 0)),0) AS "remainQty",
111
- COALESCE(SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) - MAX(COALESCE(bp.bundle_product_release_uom_value, 0)),0) AS "remainUomValue",
112
- concat(SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) - MAX(COALESCE(bp.bundle_product_release_uom_value, 0)), ' ', pd.uom) AS "remainUomValueWithUom",
113
- 'SINGLE' AS "groupType"
114
- FROM products p
115
- INNER join product_details pd on pd.product_id = p.id
116
- LEFT JOIN (
117
- SELECT i.* FROM inventories i
118
- INNER JOIN locations l2 ON i.location_id = l2.id AND i.domain_id = l2.domain_id
119
- WHERE l2.type NOT IN ('${LOCATION_TYPE.QUARANTINE}', '${LOCATION_TYPE.RESERVE}')
120
- AND i.domain_id = $1 AND i.status = 'STORED'
121
- ${inventoryBizplaceFilter ? `AND i.bizplace_id = '${inventoryBizplaceFilter.value}'` : ``}
122
- ) i ON i.product_id = pd.product_id
123
- AND i.packing_type = pd.packing_type
124
- and i.packing_size = pd.packing_size
125
- and i.uom = pd.uom
126
- LEFT JOIN oi ON i.batch_id = oi.batch_id AND p.name = oi.product_name AND i.packing_type = oi.packing_type AND i.packing_size = oi.packing_size AND i.uom = oi.uom
127
- LEFT JOIN (
128
- SELECT pbs.product_id, SUM(pbs.bundle_qty * src.release_qty) AS bundle_product_release_qty, SUM(pbs.bundle_qty * src.release_uom_value) AS bundle_product_release_uom_value
129
- FROM product_bundle_settings pbs
130
- INNER JOIN json_populate_recordset(NULL::order_inventories, '${batchBundle}') src ON src.product_id = pbs.product_bundle_id
131
- GROUP BY pbs.product_id
132
- ) bp on i.product_id = bp.product_id
133
- WHERE p.bizplace_id IN (${bizplaceIds})
134
- ${productDetailWhereClause}
135
- ${
136
- productFilter
137
- ? `AND (
138
- lower(p.sku) ilike '${productFilter.value}'
139
- OR lower(p.name) ilike '${productFilter.value}'
140
- OR lower(p.description) ilike '${productFilter.value}'
141
- )
142
- `
143
- : ``
144
- }
145
- GROUP BY
146
- p.id,
147
- pd.packing_type,
148
- pd.packing_size,
149
- pd.uom
150
- UNION
151
- SELECT packing_type, packing_size,'UNIT' AS "uom", name AS "productName", sku AS "productSKU", '-' AS "productBrand", id AS "productId",
152
- COALESCE(MIN(FLOOR(pbs."availableQty")),0) AS "remainQty",
153
- COALESCE(MIN(FLOOR(pbs."availableUomValue")),0) AS "remainUomValue",
154
- CONCAT(COALESCE(MIN(FLOOR(pbs."availableUomValue")),0),' UNIT') AS "remainUomValueWithUom",
155
- 'BUNDLE' AS "groupType"
156
- FROM product_bundles pb
157
- LEFT JOIN (
158
- SELECT pbs.product_id, pbs.product_bundle_id, min(pbs.bundle_qty),
159
- (SUM(COALESCE(i2.qty, 0)) - SUM(COALESCE(i2.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0))) / min(pbs.bundle_qty) AS "availableQty",
160
- (SUM(COALESCE(i2.uom_value, 0)) - SUM(COALESCE(i2.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0))) / min(pbs.bundle_qty) AS "availableUomValue"
161
- FROM product_bundle_settings pbs
162
- LEFT JOIN inventories i2 ON i2.product_id = pbs.product_id AND i2.domain_id = $1 AND i2.status = 'STORED'
163
- INNER JOIN locations l2 ON i2.location_id = l2.id
164
- LEFT JOIN oi ON oi.product_id = i2.product_id
165
- WHERE l2.type NOT IN ('${LOCATION_TYPE.QUARANTINE}', '${LOCATION_TYPE.RESERVE}')
166
- ${inventoryBizplaceFilter ? `AND i2.bizplace_id = '${inventoryBizplaceFilter.value}'` : ``}
167
- GROUP BY
168
- pbs.product_id,
169
- pbs.product_bundle_id
170
- ) pbs ON pbs.product_bundle_id = pb.id
171
- ${bundleWhereClause}
172
- AND pb.bizplace_id IN (${bizplaceIds})
173
- ${
174
- productFilter
175
- ? `AND (
176
- lower(pb.sku) ilike '${productFilter.value}'
177
- OR lower(pb.name) ilike '${productFilter.value}'
178
- OR lower(pb.description) ilike '${productFilter.value}'
179
- )
180
- `
181
- : ``
182
- }
183
- GROUP BY
184
- pb.packing_type,
185
- pb.packing_size,
186
- pb.name,
187
- pb.sku,
188
- pb.id
189
- )
190
- AS inv_prod_grp
191
- ${whereClause}
192
- order by "productSKU", "productName"
193
- )
194
- `
195
-
196
- await trxMgr.query(queryStrings, [domain.id])
197
-
198
- const [{ total }]: any = await trxMgr.query(`select count(*) as total from temp_inventory_product_group`)
199
- let items: any[] = []
200
-
201
- if (params?.pagination) {
202
- items = await trxMgr.query(`select * from temp_inventory_product_group OFFSET $1 LIMIT $2`, [
203
- (params.pagination.page - 1) * params.pagination.limit,
204
- params.pagination.limit
205
- ])
206
- } else {
207
- items = await trxMgr.query(`select * from temp_inventory_product_group`)
208
- }
209
-
210
- return { items, total }
211
- } catch (error) {
212
- throw error
213
- }
214
- },
215
-
216
- /**
217
- * Get all available product belonging to list of bizplaces
218
- * @param {[Object]} bizplaces
219
- * @param {Object} params
220
- * @param {Object} context
221
- * @param {Object} trxMgr
222
- * @returns { object } {items, total}
223
- */
224
- async bizplaceInventoryProductGroup(
225
- bizplaces: Bizplace[],
226
- params: ListParam,
227
- context: ResolverContext,
228
- trxMgr: EntityManager
229
- ) {
230
- try {
231
- let filters = params.filters
232
- const { domain } = context.state
233
-
234
- const { bizplaceIds, productWhereClause, bundleWhereClause, whereClause, batchBundle } = await getConditions(
235
- bizplaces,
236
- filters,
237
- trxMgr
238
- )
239
-
240
- const _groupType = filters.find(res => res.name == 'groupType')
241
-
242
- let queryStrings = `
243
- CREATE TEMP TABLE temp_inventory_product_group AS (
244
- SELECT * FROM (
245
- WITH oi as (
246
- SELECT
247
- round(cast(SUM(oi.release_qty) as numeric), 3) AS release_qty,
248
- round(cast(SUM(oi.release_uom_value) as numeric), 3) AS release_uom_value,
249
- oi.batch_id,
250
- oi.batch_id_ref,
251
- oi.product_id,
252
- p.name AS product_name,
253
- oi.packing_type,
254
- oi.packing_size,
255
- oi.uom
256
- FROM
257
- order_inventories oi
258
- LEFT JOIN
259
- products p
260
- ON
261
- oi.product_id = p.id
262
- WHERE
263
- (oi.status = 'PENDING' or oi.status = 'PENDING_RECEIVE' or oi.status = 'PENDING_WORKSHEET' or oi.status = 'PENDING_SPLIT')
264
- AND oi.batch_id NOTNULL
265
- AND oi.product_id NOTNULL
266
- AND oi.packing_type NOTNULL
267
- AND oi.packing_size NOTNULL
268
- AND oi.inventory_id IS NULL
269
- GROUP BY
270
- oi.batch_id,
271
- oi.batch_id_ref,
272
- oi.product_id,
273
- oi.packing_type,
274
- oi.packing_size,
275
- oi.uom,
276
- p.name
277
- )
278
- SELECT
279
- i.batch_id AS "batchId",
280
- i.batch_id_ref AS "batchIdRef",
281
- i.packing_type AS "packingType",
282
- i.packing_size AS "packingSize",
283
- i.pallet_id as "palletId",
284
- i.created_at as "createdAt",
285
- i.uom AS "uom",
286
- concat(p.name, ' (', p.description, ')') AS "productName",
287
- coalesce(p.sku, '') AS "productSKU",
288
- coalesce(p.brand, '') AS "productBrand",
289
- p.id AS "productId",
290
- ROUND(cast(SUM(COALESCE(i.qty, 0)) - SUM(COALESCE(i.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0)) - MAX(COALESCE(bp.bundle_product_release_qty, 0)) as numeric), 3) AS "remainQty",
291
- ROUND(cast(SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) - MAX(COALESCE(bp.bundle_product_release_uom_value, 0)) as numeric), 3) AS "remainUomValue",
292
- concat(SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) - MAX(COALESCE(bp.bundle_product_release_uom_value, 0)), ' ', i.uom) AS "remainUomValueWithUom",
293
- 'SINGLE' AS "groupType",
294
- i.expiration_date as "expirationDate"
295
- FROM
296
- inventories i
297
- LEFT JOIN products p ON i.product_id = p.id
298
- LEFT JOIN oi ON i.batch_id = oi.batch_id AND p.name = oi.product_name AND i.packing_type = oi.packing_type AND i.packing_size = oi.packing_size AND i.uom = oi.uom
299
- LEFT JOIN locations l2 ON i.location_id = l2.id AND i.domain_id = l2.domain_id
300
- LEFT JOIN (
301
- SELECT pbs.product_id, SUM(pbs.bundle_qty * src.release_qty) AS bundle_product_release_qty, SUM(pbs.bundle_qty * src.release_uom_value) AS bundle_product_release_uom_value
302
- FROM product_bundle_settings pbs
303
- INNER JOIN json_populate_recordset(NULL::order_inventories,'${batchBundle}') src ON src.product_id = pbs.product_bundle_id
304
- GROUP BY pbs.product_id
305
- ) bp on i.product_id = bp.product_id
306
- WHERE i.domain_id = $1
307
- AND l2.type NOT IN ('${LOCATION_TYPE.QUARANTINE}', '${LOCATION_TYPE.RESERVE}')
308
- ${productWhereClause}
309
- GROUP BY
310
- i.batch_id,
311
- i.batch_id_ref,
312
- p.id,
313
- i.packing_type,
314
- i.packing_size,
315
- i.expiration_date,
316
- i.pallet_id,
317
- i.created_at,
318
- i.uom
319
- UNION
320
- SELECT 'BUNDLE' AS "batchId", null as "batchIdRef", packing_type, packing_size, null AS "palletId", null AS "createdAt", 'UNIT' AS "uom", name AS "productName", sku AS "productSKU", 'brand' AS "productBrand", id AS "productId",
321
- MIN(FLOOR(pbs."availableQty")) AS "remainQty",
322
- MIN(FLOOR(pbs."availableUomValue")) AS "remainUomValue",
323
- CONCAT(MIN(FLOOR(pbs."availableUomValue")),' UNIT') AS "remainUomValueWithUom",
324
- 'BUNDLE' AS "groupType",
325
- pbs."expirationDate" as "expirationDate"
326
- FROM product_bundles pb
327
- INNER JOIN (
328
- SELECT pbs.product_id, pbs.product_bundle_id, min(pbs.bundle_qty),
329
- (SUM(COALESCE(i2.qty, 0)) - SUM(COALESCE(i2.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0))) / min(pbs.bundle_qty) AS "availableQty",
330
- (SUM(COALESCE(i2.uom_value, 0)) - SUM(COALESCE(i2.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0))) / min(pbs.bundle_qty) AS "availableUomValue", i2.expiration_date as "expirationDate"
331
- FROM product_bundle_settings pbs
332
- LEFT JOIN inventories i2 ON i2.product_id = pbs.product_id AND i2.domain_id = $1
333
- AND i2.bizplace_id IN (${bizplaceIds})
334
- AND i2.status = 'STORED'
335
- LEFT JOIN oi ON oi.product_id = i2.product_id
336
- GROUP BY pbs.product_id, pbs.product_bundle_id, i2.expiration_date
337
- ) pbs ON pbs.product_bundle_id = pb.id
338
- ${bundleWhereClause}
339
- GROUP BY pb.packing_type, pb.packing_size, pb.name, pb.sku, pb.id, pbs."expirationDate"
340
- )
341
- AS inv_prod_grp
342
- ${whereClause}
343
- )
344
- `
345
-
346
- let filterGroupTypeQuery
347
-
348
- if (_groupType) {
349
- if (_groupType.value === 'SINGLE') {
350
- filterGroupTypeQuery = `where "groupType"= 'SINGLE' `
351
- } else {
352
- filterGroupTypeQuery = `where "groupType"= 'BUNDLE' `
353
- }
354
- }
355
-
356
- let sortingArr = params.sortings || []
357
-
358
- let sortedBy = []
359
-
360
- if (sortingArr.length > 0) {
361
- sortingArr.forEach(element => {
362
- if (element.desc) {
363
- sortedBy.push(`"${element.name}" DESC `)
364
- } else {
365
- sortedBy.push(`"${element.name}" `)
366
- }
367
- })
368
- }
369
-
370
- await trxMgr.query(queryStrings, [domain.id])
371
-
372
- const [{ total }]: any = await trxMgr.query(
373
- `select count(*) as total from temp_inventory_product_group ${filterGroupTypeQuery ? filterGroupTypeQuery : ''}`
374
- )
375
- let items: any[] = []
376
-
377
- if (params?.pagination) {
378
- items = await trxMgr.query(
379
- `select * from temp_inventory_product_group ${filterGroupTypeQuery ? filterGroupTypeQuery : ''} ${
380
- sortingArr.length > 0 ? ' Order by ' + sortedBy.toString() : ''
381
- } OFFSET $1 LIMIT $2`,
382
- [(params.pagination.page - 1) * params.pagination.limit, params.pagination.limit]
383
- )
384
- } else {
385
- items = await trxMgr.query(
386
- `select * from temp_inventory_product_group ${filterGroupTypeQuery ? filterGroupTypeQuery : ''} ${
387
- sortingArr.length > 0 ? ' Order by ' + sortedBy.toString() : ''
388
- }`
389
- )
390
- }
391
-
392
- await trxMgr.query(`drop table temp_inventory_product_group`)
393
-
394
- return { items, total }
395
- } catch (error) {
396
- throw error
397
- }
398
- },
399
-
400
- async inventoryProductGroupOpenAPI(
401
- bizplaces: Bizplace[],
402
- params: ListParam,
403
- context: ResolverContext,
404
- trxMgr: EntityManager
405
- ) {
406
- try {
407
- let filters = params.filters
408
- const { domain } = context.state
409
-
410
- const { apiWhereClause } = await getConditions(bizplaces, filters, trxMgr)
411
-
412
- let queryStrings = `
413
- CREATE TEMP TABLE temp_inventory_product_group_open_api AS (
414
- SELECT * FROM (
415
- WITH oi as (
416
- SELECT
417
- SUM(oi.release_qty) AS release_qty,
418
- SUM(oi.release_uom_value) AS release_uom_value,
419
- oi.product_id,
420
- p.name AS product_name,
421
- oi.packing_type,
422
- oi.packing_size,
423
- oi.uom
424
- FROM
425
- order_inventories oi
426
- LEFT JOIN
427
- products p
428
- ON
429
- oi.product_id = p.id
430
- WHERE
431
- (oi.status = 'PENDING' or oi.status = 'PENDING_RECEIVE' or oi.status = 'PENDING_WORKSHEET' or oi.status = 'PENDING_SPLIT')
432
- AND oi.product_id NOTNULL
433
- AND oi.packing_type NOTNULL
434
- AND oi.packing_size NOTNULL
435
- AND oi.inventory_id IS NULL
436
- GROUP BY
437
- oi.product_id,
438
- oi.packing_type,
439
- oi.packing_size,
440
- oi.uom,
441
- p.name
442
- )
443
- SELECT
444
- i.packing_type AS "packingType",
445
- i.packing_size AS "packingSize",
446
- i.uom AS "uom",
447
- concat(p.name, ' (', p.description, ')') AS "productName",
448
- coalesce(p.sku, '') AS "productSKU",
449
- coalesce(p.brand, '') AS "productBrand",
450
- p.id AS "productId",
451
- SUM(COALESCE(i.qty, 0)) - SUM(COALESCE(i.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0)) AS "remainQty",
452
- SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) AS "remainUomValue",
453
- SUM(COALESCE(i.qty, 0)) AS "totalQty",
454
- SUM(COALESCE(i.uom_value, 0)) AS "totalUomValue",
455
- SUM(COALESCE(i.locked_qty, 0)) + MAX(COALESCE(oi.release_qty, 0)) AS "totalLockedQty",
456
- SUM(COALESCE(i.locked_uom_value, 0)) + MAX(COALESCE(oi.release_uom_value, 0)) AS "totalLockedUomValue",
457
- concat(SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)), ' ', i.uom) AS "remainUomValueWithUom"
458
- FROM inventories i
459
- LEFT JOIN products p ON i.product_id = p.id
460
- LEFT JOIN oi ON p.name = oi.product_name AND i.packing_type = oi.packing_type AND i.packing_size = oi.packing_size AND i.uom = oi.uom
461
- LEFT JOIN locations l2 ON i.location_id = l2.id AND i.domain_id = l2.domain_id
462
- WHERE i.domain_id = $1
463
- AND l2.type NOT IN ('${LOCATION_TYPE.QUARANTINE}', '${LOCATION_TYPE.RESERVE}')
464
- ${apiWhereClause}
465
- GROUP BY
466
- p.id,
467
- i.packing_type,
468
- i.packing_size,
469
- i.uom
470
- )
471
- AS inv_prod_grp
472
- )
473
- `
474
-
475
- await trxMgr.query(queryStrings, [domain.id])
476
-
477
- const [{ total }]: any = await trxMgr.query(`select count(*) as total from temp_inventory_product_group_open_api`)
478
- let items: any[] = []
479
-
480
- if (params?.pagination) {
481
- items = await trxMgr.query(`select * from temp_inventory_product_group_open_api OFFSET $1 LIMIT $2`, [
482
- (params.pagination.page - 1) * params.pagination.limit,
483
- params.pagination.limit
484
- ])
485
- } else {
486
- items = await trxMgr.query(`select * from temp_inventory_product_group_open_api`)
487
- }
488
-
489
- await trxMgr.query(`drop table temp_inventory_product_group_open_api`)
490
-
491
- return { items, total }
492
- } catch (error) {
493
- throw error
494
- }
495
- },
496
-
497
- /**
498
- * Validate warehouse partners product quantity
499
- * @param {Object} warehouseDomain -
500
- * @param {Object} partnerBizplace - {id}
501
- * @param {Object} orderInventories - [{id, qty}]
502
- * @param {Object} context
503
- * @param {Object} trxMgr
504
- */
505
- async validateWarehousePartnersProductsQuantity(
506
- warehouseDomain: Domain,
507
- partnerBizplace: Bizplace,
508
- orderInventories: OrderInventory[],
509
- context: any,
510
- trxMgr?: EntityManager
511
- ): Promise<void> {
512
- let json_oi = JSON.stringify(
513
- orderInventories.map(x => {
514
- return {
515
- product_id: x.product.id,
516
- batch_id: x.batchId,
517
- packing_type: x.packingType,
518
- release_qty: x.releaseQty,
519
- uom: x.uom
520
- }
521
- })
522
- )
523
-
524
- let resultQb = await trxMgr.query(
525
- `
526
- select joi.product_id as "productId", joi.batch_id as "batchId", joi.packing_type as "packingType", joi.uom as "uom", joi.release_qty as "releaseQty",
527
- ROUND(cast(sum(i.qty - coalesce(i.locked_qty,0)) - coalesce(
528
- (
529
- select sum(oi.release_qty) from order_inventories oi
530
- where (oi.status = 'PENDING' or oi.status = 'PENDING_RECEIVE' or oi.status = 'PENDING_WORKSHEET' or oi.status = 'PENDING_SPLIT')
531
- and oi.inventory_id IS null
532
- and oi.product_id = joi.product_id
533
- and oi.batch_id = joi.batch_id
534
- and oi.packing_type = joi.packing_type
535
- and oi.uom = joi.uom
536
- and oi.domain_id = $1
537
- and oi.bizplace_id = $2
538
- ),0) as numeric), 3) as "availableQty",
539
- ROUND(cast(sum(i.uom_value - coalesce(i.locked_uom_value ,0)) - coalesce(
540
- (
541
- select sum(oi.release_uom_value) from order_inventories oi
542
- where (oi.status = 'PENDING' or oi.status = 'PENDING_RECEIVE' or oi.status = 'PENDING_WORKSHEET' or oi.status = 'PENDING_SPLIT')
543
- and oi.inventory_id IS null
544
- and oi.product_id = joi.product_id
545
- and oi.batch_id = joi.batch_id
546
- and oi.packing_type = joi.packing_type
547
- and oi.uom = joi.uom
548
- and oi.domain_id = $1
549
- and oi.bizplace_id = $2
550
- ),0) as numeric), 3) as "availableUomValue"
551
- from json_populate_recordset(NULL::order_inventories,'${json_oi}') joi
552
- left join inventories i on joi.product_id = i.product_id
553
- and joi.batch_id = i.batch_id
554
- and joi.packing_type = i.packing_type and i.status ='STORED'
555
- and joi.uom = i.uom
556
- and i.domain_id = $1
557
- and i.bizplace_id = $2
558
- group by joi.product_id, joi.batch_id, joi.packing_type, joi.release_qty, joi.uom
559
- `,
560
- [warehouseDomain.id, partnerBizplace.id]
561
- )
562
-
563
- resultQb.forEach(itm => {
564
- if (itm.releaseQty > itm.availableQty) {
565
- throw new ValidationError({
566
- ...ValidationError.ERROR_CODES.RELEASE_QTY_OVER_LIMIT,
567
- detail: { data: JSON.stringify(resultQb) }
568
- })
569
- }
570
- })
571
- },
572
-
573
- async checkPalletDuplication(palletId: string, warehouseDomain: Domain, trxMgr: EntityManager): Promise<void> {
574
- const duplicatedPalletCnt: number = await trxMgr.getRepository(Inventory).countBy({
575
- domain: { id: warehouseDomain.id },
576
- palletId,
577
- status: Not(Equal(INVENTORY_STATUS.TERMINATED))
578
- })
579
-
580
- if (duplicatedPalletCnt) throw new Error(palletId + ` exists`)
581
-
582
- const duplicatedReusablePalletCnt: number = await trxMgr.getRepository(Pallet).count({
583
- where: {
584
- domain: { id: warehouseDomain.id },
585
- name: palletId
586
- }
587
- })
588
-
589
- if (duplicatedReusablePalletCnt) throw new Error(palletId + ` exists`)
590
- },
591
-
592
- /**
593
- * To pre-assign inventories automatically for orderInventories in Release Goods
594
- * @param product
595
- * @param orderInventory
596
- * @param packingType
597
- * @param locationSortingRules
598
- * @param customerBizplace
599
- * @param domain
600
- * @param trxMgr
601
- * @returns orderInventories
602
- */
603
- async autoAssignInventoryForRelease(
604
- product: Product,
605
- orderInventory: OrderInventory,
606
- packingType: string,
607
- locationSortingRules: any = [],
608
- customerBizplace: Bizplace,
609
- domain: Domain,
610
- trxMgr: EntityManager,
611
- batchId?: string
612
- ): Promise<OrderInventory[]> {
613
- let qb: SelectQueryBuilder<Inventory> = trxMgr.getRepository(Inventory).createQueryBuilder('iv')
614
- qb.leftJoinAndSelect('iv.location', 'loc')
615
- .andWhere('"iv"."domain_id" = :domainId')
616
- .andWhere('"iv"."bizplace_id" = :bizplaceId')
617
- .andWhere('"iv"."packing_type" = :packingType')
618
- .andWhere('"iv"."product_id" = :productId')
619
- .andWhere('"iv"."status" = :status')
620
- .andWhere('"iv"."qty" - COALESCE("iv"."locked_qty", 0) > 0')
621
- .andWhere('"loc"."type" NOT IN (:...locationTypes)')
622
- .setParameters({
623
- domainId: domain.id,
624
- bizplaceId: customerBizplace.id,
625
- packingType: packingType,
626
- productId: product.id,
627
- status: INVENTORY_STATUS.STORED,
628
- locationTypes: [LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
629
- })
630
-
631
- if (batchId) {
632
- qb.andWhere('"iv"."batch_id" = :batchId', { batchId: batchId })
633
- }
634
-
635
- let locationSorting = function (qb: SelectQueryBuilder<Inventory>, locationSortingRules) {
636
- if (locationSortingRules?.length) {
637
- locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
638
- idx === 0
639
- ? qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
640
- : qb.addOrderBy(`loc.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
641
- })
642
- } else {
643
- qb.addOrderBy('"loc"."name"', 'ASC')
644
- qb.addOrderBy('"iv"."created_at"', 'ASC')
645
- }
646
- return qb
647
- }
648
-
649
- switch (product?.pickingStrategy) {
650
- case 'FIFO':
651
- qb.addOrderBy('"iv"."created_at"', 'ASC')
652
- qb = locationSorting(qb, locationSortingRules)
653
- break
654
- case 'LIFO':
655
- qb.addOrderBy('"iv"."created_at"', 'DESC')
656
- qb = locationSorting(qb, locationSortingRules)
657
- break
658
- case 'FEFO':
659
- qb.addOrderBy('"iv"."expiration_date"', 'ASC')
660
- qb.addOrderBy('"iv"."created_at"', 'ASC')
661
- qb = locationSorting(qb, locationSortingRules)
662
- break
663
- case 'FMFO':
664
- qb.addOrderBy('"iv"."manufacture_date"', 'ASC')
665
- qb.addOrderBy('"iv"."created_at"', 'ASC')
666
- qb = locationSorting(qb, locationSortingRules)
667
- break
668
- case 'LOCATION':
669
- qb = locationSorting(qb, locationSortingRules)
670
- break
671
- default:
672
- qb.addOrderBy('"iv"."created_at"', 'ASC')
673
- qb = locationSorting(qb, locationSortingRules)
674
- break
675
- }
676
-
677
- let inventories: Inventory[] = await qb.getMany()
678
- if (!inventories?.length) throw new Error(`no inventories found for ${product.sku}`)
679
-
680
- inventories = inventories.map(inventory => {
681
- return {
682
- ...inventory,
683
- remainQty: inventory.qty - (inventory?.lockedQty || 0),
684
- remainUomValue: inventory.uomValue - (inventory?.lockedUomValue || 0)
685
- }
686
- })
687
-
688
- return _composeTargetInventories(product, orderInventory, inventories)
689
- },
690
-
691
- /**
692
- * @summary Do transaction on inventory record
693
- * @description It will update inventory after set a temp (domain, updater)
694
- * and then generate inventory history based on current changes
695
- */
696
- async transactionInventory(
697
- inventory: Inventory,
698
- referenceOrder: ReferenceOrderType,
699
- changedQty: number,
700
- changedWeight: number,
701
- transactionType: string,
702
- user: User,
703
- trxMgr: EntityManager
704
- ): Promise<Inventory> {
705
- if (inventory.id) {
706
- inventory = (await updateInventory(inventory, trxMgr)) as any
707
- } else {
708
- inventory = (await createInventory(inventory, trxMgr)) as any
709
- }
710
-
711
- await generateInventoryHistory(inventory, referenceOrder, transactionType, changedQty, changedWeight, user, trxMgr)
712
-
713
- return inventory
714
- }
715
- }
716
-
717
- interface Conditions {
718
- bizplaceIds: string
719
- productWhereClause: string
720
- bundleWhereClause: string
721
- whereClause: string
722
- batchBundle: string
723
- apiWhereClause: string
724
- productDetailWhereClause: string
725
- }
726
-
727
- async function getConditions(
728
- bizplaces: Bizplace[],
729
- filters: Filter[],
730
- trxMgr: EntityManager,
731
- hasRemainingQty: Boolean = true
732
- ): Promise<Conditions> {
733
- let bizplaceIds = bizplaces.map((bizplace: Bizplace) => `'${bizplace.id}'`).join()
734
-
735
- let productWhereClause = `
736
- AND i.status = 'STORED'
737
- AND i.bizplace_id IN (${bizplaceIds})
738
- `
739
- let apiWhereClause = `
740
- AND i.bizplace_id IN (${bizplaceIds})
741
- `
742
- let bundleWhereClause = `
743
- WHERE pb.status = 'ACTIVATED'
744
- `
745
-
746
- let whereClause = hasRemainingQty ? ` WHERE "remainQty" > 0` : ` WHERE 1 = 1`
747
-
748
- let productDetailWhereClause = ``
749
-
750
- let batchBundle: string
751
- if (filters?.length) {
752
- await Promise.all(
753
- filters.map(async (filter: { name: string; operator: string; value: any }) => {
754
- const name = filter.name
755
- const operator = filter.operator.toLowerCase()
756
- let value = filter.value
757
-
758
- switch (name) {
759
- case 'productId':
760
- if (operator == 'in')
761
- whereClause += `AND "productId" ${operator === 'in' ? 'IN' : 'NOT IN'} (${value
762
- .map(itm => {
763
- return `'${itm}'`
764
- })
765
- .join(',')})`
766
- break
767
-
768
- case 'productBundleId':
769
- if (operator == 'in')
770
- whereClause += `AND "productId" ${operator === 'in' ? 'IN' : 'NOT IN'} (${value
771
- .map(itm => {
772
- return `'${itm}'`
773
- })
774
- .join(',')})`
775
- break
776
-
777
- case 'batchId':
778
- whereClause += `AND LOWER("batchId") LIKE '${value.toLowerCase()}'`
779
- break
780
-
781
- case 'batchIdRef':
782
- whereClause += `AND LOWER("batchIdRef") LIKE '${value.toLowerCase()}'`
783
- break
784
-
785
- case 'productBrand':
786
- whereClause += `AND LOWER("productBrand") LIKE '${value.toLowerCase()}'`
787
- break
788
-
789
- case 'productSKU':
790
- whereClause += `AND LOWER("productSKU") LIKE '${value.toLowerCase()}'`
791
-
792
- case 'productName':
793
- const products: Product[] = await trxMgr.getRepository(Product).find({
794
- select: ['id'],
795
- where: [
796
- {
797
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
798
- name: Raw(
799
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
800
- )
801
- },
802
- {
803
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
804
- sku: Raw(
805
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
806
- )
807
- },
808
- {
809
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
810
- description: Raw(
811
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
812
- )
813
- }
814
- ]
815
- })
816
-
817
- const productIds: string = products
818
- .map((product: Product) => product.id)
819
- .map((id: string) => `'${id}'`)
820
- .join()
821
-
822
- if (productIds.length) {
823
- productWhereClause += `AND i.product_id IN (${productIds})`
824
- } else {
825
- productWhereClause += `AND i.product_id ISNULL`
826
- }
827
-
828
- // filter product bundle as product info
829
- const productBundles: ProductBundle[] = await trxMgr.getRepository(ProductBundle).find({
830
- select: ['id'],
831
- where: [
832
- {
833
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
834
- name: Raw(
835
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
836
- )
837
- },
838
- {
839
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
840
- sku: Raw(
841
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
842
- )
843
- },
844
- {
845
- bizplace: In(bizplaces.map((bizplace: Bizplace) => bizplace.id)),
846
- description: Raw(
847
- (alias: string) => `LOWER(${alias}) LIKE '${value.toLowerCase().trim().replace(/'/g, "''")}'`
848
- )
849
- }
850
- ]
851
- })
852
-
853
- const productBundleIds: string = productBundles
854
- .map((bundle: ProductBundle) => bundle.id)
855
- .map((id: string) => `'${id}'`)
856
- .join()
857
-
858
- if (productBundleIds.length) {
859
- bundleWhereClause += `AND pb.id IN (${productBundleIds})`
860
- } else {
861
- bundleWhereClause += `AND pb.id ISNULL`
862
- }
863
- break
864
-
865
- case 'packingType':
866
- whereClause += `AND LOWER("packingType") LIKE '${value.toLowerCase()}'`
867
- break
868
-
869
- case 'showBundle':
870
- whereClause += `AND LOWER("groupType") LIKE 'bundle'`
871
- break
872
-
873
- case 'hideBundle':
874
- whereClause += `AND LOWER("groupType") LIKE 'single'`
875
- break
876
-
877
- case 'batch_product':
878
- productWhereClause += `
879
- AND (i.batch_id, p.id, i.packing_type, i.packing_size) ${operator === 'in' ? 'IN' : 'NOT IN'} (${value
880
- .map(
881
- (v: { batchId: string; productId: string; packingType: string; packingSize: string }) =>
882
- `('${v.batchId}', '${v.productId}', '${v.packingType}', '${v.packingSize}')`
883
- )
884
- .join()})
885
- `
886
- break
887
-
888
- case 'batch_bundle':
889
- bundleWhereClause += `
890
- ${bundleWhereClause == '' ? 'WHERE' : 'AND'} pb.id ${operator === 'in' ? 'IN' : 'NOT IN'} (${value
891
- .map((v: { productId: string }) => `('${v.productId}')`)
892
- .join()})
893
- `
894
- break
895
-
896
- case 'product':
897
- productDetailWhereClause += `
898
- AND (pd.product_id, pd.packing_type, pd.packing_size, pd.uom) ${
899
- operator === 'in' ? 'IN' : 'NOT IN'
900
- } (${value
901
- .map(
902
- (v: { productId: string; packingType: string; packingSize: string; uom: string }) =>
903
- `('${v.productId}', '${v.packingType}', '${v.packingSize}', '${v.uom}')`
904
- )
905
- .join()})
906
- `
907
- break
908
- }
909
- })
910
- )
911
-
912
- batchBundle = JSON.stringify(
913
- filters
914
- .find(filter => filter.name === 'batch_bundle')
915
- ?.value.map(itm => {
916
- return {
917
- product_id: itm.productId,
918
- release_qty: itm?.releaseQty || 0,
919
- release_uom_value: itm?.releaseUomValue || 0
920
- }
921
- }) || []
922
- )
923
- }
924
-
925
- return {
926
- bizplaceIds,
927
- productWhereClause,
928
- apiWhereClause,
929
- bundleWhereClause,
930
- whereClause,
931
- batchBundle,
932
- productDetailWhereClause
933
- }
934
- }
935
-
936
- async function createInventory(
937
- inventory: Partial<Inventory> | Partial<Inventory>[],
938
- trxMgr: EntityManager
939
- ): Promise<Inventory | Inventory[]> {
940
- return await trxMgr.getRepository(Inventory).save(inventory as Inventory)
941
- }
942
-
943
- /**
944
- * @summary Update inventory record
945
- * @description It will update inventory after set a stamp (domain, updater)
946
- * The special point of this function is that this changes won't generate inventory history
947
- * If you want to generate inventory history automatically you would better to use transactionInventory function
948
- */
949
- async function updateInventory(
950
- inventory: Partial<Inventory> | Partial<Inventory>[],
951
- trxMgr: EntityManager
952
- ): Promise<Partial<Inventory> | Partial<Inventory>[]> {
953
- if (!(inventory as any).id) throw new Error(`Target doesn't have ID`)
954
- return await trxMgr.getRepository(Inventory).save(inventory as Inventory)
955
- }
956
-
957
- async function generateInventoryHistory(
958
- inventory: Inventory,
959
- refOrder: any,
960
- transactionType: string,
961
- qty: number,
962
- uomValue: number,
963
- user: User,
964
- trxMgr?: EntityManager
965
- ): Promise<InventoryHistory> {
966
- const invHistoryRepo: Repository<InventoryHistory> =
967
- trxMgr?.getRepository(InventoryHistory) || getRepository(InventoryHistory)
968
- const invRepo: Repository<Inventory> = trxMgr?.getRepository(Inventory) || getRepository(Inventory)
969
-
970
- if (!inventory?.id) throw new Error(`Can't find out ID of inventory.`)
971
- if (!refOrder?.id || !refOrder.name) throw new Error(`Can't find out ID or Name of Reference Order`)
972
- if (
973
- !inventory?.domain ||
974
- !inventory?.bizplace ||
975
- !inventory?.product?.id ||
976
- !inventory?.warehouse?.id ||
977
- !inventory?.location?.id
978
- ) {
979
- inventory = await invRepo.findOne({
980
- where: { id: inventory.id },
981
- relations: ['domain', 'bizplace', 'product', 'warehouse', 'location']
982
- })
983
- }
984
-
985
- const domain: Domain = inventory.domain
986
- const location: Location = inventory.location
987
-
988
- const seq: number = await invHistoryRepo.countBy({
989
- domain: { id: inventory.domain.id },
990
- palletId: inventory.palletId
991
- })
992
- let openingQty: number = 0
993
- let openingUomValue: number = 0
994
-
995
- if (seq) {
996
- const lastInvHistory: InventoryHistory = await invHistoryRepo.findOneBy({
997
- domain: { id: inventory.domain.id },
998
- palletId: inventory.palletId,
999
- seq: seq - 1
1000
- })
1001
- openingQty = lastInvHistory.openingQty + lastInvHistory.qty
1002
- openingUomValue = lastInvHistory.openingUomValue + lastInvHistory.uomValue
1003
- }
1004
-
1005
- let inventoryHistory: InventoryHistory = new InventoryHistory()
1006
- inventoryHistory.name = InventoryNoGenerator.inventoryHistoryName()
1007
- inventoryHistory.description = inventory.description
1008
- inventoryHistory.seq = seq
1009
- inventoryHistory.palletId = inventory.palletId
1010
- inventoryHistory.cartonId = inventory.cartonId
1011
- inventoryHistory.batchId = inventory.batchId
1012
- inventoryHistory.batchIdRef = inventory.batchIdRef
1013
- inventoryHistory.status = inventory.status
1014
- inventoryHistory.transactionType = transactionType
1015
- inventoryHistory.refOrderId = refOrder?.id || null
1016
- inventoryHistory.orderNo = refOrder?.name || null
1017
- inventoryHistory.orderRefNo = refOrder?.refNo || null
1018
- inventoryHistory.inventory = inventory
1019
- inventoryHistory.product = inventory.product
1020
- inventoryHistory.reusablePallet = inventory.reusablePallet
1021
- inventoryHistory.zone = inventory.zone
1022
- inventoryHistory.warehouse = inventory.warehouse
1023
- inventoryHistory.location = inventory.location
1024
- inventoryHistory.expirationDate = inventory.expirationDate
1025
- inventoryHistory.packingType = inventory.packingType
1026
- inventoryHistory.packingSize = inventory.packingSize
1027
- inventoryHistory.uom = inventory.uom
1028
- inventoryHistory.qty = qty
1029
- inventoryHistory.openingQty = openingQty
1030
- inventoryHistory.uomValue = uomValue
1031
- inventoryHistory.openingUomValue = openingUomValue
1032
- inventoryHistory.unitCost = inventory.unitCost
1033
- inventoryHistory.domain = inventory.domain
1034
- inventoryHistory.bizplace = inventory.bizplace
1035
- inventoryHistory.creator = user
1036
- inventoryHistory.updater = user
1037
-
1038
- inventoryHistory = await invHistoryRepo.save(inventoryHistory)
1039
-
1040
- if (inventory.lastSeq !== seq) {
1041
- await invRepo.save({
1042
- ...inventory,
1043
- lastSeq: inventoryHistory.seq,
1044
- updater: user
1045
- })
1046
- }
1047
-
1048
- await switchLocationStatus(domain, location, user, trxMgr)
1049
- return inventoryHistory
1050
- }
1051
-
1052
- /**
1053
- * @description: Check location emptiness and update status of location
1054
- * @param domain
1055
- * @param location
1056
- * @param updater
1057
- * @param trxMgr
1058
- */
1059
- export async function switchLocationStatus(
1060
- domain: Domain,
1061
- location: Location,
1062
- updater: User,
1063
- trxMgr?: EntityManager
1064
- ): Promise<Location> {
1065
- const invRepo: Repository<Inventory> = trxMgr?.getRepository(Inventory) || getRepository(Inventory)
1066
- const locationRepo: Repository<Location> = trxMgr?.getRepository(Location) || getRepository(Location)
1067
- const allocatedItemsCnt: number = await invRepo.countBy({
1068
- domain: { id: domain.id },
1069
- status: INVENTORY_STATUS.STORED,
1070
- location: { id: location.id }
1071
- })
1072
-
1073
- if (!allocatedItemsCnt && location.status !== LOCATION_STATUS.EMPTY) {
1074
- location = await locationRepo.save({
1075
- ...location,
1076
- status: LOCATION_STATUS.EMPTY,
1077
- updater
1078
- })
1079
- } else if (allocatedItemsCnt && location.status === LOCATION_STATUS.EMPTY) {
1080
- location = await locationRepo.save({
1081
- ...location,
1082
- status: LOCATION_STATUS.OCCUPIED,
1083
- updater
1084
- })
1085
- }
1086
-
1087
- return location
1088
- }
1089
-
1090
- export function _composeTargetInventories(product: Product, record: any, inventories: Inventory[]): OrderInventory[] {
1091
- let leftReleaseQty: number = record.releaseQty
1092
- let leftReleaseUomValue: number = record.releaseUomValue
1093
- let compReleaseQty: number = 0
1094
- let compReleaseUomValue: number = 0
1095
- let totalInventoryQty: number = inventories.reduce((total, inventory) => total + inventory.remainQty, 0)
1096
-
1097
- if (totalInventoryQty < record.releaseQty) {
1098
- throw new Error(`invalid release qty for ${product?.sku}`)
1099
- }
1100
-
1101
- let orderInventories: Partial<OrderInventory[]> = []
1102
- let idx = 0
1103
- while (compReleaseQty < record.releaseQty) {
1104
- const inventory = inventories[idx]
1105
- const {
1106
- packingType,
1107
- packingSize,
1108
- batchId,
1109
- batchIdRef,
1110
- uom
1111
- }: { packingType: string; packingSize: number; batchId: string; batchIdRef: string; uom: string } = inventory
1112
-
1113
- let orderInventory: OrderInventory = new OrderInventory()
1114
-
1115
- if (inventory.remainQty > leftReleaseQty) {
1116
- const uomValuePerQty: number = Math.round((inventory.remainUomValue / inventory.remainQty) * 100) / 100
1117
-
1118
- compReleaseQty += leftReleaseQty
1119
- compReleaseUomValue += leftReleaseUomValue
1120
-
1121
- orderInventory = {
1122
- ...orderInventory,
1123
- releaseQty: leftReleaseQty,
1124
- releaseUomValue: Math.round(leftReleaseQty * uomValuePerQty * 100) / 100
1125
- }
1126
- } else {
1127
- compReleaseQty += inventory.remainQty
1128
- compReleaseUomValue += inventory.remainUomValue
1129
- leftReleaseQty -= inventory.remainQty
1130
- leftReleaseUomValue -= inventory.remainUomValue
1131
-
1132
- orderInventory = {
1133
- ...orderInventory,
1134
- releaseQty: inventory.remainQty,
1135
- releaseUomValue: inventory.remainUomValue
1136
- }
1137
- }
1138
-
1139
- orderInventories.push({
1140
- ...orderInventory,
1141
- inventory,
1142
- packingType,
1143
- packingSize,
1144
- batchId,
1145
- batchIdRef,
1146
- uom,
1147
- product,
1148
- type: ORDER_TYPES.RELEASE_OF_GOODS
1149
- })
1150
-
1151
- idx++
1152
- }
1153
-
1154
- return orderInventories
1155
- }