104 lines
4.7 KiB
Python
104 lines
4.7 KiB
Python
"""Плоская форма ОСВ 1С (#59, change osv-flat-form): стратегия `header_account` и счёт шапки у строк."""
|
||
|
||
import pytest
|
||
from gridbuilder import L, M, sheet_from_rows
|
||
|
||
from src.contract.result import OsvDocument
|
||
from src.parser.deadline import Deadline
|
||
from src.parser.osv import OsvParser
|
||
from src.parser.rules import Rules, load_rules
|
||
|
||
TIMEOUT_SECONDS = 60
|
||
HEADER_ACCOUNT_CODE = "OSV_HIERARCHY_BY_HEADER_ACCOUNT"
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def rules() -> Rules:
|
||
return load_rules()
|
||
|
||
|
||
def _table_head() -> list[list[object]]:
|
||
return [
|
||
["Субконто", M("Сальдо на начало периода", 2), M("Обороты за период", 2), M("Сальдо на конец периода", 2)],
|
||
[None, "Дебет", "Кредит", "Дебет", "Кредит", "Дебет", "Кредит"],
|
||
]
|
||
|
||
|
||
def _parse(rules: Rules, rows: list[list[object]]) -> tuple[OsvDocument, list[str]]:
|
||
result = OsvParser(rules).parse_sheet(sheet_from_rows(rows), Deadline(TIMEOUT_SECONDS))
|
||
return result.document, [note.code for note in result.notes]
|
||
|
||
|
||
def test_flat_list_s40(rules: Rules) -> None:
|
||
"""Образец s40: счёт «по счету: 76.Н.1», вид «Контрагенты», организация ниже периода, данные без оформления."""
|
||
rows: list[list[object]] = [
|
||
[],
|
||
["Оборотно-сальдовая ведомость по счету: 76.Н.1"],
|
||
["Контрагенты"],
|
||
["за 2025 г."],
|
||
['ООО "Товарное хозяйство"'],
|
||
[],
|
||
["Единица измерения: рубль (код по ОКЕИ 383)"],
|
||
[],
|
||
*_table_head(),
|
||
["Август - Агро ООО", 1000, None, 500, 300, 1200, None],
|
||
["АВИС ООО", None, 200, 100, 400, None, 500],
|
||
["Агроаспект ООО", None, None, 50, 50, None, None],
|
||
["Итого развернутое", 1000, 200, 650, 750, 1200, 500],
|
||
["Итого", 800, None, 650, 750, 700, None],
|
||
]
|
||
|
||
document, codes = _parse(rules, rows)
|
||
|
||
assert document.status == "done"
|
||
assert HEADER_ACCOUNT_CODE in codes
|
||
data = [item for item in document.rows if item.group_type != "TOTAL"]
|
||
assert [item.row_value for item in data] == ["Август - Агро ООО", "АВИС ООО", "Агроаспект ООО"]
|
||
assert all(item.level == 0 and item.parent_row_number is None for item in data)
|
||
assert {item.account_code_raw for item in data} == {"76.Н.1"}
|
||
assert document.header.organization_name == 'ООО "Товарное хозяйство"'
|
||
|
||
|
||
def test_flat_list_s41(rules: Rules) -> None:
|
||
"""Образец s41 (R-10): схема [ACCOUNT, COUNTERPARTY («Организации»)], счёт шапки «60»."""
|
||
rows: list[list[object]] = [
|
||
[],
|
||
["Оборотно-сальдовая ведомость по счету: 60"],
|
||
["Организации"],
|
||
["за 2 Квартал 2026 г. План счетов ООО Хреновская Нива"],
|
||
[],
|
||
*_table_head(),
|
||
["Автодор-Платные Дороги ООО", 820, None, 5000, 4991, 829, None],
|
||
["АГРОПРОФИ ООО", None, 2764, None, None, None, 2764],
|
||
["Итого развернутое", 820, 2764, 5000, 4991, 829, 2764],
|
||
["Итого", None, 1944, 5000, 4991, None, 1935],
|
||
]
|
||
|
||
document, _ = _parse(rules, rows)
|
||
|
||
assert document.status == "done"
|
||
data = [item for item in document.rows if item.group_type != "TOTAL"]
|
||
assert [item.account_code_raw for item in data] == ["60", "60"]
|
||
assert [item.group_type for item in data] == ["COUNTERPARTY", "COUNTERPARTY"]
|
||
|
||
|
||
def test_outline_wins_over_header_account_and_rows_inherit_header_code(rules: Rules) -> None:
|
||
"""Review Focus 4: счёт в шапке, строк-счетов нет, outline есть — `outline`, но строки наследуют счёт шапки."""
|
||
rows: list[list[object]] = [
|
||
["ООО Зерновая"],
|
||
["Оборотно-сальдовая ведомость по счету 60"],
|
||
["Период: 9 месяцев 2025 г."],
|
||
[],
|
||
*_table_head(),
|
||
[L(0), "ООО А", 100, None, 50, 30, 120, None],
|
||
[L(1), "Договор 1", 100, None, 50, 30, 120, None],
|
||
[L(0), "Итого", 100, None, 50, 30, 120, None],
|
||
]
|
||
|
||
document, codes = _parse(rules, rows)
|
||
|
||
assert "OSV_HIERARCHY_BY_OUTLINE" in codes
|
||
assert HEADER_ACCOUNT_CODE not in codes
|
||
data = [item for item in document.rows if item.group_type != "TOTAL"]
|
||
assert [item.account_code_raw for item in data] == ["60", "60"]
|