114 lines
5.6 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.

"""Разрез кандидата по смене формы (AURORA-1734, решение владельца 06.11.2026)."""
from datetime import date
from arm_statements import WarningCode, make_warning, normalize_candidate, split_by_form
from arm_statements.loader import Rules
from arm_statements.model import DocumentType, Evidence
from tests.builders import excel, hdr, ocr
BAL_HEAD = ["Наименование показателя", "Коды", "На 30 июня 2026 года", "На 31 декабря 2025 года"]
INC_HEAD = ["Наименование показателя", "Коды", "За I полугодие 2026 года", "За I полугодие 2025 года"]
HEADER = hdr(organization_name="КФХ «АК-ЮЛ»", inn="5634000539", report_date="30.06.2026", units="тыс. руб.")
LOCATOR = "file:x.json;table:0"
def balance_rows() -> list[list[str | None]]:
"""ББ на две страницы: актив, повторная шапка, пассив."""
return [
BAL_HEAD,
["НМА", "1110", "10", "10"],
["Основные средства", "1150", "90", "90"],
["Итого по разделу I", "1100", "100", "100"],
["Запасы", "1210", "50", "50"],
["Итого по разделу II", "1200", "50", "50"],
["БАЛАНС", "1600", "150", "150"],
BAL_HEAD,
["Уставный капитал", "1310", "150", "150"],
["Итого по разделу III", "1300", "150", "150"],
["БАЛАНС", "1700", "150", "150"],
]
def income_rows() -> list[list[str | None]]:
return [
INC_HEAD,
["Выручка", "2110", "4451", "2162"],
["Себестоимость продаж", "2120", "-2700", "-1332"],
["Валовая прибыль (убыток)", "2100", "1751", "830"],
["Чистая прибыль (убыток)", "2400", "1751", "830"],
]
def test_balance_and_income_in_one_table_split(rules: Rules) -> None:
rows = [*balance_rows(), *income_rows()]
cand = ocr(rows, header=HEADER, type_hint=DocumentType.BALANCE, title="Бухгалтерский баланс", locator=LOCATOR)
parts = split_by_form(cand, rules)
assert len(parts) == 2 # noqa: PLR2004 - две формы
bal, inc = parts
assert bal.locator == LOCATOR and bal.type_hint is DocumentType.BALANCE and bal.evidence == cand.evidence
assert len(bal.rows) == len(balance_rows())
assert inc.locator == f"{LOCATOR};form:1"
assert inc.type_hint is None and inc.evidence == Evidence(okud=None, title=None) and inc.warnings == ()
assert inc.rows[0] == cand.rows[len(balance_rows())]
assert inc.header == cand.header and inc.source is cand.source
bal_doc = normalize_candidate(bal, rules).document
inc_doc = normalize_candidate(inc, rules).document
assert bal_doc is not None and inc_doc is not None
assert bal_doc.document_type is DocumentType.BALANCE
assert not [line.line_code for line in bal_doc.lines if (line.line_code or "").startswith("2")]
assert inc_doc.document_type is DocumentType.INCOME
assert [c.period_date for c in inc_doc.columns] == [date(2026, 6, 30), date(2025, 6, 30)]
assert "TYPE_CONFLICT" not in [w.code for w in inc_doc.warnings]
def test_same_form_repeat_header_not_split(rules: Rules) -> None:
cand = ocr(balance_rows(), header=HEADER, type_hint=DocumentType.BALANCE, locator=LOCATOR)
result = split_by_form(cand, rules)
assert result == (cand,)
assert result[0] is cand
def test_income_tail_25xx_not_split(rules: Rules) -> None:
tail = [
INC_HEAD,
["Результат от переоценки", "2510", "0", "0"],
["Результат от прочих операций", "2520", "0", "0"],
["Совокупный финансовый результат", "2500", "1751", "830"],
]
cand = ocr([*income_rows(), *tail], locator=LOCATOR)
assert split_by_form(cand, rules) == (cand,)
def test_two_foreign_codes_not_split(rules: Rules) -> None:
foreign = [INC_HEAD, ["Выручка", "2110", "4451", "2162"], ["Чистая прибыль (убыток)", "2400", "1751", "830"]]
cand = ocr([*balance_rows(), *foreign], locator=LOCATOR)
assert split_by_form(cand, rules) == (cand,)
def test_foreign_segment_without_header_not_split(rules: Rules) -> None:
cand = ocr([*balance_rows(), *income_rows()[1:]], locator=LOCATOR)
assert split_by_form(cand, rules) == (cand,)
def test_excel_breaks_shifted(rules: Rules) -> None:
continuation = [["Прочие доходы", "2340", "1", "1"], ["Прочие расходы", "2350", "-1", "-1"]]
cand = excel([("Б", balance_rows()), ("О", income_rows()), ("О2", continuation)], header=HEADER)
cut = len(balance_rows())
assert cand.segment_breaks == (cut, cut + len(income_rows()))
bal, inc = split_by_form(cand, rules)
assert bal.segment_breaks == ()
assert inc.segment_breaks == (0, len(income_rows()))
assert inc.locator == f"{cand.locator};form:1"
assert len(inc.rows) == len(income_rows()) + len(continuation)
def test_xlsb_source_warning_kept_in_every_part(rules: Rules) -> None:
"""`XLSB_NO_FORMATS` — свойство источника, нужно каждой части для проверки масштаба (ревью I-2)."""
no_formats = make_warning(WarningCode.XLSB_NO_FORMATS, "xlsb без числовых форматов", rules)
cand = excel([("Б", balance_rows()), ("О", income_rows())], header=HEADER, warnings=[no_formats])
bal, inc = split_by_form(cand, rules)
assert bal.warnings == (no_formats,)
assert inc.warnings == (no_formats,)