feat(calc): 二次分摊拆行与 #12 单笔侧开关——按方案比例拆给参与人、余额归负责人
- _personal_rate 的 TPL_MANAGER_SHARE 分支读 manager.personal.included:1=按业务员递减公式计提,0=单笔落 0(现状) - _load_allocations 装载生效方案并防御拦截(合计超 100%、参与人含负责人) - _persist_ledger 拆行落库:参与人=份额×比例(q2)、负责人=q2 全额−已分累计(行合计守恒防分漂);快照带 alloc_plan/alloc_role/alloc_ratio - _prepare_mode 重算改 list_active_by_receipt 作废全部拆分行;uk 三列版支持同回款按人多行 - business_service 保存侧同口径校验(超 100% 400、负责人自分 400、hint 文案定稿) - 集成用例 +5(拆行守恒/叠加进度登记/重算全作废/双拦截)+保存侧 3 条 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
48c0a86279
commit
ccfd20651b
5 files changed
+479
-58
No files matched your search
@@ -412,6 +412,71 @@ class TestEdit:
|
||||
assert len(detail["items"]) == 1
|
||||
assert Decimal(detail["items"][0]["quantity"]) == Decimal("100")
|
||||
|
||||
def test_二次分配方案合计超100_保存400(self, client: TestClient, normal: dict) -> None:
|
||||
"""口径(澄清 #1/#2 答复 2026-10-19):分配绩效、余额归合同负责人——
|
||||
超 100% 会把负责人份额算成负数,保存侧先拦(核算侧另有防御)。"""
|
||||
headers = normal["headers"]
|
||||
customer = _customer(client, headers)
|
||||
owner = _personnel(client, headers)
|
||||
part = _personnel(client, headers)
|
||||
data = _create(client, headers, customer["id"], owner_personnel_id=owner["id"])
|
||||
|
||||
resp = client.put(
|
||||
f"{_BUSINESSES}/{data['id']}",
|
||||
json=_payload(
|
||||
customer["id"],
|
||||
secondary_allocations=[
|
||||
{"personnel_id": part["id"], "ratio": "60"},
|
||||
{"personnel_id": _personnel(client, headers)["id"], "ratio": "50"},
|
||||
],
|
||||
),
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 400, resp.text
|
||||
assert "超过 100%" in resp.json()["message"]
|
||||
|
||||
def test_二次分配参与人含负责人_保存400(self, client: TestClient, normal: dict) -> None:
|
||||
headers = normal["headers"]
|
||||
customer = _customer(client, headers)
|
||||
owner = _personnel(client, headers)
|
||||
data = _create(client, headers, customer["id"], owner_personnel_id=owner["id"])
|
||||
|
||||
resp = client.put(
|
||||
f"{_BUSINESSES}/{data['id']}",
|
||||
json=_payload(
|
||||
customer["id"],
|
||||
secondary_allocations=[{"personnel_id": owner["id"], "ratio": "30"}],
|
||||
),
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 400, resp.text
|
||||
assert "含合同负责营销人员" in resp.json()["message"]
|
||||
|
||||
def test_二次分配方案合法比例_保存成功且回显(
|
||||
self, client: TestClient, normal: dict
|
||||
) -> None:
|
||||
headers = normal["headers"]
|
||||
customer = _customer(client, headers)
|
||||
owner = _personnel(client, headers)
|
||||
part = _personnel(client, headers)
|
||||
data = _create(client, headers, customer["id"], owner_personnel_id=owner["id"])
|
||||
|
||||
resp = client.put(
|
||||
f"{_BUSINESSES}/{data['id']}",
|
||||
json=_payload(
|
||||
customer["id"],
|
||||
secondary_allocations=[{"personnel_id": part["id"], "ratio": "40"}],
|
||||
),
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
# 方案在详情回包(更新回包为简版 Out,不含方案区)
|
||||
detail = client.get(f"{_BUSINESSES}/{data['id']}", headers=headers).json()["data"]
|
||||
assert len(detail["secondary_allocations"]) == 1
|
||||
assert Decimal(detail["secondary_allocations"][0]["ratio"]) == Decimal("40")
|
||||
# 余额 60% 归合同负责人(hint 文案 = #2 答复口径)
|
||||
assert detail["allocation_hint"] is not None and "60" in detail["allocation_hint"]
|
||||
|
||||
|
||||
class TestStatus:
|
||||
"""状态机:终态不可逆是终态单据禁写的前提;留痕从创建首条记起。"""
|
||||
|
||||
Reference in new issue
Block a user