@things-factory/worksheet-base 4.3.356 → 4.3.357

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 (118) hide show
  1. package/dist-server/constants/worksheet.js +2 -1
  2. package/dist-server/constants/worksheet.js.map +1 -1
  3. package/dist-server/controllers/inspect/cycle-count-worksheet-controller.js +691 -257
  4. package/dist-server/controllers/inspect/cycle-count-worksheet-controller.js.map +1 -1
  5. package/dist-server/controllers/outbound/picking-worksheet-controller.js +76 -0
  6. package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
  7. package/dist-server/controllers/worksheet-controller.js +9 -2
  8. package/dist-server/controllers/worksheet-controller.js.map +1 -1
  9. package/dist-server/entities/worksheet-detail.js +9 -4
  10. package/dist-server/entities/worksheet-detail.js.map +1 -1
  11. package/dist-server/entities/worksheet.js +4 -0
  12. package/dist-server/entities/worksheet.js.map +1 -1
  13. package/dist-server/graphql/resolvers/worksheet/check-stock-take-current-location.js +18 -11
  14. package/dist-server/graphql/resolvers/worksheet/check-stock-take-current-location.js.map +1 -1
  15. package/dist-server/graphql/resolvers/worksheet/cycle-count-adjustment.js +75 -48
  16. package/dist-server/graphql/resolvers/worksheet/cycle-count-adjustment.js.map +1 -1
  17. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet-for-carton.js +82 -0
  18. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet-for-carton.js.map +1 -0
  19. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet-for-pallet.js +82 -0
  20. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet-for-pallet.js.map +1 -0
  21. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet-carton.js +17 -0
  22. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet-carton.js.map +1 -0
  23. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet.js +4 -4
  24. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet.js.map +1 -1
  25. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/index.js +2 -1
  26. package/dist-server/graphql/resolvers/worksheet/generate-worksheet/index.js.map +1 -1
  27. package/dist-server/graphql/resolvers/worksheet/index.js +3 -2
  28. package/dist-server/graphql/resolvers/worksheet/index.js.map +1 -1
  29. package/dist-server/graphql/resolvers/worksheet/inspecting/add-extra-inventory.js +16 -0
  30. package/dist-server/graphql/resolvers/worksheet/inspecting/add-extra-inventory.js.map +1 -0
  31. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-inventory-carton.js +16 -0
  32. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-inventory-carton.js.map +1 -0
  33. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-inventory.js +16 -0
  34. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-inventory.js.map +1 -0
  35. package/dist-server/graphql/resolvers/worksheet/inspecting/index.js +9 -5
  36. package/dist-server/graphql/resolvers/worksheet/inspecting/index.js.map +1 -1
  37. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting-carton.js +16 -0
  38. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting-carton.js.map +1 -0
  39. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting-pallet.js +16 -0
  40. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting-pallet.js.map +1 -0
  41. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-inventory-carton.js +16 -0
  42. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-inventory-carton.js.map +1 -0
  43. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-inventory.js +16 -0
  44. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-inventory.js.map +1 -0
  45. package/dist-server/graphql/resolvers/worksheet/inspecting/undo-inspection-carton.js +16 -0
  46. package/dist-server/graphql/resolvers/worksheet/inspecting/undo-inspection-carton.js.map +1 -0
  47. package/dist-server/graphql/resolvers/worksheet/not-tally-target-inventories.js +9 -5
  48. package/dist-server/graphql/resolvers/worksheet/not-tally-target-inventories.js.map +1 -1
  49. package/dist-server/graphql/resolvers/worksheet/worksheet-with-pagination.js +9 -7
  50. package/dist-server/graphql/resolvers/worksheet/worksheet-with-pagination.js.map +1 -1
  51. package/dist-server/graphql/resolvers/worksheet/worksheets.js +23 -9
  52. package/dist-server/graphql/resolvers/worksheet/worksheets.js.map +1 -1
  53. package/dist-server/graphql/resolvers/worksheet-detail/create-worksheet-detail.js +8 -1
  54. package/dist-server/graphql/resolvers/worksheet-detail/create-worksheet-detail.js.map +1 -1
  55. package/dist-server/graphql/resolvers/worksheet-detail/worksheet-detail.js +12 -1
  56. package/dist-server/graphql/resolvers/worksheet-detail/worksheet-detail.js.map +1 -1
  57. package/dist-server/graphql/types/worksheet/index.js +65 -9
  58. package/dist-server/graphql/types/worksheet/index.js.map +1 -1
  59. package/dist-server/graphql/types/worksheet/inventory-check-worksheet.js +2 -1
  60. package/dist-server/graphql/types/worksheet/inventory-check-worksheet.js.map +1 -1
  61. package/dist-server/graphql/types/worksheet/worksheet-detail-info.js +3 -0
  62. package/dist-server/graphql/types/worksheet/worksheet-detail-info.js.map +1 -1
  63. package/dist-server/graphql/types/worksheet/worksheet.js +2 -0
  64. package/dist-server/graphql/types/worksheet/worksheet.js.map +1 -1
  65. package/dist-server/graphql/types/worksheet-detail/new-worksheet-detail.js +1 -0
  66. package/dist-server/graphql/types/worksheet-detail/new-worksheet-detail.js.map +1 -1
  67. package/dist-server/graphql/types/worksheet-detail/worksheet-detail-patch.js +1 -0
  68. package/dist-server/graphql/types/worksheet-detail/worksheet-detail-patch.js.map +1 -1
  69. package/dist-server/graphql/types/worksheet-detail/worksheet-detail.js +1 -0
  70. package/dist-server/graphql/types/worksheet-detail/worksheet-detail.js.map +1 -1
  71. package/package.json +6 -6
  72. package/server/constants/worksheet.ts +2 -1
  73. package/server/controllers/inspect/cycle-count-worksheet-controller.ts +904 -296
  74. package/server/controllers/outbound/picking-worksheet-controller.ts +95 -1
  75. package/server/controllers/worksheet-controller.ts +10 -4
  76. package/server/entities/worksheet-detail.ts +7 -10
  77. package/server/entities/worksheet.ts +3 -0
  78. package/server/graphql/resolvers/worksheet/check-stock-take-current-location.ts +21 -14
  79. package/server/graphql/resolvers/worksheet/cycle-count-adjustment.ts +114 -51
  80. package/server/graphql/resolvers/worksheet/cycle-count-worksheet-for-carton.ts +90 -0
  81. package/server/graphql/resolvers/worksheet/cycle-count-worksheet-for-pallet.ts +91 -0
  82. package/server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet-carton.ts +57 -0
  83. package/server/graphql/resolvers/worksheet/generate-worksheet/generate-cycle-count-worksheet.ts +17 -11
  84. package/server/graphql/resolvers/worksheet/generate-worksheet/index.ts +2 -0
  85. package/server/graphql/resolvers/worksheet/index.ts +4 -2
  86. package/server/graphql/resolvers/worksheet/inspecting/{add-extra-pallet.ts → add-extra-inventory.ts} +27 -12
  87. package/server/graphql/resolvers/worksheet/inspecting/check-missing-inventory-carton.ts +24 -0
  88. package/server/graphql/resolvers/worksheet/inspecting/{check-missing-pallet.ts → check-missing-inventory.ts} +10 -5
  89. package/server/graphql/resolvers/worksheet/inspecting/index.ts +17 -9
  90. package/server/graphql/resolvers/worksheet/inspecting/inspecting-carton.ts +51 -0
  91. package/server/graphql/resolvers/worksheet/inspecting/{inspecting.ts → inspecting-pallet.ts} +5 -5
  92. package/server/graphql/resolvers/worksheet/inspecting/relocate-inventory-carton.ts +62 -0
  93. package/server/graphql/resolvers/worksheet/inspecting/{relocate-pallet.ts → relocate-inventory.ts} +15 -5
  94. package/server/graphql/resolvers/worksheet/inspecting/undo-inspection-carton.ts +24 -0
  95. package/server/graphql/resolvers/worksheet/not-tally-target-inventories.ts +11 -7
  96. package/server/graphql/resolvers/worksheet/worksheet-with-pagination.ts +9 -7
  97. package/server/graphql/resolvers/worksheet/worksheets.ts +57 -38
  98. package/server/graphql/resolvers/worksheet-detail/create-worksheet-detail.ts +10 -2
  99. package/server/graphql/resolvers/worksheet-detail/worksheet-detail.ts +12 -1
  100. package/server/graphql/types/worksheet/index.ts +65 -9
  101. package/server/graphql/types/worksheet/inventory-check-worksheet.ts +2 -1
  102. package/server/graphql/types/worksheet/worksheet-detail-info.ts +3 -0
  103. package/server/graphql/types/worksheet/worksheet.ts +2 -0
  104. package/server/graphql/types/worksheet-detail/new-worksheet-detail.ts +1 -0
  105. package/server/graphql/types/worksheet-detail/worksheet-detail-patch.ts +1 -0
  106. package/server/graphql/types/worksheet-detail/worksheet-detail.ts +1 -0
  107. package/tsconfig.json +1 -1
  108. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet.js +0 -74
  109. package/dist-server/graphql/resolvers/worksheet/cycle-count-worksheet.js.map +0 -1
  110. package/dist-server/graphql/resolvers/worksheet/inspecting/add-extra-pallet.js +0 -16
  111. package/dist-server/graphql/resolvers/worksheet/inspecting/add-extra-pallet.js.map +0 -1
  112. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-pallet.js +0 -16
  113. package/dist-server/graphql/resolvers/worksheet/inspecting/check-missing-pallet.js.map +0 -1
  114. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting.js +0 -16
  115. package/dist-server/graphql/resolvers/worksheet/inspecting/inspecting.js.map +0 -1
  116. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-pallet.js +0 -16
  117. package/dist-server/graphql/resolvers/worksheet/inspecting/relocate-pallet.js.map +0 -1
  118. package/server/graphql/resolvers/worksheet/cycle-count-worksheet.ts +0 -80
@@ -1,15 +1,18 @@
1
- import { Brackets, Equal, In, Not, SelectQueryBuilder } from 'typeorm'
1
+ import { Brackets, Equal, In, Not, SelectQueryBuilder, MoreThan } from 'typeorm'
2
2
 
3
3
  import { Bizplace } from '@things-factory/biz-base'
4
4
  import {
5
5
  generateCycleCount,
6
6
  InventoryCheck,
7
- ORDER_INVENTORY_STATUS,
7
+ InventoryCheckItem,
8
8
  ORDER_STATUS,
9
- OrderInventory,
10
- OrderNoGenerator
9
+ OrderNoGenerator,
10
+ INVENTORY_CHECK_ITEM_STATUS
11
11
  } from '@things-factory/sales-base'
12
- import { Inventory, INVENTORY_STATUS, Location } from '@things-factory/warehouse-base'
12
+ import { Inventory, INVENTORY_STATUS, Location, InventoryNoGenerator, Warehouse } from '@things-factory/warehouse-base'
13
+ import { ProductDetail, Product } from '@things-factory/product-base'
14
+ import { PartnerSetting, Setting } from '@things-factory/setting-base'
15
+ import { generateId } from '@things-factory/id-rule-base'
13
16
 
14
17
  import { WORKSHEET_STATUS, WORKSHEET_TYPE } from '../../constants'
15
18
  import { Worksheet, WorksheetDetail } from '../../entities'
@@ -17,194 +20,388 @@ import { WorksheetNoGenerator } from '../../utils'
17
20
  import { WorksheetController } from '../worksheet-controller'
18
21
 
19
22
  export class CycleCountWorksheetController extends WorksheetController {
23
+ private async createCycleCountWorksheet(
24
+ customerBizplace: Bizplace,
25
+ customerId: string,
26
+ executionDate: string,
27
+ inventoryCheckItem: InventoryCheckItem[],
28
+ selectedLocation: string[],
29
+ limit: number
30
+ ): Promise<Worksheet> {
31
+ const cycleCount: InventoryCheck = await generateCycleCount(
32
+ this.trxMgr,
33
+ this.domain,
34
+ this.user,
35
+ executionDate,
36
+ customerId
37
+ )
38
+
39
+ // Find out inventories which is target for cycle counting
40
+ const qb: SelectQueryBuilder<Inventory> = this.trxMgr.getRepository(Inventory).createQueryBuilder('INV')
41
+ qb.leftJoinAndSelect('INV.location', 'LOC')
42
+ .leftJoinAndSelect('INV.product', 'PROD')
43
+ .leftJoinAndSelect('INV.productDetail', 'PD')
44
+ .where('INV.domain_id = :domainId', { domainId: this.domain.id })
45
+ .andWhere('INV.bizplace_id = :bizplaceId', { bizplaceId: customerBizplace.id })
46
+ .andWhere('INV.status = :status', { status: INVENTORY_STATUS.STORED })
47
+
48
+ if (inventoryCheckItem && inventoryCheckItem.length > 0) {
49
+ let keyval = {
50
+ batchId: 'batch_id',
51
+ batchIdRef: 'batch_id_ref',
52
+ productDetailId: 'product_detail_id'
53
+ }
54
+
55
+ qb.andWhere(
56
+ new Brackets(qb => {
57
+ inventoryCheckItem.forEach((itm, idx) => {
58
+ qb.orWhere(
59
+ new Brackets(qb2 => {
60
+ let first = true
61
+ for (var prop in itm) {
62
+ if (first) {
63
+ first = false
64
+ qb2.where(`INV.${keyval[prop]} ${itm[prop] == null ? 'is null' : " = '" + itm[prop] + "'"}`)
65
+ } else {
66
+ qb2.andWhere(`INV.${keyval[prop]} ${itm[prop] == null ? 'is null' : " = '" + itm[prop] + "'"}`)
67
+ }
68
+ }
69
+ })
70
+ )
71
+ })
72
+ })
73
+ )
74
+ } else if (selectedLocation && selectedLocation.length > 0) {
75
+ // If selectedLocation is provided
76
+ // Query inventory items located at the selected location
77
+ qb.andWhere('INV.location_id IN (:...locationIds)', { locationIds: selectedLocation })
78
+ } else {
79
+ if (limit > 0) {
80
+ qb.orderBy('RANDOM()').limit(limit)
81
+ }
82
+ }
83
+
84
+ let inventories: Inventory[] = await qb.getMany()
85
+
86
+ if (!inventories.length) {
87
+ throw new Error(`Failed to find inventories`)
88
+ }
89
+
90
+ let cycleCountWorksheet: Worksheet = new Worksheet()
91
+ cycleCountWorksheet.domain = this.domain
92
+ cycleCountWorksheet.bizplace = customerBizplace
93
+ cycleCountWorksheet.name = WorksheetNoGenerator.cycleCount()
94
+ cycleCountWorksheet.inventoryCheck = cycleCount
95
+ cycleCountWorksheet.type = WORKSHEET_TYPE.CYCLE_COUNT
96
+ cycleCountWorksheet.status = WORKSHEET_STATUS.DEACTIVATED
97
+ cycleCountWorksheet.checkCount = 1
98
+ cycleCountWorksheet.creator = this.user
99
+ cycleCountWorksheet.updater = this.user
100
+ cycleCountWorksheet = await this.trxMgr.getRepository(Worksheet).save(cycleCountWorksheet)
101
+
102
+ // generate order inventory mapping with inventory ID
103
+ let targetInventoryCheckItems: InventoryCheckItem[] = []
104
+ for (let i: number = 0; i < inventories.length; i++) {
105
+ const inventory: Inventory = inventories[i]
106
+
107
+ let targetInventoryCheckItem: InventoryCheckItem = new InventoryCheckItem()
108
+ targetInventoryCheckItem.domain = this.domain
109
+ targetInventoryCheckItem.bizplace = customerBizplace
110
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.PENDING
111
+ targetInventoryCheckItem.name = OrderNoGenerator.inventoryCheckItem()
112
+ targetInventoryCheckItem.inventoryCheck = cycleCount
113
+ targetInventoryCheckItem.originQty = inventory.qty
114
+ targetInventoryCheckItem.originWeight = 0
115
+ targetInventoryCheckItem.originUomValue = inventory.uomValue
116
+ targetInventoryCheckItem.originBatchNo = inventory.batchId
117
+ targetInventoryCheckItem.originLocation = inventory.location
118
+ targetInventoryCheckItem.inventory = inventory
119
+ targetInventoryCheckItem.product = inventory.product
120
+ targetInventoryCheckItem.productDetail = inventory.productDetail
121
+ targetInventoryCheckItem.countNo = 1
122
+ targetInventoryCheckItem.creator = this.user
123
+ targetInventoryCheckItem.updater = this.user
124
+ targetInventoryCheckItems.push(targetInventoryCheckItem)
125
+
126
+ inventory.updater = this.user
127
+ }
128
+
129
+ targetInventoryCheckItems = await this.trxMgr
130
+ .getRepository(InventoryCheckItem)
131
+ .save(targetInventoryCheckItems, { chunk: 500 })
132
+
133
+ inventories = await this.trxMgr.getRepository(Inventory).save(inventories, { chunk: 500 })
134
+
135
+ let cycleCountWorksheetDetails: WorksheetDetail[] = []
136
+ for (let i: number = 0; i < targetInventoryCheckItems.length; i++) {
137
+ let targetInventoryCheckItem: InventoryCheckItem = targetInventoryCheckItems[i]
138
+
139
+ let cycleCountWorksheetDetail: WorksheetDetail = new WorksheetDetail()
140
+ cycleCountWorksheetDetail.domain = this.domain
141
+ cycleCountWorksheetDetail.bizplace = customerBizplace
142
+ cycleCountWorksheetDetail.worksheet = cycleCountWorksheet
143
+ cycleCountWorksheetDetail.name = WorksheetNoGenerator.cycleCountDetail()
144
+ cycleCountWorksheetDetail.targetInventoryCheckItem = targetInventoryCheckItem
145
+ cycleCountWorksheetDetail.type = WORKSHEET_TYPE.CYCLE_COUNT
146
+ cycleCountWorksheetDetail.status = WORKSHEET_STATUS.DEACTIVATED
147
+ cycleCountWorksheetDetail.creator = this.user
148
+ cycleCountWorksheetDetail.updater = this.user
149
+
150
+ cycleCountWorksheetDetails.push(cycleCountWorksheetDetail)
151
+ }
152
+
153
+ await this.trxMgr.getRepository(WorksheetDetail).save(cycleCountWorksheetDetails, { chunk: 500 })
154
+
155
+ return cycleCountWorksheet
156
+ }
157
+
158
+ private async createNextRoundCycleCountWorksheet(
159
+ customerId: string,
160
+ existingInventoryCheck: InventoryCheck,
161
+ nextExecutionDate: string,
162
+ currentCycleCountWorksheet: Worksheet,
163
+ inventoryCheckItemIds: string[]
164
+ ): Promise<Worksheet> {
165
+ try {
166
+ // Step 1: Create a new cycle count worksheet for the next round
167
+ let nextRoundWorksheet: Worksheet = new Worksheet()
168
+ Object.assign(nextRoundWorksheet, currentCycleCountWorksheet)
169
+ nextRoundWorksheet.id = undefined
170
+ nextRoundWorksheet.domain = this.domain
171
+ nextRoundWorksheet.bizplace = customerId
172
+ nextRoundWorksheet.name = WorksheetNoGenerator.cycleCount()
173
+ nextRoundWorksheet.status = WORKSHEET_STATUS.EXECUTING
174
+ nextRoundWorksheet.endedAt = null
175
+ nextRoundWorksheet.updater = this.user
176
+ nextRoundWorksheet.worksheetDetails = null
177
+ nextRoundWorksheet.checkCount = currentCycleCountWorksheet.checkCount + 1
178
+ nextRoundWorksheet.inventoryCheck = existingInventoryCheck
179
+ nextRoundWorksheet.type = WORKSHEET_TYPE.CYCLE_COUNT
180
+
181
+ // Step 2: Save the new Worksheet
182
+ await this.trxMgr.getRepository(Worksheet).save(nextRoundWorksheet)
183
+
184
+ // Step 3: Fetch current InventoryCheckItems
185
+ const currentInventoryCheckItems: InventoryCheckItem[] = await this.trxMgr
186
+ .getRepository(InventoryCheckItem)
187
+ .findByIds(inventoryCheckItemIds)
188
+
189
+ // Step 4: Create new InventoryCheckItems based on the current ones
190
+ const nextRoundInventoryCheckItems = currentInventoryCheckItems.map(currentItem => {
191
+ let nextRoundItem: InventoryCheckItem = new InventoryCheckItem()
192
+
193
+ Object.assign(nextRoundItem, currentItem)
194
+ nextRoundItem.id = undefined
195
+ nextRoundItem.domain = this.domain
196
+ nextRoundItem.bizplace = customerId
197
+ nextRoundItem.inventoryCheck = currentItem.inventoryCheckId
198
+ nextRoundItem.status = INVENTORY_CHECK_ITEM_STATUS.INSPECTING
199
+ nextRoundItem.name = OrderNoGenerator.inventoryCheckItem()
200
+ nextRoundItem.originQty = currentItem.inspectedQty || currentItem.originQty
201
+ nextRoundItem.originUomValue = currentItem.inspectedUomValue || currentItem.originUomValue
202
+ nextRoundItem.originBatchNo = currentItem.inspectedBatchNo || currentItem.originBatchNo
203
+ nextRoundItem.originLocation = currentItem.inspectedLocationId || currentItem.originLocationId
204
+ nextRoundItem.inspectedQty = null
205
+ nextRoundItem.inspectedUomValue = null
206
+ nextRoundItem.inspectedBatchNo = null
207
+ nextRoundItem.inspectedLocationId = null
208
+ nextRoundItem.countNo = currentItem.countNo + 1
209
+ nextRoundItem.inventory = currentItem.inventoryId
210
+ nextRoundItem.product = currentItem.productId
211
+ nextRoundItem.productDetail = currentItem.productDetailId
212
+ nextRoundItem.creator = this.user
213
+ nextRoundItem.updater = this.user
214
+ return nextRoundItem
215
+ })
216
+
217
+ // Step 5: Save the new InventoryCheckItems
218
+ await this.trxMgr.getRepository(InventoryCheckItem).save(nextRoundInventoryCheckItems, { chunk: 500 })
219
+
220
+ // Step 6: Create a new cycle count worksheet details for the next round
221
+ let nextRoundWorksheetDetails = nextRoundInventoryCheckItems.map(newItem => {
222
+ let detail: WorksheetDetail = new WorksheetDetail()
223
+
224
+ detail.domain = this.domain
225
+ detail.bizplace = customerId
226
+ detail.worksheet = nextRoundWorksheet
227
+ detail.name = WorksheetNoGenerator.cycleCountDetail()
228
+ detail.targetInventoryCheckItem = newItem
229
+ detail.type = WORKSHEET_TYPE.CYCLE_COUNT
230
+ detail.status = WORKSHEET_STATUS.EXECUTING
231
+ detail.creator = this.user
232
+ detail.updater = this.user
233
+
234
+ return detail
235
+ })
236
+
237
+ // Step 7: Save the new Worksheet Details
238
+ await this.trxMgr.getRepository(WorksheetDetail).save(nextRoundWorksheetDetails, { chunk: 500 })
239
+
240
+ // Step 8: Mark the selected inventory as TERMINATED
241
+ currentInventoryCheckItems.forEach(item => {
242
+ item.status = INVENTORY_CHECK_ITEM_STATUS.TERMINATED
243
+ item.updater = this.user
244
+ })
245
+ await this.trxMgr.getRepository(InventoryCheckItem).save(currentInventoryCheckItems)
246
+
247
+ // Step 9: Directly update the worksheet details
248
+ await this.trxMgr
249
+ .getRepository(WorksheetDetail)
250
+ .createQueryBuilder()
251
+ .update(WorksheetDetail)
252
+ .set({
253
+ status: WORKSHEET_STATUS.DONE,
254
+ updater: this.user
255
+ })
256
+ .where('targetInventoryCheckItem IN (:...ids)', { ids: currentInventoryCheckItems.map(item => item.id) })
257
+ .execute()
258
+
259
+ // Check if any record is not DONE
260
+ const notDoneCount = await this.trxMgr.getRepository(WorksheetDetail).count({
261
+ where: {
262
+ worksheet: currentCycleCountWorksheet,
263
+ status: Not(Equal(WORKSHEET_STATUS.DONE))
264
+ }
265
+ })
266
+
267
+ if (notDoneCount === 0) {
268
+ // Step 11: Update status of existing InventoryCheck
269
+ existingInventoryCheck.status = ORDER_STATUS.INSPECTING
270
+ existingInventoryCheck.updater = this.user
271
+ await this.trxMgr.getRepository(InventoryCheck).save(existingInventoryCheck)
272
+
273
+ // Step 12: Mark the current worksheet as DONE
274
+ currentCycleCountWorksheet.status = WORKSHEET_STATUS.DONE
275
+ currentCycleCountWorksheet.updater = this.user
276
+ await this.trxMgr.getRepository(Worksheet).save(currentCycleCountWorksheet)
277
+ }
278
+
279
+ existingInventoryCheck.executionDate = nextExecutionDate
280
+ existingInventoryCheck.updater = this.user
281
+ await this.trxMgr.getRepository(InventoryCheck).save(existingInventoryCheck)
282
+
283
+ return nextRoundWorksheet
284
+ } catch (error) {
285
+ console.error('An error occurred while creating the next round cycle count worksheet:', error)
286
+ throw new Error('Failed to create next round cycle count worksheet')
287
+ }
288
+ }
289
+
20
290
  async generateCycleCountWorksheet(
21
291
  executionDate: string,
22
292
  customerId: string,
23
- orderInventoryIds: string[] = [],
24
- orderInventory: OrderInventory[],
25
- limit: number
293
+ inventoryCheckItemIds: string[] = [],
294
+ inventoryCheckItem: InventoryCheckItem[] = [],
295
+ limit: number = 0,
296
+ selectedLocation: string[] = [],
297
+ cycleCountNo: string
26
298
  ): Promise<Worksheet> {
27
- // Find out warehouse and customer bizplace
28
299
  const customerBizplace: Bizplace = await this.trxMgr.getRepository(Bizplace).findOne(customerId)
29
- let foundCycleCountWorksheet: Worksheet = await this.trxMgr.getRepository(Worksheet).findOne({
300
+
301
+ const foundInventoryCheck: InventoryCheck = await this.trxMgr.getRepository(InventoryCheck).findOne({
302
+ where: {
303
+ domain: this.domain,
304
+ bizplace: customerBizplace,
305
+ name: cycleCountNo
306
+ }
307
+ })
308
+
309
+ const worksheetRepository = this.trxMgr.getRepository(Worksheet)
310
+ const foundCycleCountWorksheet: Worksheet | undefined = await worksheetRepository.findOne({
30
311
  where: {
31
312
  domain: this.domain,
32
313
  bizplace: customerBizplace,
33
314
  type: In([WORKSHEET_TYPE.CYCLE_COUNT, WORKSHEET_TYPE.CYCLE_COUNT_RECHECK]),
34
315
  status: Not(WORKSHEET_STATUS.DONE)
35
316
  },
36
- relations: ['worksheetDetails', 'worksheetDetails.targetInventory', 'inventoryCheck']
317
+ order: {
318
+ checkCount: 'DESC'
319
+ },
320
+ relations: ['worksheetDetails', 'worksheetDetails.targetInventoryCheckItem', 'inventoryCheck']
37
321
  })
38
322
 
39
- // Create second round of cycle count with specified order inventories
40
- if (orderInventoryIds.length) {
41
- // Update status of target inventories (NOT_TALLY => INSPECTING)
42
- let targetInventories: OrderInventory[] = await this.trxMgr
43
- .getRepository(OrderInventory)
44
- .findByIds(orderInventoryIds)
45
- targetInventories.forEach((targetInventory: OrderInventory) => {
46
- targetInventory.status = ORDER_INVENTORY_STATUS.INSPECTING
47
- targetInventory.updater = this.user
48
- })
49
- await this.trxMgr.getRepository(OrderInventory).save(targetInventories)
50
-
51
- // Update status of worksheet (NOT_TALLY => EXECUTING)
52
- foundCycleCountWorksheet.type = WORKSHEET_TYPE.CYCLE_COUNT_RECHECK
53
- foundCycleCountWorksheet.status = WORKSHEET_STATUS.EXECUTING
54
- foundCycleCountWorksheet.endedAt = null
55
- foundCycleCountWorksheet.updater = this.user
56
- await this.trxMgr.getRepository(Worksheet).save(foundCycleCountWorksheet)
57
-
58
- // Update status of worksheet details (NOT_TALLY => EXECUTING)
59
- let worksheetDetails = foundCycleCountWorksheet.worksheetDetails.filter(
60
- (wsd: WorksheetDetail) => orderInventoryIds.indexOf(wsd.targetInventory.id) >= 0
323
+ if (inventoryCheckItemIds.length) {
324
+ return await this.createNextRoundCycleCountWorksheet(
325
+ customerId,
326
+ foundInventoryCheck,
327
+ executionDate,
328
+ foundCycleCountWorksheet,
329
+ inventoryCheckItemIds
61
330
  )
62
- worksheetDetails.forEach((wsd: WorksheetDetail) => {
63
- wsd.status = WORKSHEET_STATUS.EXECUTING
64
- wsd.updater = this.user
65
- })
66
- await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetails)
67
-
68
- // Update status of cycle count
69
- const cycleCount: InventoryCheck = foundCycleCountWorksheet.inventoryCheck
70
- cycleCount.status = ORDER_STATUS.INSPECTING
71
- cycleCount.updater = this.user
72
- await this.trxMgr.getRepository(InventoryCheck).save(cycleCount)
73
-
74
- return foundCycleCountWorksheet
75
331
  } else {
76
- // Create first round of cycle count with whole pallets
77
332
  if (foundCycleCountWorksheet) {
78
- throw new Error(`Unfinished cycle count worksheet exists.`)
333
+ throw new Error('Unfinished cycle count worksheet exists.')
79
334
  }
80
-
81
- const cycleCount: InventoryCheck = await generateCycleCount(
82
- this.trxMgr,
83
- this.domain,
84
- this.user,
335
+ return await this.createCycleCountWorksheet(
336
+ customerBizplace,
337
+ customerId,
85
338
  executionDate,
86
- customerId
339
+ inventoryCheckItem,
340
+ selectedLocation,
341
+ limit
87
342
  )
343
+ }
344
+ }
88
345
 
89
- // Find out inventories which is target for cycle counting
90
- const qb: SelectQueryBuilder<Inventory> = this.trxMgr.getRepository(Inventory).createQueryBuilder('INV')
91
- qb.leftJoinAndSelect('INV.location', 'LOC')
92
- .where('INV.domain_id = :domainId', { domainId: this.domain.id })
93
- .andWhere('INV.bizplace_id = :bizplaceId', { bizplaceId: customerBizplace.id })
94
- .andWhere('INV.status = :status', { status: INVENTORY_STATUS.STORED })
95
- .andWhere(
96
- new Brackets(qb => {
97
- qb.where('"INV"."locked_qty" ISNULL')
98
- qb.orWhere('"INV"."locked_qty" = 0')
99
- })
100
- )
101
-
102
- if (orderInventory && orderInventory.length > 0) {
103
- let keyval = {
104
- batchId: 'batch_id',
105
- batchIdRef: 'batch_id_ref',
106
- productDetailId: 'product_detail_id'
107
- }
108
-
109
- qb.andWhere(
110
- new Brackets(qb => {
111
- orderInventory.forEach((itm, idx) => {
112
- // sample itm value
113
- // batchId: 'WO00019730', batchIdRef: null, productDetailId: '1d679587-c713-42d6-bd0a-74e587e39cc7'
114
- qb.orWhere(
115
- new Brackets(qb2 => {
116
- let first = true
117
- for (var prop in itm) {
118
- if (first) {
119
- first = false
120
- qb2.where(`INV.${keyval[prop]} ${itm[prop] == null ? 'is null' : " = '" + itm[prop] + "'"}`)
121
- } else {
122
- qb2.andWhere(`INV.${keyval[prop]} ${itm[prop] == null ? 'is null' : " = '" + itm[prop] + "'"}`)
123
- }
124
- }
125
- })
126
- )
127
- })
128
- })
129
- )
130
- } else {
131
- if (limit > 0) {
132
- qb.orderBy('RANDOM()').limit(limit)
133
- }
134
- }
135
-
136
- let inventories: Inventory[] = await qb.getMany()
137
-
138
- if (!inventories.length) {
139
- throw new Error(`Failed to find inventories`)
140
- }
346
+ async generateCycleCountWorksheetCarton(
347
+ executionDate: string,
348
+ customerId: string,
349
+ cycleCountNo: string,
350
+ cartonIds: string[] = [],
351
+ batchIds: string[] = [],
352
+ productDetailIds: string[] = [],
353
+ locationNames: string[] = []
354
+ ): Promise<Worksheet> {
355
+ const customerBizplace: Bizplace = await this.trxMgr.getRepository(Bizplace).findOne(customerId)
141
356
 
142
- let cycleCountWorksheet: Worksheet = new Worksheet()
143
- cycleCountWorksheet.domain = this.domain
144
- cycleCountWorksheet.bizplace = customerBizplace
145
- cycleCountWorksheet.name = WorksheetNoGenerator.cycleCount()
146
- cycleCountWorksheet.inventoryCheck = cycleCount
147
- cycleCountWorksheet.type = WORKSHEET_TYPE.CYCLE_COUNT
148
- cycleCountWorksheet.status = WORKSHEET_STATUS.DEACTIVATED
149
- cycleCountWorksheet.creator = this.user
150
- cycleCountWorksheet.updater = this.user
151
- cycleCountWorksheet = await this.trxMgr.getRepository(Worksheet).save(cycleCountWorksheet)
152
-
153
- // generate order inventory mapping with inventory ID
154
- let targetInventories: OrderInventory[] = []
155
- for (let i: number = 0; i < inventories.length; i++) {
156
- const inventory: Inventory = inventories[i]
157
-
158
- let targetInventory: OrderInventory = new OrderInventory()
159
- targetInventory.domain = this.domain
160
- targetInventory.bizplace = customerBizplace
161
- targetInventory.status = ORDER_INVENTORY_STATUS.PENDING
162
- targetInventory.name = OrderNoGenerator.orderInventory()
163
- targetInventory.inventoryCheck = cycleCount
164
- targetInventory.originQty = inventory.qty
165
- targetInventory.originWeight = 0
166
- targetInventory.originUomValue = inventory.uomValue
167
- targetInventory.originBatchNo = inventory.batchId
168
- targetInventory.originLocation = inventory.location
169
- targetInventory.releaseQty = 0
170
- targetInventory.releaseWeight = 0
171
- targetInventory.releaseUomValue = 0
172
- targetInventory.inventory = inventory
173
- targetInventory.creator = this.user
174
- targetInventory.updater = this.user
175
- targetInventories.push(targetInventory)
176
-
177
- inventory.lockedQty = inventory.qty
178
- inventory.lockedWeight = 0
179
- inventory.lockedUomValue = inventory.uomValue
180
- inventory.updater = this.user
357
+ const foundInventoryCheck: InventoryCheck = await this.trxMgr.getRepository(InventoryCheck).findOne({
358
+ where: {
359
+ domain: this.domain,
360
+ bizplace: customerBizplace,
361
+ name: cycleCountNo
181
362
  }
363
+ })
182
364
 
183
- targetInventories = await this.trxMgr.getRepository(OrderInventory).save(targetInventories, { chunk: 500 })
184
-
185
- inventories = await this.trxMgr.getRepository(Inventory).save(inventories, { chunk: 500 })
186
-
187
- let cycleCountWorksheetDetails: WorksheetDetail[] = []
188
- for (let i: number = 0; i < targetInventories.length; i++) {
189
- let targetInventory: OrderInventory = targetInventories[i]
365
+ const worksheetRepository = this.trxMgr.getRepository(Worksheet)
366
+ const foundCycleCountWorksheet: Worksheet | undefined = await worksheetRepository.findOne({
367
+ where: {
368
+ domain: this.domain,
369
+ bizplace: customerBizplace,
370
+ type: In([WORKSHEET_TYPE.CYCLE_COUNT, WORKSHEET_TYPE.CYCLE_COUNT_RECHECK]),
371
+ status: Not(WORKSHEET_STATUS.DONE)
372
+ },
373
+ order: {
374
+ checkCount: 'DESC'
375
+ },
376
+ relations: ['worksheetDetails', 'worksheetDetails.targetInventoryCheckItem', 'inventoryCheck']
377
+ })
190
378
 
191
- let cycleCountWorksheetDetail: WorksheetDetail = new WorksheetDetail()
192
- cycleCountWorksheetDetail.domain = this.domain
193
- cycleCountWorksheetDetail.bizplace = customerBizplace
194
- cycleCountWorksheetDetail.worksheet = cycleCountWorksheet
195
- cycleCountWorksheetDetail.name = WorksheetNoGenerator.cycleCountDetail()
196
- cycleCountWorksheetDetail.targetInventory = targetInventory
197
- cycleCountWorksheetDetail.type = WORKSHEET_TYPE.CYCLE_COUNT
198
- cycleCountWorksheetDetail.status = WORKSHEET_STATUS.DEACTIVATED
199
- cycleCountWorksheetDetail.creator = this.user
200
- cycleCountWorksheetDetail.updater = this.user
379
+ const qb = this.trxMgr.getRepository(InventoryCheckItem).createQueryBuilder('inventoryCheckItem')
380
+ qb.innerJoinAndSelect('inventoryCheckItem.inventory', 'inventory')
381
+ .innerJoinAndSelect('inventoryCheckItem.productDetail', 'productDetail')
382
+ .innerJoinAndSelect('inventoryCheckItem.originLocation', 'location')
383
+ .where('inventory.cartonId IN (:...cartonIds)', { cartonIds })
384
+ .andWhere('inventory.batchId IN (:...batchIds)', { batchIds })
385
+ .andWhere('productDetail.id IN (:...productDetailIds)', { productDetailIds })
386
+ .andWhere('location.name IN (:...locationNames)', { locationNames })
387
+ .andWhere('inventoryCheckItem.domain_id = :domainId', { domainId: this.domain.id })
388
+ .andWhere('inventoryCheckItem.bizplace_id = :bizplaceId', { bizplaceId: customerBizplace.id })
389
+ .andWhere('inventoryCheckItem.inventory_check_id = :inventoryCheckId', {
390
+ inventoryCheckId: foundInventoryCheck.id
391
+ })
201
392
 
202
- cycleCountWorksheetDetails.push(cycleCountWorksheetDetail)
203
- }
393
+ const inventoryCheckItems: InventoryCheckItem[] = await qb.getMany()
204
394
 
205
- await this.trxMgr.getRepository(WorksheetDetail).save(cycleCountWorksheetDetails, { chunk: 500 })
395
+ const inventoryCheckItemIds = inventoryCheckItems.map(item => item.id)
206
396
 
207
- return cycleCountWorksheet
397
+ if (inventoryCheckItemIds.length) {
398
+ return await this.createNextRoundCycleCountWorksheet(
399
+ customerId,
400
+ foundInventoryCheck,
401
+ executionDate,
402
+ foundCycleCountWorksheet,
403
+ inventoryCheckItemIds
404
+ )
208
405
  }
209
406
  }
210
407
 
@@ -212,18 +409,20 @@ export class CycleCountWorksheetController extends WorksheetController {
212
409
  let worksheet: Worksheet = await this.findActivatableWorksheet(worksheetNo, WORKSHEET_TYPE.CYCLE_COUNT, [
213
410
  'inventoryCheck',
214
411
  'worksheetDetails',
215
- 'worksheetDetails.targetInventory'
412
+ 'worksheetDetails.targetInventoryCheckItem'
216
413
  ])
217
414
 
218
415
  let worksheetDetails: WorksheetDetail[] = worksheet.worksheetDetails
219
- let targetInventories: OrderInventory[] = worksheetDetails.map((wsd: WorksheetDetail) => wsd.targetInventory)
416
+ let targetInventoryCheckItems: InventoryCheckItem[] = worksheetDetails.map(
417
+ (wsd: WorksheetDetail) => wsd.targetInventoryCheckItem
418
+ )
220
419
 
221
- for (let i: number = 0; i < targetInventories.length; i++) {
222
- let targetInventory: OrderInventory = targetInventories[i]
223
- targetInventory.status = ORDER_INVENTORY_STATUS.INSPECTING
224
- targetInventory.updater = this.user
420
+ for (let i: number = 0; i < targetInventoryCheckItems.length; i++) {
421
+ let targetInventoryCheckItem: InventoryCheckItem = targetInventoryCheckItems[i]
422
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.INSPECTING
423
+ targetInventoryCheckItem.updater = this.user
225
424
  }
226
- await this.trxMgr.getRepository(OrderInventory).save(targetInventories, { chunk: 500 })
425
+ await this.trxMgr.getRepository(InventoryCheckItem).save(targetInventoryCheckItems, { chunk: 500 })
227
426
 
228
427
  for (let i: number = 0; i < worksheetDetails.length; i++) {
229
428
  let foundWSD: WorksheetDetail = worksheetDetails[i]
@@ -244,7 +443,7 @@ export class CycleCountWorksheetController extends WorksheetController {
244
443
  return worksheet
245
444
  }
246
445
 
247
- async inspecting(
446
+ async inspectingPallet(
248
447
  worksheetDetailName: string,
249
448
  inspectedBatchNo: string,
250
449
  inspectedQty: number,
@@ -253,124 +452,312 @@ export class CycleCountWorksheetController extends WorksheetController {
253
452
  let worksheetDetail: WorksheetDetail = await this.findExecutableWorksheetDetailByName(
254
453
  worksheetDetailName,
255
454
  WORKSHEET_TYPE.CYCLE_COUNT,
256
- ['targetInventory', 'targetInventory.inventory', 'targetInventory.inventory.location']
455
+ ['targetInventoryCheckItem', 'targetInventoryCheckItem.inventory', 'targetInventoryCheckItem.inventory.location']
257
456
  )
258
457
 
259
- let targetInventory: OrderInventory = worksheetDetail.targetInventory
260
- const inventory: Inventory = targetInventory.inventory
261
- const { batchId, qty, uomValue }: { batchId: string; qty: number; uomValue: number } = inventory
458
+ let targetInventoryCheckItem: InventoryCheckItem = worksheetDetail.targetInventoryCheckItem
459
+ const {
460
+ originBatchNo,
461
+ originQty,
462
+ originUomValue
463
+ }: { originBatchNo: string; originQty: number; originUomValue: number } = targetInventoryCheckItem
262
464
 
263
- const isChanged: boolean = batchId !== inspectedBatchNo || qty !== inspectedQty || uomValue !== inspectedUomValue
465
+ const isChanged: boolean =
466
+ originBatchNo !== inspectedBatchNo || originQty !== inspectedQty || originUomValue !== inspectedUomValue
264
467
  const worksheetDetailStatus: string = isChanged ? WORKSHEET_STATUS.NOT_TALLY : WORKSHEET_STATUS.DONE
265
- const targetInventoryStatus: string = isChanged
266
- ? ORDER_INVENTORY_STATUS.NOT_TALLY
267
- : ORDER_INVENTORY_STATUS.INSPECTED
468
+ const targetInventoryCheckItemStatus: string = isChanged
469
+ ? INVENTORY_CHECK_ITEM_STATUS.NOT_TALLY
470
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTED
268
471
 
269
472
  worksheetDetail.status = worksheetDetailStatus
270
473
  worksheetDetail.updater = this.user
271
474
  await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
272
475
 
273
- targetInventory.inspectedBatchNo = inspectedBatchNo
274
- targetInventory.inspectedQty = inspectedQty
275
- targetInventory.inspectedUomValue = inspectedUomValue
276
- targetInventory.inspectedLocation = targetInventory.inventory.location
277
- targetInventory.status = targetInventoryStatus
278
- targetInventory.updater = this.user
279
- await this.updateOrderTargets([targetInventory])
280
-
281
- //If all tally, remove lockedQty, lockedWeight, lockedUomValue
282
- if (!isChanged) {
283
- inventory.lockedQty = 0
284
- inventory.lockedWeight = 0
285
- inventory.lockedUomValue = 0
286
- inventory.updater = this.user
287
- await this.trxMgr.getRepository(Inventory).save(inventory, { chunk: 500 })
476
+ targetInventoryCheckItem.inspectedBatchNo = inspectedBatchNo
477
+ targetInventoryCheckItem.inspectedQty = inspectedQty
478
+ targetInventoryCheckItem.inspectedUomValue = inspectedUomValue
479
+ targetInventoryCheckItem.inspectedLocation = targetInventoryCheckItem.inventory.location
480
+ targetInventoryCheckItem.status = targetInventoryCheckItemStatus
481
+ targetInventoryCheckItem.updater = this.user
482
+ await this.updateOrderTargets([targetInventoryCheckItem])
483
+ }
484
+
485
+ async inspectingCarton(
486
+ productDetailId: string,
487
+ cartonId: string,
488
+ batchId: string,
489
+ locationId: string,
490
+ inspectedBatchNo: string,
491
+ inspectedQty: number,
492
+ inspectedUomValue: number
493
+ ): Promise<void> {
494
+ const productDetail: ProductDetail = await this.trxMgr.getRepository(ProductDetail).findOne({
495
+ where: { id: productDetailId },
496
+ relations: ['product']
497
+ })
498
+
499
+ let qb: SelectQueryBuilder<Inventory> = this.trxMgr
500
+ .getRepository(Inventory)
501
+ .createQueryBuilder('inv')
502
+ .where('inv.product_detail_id = :productDetailId', { productDetailId })
503
+ .andWhere('inv.carton_id = :cartonId', { cartonId })
504
+ .andWhere('inv.batch_id = :batchId', { batchId })
505
+ .andWhere('inv.location_id = :locationId', { locationId })
506
+
507
+ let orderByField = 'created_at'
508
+ let orderByDirection: 'ASC' | 'DESC' = 'ASC'
509
+
510
+ switch (productDetail.product.pickingStrategy) {
511
+ case 'LIFO':
512
+ orderByDirection = 'DESC'
513
+ break
514
+ case 'FEFO':
515
+ orderByField = 'expiration_date'
516
+ break
517
+ case 'FMFO':
518
+ orderByField = 'manufacture_date'
519
+ break
520
+ }
521
+ qb.addOrderBy(`"inv"."${orderByField}"`, orderByDirection)
522
+
523
+ if (['FEFO', 'FMFO'].includes(productDetail.product.pickingStrategy)) {
524
+ qb.addOrderBy('"inv"."created_at"', 'ASC')
525
+ }
526
+
527
+ const inventories: Inventory[] = await qb.getMany()
528
+
529
+ const inventoryIds = inventories.map(inv => inv.id)
530
+ const inventoryCheckItems: InventoryCheckItem[] = await this.trxMgr
531
+ .getRepository(InventoryCheckItem)
532
+ .createQueryBuilder('inventoryCheckItem')
533
+ .leftJoinAndSelect('inventoryCheckItem.inventory', 'inventory')
534
+ .where('inventory.id IN (:...ids)', { ids: inventoryIds })
535
+ .andWhere('inventoryCheckItem.status = :status', { status: 'INSPECTING' })
536
+ .getMany()
537
+
538
+ const totalOriginQty = inventoryCheckItems.reduce((total, item) => total + (item.originQty || 0), 0)
539
+ const totalOriginUomValue = inventoryCheckItems.reduce((total, item) => total + (item.originUomValue || 0), 0)
540
+
541
+ const hasQtyDiscrepancy = totalOriginQty !== inspectedQty
542
+ const hasUomValueDiscrepancy = totalOriginUomValue !== inspectedUomValue
543
+
544
+ const hasBatchNoDiscrepancy = inventoryCheckItems.some(checkItem => checkItem.originBatchNo !== inspectedBatchNo)
545
+
546
+ const hasDiscrepancies = hasQtyDiscrepancy || hasUomValueDiscrepancy || hasBatchNoDiscrepancy
547
+
548
+ let inventoryCheckItemStatus
549
+ let worksheetDetailStatus
550
+
551
+ if (inventoryCheckItems.length > 1) {
552
+ inventoryCheckItemStatus = hasDiscrepancies
553
+ ? INVENTORY_CHECK_ITEM_STATUS.GROUP_NOT_TALLY
554
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTED
555
+ worksheetDetailStatus = hasDiscrepancies ? WORKSHEET_STATUS.NOT_TALLY : WORKSHEET_STATUS.DONE
556
+ } else {
557
+ inventoryCheckItemStatus = hasDiscrepancies
558
+ ? INVENTORY_CHECK_ITEM_STATUS.NOT_TALLY
559
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTED
560
+ worksheetDetailStatus = hasDiscrepancies ? WORKSHEET_STATUS.NOT_TALLY : WORKSHEET_STATUS.DONE
561
+ }
562
+
563
+ let totalInspectedQty = inspectedQty
564
+ let totalInspectedUomValue = inspectedUomValue
565
+
566
+ for (const checkItem of inventoryCheckItems) {
567
+ const qtyToInspect = totalInspectedQty > 0 ? Math.min(totalInspectedQty, checkItem.originQty) : 0
568
+ const uomValueToInspect =
569
+ totalInspectedUomValue > 0 ? Math.min(totalInspectedUomValue, checkItem.originUomValue) : 0
570
+
571
+ checkItem.inspectedQty = (checkItem.inspectedQty ?? 0) + qtyToInspect
572
+ totalInspectedQty -= qtyToInspect
573
+
574
+ checkItem.inspectedUomValue = (checkItem.inspectedUomValue ?? 0) + uomValueToInspect
575
+ totalInspectedUomValue -= uomValueToInspect
576
+
577
+ checkItem.inspectedBatchNo = inspectedBatchNo
578
+ checkItem.inspectedLocation = checkItem.originLocationId
579
+ checkItem.status = inventoryCheckItemStatus
580
+ checkItem.updater = this.user
581
+
582
+ await this.trxMgr.getRepository(InventoryCheckItem).save(checkItem)
583
+
584
+ let worksheetDetail = await this.trxMgr.getRepository(WorksheetDetail).findOne({
585
+ where: { targetInventoryCheckItem: checkItem.id, type: WORKSHEET_TYPE.CYCLE_COUNT }
586
+ })
587
+
588
+ if (worksheetDetail) {
589
+ worksheetDetail.status = worksheetDetailStatus
590
+ worksheetDetail.updater = this.user
591
+ await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
592
+ }
593
+ }
594
+
595
+ if (totalInspectedQty > 0 && totalInspectedUomValue > 0 && inventoryCheckItems.length > 0) {
596
+ const lastItem = inventoryCheckItems[inventoryCheckItems.length - 1]
597
+ lastItem.inspectedQty = (lastItem.inspectedQty ?? 0) + totalInspectedQty
598
+ lastItem.inspectedUomValue = (lastItem.inspectedUomValue ?? 0) + totalInspectedUomValue
599
+ await this.trxMgr.getRepository(InventoryCheckItem).save(lastItem)
288
600
  }
289
601
  }
290
602
 
291
603
  async undoInspection(worksheetDetailName: string): Promise<void> {
292
604
  let worksheetDetail: WorksheetDetail = await this.findWorksheetDetail(
293
605
  { domain: this.domain, name: worksheetDetailName, status: Not(Equal(WORKSHEET_STATUS.EXECUTING)) },
294
- ['targetInventory', 'targetInventory.inventory']
606
+ ['targetInventoryCheckItem', 'targetInventoryCheckItem.inventory']
295
607
  )
296
608
 
297
- let targetInventory: OrderInventory = worksheetDetail.targetInventory
298
- targetInventory.inspectedBatchNo = null
299
- targetInventory.inspectedQty = null
300
- targetInventory.inspectedWeight = null
301
- targetInventory.inspectedUomValue = null
302
- targetInventory.inspectedLocation = null
303
- targetInventory.status =
304
- targetInventory.status === ORDER_INVENTORY_STATUS.RELOCATED
305
- ? ORDER_INVENTORY_STATUS.MISSING
306
- : ORDER_INVENTORY_STATUS.INSPECTING
307
- targetInventory.updater = this.user
308
- await this.updateOrderTargets([targetInventory])
609
+ let targetInventoryCheckItem: InventoryCheckItem = worksheetDetail.targetInventoryCheckItem
610
+ targetInventoryCheckItem.inspectedBatchNo = null
611
+ targetInventoryCheckItem.inspectedQty = null
612
+ targetInventoryCheckItem.inspectedWeight = null
613
+ targetInventoryCheckItem.inspectedUomValue = null
614
+ targetInventoryCheckItem.inspectedLocation = null
615
+ targetInventoryCheckItem.status =
616
+ targetInventoryCheckItem.status === INVENTORY_CHECK_ITEM_STATUS.RELOCATED
617
+ ? INVENTORY_CHECK_ITEM_STATUS.MISSING
618
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTING
619
+ targetInventoryCheckItem.updater = this.user
620
+ await this.updateOrderTargets([targetInventoryCheckItem])
309
621
 
310
622
  worksheetDetail.status =
311
- targetInventory.status === ORDER_INVENTORY_STATUS.MISSING
623
+ targetInventoryCheckItem.status === INVENTORY_CHECK_ITEM_STATUS.MISSING
312
624
  ? WORKSHEET_STATUS.NOT_TALLY
313
625
  : WORKSHEET_STATUS.EXECUTING
314
626
  worksheetDetail.updater = this.user
315
627
  await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
628
+ }
629
+
630
+ async undoInspectionCarton(
631
+ productDetailId: string,
632
+ cartonId: string,
633
+ batchId: string,
634
+ locationId: string
635
+ ): Promise<void> {
636
+ let qb: SelectQueryBuilder<Inventory> = this.trxMgr.getRepository(Inventory).createQueryBuilder('inv')
637
+ qb.where('inv.product_detail_id = :productDetailId', { productDetailId })
638
+ .andWhere('inv.carton_id = :cartonId', { cartonId })
639
+ .andWhere('inv.batch_id = :batchId', { batchId })
640
+ .andWhere('inv.location_id = :locationId', { locationId })
641
+
642
+ const inventories: Inventory[] = await qb.getMany()
643
+
644
+ const inventoryCheckItems: InventoryCheckItem[] = await Promise.all(
645
+ inventories.map(inventory =>
646
+ this.trxMgr.getRepository(InventoryCheckItem).findOne({ where: { inventory: inventory.id } })
647
+ )
648
+ )
649
+
650
+ for (const checkItem of inventoryCheckItems) {
651
+ checkItem.inspectedBatchNo = null
652
+ checkItem.inspectedQty = null
653
+ checkItem.inspectedWeight = null
654
+ checkItem.inspectedUomValue = null
655
+ checkItem.inspectedLocation = null
656
+ checkItem.status = INVENTORY_CHECK_ITEM_STATUS.INSPECTING
657
+ checkItem.updater = this.user
658
+
659
+ let worksheetDetail = await this.trxMgr.getRepository(WorksheetDetail).findOne({
660
+ where: { targetInventoryCheckItem: checkItem.id, type: WORKSHEET_TYPE.CYCLE_COUNT },
661
+ relations: ['targetInventoryCheckItem']
662
+ })
316
663
 
317
- const inventory: Inventory = targetInventory.inventory
318
- inventory.lockedQty = inventory.qty
319
- inventory.lockedWeight = inventory.weight
320
- inventory.lockedUomValue = inventory.uomValue
321
- inventory.updater = this.user
322
- await this.trxMgr.getRepository(Inventory).save(inventory, { chunk: 500 })
664
+ if (worksheetDetail) {
665
+ worksheetDetail.status = WORKSHEET_STATUS.EXECUTING
666
+ worksheetDetail.updater = this.user
667
+ await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
668
+ }
669
+ await this.trxMgr.getRepository(InventoryCheckItem).save(checkItem)
670
+ }
323
671
  }
324
672
 
325
- async checkMissingPallet(worksheetDetailName: string): Promise<void> {
673
+ async checkMissingInventory(worksheetDetailName: string): Promise<void> {
326
674
  let worksheetDetail: WorksheetDetail = await this.findExecutableWorksheetDetailByName(
327
675
  worksheetDetailName,
328
676
  WORKSHEET_TYPE.CYCLE_COUNT,
329
- ['targetInventory']
677
+ ['targetInventoryCheckItem']
330
678
  )
331
679
 
332
- let targetInventory: OrderInventory = worksheetDetail.targetInventory
680
+ let targetInventoryCheckItem: InventoryCheckItem = worksheetDetail.targetInventoryCheckItem
333
681
  worksheetDetail.status = WORKSHEET_STATUS.NOT_TALLY
334
682
  worksheetDetail.updater = this.user
335
683
  await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
336
684
 
337
- targetInventory.status = ORDER_INVENTORY_STATUS.MISSING
338
- targetInventory.updater = this.user
339
- await this.updateOrderTargets([targetInventory])
685
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.MISSING
686
+ targetInventoryCheckItem.updater = this.user
687
+ await this.updateOrderTargets([targetInventoryCheckItem])
340
688
  }
341
689
 
342
- async addExtraPallet(
690
+ async checkMissingInventoryCarton(
691
+ productDetailId: string,
692
+ cartonId: string,
693
+ batchId: string,
694
+ locationId: string
695
+ ): Promise<void> {
696
+ let qb: SelectQueryBuilder<Inventory> = this.trxMgr.getRepository(Inventory).createQueryBuilder('inv')
697
+ qb.where('inv.product_detail_id = :productDetailId', { productDetailId })
698
+ .andWhere('inv.carton_id = :cartonId', { cartonId })
699
+ .andWhere('inv.batch_id = :batchId', { batchId })
700
+ .andWhere('inv.location_id = :locationId', { locationId })
701
+
702
+ const inventories: Inventory[] = await qb.getMany()
703
+
704
+ const inventoryCheckItems: InventoryCheckItem[] = await Promise.all(
705
+ inventories.map(inventory =>
706
+ this.trxMgr.getRepository(InventoryCheckItem).findOne({ where: { inventory: inventory.id } })
707
+ )
708
+ )
709
+
710
+ for (const checkItem of inventoryCheckItems) {
711
+ checkItem.inspectedBatchNo = null
712
+ checkItem.inspectedQty = null
713
+ checkItem.inspectedWeight = null
714
+ checkItem.inspectedUomValue = null
715
+ checkItem.inspectedLocation = null
716
+ checkItem.status = INVENTORY_CHECK_ITEM_STATUS.MISSING
717
+ checkItem.updater = this.user
718
+
719
+ let worksheetDetail = await this.trxMgr.getRepository(WorksheetDetail).findOne({
720
+ where: { targetInventoryCheckItem: checkItem.id, type: WORKSHEET_TYPE.CYCLE_COUNT },
721
+ relations: ['targetInventoryCheckItem']
722
+ })
723
+
724
+ if (worksheetDetail) {
725
+ worksheetDetail.status = WORKSHEET_STATUS.NOT_TALLY
726
+ worksheetDetail.updater = this.user
727
+ await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
728
+ }
729
+ await this.trxMgr.getRepository(InventoryCheckItem).save(checkItem)
730
+ }
731
+ }
732
+
733
+ async addExtraInventory(
343
734
  cycleCountNo: string,
344
- palletId: string,
735
+ inspectedLocation: string,
736
+ productDetailId: string,
737
+ productId: string,
345
738
  inspectedBatchNo: string,
346
739
  inspectedQty: number,
347
740
  inspectedUomValue: number,
348
- locationName: string
741
+ countNo: number
349
742
  ): Promise<void> {
743
+ let today = new Date(),
744
+ year = today.getFullYear(),
745
+ month = today.getMonth(),
746
+ date = today.getDate()
747
+
350
748
  const inventoryCheck: InventoryCheck = await this.findRefOrder(
351
749
  InventoryCheck,
352
750
  {
353
751
  name: cycleCountNo,
354
752
  status: ORDER_STATUS.INSPECTING
355
753
  },
356
- ['bizplace']
754
+ ['bizplace', 'bizplace.domain']
357
755
  )
358
756
 
359
- const bizplace: Bizplace = inventoryCheck.bizplace
360
- const qb: SelectQueryBuilder<Inventory> = this.trxMgr.getRepository(Inventory).createQueryBuilder('INV')
361
- let inventory: Inventory = await qb
362
- .where('INV.domain = :domainId', { domainId: this.domain.id })
363
- .andWhere('INV.bizplace = :bizplaceId', { bizplaceId: bizplace.id })
364
- .andWhere('INV.palletId = :palletId', { palletId })
365
- .andWhere('INV.status = :status', { status: INVENTORY_STATUS.STORED })
366
- .andWhere(
367
- new Brackets(qb => {
368
- qb.where('INV.lockedQty ISNULL')
369
- qb.orWhere('INV.lockedQty = 0')
370
- })
371
- )
372
- .getOne()
373
- if (!inventory) throw new Error('Failed to find inventory')
757
+ const bizplace: Bizplace = await this.trxMgr.getRepository(Bizplace).findOne({
758
+ where: { id: inventoryCheck.bizplace.id },
759
+ relations: ['domain']
760
+ })
374
761
 
375
762
  const worksheet: Worksheet = await this.trxMgr.getRepository(Worksheet).findOne({
376
763
  where: {
@@ -381,31 +768,104 @@ export class CycleCountWorksheetController extends WorksheetController {
381
768
  }
382
769
  })
383
770
  const location: Location = await this.trxMgr.getRepository(Location).findOne({
384
- where: { domain: this.domain, name: locationName }
771
+ where: { domain: this.domain, name: inspectedLocation },
772
+ relations: ['warehouse']
773
+ })
774
+
775
+ const productDetail: ProductDetail = await this.trxMgr.getRepository(ProductDetail).findOne({
776
+ where: { id: productDetailId }
777
+ })
778
+
779
+ const product: Product = await this.trxMgr.getRepository(Product).findOne({
780
+ where: { id: productId }
781
+ })
782
+
783
+ const total = await this.trxMgr.getRepository(Inventory).count({
784
+ createdAt: MoreThan(new Date(year, month, date))
785
+ })
786
+
787
+ const yy = String(year).substr(String(year).length - 2)
788
+ const mm = String(month + 1).padStart(2, '0')
789
+ const dd = String(date).padStart(2, '0')
790
+
791
+ const dateStr = yy + mm + dd
792
+
793
+ let newInventory: Partial<Inventory> = new Inventory()
794
+ newInventory.bizplace = bizplace
795
+ newInventory.domain = this.domain
796
+ newInventory.name = InventoryNoGenerator.inventoryName()
797
+ newInventory.palletId = await generateId({
798
+ domain: this.domain,
799
+ type: 'adjustment_pallet_id',
800
+ seed: {
801
+ batchId: inspectedBatchNo,
802
+ date: dateStr
803
+ }
385
804
  })
386
805
 
387
- let targetInventory: OrderInventory = new OrderInventory()
388
- targetInventory.domain = this.domain
389
- targetInventory.bizplace = bizplace
390
- targetInventory.status = ORDER_INVENTORY_STATUS.ADDED
391
- targetInventory.name = OrderNoGenerator.orderInventory()
392
- targetInventory.inventoryCheck = inventoryCheck
393
- targetInventory.inventory = inventory
394
- targetInventory.inspectedBatchNo = inspectedBatchNo
395
- targetInventory.inspectedQty = inspectedQty
396
- targetInventory.inspectedWeight = 0
397
- targetInventory.inspectedUomValue = inspectedUomValue
398
- targetInventory.inspectedLocation = location
399
- targetInventory.creator = this.user
400
- targetInventory.updater = this.user
401
- await this.trxMgr.getRepository(OrderInventory).save(targetInventory)
806
+ const warehouseCartonSetting: Setting = await this.trxMgr.getRepository(Setting).findOne({
807
+ where: { domain: this.domain, category: 'id-rule', name: 'enable-carton-label', value: true }
808
+ })
809
+
810
+ const partnerCartonSetting: PartnerSetting = await this.trxMgr.getRepository(PartnerSetting).findOne({
811
+ where: {
812
+ setting: warehouseCartonSetting,
813
+ domain: this.domain,
814
+ partnerDomain: inventoryCheck.bizplace?.domain,
815
+ value: true
816
+ }
817
+ })
818
+
819
+ if (partnerCartonSetting?.value || warehouseCartonSetting?.value) {
820
+ let cartonId = await generateId({
821
+ domain: this.domain,
822
+ type: 'adjustment_carton_id',
823
+ seed: { date: dateStr }
824
+ })
825
+ if (!cartonId) throw new Error('No adjustment carton id setting rule found')
826
+
827
+ newInventory.cartonId = cartonId
828
+ }
829
+ newInventory.batchId = inspectedBatchNo
830
+ newInventory.product = product
831
+ newInventory.productDetail = productDetail
832
+ newInventory.packingType = productDetail.packingType
833
+ newInventory.packingSize = productDetail.packingSize
834
+ newInventory.uom = productDetail.uom
835
+ newInventory.qty = inspectedQty
836
+ newInventory.uomValue = inspectedUomValue
837
+ newInventory.location = location
838
+ newInventory.warehouse = location.warehouse
839
+ newInventory.zone = location.zone
840
+ newInventory.status = INVENTORY_STATUS.CC_ADDED
841
+ newInventory.creator = this.user
842
+ newInventory.updater = this.user
843
+ await this.trxMgr.getRepository(Inventory).save(newInventory)
844
+
845
+ let targetInventoryCheckItem: InventoryCheckItem = new InventoryCheckItem()
846
+ targetInventoryCheckItem.domain = this.domain
847
+ targetInventoryCheckItem.bizplace = bizplace
848
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.ADDED
849
+ targetInventoryCheckItem.name = OrderNoGenerator.inventoryCheckItem()
850
+ targetInventoryCheckItem.inventoryCheck = inventoryCheck
851
+ targetInventoryCheckItem.inventory = newInventory
852
+ targetInventoryCheckItem.inspectedBatchNo = inspectedBatchNo
853
+ targetInventoryCheckItem.inspectedQty = inspectedQty
854
+ targetInventoryCheckItem.inspectedUomValue = inspectedUomValue
855
+ targetInventoryCheckItem.inspectedLocation = location
856
+ targetInventoryCheckItem.productDetail = productDetail
857
+ targetInventoryCheckItem.product = product
858
+ targetInventoryCheckItem.countNo = countNo
859
+ targetInventoryCheckItem.creator = this.user
860
+ targetInventoryCheckItem.updater = this.user
861
+ await this.trxMgr.getRepository(InventoryCheckItem).save(targetInventoryCheckItem)
402
862
 
403
863
  let worksheetDetail: WorksheetDetail = new WorksheetDetail()
404
864
  worksheetDetail.domain = this.domain
405
865
  worksheetDetail.bizplace = bizplace
406
866
  worksheetDetail.worksheet = worksheet
407
867
  worksheetDetail.name = WorksheetNoGenerator.cycleCountDetail()
408
- worksheetDetail.targetInventory = targetInventory
868
+ worksheetDetail.targetInventoryCheckItem = targetInventoryCheckItem
409
869
  worksheetDetail.type = WORKSHEET_TYPE.CYCLE_COUNT
410
870
  worksheetDetail.status = WORKSHEET_STATUS.NOT_TALLY
411
871
  worksheetDetail.creator = this.user
@@ -413,7 +873,7 @@ export class CycleCountWorksheetController extends WorksheetController {
413
873
  await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
414
874
  }
415
875
 
416
- async relocatePallet(
876
+ async relocateInventory(
417
877
  worksheetDetailName: string,
418
878
  inspectedBatchNo: string,
419
879
  inspectedQty: number,
@@ -422,13 +882,17 @@ export class CycleCountWorksheetController extends WorksheetController {
422
882
  ): Promise<void> {
423
883
  let worksheetDetail: WorksheetDetail = await this.trxMgr.getRepository(WorksheetDetail).findOne({
424
884
  where: { domain: this.domain, name: worksheetDetailName, type: WORKSHEET_TYPE.CYCLE_COUNT },
425
- relations: ['targetInventory', 'targetInventory.inventory', 'targetInventory.inventory.location']
885
+ relations: [
886
+ 'targetInventoryCheckItem',
887
+ 'targetInventoryCheckItem.inventory',
888
+ 'targetInventoryCheckItem.inventory.location'
889
+ ]
426
890
  })
427
891
 
428
892
  if (!worksheetDetail) throw new Error('Failed to find worksheet detail')
429
893
 
430
- let targetInventory: OrderInventory = worksheetDetail.targetInventory
431
- const location: Location = targetInventory?.inventory?.location
894
+ let targetInventoryCheckItem: InventoryCheckItem = worksheetDetail.targetInventoryCheckItem
895
+ const location: Location = targetInventoryCheckItem?.inventory?.location
432
896
  if (location.name === inspectedLocationName) throw new Error(`You can't relocate at same location`)
433
897
 
434
898
  const inspectedLocation: Location = await this.trxMgr.getRepository(Location).findOne({
@@ -439,14 +903,156 @@ export class CycleCountWorksheetController extends WorksheetController {
439
903
  worksheetDetail.updater = this.user
440
904
  await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
441
905
 
442
- targetInventory.inspectedLocation = inspectedLocation
443
- targetInventory.inspectedBatchNo = inspectedBatchNo
444
- targetInventory.inspectedQty = inspectedQty
445
- targetInventory.inspectedWeight = 0
446
- targetInventory.inspectedUomValue = inspectedUomValue
447
- targetInventory.status = ORDER_INVENTORY_STATUS.RELOCATED
448
- targetInventory.updater = this.user
449
- await this.updateOrderTargets([targetInventory])
906
+ targetInventoryCheckItem.inspectedLocation = inspectedLocation
907
+ targetInventoryCheckItem.inspectedBatchNo = inspectedBatchNo
908
+ targetInventoryCheckItem.inspectedQty = inspectedQty
909
+ targetInventoryCheckItem.inspectedWeight = 0
910
+ targetInventoryCheckItem.inspectedUomValue = inspectedUomValue
911
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.RELOCATED
912
+ targetInventoryCheckItem.updater = this.user
913
+ await this.updateOrderTargets([targetInventoryCheckItem])
914
+ }
915
+
916
+ async relocateInventoryCarton(
917
+ productDetailId: string,
918
+ cartonId: string,
919
+ batchId: string,
920
+ locationId: string,
921
+ inspectedBatchNo: string,
922
+ inspectedQty: number,
923
+ inspectedUomValue: number,
924
+ inspectedLocationName: string
925
+ ): Promise<void> {
926
+ const productDetail: ProductDetail = await this.trxMgr.getRepository(ProductDetail).findOne({
927
+ where: { id: productDetailId },
928
+ relations: ['product']
929
+ })
930
+
931
+ let qb: SelectQueryBuilder<Inventory> = this.trxMgr
932
+ .getRepository(Inventory)
933
+ .createQueryBuilder('inv')
934
+ .where('inv.product_detail_id = :productDetailId', { productDetailId })
935
+ .andWhere('inv.carton_id = :cartonId', { cartonId })
936
+ .andWhere('inv.batch_id = :batchId', { batchId })
937
+ .andWhere('inv.location_id = :locationId', { locationId })
938
+
939
+ let orderByField = 'created_at'
940
+ let orderByDirection: 'ASC' | 'DESC' = 'ASC'
941
+
942
+ switch (productDetail.product.pickingStrategy) {
943
+ case 'LIFO':
944
+ orderByDirection = 'DESC'
945
+ break
946
+ case 'FEFO':
947
+ orderByField = 'expiration_date'
948
+ break
949
+ case 'FMFO':
950
+ orderByField = 'manufacture_date'
951
+ break
952
+ }
953
+ qb.addOrderBy(`"inv"."${orderByField}"`, orderByDirection)
954
+
955
+ if (['FEFO', 'FMFO'].includes(productDetail.product.pickingStrategy)) {
956
+ qb.addOrderBy('"inv"."created_at"', 'ASC')
957
+ }
958
+
959
+ const inventories: Inventory[] = await qb.getMany()
960
+ if (inventories.length === 0) {
961
+ throw new Error('No inventories found.')
962
+ }
963
+
964
+ const location: Location = await this.trxMgr.getRepository(Location).findOne({
965
+ where: { id: locationId }
966
+ })
967
+ if (location.name === inspectedLocationName) {
968
+ throw new Error(`You can't relocate to the same location.`)
969
+ }
970
+
971
+ const inspectedLocation: Location = await this.trxMgr.getRepository(Location).findOne({
972
+ where: { name: inspectedLocationName, domain: this.domain }
973
+ })
974
+ if (!inspectedLocation) {
975
+ throw new Error(`Inspected location "${inspectedLocationName}" not found.`)
976
+ }
977
+
978
+ const inventoryIds = inventories.map(inv => inv.id)
979
+ const inventoryCheckItems: InventoryCheckItem[] = await this.trxMgr
980
+ .getRepository(InventoryCheckItem)
981
+ .createQueryBuilder('inventoryCheckItem')
982
+ .leftJoinAndSelect('inventoryCheckItem.inventory', 'inventory')
983
+ .where('inventory.id IN (:...ids)', { ids: inventoryIds })
984
+ .andWhere('inventoryCheckItem.status = :status', { status: 'INSPECTING' })
985
+ .getMany()
986
+
987
+ if (inventoryCheckItems.length === 0) {
988
+ throw new Error('No inventory check items found.')
989
+ }
990
+
991
+ const totalOriginQty = inventoryCheckItems.reduce((total, item) => total + (item.originQty || 0), 0)
992
+ const totalOriginUomValue = inventoryCheckItems.reduce((total, item) => total + (item.originUomValue || 0), 0)
993
+
994
+ const hasQtyDiscrepancy = totalOriginQty !== inspectedQty
995
+ const hasUomValueDiscrepancy = totalOriginUomValue !== inspectedUomValue
996
+
997
+ const hasBatchNoDiscrepancy = inventoryCheckItems.some(checkItem => checkItem.originBatchNo !== inspectedBatchNo)
998
+
999
+ const hasLocationDiscrepancy = inventoryCheckItems.some(
1000
+ checkItem => checkItem.originLocationId !== inspectedLocation.id
1001
+ )
1002
+
1003
+ const hasDiscrepancies =
1004
+ hasQtyDiscrepancy || hasUomValueDiscrepancy || hasBatchNoDiscrepancy || hasLocationDiscrepancy
1005
+
1006
+ let inventoryCheckItemStatus, worksheetDetailStatus
1007
+
1008
+ if (inventoryCheckItems.length > 1) {
1009
+ inventoryCheckItemStatus = hasDiscrepancies
1010
+ ? INVENTORY_CHECK_ITEM_STATUS.RELOCATED
1011
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTED
1012
+ worksheetDetailStatus = hasDiscrepancies ? WORKSHEET_STATUS.NOT_TALLY : WORKSHEET_STATUS.DONE
1013
+ } else {
1014
+ inventoryCheckItemStatus = hasDiscrepancies
1015
+ ? INVENTORY_CHECK_ITEM_STATUS.RELOCATED
1016
+ : INVENTORY_CHECK_ITEM_STATUS.INSPECTED
1017
+ worksheetDetailStatus = hasDiscrepancies ? WORKSHEET_STATUS.NOT_TALLY : WORKSHEET_STATUS.DONE
1018
+ }
1019
+
1020
+ let totalInspectedQty = inspectedQty
1021
+ let totalInspectedUomValue = inspectedUomValue
1022
+
1023
+ for (const checkItem of inventoryCheckItems) {
1024
+ const qtyToInspect = totalInspectedQty > 0 ? Math.min(totalInspectedQty, checkItem.originQty) : 0
1025
+ const uomValueToInspect =
1026
+ totalInspectedUomValue > 0 ? Math.min(totalInspectedUomValue, checkItem.originUomValue) : 0
1027
+ checkItem.inspectedQty = (checkItem.inspectedQty ?? 0) + qtyToInspect
1028
+ totalInspectedQty -= qtyToInspect
1029
+ checkItem.inspectedUomValue = (checkItem.inspectedUomValue ?? 0) + uomValueToInspect
1030
+ totalInspectedUomValue -= uomValueToInspect
1031
+
1032
+ checkItem.inspectedBatchNo = inspectedBatchNo
1033
+ checkItem.inspectedLocation = inspectedLocation
1034
+ checkItem.status = inventoryCheckItemStatus
1035
+ checkItem.updater = this.user
1036
+
1037
+ await this.trxMgr.getRepository(InventoryCheckItem).save(checkItem)
1038
+
1039
+ let worksheetDetail = await this.trxMgr.getRepository(WorksheetDetail).findOne({
1040
+ where: { targetInventoryCheckItem: checkItem.id, type: WORKSHEET_TYPE.CYCLE_COUNT }
1041
+ })
1042
+
1043
+ if (worksheetDetail) {
1044
+ worksheetDetail.status = worksheetDetailStatus
1045
+ worksheetDetail.updater = this.user
1046
+ await this.trxMgr.getRepository(WorksheetDetail).save(worksheetDetail)
1047
+ }
1048
+ }
1049
+
1050
+ if (totalInspectedQty > 0 && totalInspectedUomValue > 0 && inventoryCheckItems.length > 0) {
1051
+ const lastItem = inventoryCheckItems[inventoryCheckItems.length - 1]
1052
+ lastItem.inspectedQty = (lastItem.inspectedQty ?? 0) + totalInspectedQty
1053
+ lastItem.inspectedUomValue = (lastItem.inspectedUomValue ?? 0) + totalInspectedUomValue
1054
+ await this.trxMgr.getRepository(InventoryCheckItem).save(lastItem)
1055
+ }
450
1056
  }
451
1057
 
452
1058
  async completeCycleCount(inventoryCheckNo: string): Promise<Worksheet> {
@@ -462,51 +1068,53 @@ export class CycleCountWorksheetController extends WorksheetController {
462
1068
  type: In([WORKSHEET_TYPE.CYCLE_COUNT, WORKSHEET_TYPE.CYCLE_COUNT_RECHECK]),
463
1069
  inventoryCheck
464
1070
  },
465
- relations: ['worksheetDetails', 'worksheetDetails.targetInventory', 'worksheetDetails.targetInventory.inventory']
1071
+ relations: [
1072
+ 'worksheetDetails',
1073
+ 'worksheetDetails.targetInventoryCheckItem',
1074
+ 'worksheetDetails.targetInventoryCheckItem.inventory'
1075
+ ]
466
1076
  })
467
1077
  this.checkRecordValidity(worksheet, { status: WORKSHEET_STATUS.EXECUTING })
468
1078
 
469
1079
  const worksheetDetails: WorksheetDetail[] = worksheet.worksheetDetails
470
- const targetInventories: OrderInventory[] = worksheetDetails.map((wsd: WorksheetDetail) => wsd.targetInventory)
1080
+ const targetInventoryCheckItems: InventoryCheckItem[] = worksheetDetails.map(
1081
+ (wsd: WorksheetDetail) => wsd.targetInventoryCheckItem
1082
+ )
471
1083
 
472
1084
  const {
473
- tallyTargetInventories,
474
- notTallyTargetInventories
1085
+ tallyTargetInventoryCheckItems,
1086
+ notTallyTargetInventoryCheckItems
475
1087
  }: {
476
- tallyTargetInventories: OrderInventory[]
477
- notTallyTargetInventories: OrderInventory[]
478
- } = targetInventories.reduce(
479
- (result, targetInventory: OrderInventory) => {
480
- if (targetInventory.status !== ORDER_INVENTORY_STATUS.INSPECTED) {
481
- result.notTallyTargetInventories.push(targetInventory)
1088
+ tallyTargetInventoryCheckItems: InventoryCheckItem[]
1089
+ notTallyTargetInventoryCheckItems: InventoryCheckItem[]
1090
+ } = targetInventoryCheckItems.reduce(
1091
+ (result, targetInventoryCheckItem: InventoryCheckItem) => {
1092
+ if (targetInventoryCheckItem.status !== INVENTORY_CHECK_ITEM_STATUS.INSPECTED) {
1093
+ result.notTallyTargetInventoryCheckItems.push(targetInventoryCheckItem)
482
1094
  } else {
483
- result.tallyTargetInventories.push(targetInventory)
1095
+ result.tallyTargetInventoryCheckItems.push(targetInventoryCheckItem)
484
1096
  }
485
1097
 
486
1098
  return result
487
1099
  },
488
1100
  {
489
- tallyTargetInventories: [],
490
- notTallyTargetInventories: []
1101
+ tallyTargetInventoryCheckItems: [],
1102
+ notTallyTargetInventoryCheckItems: []
491
1103
  }
492
1104
  )
493
1105
 
494
- const tallyInventories: Inventory[] = tallyTargetInventories.map(targetInventory => targetInventory.inventory)
495
- tallyTargetInventories.forEach((targetInventory: OrderInventory) => {
496
- targetInventory.status = ORDER_INVENTORY_STATUS.TERMINATED
497
- targetInventory.updater = this.user
1106
+ const tallyInventories: Inventory[] = tallyTargetInventoryCheckItems.map(
1107
+ targetInventoryCheckItem => targetInventoryCheckItem.inventory
1108
+ )
1109
+ tallyTargetInventoryCheckItems.forEach((targetInventoryCheckItem: InventoryCheckItem) => {
1110
+ targetInventoryCheckItem.status = INVENTORY_CHECK_ITEM_STATUS.TERMINATED
1111
+ targetInventoryCheckItem.updater = this.user
498
1112
  })
499
- await this.trxMgr.getRepository(OrderInventory).save(tallyTargetInventories, { chunk: 500 })
1113
+ await this.trxMgr.getRepository(InventoryCheckItem).save(tallyTargetInventoryCheckItems, { chunk: 500 })
500
1114
 
501
- tallyInventories.forEach((inventory: Inventory) => {
502
- inventory.lockedQty = 0
503
- inventory.lockedWeight = 0
504
- inventory.lockedUomValue = 0
505
- inventory.updater = this.user
506
- })
507
1115
  await this.trxMgr.getRepository(Inventory).save(tallyInventories, { chunk: 500 })
508
1116
 
509
- if (notTallyTargetInventories.length) {
1117
+ if (notTallyTargetInventoryCheckItems.length) {
510
1118
  worksheet.status = WORKSHEET_STATUS.NOT_TALLY
511
1119
  inventoryCheck.status = ORDER_STATUS.PENDING_REVIEW
512
1120
  } else {