Finance 是一组静态数值函数,不是财税或信贷决策系统。它能复用公式,却没有纳税人/合同聚合、政策仓、发生日期、凭证、申报、审批或结果账本。
1. BracketTaxCalculator
TaxBracket 只有 UpperLimit、Rate、QuickDeduction;名称中的“Tax”实际也被拍卖佣金和 LegalCalculators 使用。CalcByQuickFormula 选第一个 amount≤UpperLimit;找不到则用最后一档。CalcProgressive 逐段累加。
// 调用前确保上限严格递增、费率非负、末档覆盖所有输入。TaxBracket[] brackets =[ new(100_000m, 0.02m, 0m), new(decimal.MaxValue, 0.01m, 0m),];
// Progressive 逐档累加;引擎本身不会校验或舍入。var amount = BracketTaxCalculator.CalcProgressive(150_000m, brackets);
// 100000×2% + 50000×1% = 2500。Debug.Assert(amount == 2_500m);空表或 amount≤0 返回 0,不是 Validation。乱序、重复、负 rate、负 quick deduction 或末档不足均被接受。对受监管规则,先创建严格值对象再调用引擎。
2. 综合所得个税
七档上限、税率和速算扣除数与现行官方综合所得表一致。输入可直接是年度应纳税所得额,或年收入减基本、社保和专项附加扣除;终值两位 AwayFromZero。
第二个 overload 只拒绝负收入,不拒绝负 deduction。负扣除会增加 taxable income;超大扣除返回 0。没有居民身份、境内外所得、其他扣除、减免、已预缴、年度汇算或 TaxYear。
// 只传当前方法可表达的三类扣除;其他法定事实必须在上层建模。var result = IncomeTaxCalculator.CalcIndividualIncomeTax( annualIncome: 300_000m, basicDeduction: 60_000m, socialInsuranceDeduction: 36_000m, specialAdditionalDeduction: 24_000m);
// 成功只表示硬编码七档公式完成,不表示税务机关认可。if (result.IsFailure) return result.Error;return new TaxEstimate(result.Value, policyVersion, inputEvidenceHash);TaxEstimate 是目标 wrapper DTO,当前类库没有。
3. 劳务报酬预扣
收入≤4000 减 800,>4000 减 20%;再用 20/30/40% 三档速算。官方口径还要求居民个人年度汇算时并入综合所得,方法名虽写 Tax,实质是预扣预缴估算。
调用方必须区分收入“每次/每月”的归集口径、居民身份、扣缴义务人和年度汇算;当前一个 decimal 无法表达这些事实。
4. 全年一次性奖金
算法用 bonus÷12 选择月度税率,再按 bonus×rate−quick deduction。现行财政部/税务总局公告允许居民个人选择单独计税或并入综合所得,并明确执行至 2027-12-31。
当前方法不接收 TaxYear,不比较“单独/并入”两种方案,也不限制同一纳税人一年使用一次。2028 起如果政策未续期,它仍会照算。必须以 Policy EffectiveTo 阻止过期规则。
5. Mortgage 等额本息
月利率固定为 annualRate/12,月数为 years×12;decimal 快速幂计算 (1+r)^n,避免 double 精度。0 利率直接本金/月数。
Schedule 每月按剩余本金计息并舍入,本金为月供减利息,最后一期吸收剩余本金。CalcEqualInstallmentTotalInterest 却用已舍入月供×月数−本金,可能与逐月 Schedule 利息和产生舍入差异。
6. Mortgage 等额本金
每月本金先舍入两位,最后一期吸收残余;利息按当期剩余本金×月利率。总利息 API 用解析公式,不逐期舍入,因此也可能与 Schedule 总和有分差。
// 先固定本金、年利率和期限;当前 API 不接受实际起息日。var schedule = MortgageCalculator.CalcEqualPrincipalSchedule( principal: 1_200_000m, annualRate: 0.035m, years: 20);
if (schedule.IsFailure) return schedule.Error;
// 同一账单采用逐期明细的舍入合同,总额从明细求和。var totalInterest = schedule.Value.Sum(item => item.Interest);var totalPrincipal = schedule.Value.Sum(item => item.Principal);Debug.Assert(totalPrincipal == 1_200_000m);没有起息日、实际还款日、日计息、闰年、利率重定价、提前还款、费用、罚息或银行差异。
7. Mortgage 容量与溢出
years 仅校验≥1,没有最大值。years * 12 是 int 运算,极端值可溢出;巨大 months 会让 DecimalPow 溢出或 Schedule 分配海量 List。对外 wrapper 应限制本金、费率、期限和最大 schedule rows。
Calc 方法对本金=0 返回 0/全零计划,这是可定义行为。年利率没有合理上限;超高但非负的 rate 可能产生 decimal overflow 而不是 Result Failure。
8. 民间借贷利率
ValidateInterestRateCeiling 委托 Domain InterestCalculator;GetInterestRateCeiling 直接返回 LPR×4,不校验 LPR。司法解释要求使用“合同成立时”一年期 LPR 四倍,并对历史合同/受理时间存在过渡规则。
当前 API 不接收合同成立日、起诉/受理日、合同类型或是否属于民间借贷。调用方必须先选规则,不能拿当天 LPR 自动套用全部期间。
9. 分段利息
每段利息是 principal×annualRate×days/365,固定本金;负利率失败,天数≤0 静默跳过。日期差不含 EndDate,和 record 注释“结束日含”冲突。
InterestSegment[] segments =[ new(new DateTime(2026, 1, 1), new DateTime(2026, 2, 1), 0.04m), new(new DateTime(2026, 2, 1), new DateTime(2026, 3, 1), 0.036m),];
// 当前实现把每段按 [StartDate, EndDate) 计算,边界日期不重复。var result = LoanDetailCalculator.CalcInterestBySegments(500_000m, segments);
// 它不会拒绝重叠、缺口或乱序;生产 wrapper 必须先验证时间轴。if (result.IsFailure) return result.Error;若期间内有还本,固定 principal 会多计利息;需把本金余额也放入时间段或事件账本。
10. 还款抵扣与复利上限
AllocatePayment 按费用→利息→本金,超额还款进入 Remaining;每个抵扣结果两位舍入。输入在抵扣前不统一舍入,可能出现 Payment 与各项分值的分差。
复利上限以 principal×LPR×4×days/365 计算 ceiling interest,并比较传入 compoundInterest。它不自己计算复利,也不验证该利息如何形成;结果是算术校验,不是法院结论。
11. PenaltyCalculator
约定比例、按日比例和 LPR 参考均是简单公式。SuggestAdjustedPenalty 把 actualLoss×1.30 当上限,损失为 0 就建议 0。
2023 合同编通则司法解释第六十五条说超过损失 30% “一般可以认定”过高,同时要求综合合同主体、交易类型、履行、过错、背景、公平诚信等因素。返回一个确定“调整后金额”会掩盖裁量;更合适的是 RiskIndicator,展示阈值比较而不代替裁判。
12. 数值与错误合同
公开方法混用 Result 与裸 decimal。Finance Error code 稳定,但 description 中文且部分场景复用 generic InvalidInput,无法指出具体字段。decimal overflow 不被转 Result;调用方要做范围校验并统一 exception boundary。
货币没有 Currency,Rate 没有 Percent value object,days 没有 DayCountConvention。跨币种或 360/365/实际天数不能安全表达。
13. 测试矩阵
| 能力 | 必测边界 |
|---|---|
| Bracket | 每个上下限±0.01、乱序/负率/缺末档 |
| 个税 | 七档边界、负扣除、政策年度、官方黄金值 |
| 劳务 | 800、4000、20000、50000 边界与年度汇算提示 |
| 奖金 | 每个月度档边界、单独/并入比较、2027 过期 |
| Mortgage | 0 利率、1/30 年、明细合计、分差、溢出/容量 |
| Segment | 同日、包含性、闰年、重叠/缺口/乱序/还本 |
| Allocation | 0、刚好清偿、超额、舍入分差 |
| Penalty | 0 损失、130% 边界、裁量因素不可表达 |
当前这些测试均不存在。
14. 检查命令
# 公式、舍入、日期差和分档实现。rg -n "Math.Round|AwayFromZero|TotalDays|CalcByQuickFormula|CalcProgressive|years \* 12" \ src/Platform/IndustryExtensions/BitzOrcas.Platform.IndustryExtensions.Finance -g '*.cs'
# Policy/date/currency/breakdown/测试当前预期无完整实现。rg -n "TaxYear|ContractDate|Currency|DayCountConvention|PolicyVersion|\[Fact\]" \ src/Platform/IndustryExtensions tests -g '*.cs'