"""战略地图 · KPI 摘要接口(真数据 · 口径 CMA-A1 终稿,2026-10-19) 口径唯一真源:app/data/revenue_caliber.py(含科目映射与禁写清单) 本模块**不存任何金额常量**:全部对 voucher_details 现场聚合。 取数铁律: 1. 必经 `summary NOT LIKE '%结转%'` —— 否则损益双向抵销,收入恒为 0(历史踩坑); 2. 实体隔离:只取 token 绑定的 entity_id,不跨账套; 3. 无数据 ⇒ null + missing_reason,**不得兜底造数**; 4. 分业务线分列;禁净额;禁合并成单一「营业收入」(合计仅作可复算校验位)。 历史期缺同票折扣发票(四件套 0/4)⇒ 金额带 `data_status="estimated"` + `data_tag="测算/待票"`,不得静默按新式重算冒充实账。 """ from typing import Optional from fastapi import APIRouter, Depends from sqlalchemy import text from sqlalchemy.orm import Session from app.auth_middleware import require_auth from app.data import revenue_caliber as rc from app.database import get_db from app.deps import get_entity_id router = APIRouter( prefix="/api/cma/maps/summary", tags=["战略地图摘要"], dependencies=[Depends(require_auth)], ) MONTH_RE = "^[0-9]{4}-[0-9]{2}$" NOT_CARRY = "summary NOT LIKE '%结转%'" def _wan(v) -> Optional[float]: """元 → 万元(2 位),None 透传。""" if v is None: return None return round(float(v) / 10000.0, 2) def _period_window(db: Session, entity_id: int): """取该实体最新有凭证的会计月,窗口 = 当年 01 ~ 最新月(不写死日期)。""" latest = db.execute(text( f"SELECT MAX(period) FROM voucher_details " f"WHERE entity_id = :e AND period REGEXP :re AND {NOT_CARRY}" ), {"e": entity_id, "re": MONTH_RE}).scalar() if not latest: return None, None, None year = str(latest)[:4] return f"{year}-01", str(latest), year def _sum(db: Session, sql: str, params: dict): return db.execute(text(sql), params).scalar() @router.get("") def map_summary( entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db), ): """摘要条数据:分业务线营业收入 / 毛利率(新式)/ 管理指标 / 口径切换标识。""" cfg = rc.SUBJECT_MAP.get(entity_id) p0, p1, year = _period_window(db, entity_id) period_label = f"{p0}~{p1}" if p0 else None if cfg is None or p0 is None: return { "entity_id": entity_id, "period": {"from": p0, "to": p1, "label": period_label}, "revenue": { "available": False, "caliber": rc.CALIBER, "caliber_label": rc.CALIBER_LABEL, "unit": "万元", "lines": [], "missing_reason": "该账套无可用凭证期" if cfg else "未配置科目映射", }, "gross_margin": {"available": False, "value": None, "missing_reason": "无数据"}, "management": [], "switch": rc.CALIBER_SWITCH, "missing_docs": rc.MISSING_DOCS, } base = (f"entity_id = :e AND period >= :p0 AND period <= :p1 AND {NOT_CARRY}") params = {"e": entity_id, "p0": p0, "p1": p1} wine = rc.wine_sql("summary") rev_prefix = cfg["revenue"] + "%" # ── 1. 收入:主营科目按业务线分列(全额售价,禁净额)────────────────── revenue = { "available": False, "caliber": rc.CALIBER, "caliber_label": rc.CALIBER_LABEL, "unit": "万元", "period": period_label, "lines": [], } if cfg["lines"]: # 表 1:酒类线(keywords 命中) wine_net = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx AND {wine} """, {**params, "pfx": rev_prefix}) # 表 2:非酒类线(同一科目、不命中 keywords) other_net = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx AND NOT {wine} """, {**params, "pfx": rev_prefix}) # e1 => [酒类, 其他];e2 => [自营(IT)=非酒类, 代销(酒类)=酒类] values = {} if entity_id == 1: values = {"liquor": wine_net, "other": other_net} elif entity_id == 2: values = {"self_it": other_net, "agency_liquor": wine_net} lines = [] for ln in cfg["lines"]: raw = values.get(ln["key"]) lines.append({ "key": ln["key"], "label": ln["label"], "amount": _wan(raw), "unit": "万元", "data_status": "verified" if raw is not None else "missing", "missing_reason": None if raw is not None else "该业务线本期无入账凭证", }) total = _wan((wine_net or 0) + (other_net or 0)) if (wine_net is not None or other_net is not None) else None revenue = { **revenue, "available": any(l["amount"] is not None for l in lines), "lines": lines, "total": total, "total_label": "合计(各线相加,仅作复算校验)", "merge_forbidden": True, "note": "各业务线全额售价分列;禁净额、禁合并为单一「营业收入」", } # ── 2. 毛利率:新式 (收入净额 − 折扣后净进价成本) / 收入净额 ──────────── gross_margin = {"available": False, "value": None, "missing_reason": "无数据"} rev_total = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx """, {**params, "pfx": rev_prefix}) cost_total = _sum(db, f""" SELECT SUM(debit_amount - credit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx """, {**params, "pfx": cfg["cost"] + "%"}) disc_total = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx """, {**params, "pfx": cfg["purchase_discount"] + "%"}) if rev_total and float(rev_total) != 0 and cost_total is not None: net_cost = float(cost_total) - float(disc_total or 0) gm = (float(rev_total) - net_cost) / float(rev_total) * 100.0 gross_margin = { "available": True, "value": round(gm, 2), "unit": "%", "caliber": "net_purchase", "caliber_label": "新式·折扣后净进价", "formula": "(收入净额 − 折扣后净进价成本) / 收入净额 × 100", "period": period_label, # 四件套 0/4 ⇒ 历史期缺同票折扣发票,只可显示现值并带标识 "data_status": "estimated", "data_tag": "测算/待票", "note": "旧式(含 k 的 5001/571103 组合)已作废;历史缺票期不得冒充实账", } # ── 3. 管理指标(非会计科目,不进损益、不进摘要条主位)─────────────── management = [] if entity_id == 2: # 与 A1 回执可复现口径一致:分母取**折让前贷方发生额**(639,944.00), # 而不是 6603 净额(556,279.15,已抵扣借方)——两者差 28.24% 与 24.55% 之别。 disc = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx AND {wine} """, {**params, "pfx": cfg["purchase_discount"] + "%"}) base_amt = _sum(db, f""" SELECT SUM(credit_amount - debit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE '2202%' AND summary LIKE '%采购收货%' AND {rc.supplier_sql('summary')} """, params) if disc is not None and base_amt and float(base_amt) != 0: management.append({ "key": "vendor_discount_dependency", "label": "厂商折扣依赖度", "value": round(abs(float(disc)) / float(base_amt) * 100.0, 2), "unit": "%", "numerator": round(abs(float(disc)), 2), "numerator_label": "酒类采购折扣额", "denominator": round(float(base_amt), 2), "denominator_label": "酒类采购额(含税)", "period": period_label, "tag": "管理指标·非会计科目", "caliber_note": "折扣额 ÷ 酒类采购额(含税);改造单 v4 §3 文字写「÷销售额」," "但 28.24% 的复现分母是采购额,按可复现口径披露,分母已单列可复核", }) elif entity_id == 1 and cfg.get("purchase_gift") and rev_total: gift = _sum(db, f""" SELECT SUM(debit_amount - credit_amount) FROM voucher_details WHERE {base} AND subject_code LIKE :pfx """, {**params, "pfx": cfg["purchase_gift"] + "%"}) if gift is not None and float(rev_total) != 0: val = round(abs(float(gift)) / float(rev_total) * 100.0, 2) management.append({ "key": "vendor_gift_dependency", "label": "厂商赠酒依赖度", "value": val, "unit": "%", "numerator": round(abs(float(gift)), 2), "numerator_label": "采购获赠额", "denominator": round(float(rev_total), 2), "denominator_label": "营业收入(同期)", "period": period_label, "tag": "管理指标·非会计科目", "abnormal": val > 100, "caliber_note": "获赠额 ÷ 同期营业收入;>100% 属异常,须数据侧核查(不作损益行)", }) return { "entity_id": entity_id, "period": {"from": p0, "to": p1, "label": period_label}, "revenue": revenue, "gross_margin": gross_margin, "management": management, "switch": rc.CALIBER_SWITCH, "missing_docs": rc.MISSING_DOCS, "caliber_source": { "status": rc.STATUS, "decided_by": rc.DECIDED_BY, "reason": rc.REASON, }, }