提交
This commit is contained in:
1 parent
cdaa0ac273
commit
b7cd657f32
32 files changed
+2885
-1936
No files matched your search
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* 采集协议单 · 缴费模式切换(dj ↔ fq)
|
||||
* 采集协议单 · 缴费模式切换(dj / fq / djfq)
|
||||
* 规则见 docs/superpowers/specs/2026-06-04-protocol-pay-mode-switch-design.md
|
||||
*/
|
||||
|
||||
@@ -8,8 +8,9 @@ import {
|
||||
DEFAULT_INSTALLMENT_ADVANCE_DAYS,
|
||||
defaultInstallmentAmounts
|
||||
} from '@/utils/protocol-payment-installment'
|
||||
import { type PayMode, isInstallmentPayMode } from '@/utils/protocol-payment-presets'
|
||||
|
||||
export type PayMode = 'dj' | 'fq'
|
||||
export type { PayMode }
|
||||
export type DetailLike = Record<string, any>
|
||||
|
||||
const LOCKED_STATUSES = new Set(['jfqrz', 'yjf', 'tfqrz', 'ytf'])
|
||||
@@ -69,6 +70,35 @@ function countByStatus(details: DetailLike[], status: string): number {
|
||||
return details.filter((d) => String(d.payStatus || 'dfj') === status).length
|
||||
}
|
||||
|
||||
function buildInstallmentDetails(
|
||||
ctx: PayModeSwitchContext,
|
||||
rows: DetailLike[]
|
||||
): DetailLike[] {
|
||||
const count = Number(ctx.installmentCount) || 6
|
||||
const interval = Number(ctx.installmentIntervalMonths) || 6
|
||||
const startDate = ctx.installmentStartDate || ''
|
||||
const advanceDays = Number(ctx.installmentAdvanceDays ?? DEFAULT_INSTALLMENT_ADVANCE_DAYS)
|
||||
const amounts =
|
||||
ctx.installmentRatios?.length && sumPaidAmount(rows) === 0
|
||||
? ctx.installmentRatios.map((v) => Number(v || 0))
|
||||
: defaultInstallmentAmounts(ctx.contractAmount, count)
|
||||
const result: DetailLike[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
result.push(
|
||||
createPaymentDetailRow({
|
||||
dueAmount: amounts[i] ?? null,
|
||||
installmentNo: i + 1,
|
||||
installmentDueDate:
|
||||
startDate && interval
|
||||
? calcFqInstallmentDueDate(startDate, i, interval, advanceDays)
|
||||
: '',
|
||||
expanded: i === 0
|
||||
})
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function createPaymentDetailRow(options: {
|
||||
dueAmount: number | null
|
||||
installmentDueDate?: string
|
||||
@@ -110,7 +140,7 @@ export function evaluatePayModeSwitch(
|
||||
}
|
||||
}
|
||||
|
||||
if (fromMode === 'dj' && toMode === 'fq') {
|
||||
if (fromMode === 'dj' && isInstallmentPayMode(toMode)) {
|
||||
if (rows.some((d) => isPayDetailLocked(d))) {
|
||||
return {
|
||||
action: 'blocked',
|
||||
@@ -118,13 +148,14 @@ export function evaluatePayModeSwitch(
|
||||
}
|
||||
}
|
||||
const count = ctx.installmentCount || 6
|
||||
const label = toMode === 'djfq' ? '趸交+分期' : '分期'
|
||||
return {
|
||||
action: 'confirm',
|
||||
summary: `将清除当前趸交计划,按 ${count} 期分期重新生成缴费明细。是否继续?`
|
||||
summary: `将清除当前趸交计划,按 ${count} 期${label}重新生成缴费明细。是否继续?`
|
||||
}
|
||||
}
|
||||
|
||||
if (fromMode === 'fq' && toMode === 'dj') {
|
||||
if (isInstallmentPayMode(fromMode) && toMode === 'dj') {
|
||||
const dfjCount = countByStatus(rows, 'dfj')
|
||||
const yjfCount = countByStatus(rows, 'yjf')
|
||||
|
||||
@@ -156,6 +187,21 @@ export function evaluatePayModeSwitch(
|
||||
}
|
||||
}
|
||||
|
||||
if (isInstallmentPayMode(fromMode) && isInstallmentPayMode(toMode) && fromMode !== toMode) {
|
||||
if (rows.some((d) => isPayDetailLocked(d))) {
|
||||
return {
|
||||
action: 'blocked',
|
||||
reason: '分期已有缴费记录,无法切换分期类型。'
|
||||
}
|
||||
}
|
||||
const count = ctx.installmentCount || 6
|
||||
const label = toMode === 'djfq' ? '趸交+分期' : '分期'
|
||||
return {
|
||||
action: 'confirm',
|
||||
summary: `将按 ${count} 期${label}重新生成缴费明细。是否继续?`
|
||||
}
|
||||
}
|
||||
|
||||
return { action: 'noop' }
|
||||
}
|
||||
|
||||
@@ -167,33 +213,11 @@ export function applyPayModeSwitch(
|
||||
): DetailLike[] {
|
||||
const rows = activeDetails(details)
|
||||
|
||||
if (fromMode === 'dj' && toMode === 'fq') {
|
||||
const count = Number(ctx.installmentCount) || 6
|
||||
const interval = Number(ctx.installmentIntervalMonths) || 6
|
||||
const startDate = ctx.installmentStartDate || ''
|
||||
const advanceDays = Number(ctx.installmentAdvanceDays ?? DEFAULT_INSTALLMENT_ADVANCE_DAYS)
|
||||
const amounts =
|
||||
ctx.installmentRatios?.length && sumPaidAmount(rows) === 0
|
||||
? ctx.installmentRatios.map((v) => Number(v || 0))
|
||||
: defaultInstallmentAmounts(ctx.contractAmount, count)
|
||||
const result: DetailLike[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
result.push(
|
||||
createPaymentDetailRow({
|
||||
dueAmount: amounts[i] ?? null,
|
||||
installmentNo: i + 1,
|
||||
installmentDueDate:
|
||||
startDate && interval
|
||||
? calcFqInstallmentDueDate(startDate, i, interval, advanceDays)
|
||||
: '',
|
||||
expanded: i === 0
|
||||
})
|
||||
)
|
||||
}
|
||||
return result
|
||||
if (fromMode === 'dj' && isInstallmentPayMode(toMode)) {
|
||||
return buildInstallmentDetails(ctx, rows)
|
||||
}
|
||||
|
||||
if (fromMode === 'fq' && toMode === 'dj') {
|
||||
if (isInstallmentPayMode(fromMode) && toMode === 'dj') {
|
||||
const yjfRows = rows
|
||||
.filter((d) => String(d.payStatus) === 'yjf')
|
||||
.map((d) => ({ ...d, _payModeLocked: true }))
|
||||
@@ -209,14 +233,14 @@ export function applyPayModeSwitch(
|
||||
return [...yjfRows, newDjRow]
|
||||
}
|
||||
|
||||
if (isInstallmentPayMode(fromMode) && isInstallmentPayMode(toMode) && fromMode !== toMode) {
|
||||
return buildInstallmentDetails(ctx, rows)
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
/** 趸交模式下是否还可添加待缴明细 */
|
||||
export function canAddPaymentDetail(payMode: string, details: DetailLike[]): boolean {
|
||||
if (payMode === 'fq') return true
|
||||
const dfjCount = details.filter(
|
||||
(d) => !isPayDetailLocked(d) && String(d.payStatus || 'dfj') === 'dfj'
|
||||
).length
|
||||
return dfjCount < 1
|
||||
/** 趸交与分期均允许多条缴费明细(合计须等于合同金额) */
|
||||
export function canAddPaymentDetail(_payMode: string, _details: DetailLike[]): boolean {
|
||||
return true
|
||||
}
|
||||
Reference in new issue
Block a user