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
218 lines
8.3 KiB
Python
218 lines
8.3 KiB
Python
"""Test de integrare: capat-la-capat pe firma de joaca, din ambele surse.
|
|
|
|
Ruleaza `extrage.py` apoi `genereaza.py` in directoare temporare (nu scrie nimic in
|
|
radacina proiectului), din:
|
|
- exportul de foaie de calcul SAGA (exemple/saga-sqlite-balanta-09-2026.xlsx);
|
|
- copia bazei Firebird D:\\SAGA250909\\0001\\CONT_BAZA.FDB.
|
|
|
|
Verifica: xlsx-ul de import exista, are randuri de date si SUM(totdeb) = SUM(totcred).
|
|
|
|
Pe o masina fara baza / fbclient.dll / driver, teste FDB se sar, nu esueaza.
|
|
|
|
Rulare: py -m unittest discover -s tests -v
|
|
"""
|
|
import os
|
|
import pathlib
|
|
import shutil
|
|
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 extrage
|
|
import genereaza_xlsx
|
|
|
|
EXTRAGE = RADACINA / "extrage.py"
|
|
GENEREAZA = RADACINA / "genereaza.py"
|
|
XLSX_EXEMPLU = RADACINA / "exemple" / "saga-sqlite-balanta-09-2026.xlsx"
|
|
FBCLIENT = pathlib.Path(r"C:\Program Files\Firebird\Firebird30_Saga\fbclient.dll")
|
|
BAZA = pathlib.Path(r"D:\SAGA250909\0001\CONT_BAZA.FDB")
|
|
|
|
FIRMA = "JOACA"
|
|
AN, LUNA = 2026, 9
|
|
TOL = 0.01
|
|
|
|
|
|
def _driver_disponibil():
|
|
try:
|
|
import firebird.driver # noqa: F401
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _ruleaza(script, arg, dir_iesire):
|
|
"""Ruleaza un punct de intrare cu `--dir` in directorul temporar si verificat.
|
|
|
|
genereaza.py scrie implicit in iesiri/; in test totul sta in acelasi temporar.
|
|
"""
|
|
iesire = ["--dir-iesire", str(dir_iesire)] if script.name == "genereaza.py" else []
|
|
return subprocess.run(
|
|
[sys.executable, str(script), "--firma", FIRMA,
|
|
"--an", str(AN), "--luna", str(LUNA), "--dir", str(dir_iesire)]
|
|
+ iesire + arg,
|
|
cwd=str(dir_iesire), capture_output=True, text=True)
|
|
|
|
|
|
def _sume_xlsx(cale):
|
|
wb = openpyxl.load_workbook(cale)
|
|
try:
|
|
ws = wb.active
|
|
header = [c.value for c in ws[1]]
|
|
i_deb = header.index("totdeb")
|
|
i_cred = header.index("totcred")
|
|
deb = cred = 0.0
|
|
if ws.max_row > 1:
|
|
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
deb += row[i_deb] or 0
|
|
cred += row[i_cred] or 0
|
|
return ws.max_row, deb, cred
|
|
finally:
|
|
wb.close()
|
|
|
|
|
|
class TestIntegrare(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self._tmp = tempfile.TemporaryDirectory(prefix="integrare_")
|
|
self.dir = pathlib.Path(self._tmp.name)
|
|
|
|
def tearDown(self):
|
|
self._tmp.cleanup()
|
|
|
|
def _flux(self, sursa, fisier):
|
|
r = _ruleaza(EXTRAGE, ["--sursa", sursa, "--fisier", str(fisier)], self.dir)
|
|
self.assertEqual(r.returncode, 0, "extrage.py a picat:\n%s\n%s"
|
|
% (r.stdout, r.stderr))
|
|
self.assertTrue((self.dir / ("balanta_%s.csv" % FIRMA)).exists(),
|
|
"extrage.py nu a scris balanta")
|
|
|
|
r = _ruleaza(GENEREAZA, [], self.dir)
|
|
self.assertEqual(r.returncode, 0, "genereaza.py a picat:\n%s\n%s"
|
|
% (r.stdout, r.stderr))
|
|
|
|
xlsx = self.dir / ("init_%s_%d_%d.xlsx" % (FIRMA, AN, LUNA))
|
|
self.assertTrue(xlsx.exists(), "lipseste xlsx-ul de import: %s" % xlsx)
|
|
n, deb, cred = _sume_xlsx(xlsx)
|
|
self.assertGreater(n, 1, "xlsx-ul de import nu are randuri de date")
|
|
self.assertAlmostEqual(deb, cred, delta=TOL,
|
|
msg="SUM(totdeb)=%s != SUM(totcred)=%s" % (deb, cred))
|
|
return xlsx
|
|
|
|
def test_capat_la_capat_xlsx(self):
|
|
if not XLSX_EXEMPLU.exists():
|
|
self.skipTest("lipseste exportul %s" % XLSX_EXEMPLU)
|
|
self._flux("xlsx", XLSX_EXEMPLU)
|
|
|
|
def test_capat_la_capat_fdb(self):
|
|
if not FBCLIENT.exists():
|
|
self.skipTest("lipseste fbclient.dll: %s" % FBCLIENT)
|
|
if not BAZA.exists():
|
|
self.skipTest("lipseste baza Firebird: %s" % BAZA)
|
|
if not _driver_disponibil():
|
|
self.skipTest("lipseste pachetul firebird-driver")
|
|
copie = self.dir / "CONT_BAZA.FDB"
|
|
shutil.copy2(str(BAZA), str(copie))
|
|
self._flux("fdb", copie)
|
|
|
|
|
|
class TestDispecerSursa(unittest.TestCase):
|
|
|
|
def test_deducere_din_extensie(self):
|
|
self.assertEqual(extrage.deduce_sursa("balanta.pdf"), "pdf")
|
|
self.assertEqual(extrage.deduce_sursa("balanta.xls"), "xlsx")
|
|
self.assertEqual(extrage.deduce_sursa("balanta.XLSX"), "xlsx")
|
|
self.assertEqual(extrage.deduce_sursa("CONT_BAZA.FDB"), "fdb")
|
|
|
|
def test_extensie_necunoscuta_da_eroare(self):
|
|
with self.assertRaises(SystemExit):
|
|
extrage.deduce_sursa("balanta.csv")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|
|
|
|
|
|
class TestMapareSinteticPartener(unittest.TestCase):
|
|
"""Analiticele unui cont cu parteneri nu au voie sa cada pe conturi diferite:
|
|
BALANTA e agregat pe sintetic, FACTURA e per analitic, iar o redenumire
|
|
partiala ar rupe tacit corespondenta dintre ele."""
|
|
|
|
ROWS = [
|
|
{"cont": "401", "denumire": "FURNIZORI", "total_d": 0.0, "total_c": 300.0},
|
|
{"cont": "401.0001", "denumire": "A", "total_d": 0.0, "total_c": 100.0},
|
|
{"cont": "401.0002", "denumire": "B", "total_d": 0.0, "total_c": 200.0},
|
|
]
|
|
|
|
def setUp(self):
|
|
self.header = genereaza_xlsx.citeste_header()
|
|
|
|
def test_conturi_diferite_dau_eroare(self):
|
|
mapare = {"401.0001": {"cont": "4011", "acont": None},
|
|
"401.0002": {"cont": "4012", "acont": None}}
|
|
with self.assertRaises(genereaza_xlsx.Stop) as ctx:
|
|
genereaza_xlsx.genereaza(self.ROWS, self.header, 2025, 12, mapare=mapare)
|
|
self.assertIn("conturi diferite", str(ctx.exception))
|
|
|
|
def test_acelasi_cont_pe_toate_analiticele_e_legal(self):
|
|
mapare = {"401.0001": {"cont": "4011", "acont": None},
|
|
"401.0002": {"cont": "4011", "acont": None}}
|
|
out, _, _, _, _ = genereaza_xlsx.genereaza(self.ROWS, self.header, 2025, 12,
|
|
mapare=mapare)
|
|
conturi = {r["cont"] for r in out}
|
|
self.assertEqual({"4011"}, conturi)
|
|
|
|
|
|
class TestAcontPeAnaliticePartener(unittest.TestCase):
|
|
"""Cont cu parteneri care are si analitice functionale (461.SGR) si parteneri
|
|
(461.RESTUL -> 461/1): cate un BALANTA per (cont, acont), FACTURA cu acelasi acont
|
|
(decizii_import.md pct. 16), postarea direct pe sintetic pe acontul de diferenta."""
|
|
|
|
ROWS = [
|
|
{"cont": "461", "denumire": "DEBITORI DIVERSI", "total_d": 350.0, "total_c": 0.0},
|
|
{"cont": "461.SGR", "denumire": "AMBALAJE SGR", "total_d": 100.0, "total_c": 0.0},
|
|
{"cont": "461.00001", "denumire": "PARTENER X", "total_d": 200.0, "total_c": 0.0},
|
|
]
|
|
MAPARE = {"461.SGR": {"cont": "461", "acont": "SGR"},
|
|
"461.00001": {"cont": "461", "acont": "1"}}
|
|
|
|
def test_balanta_si_factura_pe_acelasi_acont(self):
|
|
out, _, _, _, _ = genereaza_xlsx.genereaza(
|
|
self.ROWS, genereaza_xlsx.citeste_header(), 2026, 6, mapare=self.MAPARE)
|
|
bal = {(r["cont"], r["acont"]): r["totdeb"] for r in out if r["TIP"] == "BALANTA"}
|
|
fac = {(r["cont"], r["acont"]): (r["nume"], r["sold"])
|
|
for r in out if r["TIP"] == "FACTURA"}
|
|
self.assertEqual(bal, {("461", "SGR"): 100.0, ("461", "1"): 200.0,
|
|
("461", "9999"): 50.0})
|
|
self.assertEqual(fac, {("461", "SGR"): ("AMBALAJE SGR", 100.0),
|
|
("461", "1"): ("PARTENER X", 200.0)})
|
|
|
|
|
|
class TestDiferentaBrutaFdb(unittest.TestCase):
|
|
"""Pe drumul FDB postarile direct pe sintetic intra cu D si C brute (rulajul ca in
|
|
SAGA); pe PDF/xls raman pe net. Soldul e acelasi in ambele cazuri."""
|
|
|
|
ROWS = [
|
|
{"cont": "401", "denumire": "FURNIZORI", "total_d": 100.0, "total_c": 600.0},
|
|
{"cont": "401.0001", "denumire": "A", "total_d": 0.0, "total_c": 300.0},
|
|
{"cont": "891", "denumire": "BILANT DE DESCHIDERE", "total_d": 0.0, "total_c": 0.0},
|
|
]
|
|
|
|
def _balanta(self, brut):
|
|
out, _, _, _, _ = genereaza_xlsx.genereaza(
|
|
self.ROWS, genereaza_xlsx.citeste_header(), 2026, 6, brut=brut)
|
|
return {(r["cont"], r["acont"]): (r["totdeb"], r["totcred"])
|
|
for r in out if r["TIP"] == "BALANTA"}
|
|
|
|
def test_brut(self):
|
|
self.assertEqual(self._balanta(True), {("401", None): (100.0, 600.0)})
|
|
|
|
def test_net(self):
|
|
self.assertEqual(self._balanta(False), {("401", None): (0.0, 500.0)})
|