Files
cma-management/backend/app/data/revenue_caliber.py
T

110 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""战略地图 · 收入 / 毛利 / 管理指标口径(单一事实源,勿在别处写死)
口径来源(引用勿改):
/srv/hermes/mailbox/finance/pending-tasks/_reports/
project-20261018-strategy-map-data-a1-addendum-finance-v2-return.md
改造单 project-20261018-cma-strategy-map-ux-revamp-fullstack 增量 v4
终稿结论(2026-10-19):
- 陕西博海、陕西酣客 **均总额法(主要责任人)**;
- 增值税 = 销售货物 13%(账内 11 张凭证实测,6% 代理服务零证据);
- 上游披露的「累计口径」金额(非年度数)= 冲减采购成本/存货成本(唯一落位,**无「补贴收入/其他收益」行**),
本模块不产生该数、也不允许任何调用点拼出该串(千分位与裸整数两种写法均在禁列);
- F2 毛利率新式 = (收入净额 − 折扣后净进价成本) / 收入净额 × 100;
旧式(含 `k` 的 5001/571103 组合)作废,**不得在本文件或调用点复现**;
- 历史期缺同票折扣发票(四件套 0/4)⇒ 金额只作现值展示并带「测算/待票」标识。
本模块只放**口径与科目映射**,不存金额;金额一律由 API 对库内 voucher_details 现算
(filter: summary NOT LIKE '%结转%',否则损益双向抵销归零)。
"""
# ── 收入口径状态(v4 起为已闭合)──────────────────────────────────────────────
STATUS = "closed"
CALIBER = "total" # total / net / mixed
CALIBER_LABEL = "总额法(会计)· 增值税13%"
DECIDED_BY = "A1 终稿(project-20261018-strategy-map-data-a1-addendum-finance-v2-return.md)"
REASON = "两家均主要责任人 ⇒ 总额法;增值税销售货物 13% 实测,无税会差异"
# ── 按实体的科目映射(各账套科目体系不同:酣客 5001/5401,博海 6001/6401)──────
SUBJECT_MAP = {
1: {
"revenue": "5001", # 主营业务收入
"cost": "5401", # 主营业务成本
"purchase_discount": "530111", # 商品折扣(采购)
"purchase_gift": "530103", # 采购获赠(捐赠收益)
"lines": [
{"key": "liquor", "label": "酒类"},
{"key": "other", "label": "其他"},
],
},
2: {
"revenue": "6001", # 主营业务收入
"cost": "6401", # 主营业务成本
"purchase_discount": "6603", # 财务费用-现金折扣(贷方=采购折扣)
"purchase_gift": None,
"lines": [
{"key": "self_it", "label": "自营(IT)"},
{"key": "agency_liquor", "label": "代销(酒类)"},
],
},
}
# 酒类判定(财务 A1 件同一口径):摘要含以下任一关键词 ⇒ 酒类业务线
WINE_KEYWORDS = ("酣客", "酒窖", "酣亲", "秦莉", "牛彦平", "横山")
# 采购来源(博海酒类采购:陕西酣客 / 酣客酒业)
LIQUOR_SUPPLIERS = ("酣客", "酒业")
def wine_sql(column: str = "summary") -> str:
"""SQL 片段:酒类业务线判定(与财务回执同口径,勿改)。"""
return "(" + " OR ".join(f"{column} LIKE '%{k}%'" for k in WINE_KEYWORDS) + ")"
def supplier_sql(column: str = "summary") -> str:
return "(" + " OR ".join(f"{column} LIKE '%{k}%'" for k in LIQUOR_SUPPLIERS) + ")"
# ── 口径切换期标识位(v4 §4:不硬编码日期;生效日以任总拍板 ASK 为准)──────────
CALIBER_SWITCH = {
"has_switch": True,
"label": "口径变更",
"effective_date": None, # 未拍板前恒为 None —— 禁止写死日期
"comparability": "口径变更·不可比",
"note": "同比/达成率/预算执行率在切换期内不可比;生效日以任总拍板(ASK)为准",
}
# ── 缺件披露(v4 §5:界面不得呈现为「已齐」)─────────────────────────────────
MISSING_DOCS = [
"经销/代理协议",
"厂家价格政策",
"逐笔折扣发票+合同审批单",
"上游退货条款",
"发票台账(invoice_check 空)",
]
def business_lines(entity_id: int) -> list:
cfg = SUBJECT_MAP.get(entity_id)
return list(cfg["lines"]) if cfg else []
def snapshot(entity_id: int) -> dict:
"""对外口径快照(无金额;金额见 /api/cma/maps/summary)。"""
return {
"entity_id": entity_id,
"status": STATUS,
"caliber": CALIBER,
"caliber_label": CALIBER_LABEL,
"decided_by": DECIDED_BY,
"reason": REASON,
"allow_single_total": False, # 分业务线分列;不合并成单一「营业收入」
"businesses": [
{"key": b["key"], "label": b["label"], "unit": "万元"}
for b in business_lines(entity_id)
],
"switch": CALIBER_SWITCH,
"missing_docs": MISSING_DOCS,
"note": "分业务线全额售价分列;禁净额、禁合并为单一「营业收入」",
}