138 lines
5.4 KiB
Python
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"
|