From 2abbb3cc8f06cbbd70ebb8d3a50a413fb563a99c Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 9 Sep 2026 01:09:31 +0800 Subject: [PATCH] fix(acceptance): preserve calculated dropship fees --- scripts/admin-test-env-acceptance.mjs | 14 ++++------- .../inbound-fees-contract.test.mjs | 10 ++++++++ .../admin-test-env-modules/inbound-fees.mjs | 25 +++++++++++++------ ...-phase4-producer-fixture-contract.test.mjs | 5 ++-- ...verseas-acceptance-entry-contract.test.mjs | 3 ++- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/scripts/admin-test-env-acceptance.mjs b/scripts/admin-test-env-acceptance.mjs index 8d514f990..a0e2e4642 100644 --- a/scripts/admin-test-env-acceptance.mjs +++ b/scripts/admin-test-env-acceptance.mjs @@ -1470,7 +1470,7 @@ export class AcceptanceRun { const variantMarker = `${matrixMarker}-P4-${label}` const contextKeys = ['orderDestinationType', 'orderFulfillmentType', 'phase4ProducerMatrixMarker'] const createdKeys = [ - 'phase3Loading', 'phase3LoadingCompletedProducer', 'phase3ContainerDocuments', 'orchestrationFixtures', 'inboundFees', 'oceanBooking', + 'phase3Loading', 'phase3LoadingCompletedProducer', 'phase3ContainerDocuments', 'orchestrationFixtures', 'oceanBooking', 'phase3CustomsSupplier', 'phase3Customs', 'serviceCenterFlow', 'portSchedule', 'phase3OutboundDispatch', 'phase3CarrierScheduleQuery', 'phase4CarrierPickupReady', ] @@ -1496,14 +1496,10 @@ export class AcceptanceRun { // become impossible when the bounded isolation guard refuses bulk location mutations. restoreFixture = await this.preparePhase3LoadingFixture('', false) if (fulfillmentType === 'ONE_CLICK_DROPSHIP') { - const measuredFixture = this.createdResources.inboundMeasurement - this.createdResources.inboundMeasurement = this.createdResources.orchestrationFixtures?.phase3Loading - try { - const inboundFees = await import(new URL('./admin-test-env-modules/inbound-fees.mjs', import.meta.url)) - await this.executeModule(`inbound-fees-phase4-${label}`, () => inboundFees.run(this)) - } finally { - this.createdResources.inboundMeasurement = measuredFixture - } + const inboundFees = await import(new URL('./admin-test-env-modules/inbound-fees.mjs', import.meta.url)) + await inboundFees.ensureInitialCalculated(this, + this.createdResources.orchestrationFixtures?.phase3Loading, + { scenario: `phase4-${label}-initial-fee`, deploymentBlocker: true }) } const loading = await import(new URL('./admin-test-env-modules/phase3-loading.mjs', import.meta.url)) await this.executeModule(`phase3-loading-phase4-${label}`, () => loading.run(this)) diff --git a/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs b/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs index 650318206..4f9b403b6 100644 --- a/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs +++ b/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs @@ -24,3 +24,13 @@ test('inbound fee lifecycle covers frozen source, version, publication and WAIT_ assert.match(source, /对客合计仅含已发布头程与已审核其他应收且其他应付单列/) assert.doesNotMatch(source, /settlementIncluded|是否结算|参与结算/) }) + +test('initial fee preparation is reusable without confirming or publishing the fee', async () => { + const source = await readFile(moduleFile, 'utf8') + assert.match(source, /export async function ensureInitialCalculated/) + const helper = source.slice(source.indexOf('export async function ensureInitialCalculated'), source.indexOf('export async function run')) + assert.match(helper, /\/fees\/latest/) + assert.match(helper, /\/fees\/calculate/) + assert.match(helper, /feeStatus === 'CALCULATED'/) + assert.doesNotMatch(helper, /\/fees\/confirm|\/fees\/send/) +}) diff --git a/scripts/admin-test-env-modules/inbound-fees.mjs b/scripts/admin-test-env-modules/inbound-fees.mjs index 8e04d69e0..8187e6130 100644 --- a/scripts/admin-test-env-modules/inbound-fees.mjs +++ b/scripts/admin-test-env-modules/inbound-fees.mjs @@ -25,15 +25,14 @@ async function listDetails(context, tenantId, inboundId, name) { return { response, rows: Array.isArray(rows) ? rows : [] } } -export async function run(context) { - const start = context.steps.length - const fixture = context.createdResources.inboundMeasurement +export async function ensureInitialCalculated(context, fixture, + options = { scenario: 'inbound-fee-initial-calculated', deploymentBlocker: true }) { const tenantId = id(fixture?.tenantId) const inboundId = id(fixture?.inboundId) context.assert(MODULE, '复用本轮审核通过且进入 WAIT_SPLIT 的入库单', null, Boolean(tenantId && inboundId && fixture?.finalStatus === 'APPROVED' && fixture?.processStatus === 'WAIT_SPLIT'), - `tenantId=${tenantId || 'missing'}, inboundId=${inboundId || 'missing'}, measurement=${fixture?.finalStatus || 'missing'}, process=${fixture?.processStatus || 'missing'}`, OPTIONS) - if (!tenantId || !inboundId) { finish(context, start); return } + `tenantId=${tenantId || 'missing'}, inboundId=${inboundId || 'missing'}, measurement=${fixture?.finalStatus || 'missing'}, process=${fixture?.processStatus || 'missing'}`, options) + if (!tenantId || !inboundId) return null const latestPath = `/api/platform/v1/inbounds/${encodeURIComponent(inboundId)}/fees/latest?tenantId=${encodeURIComponent(tenantId)}` let initialResponse = await context.request('GET', latestPath) @@ -42,12 +41,22 @@ export async function run(context) { `/api/platform/v1/inbounds/${encodeURIComponent(inboundId)}/fees/calculate?tenantId=${encodeURIComponent(tenantId)}`, undefined, undefined, requestHeaders(`${context.marker}-inbound-fee-initial-calculate`)) } - const initial = context.apiSuccess(MODULE, '读取或补齐审核后初始费用', initialResponse, '', OPTIONS) + const initial = context.apiSuccess(MODULE, '读取或补齐审核后初始费用', initialResponse, '', options) context.assert(MODULE, '初始费用保持 WAIT_SPLIT 且尚未发布', initialResponse, initial?.feeStatus === 'CALCULATED' && initial?.nextProcessStatus === 'WAIT_SPLIT' && Number(initial?.feeVersion) >= 1 && money(initial?.totalAmount) >= 0, - `feeId=${id(initial?.feeId) || 'missing'}, version=${initial?.feeVersion ?? 'missing'}, status=${initial?.feeStatus || 'missing'}, process=${initial?.nextProcessStatus || 'missing'}`, OPTIONS) - if (!initial?.feeId || initial?.feeStatus !== 'CALCULATED') { finish(context, start); return } + `feeId=${id(initial?.feeId) || 'missing'}, version=${initial?.feeVersion ?? 'missing'}, status=${initial?.feeStatus || 'missing'}, process=${initial?.nextProcessStatus || 'missing'}`, options) + return initial?.feeId && initial?.feeStatus === 'CALCULATED' + ? { initial, initialResponse, tenantId, inboundId } + : null +} + +export async function run(context) { + const start = context.steps.length + const fixture = context.createdResources.inboundMeasurement + const prepared = await ensureInitialCalculated(context, fixture, OPTIONS) + if (!prepared) { finish(context, start); return } + const { initial, initialResponse, tenantId, inboundId } = prepared const initialDetails = await listDetails(context, tenantId, inboundId, '查询初始费用派生明细') const initialDerived = initialDetails.rows.find((row) => row.sourceType === 'FBA_PRICING' diff --git a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs index 3f9380a89..2582c0b23 100644 --- a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs @@ -85,8 +85,9 @@ test('measurement and loading pass destination identity into the LOADED producer assert.match(entry, /runPhase4ProducerVariant\('DROPSHIP-PRIMARY', 'FBA', 'ONE_CLICK_DROPSHIP', true\)/) assert.match(entry, /orderFulfillmentType/) assert.match(entry, /fulfillmentType: fixtureOrder\.fulfillmentType \|\| 'STANDARD'/) - assert.match(entry, /inbound-fees-phase4-/) - assert.match(entry, /createdResources\.inboundMeasurement = this\.createdResources\.orchestrationFixtures\?\.phase3Loading/) + assert.match(entry, /inboundFees\.ensureInitialCalculated\(this,/) + assert.match(entry, /this\.createdResources\.orchestrationFixtures\?\.phase3Loading/) + assert.doesNotMatch(entry, /inbound-fees-phase4-|createdResources\.inboundMeasurement = this\.createdResources\.orchestrationFixtures/) assert.match(entry, /skuCodes: Array\.isArray\(fixtureOrder\.skuCodes\) \? fixtureOrder\.skuCodes : \[\]/) assert.match(entry, /skuLines: Array\.isArray\(fixtureOrder\.skuLines\) \? fixtureOrder\.skuLines : \[\]/) const pda = read('phase4-overseas-pda.mjs') diff --git a/scripts/phase4-overseas-acceptance-entry-contract.test.mjs b/scripts/phase4-overseas-acceptance-entry-contract.test.mjs index daef143bb..2177ff3bc 100644 --- a/scripts/phase4-overseas-acceptance-entry-contract.test.mjs +++ b/scripts/phase4-overseas-acceptance-entry-contract.test.mjs @@ -163,8 +163,9 @@ test('PDA entry expands the real phase-three producer chain and builds an FBA/OV assert.match(source, /runPhase4ProducerVariant\('SECONDARY', 'FBA', 'STANDARD', false\)/) assert.match(source, /runUntilPickupReady\(this\)/) const variant = source.slice(source.indexOf('async runPhase4ProducerVariant'), source.indexOf('async ensurePhase4PdaProducerMatrix')) - assert.ok(variant.indexOf('inbound-fees-phase4-') < variant.indexOf('phase3-loading-phase4-')) + assert.ok(variant.indexOf('ensureInitialCalculated') < variant.indexOf('phase3-loading-phase4-')) assert.match(variant, /fulfillmentType === 'ONE_CLICK_DROPSHIP'/) + assert.doesNotMatch(variant, /inboundFees\.run\(this\)/) assert.ok(variant.indexOf('phase3-outbound-dispatch-phase4-') < variant.indexOf('phase3-container-documents-phase4-')) assert.ok(variant.indexOf('phase3-container-documents-phase4-') < variant.indexOf('phase3-carrier-schedule-queries-phase4-')) assert.match(source, /\['phase3-container-documents', \['phase3-outbound-dispatch'\]\]/)