fix(caliber): 流动/速动比率改「倍」口径 + 判定层同量纲直判(ASK-20260924-11 任总放行)
ci/woodpecker/push/main Pipeline failed

- caliber_units: F_CURRENT_RATIO/F_QUICK_RATIO 由 FACTOR_PERCENT(×100) 退场为 FACTOR_IDENTITY;declared_unit=倍 / caliber=倍(无量纲)/ evidence 改引 v2.1 锚;F_GROSS_MARGIN 保持 ×100 不动

- 引用面同步 7 文件:alert_generator / kpi_unit_crosscheck / migrate_...20260916 横幅 / 三个测试套件;self-test 两条改倍口径期望 yellow

口径: CMA-CALIBER-v2.1/kpi-unit@2026-09-24 取代 2a 之两项;其余比率类仍按裁定2a 百分比口径
This commit is contained in:
wecom-fullstack
2026-09-25 10:39:18 +08:00
parent c3ff345d0a
commit a8391a03da
7 changed files with 101 additions and 63 deletions
+24 -16
View File
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
"""财务口径裁定 2026-09-17 落地测试(裁定1 / 2a / 2b)
依据:`wecom-finance/reports/CMA_口径裁定_20260917.md`
依据:`wecom-finance/reports/CMA_口径裁定_20260917.md`(裁定1 / 2a / 2b)
+ `wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md`
(`CMA-CALIBER-v2.1/kpi-unit@2026-09-24`:**取代 2a 之 `F_CURRENT_RATIO` / `F_QUICK_RATIO` 两项**
→ 改「倍」、无量纲直判、不再 ×100;其余比率类仍按 2a 百分比口径)
· 裁定1:现金流「投资活动净额=0」= 本期无投资活动,**按 0 如实参与判定**(二维映射 + 标注);
· 裁定2a:比率类 KPI 统一「百分比(%)」口径(取数×100),未核对/疑似错配**不推送**;
· 裁定2a:其余比率类 KPI 统一「百分比(%)」口径(取数×100),未核对/疑似错配**不推送**;
· 裁定2b:预警期间由「当月硬编码」→「最近可用期间」(排除 2026-H1/H2 伪期间)。
"""
import pytest
@@ -65,14 +68,19 @@ class TestRuling1InvestingZero:
assert c["classified"] is False and "缺失" in c["reason"]
# ───────────────────────── 裁定2a:单位口径 ─────────────────────────
# ───────────── 单位口径:裁定2a(百分比)+ v2.1 取代两项(倍)─────────────
class TestRuling2aCaliber:
def test_current_ratio_normalized_to_percent(self):
cal = resolve_caliber(1.29, _K("F_CURRENT_RATIO", "%", ">=200", ">=150", "<150"))
assert cal["status"] == "normalized" and cal["factor"] == 100
assert round(cal["value"], 2) == 129.0
assert "口径归一" in cal["note"]
def test_current_ratio_multiple_caliber_identity(self):
"""`F_CURRENT_RATIO` 已按 v2.1 改「倍」口径 → **同量纲直判**(factor=1,不得再 ×100)。
反向量纲回归:只改 DB 不改判定层时 1.29×100=129 去比 `>=2.0` 会**恒绿**(比原「恒红」更坏);
此处即锁「不被 ×100」这一反向风险。
"""
cal = resolve_caliber(1.29, _K("F_CURRENT_RATIO", "倍", ">=2.0", ">=1.0", "<1.0"))
assert cal["status"] == "ok" and cal["factor"] == 1
assert cal["value"] == 1.29
assert cal["note"] is None, "倍口径直判无「口径归一」说明"
def test_percent_class_not_multiplied(self):
"""返利率/收入增长率等取数已是百分数 → 因子 1(再 ×100 会反向越界)"""
@@ -148,18 +156,18 @@ class TestGateAndDryRun:
assert st["dimension_suspect"] == 1 and st["suspect_kpis"]
assert db.query(KPIAlert).count() == 0
def test_listed_ratio_kpi_alert_uses_normalized_value(self, db: Session):
"""表内比率 KPI:1.29 → 129% 参与判定,且消息里带口径归一说明"""
kpi = create_test_kpi(db, kpi_code="F_CURRENT_RATIO", kpi_name="流动比率", unit="%",
threshold_green=">=200", threshold_yellow=">=150", threshold_red="<150")
def test_listed_ratio_kpi_alert_judged_in_multiple_caliber(self, db: Session):
"""v2.1 两项按「倍」口径**同量纲直判**:1.29 落 [1.0,2.0) → 黄,且消息不带「口径归一」"""
kpi = create_test_kpi(db, kpi_code="F_CURRENT_RATIO", kpi_name="流动比率", unit="倍",
threshold_green=">=2.0", threshold_yellow=">=1.0", threshold_red="<1.0")
db.add(KPIValue(entity_id=1, kpi_id=kpi.id, period="2026-08", actual_value=1.29))
db.commit()
st = {}
n = run_alert_check(db, period="2026-08", dry_run=True, stats=st)
assert n == 1 and st["planned"][0]["level"] == "red"
assert st["planned"][0]["actual"] == 129.0 and st["planned"][0]["factor"] == 100
assert "口径归一" in st["planned"][0]["message"]
assert st["normalized"] == 1
assert n == 1 and st["planned"][0]["level"] == "yellow"
assert st["planned"][0]["actual"] == 1.29 and st["planned"][0]["factor"] == 1
assert "口径归一" not in st["planned"][0]["message"]
assert st["normalized"] == 0
def test_dry_run_writes_nothing(self, db: Session):
kpi = create_test_kpi(db, kpi_code="T_DRY", kpi_name="dry", unit="万元",