管理費定價(新舊約)與申請單帶入
回答兩件事:管理費怎麼區分新約 / 舊約,以及申請單建立時費用怎麼從規格帶進來、算在哪、何時物化。
⚠️ provenance:本頁以本機 worktree code 現狀 + SA 5.7.3.5 草案為準;#272/#274/#275 部分改動可能尚未 push/merge。行號是當下快照,方法名才是長期錨點。
一、怎麼區分新約 / 舊約
判別是「日期比較」,不是資料庫旗標。
- 依
products.f_contract_signed_date(合約簽訂日;migration318只加欄位,不含判定邏輯) 與 程式碼常數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,各配 syntheticf_t_use_fid。 - 舊約 →
f_limit > 0(正整數年限)或999(永久)。 f_limit語義(權威MaintenanceFeeFormatter.cs/ 前端maintenanceFee.tsformatMaintenanceFeeLimit):999→永久、-1→一次性、0→未填(顯示空白,不是永久)。⚠️ 舊 SA/早期 #216 討論曾把0當永久,已被現行 code 取代。tb_application_items.f_one_time_mgmt_fee已退役(migration323只 COMMENT 不 DROP,EF 映射移除、零讀寫)——真相在management_fees的-1列。
二、申請單建立怎麼帶入費用
規格設定 → 申請單顯示/計算的完整路徑:
- 後端投「原始值」不投算好的表:
ApplicationCreateService.GetCustomerProductsAsync批次查specs,把四欄原始值投影到ProductForAppDto:ManagementFeePerPeriod/ManagementFeePromoAmount/ManagementFeePeriods/ShortTermManagementFee(對應 specs 欄位f_management_fee_per_period/f_management_fee_promo_amount/f_management_fee_periods/f_short_term_management_fee)。 - 分期明細=前端算:
ApplicationsMgmtFeePanel.vue的promoPlans/applyPlan在前端把優惠總額 ÷ 期數拆成 N 期;applyRegularDefault用每期金額補預設。 - 短期管理費(遷出再收)=後端算:
ApplicationCreateService.CalculateShortTermFeeAsync算月數→期數(Math.Ceiling(月數/6))→ShortTermFeePricingManager.ResolvePerPeriodFeeAsync→SpecManagementFeePricingResolver.ResolveAsync(specId)(讀spec.FShortTermManagementFee; 取鍵已從(productType, floorCode)改為specId)→ 回算好的總額,前端直接建列不再算。 - 一次性費(新約)=前端依日期顯示欄位 + 查價:
CertificatePurchaseEditor.vueshouldShowOneTimeManagementFee/syncOneTimeManagementFeeVisibility依contractSignedDate > 2025-07-01決定顯示,查/Query/ManagementFeePricing/Lookup(contractType=new, pricingKind=one_time)帶價,可手改。 - 物化時機:
- 新約一次性在送出(submit)時由
SubmitApplicationManager.MaterializeOneTimeManagementFeesAsync物化 N 筆(冪等:已有f_authNo+f_limit=-1列則跳過)。⚠️不是完成/晉塔端。 - 舊約分期/預繳明細存進
nameplateUsersJson,由ApplicationActionService.MaterializePrepayFeesAsync物化(沿用既有預繳流程)。
- 新約一次性在送出(submit)時由
現行 code 錨點
| 目的 | 錨點 |
|---|---|
| 新舊約判定 | SubmitApplicationManager.IsNewContract(SubmitApplicationManager.cs);常數 ContractTypeCutoffDate=2025-07-01 |
| 一次性物化 | SubmitApplicationManager.MaterializeOneTimeManagementFeesAsync(送出時) |
f_limit 格式/語義 | MaintenanceFeeFormatter.cs;前端 formatMaintenanceFeeLimit(maintenanceFee.ts) |
| 規格四欄投值 | ApplicationCreateService.GetCustomerProductsAsync → ProductForAppDto |
| 分期明細(前端) | ApplicationsMgmtFeePanel.vue(promoPlans / applyPlan / applyRegularDefault) |
| 短期費(後端) | ShortTermFeePricingManager.ResolvePerPeriodFeeAsync → SpecManagementFeePricingResolver.ResolveAsync |
| 一次性欄位顯示/查價(前端) | CertificatePurchaseEditor.vue(shouldShowOneTimeManagementFee) |
| 舊約預繳物化 | ApplicationActionService.MaterializePrepayFeesAsync |
| schema | specs 定價四欄(migration 322);f_contract_signed_date(318);退役 f_one_time_mgmt_fee(323) |
限制 / 落差
- 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。
相關
- 申請單異動從建立到完成怎麼運作 — 申請單建立/送出/完成主流程(費用帶入是其中一段)
- 異動完成後去哪裡查 — 管理費完成後去哪查、退費為什麼「消失」
- 資產狀態機 —
management_fees軟刪/作廢與狀態真相