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
This commit is contained in:
Hermes CI Fix
2026-09-14 18:12:38 +08:00
parent 45832cbf45
commit 8fa6c7f520
3 changed files with 551 additions and 2 deletions
+32 -2
View File
@@ -11,11 +11,15 @@
/usr/bin/python3 scripts/kpi_value_collector.py 2026-08 # 指定期间
/usr/bin/python3 scripts/kpi_value_collector.py 2026-08 5 # 指定期间+KPI
"""
import os
import sys
import logging
from datetime import datetime
from typing import Optional, Tuple
# 复核闸门开关(R2/R3,财务裁定 2026-09-14;KPI_REVIEW_GATE=off 可关闭,仅供测试)
_REVIEW_GATE_ENABLED = os.environ.get("KPI_REVIEW_GATE", "on").strip().lower() not in ("0", "off", "false", "no")
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
@@ -161,6 +165,9 @@ def _upsert_auto_value(db, mapping, period: str, value: float, remark: str) -> N
"""upsert kpi_values: 同 kpi_id+period 已有 auto_collect 记录则更新。
人工来源(excel/manual)行不匹配本查询 → 自动归集永不覆盖人工录入值。
复核闸门(财务裁定 2026-09-14,规则 R2/R3):落 pending 后**同事务**判定是否转正 /
归并 / 进例外;判定异常一律 fail-safe 保持 pending(N 条转正条件未证成就不转正)。
"""
existing = db.query(KPIValue).filter(
KPIValue.kpi_id == mapping.kpi_id,
@@ -172,8 +179,9 @@ def _upsert_auto_value(db, mapping, period: str, value: float, remark: str) -> N
existing.source_batch = _batch_no()
existing.remark = remark
existing.data_status = "pending"
kv = existing
else:
db.add(KPIValue(
kv = KPIValue(
entity_id=mapping.entity_id,
kpi_id=mapping.kpi_id,
period=period,
@@ -182,7 +190,29 @@ def _upsert_auto_value(db, mapping, period: str, value: float, remark: str) -> N
source_batch=_batch_no(),
data_status="pending",
remark=remark,
))
)
db.add(kv)
db.flush() # 取 id(幂等去重与 remark 留痕需要)
_review_gate(db, mapping, period, kv)
def _review_gate(db, mapping, period: str, kv) -> None:
"""R2/R3 复核闸门:同事务判定并落状态。任何异常都不阻断采集(保持 pending)。"""
if not _REVIEW_GATE_ENABLED:
return
try:
try:
from scripts.kpi_review_gate import review_auto_value
except ImportError:
from kpi_review_gate import review_auto_value
except ImportError as e:
logger.warning("复核闸门模块不可用(保持 pending): %s", e)
return
try:
review_auto_value(db, mapping, kv, period)
except Exception as e:
logger.warning("复核闸门判定异常 → 保持 pending (kpi=%s period=%s): %s",
mapping.kpi_id, period, e)
# ── 科目组合归集(P1③④ 激活 · 2026-09-14)─────────────────────────