fix(caliber): 流动/速动比率改「倍」口径 + 判定层同量纲直判(ASK-20260924-11 任总放行)
ci/woodpecker/push/main Pipeline failed
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:
@@ -1,16 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""比率类 KPI 单位口径归一(取数层) —— 财务口径裁定 裁定2a
|
||||
"""比率类 KPI 单位口径归一(取数层) —— 口径裁定 20260917(2a) + 20260924(v2.1 取代两项)
|
||||
|
||||
依据:`wecom-finance/reports/CMA_口径裁定_20260917.md` 裁定2a
|
||||
· 比率类指标**统一为「百分比(%)」口径**:取数 ×100,与现有阈值(流动比率红 <150、速动 <80)同量纲;
|
||||
· 逐个比率类 KPI**核对定义与单位**,出具对照表;
|
||||
· **单位未核对前的「疑似错配」预警不推送**(降级待核,防假红灯 586/587)。
|
||||
依据(唯一有效出处):`wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md`
|
||||
—— 口径标识 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(任总 2026-09-24 16:38 放行 ASK-20260924-11)
|
||||
· **取代范围仅两项**:`F_CURRENT_RATIO` / `F_QUICK_RATIO` → 统一「倍」(无量纲),**判定层不再 ×100**
|
||||
(同量纲直判)、`kpi_definitions` 的 unit/阈值/target_value 同步改倍口径;历史 `kpi_values` **不重算**;
|
||||
· **其余比率类指标仍按 20260917 裁定2a(百分比 %)执行**:毛利率、净利率、资产负债率、渠补率、返利率、
|
||||
收入增长率等;本表内 `F_GROSS_MARGIN` 那行**保持 ×100 不动**;
|
||||
· **不得类推扩用**:再发现第三个「比率类单位/量纲矛盾」指标须**另行裁定**,不得以 v2.1 件为据类推改倍数;
|
||||
· 被取代件 `wecom-finance/reports/CMA_口径裁定_20260917.md`:其 2a 对其余比率类仍有效、2b 仍有效,
|
||||
**原件保留、一字节不改**。
|
||||
· 2a 的过渡纪律(单位未核对前的「疑似错配」预警不推送)继续生效。
|
||||
|
||||
问题实证(2026-09-17 只读侦查,entity 1 酣客):
|
||||
`kpi_definitions` 声明单位 = %,但**取数**以比率(倍)存储:流动比率 1.29 / 速动比率 0.54 / 毛利率 0.38,
|
||||
而阈值按百分数书写(<150 / <80 / <15)→ 跨 2 个数量级比较,判定不可信(09-09 的 586/587 即此形态)。
|
||||
**注意**:按裁定2a 归一后 流动比率 129% < 150 → **红是合法的**(「流动性偏紧」),586/587 不是假红灯、
|
||||
不作废;真正的错配处置是「未核对项不推送」。
|
||||
问题实证(2026-09-17 只读侦查 / 2026-09-24 v2.1 复核,entity 1 酣客):
|
||||
`kpi_definitions` 原声明单位 = %,但**取数**以比率(倍)存储:流动比率 1.29 / 速动比率 0.54 / 毛利率 0.38,
|
||||
而阈值按百分数书写(>=200 / >=100 / <15)→ 跨 2 个数量级比较,判定不可信(09-09 的 586/587 即此形态)。
|
||||
**v2.1 处置**:流动/速动两项按**倍**口径落地(unit='倍';绿>=2.0 / 黄>=1.0 / 红<1.0 与
|
||||
绿>=1.0 / 黄>=0.5 / 红<0.5)⇒ 2026-08 的 1.29 → **黄**、0.54 → **黄**;
|
||||
本模块对这两项**不再 ×100**(旧写法 `1.29×100=129` 去比 `>=2.0` 会**恒绿**,比原「恒红」更坏)。
|
||||
其余百分数类(返利率 48.44、收入增长率 -82.2、资产负债率 39.9、费用率 30.0、渠补率 75.0 …)
|
||||
**取数已是百分数**,不得再 ×100(反过来会成为量级越界)。
|
||||
|
||||
@@ -18,8 +25,9 @@
|
||||
· 归一因子表 `RATIO_CALIBER_TABLE`(kpi_code → 口径/因子/证据/是否已核对);
|
||||
· 判定侧只调 `resolve_caliber(actual, kpi)`,**不自行做字符串/量级判断**;
|
||||
· 表外且命中「疑似错配形态」的 KPI → status='dimension_suspect' → 调用方**不落预警、不推送**;
|
||||
· `norm_factor` 只影响**判定/推送口径**,不改 `kpi_values` 存储值、不改 `kpi_definitions` 阈值
|
||||
(裁定2a 明确:统一取数而非改阈值)。
|
||||
· `norm_factor` 只影响**判定/推送口径**:2a 项不改 `kpi_values` 存储值、不改 `kpi_definitions` 阈值
|
||||
(统一取数);v2.1 取代的两项反之——**DB 的 unit/阈值已同步改倍口径**,故此处必须同量纲直判(factor=1):
|
||||
**两侧只改一侧 ⇒ 恒红(未改本表)或恒绿(未改 DB)**。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -31,21 +39,22 @@ from app.utils.threshold_parser import parse_clauses
|
||||
FACTOR_PERCENT = 100
|
||||
FACTOR_IDENTITY = 1
|
||||
|
||||
#: 比率类 KPI 单位对照表(裁定2a「逐个核对定义与单位」的落地产物)
|
||||
#: norm_factor=100 → 取数为比率(倍),判定前 ×100 归一到百分数口径
|
||||
#: norm_factor=1 → 取数已是百分数/同量纲,直接判定
|
||||
#: 比率类 KPI 单位对照表(裁定2a「逐个核对定义与单位」的落地产物;2026-09-24 v2.1 取代其中两项)
|
||||
#: norm_factor=1 → 取数已是百分数/同量纲,直接判定(含 v2.1 两项「倍」口径:取数与阈值同量纲)
|
||||
#: norm_factor=100 → 取数为比率(倍),判定前 ×100 归一到百分数口径(裁定2a;**本表仅毛利率仍用**)
|
||||
RATIO_CALIBER_TABLE = {
|
||||
# ---- 需归一(取数=倍,阈值=百分数写法)----
|
||||
# ---- 倍口径(v2.1 @2026-09-24 取代 2a 之两项;取数与 DB 阈值同量纲 ⇒ 不再归一)----
|
||||
"F_CURRENT_RATIO": {
|
||||
"name": "流动比率", "declared_unit": "%", "caliber": "百分比(%)",
|
||||
"norm_factor": FACTOR_PERCENT, "verified": True,
|
||||
"evidence": "2026-08 实测 1.29(比率量级)→ ×100 = 129%;阈值 <150 为百分数写法(裁定2a 明示示例:129<150 → 红,与「流动性偏紧」一致)",
|
||||
"name": "流动比率", "declared_unit": "倍", "caliber": "倍(无量纲)",
|
||||
"norm_factor": FACTOR_IDENTITY, "verified": True,
|
||||
"evidence": "口径 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(取代 2a 之 F_CURRENT_RATIO):unit=倍 / 绿>=2.0 / 黄>=1.0 / 红<1.0;2026-08 实测 1.29(倍)→ 同量纲直判 = 黄;不再 ×100(旧 129% 判红已作废)",
|
||||
},
|
||||
"F_QUICK_RATIO": {
|
||||
"name": "速动比率", "declared_unit": "%", "caliber": "百分比(%)",
|
||||
"norm_factor": FACTOR_PERCENT, "verified": True,
|
||||
"evidence": "2026-08 实测 0.54 → ×100 = 54%;阈值 <80 为百分数写法(经验值 1:1=100%)",
|
||||
"name": "速动比率", "declared_unit": "倍", "caliber": "倍(无量纲)",
|
||||
"norm_factor": FACTOR_IDENTITY, "verified": True,
|
||||
"evidence": "口径 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(取代 2a 之 F_QUICK_RATIO):unit=倍 / 绿>=1.0 / 黄>=0.5 / 红<0.5;2026-08 实测 0.54(倍)→ 同量纲直判 = 黄;经验值 1:1 = 1.0 倍",
|
||||
},
|
||||
# ---- 需归一(取数=倍,阈值=百分数写法;裁定2a 对其余比率类仍有效,不得顺手改)----
|
||||
"F_GROSS_MARGIN": {
|
||||
"name": "毛利率", "declared_unit": "%", "caliber": "百分比(%)",
|
||||
"norm_factor": FACTOR_PERCENT, "verified": True,
|
||||
@@ -235,7 +244,7 @@ class _K:
|
||||
|
||||
|
||||
def _self_test() -> int:
|
||||
"""零库自证(裁定2a 证据):归一、同量纲直判、疑似错配降级、真实小值不误伤"""
|
||||
"""零库自证(裁定2a + 2026-09-24 v2.1):倍口径直判、2a 归一、疑似错配降级、真实小值不误伤"""
|
||||
from app.utils.threshold_parser import eval_threshold, LEVEL_PRIORITY
|
||||
|
||||
def level(actual, kpi):
|
||||
@@ -251,10 +260,10 @@ def _self_test() -> int:
|
||||
|
||||
cases = (
|
||||
# (说明, 实测, KPI, 期望)
|
||||
("流动比率 1.29 → 归一 129% vs 红<150", 1.29,
|
||||
_K("F_CURRENT_RATIO", "%", ">=200", ">=150", "<150"), "red"),
|
||||
("速动比率 0.54 → 归一 54% vs 红<80", 0.54,
|
||||
_K("F_QUICK_RATIO", "%", ">=100", ">=80", "<80"), "red"),
|
||||
("倍口径(v2.1) 流动比率 1.29 → 直判 1.29 ∈ [1.0,2.0) 黄", 1.29,
|
||||
_K("F_CURRENT_RATIO", "倍", ">=2.0", ">=1.0", "<1.0"), "yellow"),
|
||||
("倍口径(v2.1) 速动比率 0.54 → 直判 0.54 ∈ [0.5,1.0) 黄", 0.54,
|
||||
_K("F_QUICK_RATIO", "倍", ">=1.0", ">=0.5", "<0.5"), "yellow"),
|
||||
("毛利率 0.38 → 归一 38% vs 绿>=20", 0.38,
|
||||
_K("F_GROSS_MARGIN", "%", ">=20", ">=18", "<15"), "green"),
|
||||
("返利率 48.44 已百分数 → 红>12(不 ×100)", 48.44,
|
||||
|
||||
@@ -18,7 +18,10 @@ from app.database import get_session_local
|
||||
from app.models import KPIDefinition, KPIValue, KPIAlert, ActionPlan, OperationLog
|
||||
from app.utils.oplog import log_op
|
||||
from app.utils.threshold_parser import eval_threshold, LEVEL_PRIORITY
|
||||
# 裁定2a(2026-09-17 财务口径):比率类指标统一「百分比(%)」口径(取数×100),
|
||||
# 口径双轨(唯一事实源 app/utils/caliber_units.RATIO_CALIBER_TABLE):
|
||||
# · `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`(wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md):
|
||||
# `F_CURRENT_RATIO` / `F_QUICK_RATIO` 改「倍」(无量纲、同量纲直判,**不再 ×100**);
|
||||
# · 裁定2a(2026-09-17 财务口径,wecom-finance/reports/CMA_口径裁定_20260917.md):其余比率类仍「百分比(%)」(取数×100);
|
||||
# 未核对/疑似单位错配的项降级待核(不落预警、不推送)
|
||||
from app.utils.caliber_units import resolve_caliber
|
||||
# 裁定 R1/R2/R3(2026-09-19 财务口径):账套范围(demo 排除)/ 期间窗口 / 规整月期间
|
||||
@@ -142,11 +145,13 @@ def run_alert_check(db_session, period: Optional[str] = None, dry_run: bool = Fa
|
||||
stats: Optional[dict] = None, now: Optional[datetime] = None) -> int:
|
||||
"""检查所有KPI的实际值是否触发预警,返回(将)新生成的预警数
|
||||
|
||||
2026-09-17 财务口径裁定 2a/2b:
|
||||
2026-09-17 财务口径裁定 2a/2b(2a 的两项已由 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24` 取代为倍口径):
|
||||
· **期间(2b)**:period=None → 每个 KPI 回落「最近可用期间」(自己的最新 YYYY-MM,
|
||||
排除 H1/H2 伪期间),不再硬编码当月(当月无值 → 永久 0 新预警);
|
||||
· **单位(2a)**:判定前经 resolve_caliber() 归一到「百分比(%)」口径(取数×100,
|
||||
与阈值 150/80 同量纲);**未核对/疑似单位错配**的项降级待核 → 不落预警、不推送;
|
||||
· **单位口径**:判定前经 resolve_caliber()(唯一事实源 `RATIO_CALIBER_TABLE`)——
|
||||
`F_CURRENT_RATIO`/`F_QUICK_RATIO` 按 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24` **倍口径同量纲直判**
|
||||
(factor=1,与 DB 新阈值 2.0/1.0 与 1.0/0.5 同量纲);其余比率类仍按裁定2a 归一到「百分比(%)」(取数×100);
|
||||
**未核对/疑似单位错配**的项降级待核 → 不落预警、不推送;
|
||||
· dry_run=True → 只计算不写库(抽查复核与放行前证据用)。
|
||||
|
||||
2026-09-19 财务口径裁定 R1/R2/R3(`alert_entity_scope` 单一事实源):
|
||||
@@ -232,7 +237,7 @@ def run_alert_check(db_session, period: Optional[str] = None, dry_run: bool = Fa
|
||||
st["no_value"] += 1
|
||||
continue
|
||||
|
||||
# 单位口径(裁定2a):取数归一 + 疑似错配降级门
|
||||
# 单位口径(v2.1 倍口径直判 / 2a 归一):resolve_caliber 单一入口 + 疑似错配降级门
|
||||
cal = resolve_caliber(latest.actual_value, kpi)
|
||||
if cal["status"] == "dimension_suspect":
|
||||
st["dimension_suspect"] += 1
|
||||
@@ -410,7 +415,7 @@ def generate_and_push(db_session, period: Optional[str] = None, dry_run: bool =
|
||||
now: Optional[datetime] = None) -> dict:
|
||||
"""生成预警并推送,返回统计
|
||||
|
||||
2026-09-17 财务口径裁定 2a/2b:
|
||||
2026-09-17 财务口径裁定 2a/2b(2a 的两项已由 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24` 取代为倍口径):
|
||||
· **period=None** → 判定期间 = 各 KPI「最近可用期间」(裁定2b,不再硬编码当月);
|
||||
显式给出 period → 固定该期间(抽查复核用);
|
||||
· **dry_run=True** → 零写入、零推送:返回将生成的预警清单与口径证据(放行前抽查复核)。
|
||||
@@ -490,7 +495,7 @@ def generate_and_push(db_session, period: Optional[str] = None, dry_run: bool =
|
||||
"push_skipped_no_channel": push_skipped_no_channel,
|
||||
"cleaned_green": cleaned, "rule_engine": rule_stats,
|
||||
"coverage": coverage,
|
||||
# 裁定2a 口径证据
|
||||
# 口径证据(v2.1 倍口径直判 / 2a 归一;唯一事实源 caliber_units.RATIO_CALIBER_TABLE)
|
||||
"normalized": st.get("normalized", 0), "normalized_kpis": st.get("normalized_kpis", []),
|
||||
"dimension_suspect": st.get("dimension_suspect", 0), "suspect_kpis": st.get("suspect_kpis", []),
|
||||
# 期间/取值覆盖明细
|
||||
@@ -550,7 +555,7 @@ def coverage_note(result: dict) -> str:
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser(description="预警生成与推送(2026-09-17 口径裁定 2a/2b)")
|
||||
ap = argparse.ArgumentParser(description="预警生成与推送(口径:2a + 2026-09-24 v2.1 取代两项)")
|
||||
ap.add_argument("--dry-run", action="store_true", help="只计算不写库不推送(抽查复核/放行前证据)")
|
||||
ap.add_argument("--period", default=None, help="固定判定期间 YYYY-MM(缺省=各 KPI 最近可用期间,裁定2b)")
|
||||
ap.add_argument("--json", action="store_true", help="JSON 输出完整证据(含将生成的预警清单)")
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""比率类 KPI 单位对照表(只读)—— 财务口径裁定 裁定2a 的产出器
|
||||
"""比率类 KPI 单位对照表(只读)—— 口径 = 裁定2a + 2026-09-24 v2.1(取代两项)
|
||||
|
||||
依据:`wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md`
|
||||
(`CMA-CALIBER-v2.1/kpi-unit@2026-09-24`)—— 取代 `wecom-finance/reports/CMA_口径裁定_20260917.md`
|
||||
§2a 中的 `F_CURRENT_RATIO` / `F_QUICK_RATIO` **两项**(改「倍」、无量纲直判、不再 ×100);
|
||||
其余比率类指标仍按 20260917 裁定2a(百分比 %)执行。
|
||||
|
||||
用途:逐个比率类 KPI 核对「定义 / 声明单位 / 阈值量纲 / 实测值量纲」,出对照表。
|
||||
两类口径在表里分栏可见:`factor=1` + `unit=倍` = 倍口径同量纲直判;`factor=100` = 裁定2a 百分比归一。
|
||||
判定侧的事实源是 `app/utils/caliber_units.RATIO_CALIBER_TABLE`(本脚本只核对与呈现,
|
||||
不写库、不改阈值)。
|
||||
|
||||
@@ -80,7 +86,7 @@ def main():
|
||||
print(json.dumps(out, ensure_ascii=False, indent=1, default=str))
|
||||
return 0
|
||||
|
||||
print("=== 比率类 KPI 单位对照表(只读;裁定2a)===")
|
||||
print("=== 比率类 KPI 单位对照表(只读;口径 = 2a + v2.1 取代两项)===")
|
||||
print("%-4s %-8s %-22s %-6s %-10s %-12s %-8s %-6s %s" % (
|
||||
"eid", "code", "name", "unit", "latest", "actual", "status", "factor", "thresholds"))
|
||||
for x in out:
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""阈值单位口径修正 + 量纲守卫装配(迁移脚本·准备态)
|
||||
|
||||
⚠️ **已被取代,不得执行(2026-09-17)**:
|
||||
⚠️ **已被取代,不得执行(2026-09-17 首次判定;2026-09-24 复判并补新锚)**:
|
||||
本脚本基于 2026-09-16 的口径草案(**把阈值改成「倍」写法** >=2.0/>=1.0/<1.0)。
|
||||
2026-09-17 财务**正式裁定**(`wecom-finance/reports/CMA_口径裁定_20260917.md` 裁定2a)
|
||||
采用**相反方向**:比率类统一「百分比(%)」口径、**阈值保持不变**(150/80),
|
||||
在**取数层** ×100 归一(实现:`app/utils/caliber_units.py` + `scripts/alert_generator.py`)。
|
||||
⇒ 执行本脚本会把口径反向切回「倍」,与现行裁定冲突;假红灯 586/587 在新口径下
|
||||
**是合法红灯**(流动比率 1.29=129% < 150,与「流动性偏紧」一致),**不得作废**。
|
||||
保留本文件仅为审计链(历史草案)——如未来另有阈值单位新裁定,须先改 `PLAN` 与
|
||||
`--resolve-false-reds` 的语义再执行。
|
||||
**状态更新(2026-09-24)**:
|
||||
· 现行有效裁定 = `wecom-finance/reports/CMA_口径裁定_20260924_比率类单位口径_取代2a两指标.md`
|
||||
(口径标识 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24`;任总 2026-09-24 16:38 放行 ASK-20260924-11)。
|
||||
该件**只取代** `CMA_口径裁定_20260917.md` §2a 中的 **`F_CURRENT_RATIO` / `F_QUICK_RATIO`**
|
||||
两项(改「倍」、无量纲直判);**其余比率类仍按 20260917 裁定2a(百分比 %)执行**。
|
||||
· 本脚本 `PLAN` 里这两项的「倍」方向**与 v2.1 一致**,但**已由 ASK-20260924-11 单以受控路径落地**
|
||||
(DB 写经受控脚本 + 判定层 `app/utils/caliber_units.py` 同量纲直判 + 代码测试发布),
|
||||
**不得再执行本脚本**;本脚本 `PLAN` 另含 **`F_DEBT_RATIO`(不在 v2.1 取代范围内)**,
|
||||
执行会越界改动他项 ⇒ 仅保留作审计链(历史草案)。
|
||||
· `--resolve-false-reds` 的旧语义(586/587 按「口径错误」**作废**)**已作废**:v2.1 下该两条按
|
||||
**口径变更改判**(1.29 倍 → 黄、0.54 倍 → 黄),留痕写明「口径变更所致」,**不删历史行**。
|
||||
· 如未来另有阈值单位新裁定,须先改 `PLAN` 与 `--resolve-false-reds` 的语义再执行。
|
||||
|
||||
原依据:财务口径裁定 finance-20260916-cma-caliber-ruling.md v1.0 §四(裁定③)+ §3.3/§3.4
|
||||
· 4.1 单位规范:偿债能力比率=**倍**(无量纲);资产负债率等=%;返利率=%
|
||||
|
||||
@@ -25,7 +25,9 @@ from scripts.alert_generator import generate_and_push, run_alert_check
|
||||
#: 固定「当月」= 2026-09 → 窗口 = 2026-04 ~ 2026-09(口径复算不依赖真实时钟)
|
||||
NOW = datetime(2026, 9, 19, 10, 0)
|
||||
|
||||
RED_EXPR = "<58.4" # 0<实测<10 比值形态不触发 dimension_suspect(见裁定2a 降级门)
|
||||
#: 降级门口径见 `app/utils/caliber_units.suspected_unit_mismatch`(阈值量级 <100 非百分数写法
|
||||
#: 且实测 14.45 非比率量级 ⇒ 不触发;与倍口径两项 F_CURRENT_RATIO/F_QUICK_RATIO 无关)
|
||||
RED_EXPR = "<58.4"
|
||||
|
||||
|
||||
def _ent(db, eid, status, name=None):
|
||||
|
||||
@@ -39,7 +39,10 @@ NOW = datetime(2026, 9, 22, 10, 0)
|
||||
PERIOD = "2026-09"
|
||||
|
||||
#: 阈值/实测形态沿用同族既有用例(test_alert_caliber_r1r4_20260919):
|
||||
#: 避免命中裁定2a「疑似单位错配」降级门(阈值百分数写法 + 实测比率量级)
|
||||
#: 形态前提按 `CMA-CALIBER-v2.1/kpi-unit@2026-09-24` + 裁定2a 复核后重写:
|
||||
#: 本用例阈值 `"<58.4"` **不是**百分数写法(量级 <100)、实测 14.45 **不是**比率量级(>10)
|
||||
#: ⇒ 按 `app/utils/caliber_units.suspected_unit_mismatch` 判据,**任何口径下都不触发降级门**;
|
||||
#: 与两个倍口径指标(F_CURRENT_RATIO / F_QUICK_RATIO,走对照表直判)无关。
|
||||
RED_EXPR = "<58.4"
|
||||
ACTUAL = 14.45
|
||||
|
||||
|
||||
@@ -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="万元",
|
||||
|
||||
Reference in New Issue
Block a user