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).
This commit is contained in:
@@ -21,6 +21,7 @@ from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
|
||||
from app.models import KPIHierarchy, KPISubjectMap, VoucherDetail, ScenarioSuggestion, KPIDefinition
|
||||
from app.utils.voucher_guard import apply_voucher_guard
|
||||
|
||||
logger = logging.getLogger("cma.alert_attribution")
|
||||
|
||||
@@ -134,6 +135,8 @@ def build_subject_attribution(db: Session, kpi_id: int, period: str) -> list:
|
||||
q = q.filter(VoucherDetail.subject_code == p["code"])
|
||||
if entity_id is not None:
|
||||
q = q.filter(VoucherDetail.entity_id == entity_id)
|
||||
# 作废/撤单标记过滤(2026-09-14 收口): 归因拆解与归集同口径
|
||||
q = apply_voucher_guard(db, q, VoucherDetail)
|
||||
row = q.first()
|
||||
credit_sum = float(row[0] or 0)
|
||||
debit_sum = float(row[1] or 0)
|
||||
|
||||
Reference in New Issue
Block a user