125 Commits
Author SHA1 Message Date
Hermes CI Fix 5a99ed87e0 fix(alerts): enforce caliber ruling R5
- alert_entity_scope: add is_attributable_period / guard_alert_period (R5) and entity_alert_guard (R1, any write path)
- alert_rules.run_all_alert_checks: skip non-YYYY-MM period alert rows (R5)
- bot_bridge / bot_bridge_v2: R1 demo-entity guard + R5 period guard on write paths
- tests: new R5 suite (10 cases)

Not pushed / not deployed (release gate held by 任总).
2026-09-19 10:42:09 +08:00
Hermes CI Fix 3beec14d96 fix(alerts): enforce caliber rulings R1-R4 for alert generation
R1: exclude status=demo entities from alert traversal (alert_generator / deviation_engine / kpi_bep_deriver / alert_rules / cash engine)

R2: only generate alerts within the last 6 calendar months (incl. current)

R3: only regular month periods (YYYY-MM) participate

R4: decouple generation from push - empty notification_channels never blocks

Single source of truth: app/utils/alert_entity_scope.py
2026-09-19 10:05:11 +08:00
Hermes CI Fix 557771a803 fix(cma): scope ai-suggestion action_plan and preview KPI to session entity
_apply_action_plan: constrain the target KPIDefinition lookup to sug.entity_id so a
suggestion whose target_id/suggestion_data.kpi_id points at a foreign-entity KPI now
returns 404 instead of creating an ActionPlan row with a cross-entity kpi_id.

preview_suggestion: same constraint; a foreign-entity KPI now degrades to the existing
placeholder (KPI#<id>, current_target=None) instead of echoing the foreign KPI name and
current_target. Degradation (not 404) keeps the read-side preview usable when the stored
kpi_id is inconsistent.

Regression: backend/tests/test_ai_suggestion_kpi_attribution_residual_20260918.py
(RED 2 failed before, GREEN 5 passed after; full suite 1228+2F -> 1230 passed / 1 xfailed).
2026-09-18 21:04:30 +08:00
Hermes CI Fix 9cb960da25 fix(cma): scope ai-suggestion dismiss and target KPI to session entity
dismiss_suggestion: add get_entity_id dependency and filter AISuggestion.entity_id (cross-entity dismiss returned 200 and mutated the foreign row).

_apply_kpi_target/_apply_budget_adjust: constrain target KPIDefinition lookup to sug.entity_id so suggestion_data pointing at a foreign KPI yields 404 instead of rewriting it.

Regression: backend/tests/test_dismiss_suggestion_entity_scope_20260918.py (RED 3 failed before, GREEN 5 passed after; full suite 1220 -> 1225 passed, 1 xfailed).
2026-09-18 20:33:39 +08:00
Hermes CI Fix d2984857a2 fix(cma): scope read-side ai-suggestion detail/preview and map nodes
-m- ai_suggestions.get_suggestion: filter by session entity_id (foreign 404)

- ai_suggestions.preview_suggestion: use the existing entity_id dependency in the query

- deviation_push.get_map_nodes: add entity_id dependency and (entity_id or 1) ownership guard

- tests: 4 regression cases (3 RED -> GREEN + auth baseline 401)
2026-09-18 20:23:53 +08:00
Hermes CI Fix d03e322a81 fix(cma): action plan entity_id at the three residual construction sites
alert-to-plan, deviation-push review topic and AI-suggestion-to-plan built ActionPlan(...) without entity_id, so non-e1 sessions wrote rows with the model default 1 and entity_id stayed untrustworthy on those paths. Write the session entity at all three sites, scope the deviation-push topic dedupe by entity (a foreign same-title topic no longer suppresses the session entity's own topic), and require the applied suggestion to belong to the session entity. Adds 7 regression tests (both directions + cross-entity negative controls + auth floor).
2026-09-18 20:12:46 +08:00
Hermes CI Fix b072602191 fix(cma): action plan entity_id on create + okr decomposition entity scope
create_plan wrote no entity_id (fell back to model default 1) while the OKR decomposition view filtered weekly_actions by objective_id only, so cross-entity plans leaked into the other tenant view. Write the session entity on create (before adding the read-side condition - order matters) and scope weekly_actions by entity_id. Adds 4 regression tests (both directions + same-entity positive control).
2026-09-18 20:03:55 +08:00
Hermes CI Fix bab6137aa2 fix(cma): scope action-plan objective_id writes to session entity
create_plan/update_plan now validate Objective.entity_id ownership (404 on cross-entity, no partial row) and update_plan gets an explicit objective_id-vs-KR-derived priority rule. Adds 10 regression cases (batch3 bias: bidirectional + same-entity control + OKR decomposition read-side consequence).
2026-09-18 19:34:59 +08:00
Hermes CI Fix 50baa16f09 fix(cma): scope action-plan KR enrichment to session entity (batch3 follow-up)
list_plans enriched kr_title via KR.id only -> legacy rows whose kr_id points at another entity's KR echoed the foreign KR title. Add KR.entity_id filter (read-side sibling of batch3 item 4).

Local commit only; no push (push triggers Gitea deploy hook).
2026-09-18 19:03:49 +08:00
Hermes CI Fix 9fb9d14a58 fix(cma): entity isolation batch3 (cost abc write endpoints + allocations/stats reads + plan kr_id scope)
- cost.py: POST /abc/activities 按会话账套写入 entity_id;POST /abc/allocate 校验 activity_id 归属本账套(跨账套 404)并透传 entity_id;GET /abc/allocations 加账套过滤
- cost_engine.py: allocate_cost(entity_id=None) 按账套过滤作业中心 + 显式写入 AbcAllocation.entity_id(不传维持原语义)
- action_plans.py: update_plan 的 kr_id 加账套校验(跨账套 404,对齐 create_plan 既有写法);plan_stats join KPIDefinition 按账套过滤(口径与 list_plans 一致)
- tests: 新增 tests/test_cross_entity_isolation_batch3_20260918.py(10 例;改前 7 failed/3 passed → 改后 10 passed)
- 回归: 1184 passed,1 xfailed → 1194 passed,1 xfailed(无新增失败)
2026-09-18 18:42:38 +08:00
Hermes CI Fix ad953ac759 fix(cma): entity isolation batch2 (roll-forward/check-timeout/by-id/cost-abc)
ci/woodpecker/push/main Pipeline failed
Scope: backend/app/api/{alerts,action_plans,budget,cost}.py + tests

Task: 0-P1-project-20260918-entity-isolation-batch2
2026-09-18 18:15:22 +08:00
Hermes CI Fix b9c80f836e test(cma): force production Redis off during tests (P1 token leak isolation)
ci/woodpecker/push/main Pipeline failed
2026-09-18 17:33:25 +08:00
Hermes CI Fix a2fedd892a fix(cma): isolate KPI alignment tree by entity (project-20260918-alignment-entity-leak)
GET /api/cma/alignment/tree queried all active KPIs without entity_id filter,

so switching the three alignment modes showed other tenants' KPIs.

Add get_entity_id dependency and filter KPIDefinition by entity_id.

Three mode builders (_build_vertical_tree/_build_horizontal_tree/_build_bsc_chain) unchanged.

Auth semantics unchanged; cross-entity explicit entity_id still returns 403.

Test: tests/test_alignment_entity_isolation_20260918.py (3 modes x both directions, 403, 401).
2026-09-18 17:18:52 +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 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 ae6f71dbc0 feat(cma-report): 建账期初层 + 方向判定可信度 + 权益行科目集(M1/M2/M7,规则表 v1.1)
- M1 时点报表口径:余额 = 期初(report_opening_balances) + 累计发生额;期初只作用于取数层
  (不生成凭证/不写 voucher_details/不改 kpi_values);basis=period 不套期初;受 effective_date 约束
- M2 期初未补 → direction_check_unreliable=true:报表/底稿带"期初未补,方向判定不可信"水印,
  重分类触发只进底稿诊断(diagnostic_only),不落地对外行项目
- M7 权益行科目集 = {3001,3002,3101,3103,3104,3105}(含 4001/4002/4101/4103/4104 别名):补实收资本 3001
- 1901 待处理财产损溢过渡列报「其他流动资产」以保恒等式(M6 归因文案)
- 只读验收脚本 + 幂等导入器(dry-run 默认,--commit 门控)
2026-09-16 01:17:09 +08:00
Hermes CI Fix ba85e03f2f feat(cma-report): 资产负债表取数层重分类 R1~R12(调表不调账)
ci/woodpecker/push/main Pipeline failed
依据《CMA 资产负债表重分类规则表 v1》(wecom-finance,任总 2026-09-16 批准落地)。

新增 backend/app/services/report_reclass.py:12 条规则挂 CMA 真实科目编码的取数层重分类引擎
- 余额方向规范化:资产 sum(借-贷) / 负债·权益 sum(贷-借),正常余额一律为正
- 触发判定全部基于原始余额(单遍、幂等),R3/R4 等互为镜像的规则互不干扰
- 科目取数前缀匹配(1122% 覆盖 112201 明细);备抵科目 1602 反向取数(修正固定资产净额虚增 2 倍折旧)
- 只读 voucher_details + 作废守卫,不生成记账凭证
- 输出:触发条目 / 不变量 / 负数清单 / 模板待核 / 未列报科目 / 损益未结转 诊断

reports.py:
- /balance-sheet 接入重分类;新增 basis 参数(end=期末余额累计为默认 / period=本期发生额兼容回退)
- 有凭证明细时不再混入示例数据(展示口径与落库口径同源);行项目增 raw_value/reclass_adjustment 可追溯
- 规则落位项目(模板未单列)按虚拟行列报;补「长期待摊费用(1801)」行、未分配利润补 3103/3104 别名
- 新增 /balance-sheet/reclass-working-paper(+ /export) 底稿 JSON/Excel;statutory 导出新增「重分类底稿」Sheet

测试 tests/test_report_reclass.py 14 例(触发/不变量/幂等/多租户/口径/底稿/虚拟行/阻塞规则)
全量 pytest:1108 passed, 1 xfailed。现网只读验收 scripts/verify_reclass_live_20260916.py(酣客 entity1 2026-08)
2026-09-16 00:38:01 +08:00
Hermes CI Fix fdf805ab8f test(cma): clear 10 pre-existing baseline failures (cma-baseline-tests-cleanup-20260915)
No production code changed. Root causes were all test-side staleness:

- test_cash TestVoucherImport: fixture filename contained 'test' -> rejected by the intentional import guard (d71c7fe); renamed, and missing-columns case now asserts the column check is what returns 400

- test_budget_tech_improve TestP1ValueCollect: seeded voucher_details source rows; collector deliberately skips empty aggregates since d71c7fe (no more implicit 0), and preview call now passes an explicit period

- test_roadmap_r2r5 / test_budget / test_ai_suggestions: seeds hardcoded 2026-08 (authoring month) while detectors/rule engine use the current month -> replaced with current-period seeds

pytest tests/ -q -rf: 1094 passed, 1 xfailed, 0 failed (baseline was 10 failed, 1084 passed, 1 xfailed)
2026-09-15 18:36:46 +08:00
Hermes CI Fix 9d9a79545a feat(cma): external input ledgers for M2 capability eval (tax/hr/licence)
3 new tables + idempotent DDL migration (prod guard: refuses DB name cma without approval): tax_filing_log (P_TAX_TIMELY), hr_monthly_ledger (L_EMPLOYEE_TURNOVER), licence_ledger (P_LICENCE_OK).

Entry API /api/cma/ext-ledger: create / CSV batch import / query by entity+period / derive trigger. on_time is server-derived only; HR rows rejected when end != begin + hires - separations.

Deriver writes kpi_values only (source_type=derived, idempotent upsert); denominator 0 returns None and writes nothing; never creates or changes kpi_definitions.

Tests: 22 new pytest cases (22 passed); scratch-DB e2e battery 28 assertions PASS with zero production write.
2026-09-15 18:12:05 +08:00
Hermes CI Fix b796897f8c fix(auth): unify site-wide 401/403 semantics (cma-auth-401-403-unify-20260915)
require_auth now uses HTTPBearer(auto_error=False) and raises 401 explicitly
when the Authorization header is missing or non-Bearer, matching
require_auth_401. Unauthenticated requests are 401 for every user endpoint;
authenticated-but-forbidden (cross-entity / role) stays 403.

- app/auth_middleware.py: single optional bearer, 401 on missing token
  (require_auth_401 kept as a compatibility alias)
- tests: update 4 pre-existing cases that asserted 403 without a token
  (test_auth, test_reports, test_kpi_causality, test_expenses,
  test_api_auth_surface_20260914) - intentional behavior change
- add tests/test_auth_401_unification_20260915.py: sample + full sweep
  (all AUTHED GET routes -> 401), invalid token -> 401, cross-entity and
  role-denied still 403, bot channel (X-BOT-KEY) and public paths unchanged
2026-09-15 17:00:52 +08:00
Hermes CI Fix 1f7be6b5ae fix(cash): select cash-balance sync targets by explicit kpi_code whitelist
The voucher-import linkage branch picked write targets by kpi_name LIKE '%cash%' and wrote the monetary-fund ledger balance (yuan) into derived KPIs (F_OP_CFLOW / F_FCF / F_BEP_REVENUE_CASH / F_BEP_COVER_CASH). Replace with an explicit kpi_code whitelist plus a unit/caliber gate that refuses writes on mismatch and reports them via kpi_sync_skipped.

Read-only equivalence probe (real function + intercepted writes + rollback) before/after: before wrote 4 derived KPIs, after writes only F_CASH_SAFETY. tests: +9 new cases; pytest failure set identical to baseline (15/15 same ids).
2026-09-14 20:37:12 +08:00
Hermes CI Fix 8693c8e342 feat(kpi): route all voucher_details consumers through single void-mark guard
Single source: app/utils/voucher_guard.py (NOT EXISTS on voucher_void_marks,

key = entity_id+voucher_no+voucher_date and revoked_at IS NULL; same caliber as

the DB view cma.v_voucher_details_active). No hardcoded voucher numbers.

Consumers now guarded: cash.py import linkage (2 fns), reports.py subject/BS

amount (2 sites, also entity_id isolation added), dashboard.py _bep_sum_field,

alert_attribution.py build_subject_attribution. kpi_value_collector.py now

delegates to the shared guard (removes the previous duplicate implementation).

Tests: new tests/test_void_marker_consumers_20260914.py (6 cases).
2026-09-14 19:05:16 +08:00
Hermes CI Fix a8a82ff837 feat(kpi): 归集侧接作废/撤单标记过滤(voucher_void_marks)
- kpi_value_collector: 所有消费 voucher_details 的归集路径统一排除已作废凭证
  (NOT EXISTS voucher_void_marks, entity_id+voucher_no+voucher_date+revoked_at IS NULL),
  与 DB 视图 v_voucher_details_active 定义一致;标记表不存在时自动跳过(兼容旧库)
- 禁硬编码:凭证号不出现在代码/配置,撤单只需写标记表
- verify_cashflow_direct_caliber: 2026-08 从已知差异放行改为硬断言(8 期逐期一致)
- tests/test_void_marker_filter_20260914.py: 标记表在/不在 两分支 3 例
2026-09-14 18:44:39 +08:00
Hermes CI Fix 8fa6c7f520 feat(kpi): auto_collect review gate (finance ruling 2026-09-14)
R2 rule-gate auto-promotion + R3 exception interception in the same collect transaction; merge duplicate rows, keep single verified main per (kpi_id, period); reuse kpi_bep_deriver._is_period_closed; no schema change (data_status/remark only).

Task: finance-20260914-autocollect-reviewflow-impl
2026-09-14 18:12:38 +08:00
Hermes CI Fix ec58efa20b fix(budget): 偏差检查同 KPI 多计划去重(deviation-check 补 db.flush)
autoflush=False 下,check_budget_deviation 循环内 existing_alert 去重查询
查不到本次刚 add 未 flush 的行 → 同期间同 KPI 多 active 预算计划会重复告警。
add 后补 db.flush() 使其对下一次循环可见。附回归测试。

Ref: yanxue-20260914-budget-p13p14-activation 遗留②试跑发现的缺陷
2026-09-14 17:56:26 +08:00
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
fullstack-bot b815b7f623 fix(security): close unauthenticated CMA user API surface + audit tool (cma-api-auth-surface-audit-20260914)
Root cause: main.py include_router had no global auth dependency; user routers
(products/cost/predict/...) relied on assert_entity_scope, which treats a
missing session token as the Bot service channel and lets the request through.
Result: 46 user endpoints returned data without any token on a public site.

Changes:
- auth_middleware.py: add require_auth_401 (missing Bearer -> 401, delegates to
  require_auth otherwise); 401/403 semantics are explicit.
- router-level Depends(require_auth_401) on products, cost, predict,
  notifications, permissions, tenant, alignment; endpoint-level on
  /auth/roles and /admin/cache/clear.
- deps.py: document that get_entity_id/assert_entity_scope are NOT auth.
- frontend: add the missing Bearer header to 5 tokenless fetch/axios calls that
  hit the newly protected endpoints (KPIAlignment x2, RolePermissions x2,
  Dashboard x1).
- tests: new test_api_auth_surface_20260914.py (58 cases: no-token 401,
  token 200, cross-entity 403, bot channel unchanged); adapt test_predict /
  test_kpi_forecast to send session tokens like production.
- scripts/audit_api_auth_surface.py: reusable FastAPI dependency-graph audit
  (423 endpoints; A bot / B gap / C public), exits 1 when a B-class gap exists.
2026-09-14 16:41:15 +08:00
Hermes CI Fix aac1bd1df1 fix(security): retire check-governance + scope /reports/dupont to session entity
- data_quality.py: remove GET /check-governance (no caller repo-wide; its
  entity_id param had no session auth -> cross-tenant read; duplicate of
  governance-check) -> the path now returns 404
- reports.py: /reports/dupont resolves the analysis target from the session
  account (assert_entity_slug_scope / resolve_session_slug); explicit
  cross-entity entity param -> 403, illegal value -> 400; the 2026-08-31
  cross-entity exemption is revoked (docstring updated)
- frontend: drop the checkGovernance api definition; DupontAnalysis.vue
  entity radio group -> read-only session entity tag, request without param
- tests: TestCheckGovernanceRetired (404 x2), TestDupontSessionScope (3),
  TestDupont reworked to account-bound tokens (8 tests)
- docs/api-verification-split.md: drop the retired row, note 334/404 -> 333/403

pytest: 10 failed / 957 passed / 1 xfailed vs HEAD baseline 10 failed /
951 passed / 1 xfailed (identical failure set -> zero regression)

(cma-residual-cross-entity-cleanup-20260914)
2026-09-14 12:09:04 +08:00
Hermes CI Fix 115a0ec755 fix(security): 4端点补跨租户鉴权 — 实体1会话读实体2数据(200)→403 (cma-cross-entity-endpoints-auth-20260914)
背景(项目Bot 补充复验 §四#2):以下 4 个只读端点未做会话账套鉴权,
用 entity=1(酣客) 会话 token + 显式 entity 参数可读到 entity=2(博海) 数据(HTTP 200):
  - GET /data-quality/governance-check?entity_id=2   → 200 score=88(对照 entity1=72)
  - GET /products/matrix?entity_id=2                 → 200
  - GET /alerts/risk-matrix?entity=bohai             → 200 博海风险矩阵
  - GET /action-plans/coso-checklist?entity=bohai    → 200 博海 COSO total_score=55
另:risk-matrix / coso-checklist 在非法实体值下静默回落酣客数据,UI 标签与数据不一致。

改动:
1. deps.py 新增统一防护入口 assert_entity_scope / assert_entity_slug_scope /
   get_session_entity / resolve_session_slug(文案对齐 get_entity_id:无权访问企业 entity_id=X(当前账套: Y))
2. governance-check / products/matrix:entity_id 须等于会话账套,否则 403;
   缺省按会话账套(governance-check 原缺省 0=全部 → 登录态下按账套收敛)
3. risk-matrix / coso-checklist:entity 须等于会话账套,否则 403;
   非法值 → 400 显式报错(不再静默回落酣客)
4. P3:POST /auth/switch-entity 加 DEPRECATED 注释(前端已无调用,保留不删)
5. 测试:tests/test_security_multitenant.py 新增 TestCrossEntityEndpointAuth 15 例
   (跨实体 403 / 同实体 200 / 缺省按账套 / 非法显式报错 / 参照端点行为不变)

验证:pytest tests/ -q → 951 passed(10 项失败为改动前既有基线,已 stash 对照确认);
      真实后端冒烟 19/19 PASS(entity=1 会话 4 端点跨实体全 403)。
2026-09-14 11:19:05 +08:00
wecom-fullstack e159db058a fix(alerts): F4b 展示口径与落库口径同源 — summary/suggestions/upcoming_7d 按快照批次 mode 过滤 + raw/excluded/batches 可见 (dama-cashplan-zombie-alert-cleanup-20260913) 2026-09-13 07:38:58 +08:00
Hermes CI Fix b8f4a3d543 chore(security): 凭据治理收口 — 清除硬编码DB账号/口令,脚本与API统一从 backend/.env 读取(无明文兜底);取消跟踪 .pyc 构建产物 (credential-governance-20260912) 2026-09-12 23:34:35 +08:00
Hermes CI Fix 46fc1a4ecc fix(bot-bridge): subjects category 白名单补齐库内实况 revenue/profit_loss — 消除库内合法值被400拒 (dama-cma-subjects-category-whitelist-20260912) 2026-09-12 11:09:16 +08:00
Hermes CI Fix ca2832b698 fix(bot-bridge): subjects 写入端点编码长度收敛到 DB 列宽20 — 21~32字符编码原穿过校验触发 MySQL 1406 整批500, 改为400拒绝 (dama-cma-subjects-write-endpoint-20260912 复核) 2026-09-12 10:46:57 +08:00
Hermes CI Fix 840d98765d feat(bot-bridge): 新增会计科目写入端点 POST /api/cma/bot/subjects — 行级全量校验+按subject_code幂等UPSERT(dry_run/update_existing开关)+L3留痕, 绝不DELETE (dama-cma-subjects-write-endpoint-20260912) 2026-09-12 10:42:46 +08:00
Hermes CI Fix af541e9043 feat(bot-bridge): 新增智享通科目字典写入端点 POST /api/cma/bot/dict/zxt-account — 幂等UPSERT(JSON/CSV)+L3分级+audit留痕, 解除 ops 管道 API 迁移阻塞 (ops-cma-dict-write-endpoint-20260912) 2026-09-12 10:07:20 +08:00
Hermes CI Fix e22093256b fix(kpi): suggest 保留历史默认口径 middle_best — 修复未知编码被误判为 higher_better(C_REBATE_RATE/BH_SALES_10 建议退化) (project-20260911-threshold-fix) 2026-09-11 17:27:12 +08:00
Hermes CI Fix 4c6fb85cf5 fix(kpi): 阈值红黄绿读写/填写规则修复 — 统一解析器+红灯优先+建议可解析+前端校验+存量修正 (project-20260911-threshold-fix)
① 新建 app/utils/threshold_parser.py 统一解析/判定(单一事实源):
   - 支持负数(>=-100000)、闭区间 A~B、多条件 C1|C2;空串=未设置
   - 判定顺序 red→green→yellow:红灯优先(消除宽黄灯遮蔽红灯),
     green 先于 yellow(green 是主条件,yellow 是更宽兜底带)
② kpis.py / alert_generator.py 双实现收敛到统一解析器(消除重复维护)
③ thresholds.py suggest 输出可解析表达式(原黄色/适中型生成 `52.33~107.67`、`偏离55%` 等
   区间/文字 → 用户点"自动建议"填入后静默失效;另修复大数目标生成 1.6e+06 科学计数法)
④ KPIDetail.vue 三个阈值输入加 placeholder/tooltip/帮助文案 + 保存前格式校验(非法即拒)
⑤ 存量 4 个不可解析表达式修正(id=46 C_REBATE_RATE / id=327 BH_SALES_10),
   改前 mysqldump 备份 backend/scripts/backups/kpi_definitions.bak-20260911.sql +
   threshold_rows_before_20260911.json(可回滚);不可解析 5→0
⑥ 单测 97 条 + 红黄绿判定回归 367 判定点(旧→新差异 14 条,含差异说明)
2026-09-11 17:25:24 +08:00
Hermes CI Fix f61b6ce604 fix(cash): 智享通凭证行级幂等键剔除派生period + period_conflict留痕 + 批内行序防吞行 (dama-zxt-idemkey-period-20260911)
缺陷: 行级幂等键含 period(日期派生属性),summary口径切主表VoucherDate后判重失效→重复入库(实测V17756 6行)。修: 幂等键=(entity_id,voucher_no,subject_code,debit,credit,summary);命中且period不同→period_conflict计数仅留痕不覆盖;同键多行按载荷内出现次序编号防跨批次吞行;返回体/日志补计数。回归测试10项 + 真实快照41830行复推实证(测试库)。
2026-09-11 17:13:07 +08:00
Hermes CI Fix 9822fbd9ba feat(alerts): 新增客户集中度预警规则 customer_concentration + 预测偏差规则启用/联动每日预警生成
- alert_rules: 新增 rule_type=customer_concentration(单一/前N客户收入占比超阈值触发,黄=超阈值/红=阈值x倍数)
  数据源 zxt_masterbill 客户(往来单位)收入聚合,unit_id<=0 占位剔除;无客户数据可回退 KPI 口径
  复用现有 KPIAlert 生成链路:check-all 内联分支 + 专用端点 POST /run-customer-concentration
- alert_generator(每日9:00): 新增规则引擎步骤,复用上述检查函数跑 forecast_deviation/customer_concentration
- AlertList.vue: 规则类型筛选/编辑项 + 参数表单 + 客户集中度检查按钮
- 测试: tests/test_customer_concentration_rule.py 17 条(规则注册/聚合/双数据源/黄红灯/去重/check-all)
- 验证脚本: scripts/verify_forecast_deviation_rules_e2e.py / verify_customer_concentration_rule_e2e.py
2026-09-10 09:56:50 +08:00
Hermes CI Fix 397c365263 feat(maps): 战略地图版本管理完整实现(方案B)— versions API列表/详情/回滚带自动备份/备注 + MapVersions独立版本页 + 路由注册 + 版本弹窗接真实数据 (project-20260908-map-versions-page) 2026-09-08 20:11:59 +08:00
Hermes CI Fix 193061cf4d fix(data): 数据管理模块脱节修复 — Excel导入/sources端点 entity 隔离 + ERP直连通道诚实化(410) + 真实zxt链路展示
- import_excel/import_excel_smart KPI字典按 entity_id 过滤(防跨账套匹配串写,P0安全)
- import_excel_smart 自动建KPI编码防冲突(_next_code 循环取号)
- sources list/create/update/delete 全部按 entity_id 隔离(create 写入当前账套)
- sync-kpis + admin/erp-sync 下线返回410(erp-gateway DB凭据2026-08-31已删,真实通道=zxt智享通注入)
- 新增 GET /data/channel-status:展示真实zxt取数通道状态+遗留ERP数据源清单(诚实化)
- DataManage.vue:数据源tab顶部展示真实通道(最近注入period/时间/行数/落盘目录mtime)、ERP遗留源标注'非当前通道'、实际取数映射(kpi_value_sources)表格
- 新增 test_data_entity_isolation_20260908.py(8条:双账套导入隔离/CRUD隔离/sync-kpis 410/channel-status)
2026-09-08 11:19:38 +08:00
Hermes CI Fix ac645a56ee fix(security): ai_analysis多租户隔离缺口 + dashboard预警计数 + bot_kpis归属 + cost账套过滤 + kpis create白名单 (OpenCode审查20260907)
- ai_analysis: generate_rule_suggestions pending预警按entity过滤; kpi_analysis归属校验404;
  dashboard-analysis-stream/ask/review-plans 补 get_entity_id + KPI/ActionPlan/KPIAlert 全按账套过滤;
  stream cache key 含 entity_id (原跨企业串缓存)
- dashboard: summary alert_count 补 KPIAlert.entity_id 过滤 (kpi_total 已有)
- bot_kpis: update_bot_kpi_value 归属校验 404 (原任意登录可改他企 bot KPI)
- cost: ActualCost/StandardCost 确认多企业共用 → cost_overview/variance/breakdown/dashboard
  补 entity 过滤(engine 函数加 entity_id 透传); dashboard total_cost 既有 KeyError 修复
- kpis: create_kpi 字段白名单 (仿 UPDATE_KPI_WHITELIST+kpi_code), 拦截 status/important_flag 注入
- tests: 新增 test_multitenant_ai_20260907.py 11条越权用例 (跨entity 404/空返回断言)
- 全量 pytest: 745 passed / 10 failed(与基线 HEAD 一致, 存量问题非本次引入) / 1 xfailed
2026-09-07 10:22:24 +08:00
Hermes CI Fix 696f8bc934 feat(cost): 成本模型加研发归集维度 research_flag/project_type (cma-rd-dimension-20260905)
- StandardCost/ActualCost 增加 research_flag(int,idx)/project_type(varchar30,idx)
  project_type: platform_dev平台研发/client_delivery客户交付/internal内部管理
- 生产库两表 ALTER 完成, 存量 29+134 行默认 internal(保守防误归研发)
- API: create/update 支持写入维度, 未传默认 client_delivery(录入默认),
  research_flag 未显式传入按 project_type 联动(platform_dev=1);
  create/update/delete 绑定 entity_id(token优先, 防跨账套)
- API: standard/actual 列表支持 project_type/research_flag 过滤
- 前端 CostDashboard: 表单加归集类型下拉(默认客户交付+研发提示),
  列表加归集类型列(标签), 工具栏按归集类型过滤
- tests: test_cost.py +6 用例(默认值/联动/过滤/更新)
- scripts: verify_cost_research_dimension_e2e.py 生产E2E(标记+清理+回查)
2026-09-05 09:10:35 +08:00
Hermes CI Fix 9eab9bb8a1 Revert "fix(knowledge): articles共享层语义对齐评审方案——entity_id=0系统共享(替代NULL) (entity-memory-partition 复核对齐)"
This reverts commit 5e96d15888.
2026-09-03 23:09:58 +08:00
Hermes CI Fix 5e96d15888 fix(knowledge): articles共享层语义对齐评审方案——entity_id=0系统共享(替代NULL) (entity-memory-partition 复核对齐) 2026-09-03 23:09:04 +08:00
Hermes CI Fix e22e1702f8 feat(knowledge): 企业记忆分区——知识三表entity_id多租户隔离 (entity-memory-partition P1)
- knowledge_events/knowledge_summaries: entity_id NOT NULL DEFAULT 1 + 索引(企业记忆抽屉)
- knowledge_articles: entity_id 可空——NULL=全局共享(CMA教学知识), 有值=企业专属
- knowledge_service 全链路 entity_id: extract_events(KPIAlert直过滤+OperationLog经user_entities归属)/_kpi_snapshot(修跨企业KPI泄漏)/generate_summary/get_last_summary/run_*/sync
- api/knowledge.py 8端点 Depends(get_entity_id) 强制过滤;详情越权404
- api/knowledge_articles.py 全局∪本企业查询;企业专属越权404
- 生产DB三表ALTER+索引已完成; 存量38篇教学文章保持NULL全局共享
- 测试 test_security_knowledge_partition.py 8条(双实体互查越权/生成自动带entity/全局文章双可见)
- 方案文档 docs/knowledge-entity-partition-plan-20260903.md(附评审决策点)
2026-09-03 23:00:15 +08:00
Hermes CI Fix 80fbc11cba feat(bot-tier): M1 Bot侧高危建议创建通道+目标值直写收口 (P0-②分层授权)
- 新增 POST /api/cma/bot/ai/suggestions (bot_suggestions.py, X-BOT-KEY鉴权, L2)
  高危建议(kpi_target/budget_adjust/action_plan) status恒unapplied 等人审批
  source白名单(bot/rule)防冒充manual; entity多租户归属校验; 幂等防重
- risk_levels.py: API_RISK_MAP加新端点 + 文件头补写操作语义分级low/high说明
- 收口: bot/kpis/create-with-links 拒绝直写非零target_value(显式置0, 考核基准=人拍板)
- test_risk_levels: L2计数5→6; 新增test_bot_suggestion_channel 12条(鉴权/语义/多租户/幂等/审批闭环/审计)
2026-09-03 18:08:27 +08:00
Hermes CI Fix e5dd6e380b feat(cashflow): 现金流量表结构分析——三大活动净额八类企业定性判定 (P1)
- 后端: cashflow_structure.py 模块 (GET rules/periods/values/analysis + POST values upsert)
- 判定: O/I/F 三净额符号 → 8类企业定性 (CMA P2 现金流量表分析, 无'现金流肖像'字样)
- 数据: cash_flow_structure_values 表 (entity_id+period 唯一), 官方现金流量表净额按期间注入
- 前端: CashFlowStructure.vue 判定横幅/瀑布图/规则表/入库记录 + MyDialog 录入弹窗
- 菜单: 财务报表分析 → 现金流量表分析 (路由 /cashflow-structure, 角色 ceo/finance/it)
- 测试: tests/test_cashflow_structure.py 7 条通过 (规则8类/判定/覆盖/博海边界6-7/非法期间)
2026-09-03 01:22:46 +08:00
Hermes CI Fix 74dc9baff5 fix(security): 多租户隔离全量修复 security-fix multi-tenant (OpenCode审查P0)
- bot_bridge 18数据端点全部 entity_id 隔离(Depends(get_entity_id)/body),/ping /risk-levels 豁免
- alert_rules 11端点 entity_id 隔离 + KPIAlert/DynamicThresholdCache 写入 entity_id
- reports 17端点隔离 + generate_report 写 ReportHistory.entity_id + history 按 entity 过滤
- ai_analysis 移除硬编码默认key,改 _require_deepseek_key() 强制 env 缺失 503
- budget auto-decompose 硬编码 entity_id==1 改请求 entity
- kpis update_kpi 加 UPDATE_KPI_WHITELIST 白名单(status/important_flag 不可越权改)
- data_quality 收敛:删 MySQL JSON 版 _run_rule_checks,check-governance 复用 _run_governance_checks(SQLite 兼容)
- _eval_threshold invert 参数修复(>=↔< 等取反),red 分支不传 invert 保持行为
- 新增 test_security_multitenant.py 13条(bot_bridge/alert_rules/reports 隔离 + invert + SQLite governance)
- models 6表加 entity_id 列;生产库已 ALTER + 按真实归属回填(kpi_alerts 472行中216行属entity≠1)
2026-08-31 10:14:22 +08:00
Hermes CI Fix 076bd0dae0 feat(r1-touch): 建议分级+决策类推送+应用前预览 (alert不推送/同title防轰炸/preview对比) 2026-08-31 09:07:54 +08:00