Tool-ul ANAF, versiunea 2 — de la „funcționează” la „rezistă în timp”
Primul nostru executabil verifica mii de CIF-uri la ANAF în câteva secunde și mergea impecabil. Apoi am făcut un experiment simplu: i-am dat codul înapoi aceluiași model AI și l-am rugat să-l critice ca un developer senior. Răspunsul ne-a învățat ceva ce nu știam să cerem la început — diferența dintre un tool care merge azi și unul construit ca să nu se strice mâine.
// Cuprins
Punctul de plecare — un tool care funcționează
Acum 2 luni am scris povestea primului nostru tool construit prin vibe coding: un verificator de CIF-uri la ANAF. Îi dai un fișier Excel cu coduri fiscale, el interoghează serviciul web ANAF și îți întoarce, pentru fiecare firmă, statusul de plătitor TVA, TVA la încasare, starea de activ/inactiv, RO e-Factura, adresa, codul CAEN și restul câmpurilor. Două fișiere Python: unul cu logica de comunicare cu ANAF, unul cu interfața grafică în Tkinter. Împachetat cu PyInstaller într-un singur .exe.
Și funcționa. 8.000 de CIF-uri în aproximativ 80 de secunde, respectând regulile ANAF — maximum 100 de coduri pe cerere, maximum o cerere pe secundă, cu reîncercări automate când serverul dă timeout. Pentru un task pe care înainte îl făceai manual, câte un CIF pe rând pe site-ul ANAF, era diferența dintre o după-amiază pierdută și o pauză de cafea.
Aici se termină de obicei povestea de succes. Tool-ul merge, problema e rezolvată, treci mai departe. Dar pe măsură ce l-am folosit zi de zi, au început să apară întrebări incomode. Ce se întâmplă dacă ANAF schimbă adresa serviciului? De ce trebuie să recompilez tot executabilul ca să schimb o singură setare? De ce colegul primește o avertizare roșie de securitate când deschide fișierul? Niciuna dintre aceste întrebări nu era despre „merge sau nu merge”. Toate erau despre altceva — un strat pe care, la prima versiune, nici nu știam că trebuie să-l cer.
AI-ul ca cel mai răbdător critic
Am făcut un experiment care a costat zero lei și cinci minute. Am luat cele două fișiere Python — exact codul pe care AI-ul îl scrisese pentru mine — și i le-am dat înapoi, cu o singură instrucțiune: „Ești un developer senior care face code review. Spune-mi tot ce e impractic, fragil sau greu de întreținut în acest cod. Nu mă menaja.”
Răspunsul a fost mai util decât mi-aș fi dorit. Pentru că, vezi tu, când ceri AI-ului să construiască ceva, primești soluția cea mai directă care rezolvă problema descrisă. Când îi ceri să critice, primești toate lucrurile pe care nu ai știut să le ceri. E aceeași asimetrie pe care o cunoaște orice contabil: e mai ușor să întocmești o balanță decât să revizuiești una făcută de altcineva și să-i găsești fisurile. Revizia cere o altă pălărie.
Și exact asta e ideea care merită reținută din tot articolul: modelul AI care îți scrie codul poate fi, în aceeași conversație, cel mai bun critic constructiv al lui. Nu obosește, nu se supără că îi pui la îndoială munca, nu are ego de protejat. Îi pui codul în față, îl întrebi „ce ai face altfel” și primești un audit pe care un consultant l-ar factura cu câteva sute de euro.
Lecția #1: „hardcoded” — bomba cu ceas
Cel mai important cusur — și cel pe care îl bănuiam deja — se numește, în jargon, hardcoding. Înseamnă că valori care s-ar putea schimba în viitor sunt scrise direct în corpul programului, fixe, ca și cum n-ar urma să se schimbe niciodată. În codul nostru, prima linie relevantă arăta așa:
API_URL = "https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva"
MAX_CUIS_PER_REQUEST = 100
MIN_SECONDS_BETWEEN_REQUESTS = 1.0
MAX_ATTEMPTS = 3
Uită-te o secundă la acel v9 din adresă. Acel „9” înseamnă că serviciul ANAF a fost rescris de cel puțin nouă ori. Cu alte cuvinte, avem dovada scrisă, chiar în cod, că adresa se schimbă periodic. În ziua în care ANAF publică un v10 și retrage v9, executabilul nostru nu dă o eroare elegantă — pur și simplu nu mai întoarce niciun rezultat, iar utilizatorul nu are nici cea mai vagă idee de ce. Tool-ul devine, peste noapte, un fișier inutil.
Și nu e doar adresa. Maparea câmpurilor din răspunsul ANAF — denumire, cod CAEN, status TVA, adresă sediu și încă vreo cincizeci de câmpuri — e și ea scrisă manual, linie cu linie, în cod. Dacă ANAF adaugă un câmp nou (cum a făcut, de exemplu, când a introdus statusul RO e-Factura), tool-ul îl ignoră complet, pentru că nimeni nu i-a spus să-l caute. Datele vin de la ANAF, dar se pierd pe drum.
Lecția tehnică, tradusă pentru un contabil: nu amesteca datele care se schimbă cu logica ce nu se schimbă. E exact principiul pentru care nu scrii cota de TVA direct în formula din fiecare celulă, ci o pui într-o celulă separată la care toate formulele se uită. Când cota se schimbă, modifici un singur loc. La fel ar trebui să funcționeze și un tool: adresa ANAF, limitele, maparea câmpurilor — toate ar trebui să stea într-un fișier de configurare separat, ușor de actualizat, nu îngropate în cod și sigilate într-un executabil.
Lecția #2: interfața nu înseamnă doar „să meargă”
A doua observație a privit interfața. Tkinter — biblioteca grafică folosită la v1 — ne-a dat exact minimul: o fereastră fixă, două butoane, o bară de progres și o zonă de text în care se afișau primele zece rânduri ca text simplu. Funcțional, dar rigid. Nu poți sorta rezultatele, nu poți filtra, nu poți alege ce coloane vrei la export, nu poți selecta data la care interoghezi ANAF, nu poți decide dacă vrei rezultatul în Excel sau în CSV. Tot ce poți face e ce a prevăzut programul, exact așa cum a prevăzut-o.
Mai era și o problemă de transparență a erorilor. Ca să nu sperie utilizatorul cu mesaje tehnice, v1 prindea orice eroare și afișa un politicos „A apărut o eroare neașteptată”. Bine intenționat, dar inutil: când ceva chiar se strica, nici utilizatorul, nici noi nu aveam idee ce anume. Nu exista niciun jurnal, niciun fișier de log pe care să-l deschizi și să vezi ce s-a întâmplat. În contabilitate i-am spune pistă de audit. Aici, pista lipsea cu desăvârșire.
O interfață bună nu e cea care arată bine. E cea care nu te leagă de mâini când vrei să faci ceva ce autorul n-a anticipat — și care îți spune adevărul când ceva nu merge.
Lecția #3: executabilul nesemnat și problema încrederii
Al treilea cusur nu e despre cod, ci despre distribuție — și e cel care a creat cele mai multe fricțiuni în practică. Când împachetezi un program Python într-un .exe și îl trimiți unui coleg, Windows îl întâmpină cu un ecran albastru de avertizare: „Windows protected your PC”. Motivul e că fișierul nu e semnat digital.
O semnătură digitală de cod e, în esență, un certificat care confirmă cine a creat aplicația și garantează că fișierul n-a fost modificat după aceea. Funcționează ca ștampila și semnătura de pe un document oficial: nu garantează că documentul e corect, dar garantează cine și-l asumă. Fără ea, sistemul de reputație al Windows (SmartScreen) tratează fișierul ca pe un necunoscut și pune utilizatorul în gardă — vizual și în scris.
Pentru un tool intern, asta înseamnă că fiecare coleg trebuie să apese „Run anyway” și să ignore o avertizare care, pe bună dreptate, îl face nervos. Iar instinctul corect al unui om care lucrează cu date sensibile e tocmai să nu deschidă fișiere care declanșează astfel de alarme. Certificatele de semnare a codului există, dar costă bani și presupun un proces de verificare — o decizie pe care, pentru un tool de weekend, trebuie să o cântărești conștient. Important e că, la v1, nici nu știam că această problemă există până când nu am lovit-o.
.exe nesemnat nu e un capriciu — e exact reflexul de prudență pe care îl vrem cu toții când lucrăm cu date de clienți. Soluția nu e să-i înveți pe colegi să ignore alarma, ci să alegi un mod de distribuție în care alarma nici nu apare.
De la cusururi la cerințe — maturizarea tehnică
Iată momentul în care experimentul devine interesant. Niciuna dintre cele trei lecții nu mi-ar fi trecut prin cap înainte să construiesc v1. Nu știam să cer „configurare externă” pentru că nu trăisem încă spaima că ANAF schimbă adresa. Nu știam să cer „tabel interactiv” pentru că nu mă lovisem încă de un preview de text pe care nu-l puteam sorta. Nu știam de semnarea codului până când un coleg nu m-a sunat speriat de ecranul albastru.
Asta e maturizarea tehnică a non-programatorului, și se întâmplă în ordinea inversă față de cum predau manualele. Nu înveți întâi teoria și apoi construiești. Construiești ceva care merge, te lovești de limitele lui în uz real, și abia atunci înțelegi de ce există conceptele pe care developerii le iau de la sine: separarea configurației de cod, mentenabilitatea, degradarea elegantă, pista de audit, lanțul de încredere. Fiecare a încetat să fie jargon abstract și a devenit răspunsul la o durere concretă pe care o simțisem deja.
Vibe coding-ul accelerează această maturizare tocmai pentru că reduce costul fiecărei iterații aproape la zero. Când o versiune nouă te costă o conversație, nu o săptămână de muncă, îți permiți să greșești, să descoperi ce-ți lipsește și să ceri mai bine data viitoare. AI-ul scrie codul; experiența îți scrie ție specificațiile.
| Ce am pățit la v1 | Principiul tehnic învățat |
|---|---|
Adresa ANAF e fixă în cod (v9) |
Separă configurația de logică — datele schimbătoare în fișier extern |
| Câmpuri ANAF noi sunt ignorate | Mentenabilitate — codul trebuie să tolereze schimbări fără rescriere |
| Preview rigid, fără sortare/filtrare | UX-ul nu e cosmetică — e libertatea utilizatorului de a-și controla datele |
| „A apărut o eroare neașteptată” | Pistă de audit — un jurnal care îți spune ce și unde s-a stricat |
| Avertizare Windows la fiecare deschidere | Lanț de încredere — modul de livrare contează la fel de mult ca tool-ul |
Versiunea 2 — același Tkinter, alt mod de a gândi
Cu lista de cerințe scrisă de propria experiență, ne-am întors la AI cu o comandă mult mai matură decât prima dată. Nu „fă-mi un tool care verifică CIF-uri”, ci: separă configurarea de cod, semnalează-mi ce nu recunoști, ține un jurnal, lasă-mă să aleg ce exportă. Am rămas, deocamdată, la Tkinter și la formula executabilului — tocmai ca să fie clar că saltul nu vine din schimbarea tehnologiei, ci din schimbarea cerințelor. Același instrument, rescris aproape complet, pe alte principii.
Configurarea iese din cod: config.json
Schimbarea cea mai importantă. Adresa serviciului ANAF, limitele de viteză, timeout-urile și numărul de reîncercări nu mai stau în cod, ci într-un fișier config.json pe care aplicația îl creează automat, lângă executabil, la prima rulare. Când ANAF trece la v10, deschizi fișierul, schimbi o linie, salvezi. Fără recompilare, fără redistribuit un executabil nou către toată lumea.
// config.json — editabil fără să atingi codul
{
"api_url": "https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva",
"max_cuiuri_per_request": 100,
"secunde_intre_requesturi": 1.0,
"timeout_conectare": 10,
"timeout_citire": 30,
"incercari_maxime": 3
}
Și nici nu trebuie să umbli în fișier dacă nu vrei: aplicația are un buton „Configurare” care editează aceleași setări dintr-un dialog, plus un buton „Test conexiune” care îți spune pe loc dacă serviciul ANAF răspunde la adresa configurată. Exact celula cu cota de TVA din analogia de mai devreme — un singur loc de adevăr, la vedere.
Câmpurile ANAF, într-un al doilea fișier editabil: fields.json
Maparea celor peste cincizeci de câmpuri din răspunsul ANAF — care la v1 era scrisă manual, linie cu linie, în cod — stă acum într-un fișier fields.json, organizat pe grupuri logice (date generale, TVA, TVA la încasare, inactiv/reactiv, adrese), cu denumiri prietenoase pentru coloanele din export și cu presetări rapide („Esențial”, „TVA”).
Iar aici v2 face ceva ce v1 nici nu putea concepe: răspunsul ANAF e citit dinamic, nu după o listă fixă. Dacă ANAF adaugă mâine un câmp nou — cum a făcut cu statusul RO e-Factura — aplicația îl observă și îți afișează o avertizare cu exemplele de câmpuri necunoscute. Le adaugi în fields.json cu un editor de text și apar la următorul export. Datele nu se mai pierd pe drum; cel mult așteaptă să le dai un nume. Există și suport pentru alias: dacă ANAF redenumește un câmp, păstrezi denumirea veche în export fără să atingi codul.
Pista de audit: anaf_tool.log
„A apărut o eroare neașteptată” a rămas la v1. Versiunea 2 scrie un jurnal local, anaf_tool.log, în care se văd loturile procesate, reîncercările și erorile, cu tipul lor exact. Când ceva nu merge, nu mai ghicești — deschizi jurnalul și citești. Pista de audit care lipsea cu desăvârșire există acum, fișier lângă aplicație.
Interfața nu te mai leagă de mâini
Tot Tkinter, dar regândit în jurul utilizatorului. Poți verifica un singur CIF, o listă lipită direct în aplicație (separată prin virgulă, spațiu sau linie nouă) sau un fișier — Excel și CSV de data asta, cu alegerea coloanei care conține CIF-urile, nu obligatoriu prima. Poți selecta data interogării — utilă când vrei statusul firmei la data facturii, nu la data de azi. Alegi ce câmpuri intră în export, iar rezultatul iese în Excel formatat (antet colorat, filtre, primul rând înghețat) sau CSV, la alegere. Preview-ul e un tabel adevărat, cu o sută de rânduri și un sumar: câte verificate, câte găsite, câte cu probleme. În plus, CIF-urile sunt curățate automat din formatele uzuale (RO14399840, 14399840.0) și trecute printr-o validare informativă a cifrei de control.
Instrucțiuni complete, livrate cu tool-ul
Poate cel mai puțin spectaculos upgrade, dar cel care schimbă cel mai mult viața celui care primește tool-ul: v2 vine cu un ghid de utilizare complet — mod de interogare, formatul fișierelor acceptate, cum funcționează presetările de câmpuri, ce înseamnă fiecare status, limitele serviciului ANAF, ce fișiere creează aplicația la rulare. La v1, „documentația” era un mesaj pe chat către coleg. Un tool pe care îl poate folosi cineva fără să te sune nu e un moft — e diferența dintre o sculă personală și o resursă de echipă.
v1 față de v2, pe scurt
Pus cap la cap, saltul nu e despre „mai multe funcții”. E despre rezistența în timp — capacitatea tool-ului de a supraviețui unei schimbări la ANAF, de a fi întreținut fără durere și de a fi folosit de un coleg fără să te sune.
| Criteriu | v1 (Tkinter + .exe) | v2 (Tkinter + fișiere externe) |
|---|---|---|
| Parametri ANAF | Hardcoded în cod | config.json extern + dialog în aplicație |
| ANAF schimbă adresa | Tool inutilizabil, recompilare | Editezi o linie în config, test conexiune pe loc |
| Câmpuri noi de la ANAF | Ignorate, în tăcere | Detectate și semnalate; le adaugi în fields.json |
| Input | Doar Excel, obligatoriu prima coloană | CIF unic, listă lipită, Excel sau CSV, coloana la alegere |
| Data interogării | Doar ziua curentă | Orice dată — ex. data facturii |
| Rezultate | Preview text, 10 rânduri | Tabel real, 100 rânduri + sumar găsite/probleme |
| Export | Doar Excel, coloane fixe | Excel formatat sau CSV, câmpuri la alegere, presetări |
| Erori | Mesaj generic, fără log | Jurnal anaf_tool.log, mesaje cu cauza exactă |
| Documentație | Inexistentă | Ghid complet de utilizare, livrat cu tool-ul |
| Transparența codului | Doar executabilul | Sursele Python incluse, pentru audit sau modificare |
O nuanță onestă — ce nu rezolvă v2
Ca să rămânem riguroși: din cele trei lecții, v2 le închide pe primele două. A treia — executabilul nesemnat — rămâne deschisă. v2 e tot un .exe împachetat cu PyInstaller, deci ecranul albastru de la SmartScreen apare în continuare la prima deschidere. Nu am ascuns problema; am ales să o tratăm prin transparență, nu prin certificat.
Concret: arhiva pe care o distribuim conține, pe lângă executabil, și folderul cu cele două fișiere Python sursă. Oricine — în primul rând departamentul IT al firmei — poate citi codul rând cu rând înainte de a aproba utilizarea: ce adrese accesează, ce fișiere scrie, ce nu face. Iar utilizatorii mai tehnici pot merge mai departe: modifică codul după propriile nevoi și îl împachetează singuri, prin Python și PyInstaller, într-un executabil nou, construit pe mașina lor, din surse pe care le-au citit. Nu e semnătură digitală, dar e ceva ce o semnătură nu oferă: posibilitatea de a verifica ce face programul, nu doar cine îl semnează.
Și mai e ceva: configurarea externă mută o parte din responsabilitate spre cel care editează fișierul. Cu putere de configurare vine și posibilitatea de a strica setarea greșind o linie — de aceea aplicația recreează fișierele cu valori implicite dacă lipsesc și îți spune în jurnal când un fișier e invalid. Maturizarea tehnică nu înseamnă să elimini riscul, ci să-l muți într-un loc unde îl poți vedea și controla — exact ca în contabilitate, unde nu elimini judecata profesională, ci o documentezi.
Cât despre soluția care face avertizarea Windows să dispară cu totul — aplicația care rulează în browser, fără executabil, fără instalare — ea există și e pasul firesc al seriei. Îi dedicăm integral articolul următor: același tool, rescris pentru web.
Închiderea exercițiului
Primul tool ne-a rezolvat o problemă. Al doilea ne-a învățat o disciplină. Între cele două versiuni n-am devenit programatori — și nici nu e nevoie. Am devenit ceva mai util pentru un contabil: oameni care știu să ceară mai bine. Care, înainte să apese „construiește”, se întreabă ce se schimbă în timp, ce vrea utilizatorul de fapt, și cum ajunge tool-ul în mâinile altora fără să sperie pe nimeni.
Iar instrumentul care ne-a dus acolo a fost același care scrisese codul. I l-am pus înapoi în față și l-am rugat să fie dur. A fost. Asta e poate cea mai subevaluată superputere a vibe coding-ului: modelul AI nu e doar mâna care construiește, ci și ochiul care revizuiește. Cel mai bun critic constructiv pe care îl ai e exact cel care ți-a scris prima versiune — trebuie doar să-i ceri să-și schimbe pălăria.
Un tool care „merge” rezolvă problema de azi. Un tool care „rezistă” o rezolvă și pe cea de peste șase luni, când ANAF schimbă o adresă și tu nici nu observi. Diferența dintre cele două nu se învață din manual — se învață construind primul și ascultând ce-ți spune al doilea.
Descarcă tool-ul
Versiunea 2 înlocuiește prima variantă în secțiunea de resurse — face tot ce făcea v1, plus tot ce ai citit mai sus. Arhiva conține executabilul Verificare_CIF_ANAF.exe, fișierele editabile config.json și fields.json, ghidul complet de utilizare, documentația tehnică a serviciului web ANAF și folderul cu cele două fișiere Python sursă — pentru auditul departamentului IT înainte de utilizare sau pentru cei care vor să adapteze codul și să-și construiască propriul executabil.
Descarcă ANAF CIF Checker v2
Verificare bulk și single CIF direct din API-ul public ANAF, cu configurare externă, câmpuri la alegere și jurnal de rulare. Rulează din dublu-click pe orice PC cu Windows, fără instalare, fără cont. Arhiva include executabilul, fișierele editabile config.json și fields.json, ghidul de utilizare și sursele Python.