diff --git a/backend/app/services/payroll_calculation.py b/backend/app/services/payroll_calculation.py index 96e3ba8..e46f398 100644 --- a/backend/app/services/payroll_calculation.py +++ b/backend/app/services/payroll_calculation.py @@ -25,7 +25,7 @@ from app.services import audit_service, formula, payroll_months from app.services.audit_service import AuditRecord from app.services.param_service import resolve_snapshot from app.services.tplus.calculation_preview import _parameters -from app.services.tplus.fifo import _beneficiary, amount_text, money, preview_group +from app.services.tplus.fifo import START, _beneficiary, amount_text, money, preview_group from app.services.tplus.workbench import digest @@ -308,11 +308,13 @@ def _calculate_month(session, business, record, month, reason=None): } if set(previous) - current.keys(): raise BadRequestError("历史已核算事件发生变化,请核对更正或调整,不可直接覆盖") + # 启用前的发货由期初应收承接,不再进入新业务的起付比例分母。 sales = sum( ( money(s["amount"]) for s in values["shipments"] - if s["date"] <= until.isoformat() and not s.get("performance_excluded") + if START.isoformat() <= s["date"] <= until.isoformat() + and not s.get("performance_excluded") ), Decimal(0), ) diff --git a/backend/tests/unit/test_formal_payroll.py b/backend/tests/unit/test_formal_payroll.py index 0531c1f..01f65ab 100644 --- a/backend/tests/unit/test_formal_payroll.py +++ b/backend/tests/unit/test_formal_payroll.py @@ -22,6 +22,7 @@ from app.models import ( Personnel, Product, Receipt, + Shipment, ShipmentItem, TPlusSourceLink, TPlusWorkRun, @@ -268,6 +269,61 @@ def test_起付百分之六十与平方权重(payroll_case, amount, performance, ) +@pytest.mark.parametrize("legacy_date", [date(2026, 2, 10), date(2026, 2, 28)]) +def test_历史发货不重复计入新业务起付分母(payroll_case, legacy_date): + """旧应收先冲抵,启用前发货不得再次压低新业务的回款比例。""" + s, user, _ = payroll_case + business = imported(payroll_case) + record = payroll_calculation.latest_input(s, business.id) + data = copy.deepcopy(record.data_json) + legacy = Shipment( + business_id=business.id, + shipment_no="SH-LEGACY", + ship_date=legacy_date, + total_amount=Decimal("100"), + freight_amount=Decimal("0"), + ) + s.add(legacy) + s.flush() + original = s.get(ShipmentItem, data["maps"]["shipment_items"][data["shipments"][0]["line_id"]]) + item = ShipmentItem( + shipment_id=legacy.id, + product_id=original.product_id, + product_name=original.product_name, + spec_model=original.spec_model, + unit=original.unit, + quantity=Decimal("2"), + sale_price=Decimal("50"), + amount=Decimal("100"), + ) + s.add(item) + s.flush() + data["shipments"].append( + { + **data["shipments"][0], + "source_id": "99", + "line_id": "99", + "date": legacy_date.isoformat(), + } + ) + data["maps"]["shipments"]["99"] = legacy.id + data["maps"]["shipment_items"]["99"] = item.id + data["group"]["opening_balance"] = "100" + data["receipts"][0]["amount"] = "200" + s.get(Receipt, data["maps"]["receipts"][data["receipts"][0]["source_id"]]).amount = Decimal( + "200" + ) + replace_input(s, record, data) + + result = payroll_calculation.calculate(s, business.id, "2026-03", operator=user) + + assert result["start_payment_ok"] is True + assert result["performance"] == result["payable"] == "0.20" + allocations = list(s.scalars(select(PayrollAllocation))) + assert sum(row.amount for row in allocations) == Decimal("100.00") + assert all(row.data_json["shipment_date"] == "2026-03-10" for row in allocations) + + def test_跨月续算仅计应付差额且离职未发给接手人(payroll_case): s, user, _manager = payroll_case business = imported(payroll_case)