Files
client-web/src/modules/finance/api/adapters.ts
T

149 lines
4.3 KiB
TypeScript

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
}