Files
zkxw/web-admin/src/utils/protocol-payment-installment.ts
T
2026-05-17 23:08:38 +08:00

136 lines
4.3 KiB
TypeScript

/** 分期缴费:期数对齐、应缴日/金额计算(与 ProtocolFormDialog 逻辑一致) */
export function addMonthsYmd(dateStr: string, months: number): string {
const compact = String(dateStr || '').replace(/-/g, '')
if (compact.length !== 8) return ''
const y = parseInt(compact.slice(0, 4), 10)
const m = parseInt(compact.slice(4, 6), 10) - 1
const d = parseInt(compact.slice(6, 8), 10)
const date = new Date(y, m + months, d)
const yy = date.getFullYear()
const mm = String(date.getMonth() + 1).padStart(2, '0')
const dd = String(date.getDate()).padStart(2, '0')
return `${yy}${mm}${dd}`
}
export function calcInstallmentDueAmounts(
contractAmount: number | null | undefined,
count: number
): number[] {
if (contractAmount == null || !count || count < 1) return []
const totalCents = Math.round(Number(contractAmount) * 100)
const baseCents = Math.floor(totalCents / count)
const amounts: number[] = []
let sumCents = 0
for (let i = 0; i < count; i++) {
if (i === count - 1) {
amounts.push((totalCents - sumCents) / 100)
} else {
amounts.push(baseCents / 100)
sumCents += baseCents
}
}
return amounts
}
type PaymentLike = Record<string, unknown> | null | undefined
type DetailLike = Record<string, any>
/**
* 分期模式下按 installmentCount 补齐缺失期次(保留已有明细数据),仅用于编辑表单。
*/
export function alignFqPaymentDetailsForDisplay(
payment: PaymentLike,
details: DetailLike[]
): DetailLike[] {
if (!payment || payment.payMode !== 'fq') return details
const count = Number(payment.installmentCount)
if (!count || count < 1) return details
const rows = [...details]
const byNo = new Map<number, DetailLike>()
const unnumbered: DetailLike[] = []
for (const row of rows) {
const no = row.installmentNo
if (no != null && no !== '') {
byNo.set(Number(no), row)
} else {
unnumbered.push(row)
}
}
const needed = new Set(Array.from({ length: count }, (_, i) => i + 1))
const have = new Set(byNo.keys())
let missing = false
for (const n of needed) {
if (!have.has(n)) {
missing = true
break
}
}
if (!missing && rows.length >= count) {
return sortFqPaymentDetails(rows).slice(0, count)
}
const contract = payment.contractAmount != null ? Number(payment.contractAmount) : null
const amounts = calcInstallmentDueAmounts(contract, count)
const startDate = String(payment.installmentStartDate || '')
const interval = Number(payment.installmentIntervalMonths || 0)
const result: DetailLike[] = []
for (let i = 0; i < count; i++) {
const no = i + 1
if (byNo.has(no)) {
result.push(byNo.get(no)!)
continue
}
if (i < unnumbered.length) {
const raw = { ...unnumbered[i], installmentNo: no }
if (!raw.installmentDueDate && startDate && interval) {
raw.installmentDueDate = addMonthsYmd(startDate, i * interval)
}
result.push(raw)
continue
}
result.push({
installmentNo: no,
installmentDueDate:
startDate && interval ? addMonthsYmd(startDate, i * interval) : '',
dueAmount: amounts[i] ?? null,
payStatus: 'dfj',
transferType: '',
transferAmount: null,
attachments: []
})
}
return result
}
export function sortFqPaymentDetails(details: DetailLike[]): DetailLike[] {
return [...details].sort((a, b) => {
const na = a.installmentNo != null && a.installmentNo !== '' ? Number(a.installmentNo) : 9999
const nb = b.installmentNo != null && b.installmentNo !== '' ? Number(b.installmentNo) : 9999
if (na !== nb) return na - nb
return Number(a.orderNum || 0) - Number(b.orderNum || 0)
})
}
/** 编辑表单:将缴费明细行数与分期期数对齐(不覆盖已有行) */
export function syncFqPaymentDetailRowCount(
payment: {
payMode: string
installmentCount: number | null
contractAmount: number | null
installmentStartDate: string
installmentIntervalMonths: number | null
},
paymentDetails: DetailLike[]
): void {
if (payment.payMode !== 'fq') return
const count = Number(payment.installmentCount)
if (!count || count < 1) return
const aligned = alignFqPaymentDetailsForDisplay(payment, paymentDetails)
paymentDetails.splice(0, paymentDetails.length, ...aligned)
}