fix: reuse overseas receiving resources in acceptance
This commit is contained in:
@@ -90,47 +90,29 @@ async function resolvePallet(context, token, palletCode, name = '扫码解析托
|
||||
'/api/pda/v1/last-leg/pallet-operations/pallets/resolve', { code: palletCode })
|
||||
}
|
||||
|
||||
async function createLocation(context, warehouseId, suffix) {
|
||||
const locationCode = `PDAOPS-${context.marker}-${suffix}`.replace(/[^A-Za-z0-9_-]/g, '').slice(-64)
|
||||
const response = await context.requestOverseas('POST',
|
||||
`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/locations`, {
|
||||
zoneCode: 'PDAOPS', locationCode, locationType: 'STORAGE', maxLoadKg: 5000,
|
||||
status: 'IDLE', remark: `${context.marker}-pda-operations-${suffix}`,
|
||||
})
|
||||
const locationId = id(response?.body?.data)
|
||||
context.addStep(MODULE, `创建${suffix}目标库位`, response,
|
||||
success(response) && /^\d+$/.test(locationId) && locationId !== '0',
|
||||
`locationId=${locationId || 'missing'}, locationCode=${locationCode}; ${trace(response)}`, OPTIONS)
|
||||
return success(response) ? { locationId, locationCode } : null
|
||||
async function resolvePublicLocation(context, fixture) {
|
||||
const response = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/warehouse-admin/${encodeURIComponent(fixture.overseasWarehouseId)}/locations?page=1&size=200`)
|
||||
const location = records(response?.body?.data).find((item) => item?.locationType === 'VIRTUAL'
|
||||
&& item?.status === 'ACTIVE')
|
||||
context.addStep(MODULE, '复用当前海外仓公共库位', response,
|
||||
success(response) && longId(id(location?.id)) && nonBlank(location?.locationCode),
|
||||
`locationId=${id(location?.id) || 'missing'}, locationCode=${location?.locationCode || 'missing'}; ${trace(response)}`, OPTIONS)
|
||||
return success(response) && location
|
||||
? { locationId: id(location.id), locationCode: location.locationCode } : null
|
||||
}
|
||||
|
||||
export function auxiliaryPalletFbaWarehouseCode(destinationType, fbaWarehouseCode) {
|
||||
if (destinationType === 'OVERSEAS') return null
|
||||
if (destinationType === 'FBA' && nonBlank(fbaWarehouseCode)) return fbaWarehouseCode.trim()
|
||||
return null
|
||||
}
|
||||
|
||||
async function createEmptyPallet(context, token, fixture, suffix) {
|
||||
const fbaWarehouseCode = auxiliaryPalletFbaWarehouseCode(
|
||||
fixture.destinationType, fixture.fbaWarehouseCode)
|
||||
if (fixture.destinationType === 'FBA' && !fbaWarehouseCode) {
|
||||
blocker(context, `${suffix}辅助空托盘`,
|
||||
`当前FBA入仓码 ${fixture.inboundCode || 'missing'} 缺少权威fbaWarehouseCode`)
|
||||
return null
|
||||
async function resolveReusableAuxiliaryPallet(context, token, fixture) {
|
||||
for (const candidate of fixture.auxiliaryPallets || []) {
|
||||
if (!nonBlank(candidate?.palletCode) || id(candidate?.palletId) === id(fixture.pallet?.palletId)) continue
|
||||
const resolved = await resolvePallet(context, token, candidate.palletCode, '解析入仓计划已有空托盘')
|
||||
if (resolved.data && Array.isArray(resolved.data.inventory) && resolved.data.inventory.length === 0) {
|
||||
context.assert(MODULE, '辅助托盘只复用本轮入仓计划已有空托盘', resolved.response, true,
|
||||
`palletCode=${resolved.data.palletCode}; ${trace(resolved.response)}`, OPTIONS)
|
||||
return resolved.data
|
||||
}
|
||||
}
|
||||
const location = await createLocation(context, fixture.overseasWarehouseId, `${suffix}-PALLET`)
|
||||
if (!location) return null
|
||||
const palletCode = `PDAOPS-${context.marker}-${suffix}`.replace(/[^A-Za-z0-9_-]/g, '').slice(-120)
|
||||
const response = await context.requestOverseas('POST',
|
||||
`/api/platform/v1/warehouse-admin/${encodeURIComponent(fixture.overseasWarehouseId)}/pallets`, {
|
||||
palletCode, palletType: '木托盘', capacityCbm: 10, unitPrice: 0, status: 'IDLE',
|
||||
locationId: location.locationId,
|
||||
fbaWarehouseCode,
|
||||
remark: `${context.marker}-pda-operations-${suffix}`,
|
||||
})
|
||||
context.addStep(MODULE, `创建${suffix}辅助空托盘`, response, success(response), trace(response), OPTIONS)
|
||||
if (!success(response)) return null
|
||||
return (await resolvePallet(context, token, palletCode, `解析${suffix}辅助空托盘`)).data
|
||||
return null
|
||||
}
|
||||
|
||||
async function verifyStocktake(context, token, fixture) {
|
||||
@@ -201,7 +183,7 @@ async function verifyStocktake(context, token, fixture) {
|
||||
}
|
||||
|
||||
async function verifyInventoryMove(context, token, fixture) {
|
||||
const target = await createLocation(context, fixture.overseasWarehouseId, 'MOVE')
|
||||
const target = await resolvePublicLocation(context, fixture)
|
||||
if (!target) return false
|
||||
let pallet = (await resolvePallet(context, token, fixture.pallet.palletCode, 'P3移库前解析整托')).data
|
||||
if (!pallet || !nonBlank(pallet.locationCode)) {
|
||||
@@ -209,6 +191,14 @@ async function verifyInventoryMove(context, token, fixture) {
|
||||
return false
|
||||
}
|
||||
const originalLocationCode = pallet.locationCode
|
||||
if (originalLocationCode === target.locationCode) {
|
||||
const availablePackages = (pallet.inventory || []).reduce((sum, item) =>
|
||||
sum + Number(item?.availablePackages || 0), 0)
|
||||
context.assert(MODULE, 'P3托盘已在公共库位不重复移库', null,
|
||||
availablePackages > 0,
|
||||
`palletCode=${pallet.palletCode}, locationCode=${target.locationCode}, availablePackages=${availablePackages}`, OPTIONS)
|
||||
return availablePackages > 0
|
||||
}
|
||||
const path = '/api/pda/v1/last-leg/inventory-moves'
|
||||
const body = { sourcePalletCode: pallet.palletCode, targetLocationCode: target.locationCode,
|
||||
expectedPalletVersion: Number(pallet.palletVersion), idempotencyKey: key(context, 'MOVE-FORWARD'),
|
||||
@@ -258,23 +248,21 @@ async function verifyPalletOperations(context, token, fixture) {
|
||||
'主托盘只有一箱可用库存,解绑会清空唯一业务托盘;必须由本轮生产矩阵提供至少两箱')
|
||||
return false
|
||||
}
|
||||
const swapTarget = await createEmptyPallet(context, token, fixture, 'SWAP')
|
||||
const splitTarget = await createEmptyPallet(context, token, fixture, 'SPLIT')
|
||||
const unbindTarget = await createEmptyPallet(context, token, fixture, 'UNBIND')
|
||||
if (!swapTarget || !splitTarget || !unbindTarget) {
|
||||
blocker(context, 'P4辅助实物托盘', '无法创建换托、拆托和解绑所需辅助托盘')
|
||||
let auxiliary = await resolveReusableAuxiliaryPallet(context, token, fixture)
|
||||
if (!auxiliary) {
|
||||
blocker(context, 'P4辅助实物托盘', '本轮入仓计划没有可复用的已有空托盘')
|
||||
return false
|
||||
}
|
||||
|
||||
const swapBody = { idempotencyKey: key(context, 'PALLET-SWAP'),
|
||||
source: { palletCode: main.palletCode, expectedVersion: Number(main.palletVersion) },
|
||||
target: { palletCode: swapTarget.palletCode, expectedVersion: Number(swapTarget.palletVersion) },
|
||||
target: { palletCode: auxiliary.palletCode, expectedVersion: Number(auxiliary.palletVersion) },
|
||||
reason: `${context.marker}-swap` }
|
||||
const swapped = await replayOperation(context, token, 'P4整托换托', `${base}/swap`, swapBody, 'PALLET_SWAP')
|
||||
await expectConflict(context, token, 'POST', 'P4换托同键异载荷A0409', `${base}/swap`,
|
||||
{ ...swapBody, reason: `${swapBody.reason}-DIFFERENT` })
|
||||
let occupiedSwap = operationPallet(swapped.data, swapTarget.palletCode)
|
||||
|| (await resolvePallet(context, token, swapTarget.palletCode, 'P4换托后解析目标托盘')).data
|
||||
let occupiedSwap = operationPallet(swapped.data, auxiliary.palletCode)
|
||||
|| (await resolvePallet(context, token, auxiliary.palletCode, 'P4换托后解析目标托盘')).data
|
||||
let emptyMain = operationPallet(swapped.data, main.palletCode)
|
||||
|| (await resolvePallet(context, token, main.palletCode, 'P4换托后解析原托盘')).data
|
||||
await expectConflict(context, token, 'POST', 'P4换托旧托盘版本A0409', `${base}/swap`, {
|
||||
@@ -288,19 +276,20 @@ async function verifyPalletOperations(context, token, fixture) {
|
||||
await replayOperation(context, token, 'P4换托恢复主托盘', `${base}/swap`, swapBackBody, 'PALLET_SWAP')
|
||||
|
||||
main = (await resolvePallet(context, token, main.palletCode, 'P4拆托前刷新主托盘')).data
|
||||
auxiliary = (await resolvePallet(context, token, auxiliary.palletCode, 'P4拆托前刷新已有空托盘')).data
|
||||
const sourceLine = main.inventory.find((line) => Number(line.availablePackages) > 1)
|
||||
|| main.inventory.find((line) => Number(line.availablePackages) > 0)
|
||||
const onePackage = 1
|
||||
const disassembleBody = { idempotencyKey: key(context, 'PALLET-DISASSEMBLE'),
|
||||
source: { palletCode: main.palletCode, expectedVersion: Number(main.palletVersion) },
|
||||
targets: [{ target: { palletCode: splitTarget.palletCode, expectedVersion: Number(splitTarget.palletVersion) },
|
||||
targets: [{ target: { palletCode: auxiliary.palletCode, expectedVersion: Number(auxiliary.palletVersion) },
|
||||
lines: [{ positionId: id(sourceLine.positionId), packages: onePackage,
|
||||
expectedPositionVersion: Number(sourceLine.positionVersion) }] }],
|
||||
reason: `${context.marker}-disassemble` }
|
||||
const disassembled = await replayOperation(context, token, 'P4拆托一箱到辅助托盘',
|
||||
`${base}/disassemble`, disassembleBody, 'PALLET_DISASSEMBLE')
|
||||
const splitOccupied = operationPallet(disassembled.data, splitTarget.palletCode)
|
||||
|| (await resolvePallet(context, token, splitTarget.palletCode, 'P4拆托后解析辅助托盘')).data
|
||||
const splitOccupied = operationPallet(disassembled.data, auxiliary.palletCode)
|
||||
|| (await resolvePallet(context, token, auxiliary.palletCode, 'P4拆托后解析辅助托盘')).data
|
||||
const mainAfterSplit = operationPallet(disassembled.data, main.palletCode)
|
||||
|| (await resolvePallet(context, token, main.palletCode, 'P4拆托后解析主托盘')).data
|
||||
const splitLine = splitOccupied.inventory.find((line) => Number(line.availablePackages) > 0)
|
||||
@@ -313,18 +302,19 @@ async function verifyPalletOperations(context, token, fixture) {
|
||||
await replayOperation(context, token, 'P4合托恢复主托盘', `${base}/assemble`, assembleBody, 'PALLET_ASSEMBLE')
|
||||
|
||||
main = (await resolvePallet(context, token, main.palletCode, 'P4解绑前刷新主托盘')).data
|
||||
auxiliary = (await resolvePallet(context, token, auxiliary.palletCode, 'P4解绑前刷新已有空托盘')).data
|
||||
const unbindSource = main.inventory.find((line) => Number(line.availablePackages) > 1)
|
||||
|| main.inventory.find((line) => Number(line.availablePackages) > 0)
|
||||
const unbindPreparedBody = { idempotencyKey: key(context, 'PALLET-UNBIND-PREPARE'),
|
||||
source: { palletCode: main.palletCode, expectedVersion: Number(main.palletVersion) },
|
||||
targets: [{ target: { palletCode: unbindTarget.palletCode, expectedVersion: Number(unbindTarget.palletVersion) },
|
||||
targets: [{ target: { palletCode: auxiliary.palletCode, expectedVersion: Number(auxiliary.palletVersion) },
|
||||
lines: [{ positionId: id(unbindSource.positionId), packages: 1,
|
||||
expectedPositionVersion: Number(unbindSource.positionVersion) }] }],
|
||||
reason: `${context.marker}-unbind-prepare` }
|
||||
const prepared = await replayOperation(context, token, 'P4拆出一箱作为解绑样本',
|
||||
`${base}/disassemble`, unbindPreparedBody, 'PALLET_DISASSEMBLE')
|
||||
const boundAuxiliary = operationPallet(prepared.data, unbindTarget.palletCode)
|
||||
|| (await resolvePallet(context, token, unbindTarget.palletCode, 'P4解析解绑样本托盘')).data
|
||||
const boundAuxiliary = operationPallet(prepared.data, auxiliary.palletCode)
|
||||
|| (await resolvePallet(context, token, auxiliary.palletCode, 'P4解析解绑样本托盘')).data
|
||||
const unbindBody = { idempotencyKey: key(context, 'PALLET-UNBIND'),
|
||||
source: { palletCode: boundAuxiliary.palletCode, expectedVersion: Number(boundAuxiliary.palletVersion) },
|
||||
reason: `${context.marker}-unbind` }
|
||||
@@ -393,6 +383,7 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
||||
customerOrderNo: String(customerOrderNo).slice(0, 128), operationWarehouseId: id(fixture.overseasWarehouseId),
|
||||
labelDeadline: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 19),
|
||||
contactName: 'PDA换标验收', contactPhone: '13800000006', chargeAmount: 0.01, remark: context.marker,
|
||||
deliveryMethod: 'TRUCK', printShippingLabel: false,
|
||||
idempotencyKey: key(context, 'RELABEL-CREATE'),
|
||||
items: sourceLines.map(({ source, position, packageCount }, index) => {
|
||||
const newSkuCode = `${source.skuCode}-NEW-${markerSuffix}-${index + 1}`.slice(0, 128)
|
||||
|
||||
Reference in New Issue
Block a user