arm-tabular-parser/api/tests/synthetic/generate_statements.py
Andrey Epifancev d64ba71098 test(statements): синтетика st01…st05 и сценарии спеки на ней; параметры statement_file
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 18:33:58 +04:00

170 lines
7.1 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.

"""Синтетические формы ББ/ОФР (Срез 2b): `python -m tests.synthetic.generate_statements <каталог>` из `api/`.
st01 — ББ и ОФР на одном листе; st02 — «csv-подобный» лист: заголовок формы, коды без подписи «Код», заголовок в две
строки, колонка «Изменение, %», отрицательные в скобках; st03 — форма без колонки кодов; st04 — книга с листом ОСВ
и листом баланса; st05 — одинокая страница 2. Детерминирован (см. `generate.py`).
"""
import argparse
import sys
from collections.abc import Callable, Sequence
from pathlib import Path
from tests.synthetic.generate import ORG, OsvSpec, SheetSpec, base_tree, build_osv, to_xlsx
__all__ = ["FILE_NAMES", "generate_all", "main"]
BALANCE_LINES = [
("Основные средства", "1150", 100, 90),
("Итого по разделу I", "1100", 100, 90),
("Запасы", "1210", 50, 40),
("Итого по разделу II", "1200", 50, 40),
("БАЛАНС", "1600", 150, 130),
("Уставный капитал", "1310", 150, 130),
("Итого по разделу III", "1300", 150, 130),
("БАЛАНС", "1700", 150, 130),
]
INCOME_LINES = [
("Выручка", "2110", 1000, 900),
("Себестоимость продаж", "2120", "(600)", "(500)"),
("Валовая прибыль (убыток)", "2100", 400, 400),
("Коммерческие расходы", "2210", "(50)", "(40)"),
("Управленческие расходы", "2220", "(50)", "(60)"),
("Прибыль (убыток) от продаж", "2200", 300, 300),
("Проценты к уплате", "2330", "(100)", "(100)"),
("Прибыль (убыток) до налогообложения", "2300", 200, 200),
("Налог на прибыль", "2410", "(40)", "(40)"),
("Чистая прибыль (убыток)", "2400", 160, 160),
] # полный набор: все жёсткие соотношения ОФР сходятся; скобки — запись 66н
def _head(sheet: SheetSpec, row: int, title: str, subtitle: str, okud: str) -> int:
sheet.put(row, 2, title)
sheet.put(row + 1, 2, subtitle)
sheet.put(row + 2, 6, "Форма по ОКУД")
sheet.put(row + 2, 7, okud)
sheet.put(row + 3, 1, "Организация")
sheet.put(row + 3, 3, ORG)
sheet.put(row + 3, 6, "ИНН")
sheet.put(row + 3, 7, "7700000000")
sheet.put(row + 4, 1, "Единица измерения:")
sheet.put(row + 4, 3, "тыс. руб.")
return row + 6
def _table(
sheet: SheetSpec, row: int, labels: Sequence[str], lines: Sequence[tuple], *, caption: str | None = "Код"
) -> int:
sheet.put(row, 2, "Наименование показателя")
if caption:
sheet.put(row, 4, caption)
for offset, label in enumerate(labels):
sheet.put(row, 5 + offset, label)
for index, (name, code, *values) in enumerate(lines, start=row + 1):
sheet.put(index, 2, name)
sheet.put(index, 4, code)
for offset, value in enumerate(values):
sheet.put(index, 5 + offset, value)
return row + 1 + len(lines)
def _st01() -> list[SheetSpec]:
sheet = SheetSpec("Отчетность")
row = _head(sheet, 1, "Бухгалтерский баланс", "на 31 декабря 2024 г.", "0710001")
row = _table(sheet, row, ["На 31 декабря 2024 г.", "На 31 декабря 2023 г."], BALANCE_LINES)
row = _head(sheet, row + 1, "Отчет о финансовых результатах", "за 2024 г.", "0710002")
_table(sheet, row, ["За 2024 г.", "За 2023 г."], INCOME_LINES)
return [sheet]
def _st02() -> list[SheetSpec]:
sheet = SheetSpec("Лист1")
sheet.put(1, 1, "Бухгалтерский баланс")
sheet.put(2, 1, "Организация")
sheet.put(2, 2, 'ООО "Синтетика"')
sheet.put(3, 1, "ИНН")
sheet.put(3, 2, "7700000001")
sheet.put(5, 1, "Показатель")
sheet.put(5, 3, "На 31 декабря")
sheet.put(5, 4, "На 31 декабря")
sheet.put(5, 5, "Изменение, %")
sheet.put(6, 3, "2024 г.")
sheet.put(6, 4, "2023 г.")
for index, (name, code, current, previous) in enumerate(BALANCE_LINES, start=7):
sheet.put(index, 1, name)
sheet.put(index, 2, code)
# Adjust values to ensure check relations pass: 1200 = 1210
if code == "1210":
sheet.put(index, 3, "(50)")
sheet.put(index, 4, "(40)")
elif code == "1200":
sheet.put(index, 3, "(50)")
sheet.put(index, 4, "(40)")
else:
sheet.put(index, 3, current)
sheet.put(index, 4, previous)
sheet.put(index, 5, 5)
return [sheet]
def _st03() -> list[SheetSpec]:
sheet = SheetSpec("ББ")
row = _head(sheet, 1, "Бухгалтерский баланс", "на 31 декабря 2024 г.", "0710001")
sheet.put(row, 2, "Показатель")
sheet.put(row, 5, "На 31 декабря 2024 г.")
for index, (name, _, current, _) in enumerate(BALANCE_LINES, start=row + 1):
sheet.put(index, 2, name)
sheet.put(index, 5, current)
return [sheet]
def _st04() -> list[SheetSpec]:
balance = SheetSpec("Баланс")
row = _head(balance, 1, "Бухгалтерский баланс", "на 31 декабря 2024 г.", "0710001")
_table(balance, row, ["На 31 декабря 2024 г."], [(name, code, current) for name, code, current, _ in BALANCE_LINES])
return [build_osv(OsvSpec(), base_tree()), balance]
def _st05() -> list[SheetSpec]:
sheet = SheetSpec("Лист_1")
sheet.put(1, 8, "Форма 0710001 с.2")
_table(sheet, 2, ["На 31 декабря 2024 г.", "На 31 декабря 2023 г."], BALANCE_LINES[5:])
return [sheet]
FILES: dict[str, tuple[Callable[[], list[SheetSpec]], Callable[[Sequence[SheetSpec]], bytes]]] = {
"st01_both_forms_one_sheet.xlsx": (_st01, to_xlsx),
"st02_csv_like.xlsx": (_st02, to_xlsx),
"st03_no_codes.xlsx": (_st03, to_xlsx),
"st04_mixed_book.xlsx": (_st04, to_xlsx),
"st05_continuation_only.xlsx": (_st05, to_xlsx),
}
FILE_NAMES = tuple(FILES)
def generate_all(directory: Path) -> list[Path]:
directory.mkdir(parents=True, exist_ok=True)
written: list[Path] = []
for name, (build, serialize) in FILES.items():
path = directory / name
path.write_bytes(serialize(build()))
written.append(path)
return written
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(
prog="python -m tests.synthetic.generate_statements", description="Синтетические формы st01…st05."
)
parser.add_argument(
"directory", type=Path, help="каталог назначения, например ../../fixtures/statements-xlsx/synthetic"
)
args = parser.parse_args(argv)
for path in generate_all(args.directory):
print(path)
return 0
if __name__ == "__main__":
sys.exit(main())