Files
solduri2roa/docs/raport_exporturi_saga_noua.md
Marius Mutu 15bb26ac15 Etapa 2: plan v3, rapoarte de cercetare si etalonul de regresie
Cercetare: ambele formate SAGA (VFP xls/xlsx si Firebird .FDB), cu dovezi
fisier:linie in docs/raport_sursa_saga_xlsx.md si raport_exporturi_saga_noua.md.

Plan v3 dupa review de strategie si arhitectura: contract intern + trei
cititoare, mapare in doua fisiere cu proprietari diferiti, lane-uri.

Etalon de regresie anonimizat in tests/golden/ (sume si structura neatinse,
zero IBAN si zero cod fiscal real). Tabela de corespondenta ramane ignorata.

Iesirile de productie ies din git (raman pe disc); .gitignore acopera si
copiile de baze de client si iesirile intermediare ale convertorului.

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

18 KiB

Raport exporturi SAGA noua (Firebird) - cercetare read-only

Lane: exporturi-saga-noua. Sarcina de cercetare, fara modificari de cod. Fisiere analizate in D:\ROA\IMPORT2ROA\solduri2roa\exemple\: saga-sqlite-balanta-09-2026.xlsx, saga-sqlite-balanta-09-2026.xls, saga-sqlite-furnizori-09-2026.xlsx, saga-sqlite-furnizori-facturi-un-furnizor-09-2026.xlsx. Baza explorata: D:\SAGA250909\0001\CONT_BAZA.FDB (Firebird, NU SQLite). Temporarele de lucru: C:\Users\mmari\AppData\Local\Temp\opencode. Nu s-a modificat nimic in D:\SAGA250909 (doar SELECT, tranzactie read-only). Fisierele nu sunt SQLite; numele contin "sqlite" dar continutul sunt foi de calcul obisnuite (vezi mai jos).


PARTEA A - structura celor 4 exporturi

A.1 saga-sqlite-balanta-09-2026.xlsx

  • O singura foaie: Sheet1. Dimensiune A1:AI11 = 11 randuri x 35 coloane (1 antet + 10 randuri de date). Fara foi ascunse, fara celule fuzionate.
  • Antetul este pe randul 1. Datele incep la randul 2 si se termina la randul 11.
  • NU exista rand de totaluri (ultimul rand este o data: contul 6461).

Coloanele, in ordine (litera = nume):

Lit Nume Lit Nume Lit Nume
A CONT M RULAJT_D Y RULAJ_D_1
B DENUMIRE N RULAJT_C Z RULAJ_C_1
C CATEGORIE O TOTAL_DEB AA RULAJT_D_1
D TIP P TOTAL_CRED AB RULAJT_C_1
E DEB_INIT Q FIN_D AC TOTAL_DEB_1
F CRED_INIT R FIN_C AD TOTAL_CRED_1
G DEB_PREC S DEB_INIT_1 AE FIN_D_1
H CRED_PREC T CRED_INIT_1 AF FIN_C_1
I SOLD_IN_D U DEB_PREC_1 AG ANALITIC
J SOLD_IN_C V CRED_PREC_1 AH VALIDAT
K RULAJ_D W SOLD_IN_D_1 AI LINIE
L RULAJ_C X SOLD_IN_C_1

Primele 8 randuri de date (rand Excel 2..9; sumele relevante, D/C separate):

Rand A cont B denumire D tip E/F init G/H prec K/L rulaj O/P total Q/R fin AG AH AI
2 371 MARFURI A 0 / 0 2615 / 0 0 / 0 2615 / 0 2615 / 0 0 1 1
3 401 FURNIZORI P 0 / 0 0 / 3164.15 0 / 0 0 / 3164.15 0 / 3164.15 0 1 2
4 421 PERSONAL - SALARII DATORATE P 0 / 2574 0 / 2574 0 / 0 0 / 2574 0 / 2574 0 0 3
5 4315 CONTR. DE ASIGURARI SOCIALE P 0 / 938 0 / 938 0 / 0 0 / 938 0 / 938 0 0 4
6 4316 CONTR. DE ASIGURARI SOCIALE DE SANATATE P 0 / 375 0 / 375 0 / 0 0 / 375 0 / 375 0 0 5
7 436 CONTR. ASIGURATORIE DE MUNCA P 0 / 84 0 / 84 0 / 0 0 / 84 0 / 84 0 0 6
8 4426 TVA DEDUCTIBILA A 0 / 0 549.15 / 0 0 / 0 549.15 / 0 549.15 / 0 0 1 7
9 444 IMPOZITUL PE VENITURI DE NATURA SALARIILOR P 0 / 163 0 / 163 0 / 0 0 / 163 0 / 163 0 0 8

Ultimele 3 randuri (rand Excel 9..11):

Rand A cont B denumire D tip E/F init G/H prec O/P total Q/R fin AG AH AI
9 444 IMPOZIT... SALARIILOR P 0 / 163 0 / 163 0 / 163 0 / 163 0 0 8
10 641 CHELT. CU SALARIILE PERSONALULUI A 4050 / 0 4050 / 0 4050 / 0 4050 / 0 0 0 9
11 6461 CHELT. CU CONTRIB. ASIGURATORIE PT. MUNCA A SALARIATILOR A 84 / 0 84 / 0 84 / 0 84 / 0 0 0 10

Tipuri reale de valori (openpyxl):

  • CONT (A) = text (str), ex. Sheet1!A2 = '371'. Nu e numar. DENUMIRE (B) = text.
  • TIP (D) = text ('A' sau 'P'); CATEGORIE (C) = gol pe toate randurile.
  • Sumele (E..R si _1) = numerice (float la citire, dar stocate ca numar).
  • ANALITIC (AG) = boolean (False pe toate randurile); VALIDAT (AH), LINIE (AI) = intregi.

Cont sintetic vs analitic: in acest fisier nu exista niciun cont analitic - toate cele 10 coduri sunt fara punct (371, 401, 421, 4315, 4316, 436, 4426, 444, 641, 6461), iar ANALITIC (AG) este 0/False pe toate randurile. Deci din acest fisier singur nu se poate arata cum arata un rand analitic. Formatul analitic se vede in schimb in exportul de furnizori: saga-sqlite-furnizori-09-2026.xlsx!H2 = 401.00002 (text). Rezulta: sintetic = cod fara punct (401), analitic = sintetic.cod (401.00002). Daca si coloana ANALITIC distinge ceva ramane neclar (aici e 0 pe toate randurile, inclusiv pe cele care in vechiul export VFP aveau 1 - vezi B).

Coloane de sume (maparea ceruta):

Rol Coloane
precedent D / C DEB_PREC (G) / CRED_PREC (H)
rulaj D / C RULAJ_D (K) / RULAJ_C (L)
total D / C TOTAL_DEB (O) / TOTAL_CRED (P)
sold D / C FIN_D (Q) / FIN_C (R)

TOTAL_* sunt sume cumulative (precedent + rulaj), NU solduri; soldul final este FIN_*. Exista in plus: DEB_INIT/CRED_INIT (E/F) = sold la inceputul anului, SOLD_IN_D/SOLD_IN_C (I/J) = sold la inceput de perioada, RULAJT_D/RULAJT_C (M/N) = rulaj total cumulat.

Relatiile cerute, verificate pe toate cele 10 randuri (toleranta 0.005):

Verificare Rezultat
TOTAL_DEB = DEB_PREC + RULAJ_D 10/10 OK
TOTAL_CRED = CRED_PREC + RULAJ_C 10/10 OK
FIN_D - FIN_C = TOTAL_DEB - TOTAL_CRED 10/10 OK
(suplimentar) SOLD_IN_D - SOLD_IN_C = DEB_PREC - CRED_PREC 10/10 OK
(suplimentar) acelasi set _1 (S..AF) identic cu setul principal 10/10 OK

Nu exista rand care sa pice. Setul _1 (S..AF) este identic cu setul principal pe toate randurile - consecvent cu observatia din raport_sursa_saga_xlsx.md (pe conturile sintetice _1 este copia setului principal).

A.2 saga-sqlite-balanta-09-2026.xls vs .xlsx

Fisierele au acelasi continut (aceleasi 35 coloane in aceeasi ordine si aceleasi 10 randuri de date, cu aceleasi valori). Semnaturi: .xls = OLE2/BIFF8 (d0cf11e0a1b11ae1...), .xlsx = OOXML/ZIP (PK..). Diferente constatate:

Aspect .xls .xlsx
Nume foaie xl Sheet1
Antet litere mici (cont, denumire, ...) litere mari (CONT, DENUMIRE, ...)
CATEGORIE goala sir vid '' celula None
Restul valorilor identice identice

Comparatia programatica (valori numerice + text case-insensitive) a dat 0 diferente in afara celor de mai sus. Deci: acelasi export, doar alt format si alta conventie de nume/case.

A.3 saga-sqlite-furnizori-09-2026.xlsx

  • O foaie Sheet1, A1:J2 = 2 randuri x 10 coloane (1 antet + 1 rand de date).
  • Antet pe randul 1; datele pe randul 2. Nu exista rand de totaluri.
  • Coloane: A COD, B DENUMIRE, C NE_INIT, D TOTAL, E INCASARI, F NE_FIN, G NEACHITAT, H ANALITIC, I SOLD, J GRUPA.

Randul de date (rand 2): A2='00002', B2='TRANSPORT', C2=3164.15, D2=0, E2=0, F2=3164.15, G2=3164.15, H2='401.00002', I2=3164.15, J2 gol.

Tipuri: COD (A) si ANALITIC (H) = text; DENUMIRE (B) = text; sumele C..G, I numerice; GRUPA (J) gol.

Ce identifica partenerul: in acest export partenerul este identificat prin:

  • COD = codul SAGA al tertului (00002), text;
  • ANALITIC = contul analitic 401.00002 (text) - da, apare codul contului analitic;
  • DENUMIRE = TRANSPORT. Exportul NU contine codul fiscal (desi tabelul FURNIZORI din baza il are - vezi Partea C). Deci legatura cu partenerul se face pe COD si/sau pe ANALITIC, nu pe cod fiscal.

A.4 saga-sqlite-furnizori-facturi-un-furnizor-09-2026.xlsx

  • O foaie Sheet1, A1:J2 = 2 randuri x 10 coloane (1 antet + 1 rand de date).
  • Antet pe randul 1; datele pe randul 2. Nu exista rand de totaluri.
  • Coloane: A DATA, B NR, C CONT_FUR, D NE_INIT, E TOTAL, F PLATI, G NE_FIN, H NEACHITAT, I INF_SUPLM, J DATA_DOC.

Randul de date (rand 2): A2=2026-07-02 (datetime), B2='123' (text), C2='401.00002' (text), D2=3164.15, E2=0, F2=0, G2=3164.15, H2=3164.15, I2 gol, J2 gol.

Campuri de factura prezente: numar (NR), data operare (DATA), cont furnizor (CONT_FUR), sold initial neachitat (NE_INIT), total factura (TOTAL), plati (PLATI), neachitat final (NE_FIN), neachitat (NEACHITAT), informatii suplimentare (INF_SUPLM), data document (DATA_DOC). Lipsesc: data scadenta si valuta (desi in baza, tabelul de intrari, are SCADENT si COD_VALUTA - exportul nu le include). Nu exista nici o coloana de serie. Legatura de furnizor: prin CONT_FUR = 401.00002 (acelasi analitic ca in A.3) - nu exista COD de tert in acest export.


PARTEA B - comparatie cu ce stim deja

B.1 Cu CSV-ul din PDF (extract_balanta.py)

CSV-ul din PDF are coloanele: pagina, cont, denumire, prec_d, prec_c, rulaj_d, rulaj_c, total_d, total_c, sold_d, sold_c, este_total.

Corespondenta cu exportul nou:

CSV (PDF) Export nou (balanta)
cont CONT (A)
denumire DENUMIRE (B)
prec_d / prec_c DEB_PREC (G) / CRED_PREC (H)
rulaj_d / rulaj_c RULAJ_D (K) / RULAJ_C (L)
total_d / total_c TOTAL_DEB (O) / TOTAL_CRED (P)
sold_d / sold_c FIN_D (Q) / FIN_C (R)
pagina lipseste
este_total lipseste

Se poate produce acelasi CSV din exportul SAGA nou? Pentru partea de conturi + cele 8 sume: da, maparea e 1:1 si relatiile sunt aceleasi (verificate 10/10 la A.1). Ce lipseste:

  • pagina - proprie PDF-ului, depinde de paginare, nu are echivalent in export;
  • este_total - exportul nu are deloc randuri de totaluri; in CSV-ul din PDF acest marker distinge randurile "Total sume clasa N" si randul "Totaluri:", care in export nu exista (ar trebui calculate, nu citite);
  • setul de conturi poate diferi: in exemplul nou apar doar conturile cu miscare, la nivel sintetic.

B.2 Cu vechiul export SAGA-pe-VFP (raport_sursa_saga_xlsx.md, sectiunea 1)

Structura este identica cu cea documentata acolo pentru balanta.xls (VFP): aceleasi 35 de coloane, in aceeasi ordine, cu aceleasi nume (cont..fin_c, _1, analitic, validat, linie). Singurele diferente observate:

  • numele foii: xl/Sheet1 (nou) vs balanta (VFP);
  • case-ul antetului (mic in .xls, mare in .xlsx) - in raportul VFP antetul era cu litere mici;
  • valoarea coloanei analitic: in fisierul VFP vechi era 1 pe toate randurile, in exportul nou este 0/False pe toate randurile (dar aici toate randurile sunt sintetice - nu se poate decide daca diferenta e de semantica sau doar de firma/perioada).

Concluzie: un convertor care stie deja sa citeasca exportul VFP (conturi.dbf / balanta.xls) poate citi neschimbat si exportul SAGA nou Firebird - layout-ul de coloane este acelasi.

Observatie importanta (dovada in Partea C): desi layout-ul e identic cu tabelul CONTURI, valorile din export nu sunt un dump brut al tabelului - soldurile stocate in CONTURI sunt 0, iar cifrele din export corespund agregarii registrului de note (REGISTRU). Deci exportul e un raport calculat, nu o citire directa de tabel.


PARTEA C - baza Firebird CONT_BAZA.FDB (explorare, NUMAI CITIRE)

C.1 Ce client/server exista pe masina

  • Server Firebird instalat si pornit: serviciul FirebirdServerFirebird30_Saga ("C:\Program Files\Firebird\Firebird30_Saga\firebird.exe" -s Firebird30_Saga, LocalSystem).
  • C:\Program Files\Firebird\Firebird30_Saga\ contine: isql.exe, fbclient.dll, firebird.exe, gsec.exe, gbak.exe, gfix.exe, fbsvcmgr.exe, plus plugins\engine12.dll (Firebird 3.0). In plus SAGA are clientul in D:\SAGA250909\FbClient\ (fbclient.dll, plugins\engine12.dll, legacy_auth.dll, etc.).
  • Versiunea raportata de baza: 3.0.7 (via rdb$get_context('SYSTEM','ENGINE_VERSION')).
  • Port de ascultare: doar 3060 (RemoteServicePort = 3060 in firebird.conf; pe 3050 nu asculta nimic). O conexiune TCP la localhost/3060 ajunge la baza dar raspunde cu eroare de autentificare (Your user name and password are not defined) - deci parola SYSDBA nu e masterkey/masterke la server.

C.2 Cum s-a reusit conectarea (read-only)

  • S-a instalat firebird-driver (py -m pip install firebird-driver -> 2.0.3) si s-a indicat explicit clientul: fdb.load_api(r"C:\Program Files\Firebird\Firebird30_Saga\fbclient.dll").
  • Conexiunea embedded la fisier a functionat: fdb.connect(database=r"D:\SAGA250909\0001\CONT_BAZA.FDB", user="SYSDBA", password="masterkey", charset="WIN1250", no_gc=True) -> OK, versiune 3.0.7. Embedded accepta masterkey/masterke (autentificare locala/trusted); TCP la 3060 nu.
  • Respectarea "NUMAI CITIRE": s-a folosit o tranzactie read-only (fdb.tpb(Isolation.READ_COMMITTED, access_mode=TraAccessMode.READ)) si no_gc=True; s-au rulat doar SELECT-uri, apoi rollback. Nu s-a scris, nu s-a facut ALTER, nu s-a copiat baza.

C.3 Ce contine baza

191 de tabele, 0 view-uri. Cele relevante pentru conversie:

Tabel Randuri Ce este
CONTURI 586 planul de conturi + solduri stocate
FURNIZORI 2 furnizori
CLIENTI 0 clienti
INTRARI / INTRD 0 / 1 intrari = facturi furnizori (antet / detaliat)
IESIRI / IES_DET 0 / - iesiri = facturi clienti
REGISTRU 8 registru de note contabile (miscarile pe conturi)
FFACT 5 NU e tabel de facturi - e tabela de formulare/layout de tiparire

Structuri (campurile cheie; tipurile sunt cele din rdb$relation_fields/rdb$fields):

CONTURI (586 randuri) - plan de conturi: CONT VARCHAR(20) NOT NULL, DENUMIRE VARCHAR(64), TIP CHAR(1), DEB_INIT/CRED_INIT/DEB_PREC/CRED_PREC NUMERIC(15,2), CONT_INCH VARCHAR(20), DEB_INIT_V/CRED_INIT_V/DEB_PREC_V/CRED_PREC_V NUMERIC(14,2), COD_VALUTA VARCHAR(3), BLOCAT SMALLINT. Exemplu de rand: ('1012', 'CAPITAL SUBSCRIS VARSAT', 'P', 0.00, 0.00, 0.00, 0.00, ...). Atentie: CONT e text (se citeste ca sir, deci 401.00002 nu se strica); in acest exemplu toate soldurile stocate sunt 0 - vezi C.4.

FURNIZORI (2 randuri): COD VARCHAR(8) NOT NULL, DENUMIRE VARCHAR(64), COD_FISCAL VARCHAR(20), REG_COM VARCHAR(16), ANALITIC VARCHAR(20), GRUPA VARCHAR(16), plus adresa/IBAN/agent/etc. Randuri: ('00001','ROMFAST','','401.00001'), ('00002','TRANSPORT','1879855','401.00002').

CLIENTI (0 randuri): aceeasi structura de tert ca FURNIZORI (COD, DENUMIRE, COD_FISCAL, ANALITIC, GRUPA, adresa, agent, limita credit, e-Factura...).

INTRARI (0) / INTRD (1 rand) - facturi furnizori: ID_INTRARE, NR_NIR, NR_INTRARE, COD, DENUMIRE, CONT_FUR, DATA, SCADENT, NEACHITAT NUMERIC(15,2), NEACHITAT_VAL, COD_VALUTA, CURS, VAL_VAL, VAL_LEI, BAZA_TVA, TOTAL, TVA, TRANSP_VAL/LEI, VALIDAT, DATA_DOC, INF_SUPLM, ... Randul existent: ID_INTRARE=24, NR_INTRARE='123', COD='00001', CONT_FUR='401.00001', DATA=2026-07-02, SCADENT=NULL, TOTAL=0, NEACHITAT=0, COD_VALUTA='EUR'.

IESIRI (0) - facturi clienti: NR_IESIRE, ID_IESIRE, COD, DENUMIRE, CONT_CLI, DATA, SCADENT, TOTAL, NEACHITAT, BAZA_TVA, TVA, COMANDA, DATA_DOC, INF_SUPLM, IS_EF, ...

REGISTRU (8 randuri) - notele contabile (sursa miscarilor): ID_NOTA, CONT_D VARCHAR(20), CONT_C VARCHAR(20), SUMA NUMERIC(15,2), DATA, EXPLICATIE, VALIDAT, COD, TIP_O, PK. Aici se vede cheia: contul analitic apare in CONT_D/CONT_C ca text (401.00002), iar % este un cont colector.

Deci: planul de conturi cu solduri = CONTURI; furnizorii = FURNIZORI; clientii = CLIENTI; facturile furnizorilor = INTRARI/INTRD; facturile clientilor = IESIRI/IES_DET.

C.4 Dovada ca exporturile sunt rapoarte calculate, nu dump de tabel

  • CONTURI are 585 randuri utile (fara randul %) si 0 randuri cu sold stocat nenul (deb_prec/cred_prec/deb_init/cred_init = 0 pentru toti), si totusi exportul de balanta are cifre. Deci cifrele vin din agregarea REGISTRU la data raportului.
  • Potriviri exacte: REGISTRU are pe 371 debit 2615.00 -> exportul are 371 DEB_PREC=2615; pe 401.00002 credit 2615.00 + 549.15 = 3164.15 -> exportul are 401 CRED_PREC=3164.15 si exportul de furnizori SOLD=3164.15. In plus, miscarea veche pe 641 este din 2025-11-30 -> exportul are DEB_INIT=4050 (sold la inceput de an), ceea ce confirma semantica DEB_INIT/CRED_INIT = inceput de an si DEB_PREC/CRED_PREC = inceput de perioada.
  • In exportul de balanta contul apare ca 401 (sintetic), iar 401.00002 (analitic) nu apare ca rand separat -> exportul ruleaza analiticele in sinteticul lor (sau a fost cerut la nivel sintetic). Regula exacta de includere sintetic/analitic nu se poate stabili din acest singur exemplu.

C.5 Ce NU se poate stabili

  • Nu se poate confirma exact din ce tabel/se selecteaza randul de factura din exportul saga-sqlite-furnizori-facturi-un-furnizor: exportul are CONT_FUR='401.00002' si NR='123', dar singurul rand din INTRD are CONT_FUR='401.00001' (ROMFAST), tot cu NR_INTRARE='123'. Coloanele DATA/NR/CONT_FUR/TOTAL/NEACHITAT/INF_SUPLM/DATA_DOC exista in INTRD, dar NE_INIT, PLATI, NE_FIN nu exista ca si coloane in baza - sunt calculate (sold initial, plati, sold final). Potrivirea exacta a randului nu se poate face cu datele disponibile.
  • Semnificatia coloanei ANALITIC din balanta (0/False la toate randurile) nu se poate stabili.

Stare si verificari

  • Nu s-a modificat niciun fisier din proiect sau din D:\SAGA250909; nu s-a facut write-back; nu s-a comis nimic (git/svn). Baza Firebird a fost accesata doar cu SELECT, in tranzactie read-only, no_gc=True, fara copiere.
  • Verificari rulate: relatiile de sume pe balanta (10/10 OK pentru toate cele trei relatii cerute); comparatia .xls vs .xlsx (0 diferente reale); listarea tabelelor (191 tabele, 0 view-uri) si a structurilor cheie; potrivirea soldurilor export vs REGISTRU.
  • Temporare: C:\Users\mmari\AppData\Local\Temp\opencode\ (analiza.py, verif.py, fbtest*.py, fblist.py, fbcols.py, fbdata.py, fbsearch.py + dump-uri txt). Nu sunt in proiect.
  • Fara stare periculoasa: niciun .vc2 editat fara write-back, niciun proces lasat pornit, fara date de test consumate.