This commit is contained in:
2026-08-04 21:01:34 +08:00
parent 161bf580c7
commit 2313fd238c
12 changed files with 716 additions and 24 deletions
@@ -0,0 +1,20 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
const source = readFileSync(new URL('./inboundBilling.ts', import.meta.url), 'utf8')
test('formats zero values, currencies, measurements, and backend dates without hardcoded business factors', () => {
assert.match(source, /value === null \|\| value === undefined/)
assert.match(source, /Intl\.NumberFormat\('zh-CN'/)
assert.match(source, /style: 'currency'/)
assert.match(source, /Intl\.DateTimeFormat\('zh-CN'/)
assert.doesNotMatch(source, /\b167\b|\b363\b/)
})
test('translates backend rounding modes into customer-facing calculation labels', () => {
assert.match(source, /export function getRoundingModeLabel/)
assert.match(source, /CEILING: '向上取整'/)
assert.match(source, /HALF_UP: '四舍五入'/)
assert.match(source, /return labels\[value\] \|\| value \|\| '-'/)
})
@@ -0,0 +1,60 @@
export function formatInboundNumber(value: number | null | undefined, maximumFractionDigits = 3) {
if (value === null || value === undefined || !Number.isFinite(value)) return '-'
return new Intl.NumberFormat('zh-CN', { maximumFractionDigits }).format(value)
}
export function formatInboundMeasure(value: number | null | undefined, unit: string, maximumFractionDigits = 3) {
const formatted = formatInboundNumber(value, maximumFractionDigits)
return formatted === '-' ? formatted : `${formatted} ${unit}`
}
export function formatInboundMoney(value: number | null | undefined, currency: string) {
if (value === null || value === undefined || !Number.isFinite(value)) return '-'
try {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(value)
} catch {
return `${currency} ${value.toFixed(2)}`
}
}
export function formatInboundDateTime(value: string | null | undefined) {
if (!value) return '-'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return value
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
}).format(date)
}
export function getChargeModeLabel(value: 'KG' | 'CBM') {
return value === 'KG' ? '按重量计费' : '按体积计费'
}
export function getTaxModeLabel(value: string) {
if (value === 'TAX_INCLUDED') return '包税'
if (value === 'TAX_EXCLUDED') return '不包税'
return value || '-'
}
export function getRoundingModeLabel(value: string) {
const labels: Record<string, string> = {
UP: '向上舍入',
DOWN: '向下舍入',
CEILING: '向上取整',
FLOOR: '向下取整',
HALF_UP: '四舍五入',
HALF_DOWN: '五舍六入',
HALF_EVEN: '银行家舍入',
}
return labels[value] || value || '-'
}