@things-factory/warehouse-base 4.1.41 → 4.2.2
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.
- package/dist-server/constants/inventory.js +9 -2
- package/dist-server/constants/inventory.js.map +1 -1
- package/dist-server/service/index.js +6 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/inventory/inventory-query.js +37 -14
- package/dist-server/service/inventory/inventory-query.js.map +1 -1
- package/dist-server/service/inventory/inventory-types.js +12 -0
- package/dist-server/service/inventory/inventory-types.js.map +1 -1
- package/dist-server/service/inventory/inventory.js +22 -0
- package/dist-server/service/inventory/inventory.js.map +1 -1
- package/dist-server/service/inventory-change/inventory-change-mutation.js +134 -5
- package/dist-server/service/inventory-change/inventory-change-mutation.js.map +1 -1
- package/dist-server/service/inventory-change/inventory-change-query.js +1 -0
- package/dist-server/service/inventory-change/inventory-change-query.js.map +1 -1
- package/dist-server/service/inventory-change/inventory-change.js +10 -0
- package/dist-server/service/inventory-change/inventory-change.js.map +1 -1
- package/dist-server/service/inventory-item/inventory-item-query.js +82 -4
- package/dist-server/service/inventory-item/inventory-item-query.js.map +1 -1
- package/dist-server/service/inventory-item/inventory-item.js +8 -0
- package/dist-server/service/inventory-item/inventory-item.js.map +1 -1
- package/dist-server/service/inventory-item-change/index.js +9 -0
- package/dist-server/service/inventory-item-change/index.js.map +1 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-mutation.js +121 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-mutation.js.map +1 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-query.js +87 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-query.js.map +1 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-type.js +107 -0
- package/dist-server/service/inventory-item-change/inventory-item-change-type.js.map +1 -0
- package/dist-server/service/inventory-item-change/inventory-item-change.js +110 -0
- package/dist-server/service/inventory-item-change/inventory-item-change.js.map +1 -0
- package/dist-server/utils/inventory-util.js +23 -40
- package/dist-server/utils/inventory-util.js.map +1 -1
- package/package.json +6 -6
- package/server/constants/inventory.ts +9 -1
- package/server/service/index.ts +9 -0
- package/server/service/inventory/inventory-query.ts +53 -14
- package/server/service/inventory/inventory-types.ts +9 -0
- package/server/service/inventory/inventory.ts +17 -0
- package/server/service/inventory-change/inventory-change-mutation.ts +177 -6
- package/server/service/inventory-change/inventory-change-query.ts +1 -0
- package/server/service/inventory-change/inventory-change.ts +5 -0
- package/server/service/inventory-item/inventory-item-query.ts +98 -3
- package/server/service/inventory-item/inventory-item.ts +6 -0
- package/server/service/inventory-item-change/index.ts +6 -0
- package/server/service/inventory-item-change/inventory-item-change-mutation.ts +116 -0
- package/server/service/inventory-item-change/inventory-item-change-query.ts +43 -0
- package/server/service/inventory-item-change/inventory-item-change-type.ts +68 -0
- package/server/service/inventory-item-change/inventory-item-change.ts +92 -0
- package/server/utils/inventory-util.ts +21 -43
|
@@ -10,7 +10,9 @@ export const INVENTORY_STATUS = {
|
|
|
10
10
|
DELETED: 'DELETED',
|
|
11
11
|
CHECKED: 'CHECKED',
|
|
12
12
|
PICKED: 'PICKED',
|
|
13
|
-
PICKING: 'PICKING'
|
|
13
|
+
PICKING: 'PICKING',
|
|
14
|
+
SORTING: 'SORTING',
|
|
15
|
+
SORTED: 'SORTED'
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const INVENTORY_TYPES = {
|
|
@@ -56,3 +58,9 @@ export const INVENTORY_ITEM_SOURCE = {
|
|
|
56
58
|
OUTBOUND: 'OUTBOUND',
|
|
57
59
|
ADJUSTMENT: 'ADJUSTMENT'
|
|
58
60
|
}
|
|
61
|
+
|
|
62
|
+
export const INVENTORY_ITEM_CHANGE_TYPE = {
|
|
63
|
+
NEW: 'NEW',
|
|
64
|
+
MODIFIED: 'MODIFIED',
|
|
65
|
+
REMOVED: 'REMOVED'
|
|
66
|
+
}
|
package/server/service/index.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { entities as InventoryEntities, resolvers as InventoryResolvers } from '
|
|
|
2
2
|
import { entities as InventoryChangeEntities, resolvers as InventoryChangeResolvers } from './inventory-change'
|
|
3
3
|
import { entities as InventoryHistoryEntities, resolvers as InventoryHistoryResvolers } from './inventory-history'
|
|
4
4
|
import { entities as InventoryItemEntities, resolvers as InventoryItemResolvers } from './inventory-item'
|
|
5
|
+
import {
|
|
6
|
+
entities as InventoryItemChangeEntities,
|
|
7
|
+
resolvers as InventoryItemChangeResolvers
|
|
8
|
+
} from './inventory-item-change'
|
|
5
9
|
import { entities as LocationEntities, resolvers as LocationResolvers } from './location'
|
|
6
10
|
import { entities as MovementEntities, resolvers as MovementResolvers } from './movement'
|
|
7
11
|
import { entities as PalletEntities, resolvers as PalletResolvers } from './pallet'
|
|
@@ -11,7 +15,9 @@ import { entities as ReducedInventoryHistoryEntities } from './reduced-inventory
|
|
|
11
15
|
import { entities as WarehouseEntities, resolvers as WarehouseResolvers } from './warehouse'
|
|
12
16
|
|
|
13
17
|
/* EXPORT ENTITY TYPES */
|
|
18
|
+
export * from './inventory-item-change/inventory-item-change'
|
|
14
19
|
export * from './inventory-item/inventory-item'
|
|
20
|
+
export * from './inventory-item-change/inventory-item-change'
|
|
15
21
|
export * from './inventory/inventory'
|
|
16
22
|
export * from './inventory-change/inventory-change'
|
|
17
23
|
export * from './inventory-history/inventory-history'
|
|
@@ -27,6 +33,7 @@ export * from './reduced-inventory-history/reduced-inventory-history'
|
|
|
27
33
|
export * from './inventory/inventory-types'
|
|
28
34
|
export * from './inventory/inventory-types'
|
|
29
35
|
export * from './inventory-item/inventory-item'
|
|
36
|
+
export * from './inventory-item-change/inventory-item-change'
|
|
30
37
|
export * from './inventory-change/inventory-change-types'
|
|
31
38
|
export * from './inventory-history/inventory-history-types'
|
|
32
39
|
export * from './location/location-types'
|
|
@@ -41,6 +48,7 @@ export const entities = [
|
|
|
41
48
|
...InventoryChangeEntities,
|
|
42
49
|
...InventoryHistoryEntities,
|
|
43
50
|
...InventoryItemEntities,
|
|
51
|
+
...InventoryItemChangeEntities,
|
|
44
52
|
...LocationEntities,
|
|
45
53
|
...MovementEntities,
|
|
46
54
|
...PalletEntities,
|
|
@@ -56,6 +64,7 @@ export const schema = {
|
|
|
56
64
|
...InventoryChangeResolvers,
|
|
57
65
|
...InventoryHistoryResvolers,
|
|
58
66
|
...InventoryItemResolvers,
|
|
67
|
+
...InventoryItemChangeResolvers,
|
|
59
68
|
...LocationResolvers,
|
|
60
69
|
...MovementResolvers,
|
|
61
70
|
...PalletResolvers,
|
|
@@ -11,6 +11,7 @@ import { INVENTORY_STATUS, LOCATION_TYPE } from '../../constants'
|
|
|
11
11
|
import { InventoryChange } from '../inventory-change/inventory-change'
|
|
12
12
|
import { Inventory } from './inventory'
|
|
13
13
|
import { InventoryBundleGroupDetail, InventoryList } from './inventory-types'
|
|
14
|
+
import { InventoryItem } from '../inventory-item/inventory-item'
|
|
14
15
|
|
|
15
16
|
@Resolver(Inventory)
|
|
16
17
|
export class InventoryQuery {
|
|
@@ -70,6 +71,12 @@ export class InventoryQuery {
|
|
|
70
71
|
.leftJoinAndSelect('iv.product', 'product')
|
|
71
72
|
.leftJoinAndSelect('iv.warehouse', 'warehouse')
|
|
72
73
|
.leftJoinAndSelect('iv.location', 'location')
|
|
74
|
+
.leftJoin('iv.inventoryItems', 'ivi')
|
|
75
|
+
.loadRelationCountAndMap('iv.inventoryItemCount', 'iv.inventoryItems', 'ivic', qb =>
|
|
76
|
+
qb.andWhere('ivic.status = :ivicStatus', {
|
|
77
|
+
ivicStatus: INVENTORY_STATUS.STORED
|
|
78
|
+
})
|
|
79
|
+
)
|
|
73
80
|
|
|
74
81
|
if (remainOnly) {
|
|
75
82
|
qb.andWhere('iv.qty > 0')
|
|
@@ -136,10 +143,22 @@ export class InventoryQuery {
|
|
|
136
143
|
}
|
|
137
144
|
})
|
|
138
145
|
|
|
146
|
+
let inventoryItems: InventoryItem[] = await getRepository(InventoryItem).find({
|
|
147
|
+
where: { domain, inventory: item }
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
let serialNumbers: string =
|
|
151
|
+
inventoryItems
|
|
152
|
+
.map(inventoryItem => {
|
|
153
|
+
return inventoryItem.serialNumber
|
|
154
|
+
})
|
|
155
|
+
.join(',') || ''
|
|
156
|
+
|
|
139
157
|
return {
|
|
140
158
|
...item,
|
|
141
159
|
remainQty: item.qty - selectedQty,
|
|
142
|
-
remainUomValue: item.uomValue - selectedUomValue
|
|
160
|
+
remainUomValue: item.uomValue - selectedUomValue,
|
|
161
|
+
serialNumbers: serialNumbers
|
|
143
162
|
}
|
|
144
163
|
})
|
|
145
164
|
)
|
|
@@ -745,6 +764,27 @@ export class InventoryQuery {
|
|
|
745
764
|
.offset((page - 1) * limit)
|
|
746
765
|
.limit(limit)
|
|
747
766
|
.getRawMany()
|
|
767
|
+
|
|
768
|
+
await Promise.all(
|
|
769
|
+
items.map(async item => {
|
|
770
|
+
let inventoryItems: InventoryItem[] = await getRepository(InventoryItem).find({
|
|
771
|
+
where: { domain, inventory: item }
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
let serialNumbers: string =
|
|
775
|
+
inventoryItems
|
|
776
|
+
.map(inventoryItem => {
|
|
777
|
+
return inventoryItem.serialNumber
|
|
778
|
+
})
|
|
779
|
+
.join(',') || ''
|
|
780
|
+
|
|
781
|
+
return {
|
|
782
|
+
...item,
|
|
783
|
+
serialNumbers: serialNumbers
|
|
784
|
+
}
|
|
785
|
+
})
|
|
786
|
+
)
|
|
787
|
+
|
|
748
788
|
let total = await qb.getCount()
|
|
749
789
|
return { items: items.map(item => new Inventory(item)), total }
|
|
750
790
|
} catch (error) {
|
|
@@ -843,9 +883,7 @@ export async function inventoriesByStrategy(
|
|
|
843
883
|
qb.orderBy('"INV"."created_at"', 'ASC')
|
|
844
884
|
if (locationSortingRules?.length > 0) {
|
|
845
885
|
locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
|
|
846
|
-
|
|
847
|
-
? qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
848
|
-
: qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
886
|
+
qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
849
887
|
})
|
|
850
888
|
}
|
|
851
889
|
break
|
|
@@ -854,9 +892,7 @@ export async function inventoriesByStrategy(
|
|
|
854
892
|
qb.orderBy('"INV"."created_at"', 'DESC')
|
|
855
893
|
if (locationSortingRules?.length > 0) {
|
|
856
894
|
locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
|
|
857
|
-
|
|
858
|
-
? qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
859
|
-
: qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
895
|
+
qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
860
896
|
})
|
|
861
897
|
}
|
|
862
898
|
break
|
|
@@ -865,9 +901,16 @@ export async function inventoriesByStrategy(
|
|
|
865
901
|
qb.orderBy('"INV"."expiration_date"', 'ASC')
|
|
866
902
|
if (locationSortingRules?.length > 0) {
|
|
867
903
|
locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
904
|
+
qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
905
|
+
})
|
|
906
|
+
}
|
|
907
|
+
break
|
|
908
|
+
|
|
909
|
+
case 'FMFO':
|
|
910
|
+
qb.orderBy('"INV"."manufacture_date"', 'ASC')
|
|
911
|
+
if (locationSortingRules?.length > 0) {
|
|
912
|
+
locationSortingRules.forEach((rule: { name: string; desc: boolean }, idx: number) => {
|
|
913
|
+
qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
871
914
|
})
|
|
872
915
|
}
|
|
873
916
|
break
|
|
@@ -880,10 +923,6 @@ export async function inventoriesByStrategy(
|
|
|
880
923
|
: qb.addOrderBy(`LOC.${rule.name}`, rule.desc ? 'DESC' : 'ASC')
|
|
881
924
|
})
|
|
882
925
|
} else qb.orderBy('"LOC"."name"', 'DESC')
|
|
883
|
-
break
|
|
884
|
-
default:
|
|
885
|
-
qb.orderBy('"INV"."created_at"', 'ASC')
|
|
886
|
-
|
|
887
926
|
break
|
|
888
927
|
}
|
|
889
928
|
|
|
@@ -122,6 +122,12 @@ export class InventoryPatch {
|
|
|
122
122
|
@Field(type => Float, { nullable: true })
|
|
123
123
|
unitCost?: number
|
|
124
124
|
|
|
125
|
+
@Field({ nullable: true })
|
|
126
|
+
inventoryItemChangesJson?: string
|
|
127
|
+
|
|
128
|
+
@Field({ nullable: true })
|
|
129
|
+
serialNumbers?: string
|
|
130
|
+
|
|
125
131
|
@Field({ nullable: true })
|
|
126
132
|
updatedAt?: string
|
|
127
133
|
|
|
@@ -202,6 +208,9 @@ export class NewInventory {
|
|
|
202
208
|
|
|
203
209
|
@Field(type => ScalarDate, { nullable: true })
|
|
204
210
|
manufactureDate: Date
|
|
211
|
+
|
|
212
|
+
@Field({ nullable: true })
|
|
213
|
+
serialNumbers?: string
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
@ObjectType()
|
|
@@ -17,6 +17,7 @@ import { Product, ProductDetail } from '@things-factory/product-base'
|
|
|
17
17
|
import { Domain, ScalarDate } from '@things-factory/shell'
|
|
18
18
|
|
|
19
19
|
import { InventoryChange } from '../inventory-change/inventory-change'
|
|
20
|
+
import { InventoryItem } from '../inventory-item/inventory-item'
|
|
20
21
|
import { Location } from '../location/location'
|
|
21
22
|
import { Pallet } from '../pallet/pallet'
|
|
22
23
|
import { Warehouse } from '../warehouse/warehouse'
|
|
@@ -195,6 +196,13 @@ export class Inventory {
|
|
|
195
196
|
@Field(type => [InventoryChange], { nullable: true })
|
|
196
197
|
inventoryChanges: InventoryChange[]
|
|
197
198
|
|
|
199
|
+
@OneToMany(type => InventoryItem, inventoryItems => inventoryItems.inventory)
|
|
200
|
+
@Field(type => [InventoryItem], { nullable: true })
|
|
201
|
+
inventoryItems: InventoryItem[]
|
|
202
|
+
|
|
203
|
+
@Field({ nullable: true })
|
|
204
|
+
serialNumbers?: string
|
|
205
|
+
|
|
198
206
|
@Column('date', { nullable: true })
|
|
199
207
|
@Field(type => ScalarDate, { nullable: true })
|
|
200
208
|
expirationDate: Date
|
|
@@ -255,6 +263,15 @@ export class Inventory {
|
|
|
255
263
|
@Field({ nullable: true })
|
|
256
264
|
changeCount?: number
|
|
257
265
|
|
|
266
|
+
@Field({ nullable: true })
|
|
267
|
+
binLocation?: string
|
|
268
|
+
|
|
269
|
+
@Field({ nullable: true })
|
|
270
|
+
orderNo?: string
|
|
271
|
+
|
|
272
|
+
@Field({ nullable: true })
|
|
273
|
+
inventoryItemCount: number
|
|
274
|
+
|
|
258
275
|
constructor(obj?) {
|
|
259
276
|
if (obj) {
|
|
260
277
|
this.id = obj.Inventory_id
|
|
@@ -10,10 +10,13 @@ import { Product } from '@things-factory/product-base'
|
|
|
10
10
|
import { PartnerSetting, Setting } from '@things-factory/setting-base'
|
|
11
11
|
import { Domain } from '@things-factory/shell'
|
|
12
12
|
|
|
13
|
-
import { INVENTORY_STATUS, LOCATION_STATUS } from '../../constants'
|
|
13
|
+
import { INVENTORY_ITEM_CHANGE_TYPE, INVENTORY_ITEM_SOURCE, INVENTORY_STATUS, LOCATION_STATUS } from '../../constants'
|
|
14
14
|
import { EcommerceController, SellercraftController } from '../../controllers'
|
|
15
15
|
import { InventoryNoGenerator } from '../../utils'
|
|
16
16
|
import { InventoryHistory } from '../inventory-history/inventory-history'
|
|
17
|
+
import { InventoryItemChange } from '../inventory-item-change/inventory-item-change'
|
|
18
|
+
import { InventoryItem } from '../inventory-item/inventory-item'
|
|
19
|
+
import { checkExistingSerialNumber } from '../inventory-item/inventory-item-query'
|
|
17
20
|
import { Inventory } from '../inventory/inventory'
|
|
18
21
|
import { InventoryPatch } from '../inventory/inventory-types'
|
|
19
22
|
import { Location } from '../location/location'
|
|
@@ -150,6 +153,7 @@ export class InventoryChangeMutation {
|
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
if (foundExistingPallet) {
|
|
156
|
+
delete patches[i].serialNumbers
|
|
153
157
|
_updateRecords.push(patches[i])
|
|
154
158
|
} else {
|
|
155
159
|
_createRecords.push(patches[i])
|
|
@@ -157,6 +161,7 @@ export class InventoryChangeMutation {
|
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
const inventoryChangeRepo = tx.getRepository(InventoryChange)
|
|
164
|
+
const inventoryItemChangeRepo = tx.getRepository(InventoryItemChange)
|
|
160
165
|
if (_createRecords.length > 0) {
|
|
161
166
|
for (let i = 0; i < _createRecords.length; i++) {
|
|
162
167
|
const newRecord = _createRecords[i]
|
|
@@ -182,7 +187,7 @@ export class InventoryChangeMutation {
|
|
|
182
187
|
? null
|
|
183
188
|
: newRecord.expirationDate
|
|
184
189
|
|
|
185
|
-
await inventoryChangeRepo.save({
|
|
190
|
+
const newInventoryChange: InventoryChange = await inventoryChangeRepo.save({
|
|
186
191
|
...newRecord,
|
|
187
192
|
id: undefined,
|
|
188
193
|
name: InventoryNoGenerator.inventoryName(),
|
|
@@ -190,6 +195,56 @@ export class InventoryChangeMutation {
|
|
|
190
195
|
creator: user,
|
|
191
196
|
updater: user
|
|
192
197
|
})
|
|
198
|
+
|
|
199
|
+
if (newRecord?.serialNumbers) {
|
|
200
|
+
let serialNumbers: string = newRecord.serialNumbers
|
|
201
|
+
let newSerialNumbers: any = serialNumbers.split(',')
|
|
202
|
+
let inventoryItemChanges: InventoryItemChange[] = []
|
|
203
|
+
|
|
204
|
+
for (let i = 0; i < newSerialNumbers.length; i++) {
|
|
205
|
+
let newSerialNumber: string = newSerialNumbers[i]
|
|
206
|
+
let inventoryItemChange: any = {
|
|
207
|
+
serialNumber: newSerialNumber,
|
|
208
|
+
type: INVENTORY_ITEM_CHANGE_TYPE.NEW
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
inventoryItemChanges.push(inventoryItemChange)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
newRecord.inventoryItemChangesJson = JSON.stringify(inventoryItemChanges)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// create new inventory items
|
|
218
|
+
if (newRecord?.inventoryItemChangesJson) {
|
|
219
|
+
const inventoryItemChanges: InventoryItemChange[] = JSON.parse(newRecord.inventoryItemChangesJson)
|
|
220
|
+
if (product?.isRequireSerialNumberScanningInbound) {
|
|
221
|
+
if (inventoryItemChanges?.length != newRecord?.qty) {
|
|
222
|
+
throw new Error('Serial Numbers not tally with quantity')
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let newInventoryItemChanges: InventoryItemChange[] = []
|
|
227
|
+
|
|
228
|
+
inventoryItemChanges.map(inventoryItemChange => {
|
|
229
|
+
let newInventoryItemChange: InventoryItemChange = new InventoryItemChange()
|
|
230
|
+
newInventoryItemChange.name = InventoryNoGenerator.inventoryName()
|
|
231
|
+
newInventoryItemChange.serialNumber = inventoryItemChange.serialNumber
|
|
232
|
+
newInventoryItemChange.type = inventoryItemChange.type
|
|
233
|
+
newInventoryItemChange.status = newInventoryChange.status
|
|
234
|
+
newInventoryItemChange.inventoryItem =
|
|
235
|
+
newInventoryItemChange?.type == INVENTORY_ITEM_CHANGE_TYPE.NEW
|
|
236
|
+
? null
|
|
237
|
+
: 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
|
+
}
|
|
193
248
|
}
|
|
194
249
|
}
|
|
195
250
|
|
|
@@ -222,8 +277,14 @@ export class InventoryChangeMutation {
|
|
|
222
277
|
if (!!updateRecord?.bizplace?.id)
|
|
223
278
|
updateRecord.bizplace = await tx.getRepository(Bizplace).findOne(updateRecord.bizplace.id)
|
|
224
279
|
|
|
225
|
-
|
|
226
|
-
|
|
280
|
+
let product: Product
|
|
281
|
+
|
|
282
|
+
if (!!updateRecord?.product?.id) {
|
|
283
|
+
product = await tx.getRepository(Product).findOne(updateRecord.product.id)
|
|
284
|
+
updateRecord.product = product
|
|
285
|
+
} else {
|
|
286
|
+
product = existingRecord.product
|
|
287
|
+
}
|
|
227
288
|
|
|
228
289
|
updateRecord.status = 'PENDING'
|
|
229
290
|
updateRecord.transactionType = 'CHANGES'
|
|
@@ -233,7 +294,7 @@ export class InventoryChangeMutation {
|
|
|
233
294
|
? null
|
|
234
295
|
: updateRecord.expirationDate
|
|
235
296
|
|
|
236
|
-
await inventoryChangeRepo.save({
|
|
297
|
+
const updatedInventoryChange: InventoryChange = await inventoryChangeRepo.save({
|
|
237
298
|
palletId: existingRecord.palletId,
|
|
238
299
|
...updateRecord,
|
|
239
300
|
id: undefined,
|
|
@@ -243,6 +304,44 @@ export class InventoryChangeMutation {
|
|
|
243
304
|
creator: user,
|
|
244
305
|
updater: user
|
|
245
306
|
})
|
|
307
|
+
|
|
308
|
+
// create new inventory items
|
|
309
|
+
if (updateRecord?.inventoryItemChangesJson) {
|
|
310
|
+
const originalInventoryItems: InventoryItem[] = await tx.getRepository(InventoryItem).find({
|
|
311
|
+
where: { domain, inventory: existingRecord }
|
|
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
|
+
if (combinedInventoryItems?.length != qty) {
|
|
318
|
+
throw new Error('Serial Numbers not tally with quantity')
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let newInventoryItemChanges: InventoryItemChange[] = []
|
|
323
|
+
|
|
324
|
+
inventoryItemChanges.map(inventoryItemChange => {
|
|
325
|
+
let newInventoryItemChange: InventoryItemChange = new InventoryItemChange()
|
|
326
|
+
newInventoryItemChange.name = InventoryNoGenerator.inventoryName()
|
|
327
|
+
newInventoryItemChange.serialNumber = inventoryItemChange.serialNumber
|
|
328
|
+
newInventoryItemChange.type = inventoryItemChange.type
|
|
329
|
+
newInventoryItemChange.status = updatedInventoryChange.status
|
|
330
|
+
newInventoryItemChange.inventory = updatedInventoryChange.inventory
|
|
331
|
+
newInventoryItemChange.inventoryItem =
|
|
332
|
+
newInventoryItemChange?.type == INVENTORY_ITEM_CHANGE_TYPE.NEW
|
|
333
|
+
? null
|
|
334
|
+
: newInventoryItemChange.inventoryItem
|
|
335
|
+
newInventoryItemChange.inventoryChange = updatedInventoryChange
|
|
336
|
+
newInventoryItemChange.domain = domain
|
|
337
|
+
newInventoryItemChange.creator = user
|
|
338
|
+
newInventoryItemChange.updater = user
|
|
339
|
+
|
|
340
|
+
newInventoryItemChanges.push(newInventoryItemChange)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
await inventoryItemChangeRepo.save(newInventoryItemChanges)
|
|
344
|
+
}
|
|
246
345
|
}
|
|
247
346
|
}
|
|
248
347
|
|
|
@@ -509,6 +608,8 @@ export class InventoryChangeMutation {
|
|
|
509
608
|
}
|
|
510
609
|
}
|
|
511
610
|
|
|
611
|
+
await upsertInventoryItems(context, _inventoryChanges[i], 'APPROVED', tx)
|
|
612
|
+
|
|
512
613
|
const sellercraft: Sellercraft = await tx
|
|
513
614
|
.getRepository(Sellercraft)
|
|
514
615
|
.findOne({ domain: customerDomain, status: SellercraftStatus.ACTIVE })
|
|
@@ -626,6 +727,8 @@ export class InventoryChangeMutation {
|
|
|
626
727
|
_inventoryChanges[i].inventory = savedInventory
|
|
627
728
|
_inventoryChanges[i].palletId = savedInventory.palletId
|
|
628
729
|
|
|
730
|
+
await upsertInventoryItems(context, _inventoryChanges[i], 'APPROVED', tx)
|
|
731
|
+
|
|
629
732
|
const sellercraft: Sellercraft = await tx
|
|
630
733
|
.getRepository(Sellercraft)
|
|
631
734
|
.findOne({ domain: newRecord.bizplace.domain, status: SellercraftStatus.ACTIVE })
|
|
@@ -666,7 +769,7 @@ export class InventoryChangeMutation {
|
|
|
666
769
|
@Arg('patches', type => [InventoryChangePatch]) patches: InventoryChangePatch[],
|
|
667
770
|
@Ctx() context: any
|
|
668
771
|
): Promise<Boolean> {
|
|
669
|
-
const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
|
|
772
|
+
const { domain, user, tx }: { domain: Domain; user: User; tx: EntityManager } = context.state
|
|
670
773
|
|
|
671
774
|
const _inventoryChanges = await tx.getRepository(InventoryChange).find({
|
|
672
775
|
where: { id: In(patches.map(item => item.id)) },
|
|
@@ -695,6 +798,8 @@ export class InventoryChangeMutation {
|
|
|
695
798
|
})
|
|
696
799
|
|
|
697
800
|
if (latestEntry.length > 0) _inventoryChanges[i].lastInventoryHistory = latestEntry[0]
|
|
801
|
+
|
|
802
|
+
await upsertInventoryItems(context, _inventoryChanges[i], 'REJECTED', tx)
|
|
698
803
|
}
|
|
699
804
|
|
|
700
805
|
await tx.getRepository(InventoryChange).save(_inventoryChanges.filter(change => change.status == 'REJECTED'))
|
|
@@ -712,3 +817,69 @@ function clean(obj) {
|
|
|
712
817
|
}
|
|
713
818
|
}
|
|
714
819
|
}
|
|
820
|
+
|
|
821
|
+
async function upsertInventoryItems(context, inventoryChange, approvalStatus, tx) {
|
|
822
|
+
const { domain, user }: { domain: Domain; user: User } = context.state
|
|
823
|
+
|
|
824
|
+
let inventoryItemChanges: InventoryItemChange[] = await tx.getRepository(InventoryItemChange).find({
|
|
825
|
+
where: { domain, inventoryChange: inventoryChange, status: 'PENDING' },
|
|
826
|
+
relations: ['domain', 'inventoryItem', 'inventory', 'inventory.product', 'inventoryChange']
|
|
827
|
+
})
|
|
828
|
+
await Promise.all(
|
|
829
|
+
inventoryItemChanges.map(async inventoryItemChange => {
|
|
830
|
+
let inventoryItem: InventoryItem = new InventoryItem()
|
|
831
|
+
if (approvalStatus == 'APPROVED') {
|
|
832
|
+
const changeType: string = inventoryItemChange.type
|
|
833
|
+
const existingInventoryItem: InventoryItem = inventoryItemChange.inventoryItem
|
|
834
|
+
if (changeType == INVENTORY_ITEM_CHANGE_TYPE.NEW) {
|
|
835
|
+
const foundSerialNumber: Boolean = await checkExistingSerialNumber(
|
|
836
|
+
inventoryItemChange.serialNumber,
|
|
837
|
+
inventoryChange.inventory.product,
|
|
838
|
+
context,
|
|
839
|
+
inventoryChange.inventory,
|
|
840
|
+
tx
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
if (foundSerialNumber) throw new Error(`Serial number ${inventoryItemChange.serialNumber} is duplicated`)
|
|
844
|
+
|
|
845
|
+
inventoryItem.name = InventoryNoGenerator.inventoryName()
|
|
846
|
+
inventoryItem.serialNumber = inventoryItemChange.serialNumber
|
|
847
|
+
inventoryItem.status = INVENTORY_STATUS.STORED
|
|
848
|
+
inventoryItem.product = inventoryChange.inventory.product
|
|
849
|
+
inventoryItem.domain = inventoryItemChange.domain
|
|
850
|
+
inventoryItem.inventory = inventoryChange.inventory
|
|
851
|
+
inventoryItem.source = INVENTORY_ITEM_SOURCE.ADJUSTMENT
|
|
852
|
+
inventoryItem.creator = user
|
|
853
|
+
inventoryItem.updater = user
|
|
854
|
+
} else if (changeType == INVENTORY_ITEM_CHANGE_TYPE.MODIFIED) {
|
|
855
|
+
const foundSerialNumber: Boolean = await checkExistingSerialNumber(
|
|
856
|
+
inventoryItemChange.serialNumber,
|
|
857
|
+
inventoryItemChange.inventory.product,
|
|
858
|
+
context,
|
|
859
|
+
inventoryItemChange.inventory,
|
|
860
|
+
tx
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
if (foundSerialNumber) throw new Error(`Serial number ${inventoryItemChange.serialNumber} is duplicated`)
|
|
864
|
+
|
|
865
|
+
inventoryItem = {
|
|
866
|
+
...existingInventoryItem,
|
|
867
|
+
serialNumber: inventoryItemChange.serialNumber,
|
|
868
|
+
product: inventoryItemChange.inventory.product
|
|
869
|
+
}
|
|
870
|
+
} else if (changeType == INVENTORY_ITEM_CHANGE_TYPE.REMOVED) {
|
|
871
|
+
inventoryItem = {
|
|
872
|
+
...existingInventoryItem,
|
|
873
|
+
status: INVENTORY_STATUS.DELETED
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
inventoryItem = await tx.getRepository(InventoryItem).save(inventoryItem)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
await tx
|
|
881
|
+
.getRepository(InventoryItemChange)
|
|
882
|
+
.update({ id: inventoryItemChange.id }, { status: approvalStatus, inventoryItem })
|
|
883
|
+
})
|
|
884
|
+
)
|
|
885
|
+
}
|
|
@@ -44,6 +44,7 @@ export class InventoryChangeQuery {
|
|
|
44
44
|
.leftJoinAndSelect('ic.product', 'product')
|
|
45
45
|
.leftJoinAndSelect('ic.location', 'location')
|
|
46
46
|
.leftJoinAndSelect('ic.lastInventoryHistory', 'livh')
|
|
47
|
+
.leftJoinAndSelect('ic.inventoryItemChange', 'itc')
|
|
47
48
|
.leftJoinAndSelect('livh.product', 'livhProduct')
|
|
48
49
|
.leftJoinAndSelect('livh.warehouse', 'livhWarehouse')
|
|
49
50
|
.leftJoinAndSelect('livh.location', 'livhLocation')
|
|
@@ -16,6 +16,7 @@ import { Product, ProductDetail } from '@things-factory/product-base'
|
|
|
16
16
|
import { Domain, ScalarDate } from '@things-factory/shell'
|
|
17
17
|
|
|
18
18
|
import { InventoryHistory } from '../inventory-history/inventory-history'
|
|
19
|
+
import { InventoryItemChange } from '../inventory-item-change/inventory-item-change'
|
|
19
20
|
import { Inventory } from '../inventory/inventory'
|
|
20
21
|
import { Location } from '../location/location'
|
|
21
22
|
|
|
@@ -72,6 +73,10 @@ export class InventoryChange {
|
|
|
72
73
|
@Field(type => Product, { nullable: true })
|
|
73
74
|
product: Product
|
|
74
75
|
|
|
76
|
+
@ManyToOne(type => InventoryItemChange, { nullable: true })
|
|
77
|
+
@Field(type => InventoryItemChange, { nullable: true })
|
|
78
|
+
inventoryItemChange: InventoryItemChange
|
|
79
|
+
|
|
75
80
|
/**
|
|
76
81
|
* To be updated to remove nullable. once all process that links to inventory has been updated to use product detail instead of product
|
|
77
82
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Arg, Args, Ctx, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
|
2
|
-
import { Brackets, getRepository, SelectQueryBuilder } from 'typeorm'
|
|
1
|
+
import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
|
|
2
|
+
import { Brackets, EntityManager, getRepository, In, Not, SelectQueryBuilder } from 'typeorm'
|
|
3
3
|
|
|
4
4
|
import { User } from '@things-factory/auth-base'
|
|
5
|
+
import { Product } from '@things-factory/product-base'
|
|
5
6
|
import { convertListParams, Domain, ListParam } from '@things-factory/shell'
|
|
6
7
|
|
|
7
8
|
import { INVENTORY_STATUS } from '../../constants'
|
|
@@ -25,11 +26,81 @@ export class InventoryItemQuery {
|
|
|
25
26
|
const { domain } = context.state
|
|
26
27
|
|
|
27
28
|
const convertedParams = convertListParams(params, domain.id)
|
|
28
|
-
const [items, total] = await getRepository(InventoryItem).findAndCount(
|
|
29
|
+
const [items, total] = await getRepository(InventoryItem).findAndCount({
|
|
30
|
+
...convertedParams,
|
|
31
|
+
relations: ['inventory', 'product']
|
|
32
|
+
})
|
|
29
33
|
|
|
30
34
|
return { items, total }
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
@Directive('@transaction')
|
|
38
|
+
@Query(returns => InventoryItemList, { description: 'To fetch multiple InventoryItems with InventoryItemChanges' })
|
|
39
|
+
async inventoryItemsWithChanges(@Args() params: ListParam, @Ctx() context: any): Promise<InventoryItemList> {
|
|
40
|
+
const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
|
|
41
|
+
|
|
42
|
+
const page = params?.pagination?.page
|
|
43
|
+
const limit = params?.pagination?.limit
|
|
44
|
+
|
|
45
|
+
let inventoryFilter = params.filters.find(filter => filter.name == 'inventoryId')
|
|
46
|
+
|
|
47
|
+
let items: any = []
|
|
48
|
+
let total: any = 0
|
|
49
|
+
if (inventoryFilter) {
|
|
50
|
+
await tx.query(
|
|
51
|
+
`
|
|
52
|
+
create temp table temp_inventory_items on commit drop as (
|
|
53
|
+
select * from (
|
|
54
|
+
select ii.id, ii.name, ii.serial_number, '' as "type", 'ORIGINAL' as "transaction_type", i2.id as inventory_id, i2.name as inventory_name, i2.pallet_id as pallet_id, i2.carton_id as carton_id, p2.id as product_id, p2.name as product_name, p2.sku as product_sku from inventory_items ii
|
|
55
|
+
inner join inventories i2 on i2.id = ii.inventory_id
|
|
56
|
+
inner join products p2 on p2.id = i2.product_id
|
|
57
|
+
where ii.domain_id = $1 and ii.inventory_id = $2 and ii.status = $3
|
|
58
|
+
) src
|
|
59
|
+
)
|
|
60
|
+
`,
|
|
61
|
+
[domain.id, inventoryFilter.value, INVENTORY_STATUS.STORED]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const offsetQuery: string = params?.pagination ? `OFFSET ${(page - 1) * limit} LIMIT ${limit}` : ''
|
|
65
|
+
|
|
66
|
+
items = await tx.query(`
|
|
67
|
+
select * from temp_inventory_items ${offsetQuery}
|
|
68
|
+
`)
|
|
69
|
+
|
|
70
|
+
total = await tx.query(`
|
|
71
|
+
select count(*) from temp_inventory_items
|
|
72
|
+
`)
|
|
73
|
+
|
|
74
|
+
items = items.map(item => {
|
|
75
|
+
return {
|
|
76
|
+
id: item.id,
|
|
77
|
+
name: item.name,
|
|
78
|
+
serialNumber: item.serial_number,
|
|
79
|
+
type: item.type,
|
|
80
|
+
transactionType: item.transaction_type,
|
|
81
|
+
inventory: {
|
|
82
|
+
id: item.inventory_id,
|
|
83
|
+
name: item.inventory_name,
|
|
84
|
+
palletId: item.pallet_id,
|
|
85
|
+
cartonId: item.carton_id
|
|
86
|
+
},
|
|
87
|
+
product: {
|
|
88
|
+
id: item.product_id,
|
|
89
|
+
name: item.product_name,
|
|
90
|
+
sku: item.product_sku
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
total = total[0].count
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
items,
|
|
100
|
+
total
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
33
104
|
@Query(returns => InventoryItemList, { description: 'To fetch multiple InventoryItems by inventory' })
|
|
34
105
|
async inventoryItemsByInventory(@Args() params: ListParam, @Ctx() context: any): Promise<InventoryItemList> {
|
|
35
106
|
const { domain } = context.state
|
|
@@ -47,6 +118,7 @@ export class InventoryItemQuery {
|
|
|
47
118
|
qb.leftJoinAndSelect('ivi.product', 'product').leftJoinAndSelect('ivi.inventory', 'inventory')
|
|
48
119
|
qb.where('ivi.product_id=:productId', { productId: productIdFilter.value })
|
|
49
120
|
qb.andWhere('inventory.status!=:inventoryStatus', { inventoryStatus: INVENTORY_STATUS.PARTIALLY_UNLOADED })
|
|
121
|
+
qb.andWhere('inventory.domain_id=:domainId', { domainId: domain.id })
|
|
50
122
|
|
|
51
123
|
if (orderTypeFilter.value == 'inbound') {
|
|
52
124
|
qb.andWhere('ivi.inbound_order_id=:orderId', { orderId: orderIdFilter.value })
|
|
@@ -116,3 +188,26 @@ export class InventoryItemQuery {
|
|
|
116
188
|
return await getRepository(User).findOne(inventoryItem.creatorId)
|
|
117
189
|
}
|
|
118
190
|
}
|
|
191
|
+
|
|
192
|
+
export async function checkExistingSerialNumber(
|
|
193
|
+
serialNumber: string,
|
|
194
|
+
product: Product,
|
|
195
|
+
context: any,
|
|
196
|
+
inventory?: Inventory,
|
|
197
|
+
tx?: EntityManager
|
|
198
|
+
) {
|
|
199
|
+
const { domain }: { domain: Domain } = context.state
|
|
200
|
+
|
|
201
|
+
let countCondition: any = {
|
|
202
|
+
domain,
|
|
203
|
+
product,
|
|
204
|
+
serialNumber,
|
|
205
|
+
status: Not(In([INVENTORY_STATUS.DELETED, INVENTORY_STATUS.TERMINATED]))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (inventory) countCondition = { ...countCondition, inventory }
|
|
209
|
+
|
|
210
|
+
const duplicatedSerialNumberCnt: number = await tx.getRepository(InventoryItem).count(countCondition)
|
|
211
|
+
|
|
212
|
+
if (duplicatedSerialNumberCnt) return true
|
|
213
|
+
}
|