2026-10-12 04:22:32 -04:00
|
|
|
|
"""计提公式纯函数(无 IO、无会话)。
|
|
|
|
|
|
|
|
|
|
|
|
契约(与 M3 核算、计算预览共用,口径见 2026-10-12 业务裁决):
|
|
|
|
|
|
- 参数值按**存储口径**传入(含单位:比例参数为 ‰ 数如 10、百分比参数为 % 数如 60)
|
|
|
|
|
|
- 计算出的中间比率(如毛利润率)为**纯比例**(Fraction,如 9/20)
|
|
|
|
|
|
- 金额用 Decimal、比率用 Fraction,全程禁 float;Fraction→Decimal 转换在
|
|
|
|
|
|
分母仅含 2/5 因子时精确,否则按 Decimal 上下文精度(28 位)
|
|
|
|
|
|
- 绩效 = 折算毛利润额 × 计提比例(乘法方向,对照表 #3,禁按"互为倒数"实现)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from datetime import date
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
from fractions import Fraction
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-12 08:24:17 -04:00
|
|
|
|
def to_decimal(value: Fraction) -> Decimal:
|
2026-10-12 04:22:32 -04:00
|
|
|
|
"""Fraction → Decimal。有限小数精确;循环小数按 Decimal 上下文精度(28 位)。"""
|
|
|
|
|
|
return Decimal(value.numerator) / Decimal(value.denominator)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-12 08:24:17 -04:00
|
|
|
|
# 模块内沿用私有名;公开别名供 service 层金额换算复用
|
|
|
|
|
|
_to_decimal = to_decimal
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-12 04:22:32 -04:00
|
|
|
|
def payment_days(ship_date: date, receipt_date: date) -> int:
|
|
|
|
|
|
"""回款天数:发货日首日计第 1 天(对照表 #1;需规「26 天」示例作废)。"""
|
|
|
|
|
|
return (receipt_date - ship_date).days + 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def salesperson_rate(
|
|
|
|
|
|
days: int,
|
|
|
|
|
|
*,
|
|
|
|
|
|
start: Fraction,
|
|
|
|
|
|
slope: Fraction,
|
|
|
|
|
|
floor: Fraction,
|
|
|
|
|
|
boundary_days: int,
|
|
|
|
|
|
) -> Fraction:
|
|
|
|
|
|
"""业务员计提比例(纯比例)。
|
|
|
|
|
|
|
|
|
|
|
|
start/floor 为 ‰ 数、slope 为 ‰/天(存储口径,slope 为精确分数 4/360)。
|
|
|
|
|
|
≤分界天数按「起始 − 天数×斜率」线性递减;超过分界天数固定取下限
|
|
|
|
|
|
(对照表 #4:360 天=6‰,361 天起固定 4‰)。
|
2026-10-14 06:58:30 -04:00
|
|
|
|
|
|
|
|
|
|
先款后货的回款(payment_days 为负)钳制为 0 天:负天数线性外插会算出
|
|
|
|
|
|
高于起始值的比例(多算绩效),钳到 0 天即按起始比例计提;payment_days
|
|
|
|
|
|
的出参仍照实输出负值(回款登记照常入库,录入异常由台账留痕暴露)。
|
2026-10-12 04:22:32 -04:00
|
|
|
|
"""
|
2026-10-14 06:58:30 -04:00
|
|
|
|
days = max(days, 0)
|
2026-10-12 04:22:32 -04:00
|
|
|
|
if days > boundary_days:
|
|
|
|
|
|
return floor / 1000
|
|
|
|
|
|
return (start - days * slope) / 1000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def profit_rate(profit: Decimal, revenue: Decimal) -> Fraction:
|
|
|
|
|
|
"""毛利润率(纯比例):毛利润额 / 销售额(**不含运费**口径,对照表 #2)。
|
|
|
|
|
|
|
|
|
|
|
|
销售额为 0 时返回 0(防除零;折算额随之为 0)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if revenue == 0:
|
|
|
|
|
|
return Fraction(0)
|
|
|
|
|
|
return Fraction(profit) / Fraction(revenue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def converted_profit(revenue: Decimal, rate: Fraction, threshold: Fraction) -> Decimal:
|
|
|
|
|
|
"""折算毛利润额(化简式):销售额 × (毛利润率 − 阈值)。
|
|
|
|
|
|
|
|
|
|
|
|
rate 为纯比例(profit_rate 的输出);threshold 为 % 数(存储口径,如 30)。
|
|
|
|
|
|
化简式等价于定义式「毛利润额 / 毛利润率 × (毛利润率 − 阈值)」,且避免
|
|
|
|
|
|
毛利润率为 0 时除零(对照表 #12/#13)。毛利润率低于阈值时结果为负 ——
|
|
|
|
|
|
业务确认负值正常计算、不归零。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return _to_decimal(Fraction(revenue) * (rate - threshold / 100))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def performance_amount(base: Decimal, rate: Fraction) -> Decimal:
|
|
|
|
|
|
"""绩效金额 = 折算毛利润额 × 计提比例(乘法方向,对照表 #3)。"""
|
|
|
|
|
|
return _to_decimal(Fraction(base) * rate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_barter(amount: Decimal, discount: Fraction) -> Decimal:
|
|
|
|
|
|
"""以物抵顶折算:应发绩效 × 折算比例(discount 为 % 数,默认 80,对照表 #24)。"""
|
|
|
|
|
|
return _to_decimal(Fraction(amount) * discount / 100)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def payment_weight(r: int) -> Fraction:
|
|
|
|
|
|
"""支付权重 R×R(纯比例):R 为回款占销售额百分比向下取整(对照表 #22)。
|
|
|
|
|
|
|
|
|
|
|
|
60 → 36/100;100 → 1(全额)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return Fraction(r, 100) * Fraction(r, 100)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def start_payment_ok(received: Decimal, revenue: Decimal, ratio: Fraction) -> bool:
|
|
|
|
|
|
"""起付判定:单项业务回款额达到销售额(不含运费)的 ratio%(对照表 #21)。"""
|
|
|
|
|
|
if revenue == 0:
|
|
|
|
|
|
return False
|
|
|
|
|
|
return Fraction(received) / Fraction(revenue) >= ratio / 100
|