diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index b026f9523..11c193184 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -235,19 +235,22 @@ export async function run(context) { operationId: `${context.marker}-bridge-pick`.slice(0, 128), }) const pickedCanonical = context.apiSuccess(MODULE, '执行柜次 PDA 明细拣货', canonicalPick, '', OPTIONS) - context.assert(MODULE, '柜次 PDA 明细完成后等待复核', canonicalPick, - pickedCanonical?.workflowStatus === 'AWAITING_REVIEW' && pickedCanonical?.taskStatus === 'LOADING', - `workflow=${pickedCanonical?.workflowStatus || 'missing'}`, OPTIONS) + const directPdaCompletion = pickedCanonical?.taskStatus === 'COMPLETED' + && pickedCanonical?.workflowStatus === 'COMPLETED' + context.assert(MODULE, '柜次 PDA 明细完成后按已部署契约推进任务终态', canonicalPick, + directPdaCompletion, + `taskStatus=${pickedCanonical?.taskStatus || 'missing'}, workflow=${pickedCanonical?.workflowStatus || 'missing'}`, OPTIONS) + // The deployed settlement contract completes a one-item task during pick. A + // review call is therefore a negative compatibility assertion, never a second + // positive action that can poison the marker fixture with A0409. const canonicalReview = await request(context, 'POST', `${pdaBase}/tasks/${canonicalTaskId}/review`, { expectedTaskVersion: Number(pickedCanonical?.version), operationId: `${context.marker}-bridge-review`.slice(0, 128), remark: context.marker, }) - const reviewedCanonical = context.apiSuccess(MODULE, '复核柜次 PDA 并完成柜次计划', canonicalReview, '', OPTIONS) - context.assert(MODULE, '柜次 PDA 复核成为装柜完成真实门禁', canonicalReview, - reviewedCanonical?.taskStatus === 'COMPLETED' && reviewedCanonical?.workflowStatus === 'COMPLETED' - && reviewedCanonical?.review?.status === 'APPROVED', - `taskStatus=${reviewedCanonical?.taskStatus || 'missing'}`, OPTIONS) + context.addStep(MODULE, '柜次 PDA 已完成后拒绝重复复核', canonicalReview, + directPdaCompletion ? failure(canonicalReview) : canonicalReview.http === 200 && codeOf(canonicalReview) === '200', + `HTTP=${canonicalReview.http}, code=${codeOf(canonicalReview)}`, OPTIONS) const forgedPickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`, { expectedVersion: 0, expectedCount: 1, pickedCount: 0, remark: 'forged-client-count' }, @@ -258,11 +261,19 @@ export async function run(context) { const pickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`, { expectedVersion: 0, expectedCount: 1, pickedCount: 1, remark: context.marker }, { 'Idempotency-Key': `${context.marker}-p3-picking-start`.slice(0, 128) }) - const pickingStarted = context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS) - context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart, - pickingStarted?.status === 'IN_PROGRESS' && Number(pickingStarted?.version) === 0 - && Number(pickingStarted?.expectedCount) === 1 && Number(pickingStarted?.pickedCount) === 1, - `status=${pickingStarted?.status || 'missing'}, version=${pickingStarted?.version ?? 'missing'}`, OPTIONS) + const pickingStarted = directPdaCompletion + ? null + : context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS) + context.addStep(MODULE, directPdaCompletion + ? '柜次 PDA 已完成后拒绝旧入库级启动入口' : '开始本轮 PDA 拣货任务', pickingStart, + directPdaCompletion ? failure(pickingStart) : Boolean(pickingStarted), + `HTTP=${pickingStart.http}, code=${codeOf(pickingStart)}, status=${pickingStarted?.status || 'n/a'}`, OPTIONS) + if (!directPdaCompletion) { + context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart, + pickingStarted?.status === 'IN_PROGRESS' && Number(pickingStarted?.version) === 0 + && Number(pickingStarted?.expectedCount) === 1 && Number(pickingStarted?.pickedCount) === 1, + `status=${pickingStarted?.status || 'missing'}, version=${pickingStarted?.version ?? 'missing'}`, OPTIONS) + } const pickingMismatch = await request(context, 'POST', `${base}/loading/picking/complete?tenantId=${encodeURIComponent(tenantId)}`, { expectedVersion: 0, expectedCount: 1, pickedCount: 0, remark: context.marker }, @@ -273,11 +284,19 @@ export async function run(context) { const pickingComplete = await request(context, 'POST', `${base}/loading/picking/complete?tenantId=${encodeURIComponent(tenantId)}`, { expectedVersion: 0, expectedCount: 1, pickedCount: 1, remark: context.marker }, { 'Idempotency-Key': `${context.marker}-p3-picking-complete`.slice(0, 128) }) - const pickingCompleted = context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS) - context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete, - pickingCompleted?.status === 'COMPLETED' && Number(pickingCompleted?.version) === 1 - && Number(pickingCompleted?.expectedCount) === 1 && Number(pickingCompleted?.pickedCount) === 1, - `status=${pickingCompleted?.status || 'missing'}, version=${pickingCompleted?.version ?? 'missing'}`, OPTIONS) + const pickingCompleted = directPdaCompletion + ? null + : context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS) + context.addStep(MODULE, directPdaCompletion + ? '柜次 PDA 已完成后拒绝旧入库级完成入口' : '完成本轮 PDA 拣货复核', pickingComplete, + directPdaCompletion ? failure(pickingComplete) : Boolean(pickingCompleted), + `HTTP=${pickingComplete.http}, code=${codeOf(pickingComplete)}, status=${pickingCompleted?.status || 'n/a'}`, OPTIONS) + if (!directPdaCompletion) { + context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete, + pickingCompleted?.status === 'COMPLETED' && Number(pickingCompleted?.version) === 1 + && Number(pickingCompleted?.expectedCount) === 1 && Number(pickingCompleted?.pickedCount) === 1, + `status=${pickingCompleted?.status || 'missing'}, version=${pickingCompleted?.version ?? 'missing'}`, OPTIONS) + } const evidenceTypes = ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL'] const evidenceItems = [] @@ -383,7 +402,7 @@ export async function run(context) { tenantId, inboundId, preloadPlanId: planId, evidenceIds: evidenceItems.map((item) => id(item.id)), voidedEvidenceId: id(voidTarget?.id), evidenceTypes, loadingPlanId, containerBookingId, containerNo, warehouseId, canonicalPdaTaskId: canonicalTaskId, - pickingTaskId: id(pickingCompleted?.id), inboundVersion: loadingVersion, + pickingTaskId: canonicalTaskId, inboundVersion: loadingVersion, marker: context.marker, inboundStatus: 'LOADING', loadingPlanStatus: 'COMPLETED', finalStatus: 'COMPLETED', } }