打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。 新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。 补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
72 lines
3.9 KiB
Python
72 lines
3.9 KiB
Python
"""手工业务再次准备时保留交接及已封存成本。"""
|
|
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
from sqlalchemy import select
|
|
|
|
from app.core.exceptions import BadRequestError
|
|
from app.models import (
|
|
Business,
|
|
BusinessItem,
|
|
BusinessRole,
|
|
Customer,
|
|
PayrollInput,
|
|
Personnel,
|
|
Shipment,
|
|
ShipmentItem,
|
|
User,
|
|
)
|
|
from app.schemas.payroll import PayrollSetupIn
|
|
from app.services import payroll_inputs, payroll_months
|
|
from tests.unit.test_tplus_workbench import work_session as work_session
|
|
|
|
|
|
def test_手工业务再次保存保留交接并拒绝指导单价微小变更(work_session, monkeypatch):
|
|
session = work_session
|
|
sales = session.scalar(select(BusinessRole).where(BusinessRole.role_code == "salesperson"))
|
|
manager_role = BusinessRole(role_name="经理", role_code="sales_manager", calc_template="SALES_MANAGER", sort_order=2)
|
|
customer = Customer(company_name="手工客户", owner_name="负责人", customer_type="personal")
|
|
session.add_all([manager_role, customer])
|
|
session.flush()
|
|
owner = Personnel(personnel_name="业务员", business_role_id=sales.id)
|
|
receiver = Personnel(personnel_name="接手人员", business_role_id=sales.id)
|
|
manager = Personnel(personnel_name="经理", business_role_id=manager_role.id)
|
|
session.add_all([owner, receiver, manager])
|
|
session.flush()
|
|
business = Business(business_no="MANUAL-01", customer_id=customer.id)
|
|
session.add(business)
|
|
session.flush()
|
|
session.add(BusinessItem(business_id=business.id, product_name="管材", spec_model="",
|
|
product_category="municipal_water", quantity=Decimal("5"), sale_amount=Decimal("500")))
|
|
shipment = Shipment(business_id=business.id, shipment_no="SH-MANUAL", ship_date=date(2026, 3, 10))
|
|
session.add(shipment)
|
|
session.flush()
|
|
item = ShipmentItem(shipment_id=shipment.id, product_name="管材", spec_model="",
|
|
quantity=Decimal("5"), amount=Decimal("500"))
|
|
session.add(item)
|
|
session.commit()
|
|
monkeypatch.setattr(payroll_months, "ensure_allocation_editable", lambda *args: None)
|
|
payload = {"month": "2026-03", "evidence": "已核实", "opening_balance": "0", "history_confirmed": True,
|
|
"guidance_finalized": True, "shipment_ownerships": [{"shipment_item_id": item.id,
|
|
"personnel_id": owner.id, "manager_id": manager.id, "unit_cost": "1"}],
|
|
"handovers": [{"business_key": business.business_no, "mode": "RESIGNATION",
|
|
"from_personnel_id": str(owner.id), "to_personnel_id": str(receiver.id), "effective_date": "2026-03-20"}]}
|
|
payroll_inputs.setup(session, business.id, PayrollSetupIn(**payload), session.get(User, 1))
|
|
retry = {**payload, "expected_version": 1}
|
|
retry.pop("handovers")
|
|
payroll_inputs.setup(session, business.id, PayrollSetupIn(**retry), session.get(User, 1))
|
|
latest = session.scalar(select(PayrollInput).order_by(PayrollInput.version.desc()))
|
|
assert latest.data_json["facts"]["handovers"][0]["to_personnel_id"] == str(receiver.id)
|
|
changed = {**retry, "expected_version": 2,
|
|
"shipment_ownerships": [{**retry["shipment_ownerships"][0], "unit_cost": "1.0001"}]}
|
|
with pytest.raises(BadRequestError, match="指导成本已封存"):
|
|
payroll_inputs.setup(session, business.id, PayrollSetupIn(**changed), session.get(User, 1))
|
|
equivalent = {**changed, "shipment_ownerships": [{**changed["shipment_ownerships"][0], "unit_cost": "1.0000"}]}
|
|
payroll_inputs.setup(session, business.id, PayrollSetupIn(**equivalent), session.get(User, 1))
|
|
payroll_inputs.setup(session, business.id, PayrollSetupIn(**{**equivalent, "expected_version": 3, "handovers": []}),
|
|
session.get(User, 1))
|
|
latest = session.scalar(select(PayrollInput).order_by(PayrollInput.version.desc()))
|
|
assert latest.data_json["facts"]["handovers"] == []
|