import type { FinanceBillDetail, FinanceBillLine, FinanceBillStatus, FinanceCustomerConfirmationStatus, FinanceBillSummary, FinanceReceiptRecord, } from '../contracts' type WireScalar = string | number export interface FinanceBillWire { id: WireScalar customerId: WireScalar billNo: string customerName: string currency: string billDate: string | null dueDate: string | null status: FinanceBillStatus totalAmount: WireScalar receivedAmount: WireScalar outstandingAmount: WireScalar version: number customerConfirmationStatus: FinanceCustomerConfirmationStatus customerConfirmedByName: string | null customerConfirmedAt: string | null } export interface FinanceBillLineWire { id: WireScalar sourceType: string sourceId: WireScalar sourceVersion: number orderId: WireScalar | null inboundId: WireScalar | null inboundCode: string | null feeItemCode: string feeItemName: string amount: WireScalar currency: string } export interface FinanceBillDetailWire { statement: FinanceBillWire lines: FinanceBillLineWire[] } export interface FinanceReceiptAllocationWire { billId: WireScalar billNo: string appliedAmount: WireScalar } export interface FinanceReceiptWire { id: WireScalar receiptNo: string receivedAmount: WireScalar receivedAt: string paymentMethod: string | null currency: string status: string allocations: FinanceReceiptAllocationWire[] } function text(value: WireScalar) { return String(value) } function mapBillSummary(bill: FinanceBillWire): FinanceBillSummary { return { id: text(bill.id), billNo: bill.billNo, customerName: bill.customerName, currencyCode: 'CNY', totalAmount: text(bill.totalAmount), receivedAmount: text(bill.receivedAmount), outstandingAmount: text(bill.outstandingAmount), issueDate: bill.billDate, dueDate: bill.dueDate, status: bill.status, version: bill.version ?? 0, customerConfirmationStatus: bill.customerConfirmationStatus ?? 'NOT_REQUIRED', customerConfirmedByName: bill.customerConfirmedByName ?? null, customerConfirmedAt: bill.customerConfirmedAt ?? null, } } function mapBillLine(line: FinanceBillLineWire): FinanceBillLine { return { id: text(line.id), orderId: line.orderId == null ? null : text(line.orderId), inboundCode: line.inboundCode?.trim() || null, feeNo: `${line.sourceType}:${text(line.sourceId)}/v${line.sourceVersion}`, feeName: line.feeItemName, amount: text(line.amount), currencyCode: 'CNY', } } export function mapFinanceBills(bills: FinanceBillWire[]) { return bills .filter((bill) => bill.status === 'ISSUED' || bill.status === 'PARTIALLY_PAID' || bill.status === 'PAID') .map(mapBillSummary) } export function mapFinanceBillDetail(detail: FinanceBillDetailWire): FinanceBillDetail { return { ...mapBillSummary(detail.statement), lines: (detail.lines ?? []).map(mapBillLine), receipts: [], } } export function mapFinanceReceipts(receipts: FinanceReceiptWire[], billId: string): FinanceReceiptRecord[] { return receipts.flatMap((receipt) => receipt.allocations .filter((allocation) => text(allocation.billId) === billId) .map((allocation) => ({ id: text(receipt.id), receiptNo: receipt.receiptNo, receivedAt: receipt.receivedAt, amount: text(receipt.receivedAmount), currencyCode: 'CNY' as const, method: receipt.paymentMethod, allocatedAmount: text(allocation.appliedAmount), }))) } 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 }