diff --git a/.gitignore b/.gitignore index a6e9eb4..ff64370 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,6 @@ roacont_ref.FPT # --- COMUN este gestionat separat, prin propriul sau git (romfast/comun.git) --- COMUN/ + +docs/diff_runda*.patch +.gstack/ diff --git a/CLAUDE.md b/CLAUDE.md index 1cbf650..1c419c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,15 +58,12 @@ carry data, not just structure) are committed to git but sit in SVN global-ignor ### Branching (git) -Two lanes in the git mirror: -- **`main` = SVN mirror**: receives ONLY `sync SVN rN` commits, made from a clean tree (right - after `svn update`, before local work). `git diff main..claude/` then shows exactly - and only Claude's work. -- **Claude works on a branch** (`claude/`) and commits only there. -- Once the changes land in SVN (user runs `svn commit`), the branch is closed (merged into - `main` at the next sync, or deleted). -- Hygiene: sync `main` only from a clean tree โ€” an `svn update` mid-work mixes binaries on disk - (nature of SVN). +Two lanes in the git mirror: **`main`** receives ONLY `sync SVN rN` commits, made from a clean +tree (right after `svn update`, before local work); **Claude works on a branch** `claude/` +and commits only there, so `git diff main..claude/` shows exactly and only Claude's work. +The branch closes once the changes land in SVN (user runs `svn commit`). + +Full model and hygiene rules: `COMUN\docs\fluxul_svn_git.md`. ## Building / running diff --git a/Clase/ointroduceri_cont.vc2 b/Clase/ointroduceri_cont.vc2 index b205734..a60ff76 100644 --- a/Clase/ointroduceri_cont.vc2 +++ b/Clase/ointroduceri_cont.vc2 @@ -922,7 +922,9 @@ DEFINE CLASS frm_introd_compact AS formtermin OF "..\comun\clase\baza.vcx" PARAMETERS tcCont, lcCampId, lcCampNume lcCont = ALLTRIM(tcCont) - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Nume partener ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont)) + loHash = GetHash([cTitlu=>Nume partener ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) If !Empty(locauta.nume) lnid_part=locauta.id_part @@ -3849,7 +3851,9 @@ DEFINE CLASS frm_introd_compact2007 AS formtermin OF "..\comun\clase\baza.vcx" PARAMETERS tcCont, lcCampId, lcCampNume lcCont = ALLTRIM(tcCont) - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Nume partener ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont)) + loHash = GetHash([cTitlu=>Nume partener ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) If !Empty(locauta.nume) lnid_part=locauta.id_part @@ -7087,7 +7091,9 @@ DEFINE CLASS frm_note AS formtermin OF "..\comun\clase\baza.vcx" ENDIF - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Alegeti partenerul ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont)) + loHash = GetHash([cTitlu=>Alegeti partenerul ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) SELECT actactan REPLACE id_partd WITH loCauta.id_part, partd WITH locauta.nume @@ -7109,7 +7115,9 @@ DEFINE CLASS frm_note AS formtermin OF "..\comun\clase\baza.vcx" ENDIF - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Alegeti partenerul ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont)) + loHash = GetHash([cTitlu=>Alegeti partenerul ] + lcCont + [??lAdaugCorespondente=>1??cCont=>] + lcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) SELECT actactan REPLACE id_partc WITH loCauta.id_part, partc WITH locauta.nume @@ -8930,7 +8938,9 @@ DEFINE CLASS frm_note2007 AS formtermin OF "..\comun\clase\baza.vcx" If lnsucces=1 If lnrezultat>0 - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Alegeti partenerul ] + pcCont + [??lAdaugCorespondente=>1??cCont=>] + pcCont)) + loHash = GetHash([cTitlu=>Alegeti partenerul ] + pcCont + [??lAdaugCorespondente=>1??cCont=>] + pcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) Select actactan Replace id_partd With loCauta.id_part, partd With loCauta.nume IF lnIdPartd <> loCauta.id_part @@ -8969,7 +8979,9 @@ DEFINE CLASS frm_note2007 AS formtermin OF "..\comun\clase\baza.vcx" If lnsucces=1 If lnrezultat>0 - loCauta = CautPartenerContabilitate(GetHash([cTitlu=>Alegeti partenerul ] + pcCont + [??lAdaugCorespondente=>1??cCont=>] + pcCont)) + loHash = GetHash([cTitlu=>Alegeti partenerul ] + pcCont + [??lAdaugCorespondente=>1??cCont=>] + pcCont + [??lVerificaANAF=>1]) + loHash.SetValue('dDataDoc', Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) + loCauta = CautPartenerContabilitate(loHash) Select actactan Replace id_partc With loCauta.id_part, partc With loCauta.nume IF lnIdPartc <> loCauta.id_part @@ -9662,7 +9674,7 @@ DEFINE CLASS frm_plati_impozite AS _frmbase OF "..\comun\clase\_frm_base.vcx" SELECT c_part IF valoare > 0 lcTitlu = 'Nume partener ' + lcCont - locauta = caut_parteneri(lcCont, lcTitlu) + locauta = caut_parteneri(lcCont, lcTitlu, .F., .F., .F., .T., Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) If !Empty(locauta.denumire) AND !EMPTY(locauta.id_part) lnid_part=locauta.id_part @@ -9701,7 +9713,7 @@ DEFINE CLASS frm_plati_impozite AS _frmbase OF "..\comun\clase\_frm_base.vcx" SELECT c_part IF valoare > 0 lcTitlu = 'Nume partener ' + lcCont - locauta = caut_parteneri(lcCont, lcTitlu) + locauta = caut_parteneri(lcCont, lcTitlu, .F., .F., .F., .T., Iif(Type('poAct.dataact') = 'D', poAct.dataact, {})) If !Empty(locauta.denumire) AND !EMPTY(locauta.id_part) lnid_part=locauta.id_part diff --git a/Meniuri/cont2000.mn2 b/Meniuri/cont2000.mn2 index 1f45c9a..b4152da 100644 --- a/Meniuri/cont2000.mn2 +++ b/Meniuri/cont2000.mn2 @@ -207,6 +207,14 @@ ON PAD _000000003 OF _msysmenu ACTIVATE POPUP Initializa DEFINE BAR 1 OF Optiuniuti PROMPT "\ 1 + lcGrupIdLista = "" + Select cGrupCuiTemp + Scan + lcGrupIdLista = m.lcGrupIdLista + Iif(Empty(m.lcGrupIdLista), "", ",") + Transform(id_part) + Endscan + lcSqlGrup = [select id_part, count(*) as nr, sum(precdeb + debit - preccred - credit) as sold from ireg_parteneri ] + ; + [where an = ?gnAn and luna = ?gnLuna and id_part in (] + m.lcGrupIdLista + [) group by id_part ] + ; + [order by count(*) desc, abs(sum(precdeb + debit - preccred - credit)) desc] + If goExecutor.oExecuta(m.lcSqlGrup, "cGrupCuiDoc") And Reccount("cGrupCuiDoc") > 0 + Select cGrupCuiTemp + Locate For id_part = cGrupCuiDoc.id_part + If Found() + lnIdPartener = cGrupCuiTemp.id_part + lcPartener = Alltrim(Nvl(cGrupCuiTemp.denumire, "")) + Endif + Endif + If Used("cGrupCuiDoc") + Use In (Select("cGrupCuiDoc")) + Endif + Select cGrupCuiTemp + Scan + Insert Into cGrupParteneriCui (id_part_ales, id_part, denumire) Values (m.lnIdPartener, cGrupCuiTemp.id_part, Alltrim(Nvl(cGrupCuiTemp.denumire, ""))) + Endscan + Endif + If Used("cGrupCuiTemp") + Use In (Select("cGrupCuiTemp")) + Endif Endif Endif * Caut partener dupa denumire @@ -699,7 +733,31 @@ Define Class ExtrasBanca As Custom Endif ENDIF * Daca am gasit un document, ies din bucla - EXIT + EXIT + Else + lnIdPartAlternativ = 0 + If Used("cGrupParteneriCui") + Select cGrupParteneriCui + Scan For id_part_ales = m.lnIdPartener And id_part <> m.lnIdPartener + lcPartAlternativ = Alltrim(denumire) + loAct = GetDocumentByContPartenerAct(m.lcContPartener, id_part, m.lnNrAct, .F.) + If !Empty(m.loAct.id_fact) And ((m.lcTip = "D" And m.lnSuma <= - m.loAct.solddeb) Or (m.lcTip = "C" And m.lnSuma <= m.loAct.solddeb)) + lnIdPartAlternativ = id_part + Exit + Endif + Select cGrupParteneriCui + Endscan + Endif + Select cActTemp + If !Empty(m.lnIdPartAlternativ) + Select cActTemp + If m.lcTip = "D" + Replace id_factd With m.loAct.id_fact, pereched With m.loAct.nract, scd With m.loAct.Cont, ascd With m.loAct.acont, id_partd With m.lnIdPartAlternativ, partd With m.lcPartAlternativ + Else + Replace id_factc With m.loAct.id_fact, perechec With m.loAct.nract, scc With m.loAct.Cont, ascc With m.loAct.acont, id_partc With m.lnIdPartAlternativ, partc With m.lcPartAlternativ + Endif + EXIT + Endif ENDIF && !Empty(m.loAct.id_fact) ENDFOR && lnDocument ENDIF && !Empty(m.lnIdPartener) And m.lnDocumente > 0 @@ -728,7 +786,31 @@ Define Class ExtrasBanca As Custom Endif ENDIF * Daca am gasit un document, ies din bucla - EXIT + EXIT + Else + lnIdPartAlternativ = 0 + If Used("cGrupParteneriCui") + Select cGrupParteneriCui + Scan For id_part_ales = m.lnIdPartener And id_part <> m.lnIdPartener + lcPartAlternativ = Alltrim(denumire) + loAct = GetDocumentByContPartenerAct(m.lcContPartener, id_part, m.lcComanda, .T.) + If !Empty(m.loAct.id_fact) And ((m.lcTip = "D" And m.lnSuma <= - m.loAct.solddeb) Or (m.lcTip = "C" And m.lnSuma <= m.loAct.solddeb)) + lnIdPartAlternativ = id_part + Exit + Endif + Select cGrupParteneriCui + Endscan + Endif + Select cActTemp + If !Empty(m.lnIdPartAlternativ) + Select cActTemp + If m.lcTip = "D" + Replace id_factd With m.loAct.id_fact, pereched With m.loAct.nract, scd With m.loAct.Cont, ascd With m.loAct.acont, id_partd With m.lnIdPartAlternativ, partd With m.lcPartAlternativ + Else + Replace id_factc With m.loAct.id_fact, perechec With m.loAct.nract, scc With m.loAct.Cont, ascc With m.loAct.acont, id_partc With m.lnIdPartAlternativ, partc With m.lcPartAlternativ + Endif + EXIT + Endif ENDIF && !Empty(m.loAct.id_fact) ENDFOR && lnDocument ENDIF && !Empty(m.lnIdPartener) And m.lnDocumente > 0 @@ -836,6 +918,10 @@ Define Class ExtrasBanca As Custom Select (m.lcSelect) ENDIF && llSucces + If Used("cGrupParteneriCui") + Use In (Select("cGrupParteneriCui")) + Endif + Return m.llSucces Endproc && CreeazaNote @@ -4423,6 +4509,22 @@ Function GetRegExpAllNumbers Return lnResults Endfunc +* -------------------------------------------------------------------- +* Deschide tcCursor cu id_part, denumire, cod_fiscal pentru toti parteneri cu acelasi CUI normalizat +* -------------------------------------------------------------------- +Function GetParteneriByCuiNormalizat + Lparameters tcCodFiscal, tcCursor + Private pcCui + Local lcSql, llSucces + + pcCui = Alltrim(Strtran(Strtran(Upper(Alltrim(m.tcCodFiscal)), ' ', ''), 'RO', '')) + + lcSql = [select id_part, denumire, cod_fiscal from nom_parteneri where sters = 0 and inactiv = 0 ] + ; + [and replace(replace(upper(cod_fiscal),' ',''),'RO','') = ?pcCui order by id_part] + llSucces = goExecutor.oExecuta(m.lcSql, m.tcCursor) + Return m.llSucces +Endfunc + * -------------------------------------------------------------------- * Creeaza/Actualizeaza Parteneri in ROA pentru fiecare cod_fiscal din tcCursorParteneri * Verific dupa cod_fiscal, apoi dupa denumire @@ -4444,6 +4546,7 @@ Procedure CompleteazaParteneriROA Local lcSqlInsert, lcSqlPart, lcStrada, lcSufix, lcTara, lcTelefon1, lcTelefon2, lcTip_persoana Local lcWeb, lcinactiv, llCallBack, llSucces, lnIdJudet, lnIdJudetBucuresti, lnIdLocalitateBucuresti Local lnIdTaraRO, lnItem, lnItems, lnSucces + Local lcCuiNorm Private pnIdAdresa, pcCodFiscal, pcDenumire, pnIdPart, pnNrAdrese, pnIdJudet llCallBack = Type('toCallBackForm') = 'O' And Pemstatus(m.toCallBackForm, 'trace', 5) @@ -4463,6 +4566,9 @@ Procedure CompleteazaParteneriROA Return m.llSucces Endif + Select id_part, cod_fiscal, denumire, NormalizeazaCUI(cod_fiscal) As cui_norm From cParteneriROA Into Cursor cParteneriROA Readwrite + Index On cui_norm Tag cuinorm + lcAdreseParteneri = [select id_adresa, id_part, localitate, id_loc, judet, id_judet, tara, id_tara from vadrese_parteneri] llSucces = goExecutor.oExecuta(m.lcAdreseParteneri, 'cAdreseROA') @@ -4494,6 +4600,12 @@ Procedure CompleteazaParteneriROA If !Empty(m.pcCodFiscal) Locate For Alltrim(Strtran(cod_fiscal, ' ', '')) = m.pcCodFiscal *!* lnSucces = goExecutor.oSelect2Value(m.lcSqlCod, @pnIdPart) + If !Found() + lcCuiNorm = NormalizeazaCUI(m.pcCodFiscal) + If !Empty(m.lcCuiNorm) + Seek m.lcCuiNorm Order cuinorm + Endif + Endif Else Locate For Alltrim(Upper(denumire)) = m.pcDenumire *!* lnSucces = goExecutor.oSelect2Value(m.lcSqlDenumire, @pnIdPart) diff --git a/Programe/roacont.prg b/Programe/roacont.prg index bd7b82d..522fd67 100644 --- a/Programe/roacont.prg +++ b/Programe/roacont.prg @@ -576,6 +576,10 @@ If Type('laparametri',1)="A" Endif Endif +If Type('goCacheANAF_Sesiune') = 'O' + Release goCacheANAF_Sesiune +Endif + Private gcCopyRight gcCopyRight = 'ฉ ROA Romfast SRL' diff --git a/TODOS.md b/TODOS.md index ff02eec..ae27c11 100644 --- a/TODOS.md +++ b/TODOS.md @@ -36,3 +36,59 @@ Generat de /autoplan (review PRD eFactura OAuth2) pe 2026-07-07. ## ~~Lansare automatฤƒ a fluxului de token nou la eศ™ec definitiv de refresh~~ โ€” PRELUAT Decis la poarta /autoplan (D3, 2026-07-07): utilizatorul o implementeazฤƒ direct รฎn VFP (legarea mesajului de expirare refresh de apelul newToken). Nu mai e item deschis aici. + +## Amanari review /autoplan โ€” verificare ANAF la alegerea partenerului (26.07.2026) + +Sursa: `docs/design-alegere-partener-anaf.md`, REVIZIA 4 (implementata doar nivelul 1). + +## P2 โ€” Guard de duplicat pe CUI normalizat la crearea partenerului +- **Ce:** La crearea unui partener nou, verificare ca nu mai exista alt partener activ + cu acelasi CUI normalizat (`NormalizeazaCUI`); avertizare/blocare inainte de salvare. +- **De ce:** stratul cel mai ieftin de prevenire a duplicatelor โ€” netratat in 4 revizii + ale verificarii ANAF la alegere, care acopera doar alegerea unui partener deja + existent, nu crearea unuia nou. Cel mai bun raport cost/beneficiu dintre amanarile + de mai jos. +- **Pro:** cost mic, taie problema la sursa. **Contra:** cazuri legitime de acoperit + (sedii/puncte de lucru cu acelasi CUI). +- **Efort:** S. **Depinde de:** decizie UX blocare vs. avertizare. + +## P3 โ€” Raport pasiv de discordante CUI + curatare duplicate de parteneri +- **Ce:** Raport cu partenerii care au discordante fata de ANAF (nume/stare TVA) si + perechile de CUI duplicat, pentru curatare manuala periodica. +- **De ce:** REVIZIA 4 logheaza discordantele in `goLog` la alegere, dar fara raport + datele brute nu arata tiparul. +- **Efort:** M. **Depinde de:** volum de date acumulat din logarea introdusa la REVIZIA 4. + +## P3 โ€” Coloana "Ultim document" in cautarea de partener +- **Ce:** Coloana in gridul de cautare partener cu data ultimului document inregistrat + pe acel partener. +- **De ce:** discriminatorul "are documente in perioada" din Detalii (REVIZIA 4) ajuta + doar la alegere; o coloana in grid ar ajuta si la navigare fara deschiderea Detalii + pe fiecare candidat. +- **Efort:** S-M. **Depinde de:** sursa `ireg_parteneri`, deja identificata in REVIZIA 4. + +## P3 โ€” Prevenirea duplicatelor la import +- **Ce:** La import (facturi, extrase, eFactura), verificare CUI normalizat inainte de + crearea automata a unui partener nou. +- **De ce:** importurile creeaza parteneri fara sa treaca prin formularul de creare + manuala, deci guard-ul de mai sus (P2) nu acopera acest flux. +- **Efort:** M. **Depinde de:** guard-ul de duplicat pe CUI normalizat (P2 de mai sus). + +## P3 โ€” Tooltip per rand in gridul de cautare +- **Ce:** Tooltip la hover pe randul din gridul de cautare partener, cu detaliile + ANAF/discordanta randului respectiv. +- **De ce:** alternativa la click/F4 pe Detalii (REVIZIA 4), utila pe fluxurile cu mouse. +- **Efort:** S. + +## P3 โ€” Marcaj colorat pe toate randurile din grid (nivelul 2 al `RC_ANAF_VERIF_SELECTIE`) +- **Ce:** Extinderea verificarii ANAF de la randul curent (nivelul 1, implementat) la + marcaj colorat pe toate randurile din grid. +- **De ce:** amanat explicit la poarta /autoplan din 26.07.2026 (decizia D1/UC1) โ€” risc + de nedeterminism (timer unic, cursor de grid) si de rate-limit ANAF pe volum mare. +- **Conditii de intrare** (in aceasta ordine, nicio implementare inainte): + 1. Masuratori: latenta reala a unui apel ANAF la ore diferite; comportamentul la + rafale (30 loturi x 20 CUI la interval scurt โ€” de la al catelea vine 429). + 2. Date din loguri: cate discordante apar efectiv la pilot si in ce flux. +- **Cerinte tehnice deja verificate** (de nepierdut la implementare) โ€” sectiunea 8 din + `docs/design-alegere-partener-anaf.md`: UC1, D-F2, C4, H1, H4, H5, H8, H9, M1, M7. +- **Efort:** L. **Depinde de:** masuratorile de mai sus. diff --git a/changelog_roacont.txt b/changelog_roacont.txt index dc97558..ea3dc4a 100644 --- a/changelog_roacont.txt +++ b/changelog_roacont.txt @@ -1,11 +1,18 @@ - +# Design: Asistenta alegere partener corect la introducere (verificare ANAF automata) + +Generat de /office-hours pe 24.07.2026 +Branch: main (implementarea va merge pe claude/) +Repo: romfast/roacont (mirror git al SVN ^/ROACONT/Trunk) +Status: APPROVED (24.07.2026, D7) +Mode: Startup (intraprenoriat โ€” produs intern ROA) + +## Problema + +La introducerea facturilor de achizitie/vanzare (si la incasari/plati) utilizatorul +alege partenerul dintr-un dialog de cautare. Cand un partener isi schimba calitatea +de platitor TVA, conventia ROA cere ALT partener (alt `nom_parteneri.id_par`) cu +acelasi nume si cod fiscal cu/fara RO; cel vechi se marcheaza de regula INACTIV. +Probleme concrete: + +1. Utilizatorul nu are nicio informatie, la selectie, despre situatia ANAF curenta + a partenerului (platitor TVA sau nu, activ/radiat) โ€” statutul se poate schimba + oricand, chiar daca partenerul nu are inca o "pereche". +2. Perechea inactiva e complet invizibila (filtru hardcodat `STERS=0 AND INACTIV=0`), + deci la o noua schimbare de calitate utilizatorul creeaza al TREILEA duplicat in + loc sa reactiveze partenerul vechi. +3. Alegerea gresita din pereche rupe imperecherea plati/incasari cu facturi + (aceeasi cheie `id_part`), producand solduri fantoma pe doi parteneri. +4. Importurile (eFactura, extrase) compara codul fiscal doar cu UPPER+ALLTRIM, + deci `RO12345` si `12345` sunt terti diferiti โ€” fabrica de duplicate noi. + +## Evidenta cererii + +- Situatie recurenta la clientii ROACONT: parteneri care comuta calitatea de + platitor TVA de mai multe ori; utilizatori care aleg din greseala membrul gresit + al perechii; corectii manuale costisitoare la imperecherea plati-facturi. +- Butonul de verificare ANAF exista deja in nomenclator si e folosit โ€” dar e in alt + flux decat introducerea, unde se ia de fapt decizia. + +## Status quo (fluxul actual, mapat in cod) + +- Nucleu selectie: `CautPartenerContabilitate()` โ€” `COMUN\programe\ocautare.prg:56` + (SELECT `ID_PART, DENUMIRE, COD_FISCAL` din `NOM_PARTENERI`, filtru inactivi + `:117-121`), varianta casa/banca `caut_parteneri()` `:161` (param `tlInactiv` + `:175-177`), selectie multipla `caut_parteneri_xml()` `:199`. +- Dialog: `cauta_alfa_form(_plus)` โ€” `COMUN\clase\cauta_alfa_forms.vc2` (do_cauta, + do_alege, do_adauga/buton Nou). +- Puncte de apel: note contabile/facturi (`Clase\ointroduceri_cont.vc2`, + `verific_partener` :1542/:4769 โ†’ `do_cauta_partener` :921/:3848), casa/banca + (`COMUN\clase\ocasabanca.vc2:1788,1823`), stornare + (`Ferestre\frm_stornare_plinc.sc2:368`), importuri + (`frm_import_extrase_banca.sc2`, `frm_import_note_a4200.sc2`, + `oproceduri_import.prg:364/:746`). +- Verificare ANAF existenta: clasa `VerificareANAF` cu + `ANAF_SincronWebService_PlatitorTva` โ€” `COMUN\programe\validare.prg:1552,1603` + (endpoint v9 `PlatitorTvaRest`, loturi max 100 CUI, pauza 1s; intoarce `scpTVA`, + `statusInactivi`, `statusTvaIncasare`, denumire/adresa); buton `but_verifica` + (`COMUN\clase\cmd_butoane.vc2:427`) doar pe nomenclator/formular verificare. +- Statutul TVA NU e stocat pe partener: se deduce din prefixul `RO` al + `COD_FISCAL` (ex. `saft_d406.prg:1066`). Rezultatele verificarii merg doar in + istoric (`pack_parteneri.save_istoric_cod_fiscal`, `validare.prg:757`). + +## Utilizator tinta si pana cea mai ingusta + +Operatorul de contabilitate care introduce zilnic facturi si incasari/plati. +Pana cea mai ingusta: in momentul alegerii partenerului, sa i se spuna automat +"ANAF zice altceva decat codul fiscal al partenerului ales" si sa i se ofere +perechea corecta (inclusiv cea inactiva) sau crearea unui partener nou. + +## Constrangeri + +- 80/20: modificari minime cu efect maxim; fara refactorizari; fara schimbare de + schema Oracle (fara coloana "platitor TVA" pe `nom_parteneri`). +- VFP9 legacy, fara teste automate; comentarii in cod max o linie `*!*`. +- Conventia ROA ramane: partener nou la schimbare de calitate TVA; regula se + documenteaza (nu era scrisa nicaieri pana la acest document). +- Fara commit (git/SVN) fara review pe diff in `docs/`. + +## Premise agreate + +1. Modelul de date ramane neschimbat; statutul TVA se deduce din prefixul RO. +2. (revizuita) Durerea are DOUA surse: lipsa de vizibilitate la selectie SI + duplicatele nascute la import din compararea nenormalizata a codului fiscal. +3. Punctul de interventie cu efect maxim e nucleul de selectie din `ocautare.prg` + (+ dialogul `cauta_alfa_form`), mostenit de toate punctele de apel. +4. (decizie utilizator, D5) Verificarea ANAF se face AUTOMAT la fiecare alegere de + partener, nu doar cand exista pereche โ€” statutul se poate schimba intre timp. + +## Perspectiva cross-model (subagent independent) + +- Steelman: dialogul de selectie devine "ghid de decizie" โ€” o singura interventie + in nucleu, mostenita de toate formularele, fara schimbare de model de date. +- Insight-cheie: criteriul de "partener corect" nu e doar ANAF, ci SI "pe ce + id_part sunt documentele existente" โ†’ fereastra de decizie afiseaza data + ultimului document per membru al perechii. +- Premisa contestata (acceptata): duplicatele se nasc si la import โ†’ fixul de + potrivire normalizata intra in pachet. +- Idee laterala retinuta ca transa viitoare: raport pasiv de discordante + "prefix RO vs ultimul istoric ANAF" peste datele deja colectate. + +## Abordari considerate + +### A: Doar buton manual "Verificare ANAF" in dialogul de selectie +Refolosire `but_verifica`; zero latenta automata. Respinsa ca insuficienta: +protectia depinde de disciplina utilizatorului. + +### B (ALEASA): Verificare ANAF automata la alegere + fereastra de decizie + fix import +Detalii mai jos. Completeness 9/10 pe durerea descrisa. + +### C: B + raport pasiv discordante ANAF din istoric +Amanata ca transa separata (depinde de cat de des ruleaza clientii verificarea in +lot; decuplata de hot path โ€” se poate adauga oricand). + +## Abordarea recomandata (aprobata: B) + +Interventie intr-un singur punct comun + doua completari mici: + +1. **Functie noua** `VerificaPartenerLaSelectie(toPartener)` in + `COMUN\programe\ocautare.prg`, apelata din `CautPartenerContabilitate()` + **inainte de blocul `ADAUGA_CORESP_TIP_PART` (`ocautare.prg:148-150`)** โ€” daca + utilizatorul comuta pe pereche, corespondentele tip-partener se adauga pe + id_part-ul FINAL, nu pe cel initial. Acopera note/facturi, stornare, importuri + fara sa atinga cele ~10 puncte de apel. + - **Contract**: functia primeste si poate REESCRIE obiectul de retur + (`id_part`, `nume/denumire`, `cod_fiscal`, `tip`); daca `loCauta` e NULL/gol + (utilizatorul a renuntat la cautare), verificarea se sare complet. + - **Politica de activare (decizie eng review D1, apelanti triati in cod)**: + hibrid explicit. `CautPartenerContabilitate`: hook ON implicit (apelantii + sunt fluxuri de introducere), cu opt-out DOAR la `orap_terti.prg:1019` + (raport). `caut_parteneri`: hook OFF implicit, activat EXPLICIT (parametru + nou `tlVerificaANAF`) la cele 4 puncte de introducere: `ocasabanca.vc2` + (:1823), `frm_import_extrase_banca.sc2` (:2211, :2616), + `ointroduceri_cont.vc2` (:9665, :9704). Fluxurile de consultare (fisa + cont `fisa_cont_noua.sc2:459`, `orap_trezorerie.prg:31/:229`, + `orap_terti.prg:826/:1794/:1986`) raman NEATINSE, fara nicio modificare. + - Apel ANAF pentru CUI-ul partenerului ales: **wrapper nou single-CUI** peste + `VerificareANAF` (functia de lot `ANAF_SincronWebService_PlatitorTva` e + construita pentru loturi de 100 cu logging intens โ€” nu se cheama direct in + hot path); timeout-ul se seteaza pe obiectul WinHTTP cu `SetTimeouts`. + - **Cache pe sesiune** โ€” proprietate pe un obiect global existent (ex. + `goApp`) sau colectie publica declarata in `roacont.prg`, cheie = CUI + normalizat (fara RO), valoare = rezultat+timestamp; se goleste la + schimbarea firmei. Un CUI verificat o data pe sesiune nu mai genereaza + apel โ€” limiteaza si presiunea pe serviciul ANAF (mai multi operatori + simultan raman fiecare cu 1 apel/CUI/sesiune). + - **Degradare tacuta**: fara internet / ANAF cazut / timeout โ‡’ comportament + identic cu azi (fara mesaje de eroare in hot path). + - **Guard obligatoriu de mediu partajat**: `ocautare.prg` e inclus si de alte + produse ROA โ€” hook-ul verifica intai existenta clasei + (`TYPE("goApp")`/`ALINES`+`SET("PROCEDURE")` sau `try CREATEOBJECT` cu + fallback) si degradeaza tacut daca `validare.prg`/`VerificareANAF` nu sunt + incarcate in acel produs. Fara guard, orice selectie de partener din + produsele-frate ar crapa la runtime. + - **Excluderi**: persoane fizice (TIP_PERSOANA=2 / CNP 13 cifre), coduri + nenumerice dupa eliminarea RO, parteneri externi (COD_TARA<>RO). +2. **La discordanta** (ANAF `scpTVA` โ‰  prefixul RO al partenerului ales, sau + `statusInactivi` = inactiv/radiat) โ€” fereastra compacta de decizie + (**forma noua mica in `COMUN\ferestre\`**, pe stilul dialogurilor existente; + nu AMESSAGEBOX โ€” are grid cu membrii perechii si 3-4 butoane): + - Cauta perechea dupa CUI normalizat (fara RO), **INCLUSIV INACTIV=1** + (interogare Oracle ieftina; filtrul gridului principal NU se schimba). + - Afiseaza toti membrii: ID, denumire, cod fiscal, activ/inactiv, + **data ultimului document** per `id_part` (MAX peste jurnal cumparari, + jurnal vanzari si casa/banca โ€” lista exacta a tabelelor se confirma la + implementare; atentie la lipsa indexului pe `id_part` pe baze mari โ€” se + ruleaza DOAR in fereastra de discordanta, nu in hot path). + - Actiuni: (a) alege perechea concordanta cu ANAF; (b) **swap ghidat** cu + confirmare: reactiveaza perechea + inactiveaza partenerul curent (evita al + treilea duplicat); dupa swap se face **refresh pe cursorul de selectie** + (cel vechi dispare, cel reactivat apare); (c) fara pereche: propune buton + "Nou" cu CUI-ul corect precompletat; (d) continua oricum (utilizatorul + decide, nimic blocant). + - **Guvernarea swap-ului (decizie CEO review D2)**: swap-ul se executa prin + calea EXISTENTA de modificare nomenclator (`nom_parteneri_modifica` / + pachetul Oracle aferent), NU prin UPDATE ad-hoc in ocautare.prg โ€” o + singura cale de scriere in nom_parteneri. Gating cu dreptul EXISTENT de + modificare nomenclator parteneri (`verifica_drepturi`): fara drept, + butonul de swap e dezactivat cu tooltip explicativ, dar perechea poate fi + in continuare ALEASA. + - **Kill-switch (decizie CEO review D4)**: optiune noua in `settings.ini`, + sectiunea `[anaf]`, `verificare_selectie=1` implicit (citita cu `getini`). + Cu 0, verificarea automata la selectie e oprita complet (comportament + identic cu azi) โ€” rollback chirurgical per statie/client fara downgrade + de exe. + - Concordanta โ‡’ nicio fereastra, flux identic cu azi. +3. **Fix import** (`Programe\oproceduri_import.prg`, potrivirea + `GetPartenerByCodFiscal` :449 / dedup :4479-4495, citare verificata): cand + potrivirea exacta esueaza, se incearca potrivirea pe CUI normalizat (fara + RO); la gasire NU se alege automat โ€” se cere confirmare ("exista partener cu + acelasi CUI fara/cu RO โ€” folosesti acela sau creezi unul nou?"). + **Pe loturi** (zip eFactura): confirmarile NU se pun una cate una in mijlocul + importului โ€” discordantele se colecteaza si se prezinta o singura data, la + final, intr-o lista cu optiune per rand + "aplica la toate". Opreste fabrica + de duplicate fara sa blocheze importul. +4. **Optional** (redundant partial cu verificarea automata โ€” se taie daca + diff-ul creste): buton `but_verifica` si in `cauta_alfa_form`, pentru + verificare manuala INAINTE de alegere. + +### Ce NU facem (explicit) +- Nu adaugam coloana TVA / data verificare pe `nom_parteneri`. +- Nu facem merge/reasignare de documente intre id_part-uri. +- Nu schimbam filtrul `STERS=0 AND INACTIV=0` al gridului de selectie. +- Nu blocam alegerea: toate avertismentele sunt informative, cu "continua oricum". + +## Intrebari deschise + +1. ~~Drepturile swap~~ โ€” REZOLVAT (CEO review D2): dreptul existent de + modificare nomenclator parteneri; executie prin calea existenta de + modificare, nu UPDATE ad-hoc. +2. TTL cache: pe sesiune (propus) sau pe zi (persistat)? Propunerea: sesiune. +3. `caut_parteneri_xml` (selectie multipla) intra in scope sau ramane pe fluxul + vechi? Propunerea: ramane pe fluxul vechi (volum mare de CUI-uri per apel). +4. Latenta reala ANAF in orele de varf โ€” de masurat inainte de a decide timeout-ul. +5. ~~Sursa `tip` la comutare~~ โ€” REZOLVAT (eng review D2): la comutarea pe + pereche, `tip` se recalculeaza pentru id-ul perechii cu acelasi criteriu ca + in SELECT-ul gridului (apartenenta la `coresp_tip_part` pentru tipurile + contului); hook-ul fiind inainte de blocul `ocautare.prg:148-150`, blocul + existent adauga natural corespondenta lipsa pe id-ul FINAL. Nota de + implementare: wrapper-ul single-CUI sta in `validare.prg`, langa clasa + `VerificareANAF` (coeziune). + +## Criterii de succes + +- La alegerea unui partener discordant cu ANAF, utilizatorul vede fereastra de + decizie intr-un timp acceptabil (tinta orientativa <3s; pragul final se + fixeaza dupa masuratorile de latenta din Tema) si poate ajunge la perechea + inactiva fara sa iasa din fluxul de introducere. +- Zero regresie de viteza cand ANAF nu raspunde (degradare tacuta). +- Importul eFactura nu mai creeaza partener nou fara confirmare cand exista + pereche pe CUI normalizat. +- Niciun duplicat nou "al treilea" la clientii-pilot dupa o luna de folosire. + +## Plan de distributie + +Livrare in `roacont.exe` prin fluxul existent: build din VFP IDE (`roacont.PJX`), +revizie in `pj2`, intrare in `changelog_roacont.txt` (tag `:nou:`), commit SVN de +catre Marius dupa aprobarea diff-ului. Nu necesita migrare Oracle (fara DDL). + +## Dependinte + +- Serviciul ANAF `PlatitorTvaRest v9` (deja folosit in verificarea din nomenclator). +- `COMUN\programe\validare.prg` si `ocautare.prg` sunt partajate cu alte produse + ROA care le includ โ€” de verificat la implementare cine mai apeleaza + `caut_parteneri`/`CautPartenerContabilitate` din alte produse (blast radius + COMUN vs COMUNROA). + +## Anexa: harta erorilor, edge-case-uri si teste (CEO review D3) + +### Harta erorilor (regula-cheie: verificarea esueaza TACUT, swap-ul esueaza ZGOMOTOS) + +| 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 | +| 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 | + +### Ordinea operatiilor (performanta) +1. Alege partener โ†’ 2. cache lookup CUI โ†’ 3. apel ANAF (doar daca nu e in +cache) โ†’ 4. concordant โ‡’ STOP (zero query suplimentar pe selectiile normale) +โ†’ 5. DOAR la discordanta: query pereche pe CUI normalizat (inclusiv inactivi) ++ MAX(data doc) per membru โ†’ 6. fereastra de decizie. + +### Reguli de implementare +- **`NormalizeazaCUI()`** โ€” functie UNICA in COMUN (strip RO doar daca restul + e numeric, UPPER, fara spatii), folosita de wrapper, de query-ul de pereche + si de fixul de import. Fara logica duplicata in 3 locuri. +- **Indicator vizual**: wait cursor + text scurt "Verificare ANAF..." pe durata + apelului (hot path-ul devine perceptibil doar cand chiar se apeleaza ANAF). +- **Logging**: fiecare discordanta gasita se logheaza in `goLog` (CUI, id_part + ales, ce a zis ANAF, ce a decis utilizatorul) โ€” diagnosticabil ulterior. +- **Kill-switch**: `settings.ini [anaf] verificare_selectie=0` dezactiveaza + complet hook-ul (verificat la intrare in functie, inainte de orice). + +### Lista de teste (manual / harness headless โ€” test_init_env_auto) +1. Partener concordant (RO + ANAF platitor) โ†’ niciun mesaj, flux identic. +2. Discordant cu pereche ACTIVA โ†’ fereastra, alegerea perechii scrie id-ul corect. +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. +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. +8. Dublu-click rapid pe rand โ†’ un singur apel ANAF, o singura fereastra. +9. Kill-switch 0 โ†’ comportament identic cu versiunea anterioara. +10. Cache: al doilea document pe acelasi partener in aceeasi sesiune โ†’ zero + apel ANAF nou. +11. REGRESIE (eng review D1): fisa de cont si rapoartele terti/trezorerie NU + declanseaza verificarea ANAF โ€” comportament identic cu versiunea anterioara. + +## Specificatia vizuala a ferestrei de decizie (design review D2-D3) + +``` ++--- Verificare partener ANAF ------------------------------------+ +| ANAF: NEPLATITOR TVA (din 01.03.2026). Partenerul ales are | +| codul fiscal RO12345678 โ€” nu corespunde. | <- verdictul, PRIMUL ++------------------------------------------------------------------+ +| ID | Denumire | Cod fiscal | Stare | Ultim document | +| 1234 | FIRMA SRL | RO12345678 | Activ | 15.06.2026 | <- alegerea initiala +|>5678 | FIRMA SRL | 12345678 | INACTIV | 20.02.2026 | <- PERECHEA, preselectata ++------------------------------------------------------------------+ +| [Alege selectat] [Reactiveaza+inactiveaza] [Nou] [Continua] | ++------------------------------------------------------------------+ +``` + +- **Ierarhie**: 1) verdictul intr-o propozitie (status ANAF + data + de ce nu + corespunde), 2) gridul cu toti membrii, randul CONCORDANT cu ANAF preselectat, + 3) butoanele. Ton utilitar, fara alarmism โ€” o singura decizie pe ecran. +- **Tastatura (decizie D2)**: Enter = alege randul selectat (preselectat: + perechea concordanta); sageti = schimba selectia; ESC = pastreaza alegerea + originala (inchide fara nicio actiune); swap DOAR pe butonul dedicat, cu + confirmare suplimentara โ€” niciodata pe Enter. Hotkey-uri pe butoane (VFP \<). +- **Conventii existente refolosite**: randul INACTIV colorat gri + RGB(225,225,225) + tooltip โ€” aceeasi conventie pe care utilizatorii o stiu + din cauta_alfa_form ("partenerii de alt tip sunt colorati gri", + ocautare.prg:136-137); butoane din cmd_butoane.vcx; erori prin AMESSAGEBOX; + fereastra pe clasele de baza cont2000 (stil identic cu dialogurile existente). +- **Starea "fara pereche"**: fereastra FARA grid โ€” doar verdictul + butoanele + [Nou (CUI precompletat)] si [Continua]; Enter = Continua (aici NU exista + alternativa corecta de preselectat, deci implicitul ramane conservator). +- **Butonul de swap fara drept de nomenclator**: dezactivat (nu ascuns), cu + tooltip "Necesita drept de modificare nomenclator parteneri". +- **Indicator in dialogul de selectie**: pe durata apelului ANAF โ€” wait cursor + + WAIT WINDOW "Verificare ANAF..." NOWAIT, sters imediat dupa raspuns; + vizibil doar cand apelul chiar are loc (cache miss). + +## Regula de lucru documentata (nota ceruta โ€” nu era scrisa nicaieri) + +**Conventie ROA โ€” schimbarea calitatii de platitor TVA a unui partener:** +1. NU se modifica codul fiscal al partenerului existent (istoricul documentelor + ramane legat de `id_part`-ul vechi si de statutul TVA de la acea data). +2. Se foloseste ALT partener cu acelasi nume si codul fiscal cu/fara RO dupa noua + calitate. Daca perechea EXISTA deja (chiar inactiva), se REACTIVEAZA aceea โ€” + nu se creeaza al treilea duplicat. +3. De regula, membrul care nu mai corespunde se marcheaza INACTIV ca sa nu mai + apara la introducere. Daca ambii raman activi (situatii tranzitorii), + utilizatorul alege dupa situatia ANAF si dupa documentul introdus. +4. La incasari/plati se alege ACELASI `id_part` ca pe facturile pe care le + stinge โ€” altfel imperecherea plati-facturi se rupe. + +(La implementare, acest text se muta/copiaza si in `COMUN\docs\` daca vrem sa fie +vizibil si celorlalte produse ROA.) + +## Tema (assignment) + +Inainte de implementare, ruleaza pe o baza reala de client doua masuratori: +1. `SELECT` de numarare: cate grupuri de CUI normalizat (fara RO) au >1 partener + si cati dintre ei au membri inactivi โ€” dimensioneaza problema reala. +2. 10 apeluri ANAF `PlatitorTvaRest` la ore diferite โ€” masoara latenta reala + pentru alegerea timeout-ului. +Rezultatele decid timeout-ul si daca fereastra de decizie are nevoie de +pre-incarcare asincrona. + +## Ce am observat la felul in care gandesti + +- Ai respins ambele extreme si ai corectat exact pe mecanism: "chiar daca nu + exista o pereche, tot trebuie facuta verificarea automata pe ANAF, pentru ca + intre timp partenerul isi poate fi schimbat calitatea" โ€” ai vazut ca riscul e + in timp, nu in structura datelor. +- Ai adus singur cazul-limita care omoara solutiile naive: "partenerul anterior + este marcat inactiv... si exista situatii in care isi schimba calitatea de mai + multe ori" โ€” perechea invizibila din cauza filtrului INACTIV=0. +- "80/20 minim de modificari cu maxim de efecte" โ€” ai cerut explicit constrangerea + de cost inainte de solutii, nu dupa. + +## GSTACK REVIEW REPORT + +| Review | Trigger | Why | Runs | Status | Findings | +|--------|---------|-----|------|--------|----------| +| CEO Review | `/plan-ceo-review` | Scope & strategy | 1 | CLEAR | mode: HOLD_SCOPE, 3 constatari decise (swap guvernat, anexa spec, kill-switch), 0 critical gaps | +| Codex Review | `/codex review` | Independent 2nd opinion | 0 | โ€” | sarit (2 verificari independente rulate azi la office-hours) | +| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 1 | CLEAR | 2 issues (politica hook hibrid D1, recalc tip D2), 13/13 cai cu test, 0 critical gaps | +| Design Review | `/plan-design-review` | UI/UX gaps | 1 | CLEAR | score: 5/10 โ†’ 9/10, 3 decizii (focus, Enter=perechea, spec vizuala) | +| DX Review | `/plan-devex-review` | Developer experience gaps | 0 | โ€” | n/a (nu e produs developer-facing) | + +- **UNRESOLVED:** 0 โ€” toate deciziile CEO (D1-D6), eng (D1-D2) si design (D1-D3) au raspuns. +- **VERDICT:** CEO + ENG + DESIGN CLEARED โ€” planul e complet, gata de implementare pe branch claude/partener-anaf. + +--- + +## REVIZIA 2 (25.07.2026) โ€” V2': verdict ANAF in formularul de cautare + +Status: APPROVED (25.07.2026, feedback live Marius + D1a/D2 confirmate). +Aceasta revizie INLOCUIESTE sectiunile "fereastra de decizie" si "Specificatia +vizuala a ferestrei de decizie" din designul initial. Restul (hook, wrapper +single-CUI, NormalizeazaCUI, cache, kill-switch, excluderi, degradare tacuta, +fix import, politica de activare pe apelanti) RAMANE VALABIL. + +### Motivatia (test live 25.07, ROMFAST 1879855) + +Fereastra de decizie implementata in rundele 1-8 s-a dovedit confuza in uz real: +1. Utilizatorul nu intelege de ce a aparut fereastra; mesajul de sus e insuficient + (fara numele partenerului, fara ce inseamna verdictul, fara ce are de facut). +2. Gridul arata TOT grupul de CUI, inclusiv membrii cu aceeasi problema โ€” nu ghideaza. +3. Butonul Inactiveaza/Activeaza cere o operatiune de nomenclator ambigua (pe care + rand? in pereche cu cine?) in mijlocul fluxului de introducere. +4. Verificarea ANAF e invizibila cand totul corespunde (WAIT WINDOW NOWAIT dispare + la orice miscare de mouse) โ€” utilizatorul nu stie ca protectia exista. +5. Butonul "Nou" din fereastra dubleaza butonul Nou al cautarii. + +### Solutia revizuita + +**1. Label ANAF in `cauta_alfa_form` (COMUN\clase\cauta_alfa_forms.vc2)** โ€” +verdictul pentru RANDUL CURENT din grid, vizibil INAINTE de alegere: + +``` ++--- Cautare partener --------------------------------------------+ +| Cautare: ROMFAST_ [Nou] | +| | Denumire | Cod fiscal | ... || +| |>ROMFAST CONSTANTA SRL | 1879855 | || +| ANAF: PLATITOR TVA โ€” codul 1879855 (fara RO) NU corespunde. | <- label ++------------------------------------------------------------------+ +``` + +Starile labelului (texte finale, spec Marius 25.07): +- gol โ€” persoana fizica / partener extern / CUI nenumeric / kill-switch 0 / + eroare-timeout ANAF (degradare tacuta); +- "Verificare ANAF..." โ€” pe durata apelului (persistent, nu WAIT WINDOW); +- concordant (VERDE): "ANAF: Platitor TVA (RO1879855 FAGA SRL)" โ€” cod fiscal + + denumirea ANAF; +- discordant (ROSU, bold): "(!) ANAF: Platitor TVA (1879855 ROMFAST CONSTANTA + SRL) ยป detalii" โ€” marcaj (!) si sageata ยป (chr 187, cp1252) care indica + apasarea pentru detalii; cursor mana pe label (clickabil). + +Declansare: `AfterRowColChange` reporneste un timer debounce (~400ms); apelul se +face doar daca utilizatorul ramane pe rand (cache per CUI pe sesiune โ†’ un singur +apel real per partener). Navigarea rapida nu declanseaza nimic. + +Blast radius: label + timer + proprietate de activare intra in clasa comuna, dar +INERTE implicit (proprietate .F.); se activeaza doar din cautarile de parteneri +cu verificare ANAF (tlVerificaANAF / CautPartenerContabilitate), cu guard-urile +existente pentru produsele-frate. Celelalte nomenclatoare nu vad nicio diferenta. + +**2. Dublu-click pe label** โ†’ AMESSAGEBOX cu detaliile complete: nume + cod ales, +verdict ANAF, codul fiscal corect, perechea existenta (cautata pe CUI normalizat, +INCLUSIV inactiva) sau indrumarea catre butonul Nou al cautarii daca nu exista. + +**3. La alegerea unui partener discordant (D1a)** โ€” plasa de siguranta: apare +automat dialogul de decizie (formularul de cautare ramane deschis). Format +final (spec Marius, 25.07) โ€” lista numerotata + butoane care spun exact ce aleg: + +``` +1. ROMFAST CONSTANTA SRL (CUI: 1879855, ID: 108) ANAF: PLATITOR TVA +2. FAGA SRL (CUI: RO1879855, ID: 200140) inactiv + + [Alege FAGA SRL] [Alege ROMFAST CONSTANTA SRL] [Renunta] +``` + +- Butonul perechii alege perechea; butonul alesului pastreaza alegerea; + Renunta (si ESC) = ramai in cautare. Fara linie de mapare Da/Nu. +- Fara pereche: randul 1 + "Nu exista partener cu CUI RO1879855 (il puteti + crea cu butonul Nou)." โ€” butoane [Continua] [Renunta]. +- Mai multi candidati: lista numerotata a candidatilor + "Alegeti manual." โ€” + buton unic, ramai in cautare. +- Implementare: AMESSAGEBOX nu suporta etichete custom pe butoane โ€” dialogul e + o clasa mica `DEFINE CLASS ... AS Form` definita IN COD in ocautare.prg + (fara .scx, fara binar), 2-3 butoane cu Caption dinamic (denumiri trunchiate + rezonabil). Detaliile de la dublu-click pe label folosesc acelasi format + numerotat, pur informativ. + +**FARA activare/reactivare (decizie Marius, 25.07)**: alegerea unei perechi +inactive doar FOLOSESTE acel id_part pe document โ€” partenerul ramane inactiv, +"inactiv" apare pur informativ. Motivatie: pe incasari/plati utilizatorul vrea +partenerul cu facturile/soldul, indiferent de TVA; nu toti utilizatorii au +drepturi de modificare nomenclator; orice operatiune de nomenclator in fluxul +de introducere incurca. Activarea/inactivarea raman exclusiv operatiuni +manuale in nomenclatorul de parteneri. + +**4. Alegere rapida (D2)**: daca utilizatorul alege inainte ca timerul sa fi +verificat randul, verificarea se face PE LOC la selectie (alegerile rapide nu +ocolesc protectia). + +### Ce se ELIMINA din implementarea rundelor 1-8 + +- `COMUN\ferestre\frm_verif_partener_anaf` (.sc2/.scx/.sct) โ€” fereastra dispare. +- But_swap / ActualizeazaCaptionSwap / apelul `PACK_PARTENERI.MODIFICA_INACTIV`. +- Migrarea `ff_2026_07_24_01_COMUN_PACK_PARTENERI.sql` โ€” NU se mai ruleaza pe + productie; `versiune_db.txt` revine la valoarea anterioara. (Procedura ramane + aplicata pe schema de test โ€” inofensiva, se poate drop-ui separat.) +- Deschiderea ferestrei din hook (`Do Form ... frm_verif_partener_anaf`). + +### Ce se PASTREAZA neschimbat + +`NormalizeazaCUI`, wrapper single-CUI `ANAF_VerificaCuiSingle`, cache-ul pe +sesiune + golirea la schimbarea firmei, kill-switch `[anaf] verificare_selectie`, +excluderile (PF/extern/nenumeric), degradarea tacuta, fixul de import +(oproceduri_import.prg), politica de activare pe apelanti (tlVerificaANAF, +opt-out orap_terti), regula de lucru documentata, harta erorilor (mai putin +randul de swap โ€” eliminat). + +### Teste afectate + +Suita `COMUN\utile\Teste\partener_anaf\` se rescrie pe noul flux: label (stari, +debounce, dublu-click), D1a (cele 3 butoane), D2 (alegere rapida), reactivare +single-row, regresie nomenclatoare non-partener (label inert). + +--- + +## REVIZIA 3 (25.07.2026) โ€” verdict la data documentului + texte finale + +Status: APPROVED (25.07.2026, feedback live Marius pe screenshot-ul din productie). +Aceasta revizie ajusteaza doar textele si adauga data documentului; restul REVIZIEI 2 +ramane valabil. + +### 1. Textul labelului + +Denumirea si codul fiscal afisate sunt cele de la ANAF (nu cele ale randului ales), +in ordinea NUME apoi COD, plus data la care s-a cerut starea: + +- concordant (verde): `ANAF: Platitor TVA (ROMFAST SRL RO1879855) la 15.06.2026` +- discordant (rosu, bold): `(!) ANAF: Platitor TVA (ROMFAST SRL RO1879855) la 15.06.2026 >> click detalii` + +Sageata `ยป` (Chr(187)) se inlocuieste cu `>>` ASCII (fara risc de encoding cp1252), +iar textul devine `click detalii`. Marcajul `(!)` se pastreaza la discordanta. + +### 2. Dialogurile (detalii la dublu-click + confirmarea la alegere) + +Format aerisit, cu antet comun (metodele `AntetDialog` / `ListaCorecti` din +`anaf_verif_cautare`): + +``` +ROMFAST CONSTANTA SRL (CUI: 1879855, ID: 108) + +! ANAF: Platitor TVA (ROMFAST SRL, RO1879855) la 15.06.2026 + +Parteneri ROA cu CUI RO1879855: + 1. ABSOLUT SRL (ID: 598) inactiv + 2. ROMFAST S.R.L. (ID: 614) +``` + +- lista contine DOAR partenerii cu codul fiscal corect (fara cel deja ales), + ordonati alfabetic (`ORDER BY UPPER(DENUMIRE)` in `ANAF_CautaPereche`); +- fara candidati: `Nu exista partener cu CUI RO1879855 (il puteti crea cu butonul Nou).`; +- la alegere, ultimul rand ramane maparea butoanelor: + `Da = alege 1. Nu = pastreaza selectia Abandon = inapoi la cautare`. + +### 3. Starea ANAF la data documentului + +Serviciul `PlatitorTvaRest v9` primea deja o data (era `Date()`); acum primeste data +documentului, cand exista: + +- `ANAF_VerificaCuiSingle(tcCui, tdData)` si `ANAF_StarePartener(tcCodFiscal, tdData)`; + data goala/lipsa => data curenta (comportament identic cu inainte); +- cheia de cache pe sesiune devine `CUI + data` (acelasi CUI la doua date = doua apeluri); +- transport: cheia de hash `dDataDoc` pentru `CautPartenerContabilitate`, parametrul 7 + `tdDataDoc` pentru `caut_parteneri`; proprietatea `dDataDoc` pe `anaf_verif_cautare`; +- sursele datei in apelanti: `poAct.dataact` (introduceri note/facturi, + `ointroduceri_cont.vc2`: `do_cauta_partener` x2, gridurile de partener din + `frm_note`/`frm_note2007`, `frm_plati_impozite`) si `poDate.dataact` (facturare, + `ofacturare.vc2`: `do_cauta_client`, `do_cauta_furnizor`); toate cu guard `Type(...)='D'`. + +### 4. Casa/banca โ€” verificare scoasa + +Punctul activat in runda 1 (`ocasabanca.vc2`, `Ck_bancasa.InteractiveChange`) NU e +introducere de document: e checkbox-ul de filtrare care alege casa/banca proprie +(`filtru = ' AND id_bancasa = ...'`), fara data de document. Verificarea ANAF se +dezactiveaza acolo (revenire la apelul fara `tlVerificaANAF`). + +### 5. Comentarii in cod + +Regula de lucru confirmata de Marius (25.07): fara comentarii in corpul codului; o +singura linie scurta in antetul fisierului, fara autor "claude". Comentariile inline +adaugate in rundele 1-11 pentru aceasta functionalitate se elimina. + +### 6. Import din extras de cont โ€” corectie de abordare (decizie Marius, 25.07) + +La importul din extras e vorba de PLATI/INCASARI, nu de facturi: statutul ANAF +(platitor/neplatitor) nu ajuta cu nimic acolo. Singurul criteriu care conteaza e +"partenerul pe care stau facturile", pentru ca `GetDocumentByContPartenerAct` +(`COMUN\programe\oproceduri_comune.prg:6942`) cauta documentul strict pe `id_part` +(`ireg_parteneri`, an/luna curente), iar `GetPartenerByCodFiscal` (`:6557`) +normalizeaza CUI-ul si ia orbeste `MAX(id_part)` din grup. + +Prin urmare: +- verificarea ANAF se scoate din formularul de import extrase (cele doua pickere + manuale de partener din configurari); +- confirmarea pe loturi pentru discordantele de CUI (adaugata in runda 1) se + ELIMINA โ€” importul nu mai intreaba nimic; +- cand exista mai multi parteneri cu acelasi CUI normalizat, alegerea se face + dupa documente, nu dupa `MAX(id_part)`: + 1. daca linia de extras are numar de document si optiunea "Asociaza facturi" e + bifata, documentul se cauta pe TOTI membrii grupului, iar nota merge pe + partenerul pe care s-a gasit factura (se muta si `id_partd`/`id_partc`); + 2. altfel, se alege membrul cu documente in perioada curenta (`ireg_parteneri`, + numar de randuri, la egalitate soldul cel mai mare in modul); + 3. daca niciun membru nu are documente, ramane alegerea de azi. +- la initializare facturi din balanta (`CompleteazaParteneriROA`) confirmarea per + partener se elimina: se foloseste tacut partenerul existent cu acelasi CUI + normalizat, iar cursorul de parteneri primeste coloana normalizata calculata o + singura data (fara `Locate` cu apel de functie per partener). + +--- + +## REVIZIA 4 (26.07.2026) โ€” verificare neintruziva la alegerea partenerului + +Status: APROBATA CU MODIFICARI dupa review /autoplan (26.07.2026, 3 lentile: +CEO / design / inginerie, Codex indisponibil pe masina). Aceasta revizie +inlocuieste punctul 3 al REVIZIEI 2 ("La alegerea unui partener discordant") +si punctul 2 al REVIZIEI 3 (maparea butoanelor la alegere). + +Motivatia (Marius, 26.07): formularul de avertizare de la alegerea partenerului +e intruziv si confuz, mai ales pe incasari/plati, unde partenerul NU e o alegere +libera โ€” e determinat de facturile de imperecheat, iar o propunere de schimbare +a partenerului exact in acel punct sparge imperecherea (`GetDocumentByContPartenerAct` +cauta documentul strict pe `id_part`). + +### Decizii de poarta (review /autoplan, 26.07) + +| # | Decizie | Continut | +|---|---|---| +| D1 / UC1 | **Marcajul colorat pe toate randurile NU intra in aceasta transa** | Implicit ramane nivelul 1 (verdict pe randul curent). Ghidarea o preia, in Detalii, semnul "are documente in perioada" pe fiecare candidat. Marcajul in grid devine transa separata, dupa masuratori (vezi "Transa viitoare") | +| UC2 | **Polaritate inversata** | `CautPartenerContabilitate` devine OFF implicit + opt-in explicit `lVerificaANAF=>1` pe 8 apeluri. Simetric cu `caut_parteneri` | +| UC3 | **Detalii ramane AMESSAGEBOX** | Fara formular nou definit in cod. Optiunea per utilizator se comuta dintr-un punct de meniu, dupa modelul `RC_REGCUMP_LISTARE_BUG` | + +### 1. Ce se scoate din calea de alegere + +`VerificaAlegere` (`ocautare.prg:428`) nu mai deschide nimic pe comportamentul +implicit: cand nivelul de confirmare e 0 face `Return .T.` imediat, fara apel +sincron la ANAF. + +La nivelul 3 (opt-in) comportamentul ramane exact cel de azi: verificare sincrona ++ `Amessagebox(...,3+48)` cu lista numerotata + `aInputBox` pentru varianta. +ATENTIE (eroare factuala corectata): clasa `anaf_dialog` **nu exista** โ€” a fost +eliminata la runda 11, iar confirmarea de azi e AMESSAGEBOX. Nicio parte a acestei +revizii nu se sprijina pe ea. + +Chiar cand nu se afiseaza nimic, alegerea unui partener discordant **se logheaza** +in `goLog` (CUI, `id_part` ales, verdict) โ€” e singura sursa de date despre +frecventa reala a problemei, si conditia de intrare pentru transa viitoare. + +### 2. Detalii (click pe label) โ€” decizia, la cererea utilizatorului + +Ramane `AMESSAGEBOX`, cu formatul din REVIZIA 3, plus doua schimbari: + +1. **Discriminatorul pe documente** langa fiecare candidat โ€” criteriul care conteaza + efectiv la imperechere, nu statutul TVA: + ``` + ROMFAST CONSTANTA SRL (CUI: 1879855, ID: 108) + + ! ANAF: Platitor TVA (ROMFAST SRL, RO1879855) la 15.06.2026 + + Parteneri ROA cu CUI RO1879855: + 1. ABSOLUT SRL (ID: 598) inactiv + 2. ROMFAST S.R.L. (ID: 614) are documente in perioada + ``` + Sursa: `ireg_parteneri` pe anul/luna curente, un singur SELECT, **doar la + deschiderea Detalii** (niciodata in calea de navigare prin grid). +2. **Alegerea variantei se face de aici** (butoanele AMESSAGEBOX + `aInputBox` + cand sunt mai multi candidati), adica exact mecanismul existent, mutat de pe + calea impusa pe calea ceruta de utilizator. Foloseste `poANAFInlocuire` + + `AplicaInlocuirePartenerANAF`, cu guardul obligatoriu de la punctul 5 (C2). + +Detalii se deschide si cand `oStare` e `.Null.` (ANAF picat, cod fiscal invalid): +afiseaza antetul si o linie explicita ("ANAF nu a raspuns โ€” verificarea a fost +sarita"), nu returneaza mut ca azi (`ocautare.prg:411`). + +Labelul devine clickabil pe ambele stari (verde si rosu) si primeste **F4** ca +echivalent de tastatura (utilizatorii lucreaza fara mouse); textul labelului spune +`F4 = detalii`. Codul de tasta pentru F4 se confirma la implementare โ€” ramura +`OTHERWISE` din `cauta_alfa_forms.vc2:715` nu intra in conflict cu redirectarea +literelor catre caseta de cautare. + +### 3. Cascada de optiuni โ€” o singura scara + +O singura optiune, `RC_ANAF_VERIF_SELECTIE`, cu valori cumulative: + +| Valoare | Comportament | +|---|---| +| 0 | oprit (nicio verificare) | +| **1** | **verdict pe randul curent (implicit)** | +| 2 | rezervat pentru marcajul in grid (transa viitoare) | +| 3 | verdict + confirmare la alegerea unui partener discordant (opt-in) | + +Rezolvare, prima valoare gasita castiga: +1. `settings.ini [anaf] verificare_selectie` โ€” **doar kill-switch pe valoarea 0**, + nu sursa de nivel (altfel bifa/optiunea utilizatorului devine silentios + inoperanta si nimeni nu poate explica de ce); +2. `citeste_optiune_utilizator('RC_ANAF_VERIF_SELECTIE')` โ€” per utilizator; +3. `citeste_optiune('RC_ANAF_VERIF_SELECTIE')` โ€” per firma; +4. hardcodat: 1. + +Comutarea per utilizator: punct de meniu, dupa modelul `RC_REGCUMP_LISTARE_BUG` +(citeste valoarea, arata starea, scrie noua valoare cu `scrie_optiune_utilizator`). +Etichete fara ambiguitate: `Arata starea ANAF in cautarea de partener` / +`Intreaba la alegerea unui partener discordant`. + +Seed-ul per firma se amana: cascada cade oricum pe implicitul hardcodat, iar un +INSERT in `optiuni` e citit de COMUN in toate produsele โ€” nu e "discoverabilitate", +e comutator suite-wide. + +### 4. Politica de activare (inversata, UC2) + +`CautPartenerContabilitate` (`ocautare.prg:507`) devine **OFF implicit**, cu opt-in +explicit `lVerificaANAF=>1`. Motivul, verificat: exista ~68 de apelanti, dintre care +lista de opt-out acoperea ~20, toti din ROACONT; ar fi ramas ON prin omisiune +`COMUN\clase\baza.vc2:10400` (clasa de baza UI, deci toate produsele), +`orapoarte_cont.vc2:3843` (filtru de raport), `ofacturare.vc2:20946` ("Alegeti Banca"), +`:20957` ("casa in lei"), `:19441` (Agenti), `:7160/:7997/:9257` (Responsabili), +`:13793/:17826` (Partener rezervare), plus `oinventar`, `omodificari`, +`onomenclatoare`, `ferestre_cere_date`, `ooperatii_comune.prg`. `ROAGEST` si +`ROAAUTO` incarca `validare.prg` (`roagest.prg:234`, `roaauto.prg:198`), deci toate +guardurile existente trec si la ele. + +**Lista ON (singurele locuri cu `lVerificaANAF=>1`):** +- `Clase\ointroduceri_cont.vc2` โ€” `do_cauta_partener` (x2, `:921`, `:3850`); +- `Clase\ointroduceri_cont.vc2` โ€” gridurile de partener din `frm_note` (`:7096`, `:7120`) + si `frm_note2007` (`:8943`, `:8984`); +- `COMUN\clase\ofacturare.vc2` โ€” `do_cauta_client`, `do_cauta_furnizor`. + +Parametrul `lNuVerificaANAF` dispare. Consecinta de curatat: dupa inversare, niciun +apelant nu mai trimite `tlVerificaANAF` la `caut_parteneri` โ€” se decide explicit daca +parametrii `tlVerificaANAF`/`tdDataDoc` si ramura ANAF din `caut_parteneri` +(`ocautare.prg:625`) raman sau se scot ca ei cod mort. + +### 5. Reguli obligatorii de implementare (constatari verificate in cod) + +| # | Regula | Ancoraj | +|---|---|---| +| C2 | `AplicaInlocuirePartenerANAF` se apeleaza **doar cand `gnButon = 1`**; `poANAFInlocuire` se reseteaza la intrarea in Detalii si pe ramura de abandon. Altfel: utilizatorul alege varianta in Detalii, apoi anuleaza cautarea, iar `Scatter ... Blank` (`cauta_alfa.prg:243`) + guardul slab `Type('toPartener.id_part')='N'` fac ca apelantul sa primeasca totusi partenerul โ€” pe incasari rupe imperecherea | `ocautare.prg:165`, `:609`, `:666` | +| C3 | `do_termin` pe formularul de cautare se apeleaza **dupa** ce AMESSAGEBOX-ul a returnat, niciodata din interiorul unui dialog modal copil (`do_termin` face `this.Release`) | `_frm_base.vc2:363-371` | +| H2 | Eroarea de SELECT din `ANAF_CautaPereche` nu mai are voie sa apara ca "Nu exista partener cu CUI ... (il puteti crea cu butonul Nou)" โ€” asta indruma activ spre al treilea duplicat. `ListaCorecti` primeste un parametru de eroare; la eroare mesajul devine "Nu s-a putut citi lista de parteneri" si sugestia "Nou" dispare. Cursorul se inchide si pe ramura de eroare | `ocautare.prg:154-157`, `:389-405`, `:423`, `:455` | +| H6 | Cascada se citeste **o singura data**, la intrarea in `CautPartenerContabilitate`/`caut_parteneri`, cu `lcSel = Select()` / `Select(m.lcSel)` in jur. Niciodata din `Activeaza`, `VerificaRand` sau dintr-un handler de UI. `citeste_optiune_utilizator` **nu restaureaza** zona de lucru, iar `scrie_optiune_utilizator` da AMESSAGEBOX la eroare si face `TABLEUPDATE` in afara ramurii de succes | `oinit_optiuni.prg:731-740`, `:770-778` | +| H7 | Cascada testeaza `!Empty(Alltrim(valoare))` pe fiecare nivel. Patternul existent `Int(Val(Nvl(citeste_optiune_utilizator(...), '0')))` mapeaza absent -> 0 = OPRIT si ar opri verificarea la primul nivel gol | `Clase\ovanzcump.vc2` (pattern), `oinit_optiuni.prg:730`, `:808` | +| M9 | Citirea `getini` iese de pe calea per-rand: azi `ANAF_StarePartener` o face la fiecare verificare, cu `FOPEN`/scan/`FCLOSE` pe `DIRGEN\settings.ini`, care e de regula pe share de retea. Kill-switch-ul se citeste odata cu cascada | `ocautare.prg:59`, `ini.prg:288`, `roacont.prg:441` | +| M2 | Se foloseste `This.oForm.crs_cursor`, nu `_GRID1.RecordSource`: `SAVE_GRID` goleste `RecordSource` pe toata durata repopularii, care include un round-trip Oracle | `oproceduri_comune.prg:1387`, `ocautare.prg:303`, `:414`, `:443` | +| M5 | Valorile de cascada cache-uite pe sesiune se invalideaza la schimbarea firmei sau a utilizatorului (ca si cache-ul ANAF) | โ€” | +| A2 | Guard suplimentar `'OINIT_OPTIUNI' $ Upper(Set('Procedure'))` inainte de orice apel la cascada: `ROACASA` incarca `ocautare` fara `oinit_optiuni` | `ocautare.prg:63` (modelul existent) | +| B1 | Bug in codul de azi: `VerificaRand` iese pe `Reccount = 0` fara sa goleasca labelul, deci verdictul precedent ramane pe ecran atribuit unei liste goale | `ocautare.prg:304-306` | +| B2 | Spec vs implementare: `SeteazaLabel` forteaza `FontBold = .F.`, desi REVIZIA 2/3 cere discordanta in rosu **bold**. Se aliniaza (bold la discordanta) | `ocautare.prg:353` | +| B3 | `Anchor` al labelului devine 14 (Left+Bottom+Right) โ€” cu 6 textul se taie la redimensionare; labelul urca deasupra `cmd_select` (invizibil pe fluxul de partener), fara sa mai creasca inaltimea formei | `cauta_alfa_forms.vc2:47`, `:269-279`, `ocautare.prg:245-257` | +| B4 | Denumirea ANAF se trunchiaza la ~24 caractere in label (cea completa apare in Detalii): textul discordant la lungime maxima depaseste 2 randuri de Arial 10 in ~487x30px si se taie fara elipsa | `ocautare.prg:367` | +| B5 | `SET STEP ON` viu la `validare.prg:1841` (preexistent, nu din aceasta lucrare) โ€” in IDE deschide debuggerul. De scos | `validare.prg:1841` | + +Wrapperul batch ANAF **nu se scrie** in aceasta transa: fara marcaj in grid nu are +consumator (YAGNI). + +### 6. Ce se pastreaza neschimbat + +Textele si culorile labelului (REVIZIA 3 pt. 1, cu corectia B2), starea la data +documentului (REVIZIA 3 pt. 3), `dDataDoc`/`tdDataDoc`, `NormalizeazaCUI`, +`ANAF_VerificaCuiSingle`, cache-ul pe sesiune + golirea la schimbarea firmei, +excluderile (PF/extern/nenumeric), degradarea tacuta, `ANAF_CautaPereche`, +`AplicaInlocuirePartenerANAF`, fixul de import (REVIZIA 3 pt. 6), regula "fara +comentarii in corpul codului" (REVIZIA 3 pt. 5), hook-ul generic din `cauta_alfa.prg` +si cele 8 linii din `cauta_alfa_forms.vc2`. + +### 7. Teste (toate fezabile in harnessul headless) + +1. Cascada: ini `0` bate tot; utilizator bate firma; firma bate hardcodatul; + **absent != 0** (H7); lipsa `crsOptiuni`/`crsOptiuniUtilizator`/`goExecutor`. +2. Nivel 1 (implicit): label prezent; `VerificaAlegere` nu deschide nimic si **nu + face niciun apel HTTP** la alegere (contorul de apeluri exista deja in suita). +3. Nivel 0: `poVerifAlegere` neinstantiat, zero apeluri, label absent. +4. Nivel 3: dialogul existent, cele 3 rezultate โ€” testele actuale, rulate cu + optiunea pe 3. +5. **C2 (testul cel mai important, lipsa azi):** `poANAFInlocuire` setat + `gnButon = 2` + (Renunta) => obiectul returnat ramane blank, `id_part = 0`. +6. H2: `ANAF_CautaPereche` esuat (mock pe `goExecutor`) => mesaj de eroare, fara + sugestia "Nou". +7. Detalii cu `oStare` `.Null.` => se deschide, cu linia explicativa. +8. Discriminatorul "are documente in perioada" (mock pe `goExecutor`, cu si fara randuri). +9. Bug-ul B1: cautare fara rezultate => labelul se goleste. +10. Zona de lucru: `Alias()` identic inainte si dupa citirea/scrierea optiunilor (H6). +11. Punctul de meniu scrie valoarea corecta si are efect imediat (`M6`: la trecerea + pe 0 se opreste si timerul si se goleste labelul pe cautarea deschisa). +12. Regresie: apelantii care NU au `lVerificaANAF=>1` nu instantiaza `poVerifAlegere`. + +### 8. Transa viitoare โ€” marcaj in grid (nivelul 2), cu conditii de intrare + +Nu se implementeaza pana nu exista, in aceasta ordine: +1. **Masuratori** (Tema din planul initial, inca nefacuta): latenta reala a unui apel + ANAF la ore diferite; comportamentul la rafale (30 loturi de 20 CUI la interval + scurt โ€” de la al catelea vine 429); numarul real de perechi de CUI duplicat la un + client real. +2. **Date din loguri**: cate discordante apar efectiv la pilot si in ce flux. + +Cerinte tehnice de respectat atunci (toate verificate acum, ca sa nu se piarda): + +| # | Cerinta | +|---|---| +| UC1 | Criteriul de colorare corect pe trezorerie e "are documente in perioada" (SELECT local), nu statutul ANAF: pe o incasare, verdele dupa ANAF indruma spre partenerul fara facturi | +| D-F2 | `DynamicForeColor` e **invizibil pe randul selectat** (`_baza.vc2:200-201`: highlight bleumarin cu text alb). Semnalul are nevoie de al doilea canal: `DynamicFontBold`, care supravietuieste highlight-ului. Verde+bold pentru randul confirmat, restul normal; rosul dispare de pe randuri (are 3 sensuri distincte: prefix gresit, radiat la ANAF, CIF invalid) | +| C4 | Un singur `anaf_timer` cu doua consumatoare = nedeterminism: `do_cauta` face `Go Top` + `SetFocus` dupa `actualizeaza_grid1`, deci `AfterRowColChange` rearmeaza acelasi timer. Necesar: doua flag-uri (sau doua timere) + flag de suprimare `lInBatch`, `GO` pe `Recno()` salvat, colectarea CUI-urilor fara `SELECT-SQL` pe cursorul legat de grid | +| H1 | CUI-urile din `notfound` sunt inserate ca randuri blank (`scpTVA = .F.`, denumire goala) โ€” fara filtru `!Empty(denumire)` orice partener cu prefix RO negasit la ANAF ar aparea marcat gresit | `validare.prg:1984-1996` | +| 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 `` 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") | +| 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** |