# Proiectare — Decizia 42: articole factura vizibile, needitabile dupa trimiterea in eFactura Scris 10.08.2026. Agent read-only, doar cercetare/proiectare — nicio modificare de cod facuta de acest agent. ## ATENTIE — implementarea era deja IN LUCRU, de catre alt agent; bugul de mai jos e DEJA CORECTAT Inainte de a ajunge la proiectare: la momentul cercetarii, `COMUN\clase\omodificari.vc2` avea deja **modificari necomise in working tree** (`git status` in `COMUN`: `M clase/omodificari.vc2`) care implementeaza aproape exact Decizia 42 corectata (vizibil, doar needitabil). Agentul `d42-efactura`, activ in aceeasi sesiune, lucra chiar atunci pe exact acest subiect. **Acest raport nu descrie deci o proiectare de la zero, ci: (1) verificarea contractelor si (2) un review al diff-ului in lucru, cu un bug real gasit si dovedit pe schema Oracle — vezi §1 si §8.** **UPDATE, dupa trimiterea raportului**: `d42-efactura` a gasit acelasi bug independent, in timpul propriei lucrari (nu din briefingul primit de la team-lead), l-a corectat si scris in binar **inainte** sa primeasca mesajul meu. **Verificat direct pe disc de acest agent** (nu doar preluat din raportarea lui `d42-efactura`): `git diff` pe `COMUN\clase\omodificari.vc2` arata `This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact,0))`, iar `git diff` pe `COMUN\programe\ofacturare_editare.prg` arata `v.id_fact` adaugat in SELECT-ul din `IncarcaVanzareNota` si `id_fact I NULL` adaugat in schema `CREATE CURSOR tvanz` din `CreeazaCursorTvanzGol` — exact varianta B recomandata la §8. **Bugul e inchis, nu mai necesita actiune.** Legat de linia necomisa gasita atunci in `ROAGEST\Programe\roagest.prg` (`SET PROCEDURE TO ofacturare_editare.prg ADDITIVE`, "Not Committed Yet" la 10.08.2026 11:28): `d42-efactura` confirma ca nu e a lui — a atins doar `omodificari.vc2` si `ofacturare_editare.prg` (plus fisiere de test noi). Ramane deci dintr-un alt bloc de lucru (posibil S9), de verificat separat de team-lead — nu descrie starea lui `d42-efactura`. --- ## 1. Cum se afla ca documentul e in eFactura **Functie**: `FUNCTION EsteInEFactura`, globala (nu metoda de clasa), definita in `COMUN\programe\ofacturare_editare.prg:16-25`: ``` *!* parametru: id_fact *!* verifica daca factura cu id_fact dat a fost deja trimisa in eFactura (anaf_efactura) FUNCTION EsteInEFactura LPARAMETERS tnIdFact LOCAL lcSql, lnEFactura, llSucces lnEFactura = 0 lcSql = [select count(*) as lneFactura from anaf_efactura where id_fact = ] + Alltrim(Str(Nvl(m.tnIdFact,0))) llSucces = goExecutor.oSelecteaza2Value(m.lcSql, @lnEFactura) RETURN (Nvl(m.lnEFactura,0) > 0) ENDFUNC ``` **Contract, deschis din `RETURN`**: primeste `tnIdFact` — **`ID_FACT`, nu `ID_VANZARE`** — si intoarce `.T./.F.` (numar de randuri in `ANAF_EFACTURA` cu acel `id_fact` > 0). Foloseste `goExecutor` (disponibil global, aceeasi conventie ca restul aplicatiei). **Disponibilitate cross-project — VERIFICAT, nu presupus**: `ofacturare_editare.prg` e inregistrat prin `SET PROCEDURE ... ADDITIVE` in toate cele trei aplicatii: | App | Fisier | Linie | Stare git | |---|---|---|---| | ROAFACTURARE | `Programe\roafacturare.prg` | 214 | comis demult | | ROACONT | `Programe\roacont.prg` | 212 | comis, `3af0089` (08.08.2026) — mesaj: *"Fara el, pagina de articole ale facturii nu apare in registrul jurnal > modificare"* | | ROAGEST | `Programe\roagest.prg` | 260 | **necomis**, adaugat azi 10.08.2026 (vezi avertismentul de mai sus) | Deci `EsteInEFactura` **este** apelabila din contextul `omodificari.vc2`, inclusiv din ROACONT si (dupa commit-ul in curs) ROAGEST. Comentariul din `omodificari.vc2:14786-14787` ("apare doar cand ofacturare_editare.prg e incarcat (doar ROAFACTURARE - ROACONT/ROAGEST nu-l inregistreaza)") e **invechit** — scris inainte de commit-ul `3af0089`, care a inversat exact aceasta premisa pentru ROACONT. Nu se corecteaza comentariul in acest raport (read-only), dar oricine implementeaza ar trebui sa-l actualizeze cand atinge zona. **BUG GASIT in diff-ul in lucru — `id_fact` confundat cu `id_vanzare`.** Apelul din `omodificari.vc2:14798` (diff necomis) e: ``` This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) ``` dar `This.nIdVanzare` e populat cu `tvanz.id_vanzare` (linia 14796), adica `VANZARI.ID_VANZARE` — **alt camp** decat `VANZARI.ID_FACT`, pe care `EsteInEFactura` il asteapta. Dovada, in trei straturi: 1. **Cod**: precedentul functional existent, `ofacturare_comun.vc2:3739` (`lnIdFact = id_fact`) si `:3764` (`EsteInEFactura(lnIdFact)`), citeste `id_fact` dintr-un camp separat de `id_vanzare` (`:3734`, `lnIdVanzare = id_vanzare`, aceeasi cursor `crsfacturi`, doua LOCAL-uri distincte). Acelasi tipar in `anaf_efactura.prg:3123-3124`: `pnIdFact = id_fact` si `lnIdVanzare = id_vanzare` citite **pe acelasi rand** din `crsFacturiEmise` — daca ar fi acelasi numar, codul n-ar avea nevoie de doua variabile. 2. **Schema Oracle** (verificat read-only, `user_tab_columns`, `MARIUSM_AUTO`): `VANZARI` are **ambele** coloane, `ID_FACT` si `ID_VANZARE`, distincte; `ANAF_EFACTURA` are doar `ID_FACT`. 3. **Date reale** (verificat read-only, join `anaf_efactura.id_fact = vanzari.id_fact`): pentru documentele deja trimise in eFactura, cele doua valori difera constant — | id_fact | id_vanzare | cod | numar_act | data_act | |---|---|---|---|---| | 8008013 | 1013 | 1140509 | 510 | 31-AUG-25 | | 8007922 | 1007 | 1140439 | 503 | 03-JAN-25 | | 8007836 | 993 | 1140380 | 490 | 15-AUG-24 | | 8007810 | 991 | 1140369 | 488 | 11-JUL-24 | Cu bug-ul curent, `EsteInEFactura(1013)` cauta `id_fact = 1013` in `ANAF_EFACTURA` — care nu exista cu acel numar — deci **intoarce mereu `.F.`**, chiar si pentru documente reale trimise in eFactura. Garda ar fi complet inoperanta pe date reale. Corectia: §8. Niciun document dintre cele patru de mai sus nu e din luna curenta (limitare de date deja cunoscuta din bloc — garda de editare cere luna curenta), deci un test headless pe un caz real din productie de date nu se poate face acum; testul trebuie sa foloseasca un cursor `tact`/`tvanz` mock cu `id_fact` setat manual (acelasi tipar folosit deja pentru `id_vanzare_set` in S5). ## 2. Unde se aseaza conditia in omodificari.vc2 Locul folosit deja de diff-ul in lucru e corect si e cel recomandat: `frm_modific2024.Show()` (`omodificari.vc2:14748-14837` in varianta comisa `1c42ae0`; extins la 14748-14856 in diff-ul necomis), imediat dupa blocul care rezolva `This.nIdVanzare`/`This.nTipVanzare` prin `IncarcaVanzareDinNota('tact')` si inainte de `This.pgfArticole.PageCount = 3`. E punctul unde formularul stie deja daca documentul curent are rand in `VANZARI` (`This.lAreArticoleVanzari`) — conditia eFactura se agata direct pe acest rezultat, fara sa duplice logica de potrivire cod/nract/serie_act/dataact -> id_vanzare. Proprietatea noua, `lArticoleReadOnly` (declarata la nivelul clasei, langa `lAreArticoleVanzari`, `omodificari.vc2:6827` si `6867` in diff), e citita apoi din `.When`-urile coloanelor editabile ale gridului `grdArticoleFactura` (mecanismul de editabilitate per rand din S5, §3-4) — deci conditia de formular **compune** cu mecanismul existent, nu-l inlocuieste. ## 3. Controale care se dezactiveaza — confirmate pe cod | Control | Path | Linii (comis) | Ce face diff-ul | |---|---|---|---| | Buton adaugare | `pgfArticole.PAGE3.cmdAdaugaArticol` | Click: `16488-16529` | `.Enabled = !This.lArticoleReadOnly` la afisare (`Show`); plus guard `OR Thisform.lArticoleReadOnly` in `Click` (aparare in adancime, cazul `Enabled` sarit programatic) | | Buton stergere | `pgfArticole.PAGE3.cmdStergeArticol` | Click: `16531-16540` | idem | | Cantitate | `grdArticoleFactura.cCantitateArt.Text1.When` | `16549-16554` | adauga `Thisform.lArticoleReadOnly OR` in fata conditiei existente `Nvl(tvd.id_vanzare_set,0)<>0` | | Pret achizitie | `grdArticoleFactura.cPretAchizitieArt.Text1.When` | `16556-16561` | idem | | Pret | `grdArticoleFactura.cPretArt.Text1.When` | `16570-16575` | idem | | Pret cu TVA (checkbox) | `grdArticoleFactura.cPretCuTvaArt._checkbox1.When` | `16587-16589` | `RETURN !Thisform.lArticoleReadOnly AND ...` | | Discount | `pgfArticole.PAGE3.txtDiscountArt` | — | **neatins in diff** — vezi nota de mai jos | **Gol observat**: `txtDiscountArt` (discountul pe factura, cu `ControlSource = tvanz.discount`) nu are `.When` si nu apare in diff. Daca discountul e considerat parte din "articolele facturii" in sensul Deciziei 42 (afecteaza totalul liniilor), ramane editabil dupa trimiterea in eFactura — de clarificat cu Marius sau de inclus explicit. Nu era in lista `cmdAdaugaArticol`/`cmdStergeArticol` ceruta explicit in brief, deci il semnalez ca gol, nu-l tratez ca bug. ## 4. Tipar read-only pe grid, folosit deja in proiect Nu se inventeaza un tipar nou. Proiectul (din S5) foloseste deja exact acest idiom pentru randurile needitabile (liniile din seturi, `id_vanzare_set <> 0`): fiecare coloana editabila a gridului are un handler `.When` care intoarce `.F.` cand conditia de blocare e adevarata — VFP nu lasa controlul sa intre in editare daca `.When` intoarce `.F.`. Diff-ul in lucru extinde exact aceste `.When`-uri existente, adaugand `Thisform.lArticoleReadOnly OR` in fata conditiei deja acolo — e continuarea directa a mecanismului S5, nu un tipar paralel. Confirma cererea din brief: "foloseste tiparul deja folosit in proiect, nu inventa unul nou" — respectat. ## 5. Feedback vizual pentru utilizator Diff-ul adauga o eticheta noua, `pgfArticole.PAGE3.lblArticoleReadOnly` (`omodificari.vc2:12857-12871` in diff), cu `Caption = "Articole needitabile - factura trimisa in eFactura"`, `Visible` legat de `This.lArticoleReadOnly` in `Show()`. E minimul cerut de brief — niciun tipar de marcaj standard read-only nu exista deja in proiect (nu s-a gasit unul in cautarea pentru §4), deci eticheta simpla e alegerea rezonabila. Singura observatie: eticheta e pozitionata `Top = 4, Left = 360` — de verificat pe ecran (fara VFP IDE nu se poate confirma vizual) ca nu se suprapune cu alt control din PAGE3 la latimile de forma folosite azi. ## 6. Suprafata de regresie — de ce notele obisnuite din registrul jurnal nu sunt atinse Blocul care calculeaza `lArticoleReadOnly` ruleaza **doar** in interiorul conditiei deja existente: ``` IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Reccount('tact') > 0 IncarcaVanzareDinNota('tact') IF Reccount('tvanz') = 1 This.lAreArticoleVanzari = .T. ... This.lArticoleReadOnly = EsteInEFactura(...) ENDIF ENDIF ``` `IncarcaVanzareDinNota` cauta in `VANZARI` un rand cu tripletul (cod, nract, serie_act, dataact) al notei curente. Pentru o nota contabila obisnuita (nu factura de vanzare), acest rand nu exista, `Reccount('tvanz')` ramane `0`, deci **intreg blocul `IF Reccount('tvanz') = 1` e sarit** — `This.lAreArticoleVanzari` ramane `.F.` si `This.lArticoleReadOnly` ramane la valoarea implicita `.F.` (setata explicit chiar inainte de bloc, `:14791`). Consecinta directa: `This.pgfArticole.PageCount = 2` (fara PAGE3), deci `cmdAdaugaArticol`/`cmdStergeArticol`/gridul de articole **nici nu exista** pentru documentul respectiv — nu doar ca nu sunt dezactivate, ci nu sunt instantiate in fluxul de afisare. Riscul de regresie pe notele obisnuite din ROAGEST/ROACONT e deci **zero prin constructie**, mostenit din gating-ul `lAreArticoleVanzari` deja livrat si testat in S4/S5 — Decizia 42 doar adauga o conditie suplimentara **in interiorul** ramurii deja izolate pentru facturi de vanzare, nu schimba gating-ul insusi. Acesta e exact argumentul cerut in brief la punctul 6, si e verificabil citind codul, nu presupus. `comun.vc2` (clasa `afisjurcom`, `do_modifica`, `2222-2572`) nu e atins de acest diff — ramane neschimbat, confirmand ca intreaga logica sta in `omodificari.vc2`, un singur punct de intretinere. ## 7. Teste minime propuse (headless, in stilul suitei existente) Suita `COMUN\utile\Teste\editare_factura\` foloseste harness-ul `ui_harness.prg` + `asserteaza`, cu formular vizibil (`WindowType = 0`, `Show()`, `DOEVENTS FORCE`), fara input real (nici un `keybd_event`/`SendInput` — doar citire directa de proprietati si apel direct de metode). Propunere, dupa modelul `test_ui_sterge_linie.prg`: **`test_d42_efactura_readonly.prg`** — cazul pozitiv, cu date mock (nu exista document real din luna curenta trimis in eFactura, cf. §1): 1. Incarca `tact`/`tvd` pentru un document real din luna curenta cu `lAreArticoleVanzari = .T.` (ex. acelasi `id_vanzare = 1049` mentionat in `handoff_punct6_dupa_s5.md`, daca inca in luna curenta la momentul rularii). 2. Dupa `IncarcaVanzareDinNota`, forteaza `tvanz.id_fact` (sau `tact.id_fact`, dupa care varianta se alege in §8) la o valoare stiuta ca exista in `ANAF_EFACTURA` (mock: `INSERT` intr-un cursor local, nu in Oracle) — sau, mai simplu, mock-uieste direct `EsteInEFactura` prin `SET PROCEDURE`/redefinire temporara, dupa tiparul deja folosit in proiect pentru izolarea de Oracle in teste UI. 3. `assert`: `loForm.lArticoleReadOnly = .T.` 4. `assert`: `loForm.pgfArticole.PAGE3.cmdAdaugaArticol.Enabled = .F.` 5. `assert`: `loForm.pgfArticole.PAGE3.cmdStergeArticol.Enabled = .F.` 6. `assert`: `loForm.pgfArticole.PAGE3.lblArticoleReadOnly.Visible = .T.` 7. `assert`: grid-ul e in continuare **vizibil** si populat — `loForm.pgfArticole.PageCount = 3`, `Reccount('tvd') > 0` — confirma partea corectata a deciziei (nu se suprima pagina). 8. `assert`: pozitionare pe `grdArticoleFactura`, `SetFocus` pe coloana `cCantitateArt` nu intra in editare — verificat prin apelul direct al handlerului `.When` (`loForm.pgfArticole.PAGE3. grdArticoleFactura.cCantitateArt.Text1.When()` trebuie sa intoarca `.F.`), nu prin tastare. **`test_d42_efactura_editabil.prg`** — cazul negativ (control): acelasi document, dar `EsteInEFactura` mock-uit sa intoarca `.F.` -> toate assert-urile de mai sus inversate (`Enabled = .T.`, `.When()` nu intoarce `.F.` din cauza flagului — poate intoarce `.F.` din alt motiv, ex. `id_vanzare_set`, testat separat). **`test_d42_nota_obisnuita_neatinsa.prg`** — cazul de regresie cerut la §6: incarca o nota contabila fara corespondent in `VANZARI` (orice test existent din `COMUN\utile\Teste\` care nu tine de facturare), verifica `loForm.pgfArticole.PageCount = 2` si ca `lArticoleReadOnly` ramane `.F.` fara sa fi fost nevoie sa se apeleze `EsteInEFactura` deloc (se poate confirma indirect, verificand ca nu s-a facut nicio interogare Oracle suplimentara, sau direct daca se mock-uieste functia cu un contor de apeluri). Toate cele trei suite: fara scriere in Oracle, `QUIT` la final, log langa `.prg` cu acelasi nume + `_log.txt`, conform conventiei din `handoff_punct6_dupa_s5.md`. ## 8. Schita de diff — corectia necesara peste diff-ul in lucru Doua variante, cu recomandare pentru B. **Varianta A — minima**, refoloseste `id_fact` deja prezent pe cursorul `tact` (confirmat: `tact` vine din view-ul `vact_tot`, care are coloana `id_fact`, folosita deja in `ofacturare_comun.vc2:3776` ca `Locate For Nvl(id_fact, 0) = lnIdFact` pe acelasi cursor `actactan`/`tact`): ```diff --- a/COMUN/clase/omodificari.vc2 +++ b/COMUN/clase/omodificari.vc2 @@ frm_modific2024.Show This.nIdVanzare = tvanz.id_vanzare This.nTipVanzare = tvanz.tip - This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) + This.lArticoleReadOnly = EsteInEFactura(Nvl(tact.id_fact, 0)) ``` Risc al variantei A: presupune ca recordul curent din `tact` (la momentul `Show()`, imediat dupa incarcare, deci pe Top) are `id_fact` valid pentru documentul gasit — valabil in cazul normal, dar nu la fel de robust ca precedentul din `ofacturare_comun.vc2:3775-3783`, care cauta explicit randul cu `id_fact` potrivit inainte sa cada pe fallback (`Go Top`). **Varianta B — recomandata**, aduce `id_fact` chiar pe `tvanz` (randul din `VANZARI` deja gasit prin tripletul cod/nract/serie_act/dataact), simetric cu `id_vanzare`: ```diff --- a/COMUN/programe/ofacturare_editare.prg +++ b/COMUN/programe/ofacturare_editare.prg @@ CreeazaCursorTvanzGol - CREATE CURSOR tvanz (id_vanzare I, tip I, discount N(12,2), total_fara_tva N(14,2), total_tva N(14,2), total_cu_tva N(14,2), curs N(10,4), multiplicator N(10,4), ; + CREATE CURSOR tvanz (id_vanzare I, id_fact I NULL, tip I, discount N(12,2), total_fara_tva N(14,2), total_tva N(14,2), total_cu_tva N(14,2), curs N(10,4), multiplicator N(10,4), ; in_valuta I NULL, id_valuta I NULL, nume_val C(20) NULL) @@ IncarcaVanzareNota - lcSql = [select v.id_vanzare, v.tip, v.discount, v.total_fara_tva, v.total_tva, v.total_cu_tva, v.curs, v.multiplicator, ] + ; + lcSql = [select v.id_vanzare, v.id_fact, v.tip, v.discount, v.total_fara_tva, v.total_tva, v.total_cu_tva, v.curs, v.multiplicator, ] + ; [v.in_valuta, v.id_valuta, nv.nume_val from vanzari v left join nom_valute nv on nv.id_valuta = v.id_valuta where v.cod = ] + Transform(m.tnCod) + ; --- a/COMUN/clase/omodificari.vc2 +++ b/COMUN/clase/omodificari.vc2 @@ frm_modific2024.Show This.nIdVanzare = tvanz.id_vanzare This.nTipVanzare = tvanz.tip - This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) + This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact, 0)) ``` Varianta B e mai robusta pentru ca foloseste `VANZARI.ID_FACT` direct — exact coloana pe care `ANAF_EFACTURA` o are ca cheie (verificat pe schema Oracle, §1) — fara sa depinda de pozitia curenta a cursorului `tact`. Costul: doua fisiere in loc de unul (`ofacturare_editare.prg` + `omodificari.vc2`), plus verificarea ca `select v.id_fact` nu produce eroare Oracle daca vreun rand vechi din `VANZARI` are `id_fact` `NULL` (coloana e deja `NULL`-abila judecand dupa restul cursorului, `in_valuta I NULL` etc., deci nu ar trebui sa fie o problema). **Neschimbat, corect asa cum e**: restul diff-ului (`.When`-urile pe grid, `cmdAdaugaArticol`/ `cmdStergeArticol.Enabled`, eticheta `lblArticoleReadOnly`) — vezi §2-5. --- ## Rezumat pentru implementare 1. Diff-ul lui `d42-efactura` (necomis la momentul acestui raport, in `COMUN\clase\omodificari.vc2` + `COMUN\programe\ofacturare_editare.prg`) e **structural corect** — locul (§2), controalele (§3), tiparul de grid (§4) si feedback-ul (§5) respecta cerintele Deciziei 42 corectate. 2. **Bug gasit, dovedit pe schema si date, si INCHIS**: `EsteInEFactura(This.nIdVanzare)` trebuia sa foloseasca `id_fact`, nu `id_vanzare` — `d42-efactura` l-a gasit independent si l-a corectat cu exact varianta B din §8 (`EsteInEFactura(Nvl(tvanz.id_fact,0))`), **verificat pe disc de acest agent** dupa corectie. Nu mai necesita nicio actiune. 3. Gol de clarificat, nu bug: `txtDiscountArt` ramane editabil dupa eFactura — de decis cu Marius daca discountul intra sub "articole" (§3). Confirmat si de `d42-efactura` ca gol cunoscut, in afara scope-ului primit de la team-lead. 4. Comentariul din `omodificari.vc2:14786-14787` ("ROACONT/ROAGEST nu-l inregistreaza") e invechit de la commit-ul `3af0089` — de actualizat cand se atinge zona (§1). 5. Teste propuse in §7, aliniate stilistic cu suita existenta — de confirmat cu `d42-efactura` daca au fost deja scrise (mesajul lui mentioneaza "doua fisiere noi de test"). 6. `ROAGEST\Programe\roagest.prg` (linia necomisa `SET PROCEDURE TO ofacturare_editare.prg`) **nu** apartine lui `d42-efactura` — ramane dintr-un alt bloc de lucru, de identificat separat de team-lead.