|
|
|
@@ -29,6 +29,15 @@ function operationPallet(result, palletCode) {
|
|
|
|
|
return (result?.pallets || []).find((item) => item?.palletCode === palletCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function relabelItemForUnit(detail, unitIndex) {
|
|
|
|
|
let remaining = unitIndex
|
|
|
|
|
for (const item of detail?.items || []) {
|
|
|
|
|
if (remaining < Number(item?.expectedPackages || 0)) return item
|
|
|
|
|
remaining -= Number(item?.expectedPackages || 0)
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function blocker(context, name, reason, response = null) {
|
|
|
|
|
context.addCoverageGap(MODULE, SCENARIO, name, reason, 'fixture-unavailable', { deploymentBlocker: true })
|
|
|
|
|
context.assert(MODULE, `${name}覆盖前置`, response, false, `${reason}; ${trace(response)}`, OPTIONS)
|
|
|
|
@@ -344,35 +353,51 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|
|
|
|
}), undefined, {}, merchantToken)
|
|
|
|
|
const inventoryCandidates = success(inventoryCandidatesResponse)
|
|
|
|
|
&& Array.isArray(inventoryCandidatesResponse.body?.data) ? inventoryCandidatesResponse.body.data : []
|
|
|
|
|
const source = inventoryCandidates.find((item) => id(item?.inventoryBalanceId) === id(inventory?.balanceId))
|
|
|
|
|
|| inventoryCandidates.find((item) => item?.skuCode === inventory.skuCode
|
|
|
|
|
&& item?.inboundCode === fixture.inboundCode)
|
|
|
|
|
const sourcePosition = source?.positions?.find((item) => id(item?.positionId) === id(inventory?.positionId)
|
|
|
|
|
&& Number(item?.availablePackages) >= 3)
|
|
|
|
|
|| source?.positions?.find((item) => Number(item?.availablePackages) >= 3)
|
|
|
|
|
const orderedCandidates = [...inventoryCandidates].sort((left, right) => {
|
|
|
|
|
const leftPreferred = id(left?.inventoryBalanceId) === id(inventory?.balanceId) ? 1 : 0
|
|
|
|
|
const rightPreferred = id(right?.inventoryBalanceId) === id(inventory?.balanceId) ? 1 : 0
|
|
|
|
|
return rightPreferred - leftPreferred
|
|
|
|
|
})
|
|
|
|
|
const sourceLines = []
|
|
|
|
|
let requiredPackages = 3
|
|
|
|
|
for (const source of orderedCandidates) {
|
|
|
|
|
if (!id(source?.inventoryBalanceId) || !id(source?.inboundOrderId)) continue
|
|
|
|
|
for (const position of source?.positions || []) {
|
|
|
|
|
if (!id(position?.positionId) || Number(position?.availablePackages) <= 0) continue
|
|
|
|
|
const packageCount = Math.min(requiredPackages, Number(position.availablePackages))
|
|
|
|
|
sourceLines.push({ source, position, packageCount })
|
|
|
|
|
requiredPackages -= packageCount
|
|
|
|
|
if (requiredPackages === 0) break
|
|
|
|
|
}
|
|
|
|
|
if (requiredPackages === 0) break
|
|
|
|
|
}
|
|
|
|
|
context.addStep(MODULE, 'P5客户库存候选返回换标权威来源', inventoryCandidatesResponse,
|
|
|
|
|
Boolean(source) && Boolean(sourcePosition) && id(source?.inboundOrderId) !== '',
|
|
|
|
|
`balanceId=${id(source?.inventoryBalanceId) || 'missing'}, positionId=${id(sourcePosition?.positionId) || 'missing'}, inboundOrderId=${id(source?.inboundOrderId) || 'missing'}; ${trace(inventoryCandidatesResponse)}`, OPTIONS)
|
|
|
|
|
if (!source || !sourcePosition || !id(source.inboundOrderId)) {
|
|
|
|
|
blocker(context, 'P5本轮库存换标来源', '客户库存候选未返回本轮库存的余额、至少三箱位置和来源入库单', inventoryCandidatesResponse)
|
|
|
|
|
requiredPackages === 0,
|
|
|
|
|
`sourceLines=${sourceLines.length}, selectedPackages=${3 - requiredPackages}; ${trace(inventoryCandidatesResponse)}`, OPTIONS)
|
|
|
|
|
if (requiredPackages !== 0) {
|
|
|
|
|
blocker(context, 'P5本轮库存换标来源', '客户库存候选未返回可组合三箱的权威余额、位置和来源入库单', inventoryCandidatesResponse)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
const relabelBase = '/api/platform/v1/last-leg/relabel-applications'
|
|
|
|
|
const pdaBase = '/api/pda/v1/last-leg/relabel-tasks'
|
|
|
|
|
const newSku = `${inventory.skuCode}-NEW-${String(context.marker).slice(-8)}`.slice(0, 128)
|
|
|
|
|
const markerSuffix = String(context.marker).slice(-8)
|
|
|
|
|
const createBody = {
|
|
|
|
|
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,
|
|
|
|
|
idempotencyKey: key(context, 'RELABEL-CREATE'),
|
|
|
|
|
items: [{ sourceInventoryBalanceId: id(source.inventoryBalanceId),
|
|
|
|
|
sourceInventoryPositionId: id(sourcePosition.positionId), sourceInboundOrderId: id(source.inboundOrderId),
|
|
|
|
|
items: sourceLines.map(({ source, position, packageCount }, index) => ({
|
|
|
|
|
sourceInventoryBalanceId: id(source.inventoryBalanceId),
|
|
|
|
|
sourceInventoryPositionId: id(position.positionId), sourceInboundOrderId: id(source.inboundOrderId),
|
|
|
|
|
skuCode: source.skuCode, productNameZh: source.productNameZh || inventory.productName || '本轮库存商品',
|
|
|
|
|
totalPackageCount: 3, notifiedPackageCount: 3, shipmentNo: `SHIP-${context.marker}`.slice(0, 128),
|
|
|
|
|
newSkuCode: newSku, labelRequirement: context.marker,
|
|
|
|
|
cartonMark: `CM-${String(context.marker).slice(-32)}`,
|
|
|
|
|
fbaCartonMark: `FBA-${String(context.marker).slice(-32)}`,
|
|
|
|
|
destinationFbaWarehouseCode: source.destinationFbaWarehouseCode }],
|
|
|
|
|
totalPackageCount: packageCount, notifiedPackageCount: packageCount,
|
|
|
|
|
shipmentNo: `SHIP-${context.marker}-${index + 1}`.slice(0, 128),
|
|
|
|
|
newSkuCode: `${source.skuCode}-NEW-${markerSuffix}-${index + 1}`.slice(0, 128),
|
|
|
|
|
labelRequirement: context.marker,
|
|
|
|
|
cartonMark: `CM-${String(context.marker).slice(-28)}-${index + 1}`,
|
|
|
|
|
fbaCartonMark: `FBA-${String(context.marker).slice(-27)}-${index + 1}`,
|
|
|
|
|
destinationFbaWarehouseCode: source.destinationFbaWarehouseCode,
|
|
|
|
|
})),
|
|
|
|
|
}
|
|
|
|
|
const createdResponse = await context.requestOverseas('POST',
|
|
|
|
|
query(relabelBase, { customerId }), createBody)
|
|
|
|
@@ -407,13 +432,13 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|
|
|
|
claimReplay.data?.taskStatus === 'PROCESSING' && claimed.data?.taskStatus === 'PROCESSING',
|
|
|
|
|
trace(claimReplay.response), OPTIONS)
|
|
|
|
|
let detail = claimed.data || available.data
|
|
|
|
|
const item = detail?.items?.[0]
|
|
|
|
|
const item = relabelItemForUnit(detail, 0)
|
|
|
|
|
if (!id(item?.itemId)) {
|
|
|
|
|
blocker(context, 'P5换标明细', '认领后的换标任务没有真实明细', claimed.response)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
const unitPath = `${pdaBase}/${encodeURIComponent(relabelNo)}/units`
|
|
|
|
|
const scanBody = { itemId: id(item.itemId), oldLabel: `${context.marker}-OLD-1`, newLabel: newSku,
|
|
|
|
|
const scanBody = { itemId: id(item.itemId), oldLabel: `${context.marker}-OLD-1`, newLabel: item.newSkuCode,
|
|
|
|
|
expectedVersion: Number(detail.version), idempotencyKey: key(context, 'RELABEL-NORMAL') }
|
|
|
|
|
const scanned = await request(context, token, 'POST', 'P5正常换标一箱', unitPath, scanBody)
|
|
|
|
|
const scanReplay = await request(context, token, 'POST', 'P5正常换标同键重放', unitPath, scanBody)
|
|
|
|
@@ -424,7 +449,8 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|
|
|
|
await expectConflict(context, token, 'POST', 'P5正常换标旧版本A0409', unitPath,
|
|
|
|
|
{ ...scanBody, oldLabel: `${context.marker}-OLD-STALE`, idempotencyKey: key(context, 'RELABEL-STALE') })
|
|
|
|
|
detail = scanned.data
|
|
|
|
|
const skipBody = { itemId: id(item.itemId), oldLabel: `${context.marker}-OLD-2`,
|
|
|
|
|
const skipItem = relabelItemForUnit(detail, 1)
|
|
|
|
|
const skipBody = { itemId: id(skipItem?.itemId), oldLabel: `${context.marker}-OLD-2`,
|
|
|
|
|
reason: `${context.marker}-skip`, expectedVersion: Number(detail.version),
|
|
|
|
|
idempotencyKey: key(context, 'RELABEL-SKIP') }
|
|
|
|
|
const skipped = await request(context, token, 'POST', 'P5跳过换标一箱', `${unitPath}/skip`, skipBody)
|
|
|
|
@@ -432,7 +458,8 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|
|
|
|
context.assert(MODULE, 'P5跳过换标重放不重复计数', skipReplay.response,
|
|
|
|
|
Number(skipReplay.data?.skippedPackages) === 1, trace(skipReplay.response), OPTIONS)
|
|
|
|
|
detail = skipped.data
|
|
|
|
|
const exceptionBody = { itemId: id(item.itemId), oldLabel: `${context.marker}-OLD-3`,
|
|
|
|
|
const exceptionItem = relabelItemForUnit(detail, 2)
|
|
|
|
|
const exceptionBody = { itemId: id(exceptionItem?.itemId), oldLabel: `${context.marker}-OLD-3`,
|
|
|
|
|
exceptionCode: 'DAMAGED', reason: `${context.marker}-damaged`, expectedVersion: Number(detail.version),
|
|
|
|
|
idempotencyKey: key(context, 'RELABEL-EXCEPTION') }
|
|
|
|
|
const exception = await request(context, token, 'POST', 'P5登记换标异常一箱',
|
|
|
|
|