# ============================================================================
# L1-主营业务收入算法规则.yaml（脱敏展示版）
#
# 用途：本文件是 L1 层唯一权威规则文档，同时服务两类读者——
#   1. 财务人员审计：核对口径、取数范围、剔除规则是否符合业务理解
#   2. 外部 AI Agent 复现：按"数据来源→取数规则→清洗→剔除→分桶→输出"链路独立复现
#
# 设计理念：业务人员读得懂（每个规则带业务解释），AI 解析得了（结构化 YAML），
#           改动可审计（每条规则可追溯、可验证）
# ============================================================================

层级: L1
名称: 主营业务收入
定义: 按服务月份归属的不含税主营业务收入
责任人: 项目经理 / 业务Owner
管理问题: 项目产生了多少经营收入
依据: 管理层裁决 · L1-L6利润阶梯

# ───────────────────────────────────────────── 数据来源
数据源:
  应收单:
    系统: ERP财务系统
    口径: 已剔除内部交易
    覆盖: 19个完整审核月
    行数: 数万行级
    关键字段:
      项目编码: 项目主键
      不含税金额: L1金额字段
      账单归属月: 业务归属期（valid time）
      审核日期: 系统确认时点（transaction time），取数闸门

  项目归属表:
    系统: 业务运营系统
    行数: 数千行
    用途: 项目 → 项目经理/签约销售工号，是部门归属链路的起点

# ───────────────────────────────────────────── 时间口径
时间口径:
  核心原则: >-
    审核日期(transaction time)与账单归属月(valid time)各司其职：
    审核日期只决定"这条单子什么时候进入数据池"，
    账单归属月只决定"这笔收入算进哪一期报表"。
    混用这两个字段是所有漏记/重记/倒改历史的根源。

  审核月封闭:
    系统事实: 反审核只能在审核当月内进行；跨月不可反审核
    推论: 审核月一旦结束，该月已审核记录永久冻结，不可再被改写
    铁律: 只取已完整结束的审核月，绝不取当月进行中数据
    收益: 池中每条记录均为不可变最终态，无需任何历史修正/重述机制

  累积池:
    机制: 只增不改。每期导出的增量追加入池，已有记录永不更新或覆盖
    去重Key: 单据编号
    异常处理: 出现重复单据编号时报错拦截并人工核查，不静默覆盖

  水位线:
    单位: 完整审核月（不用具体日期，避免边界歧义）
    规则: 每次取数推进到上一个完整审核月
    执行时点: 目标审核月完全结束之后

  出具时点:
    规则: T月报表在 T+1月完全结束之后出具
    原因1_业务: T月归属的单据大部分要到T+1月才审核完（实测83%的单据审核晚1个月）
    原因2_系统: T+1月的审核记录只有等T+1月结束后才永久冻结

# ───────────────────────────────────────────── 清洗
清洗规则:
  - 名称: 状态过滤
    规则: 只保留 单据状态 = "已审核"
    业务解释: 已审核为收入确认依据

  - 名称: 金额转数值
    规则: 金额字段由文本转数值，保留2位小数
    背景: 源数据86%金额存为字符串
    校验: 价税合计 = 不含税金额 + 税额，全量平衡，0条不一致

  - 名称: 归属月截断
    规则: 账单归属月由具体日期截断为 YYYY-MM
    背景: 字段名为"月"但值是具体日期，日分布散布1~31号

  - 名称: 项目编码映射
    规则: 按映射表将财务侧旧编码映射为业务侧标准编码，原值另存不覆盖
    背景: 同一项目两套系统编码不同，不映射则JOIN失败

  - 名称: 红冲即负数项目收入
    规则: 负数（红字/冲销）单据不剔除、不特殊处理，按其自身的审核月与归属月正常参与分期
    理由: >-
      前期报表已发出且不倒改，原单在其所属期已计入，
      红冲在其所属期计为负数，两期各自成立——无需追溯原单

  - 名称: 归属月异常预警
    规则: 归属月异常久远的记录仅预警，不修改、不剔除

# ───────────────────────────────────────────── 剔除
剔除规则:
  原则: 剔除不是删除，是分流。剔除记录必须落盘留痕，不得静默丢弃
  匹配方式: 按项目编码精确匹配（不用名称模糊匹配，名称可能变更）
  守恒校验: 主营业务收入 + 剔除明细金额 = 全量金额（每期必须成立）
  兜底规则: >-
    凡出现匹配失败的新编码，脚本必须报警提示人工判断，
    不允许自动归入，也不允许自动剔除

# ───────────────────────────────────────────── 归属链路
归属链路:
  主干原则: 全程只认一个部门归属，不存在双算，因此不需要任何去重逻辑
  链路: 应收单.项目编码 → 项目归属表.工号 → HR系统 → 部门
  兜底顺序: 项目经理工号优先；查无时改用签约销售工号

  工号规范化规则:
    背景: HR系统工号补零3-4位，业务系统不补零，格式不一致
    规则: 两侧工号统一去前导零后比对
    效果: 部门解析金额占比由 87.6% 提升至 99.7%
    冲突前提: 已验证全量工号中不存在"去零后同值的不同工号"，故无需冲突拦截

# ───────────────────────────────────────────── 报表期分桶
报表期分桶:
  核心公式: 偏移 = 审核月 - 归属月
  分配规则:
    - 条件: 偏移 <= 1（含负数，即未来期间）
      报表期: 归属月
      桶: A 当期收入
    - 条件: 偏移 >= 2
      报表期: 审核月 - 1
      桶: B 以前期间调整
  不漏不重保证: 每条单据偏移唯一，故必然落进且只落进一期（数学上封闭）

  做T月报表的筛选条件:
    A桶: 归属月 = T   且 审核月 <= T+1
    B桶: 审核月 = T+1 且 归属月 < T
  B桶呈现要求: 以前期间调整必须在报表中作为独立行展示，不混入当期经营性收入，不倒改已发出的历史报表

  实测分布（19个月全量验证）:
    A桶主体: 约83%（单据审核晚1个月的稳定业务节奏）
    B桶跨期: 约3%（最远回溯8个月）
  结论: T+1业务节奏在全量19个月中稳定成立

# ───────────────────────────────────────────── 校验
校验:
  - 名称: 金额守恒
    断言: 保留金额 + 剔除金额 = 全量金额
  - 名称: 聚合一致
    断言: 项目级汇总金额 = 清洗后保留金额
  - 名称: 分桶守恒
    断言: A桶 + B桶 = 池金额
  - 名称: 分桶互斥
    断言: 各桶记录无重叠，每条记录恰好分配到一期
  - 名称: 全域不漏不重
    断言: 各报表期L1加总 = 累积池金额
  - 名称: 出具条件阻断
    断言: 报表期T所需的审核月T+1数据不完整时，拒绝出具报表

# ───────────────────────────────────────────── 错误做法对比（实测）
错误做法对比:
  做法: 直接把"审核月=T"的导出当T月报表
  结果: 金额差异仅约1.5%，但成分完全错位——其中84%实为上月业务
  教训: 金额差异小不等于口径正确。口径错误是结构性的，差异大小与错误严重性无关
