This commit is contained in:
2026-08-11 10:46:01 +08:00
parent 88788dc473
commit de0a90e853
12 changed files with 517 additions and 19 deletions
+122
View File
@@ -0,0 +1,122 @@
import type {
FinanceBillDetail,
FinanceBillLine,
FinanceBillStatus,
FinanceBillSummary,
FinanceReceiptRecord,
} from '../contracts'
type WireScalar = string | number
export interface FinanceBillWire {
id: WireScalar
bill_no: string
customer_name_snapshot: string
currency_code: string
bill_date: string | null
due_date: string | null
status: FinanceBillStatus
total_amount: WireScalar
received_amount: WireScalar
outstanding_amount: WireScalar
lines?: FinanceBillLineWire[]
}
export interface FinanceBillLineWire {
id: WireScalar
source_no: string | null
order_id: WireScalar | null
fee_item_name: string
amount: WireScalar
currency_code: string
}
export interface FinanceReceiptWire {
id: WireScalar
receipt_no: string
received_amount: WireScalar
received_at: string
payment_method: string | null
voucher_no: string | null
status: string
bill_id: WireScalar | null
applied_amount: WireScalar | null
}
function text(value: WireScalar) {
return String(value)
}
function mapBillSummary(bill: FinanceBillWire): FinanceBillSummary {
return {
id: text(bill.id),
billNo: bill.bill_no,
customerName: bill.customer_name_snapshot,
currencyCode: 'CNY',
totalAmount: text(bill.total_amount),
receivedAmount: text(bill.received_amount),
outstandingAmount: text(bill.outstanding_amount),
issueDate: bill.bill_date,
dueDate: bill.due_date,
status: bill.status,
}
}
function mapBillLine(line: FinanceBillLineWire): FinanceBillLine {
return {
id: text(line.id),
orderId: line.order_id == null ? null : text(line.order_id),
feeNo: line.source_no,
feeName: line.fee_item_name,
amount: text(line.amount),
currencyCode: 'CNY',
}
}
export function mapFinanceBills(bills: FinanceBillWire[]) {
return bills.map(mapBillSummary)
}
export function mapFinanceBillDetail(bill: FinanceBillWire): FinanceBillDetail {
return {
...mapBillSummary(bill),
lines: (bill.lines ?? []).map(mapBillLine),
receipts: [],
}
}
export function mapFinanceReceipts(receipts: FinanceReceiptWire[], billId: string): FinanceReceiptRecord[] {
return receipts
.filter((receipt) => receipt.bill_id != null && text(receipt.bill_id) === billId)
.map((receipt) => ({
id: text(receipt.id),
receiptNo: receipt.receipt_no,
receivedAt: receipt.received_at,
amount: text(receipt.received_amount),
currencyCode: 'CNY',
method: receipt.payment_method,
voucherNo: receipt.voucher_no,
allocatedAmount: receipt.applied_amount == null ? '0' : text(receipt.applied_amount),
}))
}
export function sumDecimalStrings(values: string[]) {
let scale = 0
const parsed = values.map((value) => {
const normalized = value.trim() || '0'
const [integer = '0', fraction = ''] = normalized.split('.')
scale = Math.max(scale, fraction.length)
return { integer, fraction }
})
const total = parsed.reduce((sum, value) => {
const sign = value.integer.startsWith('-') ? -1n : 1n
const integer = value.integer.replace(/^[+-]/, '') || '0'
const digits = `${integer}${value.fraction.padEnd(scale, '0')}`
return sum + sign * BigInt(digits || '0')
}, 0n)
if (scale === 0) return total.toString()
const negative = total < 0n
const absolute = (negative ? -total : total).toString().padStart(scale + 1, '0')
const result = `${absolute.slice(0, -scale)}.${absolute.slice(-scale)}`
return negative ? `-${result}` : result
}