Files
SunCheng 9dce12c61b
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 18s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
归档
2026-02-21 21:58:55 +08:00

132 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Spec: 存款明细计算核心算法
## ADDED Requirements
### Requirement: 月度计划存款计算公式
系统 SHALL 使用以下公式计算月度计划存款:
**月度计划存款 = 收入预算 + 发生在本月的年度收入 - 支出预算 - 发生在本月的年度支出**
其中:
- **收入预算**:所有月度收入预算项的预算金额之和
- **发生在本月的年度收入**年度收入预算项在本月实际发生的金额actual > 0
- **支出预算**:所有月度支出预算项的预算金额之和
- **发生在本月的年度支出**年度支出预算项在本月实际发生的金额actual > 0
#### Scenario: 纯月度预算计算
- **WHEN** 用户查询 2026年2月的月度存款且只有月度预算工资10000、奖金5000、房租3000、餐饮2000
- **THEN** 系统返回计划存款 = 10000 + 5000 - 3000 - 2000 = 10000
#### Scenario: 月度预算 + 本月发生的年度预算
- **WHEN** 用户查询 2026年2月的月度存款月度预算工资10000、房租3000且年度旅游支出在本月实际发生3000元
- **THEN** 系统返回计划存款 = 10000 - 3000 - 3000 = 4000
#### Scenario: 年度预算未在本月发生
- **WHEN** 用户查询 2026年2月的月度存款月度预算工资10000、房租3000年度年终奖预算50000但本月实际为0
- **THEN** 系统返回计划存款 = 10000 - 3000 = 7000年终奖不计入
### Requirement: 年度计划存款计算公式
系统 SHALL 使用以下公式计算年度计划存款:
**年度计划存款 = 归档月已实收 + 未来月(包含本月)收入预算 - 归档月已实支 - 未来月(包含本月)支出预算**
其中:
- **归档月已实收**已归档月份1月~当前月-1的实际收入金额之和`BudgetArchive` 读取
- **未来月收入预算**:当前月及未来月份的月度收入预算 × 剩余月数
- **归档月已实支**:已归档月份的实际支出金额之和,从 `BudgetArchive` 读取
- **未来月支出预算**:当前月及未来月份的月度支出预算 × 剩余月数
#### Scenario: 年初无归档数据
- **WHEN** 用户在 2026年1月查询年度存款月度预算工资10000/月、房租3000/月),无归档数据
- **THEN** 系统返回计划存款 = (10000 - 3000) × 12 = 84000
#### Scenario: 年中有归档数据
- **WHEN** 用户在 2026年3月查询年度存款1月归档已实收15000、已实支48002月归档已实收14000、已实支52003~12月月度预算工资10000、房租3000
- **THEN** 系统返回计划存款 = (15000 + 14000) + (10000 × 10) - (4800 + 5200) - (3000 × 10) = 129000
#### Scenario: 归档数据包含年度预算
- **WHEN** 归档数据中包含年度预算的实际发生金额如1月旅游支出3000
- **THEN** 系统将其计入"归档月已实支",不重复计算
### Requirement: 明细项计算用金额 - 收入规则
对于收入类预算项,系统 SHALL 根据以下规则计算"计算用金额"
- 如果实际金额 > 0计算用金额 = 实际金额
- 如果实际金额 = 0计算用金额 = 预算金额
#### Scenario: 收入已发生
- **WHEN** 工资预算10000实际发生9500
- **THEN** 计算用金额 = 9500标注为"使用实际"
#### Scenario: 收入未发生
- **WHEN** 奖金预算5000实际发生0
- **THEN** 计算用金额 = 5000标注为"使用预算"
### Requirement: 明细项计算用金额 - 支出规则(普通)
对于非硬性支出类预算项,系统 SHALL 计算用金额 = MAX(预算金额, 实际金额)
#### Scenario: 支出未超预算
- **WHEN** 餐饮预算2000实际发生1800
- **THEN** 计算用金额 = 2000标注为"使用预算"
#### Scenario: 支出超预算
- **WHEN** 餐饮预算2000实际发生2500
- **THEN** 计算用金额 = 2500标注为"使用实际(超支)",高亮显示
### Requirement: 明细项计算用金额 - 支出规则(硬性)
对于硬性支出(`IsMandatoryExpense = true`)且实际金额 = 0 的预算项,系统 SHALL 按天数折算计算用金额,不进行 MAX 比较。
**月度折算公式**:计算用金额 = 预算金额 / 当月天数 × 当前日期
**年度折算公式**:计算用金额 = 预算金额 / 当年天数 × 当前天数
#### Scenario: 硬性支出未发生(月度)
- **WHEN** 房租预算3000硬性实际为0当前日期为2月15日2月共28天
- **THEN** 计算用金额 = 3000 / 28 × 15 ≈ 1607.14,标注为"按天折算"
#### Scenario: 硬性支出已发生
- **WHEN** 房租预算3000硬性实际发生3000
- **THEN** 计算用金额 = MAX(3000, 3000) = 3000标注为"使用实际"
#### Scenario: 硬性支出超预算
- **WHEN** 水电预算500硬性实际发生600
- **THEN** 计算用金额 = MAX(500, 600) = 600标注为"使用实际(超支)"
#### Scenario: 硬性支出按天折算可能超预算
- **WHEN** 房租预算3000硬性实际为0当前日期为2月29日2月共28天
- **THEN** 计算用金额 = 3000 / 28 × 29 ≈ 3107.14(大于预算),标注为"按天折算"
### Requirement: 归档月份数据处理
对于已归档月份的预算数据,系统 SHALL 直接使用归档中的实际金额(`BudgetArchive.Content[].Actual`),不重新计算。
#### Scenario: 读取归档数据
- **WHEN** 用户在3月查询年度存款1月归档中工资实际10000、房租实际3000
- **THEN** 系统使用归档实际值10000和3000不根据当前预算重新计算
#### Scenario: 归档后预算调整
- **WHEN** 1月归档时工资预算10000实际100002月将工资预算调整为12000用户在3月查询年度存款
- **THEN** 1月仍使用归档的实际100002月及以后使用新预算12000
### Requirement: 闰年和月末边界处理
系统 SHALL 正确处理闰年和月末边界情况:
- 闰年判断:使用 `DateTime.IsLeapYear(year)` 判断闰年366天平年365天
- 月末天数:使用 `DateTime.DaysInMonth(year, month)` 获取
#### Scenario: 闰年2月硬性支出折算
- **WHEN** 2024年2月29日闰年房租预算3000硬性实际为0
- **THEN** 计算用金额 = 3000 / 29 × 29 = 3000
#### Scenario: 平年2月硬性支出折算
- **WHEN** 2026年2月28日平年房租预算3000硬性实际为0
- **THEN** 计算用金额 = 3000 / 28 × 28 = 3000
#### Scenario: 年度硬性支出闰年折算
- **WHEN** 2024年闰年第100天年度保险预算12000硬性实际为0
- **THEN** 计算用金额 = 12000 / 366 × 100 ≈ 3278.69
### Requirement: 不记额预算处理
对于不记额预算(`NoLimit = true`)的预算项,系统 SHALL 排除在计划存款计算之外,但在明细中显示为"不限额"。
#### Scenario: 不记额收入
- **WHEN** 存在不记额收入预算项如意外收入实际发生1000
- **THEN** 该项不计入"收入预算",明细中显示预算为"不限额"实际为1000