@things-factory/warehouse-base 8.0.3 → 8.0.6

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 (88) hide show
  1. package/dist-server/tsconfig.tsbuildinfo +1 -1
  2. package/package.json +8 -8
  3. package/server/constants/index.ts +0 -5
  4. package/server/constants/inventory.ts +0 -67
  5. package/server/constants/location.ts +0 -14
  6. package/server/constants/pallet.ts +0 -10
  7. package/server/constants/rule-type.ts +0 -5
  8. package/server/constants/tote.ts +0 -5
  9. package/server/controllers/ecommerce/ecommerce-controller.ts +0 -108
  10. package/server/controllers/ecommerce/index.ts +0 -2
  11. package/server/controllers/ecommerce/sellercraft-controller.ts +0 -100
  12. package/server/controllers/index.ts +0 -2
  13. package/server/controllers/warehouse-controller.ts +0 -181
  14. package/server/index.ts +0 -9
  15. package/server/middlewares/index.ts +0 -0
  16. package/server/migrations/index.ts +0 -9
  17. package/server/service/index.ts +0 -80
  18. package/server/service/inventory/index.ts +0 -6
  19. package/server/service/inventory/inventory-mutation.ts +0 -530
  20. package/server/service/inventory/inventory-query.ts +0 -1263
  21. package/server/service/inventory/inventory-types.ts +0 -367
  22. package/server/service/inventory/inventory.ts +0 -408
  23. package/server/service/inventory-change/index.ts +0 -6
  24. package/server/service/inventory-change/inventory-change-mutation.ts +0 -969
  25. package/server/service/inventory-change/inventory-change-query.ts +0 -93
  26. package/server/service/inventory-change/inventory-change-types.ts +0 -36
  27. package/server/service/inventory-change/inventory-change.ts +0 -164
  28. package/server/service/inventory-history/index.ts +0 -6
  29. package/server/service/inventory-history/inventory-history-mutation.ts +0 -116
  30. package/server/service/inventory-history/inventory-history-query.ts +0 -1845
  31. package/server/service/inventory-history/inventory-history-types.ts +0 -444
  32. package/server/service/inventory-history/inventory-history.ts +0 -203
  33. package/server/service/inventory-item/index.ts +0 -6
  34. package/server/service/inventory-item/inventory-item-mutation.ts +0 -217
  35. package/server/service/inventory-item/inventory-item-query.ts +0 -226
  36. package/server/service/inventory-item/inventory-item-type.ts +0 -74
  37. package/server/service/inventory-item/inventory-item.ts +0 -105
  38. package/server/service/inventory-item-change/index.ts +0 -6
  39. package/server/service/inventory-item-change/inventory-item-change-mutation.ts +0 -119
  40. package/server/service/inventory-item-change/inventory-item-change-query.ts +0 -47
  41. package/server/service/inventory-item-change/inventory-item-change-type.ts +0 -68
  42. package/server/service/inventory-item-change/inventory-item-change.ts +0 -92
  43. package/server/service/inventory-product/index.ts +0 -6
  44. package/server/service/inventory-product/inventory-product-mutation.ts +0 -116
  45. package/server/service/inventory-product/inventory-product-query.ts +0 -47
  46. package/server/service/inventory-product/inventory-product-type.ts +0 -59
  47. package/server/service/inventory-product/inventory-product.ts +0 -88
  48. package/server/service/location/index.ts +0 -6
  49. package/server/service/location/location-mutation.ts +0 -134
  50. package/server/service/location/location-query.ts +0 -244
  51. package/server/service/location/location-types.ts +0 -173
  52. package/server/service/location/location.ts +0 -121
  53. package/server/service/movement/index.ts +0 -6
  54. package/server/service/movement/movement-mutation.ts +0 -60
  55. package/server/service/movement/movement-query.ts +0 -263
  56. package/server/service/movement/movement-types.ts +0 -74
  57. package/server/service/movement/movement.ts +0 -81
  58. package/server/service/pallet/index.ts +0 -6
  59. package/server/service/pallet/pallet-mutation.ts +0 -242
  60. package/server/service/pallet/pallet-query.ts +0 -106
  61. package/server/service/pallet/pallet-types.ts +0 -80
  62. package/server/service/pallet/pallet.ts +0 -92
  63. package/server/service/pallet-count/index.ts +0 -6
  64. package/server/service/pallet-count/pallet-count-mutation.ts +0 -151
  65. package/server/service/pallet-count/pallet-count-query.ts +0 -45
  66. package/server/service/pallet-count/pallet-count-types.ts +0 -36
  67. package/server/service/pallet-count/pallet-count.ts +0 -70
  68. package/server/service/pallet-history/index.ts +0 -6
  69. package/server/service/pallet-history/pallet-history-mutation.ts +0 -114
  70. package/server/service/pallet-history/pallet-history-query.ts +0 -48
  71. package/server/service/pallet-history/pallet-history-types.ts +0 -36
  72. package/server/service/pallet-history/pallet-history.ts +0 -89
  73. package/server/service/reduced-inventory-history/index.ts +0 -3
  74. package/server/service/reduced-inventory-history/reduced-inventory-history.ts +0 -92
  75. package/server/service/tote/index.ts +0 -6
  76. package/server/service/tote/tote-mutation.ts +0 -201
  77. package/server/service/tote/tote-query.ts +0 -106
  78. package/server/service/tote/tote-types.ts +0 -44
  79. package/server/service/tote/tote.ts +0 -77
  80. package/server/service/warehouse/index.ts +0 -6
  81. package/server/service/warehouse/warehouse-mutation.ts +0 -152
  82. package/server/service/warehouse/warehouse-query.ts +0 -58
  83. package/server/service/warehouse/warehouse-types.ts +0 -50
  84. package/server/service/warehouse/warehouse.ts +0 -95
  85. package/server/utils/datetime-util.ts +0 -54
  86. package/server/utils/index.ts +0 -3
  87. package/server/utils/inventory-no-generator.ts +0 -15
  88. package/server/utils/inventory-util.ts +0 -490
@@ -1,969 +0,0 @@
1
- import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
2
- import { Equal, In, MoreThan, Not } from 'typeorm'
3
-
4
- import { Bizplace } from '@things-factory/biz-base'
5
- import { generateId } from '@things-factory/id-rule-base'
6
- import { MarketplaceStore } from '@things-factory/integration-marketplace'
7
- import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
8
- import { Product } from '@things-factory/product-base'
9
- import { PartnerSetting, Setting } from '@things-factory/setting-base'
10
- import { Domain } from '@things-factory/shell'
11
-
12
- import { INVENTORY_ITEM_CHANGE_TYPE, INVENTORY_ITEM_SOURCE, INVENTORY_STATUS, LOCATION_STATUS } from '../../constants'
13
- import { EcommerceController, SellercraftController } from '../../controllers'
14
- import { InventoryNoGenerator } from '../../utils'
15
- import { InventoryHistory } from '../inventory-history/inventory-history'
16
- import { InventoryItemChange } from '../inventory-item-change/inventory-item-change'
17
- import { InventoryItem } from '../inventory-item/inventory-item'
18
- import { checkExistingSerialNumber } from '../inventory-item/inventory-item-query'
19
- import { Inventory } from '../inventory/inventory'
20
- import { InventoryPatch } from '../inventory/inventory-types'
21
- import { Location } from '../location/location'
22
- import { InventoryChange } from './inventory-change'
23
- import { InventoryChangeList, InventoryChangePatch, NewInventoryChange } from './inventory-change-types'
24
-
25
- @Resolver(InventoryChange)
26
- export class InventoryChangeMutation {
27
- @Directive('@transaction')
28
- @Mutation(returns => InventoryChange)
29
- async createInventoryChange(@Arg('inventoryChange') inventoryChange: NewInventoryChange, @Ctx() context: ResolverContext): Promise<InventoryChange> {
30
- const { domain, user, tx } = context.state
31
-
32
- return await tx.getRepository(InventoryChange).save({
33
- ...inventoryChange,
34
- domain: domain,
35
- creator: user,
36
- updater: user
37
- })
38
- }
39
-
40
- @Directive('@transaction')
41
- @Mutation(returns => InventoryChange)
42
- async updateInventoryChange(@Arg('name') name: string, @Arg('patch') patch: InventoryChangePatch, @Ctx() context: ResolverContext): Promise<InventoryChange> {
43
- const { domain, user, tx } = context.state
44
-
45
- const repository = tx.getRepository(InventoryChange)
46
- const inventoryChange = await repository.findOne({
47
- where: { domain: { id: domain.id }, name }
48
- })
49
-
50
- return await repository.save({
51
- ...inventoryChange,
52
- ...patch,
53
- updater: user
54
- })
55
- }
56
-
57
- @Directive('@transaction')
58
- @Mutation(returns => [InventoryChange])
59
- async updateMultipleInventoryChange(
60
- @Arg('patches', type => [InventoryChangePatch]) patches: InventoryChangePatch[],
61
- @Ctx() context: ResolverContext
62
- ): Promise<InventoryChange[]> {
63
- const { domain, user, tx } = context.state
64
- let results = []
65
- const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
66
- const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
67
- const inventoryChangeRepo = tx.getRepository(InventoryChange)
68
-
69
- if (_createRecords.length > 0) {
70
- for (let i = 0; i < _createRecords.length; i++) {
71
- const newRecord = _createRecords[i]
72
-
73
- const result = await inventoryChangeRepo.save({
74
- ...newRecord,
75
- domain: domain,
76
- creator: user,
77
- updater: user
78
- })
79
-
80
- results.push({ ...result, cuFlag: '+' })
81
- }
82
- }
83
-
84
- if (_updateRecords.length > 0) {
85
- for (let i = 0; i < _updateRecords.length; i++) {
86
- const newRecord = _updateRecords[i]
87
- const inventoryChange = await inventoryChangeRepo.findOneBy({ id: newRecord.id })
88
-
89
- const result = await inventoryChangeRepo.save({
90
- ...inventoryChange,
91
- ...newRecord,
92
- updater: user
93
- })
94
-
95
- results.push({ ...result, cuFlag: 'M' })
96
- }
97
- }
98
-
99
- return results
100
- }
101
-
102
- @Directive('@transaction')
103
- @Mutation(returns => Boolean)
104
- async deleteInventoryChange(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Boolean> {
105
- const { domain, tx } = context.state
106
-
107
- await tx.getRepository(InventoryChange).delete({ domain: { id: domain.id }, name })
108
- return true
109
- }
110
-
111
- @Directive('@transaction')
112
- @Mutation(returns => Boolean)
113
- async deleteInventoryChanges(@Arg('names', type => [String]) names: string[], @Ctx() context: ResolverContext): Promise<Boolean> {
114
- const { domain, tx } = context.state
115
-
116
- await tx.getRepository(InventoryChange).delete({
117
- domain: { id: domain.id },
118
- name: In(names)
119
- })
120
- return true
121
- }
122
-
123
- @Directive('@privilege(category: "inventory", privilege: "mutation")')
124
- @Directive('@transaction')
125
- @Mutation(returns => Boolean)
126
- async submitInventoryChanges(@Arg('patches', type => [InventoryPatch]) patches: InventoryPatch[], @Ctx() context: ResolverContext) {
127
- const { domain, user, tx } = context.state
128
- try {
129
- const _createRecords = []
130
- const _updateRecords = []
131
-
132
- for (let i = 0; i < patches.length; i++) {
133
- let foundExistingPallet = await tx
134
- .getRepository(Inventory)
135
- .createQueryBuilder('iv')
136
- .where('iv.domain_id = :domain_id', { domain_id: domain.id })
137
- .andWhere('(iv.pallet_id = :pallet_id or iv.id = :id)', {
138
- pallet_id: patches[i]?.palletId,
139
- id: patches[i]?.id
140
- })
141
- .getOne()
142
-
143
- //remove any unchanged records
144
- for (const [key, value] of Object.entries(patches[i])) {
145
- if (foundExistingPallet && key != 'id' && key != 'cuFlag' && value == foundExistingPallet[key]) {
146
- delete patches[i][key]
147
- }
148
- }
149
-
150
- // skip process when no processable data found
151
- if (Object.keys(patches[i]).filter(x => x != 'id' && x != 'cuFlag').length == 0) {
152
- continue
153
- }
154
-
155
- const invLockedQty: number = foundExistingPallet?.lockedQty == null ? 0 : foundExistingPallet?.lockedQty
156
-
157
- if (patches[i]?.qty < invLockedQty) {
158
- throw new Error('Adjusted qty value should not be lower than released qty, kindly contact our support for assistance')
159
- }
160
-
161
- if (foundExistingPallet) {
162
- delete patches[i].serialNumbers
163
- _updateRecords.push(patches[i])
164
- } else {
165
- _createRecords.push(patches[i])
166
- }
167
- }
168
-
169
- const inventoryChangeRepo = tx.getRepository(InventoryChange)
170
- const inventoryItemChangeRepo = tx.getRepository(InventoryItemChange)
171
- if (_createRecords.length > 0) {
172
- for (let i = 0; i < _createRecords.length; i++) {
173
- const newRecord = _createRecords[i]
174
-
175
- var location = await tx.getRepository(Location).findOne({
176
- where: { id: newRecord.location.id },
177
- relations: ['warehouse']
178
- })
179
- newRecord.location = location
180
- newRecord.zone = location.zone
181
- newRecord.warehouse = location.warehouse
182
-
183
- newRecord.bizplace = await tx.getRepository(Bizplace).findOneBy({ id: newRecord.bizplace.id })
184
-
185
- var product: Product = await tx.getRepository(Product).findOne(newRecord.product.id)
186
- newRecord.product = product
187
-
188
- newRecord.status = 'PENDING'
189
- newRecord.transactionType = 'NEW'
190
-
191
- newRecord.expirationDate = ((newRecord?.expirationDate && new Date(newRecord.expirationDate).getFullYear()) || 0) < 2000 ? null : newRecord.expirationDate
192
-
193
- const newInventoryChange: InventoryChange = await inventoryChangeRepo.save({
194
- ...newRecord,
195
- id: undefined,
196
- name: InventoryNoGenerator.inventoryName(),
197
- domain: domain,
198
- creator: user,
199
- updater: user
200
- })
201
-
202
- if (newRecord?.serialNumbers) {
203
- let serialNumbers: string = newRecord.serialNumbers
204
- let newSerialNumbers: any = serialNumbers.split(',')
205
- let inventoryItemChanges: InventoryItemChange[] = []
206
-
207
- for (let i = 0; i < newSerialNumbers.length; i++) {
208
- let newSerialNumber: string = newSerialNumbers[i]
209
- let inventoryItemChange: any = {
210
- serialNumber: newSerialNumber,
211
- type: INVENTORY_ITEM_CHANGE_TYPE.NEW
212
- }
213
-
214
- inventoryItemChanges.push(inventoryItemChange)
215
- }
216
-
217
- newRecord.inventoryItemChangesJson = JSON.stringify(inventoryItemChanges)
218
- }
219
-
220
- // create new inventory items
221
- if (newRecord?.inventoryItemChangesJson) {
222
- const inventoryItemChanges: InventoryItemChange[] = JSON.parse(newRecord.inventoryItemChangesJson)
223
- if (product?.isRequireSerialNumberScanningInbound) {
224
- if (inventoryItemChanges?.length != newRecord?.qty) {
225
- throw new Error('Serial Numbers not tally with quantity')
226
- }
227
- }
228
-
229
- let newInventoryItemChanges: InventoryItemChange[] = []
230
-
231
- inventoryItemChanges.map(inventoryItemChange => {
232
- let newInventoryItemChange: InventoryItemChange = new InventoryItemChange()
233
- newInventoryItemChange.name = InventoryNoGenerator.inventoryName()
234
- newInventoryItemChange.serialNumber = inventoryItemChange.serialNumber
235
- newInventoryItemChange.type = inventoryItemChange.type
236
- newInventoryItemChange.status = newInventoryChange.status
237
- newInventoryItemChange.inventoryItem = newInventoryItemChange?.type == INVENTORY_ITEM_CHANGE_TYPE.NEW ? null : newInventoryItemChange.inventoryItem
238
- newInventoryItemChange.inventoryChange = newInventoryChange
239
- newInventoryItemChange.domain = domain
240
- newInventoryItemChange.creator = user
241
- newInventoryItemChange.updater = user
242
-
243
- newInventoryItemChanges.push(newInventoryItemChange)
244
- })
245
-
246
- await inventoryItemChangeRepo.save(newInventoryItemChanges)
247
- }
248
- }
249
- }
250
-
251
- if (_updateRecords.length > 0) {
252
- for (let i = 0; i < _updateRecords.length; i++) {
253
- const updateRecord = _updateRecords[i]
254
-
255
- let existingRecord = await tx.getRepository(Inventory).findOne({
256
- where: { domain: { id: domain.id }, palletId: Equal(updateRecord?.palletId) },
257
- relations: ['location', 'warehouse', 'product', 'bizplace']
258
- })
259
-
260
- if (!existingRecord) {
261
- existingRecord = await tx.getRepository(Inventory).findOne({
262
- where: { domain: { id: domain.id }, id: updateRecord?.id },
263
- relations: ['location', 'warehouse', 'product', 'bizplace']
264
- })
265
- }
266
-
267
- if (!!updateRecord?.location?.id) {
268
- var location = await tx.getRepository(Location).findOne({
269
- where: { id: updateRecord.location.id },
270
- relations: ['warehouse']
271
- })
272
- updateRecord.location = location
273
- updateRecord.zone = location.zone
274
- updateRecord.warehouse = location.warehouse
275
- }
276
-
277
- if (!!updateRecord?.bizplace?.id) updateRecord.bizplace = await tx.getRepository(Bizplace).findOne(updateRecord.bizplace.id)
278
-
279
- let product: Product
280
-
281
- if (!!updateRecord?.product?.id) {
282
- product = await tx.getRepository(Product).findOne(updateRecord.product.id)
283
- updateRecord.product = product
284
- } else {
285
- product = existingRecord.product
286
- }
287
-
288
- if (!updateRecord.transactionType) {
289
- updateRecord.transactionType = 'CHANGES'
290
- }
291
-
292
- updateRecord.status = 'PENDING'
293
-
294
- updateRecord.expirationDate =
295
- ((updateRecord?.expirationDate && new Date(updateRecord.expirationDate).getFullYear()) || 0) < 2000 ? null : updateRecord.expirationDate
296
-
297
- const updatedInventoryChange: InventoryChange = await inventoryChangeRepo.save({
298
- palletId: existingRecord.palletId,
299
- ...updateRecord,
300
- id: undefined,
301
- name: InventoryNoGenerator.inventoryName(),
302
- inventory: existingRecord,
303
- domain: domain,
304
- creator: user,
305
- updater: user
306
- })
307
-
308
- // create new inventory items
309
- if (updateRecord?.inventoryItemChangesJson) {
310
- const originalInventoryItems: InventoryItem[] = await tx.getRepository(InventoryItem).find({
311
- where: { domain: { id: domain.id }, inventory: { id: existingRecord.id }, status: Not('DELETED') }
312
- })
313
- const inventoryItemChanges: InventoryItemChange[] = JSON.parse(updateRecord.inventoryItemChangesJson)
314
- if (product?.isRequireSerialNumberScanningInbound) {
315
- // const combinedInventoryItems: any[] = [...originalInventoryItems, ...inventoryItemChanges]
316
- let qty: number = updateRecord?.qty ? updateRecord.qty : existingRecord.qty
317
-
318
- if (originalInventoryItems.length == 0) {
319
- if (inventoryItemChanges.length !== qty) {
320
- throw new Error('Serial Numbers not tally with quantity')
321
- }
322
- } else {
323
- if (originalInventoryItems?.length - inventoryItemChanges.length !== qty) {
324
- throw new Error('Serial Numbers not tally with quantity')
325
- }
326
- }
327
- }
328
-
329
- let newInventoryItemChanges: InventoryItemChange[] = []
330
-
331
- inventoryItemChanges.map(inventoryItemChange => {
332
- let newInventoryItemChange: InventoryItemChange = new InventoryItemChange()
333
- newInventoryItemChange.name = InventoryNoGenerator.inventoryName()
334
- newInventoryItemChange.serialNumber = inventoryItemChange.serialNumber
335
- newInventoryItemChange.type = inventoryItemChange.type
336
- newInventoryItemChange.status = updatedInventoryChange.status
337
- newInventoryItemChange.inventory = updatedInventoryChange.inventory
338
- newInventoryItemChange.inventoryItem = newInventoryItemChange?.type == INVENTORY_ITEM_CHANGE_TYPE.NEW ? null : inventoryItemChange.inventoryItem
339
- newInventoryItemChange.inventoryChange = updatedInventoryChange
340
- newInventoryItemChange.domain = domain
341
- newInventoryItemChange.creator = user
342
- newInventoryItemChange.updater = user
343
-
344
- newInventoryItemChanges.push(newInventoryItemChange)
345
- })
346
-
347
- await inventoryItemChangeRepo.save(newInventoryItemChanges)
348
- }
349
- }
350
- }
351
-
352
- /**
353
- * @notes Temporary off sendNotification due to suspect of causing wms down
354
- */
355
- // const notificationApprover: any = await tx
356
- // .getRepository(Privilege)
357
- // .createQueryBuilder('p')
358
- // .select('u.*')
359
- // .addSelect('r.*')
360
- // .innerJoin('p.roles', 'r')
361
- // .innerJoin('r.users', 'u')
362
- // .where('p.name = :name', { name: 'mutation' })
363
- // .andWhere('p.category = :category', { category: 'inventory' })
364
- // .andWhere('r.domain_id = :domainId', { domainId: domain.id })
365
- // .groupBy('u.id')
366
- // .addGroupBy('r.id')
367
- // .getRawMany()
368
-
369
- // if (_updateRecords.some(res => res.transactionType == 'MISSING')) {
370
- // if (notificationApprover?.length && context.header?.referer) {
371
- // const receivers: any[] = notificationApprover.map(user => user.id)
372
- // const msg = {
373
- // title: `Missing stock identified. Review this transaction in Inventory Adjustment Approval.`,
374
- // body: ``,
375
- // url: context.header.referer,
376
- // data: { url: context.header.referer }
377
- // }
378
- // await sendNotification({
379
- // receivers,
380
- // message: { ...msg }
381
- // })
382
- // }
383
- // } else {
384
- // if (notificationApprover?.length && context.header?.referer) {
385
- // const receivers: any[] = notificationApprover.map(user => user.id)
386
- // const msg = {
387
- // title: `There is an inventory adjustment pending for approval, kindly review within 1 hour to avoid any operation conflict.`,
388
- // body: ``,
389
- // url: context.header.referer,
390
- // data: { url: context.header.referer }
391
- // }
392
- // await sendNotification({
393
- // receivers,
394
- // message: { ...msg }
395
- // })
396
- // }
397
- // }
398
-
399
- return true
400
- } catch (error) {
401
- throw error
402
- }
403
- }
404
-
405
- @Directive('@privilege(category: "inventory_adjustment_approval", privilege: "mutation")')
406
- @Directive('@transaction')
407
- @Mutation(returns => InventoryChangeList)
408
- async approveInventoryChanges(
409
- @Arg('patches', type => [InventoryChangePatch]) patches: InventoryChangePatch[],
410
- @Ctx() context: ResolverContext
411
- ): Promise<InventoryChangeList> {
412
- const inventoryChangeList = await approveInventoryChanges(patches, context)
413
-
414
- return inventoryChangeList
415
- }
416
-
417
- @Directive('@privilege(category: "inventory_adjustment_approval", privilege: "mutation")')
418
- @Directive('@transaction')
419
- @Mutation(returns => Boolean)
420
- async rejectInventoryChanges(@Arg('patches', type => [InventoryChangePatch]) patches: InventoryChangePatch[], @Ctx() context: ResolverContext): Promise<Boolean> {
421
- const { domain, user, tx } = context.state
422
-
423
- const _inventoryChanges = await tx.getRepository(InventoryChange).find({
424
- where: { id: In(patches.map(item => item.id)) },
425
- relations: ['inventory', 'inventory.bizplace', 'inventory.product', 'inventory.location', 'inventory.warehouse', 'bizplace', 'product', 'location']
426
- })
427
-
428
- if (_inventoryChanges.length > 0) {
429
- for (let i = 0; i < _inventoryChanges.length; i++) {
430
- if (_inventoryChanges[i].status.toLocaleLowerCase() != 'pending') return true
431
- _inventoryChanges[i].status = 'REJECTED'
432
-
433
- // Get last row of InventoryHistory
434
- let latestEntry = await tx.getRepository(InventoryHistory).find({
435
- where: { palletId: _inventoryChanges[i].palletId, domain: { id: domain.id } },
436
- order: { seq: 'DESC' },
437
- take: 1
438
- })
439
-
440
- if (latestEntry.length > 0) _inventoryChanges[i].lastInventoryHistory = latestEntry[0]
441
-
442
- await upsertInventoryItems(context, _inventoryChanges[i], 'REJECTED', tx)
443
- }
444
-
445
- await tx.getRepository(InventoryChange).save(_inventoryChanges.filter(change => change.status == 'REJECTED'))
446
- }
447
- return true
448
- }
449
- }
450
-
451
- function clean(obj) {
452
- var propNames = Object.getOwnPropertyNames(obj)
453
- for (var i = 0; i < propNames.length; i++) {
454
- var propName = propNames[i]
455
- if (obj[propName] === null || obj[propName] === undefined) {
456
- delete obj[propName]
457
- }
458
- }
459
- }
460
-
461
- async function upsertInventoryItems(context, inventoryChange, approvalStatus, tx) {
462
- const { domain, user } = context.state
463
-
464
- let inventoryItemChanges: InventoryItemChange[] = await tx.getRepository(InventoryItemChange).find({
465
- where: { domain: { id: domain.id }, inventoryChange: { id: inventoryChange.id }, status: 'PENDING' },
466
- relations: ['domain', 'inventoryItem', 'inventory', 'inventory.product', 'inventoryChange']
467
- })
468
- await Promise.all(
469
- inventoryItemChanges.map(async inventoryItemChange => {
470
- let inventoryItem: InventoryItem = new InventoryItem()
471
- if (approvalStatus == 'APPROVED') {
472
- const changeType: string = inventoryItemChange.type
473
- const existingInventoryItem: InventoryItem = inventoryItemChange.inventoryItem
474
- if (changeType == INVENTORY_ITEM_CHANGE_TYPE.NEW) {
475
- const foundSerialNumber: Boolean = await checkExistingSerialNumber(
476
- inventoryItemChange.serialNumber,
477
- inventoryChange.inventory.product,
478
- context,
479
- inventoryChange.inventory,
480
- tx
481
- )
482
-
483
- if (foundSerialNumber) throw new Error(`Serial number ${inventoryItemChange.serialNumber} is duplicated`)
484
-
485
- inventoryItem.name = InventoryNoGenerator.inventoryName()
486
- inventoryItem.serialNumber = inventoryItemChange.serialNumber
487
- inventoryItem.status = INVENTORY_STATUS.STORED
488
- inventoryItem.product = inventoryChange.inventory.product
489
- inventoryItem.domain = inventoryItemChange.domain
490
- inventoryItem.inventory = inventoryChange.inventory
491
- inventoryItem.source = INVENTORY_ITEM_SOURCE.ADJUSTMENT
492
- inventoryItem.creator = user
493
- inventoryItem.updater = user
494
- } else if (changeType == INVENTORY_ITEM_CHANGE_TYPE.MODIFIED) {
495
- const foundSerialNumber: Boolean = await checkExistingSerialNumber(
496
- inventoryItemChange.serialNumber,
497
- inventoryItemChange.inventory.product,
498
- context,
499
- inventoryItemChange.inventory,
500
- tx
501
- )
502
-
503
- if (foundSerialNumber) throw new Error(`Serial number ${inventoryItemChange.serialNumber} is duplicated`)
504
-
505
- inventoryItem = {
506
- ...existingInventoryItem,
507
- serialNumber: inventoryItemChange.serialNumber,
508
- product: inventoryItemChange.inventory.product
509
- }
510
- } else if (changeType == INVENTORY_ITEM_CHANGE_TYPE.REMOVED) {
511
- inventoryItem = {
512
- ...existingInventoryItem,
513
- status: INVENTORY_STATUS.DELETED
514
- }
515
- }
516
-
517
- inventoryItem = await tx.getRepository(InventoryItem).save(inventoryItem)
518
- }
519
-
520
- await tx.getRepository(InventoryItemChange).update({ id: inventoryItemChange.id }, { status: approvalStatus, inventoryItem })
521
- })
522
- )
523
- }
524
-
525
- export async function approveInventoryChanges(patches: any, context: ResolverContext) {
526
- const { domain, user, tx } = context.state
527
-
528
- // Get Selected Inventory Change Data
529
- const _inventoryChanges = await tx.getRepository(InventoryChange).find({
530
- where: { id: In(patches.map(item => item.id)) },
531
- relations: [
532
- 'inventory',
533
- 'inventory.bizplace',
534
- 'inventory.bizplace.domain',
535
- 'inventory.product',
536
- 'inventory.location',
537
- 'inventory.warehouse',
538
- 'bizplace',
539
- 'bizplace.domain',
540
- 'bizplace.company',
541
- 'bizplace.company.domain',
542
- 'product',
543
- 'product.productDetails',
544
- 'product.productDetails.childProductDetail',
545
- 'location',
546
- 'location.warehouse'
547
- ],
548
- order: {
549
- createdAt: 'ASC'
550
- }
551
- })
552
-
553
- let arrLockedInventory: InventoryChange[] = []
554
-
555
- if (_inventoryChanges.length > 0) {
556
- let today = new Date(),
557
- year = today.getFullYear(),
558
- month = today.getMonth(),
559
- date = today.getDate()
560
-
561
- for (let i = 0; i < _inventoryChanges.length; i++) {
562
- if (_inventoryChanges[i].status.toLocaleLowerCase() != 'pending') continue
563
-
564
- const newRecord: any = { ..._inventoryChanges[i] },
565
- newHistoryRecord: any = { ..._inventoryChanges[i] }
566
-
567
- // Adjustment of existing Inventory
568
- if (_inventoryChanges[i].inventory != null) {
569
- let inventoryId = _inventoryChanges[i].inventory.id
570
- let inventory: Inventory = await tx.getRepository(Inventory).findOne({
571
- where: { id: inventoryId },
572
- relations: [
573
- 'domain',
574
- 'bizplace',
575
- 'bizplace.domain',
576
- 'bizplace.company',
577
- 'bizplace.company.domain',
578
- 'product',
579
- 'product.productDetails',
580
- 'product.productDetails.childProductDetail',
581
- 'warehouse',
582
- 'location',
583
- 'creator',
584
- 'updater'
585
- ]
586
- })
587
-
588
- const customerDomain: Domain = inventory.bizplace.domain
589
-
590
- // Check for locked inventory, and stop from udpating inventory
591
- if (inventory.qty < inventory.lockedQty) {
592
- arrLockedInventory.push(_inventoryChanges[i])
593
- continue
594
- }
595
-
596
- // Check for Terminated inventory, then update InventoryChange entity, and stop from udpating inventory
597
- if (inventory.status == INVENTORY_STATUS.TERMINATED) {
598
- _inventoryChanges[i] = { ..._inventoryChanges[i], status: 'TERMINATED' }
599
- continue
600
- }
601
-
602
- let lastSeq = inventory.lastSeq,
603
- transactionType = ''
604
-
605
- newHistoryRecord.openingQty = inventory.qty
606
- newHistoryRecord.openingUomValue = inventory.uomValue
607
-
608
- // Get last row of InventoryHistory
609
- let latestEntry = await tx.getRepository(InventoryHistory).findOne({
610
- where: { palletId: inventory.palletId, domain: { id: domain.id } },
611
- order: { seq: 'DESC' }
612
- })
613
- _inventoryChanges[i].lastInventoryHistory = latestEntry
614
-
615
- // Check Change of existing inventory location
616
- if (newRecord.location && newRecord.location.id != inventory.location.id) {
617
- newRecord.zone = newRecord.location.zone
618
- newRecord.warehouse = { id: newRecord.location.warehouse.id }
619
- transactionType = 'ADJUSTMENT'
620
-
621
- // Check and set current location status
622
- let currentLocationInventoryCount = await tx.getRepository(Inventory).count({
623
- where: { location: { id: inventory.location.id }, status: 'STORED', id: Not(inventory.id) }
624
- })
625
-
626
- if (currentLocationInventoryCount == 0) {
627
- let currentLocation = await tx.getRepository(Location).findOne({
628
- where: { id: inventory.location.id }
629
- })
630
- await tx.getRepository(Location).save({
631
- ...currentLocation,
632
- status: LOCATION_STATUS.EMPTY
633
- })
634
- }
635
- }
636
-
637
- // Check Change of existing inventory quantity
638
- if (newRecord.qty != null && newRecord.qty != inventory.qty) {
639
- newHistoryRecord.qty = newRecord.qty - inventory.qty
640
- if (newRecord.qty <= 0) {
641
- newRecord.qty = 0
642
- newRecord.uomValue = 0
643
- }
644
- transactionType = 'ADJUSTMENT'
645
- } else {
646
- newHistoryRecord.qty = 0
647
- }
648
-
649
- // Check Change of existing inventory uomValue
650
- if (newRecord.uomValue != null && newRecord.uomValue != inventory.uomValue) {
651
- newHistoryRecord.uomValue = newRecord.uomValue - inventory.uomValue
652
- Math.round(newHistoryRecord.uomValue * 100) / 100
653
- transactionType = 'ADJUSTMENT'
654
- } else {
655
- newHistoryRecord.uomValue = 0
656
- }
657
-
658
- // Terminate current inventory history if there is change of bizplace, product, batchId, packingType, or uom
659
- if (
660
- (newRecord.bizplace && inventory.bizplace.id !== newRecord.bizplace.id) ||
661
- (newRecord.product && inventory.product.id !== newRecord.product.id) ||
662
- (newRecord.batchId && inventory.batchId !== newRecord.batchId) ||
663
- (newRecord.packingType && inventory.packingType !== newRecord.packingType) ||
664
- (newRecord.uom && inventory.uom !== newRecord.uom)
665
- ) {
666
- transactionType = 'ADJUSTMENT'
667
- lastSeq = lastSeq + 1
668
- let inventoryHistory = {
669
- ...inventory,
670
- domain: domain,
671
- bizplace: inventory.bizplace.id,
672
- openingQty: inventory.qty,
673
- openingUomValue: inventory.uomValue,
674
- qty: -inventory.qty || 0,
675
- uom: inventory.uom,
676
- uomValue: -inventory.uomValue || 0,
677
- name: InventoryNoGenerator.inventoryHistoryName(),
678
- seq: lastSeq,
679
- transactionType: transactionType,
680
- status: INVENTORY_STATUS.TERMINATED,
681
- productId: inventory.product.id,
682
- warehouseId: inventory.warehouse.id,
683
- locationId: inventory.location.id,
684
- packingType: inventory.packingType,
685
- creator: user,
686
- updater: user
687
- }
688
- delete inventoryHistory.id
689
- delete inventoryHistory.createdAt
690
- delete inventoryHistory.updatedAt
691
- await tx.getRepository(InventoryHistory).save(inventoryHistory as any)
692
- newHistoryRecord.qty = newRecord.qty != null ? newRecord.qty : inventory.qty || 0
693
- newHistoryRecord.uomValue = newRecord.uomValue != null ? newRecord.uomValue : inventory.uomValue || 0
694
- newHistoryRecord.openingQty = 0
695
- newHistoryRecord.openingUomValue = 0
696
- }
697
-
698
- // Set and update inventory and inventory history data
699
- lastSeq = lastSeq + 1
700
- clean(newHistoryRecord)
701
- let inventoryHistory = {
702
- ...inventory,
703
- ...newHistoryRecord,
704
- domain: domain,
705
- creator: user,
706
- updater: user,
707
- name: InventoryNoGenerator.inventoryHistoryName(),
708
- status: newHistoryRecord.transactionType == 'MISSING' ? 'MISSING' : 'STORED',
709
- seq: lastSeq,
710
- transactionType: transactionType == '' ? 'ADJUSTMENT' : transactionType,
711
- productId: newRecord.product ? newRecord.product.id : inventory.product.id,
712
- warehouseId: newRecord.warehouse ? newRecord.warehouse.id : inventory.warehouse.id,
713
- locationId: newRecord.location && newRecord.location.id != inventory.location.id ? newRecord.location.id : inventory.location.id,
714
- uom: newRecord.uom != null ? newRecord.uom : inventory.uom
715
- }
716
- delete inventoryHistory.id
717
- delete inventoryHistory.createdAt
718
- delete inventoryHistory.updatedAt
719
- await tx.getRepository(InventoryHistory).save(inventoryHistory)
720
-
721
- if (newRecord.qty != null && newRecord.qty == 0) {
722
- ++lastSeq
723
- delete inventoryHistory.id
724
- inventoryHistory = {
725
- ...inventoryHistory,
726
- name: InventoryNoGenerator.inventoryHistoryName(),
727
- qty: 0,
728
- uomValue: 0,
729
- openingQty: 0,
730
- openingUomValue: 0,
731
- seq: lastSeq,
732
- transactionType: 'TERMINATED',
733
- status: 'TERMINATED'
734
- }
735
-
736
- await tx.getRepository(InventoryHistory).save(inventoryHistory)
737
- }
738
-
739
- clean(newRecord)
740
- let statusFilter = ''
741
- if (newRecord.transactionType == 'MISSING') {
742
- statusFilter = 'MISSING'
743
- } else {
744
- statusFilter = (newRecord.qty != null ? newRecord.qty : inventory.qty) > 0 ? 'STORED' : 'TERMINATED'
745
- }
746
-
747
- delete newRecord.createdAt
748
- await tx.getRepository(Inventory).save({
749
- ...inventory,
750
- ...newRecord,
751
- id: inventoryId,
752
- status: statusFilter,
753
- updater: user,
754
- lastSeq: lastSeq
755
- })
756
-
757
- //Check and set latest location status
758
- if (newRecord.qty != null ? newRecord.qty : inventory.qty > 0) {
759
- var location = await tx.getRepository(Location).findOne({
760
- where: { id: newRecord.location ? newRecord.location.id : inventory.location.id }
761
- })
762
- await tx.getRepository(Location).save({
763
- ...location,
764
- status: LOCATION_STATUS.OCCUPIED
765
- })
766
- } else {
767
- let latestLocationInventoryCount = await tx.getRepository(Inventory).count({
768
- where: {
769
- location: { id: newRecord.location ? newRecord.location.id : inventory.location.id },
770
- status: 'STORED',
771
- id: Not(inventory.id)
772
- }
773
- })
774
- if (latestLocationInventoryCount == 0) {
775
- let latestLocation = await tx.getRepository(Location).findOne({
776
- where: { id: newRecord.location ? newRecord.location.id : inventory.location.id }
777
- })
778
- await tx.getRepository(Location).save({
779
- ...latestLocation,
780
- status: LOCATION_STATUS.EMPTY
781
- })
782
- }
783
- }
784
-
785
- await upsertInventoryItems(context, _inventoryChanges[i], 'APPROVED', tx)
786
-
787
- const companyDomain: Domain = inventory.bizplace?.company.domain
788
-
789
- const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
790
- if (newRecord?.bizplace?.id && inventory.bizplace.id != newRecord?.bizplace?.id) {
791
- const prevBizSC: Sellercraft = await tx.getRepository(Sellercraft).findOne({
792
- where: {
793
- domain: { id: inventory.bizplace?.domain.id },
794
- status: SellercraftStatus.ACTIVE
795
- },
796
- relations: ['domain']
797
- })
798
- if (prevBizSC) {
799
- await sellercraftCtrl.updateSellercraftStock(prevBizSC, { ...inventory, bizplace: inventory.bizplace })
800
- }
801
- }
802
-
803
- const currentBizSC: Sellercraft = await tx.getRepository(Sellercraft).findOne({
804
- where: {
805
- domain: { id: newRecord?.bizplace?.domain.id || inventory?.bizplace?.domain.id },
806
- status: SellercraftStatus.ACTIVE
807
- },
808
- relations: ['domain']
809
- })
810
-
811
- if (currentBizSC) {
812
- if (newRecord?.product?.id && newRecord?.product?.id != inventory?.product?.id)
813
- await sellercraftCtrl.updateSellercraftStock(currentBizSC, {
814
- ...inventory,
815
- bizplace: newRecord?.bizplace || inventory?.bizplace,
816
- product: inventory.product
817
- })
818
-
819
- await sellercraftCtrl.updateSellercraftStock(currentBizSC, {
820
- ...inventory,
821
- bizplace: newRecord?.bizplace || inventory?.bizplace,
822
- product: newRecord?.product || inventory?.product
823
- })
824
- }
825
-
826
- // check for any existing active marketplace connection, update marketplace selling qty
827
- const marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
828
- where: { domain: { id: companyDomain.id }, status: 'ACTIVE', isAutoUpdateStockQty: true },
829
- relations: ['marketplaceDistributors']
830
- })
831
-
832
- if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
833
- const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
834
- await ecommerceCtrl.updateProductVariationStock(marketplaceStores, inventory.product.id, companyDomain)
835
-
836
- if (newRecord?.product || newRecord?.bizplace) await ecommerceCtrl.updateProductVariationStock(marketplaceStores, newRecord.product.id, companyDomain)
837
- }
838
- }
839
-
840
- // Adding Inventory
841
- else {
842
- const total = await tx.getRepository(Inventory).countBy({
843
- createdAt: MoreThan(new Date(year, month, date))
844
- })
845
-
846
- const yy = String(year).substr(String(year).length - 2)
847
- const mm = String(month + 1).padStart(2, '0')
848
- const dd = String(date).padStart(2, '0')
849
-
850
- const dateStr = yy + mm + dd
851
-
852
- let palletId = newRecord.palletId
853
-
854
- if (!newRecord.palletId?.trim()) {
855
- palletId = await generateId({
856
- domain: domain,
857
- type: 'adjustment_pallet_id',
858
- seed: {
859
- batchId: newRecord.batchId,
860
- date: dateStr
861
- }
862
- })
863
- if (!palletId) throw new Error('No adjustment pallet id setting rule found')
864
- }
865
-
866
- var location = await tx.getRepository(Location).findOne({
867
- where: { id: newRecord.location.id },
868
- relations: ['warehouse']
869
- })
870
- newRecord.location = location
871
- newRecord.zone = location.zone
872
- newRecord.warehouse = location.warehouse
873
-
874
- newRecord.status = INVENTORY_STATUS.STORED
875
- newRecord.name = palletId
876
- newRecord.palletId = palletId
877
-
878
- const warehouseCartonSetting: Setting = await tx.getRepository(Setting).findOne({
879
- where: { domain: { id: domain.id }, category: 'id-rule', name: 'enable-carton-label' }
880
- })
881
-
882
- const partnerCartonSetting: PartnerSetting = await tx.getRepository(PartnerSetting).findOne({
883
- where: {
884
- setting: { id: warehouseCartonSetting.id },
885
- domain: { id: domain.id },
886
- partnerDomain: { id: newRecord.bizplace?.domain.id }
887
- }
888
- })
889
-
890
- if (partnerCartonSetting?.value) {
891
- let cartonId = await generateId({
892
- domain: domain,
893
- type: 'adjustment_carton_id',
894
- seed: { date: dateStr }
895
- })
896
- if (!cartonId) throw new Error('No adjustment carton id setting rule found')
897
-
898
- newRecord.cartonId = cartonId
899
- }
900
-
901
- let expirationDate = ((newRecord?.expirationDate && new Date(newRecord.expirationDate).getFullYear()) || 0) < 2000 ? null : newRecord.expirationDate
902
-
903
- let savedInventory = await tx.getRepository(Inventory).save({
904
- ...newRecord,
905
- expirationDate,
906
- domain: domain,
907
- creator: user,
908
- updater: user,
909
- lastSeq: 0
910
- })
911
-
912
- await tx.getRepository(InventoryHistory).save({
913
- ...newRecord,
914
- domain: domain,
915
- creator: user,
916
- updater: user,
917
- name: InventoryNoGenerator.inventoryHistoryName(),
918
- seq: 0,
919
- transactionType: 'NEW',
920
- productId: newRecord.product.id,
921
- warehouseId: newRecord.warehouse.id,
922
- locationId: newRecord.location.id,
923
- inventory: savedInventory,
924
- expirationDate: expirationDate
925
- })
926
-
927
- await tx.getRepository(Location).save({
928
- ...location,
929
- status: LOCATION_STATUS.OCCUPIED
930
- })
931
-
932
- _inventoryChanges[i].inventory = savedInventory
933
- _inventoryChanges[i].palletId = savedInventory.palletId
934
-
935
- await upsertInventoryItems(context, _inventoryChanges[i], 'APPROVED', tx)
936
-
937
- const sellercraft: Sellercraft = await tx.getRepository(Sellercraft).findOne({
938
- where: { domain: { id: newRecord.bizplace.domain.id }, status: SellercraftStatus.ACTIVE },
939
- relations: ['domain']
940
- })
941
-
942
- const companyDomain: Domain = newRecord.bizplace?.company.domain
943
-
944
- if (sellercraft) {
945
- const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
946
- await sellercraftCtrl.updateSellercraftStock(sellercraft, newRecord)
947
- }
948
-
949
- // update marketplace store qty
950
- // check for any existing active marketplace connection, update marketplace selling qty
951
- const marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
952
- where: { domain: { id: companyDomain.id }, status: 'ACTIVE', isAutoUpdateStockQty: true },
953
- relations: ['marketplaceDistributors']
954
- })
955
-
956
- if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
957
- const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
958
- await ecommerceCtrl.updateProductVariationStock(marketplaceStores, newRecord.product.id, companyDomain)
959
- }
960
- }
961
-
962
- _inventoryChanges[i].status = 'APPROVED'
963
- }
964
-
965
- await tx.getRepository(InventoryChange).save(_inventoryChanges)
966
- }
967
-
968
- return { items: arrLockedInventory, total: arrLockedInventory.length }
969
- }