# 节省事实账本与 CFO 经营价值看板 开发 TODO

更新时间：2026-07-17

## 使用规则

- 每条 TODO 必须回链 `CONCEPT.md` 的章节或语义段落。
- 只有代码、接口或容器验证提供真实证据后才能勾选 `[x]`。
- 现金节省、工时价值、风险暴露和预计机会始终分开，不以演示数据代替缺失事实。
- 实施顺序为：安全前置 → 账本 → 首条闭环 → 基线/分析 → CFO 前端 → PostgreSQL/E2E → 文档收口。

## 1. 调研与边界

- [x] [CONCEPT: 背景与问题] 盘点财务看板、预算、Claim、Expense Case、Business Event、风险、审批、付款和标准重算事实源。
  证据：`finance_dashboard.py`、`finance_dashboard_snapshot.py`、`financial_record.py`、`expense_cases.py`、`expense_claim_standard_adjustment.py`、`expense_claim_approval_flow.py` 的只读审计。
- [x] [CONCEPT: 目标与非目标] 确认风险金额、未用预算、预计机会和未确认结果不得进入已确认节省。
  证据：CONCEPT「目标与非目标」「算法与公式」。
- [x] [CONCEPT: 用户与场景] 选择住宿职级标准重算作为首条纵向闭环，不选择证据不足的重复支付或供应商议价。
  证据：现有标准重算和付款业务事件可形成最短可信证据链；`AccountsPayableRecord` 已有租户字段，但仍缺合同价、数量、单位价格和外部付款事实。
- [x] [CONCEPT: 风险与开放问题] 识别旧财务聚合全表读取、快照键缺租户、接口缺角色限制和标准重算信任客户端原金额问题。
  证据：`FinanceDashboardMetricMixin._fetch_claims()`、`_fetch_budget_allocations()`、`FinanceDashboardSnapshotService._cache_key()`、`accept_standard_adjustment()`。

## 2. 契约与设计

- [x] [CONCEPT: 数据与契约] 定义 baseline、opportunity、realization、evidence 和 append-only event 的职责与关键字段。
  证据：CONCEPT「数据与契约」。
- [x] [CONCEPT: 算法与规则] 定义 identified → accepted → in_progress → realized → verified → reversed 状态和 rejected/expired 终态。
  证据：CONCEPT「状态转换」。
- [x] [CONCEPT: 算法与规则] 定义 benefit key、canonical realization、归因比例和追加冲回规则。
  证据：CONCEPT「收益去重」。
- [x] [CONCEPT: 权限] 定义租户、finance/executive、预算范围、管理员只读、自证禁止和数据范围。
  证据：CONCEPT「权限」。
- [x] [CONCEPT: 算法与公式] 定义 3 个主 KPI、驱动指标、风险护栏和缺数据边界。
  证据：CONCEPT「算法与公式」。

## 3. 安全前置

- [x] [CONCEPT: 后端] 让旧财务看板显式接收可信租户与数据范围，Claim/预算查询不得全表混算。
  证据：`finance_dashboard_access_policy.py`、`finance_dashboard_scope.py`、`finance_dashboard_budget.py`、`finance_dashboard.py`；Claim 直接按结构化 `ExpenseClaim.tenant_id` 首层过滤，预算历史兼容仅限 default 租户。
- [x] [CONCEPT: 后端] 给财务快照缓存键和后台任务加入租户/数据范围，禁止跨租户复用。
  证据：`finance_dashboard_snapshot.py`、`finance_dashboard_scheduler.py`；快照键包含 tenant 与 scope fingerprint，调度入口必须显式携带租户。
- [x] [CONCEPT: 权限] 给财务与 CFO 分析接口增加后端角色和范围校验，管理员身份不自动获得业务确认权。
  证据：`finance_dashboard_access_policy.py`、`savings_access_policy.py`、`agent_run_access_policy.py`、`GET /api/v1/analytics/cfo-value`；容器租户/缓存/角色回归 17 项通过。
- [x] [CONCEPT: 第一条可信机会] 标准重算只使用锁定数据库明细原金额，客户端原金额不能影响结果和节省。
  证据：`expense_claim_standard_adjustment.py`；原金额只读锁定 `ExpenseClaimItem.item_amount`，政策结果只由服务端重算，陈旧版本使用内容指纹并降低基线质量等级。
- [x] [CONCEPT: 后端] 将标准重算、审计和账本写入收口到同一事务边界。
  证据：`expense_claim_standard_adjustment.py`以 `commit=False` 写审计，同一 API 事务写 Claim、Baseline、Opportunity、Evidence、SavingsEvent 和 BusinessEvent；事务失败整体回滚。

## 4. Savings Ledger 后端

- [x] [CONCEPT: 数据与契约] 新增 `profile_baseline_snapshots`、`savings_opportunities`、`savings_realizations`、`savings_evidence_links` 和 `savings_events` 模型。
  证据：`savings.py`（模型）及 `test_savings_models.py`；五类事实分离保存基线、机会、结果、证据和不可变操作。
- [x] [CONCEPT: 数据与契约] 新增 Alembic 0015、迁移所有权、复合租户外键、唯一键、检查约束、索引和 append-only 触发器。
  证据：`20260716_0015_savings_value_ledger.py`、`migration_preflight.py`、`schema_ownership.py`；一次性 PostgreSQL 17 空库完整迁移循环通过。
- [x] [CONCEPT: 后端] 实现 discovery、action、realization、query、access policy 和 response builder 独立服务。
  证据：`savings_discovery.py`、`savings_actions.py`、`savings_realization.py`、`savings_query.py`、`savings_access_policy.py`、`savings_read_projection.py`、`savings_protocol.py`。
- [x] [CONCEPT: 后端] 实现分页、筛选、排序、详情、可用动作、证据和事件 DTO。
  证据：`GET /api/v1/savings/opportunities`、`GET /api/v1/savings/opportunities/{id}`、`savings.py`（schema/API）、`test_savings_endpoints.py`。
- [x] [CONCEPT: 算法与规则] 实现版本锁、请求指纹、不可变响应重放、收益去重和归因比例约束。
  证据：`SavingsRequestProtocol`、数据库唯一/检查约束与 `test_savings_concurrency_postgres.py`；同 request 并发仅一次写入，同 benefit 双确认仅一个 canonical winner。
- [x] [CONCEPT: 状态转换] 实现 accept/start/record/verify/reject/expire/reverse 合法与非法转换。
  证据：`savings_actions.py`、`savings_realization.py`、`test_savings_ledger_services.py`；负向 reversal 只追加，不改写原已确认金额。
- [x] [CONCEPT: 证据与审计] 把 Savings 关键动作写入同 Case 的 `BusinessEvent` 并保留 correlation/causation。
  证据：`ExpenseCaseService` 资源链接与 `savings_*` 服务；端到端测试核验 opportunity/payment/action/confirm/reverse 事件同 Case 可回放。
- [x] [CONCEPT: 后端] 新增历史标准重算机会 dry-run/apply 回填脚本；缺 Case/租户/政策证据只输出数据质量报告。
  证据：`savings_standard_adjustment_backfill.py`、`backfill_standard_adjustment_savings.py`；显式租户/目标库、指纹重算、批次锁、稳定键重放，容器直接测试 8 项通过。

## 5. 首条真实纵向闭环

- [x] [CONCEPT: 第一条可信机会] 接受住宿标准重算时冻结服务端原金额、政策输入/版本、目标金额、差额、维度和证据。
  证据：`SavingsDiscoveryService`冻结 `ProfileBaselineSnapshot` 与内容指纹，政式发布版本为 complete，内容指纹版本显式标记 partial。
- [x] [CONCEPT: 第一条可信机会] 同事务以稳定 opportunity key 创建或重放机会，并进入 in_progress。
  证据：稳定键由 tenant + claim + item + policy version + calculation fingerprint 生成；重试返回原机会。
- [x] [CONCEPT: 后端] 付款业务事件后同事务创建 actual realization，重复付款事件不重复计入。
  证据：`expense_claim_approval_flow.py`调用 `realize_paid_claim()`；PostgreSQL 同付款事件并发结果为 `[0, 1]`，仅一条 realization 和一条完成事件。
- [x] [CONCEPT: 财务确认] 实现独立财务 verify/reject，未确认实际结果不得进入主 KPI。
  证据：所有人工实际结果必须至少一条可追溯证据；独立确认人会固化证据复核人/时间，pending 结果在 CFO 口径中为 0。
- [x] [CONCEPT: 冲回能力] 实现补付/申诉/归因修正的负向 reversal，原确认不可删除。
  证据：原 actual 保留 `finance_confirmed`，新增负向 canonical reversal；`as_of` 可回放冲回前结果。
- [x] [CONCEPT: 权限] 验证申请人、机会负责人、结果填报人、纯管理员和跨租户用户不能自证或越权。
  证据：`SavingsAccessPolicy`与 PostgreSQL 并发安全测试；owner/recorder/admin-only 自证拒绝，独立 finance 可确认，跨租户无副作用。

## 6. 费用基线与经营分析

- [x] [CONCEPT: 基线快照] 按员工、部门、费用类型、城市、项目和流程从租户安全真实数据生成基线窗口、样本量和版本。
  证据：`savings_fact_scope.py`、`savings_baseline_generation.py`、`savings_insights.py`；金额五维使用已归档明细中位数，流程维度只使用同租户报销提交时间与首个 `payment_completed` 业务事件，冻结 elapsed minutes、窗口、样本量、算法版本、查询指纹、质量、证据和审计事件；`source_workflow_cycle_count` 明确披露覆盖，活跃工时保持 unavailable。
- [x] [CONCEPT: 基线快照] 供应商数据缺少租户/合同事实时显示 coverage gap，不从应付模拟种子生成可信基线。
  证据：基线和洞察 API 均返回 `supplier_dimension_unavailable` / `supplier_price_drift_unavailable`，要求核验供应商、数量和单位价格；不会读取 `AccountsPayableRecord` 模拟事实或创建货币化机会。
- [x] [CONCEPT: 算法与规则] 实现预算预测、描述性异常归因、重复小额浪费、历史偏离和只读政策模拟准备项；证据不足时不自动货币化。
  证据：`savings_insight_budget.py` 复用预算配置/核销事实并以 `min(as_of, window_end)` 截止；`savings_insight_analysis.py`、`savings_insight_attribution.py` 输出历史偏离、部门/费用类型/城市/项目异常集中和版本化政策模拟必需输入。所有缺少正式反事实的候选 `estimated_savings=None`、`created_opportunity_ids=[]`，稳定重放不写 `SavingsOpportunity`。
- [x] [CONCEPT: 后端] 实现 `GET /analytics/cfo-value`，统一时间、维度、币种、状态和 `as_of` 口径。
  证据：`cfo_value.py`（API/schema）、`cfo_value_analytics.py`；租户、角色、预算范围、时间和维度过滤均在服务端执行。
- [x] [CONCEPT: 算法与公式] 实现确认现金、工时待采集、安全直通率资格、漏斗、兑现周期、逾期、来源和护栏聚合。
  证据：确认现金只求和 finance_confirmed + canonical + cash；工时和安全直通率显式返回 collecting；漏斗、趋势、驱动、逾期、冲回和数据质量不与主 KPI 混算。
- [x] [CONCEPT: 降级策略] 多币种、工时、外部付款和审计结果缺失时返回明确数据质量状态，不伪造 0。
  证据：多币种按原币分组；无人工活跃时间/审计事实时返回 collecting/unavailable 和 coverage gap，不使用 mock 回退。

## 7. CFO 前端

- [x] [CONCEPT: 前端] 在分析看板新增 `value` 入口，并把 dashboard 状态同步 URL。
  证据：`useTopBarOverviewRange.js`、`AppShellRouteView.vue`、`OverviewView.vue`；`dashboard=value` 与价值筛选/页码写入 query，刷新和返回可恢复。
- [x] [CONCEPT: 前端] 新增独立 CFO 组件、composable、API service 和展示模型，不继续扩大 `useOverviewView.js`。
  证据：`CfoValueDashboard.vue`、`CfoValueTrendChart.vue`、`CfoValueOpportunityDrawer.vue`、`CfoValueActionDialog.vue`、`useCfoValueDashboard.js`、`analyticsValue.js`、`cfoValueDashboardModel.js`；业务组件和状态职责已拆分，核心文件均低于 800 行。
- [x] [CONCEPT: CFO 看板] 实现主 KPI、护栏、价值漏斗、趋势、来源/组织驱动、机会表和数据质量。
  证据：`CfoValueDashboard.vue` 与 `cfo-value-dashboard.css`；现金、工时和直通率分卡，趋势按币种切换，预计/实际/确认/冲回不混算，缺数据显式展示。
- [x] [CONCEPT: 前端] 实现时间、部门、费用类型、价值类型筛选和项目/供应商/城市/负责人高级筛选。
  证据：顶部时间窗口与价值 query 联动，`createEmptyValueFilters`、`readValueFiltersFromQuery`、`writeValueFiltersToQuery` 和 API query 白名单覆盖全部筛选字段。
- [x] [CONCEPT: 前端] 实现基线、建议、执行、实际、确认、去重和证据详情。
  证据：`CfoValueOpportunityDrawer.vue` 展示冻结基线、机会状态、实际净值、记录人、canonical 去重、财务确认人/时间/说明、证据索引和不可变事件；无可追溯凭证时不开放手工实际结果登记。
- [x] [CONCEPT: 前端] 实现单据、风险、预算、维度下钻与返回状态恢复。
  证据：`cfoValueSourceLinks.js` 统一构造来源路由；Claim 进入 `app-document-detail`，风险携带最小 focus/观察/决策参数和现有锚点，预算进入 `app-budget` 配置视图并应用部门/费用类型焦点，维度返回 `app-overview?dashboard=value` 相应筛选。`useCfoValueDashboard.js` 以 `value_opportunity` 恢复抽屉，并在非法 ID、403/404、跨租户不可见或不符合当前筛选/时间窗口时安全清除；`useAppShell.js` 从单据详情恢复 CFO 查询。限制：预算中心仍是演示配置视图，页面明确金额不是当前机会的真实预算事实；未覆盖费用科目显示未配置而不是零预算。
- [x] [CONCEPT: 降级策略] 区分零、无数据、基线不足、无权限、失败、部分数据和快照过期；删除 CFO 演示回退。
  证据：`classifyCfoDashboardState`、`buildValueKpis` 与页面状态区；接口失败不读取 `data/metrics.js` 或 demo/fallback 数字。
- [x] [CONCEPT: 前端] 完成移动端、键盘、焦点、44px 触控和无障碍状态提示。
  证据：CFO 样式移动断点、44px 按钮、语义化 `label`/`role=alert`/`aria-live`、抽屉关闭标签及趋势表格降级；生产构建通过。

## 8. 测试与验证

- [x] [CONCEPT: 测试方案] 后端状态、金额、权限、租户、幂等、证据、去重、冲回和看板聚合单测通过。
  证据：容器组合回归 84 项通过；本轮 Savings/CFO 基线、洞察、端点、账本、回填与 E2E 组合 `34 passed, 6 skipped`，6 项为未配置 PostgreSQL 专用 URL 的预期跳过。
- [x] [CONCEPT: 测试方案] 标准重算客户端金额伪造、政策失败、Case 缺失和事务回滚测试通过。
  证据：`test_expense_claim_service.py -k standard_adjustment` 8 项加付款集成 1 项通过，HTTP 标准重算 1 项通过。
- [x] [CONCEPT: 测试方案] 前端数据归一化、状态、筛选、URL、证据动作和响应式测试通过。
  证据：容器内 `cfo-value-dashboard.test.mjs` 14 项通过，新增机会 URL 恢复/清理、筛选上下文、风险/单据/预算/维度链接和预算非事实口径断言；与 App Shell 返回链、路由加载和筛选样式组合回归 37 项通过；带凭证序列化和无证据入口 fail-closed 均有断言。
- [x] [CONCEPT: 测试方案] 一次性 PostgreSQL 空库迁移、重复升级、约束、append-only、无损降级和并发测试通过。
  证据：一次性 PostgreSQL 17 迁移循环通过；`test_savings_concurrency_postgres.py` 6 项通过，覆盖重放、canonical 竞态、跨租户、独立确认、付款单事实和 append-only DB 触发器。
- [x] [CONCEPT: 集成] 住宿标准重算 → 机会 → 审批 → 付款 → 实际 → 财务确认 → CFO 看板 E2E 通过。
  证据：`test_savings_value_e2e.py` 从服务端政策差额、付款动作、待确认排除、独立财务确认到 CFO 金额对账单项通过。
- [x] [CONCEPT: 集成] 确认后负向冲回和报告 `as_of` 回放 E2E 通过。
  证据：`test_savings_value_e2e.py`与 `test_cfo_value_analytics.py`同时验证当前净值归零和冲回前历史金额回放。
- [x] [CONCEPT: 容器验证] 相关 pytest、Ruff、前端测试和生产构建均在 `local-x-financial-linux` 内通过。
  证据：Savings/CFO 相关切片与端到端均通过；fresh PostgreSQL 总探针 `87 passed / 0 skipped / 0 failed`，其中 Savings 并发 6 项；Web 全量 `815 passed / 0 failed` 与 Vite build 通过；新增 Python 文件 Ruff 和 `git diff --check` 通过。

## 9. 文档收尾

- [x] [CONCEPT: 指标与验收] 逐项核对 A1-A8，并把文件、接口、迁移、测试和运行结果写回证据。
  证据：A1 纵向闭环由 `test_savings_value_e2e.py`；A2-A4/A4.1 由 Savings schema、`cfo_value_analytics.py`、基线/分析测试；A5-A6 由 CFO 组件、来源下钻和前端状态测试；A7 由 0015 迁移与 PostgreSQL 并发；A8 由本节最终容器汇总证明。
- [ ] [CONCEPT: 风险与开放问题] 记录付款证据等级、汇率、双人复核、工时口径和试点目标的最终边界。
  证据：
- [x] [CONCEPT: 本轮实现记录] 同步更新上位 `ai-expense-closed-loop-and-value-proof` 文档，不删除历史证据。
  证据：上位 TODO 已回填 Savings Ledger、状态机、CFO 看板、下钻、证据详情和 E2E；历史证据原样保留。
