136 lines
4.3 KiB
TypeScript
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)
|
|
}
|