116 lines
6.2 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.

from decimal import Decimal as D
from arm_statements.header import parse_header
from arm_statements.loader import Rules
from arm_statements.model import RowRole
from arm_statements.segments import find_layout
from arm_statements.tests.builders import excel, hdr, ocr, place
DATES3 = ["На 30 сентября 2025 г.", "На 31 декабря 2024 г.", "На 31 декабря 2023 г."]
def test_npz_sheet_with_own_header_maps_by_date(rules: Rules) -> None:
first = [
place({1: "Наименование показателя", 12: "Код", 16: DATES3[0], 21: DATES3[1], 24: DATES3[2]}),
place({1: "Нематериальные активы", 12: "1110", 16: D("10"), 21: D("9"), 24: D("8")}),
place({1: "Итого по разделу I", 12: "1100", 16: D("10"), 21: D("9"), 24: D("8")}),
]
sheet4 = [
place({0: "Наименование показателя", 6: "Код", 8: DATES3[0], 9: DATES3[1], 10: DATES3[2]}),
place({0: "Заемные средства", 6: "1510", 8: D("3"), 9: D("2"), 10: D("1")}),
]
cand = excel([("Бухгалтерский баланс", first), ("Бухгалтерский баланс. Лист 4", sheet4)])
layout = find_layout(cand, parse_header(hdr(report_date="30.09.2025"), rules), rules)
assert [c.column_index for c in layout.columns] == [16, 21, 24]
assert layout.segments[1].value_cols == ((8, 16), (9, 21), (10, 24))
assert not [w for w in layout.warnings if w.code.startswith("SEGMENT_")]
def test_repeat_header_mid_table(rules: Rules) -> None:
rows = [
["Наименование показателя", "Код", "На 30.09.2025", "На 31.12.2024"],
["Запасы", "1210", "5", "4"],
["Наименование показателя", "Код", "На 30.09.2025", "На 31.12.2024"],
["Итого", "1200", "5", "4"],
]
layout = find_layout(ocr(rows), parse_header(hdr(), rules), rules)
assert [s.first_row for s in layout.segments] == [0, 2]
assert layout.roles[2] is RowRole.HEADER
def test_repeat_header_without_dates_maps_by_order(rules: Rules) -> None:
rows = [
["Наименование", "Код", "На 30.09.2025", "На 31.12.2024"],
["Запасы", "1210", "5", "4"],
["Наименование", "Код", "отчетн", "прошл"],
["Итого", "1200", "5", "4"],
]
layout = find_layout(ocr(rows), parse_header(hdr(), rules), rules)
assert layout.segments[1].value_cols == ((2, 2), (3, 3))
assert layout.warnings == ()
def test_wider_segment_is_mismatch(rules: Rules) -> None:
rows = [
["Наименование", "Код", "На 30.09.2025", "На 31.12.2024", "На 31.12.2023"],
["Запасы", "1210", "5", "4", "3"],
["Наименование", "Код", "графа 3", "графа 4", "графа 5", "графа 6"],
["Итого", "1200", "5", "4", "3", "2"],
]
layout = find_layout(ocr(rows), parse_header(hdr(), rules), rules)
(warning,) = [w for w in layout.warnings if w.code == "SEGMENT_COLUMNS_MISMATCH"]
assert warning.severity.value == "error" and "5" in warning.message
assert layout.segments[1].value_cols == ((2, 2), (3, 3), (4, 4)) and layout.segments[1].extra_cols == (5,)
def test_second_sheet_single_code_in_other_column(rules: Rules) -> None:
first = [
place({0: "Наименование", 3: "Код", 4: "За 9 месяцев 2025 г.", 5: "За 9 месяцев 2024 г."}),
place({0: "Выручка", 3: "2110", 4: D("100"), 5: D("90")}),
place({0: "Себестоимость", 3: "2120", 4: D("-60"), 5: D("-50")}),
place({0: "Валовая прибыль", 3: "2100", 4: D("40"), 5: D("40")}),
]
second = [place({0: "Чистая прибыль", 1: "2400", 2: D("7"), 3: D("6")})]
cand = excel([("ОФР", first), ("ОФР_2", second)])
layout = find_layout(cand, parse_header(hdr(), rules), rules)
assert layout.segments[1].code_col == 1
assert layout.segments[1].value_cols == ((2, 4), (3, 5))
assert not [w for w in layout.warnings if w.code.startswith(("SEGMENT_", "HEADER_"))]
def test_header_not_found_anywhere(rules: Rules) -> None:
rows = [["Запасы", "1210", "5", "4"], ["Дебиторы", "1230", "3", "2"], ["Итого", "1200", "8", "6"]]
layout = find_layout(ocr(rows), parse_header(hdr(), rules), rules)
assert [w.code for w in layout.warnings] == ["HEADER_ROW_NOT_FOUND"]
assert [(c.column_index, c.label) for c in layout.columns] == [(2, ""), (3, "")]
def test_segment_without_codes(rules: Rules) -> None:
first = [["Наименование", "Код", "На 30.09.2025"], ["Запасы", "1210", "5"], ["Итого", "1200", "5"]]
tail = [["Примечание к форме", "", "7"]]
layout = find_layout(excel([("Баланс", first), ("Хвост", tail)]), parse_header(hdr(), rules), rules)
assert [w.code for w in layout.warnings] == ["SEGMENT_WITHOUT_CODES"]
assert layout.roles[3] is RowRole.DATA and layout.code_found
def test_first_segment_without_header_columns_from_first_headed(rules: Rules) -> None:
"""P1-18: колонки документа задаёт первый сегмент с заголовком, у первого значения — по порядку."""
rows = [
["Запасы", "1210", "5", "4"],
["Дебиторы", "1230", "3", "2"],
["Итого", "1200", "8", "6"],
["Наименование", "Код", "На 30.09.2025", "На 31.12.2024"],
["Прочее", "1250", "1", "1"],
]
layout = find_layout(ocr(rows), parse_header(hdr(), rules), rules)
assert [s.first_row for s in layout.segments] == [0, 3]
assert [c.column_index for c in layout.columns] == [2, 3]
assert layout.segments[0].value_cols == ((2, 2), (3, 3))
assert layout.warnings == ()
def test_no_codes_anywhere_has_no_segment_warnings(rules: Rules) -> None:
layout = find_layout(ocr([["Просто", "текст"], ["ещё", "строка"]]), parse_header(hdr(), rules), rules)
assert not layout.code_found and layout.columns == () and layout.warnings == ()
assert layout.roles == (RowRole.DATA, RowRole.DATA)