# Verificare adversariala — proiectarea parametrului de cont (`canal_cont_venit_fara_politica.md`, sectiunea "Proiectarea parametrului de cont contabil") Mandat: **nu re-proiecta** — proiectarea exista deja in `canal_cont_venit_fara_politica.md:13-227` (citita integral). Aici: incercare de a o sparge + inchiderea celor 4 goluri semnalate de autor. Sursa PL/SQL: aceeasi, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17217 linii). Oracle: doar `SELECT`, schema `MARIUSM_AUTO`/`ROA_CENTRAL`. VFP: doar `vfp_symbols.ps1` (index, read-only). **Niciun fisier de cod atins.** ## Verdict, in patru randuri **Proiectarea rezista la verificarea adversariala** — nu am gasit nicio eroare care sa-i invalideze concluzia centrala. Am gasit **un rand din tabel mai slab decat descris** (`CU_TVA=1` hardcodat NU e complet inofensiv — are un efect colateral masurabil, minor, prin `nproc_tva_max`), **un rand mai tare decat descris** (`IN_VALUTA` din `nin_valuta` e o sursa solida, nu doar plauzibila — parametru obligatoriu, fara `DEFAULT`), **o excludere confirmata corecta prin structura schemei** (articolul "compus" nu poate exista pe o linie fara politica — nu o gaura), si **un gol raportat de autor acum inchis cu fapt** (cele doua clase de la `:14069`/`:18089` sunt distincte, nu o duplicare a aceleiasi metode — 2 locuri VFP de atins, nu 1). Pe decizia 35 (regenerare = acelasi cod): **DA, calea Oracle e identica**, dar exista un obstacol real, deja proiectat separat (`idfact_refolosire_si_documente.md`), **in alt pachet** (`PACK_CONTAFIN`), nu in `pack_facturare` — nu invalideaza design-ul curent, dar regenerarea nu e completa fara acea a doua bucata de lucru. --- ## 1. Decizia 35 — regenerarea foloseste identic `pack_facturare`? **DA pe calea Oracle relevanta pentru parametrul de cont, cu o exceptie deja cunoscuta si separata.** Verificat direct: `scrie_factura2` reseteaza starea de sesiune la fiecare apel prin `initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`), care face `DELETE FROM VANZARI_DETALII_TEMP;` (`:1835`) si `pack_facturare.nid_act := 0;` (`:1836`) — **contorul folosit de `scrie_nota`/`scrie_discount`/`scrie_tva` la fiecare `INSERT INTO ACT_TEMP` porneste curat la fiecare emitere, inclusiv la o a doua** (regenerare). Nimic in `contabilizeaza_articol`, ramura noua inclusa, citeste vreo stare care ar presupune "documentul e nou" — toate variabilele folosite (`nid_venchelt`, `nid_sectie_stoc`, `nin_valuta`, `nid_set`, `nid_util`) sunt **citite**, nu verificate contra unei stari anterioare. **Obstacolul real e in `PACK_CONTAFIN`, nu in `pack_facturare`**: reemiterea cu acelasi `ID_FACT` (ca sa nu se dubleze documentul contabil la regenerare) ar da azi `ORA-00001` pe `PK_DOCUMENTE`, pentru ca `SET_IDFACT` ia mereu `SEQ_IdFact.NEXTVAL` si documentul vechi ramane in tabel doar soft-sters (`STERS=1`), nu disparut — analiza completa, cu solutia (variabila de pachet noua in `PACK_CONTAFIN`, `MERGE ... WHEN MATCHED` pe `DOCUMENTE`), e deja facuta integral in `idfact_refolosire_si_documente.md` (sectiunile A-C). **E exact "ceva care ar cere cod separat" — dar separat inseamna alt pachet/alta procedura (`SET_IDFACT`, scrierea in `DOCUMENTE`), nu o ramura in plus in `contabilizeaza_articol` sau `adauga_articol_factura`.** Ramane o bucata de lucru distincta, necesara pentru ca regenerarea sa fie completa, dar nu intersecteaza si nu invalideaza design-ul parametrului de cont. **Un gol neadresat de design, gasit acum**: ramura `pack_facturare.ntip = 4` ("facturare din avize", `:7520-7537`) apeleaza `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`+discount — design-ul nu spune ce se intampla pe aceasta ramura in cazul fallback. In practica, **probabil nu conteaza**: "facturare din aviz" cere azi ca linia sursa sa aiba deja `id_pol` (`adauga_articol_factura:5096`, `AND A.ID_POL = V_ID_POL` pe `VANZARI_DETALII` a avizului sursa) — un aviz fara politica n-ar fi ajuns niciodata pana la factura pe acest drum, deci scenariul "fallback pe `ntip=4`" e putin probabil sa apara. **Dar design-ul nu spune asta explicit** — de adaugat o linie care sa acopere/exclude explicit acest caz inainte de implementare, nu de presupus tacit. ## 2. Cele 4 goluri semnalate de autor ### 2a. `ofacturare.vc2:14069`/`:18089` — doua metode sau o duplicare? `vfp_symbols.ps1 -Where 'ofacturare.vc2:14069'` -> **`frm_facturare_articole.do_scrie_articole`** (`ofacturare.vc2:13967-14195`). `-Where 'ofacturare.vc2:18089'` -> **`frm_facturare_articole2.do_scrie_articole`** (`ofacturare.vc2:18003-18221`). **Confirmat: doua clase distincte, `frm_facturare_articole` si `frm_facturare_articole2`, fiecare cu propria metoda `do_scrie_articole`** (acelasi nume de metoda, nu aceeasi clasa) — nu o duplicare literala a unui singur cod. **Consecinta pentru implementare**: parametrul nou trebuie cablat **in doua locuri VFP**, nu unul — ambele clase construiesc separat apelul RPC catre `adauga_articol_factura`. Nu schimba verdictul de fezabilitate, dar schimba suprafata de lucru VFP fata de impresia "un singur loc de atins". ### 2b. `scrie_tva` la cota 0% cu `CU_TVA` hardcodat `1` — efect real, nu inofensiv Verificat corpul `scrie_nota` (`:12537-12558`): actualizarea `nproc_tva_max`/`nid_jtva_coloana`/ `nTaxCode` **nu e neconditionata** — e in interiorul `IF V_CU_TVA = 1 THEN`, deci exact conditionata de flagul pe care design-ul propune sa-l hardcodeze. In interior insa, comparatia `IF pack_facturare.nproc_tva_max < V_PTVA THEN` **nu se uita la suma**, doar la rata — deci daca articolul fallback are `proc_tvav` real 0% (scutit) si `CU_TVA` e fortat la `1`, rata 0% **intra in comparatia de maxim** si, daca e prima/singura linie a documentului (`nproc_tva_max` initial `-1`, `:1885`), **castiga** — seteaza `nid_jtva_coloana`/`nTaxCode` la valorile acelei linii scutite. Aceste doua variabile sunt consumate mai departe **in aceeasi procedura `scrie_factura2`**, la discountul global pe factura (`:6164-6184`, `V_DISCOUNT_FACTURA <> 0`): rata/coloana/taxcode ale liniei scutite ar ajunge sa descrie linia de discount a **intregii facturi**, chiar daca alte linii au TVA real. **Verdict: `CU_TVA=1` hardcodat nu e "inofensiv" in toate cazurile cum spune design-ul — are un efect colateral real, dar restrans la o combinatie specifica (linie fallback cu TVA 0% + discount global pe factura + acea linie e cea cu rata "maxima" vazuta pana atunci).** Nu invalideaza alegerea `CU_TVA=1` ca implicit (majoritatea liniilor reale au TVA nenul), dar intareste, nu slabeste, cererea deja facuta de autor ("de confirmat cu Marius") — motivul de confirmat e mai concret decat "linie de TVA cu suma 0, inofensiv". ### 2c. `INSERT INTO VANZARI_DETALII ... FROM VANZARI_DETALII_TEMP` — linie exacta, confirmata `PACK_FACTURARE:13705-13757`, in `scrie_in_vanzari` (spec `:13488`, apelata din `finalizeaza_factura` `:14787`, care e apelata la finalul fluxului de verificare — nu din `scrie_factura2`, alta faza a aceluiasi pipeline). **Confirmat: lista de coloane e explicita**, 24 coloane (`ID_VANZARE, ID_ARTICOL, LOT, SERIE, ID_RATA, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, PRET, PROC_TVAV, ID_JTVA_COLOANA, ID_JTVA_COLOANA_EX, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, EXPLICATIE, PRET_CU_TVA, DIFERENTA, CUSTODIE, ID_VANZARE_SET, ID_CTR, TAXCODE`) — **nu** `SELECT *`. Exact ce presupunea design-ul (sectiunea 2, citand `nota_contabila_fara_politica.md` fara re-verificare): daca se doreste ca `CONT_VENIT` sa ajunga si in `VANZARI_DETALII` (trasabilitate), **aceasta lista trebuie extinsa explicit** — fara acest pas, coloana noua ramane doar pe `VANZARI_DETALII_TEMP` si `ACT_TEMP`, invizibila in `VANZARI_DETALII` dupa fapt. Confirmarea nu schimba verdictul design-ului (deja marcase pasul ca "recomandat, nu strict necesar"), doar il transforma din presupunere in fapt verificat. ### 2d. Apelanti `adauga_articol_factura` din restul suitei Sarit, cum a cerut team-lead-ul — alt agent lucreaza pe suprafata de regresie. --- ## 3. Verificare adversariala pe tabelul din design (sectiunea 3 a raportului sursa) | Rand din tabel | Incercare de infirmare | Rezultat | |---|---|---| | `IN_VALUTA` <- `pack_facturare.nin_valuta` | E setat pe toate fluxurile relevante, sau ramane nul si azi nu conteaza? | **Mai solid decat descris.** `nin_valuta := V_IN_VALUTA` (`:1902`) e in `initializeaza_date_factura`, apelata **o data la inceputul fiecarei emiteri**, cu `V_IN_VALUTA IN NUMBER` **fara `DEFAULT`** in semnatura (`:1827`) — VFP e obligat sa trimita o valoare, nu poate omite parametrul. Nu e un fallback de sesiune care "poate ramane nesetat" (ca `nid_venchelt`), e un flag de document obligatoriu, mereu curent. | | `ID_VENCHELT`/`ID_SECTIE` <- variabile de sesiune | Cine le seteaza si cand? | **Confirmat, cu nuanta.** `nid_venchelt := V_ID_VENCHELT` si `nid_sectie_stoc := V_ID_SECTIE` (`:1876-1877`), tot in `initializeaza_date_factura`, din parametri **fara `DEFAULT`** insa **VFP poate trimite `NULL`** pe ei (sunt opționale ca *valoare*, nu ca prezenta in apel) — deci pot ramane `NULL` pe tot documentul. **Nu e o regresie**: pe ramura veche, `NVL(nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))` ar da tot `NULL` daca nici politica nu are aceste campuri — acelasi rezultat posibil, aceeasi cauza (sesiune nesetata), nu unul nou introdus de fallback. | | Garda `descarca_gestiune` copiata identic | Acopera `id_gestiune=-1000` si `in_stoc`? | **Da, fara diferenta.** Garda foloseste `detalii_articol.id_gestiune`/`.in_stoc` — campuri populate identic de `adauga_articol_factura` indiferent de ramura (nu depind de politica azi, nici in design). Copierea literala a conditiei (`:7473-7475`) e corecta prin constructie. | | `ASCD`/`ASCC` <- `GetAnaliticByGrupUtilizatori` | Ce intoarce pe `NO_DATA_FOUND`, e acceptabil? | **Confirmat `NULL`** (corp citit `:16704-16723`: `lcAcont` declarat fara valoare implicita, `EXCEPTION WHEN NO_DATA_FOUND THEN NULL;`, `RETURN lcAcont`). Acceptabil — e exact fallback-ul deja folosit azi, necondiționat, pe ramurile de aviz (`:7416-7417,7421-7422`) si `ACT_TEMP.ASCD/ASCC` sunt nullable (confirmat DDL, `canal_cont_venit_fara_politica.md` DDL section). Nu e un risc nou. | | Articol "compus" (`V_COMPUS=1`) lasat in afara scopului | Poate un articol ales ad-hoc din nomenclator fi compus? | **NU — exclus prin structura schemei, nu prin presupunere.** Interogat direct `ALL_VIEWS.VCRM_POLITICI_PRET_ART`: coloana `COMPUS` citita de `contabilizeaza_articol` (`SELECT COMPUS, ID_POL_ART ... FROM VCRM_POLITICI_PRET_ART`, `:7279-7283`) e definita in view ca `CASE WHEN PA.ID_POL_ART IN (SELECT DISTINCT ID_PACHET FROM CRM_PACHETE_ARTICOLE WHERE STERS=0) THEN 1 ELSE 0 END` — o proprietate a **perechii (articol, politica)**, identificata prin `ID_POL_ART` (cheia surogat a randului din `CRM_POLITICI_PRET_ART`). O linie fara politica **nu are** un `ID_POL_ART` — deci intrebarea "e articolul compus" nu se poate pune structural pe aceasta ramura. (`NOM_ARTICOLE.COMPUS` exista ca alta coloana, aliasata `art_compus` in acelasi view, dar **nu e citita de `contabilizeaza_articol`** — irelevanta aici.) Excluderea din design e corecta, nu o gaura. | | `RETURN V_INCASAT_CALCUL` | Se calculeaza corect pe ramura noua sau intoarce 0? | **Corect, prin constructie.** Design-ul specifica explicit aceeasi acumulare ca azi (`V_INCASAT_CALCUL := V_INCASAT_CALCUL + scrie_nota(...)`, apoi `- scrie_discount(...)`), cu `V_INCASAT_CALCUL` initializat `0` la declaratie (`:7178`), inainte de `IF`-ul care selecteaza ramura — identic cu azi. Niciun risc de `RETURN 0` gasit. | --- ## 4. Hardcodarile `SCD='4111'` / `CU_TVA=1` — exista o sursa mai buna? Cautare directa in tot `PACK_FACTURARE` pentru orice sursa alternativa care nu trece prin `NOTE_CONTABILE`: config de firma (`getoptiunefirma('CONT...')`), cont implicit pe partener/client (`PARTENERI.CONT*`), flag de scutire TVA pe articol/client (`SCUTIT`, `EXCEPTAT`) — **zero rezultate pentru toate cele trei cautari**. Singurul camp inrudit gasit, `ACT_TEMP.NEIMPOZAB`, e folosit in alt scop (raportare sume neimpozabile), nu ca sursa pentru `CU_TVA`. **Concluzie: nu exista o sursa mai buna in cod — hardcodarea (sau parametrul explicit trimis de VFP) ramane singura optiune.** Asta intareste recomandarea deja facuta de autor: **de decis explicit cu Marius, nu de dedus din date**, mai ales pentru `CU_TVA` dupa gasirea de la punctul 2b (efectul via `nproc_tva_max` nu mai e "pur cosmetic"). --- ## 5. Completare: `goExecutor.oExecuta` si cursorul de verificare — fals pozitiv, nu bug Verdict: **fals pozitiv, confirmat cu argumente, nu doar presupus.** `oExecuta` (funcție-wrapper, `COMUN\programe\oproceduri_comune.prg:121-159`) deleagă la `oExecute` (`:173-504`), care la rândul ei face `SQLExec(lnHandle, lcSql, lcCursor)` (`:330`) — **`lcSql` e folosit exact cum a fost construit de apelant, fără nicio rescriere care să adauge un bind lipsă.** Textul construit în `COMUN\clase\ofacturare.vc2:14345-14359` (și identic la `:14373-14387`, `:18343-18371`) e sintaxă ODBC escape `{call pack_facturare.scrie_factura2(...)}`, cu **16** argumente poziționale (15 `IN` + `?@poDate.nid_vanzare` pentru `V_ID_VANZARE OUT NUMBER`, al 16-lea parametru declarat), paranteza se închide imediat după — **al 17-lea parametru, `V_CURSOR_VERIFICARE OUT pack_facturare.cursor_facturare` (spec `PACK_FACTURARE:656`), nu are niciun placeholder în text, nici legat, nici nelegat.** Asta e **exact tiparul cunoscut al driverului ODBC Oracle pe care VFP îl folosește prin `SQLExec`**: cand ultimul parametru declarat al unei proceduri e un `REF CURSOR OUT`, driverul îl detectează din catalogul Oracle (nu din textul apelului) și întoarce automat rândurile lui ca *result set* al apelului — **al treilea argument al `SQLExec`/`oExecute` (numele de cursor VFP, aici `lcCursorVerificare`) e exact mecanismul de captare a acelui result set**, fără sa fie nevoie de bind explicit. Codul imediat după apel (`ofacturare.vc2:14394-14397`, `llReturn = goExecutor.oExecuta(lcSql,lcCursorVerificare)` urmat de `If Reccount(lcCursorVerificare)>0`) tratează cursorul ca fiind deja populat cu rânduri — comportament incompatibil cu un apel eșuat sau cu un parametru nelegat (care ar da eroare Oracle, nu un cursor gol interpretabil). **Nu pot confirma mecanismul din interiorul driverului însuși** (e extern codebase-ului, verificabil doar prin comportament) — dar dovada indirectă e puternică: același tipar apare identic în toate cele 4 locuri de apel găsite, neschimbat de-a lungul mai multor versiuni (`v 2.0.13` -> `v 2.0.93`, comentarii de istoric vizibile în cod), iar ecranul de verificare (funcție activă, folosită la fiecare emitere) depinde de acest cursor populat — dacă apelul ar eșua silențios, ecranul de verificare n-ar arăta niciodată note propuse, ceea ce ar fi fost observat imediat. **Concluzie pentru plan: anomalia nu există — nu trebuie tratată ca risc pe cele 7 produse.** ## Ce ramane neverificat - Comportamentul exact al ramurii `ntip=4`/`scrie_fact_aviz_custodie` in scenariul fallback (punctul 1) — argumentat ca improbabil, nu testat/exclus explicit in design. - Daca vreun raport Oracle activ presupune `ACT_TEMP.ID_VENCHELT`/`ID_SECTIE` populate pe liniile de venit (relevant doar daca sesiunea nu seteaza `nid_venchelt`/`nid_sectie_stoc`) — in afara scopului acestei verificari (cod PL/SQL only). - Suprafata de regresie la nivelul apelantilor `adauga_articol_factura` din restul suitei — explicit lasata altui agent.