diff --git a/scripts/admin-test-env-modules/phase3-container-documents-contract.test.mjs b/scripts/admin-test-env-modules/phase3-container-documents-contract.test.mjs index 32b0234a4..5ba7f26f0 100644 --- a/scripts/admin-test-env-modules/phase3-container-documents-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-container-documents-contract.test.mjs @@ -16,6 +16,14 @@ import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs' const source = readFileSync(new URL('./phase3-container-documents.mjs', import.meta.url), 'utf8') const entry = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8') +test('document applicability resolves exact released booking rather than missing container channel', () => { + assert.match(source, /bookingId = id\(detail.container.oceanBookingId\)/) + assert.match(source, /id\(booking\?\.id\) !== bookingId/) + assert.match(source, /booking\?\.status !== 'RELEASED'/) + assert.match(source, /applicableDocumentTypes\(booking.channelCode\)/) + assert.doesNotMatch(source, /applicableDocumentTypes\(detail.container.channelCode\)/) +}) + test('destination clearance and delivery order documents are frozen while pickup uses the cabinet code', () => { assert.deepEqual(DOCUMENT_TYPES, [ 'DESTINATION_CLEARANCE', 'DELIVERY_ORDER', diff --git a/scripts/admin-test-env-modules/phase3-container-documents.mjs b/scripts/admin-test-env-modules/phase3-container-documents.mjs index 00bc03c9b..738387045 100644 --- a/scripts/admin-test-env-modules/phase3-container-documents.mjs +++ b/scripts/admin-test-env-modules/phase3-container-documents.mjs @@ -124,11 +124,18 @@ export async function run(context) { const detailResponse = await context.request('GET', `${CONTAINERS}/domestic/${containerId}`) const detail = context.apiSuccess(MODULE, '读取本轮货柜权威渠道', detailResponse, '', OPTIONS) - if (id(detail?.container?.id) !== containerId || !detail?.container?.channelCode) { - context.addStep(MODULE, '货柜渠道适用性前置', detailResponse, false, '缺少同柜权威渠道', OPTIONS) + if (id(detail?.container?.id) !== containerId || !detail?.container?.oceanBookingId) { + context.addStep(MODULE, '货柜渠道适用性前置', detailResponse, false, '缺少同柜关联订舱', OPTIONS) return } - const expectedTypes = applicableDocumentTypes(detail.container.channelCode) + const bookingId = id(detail.container.oceanBookingId) + const bookingResponse = await context.request('GET', `/api/platform/v1/ocean-bookings/${encodeURIComponent(bookingId)}`) + const booking = context.apiSuccess(MODULE, '读取本柜关联订舱权威渠道', bookingResponse, '', OPTIONS)?.oceanBooking + if (id(booking?.id) !== bookingId || booking?.status !== 'RELEASED' || !booking?.channelCode) { + context.addStep(MODULE, '货柜订舱渠道适用性前置', bookingResponse, false, '关联订舱身份、放舱状态或渠道缺失', OPTIONS) + return + } + const expectedTypes = applicableDocumentTypes(booking.channelCode) const specs = [ { type: 'DESTINATION_CLEARANCE', diff --git a/scripts/admin-test-env-modules/service-center-flow.mjs b/scripts/admin-test-env-modules/service-center-flow.mjs index c39789d35..0e55ee6bc 100644 --- a/scripts/admin-test-env-modules/service-center-flow.mjs +++ b/scripts/admin-test-env-modules/service-center-flow.mjs @@ -104,6 +104,25 @@ export async function run(context) { `appointmentId=${appointmentId || 'missing'}, oceanBookingId=${appointment?.oceanBookingId || 'missing'}, containerBookingId=${appointment?.containerBookingId || 'n/a'}, loadingPlanId=${appointment?.loadingPlanId || 'n/a'}, version=${appointment?.version ?? 'missing'}`, OPTIONS) if (!appointmentId) return const path = `${TRUCK}/${appointmentId}` + // This consumer can run after all booking containers have departed. Creation then + // completes the appointment transactionally; trying SUBMIT would be an illegal reversal. + if (appointment.status === 'COMPLETED') { + const currentResponse = await context.request('GET', path) + const current = context.apiSuccess(MODULE, '回查离仓后创建拖车的自动完成状态', currentResponse, '', OPTIONS) + const historyResponse = await context.request('GET', `${path}/history`) + const history = context.apiSuccess(MODULE, '回查拖车自动完成权威历史', historyResponse, '', OPTIONS) + const verified = current?.status === 'COMPLETED' && id(current.oceanBookingId) === id(ocean.id) + && Array.isArray(history) && history.some(row => row.action === 'CREATE' && id(row.appointmentId) === appointmentId) + && history.some(row => row.action === 'AUTO_COMPLETE' && row.toStatus === 'COMPLETED' + && id(row.appointmentId) === appointmentId && Boolean(row.createdAt)) + context.assert(MODULE, '订舱已全部离仓时拖车自动完成且不逆向提交', currentResponse, verified, + `appointmentId=${appointmentId}, status=${current?.status || 'missing'}`, OPTIONS) + if (verified) context.createdResources.serviceCenterFlow = { + marker: context.marker, appointmentId, oceanBookingId: ocean.id, finalStatus: 'COMPLETED', + completionMode: 'AUTO_COMPLETE_AFTER_DEPARTURE', + } + return + } const dispatchUpload = await uploadDispatchOrder(context, appointmentId) const dispatchAttachment = context.apiSuccess(MODULE, '跨服务拖车上传有效派车单附件', dispatchUpload, '', OPTIONS) if (!dispatchAttachment?.id) return