arm-core/api/tests/test_form_confirmable.py
Andrey Epifancev 588c098838 refactor(statements): form_confirmable — условия подтверждения формы для ручного и автоматического пути (#64)
Условия подтверждения формы ББ/ОФР вынесены в statement_hard_failures и form_confirmable
(модульные функции services/statements.py); ручной путь (documents._check_statement) использует
тот же statement_hard_failures и отвечает 409. Тест с нарушением строгого соотношения на 2200
(соотношения с 2400 в словаре мягкие).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:57:35 +04:00

84 lines
3.9 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.

"""Условия подтверждения формы ББ/ОФР — общие для ручного и автоматического пути (OQ-157, design D-3; D-9 Среза 3b)."""
import json
from pathlib import Path
import pytest
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from src.models.directories import LegalEntity
from src.models.enums import DocumentType
from src.models.files import Document
from src.models.statements import FinancialStatementLine, FinancialStatementMeta, FinancialStatementValue
from src.services.statements import form_confirmable, statement_hard_failures
from tests.factories import make_legal_entity
from tests.fixtures_helpers import GOLDEN_STATEMENTS, map_golden_snapshot
pytestmark = [pytest.mark.integration, pytest.mark.fixtures]
MEZH = GOLDEN_STATEMENTS / "mezheninovskaya" / "2025-Q3"
SYNTH = GOLDEN_STATEMENTS / "synthetic"
async def _owner(session: AsyncSession, path: Path) -> LegalEntity:
"""Владелец с ИНН шапки формы (MATCH)."""
data = json.loads(path.read_text("utf-8"))
inn = next((d["header"].get("inn") for d in data["documents"] if d.get("header", {}).get("inn")), None)
return await make_legal_entity(session, name="ООО Владелец", inn=inn)
async def _income(session: AsyncSession, fixtures_dir: Path) -> Document:
path = fixtures_dir / MEZH / "1_Форма2_бух_09.json"
(income,) = await map_golden_snapshot(session, entity=await _owner(session, path), path=path, uploaded_by=None)
assert income.document_type is DocumentType.INCOME
return income
async def test_clean_form_is_confirmable(seeded: AsyncSession, fixtures_dir: Path) -> None:
assert await form_confirmable(seeded, await _income(seeded, fixtures_dir)) is True
async def test_form_without_period_is_not_confirmable(seeded: AsyncSession, fixtures_dir: Path) -> None:
income = await _income(seeded, fixtures_dir)
income.period_quarter = None
await seeded.flush()
assert await form_confirmable(seeded, income) is False
async def test_form_with_hard_failure_is_not_confirmable_until_accepted(
seeded: AsyncSession, fixtures_dir: Path
) -> None:
income = await _income(seeded, fixtures_dir)
meta = (
await seeded.execute(select(FinancialStatementMeta).where(FinancialStatementMeta.document_id == income.id))
).scalar_one()
# ломаем итог 2200 во всех колонках: строгое соотношение «2200 = 2100 - 2210 - 2220» перестаёт сходиться
# (соотношения с 2400 в словаре мягкие — strict: false, поэтому на них жёсткое нарушение не проверить)
values = (
await seeded.execute(
select(FinancialStatementValue)
.join(FinancialStatementLine, FinancialStatementLine.id == FinancialStatementValue.line_id)
.where(FinancialStatementLine.meta_id == meta.id, FinancialStatementLine.line_code == "2200")
)
).scalars()
for value in values:
value.amount = (value.amount or 0) + 1000
await seeded.flush()
assert await statement_hard_failures(seeded, income, meta) != ()
assert await form_confirmable(seeded, income) is False
meta.checks_accepted_at = income.created_at # акцепт: любые непустые дата и автор
await seeded.flush()
assert await form_confirmable(seeded, income) is True
async def test_disclosure_is_confirmable(seeded: AsyncSession, fixtures_dir: Path) -> None:
owner = await make_legal_entity(seeded, name="ООО Синтетика")
documents = await map_golden_snapshot(
seeded, entity=owner, path=fixtures_dir / SYNTH / "st06_header_variants.json", uploaded_by=None
)
disclosure = next(d for d in documents if d.document_type is DocumentType.DISCLOSURE)
assert await form_confirmable(seeded, disclosure) is True