Verificare ANAF la alegerea partenerului: 404, disponibilitate serviciu (docs)

Design doc REVIZIA 5: contractul 404 (verdict "inexistent" doar cand raspunsul
contine chiar codul cerut), starile serviciului pe sesiune si banda cand ANAF nu
raspunde, cache doar pe verdicte pozitive, codul nenumeric.

Changelog: intrarea 2.11.65 descrie comportamentul asa cum ajunge la utilizator -
functionalitatea nu a fost inca livrata, deci corectiile din interiorul ei nu se
pomenesc separat.

TODOS: contractul 404 pe drumul batch (P4) si verificarea dupa tara partenerului
(P5), amanate explicit.

Codul: COMUN 95cf54e.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhJwsrFFe1ZNAAtS8Twuti
This commit is contained in:
2026-07-27 16:25:18 +03:00
parent 96577f0005
commit 5bc2cb82f1
3 changed files with 195 additions and 8 deletions

View File

@@ -247,13 +247,13 @@ catre Marius dupa aprobarea diff-ului. Nu necesita migrare Oracle (fara DDL).
| Cale | Ce poate merge prost | Actiune | Utilizatorul vede |
|---|---|---|---|
| Apel ANAF (wrapper) | timeout / DNS / HTTP 5xx / 429 | degradare tacuta + UN log pe sesiune (nu per apel) | nimic (flux ca azi) |
| Raspuns ANAF | JSON malformat / CUI negasit / refuz | tratat ca "fara informatie" degradare tacuta | nimic |
| Apel ANAF (wrapper) | timeout / DNS / HTTP 5xx / 429 | degradare tacuta + UN log pe sesiune (nu per apel) | ~~nimic (flux ca azi)~~ REVIZUIT REVIZIA 5: banda gri "ANAF nu a raspuns - verificare sarita" in toate cazurile (timeout/DNS/conexiune refuzata SI HTTP 5xx/429); doar timeout/DNS/conexiune refuzata (`FARA_RASPUNS`) deschide intrerupatorul, vezi REVIZIA 5 pt. 5 |
| Raspuns ANAF | JSON malformat / ~~CUI negasit~~ / refuz | tratat ca "fara informatie" degradare tacuta | nimic REVIZUIT REVIZIA 5: 404 cu `notFound` continand chiar CUI-ul cerut nu mai e "fara informatie", da verdict rosu "Cod fiscal inexistent la ANAF" (vezi REVIZIA 5 pt. 4) |
| Query pereche Oracle | eroare goExecutor | degradare tacuta + log | nimic |
| MAX(data doc) per id_part | lent / eroare | fereastra se deschide FARA coloana "ultim doc" | fereastra fara acea coloana |
| Swap (via nom_parteneri_modifica) | eroare Oracle / lock / drept lipsa | mesaj de EROARE vizibil; partenerul ramane neschimbat | AMESSAGEBOX cu cauza |
| Anulare fereastra (ESC/inchidere) | | selectia ORIGINALA ramane valabila | revine in formular cu partenerul ales initial |
| Dublu-click / reintrare | apel dublu in curs | guard de reintrare (flag "verificare in curs") | un singur apel, o singura fereastra |
| Dublu-click / reintrare | apel dublu in curs | guard de reintrare (flag "verificare in curs") implementat REVIZIA 5 (`lVerificareInCurs`, vezi REVIZIA 5 pt. 8), nu doar cerut | un singur apel, o singura fereastra |
### Ordinea operatiilor (performanta)
1. Alege partener 2. cache lookup CUI 3. apel ANAF (doar daca nu e in
@@ -278,7 +278,10 @@ cache) → 4. concordant ⇒ STOP (zero query suplimentar pe selectiile normale)
3. Discordant cu pereche INACTIVA fereastra o arata; swap reactiveaza/inactiveaza
+ refresh cursor; fara drept de nomenclator buton dezactivat, alegerea merge.
4. Discordant FARA pereche propunere "Nou" cu CUI precompletat.
5. Fara internet / ANAF cazut zero mesaje, flux identic cu azi, un log.
5. ~~Fara internet / ANAF cazutzero mesaje, flux identic cu azi, un log.~~
REVIZUIT REVIZIA 5: Fara internet / ANAF cazut banda gri "ANAF nu a
raspuns - verificare sarita" (nu zero mesaje), niciun dialog modal, flux
fara blocare; log pe tranzitie de stare, nu per apel (vezi REVIZIA 5 pt. 1-2).
6. Persoana fizica (CNP) / partener extern (COD_TARA<>RO) / CUI gol → skip total.
7. Import lot eFactura cu discordante → confirmarile apar O DATA la final,
"aplica la toate" functioneaza, niciun partener creat fara confirmare.
@@ -414,8 +417,10 @@ verdictul pentru RANDUL CURENT din grid, vizibil INAINTE de alegere:
```
Starile labelului (texte finale, spec Marius 25.07):
- gol — persoana fizica / partener extern / CUI nenumeric / kill-switch 0 /
eroare-timeout ANAF (degradare tacuta);
- gol — persoana fizica / partener extern / CUI nenumeric / kill-switch 0;
- ~~eroare-timeout ANAF (degradare tacuta)~~ — REVIZUIT REVIZIA 5: la lipsa
raspunsului HTTP labelul nu mai ramane gol, arata gri "ANAF nu a raspuns -
verificare sarita" (vezi REVIZIA 5 pt. 1);
- "Verificare ANAF..." — pe durata apelului (persistent, nu WAIT WINDOW);
- concordant (VERDE): "ANAF: Platitor TVA (RO1879855 FAGA SRL)" — cod fiscal +
denumirea ANAF;
@@ -786,8 +791,151 @@ Cerinte tehnice de respectat atunci (toate verificate acum, ca sa nu se piarda):
| H4 | `Collection.GetKey` e supraincarcat: cu argument numeric intoarce cheia de la index, nu indexul. Cheile se construiesc string: `'P' + Transform(id_part)`, si `Item()` doar dupa `GetKey(...) <> 0` |
| H5 | Expresia `Dynamic*` se evalueaza in context de pictare: referinta necalificata (`ANAF_CuloareRand(id_part)`), corp intreg in `TRY/CATCH` cu `Return Rgb(0,0,0)`, `Nvl(id_part,0)` (randul `<TOATE INREGISTRARILE>` de pe ramura `lAllInList` are `id_part` blank), zero apeluri care schimba zona de lucru. O eroare aici se repeta la fiecare repaint |
| H8 | Curatarea binding-ului pe metoda: `Unbindevents(form, 'actualizeaza_grid1', This, 'DupaGrid')` cu 4 argumente — varianta cu 1 argument sterge si `BINDEVENT(Thisform,"lAles",...)` din `Init` si rupe selectia multipla |
| H9 | Codul ROA impune >= 1s intre interogarile ANAF (`validare.prg:1777`, `INKEY(1)`); un debounce de 400ms il incalca. Necesare: interval minim global pe sesiune + circuit-breaker la 3 esecuri + log per cod HTTP distinct (azi `gvANAF_EroareLogataSesiune` amuteste toata sesiunea dupa prima eroare, iar 429/5xx sunt indistinguibile de "fara informatie") |
| H9 | Codul ROA impune >= 1s intre interogarile ANAF (`validare.prg:1777`, `INKEY(1)`); un debounce de 400ms il incalca. Necesare: interval minim global pe sesiune + circuit-breaker la 3 esecuri + log per cod HTTP distinct (azi `gvANAF_EroareLogataSesiune` amuteste toata sesiunea dupa prima eroare, iar 429/5xx sunt indistinguibile de "fara informatie"). Nota REVIZIA 5: intrerupatorul livrat acolo e pe timp (10 minute dupa un singur esec FARA_RASPUNS), mecanism diferit — contorul de 3 esecuri si logul per cod HTTP raman cerinta deschisa, neacoperita, pentru nivelul 2 |
| M1 | Delegatul `DupaGrid` trebuie sa aiba semnatura metodei delegate (`Lparameters pcFiltru`), altfel eroare 1229 |
| M7 | Codul fiscal invalid nu are stare in schema de culori — se decide explicit sau se documenteaza omisiunea |
| Test | Partile netestabile in harnessul actual: ordinea actiunilor sub timer, reentranta pe cursorul gridului, culorile efectiv randate. Daca intra, intra cu risc de regresie permanent |
| Invariant | Intreg mecanismul depinde de `PRIVATE poVerifAlegere`/`poANAFInlocuire` vizibile prin domeniu dinamic, ceea ce functioneaza doar pentru ca formularul de cautare e `WindowType = 1` (modal). Orice trecere la modeless rupe atribuirea **fara eroare** |
---
## REVIZIA 5 (27.07.2026) — reparatie 404 + intrerupator de disponibilitate ANAF
Status: APROBATA (27.07.2026, plan `docs\plan-reparatie-anaf-404-breaker.md`, T1-T8).
Aceasta revizie nu schimba fereastra sau textele labelului (raman cele din REVIZIA 3,
cu corectia B2 din REVIZIA 4); corecteaza contractul dintre wrapper-ul ANAF
(`COMUN\programe\validare.prg`) si consumatorul lui (`COMUN\programe\ocautare.prg`)
si adauga o a treia stare de banda pentru cand serviciul ANAF nu raspunde.
### Motivatia
Doua simptome constatate pe ecran: (1) `RO123456789` — cod valid ca cifra de
control dar inexistent la ANAF, banda arata promptul gri generic in loc de
"inexistent la ANAF"; (2) `FANALEX OIL MARKET S.R.L.` scris in campul cod fiscal —
banda tace complet. Plus un al treilea defect gasit la analiza: serviciul cazut sau
supraincarcat poate bloca interfata pana la cateva zeci de secunde per rand, pentru
ca garda existenta din jurul `SetTimeouts` nu se aplica pe obiectul COM folosit.
### 1. Regula "esec tacit" revizuita pentru banda
Regula veche (REVIZIA 2, "Starile labelului"): orice eroare/timeout ANAF lasa
labelul gol, identic cu starea "nimic de verificat". Regula noua: "esec tacit"
ramane valabila pentru dialogurile modale (fara AMESSAGEBOX la esec, ca azi), dar
banda de stare VORBESTE:
- lipsa de raspuns HTTP (`FARA_RASPUNS`), raspuns HTTP primit dar neinteles
(`RASPUNS_NEINTELES`, punctul 5) sau apel sarit fiindca serviciul e deja in
starea CAZUT/SE TESTEAZA (punctul 3) → acelasi text de banda gri, in toate
trei situatiile: **"ANAF nu a raspuns - verificare sarita"**, distincta de
promptul gri generic (persoana fizica / partener extern / CUI nenumeric /
kill-switch 0);
- ce difera intre `FARA_RASPUNS` si `RASPUNS_NEINTELES` e DOAR intrerupatorul:
`FARA_RASPUNS` il deschide (stare CAZUT); `RASPUNS_NEINTELES` schimba banda la
fel, dar nu deschide intrerupatorul si nu intra in cache;
- 404 cu `notFound` continand chiar CUI-ul cerut → verdict rosu "Cod fiscal
inexistent la ANAF" (punctul 4), nu mai e tratat ca "fara informatie".
### 2. Testul 5 actualizat
Testul 5 din "Lista de teste" (mai sus) trece de la "zero mesaje" la banda care
vorbeste explicit la lipsa raspunsului; vezi textul revizuit la pozitia lui in
lista. Regresiile de pastrat verzi: celelalte 10 teste ale listei plus cele 30 de
scenarii existente in suita headless (`test_verif_partener_anaf_v2_ui.ps1`).
### 3. Starea serviciului ANAF pe sesiune
Patru stari, tinute pe sesiune (nu per CUI), cu intrerupator de 10 minute:
```
+-------------+
pornire sesiune ---->| NECUNOSCUT |
+------+------+
deschidere formular | (sonda pleaca asincron)
v
+-------------+ sonda / verificare reala OK
| SE TESTEAZA+-------------------------------> +-----+
+------+------+ | VIU |
| sonda esuata +--+--+
v |
+-------------+ <--------------------------------+
| CAZUT (10') | apel real esuat (FARA_RASPUNS)
+------+------+
| au trecut 10 minute + se deschide un formular
+--> SE TESTEAZA (sonda noua, tot asincron)
```
- **NECUNOSCUT / CAZUT expirat** → la deschiderea formularului de cautare pleaca o
sonda asincrona (acelasi constructor de cerere ca verificarea normala), fara sa
blocheze interfata.
- **SE TESTEAZA** → randul care cere verdict NU face apel propriu; asteapta
raspunsul sondei (banda: "ANAF: se verifica <cod>..."), interogat neblocant de
timerul de debounce existent (400ms). Sonda are propriul termen de viata; daca
expira fara raspuns, starea trece direct pe CAZUT.
- **VIU** → verificari sincrone normale, fara sonda.
- **CAZUT** → zero apeluri catre ANAF pentru randul curent, banda gri "ANAF nu a
raspuns - verificare sarita".
- La tranzitia CAZUT/NECUNOSCUT → VIU, `nIdPart` se invalideaza, ca sa se
re-evalueze randul curent (altfel ramane pe verdictul dinaintea revenirii).
- La nivelul 3 (confirmare la alegere, opt-in): in starile CAZUT si SE TESTEAZA
verificarea sincrona de la alegere se sare, iar alegerea trece fara blocare.
### 4. Contractul 404
Corpul raspunsului ANAF se citeste si pe status 404, nu doar pe 200. Verdictul
"Cod fiscal inexistent la ANAF" se da **doar** cand `notFound` din raspuns contine
chiar CUI-ul cerut — niciun alt corp neinteles nu mai produce acest verdict; ramura
veche care trata orice corp diferit de "gasit" ca "inexistent" se elimina.
### 5. Clasa de esec a wrapper-ului
`ANAF_VerificaCuiSingle` primeste un parametru suplimentar, prin referinta, cu
clasa esecului. `ANAF_StarePartener` o preia si o expune mai departe apelantului,
tot prin referinta:
| Valoare | Cand | Banda | Intrerupator |
|---|---|---|---|
| `''` | apel reusit (obiect intors) sau verdict "inexistent" valid | verdict normal (verde/rosu) | nimic |
| `FARA_RASPUNS` | niciun raspuns HTTP: exceptie la deschiderea/trimiterea cererii, timeout, DNS, conexiune refuzata | gri "ANAF nu a raspuns - verificare sarita" | **singura** valoare care il deschide (stare CAZUT) |
| `RASPUNS_NEINTELES` | raspuns HTTP primit dar corpul nu se poate interpreta: status neasteptat, corp gol, JSON neparsabil, `notFound` fara CUI-ul cerut, 429, 500 | acelasi gri "ANAF nu a raspuns - verificare sarita" | nu il deschide, nu intra in cache |
Apelul fara acest parametru ramane valabil (parametru optional) — apelantii
existenti nu se schimba.
### 6. Cache doar pe verdicte pozitive
Verdictul `'NEGASIT'` si esecurile nu se mai tin in cache toata sesiunea. Cache-ul
pe sesiune (cheie CUI+data, vezi REVIZIA 3 pt. 3) ramane doar pentru verdicte
pozitive (platitor/neplatitor gasit la ANAF). Rolul de "nu bate ANAF de doua ori
pentru acelasi caz esuat" il preia intrerupatorul de la punctul 3, nu cache-ul —
altfel un verdict negativ dintr-un serviciu picat sau degenerat ar ramane lipit pe
CUI pana la schimbarea firmei.
### 7. Cod normalizat cu litere in campul cod fiscal
Cand codul din campul cod fiscal contine litere dupa normalizare (cazul
`FANALEX OIL MARKET S.R.L.` scris acolo din greseala) → banda gri "nu se poate
verifica la ANAF", nu tacere ca azi. `VALIDARE_CIF` nu se modifica in aceasta
transa (ramane folosita neschimbata in restul suitei — D406, import, facturare);
tara partenerului nu intra in aceasta transa — punctul D (`cod_tara` in cursorul
de cautare) ramane amanat, ca in "Ce NU facem" mai sus.
### 8. Guard de reintrare implementat
Guard-ul de reintrare (flag "verificare in curs") cerut deja in harta erorilor de
mai sus e implementat in aceasta transa ca `lVerificareInCurs`, nu doar specificat:
fara el, timerele VFP care trag si in timpul unui `AMESSAGEBOX`/`WaitForResponse`
pot intrerupe secventa "verifica cheia → apel → adauga in cache" si arunca eroare
la a doua inserare pe aceeasi cheie.
### 9. Ce se pastreaza neschimbat
Textele si culorile labelului (REVIZIA 3 pt. 1, cu corectia B2), starea la data
documentului (REVIZIA 3 pt. 3), `NormalizeazaCUI`, cache-ul pe sesiune + golirea la
schimbarea firmei (cu restrangerea de la pct. 6), excluderile (PF/extern/
nenumeric — extinse acum cu banda gri de la pct. 7 in loc de tacere),
degradarea tacuta pentru `RASPUNS_NEINTELES` (niciun AMESSAGEBOX, nicio acuzatie,
nicio scriere in cache, intrerupatorul neatins — dar banda vorbeste, ca la pct. 1),
`ANAF_CautaPereche`,
`AplicaInlocuirePartenerANAF`, fixul de import, cascada de optiuni
`RC_ANAF_VERIF_SELECTIE` (REVIZIA 4 pt. 3), politica de activare pe apelanti
(REVIZIA 4 pt. 4).