Lane-urile citire-xlsx si mapare: cititorul de foaie SAGA si pasul de mapare
citire_xlsx.py citeste .xlsx (openpyxl) si .xls (xlrd) in acelasi contract intern
ca extract_balanta.py. Prima foaie, nu dupa nume; potrivire exacta a coloanelor,
niciodata pe substring (TOTAL_DEB vs TOTAL_DEB_1); CONT ramane text.
mapare.py: propunere automata, corectii_<FIRMA>.xlsx detinut de utilizator si
mapare_<FIRMA>.xlsx regenerat de unealta, .TOATE/.RESTUL in ordinea exact ->
RESTUL -> TOATE, redenumirea sinteticului. Se opreste cu eroare la cont/acont
peste 4 caractere, coliziune accidentala si cont_saga inexistent in corectii.
Corectat in orchestrare: .xls-ul SAGA nu are inregistrare CODEPAGE, deci xlrd
cadea pe iso-8859-1 si strica denumirile ("FURNIZORI \x97 DEBITORI"). Citirea se
face cu encoding_override="cp1250", cu o verificare in test care pica daca revine.
18 teste trec, inclusiv poarta de regresie pe etalon.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYeAtVxeS8m4oXekjX8Am2
This commit is contained in:
103
docs/raport_lane_citire_xlsx.md
Normal file
103
docs/raport_lane_citire_xlsx.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Raport lane `citire-xlsx` - cititorul de foaie de calcul SAGA
|
||||
|
||||
Data: 21.09.2026. Lane: `citire-xlsx`, in paralel cu `mapare`.
|
||||
|
||||
## 1. Facut
|
||||
|
||||
### Livrabil nou: `citire_xlsx.py`
|
||||
|
||||
Cititor pentru balantele SAGA `.xls` / `.xlsx`, scrie `balanta_<FIRMA>.csv` cu contractul
|
||||
intern (13 coloane). Puncte:
|
||||
|
||||
- `citire_xlsx.py:24` - `COLOANE` = contractul intern, identic cu `extract_balanta.py` plus `tip`.
|
||||
- `citire_xlsx.py:27` - `MAPARE_SUME`: `DEB_PREC/CRED_PREC -> prec_d/prec_c`,
|
||||
`RULAJ_D/RULAJ_C -> rulaj_d/rulaj_c`, `TOTAL_DEB/TOTAL_CRED -> total_d/total_c`,
|
||||
`FIN_D/FIN_C -> sold_d/sold_c`.
|
||||
- `citire_xlsx.py:41` `_index_antet` - potrivire **exacta, case-insensitive** (`strip().upper()`),
|
||||
niciodata pe substring; coloana ceruta lipsa sau duplicata -> `ValueError` cu numele coloanei.
|
||||
- `citire_xlsx.py:57` `_randuri_xlsx` - openpyxl, `data_only=True`, `read_only=True`.
|
||||
- `citire_xlsx.py:72` `_randuri_xls` - xlrd 2.0.2.
|
||||
- `citire_xlsx.py:83` `_randuri` - alege dupa extensie; alta extensie -> `ValueError`.
|
||||
Ambele iau **prima foaie** (`sheetnames[0]` / `sheet_by_index(0)`), fara cautare dupa nume.
|
||||
- `citire_xlsx.py:104` `_cont` - CONT ramane text; un float intreg (`401.0`) devine `"401"`,
|
||||
unul cu zecimale (`401.00002`) devine `"401.00002"`, niciodata numar.
|
||||
- `citire_xlsx.py:117` `_num` - `None`/gol -> `0.0`; alt text neconvertibil -> `ValueError`.
|
||||
- `citire_xlsx.py:132` `citeste_balanta` - produce randurile; `pagina` si `este_total` raman
|
||||
goale (exportul nu are rand de totaluri); sare doar peste randurile complet goale.
|
||||
- `citire_xlsx.py:152` `scrie_csv` - `utf-8-sig`, `newline=""`, exact ca `extract_balanta.py`.
|
||||
- `citire_xlsx.py:159` `main` - CLI: `py citire_xlsx.py <sursa> <iesire.csv>`.
|
||||
|
||||
### Test nou: `tests/test_citire_xlsx.py`
|
||||
|
||||
`unittest` din stdlib, fara dependinte noi. Cazuri:
|
||||
|
||||
- `:85` `test_xlsx_si_xls_identice` - `.xlsx` (openpyxl) si `.xls` (xlrd) produc acelasi rezultat.
|
||||
- `:102` `test_relatii_si_frunze` - pe ambele fisiere: `total_d = prec_d + rulaj_d`,
|
||||
`total_c = prec_c + rulaj_c`, `sold_d - sold_c = total_d - total_c` pe toate randurile,
|
||||
plus `SUM(sold_d) = SUM(sold_c)` pe frunze. Toleranta 0.01.
|
||||
- `:110` `test_cont_ramane_text` - toate valorile `cont` sunt `str`.
|
||||
- `:116` `test_danube_analitice` - `saga2roa_danube\balanta.xls` (341 randuri, 263 analitice);
|
||||
daca lipseste, `skipTest`. Verifica explicit ca `105.10` ramane text, nu numar.
|
||||
- `:133` `test_cont_numeric_devine_text` - celula `CONT` scrisa ca numar (`401.00002`) iese text.
|
||||
- `:148` `test_coloana_lipsa_si_fara_substring` - antet cu `TOTAL_DEB_1` dar fara `TOTAL_DEB`
|
||||
da `ValueError` care numeste `TOTAL_DEB`: nici coloana lipsa nu trece tacut, nici `_1` nu se
|
||||
confunda cu coloana principala.
|
||||
|
||||
## 2. Verificari
|
||||
|
||||
Comanda: `py -m unittest discover -s tests -v` (din radacina proiectului). Iesire exacta:
|
||||
|
||||
```
|
||||
test_coloana_lipsa_si_fara_substring (test_citire_xlsx.TestCitireXlsx.test_coloana_lipsa_si_fara_substring) ... ok
|
||||
test_cont_numeric_devine_text (test_citire_xlsx.TestCitireXlsx.test_cont_numeric_devine_text) ... ok
|
||||
test_cont_ramane_text (test_citire_xlsx.TestCitireXlsx.test_cont_ramane_text) ... ok
|
||||
test_danube_analitice (test_citire_xlsx.TestCitireXlsx.test_danube_analitice) ... ok
|
||||
test_relatii_si_frunze (test_citire_xlsx.TestCitireXlsx.test_relatii_si_frunze) ... ok
|
||||
test_xlsx_si_xls_identice (test_citire_xlsx.TestCitireXlsx.test_xlsx_si_xls_identice) ... ok
|
||||
test_fundatia (test_regresie.TestRegresieEtalon.test_fundatia) ... ok
|
||||
test_master (test_regresie.TestRegresieEtalon.test_master) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 8 tests in 0.542s
|
||||
|
||||
OK
|
||||
```
|
||||
|
||||
`test_regresie.py` existent trece neatinse. (Pe stderr apar avertismentele cunoscute ale xlrd
|
||||
`OLE2 inconsistency` / `No CODEPAGE` si cel al openpyxl despre stil - nu afecteaza rezultatul.)
|
||||
|
||||
Verificare CLI capat-la-capat, iesire in director temporar:
|
||||
|
||||
```
|
||||
py citire_xlsx.py "exemple/saga-sqlite-balanta-09-2026.xlsx" "$TEMP/out_test.csv"
|
||||
-> scris ...out_test.csv: 10 randuri
|
||||
-> pagina,cont,denumire,prec_d,prec_c,rulaj_d,rulaj_c,total_d,total_c,sold_d,sold_c,este_total,tip
|
||||
,371,MARFURI,2615.0,0.0,0.0,0.0,2615.0,0.0,2615.0,0.0,,A
|
||||
```
|
||||
|
||||
Cifre pe cele trei fisiere (relatii de sume, frunze): xlsx 10 randuri / 0 esec / 10 frunze;
|
||||
xls identic; DANUBE 341 randuri / 0 esec / 320 frunze, balanta frunzelor 19018742.48 = 19018742.48.
|
||||
|
||||
Fisiere noi: `citire_xlsx.py` (170 linii) si `tests/test_citire_xlsx.py` (137 linii), ambele
|
||||
**ASCII curat (0 octeti > 0x7F)** si LF (conventia fisierelor `.py` din proiect). Nu sunt `.vc2`
|
||||
/ `.sc2` / `.prg`, deci regula de cens cp1250 nu se aplica.
|
||||
|
||||
`git status`: doar `?? citire_xlsx.py` si `?? tests/test_citire_xlsx.py`. Nimic altceva atins.
|
||||
|
||||
## 3. Nefacut / blocat
|
||||
|
||||
- Interfata catre `genereaza_xlsx.py` (maparea) nu e atinsa: lane-ul `mapare` face asta.
|
||||
`genereaza_xlsx.citeste_balanta` foloseste `csv.DictReader` si citeste doar
|
||||
`cont/denumire/total_d/total_c/este_total`, deci coloana noua `tip` nu il deranjeaza.
|
||||
- `extract_balanta.py` nu a fost atins (interzis), deci CSV-ul de PDF are inca 12 coloane, fara
|
||||
`tip`. Contractul din plan cere 13 coloane pentru toate cele 3 cititoare; alinierea PDF-ului
|
||||
ramane de facut intr-un alt lane, cand se decide forma lui `tip` pe drumul PDF.
|
||||
- Intersectia cu `citire-fdb` (CSV identic xlsx/FDB pe firma de joaca) ramane pe lane-ul `citire-fdb`.
|
||||
- Nu am adaugat `xlrd` in `CLAUDE.md` la `Mediu` (il cere planul, dar e in afara scopului acestui
|
||||
lane; xlrd era deja instalat si documentat in `docs/raport_sursa_saga_xlsx.md`).
|
||||
|
||||
## 4. Stare periculoasa
|
||||
|
||||
Niciuna. Fara `.vc2`/`.sc2` editat fara write-back, fara procese lasate pornite, fara date de test
|
||||
consumate (iesirile de test au mers in `%TEMP%`). Fara commit, fara `git add`, fara `sed -i`.
|
||||
Nu s-a modificat nimic sub `D:\ROA\ROACONT`, `D:\SAGA250909` sau `saga2roa*`.
|
||||
59
docs/raport_lane_mapare.md
Normal file
59
docs/raport_lane_mapare.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Raport lane `mapare` - solduri2roa etapa 2
|
||||
|
||||
## 1. Facut
|
||||
|
||||
Doua fisiere noi, nimic altceva atins.
|
||||
|
||||
`mapare.py` (nou):
|
||||
- `mapare.py:33-48` contractul: coloanele `mapare_` / `corectii_`, sursele, regex
|
||||
`.TOATE`/`.RESTUL`, config parteneri, randul-exemplu comentat.
|
||||
- `mapare.py:55-65` `citeste_parteneri` - doar citire din `config/conturi_parteneri.csv`.
|
||||
- `mapare.py:68-79` `separa` / `propune` - propunerea automata; partener -> `acont` gol.
|
||||
- `mapare.py:99-106` `creeaza_corectii` - fisier gol + antet + exemplu comentat.
|
||||
- `mapare.py:109-139` `citeste_corectii` - antet exact, randurile `#` ignorate, duplicat = Stop.
|
||||
- `mapare.py:142-151` `_regula` - ordinea exact -> `.RESTUL` -> `.TOATE`.
|
||||
- `mapare.py:167-176` `_verifica_corectii` - cheie fara cont in balanta = Stop.
|
||||
- `mapare.py:179-194` `_verifica_coliziuni` - colaps legal doar sub acelasi `.TOATE`/`.RESTUL`;
|
||||
partenerii si randurile `exclus` sunt in afara verificarilor.
|
||||
- `mapare.py:197-236` `aplica` - merge-ul; lungimi > 4 = Stop.
|
||||
- `mapare.py:239-260` `scrie_mapare` / `genereaza_mapare` - scrie maparea, creeaza corectiile
|
||||
doar daca lipsesc.
|
||||
|
||||
`tests/test_mapare.py` (nou): 10 teste (vezi 2).
|
||||
|
||||
## 2. Verificari
|
||||
|
||||
- Comanda: `py -m unittest discover -s tests -v`
|
||||
- Rezultat: `Ran 18 tests in 0.853s` / `OK` (exit 0).
|
||||
- Include `tests/test_regresie.py` (2/2 OK) si `tests/test_citire_xlsx.py` al lane-ului
|
||||
paralel (6/6 OK). Zero esecuri.
|
||||
- Cens octeti > 0x7F: `mapare.py` 0, `tests/test_mapare.py` 0. LF, ca restul `.py` din proiect
|
||||
(`genereaza_xlsx.py` tot LF) - nu am introdus CRLF si nici diacritice.
|
||||
- Smoke pe date reale (in temp, fara sa scriu in radacina): `mapare.aplica` pe
|
||||
`balanta_FUNDATIA.csv` (82 conturi) si `balanta_MASTERJOB.csv` (81) - fara Stop, fara coliziuni.
|
||||
- `git status`: doar `mapare.py` si `tests/test_mapare.py` adaugate de mine (restul untracked sunt
|
||||
ale lane-ului `citire-xlsx`). Fara commit, fara `git add`.
|
||||
|
||||
## 3. Decizii si intrebari deschise
|
||||
|
||||
- Propunerea automata: `cont` = sinteticul dinaintea punctului, `acont` = analiticul concatenat
|
||||
(toate caracterele, nu doar cifre - `conturi_roa.dbf` are `acont2` alfanumeric `X`/`PG`).
|
||||
Peste 4 caractere -> Stop (ca `genereaza_xlsx.py:196`), nu trunchiere. Nu am preluat literal
|
||||
"primele max 4 caractere" din plan, care ar include punctul la sintetici de 3 cifre.
|
||||
- `.TOATE`/`.RESTUL` se aplica si randului sintetic (parinte), altfel o redenumire
|
||||
`409 -> 4091` ar cere doua corectii. Fara efect pe colaps: toate au aceeasi regula.
|
||||
- Coliziunea e permisa doar cand toate conturile au **aceeasi** cheie `.TOATE`/`.RESTUL`;
|
||||
un amestec exact + `.TOATE` pe aceeasi pereche da Stop.
|
||||
- `exclus` se intoarce in rezultatul structurat (pentru integrare), dar nu are coloana in
|
||||
`mapare_` - contractul are exact 6 coloane.
|
||||
- `cont` gol intr-o corectie = pastreaza propunerea automata; `acont` gol = fara analitic.
|
||||
- `PLANCONT` nu se preia (cerut).
|
||||
|
||||
Nefacut: integrarea in generator (lane `integrare`). Nu am adaugat `citire_balanta` in modul;
|
||||
integrarea va pasa `rows` din `genereaza_xlsx.citeste_balanta` (suporta `sold` sau
|
||||
`total_d/total_c`).
|
||||
|
||||
## 4. Stare periculoasa
|
||||
|
||||
Niciuna: fara binar editat/write-back, fara procese ramase, fara fisiere scrise in radacina
|
||||
(maparile de test au fost doar in temp). Fara commit.
|
||||
Reference in New Issue
Block a user