arm-ka-fixtures/synthgen/tests/test_economy.py
Andrey Epifancev 418f066bd8 synthgen: периоды, внешние контрагенты, экономика компаний (#38)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 23:49:07 +04:00

121 lines
4.5 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.

from datetime import date
from decimal import Decimal
import pytest
from synthgen import dictionary
from synthgen.config import Config
from synthgen.counterparties import plan as make_plan
from synthgen.economy import build_ledgers, last_day, months
from synthgen.group import build_group
from synthgen.periods import book_start, build_periods
D = Decimal
def _build(**kw):
cfg = Config(group_size=kw.pop("group_size", 3), periods=kw.pop("periods", 4), osv_counterparties_mean=kw.pop("mean", 20), **kw)
g = build_group(cfg, dictionary.load())
ps = build_periods(cfg.start, cfg.periods)
pl = make_plan(cfg, g)
return cfg, g, ps, pl, build_ledgers(cfg, g, ps, pl)
@pytest.fixture(scope="module")
def built():
return _build()
def test_books_balanced(built):
cfg, g, ps, pl, books = built
start = book_start(ps)
for lg in books.values():
for day in (start, date(2025, 3, 31), date(2025, 12, 31)):
assert sum(lg.balance(day).values()) == 0
assert lg.balance(day, "000") == {}
assert not [p for p in lg.postings if "000" in (p.dt, p.ct) and p.day > start]
def test_bs_identity(built):
cfg, g, ps, pl, books = built
for lg in books.values():
for p in ps:
day = p.end
for acct in ("90", "91"):
assert lg.balance(day, acct) == {}
if p.quarter == 4:
assert lg.balance(day, "99") == {}
bal = lg.balance(day)
assert sum(v for (a, _), v in bal.items() if a != "000") == 0
# активы не уходят в минус: денежные средства на конец каждого месяца
for y, m in months(ps):
assert lg.balance(last_day(y, m), "51").get(("51", None), D(0)) >= 0
def test_externals_used(built):
cfg, g, ps, pl, books = built
for (code, acct), lst in pl.by_file.items():
if not lst or acct not in ("58", "60", "62", "66", "67", "76"):
continue
seen = set()
for p in books[code].postings:
for an in (p.dt_an, p.ct_an):
if an is not None and an.cp:
seen.add(an.cp)
used = sum(1 for o in lst if o.inn in seen)
assert used >= 0.8 * len(lst), (code, acct, used, len(lst))
def test_pareto_shape(built):
cfg, g, ps, pl, books = built
tot = {code: sum(c for (a, _), (_, c) in lg.turnover(date(2025, 1, 1), date(2025, 12, 31), "62").items()) for code, lg in books.items()}
biggest = max(tot, key=tot.get)
by_cp = {}
for (a, an), (dt, ct) in books[biggest].turnover(date(2025, 1, 1), date(2025, 12, 31), "62").items():
if an is not None:
by_cp[an.cp] = by_cp.get(an.cp, D(0)) + dt + ct
vals = sorted(by_cp.values(), reverse=True)
top = max(1, len(vals) // 10)
assert sum(vals[:top]) >= D("0.5") * sum(vals)
def test_signs_variety(built):
cfg, g, ps, pl, books = built
for acct in ("60", "62"):
signs = set()
for lg in books.values():
for v in lg.balance(date(2025, 12, 31), acct).values():
signs.add(v > 0)
assert signs == {True, False}, acct
def test_year_rollover():
cfg, g, ps, pl, books = _build(start="2025-Q3")
dec = date(2025, 12, 31)
for lg in books.values():
assert lg.balance(dec, "90") == {} and lg.balance(dec, "91") == {} and lg.balance(dec, "99") == {}
assert lg.balance(dec, "84") != lg.balance(date(2024, 12, 31), "84")
t = lg.turnover(date(2026, 1, 1), date(2026, 1, 1), "90")
assert t == {} # 2026 начинается с нуля; обороты 90 в январе — не 1 января
jan = lg.turnover(date(2026, 1, 1), date(2026, 1, 31), "90")
assert any(dt or ct for dt, ct in jan.values())
assert max(p.day for lg in books.values() for p in lg.postings) == date(2026, 6, 30)
def test_extra_hook_called_before_close():
cfg = Config(group_size=2, periods=1, osv_counterparties_mean=5)
g = build_group(cfg, dictionary.load())
ps = build_periods(cfg.start, 1)
seen = []
def hook(ledgers, year, month):
seen.append((year, month, len(ledgers["C01"].postings)))
build_ledgers(cfg, g, ps, make_plan(cfg, g), extra=hook)
assert [(y, m) for y, m, _ in seen] == [(2025, 1), (2025, 2), (2025, 3)]
def test_deterministic_and_zero_mean():
a = _build(mean=0)[4]
b = _build(mean=0)[4]
assert [p for p in a["C01"].postings] == [p for p in b["C01"].postings]
assert a["C01"].postings