Muta datele din radacina in date/, iesiri/ si sablon/
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
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,11 +3,13 @@ __pycache__/
|
||||
.gstack/
|
||||
|
||||
# iesiri si date de firme de productie - raman pe disc, nu in git
|
||||
# (docs/verificare_*.md raman in git: sunt dovada importurilor validate in productie)
|
||||
iesiri/
|
||||
init_*.xlsx
|
||||
export_*.xlsx
|
||||
mapare_*.xlsx
|
||||
balanta_*.csv
|
||||
!init_facturi_balanta_note.xlsx
|
||||
!sablon/init_facturi_balanta_note.xlsx
|
||||
|
||||
# etalonul de regresie e anonimizat si se comite; tabela de corespondenta NU
|
||||
tests/_corespondenta_anonimizare.json
|
||||
|
||||
@@ -18,6 +18,9 @@ Convertor balante SAGA (PDF) -> xlsx de initializare solduri ROACONT.
|
||||
| `citire_fdb.py` | CONT_BAZA.FDB -> balanta + parteneri + facturi (verificat doar pe sintetic) |
|
||||
| `mapare.py` | propunere automata + `corectii_<FIRMA>.xlsx`, .TOATE/.RESTUL |
|
||||
| `config/conturi_parteneri.csv` | **conturile cu parteneri** - autoritatea, nu CONFIG_CONT_IREG |
|
||||
| `date/` | intrari de la client (PDF, export SAGA) + balanta intermediara |
|
||||
| `iesiri/` | xlsx-urile generate + `verificare_<FIRMA>.md` (ignorat de git) |
|
||||
| `sablon/init_facturi_balanta_note.xlsx` | sablonul ROA (rand 1 = coloane) |
|
||||
| `genereaza_xlsx.py` | CSV -> `init_<FIRMA>_<an>_<luna>.xlsx` |
|
||||
| `init_facturi_balanta_note.xlsx` | sablonul ROA (rand 1 = coloane, 2-13 instructiuni, 20+ exemple) |
|
||||
| `docs/decizii_import.md` | **regulile de mapare - citeste intai** |
|
||||
|
||||
@@ -24,6 +24,7 @@ Actualizat 21.09.2026, la finalul executiei. Versiunea anterioara a acestui fisi
|
||||
| `tests/` | 33 de teste, `unittest` din stdlib |
|
||||
| `docs/procedura_copie_fdb_client.md` | cum isi face clientul copia bazei |
|
||||
| `docs/raport_lane_*.md` | raportul fiecarui lane |
|
||||
| `date/`, `iesiri/`, `sablon/` | intrari de la client / xlsx-uri generate / sablonul ROA |
|
||||
|
||||
Commit-uri: `15bb26a` (plan + etalon), `f23b336` (contract), `ba4cbbf` (citire-xlsx + mapare),
|
||||
`6b37d37` (citire-fdb), `82511c8` (integrare), plus commit-ul final de regresie.
|
||||
@@ -84,15 +85,23 @@ de pe 4296 a fost oprit), `D:\ROA\ROACONT`, `D:\SAGA250909` si `saga2roa*` neati
|
||||
joaca deschisa numai prin copie in temporar, `tests/_corespondenta_anonimizare.json` ramasa ignorata
|
||||
de git.
|
||||
|
||||
Fisierele `init_FUNDATIA_2025_12.xlsx` si `init_MASTER_2025_12.xlsx` sunt pe disc dar nu in git;
|
||||
sunt necesare doar daca vrei sa **regenerezi** etalonul (`py tests\anonimizeaza_etalon.py`).
|
||||
Fisierele `iesiri/init_FUNDATIA_2025_12.xlsx` si `iesiri/init_MASTER_2025_12.xlsx` sunt pe
|
||||
disc dar nu in git; sunt necesare doar daca vrei sa **regenerezi** etalonul
|
||||
(`py tests\anonimizeaza_etalon.py`, care le citeste din `iesiri/`).
|
||||
|
||||
## 8. Cum rulezi acum unealta
|
||||
|
||||
```
|
||||
py extrage.py --fisier "IBB 31.12.2025.pdf" --firma FUNDATIA --an 2025 --luna 12
|
||||
py extrage.py --fisier "date\IBB 31.12.2025.pdf" --firma FUNDATIA --an 2025 --luna 12
|
||||
py genereaza.py --firma FUNDATIA --an 2025 --luna 12
|
||||
```
|
||||
|
||||
Implicit `extrage.py` scrie in `date/`, iar `genereaza.py` citeste din `date/` si scrie in
|
||||
`iesiri/` (`--dir` / `--dir-iesire` le schimba). Sablonul e in `sablon/`.
|
||||
|
||||
**Capcana de nume**: balanta firmei MASTER se numeste `balanta_MASTERJOB.csv`, nu
|
||||
`balanta_MASTER.csv`, deci pentru ea trebuie dat explicit
|
||||
`--balanta date\balanta_MASTERJOB.csv`. Eroarea e clara, nu tacuta.
|
||||
|
||||
Sursa se deduce din extensie (`.pdf`, `.xls`/`.xlsx`, `.fdb`); o extensie necunoscuta da eroare.
|
||||
Pentru Firebird, da calea catre o **copie** a bazei, niciodata baza vie a clientului.
|
||||
|
||||
@@ -67,7 +67,8 @@ def main():
|
||||
parser.add_argument("--firma", required=True)
|
||||
parser.add_argument("--an", type=int)
|
||||
parser.add_argument("--luna", type=int)
|
||||
parser.add_argument("--dir", default=".", help="directorul de iesire")
|
||||
parser.add_argument("--dir", default="date",
|
||||
help="unde se scrie contractul intern (implicit date/)")
|
||||
a = parser.parse_args()
|
||||
|
||||
sursa = a.sursa or deduce_sursa(a.fisier)
|
||||
|
||||
12
genereaza.py
12
genereaza.py
@@ -66,10 +66,14 @@ def main():
|
||||
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")
|
||||
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)
|
||||
@@ -90,12 +94,12 @@ def main():
|
||||
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"
|
||||
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=os.path.join(a.dir, "docs"))
|
||||
dir_verificare=a.dir_iesire)
|
||||
|
||||
print("=== %s ===" % a.firma)
|
||||
print(" rows: %d totdeb=%s totcred=%s net=%s"
|
||||
@@ -106,7 +110,7 @@ def main():
|
||||
"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",
|
||||
os.path.join(a.dir_iesire,
|
||||
"verificare_%s.md" % a.firma)))
|
||||
if rez["negative"]:
|
||||
print(" analitice cu semn negativ: %s" % (rez["negative"],))
|
||||
|
||||
@@ -37,7 +37,7 @@ VALUTA = None
|
||||
|
||||
# Sablonul sta langa script, nu langa directorul de lucru: iesirile pot merge in temp.
|
||||
TEMPLATE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"init_facturi_balanta_note.xlsx")
|
||||
"sablon", "init_facturi_balanta_note.xlsx")
|
||||
|
||||
# Latura pe care formularul pune randurile FACTURA (frm...sc2:379/'383'):
|
||||
# conturile din lista merg pe CREDIT, tot restul pe DEBIT.
|
||||
|
||||
@@ -115,10 +115,10 @@ def anonimizeaza():
|
||||
raport = []
|
||||
|
||||
for sursa, tinta in SURSE:
|
||||
cale = RADACINA / sursa
|
||||
cale = RADACINA / "iesiri" / sursa
|
||||
if not cale.exists():
|
||||
raise SystemExit(
|
||||
"Lipseste %s. Etalonul nu se poate construi - vezi docs/handoff_etapa2_convertor.md"
|
||||
"Lipseste iesiri/%s. Etalonul nu se poate construi - vezi docs/handoff_etapa2_convertor.md"
|
||||
% sursa
|
||||
)
|
||||
shutil.copy(cale, GOLDEN / tinta)
|
||||
|
||||
@@ -48,10 +48,15 @@ def _driver_disponibil():
|
||||
|
||||
|
||||
def _ruleaza(script, arg, dir_iesire):
|
||||
"""Ruleaza un punct de intrare cu `--dir` in directorul temporar si verificat."""
|
||||
"""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)] + arg,
|
||||
"--an", str(AN), "--luna", str(LUNA), "--dir", str(dir_iesire)]
|
||||
+ iesire + arg,
|
||||
cwd=str(dir_iesire), capture_output=True, text=True)
|
||||
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class TestRegresieEtalon(unittest.TestCase):
|
||||
try:
|
||||
header = generator.citeste_header()
|
||||
for firma, csv_nume, golden_nume in FIRME:
|
||||
rows = generator.citeste_balanta(str(RADACINA / csv_nume))
|
||||
rows = generator.citeste_balanta(str(RADACINA / "date" / csv_nume))
|
||||
out, _, _, _, _ = generator.genereaza(rows, header)
|
||||
brut = tmpdir / ("init_%s_2025_12.xlsx" % firma)
|
||||
generator.scrie_xlsx(header, out, str(brut))
|
||||
|
||||
@@ -12,7 +12,7 @@ reguli si ACEIASI tabela ca tests/anonimizeaza_etalon.py si se compara cu
|
||||
tests/golden/ pe VALORI DE CELULA, nu pe hash de fisier.
|
||||
|
||||
Asocierea PDF -> firma nu se presupune: fiecare balanta extrasa se compara cu
|
||||
balanta de referinta din radacina (balanta_FUNDATIA.csv / balanta_MASTERJOB.csv),
|
||||
balanta de referinta din date/ (balanta_FUNDATIA.csv / balanta_MASTERJOB.csv),
|
||||
care e cea folosita la constructia etalonului.
|
||||
|
||||
Daca iesirea difera de etalon, etalonul are dreptate: se raporteaza, nu se ajusteaza.
|
||||
@@ -39,13 +39,14 @@ import anonimizeaza_etalon as anonim
|
||||
|
||||
CORESP = TESTS / "_corespondenta_anonimizare.json"
|
||||
GOLDEN = TESTS / "golden"
|
||||
DATE = RADACINA / "date"
|
||||
EXTRAGE = RADACINA / "extrage.py"
|
||||
GENEREAZA = RADACINA / "genereaza.py"
|
||||
|
||||
AN, LUNA = 2025, 12
|
||||
MAX_DIFERENTE_RAPORTATE = 20
|
||||
|
||||
# PDF din radacina -> firma -> balanta de referinta din radacina -> etalon.
|
||||
# PDF din date/ -> firma -> balanta de referinta din date/ -> etalon.
|
||||
# Asocierea e verificata de test_pdf_corespunde_referintei, nu doar afirmata.
|
||||
FIRME = [
|
||||
("FUNDATIA", "IBB 31.12.2025.pdf", "balanta_FUNDATIA.csv",
|
||||
@@ -100,9 +101,9 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
lipsa = [pdf for _, pdf, _, _ in FIRME if not (RADACINA / pdf).exists()]
|
||||
lipsa = [pdf for _, pdf, _, _ in FIRME if not (DATE / pdf).exists()]
|
||||
if lipsa:
|
||||
raise unittest.SkipTest("lipsesc PDF-urile din radacina: %s"
|
||||
raise unittest.SkipTest("lipsesc PDF-urile din date/: %s"
|
||||
% ", ".join(lipsa))
|
||||
if not CORESP.exists():
|
||||
raise unittest.SkipTest(
|
||||
@@ -116,14 +117,15 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
||||
cls.iesiri = {}
|
||||
try:
|
||||
for firma, pdf, _, _ in FIRME:
|
||||
_ruleaza(EXTRAGE, ["--sursa", "pdf", "--fisier", str(RADACINA / pdf),
|
||||
_ruleaza(EXTRAGE, ["--sursa", "pdf", "--fisier", str(DATE / pdf),
|
||||
"--firma", firma, "--an", AN, "--luna", LUNA,
|
||||
"--dir", str(tmpdir)], tmpdir)
|
||||
cls.balante[firma] = tmpdir / ("balanta_%s.csv" % firma)
|
||||
|
||||
for firma, _, _, _ in FIRME:
|
||||
_ruleaza(GENEREAZA, ["--firma", firma, "--an", AN, "--luna", LUNA,
|
||||
"--dir", str(tmpdir)], tmpdir)
|
||||
"--dir", str(tmpdir),
|
||||
"--dir-iesire", str(tmpdir)], tmpdir)
|
||||
|
||||
for firma, _, _, golden_nume in FIRME:
|
||||
brut = tmpdir / ("init_%s_%d_%d.xlsx" % (firma, AN, LUNA))
|
||||
@@ -140,7 +142,7 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
||||
|
||||
def _verifica_referinta(self, firma):
|
||||
"""Balanta extrasa din PDF = balanta de referinta (verifica asocierea PDF->firma)."""
|
||||
referinta = RADACINA / dict(
|
||||
referinta = DATE / dict(
|
||||
(f, r) for f, _, r, _ in FIRME)[firma]
|
||||
if not referinta.exists():
|
||||
self.skipTest("lipseste referinta %s" % referinta)
|
||||
|
||||
Reference in New Issue
Block a user