diff --git a/src/modules/first-leg/api/chargeableWeights.test.mjs b/src/modules/first-leg/api/chargeableWeights.test.mjs deleted file mode 100644 index 3c60a8d..0000000 --- a/src/modules/first-leg/api/chargeableWeights.test.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import assert from 'node:assert/strict' -import test from 'node:test' -import { readFileSync } from 'node:fs' -import ts from 'typescript' - -test('计费重确认保留大整数来源和原幂等键,只提交预期重量版本', async () => { - const calls = [] - const source = readFileSync(new URL('./chargeableWeights.ts', import.meta.url), 'utf8') - const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText - const exports = {} - new Function('require', 'exports', code)(() => ({ apiRequest: async request => { calls.push(request); return [] } }), exports) - const id = '9223372036854775806' - await exports.getChargeableWeights(id) - await exports.confirmChargeableWeight({ inboundId: id, fingerprint: 'abc' }, 'original-command') - assert.equal(calls[0].url, `/merchant/v1/first-leg/orders/${id}/chargeable-weights`) - assert.equal(calls[1].url, `/merchant/v1/first-leg/chargeable-weights/${id}/confirm`) - assert.deepEqual(calls[1].headers, { 'Idempotency-Key': 'original-command' }) - assert.deepEqual(calls[1].data, { expectedFingerprint: 'abc' }) - assert.equal(calls.some(call => call.url.includes('inbound-billing')), false) -}) diff --git a/src/modules/first-leg/api/chargeableWeights.ts b/src/modules/first-leg/api/chargeableWeights.ts deleted file mode 100644 index f34ce49..0000000 --- a/src/modules/first-leg/api/chargeableWeights.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { apiRequest } from '@/api/http' -export interface ChargeableWeight { - inboundId: string - orderId: string - chargeableWeightKg: number - fingerprint: string - confirmed: boolean - confirmedAt: string | null -} -export function getChargeableWeights(orderId: string) { - return apiRequest({ method: 'GET', url: `/merchant/v1/first-leg/orders/${encodeURIComponent(orderId)}/chargeable-weights` }) -} -export function confirmChargeableWeight(weight: ChargeableWeight, key: string) { - return apiRequest({ method: 'POST', url: `/merchant/v1/first-leg/chargeable-weights/${encodeURIComponent(weight.inboundId)}/confirm`, - headers: { 'Idempotency-Key': key }, data: { expectedFingerprint: weight.fingerprint } }) -} diff --git a/src/modules/first-leg/components/ChargeableWeightDialog.test.mjs b/src/modules/first-leg/components/ChargeableWeightDialog.test.mjs deleted file mode 100644 index 2d09f05..0000000 --- a/src/modules/first-leg/components/ChargeableWeightDialog.test.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'node:assert/strict' -import test from 'node:test' -import { readFileSync } from 'node:fs' -import ts from 'typescript' -import { ref } from 'vue' - -const flush = async () => { for (let i = 0; i < 8; i++) await Promise.resolve() } -function harness(api, props = { orderId: 'one', canConfirm: true }) { - let changed, disposed - const events = [] - const source = readFileSync(new URL('./ChargeableWeightDialog.vue', import.meta.url), 'utf8').split('')[0] - const code = ts.transpileModule(source + '\nexports.state = { rows, loading, saving, error, confirm, load };', { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, - }).outputText - const exports = {} - new Function('require', 'exports', 'defineProps', 'defineEmits', code)(name => { - if (name === 'vue') return { ref, watch: (_, callback) => { changed = callback; callback() }, onBeforeUnmount: callback => { disposed = callback } } - if (name === 'element-plus') return { ElMessage: { success() {} } } - return api - }, exports, () => props, () => name => events.push(name)) - return { ...exports.state, props, change: () => changed(), dispose: () => disposed(), events } -} - -test('确认结果未知时重试原命令,成功后回查并通知工作台刷新', async () => { - const row = { inboundId: '100', fingerprint: 'weight-v1', chargeableWeightKg: 50, confirmed: false } - const commands = [] - let fail = true - const h = harness({ getChargeableWeights: async () => [row], confirmChargeableWeight: async (_, key) => { - commands.push(key); if (fail) throw new Error('响应丢失'); row.confirmed = true - } }) - await flush(); await h.confirm(row) - assert.equal(h.error.value, '响应丢失'); assert.equal(h.saving.value, '') - fail = false; await h.confirm(row) - assert.equal(commands[0], commands[1]); assert.deepEqual(h.events, ['confirmed']) - assert.equal(h.rows.value[0].confirmed, true); assert.equal(h.saving.value, '') -}) - -test('切换订单或关闭后丢弃迟到的读取结果,无权限不能确认', async () => { - let resolveOld - const h = harness({ getChargeableWeights: order => order === 'one' ? new Promise(resolve => { resolveOld = resolve }) : Promise.resolve([]), - confirmChargeableWeight: () => { throw new Error('无权限不得写入') } }, { orderId: 'one', canConfirm: false }) - h.props.orderId = 'two'; h.change(); await flush() - resolveOld([{ inboundId: 'old' }]); await flush() - assert.deepEqual(h.rows.value, []) - await h.confirm({ inboundId: 'old', fingerprint: 'old' }) - assert.deepEqual(h.events, []) - h.dispose() -}) diff --git a/src/modules/first-leg/components/ChargeableWeightDialog.vue b/src/modules/first-leg/components/ChargeableWeightDialog.vue deleted file mode 100644 index f8ab6a8..0000000 --- a/src/modules/first-leg/components/ChargeableWeightDialog.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - diff --git a/src/modules/first-leg/components/InboundBillingPanel.test.mjs b/src/modules/first-leg/components/InboundBillingPanel.test.mjs index 868609c..9a7de06 100644 --- a/src/modules/first-leg/components/InboundBillingPanel.test.mjs +++ b/src/modules/first-leg/components/InboundBillingPanel.test.mjs @@ -30,7 +30,7 @@ test('shows the published merchant inbound, measurement details and customer con } assert.match(source, /formatInboundMoney\(props\.data\.latestFee\.totalAmount/) assert.match(source, /formatConversionPrecision\(/) - assert.match(source, /确认费用/) + assert.match(source, /title="确认计费重"/) assert.match(source, /确认后当前费用版本将锁定/) assert.match(source, /customerConfirmationStatus === 'CONFIRMED'/) assert.match(source, /confirmMerchantInboundFee/) diff --git a/src/modules/first-leg/components/InboundBillingPanel.vue b/src/modules/first-leg/components/InboundBillingPanel.vue index dfc2873..c7f6a57 100644 --- a/src/modules/first-leg/components/InboundBillingPanel.vue +++ b/src/modules/first-leg/components/InboundBillingPanel.vue @@ -77,7 +77,7 @@ async function confirmFee() { {{ props.data.latestFee.customerConfirmationStatus === 'CONFIRMED' ? '客户已确认 · 费用已锁定' : '费用已发布 · 待确认' }} - 确认费用 + 确认计费重 查看费用 @@ -198,7 +198,7 @@ async function confirmFee() { - +
正在加载费用 diff --git a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs index 9fc50c2..1213a36 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs +++ b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs @@ -642,8 +642,11 @@ test('opens billing-weight confirmation independently from order details', () => assert.match(source, /unavailable-message="未推送费用"/) }) -test('shows billing-weight confirmation only when the backend marks it pending', () => { - assert.match(source, /if \(canConfirmInboundFee\.value && order\.businessSummary\.customerFeeConfirmationRequired\) \{\s*actions\.push\(\{ key: 'fee'/) +test('uses one published-fee dialog for billing-weight confirmation on every order row', () => { + const actions = source.slice(source.indexOf('function getOrderRowActions'), source.indexOf('function getOrderRowActions') + 1800) + assert.match(actions, /actions\.push\(\{ key: 'fee', label: '确认计费重', run: \(\) => openFeeConfirmation\(order\) \}\)/) + assert.doesNotMatch(actions, /customerFeeConfirmationRequired|label: '确认费用'|key: 'weight'/) + assert.doesNotMatch(source, /ChargeableWeightDialog|weightConfirmationOrderId/) assert.match(source, /:row-actions="getOrderRowActions"/) assert.match(orderStoreSource, /customerFeeConfirmationRequired: summary\?\.customerFeeConfirmationRequired \?\? false/) }) diff --git a/src/modules/first-leg/views/FirstLegOrdersView.vue b/src/modules/first-leg/views/FirstLegOrdersView.vue index b57b896..8ba11aa 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.vue +++ b/src/modules/first-leg/views/FirstLegOrdersView.vue @@ -9,7 +9,6 @@ import DataTable, { type DataTableColumn } from '@/components/common/DataTable.v import type { TableAction } from '@/components/common/tableActions' import { ApiError } from '@/api/http' import FirstLegOrderEditorShell from '@/modules/first-leg/components/FirstLegOrderEditorShell.vue' -import ChargeableWeightDialog from '@/modules/first-leg/components/ChargeableWeightDialog.vue' import InboundBillingPanel from '@/modules/first-leg/components/InboundBillingPanel.vue' import InboundCodeAssociationDialog from '@/modules/first-leg/components/InboundCodeAssociationDialog.vue' import OrderProgressPanel from '@/modules/first-leg/components/OrderProgressPanel.vue' @@ -268,7 +267,6 @@ const inboundBillingUnavailableMessage = ref('还未测量推送') const inboundBillingError = ref('') const inboundBillingOrderId = ref('') const feeConfirmationOpen = ref(false) -const weightConfirmationOrderId = ref('') const customsByOrder = reactive>({}) const customsEligibilityPending = new Set() const customsDialogOpen = ref(false) @@ -1363,12 +1361,7 @@ function getOrderRowActions(row: object): TableAction[] { if (canCreateOrder.value) { actions.push({ key: 'copy', label: '复制下单', loading: copyingOrderId.value === order.id, run: () => copyOrder(order) }) } - if (canConfirmInboundFee.value && !['DRAFT', 'CANCELLED', 'REJECTED', 'SUBMITTED'].includes(order.status)) { - actions.push({ key: 'weight', label: '确认计费重', run: () => { weightConfirmationOrderId.value = order.id } }) - } - if (canConfirmInboundFee.value && order.businessSummary.customerFeeConfirmationRequired) { - actions.push({ key: 'fee', label: '确认费用', run: () => openFeeConfirmation(order) }) - } + actions.push({ key: 'fee', label: '确认计费重', run: () => openFeeConfirmation(order) }) if (canEditOrder.value && canClientEditFirstLegOrder(order.status)) { actions.push({ key: 'edit', label: '编辑', loading: openingEditor.value, run: () => openEdit(order) }) } @@ -2560,7 +2553,6 @@ async function searchOrders(model: QueryModel) { @confirmed="handleInboundFeeConfirmed" @closed="feeConfirmationOpen = false" /> -