Просмотр исходного кода

feat(web-admin): wire fq last-period and dj deadline autofill in SamplePaymentPanel

Auto-recalculate the final installment due amount when earlier periods
change (with manual override), and sync lump-sum deadline to due date.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 недель назад
Родитель
Сommit
618d8fe21c
1 измененных файлов с 65 добавлено и 1 удалено
  1. 65 1
      web-admin/src/views/cell-storage/protocol/SamplePaymentPanel.vue

+ 65 - 1
web-admin/src/views/cell-storage/protocol/SamplePaymentPanel.vue

@@ -59,7 +59,7 @@
               placeholder="选择截止时间"
               clearable
               style="width: 100%"
-              @update:model-value="(v: string) => patchPay(pIdx, { lumpSumDeadline: v || '' })"
+              @update:model-value="(v: string) => onLumpSumDeadlineChange(pIdx, v || '')"
             />
           </el-form-item>
         </template>
@@ -364,12 +364,15 @@
   import {
     applyConsolidateRemainingToPayment,
     applyExpandRemainingToInstallment,
+    applyLumpSumDeadlineToLastDueDate,
     availableSamplePayModes,
+    calcLastPeriodDueAmount,
     parseInstallmentRatios,
     parsePreferentialPrice,
     resolvePerSamplePaymentDefaults,
     samplePaymentHasConfirmedTransfer,
     schedulePaidArrears,
+    shouldAutoFillLastPeriodDue,
     shouldReplaceContractWithPreferentialPrice
   } from '@/utils/protocol-sample-payment'
   import { isMultiSchedulePayMode } from '@/utils/protocol-payment-presets'
@@ -430,6 +433,8 @@
   const transferTypeOptions = ref<Api.Dict.DictDataOption[]>([])
   const submittingId = ref('')
   const savingKey = ref('')
+  /** 按样本 key:用户手改过最后一期应缴金额则 true,前几期再变时清除 */
+  const lastPeriodDueOverrideByKey = ref<Record<string, boolean>>({})
 
   async function ensureTransferTypes() {
     await loadTransferTypeDict()
@@ -442,6 +447,18 @@
     return '¥' + formatMoneyDisplay(v)
   }
 
+  function samplePayKey(pay: Record<string, any>, pIdx: number): string {
+    return String(pay.sampleCode || pay.protocolSampleId || pIdx)
+  }
+
+  function activeScheduleIndices(schedules: Record<string, any>[]): number[] {
+    const idx: number[] = []
+    schedules.forEach((s, i) => {
+      if (String(s.scheduleStatus || 'open') !== 'void' && !s._deleted) idx.push(i)
+    })
+    return idx
+  }
+
   /** 仅财务已确认(yjf)锁定;付款确认中仍可改,直至财务确认 */
   function isTransferLocked(tr: Record<string, any>) {
     return tr.payStatus === 'yjf'
@@ -458,6 +475,37 @@
     const pay = { ...list[pIdx] }
     const schedules = [...(pay.schedules || [])]
     schedules[sIdx] = { ...schedules[sIdx], ...patch }
+    const mode = String(pay.payMode || '')
+    const key = samplePayKey(pay, pIdx)
+    const activeIdx = activeScheduleIndices(schedules)
+
+    if ('dueAmount' in patch && mode === 'fq' && activeIdx.length >= 2) {
+      const lastIdx = activeIdx[activeIdx.length - 1]
+      if (sIdx === lastIdx) {
+        lastPeriodDueOverrideByKey.value = {
+          ...lastPeriodDueOverrideByKey.value,
+          [key]: true
+        }
+      } else if (activeIdx.slice(0, -1).includes(sIdx)) {
+        lastPeriodDueOverrideByKey.value = {
+          ...lastPeriodDueOverrideByKey.value,
+          [key]: false
+        }
+        const lastSch = schedules[lastIdx]
+        const canFill = shouldAutoFillLastPeriodDue({
+          lastManualOverride: false,
+          lastLocked: isScheduleDueLocked(lastSch)
+        })
+        if (canFill) {
+          const priors = activeIdx.slice(0, -1).map((i) => schedules[i]?.dueAmount)
+          const lastAmt = calcLastPeriodDueAmount(pay.contractAmount, priors)
+          if (lastAmt != null) {
+            schedules[lastIdx] = { ...schedules[lastIdx], dueAmount: lastAmt }
+          }
+        }
+      }
+    }
+
     pay.schedules = schedules
     if ('dueAmount' in patch) {
       pay.installmentRatios = schedules.map((s: Record<string, any>) => {
@@ -469,6 +517,22 @@
     emit('update:modelValue', list)
   }
 
+  function onLumpSumDeadlineChange(pIdx: number, raw: string) {
+    const list = [...(props.modelValue || [])]
+    const pay = { ...list[pIdx] }
+    const deadline = raw || ''
+    pay.lumpSumDeadline = deadline
+    if (String(pay.payMode || '') === 'dj') {
+      pay.schedules = applyLumpSumDeadlineToLastDueDate(
+        [...(pay.schedules || [])],
+        deadline,
+        isScheduleDueLocked
+      )
+    }
+    list[pIdx] = pay
+    emit('update:modelValue', list)
+  }
+
   function sampleTitle(pay: Record<string, any>) {
     const sample = (props.samples || []).find(
       (s) => String(s.sampleCode || '') === String(pay.sampleCode || '')