diff --git a/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs b/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs index 1f597555b..f5438fa79 100644 --- a/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs +++ b/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs @@ -30,6 +30,15 @@ test('fee writes use verified Firstleg paths and do not fabricate historical IDs assert.match(source, /chargeUnit === 'CBM'/) }) +test('automatic fee fixture completes the reviewed pricing workflow without recalculating', () => { + assert.match(source, /latest\?\.feeId/) + assert.doesNotMatch(source, /latest\?\.id/) + assert.match(source, /`\$\{pricingBase\}\/latest\?\$\{tenantQuery\}`/) + assert.match(source, /request\(context, 'POST', `\$\{pricingBase\}\/confirm\?\$\{tenantQuery\}`\)/) + assert.match(source, /request\(context, 'POST', `\$\{pricingBase\}\/send\?\$\{tenantQuery\}`\)/) + assert.doesNotMatch(source, /request\(context, 'POST', `\$\{pricingBase\}\/calculate/) +}) + test('shared fixture source names the finance full acceptance contract', () => { for (const field of ['marker', 'customerId', 'orders', 'feeSources', 'container', 'allocations', 'supplier', 'voucherFileId', 'deploymentBlocker']) assert.match(source, new RegExp(`\\b${field}\\b`)) for (const field of ['feeSourceId', 'sourceType', 'direction', 'currency', 'amount', 'businessDate', 'billingBasis']) { diff --git a/scripts/admin-test-env-modules/finance-full-fixtures.mjs b/scripts/admin-test-env-modules/finance-full-fixtures.mjs index d0f55ac86..7d7eace8d 100644 --- a/scripts/admin-test-env-modules/finance-full-fixtures.mjs +++ b/scripts/admin-test-env-modules/finance-full-fixtures.mjs @@ -144,6 +144,31 @@ async function loadPublishedAutomaticFee(context, resource) { `服务端计费实际读取 fl_inbound_order.actual_gross_weight_kg/actual_volume_cbm;inboundId=${resource.inboundId}, actualWeightKg=${actualWeightKg || 'missing'}, actualVolumeCbm=${actualVolumeCbm || 'missing'};测量表有值但主表未同步`) return null } + const pricingBase = `/api/platform/v1/inbounds/${encodeURIComponent(resource.inboundId)}/fees` + const tenantQuery = `tenantId=${encodeURIComponent(resource.tenantId)}` + const latestResponse = await context.request('GET', `${pricingBase}/latest?${tenantQuery}`) + let latest = context.apiSuccess(MODULE, '回读测量审核生成的自动费用试算', latestResponse, + `inboundId=${resource.inboundId}`, OPTIONS) + if (!latest?.feeId || !['CALCULATED', 'CONFIRMED', 'SENT'].includes(latest?.feeStatus)) { + blocker(context, '测量审核已生成可确认的自动费用试算', + `inboundId=${resource.inboundId}, feeId=${id(latest?.feeId) || 'missing'}, feeStatus=${latest?.feeStatus || 'missing'}, requestId=${latestResponse?.requestId || 'missing'}, traceId=${latestResponse?.traceId || 'missing'};审核只负责初算,Finance fixture 不重复试算`) + return null + } + if (latest.feeStatus === 'CALCULATED') { + const confirmResponse = await request(context, 'POST', `${pricingBase}/confirm?${tenantQuery}`) + latest = context.apiSuccess(MODULE, '确认测量审核生成的自动费用试算', confirmResponse, + `inboundId=${resource.inboundId}, feeId=${id(latest.feeId)}`, OPTIONS) + } + if (latest?.feeStatus === 'CONFIRMED') { + const sendResponse = await request(context, 'POST', `${pricingBase}/send?${tenantQuery}`) + latest = context.apiSuccess(MODULE, '发送已确认自动费用并发布来源', sendResponse, + `inboundId=${resource.inboundId}, feeId=${id(latest.feeId)}`, OPTIONS) + } + if (latest?.feeStatus !== 'SENT') { + blocker(context, '自动费用确认发送状态闭环', + `inboundId=${resource.inboundId}, feeId=${id(latest?.feeId) || 'missing'}, feeStatus=${latest?.feeStatus || 'missing'}`) + return null + } const listPath = `/api/platform/v1/inbounds/${encodeURIComponent(resource.inboundId)}/fee-details?accountId=${encodeURIComponent(resource.tenantId)}` const listResponse = await context.request('GET', listPath) const details = context.apiSuccess(MODULE, '回读审核链已发布自动费用来源', listResponse, @@ -153,7 +178,7 @@ async function loadPublishedAutomaticFee(context, resource) { && item?.settlementIncluded === true) if (!published) { blocker(context, '审核链已生成可结算自动费用来源', - `inboundId=${resource.inboundId}, processStatus=${pricingSummary.processStatus || pricingInbound?.processStatus || 'missing'}, feeDetails=${rows(details).length};测量审核完成链应在进入 WAIT_SPLIT 前生成 APPROVED + PUBLISHED 的 FBA_PRICING 来源,fixture 不重复试算/确认/发布`) + `inboundId=${resource.inboundId}, processStatus=${pricingSummary.processStatus || pricingInbound?.processStatus || 'missing'}, feeId=${id(latest.feeId)}, feeStatus=${latest.feeStatus}, feeDetails=${rows(details).length};确认和发送成功后应同步生成 APPROVED + PUBLISHED 的 FBA_PRICING 来源`) return null } const feeSourceId = id(published.sourceFeeId)