feat(migration): V7 核算台账/月结/调整单四表与核算纯函数
- V7 四表:performance_ledgers(append-only 版本行,uk(receipt_id,calc_version) 库级防重兜底)/monthly_settlements(月份 uk,通过即锁定终态)/ settlement_items(按人固化可负工资,uk 三列)/adjustment_orders(TZ 单号) - registry +1:approval.adjustment.enabled(19 项,BOOL 首组扩至三开关) - formula 补核算纯函数:clamp_weight_r(从预览内联提取共用)/monthly_salary 三口径/probation_annual_note/manager_team_share(#12 暂口径:基数不含 经理个人户业绩,聚合集中一处答复后只改) - 迁移 downgrade 只保留 drop_table+alter 回滚+种子行反向 DELETE; performance_ledgers 表名复数(test_models 红线断言) - 断言同步:EXPECTED_TABLES 四新表/registry 19/BOOL 三开关/conftest 清理四新表与 ParamValue 边界 22 - schema_sync+全量单测 460 passed Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
30a179fc4c
commit
a5c86640e2
17 files changed
+1464
-51
No files matched your search
@@ -95,3 +95,63 @@ def start_payment_ok(received: Decimal, revenue: Decimal, ratio: Fraction) -> bo
|
||||
if revenue == 0:
|
||||
return False
|
||||
return Fraction(received) / Fraction(revenue) >= ratio / 100
|
||||
|
||||
|
||||
def clamp_weight_r(received: Decimal, revenue: Decimal) -> int:
|
||||
"""支付权重 R:回款占销售额百分比,封顶封底 [0, 100](对照表 #22)。
|
||||
|
||||
received 非负时 int() 即向下取整;min/max 同时是红字冲销(负数回款)
|
||||
接入时的方向防线。从计算预览的内联实现提取 —— ④ 核算与预览共用。
|
||||
"""
|
||||
if revenue <= 0:
|
||||
return 0
|
||||
return max(0, min(100, int(Fraction(received) / Fraction(revenue) * 100)))
|
||||
|
||||
|
||||
def monthly_salary(performance: Decimal, basic: Decimal | None, mode: str) -> Decimal | None:
|
||||
"""月工资分岗口径(对照表 #8,salary.basic.mode 按角色维度取值)。
|
||||
|
||||
- INCLUDE:绩效 + 基本工资(业务员默认——绩效之外照发基本工资)
|
||||
- DEDUCT:绩效 − 基本工资(经理默认——基本工资从绩效分成中扣除)
|
||||
- EXCLUDE:仅绩效,基本工资不参与
|
||||
basic 缺失时按 0 参与(INCLUDE/EXCLUDE 等价于纯绩效)。
|
||||
"""
|
||||
basic_value = basic if basic is not None else Decimal("0")
|
||||
if mode == "INCLUDE":
|
||||
return performance + basic_value
|
||||
if mode == "DEDUCT":
|
||||
return performance - basic_value
|
||||
if mode == "EXCLUDE":
|
||||
return performance
|
||||
raise ValueError(f"未知的月工资口径:{mode}")
|
||||
|
||||
|
||||
def probation_annual_note(year_total: Decimal, threshold: Decimal) -> str | None:
|
||||
"""试用期年累计说明:超阈值部分年底一次性结算(对照表 #17)。
|
||||
|
||||
未超阈值返回 None(无需说明);阈值按元展示(Decimal 直出)。
|
||||
"""
|
||||
if year_total <= threshold:
|
||||
return None
|
||||
excess = (year_total - threshold).quantize(Decimal("0.01"), rounding="ROUND_HALF_UP")
|
||||
return f"试用期绩效照算不发放;年累计超 {threshold} 元部分年底一次性结算(当前超 {excess} 元)"
|
||||
|
||||
|
||||
def manager_team_share(
|
||||
team_performance: Decimal,
|
||||
company_performance: Decimal,
|
||||
*,
|
||||
team_pct: Fraction,
|
||||
company_pct: Fraction,
|
||||
) -> Decimal:
|
||||
"""经理绩效 = 本团队业务员绩效 × 12.5% + 全公司业务员绩效 × 5%(裁决 #6/#15/#16)。
|
||||
|
||||
**#12 策略点(待业务答复,2026-10-16 暂定口径)**:基数**不含**经理本人
|
||||
名下经理个人户业务的业绩——该部分按经理个人户规则另行处理(单笔落台账
|
||||
rate=0/绩效 0,待答复后若需并入,只改调用方传入的基数集合)。
|
||||
团队口径:manager_id=该经理且岗位非「营销经理」的业务员绩效行之和;
|
||||
全公司口径:全部业务员(非经理岗)绩效行之和。
|
||||
"""
|
||||
return _to_decimal(Fraction(team_performance) * team_pct / 100) + _to_decimal(
|
||||
Fraction(company_performance) * company_pct / 100
|
||||
)
|
||||
@@ -290,6 +290,23 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
),
|
||||
sort_order=18,
|
||||
),
|
||||
ParamSpec(
|
||||
code="approval.adjustment.enabled",
|
||||
name="调整单审批开关",
|
||||
group="审批开关",
|
||||
value_type="BOOL",
|
||||
unit=None,
|
||||
dimension_type="NONE",
|
||||
options=None,
|
||||
default_value="0",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description=(
|
||||
"0=调整单提交即生效(系统自动通过留痕);1=走财务复核→领导审核审批流。"
|
||||
"调整单是已锁定月份补发/扣减的唯一通道"
|
||||
),
|
||||
sort_order=19,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -614,11 +614,8 @@ def preview(session: Session, payload: ParamPreviewIn) -> ParamPreviewOut:
|
||||
|
||||
start_ratio = take("payment.start.ratio")
|
||||
start_ok = formula.start_payment_ok(received, revenue, start_ratio)
|
||||
# R 按「回款占销售额百分比向下取整」口径封顶封底:received 非负时 int()
|
||||
# 即向下取整;min/max 同时是红字冲销(负数回款)接入时的方向防线
|
||||
weight_r = (
|
||||
max(0, min(100, int(Fraction(received) / Fraction(revenue) * 100))) if revenue > 0 else 0
|
||||
)
|
||||
# R 钳制已提取为纯函数 clamp_weight_r(④ 核算与预览共用同一实现)
|
||||
weight_r = formula.clamp_weight_r(received, revenue)
|
||||
weight = formula.payment_weight(weight_r)
|
||||
base_amount = after_barter if barter_applied else performance
|
||||
payable = to_decimal(Fraction(base_amount) * weight)
|
||||
|
||||
Reference in new issue
Block a user