提交
This commit is contained in:
1 parent
427bcb1551
commit
31b04575e2
247 files changed
+39284
-20367
No files matched your search
@@ -43,6 +43,114 @@ export function isInstallmentRatiosValid(ratios: number[]): boolean {
|
||||
return Math.abs(total - 100) < 0.01
|
||||
}
|
||||
|
||||
/** 历史数据:installmentRatios 存的是百分比(合计约 100) */
|
||||
export function isLegacyInstallmentRatios(
|
||||
values: number[],
|
||||
contractAmount?: number | null
|
||||
): boolean {
|
||||
if (!values.length) return false
|
||||
const nums = values.map((v) => Number(v || 0))
|
||||
const sum = nums.reduce((s, v) => s + v, 0)
|
||||
const max = Math.max(...nums)
|
||||
if (contractAmount != null && Math.abs(sum - Number(contractAmount)) < 0.02) {
|
||||
return false
|
||||
}
|
||||
return max <= 100 && Math.abs(sum - 100) < 0.05
|
||||
}
|
||||
|
||||
/** 将存储值规范为各期金额(元);兼容旧比例数据 */
|
||||
export function normalizeInstallmentAmounts(
|
||||
values: number[],
|
||||
contractAmount: number | null | undefined,
|
||||
count: number
|
||||
): number[] {
|
||||
if (!values.length) {
|
||||
return defaultInstallmentAmounts(contractAmount, count)
|
||||
}
|
||||
if (isLegacyInstallmentRatios(values, contractAmount)) {
|
||||
const sized =
|
||||
values.length === count ? values : resizeInstallmentRatios(values, count)
|
||||
return calcInstallmentDueAmountsByRatios(contractAmount, finalizeInstallmentRatios(sized))
|
||||
}
|
||||
const amounts = values.map((v) => Number(v || 0))
|
||||
if (amounts.length === count) return amounts
|
||||
return resizeInstallmentAmounts(amounts, count, contractAmount)
|
||||
}
|
||||
|
||||
export function defaultInstallmentAmounts(
|
||||
contractAmount: number | null | undefined,
|
||||
count: number
|
||||
): number[] {
|
||||
return calcInstallmentDueAmounts(contractAmount, count)
|
||||
}
|
||||
|
||||
export function resizeInstallmentAmounts(
|
||||
oldAmounts: number[],
|
||||
newCount: number,
|
||||
contractAmount: number | null | undefined
|
||||
): number[] {
|
||||
if (newCount <= 0) return []
|
||||
const head = oldAmounts.map((v) => Number(v || 0))
|
||||
if (newCount > head.length) {
|
||||
const existingSum = sumInstallmentAmounts(head)
|
||||
const remaining =
|
||||
contractAmount != null
|
||||
? Math.max(0, Math.round((Number(contractAmount) - existingSum) * 100) / 100)
|
||||
: 0
|
||||
const extraCount = newCount - head.length
|
||||
const extra =
|
||||
remaining > 0 && contractAmount != null
|
||||
? defaultInstallmentAmounts(remaining, extraCount)
|
||||
: Array(extraCount).fill(0)
|
||||
return [...head, ...extra]
|
||||
}
|
||||
if (newCount < head.length) {
|
||||
return head.slice(0, newCount)
|
||||
}
|
||||
return head
|
||||
}
|
||||
|
||||
export function isInstallmentAmountsValid(
|
||||
amounts: number[],
|
||||
contractAmount: number | null | undefined
|
||||
): boolean {
|
||||
if (!amounts.length || contractAmount == null) return false
|
||||
if (amounts.some((v) => Number(v) < 0)) return false
|
||||
const sum = amounts.reduce((s, v) => s + Number(v || 0), 0)
|
||||
return Math.abs(sum - Number(contractAmount)) < 0.01
|
||||
}
|
||||
|
||||
export function sumInstallmentAmounts(amounts: number[]): number {
|
||||
const total = amounts.reduce((s, v) => s + Number(v || 0), 0)
|
||||
return Math.round(total * 100) / 100
|
||||
}
|
||||
|
||||
/** 合同金额变更时,按原各期占比等比缩放金额 */
|
||||
export function scaleInstallmentAmountsToContract(
|
||||
amounts: number[],
|
||||
contractAmount: number | null | undefined
|
||||
): number[] {
|
||||
if (!amounts.length || contractAmount == null) return amounts
|
||||
const oldSum = sumInstallmentAmounts(amounts)
|
||||
if (oldSum <= 0) {
|
||||
return defaultInstallmentAmounts(contractAmount, amounts.length)
|
||||
}
|
||||
const target = Number(contractAmount)
|
||||
const scaled: number[] = []
|
||||
let sumCents = 0
|
||||
const totalCents = Math.round(target * 100)
|
||||
for (let i = 0; i < amounts.length; i++) {
|
||||
if (i === amounts.length - 1) {
|
||||
scaled.push((totalCents - sumCents) / 100)
|
||||
} else {
|
||||
const cents = Math.floor((totalCents * Number(amounts[i] || 0)) / oldSum)
|
||||
scaled.push(cents / 100)
|
||||
sumCents += cents
|
||||
}
|
||||
}
|
||||
return scaled
|
||||
}
|
||||
|
||||
export function addMonthsYmd(dateStr: string, months: number): string {
|
||||
const compact = String(dateStr || '').replace(/-/g, '')
|
||||
if (compact.length !== 8) return ''
|
||||
@@ -175,29 +283,23 @@ type DetailLike = Record<string, any>
|
||||
|
||||
function resolveFqAmounts(payment: PaymentLike, count: number, details?: DetailLike[]): number[] {
|
||||
const contract = payment?.contractAmount != null ? Number(payment.contractAmount) : null
|
||||
const ratios = Array.isArray(payment?.installmentRatios)
|
||||
const stored = Array.isArray(payment?.installmentRatios)
|
||||
? (payment!.installmentRatios as number[])
|
||||
: []
|
||||
if (ratios.length === count && contract != null) {
|
||||
if (stored.length === count && contract != null) {
|
||||
const baseAmounts = isLegacyInstallmentRatios(stored, contract)
|
||||
? calcInstallmentDueAmountsByRatios(contract, finalizeInstallmentRatios(stored))
|
||||
: stored.map((v) => Number(v || 0))
|
||||
const paidRows = (details || []).filter((d) => String(d.payStatus) === 'yjf')
|
||||
if (paidRows.length > 0) {
|
||||
const paidSum = paidRows.reduce((s, d) => s + (Number(d.dueAmount) || 0), 0)
|
||||
const remaining = Math.round((contract - paidSum) * 100) / 100
|
||||
const unpaidIndices = Array.from({ length: count }, (_, i) => i).filter(
|
||||
(i) => !paidRows.some((d) => Number(d.installmentNo) === i + 1)
|
||||
)
|
||||
const unpaidAmounts = calcUnpaidAmountsByRatios(remaining, ratios, unpaidIndices)
|
||||
const amounts = Array(count).fill(0)
|
||||
unpaidIndices.forEach((idx, j) => {
|
||||
amounts[idx] = unpaidAmounts[j] ?? 0
|
||||
})
|
||||
const amounts = [...baseAmounts]
|
||||
paidRows.forEach((d) => {
|
||||
const no = Number(d.installmentNo)
|
||||
if (no >= 1 && no <= count) amounts[no - 1] = Number(d.dueAmount) || 0
|
||||
})
|
||||
return amounts
|
||||
}
|
||||
return calcInstallmentDueAmountsByRatios(contract, ratios)
|
||||
return baseAmounts
|
||||
}
|
||||
return calcInstallmentDueAmounts(contract, count)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user