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/
|
.gstack/
|
||||||
|
|
||||||
# iesiri si date de firme de productie - raman pe disc, nu in git
|
# 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
|
init_*.xlsx
|
||||||
export_*.xlsx
|
export_*.xlsx
|
||||||
mapare_*.xlsx
|
mapare_*.xlsx
|
||||||
balanta_*.csv
|
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
|
# etalonul de regresie e anonimizat si se comite; tabela de corespondenta NU
|
||||||
tests/_corespondenta_anonimizare.json
|
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) |
|
| `citire_fdb.py` | CONT_BAZA.FDB -> balanta + parteneri + facturi (verificat doar pe sintetic) |
|
||||||
| `mapare.py` | propunere automata + `corectii_<FIRMA>.xlsx`, .TOATE/.RESTUL |
|
| `mapare.py` | propunere automata + `corectii_<FIRMA>.xlsx`, .TOATE/.RESTUL |
|
||||||
| `config/conturi_parteneri.csv` | **conturile cu parteneri** - autoritatea, nu CONFIG_CONT_IREG |
|
| `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` |
|
| `genereaza_xlsx.py` | CSV -> `init_<FIRMA>_<an>_<luna>.xlsx` |
|
||||||
| `init_facturi_balanta_note.xlsx` | sablonul ROA (rand 1 = coloane, 2-13 instructiuni, 20+ exemple) |
|
| `init_facturi_balanta_note.xlsx` | sablonul ROA (rand 1 = coloane, 2-13 instructiuni, 20+ exemple) |
|
||||||
| `docs/decizii_import.md` | **regulile de mapare - citeste intai** |
|
| `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 |
|
| `tests/` | 33 de teste, `unittest` din stdlib |
|
||||||
| `docs/procedura_copie_fdb_client.md` | cum isi face clientul copia bazei |
|
| `docs/procedura_copie_fdb_client.md` | cum isi face clientul copia bazei |
|
||||||
| `docs/raport_lane_*.md` | raportul fiecarui lane |
|
| `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),
|
Commit-uri: `15bb26a` (plan + etalon), `f23b336` (contract), `ba4cbbf` (citire-xlsx + mapare),
|
||||||
`6b37d37` (citire-fdb), `82511c8` (integrare), plus commit-ul final de regresie.
|
`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
|
joaca deschisa numai prin copie in temporar, `tests/_corespondenta_anonimizare.json` ramasa ignorata
|
||||||
de git.
|
de git.
|
||||||
|
|
||||||
Fisierele `init_FUNDATIA_2025_12.xlsx` si `init_MASTER_2025_12.xlsx` sunt pe disc dar nu in git;
|
Fisierele `iesiri/init_FUNDATIA_2025_12.xlsx` si `iesiri/init_MASTER_2025_12.xlsx` sunt pe
|
||||||
sunt necesare doar daca vrei sa **regenerezi** etalonul (`py tests\anonimizeaza_etalon.py`).
|
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
|
## 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
|
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.
|
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.
|
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("--firma", required=True)
|
||||||
parser.add_argument("--an", type=int)
|
parser.add_argument("--an", type=int)
|
||||||
parser.add_argument("--luna", 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()
|
a = parser.parse_args()
|
||||||
|
|
||||||
sursa = a.sursa or deduce_sursa(a.fisier)
|
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("--luna", type=int, required=True)
|
||||||
parser.add_argument("--balanta",
|
parser.add_argument("--balanta",
|
||||||
help="implicit balanta_<FIRMA>.csv din --dir")
|
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()
|
a = parser.parse_args()
|
||||||
|
|
||||||
os.makedirs(a.dir, exist_ok=True)
|
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)
|
balanta = a.balanta or os.path.join(a.dir, "balanta_%s.csv" % a.firma)
|
||||||
if not os.path.exists(balanta):
|
if not os.path.exists(balanta):
|
||||||
raise SystemExit("lipseste balanta: %s" % balanta)
|
raise SystemExit("lipseste balanta: %s" % balanta)
|
||||||
@@ -90,12 +94,12 @@ def main():
|
|||||||
rows, header, an=a.an, luna=a.luna, mapare=override,
|
rows, header, an=a.an, luna=a.luna, mapare=override,
|
||||||
parteneri=parteneri, facturi=facturi)
|
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))
|
% (a.firma, a.an, a.luna))
|
||||||
generator.scrie_xlsx(header, out, xlsx_path)
|
generator.scrie_xlsx(header, out, xlsx_path)
|
||||||
rez = generator.verifica(a.firma, balanta, xlsx_path, rows, out, diffs,
|
rez = generator.verifica(a.firma, balanta, xlsx_path, rows, out, diffs,
|
||||||
leaf_map, leaf_rep, negative, header,
|
leaf_map, leaf_rep, negative, header,
|
||||||
dir_verificare=os.path.join(a.dir, "docs"))
|
dir_verificare=a.dir_iesire)
|
||||||
|
|
||||||
print("=== %s ===" % a.firma)
|
print("=== %s ===" % a.firma)
|
||||||
print(" rows: %d totdeb=%s totcred=%s net=%s"
|
print(" rows: %d totdeb=%s totcred=%s net=%s"
|
||||||
@@ -106,7 +110,7 @@ def main():
|
|||||||
"OK" if rez["part"] else "ESEC",
|
"OK" if rez["part"] else "ESEC",
|
||||||
"OK" if rez["lung"] else "ESEC"))
|
"OK" if rez["lung"] else "ESEC"))
|
||||||
print(" scris %s si %s" % (xlsx_path,
|
print(" scris %s si %s" % (xlsx_path,
|
||||||
os.path.join(a.dir, "docs",
|
os.path.join(a.dir_iesire,
|
||||||
"verificare_%s.md" % a.firma)))
|
"verificare_%s.md" % a.firma)))
|
||||||
if rez["negative"]:
|
if rez["negative"]:
|
||||||
print(" analitice cu semn negativ: %s" % (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.
|
# 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__)),
|
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'):
|
# Latura pe care formularul pune randurile FACTURA (frm...sc2:379/'383'):
|
||||||
# conturile din lista merg pe CREDIT, tot restul pe DEBIT.
|
# conturile din lista merg pe CREDIT, tot restul pe DEBIT.
|
||||||
|
|||||||
@@ -115,10 +115,10 @@ def anonimizeaza():
|
|||||||
raport = []
|
raport = []
|
||||||
|
|
||||||
for sursa, tinta in SURSE:
|
for sursa, tinta in SURSE:
|
||||||
cale = RADACINA / sursa
|
cale = RADACINA / "iesiri" / sursa
|
||||||
if not cale.exists():
|
if not cale.exists():
|
||||||
raise SystemExit(
|
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
|
% sursa
|
||||||
)
|
)
|
||||||
shutil.copy(cale, GOLDEN / tinta)
|
shutil.copy(cale, GOLDEN / tinta)
|
||||||
|
|||||||
@@ -48,10 +48,15 @@ def _driver_disponibil():
|
|||||||
|
|
||||||
|
|
||||||
def _ruleaza(script, arg, dir_iesire):
|
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(
|
return subprocess.run(
|
||||||
[sys.executable, str(script), "--firma", FIRMA,
|
[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)
|
cwd=str(dir_iesire), capture_output=True, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class TestRegresieEtalon(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
header = generator.citeste_header()
|
header = generator.citeste_header()
|
||||||
for firma, csv_nume, golden_nume in FIRME:
|
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)
|
out, _, _, _, _ = generator.genereaza(rows, header)
|
||||||
brut = tmpdir / ("init_%s_2025_12.xlsx" % firma)
|
brut = tmpdir / ("init_%s_2025_12.xlsx" % firma)
|
||||||
generator.scrie_xlsx(header, out, str(brut))
|
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.
|
tests/golden/ pe VALORI DE CELULA, nu pe hash de fisier.
|
||||||
|
|
||||||
Asocierea PDF -> firma nu se presupune: fiecare balanta extrasa se compara cu
|
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.
|
care e cea folosita la constructia etalonului.
|
||||||
|
|
||||||
Daca iesirea difera de etalon, etalonul are dreptate: se raporteaza, nu se ajusteaza.
|
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"
|
CORESP = TESTS / "_corespondenta_anonimizare.json"
|
||||||
GOLDEN = TESTS / "golden"
|
GOLDEN = TESTS / "golden"
|
||||||
|
DATE = RADACINA / "date"
|
||||||
EXTRAGE = RADACINA / "extrage.py"
|
EXTRAGE = RADACINA / "extrage.py"
|
||||||
GENEREAZA = RADACINA / "genereaza.py"
|
GENEREAZA = RADACINA / "genereaza.py"
|
||||||
|
|
||||||
AN, LUNA = 2025, 12
|
AN, LUNA = 2025, 12
|
||||||
MAX_DIFERENTE_RAPORTATE = 20
|
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.
|
# Asocierea e verificata de test_pdf_corespunde_referintei, nu doar afirmata.
|
||||||
FIRME = [
|
FIRME = [
|
||||||
("FUNDATIA", "IBB 31.12.2025.pdf", "balanta_FUNDATIA.csv",
|
("FUNDATIA", "IBB 31.12.2025.pdf", "balanta_FUNDATIA.csv",
|
||||||
@@ -100,9 +101,9 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
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:
|
if lipsa:
|
||||||
raise unittest.SkipTest("lipsesc PDF-urile din radacina: %s"
|
raise unittest.SkipTest("lipsesc PDF-urile din date/: %s"
|
||||||
% ", ".join(lipsa))
|
% ", ".join(lipsa))
|
||||||
if not CORESP.exists():
|
if not CORESP.exists():
|
||||||
raise unittest.SkipTest(
|
raise unittest.SkipTest(
|
||||||
@@ -116,14 +117,15 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
|||||||
cls.iesiri = {}
|
cls.iesiri = {}
|
||||||
try:
|
try:
|
||||||
for firma, pdf, _, _ in FIRME:
|
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,
|
"--firma", firma, "--an", AN, "--luna", LUNA,
|
||||||
"--dir", str(tmpdir)], tmpdir)
|
"--dir", str(tmpdir)], tmpdir)
|
||||||
cls.balante[firma] = tmpdir / ("balanta_%s.csv" % firma)
|
cls.balante[firma] = tmpdir / ("balanta_%s.csv" % firma)
|
||||||
|
|
||||||
for firma, _, _, _ in FIRME:
|
for firma, _, _, _ in FIRME:
|
||||||
_ruleaza(GENEREAZA, ["--firma", firma, "--an", AN, "--luna", LUNA,
|
_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:
|
for firma, _, _, golden_nume in FIRME:
|
||||||
brut = tmpdir / ("init_%s_%d_%d.xlsx" % (firma, AN, LUNA))
|
brut = tmpdir / ("init_%s_%d_%d.xlsx" % (firma, AN, LUNA))
|
||||||
@@ -140,7 +142,7 @@ class TestRegresieCapatLaCapat(unittest.TestCase):
|
|||||||
|
|
||||||
def _verifica_referinta(self, firma):
|
def _verifica_referinta(self, firma):
|
||||||
"""Balanta extrasa din PDF = balanta de referinta (verifica asocierea PDF->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]
|
(f, r) for f, _, r, _ in FIRME)[firma]
|
||||||
if not referinta.exists():
|
if not referinta.exists():
|
||||||
self.skipTest("lipseste referinta %s" % referinta)
|
self.skipTest("lipseste referinta %s" % referinta)
|
||||||
|
|||||||
Reference in New Issue
Block a user