管理費定價(新舊約)與申請單帶入

回答兩件事:管理費怎麼區分新約 / 舊約,以及申請單建立時費用怎麼從規格帶進來、算在哪、何時物化

⚠️ provenance:本頁以本機 worktree code 現狀 + SA 5.7.3.5 草案為準;#272/#274/#275 部分改動可能尚未 push/merge。行號是當下快照,方法名才是長期錨點

一、怎麼區分新約 / 舊約

判別是「日期比較」,不是資料庫旗標。

  • products.f_contract_signed_date(合約簽訂日;migration 318加欄位,不含判定邏輯) 與 程式碼常數 ContractTypeCutoffDate = 2025-07-01 比較:
    • > 2025-07-01新約<= 或未填 → 舊約
  • 判定邏輯在 SubmitApplicationManager.IsNewContract(DateTime)SubmitApplicationManager.cs)。 DB 層沒有 enum/constraint 直接標記新舊約——別去 grep 一個「新舊約欄位」。

兩種定價模型:

新約(塔/蓮)舊約
模型一次性管理費(一次繳清)分期 / 優惠價 / 短期 / 永久年限
UI 帶價來源tb_management_fee_pricing(現行 UI 維護 pricing_kind='one_time'已遷移到塔位/蓮位規格 specs 欄位(migration 322,#275)

management_fees 落地樣貌:

  • 新約一次性 → 物化為 N 筆(N=規格納骨量),f_limit = -1(一次性哨兵,#216), 金額=總額÷N(餘數歸末筆),f_markD='2'(待核銷),x_bool=Active,各配 synthetic f_t_use_fid
  • 舊約 → f_limit > 0(正整數年限)或 999(永久)。
  • f_limit 語義(權威 MaintenanceFeeFormatter.cs / 前端 maintenanceFee.ts formatMaintenanceFeeLimit): 999→永久、-1→一次性、0未填(顯示空白,不是永久)。⚠️ 舊 SA/早期 #216 討論曾把 0 當永久,已被現行 code 取代
  • tb_application_items.f_one_time_mgmt_fee退役(migration 323 只 COMMENT 不 DROP,EF 映射移除、零讀寫)——真相在 management_fees-1 列。

二、申請單建立怎麼帶入費用

規格設定 → 申請單顯示/計算的完整路徑:

  1. 後端投「原始值」不投算好的表ApplicationCreateService.GetCustomerProductsAsync 批次查 specs,把四欄原始值投影到 ProductForAppDtoManagementFeePerPeriod / ManagementFeePromoAmount / ManagementFeePeriods / ShortTermManagementFee (對應 specs 欄位 f_management_fee_per_period / f_management_fee_promo_amount / f_management_fee_periods / f_short_term_management_fee)。
  2. 分期明細=前端算ApplicationsMgmtFeePanel.vuepromoPlans / applyPlan 在前端把 優惠總額 ÷ 期數 拆成 N 期;applyRegularDefault每期金額 補預設。
  3. 短期管理費(遷出再收)=後端算ApplicationCreateService.CalculateShortTermFeeAsync 算月數→期數(Math.Ceiling(月數/6))→ ShortTermFeePricingManager.ResolvePerPeriodFeeAsyncSpecManagementFeePricingResolver.ResolveAsync(specId)(讀 spec.FShortTermManagementFee; 取鍵已從 (productType, floorCode) 改為 specId)→ 回算好的總額,前端直接建列不再算。
  4. 一次性費(新約)=前端依日期顯示欄位 + 查價CertificatePurchaseEditor.vue shouldShowOneTimeManagementFee / syncOneTimeManagementFeeVisibilitycontractSignedDate > 2025-07-01 決定顯示,查 /Query/ManagementFeePricing/LookupcontractType=new, pricingKind=one_time)帶價,可手改。
  5. 物化時機
    • 新約一次性在送出(submit)時由 SubmitApplicationManager.MaterializeOneTimeManagementFeesAsync 物化 N 筆(冪等:已有 f_authNo+f_limit=-1 列則跳過)。⚠️不是完成/晉塔端。
    • 舊約分期/預繳明細存進 nameplateUsersJson,由 ApplicationActionService.MaterializePrepayFeesAsync 物化(沿用既有預繳流程)。

現行 code 錨點

目的錨點
新舊約判定SubmitApplicationManager.IsNewContractSubmitApplicationManager.cs);常數 ContractTypeCutoffDate=2025-07-01
一次性物化SubmitApplicationManager.MaterializeOneTimeManagementFeesAsync(送出時)
f_limit 格式/語義MaintenanceFeeFormatter.cs;前端 formatMaintenanceFeeLimitmaintenanceFee.ts
規格四欄投值ApplicationCreateService.GetCustomerProductsAsyncProductForAppDto
分期明細(前端)ApplicationsMgmtFeePanel.vuepromoPlans / applyPlan / applyRegularDefault
短期費(後端)ShortTermFeePricingManager.ResolvePerPeriodFeeAsyncSpecManagementFeePricingResolver.ResolveAsync
一次性欄位顯示/查價(前端)CertificatePurchaseEditor.vueshouldShowOneTimeManagementFee
舊約預繳物化ApplicationActionService.MaterializePrepayFeesAsync
schemaspecs 定價四欄(migration 322);f_contract_signed_date318);退役 f_one_time_mgmt_fee323

限制 / 落差

  • SA 5.7.3.5 是工程草擬草案、待 Allen 審核;與 code 現況一致(UI 只維護 one_time、其餘遷 specs),但非最終定稿。
  • 物化時機在 submit 非完成端——以「完成才物化」假設追 bug 會找錯位置。
  • f_limit=0 = 未填(非永久),舊語義已被 formatMaintenanceFeeLimit 取代。
  • 「同人一次性用過即鎖」的晉塔綁費分流(#274 Task 3/3-B)落地用 f_useName 姓名基準、 下拉端與 write-guard 端刻意不對稱;只看規則矩陣易誤判為 bug,細節見該 plan 的 AS-BUILT。

相關