Lane contract: config/conturi_parteneri.csv, divergenta Oracle, poarta de regresie

Setul de conturi cu parteneri iese din literali si trece in
config/conturi_parteneri.csv, cu categorie si motiv. Comportamentul e neschimbat;
lipsa fisierului opreste cu eroare, nu cade pe set gol.

CONFIG_CONT_IREG ramane consultativ: tools/divergenta_parteneri.py raporteaza
divergenta, nu o aplica. Constatarea (27 conturi in Oracle si nu la noi, 3 la noi
si nu in Oracle - 5121, 5124, 5311) e scrisa in docs/decizii_import.md.

tests/test_regresie.py compara iesirea codului de azi cu tests/golden/ pe valori
de celula, prin anonimizatorul refolosit ca functie. Trece pe FUNDATIA si MASTER;
verificat ca pica la o schimbare reala de set de conturi.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYeAtVxeS8m4oXekjX8Am2
This commit is contained in:
2026-09-21 15:53:00 +03:00
parent 15bb26ac15
commit f23b3367d8
9 changed files with 477 additions and 49 deletions

View File

@@ -0,0 +1,37 @@
# Snapshot consultativ CONFIG_CONT_IREG (MARIUSM_AUTO@ROA_CENTRAL, Oracle local), conturi cu CU_INREGISTRARI = 1.
# Construit o singura data la 21.09.2026 din docs/config_cont_ireg.md (sectiunea "Conturi cu CU_INREGISTRARI = 1").
# Pentru reimprospatare din Oracle: sqlplus ... @sql/export_config_cont_ireg.sql (suprascrie acest fisier).
cont
1621
167
232
234
261
2678
2691
401
403
404
408
409
4091
4092
4093
4094
4111
4118
413
418
419
426
4511
4551
456
457
461
462
471
472
4754
542
8051
Can't render this file because it contains an unexpected character in line 2 and column 82.

View File

@@ -0,0 +1,10 @@
cont,categorie,motiv
5121,BANCA,Cont bancar in lei: analiticele raman analitice si primesc partener.
5124,BANCA,Cont bancar in valuta: analiticele raman analitice si primesc partener.
401,SINTETIC,Furnizori: BALANTA agregat pe sintetic plus cate un FACTURA per analitic.
4092,SINTETIC,Furnizori de imobilizari: BALANTA agregat plus FACTURA per analitic.
4111,SINTETIC,Clienti: BALANTA agregat plus FACTURA per analitic.
461,SINTETIC,Debitori diversi: BALANTA agregat plus FACTURA per analitic.
462,SINTETIC,Creditori diversi: BALANTA agregat plus FACTURA per analitic.
4551,SINTETIC,Actionari: BALANTA agregat plus FACTURA per analitic.
5311,FARA_ANALITICE,Casa in lei: BALANTA plus FACTURA pe sintetic cu partener CENTRALA.
1 cont categorie motiv
2 5121 BANCA Cont bancar in lei: analiticele raman analitice si primesc partener.
3 5124 BANCA Cont bancar in valuta: analiticele raman analitice si primesc partener.
4 401 SINTETIC Furnizori: BALANTA agregat pe sintetic plus cate un FACTURA per analitic.
5 4092 SINTETIC Furnizori de imobilizari: BALANTA agregat plus FACTURA per analitic.
6 4111 SINTETIC Clienti: BALANTA agregat plus FACTURA per analitic.
7 461 SINTETIC Debitori diversi: BALANTA agregat plus FACTURA per analitic.
8 462 SINTETIC Creditori diversi: BALANTA agregat plus FACTURA per analitic.
9 4551 SINTETIC Actionari: BALANTA agregat plus FACTURA per analitic.
10 5311 FARA_ANALITICE Casa in lei: BALANTA plus FACTURA pe sintetic cu partener CENTRALA.

View File

@@ -118,3 +118,24 @@ Consecinte:
ambele randuri cu `acont` gol. Cont cu parteneri **si** analitice in ROA (5121, 5124) ->
ambele randuri cu acelasi `acont`.
- Latura conteaza si ea: un rand FACTURA pe debit nu atinge randul BALANTA de pe credit.
## Divergenta fata de CONFIG_CONT_IREG (constatata 21.09.2026)
Setul operational de conturi cu parteneri este `config/conturi_parteneri.csv` (9 conturi), validat
in productie. `CONFIG_CONT_IREG` din Oracle este doar **consultativ**; divergentele se raporteaza,
nu se aplica. Comparatia se reface oricand cu:
```
py tools/divergenta_parteneri.py config/config_cont_ireg_snapshot.csv
```
Rezultatul de azi (snapshot: conturile cu `CU_INREGISTRARI = 1` din `docs/config_cont_ireg.md`):
- **In Oracle, nu la noi (27):** `1621, 167, 232, 234, 261, 2678, 2691, 403, 404, 408, 409, 4091,
4093, 4094, 4118, 413, 418, 419, 426, 4511, 456, 457, 471, 472, 4754, 542, 8051`
- **La noi, nu in Oracle (3):** `5121, 5124, 5311`
Interpretare: Oracle listeaza conturi de parteneri pe care codul validat nu le trateaza ca parteneri
(inclusiv 404, 408, 409, 419 si derivatele 409x). Invers, `5121`, `5124` (banca) si `5311` (casa)
sunt tratate ca parteneri de `genereaza_xlsx.py` desi Oracle nu le listeaza (pct. 11-15). Daca vreodata
se schimba setul operational, se schimba constient in CSV, nu pe baza acestui export.

View File

@@ -0,0 +1,95 @@
# Raport lane `contract` - etapa 2
## 1. Facut
### 1.1 `config/conturi_parteneri.csv`
- `config/conturi_parteneri.csv:1-10` - 9 conturi, antet exact `cont,categorie,motiv`.
Categoriile (`BANCA` / `SINTETIC` / `FARA_ANALITICE`) corespund exact seturilor
`PARTENER_BANCA` / `PARTENER_SINTETIC` / `PARTENER_FARA_ANALITICE` de dinainte
(`genereaza_xlsx.py:46-50`).
### 1.2 `genereaza_xlsx.py` (singura modificare permisa)
- `genereaza_xlsx.py:48-49` - `CONFIG_PARTENERI`, cale absoluta fata de fisierul scriptului.
- `genereaza_xlsx.py:56-70` - `citeste_conturi_parteneri()`: citeste CSV-ul la pornire; fisier
lipsa sau categorie necunoscuta -> `Stop` cu mesaj clar (nu cade pe set gol).
- `genereaza_xlsx.py:77-78` - cele trei seturi se incarca din CSV la import.
- `LATURA_CREDIT` (`:43`) **nu** s-a mutat in CSV - neatins.
- Verificat bit-identic: regenerare FUNDATIA/MASTER => 0 diferente de celula fata de
`init_FUNDATIA_2025_12.xlsx` / `init_MASTER_2025_12.xlsx` din radacina (76x39, respectiv 81x39).
### 1.3 Script divergenta fata de Oracle
- `sql/export_config_cont_ireg.sql` - `SELECT cont FROM config_cont_ireg WHERE cu_inregistrari = 1`,
spool CSV cu o coloana (`CONT`); nume de coloane luate din `docs/config_cont_ireg.md`.
- `config/config_cont_ireg_snapshot.csv` - snapshot construit o singura data (21.09.2026) din
`docs/config_cont_ireg.md:91`, cu comentariu in antet despre sursa si data.
- `tools/divergenta_parteneri.py:17` citeste config-ul, `:30` snapshot-ul (ignora liniile `#` si
antetul `cont`/`CONT`), `:51` tipareste cele doua liste. Nu scrie in config, nu se conecteaza la
Oracle, doar stdlib.
### 1.4 `docs/decizii_import.md`
- `docs/decizii_import.md:122` - sectiune noua "Divergenta fata de CONFIG_CONT_IREG (constatata
21.09.2026)" cu ambele liste complete si comanda de refacere.
### 1.5 Anonimizator + poarta de regresie
- `tests/anonimizeaza_etalon.py:49` `citeste_corespondenta`, `:54` `anonimizeaza_foaie`,
`:99` `anonimizeaza_fisier` - functii importabile; `:112` `anonimizeaza()` refoloseste acelasi nucleu.
Verificat: golden regenerat din `init_*.xlsx` = golden comis pe valori de celula (0 diferente),
tabela de corespondenta neschimbata.
- `tests/test_regresie.py:44` `setUpClass` (skip daca lipseste tabela, `:46`), `:78` `_verifica`
(comparatie pe valori de celula), `:115/:118` cele doua teste. Scrie xlsx-urile intr-un director
temporar, nu peste cele din radacina.
## 2. Verificari
- Cens octeti: toate fisierele atinse raman **ASCII pur** (0 octeti > 0x7F inainte si dupa).
Nu s-a atins niciun `.vc2/.sc2/.prg`.
- Terminatoare de linie: `sql/export_config_cont_ireg.sql` scris **CRLF** (27 linii). Fisierele
`.py`/`.csv` LF, ca restul surselor Python existente.
- Comanda de test (output exact):
```
> py -m unittest discover -s tests -v
test_fundatia (test_regresie.TestRegresieEtalon.test_fundatia) ... ok
test_master (test_regresie.TestRegresieEtalon.test_master) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.416s
OK
```
- Comanda de divergenta (output exact):
```
> py tools/divergenta_parteneri.py config/config_cont_ireg_snapshot.csv
Conturi comparate: la noi 9, in Oracle 33
In Oracle, nu la noi (27): 1621, 167, 232, 234, 261, 2678, 2691, 403, 404, 408, 409, 4091, 4093, 4094, 4118, 413, 418, 419, 426, 4511, 456, 457, 471, 472, 4754, 542, 8051
La noi, nu in Oracle (3): 5121, 5124, 5311
```
- Verificat ca mesajul de esec al testului contine fisier, rand, coloana, asteptat si gasit
(simulat cu un golden modificat): `g.xlsx: rand 3, coloana 2: asteptat 'TAMPER', gasit 12`.
- Verificat ca `SkipTest` se declanseaza cand lipseste `tests/_corespondenta_anonimizare.json`.
- Netinse: `init_FUNDATIA_2025_12.xlsx`, `init_MASTER_2025_12.xlsx` (mtime 00:13), `tests/golden/*`,
`tests/_corespondenta_anonimizare.json`, `docs/verificare_*.md`, `init_facturi_balanta_note.xlsx`.
## 3. Divergenta constatata fata de Oracle
- **In Oracle, nu la noi (27):** `1621, 167, 232, 234, 261, 2678, 2691, 403, 404, 408, 409, 4091,
4093, 4094, 4118, 413, 418, 419, 426, 4511, 456, 457, 471, 472, 4754, 542, 8051`
- **La noi, nu in Oracle (3):** `5121, 5124, 5311`
Aceeasi divergenta e scrisa in `docs/decizii_import.md:122`.
## 4. Nefacut / blocat
- SQL-ul **nu s-a rulat pe Oracle** (interzis in acest lane). Snapshot-ul e construit din
`docs/config_cont_ireg.md`. `tools/divergenta_parteneri.py` citeste si formatul produs de SQL
(antet `CONT`), deci refacerea din Oracle nu cere modificari.
- Nimic blocat. Singura modificare in `genereaza_xlsx.py` este mutarea seturilor de conturi in CSV.
## 5. Stare periculoasa
Niciuna: fara commit / `git add`, fara write-back in binar, fara procese ramase, fisierele
`init_*.xlsx` din radacina si etalonul neatinse. Toate iesirile de test sunt intr-un director
temporar, sters la finalul testului. Un `bash.exe.stackdump` aparut in timpul sesiunii a fost sters.

View File

@@ -42,13 +42,39 @@ TEMPLATE = "init_facturi_balanta_note.xlsx"
# conturile din lista merg pe CREDIT, tot restul pe DEBIT.
LATURA_CREDIT = {"401", "404", "462", "419", "408", "4551"}
# 5121/5124: analiticele raman analitice SI primesc partener (BALANTA + FACTURA
# pe acelasi acont).
PARTENER_BANCA = {"5121", "5124"}
# Conturi cu parteneri, un singur BALANTA agregat pe sintetic + FACTURA per analitic.
PARTENER_SINTETIC = {"401", "4092", "4111", "461", "462", "4551"}
# Casa: fara analitice, BALANTA + FACTURA pe sintetic.
PARTENER_FARA_ANALITICE = {"5311"}
# Setul operational de conturi cu parteneri: config/conturi_parteneri.csv (validat
# in productie; CONFIG_CONT_IREG e doar consultativ, vezi docs/decizii_import.md).
# Categoriile CSV corespund celor trei seturi: BANCA, SINTETIC, FARA_ANALITICE.
CONFIG_PARTENERI = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"config", "conturi_parteneri.csv")
class Stop(Exception):
"""Conditie neprevazuta: nu ghicim, raportam."""
def citeste_conturi_parteneri(path=CONFIG_PARTENERI):
"""(BANCA, SINTETIC, FARA_ANALITICE) din CSV; fisier lipsa = eroare, nu set gol."""
if not os.path.exists(path):
raise Stop(f"lipseste fisierul de configurare a partenerilor: {path}")
seturi = {"BANCA": set(), "SINTETIC": set(), "FARA_ANALITICE": set()}
with open(path, encoding="utf-8-sig", newline="") as f:
for r in csv.DictReader(f):
cont = (r.get("cont") or "").strip()
categorie = (r.get("categorie") or "").strip()
if not cont:
continue
if categorie not in seturi:
raise Stop(f"categorie necunoscuta in {path}: {categorie!r}")
seturi[categorie].add(cont)
return seturi["BANCA"], seturi["SINTETIC"], seturi["FARA_ANALITICE"]
# BANCA (5121/5124): analiticele raman analitice SI primesc partener.
# SINTETIC: un singur BALANTA agregat pe sintetic + FACTURA per analitic.
# FARA_ANALITICE (5311): fara analitice, BALANTA + FACTURA pe sintetic.
(PARTENER_BANCA, PARTENER_SINTETIC,
PARTENER_FARA_ANALITICE) = citeste_conturi_parteneri()
PARTENER = PARTENER_BANCA | PARTENER_SINTETIC | PARTENER_FARA_ANALITICE
@@ -58,10 +84,6 @@ FIRME = [
]
class Stop(Exception):
"""Conditie neprevazuta: nu ghicim, raportam."""
def citeste_header():
wb = load_workbook(TEMPLATE, read_only=True)
ws = wb.active

View File

@@ -0,0 +1,27 @@
-- ============================================================
-- solduri2roa - export consultativ CONFIG_CONT_IREG
-- Schema : MARIUSM_AUTO@ROA_CENTRAL (Oracle local, NU productie)
-- Data : 2026-09-21
-- Ce face : listeaza conturile cu CU_INREGISTRARI = 1, ca CSV cu o
-- singura coloana (cont). Rezultatul e comparat consultativ
-- cu config/conturi_parteneri.csv de tools/divergenta_parteneri.py;
-- divergentele se RAPORTEAZA, nu se aplica.
-- Sursa : docs/config_cont_ireg.md (coloanele CONT, CU_INREGISTRARI).
-- Rulare : sqlplus -S "MARIUSM_AUTO/...@ROA_CENTRAL" @sql/export_config_cont_ireg.sql
-- Atentie : spool-ul suprascrie config/config_cont_ireg_snapshot.csv (fisierul
-- citit de tools/divergenta_parteneri.py). Ruleaza din radacina proiectului.
-- ============================================================
set heading on
set feedback off
set pagesize 500
set linesize 100
set trimspool on
set colsep ,
spool config/config_cont_ireg_snapshot.csv
select cont
from config_cont_ireg
where cu_inregistrari = 1
order by cont;
spool off
exit;

View File

@@ -46,6 +46,69 @@ COL_TIP = 5
IBAN = re.compile(r"RO\s?\d{2}(?:\s?[A-Z0-9]{4}){5}", re.IGNORECASE)
def citeste_corespondenta(cale=CORESP):
"""Tabela real -> fals folosita la construirea etalonului. NU se regenereaza."""
return json.loads(pathlib.Path(cale).read_text(encoding="utf-8"))
def anonimizeaza_foaie(ws, coresp):
"""Anonimizeaza foaia `ws` in loc, completand tabela `coresp`. NU salveaza.
Intoarce (randuri FACTURA atinse, celule cu IBAN inlocuite).
"""
schimbate = 0
for rand in range(2, ws.max_row + 1):
if (ws.cell(rand, COL_TIP).value or "").strip().upper() != "FACTURA":
continue
schimbate += 1
nume = ws.cell(rand, COL_NUME).value
if nume:
if nume not in coresp["nume"]:
coresp["nume"][nume] = "PARTENER %03d" % (len(coresp["nume"]) + 1)
ws.cell(rand, COL_NUME).value = coresp["nume"][nume]
cf = ws.cell(rand, COL_COD_FISCAL).value
if cf:
cf = str(cf)
if cf not in coresp["cod_fiscal"]:
coresp["cod_fiscal"][cf] = "CF%06d" % (len(coresp["cod_fiscal"]) + 1)
ws.cell(rand, COL_COD_FISCAL).value = coresp["cod_fiscal"][cf]
for col in (COL_REG_COM, COL_ADRESA, COL_EXPLICATIA):
if ws.cell(rand, col).value:
ws.cell(rand, col).value = None
# IBAN-urile apar si in afara randurilor FACTURA (denumirea analiticului de banca)
ibanuri = 0
for rand in ws.iter_rows():
for celula in rand:
if not isinstance(celula.value, str) or not IBAN.search(celula.value):
continue
def fals(m, _c=coresp):
real = m.group(0)
if real not in _c["iban"]:
_c["iban"][real] = "RO00 IBAN %04d" % (len(_c["iban"]) + 1)
return _c["iban"][real]
celula.value = IBAN.sub(fals, celula.value)
ibanuri += 1
return schimbate, ibanuri
def anonimizeaza_fisier(sursa, tinta, coresp=None):
"""Scrie in `tinta` o copie anonimizata a lui `sursa`, cu aceleasi reguli.
Tabela implicita e cea comisa (CORESP); nu o regenereaza, doar o citeste.
"""
if coresp is None:
coresp = citeste_corespondenta()
wb = openpyxl.load_workbook(sursa)
schimbate, ibanuri = anonimizeaza_foaie(wb.active, coresp)
wb.save(tinta)
return schimbate, ibanuri
def anonimizeaza():
GOLDEN.mkdir(parents=True, exist_ok=True)
coresp = {"nume": {}, "cod_fiscal": {}, "iban": {}}
@@ -61,44 +124,7 @@ def anonimizeaza():
shutil.copy(cale, GOLDEN / tinta)
wb = openpyxl.load_workbook(GOLDEN / tinta)
ws = wb.active
schimbate = 0
for rand in range(2, ws.max_row + 1):
if (ws.cell(rand, COL_TIP).value or "").strip().upper() != "FACTURA":
continue
schimbate += 1
nume = ws.cell(rand, COL_NUME).value
if nume:
if nume not in coresp["nume"]:
coresp["nume"][nume] = "PARTENER %03d" % (len(coresp["nume"]) + 1)
ws.cell(rand, COL_NUME).value = coresp["nume"][nume]
cf = ws.cell(rand, COL_COD_FISCAL).value
if cf:
cf = str(cf)
if cf not in coresp["cod_fiscal"]:
coresp["cod_fiscal"][cf] = "CF%06d" % (len(coresp["cod_fiscal"]) + 1)
ws.cell(rand, COL_COD_FISCAL).value = coresp["cod_fiscal"][cf]
for col in (COL_REG_COM, COL_ADRESA, COL_EXPLICATIA):
if ws.cell(rand, col).value:
ws.cell(rand, col).value = None
# IBAN-urile apar si in afara randurilor FACTURA (denumirea analiticului de banca)
ibanuri = 0
for rand in ws.iter_rows():
for celula in rand:
if not isinstance(celula.value, str) or not IBAN.search(celula.value):
continue
def fals(m, _c=coresp):
real = m.group(0)
if real not in _c["iban"]:
_c["iban"][real] = "RO00 IBAN %04d" % (len(_c["iban"]) + 1)
return _c["iban"][real]
celula.value = IBAN.sub(fals, celula.value)
ibanuri += 1
schimbate, ibanuri = anonimizeaza_foaie(ws, coresp)
wb.save(GOLDEN / tinta)
raport.append((tinta, ws.max_row - 1, schimbate, ibanuri))

123
tests/test_regresie.py Normal file
View File

@@ -0,0 +1,123 @@
"""Poarta de regresie: codul de conversie de azi trebuie sa reproduca etalonul.
Ruleaza convertorul pe balantele CSV de pe disc (FUNDATIA, MASTER), scrie xlsx-urile
de initializare intr-un director temporar (nu peste cele din radacina), le
anonimizeaza cu ACELEASI reguli si ACEIASI tabela ca tests/anonimizeaza_etalon.py si
le compara cu tests/golden/ pe VALORI DE CELULA.
Comparatia nu se face niciodata pe hash de fisier: un xlsx rescris de alta versiune
de openpyxl are alti octeti cu acelasi continut.
Rulare: py -m unittest discover -s tests -v
"""
import os
import pathlib
import sys
import tempfile
import unittest
import openpyxl
RADACINA = pathlib.Path(__file__).resolve().parent.parent
TESTS = pathlib.Path(__file__).resolve().parent
for cale in (str(RADACINA), str(TESTS)):
if cale not in sys.path:
sys.path.insert(0, cale)
import genereaza_xlsx as generator
import anonimizeaza_etalon as anonim
CORESP = TESTS / "_corespondenta_anonimizare.json"
GOLDEN = TESTS / "golden"
FIRME = [
("FUNDATIA", "balanta_FUNDATIA.csv", "golden_FUNDATIA_2025_12.xlsx"),
("MASTER", "balanta_MASTERJOB.csv", "golden_MASTER_2025_12.xlsx"),
]
MAX_DIFERENTE_RAPORTATE = 20
class TestRegresieEtalon(unittest.TestCase):
@classmethod
def setUpClass(cls):
if not CORESP.exists():
raise unittest.SkipTest(
"Lipseste %s (e ignorata de git). Etalonul nu se poate reproduce "
"fara tabela de corespondenta - vezi docs/handoff_etapa2_convertor.md, "
"sectiunea despre etalon." % CORESP
)
cls.coresp = anonim.citeste_corespondenta(CORESP)
cls._cwd = os.getcwd()
os.chdir(str(RADACINA)) # TEMPLATE si CSV-urile sunt relative la radacina
cls._tmp = tempfile.TemporaryDirectory(prefix="regresie_")
tmpdir = pathlib.Path(cls._tmp.name)
cls.iesiri = {}
try:
header = generator.citeste_header()
for firma, csv_nume, golden_nume in FIRME:
rows = generator.citeste_balanta(str(RADACINA / csv_nume))
out, _, _, _, _ = generator.genereaza(rows, header)
brut = tmpdir / ("init_%s_2025_12.xlsx" % firma)
generator.scrie_xlsx(header, out, str(brut))
anonimizat = tmpdir / ("anon_%s.xlsx" % firma)
anonim.anonimizeaza_fisier(str(brut), str(anonimizat), cls.coresp)
cls.iesiri[firma] = (anonimizat, GOLDEN / golden_nume)
except Exception:
os.chdir(cls._cwd)
cls._tmp.cleanup()
raise
@classmethod
def tearDownClass(cls):
os.chdir(cls._cwd)
cls._tmp.cleanup()
def _verifica(self, firma):
anonimizat, golden = self.iesiri[firma]
self.assertTrue(golden.exists(), "lipseste etalonul %s" % golden)
wb_gasit = openpyxl.load_workbook(anonimizat)
wb_asteptat = openpyxl.load_workbook(golden)
ws_gasit, ws_asteptat = wb_gasit.active, wb_asteptat.active
nr_g = ws_gasit.max_row
nc_g = ws_gasit.max_column
nr_a = ws_asteptat.max_row
nc_a = ws_asteptat.max_column
self.assertEqual(
(nr_g, nc_g), (nr_a, nc_a),
"Dimensiuni diferite pentru %s: asteptat %d randuri x %d coloane, "
"gasit %d randuri x %d coloane" % (golden.name, nr_a, nc_a, nr_g, nc_g),
)
diferente = []
for rand in range(1, max(nr_g, nr_a) + 1):
for col in range(1, max(nc_g, nc_a) + 1):
gasit = ws_gasit.cell(rand, col).value
asteptat = ws_asteptat.cell(rand, col).value
if gasit != asteptat:
diferente.append((rand, col, asteptat, gasit))
if diferente:
linii = ["%d celule difera fata de %s:" % (len(diferente), golden.name)]
for rand, col, asteptat, gasit in diferente[:MAX_DIFERENTE_RAPORTATE]:
linii.append(
" %s: rand %d, coloana %d: asteptat %r, gasit %r"
% (golden.name, rand, col, asteptat, gasit)
)
if len(diferente) > MAX_DIFERENTE_RAPORTATE:
linii.append(" ... si alte %d celule"
% (len(diferente) - MAX_DIFERENTE_RAPORTATE))
self.fail("\n".join(linii))
def test_fundatia(self):
self._verifica("FUNDATIA")
def test_master(self):
self._verifica("MASTER")
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,67 @@
"""Compara setul operational de conturi cu parteneri cu snapshot-ul CONFIG_CONT_IREG.
RAPORTEAZA divergentele, nu le aplica: nu scrie nimic in config/conturi_parteneri.csv
si nu se conecteaza la Oracle. Snapshot-ul vine din sql/export_config_cont_ireg.sql
sau, in lipsa unei rulari pe Oracle, din config/config_cont_ireg_snapshot.csv.
Rulare: py tools/divergenta_parteneri.py config/config_cont_ireg_snapshot.csv
"""
import csv
import os
import sys
RADACINA = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG = os.path.join(RADACINA, "config", "conturi_parteneri.csv")
def citeste_config(path=CONFIG):
"""Conturile cu parteneri tinute de noi (coloana cont din config)."""
if not os.path.exists(path):
raise SystemExit("lipseste configuratorul de parteneri: %s" % path)
conturi = set()
with open(path, encoding="utf-8-sig", newline="") as f:
for r in csv.DictReader(f):
cont = (r.get("cont") or "").strip()
if cont:
conturi.add(cont)
return conturi
def citeste_snapshot(path):
"""Conturile CU_INREGISTRARI = 1 din snapshot; ignora comentariile si antetul."""
if not os.path.exists(path):
raise SystemExit("lipseste snapshot-ul: %s" % path)
conturi = set()
primul = True
with open(path, encoding="utf-8-sig") as f:
for linie in f:
linie = linie.strip()
if not linie or linie.startswith("#"):
continue
valoare = linie.split(",")[0].strip().strip('"').strip()
if primul and valoare.lower() == "cont":
primul = False
continue
primul = False
if valoare:
conturi.add(valoare)
return conturi
def main(argv):
if len(argv) != 2:
raise SystemExit("Utilizare: py tools/divergenta_parteneri.py <snapshot.csv>")
noi = citeste_config()
oracle = citeste_snapshot(argv[1])
doar_oracle = sorted(oracle - noi)
doar_noi = sorted(noi - oracle)
print("Conturi comparate: la noi %d, in Oracle %d" % (len(noi), len(oracle)))
print("In Oracle, nu la noi (%d): %s"
% (len(doar_oracle), ", ".join(doar_oracle) if doar_oracle else "(niciunul)"))
print("La noi, nu in Oracle (%d): %s"
% (len(doar_noi), ", ".join(doar_noi) if doar_noi else "(niciunul)"))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))