fix(matrix): ограничить PRESET правилами ББ

This commit is contained in:
PotapovaA 2026-10-21 18:43:19 +03:00
parent e9dc67b2e9
commit 38c8eefa18
5 changed files with 46 additions and 5 deletions

View File

@ -16,7 +16,7 @@ from src.domain.policy import VisibilityScope
from src.models.accounting import ChartOfAccount, EliminationRule
from src.models.consolidation import Consolidation, ConsolidationVersion
from src.models.directories import CompanyGroup
from src.models.enums import AccountKind, PeriodQuarter
from src.models.enums import AccountKind, PeriodQuarter, StatementType
from src.repositories import directories as directory_repo
from src.repositories import elimination_rules as elimination_rules_repo
from src.schemas.consolidations import (
@ -419,6 +419,8 @@ class IntercompanyMatrixService:
rule, rule_accounts = await elimination_rules_repo.rule_with_account(self._session, rule_id)
if rule is None or not elimination_rules_repo.is_valid_for_version(rule, version.elimination_rules_version):
raise NotFoundError("правило-пресет не найдено для версии консолидации")
if rule.statement_type != StatementType.balance:
raise DomainValidationError("INVALID_RULE", "Шахматка PRESET поддерживает только правила ББ")
if rule.side_1 not in ("D", "K") or rule.measure not in ("closing", "turnover"):
raise DomainValidationError("INVALID_RULE", "Неверное направление или показатель правила-пресета")

View File

@ -17,7 +17,7 @@ from src.models.accounting import ChartOfAccount, EliminationRule
from src.models.audit_ex import AuditEvent
from src.models.consolidation import ConsolidationDocument, ConsolidationVersion
from src.models.directories import CompanyGroup, LegalEntity, RegionalOffice
from src.models.enums import AuditAction, AuditSection, CounterpartyMatchKind, PeriodQuarter
from src.models.enums import AuditAction, AuditSection, CounterpartyMatchKind, PeriodQuarter, StatementType
from src.models.osv import OsvEntry
from src.repositories import consolidations as consolidation_repo
from src.repositories import directories as directory_repo
@ -179,6 +179,23 @@ async def test_preset_uses_rule_sides_and_exclusions(auth_client: AuthClientFact
assert audit.after_value["rule_id"] == str(rule.id)
@pytest.mark.parametrize("suffix", ["", "/export"])
async def test_preset_rejects_income_rule(auth_client: AuthClientFactory, seeded: AsyncSession, suffix: str) -> None:
world = await _world(seeded)
rule = (
await seeded.scalars(select(EliminationRule).where(EliminationRule.rule_no == RECEIVABLES_PAYABLES_RULE_NO))
).one()
rule.statement_type = StatementType.income
await seeded.flush()
client = await auth_client(KA_RF)
response = await client.get(
f"/consolidations/versions/{world.version.id}/intercompany-matrix/preset/{rule.id}{suffix}"
)
assert response.status_code == UNPROCESSABLE, response.text
assert response.json()["detail"]["code"] == "INVALID_RULE"
async def test_preset_unpaired_osv_returns_rows_and_total(auth_client: AuthClientFactory, seeded: AsyncSession) -> None:
world = await _world(seeded)
rule = (

View File

@ -7,9 +7,10 @@ from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from fastapi import status as http_status
from src.api.endpoints.consolidations.intercompany_matrix import router
from src.models.enums import AccountKind
from src.models.enums import AccountKind, StatementType
from src.schemas.consolidations import (
IntercompanyMatrixCellOut,
IntercompanyMatrixCompanyOut,
@ -128,6 +129,7 @@ async def test_preset_applies_exclusions_to_its_own_side_and_rule_measure(monkey
monkeypatch.setattr(matrix.directory_repo, "list_accounts", AsyncMock(return_value=[parent, child]))
rule = SimpleNamespace(
id=uuid.uuid4(),
statement_type=StatementType.balance,
version_no=1,
side_1="D",
measure="turnover",
@ -162,6 +164,22 @@ async def test_preset_applies_exclusions_to_its_own_side_and_rule_measure(monkey
assert info.measure == "turnover"
async def test_preset_rejects_income_rule_before_selecting_accounts(monkeypatch: pytest.MonkeyPatch) -> None:
rule = SimpleNamespace(
statement_type=StatementType.income,
version_no=1,
valid_to=None,
)
monkeypatch.setattr(matrix.elimination_rules_repo, "rule_with_account", AsyncMock(return_value=(rule, [])))
service = matrix.IntercompanyMatrixService(object(), None, object())
with pytest.raises(matrix.DomainValidationError) as error:
await service._resolve_preset_selection(uuid.uuid4(), SimpleNamespace(elimination_rules_version=1), {}, {})
assert error.value.status == http_status.HTTP_422_UNPROCESSABLE_CONTENT
assert error.value.code == "INVALID_RULE"
def test_one_sided_reflects_osv_sources_even_when_amount_is_zero() -> None:
company_a, company_b = uuid.uuid4(), uuid.uuid4()
debit_account, credit_account = uuid.uuid4(), uuid.uuid4()

View File

@ -9,7 +9,7 @@
`closing` — конечное сальдо (`closing_debit`/`closing_credit`), `turnover` — обороты (`turnover_debit`/`turnover_credit`).
Правило SHALL быть действительным для версии консолидации: при заданном `elimination_rules_version` — с тем же
`version_no`, иначе — без `valid_to`; несуществующее или недействительное правило SHALL давать 404. Ошибки 422:
`RULE_REQUIRED` — правило не выбрано, `INVALID_RULE` — неверные `side_1`, `measure` или сторона счёта правила,
`RULE_REQUIRED` — правило не выбрано, `INVALID_RULE` — правило ОФР либо неверные `side_1`, `measure` или сторона счёта правила,
`ACCOUNTS_REQUIRED` — у одной из сторон нет счетов. Невидимая и несуществующая версия SHALL отвечать 404 `NOT_FOUND`.
#### Scenario: Пресет по сальдо
@ -28,6 +28,10 @@
- **WHEN** `rule_id` не существует
- **THEN** 404
#### Scenario: Правило ОФР
- **WHEN** у правила `statement_type = income`
- **THEN** расчёт и выгрузка возвращают 422 `INVALID_RULE`
### Requirement: Строки сводной ОСВ без пары по пресету
`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/preset/{rule_id}/unpaired-osv`
(`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{items, total}`: все строки сводной ОСВ версии, чьи записи ОСВ

View File

@ -9,6 +9,6 @@
`correction_ids_*`) вместе с тестами; код выделен в `AURORA-1747`.
- [ ] 2.1a Убрать ручку `unpaired-osv` (`get_unpaired_rows`, `UnpairedOsvOut`, маршрут) вместе с тестами;
код сохранён в `wip/shahmatka-corrections-unpaired`.
- [ ] 2.2 PRESET — только правила `statement_type = balance`; правило ОФР — 422 `INVALID_RULE`, тест.
- [x] 2.2 PRESET — только правила `statement_type = balance`; правило ОФР — 422 `INVALID_RULE`, тест.
- [ ] 2.3 Несколько открытых `version_no` — одна версия (max) или явная ошибка, тест.
- [ ] 2.4 Ревьюер: убрать из дельты `consolidated-osv` требования про корректировки и `unpaired-osv`, обновить README.