fix: align dropship acceptance with precreated pallets

This commit is contained in:
2026-09-11 12:26:52 +08:00
parent e61050c490
commit b57ca7def7
5 changed files with 57 additions and 60 deletions
@@ -90,7 +90,7 @@ async function waitForCompletedMeasurement(context, measurementPath, expectedSta
export async function completeDropshipPalletization(context, { inboundId, inboundCode, warehouseId, measurement }) {
const pendingResponse = await context.request('GET',
`/api/platform/v1/first-leg/pda-dropship/tasks/pending?keyword=${encodeURIComponent(inboundCode)}`)
`/api/platform/v1/first-leg/pda-dropship/tasks/pending?warehouseId=${warehouseId}&keyword=${encodeURIComponent(inboundCode)}`)
const pending = context.apiSuccess(MODULE, '查询一件代发待合托任务', pendingResponse,
`inboundId=${inboundId}`, OPTIONS)
const task = (pending || []).find((item) => asId(item?.inboundId) === inboundId
@@ -110,31 +110,17 @@ export async function completeDropshipPalletization(context, { inboundId, inboun
`taskStatus=${claimed?.taskStatus ?? 'missing'}`, OPTIONS)
const totalCartons = Number(measurement?.actualPackageCount)
const createPath = `/api/platform/v1/first-leg/pda-dropship/tasks/${taskId}/pallets/create-and-bind`
const expectedPalletCount = Number(measurement?.expectedPalletCount)
const taskPath = `/api/platform/v1/first-leg/pda-dropship/tasks/${taskId}?warehouseId=${warehouseId}`
const createPayload = { warehouseId, operationId: `${context.marker}-dropship-create-bind`.slice(0, 128) }
let palletNo = ''
const generatedPallets = Array.isArray(claimed?.pallets) ? claimed.pallets : []
context.assert(MODULE, '审核通过时已按预计托数预生成全部DP托盘码', claimResponse,
generatedPallets.length === expectedPalletCount && generatedPallets.every((item) =>
item?.status === 'OPEN' && Number(item?.cartonCount) === 0 && /^DP-\d{6}-\d{4}$/.test(item?.palletNo || '')),
`expected=${expectedPalletCount}, generated=${generatedPallets.length}`, OPTIONS)
if (!generatedPallets.length) return null
let countedCartons = 0
const sealedCartonMarks = []
let pallet = null
const palletSnapshot = (value) => JSON.stringify({
counted: value?.countedCartons, bound: value?.boundCartons, unbound: value?.unboundCartons,
pallets: value?.pallets?.map((item) => ({ id: asId(item.palletId), no: item.palletNo,
count: item.cartonCount, status: item.status, version: item.version })),
})
async function rejectEmptyCreation(before, suffix) {
const response = await context.request('POST', createPath,
{ ...createPayload, operationId: `${context.marker}-dropship-empty-${suffix}`.slice(0, 128) })
context.assert(MODULE, `无待绑箱创建托盘被拒绝-${suffix}`, response,
response.http === 200 && String(response.body?.code) === 'A0409',
`HTTP=${response.http}, code=${response.body?.code || 'missing'}`, OPTIONS)
const afterResponse = await context.request('GET', taskPath)
const after = context.apiSuccess(MODULE, `拒绝创建后回查任务-${suffix}`, afterResponse, '', OPTIONS)
context.assert(MODULE, `拒绝创建没有空托或计数漂移-${suffix}`, afterResponse,
palletSnapshot(after) === palletSnapshot(before), `pallets=${after?.pallets?.length ?? 'missing'}`, OPTIONS)
}
await rejectEmptyCreation(claimed, 'before-count')
for (let index = 0; index < totalCartons; index += 1) {
const boxCode = `${inboundCode}#${totalCartons}-${index + 1}`
@@ -153,7 +139,7 @@ export async function completeDropshipPalletization(context, { inboundId, inboun
actualSkuQuantity: Number(resolved.expectedSkuQuantity),
discrepancyAction: 'MATCH',
discrepancyRemark: null,
palletNo: index === 0 ? null : palletNo,
palletNo: index === 0 ? null : generatedPallets[Math.min(index, generatedPallets.length - 1)].palletNo,
operationId: `${context.marker}-dropship-count-${index + 1}`.slice(0, 128),
}
const countResponse = await context.request('POST',
@@ -184,55 +170,57 @@ export async function completeDropshipPalletization(context, { inboundId, inboun
&& Number(replayed?.unboundCartons) === Number(counted?.unboundCartons),
`counted=${replayed?.countedCartons ?? 'missing'}, unbound=${replayed?.unboundCartons ?? 'missing'}`, OPTIONS)
const bindResponse = await context.request('POST', createPath, createPayload)
const bound = context.apiSuccess(MODULE, '系统发码创建托盘并绑定全部待组托箱', bindResponse,
const firstPallet = generatedPallets[0]
const bindPayload = { warehouseId, palletNo: firstPallet.palletNo,
operationId: `${context.marker}-dropship-bind-first`.slice(0, 128) }
const bindResponse = await context.request('POST',
`/api/platform/v1/first-leg/pda-dropship/tasks/${taskId}/pallets/bind-pending`, bindPayload)
const bound = context.apiSuccess(MODULE, '后扫预生成托盘码并绑定全部待组托箱', bindResponse,
`taskId=${taskId}`, OPTIONS)
pallet = bound?.pallets?.find((item) => Number(item.cartonCount) === 1) || null
palletNo = pallet?.palletNo || ''
const pallet = bound?.pallets?.find((item) => asId(item.palletId) === asId(firstPallet.palletId)) || null
context.assert(MODULE, '待组托箱绑定到OPEN托盘且不再游离', bindResponse,
Number(bound?.unboundCartons) === 0 && Number(pallet?.cartonCount) === 1
&& pallet?.status === 'OPEN' && /^DP-\d{6}-\d{4}$/.test(palletNo)
&& bound?.pallets?.length === 1,
&& pallet?.status === 'OPEN' && pallet?.palletNo === firstPallet.palletNo
&& bound?.pallets?.length === expectedPalletCount,
`unbound=${bound?.unboundCartons ?? 'missing'}, palletStatus=${pallet?.status ?? 'missing'}`, OPTIONS)
const createReplayResponse = await context.request('POST', createPath, createPayload)
const createReplay = context.apiSuccess(MODULE, '系统发码同operationId重放', createReplayResponse, '', OPTIONS)
context.assert(MODULE, '系统发码重放不新增托盘且业务码和计数稳定', createReplayResponse,
palletSnapshot(createReplay) === palletSnapshot(bound), `palletNo=${palletNo}`, OPTIONS)
await rejectEmptyCreation(bound, 'after-bind')
} else {
pallet = counted?.pallet || pallet
const expectedPallet = generatedPallets[Math.min(index, generatedPallets.length - 1)]
context.assert(MODULE, `第${index + 1}箱直接进入当前OPEN托盘`, countResponse,
counted?.pallet?.palletNo === palletNo && counted?.pallet?.status === 'OPEN',
counted?.pallet?.palletNo === expectedPallet.palletNo && counted?.pallet?.status === 'OPEN',
`palletNo=${counted?.pallet?.palletNo ?? 'missing'}, status=${counted?.pallet?.status ?? 'missing'}`, OPTIONS)
}
}
context.assert(MODULE, '一件代发工作托盘覆盖测量冻结的全部箱数', null,
totalCartons > 0 && countedCartons === totalCartons && Number(pallet?.cartonCount) === totalCartons,
`counted=${countedCartons}, palletCartons=${pallet?.cartonCount ?? 'missing'}, expected=${totalCartons || 'missing'}`, OPTIONS)
const countedTaskResponse = await context.request('GET', taskPath)
let sealed = context.apiSuccess(MODULE, '清点完成后回查预生成托盘分配', countedTaskResponse, '', OPTIONS)
context.assert(MODULE, '预生成托盘覆盖测量冻结的全部箱数', countedTaskResponse,
totalCartons > 0 && countedCartons === totalCartons
&& Number(sealed?.boundCartons) === totalCartons
&& sealed?.pallets?.length === expectedPalletCount
&& sealed?.pallets?.every((item) => Number(item?.cartonCount) > 0),
`counted=${countedCartons}, bound=${sealed?.boundCartons ?? 'missing'}, expected=${totalCartons || 'missing'}`, OPTIONS)
const palletId = asId(pallet?.palletId)
if (!palletId) return null
const sealResponse = await context.request('POST',
`/api/platform/v1/first-leg/pda-dropship/tasks/${taskId}/pallets/${palletId}/seal`, {
warehouseId, expectedVersion: Number(pallet?.version),
operationId: `${context.marker}-dropship-seal`.slice(0, 128),
})
const sealed = context.apiSuccess(MODULE, '封托并完成一件代发合托任务', sealResponse,
`taskId=${taskId}, palletId=${palletId}`, OPTIONS)
context.assert(MODULE, '全部箱数封托后入仓单允许进入WAIT_SPLIT', sealResponse,
let lastSealResponse = countedTaskResponse
for (let index = 0; index < (sealed?.pallets || []).length; index += 1) {
const pallet = sealed.pallets.find((item) => item.status === 'OPEN')
if (!pallet) break
const sealResponse = await context.request('POST',
`/api/platform/v1/first-leg/pda-dropship/tasks/${taskId}/pallets/${asId(pallet.palletId)}/seal`, {
warehouseId, expectedVersion: Number(pallet.version),
operationId: `${context.marker}-dropship-seal-${index + 1}`.slice(0, 128),
})
lastSealResponse = sealResponse
sealed = context.apiSuccess(MODULE, `封托第${index + 1}个预生成托盘`, sealResponse,
`taskId=${taskId}, palletNo=${pallet.palletNo}`, OPTIONS)
}
context.assert(MODULE, '全部箱数封托后入仓单允许进入WAIT_SPLIT', lastSealResponse,
sealed?.taskStatus === 'COMPLETED'
&& Number(sealed?.countedCartons) === Number(sealed?.totalCartons)
&& Number(sealed?.boundCartons) === Number(sealed?.totalCartons)
&& Number(sealed?.unboundCartons) === 0
&& sealed?.pallets?.every((item) => item?.status === 'SEALED'),
`taskStatus=${sealed?.taskStatus ?? 'missing'}, counted=${sealed?.countedCartons ?? 'missing'}, bound=${sealed?.boundCartons ?? 'missing'}`, OPTIONS)
const sealedReplayResponse = await context.request('POST', createPath, createPayload)
const sealedReplay = context.apiSuccess(MODULE, '封托完成后系统发码原操作仍可重放', sealedReplayResponse, '', OPTIONS)
context.assert(MODULE, '完成后重放不重开任务或新建空托', sealedReplayResponse,
sealedReplay?.taskStatus === 'COMPLETED' && palletSnapshot(sealedReplay) === palletSnapshot(sealed),
`taskStatus=${sealedReplay?.taskStatus || 'missing'}, palletNo=${palletNo}`, OPTIONS)
return { ...sealed, sealedCartonMarks, sealedSourcePalletNos: [palletNo] }
return { ...sealed, sealedCartonMarks,
sealedSourcePalletNos: (sealed?.pallets || []).map((item) => item.palletNo).filter(Boolean) }
}
async function provisionWarehouseResources(context, { inboundId, tenantId, warehouseId, palletCount }) {
@@ -846,9 +834,13 @@ export async function run(context) {
? 'ADD' : measuredItem.coefficientMode ?? null,
remark: context.marker,
}]
const palletCount = Math.max(1, Math.min(originalPackageCount,
Number(context.context?.waitSplitVariant?.palletCount || 1)))
const draftPayload = {
measurementId: measured.measurementId, version: Number(measured.version),
actualGoodsName: `墨家测量验收-${context.marker.slice(-12)}`,
expectedPalletCount: context.createdResources?.orders?.fulfillmentType === 'ONE_CLICK_DROPSHIP'
? palletCount : null,
items: draftItems,
}
const draftResponse = await context.request('PUT',
@@ -864,7 +856,6 @@ export async function run(context) {
return
}
const palletCount = Math.max(1, Number(context.context?.waitSplitVariant?.palletCount || 1))
const warehouseResourcesReady = await provisionWarehouseResources(context, {
inboundId, tenantId, warehouseId: asId(summary?.warehouseId), palletCount,
})