- 部门汇总全公司 5% 按每位有台账聚合行的营销经理各计一次, 团队应发小计取聚合额 + 调整净额;与月结 total_payable 对拍 (有调整单时 + adjust_net_total),分列舍入差单列 - 人员归属按行类型拆分(普通/经理个人户/仅调整单),调整单全量并入, 团队与未分配不重复;按经理筛选只汇总展示团队 - 导出改单 sheet 分块签字稿,审计覆盖取数与超限;筛选页眉 ID→名称 - 月份校验抽 core/month,报表/月结/调整单非法月份统一 400; 岗位只读选项供财务/领导筛选 - 前端修复:部门下钻带入筛选、回款多桶独立状态、打印截断判定、 summary 请求序号保护 Co-Authored-By: Claude Code <noreply@anthropic.com>
80 lines
3.3 KiB
Python
80 lines
3.3 KiB
Python
"""报表统计元数据(区间桶、中文标签、导出上限)——叶子模块。
|
||
|
||
刻意不导入 app 内任何模块:report_service 与 report_export 都要消费这里的
|
||
定义,若它反向依赖 service 层会形成循环导入;也不走参数体系——FR-031
|
||
「区间可配置」一期以代码常量落地(参数化需注册表 + 迁移 + init-dev +
|
||
registry 测试四处同改,业务未提诉求前不做),配置缝收在
|
||
get_payment_day_buckets 单函数,未来在线可配时只改该函数。
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class DayBucket:
|
||
"""回款天数区间档位:min/max 均为 None 表示「无回款天数」兜底桶。"""
|
||
|
||
code: str
|
||
label: str
|
||
min_days: int | None
|
||
max_days: int | None
|
||
|
||
|
||
# 8 档区间(催办清单 R2 默认口径)。两个兜底桶必须保留:payment_days 可空
|
||
# (回款未指定发货批次)且可为负(先款后货照实负值),缺了它们这些行会
|
||
# 静默消失、金额对不上。
|
||
PAYMENT_DAY_BUCKETS: tuple[DayBucket, ...] = (
|
||
DayBucket("le0", "≤0 天(先款后货)", None, 0),
|
||
DayBucket("1-30", "1-30 天", 1, 30),
|
||
DayBucket("31-60", "31-60 天", 31, 60),
|
||
DayBucket("61-90", "61-90 天", 61, 90),
|
||
DayBucket("91-180", "91-180 天", 91, 180),
|
||
DayBucket("181-360", "181-360 天", 181, 360),
|
||
DayBucket("361+", "361 天以上", 361, None),
|
||
DayBucket("none", "无回款天数", None, None),
|
||
)
|
||
|
||
CUSTOMER_TYPE_LABELS: dict[str, str] = {
|
||
"personal": "个人户",
|
||
"manager_personal": "经理个人户",
|
||
"enterprise": "企业户",
|
||
}
|
||
|
||
# mixed 不在 core.constants.PRODUCT_CATEGORIES(那只是可维护的档案类别),
|
||
# 但台账行可以是 mixed(混合业务行级明细入 snapshot_json),报表必须能标它
|
||
PRODUCT_CATEGORY_LABELS: dict[str, str] = {
|
||
"municipal_water": "市政水利",
|
||
"rail_transit": "轨道交通",
|
||
"mixed": "混合业务",
|
||
}
|
||
|
||
# 业务状态中文标签:导出列、筛选页眉与前端标签共用同一份映射
|
||
BUSINESS_STATUS_LABELS: dict[int, str] = {1: "进行中", 2: "已完成", 3: "已终止"}
|
||
|
||
REPORT_TEMPLATE_VERSION = "V1.2"
|
||
# 导出超限即拒绝(需求「大数据量分批/超限提示」二选一取后者):普通
|
||
# Workbook 全量载入内存,2 万行约数十 MB,超过该量级必须引导按月导出
|
||
REPORT_EXPORT_MAX_ROWS = 20000
|
||
|
||
|
||
def get_payment_day_buckets() -> tuple[DayBucket, ...]:
|
||
"""区间档位唯一出口:前端下拉与分桶都以本函数返回为准,禁止硬编码。
|
||
|
||
未来「区间在线可配」时改此处读参数即可,调用方与前端零改动。
|
||
"""
|
||
return PAYMENT_DAY_BUCKETS
|
||
|
||
|
||
def bucket_of(payment_days: int | None) -> DayBucket:
|
||
"""把单笔回款天数落入对应区间桶;未匹配任何区间按「无回款天数」兜底。"""
|
||
if payment_days is None:
|
||
return next(b for b in PAYMENT_DAY_BUCKETS if b.code == "none")
|
||
for bucket in PAYMENT_DAY_BUCKETS:
|
||
if bucket.code == "none":
|
||
continue
|
||
lower_ok = bucket.min_days is None or payment_days >= bucket.min_days
|
||
upper_ok = bucket.max_days is None or payment_days <= bucket.max_days
|
||
if lower_ok and upper_ok:
|
||
return bucket
|
||
return next(b for b in PAYMENT_DAY_BUCKETS if b.code == "none")
|