"""Условие RECOGNIZED при приёме 2.0 (05-domain-rules §9.3; Р-16, Р-18, Р-19, Р-28).""" from decimal import Decimal import pytest from src.domain.statement_lines import load_line_catalog from src.domain.statement_status import ( NameCheckLine, duplicate_period_forms, form_status_v2, has_error_warning, name_mismatches, osv_status_v2, ) from src.models.enums import DocumentCheckResult, DocumentStatus, DocumentType, PeriodQuarter BAL, INC = DocumentType.BALANCE, DocumentType.INCOME Q3 = PeriodQuarter.Q3 Q4 = PeriodQuarter.Q4 CATALOG = load_line_catalog() OK = dict( service_done=True, file_error_warning=False, counterparty=DocumentCheckResult.MATCH, period_known=True, names_ok=True, duplicate_in_file=False, ) def test_form_recognized() -> None: assert form_status_v2(**OK) is DocumentStatus.RECOGNIZED # type: ignore[arg-type] @pytest.mark.parametrize( "change", [ {"service_done": False}, {"file_error_warning": True}, {"counterparty": DocumentCheckResult.MISMATCH}, {"period_known": False}, {"names_ok": False}, {"duplicate_in_file": True}, ], ) def test_form_needs_check(change: dict) -> None: assert form_status_v2(**OK | change) is DocumentStatus.NEEDS_CHECK # type: ignore[arg-type] @pytest.mark.parametrize("cp", [None, DocumentCheckResult.UNKNOWN]) def test_unknown_counterparty_allowed(cp: DocumentCheckResult | None) -> None: assert form_status_v2(**OK | {"counterparty": cp}) is DocumentStatus.RECOGNIZED # type: ignore[arg-type] def test_duplicate_period_forms() -> None: forms = [ (1, BAL, 2025, Q3), (2, BAL, 2025, Q3), (3, BAL, 2024, Q4), (4, INC, 2025, Q3), (5, BAL, 2025, None), (6, BAL, 2025, None), ] assert duplicate_period_forms(forms) == {1, 2} def test_has_error_warning() -> None: assert has_error_warning([{"code": "JSON_FAILED", "severity": "error", "message": "m"}]) assert not has_error_warning([{"code": "LEGACY_WARNING", "severity": "info", "message": "m"}, "строка"]) assert not has_error_warning(["error", 5, None, {"severity": "ERROR"}]) assert not has_error_warning(None) assert not has_error_warning({"severity": "error"}) OSV_OK = dict( osv_done=True, checks_ok=True, file_error_warning=False, counterparty=DocumentCheckResult.MATCH, sheet_mapping_warnings=False, ) def test_osv_recognized() -> None: assert osv_status_v2(**OSV_OK) is DocumentStatus.RECOGNIZED # type: ignore[arg-type] assert osv_status_v2(**OSV_OK | {"counterparty": None}) is DocumentStatus.RECOGNIZED # type: ignore[arg-type] @pytest.mark.parametrize( "change", [ {"osv_done": False}, {"checks_ok": False}, {"file_error_warning": True}, {"counterparty": DocumentCheckResult.MISMATCH}, {"sheet_mapping_warnings": True}, ], ) def test_osv_needs_check(change: dict) -> None: assert osv_status_v2(**OSV_OK | change) is DocumentStatus.NEEDS_CHECK # type: ignore[arg-type] def _line(key: int, code: str | None, conf: str | None, name: str | None, *, fixed: bool = False, status: str = "ok"): # noqa: PLR0913 return NameCheckLine(key, status, code, None if conf is None else Decimal(conf), name, fixed) def test_name_mismatches() -> None: lines = [ _line(1, "1120", "50", "Нематериальные активы"), _line(2, "1220", "50", "НДС, начисленный по отгрузке"), _line(3, "1200", "50", "Итого по разделу I"), _line(4, "1500", "56", "Итого по разделу V1"), _line(5, "1105", "67", "АКТИВ I. Внеоборотные активы Гудвил"), _line(6, "1120", "50", "Нематериальные активы", fixed=True), _line(7, "1110", "100", "Чужое имя"), _line(8, "9999", "50", "Что угодно"), _line(9, "1120", "50", ""), _line(10, None, None, "Без кода", status="unreadable"), ] assert name_mismatches(lines, CATALOG, "BALANCE") == [1, 3, 8, 9] def test_name_mismatches_missing_confidence_is_confident() -> None: """T10.1: `line_code_confidence = None` не даёт `NAME_MISMATCH` (спека: `confidence < 100`).""" assert name_mismatches([_line(1, "1120", None, "Чужое имя")], CATALOG, "BALANCE") == []