fix(acceptance): align latest overseas contracts

This commit is contained in:
2026-09-09 00:53:40 +08:00
parent 5da3417429
commit 0d73e185c0
7 changed files with 76 additions and 11 deletions
@@ -15,11 +15,12 @@ test('admin full acceptance registers inbound fee lifecycle after measurement',
test('inbound fee lifecycle covers frozen source, version, publication and WAIT_SPLIT contracts', async () => {
const source = await readFile(moduleFile, 'utf8')
for (const token of [
'/fees/latest', '/fees/published', '/fees/confirm', '/fees/send', '/fee-details',
'/fees/latest', '/fees/calculate', '/fees/published', '/fees/confirm', '/fees/send', '/fee-details',
'FBA_PRICING', 'MANUAL', 'CALCULATED', 'CONFIRMED', 'SENT', 'PUBLISHED',
'sourceFeeVersion', 'reviewStatus', 'publicationStatus', 'WAIT_SPLIT', 'toleranceConfirmed: true',
]) assert.match(source, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
assert.match(source, /人工发布头程费用并触发 Finance Outbox/)
assert.match(source, /String\(initialResponse\?\.body\?\.code\) === 'A0404'/)
assert.match(source, /对客合计仅含已发布头程与已审核其他应收且其他应付单列/)
assert.doesNotMatch(source, /settlementIncluded|是否结算|参与结算/)
})
@@ -36,15 +36,20 @@ export async function run(context) {
if (!tenantId || !inboundId) { finish(context, start); return }
const latestPath = `/api/platform/v1/inbounds/${encodeURIComponent(inboundId)}/fees/latest?tenantId=${encodeURIComponent(tenantId)}`
const latestResponse = await context.request('GET', latestPath)
const initial = context.apiSuccess(MODULE, '审核成功后读取自动初算费用', latestResponse, '', OPTIONS)
context.assert(MODULE, '自动初算保持 WAIT_SPLIT 且尚未发布', latestResponse,
let initialResponse = await context.request('GET', latestPath)
if (!success(initialResponse) && String(initialResponse?.body?.code) === 'A0404') {
initialResponse = await context.request('POST',
`/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)
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 }
const initialDetails = await listDetails(context, tenantId, inboundId, '查询自动初算派生费用明细')
const initialDetails = await listDetails(context, tenantId, inboundId, '查询初始费用派生明细')
const initialDerived = initialDetails.rows.find((row) => row.sourceType === 'FBA_PRICING'
&& id(row.sourceFeeId) === id(initial.feeId))
context.assert(MODULE, 'FBA_PRICING 明细是初算版本的只读草稿派生', initialDetails.response,
@@ -30,6 +30,9 @@ test('PDA module covers the container-number receiving contract and excludes out
for (const role of ['LICENSE_PLATE', 'CONTAINER_SEAL', 'DOOR_OPEN']) assert.ok(source.includes(role), role)
for (const forbidden of ['/outbound', '/dispatch', '/delivery', '/internal/']) assert.equal(source.includes(forbidden), false, forbidden)
assert.match(source, /同键同载荷重放/)
assert.match(source, /inbound-plans\/container-preview/)
assert.match(source, /snapshotVersion: Number\(preview\.snapshotVersion\)/)
assert.match(source, /snapshotHash: String\(preview\.snapshotHash\)/)
assert.match(source, /同键异载荷冲突/)
assert.match(source, /跨柜切换锁返回409/)
assert.match(source, /item\.operable === true && item\.allPlansReady === true/)
@@ -306,7 +306,23 @@ async function convertReceivingContainers(context, producers) {
convertible,
`sourceContainerBookingId=${id(summary?.sourceContainerBookingId) || 'missing'}, allPlansReady=${summary?.allPlansReady === true}, orders=${totalOrders}, inboundCodes=${inboundCodes}, plans=${existingPlans}, pending=${pendingPlans}; ${trace(summaryResponse)}`, OPTIONS)
if (!convertible) return false
const body = { sourceContainerBookingId: id(summary.sourceContainerBookingId) }
const previewResponse = await context.requestOverseas('GET',
`/api/platform/v1/last-leg/inbound-plans/container-preview?sourceContainerBookingId=${encodeURIComponent(summary.sourceContainerBookingId)}`,
undefined, {}, context.overseasToken)
const preview = success(previewResponse) ? previewResponse.body?.data : null
const previewReady = preview?.ready === true
&& Number.isInteger(Number(preview?.snapshotVersion)) && Number(preview.snapshotVersion) >= 0
&& /^[0-9a-f]{64}$/.test(String(preview?.snapshotHash || ''))
&& Number(preview?.pendingOrderCount) === pendingPlans
context.addStep(MODULE, `预览整柜原子转换快照 ${containerNo}`, previewResponse,
previewReady,
`ready=${preview?.ready === true}, version=${preview?.snapshotVersion ?? 'missing'}, hash=${nonBlank(preview?.snapshotHash) ? 'present' : 'missing'}, pending=${preview?.pendingOrderCount ?? 'missing'}; ${trace(previewResponse)}`, OPTIONS)
if (!previewReady) return false
const body = {
sourceContainerBookingId: id(summary.sourceContainerBookingId),
snapshotVersion: Number(preview.snapshotVersion),
snapshotHash: String(preview.snapshotHash),
}
const key = `${context.marker}-pda-convert-${containerNo}`.slice(0, 128)
const convertedResponse = await context.requestOverseas('POST',
'/api/platform/v1/last-leg/inbound-plans/convert-container', body,