Раньше первый месяц диапазона давал «2024 Q4, нарастающий». Теперь словарь periods.yaml (months, range) разбирает диапазон: период режется по году конца, январь–конец квартала — нарастающий, ровно квартал — квартальный, остальное (скользящие 12 месяцев) — год конца, квартал и is_cumulative = null + предупреждение «не с начала года». Контракт v1.1 не менялся. Refs #11 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
532 lines
21 KiB
Python
532 lines
21 KiB
Python
"""Шапка документа и схема группировок (spec «Шапка документа и схема группировок», `05-domain-rules.md` §1.1, §1.5).
|
||
|
||
Сетки собираются `gridbuilder`; шапка и заголовок таблицы находятся по содержимому, позиции в сетках произвольные.
|
||
"""
|
||
|
||
import datetime
|
||
|
||
import pytest
|
||
from gridbuilder import M, at, sheet_from_rows
|
||
|
||
from src.parser.detect import TableHeader, find_table_header
|
||
from src.parser.grid import Sheet
|
||
from src.parser.header import DocHeader, build_group_schema, parse_doc_header
|
||
from src.parser.rules import Rules, load_rules
|
||
|
||
OPENING = "Сальдо на начало периода"
|
||
TURNOVER = "Обороты за период"
|
||
CLOSING = "Сальдо на конец периода"
|
||
MISSING_FIELDS_WARNINGS = 5 # организация, счёт, период, тип данных, валюта
|
||
VARIANT_2 = 2
|
||
DATA_ROW = at({1: "60", 5: 100, 8: 20, 10: 30, 12: 40, 15: 50, 17: 60})
|
||
SUBHEAD = ["Дебет", None, None, "Кредит", None, "Дебет", "Кредит", "Дебет", None, "Кредит"]
|
||
GROUPS_ROW = [M("Счет", 4), M(OPENING, 5), M(TURNOVER, 5), M(CLOSING, 3)]
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def rules() -> Rules:
|
||
return load_rules()
|
||
|
||
|
||
def _table(*label_rows: list[object]) -> list[list[object]]:
|
||
"""Заголовок таблицы варианта 1: строка групп, подзаголовки сумм в строке первой метки, остальные метки ниже."""
|
||
first, *rest = label_rows
|
||
return [
|
||
GROUPS_ROW,
|
||
[*first, *at({5: "Дебет", 8: "Кредит", 10: "Дебет", 12: "Кредит", 15: "Дебет", 17: "Кредит"})[len(first) :]],
|
||
*rest,
|
||
DATA_ROW,
|
||
]
|
||
|
||
|
||
def _parse(rows: list[list[object]], rules: Rules) -> tuple[Sheet, TableHeader, DocHeader]:
|
||
sheet = sheet_from_rows(rows)
|
||
header = find_table_header(sheet, rules)
|
||
return sheet, header, parse_doc_header(sheet, header, rules)
|
||
|
||
|
||
def _variant1(
|
||
rules: Rules, *, org: object = 'АО "Ромашка"', title: str, params: object = "Валюта БУ, НУ: RUB"
|
||
) -> DocHeader:
|
||
rows: list[list[object]] = [
|
||
[org],
|
||
[title],
|
||
[],
|
||
["Выводимые данные: БУ (данные бухгалтерского учета)"],
|
||
[],
|
||
["Параметры:", None, params],
|
||
[],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
return _parse(rows, rules)[2]
|
||
|
||
|
||
def _title(period: str, account: str = "60") -> str:
|
||
return f"Оборотно-сальдовая ведомость по счету {account} за {period}"
|
||
|
||
|
||
# --- Вариант 1: организация, счёт, период, тип данных, валюта ---
|
||
|
||
|
||
def test_variant1_header_fields(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("3 квартал 2024 г."), params="Валюта БУ, НУ: руб.")
|
||
|
||
assert header == DocHeader(
|
||
organization_name='АО "Ромашка"',
|
||
account_code_raw="60",
|
||
period_text="3 квартал 2024 г.",
|
||
period_year=2024,
|
||
period_quarter="Q3",
|
||
is_cumulative=False,
|
||
data_type="BU",
|
||
currency="RUB",
|
||
header_variant=1,
|
||
warnings=(),
|
||
)
|
||
|
||
|
||
def test_account_with_subaccount_is_kept_as_in_file(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("3 квартал 2024 г.", "58.03"))
|
||
|
||
assert header.account_code_raw == "58.03"
|
||
|
||
|
||
@pytest.mark.parametrize("currency", ["RUB", "rub", "руб.", "Руб", "рублей"])
|
||
def test_currency_spellings_give_rub(rules: Rules, currency: str) -> None:
|
||
header = _variant1(rules, title=_title("2024 г."), params=f"Валюта БУ, НУ: {currency}")
|
||
|
||
assert header.currency == "RUB"
|
||
assert not any("алют" in warning for warning in header.warnings)
|
||
|
||
|
||
def test_other_currency_code_is_kept(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("2024 г."), params="Валюта БУ, НУ: USD")
|
||
|
||
assert header.currency == "USD"
|
||
|
||
|
||
def test_currency_value_in_neighbour_cell(rules: Rules) -> None:
|
||
rows: list[list[object]] = [
|
||
["Организация"],
|
||
[_title("2024 г.")],
|
||
["Параметры:", None, "Валюта БУ, НУ:", None, "RUB"],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
|
||
assert _parse(rows, rules)[2].currency == "RUB"
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("text", "expected"),
|
||
[
|
||
("БУ (данные бухгалтерского учета)", "BU"),
|
||
("НУ (данные налогового учета)", "NU"),
|
||
("сумма", "SUM"),
|
||
],
|
||
)
|
||
def test_data_type_values(rules: Rules, text: str, expected: str) -> None:
|
||
rows: list[list[object]] = [
|
||
["Организация"],
|
||
[_title("2024 г.")],
|
||
[f"Выводимые данные: {text}"],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
|
||
assert _parse(rows, rules)[2].data_type == expected
|
||
|
||
|
||
# --- Период: все строки таблицы §1.5 ---
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("period", "quarter", "cumulative"),
|
||
[
|
||
("1 квартал 2025 г.", "Q1", True),
|
||
("I кв. 2025 г.", "Q1", True),
|
||
("3 месяца 2025 г.", "Q1", True),
|
||
("январь-март 2025 г.", "Q1", True),
|
||
("01.01.2025 - 31.03.2025", "Q1", True),
|
||
("2 квартал 2025 г.", "Q2", False),
|
||
("II кв. 2025 г.", "Q2", False),
|
||
("полугодие 2025 г.", "Q2", True),
|
||
("1 полугодие 2025 г.", "Q2", True),
|
||
("6 месяцев 2025 г.", "Q2", True),
|
||
("январь-июнь 2025 г.", "Q2", True),
|
||
("3 квартал 2025 г.", "Q3", False),
|
||
("III кв. 2025 г.", "Q3", False),
|
||
("9 месяцев 2025 г.", "Q3", True),
|
||
("январь-сентябрь 2025 г.", "Q3", True),
|
||
("4 квартал 2025 г.", "Q4", False),
|
||
("IV кв. 2025 г.", "Q4", False),
|
||
("2025 год", "Q4", True),
|
||
("2025 г.", "Q4", True),
|
||
("12 месяцев 2025 г.", "Q4", True),
|
||
("январь-декабрь 2025 г.", "Q4", True),
|
||
("2025 год (годовая)", "Q4", True),
|
||
],
|
||
)
|
||
def test_period_table_of_domain_rules(rules: Rules, period: str, quarter: str, cumulative: bool) -> None:
|
||
header = _variant1(rules, title=_title(period))
|
||
|
||
assert (header.period_quarter, header.is_cumulative, header.period_year) == (quarter, cumulative, 2025)
|
||
assert header.warnings == ()
|
||
|
||
|
||
def test_period_text_ends_at_year_marker(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("1 квартал 2025 г. (по подразделениям)"))
|
||
|
||
assert header.period_text == "1 квартал 2025 г."
|
||
|
||
|
||
def test_period_without_year_marker_runs_to_line_end(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("с 01.01.2025 по 31.03.2025"))
|
||
|
||
assert header.period_text == "с 01.01.2025 по 31.03.2025"
|
||
assert header.period_quarter == "Q1"
|
||
|
||
|
||
def test_unrecognised_period_text_gives_none_and_warning(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("неделю"))
|
||
|
||
assert header.period_text == "неделю"
|
||
assert (header.period_quarter, header.is_cumulative, header.period_year) == (None, None, None)
|
||
assert any("период" in warning for warning in header.warnings)
|
||
|
||
|
||
# --- Отсутствующие поля шапки ---
|
||
|
||
|
||
def test_missing_organization_gives_empty_string_and_warning(rules: Rules) -> None:
|
||
rows: list[list[object]] = [[_title("2024 г.")], ["Выводимые данные: БУ"], *_table(["Контрагенты"])]
|
||
|
||
header = _parse(rows, rules)[2]
|
||
|
||
assert header.organization_name == ""
|
||
assert any("организац" in warning for warning in header.warnings)
|
||
assert header.account_code_raw == "60"
|
||
|
||
|
||
def test_empty_head_area_gives_empty_strings_none_and_warnings(rules: Rules) -> None:
|
||
header = _parse(_table(["Контрагенты"]), rules)[2]
|
||
|
||
assert header.organization_name == ""
|
||
assert header.account_code_raw == ""
|
||
assert header.period_text == ""
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (None, None, None)
|
||
assert (header.data_type, header.currency, header.header_variant) == (None, None, 1)
|
||
assert len(header.warnings) == MISSING_FIELDS_WARNINGS
|
||
assert all(warning for warning in header.warnings)
|
||
|
||
|
||
def test_numeric_and_date_cells_in_head_do_not_break_parsing(rules: Rules) -> None:
|
||
rows: list[list[object]] = [
|
||
[45658], # xlsb: даты остаются числами
|
||
["Организация", None, 45659],
|
||
[_title("2024 г.")],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
|
||
header = _parse(rows, rules)[2]
|
||
|
||
assert header.organization_name == "Организация"
|
||
assert header.period_quarter == "Q4"
|
||
|
||
|
||
def test_organization_is_first_text_cell_that_is_not_a_marker(rules: Rules) -> None:
|
||
rows: list[list[object]] = [
|
||
[],
|
||
["Оборотно-сальдовая ведомость"],
|
||
["Организация Б"],
|
||
["Организация В"],
|
||
[_title("2024 г.")],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
|
||
assert _parse(rows, rules)[2].organization_name == "Организация Б"
|
||
|
||
|
||
# --- Схема группировок ---
|
||
|
||
|
||
def test_group_schema_variant1_account_counterparty_contract(rules: Rules) -> None:
|
||
sheet, header, doc = _parse(
|
||
[["Организация"], [_title("2024 г.")], *_table(["Контрагенты"], ["Договоры"])],
|
||
rules,
|
||
)
|
||
|
||
groups, variant = build_group_schema(sheet, header, rules)
|
||
|
||
assert variant == doc.header_variant == 1
|
||
assert [(g.type, g.name, g.has_inn) for g in groups] == [
|
||
("ACCOUNT", "Счет", None),
|
||
("COUNTERPARTY", "Контрагенты", None),
|
||
("CONTRACT", "Договоры", None),
|
||
]
|
||
|
||
|
||
def test_group_schema_inn_in_separate_cell_of_counterparty_row(rules: Rules) -> None:
|
||
rows: list[list[object]] = [
|
||
["Организация"],
|
||
[_title("2024 г.")],
|
||
*_table(["Подразделение"], [M("Контрагенты", 2), "ИНН"]),
|
||
]
|
||
sheet, header, _ = _parse(rows, rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert [(g.type, g.has_inn) for g in groups] == [("ACCOUNT", None), ("DEPARTMENT", None), ("COUNTERPARTY", True)]
|
||
|
||
|
||
def test_group_schema_inn_in_the_label_cell(rules: Rules) -> None:
|
||
rows: list[list[object]] = [["Организация"], [_title("2024 г.")], *_table(["Контрагенты, ИНН"])]
|
||
sheet, header, _ = _parse(rows, rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert [(g.type, g.name, g.has_inn) for g in groups] == [
|
||
("ACCOUNT", "Счет", None),
|
||
("COUNTERPARTY", "Контрагенты, ИНН", True),
|
||
]
|
||
|
||
|
||
def test_first_group_is_account_whatever_its_text_says(rules: Rules) -> None:
|
||
rows: list[list[object]] = [
|
||
["Организация"],
|
||
[_title("2024 г.")],
|
||
[M("Подразделение", 4), M(OPENING, 5), M(TURNOVER, 5), M(CLOSING, 3)],
|
||
["Контрагенты", *at({5: "Дебет", 8: "Кредит", 10: "Дебет", 12: "Кредит", 15: "Дебет", 17: "Кредит"})[1:]],
|
||
DATA_ROW,
|
||
]
|
||
sheet, header, _ = _parse(rows, rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert [(g.type, g.name) for g in groups] == [("ACCOUNT", "Подразделение"), ("COUNTERPARTY", "Контрагенты")]
|
||
|
||
|
||
def test_unknown_group_text_is_other(rules: Rules) -> None:
|
||
rows: list[list[object]] = [["Организация"], [_title("2024 г.")], *_table(["Основные средства"])]
|
||
sheet, header, _ = _parse(rows, rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert [g.type for g in groups] == ["ACCOUNT", "OTHER"]
|
||
|
||
|
||
def test_without_inn_cell_has_inn_stays_unset(rules: Rules) -> None:
|
||
rows: list[list[object]] = [["Организация"], [_title("2024 г.")], *_table(["Контрагенты"], ["Договоры"])]
|
||
sheet, header, _ = _parse(rows, rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert all(g.has_inn is None for g in groups)
|
||
|
||
|
||
# --- Вариант 2 ---
|
||
|
||
|
||
def _variant2_rows(detail: str = "Детализация по субсчетам, субконто: Контрагенты, Договоры") -> list[list[object]]:
|
||
return [
|
||
["ООО Вторая"],
|
||
["Оборотно-сальдовая ведомость по счету 60"],
|
||
["Период: 1 квартал 2026 г."],
|
||
[detail],
|
||
["Выводимые данные: сумма"],
|
||
[M("Субконто", 4), *GROUPS_ROW[1:]],
|
||
["", *at({5: "Дебет", 8: "Кредит", 10: "Дебет", 12: "Кредит", 15: "Дебет", 17: "Кредит"})[1:]],
|
||
DATA_ROW,
|
||
]
|
||
|
||
|
||
def test_variant2_header(rules: Rules) -> None:
|
||
sheet, header, doc = _parse(_variant2_rows(), rules)
|
||
|
||
groups, variant = build_group_schema(sheet, header, rules)
|
||
|
||
assert variant == doc.header_variant == VARIANT_2
|
||
assert [(g.type, g.name) for g in groups] == [
|
||
("ACCOUNT", "Субконто"),
|
||
("COUNTERPARTY", "Контрагенты"),
|
||
("CONTRACT", "Договоры"),
|
||
]
|
||
assert doc.organization_name == "ООО Вторая"
|
||
assert doc.account_code_raw == "60"
|
||
assert (doc.period_text, doc.period_year, doc.period_quarter, doc.is_cumulative) == (
|
||
"1 квартал 2026 г.",
|
||
2026,
|
||
"Q1",
|
||
True,
|
||
)
|
||
assert doc.data_type == "SUM"
|
||
assert doc.currency is None
|
||
assert any("алют" in warning for warning in doc.warnings)
|
||
|
||
|
||
def test_variant2_single_item(rules: Rules) -> None:
|
||
sheet, header, _ = _parse(_variant2_rows("Детализация по субсчетам, субконто: Контрагенты"), rules)
|
||
|
||
groups, variant = build_group_schema(sheet, header, rules)
|
||
|
||
assert variant == VARIANT_2
|
||
assert [g.type for g in groups] == ["ACCOUNT", "COUNTERPARTY"]
|
||
|
||
|
||
def test_variant2_detail_row_is_not_taken_for_organization(rules: Rules) -> None:
|
||
rows = _variant2_rows()
|
||
rows[0] = []
|
||
|
||
doc = _parse(rows, rules)[2]
|
||
|
||
assert doc.organization_name == ""
|
||
|
||
|
||
# --- Название организации со словами «за» и «валюта» (T5.1) ---
|
||
|
||
|
||
@pytest.mark.parametrize("organization", ["ООО «За Родину»", "Валюта Трейд", "За Урожай ЗАО"])
|
||
def test_organization_name_with_marker_words_does_not_hijack_period_or_currency(
|
||
rules: Rules, organization: str
|
||
) -> None:
|
||
header = _variant1(rules, org=organization, title=_title("3 квартал 2024 г."))
|
||
|
||
assert header.organization_name == organization
|
||
assert header.period_text == "3 квартал 2024 г."
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2024, "Q3", False)
|
||
assert header.currency == "RUB"
|
||
assert header.warnings == ()
|
||
|
||
|
||
def test_organization_with_currency_word_and_no_currency_row_is_still_organization(rules: Rules) -> None:
|
||
rows: list[list[object]] = [["Валюта Трейд"], [_title("2024 г.")], *_table(["Контрагенты"])]
|
||
|
||
header = _parse(rows, rules)[2]
|
||
|
||
assert header.organization_name == "Валюта Трейд"
|
||
assert header.currency is None
|
||
assert any("алют" in warning for warning in header.warnings)
|
||
assert header.period_quarter == "Q4"
|
||
|
||
|
||
# --- Диапазоны дат с «г.» (T5.2) ---
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("period", "quarter"),
|
||
[
|
||
("01.01.2025 г. - 31.03.2025 г.", "Q1"),
|
||
("с 01.01.2025 г. по 30.06.2025 г.", "Q2"),
|
||
("01.01.2025 г. по 31.12.2025 г.", "Q4"),
|
||
("01.01.2025 - 30.09.2025 г. (по подразделениям)", "Q3"),
|
||
],
|
||
)
|
||
def test_date_range_with_year_marks_is_kept_whole(rules: Rules, period: str, quarter: str) -> None:
|
||
header = _variant1(rules, title=_title(period))
|
||
|
||
assert header.period_text == period.removesuffix(" (по подразделениям)")
|
||
assert (header.period_quarter, header.is_cumulative, header.period_year) == (quarter, True, 2025)
|
||
assert header.warnings == ()
|
||
|
||
|
||
# --- Диапазон месяцев с годами: скользящий период (issue #11 п. 1, OQ-131) ---
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"period",
|
||
[
|
||
"Октябрь 2024 г. - Сентябрь 2025 г.",
|
||
"с октября 2024 г. по сентябрь 2025 г.",
|
||
"октябрь 2024 - сентябрь 2025",
|
||
"Октябрь 2024 г. - Сентябрь 2025 г. (по подразделениям)",
|
||
],
|
||
)
|
||
def test_rolling_period_is_not_read_as_first_month_quarter(rules: Rules, period: str) -> None:
|
||
header = _variant1(rules, title=_title(period))
|
||
|
||
assert header.period_text == period.removesuffix(" (по подразделениям)")
|
||
# Конец диапазона определяет год; квартал и «нарастающий» не определены: 12 месяцев — не квартал и не с начала года
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2025, None, None)
|
||
assert any("не с начала года" in warning and "12 мес" in warning for warning in header.warnings)
|
||
|
||
|
||
def test_month_range_of_one_quarter_is_a_quarter_not_cumulative(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("Апрель 2025 г. - Июнь 2025 г."))
|
||
|
||
assert header.period_text == "Апрель 2025 г. - Июнь 2025 г."
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2025, "Q2", False)
|
||
assert header.warnings == ()
|
||
|
||
|
||
def test_month_range_inside_year_not_from_january_is_not_a_quarter(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("Апрель 2025 г. - Сентябрь 2025 г."))
|
||
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2025, None, None)
|
||
assert any("не с начала года" in warning for warning in header.warnings)
|
||
|
||
|
||
def test_month_range_from_january_to_quarter_end_stays_cumulative(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("Январь 2025 г. - Сентябрь 2025 г."))
|
||
|
||
assert header.period_text == "Январь 2025 г. - Сентябрь 2025 г."
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2025, "Q3", True)
|
||
assert header.warnings == ()
|
||
|
||
|
||
def test_month_range_from_january_to_mid_quarter_has_no_quarter(rules: Rules) -> None:
|
||
header = _variant1(rules, title=_title("Январь 2025 г. - Май 2025 г."))
|
||
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (2025, None, None)
|
||
assert any("период" in warning for warning in header.warnings)
|
||
|
||
|
||
# --- Даты числами / датами (xlsb) (T5.5) ---
|
||
|
||
|
||
def test_period_carried_only_by_date_cells_is_not_read(rules: Rules) -> None:
|
||
"""Период «за» + ячейки-даты (xlsb не отдаёт форматы, даты — числа): текста периода нет, это не ошибка разбора."""
|
||
rows: list[list[object]] = [
|
||
["Организация"],
|
||
["Оборотно-сальдовая ведомость по счету 60 за", datetime.date(2025, 1, 1), datetime.date(2025, 3, 31)],
|
||
*_table(["Контрагенты"]),
|
||
]
|
||
|
||
header = _parse(rows, rules)[2]
|
||
|
||
assert header.organization_name == "Организация"
|
||
assert header.account_code_raw == "60"
|
||
assert header.period_text == ""
|
||
assert (header.period_year, header.period_quarter, header.is_cumulative) == (None, None, None)
|
||
assert any("период" in warning for warning in header.warnings)
|
||
|
||
|
||
# --- Вариант 2: ИНН (T5.4) ---
|
||
|
||
|
||
def _variant2_with_inn_column() -> list[list[object]]:
|
||
rows = _variant2_rows()
|
||
rows[6] = [
|
||
"",
|
||
None,
|
||
"ИНН",
|
||
None,
|
||
*at({5: "Дебет", 8: "Кредит", 10: "Дебет", 12: "Кредит", 15: "Дебет", 17: "Кредит"})[4:],
|
||
]
|
||
return rows
|
||
|
||
|
||
def test_variant2_inn_column_marks_the_first_counterparty(rules: Rules) -> None:
|
||
sheet, header, _ = _parse(_variant2_with_inn_column(), rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert header.inn_col is not None
|
||
assert [(g.type, g.has_inn) for g in groups] == [("ACCOUNT", None), ("COUNTERPARTY", True), ("CONTRACT", None)]
|
||
|
||
|
||
def test_variant2_without_inn_column_leaves_has_inn_unset(rules: Rules) -> None:
|
||
sheet, header, _ = _parse(_variant2_rows(), rules)
|
||
|
||
groups, _ = build_group_schema(sheet, header, rules)
|
||
|
||
assert header.inn_col is None
|
||
assert all(g.has_inn is None for g in groups)
|