fix(cma): my-dashboard KPI 取数同期内主值优先(重复行不再被选中)
同一期间并存两行(主值 ledger/verified 与自动归集重复行 auto_collect/error)时, 原判据按 calculated_at 倒序会取到写入更晚的重复行;今日两行数值一致故不可见, 漂移后卡片会静默显示重复行值。故在月期分支的排序中加「主值优先」: verified > ledger > 其余,置于 period 倒序之后(跨期口径不变)。 范围: backend/app/api/dashboard.py 的 latest_kpi_value() 单函数(含 case 导入)。 无 DB 写入、不动采集器/派生函数/cron/口径定义/前端。 单号: project-20260918-kpi-row-priority
This commit is contained in:
committed by
Hermes CI Fix
parent
b4f94a12b3
commit
27ea7e7f53
@@ -1,7 +1,7 @@
|
||||
"""驾驶舱 API v2 — 支持时间区间"""
|
||||
from fastapi import APIRouter, Depends, Query, Request, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, or_
|
||||
from sqlalchemy import case, func, or_
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
@@ -580,13 +580,33 @@ def predict_kpis(db: Session = Depends(get_db), entity_id: int = Depends(get_ent
|
||||
# 季度汇总行/半年度预估值(forecast)当成"最新"。
|
||||
_MONTH_PERIOD_REGEX = r"^[0-9]{4}-(0[1-9]|1[0-2])$"
|
||||
|
||||
# 主值优先(2026-09-18 同期间重复行判据收口,项目单 project-20260918-kpi-row-priority)
|
||||
# 同一期间可能并存两行:①主值(source_type='ledger' / data_status='verified',结账/复核口径)
|
||||
# ②自动归集重复行(source_type='auto_collect',备注自述「与主值为同一事实重复行」)。
|
||||
# 原判据仅看写入时刻:重复行由复采批量写入、calculated_at 更晚 ⇒ 会被选中(实测 entity1
|
||||
# 2026-08 的 F_REVENUE/F_NET_PROFIT:2136/2127 auto_collect 写于 09-14,晚于主值 1822/2102)。
|
||||
# 今日两行数值一致(Δ=0)故用户不可见;一旦重复行与主值漂移,卡片会静默显示重复行的值。
|
||||
# 故在**同一期间内**加「主值优先」:verified(已复核)> ledger(账面主值)> 其余。
|
||||
# 注意:跨期间仍以 period 倒序为第一判据(不改变 d33b5e1 的期间口径——当期只有未复核行时,
|
||||
# 不会回退到上一期已复核行)。
|
||||
_MAIN_VALUE_PRIORITY = case(
|
||||
(KPIValue.data_status == "verified", 0),
|
||||
(KPIValue.source_type == "ledger", 1),
|
||||
else_=2,
|
||||
)
|
||||
|
||||
|
||||
def latest_kpi_value(db: Session, kpi_id: int) -> Optional[KPIValue]:
|
||||
"""该 KPI 的最新可用值:月期间优先,无月期间行时回退 calculated_at 倒序。"""
|
||||
"""该 KPI 的最新可用值:月期间优先(同期内主值优先),无月期间行时回退 calculated_at 倒序。"""
|
||||
monthly = (
|
||||
db.query(KPIValue)
|
||||
.filter(KPIValue.kpi_id == kpi_id, KPIValue.period.op("REGEXP")(_MONTH_PERIOD_REGEX))
|
||||
.order_by(KPIValue.period.desc(), KPIValue.calculated_at.desc(), KPIValue.id.desc())
|
||||
.order_by(
|
||||
KPIValue.period.desc(),
|
||||
_MAIN_VALUE_PRIORITY,
|
||||
KPIValue.calculated_at.desc(),
|
||||
KPIValue.id.desc(),
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if monthly is not None:
|
||||
|
||||
Reference in New Issue
Block a user