Files
cma-management/docs/manuals/budget-version.md
T
Hermes CI Fix 40d522cd34 fix(security): retire default-credential surface (yanxue-20260914-cma-defaultcred-code-fix)
- Login.vue: no prefilled default creds; no anonymous entity enumeration on mount

- deploy.sh: readiness polling (0.5s/30s cap) replaces fixed sleep 3; login smoke reads same-source 600 secret file, missing file = explicit abort

- 19 verify/check/gen_report scripts: shared _admin_creds() loader (CMA_ADMIN_ENV -> ops .secrets -> profile copy); no hardcoded creds

- tests: single TEST_USER_PASSWORD fixture constant; docs: pointer to 600 secret file; removed 4 legacy .bak + pycache residue
2026-09-14 17:39:53 +08:00

65 lines
8.5 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.
# 预算版本管理 操作手册
> 模块 slug: budget-version(对应队列 cma-module-queue.json)
> 最近一次修复后状态定稿:2026-09-07(project-20260907-budget-version-ui-fix 完成并验证后)
> 使用本手册前请先读「④ 已知边界/坑」——本模块历史上反复出现字段/状态机错位,改前必查。
## ① 模块定位
- **模块名**:预算版本管理(Budget Version Management)
- **解决什么问题**:年度预算允许保留多个版本(如零基 v1.0/v2.0、年度 2026H2),支持"编制 → 提交审批 → 批准/驳回 → 重新提交"的版本状态机,以及两个版本间的差异对比。防止把"草稿中版本"误当"已批准版本"执行。
- **所属流程环节**:预算 → 版本管理(预算编制/分解/执行的上游容器;预算计划行挂在具体 version 下,active 版本为当前生效版本)
- **主要角色**:finance(编制/提交审批)、ceo(批准/驳回)、it(维护),entity1=陕西酣客、entity2=陕西博海
## ② 操作步骤
页面路径:登录后侧边栏「预算管理」(/budget,zone B 可编辑)→ 顶部 tab「版本管理」。
1. **查看版本列表**(默认按当年 year 加载)
- 版本列表加载:前端调 `GET /api/cma/budget/versions?year=YYYY`(**返回裸数组**,前端需 `Array.isArray(r) ? r : r.data || []` 解包)
- 列含义:`版本`(标签,当前生效版本绿色高亮并带「(当前)」后缀) | `状态`(审批状态标签:草稿/待审批/已批准/已驳回) | `条目数`(plan_count=预算行数) | `总额`(total_budget) | `最后更新`(last_updated, YYYY-MM-DD HH:MM) | `操作`(按状态出按钮)
2. **状态机流转**(DB `budget_plans.status` 原值 vs UI 展示 `approval_status`)
- UI 语义:`draft`(可提交审批) → `submitted`(待审批) → `approved`(已批准只读) / `rejected`(已驳回可重新提交)
- **后端映射**:DB 默认态 `active` → UI 展示为 `draft`(服务端语义收敛,`approval_status = "draft" if status=="active" else status`);approved/submitted/rejected 原样透传。**DB status 保留 active/approved/submitted/rejected 四种,勿改 DB 值语义**(active=当前生效版本,版本下拉「(当前)」标记依赖 `status==='active'`)
3. **提交审批**:状态为「草稿」的行 → 点「提交审批」→ 确认弹窗 → 后端 `POST /api/cma/budget/versions/submit`(body `{"version": "v2026H2"}`),状态 active → submitted
4. **审批**:状态为「待审批」的行 → 点「批准」(`POST /versions/approve` body action=approved,submitted → approved) 或「驳回」(action=rejected,submitted → rejected)
5. **重新提交**:状态为「已驳回」的行 → 点「重新提交」(复用 submit 端点,rejected → submitted)
6. **已批准只读**:approved 版本**操作列无任何按钮**,不可再提交/修改(只读语义,正确表现即无按钮)
7. **版本差异对比**:勾选列表左侧复选框(**最多 2 个**,超过自动取消最早勾选)→ 点「版本差异对比」→ 弹窗展示两版本逐期间/逐 KPI 差异(`POST /api/cma/budget/versions/diff`,body 含 version_a/version_b/year)
8. 预算行编制在「预算编制」相关 tab 内按当前选中版本录入(顶部版本下拉 filterVersion 选择后 loadBudget)
## ③ 验证路径
- **自动脚本**:
1. 生产 API 双账套断言:`/root/cma-management/backend/scripts/verify_budget_versions_ui_fix.py`(`cd backend && venv/bin/python3 scripts/verify_budget_versions_ui_fix.py`;凭据见 600 密钥文件 `CMA_ADMIN_USER` / `CMA_ADMIN_PASS`(脚本同源读取:`$CMA_ADMIN_ENV` 或 `.secrets/cma-admin.env`)分别登录 entity1/entity2,断言新字段存在 + active→draft 映射 + plan_count==kpi_count + last_updated==updated_at)
2. 浏览器页面 DOM 实测:`/root/cma-management/backend/scripts/verify_budget_versions_ui_browser.py`(需先起 Chrome CDP 9224,命令见 cma-fullstack-ops 技能 references/budget-version-ui-field-state-fix-20260907.md 的 Chrome 起法;登录博海 entity2 断言 v2026H2 行=草稿/25条/462/最后更新非空/「提交审批」按钮,切酣客 entity1 断言 5 个 active 行有提交按钮 + 2026H2 approved「已批准」无按钮)
- **期望输出**:两个脚本末尾均输出 `ALL_PASS` 且 exit 0
- **已测基线**(最近一次验证日期+结果):2026-09-07 复跑双脚本 ALL_PASS。实测数据:博海 entity2 `v2026H2 | status=active | approval_status=draft | plan_count=25 | total=462.0 | last_updated='2026-08-30 11:24'`;酣客 entity1 共 6 版本,5 个 active(v1.0/v2.0/incremental/flexible/zero_based-20260826)→ 草稿+「提交审批」按钮,2026H2 approved → 「已批准」无按钮
## ④ 已知边界 / 坑
- **历史 bug**:
- 2026-09-04 版本列表永远为空:后端 `list_budget_versions` 返回**裸数组**,前端 axios 拦截器剥层后 `r.data` 为 undefined → 空列表无报错。修复 commit 46096e8:前端 `Array.isArray` 兼容解包。**同根因 bug 出现在 BudgetManagement.vue loadVersions,全仓扫描见技能 axios-unwrap-bare-array reference**
- 2026-09-07 用户反馈「版本管理仅有操作复选框」:**两个独立 bug 叠加**——①前端列取 `approval_status/plan_count/last_updated` 而后端返回 `status/kpi_count/updated_at` → 状态列恒显示 else 分支「草稿」、条目数/最后更新恒空白;②DB `budget_plans.status` 默认态是 `active`(不在前端按钮条件 draft/submitted/rejected 里)→ active 行整行无按钮。修复 commit b01a108:后端输出补 `approval_status`(active→draft 映射)+`plan_count`+`last_updated` 且**保留 status/kpi_count/updated_at 原字段**;前端按钮条件补 `|| row.approval_status === 'active'` 双端保险
- **边界条件**:
- active(=UI draft)**可提交审批**;approved **只读**(无按钮、不应有编辑入口);submitted 只能批准/驳回不能重复提交;rejected 可重新提交
- 版本下拉「(当前)」标记用 **DB 原字段 `status==='active'`**,与 UI approval_status 展示解耦——两字段都必须在接口里
- 版本「选中当前版本」逻辑:加载版本后默认选 `status==='active'` 的版本,避免全版本堆叠重复期间(2026-08-27 根治)
- **⚠️ 易踩点**:
1. **后端返回字段改名必须保留原字段别名**——前端/测试契约(下拉「当前」标记、verify 脚本断言)依赖 status/kpi_count/updated_at
2. **approval_status 由后端统一映射,勿在前端做 DB 状态翻译**(会出现双端不一致)
3. `/versions` 端点返回裸数组是既有契约,改后端请勿包一层 data(会复现 09-04 空列表 bug);改前端勿忘 Array.isArray 解包
4. 验证脚本(浏览器版)存在 tab 创建竞态(Target.createTarget 后偶发 about:blank → localStorage SecurityError),已加导航等待(commit 616f444);复跑若挂先确认 Chrome 起法与 host-resolver-rules
## ⑤ 修改记录
| 日期 | 修改人 | 改动 | commit | 影响 |
|------|--------|------|--------|------|
| 2026-09-07 | - | 手册创建(修复后状态定稿) | - | - |
| 2026-09-07 | 全栈Bot | UI字段/状态机对齐:后端补 approval_status(active→draft)/plan_count/last_updated + 前端按钮 active 兜底 | b01a108 | 修复「版本管理只有复选框」 |
| 2026-09-07 | 全栈Bot | 验证脚本入库(API+浏览器双录) | f8018aa | 可复跑资产 |
| 2026-09-07 | 全栈Bot | 浏览器验证脚本 tab 竞态加导航等待 | 616f444 | 修复复跑必挂 |
| 2026-09-04 | 全栈Bot | loadVersions 裸数组解包 bug 修复 | 46096e8 | 修复版本列表永远为空 |
| 2026-08-27 | 全栈Bot | 加载版本默认选中当前 active 版本 | - | 防全版本堆叠 |
## ⑥ 关联
- **依赖表**:budget_plans(version/status/period/amount 等,预算行按 version 归属)、kpi_definitions(预算行关联 KPI)
- **依赖接口**:GET /api/cma/budget/versions;POST /api/cma/budget/versions/submit;POST /api/cma/budget/versions/approve;POST /api/cma/budget/versions/diff(均带 Depends(get_entity_id) 账套隔离)
- **关联模块**:预算编制/分解(auto-decompose,批量分解幂等)、预算执行(execution)、预编报表(proforma reports 取 active 版本预算);前端 BudgetManagement.vue
- **验证资产**:backend/scripts/verify_budget_versions_ui_fix.py(API)、verify_budget_versions_ui_browser.py(浏览器 CDP)