@things-factory/worksheet-base 4.3.794 → 4.3.797
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/controllers/outbound/picking-worksheet-controller.js +32 -35
- package/dist-server/controllers/outbound/picking-worksheet-controller.js.map +1 -1
- package/dist-server/controllers/replenishment/replenishment-worksheet-controller.js +1 -0
- package/dist-server/controllers/replenishment/replenishment-worksheet-controller.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking/activate-batch-picking.js +71 -47
- package/dist-server/graphql/resolvers/worksheet/picking/activate-batch-picking.js.map +1 -1
- package/dist-server/graphql/resolvers/worksheet/picking-worksheet.js +41 -32
- package/dist-server/graphql/resolvers/worksheet/picking-worksheet.js.map +1 -1
- package/package.json +3 -3
- package/server/controllers/outbound/picking-worksheet-controller.ts +35 -42
- package/server/controllers/replenishment/replenishment-worksheet-controller.ts +1 -0
- package/server/graphql/resolvers/worksheet/picking/activate-batch-picking.ts +88 -63
- package/server/graphql/resolvers/worksheet/picking-worksheet.ts +43 -34
|
@@ -26,51 +26,81 @@ export async function activateBatchPicking(
|
|
|
26
26
|
): Promise<Worksheet> {
|
|
27
27
|
try {
|
|
28
28
|
const worksheetController: PickingWorksheetController = new PickingWorksheetController(tx, domain, user)
|
|
29
|
-
const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
|
|
30
29
|
const worksheet = await worksheetController.activateBatchPicking(worksheetNo)
|
|
31
30
|
const worksheetDetails = worksheet.worksheetDetails
|
|
32
31
|
const companyDomain: Domain = worksheet?.bizplace.company.domain
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
// Collect release good IDs from the controller result for post-commit processing
|
|
34
|
+
const orderInventories: any[] = worksheetDetails.map(wsd => wsd.targetInventory)
|
|
35
|
+
const releaseGoodIds: string[] = orderInventories.reduce((data, orderInventory) => {
|
|
36
|
+
if (orderInventory?.releaseGood?.id && !data.includes(orderInventory.releaseGood.id)) {
|
|
37
|
+
data.push(orderInventory.releaseGood.id)
|
|
38
|
+
}
|
|
39
|
+
return data
|
|
40
|
+
}, [])
|
|
41
|
+
|
|
42
|
+
// Schedule all marketplace/LMD side effects to run AFTER the transaction commits.
|
|
43
|
+
// This prevents deadlocks caused by nested transactions competing for locks
|
|
44
|
+
// held by the outer @transaction decorator.
|
|
45
|
+
setImmediate(() => {
|
|
46
|
+
processPostActivationSideEffects(
|
|
47
|
+
domain,
|
|
48
|
+
user,
|
|
49
|
+
companyDomain,
|
|
50
|
+
worksheetNo,
|
|
51
|
+
worksheetDetails,
|
|
52
|
+
releaseGoodIds
|
|
53
|
+
).catch(error => {
|
|
54
|
+
logger.error(`activate-batch-picking[postActivation]: ${worksheetNo}: ${error}`)
|
|
55
|
+
})
|
|
40
56
|
})
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
return worksheet
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error(`activate-batch-picking[activateBatchPicking]: ${worksheetNo + ':' + error}`)
|
|
61
|
+
throw new Error('Something went wrong. Please contact support.')
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handles marketplace stock sync, MMS order packages, and LMD parcel creation.
|
|
67
|
+
* Runs outside the main activation transaction to prevent deadlocks.
|
|
68
|
+
*/
|
|
69
|
+
async function processPostActivationSideEffects(
|
|
70
|
+
domain: Domain,
|
|
71
|
+
user: User,
|
|
72
|
+
companyDomain: Domain,
|
|
73
|
+
worksheetNo: string,
|
|
74
|
+
worksheetDetails: any[],
|
|
75
|
+
releaseGoodIds: string[]
|
|
76
|
+
) {
|
|
77
|
+
try {
|
|
78
|
+
// Find marketplace store connections
|
|
79
|
+
const marketplaceStores: MarketplaceStore[] = await getConnection()
|
|
80
|
+
.getRepository(MarketplaceStore)
|
|
81
|
+
.find({
|
|
82
|
+
where: { domain: companyDomain, status: 'ACTIVE', isAutoUpdateStockQty: true },
|
|
83
|
+
relations: ['marketplaceDistributors']
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Update marketplace product variation stock
|
|
87
|
+
if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
|
|
49
88
|
try {
|
|
50
89
|
await getConnection().transaction(async (tx2: EntityManager) => {
|
|
51
|
-
|
|
90
|
+
const orderInventories: any[] = worksheetDetails.map(wsd => wsd.targetInventory)
|
|
52
91
|
const ecommerceCtrl: EcommerceController = new EcommerceController(tx2, domain, user)
|
|
53
92
|
await ecommerceCtrl.updateProductVariationStock(marketplaceStores, orderInventories, companyDomain)
|
|
54
93
|
})
|
|
55
94
|
} catch (error) {
|
|
56
|
-
logger.error(`activate-batch-picking[
|
|
95
|
+
logger.error(`activate-batch-picking[marketplaceStockUpdate]: ${worksheetNo}: ${error}`)
|
|
57
96
|
}
|
|
58
97
|
}
|
|
59
98
|
|
|
60
|
-
if (
|
|
61
|
-
updateMarketplaceProductVariationStock(worksheetDetails, domain, user, marketplaceStores, companyDomain)
|
|
62
|
-
}
|
|
99
|
+
if (!releaseGoodIds.length) return
|
|
63
100
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!data.find(x => x.id == orderInventory.releaseGood.id)) {
|
|
68
|
-
data.push(orderInventory.releaseGood.id)
|
|
69
|
-
}
|
|
70
|
-
return data
|
|
71
|
-
}, [])
|
|
72
|
-
|
|
73
|
-
releaseGoods = await tx.getRepository(ReleaseGood).find({
|
|
101
|
+
// Load release goods and process marketplace order packages
|
|
102
|
+
await getConnection().transaction(async (tx: EntityManager) => {
|
|
103
|
+
const releaseGoods: ReleaseGood[] = await tx.getRepository(ReleaseGood).find({
|
|
74
104
|
where: { id: In(releaseGoodIds) },
|
|
75
105
|
relations: [
|
|
76
106
|
'domain',
|
|
@@ -88,48 +118,43 @@ export async function activateBatchPicking(
|
|
|
88
118
|
]
|
|
89
119
|
})
|
|
90
120
|
|
|
91
|
-
if (releaseGoods?.length)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
break
|
|
121
|
+
if (!releaseGoods?.length) return
|
|
122
|
+
|
|
123
|
+
// Batch load marketplace orders for MMS release goods (avoids N+1 queries)
|
|
124
|
+
const mmsReleaseGoods = releaseGoods.filter(rg => rg?.source === ApplicationType.MMS)
|
|
125
|
+
|
|
126
|
+
if (mmsReleaseGoods.length && marketplaceStores?.length) {
|
|
127
|
+
const refNos = mmsReleaseGoods.map(rg => rg.refNo).filter(Boolean)
|
|
128
|
+
const marketplaceOrders: MarketplaceOrder[] = refNos.length
|
|
129
|
+
? await tx.getRepository(MarketplaceOrder).find({
|
|
130
|
+
where: { orderNo: In(refNos), domain: companyDomain },
|
|
131
|
+
relations: ['marketplaceStore']
|
|
132
|
+
})
|
|
133
|
+
: []
|
|
134
|
+
|
|
135
|
+
const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
|
|
136
|
+
for (const releaseGood of mmsReleaseGoods) {
|
|
137
|
+
try {
|
|
138
|
+
const marketplaceOrder = marketplaceOrders.find(mo => mo.orderNo === releaseGood.refNo)
|
|
139
|
+
if (marketplaceOrder) {
|
|
140
|
+
const marketplaceStore: MarketplaceStore = marketplaceOrder.marketplaceStore
|
|
141
|
+
await ecommerceCtrl.createOrderPackage(tx, marketplaceOrder, companyDomain, marketplaceStore, releaseGood)
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logger.error(`activate-batch-picking[mmsOrderPackage]: ${worksheetNo}: ${error}`)
|
|
116
145
|
}
|
|
117
146
|
}
|
|
118
|
-
// Process LMD orders
|
|
119
|
-
processLmdOrders(releaseGoods, domain, companyDomain, user)
|
|
120
147
|
}
|
|
121
|
-
} catch (e) {
|
|
122
|
-
logger.error(e)
|
|
123
|
-
}
|
|
124
148
|
|
|
125
|
-
|
|
149
|
+
// Process LMD orders
|
|
150
|
+
processLmdOrders(releaseGoods, domain, companyDomain, user)
|
|
151
|
+
})
|
|
126
152
|
} catch (error) {
|
|
127
|
-
logger.error(`activate-batch-picking[
|
|
128
|
-
throw new Error('Something went wrong. Please contact support.')
|
|
153
|
+
logger.error(`activate-batch-picking[postActivation]: ${worksheetNo}: ${error}`)
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
//
|
|
157
|
+
// Trigger create parcel for LMD orders
|
|
133
158
|
async function processLmdOrders(releaseGoods: ReleaseGood[], domain: Domain, companyDomain: Domain, user: User) {
|
|
134
159
|
// Filter only those that need processing
|
|
135
160
|
const validReleaseGoods = releaseGoods.filter(
|
|
@@ -106,6 +106,48 @@ export async function pickingWorksheet(domain: Domain, orderNo: String, location
|
|
|
106
106
|
.andWhere('"T_INV"."status" != :t_invstatus', { t_invstatus: ORDER_INVENTORY_STATUS.CANCELLED })
|
|
107
107
|
.getMany()
|
|
108
108
|
|
|
109
|
+
const inventoryIds = worksheetDetails
|
|
110
|
+
.map((wd: WorksheetDetail) => wd.targetInventory?.inventory?.id)
|
|
111
|
+
.filter((id): id is string => id != null)
|
|
112
|
+
const inventoryIdsWithMissing = new Set<string>()
|
|
113
|
+
if (inventoryIds.length > 0) {
|
|
114
|
+
const rows = await tx
|
|
115
|
+
.getRepository(InventoryChange)
|
|
116
|
+
.createQueryBuilder('ic')
|
|
117
|
+
.select('DISTINCT ic.inventory_id', 'inventoryId')
|
|
118
|
+
.where('ic.inventory_id IN (:...ids)', { ids: inventoryIds })
|
|
119
|
+
.andWhere('ic.status = :status', { status: 'PENDING' })
|
|
120
|
+
.andWhere('ic.transaction_type = :transactionType', { transactionType: 'MISSING' })
|
|
121
|
+
.getRawMany()
|
|
122
|
+
rows.forEach((r: { inventoryId: string }) => inventoryIdsWithMissing.add(r.inventoryId))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const worksheetDetailInfos = worksheetDetails.map((pickingWSD: WorksheetDetail) => {
|
|
126
|
+
const targetInventory: OrderInventory = pickingWSD.targetInventory
|
|
127
|
+
const inventory: Inventory = targetInventory.inventory
|
|
128
|
+
return {
|
|
129
|
+
name: pickingWSD.name,
|
|
130
|
+
palletId: inventory?.palletId,
|
|
131
|
+
cartonId: inventory?.cartonId,
|
|
132
|
+
batchId: inventory?.batchId,
|
|
133
|
+
batchIdRef: inventory?.batchIdRef,
|
|
134
|
+
product: inventory?.product,
|
|
135
|
+
qty: inventory?.qty,
|
|
136
|
+
binLocation: targetInventory?.binLocation || '',
|
|
137
|
+
releaseQty: targetInventory.releaseQty,
|
|
138
|
+
pickedQty: targetInventory.pickedQty,
|
|
139
|
+
status: pickingWSD.status,
|
|
140
|
+
description: pickingWSD.description,
|
|
141
|
+
targetName: targetInventory.name,
|
|
142
|
+
packingType: inventory?.packingType,
|
|
143
|
+
packingSize: inventory?.packingSize,
|
|
144
|
+
expirationDate: inventory?.expirationDate,
|
|
145
|
+
location: inventory?.location,
|
|
146
|
+
relatedOrderInv: targetInventory,
|
|
147
|
+
hasMissingInventoryChanges: inventory?.id ? inventoryIdsWithMissing.has(inventory.id) : false
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
109
151
|
return {
|
|
110
152
|
worksheetInfo: {
|
|
111
153
|
worksheet,
|
|
@@ -123,40 +165,7 @@ export async function pickingWorksheet(domain: Domain, orderNo: String, location
|
|
|
123
165
|
customerCompanyDomainId: releaseGood.bizplace?.company?.domain?.id,
|
|
124
166
|
releaseGood
|
|
125
167
|
},
|
|
126
|
-
worksheetDetailInfos
|
|
127
|
-
const targetInventory: OrderInventory = pickingWSD.targetInventory
|
|
128
|
-
const inventory: Inventory = targetInventory.inventory
|
|
129
|
-
|
|
130
|
-
const inventoryChangesCount: number = await tx.getRepository(InventoryChange).count({
|
|
131
|
-
where: {
|
|
132
|
-
inventory: inventory.id,
|
|
133
|
-
status: 'PENDING',
|
|
134
|
-
transactionType: 'MISSING'
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
name: pickingWSD.name,
|
|
140
|
-
palletId: inventory?.palletId,
|
|
141
|
-
cartonId: inventory?.cartonId,
|
|
142
|
-
batchId: inventory?.batchId,
|
|
143
|
-
batchIdRef: inventory?.batchIdRef,
|
|
144
|
-
product: inventory?.product,
|
|
145
|
-
qty: inventory?.qty,
|
|
146
|
-
binLocation: targetInventory?.binLocation || '',
|
|
147
|
-
releaseQty: targetInventory.releaseQty,
|
|
148
|
-
pickedQty: targetInventory.pickedQty,
|
|
149
|
-
status: pickingWSD.status,
|
|
150
|
-
description: pickingWSD.description,
|
|
151
|
-
targetName: targetInventory.name,
|
|
152
|
-
packingType: inventory?.packingType,
|
|
153
|
-
packingSize: inventory?.packingSize,
|
|
154
|
-
expirationDate: inventory?.expirationDate,
|
|
155
|
-
location: inventory?.location,
|
|
156
|
-
relatedOrderInv: targetInventory,
|
|
157
|
-
hasMissingInventoryChanges: inventoryChangesCount > 0 ? true : false
|
|
158
|
-
}
|
|
159
|
-
})
|
|
168
|
+
worksheetDetailInfos
|
|
160
169
|
}
|
|
161
170
|
}
|
|
162
171
|
}
|