date/ - intrari de la client (PDF-urile, exportul SAGA) si balanta intermediara iesiri/ - xlsx-urile generate si verificare_<FIRMA>.md (ignorat de git) sablon/ - sablonul ROA Punctele de intrare scriu acolo implicit: extrage.py --dir=date, genereaza.py citeste din date/ si scrie in iesiri/ (--dir-iesire), ca radacina sa nu se murdareasca la fiecare rulare. Sablonul e cautat langa script, in sablon/. Actualizate: TEMPLATE, cele trei teste care tin caile, .gitignore, CLAUDE.md si handoff-ul. Poarta capat-la-capat incepuse sa sara tacut dupa mutarea PDF-urilor (skipTest pe "lipsesc PDF-urile"); acum arata din nou catre date/ si ruleaza. 33 de teste trec, zero skip-uri. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYeAtVxeS8m4oXekjX8Am2
128 lines
4.9 KiB
Python
128 lines
4.9 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="date",
|
|
help="de unde se citeste balanta (implicit date/)")
|
|
parser.add_argument("--dir-iesire", dest="dir_iesire", default="iesiri",
|
|
help="unde se scriu xlsx-urile (implicit iesiri/)")
|
|
a = parser.parse_args()
|
|
|
|
os.makedirs(a.dir, exist_ok=True)
|
|
os.makedirs(a.dir_iesire, 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_iesire, "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=a.dir_iesire)
|
|
|
|
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_iesire,
|
|
"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()
|