@things-factory/sales-base 8.0.0-beta.8 → 8.0.0

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 +13 -13
  3. package/server/constants/attachment-type.ts +9 -0
  4. package/server/constants/index.ts +7 -0
  5. package/server/constants/load-type.ts +4 -0
  6. package/server/constants/order.ts +203 -0
  7. package/server/constants/product-group-type.ts +4 -0
  8. package/server/constants/release-good.ts +9 -0
  9. package/server/constants/transfer-order-type.ts +6 -0
  10. package/server/constants/validation-error-code.ts +3 -0
  11. package/server/constants/vas-target-type.ts +25 -0
  12. package/server/controllers/ecommerce/ecommerce-controller.ts +122 -0
  13. package/server/controllers/ecommerce/index.ts +2 -0
  14. package/server/controllers/ecommerce/sellercraft-controller.ts +182 -0
  15. package/server/controllers/index.ts +2 -0
  16. package/server/controllers/order-controller.ts +296 -0
  17. package/server/errors/index.ts +1 -0
  18. package/server/errors/validation-error.ts +25 -0
  19. package/server/index.ts +5 -0
  20. package/server/migrations/index.ts +9 -0
  21. package/server/service/arrival-notice/arrival-notice-mutation.ts +1152 -0
  22. package/server/service/arrival-notice/arrival-notice-query.ts +549 -0
  23. package/server/service/arrival-notice/arrival-notice-types.ts +310 -0
  24. package/server/service/arrival-notice/arrival-notice.ts +202 -0
  25. package/server/service/arrival-notice/index.ts +9 -0
  26. package/server/service/claim/claim-mutation.ts +308 -0
  27. package/server/service/claim/claim-query.ts +122 -0
  28. package/server/service/claim/claim-types.ts +130 -0
  29. package/server/service/claim/claim.ts +140 -0
  30. package/server/service/claim/index.ts +9 -0
  31. package/server/service/claim-detail/claim-detail-mutation.ts +102 -0
  32. package/server/service/claim-detail/claim-detail-query.ts +55 -0
  33. package/server/service/claim-detail/claim-detail-types.ts +47 -0
  34. package/server/service/claim-detail/claim-detail.ts +69 -0
  35. package/server/service/claim-detail/index.ts +9 -0
  36. package/server/service/claim-order/claim-order-mutation.ts +101 -0
  37. package/server/service/claim-order/claim-order-query.ts +47 -0
  38. package/server/service/claim-order/claim-order-types.ts +35 -0
  39. package/server/service/claim-order/claim-order.ts +81 -0
  40. package/server/service/claim-order/index.ts +9 -0
  41. package/server/service/collection-order/collection-order-mutation.ts +245 -0
  42. package/server/service/collection-order/collection-order-query.ts +97 -0
  43. package/server/service/collection-order/collection-order-types.ts +165 -0
  44. package/server/service/collection-order/collection-order.ts +135 -0
  45. package/server/service/collection-order/index.ts +9 -0
  46. package/server/service/delivery-order/delivery-order-mutation.ts +967 -0
  47. package/server/service/delivery-order/delivery-order-query.ts +631 -0
  48. package/server/service/delivery-order/delivery-order-types.ts +268 -0
  49. package/server/service/delivery-order/delivery-order.ts +258 -0
  50. package/server/service/delivery-order/index.ts +9 -0
  51. package/server/service/draft-release-good/draft-release-good-mutation.ts +765 -0
  52. package/server/service/draft-release-good/draft-release-good-query.ts +354 -0
  53. package/server/service/draft-release-good/draft-release-good-type.ts +261 -0
  54. package/server/service/draft-release-good/draft-release-good.ts +284 -0
  55. package/server/service/draft-release-good/index.ts +9 -0
  56. package/server/service/goods-receival-note/goods-receival-note-mutation.ts +129 -0
  57. package/server/service/goods-receival-note/goods-receival-note-query.ts +280 -0
  58. package/server/service/goods-receival-note/goods-receival-note-types.ts +105 -0
  59. package/server/service/goods-receival-note/goods-receival-note.ts +127 -0
  60. package/server/service/goods-receival-note/index.ts +9 -0
  61. package/server/service/index.ts +238 -0
  62. package/server/service/inventory-check/index.ts +9 -0
  63. package/server/service/inventory-check/inventory-check-mutation.ts +149 -0
  64. package/server/service/inventory-check/inventory-check-query.ts +48 -0
  65. package/server/service/inventory-check/inventory-check-types.ts +48 -0
  66. package/server/service/inventory-check/inventory-check.ts +90 -0
  67. package/server/service/invoice/index.ts +9 -0
  68. package/server/service/invoice/invoice-mutation.ts +95 -0
  69. package/server/service/invoice/invoice-query.ts +53 -0
  70. package/server/service/invoice/invoice-types.ts +279 -0
  71. package/server/service/invoice/invoice.ts +230 -0
  72. package/server/service/invoice-product/index.ts +9 -0
  73. package/server/service/invoice-product/invoice-product-mutation.ts +54 -0
  74. package/server/service/invoice-product/invoice-product-query.ts +54 -0
  75. package/server/service/invoice-product/invoice-product-types.ts +84 -0
  76. package/server/service/invoice-product/invoice-product.ts +92 -0
  77. package/server/service/job-sheet/index.ts +9 -0
  78. package/server/service/job-sheet/job-sheet-mutation.ts +92 -0
  79. package/server/service/job-sheet/job-sheet-query.ts +112 -0
  80. package/server/service/job-sheet/job-sheet-types.ts +78 -0
  81. package/server/service/job-sheet/job-sheet.ts +102 -0
  82. package/server/service/manifest/index.ts +6 -0
  83. package/server/service/manifest/manifest-mutation.ts +190 -0
  84. package/server/service/manifest/manifest-query.ts +149 -0
  85. package/server/service/manifest/manifest-type.ts +84 -0
  86. package/server/service/manifest/manifest.ts +114 -0
  87. package/server/service/order-inventory/index.ts +9 -0
  88. package/server/service/order-inventory/order-inventory-mutation.ts +54 -0
  89. package/server/service/order-inventory/order-inventory-query.ts +722 -0
  90. package/server/service/order-inventory/order-inventory-types.ts +238 -0
  91. package/server/service/order-inventory/order-inventory.ts +401 -0
  92. package/server/service/order-product/index.ts +9 -0
  93. package/server/service/order-product/order-product-mutation.ts +48 -0
  94. package/server/service/order-product/order-product-query.ts +89 -0
  95. package/server/service/order-product/order-product-types.ts +335 -0
  96. package/server/service/order-product/order-product.ts +362 -0
  97. package/server/service/order-tote/index.ts +9 -0
  98. package/server/service/order-tote/order-tote-mutation.ts +31 -0
  99. package/server/service/order-tote/order-tote-query.ts +112 -0
  100. package/server/service/order-tote/order-tote-types.ts +47 -0
  101. package/server/service/order-tote/order-tote.ts +73 -0
  102. package/server/service/order-tote-item/index.ts +9 -0
  103. package/server/service/order-tote-item/order-tote-item-mutation.ts +31 -0
  104. package/server/service/order-tote-item/order-tote-item-query.ts +82 -0
  105. package/server/service/order-tote-item/order-tote-item-types.ts +56 -0
  106. package/server/service/order-tote-item/order-tote-item.ts +72 -0
  107. package/server/service/order-tote-seal/index.ts +9 -0
  108. package/server/service/order-tote-seal/order-tote-seal-mutation.ts +31 -0
  109. package/server/service/order-tote-seal/order-tote-seal-query.ts +59 -0
  110. package/server/service/order-tote-seal/order-tote-seal-types.ts +41 -0
  111. package/server/service/order-tote-seal/order-tote-seal.ts +46 -0
  112. package/server/service/order-vas/index.ts +9 -0
  113. package/server/service/order-vas/order-vas-mutation.ts +20 -0
  114. package/server/service/order-vas/order-vas-query.ts +72 -0
  115. package/server/service/order-vas/order-vas-types.ts +159 -0
  116. package/server/service/order-vas/order-vas.ts +207 -0
  117. package/server/service/others/index.ts +5 -0
  118. package/server/service/others/other-query.ts +563 -0
  119. package/server/service/others/other-types.ts +115 -0
  120. package/server/service/purchase-order/index.ts +9 -0
  121. package/server/service/purchase-order/purchase-order-mutation.ts +458 -0
  122. package/server/service/purchase-order/purchase-order-query.ts +90 -0
  123. package/server/service/purchase-order/purchase-order-types.ts +154 -0
  124. package/server/service/purchase-order/purchase-order.ts +172 -0
  125. package/server/service/purchase-order-other-charge/index.ts +9 -0
  126. package/server/service/purchase-order-other-charge/purchase-order-other-charge-mutation.ts +31 -0
  127. package/server/service/purchase-order-other-charge/purchase-order-other-charge-query.ts +52 -0
  128. package/server/service/purchase-order-other-charge/purchase-order-other-charge-types.ts +44 -0
  129. package/server/service/purchase-order-other-charge/purchase-order-other-charge.ts +68 -0
  130. package/server/service/release-good/index.ts +9 -0
  131. package/server/service/release-good/release-good-mutation.ts +1686 -0
  132. package/server/service/release-good/release-good-query.ts +980 -0
  133. package/server/service/release-good/release-good-types.ts +662 -0
  134. package/server/service/release-good/release-good.ts +490 -0
  135. package/server/service/retail-replenishment-order/index.ts +9 -0
  136. package/server/service/retail-replenishment-order/retail-replenishment-order-mutation.ts +382 -0
  137. package/server/service/retail-replenishment-order/retail-replenishment-order-query.ts +54 -0
  138. package/server/service/retail-replenishment-order/retail-replenishment-order-types.ts +101 -0
  139. package/server/service/retail-replenishment-order/retail-replenishment-order.ts +115 -0
  140. package/server/service/return-order/index.ts +9 -0
  141. package/server/service/return-order/return-order-mutation.ts +516 -0
  142. package/server/service/return-order/return-order-query.ts +226 -0
  143. package/server/service/return-order/return-order-types.ts +196 -0
  144. package/server/service/return-order/return-order.ts +127 -0
  145. package/server/service/reverse-kitting-order/index.ts +9 -0
  146. package/server/service/reverse-kitting-order/reverse-kitting-order-mutation.ts +500 -0
  147. package/server/service/reverse-kitting-order/reverse-kitting-order-query.ts +197 -0
  148. package/server/service/reverse-kitting-order/reverse-kitting-order-type.ts +173 -0
  149. package/server/service/reverse-kitting-order/reverse-kitting-order.ts +121 -0
  150. package/server/service/reverse-kitting-order-inventory/index.ts +9 -0
  151. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-mutation.ts +129 -0
  152. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-query.ts +52 -0
  153. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory-type.ts +95 -0
  154. package/server/service/reverse-kitting-order-inventory/reverse-kitting-order-inventory.ts +143 -0
  155. package/server/service/shipping-order/index.ts +9 -0
  156. package/server/service/shipping-order/shipping-order-mutation.ts +61 -0
  157. package/server/service/shipping-order/shipping-order-query.ts +61 -0
  158. package/server/service/shipping-order/shipping-order-types.ts +89 -0
  159. package/server/service/shipping-order/shipping-order.ts +129 -0
  160. package/server/service/transfer-order/index.ts +9 -0
  161. package/server/service/transfer-order/transfer-order-mutation.ts +309 -0
  162. package/server/service/transfer-order/transfer-order-query.ts +66 -0
  163. package/server/service/transfer-order/transfer-order-types.ts +97 -0
  164. package/server/service/transfer-order/transfer-order.ts +117 -0
  165. package/server/service/vas/index.ts +9 -0
  166. package/server/service/vas/vas-mutation.ts +106 -0
  167. package/server/service/vas/vas-query.ts +60 -0
  168. package/server/service/vas/vas-types.ts +71 -0
  169. package/server/service/vas/vas.ts +77 -0
  170. package/server/service/vas-order/index.ts +9 -0
  171. package/server/service/vas-order/vas-order-mutation.ts +259 -0
  172. package/server/service/vas-order/vas-order-query.ts +119 -0
  173. package/server/service/vas-order/vas-order-types.ts +49 -0
  174. package/server/service/vas-order/vas-order.ts +81 -0
  175. package/server/utils/datetime-util.ts +54 -0
  176. package/server/utils/index.ts +3 -0
  177. package/server/utils/inventory-util.ts +1155 -0
  178. package/server/utils/order-no-generator.ts +146 -0
@@ -0,0 +1,980 @@
1
+ import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
2
+ import { EntityManager, In, OrderByCondition, Repository, SelectQueryBuilder } from 'typeorm'
3
+
4
+ import { Attachment } from '@things-factory/attachment-base'
5
+ import { checkUserBelongsDomain, User } from '@things-factory/auth-base'
6
+ import { Bizplace, getCompanyBizplace, getMyBizplace, getPermittedBizplaceIds } from '@things-factory/biz-base'
7
+ import { logger } from '@things-factory/env'
8
+ import { buildQuery, Domain, Filter, getRepository, ListParam, Pagination, Sorting } from '@things-factory/shell'
9
+ import { Inventory, LOCATION_TYPE } from '@things-factory/warehouse-base'
10
+
11
+ import { ATTACHMENT_TYPE, ORDER_INVENTORY_STATUS, ORDER_STATUS } from '../../constants'
12
+ import { OrderInventory } from '../order-inventory/order-inventory'
13
+ import { ShippingOrder } from '../shipping-order/shipping-order'
14
+ import { ReleaseGood } from './release-good'
15
+ import { NewReleaseGood, ReleasableInventory, ReleasableInventoryList, ReleaseGoodList } from './release-good-types'
16
+
17
+ @Resolver(ReleaseGood)
18
+ export class ReleaseGoodQuery {
19
+ @Directive('@privilege(category: "order_customer", privilege: "query", domainOwnerGranted: true)')
20
+ @Query(returns => ReleaseGoodList)
21
+ async releaseGoods(
22
+ @Ctx() context: ResolverContext,
23
+ @Args(type => ListParam) params: ListParam
24
+ ): Promise<ReleaseGoodList> {
25
+ try {
26
+ const { domain, user } = context.state
27
+
28
+ if (!params.filters.find((filter: any) => filter.name === 'bizplace')) {
29
+ params.filters.push({
30
+ name: 'bizplaceId',
31
+ operator: 'in',
32
+ value: await getPermittedBizplaceIds(domain, user),
33
+ relation: false
34
+ })
35
+ }
36
+
37
+ const fromDateParamIdx = params.filters.findIndex(param => param.name === 'fromDate')
38
+ if (fromDateParamIdx >= 0) {
39
+ let fromDateVal = new Date(params.filters[fromDateParamIdx].value)
40
+ params.filters.splice(fromDateParamIdx, 1)
41
+
42
+ params.filters.push({
43
+ name: 'createdAt',
44
+ operator: 'gte',
45
+ value: fromDateVal.toISOString(),
46
+ relation: false
47
+ })
48
+ }
49
+
50
+ const toDateParamIdx = params.filters.findIndex(param => param.name === 'toDate')
51
+ if (toDateParamIdx >= 0) {
52
+ let toDateVal = new Date(params.filters[toDateParamIdx].value)
53
+ params.filters.splice(toDateParamIdx, 1)
54
+
55
+ params.filters.push({
56
+ name: 'createdAt',
57
+ operator: 'lt',
58
+ value: new Date(toDateVal.setDate(toDateVal.getDate() + 1)).toISOString(),
59
+ relation: false
60
+ })
61
+ }
62
+
63
+ const qb: SelectQueryBuilder<ReleaseGood> = getRepository(ReleaseGood).createQueryBuilder('rg')
64
+ buildQuery(qb, params, context)
65
+ let items: any
66
+ let total: number
67
+ qb.addSelect('COALESCE("cc".rank, 99999)', 'rank')
68
+ qb.leftJoinAndSelect('rg.domain', 'domain')
69
+ qb.leftJoinAndSelect('rg.bizplace', 'bizplace')
70
+ qb.leftJoinAndSelect('rg.orderInventories', 'oi')
71
+ qb.leftJoinAndSelect('rg.orderProducts', 'op')
72
+ qb.leftJoinAndSelect('oi.inventory', 'inv')
73
+ qb.leftJoinAndSelect('oi.product', 'prod')
74
+ qb.leftJoinAndSelect('rg.arrivalNotice', 'an')
75
+ qb.leftJoinAndSelect('rg.creator', 'creator')
76
+ qb.leftJoinAndSelect('rg.updater', 'updater')
77
+ qb.leftJoin(
78
+ subQuery => {
79
+ return subQuery
80
+ .select(`ccd.rank`, 'rank')
81
+ .addSelect(`ccd.name`, 'status')
82
+ .from(`common_code_details`, 'ccd')
83
+ .innerJoin(`ccd.commonCode`, 'cc')
84
+ .where(`ccd.domain_id = :domainId`, { domainId: domain.id })
85
+ .andWhere(`cc.name = 'RO_LIST_STATUS'`)
86
+ },
87
+ 'cc',
88
+ 'cc.status = rg.status'
89
+ )
90
+
91
+ const arrChildSortData = ['bizplace']
92
+ const sort: OrderByCondition = (params.sortings || []).reduce(
93
+ (acc, sort) => ({
94
+ ...acc,
95
+ [arrChildSortData.indexOf(sort.name) >= 0 ? sort.name + '.name' : 'rg.' + sort.name]: sort.desc
96
+ ? 'DESC'
97
+ : 'ASC'
98
+ }),
99
+ !params.sortings.some(e => e.name === 'status') ? { rank: 'ASC' } : {}
100
+ )
101
+
102
+ qb.orderBy(sort)
103
+
104
+ let [itemsRes, totalRes] = await qb.getManyAndCount()
105
+ items = itemsRes
106
+ total = totalRes
107
+
108
+ return { items, total }
109
+ } catch (error) {
110
+ throw error
111
+ }
112
+ }
113
+
114
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
115
+ @Query(returns => ReleaseGood)
116
+ async releaseGood(
117
+ @Ctx() context: ResolverContext,
118
+ @Arg('name', { nullable: true }) name: string,
119
+ @Arg('trackingNo', { nullable: true }) trackingNo: string
120
+ ): Promise<ReleaseGood> {
121
+ const { domain, user } = context.state
122
+
123
+ if (name) {
124
+ return await getRepository(ReleaseGood).findOne({
125
+ where: {
126
+ domain: { id: domain.id },
127
+ name,
128
+ bizplace: { id: In(await getPermittedBizplaceIds(domain, user)) }
129
+ },
130
+ relations: [
131
+ 'domain',
132
+ 'bizplace',
133
+ 'shippingOrder',
134
+ 'arrivalNotice',
135
+ 'orderInventories',
136
+ 'orderInventories.product',
137
+ 'orderInventories.inventory',
138
+ 'orderInventories.inventory.product',
139
+ 'orderVass',
140
+ 'orderVass.vas',
141
+ 'creator',
142
+ 'updater'
143
+ ]
144
+ })
145
+ } else if (trackingNo) {
146
+ let releaseGood = await getRepository(ReleaseGood).findOne({
147
+ where: {
148
+ domain: { id: domain.id },
149
+ trackingNo,
150
+ bizplace: { id: In(await getPermittedBizplaceIds(domain, user)) }
151
+ },
152
+ relations: ['bizplace']
153
+ })
154
+
155
+ if (!releaseGood) {
156
+ throw new Error('Tracking No not found in any orders')
157
+ } else {
158
+ return releaseGood
159
+ }
160
+ }
161
+ }
162
+
163
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
164
+ @Directive('@transaction')
165
+ @Query(returns => ReleaseGood)
166
+ async releaseGoodDetail(
167
+ @Ctx() context: ResolverContext,
168
+ @Arg('refNo', { nullable: true }) refNo?: string,
169
+ @Arg('name', { nullable: true }) name?: string
170
+ ): Promise<ReleaseGood> {
171
+ try {
172
+ const { domain, tx } = context.state
173
+ let param: any
174
+
175
+ if (name) param = { domain, name }
176
+ else param = { domain, refNo }
177
+
178
+ const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
179
+ where: { ...param },
180
+ relations: [
181
+ 'domain',
182
+ 'bizplace',
183
+ 'shippingOrder',
184
+ 'arrivalNotice',
185
+ 'orderProducts',
186
+ 'orderProducts.product',
187
+ 'orderVass',
188
+ 'orderVass.vas',
189
+ 'orderVass.targetProduct',
190
+ 'orderVass.inventory',
191
+ 'creator',
192
+ 'updater'
193
+ ]
194
+ })
195
+
196
+ if (releaseGood) {
197
+ let oiQb: SelectQueryBuilder<OrderInventory> = tx
198
+ .getRepository(OrderInventory)
199
+ .createQueryBuilder('oi')
200
+ .leftJoinAndSelect('oi.inventory', 'inventory')
201
+ .leftJoinAndSelect('oi.product', 'product')
202
+ .leftJoinAndSelect('inventory.location', 'location')
203
+ .where('oi.domain_id = :domainId', { domainId: domain.id })
204
+ .andWhere('oi.release_good_id = :releaseGoodId', { releaseGoodId: releaseGood.id })
205
+ .andWhere('oi.status NOT IN (:...orderInventoryStatus)', {
206
+ orderInventoryStatus: [ORDER_INVENTORY_STATUS.COMPLETE_SPLIT, ORDER_INVENTORY_STATUS.REPLACED]
207
+ })
208
+ .orderBy('product.sku', 'ASC')
209
+
210
+ let orderInventories: OrderInventory[] = await oiQb.getMany()
211
+
212
+ const shippingOrder: ShippingOrder = releaseGood.shippingOrder
213
+ const foundAttachments: Attachment[] = await tx.getRepository(Attachment).find({
214
+ where: {
215
+ domain: { id: domain.id },
216
+ refBy: releaseGood.id,
217
+ category: ATTACHMENT_TYPE.DELIVERY_ORDER
218
+ }
219
+ })
220
+
221
+ let invInfos = await Promise.all(
222
+ orderInventories.map(async (orderInv: OrderInventory) => {
223
+ if (orderInv?.inventory?.id) {
224
+ const inventory: Inventory = orderInv.inventory
225
+ return {
226
+ id: inventory.id,
227
+ name: inventory.name,
228
+ batchId: inventory.batchId,
229
+ batchIdRef: inventory.batchIdRef,
230
+ palletId: inventory.palletId,
231
+ product: orderInv.product,
232
+ productIdRef: orderInv.product.id,
233
+ productName: `${orderInv.product.name} (${orderInv.product.description})`,
234
+ packingType: orderInv.packingType,
235
+ packingSize: orderInv.packingSize,
236
+ location: inventory.location,
237
+ qty: inventory.qty,
238
+ lockedQty: inventory.lockedQty,
239
+ lockedUomValue: inventory.lockedUomValue,
240
+ uom: inventory.uom,
241
+ uomValue: inventory.uomValue,
242
+ invStatus: inventory.status,
243
+ releaseQty: orderInv.releaseQty,
244
+ releaseUomValue: orderInv.releaseUomValue,
245
+ status: orderInv.status,
246
+ refWorksheetId: orderInv.refWorksheetId
247
+ }
248
+ }
249
+ })
250
+ )
251
+
252
+ const result = {
253
+ ...releaseGood,
254
+ attachment: foundAttachments,
255
+ shippingOrderInfo: {
256
+ containerNo: shippingOrder?.containerNo || '',
257
+ containerSize: shippingOrder?.containerSize || '',
258
+ containerLeavingDate: shippingOrder?.containerLeavingDate || '',
259
+ containerArrivalDate: shippingOrder?.containerArrivalDate || '',
260
+ containerClosureDate: shippingOrder?.containerClosureDate || '',
261
+ shipName: shippingOrder?.shipName || '',
262
+ remark: shippingOrder?.remark || '',
263
+ remindContainerDeparture: shippingOrder?.remindContainerDeparture || null
264
+ },
265
+ inventoryInfos: invInfos
266
+ }
267
+
268
+ return result
269
+ }
270
+ } catch (error) {
271
+ logger.error(`release-good-query[releaseGoodDetail]: ${error}`)
272
+ throw new Error(error)
273
+ }
274
+ }
275
+
276
+ @Directive('@privilege(category: "order_warehouse", privilege: "query", domainOwnerGranted: true)')
277
+ @Query(returns => ReleaseGoodList)
278
+ async releaseGoodRequests(
279
+ @Ctx() context: ResolverContext,
280
+ @Args(type => ListParam) params: ListParam
281
+ ): Promise<ReleaseGoodList> {
282
+ try {
283
+ console.time('t1')
284
+ const { domain, user } = context.state
285
+
286
+ const statusFilter = params.filters.some(e => e.name === 'status')
287
+ const bizplaceFilter = params.filters.find(param => param.name === 'bizplaceId')
288
+ const noOfItemsFilter = params.filters.find(param => param.name === 'noOfItems')
289
+ const skuFilter = params.filters.find(param => param.name === 'sku')
290
+ const routeId = params.filters.find(param => param.name === 'routeId')
291
+ const orderRemarkFilter = params.filters.find(param => param.name === 'orderRemark')
292
+ const manifestedFilter = params.filters.find(param => param.name === 'manifested')
293
+ const orderInfoFilter = params.filters.find(param => param.name === 'name')
294
+ const typeFilter = params.filters.find(param => param.name === 'type')?.value
295
+
296
+ if (await checkUserBelongsDomain(domain, user)) {
297
+ if (!statusFilter && !params.filters.some(e => e.name === 'name')) {
298
+ params.filters.push({
299
+ name: 'status',
300
+ operator: 'notin',
301
+ value: [ORDER_STATUS.PENDING, ORDER_STATUS.EDITING, ''],
302
+ relation: false
303
+ })
304
+ }
305
+ }
306
+
307
+ if (!bizplaceFilter) {
308
+ params.filters.push({
309
+ name: 'bizplaceId',
310
+ operator: 'in',
311
+ value: await getPermittedBizplaceIds(domain, user),
312
+ relation: false
313
+ })
314
+ }
315
+
316
+ if (skuFilter) {
317
+ params.filters = params.filters.filter(param => param.name !== 'sku')
318
+ }
319
+
320
+ if (orderInfoFilter) {
321
+ params.filters = params.filters.filter(param => param.name !== 'name')
322
+ }
323
+
324
+ if (routeId) {
325
+ if (routeId.value == true) {
326
+ params.filters.find(param => param.name === 'routeId').value = 'null'
327
+ } else {
328
+ params.filters.find(param => param.name === 'routeId').operator = 'is_null'
329
+ params.filters.find(param => param.name === 'routeId').value = null
330
+ }
331
+ }
332
+
333
+ if (orderRemarkFilter) {
334
+ const orderRemarkIdx = params.filters.findIndex(param => param.name === 'orderRemark')
335
+ if (orderRemarkFilter.value == true) {
336
+ params.filters.splice(orderRemarkIdx, 1)
337
+ params.filters.push({
338
+ name: 'remark',
339
+ operator: 'is_not_null',
340
+ value: null,
341
+ relation: false
342
+ })
343
+ } else {
344
+ params.filters.splice(orderRemarkIdx, 1)
345
+ params.filters.push({
346
+ name: 'remark',
347
+ operator: 'is_null',
348
+ value: null,
349
+ relation: false
350
+ })
351
+ }
352
+ }
353
+
354
+ if (manifestedFilter) {
355
+ const manifestedIdx = params.filters.findIndex(param => param.name === 'manifested')
356
+ if (manifestedFilter.value == true) {
357
+ params.filters.splice(manifestedIdx, 1)
358
+ params.filters.push({
359
+ name: 'manifestId',
360
+ operator: 'is_not_null',
361
+ value: null,
362
+ relation: false
363
+ })
364
+ } else {
365
+ params.filters.splice(manifestedIdx, 1)
366
+ params.filters.push({
367
+ name: 'manifestId',
368
+ operator: 'is_null',
369
+ value: null,
370
+ relation: false
371
+ })
372
+ }
373
+ }
374
+
375
+ const fromDateParamIdx = params.filters.findIndex(param => param.name === 'fromDate')
376
+ if (fromDateParamIdx >= 0) {
377
+ let fromDateVal = new Date(params.filters[fromDateParamIdx].value)
378
+ let releaseDateFrom = params.filters.find(param => param.name === 'fromDate')?.value
379
+ params.filters.splice(fromDateParamIdx, 1)
380
+
381
+ params.filters.push({
382
+ name: typeFilter == 'b2c' ? 'releaseDate' : 'createdAt',
383
+ operator: 'gte',
384
+ value: typeFilter == 'b2c' ? releaseDateFrom : fromDateVal.toISOString(),
385
+ relation: false
386
+ })
387
+ }
388
+
389
+ const toDateParamIdx = params.filters.findIndex(param => param.name === 'toDate')
390
+ if (toDateParamIdx >= 0) {
391
+ let toDateVal = new Date(params.filters[toDateParamIdx].value)
392
+ let releaseDateTo = params.filters.find(param => param.name === 'toDate')?.value
393
+ params.filters.splice(toDateParamIdx, 1)
394
+
395
+ params.filters.push({
396
+ name: typeFilter == 'b2c' ? 'releaseDate' : 'createdAt',
397
+ operator: typeFilter == 'b2c' ? 'lte' : 'lt',
398
+ value:
399
+ typeFilter == 'b2c' ? releaseDateTo : new Date(toDateVal.setDate(toDateVal.getDate() + 1)).toISOString(),
400
+ relation: false
401
+ })
402
+ }
403
+
404
+ if (noOfItemsFilter && noOfItemsFilter.value == 1) {
405
+ params.filters.find(param => param.name === 'noOfItems').operator = 'eq'
406
+ }
407
+
408
+ const qb: SelectQueryBuilder<ReleaseGood> = getRepository(ReleaseGood).createQueryBuilder('rg')
409
+ buildQuery(qb, params, context)
410
+ qb.addSelect('COALESCE("cc".rank, 99999)', 'rank')
411
+ qb.leftJoinAndSelect('rg.domain', 'domain')
412
+ qb.leftJoinAndSelect('rg.bizplace', 'bizplace')
413
+ qb.leftJoinAndSelect('rg.arrivalNotice', 'an')
414
+ qb.leftJoinAndSelect('rg.creator', 'creator')
415
+ qb.leftJoinAndSelect('rg.updater', 'updater')
416
+ qb.leftJoinAndSelect('rg.acceptedBy', 'acceptedBy')
417
+ qb.leftJoinAndSelect('rg.deliverTo', 'deliverTo')
418
+ qb.leftJoin(
419
+ subQuery => {
420
+ return subQuery
421
+ .select(`ccd.rank`, 'rank')
422
+ .addSelect(`ccd.name`, 'status')
423
+ .from(`common_code_details`, 'ccd')
424
+ .innerJoin(`ccd.commonCode`, 'cc')
425
+ .where(`ccd.domain_id = :domainId`, { domainId: domain.id })
426
+ .andWhere(`cc.name = 'RO_REQUESTS_STATUS'`)
427
+ },
428
+ 'cc',
429
+ 'cc.status = rg.status'
430
+ )
431
+
432
+ if (skuFilter) {
433
+ let removeSymbol = skuFilter.value
434
+ .split('')
435
+ .filter(res => res !== '%')
436
+ .join('')
437
+
438
+ let products = removeSymbol
439
+ .toLowerCase()
440
+ .split(',')
441
+ .map(prod => {
442
+ return "'%" + prod.trim().replace(/'/g, "''") + "%'"
443
+ })
444
+ .join(',')
445
+
446
+ qb.andWhere(
447
+ `
448
+ exists (select * from order_inventories oi
449
+ inner join products p on oi.product_id = p.id
450
+ inner join inventories i on i.product_id = p.id
451
+ where oi.release_good_id = rg.id
452
+ and (lower(p.sku) like any(array[${products}])
453
+ or lower(p.brand_sku) like any(array[${products}])
454
+ or lower(p.name) like any(array[${products}])
455
+ or lower(p.description) like any(array[${products}]))
456
+ )
457
+ `
458
+ )
459
+ }
460
+
461
+ if (orderInfoFilter) {
462
+ let removeSymbol = orderInfoFilter.value
463
+ .split('')
464
+ .filter(res => res !== '%')
465
+ .join('')
466
+
467
+ let orderInfos = removeSymbol
468
+ .toLowerCase()
469
+ .split(',')
470
+ .map(prod => {
471
+ return "'%" + prod.trim().replace(/'/g, "''") + "%'"
472
+ })
473
+ .join(',')
474
+
475
+ qb.andWhere(`(
476
+ lower(rg.name) like any(array[${orderInfos}])
477
+ or lower(rg.ref_no) like any(array[${orderInfos}])
478
+ or lower(rg.ref_no_2) like any(array[${orderInfos}])
479
+ or lower(rg.ref_no_3) like any(array[${orderInfos}])
480
+ )`)
481
+ }
482
+
483
+ const arrChildSortData = ['bizplace', 'creator']
484
+ const sort: OrderByCondition = (params.sortings || []).reduce(
485
+ (acc, sort) => ({
486
+ ...acc,
487
+ [arrChildSortData.indexOf(sort.name) >= 0 ? sort.name + '.name' : 'rg.' + sort.name]: sort.desc
488
+ ? 'DESC'
489
+ : 'ASC'
490
+ }),
491
+ params.sortings.length < 1 ? { rank: 'ASC', 'rg.createdAt': 'DESC' } : {}
492
+ )
493
+
494
+ qb.orderBy(sort)
495
+
496
+ let [items, total] = await qb.getManyAndCount()
497
+
498
+ items = items
499
+ .filter(item => {
500
+ return item.domainId === domain.id
501
+ })
502
+ .map(item => {
503
+ return {
504
+ ...item,
505
+ orderRemark: item?.remark ? true : false
506
+ }
507
+ })
508
+ console.timeEnd('t1')
509
+ return { items, total }
510
+ } catch (error) {
511
+ logger.error(`release-good-query[releaseGoodRequests]: ${error}`)
512
+ throw error
513
+ }
514
+ }
515
+
516
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
517
+ @Query(returns => ReleasableInventoryList)
518
+ async releasableInventories(
519
+ @Ctx() context: ResolverContext,
520
+ @Arg('filters', type => [Filter], { nullable: true }) filters?: Filter[],
521
+ @Arg('pagination', type => Pagination, { nullable: true }) pagination?: Pagination,
522
+ @Arg('sortings', type => [Sorting], { nullable: true }) sortings?: Sorting[]
523
+ ): Promise<ReleasableInventoryList> {
524
+ const myBizplace: Bizplace = await getMyBizplace(context.state.domain, context.state.user)
525
+ const INV_ALIAS = 'INV'
526
+ const PROD_ALIAS = 'PROD'
527
+ const GAN_ALIAS = 'GAN'
528
+ const conditions = filters
529
+ let {
530
+ batchId = null,
531
+ containerNo = null,
532
+ product = [],
533
+ packingType = null,
534
+ inventory = []
535
+ } = getConditionValues(conditions)
536
+
537
+ const SELECT: string = `
538
+ SELECT
539
+ ${INV_ALIAS}.batch_id,
540
+ ${INV_ALIAS}.packing_type,
541
+ (SUM(COALESCE(${INV_ALIAS}.qty, 0)) - SUM(COALESCE(${INV_ALIAS}.locked_qty, 0))) as remain_qty,
542
+ (SUM(COALESCE(${INV_ALIAS}.uomValue, 0)) - SUM(COALESCE(${INV_ALIAS}.locked_uom_value, 0))) as remain_uom_value,
543
+ ${PROD_ALIAS}.id as product_id,
544
+ ${PROD_ALIAS}.name as product_name,
545
+ ${GAN_ALIAS}.container_no
546
+ `
547
+ const FROM: string = `
548
+ FROM
549
+ inventories ${INV_ALIAS}
550
+ LEFT JOIN
551
+ products ${PROD_ALIAS}
552
+ ON
553
+ ${PROD_ALIAS}.id = ${INV_ALIAS}.product_id
554
+ LEFT JOIN
555
+ arrival_notices ${GAN_ALIAS}
556
+ ON
557
+ ${GAN_ALIAS}.id = CAST(${INV_ALIAS}.ref_order_id as uuid)
558
+ `
559
+
560
+ const WHERE: string = `
561
+ WHERE
562
+ ${INV_ALIAS}.status = 'STORED'
563
+ AND ${INV_ALIAS}.qty > 0
564
+ AND ${INV_ALIAS}.bizplace_id = '${myBizplace.id}'
565
+ ${batchId ? `AND LOWER(${INV_ALIAS}.batch_id) LIKE '%${batchId.toLowerCase()}%'` : ''}
566
+ ${packingType ? `AND LOWER(${INV_ALIAS}.packing_type) LIKE '%${packingType.toLowerCase()}%'` : ''}
567
+ ${containerNo ? `AND LOWER(${GAN_ALIAS}.container_no) LIKE '%${containerNo.toLowerCase()}%'` : ''}
568
+ ${
569
+ product?.length > 0 && product[0]
570
+ ? `AND ${PROD_ALIAS}.id IN (${product.map((id: string) => `'${id}'`).join(', ')})`
571
+ : product[0] === null
572
+ ? `AND ${PROD_ALIAS}.id isnull`
573
+ : ''
574
+ }
575
+ ${
576
+ inventory?.length > 0
577
+ ? `
578
+ AND (${INV_ALIAS}.batch_id, ${PROD_ALIAS}.id) ${
579
+ filters.find((filter: { name: string; operator: string; value: any }) => filter.name === 'inventory')
580
+ .operator
581
+ } (
582
+ ${inventory
583
+ .map((inv: { batchId: string; productId: string }) => `('${inv.batchId}', '${inv.productId}')`)
584
+ .join(', ')}
585
+ )
586
+ `
587
+ : ''
588
+ }
589
+ `
590
+
591
+ // ${product?.length > 0 ? `AND ${PROD_ALIAS}.id IN (${product.map((id: string) => id ? `'${id}'` : null).join(', ')})` : ''}
592
+ const GROUP_BY: string = `
593
+ GROUP BY
594
+ ${INV_ALIAS}.batch_id,
595
+ ${INV_ALIAS}.packing_type,
596
+ ${GAN_ALIAS}.container_no,
597
+ ${PROD_ALIAS}.id
598
+ `
599
+
600
+ const OFFSET_LIMIT: string = `
601
+ OFFSET ${pagination.limit * (pagination.page - 1)}
602
+ LIMIT ${pagination.limit}
603
+ `
604
+
605
+ const invRepo: Repository<Inventory> = getRepository(Inventory)
606
+ let items: any[] = await invRepo.query(`
607
+ ${SELECT} ${FROM} ${WHERE} ${GROUP_BY} ${OFFSET_LIMIT}
608
+ `)
609
+ items = items.map((item: any) => {
610
+ return {
611
+ batchId: item.batch_id,
612
+ packingType: item.packing_type,
613
+ remainQty: item.remain_qty,
614
+ remainUomValue: item.remain_uom_value,
615
+ productName: item.product_name,
616
+ product: {
617
+ id: item.product_id,
618
+ name: item.product_name
619
+ },
620
+ containerNo: item.container_no
621
+ }
622
+ }) as any
623
+
624
+ const results: [{ total: number }] = await invRepo.query(`
625
+ SELECT max(cnt.cnt) as total FROM (
626
+ SELECT (ROW_NUMBER() OVER()) as cnt ${FROM} ${WHERE} ${GROUP_BY}
627
+ ) as cnt
628
+ `)
629
+
630
+ return {
631
+ items,
632
+ total: Number(results[0].total)
633
+ }
634
+ }
635
+
636
+ @Directive('@transaction')
637
+ @Query(returns => [ReleaseGood])
638
+ async bulkReleaseGoodsAvailableItems(
639
+ @Ctx() context: ResolverContext,
640
+ @Arg('rawReleaseGoods', type => [NewReleaseGood], { nullable: true }) rawReleaseGoods: NewReleaseGood[],
641
+ @Arg('bizplaceId', type => String) bizplaceId: string
642
+ ): Promise<ReleaseGood[]> {
643
+ const { tx } = context.state
644
+ const availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(rawReleaseGoods, bizplaceId, context, tx)
645
+
646
+ return availableItems
647
+ }
648
+
649
+ @FieldResolver(type => Domain)
650
+ async domain(@Root() releaseGood: ReleaseGood): Promise<Domain> {
651
+ return await getRepository(Domain).findOneBy({ id: releaseGood.domainId })
652
+ }
653
+
654
+ @FieldResolver(type => User)
655
+ async creator(@Root() releaseGood: ReleaseGood): Promise<User> {
656
+ return await getRepository(User).findOneBy({ id: releaseGood.creatorId })
657
+ }
658
+
659
+ @FieldResolver(type => User)
660
+ async updater(@Root() releaseGood: ReleaseGood): Promise<User> {
661
+ return await getRepository(User).findOneBy({ id: releaseGood.updaterId })
662
+ }
663
+ }
664
+
665
+ async function prepOITempTable(domain: Domain, tx: EntityManager) {
666
+ await tx.query(
667
+ `
668
+ CREATE TEMP TABLE oi ON COMMIT DROP AS (
669
+ SELECT
670
+ SUM(oi.release_qty) as release_qty,
671
+ SUM(oi.release_uom_value) as release_uom_value,
672
+ oi.batch_id,
673
+ p.id as product_id,
674
+ oi.packing_type
675
+ FROM
676
+ order_inventories oi
677
+ LEFT JOIN
678
+ products p
679
+ ON
680
+ oi.product_id = p.id
681
+ WHERE
682
+ oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
683
+ AND oi.batch_id NOTNULL
684
+ AND oi.product_id NOTNULL
685
+ AND oi.packing_type NOTNULL
686
+ AND oi.inventory_id IS NULL
687
+ AND oi.domain_id = $1
688
+ GROUP BY
689
+ oi.product_id,
690
+ oi.batch_id,
691
+ oi.packing_type,
692
+ p.id
693
+ )`,
694
+ [domain.id]
695
+ )
696
+ }
697
+
698
+ async function getAvailableAmount(
699
+ roBizId: string,
700
+ productIdRef: string,
701
+ batchId: string,
702
+ packingType: string,
703
+ tx: EntityManager
704
+ ): Promise<{ qty: number; uomValue: number; uom: string }> {
705
+ const result: any[] = await tx.getRepository(Inventory).query(`
706
+ SELECT
707
+ SUM(COALESCE(i.qty, 0)) - SUM(COALESCE(i.locked_qty, 0)) - MAX(COALESCE(oi.release_qty, 0)) as "qty",
708
+ SUM(COALESCE(i.uom_value, 0)) - SUM(COALESCE(i.locked_uom_value, 0)) - MAX(COALESCE(oi.release_uom_value, 0)) as "uom_value",
709
+ uom
710
+ FROM
711
+ inventories i
712
+ LEFT JOIN products p on i.product_id = p.id
713
+ LEFT JOIN oi on i.batch_id = oi.batch_id
714
+ AND p.id = oi.product_id
715
+ AND i.packing_type = oi.packing_type
716
+ WHERE
717
+ i.bizplace_id = '${roBizId}'
718
+ AND i.status = 'STORED'
719
+ AND i.batch_id = '${batchId}'
720
+ AND p.id = '${productIdRef}'
721
+ AND i.packing_type = '${packingType}'
722
+ GROUP BY
723
+ i.batch_id,
724
+ p.id,
725
+ i.packing_type,
726
+ i.uom
727
+ `)
728
+
729
+ let qty: number = 0
730
+ let uomValue: number = 0
731
+ let uom: string = ''
732
+ if (result?.length) {
733
+ qty = result[0].qty
734
+ uomValue = result[0].uom_value
735
+ uom = result[0].uom
736
+ }
737
+
738
+ return { qty, uomValue, uom }
739
+ }
740
+
741
+ async function dropOITempTable(domain: Domain, tx: EntityManager) {
742
+ await tx.query(`DROP TABLE oi`)
743
+ }
744
+
745
+ export async function bulkReleaseGoodsAvailableItemsFunction(
746
+ rawReleaseGoods: any[],
747
+ bizplaceId: string,
748
+ context: any,
749
+ tx?: EntityManager
750
+ ): Promise<any[]> {
751
+ const { domain } = context.state
752
+ const companyBizplaceId: Bizplace = await getCompanyBizplace(null, null, bizplaceId)
753
+
754
+ if (!rawReleaseGoods) return
755
+ const json_oi = JSON.stringify(
756
+ rawReleaseGoods.map(raw => {
757
+ return {
758
+ sku: raw.sku,
759
+ packing_type: raw.packingType,
760
+ packing_size: raw.packingSize,
761
+ uom: raw.uom,
762
+ releaseQty: raw.releaseQty,
763
+ release_qty: raw.releaseQty
764
+ }
765
+ })
766
+ )
767
+
768
+ await tx.query(
769
+ `
770
+ CREATE TEMP TABLE raw_release_goods(
771
+ product_id VARCHAR(50),
772
+ product_detail_id VARCHAR(50),
773
+ sku VARCHAR(150),
774
+ product_info VARCHAR(250),
775
+ packing_type VARCHAR(50),
776
+ packing_size INT,
777
+ uom VARCHAR(10),
778
+ release_qty INT
779
+ );
780
+ `
781
+ )
782
+
783
+ await tx.query(
784
+ `
785
+ INSERT INTO raw_release_goods
786
+ SELECT p.id AS product_id,
787
+ pd.id AS product_detail_id,
788
+ js.sku,
789
+ CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
790
+ ELSE p.name END AS product_info,
791
+ CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
792
+ CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
793
+ CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END
794
+ FROM JSON_POPULATE_RECORDSET(NULL::raw_release_goods, $1) js
795
+ LEFT JOIN products p ON LOWER(js.sku) = LOWER(p.sku) AND p.bizplace_id = $2
796
+ LEFT JOIN product_details pd ON p.id = pd.product_id
797
+ AND CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom NOTNULL
798
+ THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.uom = js.uom
799
+ WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL AND js.uom ISNULL
800
+ THEN pd.packing_type = js.packing_type AND pd.packing_size = js.packing_size AND pd.is_default IS TRUE
801
+ WHEN js.packing_type ISNULL AND js.packing_size ISNULL AND js.uom NOTNULL
802
+ THEN pd.uom = js.uom AND pd.is_default IS TRUE ELSE pd.is_default IS TRUE
803
+ END;
804
+ `,
805
+ [json_oi, companyBizplaceId.id]
806
+ )
807
+
808
+ let availableItems = await tx.query(
809
+ `
810
+ WITH inv AS (
811
+ SELECT i.product_id, foo.product_detail_id, foo.sku, foo.product_info, i.packing_type, i.packing_size, i.uom,
812
+ SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
813
+ (
814
+ SELECT SUM(oi.release_qty) FROM order_inventories oi
815
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
816
+ AND oi.inventory_id IS null
817
+ AND oi.product_id = i.product_id::uuid
818
+ AND oi.packing_type = i.packing_type
819
+ AND oi.uom = i.uom
820
+ AND oi.domain_id = $1
821
+ AND oi.bizplace_id = $2
822
+ ), 0) as "remain_qty",
823
+ SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
824
+ (
825
+ SELECT SUM(oi.release_uom_value) FROM order_inventories oi
826
+ WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
827
+ AND oi.inventory_id IS null
828
+ AND oi.product_id = i.product_id::uuid
829
+ AND oi.packing_type = i.packing_type
830
+ AND oi.uom = i.uom
831
+ AND oi.domain_id = $1
832
+ AND oi.bizplace_id = $2
833
+ ), 0) as "remain_uom_value"
834
+ FROM inventories i
835
+ LEFT JOIN locations l
836
+ ON i.location_id = l.id
837
+ INNER JOIN (
838
+ SELECT rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
839
+ FROM raw_release_goods rrg
840
+ GROUP BY rrg.product_id, rrg.product_detail_id, rrg.sku, rrg.product_info, rrg.packing_type, rrg.packing_size, rrg.uom
841
+ ) AS foo
842
+ ON i.product_id = foo.product_id::uuid
843
+ AND i.packing_type = foo.packing_type
844
+ AND i.packing_size = foo.packing_size
845
+ AND i.uom = foo.uom
846
+ AND i.domain_id = $1
847
+ AND i.bizplace_id = $2
848
+ WHERE l.type NOT IN ($3, $4)
849
+ GROUP BY i.product_id, foo.product_detail_id, foo.sku,foo.product_info, i.packing_type, i.packing_size, i.uom
850
+ ORDER BY foo.sku, remain_qty
851
+ ) SELECT * FROM inv WHERE remain_qty > 0
852
+ `,
853
+ [domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
854
+ )
855
+
856
+ await tx.query(`DROP TABLE raw_release_goods`)
857
+
858
+ availableItems = availableItems.map(item => {
859
+ return {
860
+ productId: item.product_id,
861
+ productDetailId: item.product_detail_id,
862
+ productInfo: item.product_info,
863
+ sku: item.sku,
864
+ packingType: item.packing_type,
865
+ packingSize: Number(item.packing_size || 0),
866
+ uom: item.uom,
867
+ remainQty: item.remain_qty,
868
+ remainUomValue: item.remain_uom_value
869
+ }
870
+ })
871
+
872
+ return _extractData(rawReleaseGoods, availableItems)
873
+ }
874
+
875
+ function _extractData(rawData, validatedData) {
876
+ return rawData.map(raw => {
877
+ const idx = validatedData.findIndex(val => {
878
+ const comparison = ['packingType', 'packingSize', 'uom']
879
+
880
+ let a: any = {},
881
+ b: any = {}
882
+
883
+ comparison.forEach(cc => {
884
+ if (raw[cc]) {
885
+ a[cc] = raw[cc]
886
+ b[cc] = val[cc]
887
+ }
888
+ })
889
+
890
+ a = JSON.stringify(Object.fromEntries(Object.entries(a).sort()))
891
+ b = JSON.stringify(Object.fromEntries(Object.entries(b).sort()))
892
+
893
+ return val.sku.toLowerCase() == raw.sku.toLowerCase() && a === b
894
+ })
895
+
896
+ let releaseUomValue = 0
897
+
898
+ // if sku is matched, assign qty and product id
899
+ if (idx >= 0) {
900
+ // assign qty to rawData as much as possible
901
+ releaseUomValue =
902
+ (Math.round((validatedData[idx]?.remainUomValue / validatedData[idx]?.remainQty) * 100) / 100) * raw.releaseQty
903
+
904
+ raw.assignedQty =
905
+ validatedData[idx].remainQty >= raw.releaseQty
906
+ ? raw.releaseQty > 0
907
+ ? raw.releaseQty
908
+ : 0
909
+ : validatedData[idx].remainQty
910
+
911
+ raw.assignedUomValue =
912
+ validatedData[idx].remainUomValue >= releaseUomValue
913
+ ? releaseUomValue > 0
914
+ ? releaseUomValue
915
+ : 0
916
+ : validatedData[idx].remainUomValue
917
+
918
+ // deduct qty & uomValue from validateData
919
+ validatedData[idx].remainQty -= raw.assignedQty
920
+ validatedData[idx].remainUomValue -= raw.assignedUomValue
921
+
922
+ raw.productId = validatedData[idx].productId
923
+ raw.productDetailId = validatedData[idx].productDetailId
924
+ raw.productInfo = validatedData[idx].productInfo
925
+ raw.packingType = validatedData[idx].packingType
926
+ raw.packingSize = validatedData[idx].packingSize
927
+ raw.uom = validatedData[idx].uom
928
+ } else {
929
+ raw.assignedQty = 0
930
+ raw.assignedUomValue = 0
931
+ raw.productId = null
932
+ }
933
+
934
+ let releaseDate = _getStdDateStr(new Date(raw.releaseDate || ''))
935
+
936
+ return {
937
+ ...raw,
938
+ releaseUomValue,
939
+ errorMsg:
940
+ !raw.productId || !raw.productDetailId
941
+ ? 'inventory or product not found'
942
+ : raw.releaseQty <= 0
943
+ ? 'invalid release qty'
944
+ : raw.assignedQty < raw.releaseQty
945
+ ? 'insufficient stock'
946
+ : raw.releaseDate == ''
947
+ ? 'release date is empty'
948
+ : releaseDate < _getStdDateStr(new Date())
949
+ ? 'backdate is not allowed'
950
+ : !raw.refNo
951
+ ? 'ref no is empty'
952
+ : ''
953
+ }
954
+ })
955
+ }
956
+
957
+ function getConditionValues(conditions: Filter[]): {
958
+ batchId?: string
959
+ containerNo?: string
960
+ product?: string[]
961
+ packingType?: string
962
+ inventory?: { batchId: string; productId: string }[]
963
+ } {
964
+ return conditions.reduce((condition, cond: Filter): {} => {
965
+ condition = {
966
+ ...condition,
967
+ [cond.name]: cond.value
968
+ }
969
+
970
+ return condition
971
+ }, {})
972
+ }
973
+
974
+ function _getStdDateStr(date) {
975
+ if (isNaN(date.getFullYear())) return ''
976
+ else {
977
+ date.setHours(date.getHours() + 8)
978
+ return date.toISOString().split('T')[0]
979
+ }
980
+ }