123 lines
8.4 KiB
JavaScript
123 lines
8.4 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
|
|
const MODULE = 'manual-pda-ready-verify'
|
|
const OPTIONS = { scenario: 'manual-pda-ready-readonly-verification', deploymentBlocker: true }
|
|
const SMART = '/api/platform/v1/first-leg/smart-loading'
|
|
const FIRST_LEG = '/api/platform/v1/first-leg'
|
|
const CONTAINERS = '/api/platform/v1/containers'
|
|
const PDA = `${FIRST_LEG}/pda-loading`
|
|
|
|
function id(value) { return value == null ? '' : String(value) }
|
|
function rows(value) { return Array.isArray(value) ? value : [] }
|
|
function positiveId(value) { return /^[1-9][0-9]*$/.test(id(value)) }
|
|
function itemSnapshot(value) {
|
|
return rows(value).map((item) => ({ id: id(item.id), inboundId: id(item.inboundId),
|
|
packageCount: Number(item.packageCount) }))
|
|
.sort((left, right) => left.inboundId.localeCompare(right.inboundId) || left.id.localeCompare(right.id))
|
|
}
|
|
|
|
async function loadFixture(context) {
|
|
const report = JSON.parse(await readFile(resolve(context.options.manualPdaSourceReport), 'utf8'))
|
|
const fixture = report?.createdResources?.manualPdaReadyFixture
|
|
const sourceGateway = String(report?.gateway || '').replace(/\/$/, '')
|
|
const valid = sourceGateway === context.options.gateway && fixture?.schemaVersion === 1
|
|
&& fixture?.stage === 'MANUAL_BOOKING_LINK_CONFIRM_READY' && fixture?.loadingPlanId
|
|
&& fixture?.containerBookingId && Array.isArray(fixture?.beforeItems)
|
|
&& Array.isArray(fixture?.expectedAssignments) && fixture.expectedAssignments.length > 0
|
|
context.assert(MODULE, '读取同环境 PDA 就绪准备报告', null, valid,
|
|
`sourceMarker=${report?.marker || 'missing'}, sameGateway=${sourceGateway === context.options.gateway}, loadingPlanId=${id(fixture?.loadingPlanId) || 'missing'}`,
|
|
OPTIONS)
|
|
return valid ? fixture : null
|
|
}
|
|
|
|
export async function run(context) {
|
|
const fixture = await loadFixture(context)
|
|
if (!fixture) return
|
|
const planId = id(fixture.loadingPlanId)
|
|
const containerId = id(fixture.containerBookingId)
|
|
|
|
const planResponse = await context.request('GET', `${SMART}/plans/${encodeURIComponent(planId)}`)
|
|
const plan = context.apiSuccess(MODULE, '读取预装确认后的原智能装柜计划', planResponse, '', OPTIONS)
|
|
const afterItems = itemSnapshot(plan?.items)
|
|
context.assert(MODULE, '订舱关联和预装确认后计划身份、状态及明细稳定', planResponse,
|
|
id(plan?.id) === planId && id(plan?.bookingId || plan?.containerBookingId) === containerId
|
|
&& plan?.status === 'EDITING' && Number(plan?.version) > Number(fixture.beforePlanVersion)
|
|
&& JSON.stringify(afterItems) === JSON.stringify(fixture.beforeItems),
|
|
`status=${plan?.status || 'missing'}, beforeVersion=${fixture.beforePlanVersion}, afterVersion=${plan?.version ?? 'missing'}, items=${afterItems.length}`,
|
|
OPTIONS)
|
|
|
|
const containerResponse = await context.request('GET', `${CONTAINERS}/domestic/${encodeURIComponent(containerId)}`)
|
|
const container = context.apiSuccess(MODULE, '读取关联真实柜号后的稳定柜次', containerResponse, '', OPTIONS)?.container
|
|
const oceanBookingId = id(container?.oceanBookingId)
|
|
const realContainerNo = String(container?.containerNo || '')
|
|
context.assert(MODULE, '真实柜号和订舱原地补全且内部柜次身份不变', containerResponse,
|
|
id(container?.id) === containerId && container?.cabinetNo === fixture.cabinetNo
|
|
&& realContainerNo.length > 0 && positiveId(oceanBookingId)
|
|
&& Number(container?.version) > Number(fixture.beforeContainerVersion)
|
|
&& id(container?.warehouseId) === id(fixture.warehouseId) && container?.channelCode === fixture.channelCode,
|
|
`containerId=${id(container?.id) || 'missing'}, cabinetNo=${container?.cabinetNo || 'missing'}, containerNo=${realContainerNo || 'missing'}, oceanBookingId=${oceanBookingId || 'missing'}`,
|
|
OPTIONS)
|
|
if (!positiveId(oceanBookingId) || !realContainerNo) return
|
|
|
|
const bookingResponse = await context.request('GET', `/api/platform/v1/ocean-bookings/${encodeURIComponent(oceanBookingId)}`)
|
|
const booking = context.apiSuccess(MODULE, '读取关联后的 RELEASED 订舱与 allocation', bookingResponse, '', OPTIONS)
|
|
const allocation = rows(booking?.containers).find((item) => id(item.containerBookingId) === containerId
|
|
&& item.containerNo === realContainerNo)
|
|
context.assert(MODULE, '订舱已放舱且唯一 allocation 指向原柜次', bookingResponse,
|
|
booking?.oceanBooking?.status === 'RELEASED' && positiveId(allocation?.requirementId)
|
|
&& positiveId(allocation?.allocationId)
|
|
&& rows(booking?.containers).filter((item) => id(item.containerBookingId) === containerId).length === 1,
|
|
`status=${booking?.oceanBooking?.status || 'missing'}, bookingNo=${booking?.oceanBooking?.bookingNo || 'missing'}, requirementId=${id(allocation?.requirementId) || 'missing'}, allocationId=${id(allocation?.allocationId) || 'missing'}`,
|
|
OPTIONS)
|
|
|
|
const detailResponse = await context.request('GET',
|
|
`${FIRST_LEG}/loading-plans/${encodeURIComponent(planId)}?warehouseId=${encodeURIComponent(fixture.warehouseId)}`)
|
|
const detail = context.apiSuccess(MODULE, '读取整份预装确认结果', detailResponse, '', OPTIONS)
|
|
const taskId = id(detail?.summary?.readiness?.pdaTaskId)
|
|
context.assert(MODULE, '预装确认状态为 CONFIRMED 并返回唯一 PDA taskId', detailResponse,
|
|
id(detail?.summary?.planId) === planId && detail?.summary?.status === 'EDITING'
|
|
&& detail?.summary?.readiness?.confirmationStatus === 'CONFIRMED'
|
|
&& detail?.summary?.readiness?.canConfirm === false
|
|
&& detail?.summary?.readiness?.readyForPda === true && positiveId(taskId),
|
|
`confirmationStatus=${detail?.summary?.readiness?.confirmationStatus || 'missing'}, readyForPda=${detail?.summary?.readiness?.readyForPda}, taskId=${taskId || 'missing'}`,
|
|
OPTIONS)
|
|
if (!positiveId(taskId)) return
|
|
|
|
const taskResponse = await context.request('GET',
|
|
`${PDA}/tasks?loadingPlanId=${encodeURIComponent(planId)}&warehouseId=${encodeURIComponent(fixture.warehouseId)}`)
|
|
const task = context.apiSuccess(MODULE, '读取唯一待领取 PDA 任务', taskResponse, '', OPTIONS)
|
|
const plannedPackages = rows(task?.items).reduce((sum, item) => sum + Number(item.plannedPackageCount || 0), 0)
|
|
const expectedPackages = rows(fixture.expectedAssignments)
|
|
.reduce((sum, item) => sum + Number(item.expectedAllocatedPackageCount || 0), 0)
|
|
context.assert(MODULE, 'PDA 任务四元身份、未领取状态和计划箱数正确', taskResponse,
|
|
id(task?.id) === taskId && id(task?.loadingPlanId) === planId
|
|
&& id(task?.containerBookingId) === containerId && task?.cabinetNo === fixture.cabinetNo
|
|
&& task?.currentContainerNo === realContainerNo && task?.taskStatus === 'LOADING'
|
|
&& task?.workflowStatus === 'UNASSIGNED' && task?.assignment == null
|
|
&& rows(task?.items).length > 0 && plannedPackages === expectedPackages,
|
|
`taskId=${id(task?.id) || 'missing'}, taskStatus=${task?.taskStatus || 'missing'}, workflowStatus=${task?.workflowStatus || 'missing'}, plannedPackages=${plannedPackages}, expectedPackages=${expectedPackages}`,
|
|
OPTIONS)
|
|
|
|
for (const assignment of fixture.expectedAssignments || []) {
|
|
const inboundResponse = await context.request('GET',
|
|
`/api/platform/inbounds/${encodeURIComponent(assignment.inboundId)}?tenantId=${encodeURIComponent(fixture.tenantId)}`)
|
|
const inbound = context.apiSuccess(MODULE, `${assignment.role} claim 前回查入库状态`, inboundResponse, '', OPTIONS)
|
|
context.assert(MODULE, `${assignment.role} 在 PDA 领取前仍为 WAIT_SPLIT`, inboundResponse,
|
|
id(inbound?.summary?.id) === id(assignment.inboundId)
|
|
&& inbound?.summary?.processStatus === 'WAIT_SPLIT',
|
|
`inboundId=${id(inbound?.summary?.id) || 'missing'}, status=${inbound?.summary?.processStatus || 'missing'}`,
|
|
OPTIONS)
|
|
}
|
|
|
|
context.createdResources.manualPdaReadyVerification = {
|
|
sourceMarker: fixture.sourceMarker, verifiedAt: new Date().toISOString(), stage: 'PDA_UNASSIGNED',
|
|
oceanBookingId, bookingNo: booking?.oceanBooking?.bookingNo || null,
|
|
requirementId: id(allocation?.requirementId), allocationId: id(allocation?.allocationId),
|
|
loadingPlanId: planId, containerBookingId: containerId, cabinetNo: fixture.cabinetNo,
|
|
containerNo: realContainerNo, pdaTaskId: taskId, tenantId: id(fixture.tenantId),
|
|
warehouseId: id(fixture.warehouseId), plannedPackageCount: plannedPackages,
|
|
nextStage: 'PDA 人工领取任务后进入 LOADING,再执行扫码、批量提交和待复核验收。',
|
|
}
|
|
}
|