arm-ka-fixtures/synthgen/tests/test_osv_parser.py
Andrey Epifancev 6fb42be8ff synthgen: ОСВ в оформлении 1С (#38)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 00:20:44 +04:00

88 lines
3.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.

"""Приёмка ОСВ парсером: CLI tabular-parser из зеркального worktree (`TP_WT`), маркер `parser`.
Запуск: `$UVRUN python -m pytest synthgen/tests -q -m parser`. Без окружения тесты пропускаются.
"""
import json
import os
import subprocess
from decimal import Decimal
from pathlib import Path
import pytest
from synthgen import osv
pytestmark = pytest.mark.parser
TP_WT = Path(os.environ.get("TP_WT") or "/Users/andrey.epifancev/projects/awp-ca/.wt/synthetic-group/tabular-parser")
PY = TP_WT / "api" / ".venv" / "bin" / "python"
ACCOUNTS = ("60", "62", "76")
def _run(path: Path) -> dict:
env = {**os.environ, "ENV_FILE": str(TP_WT / "api" / ".env.test"), "PYTHONPATH": str(TP_WT / "api")}
proc = subprocess.run(
[str(PY), "-m", "src.parser.cli", str(path)], cwd=TP_WT / "api", env=env, capture_output=True, text=True, check=False
)
return json.loads(proc.stdout)
@pytest.fixture(scope="module")
def parsed(osv_world, tmp_path_factory):
if not PY.exists():
pytest.skip(f"нет окружения парсера: {PY}")
cfg, group, periods, books = osv_world
root = tmp_path_factory.mktemp("osv")
out = []
for member in group.members:
with_inn = member.code not in group.no_inn
for account in ACCOUNTS:
for period in (periods[0], periods[-1]):
data = osv.rows(books[member.code], account, period, member, group)
path = root / f"{member.code}_{period.key}_{osv.file_name(account, period)}"
osv.write(path, member, account, period, data, with_inn=with_inn)
out.append((member, account, period, data, with_inn, _run(path)))
return out
def test_parser_status_and_account(parsed):
bad = []
for member, account, period, _, _, res in parsed:
docs = res.get("documents", [])
if res.get("status") != "done" or len(docs) != 1 or docs[0]["header"]["account_code_raw"] != account:
bad.append((member.code, account, period.key, res.get("status"), res.get("error")))
assert not bad, bad[:10]
def test_parser_inn(parsed, osv_world):
_, group, _, _ = osv_world
group_names = {m.name_1c: m.inn for m in group.members}
bad = []
for member, account, period, data, with_inn, res in parsed:
got = [r for r in res["documents"][0]["rows"] if r["group_type"] == "COUNTERPARTY" and r["data_type"] == "BU"]
if with_inn:
for r in got:
if r["counterparty_name"] in group_names and r["counterparty_inn"] != group_names[r["counterparty_name"]]:
bad.append((member.code, account, r["counterparty_name"], r["counterparty_inn"]))
elif any(r["counterparty_inn"] for r in got):
bad.append((member.code, account, "ИНН у ОСВ без ИНН"))
assert not bad, bad[:10]
def test_parser_totals(parsed):
"""Сумма корневой строки счёта в документе = «Итого» листа = суммам `rows`; проверки парсера чистые."""
keys = ("opening_debit", "opening_credit", "turnover_debit", "turnover_credit", "closing_debit", "closing_credit")
cent = Decimal("0.01")
bad = []
for member, account, period, data, _, res in parsed:
doc = res["documents"][0]
checks = doc["checks"]
if not checks["total_row_matches_root"] or checks["parent_sum_violations"] or checks["balance_identity_violations"]:
bad.append((member.code, account, period.key, checks))
root = next(r for r in doc["rows"] if r["group_type"] == "ACCOUNT" and r["data_type"] == "BU")
got = tuple(Decimal(str(root[k] or 0)).quantize(cent) for k in keys)
if got != tuple(v.quantize(cent) for v in osv.total_of(data)):
bad.append((member.code, account, period.key, got))
assert not bad, bad[:10]