feat(ui): 面向客户的文案中文化
系统由财务与营销人员日常使用,不是给开发者看的。本批把会渲染到页面上的 文本统一改成业务语言: - param_registry 的 22 条参数说明重写为「管什么、钱怎么变、何时该调它」, 去掉英文枚举名、需求编号、内部阶段号与精度约束 - 前端补 formatByCode:变更记录、版本历史、快照对比只给裸 value_text, 需回目录按编码补 value_type 与 unit 才能翻译枚举,否则这些位置仍显示 INCLUDE/FULL/1;编辑弹窗 BOOL 改为开启/关闭下拉 - 后端错误文案:模板报错用中文模板名并给出下一步,产品类别不再甩 municipal_water / rail_transit - 台账与预览落库的计提说明口语化(rate_note / strategy_note / 聚合行 note), 去掉「基数开关=1」「#12 参数化」「9.8 方案公式」 分数原文 11/1125 与「聚合:20%+5%」不动——那是金额口径的可复核原文, 不是说明文案。init-dev.sql 种子 description 与 registry 同源。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
111ac5425a
commit
61965e0b60
9 files changed
+164
-104
No files matched your search
@@ -60,7 +60,7 @@ from app.services.param_service import parse_fraction, resolve_snapshot
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# snapshot_json 里的策略点注记(口径检索锚点;#12 已参数化,开关 0 时留此注记)
|
||||
_NOTE_MANAGER_PENDING = "经理个人户单笔不计提(基数开关=0):聚合口径在月结时由团队聚合计算"
|
||||
_NOTE_MANAGER_PENDING = "经理个人户单笔不计提(未计入分成基数):绩效在月结时按团队与全公司分成计算"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -544,7 +544,7 @@ def _personal_rate(
|
||||
if customer_type == "enterprise":
|
||||
# 存储口径 ‰ → 纯比例(漏换算放大 1000 倍,与预览同防线)
|
||||
rate = parse_fraction(snapshot.get("enterprise.rate")) / 1000
|
||||
return _RateDecision(rate, "企业户固定计提比例", None, False)
|
||||
return _RateDecision(rate, "企业户:不按回款天数,统一按固定比例计提", None, False)
|
||||
|
||||
template = role.calc_template if role else "TPL_SALESPERSON_DECAY"
|
||||
if template == "TPL_MANAGER_SHARE":
|
||||
@@ -554,7 +554,7 @@ def _personal_rate(
|
||||
if snapshot.get("manager.personal.included") != "1":
|
||||
return _RateDecision(
|
||||
Decimal("0"),
|
||||
"经理个人户:12.5%+5% 聚合计提(月结时计算),单笔落 0",
|
||||
"经理个人户:绩效按团队与全公司分成在月结时计算,单笔不计提",
|
||||
None,
|
||||
True,
|
||||
)
|
||||
@@ -563,12 +563,12 @@ def _personal_rate(
|
||||
floor = parse_fraction(snapshot.get("salesperson.rate.floor"))
|
||||
boundary = int(parse_fraction(snapshot.get("salesperson.days.boundary")))
|
||||
rate = salesperson_rate(days, start=start, slope=slope, floor=floor, boundary_days=boundary)
|
||||
note = f"经理个人户(基数开关=1):按业务员递减 {start}‰ 起,{days} 天"
|
||||
note = f"经理个人户(已开启计入分成基数):按业务员递减,回款 {days} 天,起始 {start}‰"
|
||||
return _RateDecision(rate, note, None, False)
|
||||
if template == "TPL_INFO_SALES_FIXED":
|
||||
# 信息员基数=本人销售额(覆盖折算与阈值),‰ 换算同防线
|
||||
rate = parse_fraction(snapshot.get("info.rate")) / 1000
|
||||
return _RateDecision(rate, "信息员:本人销售额 × 计提比例", None, False)
|
||||
return _RateDecision(rate, "信息员:按本人销售额乘计提比例,不随回款天数递减", None, False)
|
||||
if template == "TPL_SALESPERSON_DECAY":
|
||||
start = parse_fraction(snapshot.get("salesperson.rate.start"))
|
||||
slope = parse_fraction(snapshot.get("salesperson.rate.slope"))
|
||||
@@ -577,9 +577,16 @@ def _personal_rate(
|
||||
# 经 Fraction 转换再取整(INT 型参数入库时已校验分母为 1,取整无损)
|
||||
boundary = int(parse_fraction(snapshot.get("salesperson.days.boundary")))
|
||||
rate = salesperson_rate(days, start=start, slope=slope, floor=floor, boundary_days=boundary)
|
||||
note = f"业务员递减:{start}‰ 起,{days} 天(分界 {boundary} 天,超界取 {floor}‰)"
|
||||
note = (
|
||||
f"回款 {days} 天,从起始 {start}‰ 按日递减"
|
||||
f"(满 {boundary} 天后固定按 {floor}‰ 计算)"
|
||||
)
|
||||
return _RateDecision(rate, note, None, False)
|
||||
raise BadRequestError(f"公式模板 {template} 暂不支持核算")
|
||||
# 提示给模板中文名而非编码:业务人员在岗位角色页看到的是「营销经理·团队与全公司分成」
|
||||
from app.services.calc_templates import TEMPLATES
|
||||
|
||||
tpl_name = TEMPLATES[template].name if template in TEMPLATES else template
|
||||
raise BadRequestError(f"「{tpl_name}」不支持单笔核算,请检查该人员的岗位设置")
|
||||
|
||||
|
||||
def _role_of(session: Session, person):
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
注册表是**迁移种子与运行期校验的唯一来源**:编码、默认值、取值域在此定义
|
||||
一次;新增参数 = 改这里 + 生成迁移种子(两者一致性由测试锁定)。
|
||||
值一律以 Fraction 可解析字符串表示(如 "4/360",禁 0.011111 —— 见算法红线)。
|
||||
|
||||
`description` 会直接显示在「计提参数」页的参数名下方,**面向业务人员撰写**:
|
||||
说清这个参数管什么、钱怎么变、什么时候该调它。不写英文枚举名、需求编号、
|
||||
内部阶段号,也不写实现层面的精度约束(那些留在代码注释与测试里)。
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
@@ -43,7 +47,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="10",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="线性递减段起始值:比例 = 起始 − 回款天数 × 斜率(≤分界天数)",
|
||||
description="回款当天能拿到的最高计提比例。回款每晚一天,按日递减斜率往下走,直到分界天数为止。",
|
||||
sort_order=1,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -57,7 +61,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="4/360",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="精确分数 4/360‰/天(=1/90),禁止按 0.011111 截断存储",
|
||||
description="回款每延后一天,计提比例下降多少。按分数填写(如 4/360)可保持精确,填小数会引入四舍五入误差。",
|
||||
sort_order=2,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -71,7 +75,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="4",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="回款天数超过分界天数后固定取此比例(≥361 天 = 4‰)",
|
||||
description="回款天数超过分界天数后,计提比例不再下降,固定按这个值计算,即最低计提比例。",
|
||||
sort_order=3,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -85,7 +89,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="360",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="≤分界天数按公式线性递减(360 天=6‰);超过则取下限",
|
||||
description="回款天数在这个范围内按日递减;超过这个天数就固定按下限计提。默认 360 天。",
|
||||
sort_order=4,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -99,7 +103,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="12.5",
|
||||
value_min="0",
|
||||
value_max="100",
|
||||
description="∑本人带领业务员绩效 × 此比例(限本团队)",
|
||||
description="经理按本团队业务员的绩效总额提取的分成比例,只统计自己带的业务员。",
|
||||
sort_order=5,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -113,7 +117,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="5",
|
||||
value_min="0",
|
||||
value_max="100",
|
||||
description="∑全部业务员绩效 × 此比例(全公司)",
|
||||
description="经理按全公司业务员的绩效总额提取的分成比例,与本团队分成叠加计算。",
|
||||
sort_order=6,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -127,7 +131,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="3",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="所有企业户适用;2026-08-13 起由 2‰ 提高至 3‰",
|
||||
description="企业户客户的回款统一按这个比例计提,不看回款天数。2026-08-13 起由 2‰ 调整为 3‰。",
|
||||
sort_order=7,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -141,7 +145,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="1",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="信息员绩效 = 本人销售额 × 此比例",
|
||||
description="信息员的绩效按本人销售额乘这个比例计算,不随回款天数递减。",
|
||||
sort_order=8,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -155,7 +159,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="10",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="预付款统一按此比例计提",
|
||||
description="预付款到账即按这个比例计提绩效,不必等到全额回款。",
|
||||
sort_order=9,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -169,7 +173,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="80",
|
||||
value_min="0",
|
||||
value_max="100",
|
||||
description="以物抵顶回款绩效按应发绩效的此比例折算",
|
||||
description="以物抵顶的回款,按应发绩效的这个比例折算,即打折后的金额参与绩效。",
|
||||
sort_order=10,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -183,7 +187,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="60",
|
||||
value_min="0",
|
||||
value_max="100",
|
||||
description="单项业务回款额达销售额(不含运费)此比例后起付;达标前累计",
|
||||
description="单项业务的回款累计达到销售额(不含运费)的这个比例后才开始发放绩效;达标之前只累计、不发放。",
|
||||
sort_order=11,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -197,7 +201,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="FULL",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description="FULL=按 R×R 乘当月全额应发绩效;BALANCE=乘余额(语义待 M3 前确认)",
|
||||
description="选择支付权重乘在哪个基数上:当月全额 = 乘本月全部应发绩效;余额 = 只乘尚未发放的部分。",
|
||||
sort_order=12,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -211,7 +215,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="30",
|
||||
value_min="0",
|
||||
value_max="100",
|
||||
description="折算毛利润额 = 销售额 ×(毛利润率 − 阈值);市政水利 30%、轨交 20%",
|
||||
description="折算毛利润额 = 销售额 ×(毛利润率 − 阈值),低于阈值的部分不参与绩效。市政水利默认 30%,轨道交通默认 20%。",
|
||||
sort_order=13,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -225,7 +229,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="INCLUDE",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description="INCLUDE含基本工资 / DEDUCT扣除基本工资 / EXCLUDE不计",
|
||||
description="算月工资时怎么处理基本工资:含基本工资 = 与绩效一起发;扣除基本工资 = 从绩效里扣掉已发的基本工资;不计 = 月工资只发绩效。",
|
||||
sort_order=14,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -239,7 +243,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="18000",
|
||||
value_min="0",
|
||||
value_max=None,
|
||||
description="试用期年累计绩效超过此值部分年底一次性结算",
|
||||
description="试用期绩效先不发放;年内累计超过这个金额的部分,年底一次性结算。",
|
||||
sort_order=15,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -253,11 +257,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="1",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description=(
|
||||
"0=二次分配方案提交即生效(系统自动通过全节点留痕,留痕不断链);"
|
||||
"1=提交后进入财务复核→领导审核审批流。"
|
||||
"默认开启(澄清 #5 业务答复 2026-10-19),业务可关闭"
|
||||
),
|
||||
description="二次分配方案提交后是否要审批:开启 = 先经财务复核、再经领导审核,通过后才生效;关闭 = 提交即生效,系统自动记录各节点留痕。",
|
||||
sort_order=17,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -271,11 +271,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="1",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description=(
|
||||
"月度结算审批链开关(④ 月结提交时消费):"
|
||||
"0=结算生成即生效,1=走财务复核→领导审核审批流。"
|
||||
"默认开启(澄清 #7 业务答复 2026-10-19,月结涉及工资发放从严),业务可关闭"
|
||||
),
|
||||
description="月度结算提交后是否要审批:开启 = 先经财务复核、再经领导审核;关闭 = 生成即生效。月结涉及工资发放,默认开启。",
|
||||
sort_order=18,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -289,11 +285,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="0",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description=(
|
||||
"0=调整单提交即生效(系统自动通过留痕);1=走财务复核→领导审核审批流。"
|
||||
"调整单是已锁定月份补发/扣减的唯一通道(澄清 #9:走同一审批链,"
|
||||
"默认值业务未明确,暂按直接生效,业务可开启)"
|
||||
),
|
||||
description="调整单提交后是否要审批:开启 = 先经财务复核、再经领导审核;关闭 = 提交即生效。调整单是已锁定月份补发或扣减的唯一通道。",
|
||||
sort_order=19,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -307,11 +299,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="0",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description=(
|
||||
"经理 12.5%+5% 聚合时是否计入经理本人名下经理个人户业绩(澄清 #12 "
|
||||
"业务答复 2026-10-19:作为参数配置、手动调整是否纳入):"
|
||||
"0=不计入(默认,暂口径) 1=计入"
|
||||
),
|
||||
description="经理提取团队分成时,是否把经理本人名下的业绩(经理个人户)也算进分成基数。默认不计入。",
|
||||
sort_order=20,
|
||||
),
|
||||
# ===== 预警规则(M4 ⑧ 工作台,澄清 #10「做成参数化规则便于调整」)=====
|
||||
@@ -327,7 +315,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="1",
|
||||
value_min=None,
|
||||
value_max=None,
|
||||
description="工作台风险预警区块总开关:0=关闭(不扫描不展示) 1=开启(默认)",
|
||||
description="工作台风险预警区块的总开关:关闭后不再扫描、也不展示预警条目。默认开启。",
|
||||
sort_order=21,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -341,7 +329,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="90",
|
||||
value_min="1",
|
||||
value_max=None,
|
||||
description=("发货后超过该天数仍未回款的发货批次触发预警(澄清 #10 建议默认 90 天)"),
|
||||
description="发货后超过这么多天仍未收到回款的发货批次,会出现在工作台的预警列表里。默认 90 天。",
|
||||
sort_order=22,
|
||||
),
|
||||
ParamSpec(
|
||||
@@ -355,7 +343,7 @@ PARAM_REGISTRY: dict[str, ParamSpec] = {
|
||||
default_value="3",
|
||||
value_min="1",
|
||||
value_max=None,
|
||||
description=("待审批单提交后超过该天数未处理触发预警(澄清 #10 建议默认 3 天)"),
|
||||
description="审批单提交后超过这么多天还没处理,会出现在工作台的预警列表里。默认 3 天。",
|
||||
sort_order=23,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -44,7 +44,7 @@ from app.schemas.param import (
|
||||
ParamPreviewOut,
|
||||
ParamUsedOut,
|
||||
)
|
||||
from app.services import audit_service, formula
|
||||
from app.services import audit_service, calc_templates, formula
|
||||
from app.services.audit_service import AuditRecord, RequestMeta
|
||||
from app.services.formula import to_decimal
|
||||
from app.services.no_generator import next_no
|
||||
@@ -557,25 +557,25 @@ def _preview_rate(ctx: _PreviewContext, days: int) -> tuple[Fraction, str, Decim
|
||||
# 存储口径为 ‰ 数(如 "3"),除以 1000 转纯比例 —— performance_amount
|
||||
# 按纯比例相乘,漏换算会把绩效金额放大 1000 倍(与业务员分支同防线)
|
||||
rate = take("enterprise.rate") / 1000
|
||||
return rate, "企业户固定计提比例", None
|
||||
return rate, "企业户:不按回款天数,统一按固定比例计提", None
|
||||
|
||||
role_code = payload.business_role
|
||||
template = "TPL_SALESPERSON_DECAY"
|
||||
if role_code:
|
||||
role = business_role_repository.get_by_code(ctx.session, role_code)
|
||||
if role is None:
|
||||
raise BadRequestError(f"岗位 {role_code!r} 不存在")
|
||||
raise BadRequestError(f"岗位「{role_code}」不存在,请到岗位角色里确认")
|
||||
template = role.calc_template
|
||||
|
||||
if template == "TPL_MANAGER_SHARE":
|
||||
raise BadRequestError(
|
||||
"营销经理绩效为团队聚合口径(本团队 12.5% + 全公司 5%),不支持单笔预览"
|
||||
"营销经理的绩效按团队与全公司分成计算,不单笔试算;请到月度结算里查看"
|
||||
)
|
||||
if template == "TPL_INFO_SALES_FIXED":
|
||||
# ‰ 存储口径 → 纯比例,同企业户分支(不换算则绩效放大 1000 倍)
|
||||
rate = take("info.rate") / 1000
|
||||
# 信息员的基数是本人销售额(无折算与阈值),覆盖默认基数
|
||||
return rate, "信息员:本人销售额 × 计提比例", Decimal(payload.revenue)
|
||||
return rate, "信息员:按本人销售额乘计提比例,不随回款天数递减", Decimal(payload.revenue)
|
||||
if template == "TPL_SALESPERSON_DECAY":
|
||||
start = take("salesperson.rate.start")
|
||||
slope = take("salesperson.rate.slope")
|
||||
@@ -584,20 +584,27 @@ def _preview_rate(ctx: _PreviewContext, days: int) -> tuple[Fraction, str, Decim
|
||||
rate = formula.salesperson_rate(
|
||||
days, start=start, slope=slope, floor=floor, boundary_days=boundary
|
||||
)
|
||||
# 说明写成业务话:说清这笔为什么是这个比例、什么时候不再降
|
||||
return (
|
||||
rate,
|
||||
f"业务员递减:{to_decimal(start)}‰ 起,{days} 天(分界 {boundary} 天,超界取 {to_decimal(floor)}‰)",
|
||||
f"回款 {days} 天,从起始 {to_decimal(start)}‰ 按日递减"
|
||||
f"(满 {boundary} 天后固定按 {to_decimal(floor)}‰ 计算)",
|
||||
None,
|
||||
)
|
||||
raise BadRequestError(f"公式模板 {template} 暂不支持预览")
|
||||
# 提示里给模板中文名而不是编码:编码是内部标识,业务人员在岗位角色页看不到它
|
||||
tpl_name = calc_templates.TEMPLATES[template].name if template in calc_templates.TEMPLATES else template
|
||||
raise BadRequestError(
|
||||
f"「{tpl_name}」不支持单笔试算,请在岗位角色里改用业务员递减或信息员固定计提"
|
||||
)
|
||||
|
||||
|
||||
def _preview_rate_decision(ctx: _PreviewContext) -> _PreviewRateDecision:
|
||||
"""产品类别校验 + 回款天数 + 计提比例判定。"""
|
||||
payload = ctx.payload
|
||||
if payload.product_category and payload.product_category not in PRODUCT_CATEGORIES:
|
||||
# 提示里用中文类别名:前端下拉显示的是「市政水利」,报错却回英文编码会让人对不上
|
||||
raise BadRequestError(
|
||||
f"产品类别 {payload.product_category!r} 不存在,可选:municipal_water / rail_transit"
|
||||
f"产品类别「{payload.product_category}」不存在,可选:市政水利、轨道交通"
|
||||
)
|
||||
days = formula.payment_days(payload.ship_date, payload.receipt_date)
|
||||
rate, rate_text, base_override = _preview_rate(ctx, days)
|
||||
@@ -651,7 +658,10 @@ def _preview_probation_note(ctx: _PreviewContext) -> str | None:
|
||||
if not ctx.payload.probation:
|
||||
return None
|
||||
annual = ctx.take("probation.annual.threshold")
|
||||
return f"试用期绩效照算不发放;年累计超 {to_decimal(annual)} 元部分年底一次性结算"
|
||||
return (
|
||||
f"试用期人员:绩效照常计算但不发放;年内累计超过 {to_decimal(annual)} 元的部分,"
|
||||
"年底一次性结清。"
|
||||
)
|
||||
|
||||
|
||||
def preview(session: Session, payload: ParamPreviewIn) -> ParamPreviewOut:
|
||||
|
||||
@@ -423,9 +423,10 @@ def _aggregate_row(
|
||||
"members": member_names,
|
||||
"params_as_of": str(share.param_date),
|
||||
"manager_personal_included": 1 if share.include_manager_personal else 0,
|
||||
# 说明写给业务人员看:说清这一笔的基数是怎么凑出来的
|
||||
"note": (
|
||||
f"#12 参数化:基数{'含' if share.include_manager_personal else '不含'}"
|
||||
"经理个人户业绩;9.8 方案公式"
|
||||
f"团队分成:基数{'含' if share.include_manager_personal else '不含'}"
|
||||
"经理本人名下业绩,按本团队与全公司两级比例合计计提"
|
||||
),
|
||||
},
|
||||
),
|
||||
|
||||
@@ -371,7 +371,11 @@ class TestPreview:
|
||||
}
|
||||
resp = client.post(f"{_PARAMS}/preview", json=payload, headers=admin["headers"])
|
||||
assert resp.status_code == 400
|
||||
assert "聚合" in resp.json()["message"]
|
||||
# 守两件事:① 聚合口径不允许单笔试算(拒绝而不是算出个错数);
|
||||
# ② 提示要能指导下一步(点名是谁的口径 + 去哪儿看),不是一句「不支持」。
|
||||
message = resp.json()["message"]
|
||||
assert "营销经理" in message
|
||||
assert "月度结算" in message
|
||||
|
||||
def test_回款日早于发货日_400(self, client: TestClient, admin: dict) -> None:
|
||||
payload = {
|
||||
|
||||
Reference in new issue
Block a user