Commit Graph
264 Commits
Author SHA1 Message Date
wecom-fullstack ecbe55c48a merge: origin/main 合入战略地图改造分支(解 MapCanvas 模板冲突:摘要条+编辑条+口径条并存)
ci/woodpecker/push/main Pipeline was successful
2026-10-20 11:34:29 +08:00
wecom-fullstack c030fb7d18 fix(strategy-map): 口径违禁串代码级清零(9,492.6/83.9%/19/19/el-dialog 仅存于注释)+ components.d.ts 补 MapDimensionFilter 2026-10-20 11:33:58 +08:00
wecom-fullstack a8c9b38fff feat(caliber-label): 科目口径·缺票 常驻标识 + 测算/待票 数值标识(展示层 P2)
ci/woodpecker/push/main Pipeline was canceled
- 新增 constants/caliber.ts(文案单一真源)+ components/CaliberBadge.vue(常驻,非 hover)
- 经营看板/Dashboard:页头常驻「科目口径 · 缺票」(tooltip 含 四件套 0/4、invoice_check 0 行)+ 毛利率与 A 口径 KPI 卡「测算/待票」
- 战略地图看板/StrategyMap:meta-bar 常驻标识位 + 节点级测算标识(与 v4 §2/§4 共用同一组件)
- 战略地图画布/MapCanvas、KPI 列表 KPIListView 同步复用同一组件
- 只做展示层:不改库、不重算历史值、不静默按实账回填
2026-10-20 02:11:00 +08:00
Hermes CI Fix 53cad7eaa9 merge: origin/main (A3 三维切片) 合入战略地图改造分支 2026-10-19 14:32:33 +08:00
Hermes CI Fix a8141e8879 wip(strategy-map): v4 口径落码(分线营收/新式毛利率/管理指标/C1 hold/57 归档) 2026-10-19 14:32:08 +08:00
wecom-fullstackandHermes CI Fix d85dd2a155 feat(strategy-map): A3 三维切片(时间/门店/产品)——切片重算 + 显式空态 + entity 隔离
ci/woodpecker/push/main Pipeline was successful
- 新增 /api/cma/maps/{id}/dimension-options 与 /{id}/slice(只读)
- 时间:kpi_values.period 月/季/年;flow 求和、ratio 均值、balance/status 取最新
- 产品:product_sales + 逐月源一致性闸门(偏差>1% 不切片)
- 门店:库内无门店级事实 ⇒ available=false + reason,绝不回退总额
- 前端 MapDimensionFilter 挂到 MapCanvas,切换即 emit 切片,清除恢复 /review
2026-10-19 13:32:49 +08:00
Hermes CI Fix 4d20c27de4 wip(strategy-map): V1+V3 合版 + v2/v3 口径(完备度/C1 hold/57 归档组) 2026-10-19 10:33:40 +08:00
project-bot c92dbab96a feat(kb): KnowledgePanel 接线 6 页 + /maps/canvas 错配消除
ci/woodpecker/push/main Pipeline was successful
- /budget /kpis /deviations /predict /maps-review /alerts 六页按 related_page 挂载 KnowledgePanel
- /maps/canvas 页 prop 由 /maps 更正为 /maps/canvas(与表值一致,消除恒取空)
- 匹配口径走 backend/app/utils/route_key.py(origin/main 已有,KBFF-v11c 精确等值)
- 验证:本地产物 CDP 冒烟 18/18(六页 dom=api=1;/maps 命中 0 负对照)
2026-10-19 10:23:12 +08:00
wecom-fullstack 0fc2c6109e style(page-title): 补漏——战略地图看板页标题弱化 + KPIListView 标题 emoji 清零
ci/woodpecker/push/main Pipeline was successful
- StrategyMap.vue: 页内 h2 19px/600/深色(与路由标题「战略地图看板」基本同名)降为
  div.page-subtitle 14px/500/#64748B(保留地图名+版本+状态等增量信息,不再充当 H1)
- KPIListView.vue: 组件 h3 图标默认值 '📊' 置空(v-if 渲染),分类标题与明细卡头
  '📋' 改为 Lucide 内联 SVG(clipboard-list,18/16px,#64748B)
- 仅模板与样式:不动 MainLayout、不改接口/数据/路由 key、不碰 MapCanvas.vue
2026-10-19 04:34:35 +08:00
wecom-fullstack bf641d8d31 refactor(menu): 侧边栏按决策域重构(L1 九域 + L2 决策场景)+ PDCA 回退开关
ci/woodpecker/push/main Pipeline was successful
- 新增 frontend/src/config/decisionDomains.ts:9 个决策域(100 战略…900 系统)
  与 22 个 L2 决策场景,只写 path 关系;文案/图标/roles 仍取 permission.ts MENU_ITEMS
- MainLayout.vue:决策域菜单为默认(el-sub-menu 域 → el-menu-item-group 场景 → 页面),
  「我的工作台」常驻置顶,系统域默认折叠;未登记菜单项兜底进 900「未归类」(不丢项)
- 回退开关:localStorage.cma_menu_mode='pdca' 或 URL ?menu=pdca,右上角下拉可切换
- 路由 path、页面、API、DB、zone A/B 权限判定一律未改
- components.d.ts 由 unplugin-vue-components 自动补 ElMenuItemGroup 声明
2026-10-18 21:45:32 +08:00
Hermes CI Fix 7f36608f60 chore(frontend): 重新生成 components.d.ts(budget 面板组件 + ForecastTag 自动注册)
ci/woodpecker/push/main Pipeline failed
2026-10-18 20:37:51 +08:00
Hermes CI Fix 5bda882aa9 Merge remote-tracking branch 'origin/main' into integration/merge-wt-20261018 2026-10-18 20:32:32 +08:00
wecom-fullstack f51fc72ac7 style(page-title): B 全站规范——页面内同名标题清零 + 增量副标题弱化 + 标题 emoji 换 Lucide
ci/woodpecker/push/main Pipeline failed
- 39+ 页删除与布局标题同名/基本同名的页面内 h1-h4(同屏三标题→两处)
- 增量副标题弱化为 14px #64748B 小字:收付款计划/战略回顾/地图版本/会计科目打标
- 标题 emoji 清零,保留标题处改 Lucide 内联 SVG(bar-chart-3/video/scale/clipboard-list/alert-circle/map/history)
- KPIListView 增加 hideTitle(仅展示),客户/学习看板隐藏组件内重复页头
- 不动 MainLayout、不改接口/业务逻辑、不碰 MapCanvas.vue
2026-10-18 19:33:11 +08:00
Hermes CI Fix fa1267c323 feat(budget): effective-version caliber + process-order tabs + 4 missing rings
Task: CMA-20261003 (budget module governance). Local commit only; push is
gated on release approval (see report).

Backend
- app/utils/budget_status.py (new): single source for the effective-version
  caliber. EFFECTIVE_STATUSES = approved + active; effective_plan_clause()
  enforces entity isolation; pick_effective() priority active > approved >
  updated_at desc. Fixes: approved budgets disappeared from the execution
  surface because the three queries only read status='active'.
- budget.py: /comparison, /comparison/kpi/{id}, /deviation-check now read the
  effective surface; versions list gains is_effective / can_release fields;
  submit/approve are fail-closed (draft|active -> submitted -> approved).
- deviation_engine.get_budget_for_kpi: effective surface + deterministic pick.
- 4 missing rings, zero DDL, appended to the same router:
  POST /versions/release (approved -> active, idempotent by operation_logs),
  POST /adjustments (derive a new draft version, base rows untouched),
  GET /control/status (effective budget vs actual -> ok/warn/over),
  GET /assessment (achieve rate + A/B/C/D grade).
  Audit rows written through app.utils.oplog.log_op with entity_id.

Frontend
- BudgetManagement.vue: top-level tabs reordered to the process order
  (strategy, method, driver, input, decompose, versions, release, execution,
  deviation, adjust, assess, rolling). Pane name values unchanged, so old
  bookmarks keep working; routes unchanged.
- Cash classify rules / unclassified queue moved to /cash-plan
  (components/CashClassifyPanel.vue); budget actual-value collection moved to
  /data-manage (components/BudgetCollectPanel.vue). Mount points only: no
  change to cash_plans data, classify logic, collection logic or kpi_values.
- api/index.ts: 4 ring endpoints exposed.

Docs / tests
- docs/budget-module-governance-design.md: phase-0 design doc (status machine,
  tab order, domain boundaries, 4-ring design, cleanup write pack scope).
- tests/test_budget_status_machine.py (new): effective surface, version
  guards, release idempotency, adjustment, control and assessment.
- tests/test_budget.py: existing cases adapted to the fail-closed contract.

(cherry picked from commit 4267fc4d25ee45c5675a541af9777be96fe9f9c2)
2026-10-18 16:32:55 +08:00
Hermes CI Fix 75a48f55d2 feat(strategy-map): live board reading map_objectives + kpi_targets (read-only)
Task: finance-20261011-strategy-map-frontend-fullstack (P2, read-only integration)

- backend/app/utils/strategy_board.py: single source of truth for the level
  caliber (layer colors, inverse-kpi explicit whitelist, status class by
  unit, zero guard, period ranking, latest-actual picking). Pure functions.
- backend/app/api/maps.py: GET /api/cma/maps/{map_id}/strategy-board
  - nodes straight from map_objectives (sorted by sort_order, id)
  - node<->kpi binding with 4-step fallback, binding_source echoed back
  - targets from kpi_targets (target_type compatible: yearly + annual),
    deduped per (kpi_id, period) with yearly preferred
  - actuals from kpi_values actual-only scope, latest period
  - causality edges from kpi_causality, out-of-map endpoints dropped+counted
  - entity_id taken from the token only (no cross-tenant reads)
  - zero writes; nothing is persisted by this endpoint
- frontend/src/views/StrategyMap.vue: /strategy-map live board (4 swim lanes
  bottom-up, node cards, 20 causality arrows in SVG, period switch, A3 print).
  No emoji, null guards everywhere (no toFixed on null).
- api/index.ts + router/index.ts + permission.ts: route + menu registration
  (/strategy-map, zone A read-only, roles ceo/finance/business/it).
- backend/tests/test_strategy_board_20261011.py: 33 cases (spec section 4 /
  4.1 / 4.2 / 4.3 + section 7 fixtures + threshold boundaries + period rank).
- backend/tests/test_map_objectives_kpis_form_20261011.py: 14 cases for the
  four kpis column shapes (list, json string, object list, NULL/[]/weird).

Not included: push + backend restart (production write, separate gate).

(cherry picked from commit c675bde6b4beaf1d8a9d02a80f5434203a53f348)
2026-10-18 16:31:52 +08:00
Hermes CI Fix 2cff58ca6a feat(docs-center): report reading section (type=report) on /api/cma/docs
Increment #2 over PoC ede0524 (task yanxue-20260924-cma-report-source).

- backend derives reports from existing drop points (wisdom/_reports + every profile pending-tasks/_reports), read live per request, no cache, no rebuild

- gate = front-matter plus non-empty conclusion; missing either means silently out of the list (receipts/audits never enter); title/date/domain/sources missing are marked incomplete instead of rejected

- entity + role partitioning (ceo/it cross-entity; others own entity plus group/generic); kpi_refs resolve to a one-hop metric link

- new GET /api/cma/docs/report/{rid} detail endpoint; knowledge branch unchanged (type defaults to knowledge)

- frontend: CMAKnowledge adds a report reading pane (filters + detail dialog), reuses the existing markdown renderer, no new deps

(cherry picked from commit 7c6e0857e68133f5ba2a539fd3569aeb82547911)
2026-10-18 16:31:52 +08:00
wecom-fullstack 68ca27109e feat(report): 管理利润表「变动成本」行加负值标注(脚注+tooltip,纯展示)
ci/woodpecker/push/main Pipeline was successful
依据 e1 变动成本负值口径核查(口径无误,设计使然):变动成本含贷方冲减项(厂补),
个别期间(2026-08 / 2025-07)可为负。本单仅在报表层加说明,防读者误读为取数/口径错误。

- 项目列「变动成本」行加 InfoFilled 悬停 tooltip(按 line_key === 'variable_cost' 精确命中)
- 表格下方加脚注一行
- 未改取数逻辑、未改口径、未改任何数值(diff:1 file, 11 insertions(+), 0 deletions(-))
2026-10-16 18:13:50 +08:00
Hermes CI Fix 289cc8f2cc fix(profit-caliber): 博海口径 §4-1/§4-2/§4-5 落地 —— 固定成本仅借方 + 6403 进变动 + 财务收益单列 + 批次备注
ci/woodpecker/push/main Pipeline was successful
ASK-20261014-04(任总 2026-10-14 11:20「放行4」)批后口径:
- §4-1 博海固定成本弃净额:6601/6602/6603 仅借方(133.98 万,2026-01..09);2026-03 单月由 -9.02 万转正;
  6603 贷方(现金折扣收益 64.40 万)改 finance_income_terms 单列备查,不进固定成本/末行/勾稽。
- §4-2 博海变动成本追加 6403 营业税金及附加(530.17 万)。
- §4-3/§4-4 维持现行(酣客收入/边际贡献基数不动)。
- §4-5 管理利润表页加「数据批次/导入时刻」纯展示备注(import_logs)。
- 口径唯一源仍为 bep_caliber.ENTITY_CALIBERS;报表/卡面/派生器一律经 fixed_cost_spec() 解析(读口写口一致)。
2026-10-16 12:32:52 +08:00
wecom-fullstackandHermes CI Fix 32f368a846 fix(infra): pnpm 工具链去脆弱化 —— frontend/package.json 落 packageManager pin + deploy.sh pnpm 版本断言(fail-closed)
ci/woodpecker/push/main Pipeline failed
① frontend/package.json 补 "packageManager": "pnpm@10.33.2+sha512.a90faf6f…":
   corepack 在项目未声明该字段时按全局 lastKnownGood.json 解析版本,而该文件会被任何跑过
   corepack 的项目改写(2026-10-13 → 12.11.2 ⇒ MODULE_NOT_FOUND;2026-10-14 → 9.15.9 ⇒
   packages field missing)。pin 落库后 corepack 只认 pin,宿主机状态漂移不再影响部署树。
   选 10.33.2 的依据:pnpm-workspace.yaml 的 allowBuilds 仅 pnpm 10 识别;部署树 node_modules
   .modules.yaml packageManager=pnpm@10.33.2(store/v11/v10);lockfileVersion 9.0;2026-10-13
   22:53 该版本下部署成功。
② deploy.sh 新增 assert_pnpm_version()(fail-closed,两处调用:1.2a 首次 pnpm 调用前、[2] 构建前):
   期望版本只取自 package.json 的 packageManager(单一源),与 pnpm -v 实读逐字比对,缺失/不一致
   即中止;两次调用都先于 [3] 铺站与 [5] 重启 ⇒ 任何生产写之前失败关闭。

零生产写:候选仅落本 worktree 分支,未 push、未动 /srv/hermes/shared/cma-deploy 与 dev 树。
2026-10-15 08:35:20 +08:00
wecom-fullstack 6d8ef0d443 fix(infra): pnpm-workspace.yaml 补 packages 字段(pnpm 9.15.9 强制)+ vue-demi 占位符改 true
ci/woodpecker/push/main Pipeline failed
根因:corepack 修到 pnpm 9.15.9 后,pnpm 见 pnpm-workspace.yaml 即进 workspace 模式,
强制要求 packages 字段;缺失 ⇒ 'packages field missing or empty' ⇒ 部署树 pnpm build 失败。
本地 pnpm build 验证通过(16.05s, exit 0)。
2026-10-14 11:35:05 +08:00
wecom-fullstack e55f0ecdf9 fix(ui): 侧栏「资金内控」图标缺失——MainLayout 补 Lock 到 import 与 iconMap
ci/woodpecker/push/main Pipeline failed
任总 2026-10-14 10:49 报「资金内控:导航栏缺少图标」。
根因:permission.ts:40 icon:"Lock",而 layouts/MainLayout.vue 的
@element-plus/icons-vue import 与 iconMap 均无 Lock ⇒ iconMap["Lock"]=undefined
⇒ <component :is="undefined"> 渲染为空。全量核:菜单 40 项 / 19 个不同 icon 值,
仅 Lock 真缺。本提交仅该 1 文件 2 行(import + iconMap 各补 Lock),
不改其它图标、菜单顺序、分组、角色可见性。
2026-10-14 10:54:11 +08:00
wecom-fullstackandHermes CI Fix 26db15e6d3 fix(ui): 系统级设置归位(KPI对齐/预算模式切换入口收归系统设置)+ 业务页右下浮窗提醒
ci/woodpecker/push/main Pipeline failed
- KPIAlignment.vue: 移除页头切换按钮与模式选择器,去 saveAlignConfig;新增右下浮窗
- BudgetManagement.vue: 预算模式 el-switch -> 只读标签,去 onBudgetModeChange;新增右下浮窗
- RolePermissions.vue: 保留目标对齐正本,新增「预算模式」Tab(系统级切换入口)
- 仅 frontend/ 三件;build 通过
2026-10-13 23:04:07 +08:00
Hermes CI Fix 501322a8b9 feat(maps): 战略地图按 objective 粒度合并(granular 通道),不同 objective 并发不再互相 409
ci/woodpecker/push/main Pipeline failed
2026-10-13 11:37:24 +08:00
wecom-fullstack 03254bd4c7 fix(maps): 战略地图写通道乐观并发 + 画布自动保存仅脏时写
生产缺陷 cma-strategy-map-autosave-stale-overwrite(D4 批次 19 行 map_objectives 被过期快照覆写):
- 后端 update_map: 覆写 dimensions/canvas_data 须带 base_updated_at,与服务端 updated_at 不符 → 409;
  校验在任何写入之前完成(校验不过时一行都不删/不改)
- 连线写通道(POST/DELETE connections)响应补 updated_at,供前端采纳新基准、避免自伤 409
- 前端 MapCanvas: 自动保存仅在 draft + isDirty 时触发;请求体带 base_updated_at;收到 409 停自动保存不重试
- 修复 async onMounted 竞态:await 期间被卸载后其续段仍注册定时器(清理钩子先跑完、无人回收)
  → 泄漏写者永久在后台用过期快照覆写(生产 referer=/maps 仍每 30–60s 写)。加 disposed 守卫 + stopAutoSave
- 测试: backend/tests/test_map_concurrency_20261012.py(8 例,改前 5 败);前端行为台架 11/11(改前 3/11)

任务: yanxue-20261012-cma-map-autosave-stale-overwrite-defect-fullstack
2026-10-12 11:42:56 +08:00
Hermes CI Fix 451c994a86 feat(cpa): customer profitability MVP P0-a/b - revenue caliber single source + snapshot table + 9-card tri-state
Task: finance-20261003-cpa-customer-profitability-mvp-fullstack (P0-a / P0-b)

- backend/app/utils/cpa_caliber.py (new): single source of truth for
  customer-level revenue aggregation from zxt_masterbill. Reused by
  alert_rules._customer_revenue_shares (dedupe: no second implementation).
- backend/scripts/migrate_cpa_snapshot_20261004.py (new): idempotent DDL for
  customer_profitability_snapshot (UNIQUE entity_id+period+unit_id),
  --check / --apply / --rollback / --status / --show-create + journal.
- backend/scripts/kpi_customer_profitability_deriver.py (new): monthly
  snapshot + kpi_values upsert (single transaction, idempotent).
  fail-closed: cogs_alloc / chan_sub / contrib / contrib_rate stay NULL and
  are never written as 0 or as an apportioned value.
- backend/scripts/sql/cpa_kpi_defs_20261004.sql (new): C_CUST_REVENUE and
  C_CUST_COUNT active; 5 P1 codes registered inactive with fail-closed reason.
- backend/app/api/customer_dashboard.py: new read-only endpoint
  GET /api/cma/customer-dashboard/standard-indicators returning the 9-card
  tri-state (value / no_data / undefined). Reads kpi_definitions via raw SQL
  to stay decoupled from the in-flight KPI direction DDL.
- frontend/src/views/CustomerDashboard.vue: 9-card panel rendering the three
  states (never renders an empty value as 0).

Anchor check 2026-01..09 (offline sqlite rehearsal, ALL_PASS=true):
73 customers / 4883.52 wan / top1 25.3835 / top2 23.4905 / top4 63.3249 /
top10 81.7675 - all six match the task acceptance anchors.

No production write and no deploy in this commit; release is gated.
2026-10-09 17:00:37 +08:00
wecom-fullstackandHermes CI Fix 587d5b3983 fix(cvp): BEP ⑤ 口径统一落地(变动成本率=完整口径)
ci/woodpecker/push/main Pipeline failed
- 派生器/口径:F_BEP_VC_RATE 新增;卡面/公式行文案改「月固定成本 ÷ 边际贡献率」
- 保本额 = 月固定成本 ÷ (1 − 变动成本率);实测渠补率(F_BEP_REBATE)降为 e1 专用 chip
- 报告卡口径说明文字同步(固定成本=固定费用合计)

任务: finance-20261003-cvp-fix-and-bohai-coverage-fullstack ⑤
2026-10-04 19:53:12 +08:00
Hermes CI Fix 38a6de9325 feat(dupont): period-basis design (cumulative flow x average stock, optional annualize, identity guard)
ci/woodpecker/push/main Pipeline failed
Task: finance-20261003-dupont-period-basis-design-fullstack (P1; proposer wecom-finance)
Basis ruling: finance 2026-10-03 (Boss 09:49 CST DM approval of the design task)

- /reports/dupont accepts period/start/end/annualize (default YTD), returns
  period_start/period_end/basis/annualized metadata; identity guard
  |ROE - npm x turnover x multiplier| <= 0.05 pp, else identity_broken (no values).
- new app/utils/dupont_basis.py = single source of truth (window resolve, flow
  cumulative via voucher_details, stock average via subject_balances level=1,
  fail-closed basis_fallback, pure calc + identity check).
- scripts/kpi_dupont_deriver.py (idempotent derived writer, dry-run/check/write),
  scripts/apply_dupont_kpi_defs.py + sql/dupont_kpi_defs_20261003.sql (seed:
  F_ASSET_TOTAL/F_EQUITY_TOTAL/F_EQUITY_MULTIPLIER as derived; F_ASSET_TURNOVER
  manual -> derived). Seed already applied under approval 296cc1fd.
- scripts/verify_dupont_period_basis.py: read-only acceptance battery A1-A9
  (independent SQL recompute). BEFORE (base tree) 1/10 PASS -> AFTER 10/13 PASS;
  remaining 3 need the deriver value write (approval-gated).
- frontend DupontAnalysis.vue: period selector (YTD default) + annualize switch
  + caliber metadata bar + identity_broken alert.

No DDL. No ERP direct connection (old register_dupont_kpis.py caliber dropped).
2026-10-04 13:35:25 +08:00
Hermes CI Fix 1f1385ebbf feat(cvp): CM≤0 fail-closed 禁写0.0 + 博海BEP扩面(新口径) + 影子CVP标注is_mock + 前端NULL守卫/不适用
ci/woodpecker/push/main Pipeline failed
- kpi_bep_deriver.py: 实体口径注册表(app/utils/bep_caliber.py 单一事实源);博海(6001/6401/6601-03/1602);
  fail-closed② 边际贡献非正/固定成本退化 ⇒ 五条保本值 NULL+pending+remark'不适用'(禁回落0.0)
- dashboard.py /bep-card: 多实体口径 + not_applicable 态返回 label/reason
- predict.py /cvp-detailed: 显式 source=scenario_simulation + is_mock=true(影子CVP标注)
- 前端 BEPBreakEvenCard.vue: 全量数值判空 + '不适用'态;PredictDashboard.vue: 情景模拟·非实测标签

验收 accept-bep-cvp-20261003.py: BEFORE 2/11 → AFTER 11/11 PASS
任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack
2026-10-03 16:03:12 +08:00
Hermes CI Fix 8631ce718f feat(profitability): real ROI (op profit / invested capital), enable EVA, add RI
ci/woodpecker/push/main Pipeline failed
Kaliber: wecom-finance 2026-10-02 ruling (Boss 11:59 CST "li; but (1) add real ROI").

- kpi_definitions seed scripts/sql/profitability_kpi_defs_20261003.sql:
  F_ROI renamed to F_ROA (same kpi_id=20, all 8 historical values kept), new F_ROI
  (real IMA caliber = operating profit / invested capital), F_EVA activated with unit
  and thresholds aligned to 万元, new F_RI (residual income).
- parameters are NOT hardcoded in formula text: they live in
  kpi_definitions.data_source_config.params (tax_rate / wacc / min_return_rate /
  include_factory_subsidy / include_channel_subsidy / include_sales_discount /
  annualize / capital_basis); single reader = app/utils/profitability_params.py.
- deriver scripts/kpi_profitability_deriver.py: YTD operating profit from
  voucher_details (carry_forward=0 net), invested capital from subject_balances
  level-1 rows only, idempotent upsert into kpi_values (source_type=derived, no
  data_status downgrade, fail-closed when invested capital is 0).
- read-only endpoint GET /reports/value-management reads the derived rows only
  (single source of truth, no recomputation) and exposes the parameter block.
- DupontAnalysis.vue: value management section (ROI / ROA / ROE / EVA / RI).
- caliber_units.py: RATIO_CALIBER_TABLE key F_ROI renamed to F_ROA, new F_ROI row.
- tests: backend/tests/test_profitability_indicators.py (21 cases).
2026-10-03 02:10:47 +08:00
Hermes CI Fix 5d7b3cc728 feat(h2-forecast): 实测区/预估区口径分离 —— 对外读路径默认只取实测区
ci/woodpecker/push/main Pipeline failed
- 新增 app/utils/kpi_value_scope.py(口径单一事实源:actual_only_clause / forecast_only_clause / is_forecast_row)
- dashboard.py: /kpis /my-kpis /finance-analysis /predict /my-dashboard / latest_kpi_value 默认剔预估,include_forecast=true 单列返回
- alert_rules.py: 动态阈值白名单去 estimated + 4 处读路径加实测区判据
- 前端 ForecastTag.vue 新增;Dashboard.vue / MyDashboard.vue 预估单列 + 标签(不与实测相加)

Task: finance-20261002-h2-forecast-separation-deploy-fullstack
2026-10-02 23:33:15 +08:00
Hermes CI Fix 29dc5c0bdc fix(receivables): unwrap response envelope once + align card/column field names
- Receivables.vue: const d = r (was r.data || r) -> cards/table no longer blank

- cards: pending_balance(待收余额) / overdue / completed_amount / completed_count

- columns: counterparty(+description fallback) / owner(未分配) / settled_amount / plan_date / aging_bucket

- cash.py: summary.completed_count + _plan_dict.settled_amount (existing keys untouched)

- api/index.ts untouched (global interceptor)

(cherry picked from commit d60557622441b971970fb9dc86ef7491c3a9d72b)
2026-10-02 21:12:20 +08:00
wecom-fullstack 0824efc8ed feat(data-quality): 规则配置可编辑入口 + 经营口径排除接线(ASK-20260928-05 尾项)
ci/woodpecker/push/main Pipeline failed
依据:任总 2026-09-29 08:49「P1和P2批」放行 P1-cma-dq-rule-config-approval-20260923 ①③
     (派单件 0-P1-project-20260929-qrc-write-entry-and-demo-exclusion-fullstack)

三处尾项(本单):

1) 前端可编辑入口(补拍板件 ① 未被上轮覆盖的部分)
   - frontend/src/views/DataQuality.vue:规则配置 Tab 由「只读」改为
     ceo/it 可编辑、finance/business 只读(角色口径与 router/index.ts:45 及
     系统设置页一致);新增「操作/编辑」列(平台级项仅 it 可点、演示账套禁用);
     编辑走 MyDialog(业务弹窗铁律),必填变更理由(>=10 字),取值区间/枚举
     由后端返回字段呈现,越界/非法一律如实回显后端 detail,前端不自造校验通过。

2) scope.excluded_entities 接线 + 出厂默认 [3,4,5,6](拍板件 ③-2「排除」)
   - app/dq_rule_config.py:默认由 [] 改为 [3,4,5,6];新增单一事实源
     EXCLUDED_ENTITIES_KEY / EXCLUDED_ENTITIES_FIELD、normalize_entity_ids()、
     excluded_entities_from_resolution()、excluded_entity_ids()、
     is_excluded_from_business_caliber()。
   - app/api/data_quality.py:新增 _excluded_clause()(仅全账套口径 eid=0 生效,
     COALESCE(col,1) NOT IN (...) 参数化,NULL 归属不吞行)与 _business_excluded();
     七条规则的明细查询与 _detail_count 全部叠加排除子句;_plan_id_set /
     _pending_alert_plan_links 支持 excluded(播报口径同源)。
   - 响应新增 business_caliber{caliber,exclusion_applied,excluded_entities,included}
     与 scope_excluded_entities;单账套查询保持展示、仅标注不计入经营口径。
   - PUT /rule-config:平台级项改写全账套模板行(entity_id=0),审计 detail 与响应
     增 target_entity_id;GET 增 scope_note 与 write_contract{roles,platform_roles,
     min_reason_len,entity_config_entities,bot_channel_write,endpoint}。

3) 修复两处实现缺陷(本单测试新增用例的 RED 驱动)
   - excluded_entities_from_resolution() 取键与 resolve()["scope"] 的归组形态不一致
     (配了 scope. 全名去取已归组后的短名字典)⇒ 恒返回空集,接线静默失效;
     改为短名优先、全名兼容(EXCLUDED_ENTITIES_FIELD)。
   - upsert_param() 全局项 before 取值多一层 rule_key 查找 ⇒ before 恒 None;
     改为全局项直接以 param_key 为键。

4) 测试:tests/test_dq_rule_config_20260928.py 3 处既有断言按新口径更新 +
   新增 E 类 8 例(排除默认/单一事实源、全账套与单账套两种口径面、A/B 聚合差异
   只来自被排除账套、明细计数同源、平台级项改名单即时生效、平台级写落模板行)。

不改:判定表达式本体 / 级别归属 / 评分函数 / 规则集(DAMA §4 锁死 10 类)、
      unit_check.amount_threshold(保持 10000)、virtual_pollution 关键词表与来源白名单。

边界:本提交只含 4 文件(backend/app/dq_rule_config.py、
      backend/app/api/data_quality.py、backend/tests/test_dq_rule_config_20260928.py、
      frontend/src/views/DataQuality.vue);未裹挟他人在制件
      (audit_log.py / migrate_subjects_entity_id_20260923.py / package.json)。

证据:pending-tasks/_evidence/ask5-tail-20260929/
      (pytest 全量 1 failed/1516 passed + 失败项归因 A/B、vue-tsc rc=0、
       ab-exclusion-pre-write.json 只读 A/B 原始输出、gate dry-run 等)
2026-09-29 13:00:48 +08:00
wecom-fullstack 36f4f54799 feat(data-quality): 财务七规则可配置化最小实现(ASK-20260928-05)
ci/woodpecker/push/main Pipeline failed
任总 2026-09-28 13:38 放行「4 和 5」→ 落笔最小实现:
- 新表 quality_rule_config(39 项:37 规则级 + 2 全局)= 唯一配置源
- 三层解析:账套行 → 全账套模板(entity_id=0) → 代码内置默认(=清单 §7 建议值,零行为差异)
- 端点 /api/cma/data-quality/rule-config(GET/PUT)+ /history + /meta:写入仅 ceo/it,
  platform 级仅 it,演示账套(functional)写禁,变更落 operation_logs 同事务
- 数据质量页新增「规则配置」只读 Tab(懒加载)
- 唯一行为变更:污染判定去掉 sync —— 酣客 zxt-sync 系统迁入数据不再被误判(口径可配置)
- 出厂默认落库脚本 scripts/seed_dq_rule_config.py(--check/--apply/--rollback,幂等 39/0)
- 回归:新套件 35 例 + 存量 1508 passed(唯一 FAILED 为他人未完成
  migrate_subjects_entity_id_20260923.py 改动所致,HEAD 干净树同例通过)

生产写(DDL/seed)与 push 均走审批门:DDL=54434656 push=3bd00774
2026-09-28 13:56:16 +08:00
fullstack-bot 23f12fc866 fix(knowledge): render DB dynamic articles via markdown safe renderer
ci/woodpecker/push/main Pipeline failed
A: dynamic knowledge-articles content goes through marked instead of raw v-html (escape-first, no new deps).

B: plain-text fallback normalize (bracket headings, circled numbers to ordered list, blank lines, key-number bold, idempotent).

C: detail-content styles for h2/h3/pre/blockquote/paragraph spacing.

D: dialog width 700 to 860, content padding, in-dialog scroll.

Task: project-20260924-knowledge-dialog-render-fullstack
2026-09-24 23:32:50 +08:00
Hermes CI Fix 07dc10d557 fix(ui): 全仓 el-dialog 清剿 → MyDialog(26 处/17 文件)
ci/woodpecker/push/main Pipeline failed
- 项目铁律:一律 MyDialog(Element 原生弹窗在本项目长期不可见)
- 含用户报的 OKR 模板库「应用」弹窗(OKRTemplates.vue)
- 等价适配:width 字符串 px → 数字绑定;append-to-body/destroy-on-close
  /top/show-close/class 等 EP 专有属性移除(MyDialog 已 teleport+卸载语义)
- 新增 MyDialog 显式 import;零后端/零数据改动
- 全仓 <el-dialog> 0 命中;components.d.ts 随之移除 ElDialog 自动注册
2026-09-24 17:36:05 +08:00
Hermes CI Fix ede0524c22 feat(docs-center): CMAKnowledge reads md single source via new /api/cma/docs
ci/woodpecker/push/main Pipeline failed
New router app/api/doc_center.py: GET /api/cma/docs (list, ?with_content) and GET /api/cma/docs/{slug}; runtime disk read, role-gated (ceo/finance/business/it), no existing API semantics changed.

Content dir /srv/hermes/shared/cma-content (docs/*.md + categories.json). Placed outside the repo on purpose: the deploy tree equals the committed tree (deploy.sh checkout -f + clean -fdx), so in-repo content would be reset on every deploy and content edits would fail the deploy-tree dirty check.

CMAKnowledge.vue: 20 hardcoded items migrated to md, page now fetches at runtime and renders md with the existing marked dep (no new deps); table borders restored via :deep() since scoped styles never reach v-html content.

Tests: backend/tests/test_doc_center_20260924.py (20 cases). Full suite 1448 passed (baseline 1428 + 20).
2026-09-24 01:07:29 +08:00
Hermes CI Fix 72d4d97c83 fix(data-quality): KPI质量监控页 — 账套口径收口 + 环图颜色真值 + 天数单一来源 + 异常数下钻
ci/woodpecker/push/main Pipeline failed
1) 跨账套混算修复(最高优先):/stats 的 total_logs/open_logs/severity_counts/type_counts
   原先直查 kpi_data_quality_log 无任何账套过滤(全库数),而同页 /logs 已按账套收口,
   导致「卡片数字与下方列表对不上」。现四项与 /logs 共用 _scoped_quality_log_query
   (kpi_id join kpi_definitions,内连接,孤儿日志不计)。零 DDL:不给该表加 entity_id 列。
2) /logs 服务端分页(page/page_size,默认 50、上限 200)+ total 改真实 COUNT(*)
   (原为截断后行数 len(),且 limit=100 静默截断)。
3) 新增只读端点 GET /missing-data-kpis:「无实际值 KPI」清单(服务端分页 + 真实 total,
   口径与 /stats.data_missing 逐字一致),供「无实际值 KPI 占比」卡片下钻。
4) 环图黑块修复:ECharts 走 canvas,不解析 CSS 变量,原 itemStyle.color 传 CSS 变量会回退黑色。
   改合法 HEX 实值(与主题色同值 #F56C6C / #E6A23C)。全仓同类写法扫描:33 处 CSS 变量命中,
   仅此 2 处在 ECharts 上下文(已修);ReportCenter.vue:848-849(经 bscScoreColor() 流向 :style)、
   KPIDetail.vue:244/267(el-timeline-item :color 落 inline style)均为 CSS 上下文,非缺陷,未改。
5) 阈值文案单一来源:新增 STALE_THRESHOLD_DAYS = 180,接口 threshold_days 与之同源,
   后端注释同步修正;阈值本身保持 180 不动(改阈值属口径变更,须另行拍板)。
6) 前端 DataQuality.vue:卡片标题加维度限定词(KPI元数据完整度评分 / 无实际值 KPI 占比 /
   超 N 天未更新预警由接口渲染)、顶部四项加层级标注(记录数 vs 实体数)、三张异常卡 + 无实际值
   KPI 可点击,用 MyDialog 打开明细(0 值置灰不可点,服务端分页 50/页,导出 CSV 全量)、
   质量异常日志列表改服务端分页(显示 total,与卡片同口径)。未新增 el-dialog、未改 api/index.ts。

未动:KPI 检查算法 / 检查类型集合 / 异常判定阈值;未回退「财务七规则」标签页既有下钻
(govDetail* 与 rule-detail 端点)。
2026-09-24 00:40:11 +08:00
Hermes CI Fix 0b3a8aa111 feat(data-quality): anomaly-count drilldown to full detail + real COUNT(*) caliber
ci/woodpecker/push/main Pipeline failed
backend: GET /api/cma/data-quality/rule-detail (COUNT(*) total, structured items, server-side paging, scope=manual); governance seven-rule counts use real totals instead of len(LIMIT-ed rows). frontend: clickable anomaly count -> MyDialog detail (no new el-dialog) + full CSV export.
2026-09-23 18:49:57 +08:00
Hermes CI Fix e79b51666d fix(growth-quality): default period to newest period from API
ci/woodpecker/push/main Pipeline failed
The component shipped a hardcoded initial period (2026-06) that is present in the periods list, so the previous guard kept it and the page never defaulted to the newest period. Initialize empty so loadPeriods() applies periods[0] (backend returns DESC); /diagnosis treats an empty period as 'newest with data'.
2026-09-23 15:44:07 +08:00
Hermes CI Fix c88e1ad6c9 fix(growth-quality): period select reads periods array from object response
ci/woodpecker/push/main Pipeline failed
Backend GET /growth-quality/periods returns {entity_id,periods[],coverage{}}; axios interceptor already unwraps response.data so the old r.data||r||[] put the whole object into el-select and Vue rendered JSON.

Also default to periods[0]: backend returns periods in DESC order, so old code picked the OLDEST period. Only frontend/src/views/GrowthQuality.vue changed.
2026-09-23 15:29:44 +08:00
fullstackandHermes CI Fix bd4f58aae1 feat(action-plans): COSO 自检未通过条目一键生成改善行动(接通死按钮)
ci/woodpecker/push/main Pipeline failed
- 后端新增 POST /api/cma/action-plans/coso-generate-actions:未通过条目→action_plans,
  账套取 token 绑定(忽略 body entity_id)、幂等键 (entity_id,title)、不写 auto_verify_rule
- 前端 ActionPlanLibrary.vue 按钮接 @click(含未通过条数提示 + 二次确认 + 生成后刷新并跳列表)
- api 客户端补 cosoGenerateActions
- 新增 tests/test_coso_generate_actions_20260921.py(生成/幂等/隔离/未认证/归属校验/空集)
2026-09-23 01:03:19 +08:00
fullstack 30fa745135 fix(cma): 工作台跨实体治本 · DAMA 回查收口 — 审计可归属 + 通知日志口径
DAMA 独立回查 dama-recheck-workbench-entity-isolation-20260922 的 §4b/§5 两处待办:

1. POST /api/cma/tenant/switch 审计行原来不可归属:
   - user_id=None → 改为注入会话用户(user=Depends(require_auth_401))
   - target_type/target_id 从 system_config/None 对齐为 entity/<entity_id>,
     与 /auth/switch-entity 的 switch_entity 共用目标维度 ⇒ 按账套可过滤,零 DDL
   - action 字面 switch_tenant_state 保留不合并(与会话级 switch_entity 语义不同),
     口径登记 docs/audit-and-notification-caliber-20260922.md

2. /api/cma/notifications/logs 增加 policy/note 口径字段,前端发送记录表
   empty-text 提示同一口径 —— 避免「B2 上线后 e1/e2 均 0 条」被当成页面故障
   (现库 8 行历史记录全为 alert_id IS NULL 的 OKR 复盘提醒,无账套归属)

回归:tests/test_workbench_entity_isolation_20260922.py 22/22;全量 1370 passed;前端 vite build 通过
2026-09-22 11:21:24 +08:00
fullstack-bot e89629228e fix(cma): 缺失值 NULL 语义——同环比/执行率/进度不把缺失当 0
ci/woodpecker/push/main Pipeline failed
财务裁定 2026-09-21(博海 2026-08 批次 §2):前端对缺失值须显式 NULL 语义,
同环比不得按 0 参与。

- ProfitBreakdown: revChg/profitChg/rateChg 上期缺失→null(原 return 0 显示为持平 0.0%),
  去掉 prev || 0.01 假分母;模板 null 显示「缺上期数据」
- MyDashboard: 进度条 actual 缺失不再显示 0%,改显 —
- DeviationDashboard: 趋势图 actual/budget 保留 null,barHeight 对 null 不画柱
- BudgetManagement: 执行率/偏差额 actual 缺失→null(表格显示 -),平均执行率只对非空取平均
2026-09-21 16:07:03 +08:00
Hermes CI Fix 14ab6d211c feat(cma): alignment mode system-level hint + double confirm (project-20260918-alignment-config-hint)
Frontend only: KPIAlignment.vue adds system-level notice and ElMessageBox confirm before save. No backend/storage change.
2026-09-18 18:32:22 +08:00
Hermes CI Fix d33b5e1ced fix(cma): my-dashboard KPI period caliber (monthly period priority) + as-of/stale marks
my_dashboard picked the row with max calculated_at, so batch backfills made stale periods win (revenue/net-profit showed 2026-07, rebate rate 2026-04). Now prefer the latest monthly period, fall back to calculated_at. Frontend adds data-as-of label and stale tag; not deployed yet (push gated by another pending commit).
2026-09-18 15:39:12 +08:00
Hermes CI Fix 3bb55189b1 feat(cma): 口径裁定 2026-09-17 落地(现金流投资=0 二维判定 / 预警单位+期间口径)
ci/woodpecker/push/main Pipeline failed
依据:wecom-finance/reports/CMA_口径裁定_20260917.md(任总 2026-09-17 23:14「按建议执行」)

裁定1(现金流「投资活动净额=0」参与判定)
- classify_enterprise():0 为合法输入;投资恒为 0 → 经营(±)×筹资(±) 二维映射出 2 个
  候选类型(candidate_type_nos/candidates),不再整体判「未判定」;投资栏 investing_note
  标注「本期无投资活动」;经营/筹资亦为 0 → 回退「未判定(无投资活动)」,不报错不静默
- /analysis 增 classification_dimension / investing_note / nets.investing.note;前端出
  二维横幅 + 候选行高亮 + 投资栏「本期无投资活动」(不给单一类型名)
- import_cashflow_statements.py:打印层修 2D(type_no=None)TypeError 崩溃;NA 措辞改为
  「按 0 参与判定」(机读短语 NA_PHRASE_TMPL/parse_na_activities 未动)

裁定2a(比率类单位统一「百分比(%)」口径,先做)
- 新增 app/utils/caliber_units.py:逐 KPI 对照表 RATIO_CALIBER_TABLE(唯一事实源)+ 判定侧
  唯一入口 resolve_caliber();3 项需归一 ×100(流动/速动/毛利率),12 项取数已百分数不归一;
  表外且命中「疑似错配」窄形态(阈值子句量级≥100 且 0<|实测|<10 且单位属比率类)→
  dimension_suspect:不落预警、不推送(唯一规则,防 586/587 假红灯复发)
- alert_generator:判定前归一,文案写「口径归一 ×100(原值 1.29 → 129%)」留审计线索
- 新增 scripts/kpi_unit_crosscheck.py(只读对照表产出器)

裁定2b(期间由「当月硬编码」→「最近可用期间」,后做)
- resolve_latest_period():取该 KPI 自己的最新 YYYY-MM(禁全局 MAX,防演示账套劫持);
  伪期间(2026-H1/H2、2025-FY)在 Python 侧过滤(保「单测=生产」语义)
- 过渡期覆盖度自检扩展:记录回落后的期间分布 + 当月 0 条归因提示
- --dry-run(零写入零推送)作放行前抽查证据;generate_and_push/run_alert_check 支持 dry_run

测试:backend/tests/test_caliber_rulings_20260917.py 17 项;全量 1158 passed/1 xfailed
2026-09-18 00:34:04 +08:00
Hermes CI Fix 27d3e1cd73 feat(cma): strategy map lane KR delete entry (cma-map-kr-delete-entry-20260916)
- MapNode.vue: hover trash icon per KR row emitting delete-kr (inline SVG, no emoji)

- StrategyLayer.vue / MapCanvas.vue: passthrough + MyDialog second confirm + okrApi.deleteKr + whole-map save

- MapsDialog: single-KR policy hint (keep at least 1 KR per objective)
2026-09-16 15:26:38 +08:00
Hermes CI Fix 06c23927aa fix(cma): KR 名称污染存量清理 + 透传点护栏 (cma-kr-name-pollution-cleanup-20260916)
ci/woodpecker/push/main Pipeline was canceled
- kr_name.py/krName.ts: sanitize_kr_name 判据(尾部≥/≤数字≠目标值→截断;==目标值→保留)
- maps.py/okr.py/okr_templates.py: 模板名/JSON→krs.title 透传点护栏
- test: 28 passed(独立复核)
- 原会话因迭代上限停手,本提交代收口(工作已复核完成)
2026-09-16 14:48:06 +08:00
Hermes CI Fix d54ffaf531 fix(cma): T4 residual bugs - predict 500 + reports restateDiffClass (cma-t4-residual-bugs-20260916)
ci/woodpecker/push/main Pipeline failed
B1: /api/cma/dashboard/predict hand-rolled threshold parsing called float() on the whole

expression -> ValueError on multi-clause thresholds like '<=36|>=124' -> 500. Now routes

through app.utils.threshold_parser.eval_threshold (single source of truth; red then yellow,

invalid/blank expr -> False, no crash).

B2: ReportCenter.vue restatement tab template called restateDiffClass while the script

defines restatementDiffClass (name mismatch since ec6af75) -> TypeError on render.

Verified: predict 200 on new code vs 500 on live code; /reports A/B browser run shows 0 vs 3

occurrences of the TypeError; pytest 1113 passed / 1 xfailed; vite build OK.
2026-09-16 02:07:42 +08:00
Hermes CI Fix 7b74736980 refactor(cma-frontend): route all API calls through shared axios instance (cma-frontend-axios-unify-20260915)
api/index.ts: add permissionApi.getModules/getConfig/saveConfig + alignmentApi.config/tree.

views: replace 13 raw fetch calls and 6 local axios.create instances (each hand-building the Authorization header, without the shared 401 redirect / timeout toast) with the shared instance from api/index.ts.

KPIAlignment: fixes a live bug - the header value was the string literal 'Bearer ' + token, so /alignment/tree always returned 401 (reproduced against the production bundle).

Unwrap semantics preserved: the shared response interceptor returns response.data, so call sites that used res.data were adjusted accordingly.

Verified: scan-frontend-tokenless RAW-TOKENLESS=0; vite build ok; headless A/B (new dist vs production bundle, 8 affected pages, real login) -> identical rendered rows and identical pre-existing error profile, /alignment 401->200.

Push not executed: main is ahead 4 with other tickets' gated commits (see cma-main-ahead3-push-gate-notice-20260915).
2026-09-15 22:21:56 +08:00