Andrey Epifancev e6696fa440 fix(statements): cleanup guard tests — удалены ложноположительные тесты (Fix round 2)
Обнаружено: test_inn_from_auditor_above_organization_inn_line не тестирует guard,
т.к. org INN всегда раньше auditor block в реальных файлах. Без guard первый найденный
INN всё равно организации (появляется раньше). Guard не differentiates этот сценарий.

Удалены:
- test_inn_skips_auditor_block — ложноположительный, docstring утверждал Review Focus,
  но тест проходит из-за порядка (org INN раньше auditor), не из-за guard.
  Функциональность покрыта test_one_c_header.
- test_inn_from_auditor_above_organization_inn_line — guard не differentiates;
  добавлен в попытке тестировать сценарий, невозможный по дизайну.

Оставлен test_inn_from_auditor_block_only_is_none — единственный тест, где guard
реально нужен: без org INN, только auditor блок → guard исключает его, возвращает None.

Guard работает корректно: тестирует edge case отсутствия org INN.

Итог: 6 тестов, все честны относительно требований к guard.

Refs #14

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 16:35:55 +04:00

187 lines
8.9 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.

"""Шапка формы по меткам (design D-5; spec «Шапка документа»)."""
import datetime
import pytest
from gridbuilder import at, sheet_from_rows
from src.parser.rules import Rules, load_rules
from src.parser.statements.detect import find_forms
from src.parser.statements.header import parse_header
@pytest.fixture(scope="module")
def rules() -> Rules:
return load_rules()
ONE_C = [
[*at({9: "Бухгалтерский баланс"})],
[*at({9: "на 31 декабря 2024 г."})],
[*at({34: "Коды"})],
[*at({33: "Форма по ОКУД", 34: "0710001"})],
[*at({33: "Дата (число, месяц, год)", 34: "31", 37: "12", 40: "2024"})],
[*at({2: "Организация", 4: 'Акционерное общество "Унагранде Компани"', 33: "по ОКПО", 34: "47888105"})],
[*at({2: "Идентификационный номер налогоплательщика", 33: "ИНН", 34: "3249004697"})],
[
*at(
{
2: "Вид экономической\nдеятельности",
7: "Производство сыра",
30: "по\nОКВЭД 2",
34: "10.51.3",
}
)
],
[*at({2: "Организационно-правовая форма / форма собственности", 34: "12267", 39: "16"})],
[*at({2: "Непубличные акционерные общества", 14: " /", 15: "Частная собственность", 33: "по ОКОПФ / ОКФС"})],
[*at({2: "Единица измерения:", 7: "в тыс. рублей", 33: "по ОКЕИ", 34: "384"})],
[*at({2: "Местонахождение (адрес)"})],
[*at({2: "242440, Брянская обл, г Севск, ул Ленина, д. 130"})],
[*at({2: "Наименование аудиторской организации"})],
[*at({2: 'АО "ЮНИКОН"'})],
[*at({2: "Идентификационный номер налогоплательщика аудиторской организации", 33: "ИНН", 34: "7716021332"})],
[*at({3: "Пояснения", 5: "Наименование показателя", 22: "Код", 25: "На 31 декабря 2024 г."})],
[*at({5: "Нематериальные активы", 22: "1110", 25: 1})],
]
def _block(sheet, rules): # type: ignore[no-untyped-def]
return find_forms(sheet, rules)[0][0]
def test_one_c_header(rules: Rules) -> None:
sheet = sheet_from_rows(ONE_C)
header, warnings = parse_header(sheet, rules, 1, 16, _block(sheet, rules))
assert header.form_name == "Бухгалтерский баланс" and header.okud == "0710001"
assert header.organization_name == 'Акционерное общество "Унагранде Компани"'
assert header.inn == "3249004697" # Review Focus 2: не ИНН аудитора
assert header.okpo == "47888105"
assert header.okopf == "12267" and header.okfs == "16"
assert header.units == "в тыс. рублей"
assert header.report_date_text == "на 31 декабря 2024 г." and header.report_date == datetime.date(2024, 12, 31)
assert header.address == "242440, Брянская обл, г Севск, ул Ленина, д. 130"
assert header.organization_name_confidence == 100 and header.inn_confidence == 100 # noqa: PLR2004
assert warnings == []
def test_organization_above_label_and_income_period(rules: Rules) -> None:
sheet = sheet_from_rows(
[
[*at({3: "Отчет о финансовых результатах"})],
[*at({3: "за январь - сентябрь 2025 г."})],
[*at({13: "Форма по ОКУД", 14: "0710002"})],
[*at({7: "Отчетная дата\n(число, месяц, год)", 14: "30", 15: "09", 16: "2025"})],
[*at({7: 'ООО "НОВОРОССИЙСКИЙ ПРОКАТНЫЙ ЗАВОД"'})],
[*at({3: "Организация:", 13: "по ОКПО", 14: "16098811"})],
[*at({3: "Единица измерения", 8: "тыс. руб.", 13: "по ОКЕИ", 14: "384"})],
[
*at(
{
3: "Пояснения",
5: "Наименование показателя",
10: "Код",
11: "За январь - сентябрь 2025 г.",
}
)
],
]
)
header, _ = parse_header(sheet, rules, 1, 7, _block(sheet, rules))
assert header.organization_name == 'ООО "НОВОРОССИЙСКИЙ ПРОКАТНЫЙ ЗАВОД"'
assert header.inn is None
assert header.okpo == "16098811"
assert header.units == "тыс. руб."
assert header.report_date == datetime.date(2025, 9, 30)
assert header.report_date_text == "за январь - сентябрь 2025 г."
def test_date_from_numeric_triple_and_name_below_label(rules: Rules) -> None:
sheet = sheet_from_rows(
[
[*at({2: "БУХГАЛТЕРСКИЙ БАЛАНС"})],
[*at({17: "Форма по ОКУД", 18: "0710001"})],
[*at({17: "Дата (число, месяц, год)", 18: 30, 19: 9, 20: 2025})],
[
*at(
{
2: "Организация (орган исполнительной власти)",
7: 'ООО "АГРОФИРМА "МЕЖЕНИНОВСКАЯ"',
17: "по ОКПО",
18: "85262693",
}
)
],
[*at({2: "Идентификационный номер налогоплательщика", 17: "ИНН", 18: "7016006635"})],
[*at({2: "Организационно-правовая форма***", 7: "ООО", 15: "по ОКОПФ/\nОКФС", 18: "65", 19: "16"})],
[*at({2: "Единица измерения:", 7: "тыс. руб", 17: "по ОКЕИ", 18: "384"})],
[*at({2: "Местонахождение (адрес)", 7: "636141, Томская обл"})],
]
)
header, _ = parse_header(sheet, rules, 1, 8, _block(sheet, rules))
assert header.report_date == datetime.date(2025, 9, 30)
assert header.report_date_text is None
assert header.inn == "7016006635"
assert header.okopf == "65" and header.okfs == "16"
assert header.address == "636141, Томская обл"
def test_continuation_header_is_empty(rules: Rules) -> None:
sheet = sheet_from_rows(
[
[*at({17: "Форма 0710001 с.2"})],
["Пояснения", "Наименование показателя", "Код", "На 31 декабря 2024 г."],
]
)
header, warnings = parse_header(sheet, rules, 1, 1, None)
assert header.form_name == "Форма 0710001 с.2"
assert header.organization_name is None and header.inn is None
assert header.report_date is None and warnings == []
def test_missing_units_and_organization_warn(rules: Rules) -> None:
sheet = sheet_from_rows([["Бухгалтерский баланс"], ["на 31 декабря 2024 г."]])
header, warnings = parse_header(sheet, rules, 1, 2, _block(sheet, rules))
assert header.units is None and header.organization_name is None
assert any("единица измерения" in warning for warning in warnings)
assert any("организация" in warning for warning in warnings)
def test_inn_from_auditor_block_only_is_none(rules: Rules) -> None:
"""Guard test (a): without org INN, only auditor block ИНН — should be None."""
sheet = sheet_from_rows(
[
[*at({3: "Отчет о финансовых результатах"})],
[*at({3: "за январь - сентябрь 2025 г."})],
[*at({13: "Форма по ОКУД", 14: "0710002"})],
[*at({7: 'ООО "ПРОКАТНЫЙ ЗАВОД"'})],
[*at({3: "Организация:", 13: "по ОКПО", 14: "16098811"})],
[*at({3: "Единица измерения", 8: "тыс. руб.", 13: "по ОКЕИ", 14: "384"})],
[*at({2: "Наименование аудиторской организации"})],
[*at({2: 'АО "АУДИТ-КОМПАНИЯ"'})],
[
*at(
{
2: "Идентификационный номер налогоплательщика аудиторской организации",
33: "ИНН",
34: "9999999999",
}
)
],
]
)
header, _ = parse_header(sheet, rules, 1, 9, _block(sheet, rules))
assert header.inn is None, f"Should not pick up auditor INN 9999999999, got {header.inn}"