93 lines
4.3 KiB
Python
93 lines
4.3 KiB
Python
"""Приёмка ОСВ парсером: 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
|
||
from synthgen.counterparties import OSV_ACCOUNTS
|
||
|
||
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 = OSV_ACCOUNTS # все счета набора (решение 43: приёмка расширена с 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
|
||
)
|
||
assert proc.stdout.strip(), proc.stderr
|
||
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"]
|
||
assert bool(got) == any(r.is_cp for r in data), (member.code, account, period.key) # 01/80/90 без контрагентов
|
||
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))
|
||
if not data: # пустая ОСВ: только «Итого»
|
||
continue
|
||
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]
|