更新计费预览与测量精度并精简作业界面

This commit is contained in:
2026-10-28 11:06:42 +08:00
parent e41e9e6d3f
commit 6f8dc920ca
21 changed files with 522 additions and 150 deletions
+10
View File
@@ -7,6 +7,7 @@ import {
createFeeActionIdempotencyKey,
createFeeAdjustmentIdempotencyKey,
getLatestInboundFee,
previewInboundFee,
sendInboundFee,
} from './api'
@@ -32,6 +33,15 @@ describe('inbound fee API', () => {
expect(http.get).not.toHaveBeenCalled()
})
it('previews selected outer billing values without a write idempotency header', async () => {
const preview = { actualWeightKg: 570.5, actualVolumeCbm: 3.894, chargeableQuantity: 650.298, priceMatched: true }
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: preview })
const payload = { selectedWeightKg: 570.5, selectedVolumeCbm: 3.894, selectedUnitPrice: 13.6, weightSource: 'ACTUAL', volumeSource: 'OUTER' }
await expect(previewInboundFee('10001', '88', payload)).resolves.toEqual(preview)
expect(http.post).toHaveBeenCalledWith('/platform/v1/inbounds/88/fees/preview', payload, { params: { tenantId: '10001' } })
})
it('loads the latest immutable fee snapshot with string IDs', async () => {
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: snapshot })
+9 -2
View File
@@ -1,5 +1,5 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import type { AdjustPublishedInboundFeeRequest, InboundFeeSnapshot } from './types'
import type { AdjustPublishedInboundFeeRequest, InboundFeePreview, InboundFeePreviewRequest, InboundFeeSelectionRequest, InboundFeeSnapshot } from './types'
function feeBasePath(inboundId: string) {
const identifier = String(inboundId ?? '').trim()
@@ -15,7 +15,14 @@ export async function getLatestInboundFee(tenantId: string, inboundId: string) {
return unwrapApiResponse(response)
}
export async function calculateInboundFee(tenantId: string, inboundId: string, payload: { selectedWeightKg: number; selectedVolumeCbm: number; weightSource: string; volumeSource: string }, idempotencyKey: string) {
export async function previewInboundFee(tenantId: string, inboundId: string, payload: InboundFeePreviewRequest) {
const response = await http.post<unknown, ApiEnvelope<InboundFeePreview>>(
`${feeBasePath(inboundId)}/preview`, payload, { params: { tenantId } },
)
return unwrapApiResponse(response)
}
export async function calculateInboundFee(tenantId: string, inboundId: string, payload: InboundFeeSelectionRequest, idempotencyKey: string) {
const response = await http.post<unknown, ApiEnvelope<InboundFeeSnapshot>>(
`${feeBasePath(inboundId)}/calculate`, payload,
{ params: { tenantId }, headers: { 'Idempotency-Key': idempotencyKey } },
+26
View File
@@ -1,6 +1,32 @@
export type InboundFeeStatus = 'CALCULATED' | 'CONFIRMED' | 'SENT' | 'SUPERSEDED' | string
export type InboundChargeMode = 'KG' | 'CBM' | string
export interface InboundFeeSelectionRequest {
selectedWeightKg: number
selectedVolumeCbm: number
weightSource: string
volumeSource: string
}
export interface InboundFeePreview {
actualWeightKg: number
actualVolumeCbm: number
chargeableQuantity: number
chargeUnit: string
conversionFactor: number
roundingScale: number
roundingMode: string
unitPrice: number | null
additionalAmount: number
totalAmount: number | null
currency: string
priceMatched: boolean
}
export interface InboundFeePreviewRequest extends InboundFeeSelectionRequest {
selectedUnitPrice: number | null
}
export interface InboundFeeSnapshot {
feeId: string
feeNo: string