PayrollManagementSystem/backend/tests/integration/test_receipt_confirmation.py
liuwenxin b3de35c2ec fix(核算): 对齐抵顶口径并完善界面细节
回款确认时间收敛到秒级;抵顶回款不再自动折算并保留标记。补齐公式模板角色标签与提示、指导成本登记入口及基础布局、分页、设置等响应式细节。同步忽略本地排查产物。
2026-11-06 16:27:45 +08:00

143 lines
6.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""隔离MySQL/API验证独立确认、默认门槛、凭证及内容变更,不写真实工资。"""
import copy
from decimal import Decimal
import pytest
from sqlalchemy import func, select
from app.core.db import get_session_factory
from app.models import OperationLog, PayrollInput, PerformanceLedger, Receipt
from tests.integration.test_formal_payroll import (
post,
)
from tests.integration.test_formal_payroll import (
unconfirmed_formal_chain as unconfirmed_formal_chain,
)
pytestmark = pytest.mark.integration
ROOT = "/api/v1/business/receipts"
def confirm(client, headers, receipt):
return client.post(f"{ROOT}/{receipt['id']}/confirm", headers=headers,
json={"expected_revision": receipt["confirmation_revision"]})
def calculate(client, headers, business):
return client.post(f"/api/v1/payroll/businesses/{business['id']}/calculate", headers=headers,
json={"month": "2026-03"})
def switch_off(client, headers):
post(client, "/api/v1/system/params/changes", headers, {
"change_type": 1, "remark": "t_独立确认关闭场景",
"items": [{"param_code": "receipt.confirmation.enabled", "dimension_value": "", "value": "0"}],
})
def test_默认开启_保存资料和凭证资格不能代替人工确认(client, admin, unconfirmed_formal_chain):
business, receipt, *_ = unconfirmed_formal_chain
headers = admin["headers"]
assert client.get(ROOT + "/confirmation-policy", headers=headers).json()["data"]["enabled"] is True
assert receipt["confirmation_status"] == 0
response = calculate(client, headers, business)
assert response.status_code == 400 and "人工确认" in response.text
with get_session_factory()() as s:
assert s.get(Receipt, receipt["id"]).confirmation_status == 0
assert s.scalar(select(func.count()).select_from(PerformanceLedger)) == 0
def test_独立确认后核算_重复确认幂等及审计(client, admin, unconfirmed_formal_chain):
business, receipt, *_ = unconfirmed_formal_chain
headers = admin["headers"]
response = confirm(client, headers, receipt)
assert response.status_code == 200, response.text
confirmed = response.json()["data"]
assert confirmed["confirmation_status"] == 1 and confirmed["confirmed_by"]
again = confirm(client, headers, receipt).json()["data"]
assert again["confirmed_at"] == confirmed["confirmed_at"]
result = calculate(client, headers, business)
assert result.status_code == 200, result.text
assert result.json()["data"]["performance"] == "0.20"
with get_session_factory()() as s:
assert s.get(Receipt, receipt["id"]).settlement_status == 1
assert s.scalar(select(func.count()).select_from(OperationLog).where(OperationLog.action == "人工确认")) >= 1
def test_关闭仅跳过人工确认_不产生确认状态(client, admin, unconfirmed_formal_chain):
business, receipt, *_ = unconfirmed_formal_chain
switch_off(client, admin["headers"])
result = calculate(client, admin["headers"], business)
assert result.status_code == 200, result.text
with get_session_factory()() as s:
assert s.get(Receipt, receipt["id"]).confirmation_status == 0
@pytest.mark.parametrize("manually_confirmed", [False, True])
def test_关闭开关或人工确认都不能豁免无效凭证(client, admin, unconfirmed_formal_chain, manually_confirmed):
business, receipt, *_ = unconfirmed_formal_chain
if manually_confirmed:
assert confirm(client, admin["headers"], receipt).status_code == 200
else:
switch_off(client, admin["headers"])
with get_session_factory()() as s:
record = s.scalar(select(PayrollInput).where(PayrollInput.business_id == business["id"]))
data = copy.deepcopy(record.data_json)
data["receipts"][0]["voucher_confirmed"] = False
record.data_json = data
s.commit()
result = calculate(client, admin["headers"], business)
assert result.status_code == 400 and "VOUCHER_STATE_PENDING" in result.text
def test_修改关键内容重置确认_旧页面不能误确认(client, admin, unconfirmed_formal_chain):
_, receipt, *_ = unconfirmed_formal_chain
headers = admin["headers"]
assert confirm(client, headers, receipt).status_code == 200
changed = client.put(f"{ROOT}/{receipt['id']}", headers=headers,
json={"receipt_date": receipt["receipt_date"], "amount": "101"})
assert changed.status_code == 200, changed.text
data = changed.json()["data"]
assert data["confirmation_status"] == 0 and data["confirmed_at"] is None
assert confirm(client, headers, receipt).status_code == 409
assert confirm(client, headers, data).status_code == 200
def test_只读拒绝确认及切换开关(client, admin, normal, unconfirmed_formal_chain):
_, receipt, *_ = unconfirmed_formal_chain
assert confirm(client, normal["headers"], receipt).status_code == 403
response = client.post("/api/v1/system/params/changes", headers=normal["headers"], json={
"change_type": 1, "items": [{"param_code": "receipt.confirmation.enabled", "value": "0"}],
})
assert response.status_code == 403
@pytest.mark.parametrize("unconfirmed_formal_chain,expected", [
({"unit_cost": "50"}, "0.00"), ({"unit_cost": "45"}, "-20.00"),
], indirect=["unconfirmed_formal_chain"])
def test_正式核算零毛利特例但低阈值仍为负(client, admin, unconfirmed_formal_chain, expected):
business, receipt, *_ = unconfirmed_formal_chain
assert confirm(client, admin["headers"], receipt).status_code == 200
result = calculate(client, admin["headers"], business)
assert result.status_code == 200, result.text
with get_session_factory()() as s:
ledger = s.scalar(select(PerformanceLedger).where(PerformanceLedger.business_id == business["id"]))
assert ledger.converted_profit == Decimal(expected)
assert ledger.performance_amount == Decimal(expected) * Decimal("0.01")
@pytest.mark.parametrize("role,profit,converted,performance", [
("salesperson", "0", "0", "0"),
("salesperson", "10", "-20", "-0.20"),
("info_officer", "0", "100", "0.10"),
])
def test_试算零毛利与信息员基数保持独立(client, admin, role, profit, converted, performance):
data = post(client, "/api/v1/system/params/preview", admin["headers"], {
"business_role": role, "customer_type": "personal", "product_category": "municipal_water",
"ship_date": "2026-03-10", "receipt_date": "2026-03-10", "revenue": "100",
"profit": profit, "received_amount": "100",
})
assert Decimal(data["converted_profit"]) == Decimal(converted)
assert Decimal(data["performance_amount"]) == Decimal(performance)