arm-core/api/tests/test_intercompany_matrix.py

186 lines
8.6 KiB
Python
Raw 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.

"""Выбор счетов и показателя в режимах шахматки без обращения к БД."""
import uuid
from decimal import Decimal
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from src.api.endpoints.consolidations.intercompany_matrix import router
from src.common.exceptions import DomainValidationError
from src.models.enums import AccountKind, StatementType
from src.schemas.consolidations import IntercompanyMatrixCompanyOut, IntercompanyMatrixOut, IntercompanyResultTableOut
from src.services import intercompany_matrix as matrix
def _account(kind: AccountKind, parent_id: uuid.UUID | None = None) -> SimpleNamespace:
return SimpleNamespace(id=uuid.uuid4(), kind=kind, parent_id=parent_id)
def _row(
owner: uuid.UUID,
counterparty: uuid.UUID,
account_id: uuid.UUID,
*,
debit: str = "0",
credit: str = "0",
) -> SimpleNamespace:
return SimpleNamespace(
legal_entity_id=owner,
counterparty_id=counterparty,
account_id=account_id,
closing_debit=Decimal(debit),
closing_credit=Decimal(credit),
turnover_debit=Decimal("0"),
turnover_credit=Decimal("0"),
)
def test_routes_are_separate_and_matrix_expands_manual_accounts() -> None:
paths = {(route.path, tuple(sorted(route.methods))) for route in router.routes}
assert ("/consolidations/versions/{version_id}/intercompany-matrix/all", ("GET",)) in paths
assert ("/consolidations/versions/{version_id}/intercompany-matrix/manual", ("POST",)) in paths
assert ("/consolidations/versions/{version_id}/intercompany-matrix/result-table", ("GET",)) in paths
assert (
"/consolidations/versions/{version_id}/intercompany-matrix/presets/{rule_no}",
("GET",),
) in paths
debit_parent = _account(AccountKind.A)
debit_child = _account(AccountKind.A, debit_parent.id)
credit_parent = _account(AccountKind.P)
credit_child = _account(AccountKind.P, credit_parent.id)
by_id, children = matrix._account_tree([debit_parent, debit_child, credit_parent, credit_child])
debit = matrix._selected_accounts([debit_parent.id], [], "D", by_id, children)
credit = matrix._selected_accounts([credit_parent.id], [], "K", by_id, children)
assert debit == {debit_parent.id, debit_child.id}
assert credit == {credit_parent.id, credit_child.id}
company_a, company_b = uuid.uuid4(), uuid.uuid4()
result = matrix.calculate_matrix(
[
_row(company_a, company_b, debit_child.id, debit="100"),
_row(company_b, company_a, credit_child.id, credit="90"),
],
[
IntercompanyMatrixCompanyOut(id=company_a, name="Компания А"),
IntercompanyMatrixCompanyOut(id=company_b, name="Компания Б"),
],
matrix.MatrixSelection(debit, credit, "closing", "D"),
)
assert [(company.id, company.name) for company in result.companies] == [
(company_a, "Компания А"),
(company_b, "Компания Б"),
]
assert result.cells[0].amount_a == Decimal("100")
assert result.cells[0].amount_b == Decimal("90")
assert result.cells[0].variance_percent == Decimal("100") / Decimal("9")
assert isinstance(result, IntercompanyMatrixOut)
response = result
assert "exceeds_threshold" not in response.model_dump(mode="json")["cells"][0]
assert response.model_dump(mode="json")["companies"] == [
{"id": str(company_a), "name": "Компания А"},
{"id": str(company_b), "name": "Компания Б"},
]
def test_result_table_uses_fixed_articles_and_group_totals() -> None:
company_a, company_b, external = uuid.uuid4(), uuid.uuid4(), uuid.uuid4()
required_codes = {
code for article in matrix.RESULT_ARTICLES for code in (*article.account_codes, *article.excluded_codes)
}
kinds = {
code: AccountKind.A if article.side == "D" else AccountKind.P
for article in matrix.RESULT_ARTICLES
for code in article.account_codes
}
kinds.update({"76": AccountKind.AP, "76.07.1": AccountKind.P, "76.07.5": AccountKind.A, "76.07.9": AccountKind.A})
ids = {code: uuid.uuid4() for code in required_codes}
parents = {"67.03": "67", "66.03": "66", "66.04": "66", "76.07.1": "76", "76.07.5": "76", "76.07.9": "76"}
accounts = [
SimpleNamespace(id=ids[code], code=code, kind=kinds[code], parent_id=ids.get(parents.get(code)))
for code in required_codes
]
rows = [
_row(company_a, company_b, ids["60.02"], debit="100"),
_row(company_a, company_b, ids["76.07.5"], debit="50"),
_row(company_a, company_b, ids["76.07.1"], credit="20"),
_row(company_a, company_b, ids["58.01"], debit="30"),
_row(company_b, company_a, ids["60.01"], credit="70"),
_row(company_b, company_a, ids["67.03"], credit="40"),
_row(company_a, company_b, ids["66.04"], credit="10"),
_row(company_a, external, ids["60.02"], debit="999"),
]
result = matrix.calculate_result_table(
rows,
[
IntercompanyMatrixCompanyOut(id=company_a, name="Компания А"),
IntercompanyMatrixCompanyOut(id=company_b, name="Компания Б"),
],
accounts,
)
articles = {article.code: article for article in result.articles}
assert list(articles) == [article.code for article in matrix.RESULT_ARTICLES]
assert [item.amount for item in articles["receivables"].amounts] == [Decimal("100"), Decimal("0")]
assert articles["receivables"].group_total == Decimal("100")
assert articles["payables"].group_total == Decimal("70")
assert articles["financial_investments"].group_total == Decimal("30")
assert articles["long_term_loans"].group_total == Decimal("40")
assert articles["short_term_loans"].group_total == Decimal("10")
assert isinstance(result, IntercompanyResultTableOut)
response = result
assert response.articles[0].amounts[0].company_id == company_a
@pytest.mark.asyncio
async def test_preset_uses_rule_version_measure_side_and_exclusions(monkeypatch: pytest.MonkeyPatch) -> None:
version_id, company_a, company_b = uuid.uuid4(), uuid.uuid4(), uuid.uuid4()
debit_root = _account(AccountKind.A)
debit_excluded = _account(AccountKind.A, debit_root.id)
credit_root = _account(AccountKind.P)
rows = [
_row(company_a, company_b, credit_root.id, credit="999"),
_row(company_b, company_a, debit_root.id, debit="999"),
_row(company_b, company_a, debit_excluded.id, debit="999"),
]
rows[0].turnover_credit = Decimal("80")
rows[1].turnover_debit = Decimal("70")
rows[2].turnover_debit = Decimal("20")
version = SimpleNamespace(id=version_id, elimination_rules_version=2)
osv = SimpleNamespace(build=AsyncMock(return_value=(SimpleNamespace(rows=rows), version, None)))
session = SimpleNamespace(
scalars=AsyncMock(return_value=SimpleNamespace(all=lambda: [debit_root, debit_excluded, credit_root]))
)
rule = SimpleNamespace(
id=uuid.uuid4(), statement_type=StatementType.income, rule_no=3, measure="turnover", side_1="K"
)
rule_accounts = [
SimpleNamespace(company_no=1, account_id=credit_root.id, is_excluded=False),
SimpleNamespace(company_no=2, account_id=debit_root.id, is_excluded=False),
SimpleNamespace(company_no=2, account_id=debit_excluded.id, is_excluded=True),
]
list_rules = AsyncMock(return_value=[(rule, account) for account in rule_accounts])
members = AsyncMock(
return_value=[
(None, SimpleNamespace(id=company_a, name="Компания А")),
(None, SimpleNamespace(id=company_b, name="Компания Б")),
]
)
monkeypatch.setattr(matrix.rules_repo, "list_rules_with_accounts", list_rules)
monkeypatch.setattr(matrix.repo, "members", members)
service = matrix.IntercompanyMatrixService(session, SimpleNamespace(), osv)
result = await service.build_preset(version_id, 3)
list_rules.assert_awaited_once_with(session, version_id)
assert result.cells[0].amount_a == Decimal("80")
assert result.cells[0].amount_b == Decimal("70")
assert result.cells[0].variance_amount == Decimal("10")
assert [company.name for company in result.companies] == ["Компания А", "Компания Б"]
duplicate = SimpleNamespace(id=uuid.uuid4(), statement_type=StatementType.balance, rule_no=3)
list_rules.return_value = [(rule, rule_accounts[0]), (duplicate, None)]
with pytest.raises(DomainValidationError, match="Номер правила не уникален"):
await service.build_preset(version_id, 3)