105 lines
5.0 KiB
Python
105 lines
5.0 KiB
Python
"""Разрез кандидата по смене формы (AURORA-1734, решение владельца 06.11.2026)."""
|
||
|
||
from datetime import date
|
||
|
||
from arm_statements import 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)
|