Files
solduri2roa/genereaza.py
Marius Mutu 82511c8adb Lane integrare: extrage.py si genereaza.py cu parametri, maparea legata
Firmele si lunile nu mai sunt hardcodate. extrage.py dispecerizeaza catre cele
trei cititoare (pdf / xlsx / fdb, dedus din extensie, extensie necunoscuta =
eroare) si scrie contractul intern; genereaza.py aplica maparea si scrie
init_<FIRMA>_<an>_<luna>.xlsx plus mapare_<FIRMA>.xlsx.

genereaza_xlsx.py devine biblioteca: an/luna parametri, data si scadenta
calculate din ele, sablonul cautat langa script. Cand exista parteneri_ si
facturi_ (drumul FDB) se emite cate un FACTURA per document, cu cod fiscal real,
si numai daca suma documentelor da netul contului; altfel ramane comportamentul
de azi, un FACTURA agregat.

Corectat in orchestrare: la redenumirea unui cont cu parteneri, generatorul lua
primul analitic ca sa afle contul sintetic. Daca analiticele erau mapate pe
conturi diferite, BALANTA agregat si randurile FACTURA cadeau pe conturi
diferite, tacut. Acum se opreste cu eroare; colapsarea pe acelasi cont ramane
legala. Doua teste acopera ambele cazuri.

29 de teste trec, inclusiv regresia pe etalon cu generatorul modificat.

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

124 lines
4.7 KiB
Python

"""Punct de intrare: de la contractul intern la xlsx-ul de import ROA.
Citeste balanta_<FIRMA>.csv, aplica maparea (mapare.py) si scrie:
init_<FIRMA>_<an>_<luna>.xlsx - xlsx-ul de import
mapare_<FIRMA>.xlsx - propunerea completa (doar unealta scrie)
corectii_<FIRMA>.xlsx - creat gol la prima rulare a firmei
docs/verificare_<FIRMA>.md - verificarile obligatorii
Maparea hotaraste cont/acont pentru conturile obisnuite. La conturile cu parteneri
forma lui acont ramane a generatorului: maparea suprascrie doar ce i se da explicit
in corectii_<FIRMA>.xlsx (docs/decizii_import.md pct. 16). Randurile de diferenta
raman calculate in cod.
Daca exista parteneri_<FIRMA>.csv / facturi_<FIRMA>.csv (drumul FDB), se folosesc:
cod fiscal real si un rand FACTURA per document. Lipsa lor = comportamentul de azi
(un FACTURA per partener, sold total).
Utilizare:
py genereaza.py --firma FIRMA --an 2025 --luna 12 [--balanta balanta_FIRMA.csv]
"""
import argparse
import csv
import os
import genereaza_xlsx as generator
import mapare
def _citeste_csv(path):
if not os.path.exists(path):
return []
with open(path, encoding="utf-8-sig", newline="") as f:
return list(csv.DictReader(f))
def _mapare_si_exclusi(rezultat):
"""{cont_saga: {cont, acont}} pentru corectiile explicite + multimea exclusilor."""
override = {}
exclusi = set()
for r in rezultat:
if r["exclus"]:
exclusi.add(r["cont_saga"])
elif r["sursa"] == mapare.SURSA_CORECTAT:
override[r["cont_saga"]] = {"cont": r["cont"], "acont": r["acont"]}
return override, exclusi
def _fara_exclusi(rows, exclusi):
"""Un cont exclus iese cu tot cu subarborele lui (un parinte fara copii ar rupe arborele)."""
if not exclusi:
return rows
out = []
for r in rows:
c = r["cont"]
if any(c == e or c.startswith(e + ".") for e in exclusi):
continue
out.append(r)
return out
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--firma", required=True)
parser.add_argument("--an", type=int, required=True)
parser.add_argument("--luna", type=int, required=True)
parser.add_argument("--balanta",
help="implicit balanta_<FIRMA>.csv din --dir")
parser.add_argument("--dir", default=".", help="directorul de iesire")
a = parser.parse_args()
os.makedirs(a.dir, exist_ok=True)
balanta = a.balanta or os.path.join(a.dir, "balanta_%s.csv" % a.firma)
if not os.path.exists(balanta):
raise SystemExit("lipseste balanta: %s" % balanta)
header = generator.citeste_header()
if len(header) != 39:
raise SystemExit("cap de tabel neasteptat: %d coloane" % len(header))
rows = generator.citeste_balanta(balanta)
rezultat = mapare.genereaza_mapare(rows, a.firma, a.dir)
override, exclusi = _mapare_si_exclusi(rezultat)
rows = _fara_exclusi(rows, exclusi)
parteneri = _citeste_csv(os.path.join(a.dir, "parteneri_%s.csv" % a.firma))
facturi = _citeste_csv(os.path.join(a.dir, "facturi_%s.csv" % a.firma))
out, diffs, leaf_map, leaf_rep, negative = generator.genereaza(
rows, header, an=a.an, luna=a.luna, mapare=override,
parteneri=parteneri, facturi=facturi)
xlsx_path = os.path.join(a.dir, "init_%s_%d_%d.xlsx"
% (a.firma, a.an, a.luna))
generator.scrie_xlsx(header, out, xlsx_path)
rez = generator.verifica(a.firma, balanta, xlsx_path, rows, out, diffs,
leaf_map, leaf_rep, negative, header,
dir_verificare=os.path.join(a.dir, "docs"))
print("=== %s ===" % a.firma)
print(" rows: %d totdeb=%s totcred=%s net=%s"
% (len(out), rez["totdeb"], rez["totcred"], rez["net"]))
print(" balanta=%s frunze=%s parteneri=%s lungimi=%s"
% ("OK" if rez["bal"] else "ESEC",
"OK" if rez["leaves"] else "ESEC",
"OK" if rez["part"] else "ESEC",
"OK" if rez["lung"] else "ESEC"))
print(" scris %s si %s" % (xlsx_path,
os.path.join(a.dir, "docs",
"verificare_%s.md" % a.firma)))
if rez["negative"]:
print(" analitice cu semn negativ: %s" % (rez["negative"],))
if rez["pierdute"] or rez["dubluri_leaf"] or rez["dubluri_bal"] or rez["lungimi"]:
print(" ATENTIE: %s %s %s %s"
% (rez["pierdute"], rez["dubluri_leaf"], rez["dubluri_bal"],
rez["lungimi"]))
toate = rez["bal"] and rez["leaves"] and rez["part"] and rez["lung"]
raise SystemExit(0 if toate else 1)
if __name__ == "__main__":
main()