Files
cma-management/docs/manuals/budget-version.md
T

8.3 KiB
Raw Blame History

预算版本管理 操作手册

模块 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;用 admin/admin123 分别登录 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)