Files
solduri2roa/tests/test_citire_fdb.py
Marius Mutu 6b37d370d7 Lane citire-fdb: cititorul Firebird, cu poarta pe firma de joaca
citire_fdb.py produce balanta, parteneri si facturi din CONT_BAZA.FDB. Soldurile
stocate in CONTURI sunt 0, deci balanta se obtine agregand REGISTRU pe cele trei
intervale de data; analiticul se ruleaza in sintetic si se emit ambele randuri;
contul colector % se exclude. Conexiune embedded, READ, doar SELECT, rollback.

Garzi pe NULL: scadenta lipsa cade pe ultima zi a lunii, codul fiscal lipsa pe
codul provizoriu, partenerul intrand in lista pentru cautarea ANAF.

Poarta: CSV-ul din FDB e identic la octet cu cel produs de citire_xlsx.py din
exportul firmei de joaca (09/2026), iar SUM(REGISTRU) pe fiecare latura da netul
balantei. Verificat independent: 10 conturi, 7298.15 pe ambele laturi.

Limita, scrisa si in cod si in raport: firma de joaca nu are niciun analitic,
deci cititorul e verificat DOAR pe cazul sintetic si nu se foloseste la un client
fara rerularea portii pe datele lui.

docs/procedura_copie_fdb_client.md: cum isi face clientul copia bazei.

23 de teste trec, fara skip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYeAtVxeS8m4oXekjX8Am2
2026-09-21 16:13:30 +03:00

130 lines
4.8 KiB
Python

"""Teste pentru citire_fdb: balanta din Firebird identica cu exportul .xlsx.
Poarta: `balanta` produsa din copia unei baze `CONT_BAZA.FDB` trebuie sa fie
identica (pana la octet) cu cea produsa de `citire_xlsx.py` din exportul de foaie
de calcul, pe firma de joaca, luna 09/2026. Baza originala NU se deschide: se
copiaza intr-un director temporar.
Pe o masina fara SAGA (fara fbclient.dll, baza, driver sau export) testele se
sar, nu esueaza.
Rulare: py -m unittest discover -s tests -v
"""
import os
import pathlib
import shutil
import sys
import tempfile
import unittest
RADACINA = pathlib.Path(__file__).resolve().parent.parent
if str(RADACINA) not in sys.path:
sys.path.insert(0, str(RADACINA))
import citire_fdb
import citire_xlsx
FBCLIENT = pathlib.Path(r"C:\Program Files\Firebird\Firebird30_Saga\fbclient.dll")
BAZA = pathlib.Path(r"D:\SAGA250909\0001\CONT_BAZA.FDB")
XLSX = RADACINA / "exemple" / "saga-sqlite-balanta-09-2026.xlsx"
AN, LUNA = 2026, 9
TOL = 0.01
def _driver_disponibil():
try:
import firebird.driver # noqa: F401
return True
except Exception:
return False
class TestCitireFdb(unittest.TestCase):
@classmethod
def setUpClass(cls):
if not FBCLIENT.exists():
raise unittest.SkipTest("lipseste fbclient.dll: %s" % FBCLIENT)
if not BAZA.exists():
raise unittest.SkipTest("lipseste baza Firebird: %s" % BAZA)
if not _driver_disponibil():
raise unittest.SkipTest("lipseste pachetul firebird-driver")
cls._tmp = tempfile.TemporaryDirectory(prefix="citire_fdb_")
cls.baza = os.path.join(cls._tmp.name, "CONT_BAZA.FDB")
shutil.copy2(str(BAZA), cls.baza)
cls.dir = cls._tmp.name
@classmethod
def tearDownClass(cls):
cls._tmp.cleanup()
def test_poarta_identica_cu_xlsx(self):
if not XLSX.exists():
self.skipTest("lipseste exportul %s" % XLSX)
rows_xlsx = citire_xlsx.citeste_balanta(str(XLSX))
rows_fdb = citire_fdb.citeste_balanta(self.baza, AN, LUNA)
self.assertTrue(rows_xlsx, "exportul xlsx nu are randuri")
self.assertEqual(rows_xlsx, rows_fdb,
"balanta din FDB difera de exportul xlsx")
# comparatie stricta: acelasi fisier CSV, la octet
cale_xlsx = os.path.join(self.dir, "din_xlsx.csv")
cale_fdb = os.path.join(self.dir, "din_fdb.csv")
citire_xlsx.scrie_csv(rows_xlsx, cale_xlsx)
citire_fdb.scrie_csv(rows_fdb, cale_fdb, citire_fdb.COLOANE_BALANTA)
with open(cale_xlsx, "rb") as f:
a = f.read()
with open(cale_fdb, "rb") as f:
b = f.read()
self.assertEqual(a, b, "CSV-urile difera la nivel de octet")
def test_sum_registru_egal_netul_balantei(self):
d_an, d_luna, d_urm = citire_fdb._limite(AN, LUNA)
con = citire_fdb._conecteaza(self.baza)
try:
deb, cred = citire_fdb._select(
con,
"SELECT "
"SUM(CASE WHEN TRIM(CONT_D) NOT IN ('', '%') THEN SUMA ELSE 0 END), "
"SUM(CASE WHEN TRIM(CONT_C) NOT IN ('', '%') THEN SUMA ELSE 0 END) "
"FROM REGISTRU WHERE DATA < ?", (d_urm,))[0]
finally:
con.rollback()
con.close()
self.assertAlmostEqual(float(deb), float(cred), delta=TOL,
msg="SUM(REGISTRU) nu e echilibrat pe laturi")
rows = citire_fdb.citeste_balanta(self.baza, AN, LUNA)
net_balanta = sum(r["total_d"] - r["total_c"] for r in rows)
self.assertAlmostEqual(float(deb) - float(cred), net_balanta, delta=TOL,
msg="netul din REGISTRU != netul balantei")
def test_scadent_null_cade_pe_ultima_zi_a_lunii(self):
rows = citire_fdb.citeste_facturi(self.baza, AN, LUNA)
self.assertTrue(rows, "nu s-au citit facturi")
asteptat = citire_fdb._ultima_zi(AN, LUNA).isoformat()
# singura factura din firma de joaca are SCADENT NULL in INTRD
self.assertEqual(rows[0]["scadent"], asteptat)
def test_cont_colector_exclus(self):
rows = citire_fdb.citeste_balanta(self.baza, AN, LUNA)
self.assertNotIn("%", [r["cont"] for r in rows])
self.assertTrue(all(r["cont"] for r in rows), "rand fara cont")
def test_parteneri_si_facturi(self):
parteneri = citire_fdb.citeste_parteneri(self.baza)
self.assertTrue(parteneri, "nu s-au citit parteneri")
for r in parteneri:
self.assertTrue(r["cont_analitic"])
self.assertTrue(r["cod_fiscal"],
"cod fiscal gol pentru %s" % r["cont_analitic"])
facturi = citire_fdb.citeste_facturi(self.baza, AN, LUNA)
for r in facturi:
self.assertTrue(r["cont_analitic"])
self.assertTrue(r["scadent"])
if __name__ == "__main__":
unittest.main()