From 3296e4a849498950554c4ef4fbe44ee8e1aed7c2 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 10 Nov 2026 13:04:10 +0400 Subject: [PATCH] =?UTF-8?q?fix(#237):=20=D1=80=D0=B0=D0=B7=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=B5=D0=BD=D0=B8=D0=B5=20=E2=80=94=20=D0=B1=D0=B5=D0=B7?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=20=D0=B2=D0=BD=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D0=BC=D0=B5=D1=82=D1=80=D0=B0=20=D0=B8=20?= =?UTF-8?q?=C2=AB=D1=81=D0=B0=D0=BC=20=D0=BD=D0=B0=20=D1=81=D0=B5=D0=B1?= =?UTF-8?q?=D1=8F=C2=BB=20(AURORA-2034)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §5 п. 2a: в агрегаты и нераспределённые идут только строки между разными компаниями периметра версии; строка с ручным решением аналитика остаётся видна. На копии стенда нераспределённых РБ 2025 К1 — 37 → 3, РБ 2024 К3 — 31 → 3 (вместе с «76-исключениями»). Co-Authored-By: Claude Opus 5.5 --- api/src/domain/vgo_distribution.py | 20 +++++++++------ api/tests/test_vgo.py | 39 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/api/src/domain/vgo_distribution.py b/api/src/domain/vgo_distribution.py index a793947..ebab240 100644 --- a/api/src/domain/vgo_distribution.py +++ b/api/src/domain/vgo_distribution.py @@ -100,17 +100,23 @@ def distribute_vgo_rows(data: DistributionInput) -> Distribution: for row in data.rows: if not used.isdisjoint(row.entry_ids): continue + manual = row.entry_ids in data.manual_keys + # §5 п. 2a: разносятся только строки между разными компаниями периметра; строка с контрагентом вне периметра + # или «сам на себя» в таблицу не попадает, если по ней нет ручного решения аналитика (AURORA-2034) + in_perimeter = ( + row.legal_entity_id in data.members + and row.counterparty_id in data.members + and row.legal_entity_id != row.counterparty_id + ) + if not manual and not in_perimeter: + continue rule_ids = rules_for(data.owners, row.account_id, row_side(row.closing_debit, row.closing_credit)) if ( - row.entry_ids not in data.manual_keys + not manual and len(rule_ids) == 1 - and ( - row.legal_entity_id in data.members - and row.counterparty_id in data.members - and (row.closing_debit not in (None, ZERO) or row.closing_credit not in (None, ZERO)) - ) + and (row.closing_debit not in (None, ZERO) or row.closing_credit not in (None, ZERO)) ): auto_rules[row.entry_ids] = next(iter(rule_ids)) - elif rule_ids or row.entry_ids in data.manual_keys: + elif rule_ids or manual: unassigned.add(row.entry_ids) return Distribution(selected, auto_rules, frozenset(unassigned), frozenset(used)) diff --git a/api/tests/test_vgo.py b/api/tests/test_vgo.py index 25bd82c..fe3d54e 100644 --- a/api/tests/test_vgo.py +++ b/api/tests/test_vgo.py @@ -131,6 +131,45 @@ def test_credit_row_is_matched_by_credit_side_rules_only() -> None: assert distribution.unassigned == {debit_row.entry_ids} +def test_rows_outside_perimeter_or_to_itself_are_not_unallocated() -> None: + """Разносятся только строки между компаниями периметра (§5 п. 2a): контрагент вне периметра и «сам на себя» + в нераспределённые не попадают; ручное решение аналитика по такой строке сохраняется (AURORA-2034).""" + rule_5, rule_6, account, company_a, company_b, outsider = (uuid.uuid4() for _ in range(6)) + inside = _row(uuid.uuid4(), company_a, company_b, 1) + outside = _row(uuid.uuid4(), company_a, outsider, 2) + to_itself = _row(uuid.uuid4(), company_a, company_a, 3) + to_itself_single = _row(uuid.uuid4(), company_b, company_b, 4) + outside_manual = _row(uuid.uuid4(), company_a, outsider, 5) + for row in (inside, outside, to_itself, to_itself_single, outside_manual): + row.account_id = account + owners = account_owners([(rule_5, {account}, set()), (rule_6, {account}, {account})]) + single = account_owners([(rule_6, {account}, set())]) + distribution = distribute_vgo_rows( + DistributionInput( + rows=[inside, outside, to_itself, outside_manual], + exact_ids=set(), + diff_ids=[], + manual_keys={outside_manual.entry_ids}, + owners=owners, + members={company_a, company_b}, + ) + ) + assert distribution.unassigned == {inside.entry_ids, outside_manual.entry_ids} + assert distribution.auto_rules == {} + single_distribution = distribute_vgo_rows( + DistributionInput( + rows=[to_itself_single], + exact_ids=set(), + diff_ids=[], + manual_keys=set(), + owners=single, + members={company_a, company_b}, + ) + ) + assert single_distribution.auto_rules == {} + assert single_distribution.unassigned == frozenset() + + async def test_unallocated_credit_row_offers_only_credit_side_rules(monkeypatch: pytest.MonkeyPatch) -> None: """Меню «Отнести к правилу» Кт-строки — только правила, где счёт на стороне кредита (AURORA-2034).""" version_id, company_a, company_b, rule_1, rule_2, rule_3, account = (uuid.uuid4() for _ in range(7))