Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
"""Условие 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]
|