synthgen: тесты ОСВ сверяют контрагентов с книгой и вариантные имена (#38)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6fb42be8ff
commit
7a4a44a6b3
@ -47,6 +47,7 @@ class OsvRow:
|
||||
cd: Decimal # сальдо на конец, Дт
|
||||
cc: Decimal # сальдо на конец, Кт
|
||||
is_cp: bool = False # строка контрагента
|
||||
cp: str | None = None # код аналитики контрагента в книге (для сверки с Ledger)
|
||||
|
||||
@property
|
||||
def six(self) -> Six:
|
||||
@ -196,7 +197,7 @@ def rows(ledger: Ledger, account: str, period: Period, member: Member, group: Gr
|
||||
for name, inn, cp in named:
|
||||
six = by_cp[cp]
|
||||
total = _add(total, six)
|
||||
out.append(OsvRow(level, name, inn, *six, is_cp=True))
|
||||
out.append(OsvRow(level, name, inn, *six, is_cp=True, cp=cp))
|
||||
return out, total
|
||||
|
||||
def build(code: str, level: int) -> tuple[list[OsvRow], Six]:
|
||||
|
||||
@ -103,19 +103,46 @@ def test_no_inn_file(tmp_path, osv_world):
|
||||
|
||||
|
||||
def test_rows_match_ledger(osv_world):
|
||||
member, period, data, group, books = _pick(osv_world, "62", inn=True)
|
||||
"""Каждая строка контрагента равна Ledger.balance/turnover по ключу (субсчёт, контрагент)."""
|
||||
cfg, group, periods, books = osv_world
|
||||
checked = 0
|
||||
for member in group.members:
|
||||
ledger = books[member.code]
|
||||
bal0 = ledger.balance(period.start - timedelta(days=1), "62")
|
||||
bal1 = ledger.balance(period.end, "62")
|
||||
for period in periods:
|
||||
data = osv.rows(ledger, "62", period, member, group)
|
||||
opening = ledger.balance(period.start - timedelta(days=1), "62")
|
||||
closing = ledger.balance(period.end, "62")
|
||||
turn = ledger.turnover(period.start, period.end, "62")
|
||||
cps = [r for r in data if r.is_cp]
|
||||
assert cps
|
||||
for key, (td, tc) in turn.items():
|
||||
assert key[1] is not None
|
||||
assert sum(r.td for r in cps) == sum(t[0] for t in turn.values())
|
||||
assert sum(r.tc for r in cps) == sum(t[1] for t in turn.values())
|
||||
assert sum(r.cd - r.cc for r in cps) == sum(bal1.values())
|
||||
assert sum(r.od - r.oc for r in cps) == sum(bal0.values())
|
||||
|
||||
def fold(src, pick):
|
||||
out = {}
|
||||
for (code, an), v in src.items():
|
||||
key = (code, an.cp if an else None)
|
||||
out[key] = out.get(key, ZERO) + pick(v)
|
||||
return out
|
||||
|
||||
want = {
|
||||
"o": fold(opening, lambda v: v), "c": fold(closing, lambda v: v),
|
||||
"td": fold(turn, lambda v: v[0]), "tc": fold(turn, lambda v: v[1]),
|
||||
}
|
||||
code = None
|
||||
seen = set()
|
||||
for r in data:
|
||||
if not r.is_cp:
|
||||
code = r.label
|
||||
continue
|
||||
key = (code, r.cp)
|
||||
seen.add(key)
|
||||
assert r.od - r.oc == want["o"].get(key, ZERO), (member.code, period.key, key)
|
||||
assert r.cd - r.cc == want["c"].get(key, ZERO), (member.code, period.key, key)
|
||||
assert r.td == want["td"].get(key, ZERO) and r.tc == want["tc"].get(key, ZERO), (member.code, period.key, key)
|
||||
checked += 1
|
||||
# обратное: всё ненулевое из книги попало в строки
|
||||
for kind, src in want.items():
|
||||
for key, v in src.items():
|
||||
if v != 0:
|
||||
assert key in seen, (member.code, period.key, kind, key)
|
||||
assert checked > 20
|
||||
|
||||
|
||||
def test_parent_sums(osv_world):
|
||||
@ -138,19 +165,30 @@ def test_parent_sums(osv_world):
|
||||
|
||||
|
||||
def test_group_names(osv_world):
|
||||
"""В ОСВ без ИНН ровно один член группы записан вариантным именем, остальные — `name_1c`; с ИНН — все `name_1c`."""
|
||||
cfg, group, periods, books = osv_world
|
||||
by_code = {m.code: m for m in group.members}
|
||||
variants_seen = 0
|
||||
for member in group.members:
|
||||
data = osv.rows(books[member.code], "62", periods[-1], member, group)
|
||||
names = [r.label for r in data if r.is_cp]
|
||||
others = [m for m in group.members if m.code != member.code]
|
||||
present = [m for m in others if m.name_1c in names or osv.variant_spelling(m) in names]
|
||||
if member.code in group.no_inn:
|
||||
variant = next(m for m in group.members if m.code == group.variant_name[member.code])
|
||||
assert not any(m.name_1c in names for m in others if m is variant)
|
||||
assert all(r.inn is None for r in data)
|
||||
for account in ("60", "62", "76"):
|
||||
for period in periods:
|
||||
data = osv.rows(books[member.code], account, period, member, group)
|
||||
cps = {r.cp: r for r in data if r.is_cp}
|
||||
for m in others:
|
||||
if m.code not in cps:
|
||||
continue
|
||||
r = cps[m.code]
|
||||
if member.code in group.no_inn and group.variant_name[member.code] == m.code:
|
||||
assert r.label == osv.variant_spelling(m) != m.name_1c
|
||||
variants_seen += 1
|
||||
else:
|
||||
assert all(m.name_1c in names for m in present)
|
||||
assert all(r.inn for r in data if r.is_cp)
|
||||
assert r.label == m.name_1c, (member.code, account, period.key, m.code)
|
||||
assert r.inn == (None if member.code in group.no_inn else m.inn)
|
||||
if member.code in group.no_inn:
|
||||
variant = by_code[group.variant_name[member.code]]
|
||||
assert sum(1 for r in data if r.is_cp and r.label == variant.name_1c) == 0
|
||||
assert variants_seen > 0, "вариантное написание нигде не встретилось"
|
||||
|
||||
|
||||
def test_osv_without_externals(tmp_path):
|
||||
|
||||
@ -25,6 +25,7 @@ def _run(path: Path) -> dict:
|
||||
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)
|
||||
|
||||
|
||||
@ -61,6 +62,7 @@ def test_parser_inn(parsed, osv_world):
|
||||
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 got, (member.code, account, period.key)
|
||||
if with_inn:
|
||||
for r in got:
|
||||
if r["counterparty_name"] in group_names and r["counterparty_inn"] != group_names[r["counterparty_name"]]:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user