PayrollManagementSystem/backend/tests/unit/test_historical_roles.py
liuwenxin 1e53792ad6 feat(T+): 完成数据贯通与自动接入闭环
打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。
新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。
补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
2026-11-05 18:47:13 +08:00

138 lines
5.4 KiB
Python

"""历史任职不能被当前岗位覆盖,预演、台账与工资分项必须使用同一依据。"""
from datetime import date
from decimal import Decimal
import pytest
from pydantic import ValidationError
from sqlalchemy import select
from app.models import Customer, PerformanceLedger, Product, TPlusFact
from app.schemas.tplus_workbench import PersonFact
from app.services import payroll_calculation, settlement_service
from tests.unit.test_formal_payroll import imported
from tests.unit.test_formal_payroll import payroll_case as payroll_case
from tests.unit.test_tplus_fifo import inputs, run
from tests.unit.test_tplus_workbench import apply, fact, preview, source
from tests.unit.test_tplus_workbench import work_session as work_session
def periods():
return [{"start_month": "2026-03", "end_month": "2026-07", "role_code": "sales_manager"}]
def test_按月份保存已确认岗位而不补造转岗日():
person = PersonFact(
code="010110",
name="历史岗位样本",
role_code="salesperson",
status=1,
role_history=periods(),
)
assert person.model_dump()["role_history"] == periods()
@pytest.mark.parametrize(
"history",
[
[{"start_month": "2026-13", "end_month": "2026-07", "role_code": "sales_manager"}],
[{"start_month": "2026-08", "end_month": "2026-07", "role_code": "sales_manager"}],
[
*periods(),
{"start_month": "2026-07", "end_month": "2026-09", "role_code": "salesperson"},
],
],
)
def test_无效或重叠历史岗位拒绝入库(history):
with pytest.raises(ValidationError):
PersonFact(
code="010110",
name="历史岗位样本",
role_code="salesperson",
status=1,
role_history=history,
)
def test_七月经理本人业务在八月转岗后仍排除():
group, ship, cash, facts, params = inputs()
ship["date"], cash["date"] = "2026-07-16", "2026-08-16"
facts["personnel"]["001"].update(role_history=periods())
params["2026-08-16"] = params.pop("2026-03-10")
params["august_confirmed"] = True
line = run(group, [ship], [cash], facts, params, date(2026, 8, 31))["lines"][0]
assert line["performance"] == "0.00"
assert line["excluded_manager_own"]
assert line["original_role_code"] == "sales_manager"
assert line["role_code"] == "salesperson"
def test_八月新业务按业务员且不沿用七月经理岗位():
group, ship, cash, facts, params = inputs()
ship["date"] = cash["date"] = "2026-08-16"
facts["personnel"]["001"].update(role_history=periods())
params["2026-08-16"] = params.pop("2026-03-10")
params["august_confirmed"] = True
line = run(group, [ship], [cash], facts, params, date(2026, 8, 31))["lines"][0]
assert line["performance"] == "0.20" and not line["excluded_manager_own"]
def test_历史经理贯通正式导入台账及月结岗位(payroll_case):
session, operator, _ = payroll_case
fact = session.scalar(
select(TPlusFact).where(TPlusFact.kind == "PERSONNEL", TPlusFact.fact_key == "00043")
)
fact.data_json = {**fact.data_json, "role_history": periods()}
session.commit()
business = imported(payroll_case)
fact.data_json = {**fact.data_json, "role_history": []}
session.commit()
result = payroll_calculation.calculate(session, business.id, "2026-03", operator=operator)
assert result["performance"] == result["payable"] == "0.00"
row = session.scalar(select(PerformanceLedger).where(PerformanceLedger.calc_type == 1))
assert row.role_code == "sales_manager"
assert row.snapshot_json["manager_own_excluded"]
settlement = settlement_service.generate(session, "2026-03", operator=operator)
item = next(item for item in settlement.items if item.personnel_id == row.personnel_id)
assert item.role_code == "sales_manager"
def test_源客户允许负责人未知且不冒用营销人员姓名(work_session):
session = work_session
source(session, "customers", [{"id": 9647, "name": "真实样本客户", "partnerType": 211}])
fact(session, "CUSTOMER", "9647", {"customer_type": "enterprise"})
imported_run = apply(session, preview(session, ["customers"]))
assert imported_run.summary_json["counts"] == {"CREATE": 1}
customer = session.scalar(select(Customer))
assert customer.owner_name == "" and customer.customer_type == "enterprise"
def test_带真实发货参照价格的产品可预览并正式导入(work_session):
session = work_session
source(session, "products", [{"id": 9, "name": "样本产品", "unit_name": "根"}])
source(
session,
"shipments",
[{"id": 20, "code": "SA-SAMPLE", "voucherdate": "2026-07-16", "voucherState": 189}],
)
source(
session,
"shipment_items",
[
{
"id": 21,
"source_parent_id": 20,
"idinventory": 9,
"taxAmount": "3850.00000000000000",
"quantity": "28.00000000000000",
"taxPrice": "137.50000000000000",
}
],
)
fact(session, "PRODUCT", "9", {"category": "municipal_water", "unit": "根"})
imported_run = apply(session, preview(session, ["products"]))
assert imported_run.summary_json["counts"] == {"CREATE": 1}
product = session.scalar(select(Product))
assert product.stock_quantity == Decimal("28")
assert product.source_trade_json["price"] == "137.5000"