fix(cma): entity isolation batch4 (cash plan writes, budget ownership, read leaks, okr template)

P0-A cash.py: scope api_update_plan / api_delete_plan / api_complete_plan /
api_register_payment to session entity (cross-entity id -> 404, aligning with
the file's existing get_entity_id endpoints).
P0-B budget.py: write entity_id on BudgetPlan creation + require the KPI to
belong to the session entity; scope /comparison, /comparison/kpi/{id} and
/deviation-report reads to the session entity.
P1 notifications.py: explicit field whitelist (name/config/enabled) on
update_channel to close mass-assignment.
P1 read leaks scoped to session entity: bot_kpis list, data_quality check/stats
(KPI calibre only; quality-log table has no entity_id column), kpis causality
chain + associate-map (KPI and map ownership), dashboard my-kpis,
customer_dashboard list, kpi_governance audit, budget_generate candidates,
driver_budget history (+ entity_id on its write path), expenses stats
(+ entity_id on its write path).
P2 okr_templates.py: write entity_id on the StrategicMap created by apply.

tests: tests/test_cross_entity_isolation_batch4_20260921.py (30 cases;
RED before fix = 22 failed, GREEN after = 30 passed; full suite 1302 passed /
1 xfailed / 0 failed).
This commit is contained in:
Hermes CI Fix
2026-09-21 09:37:38 +08:00
parent 8c5853f64b
commit a0762b367f
14 changed files with 749 additions and 44 deletions
+7 -2
View File
@@ -5,6 +5,7 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role
from app.models import KPIDefinition, BudgetPlan, KPIValue, OperationLog
import json
@@ -75,12 +76,16 @@ def _calc_budget(kpi: KPIDefinition) -> dict:
def get_kpi_budget_candidates(
year: int = None,
db: Session = Depends(get_db),
entity_id: int = Depends(get_entity_id),
):
"""获取可用于生成预算的KPI列表,按类型分类"""
"""获取本账套可用于生成预算的KPI列表,按类型分类(账套隔离 2026-09-21)"""
if not year:
year = datetime.now().year
kpis = db.query(KPIDefinition).filter(KPIDefinition.status == "active").all()
kpis = db.query(KPIDefinition).filter(
KPIDefinition.status == "active",
KPIDefinition.entity_id == entity_id,
).all()
# 获取每个KPI的最新实际值
latest_values = {}