From 7678fd85ff73aa43a0144a16b0b0ddd22a3e907d Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 9 Sep 2026 18:43:08 +0800 Subject: [PATCH] test: cover customer inbound fee confirmation --- .../inbound-fees-contract.test.mjs | 9 +++ .../admin-test-env-modules/inbound-fees.mjs | 60 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) 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 e20b644b0..d0ac0094c 100644 --- a/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs +++ b/scripts/admin-test-env-modules/inbound-fees-contract.test.mjs @@ -31,3 +31,12 @@ test('automatic fee adjustment confirms the ten percent tolerance boundary', asy assert.match(source, /人工对客单价写入最新费用版本/) assert.match(source, /money\(published\?\.unitPrice\) === adjustedUnitPrice/) }) + +test('customer confirms chargeable quantity and manual unit price in one locked version', async () => { + const source = await readFile(moduleFile, 'utf8') + assert.match(source, /Client 读取已推送计费量、对客单价和费用合计/) + assert.match(source, /Client 一次确认计费量、对客单价和费用/) + assert.match(source, /customerConfirmationStatus === 'CONFIRMED'/) + assert.match(source, /客户费用确认重放返回同一锁定版本/) + assert.match(source, /客户确认后平台不得再次修改计费量或对客单价/) +}) diff --git a/scripts/admin-test-env-modules/inbound-fees.mjs b/scripts/admin-test-env-modules/inbound-fees.mjs index 433f192ff..0db92fe68 100644 --- a/scripts/admin-test-env-modules/inbound-fees.mjs +++ b/scripts/admin-test-env-modules/inbound-fees.mjs @@ -117,6 +117,64 @@ export async function run(context) { && money(published?.amount) === money(sent?.totalAmount), `status=${sent?.feeStatus || 'missing'}, process=${sent?.nextProcessStatus || 'missing'}, publication=${published?.publicationStatus || 'missing'}`, OPTIONS) + const orderId = id(context.createdResources.orders?.orderId) + const merchantLoginResponse = await context.request('POST', '/api/auth/login', { + loginId: context.options.merchantUsername, password: context.options.merchantPassword, + }, '') + const merchantLogin = context.apiSuccess(MODULE, 'Client 登录并准备确认计费量和费用', merchantLoginResponse, + `orderId=${orderId || 'missing'}`, OPTIONS) + const merchantToken = merchantLogin?.accessToken + context.assert(MODULE, '本轮客户订单与登录令牌可用于费用确认', merchantLoginResponse, + Boolean(orderId && merchantToken), `orderId=${orderId || 'missing'}, token=${merchantToken ? 'present' : 'missing'}`, OPTIONS) + if (!orderId || !merchantToken) { finish(context, start); return } + + const merchantBillingPath = `/api/merchant/v1/first-leg/orders/${encodeURIComponent(orderId)}/inbound-billing` + const merchantBillingResponse = await context.request('GET', merchantBillingPath, undefined, merchantToken) + const merchantBilling = context.apiSuccess(MODULE, 'Client 读取已推送计费量、对客单价和费用合计', + merchantBillingResponse, '', OPTIONS) + context.assert(MODULE, '客户看到的平台推送版本、对客单价和合计完全一致', merchantBillingResponse, + Number(merchantBilling?.latestFee?.feeVersion) === Number(sent.feeVersion) + && merchantBilling?.latestFee?.feeStatus === 'SENT' + && money(merchantBilling?.latestFee?.unitPrice) === adjustedUnitPrice + && money(merchantBilling?.latestFee?.chargeableQuantity) === money(sent.chargeableQuantity) + && money(merchantBilling?.latestFee?.totalAmount) === money(sent.totalAmount), + `version=${merchantBilling?.latestFee?.feeVersion ?? 'missing'}, unitPrice=${merchantBilling?.latestFee?.unitPrice ?? 'missing'}, total=${merchantBilling?.latestFee?.totalAmount ?? 'missing'}`, OPTIONS) + + const customerConfirmKey = `${context.marker}-customer-fee-confirm`.slice(0, 128) + const customerConfirmResponse = await context.request('POST', `${merchantBillingPath}/confirm`, + { expectedFeeVersion: Number(sent.feeVersion) }, merchantToken, requestHeaders(customerConfirmKey)) + const customerConfirmed = context.apiSuccess(MODULE, 'Client 一次确认计费量、对客单价和费用', + customerConfirmResponse, '', OPTIONS) + context.assert(MODULE, '客户确认锁定原推送版本及其单价和合计', customerConfirmResponse, + customerConfirmed?.customerConfirmationStatus === 'CONFIRMED' + && Number(customerConfirmed?.feeVersion) === Number(sent.feeVersion) + && money(customerConfirmed?.unitPrice) === adjustedUnitPrice + && money(customerConfirmed?.chargeableQuantity) === money(sent.chargeableQuantity) + && money(customerConfirmed?.totalAmount) === money(sent.totalAmount) + && Boolean(customerConfirmed?.customerConfirmedAt), + `confirmation=${customerConfirmed?.customerConfirmationStatus || 'missing'}, version=${customerConfirmed?.feeVersion ?? 'missing'}, unitPrice=${customerConfirmed?.unitPrice ?? 'missing'}`, OPTIONS) + + const customerConfirmReplayResponse = await context.request('POST', `${merchantBillingPath}/confirm`, + { expectedFeeVersion: Number(sent.feeVersion) }, merchantToken, requestHeaders(customerConfirmKey)) + const customerConfirmReplay = context.apiSuccess(MODULE, 'Client 费用确认幂等重放', + customerConfirmReplayResponse, '', OPTIONS) + context.assert(MODULE, '客户费用确认重放返回同一锁定版本', customerConfirmReplayResponse, + customerConfirmReplay?.customerConfirmationStatus === 'CONFIRMED' + && id(customerConfirmReplay?.feeId) === id(customerConfirmed?.feeId) + && Number(customerConfirmReplay?.feeVersion) === Number(customerConfirmed?.feeVersion), + `feeId=${id(customerConfirmReplay?.feeId) || 'missing'}, version=${customerConfirmReplay?.feeVersion ?? 'missing'}`, OPTIONS) + + const lockedAdjustResponse = await context.request('PUT', + `/api/platform/v1/inbounds/${encodeURIComponent(inboundId)}/fees/published?tenantId=${encodeURIComponent(tenantId)}`, + { expectedFeeVersion: Number(sent.feeVersion), chargeableQuantity: money(sent.chargeableQuantity), + unitPrice: Number((adjustedUnitPrice + 0.01).toFixed(4)), additionalAmount: money(sent.additionalAmount), + reason: `${context.marker}-客户确认后禁止改单价`, toleranceConfirmed: true }, undefined, + requestHeaders(`${context.marker}-locked-fee-adjust`)) + context.assert(MODULE, '客户确认后平台不得再次修改计费量或对客单价', lockedAdjustResponse, + lockedAdjustResponse.http === 200 && !success(lockedAdjustResponse) + && String(lockedAdjustResponse.body?.code) === 'A0409', + `code=${lockedAdjustResponse.body?.code ?? 'missing'}, message=${lockedAdjustResponse.body?.message || 'missing'}`, OPTIONS) + const feeItemsResponse = await context.request('GET', '/api/platform/v1/fee-items?page=1&size=200&status=ACTIVE') const feeItemsPage = context.apiSuccess(MODULE, '查询可用于其他应收的费用项目', feeItemsResponse, '', OPTIONS) const feeItems = Array.isArray(feeItemsPage?.records) ? feeItemsPage.records @@ -190,6 +248,6 @@ export async function run(context) { context.createdResources.inboundFees = { marker: context.marker, tenantId, inboundId, freightFeeId: id(sent.feeId), freightVersion: sent.feeVersion, manualDetailId: id(manualV2.id), - manualVersion: manualV2.sourceFeeVersion } + manualVersion: manualV2.sourceFeeVersion, customerConfirmationStatus: customerConfirmed.customerConfirmationStatus } finish(context, start) }