Files
solduri2roa/tests/test_lacerta.py
Marius Mutu 7ac40f9a34 FDB: postarile direct pe sintetic raman brute; conturile 0/0 nu intra
Pe drumul FDB sinteticul e suma bruta a notelor, deci diferenta fata de
analitice se scrie cu D si C separat, nu pe net: rulajul 401 LACERTA iese ca
in SAGA (8.991,55 / 57.253,22). PDF/xls raman pe net (etalonul trece).
Conturile nepartener fara sold si fara rulaj (891) nu mai intra in xlsx.

LACERTA: 51/53 frunze identice cu REF; restul sunt asezari alese (5121,
4428), cu totaluri egale pe cont.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYeAtVxeS8m4oXekjX8Am2
2026-09-27 16:58:46 +03:00

153 lines
6.1 KiB
Python

"""Poarta pe o firma reala cu analitice: LACERTA, drumul FDB, 06/2026.
Ruleaza ca subprocese, pornind de la copia bazei primita de la client (in stare
`full shutdown`, deci exerseaza si repunerea online pe copia temporara):
py extrage.py --fisier exemple/lacerta_CONT_BAZA-20-08_2026.FDB --firma LACERTA --an 2026 --luna 6
py genereaza.py --firma LACERTA --an 2026 --luna 6
si compara frunzele BALANTA cu importul facut din exportul SAGA (REF, in exemple/).
Analiza completa: docs/analiza_lacerta_fdb.md.
REF nu e un etalon curat. Diferentele de mai jos sunt explicate acolo si scrise aici
explicit; orice alta diferenta pica testul. Nu se adauga exceptii ca sa treaca.
Fisierele clientului nu sunt in git (nume, CNP-uri): lipsa lor = test sarit.
"""
import collections
import pathlib
import subprocess
import sys
import tempfile
import unittest
import openpyxl
RADACINA = pathlib.Path(__file__).resolve().parent.parent
if str(RADACINA) not in sys.path:
sys.path.insert(0, str(RADACINA))
import mapare
EXEMPLE = RADACINA / "exemple"
BAZA = EXEMPLE / "lacerta_CONT_BAZA-20-08_2026.FDB"
REF = EXEMPLE / "lacerta_init_facturi_balanta_note-20-08_2026.xlsx"
AN, LUNA = 2026, 6
# deciziile de mapare din REF, pentru analiticele care nu intra in acont
CORECTII = [
["419.NETOPIA", "419", "2", None, "ca in REF"],
# REF are un singur rand 4428; 01 ca sa iasa frunze curate langa diferenta 4428/99
["4428.TOATE", "4428", "01", None, "decizie 26.09.2026"],
# analitic functional pe cont cu parteneri: ramane analitic (decizie 27.09.2026).
# 461.SGR exista in plan, dar fara note: 461 e postat doar pe sintetic.
["4092.1", "4092", "1", None, "analitic functional"],
]
# frunza -> (REF, NOI), cu (totdeb, totcred); None = frunza lipseste
DIFERENTE_ASTEPTATE = {
# 4092: note direct pe sintetic -> acont de diferenta; REF le-a pierdut
("4092", "99"): (None, (1563.37, 0.0)),
# nota de 900 direct pe 419: analitic de diferenta; REF a pierdut-o
("419", "9999"): (None, (0.0, 900.0)),
# 4428: analiticele M/TI/TP pe 01 (corectie), postarile direct pe sintetic pe 99;
# totalul coincide cu randul unic din REF
("4428", ""): ((1749.68, 0.0), None),
("4428", "01"): (None, (373.14, 0.0)),
("4428", "99"): (None, (1376.54, 0.0)),
# 5121: regula de banca (BALANTA cu acont per cont bancar), validata la MASTER
("5121", ""): ((40801.41, 35876.25), None),
("5121", "1"): (None, (40601.41, 35876.25)),
("5121", "2"): (None, (200.0, 0.0)),
}
def _randuri(cale):
ws = openpyxl.load_workbook(cale, data_only=True).worksheets[0]
h = [c.value for c in ws[1]]
return [d for d in (dict(zip(h, r)) for r in ws.iter_rows(min_row=2, values_only=True))
if d.get("TIP") in ("BALANTA", "FACTURA")]
def _v(x):
return round(float(x or 0), 2)
def _frunze(randuri):
"""{(cont, acont): (totdeb, totcred)}. REF are pe 215/2815/4091 si sinteticul
langa analitice, cu suma lor (dublura): acela se scoate, oricare altul ramane."""
d = {}
for r in randuri:
if r["TIP"] == "BALANTA":
d[(str(r["cont"]), str(r["acont"] or ""))] = (_v(r["totdeb"]), _v(r["totcred"]))
suma = collections.defaultdict(lambda: [0.0, 0.0])
for (c, a), (td, tc) in d.items():
if a:
suma[c][0] += td
suma[c][1] += tc
dubluri = {(c, "") for c, (td, tc) in suma.items()
if d.get((c, "")) == (round(td, 2), round(tc, 2))}
return {k: v for k, v in d.items() if k not in dubluri}
@unittest.skipUnless(BAZA.exists() and REF.exists(), "lipsesc fisierele LACERTA din exemple/")
class TestLacerta(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._tmp = tempfile.TemporaryDirectory(prefix="lacerta_")
d = pathlib.Path(cls._tmp.name)
wb = openpyxl.Workbook()
wb.active.append(mapare.COLOANE_CORECTII)
for r in CORECTII:
wb.active.append(r)
wb.save(d / "corectii_LACERTA.xlsx")
for cmd in (["extrage.py", "--fisier", str(BAZA), "--dir", str(d)],
["genereaza.py", "--dir", str(d), "--dir-iesire", str(d)]):
subprocess.run([sys.executable, str(RADACINA / cmd[0])] + cmd[1:] +
["--firma", "LACERTA", "--an", str(AN), "--luna", str(LUNA)],
check=True, capture_output=True, cwd=str(RADACINA))
cls.noi = _randuri(d / ("init_LACERTA_%d_%d.xlsx" % (AN, LUNA)))
cls.ref = _randuri(REF)
@classmethod
def tearDownClass(cls):
cls._tmp.cleanup()
def test_frunze_balanta_ca_in_ref(self):
ref, noi = _frunze(self.ref), _frunze(self.noi)
gasite = {}
for k in sorted(set(ref) | set(noi)):
if ref.get(k) != noi.get(k):
gasite[k] = (ref.get(k), noi.get(k))
self.assertEqual(gasite, DIFERENTE_ASTEPTATE)
def test_echilibru(self):
bal = [r for r in self.noi if r["TIP"] == "BALANTA"]
self.assertAlmostEqual(sum(_v(r["totdeb"]) for r in bal),
sum(_v(r["totcred"]) for r in bal), delta=0.005)
def test_facturi_per_document_cu_cod_fiscal_real(self):
fac = [r for r in self.noi if r["TIP"] == "FACTURA" and str(r["cont"]) == "401"]
# un rand per document nesoldat; suma = cea din REF
self.assertEqual(len(fac), 12)
self.assertAlmostEqual(sum(_v(r["sold"]) for r in fac), 9220.75, delta=0.005)
provizorii = [r["cod_fiscal"] for r in fac if str(r["cod_fiscal"]).startswith("401.")]
self.assertEqual(provizorii, [], "furnizor fara cod fiscal real")
def test_facturi_per_cont(self):
c = collections.Counter()
s = collections.defaultdict(float)
for r in self.noi:
if r["TIP"] == "FACTURA":
c[str(r["cont"])] += 1
s[str(r["cont"])] += _v(r["sold"])
self.assertEqual({k: (c[k], round(s[k], 2)) for k in c}, {
"401": (12, 9220.75), "4092": (1, 2606.86), "4111": (1, 90.0),
"4551": (3, 3023583.0), "461": (1, 2250.0), "5121": (2, 4925.16),
"542": (2, 129.44)})
if __name__ == "__main__":
unittest.main()