# S4 — doua defecte pe pagina de articole: culoarea liniei sterse + valuta liniilor noi (09.08.2026) Doua defecte raportate dupa livrarea sub-blocului B (stergere + adaugare de linii). Ambele in `COMUN\clase\omodificari.vc2` (`frm_modific2024`, `pgfArticole.PAGE3`); al doilea atinge si `COMUN\programe\ofacturare_editare.prg`. **Ambele REZOLVATE, dovedite, scrise in binar, necomise.** ## Defectul 1 — marcajul vizual `DynamicForeColor` nu se aplica pe linia stearsa ### Cauza reala (nu ipoteza de start, confirmata pe cod) `grdArticoleFactura` e definit ca `ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura' AS _grdrow` (`omodificari.vc2:12306`), unde `_grdrow` (`COMUN\clase\_grd_base.vc2:445-489`) e o clasa a suitei comune (nu proprietatea acestei livrari — doar citita) care evidentiaza linia curenta a gridului: ``` PROCEDURE Init DoDefault() If This.nRgbrow = 1 ... This.SetAll("DynamicForeColor","iif(RECNO() = This.nRecno,RGB(" + This.cRGB_Font + "),RGB(0,0,0))","Column") Endif ENDPROC ``` `nrgbrow` implicit e `1`. La instantiere, dupa ce `PropValue`-urile clasei (inclusiv `Column1..14.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))"`) sunt aplicate, `Init` ruleaza si **`SetAll` suprascrie necondiționat** `DynamicForeColor` pe toate cele 14 coloane cu o expresie care ignora `tvd.sters` complet — de-asta linia stearsa avea pixeli negri puri (0,0,0), nu gri (150,150,150): codul din clasa era corect, dar era deja inlocuit inainte de primul `Refresh()`. Ipoteza de start din briefing ("`_grdrow.Init` castiga in fata lui `DynamicForeColor`") s-a confirmat exact, cu mecanismul precis identificat (`SetAll` in `Init`, nu doar `AfterRowColChange`). ### De ce nu solutia gridurilor surori `grdRulaje`/`grdRulajeObinv` (`pgfArticole.PAGE1`/`PAGE2`) rezolva coliziunea cu `Init` **gol** (`*Nu sterge`, `omodificari.vc2:~15659`/`~15926`) — asta taie tot lantul `DoDefault()`, deci si `_grid.Init()` (cel care seteaza `ReadOnly` pe coloanele text dupa `lcamptextneeditabil`). `grdArticoleFactura` are nevoie sa ramana editabil pe `cantitate`/`pret`/`pret_cu_tva` (`lcamptextneeditabil=.F.`, fix din sesiunea anterioara) — un `Init` gol ar fi rupt din nou editabilitatea. ### Fix O singura proprietate pe `ADD OBJECT`-ul gridului (`omodificari.vc2:12317`): ``` nrgbrow = 0, ; ``` `nrgbrow=0` scurt-circuiteaza intreg blocul `If This.nRgbrow = 1` din `_grdrow.Init` (deci si `SetAll`) **fara sa taie** `DoDefault()` -> `_grid.Init()` — editabilitatea ramane intacta. Tiparul e deja folosit in **20+ griduri** din suita comuna (`COMUN\clase\configurare.vc2`, `COMUN\clase\onomenclatoare.vc2`), deci nu e o solutie inventata pentru acest caz — e conventia existenta pentru "grid cu culoare proprie pe randuri, fara evidentierea implicita a liniei curente". ### Dovada — esantionare de pixeli, nu vizual Doua capturi noi, sub `vfp_ui_harness.ps1 -SyncDir` explicit (capcana de sincronizare deja rezolvata in sesiunea precedenta): - `COMUN\utile\Teste\editare_factura\screenshots_after_fix_culoare\step_0_contrast_gri_vs_negru.png` — document cu 4 linii (`cod=1140895`/`id_vanzare=1050`), linia 2 marcata stearsa prin `cmdStergeArticol.Click()`, linia curenta a gridului mutata pe linia 3 (`GO 3` + `loGrid.SetFocus()`) ca selectia (fundal albastru) sa nu acopere nici linia stearsa, nici o linie de control. Esantionare cu `System.Drawing.Bitmap.GetPixel` pe banda de text a fiecarei linii (PowerShell, cautare pixel cu luminanta minima in regiune): | Linie | Pixel cel mai inchis (RGB) | |---|---| | Linia 1 (normala, `sters=0`) | `(0,0,0)` | | **Linia 2 (STEARSA, `sters=1`)** | **`(150,150,150)`** — exact `RGB(150,150,150)` din `DynamicForeColor` | | Linia 4 (normala, `sters=0`) | `(0,0,0)` | - `COMUN\utile\Teste\editare_factura\screenshots_after_fix_culoare\step_0_linia_grizata.png` — document cu 2 linii (`cod=1139934`/`id_vanzare=882`), aceeasi verificare, acelasi rezultat (`RGB(150,150,150)` pe linia stearsa). Capturile "before" (linia stearsa cu pixeli negri puri, dovada defectului inainte de fix), mutate din sesiunea anterioara: `screenshots_before_fix_culoare\step_0_linia grizata dupa sters.png` + `step_0_crop_zoom.png`. ### Testat, regresie `test_ui_sterge_linie.prg` (existent, nemodificat in logica — doar rerulat): **8 PASS / 0 FAIL**, log complet pana la `REZULTAT`. `test_page3_articole.prg`: **14 PASS / 2 FAIL** (identic cu baseline, cele 2 FAIL sunt artefactul headless cunoscut de la datoria 7 — `ColumnCount`/`ReadOnly` nu se materializeaza sub `-A -T`). `test_incarca_vanzare_din_nota.prg`: **5 PASS / 0 FAIL**. Suita noua `test_ui_culoare_contrast.prg` (dedicata acestui fix, ramane in suita de regresie): descopera dinamic un document cu minim 3 linii (`DescoperaCazTest('FACTURA_ARTICOLE', ...)`, nu ancorat pe `cod`), marcheaza a doua linie stearsa, muta linia curenta pe a treia, face captura. **1 PASS / 0 FAIL** pe asertia functionala (`sters=1` dupa click); dovada vizuala e in captura + esantionarea de pixeli de mai sus, nu intr-o asertie automata (culoarea nu se poate verifica prin `EVALUATE` in interiorul testului — eroarea 1929, deja documentata in `rec_s4_runda3b2.md`). ## Defectul 2 — liniile adaugate intrau fortat in RON ### Cauza reala (diferita de formularea initiala din briefing) Briefingul presupunea ca `AdaugaLinieTvdDinArticol` scrie `tip_valuta = 0` fix — **verificat pe cod, fals**: `tvd` (structura din `CreeazaCursorArticoleGol`, `ofacturare_editare.prg:268-271`) nu are deloc un camp `tip_valuta`, `curs` sau `multiplicator` — acele campuri nu exista in view-ul `VVANZARI_ARTICOLE` (linia stocheaza doar `id_valuta`, o referinta la `nom_valute`, fara factor de conversie propriu). Cauza reala e in alta parte: `tip_valuta=0`/`Curs=1`/`multiplicator=1` sunt hardcodate pe **`poArticol`** in `CreeazaPoArticolNouTvd` (`ofacturare_editare.prg:429-431`), iar `poDate.in_valuta` e hardcodat `0` in `cmdAdaugaArticol.Click` (`omodificari.vc2:16190`) — asta tine dialogul `frm_articol_factura` in **modul RON** indiferent de valuta reala a documentului. Dialogul calculeaza `pretftva`/`pretctva` (campurile RON) pe baza a ce introduce utilizatorul, iar `AdaugaLinieTvdDinArticol` scria acele valori **neconvertite** in `tvd.pret`/`tvd.discount_unitar`. Bara de totaluri (`ActualizeazaBaraTotaluri`, decizia 33, `omodificari.vc2:12856-12899`) insumeaza `tvd.valoare` **brut** (`SUM valoare FOR sters<>1`) si aplica **o singura data** `tvanz.curs`/`multiplicator` pe suma — asta presupune ca fiecare linie e deja stocata in valuta documentului, nu in RON. Verificat pe date (schema `MARIUSM_AUTO`): | `id_vanzare` | `VANZARI.in_valuta` | `VANZARI.id_valuta` | `curs` | linia (`VANZARI_DETALII.pret`) | |---|---|---|---|---| | 1037 | 1 | 2 (EURO) | 5.2688 | `pret=200` — 200 EUR brut, nu 200 RON | O linie noua scrisa in RON (ex. utilizatorul intentioneaza 1053.76 RON) ar fi ramas `pret=1053.76` in `tvd`, iar bara de totaluri ar fi calculat `1053.76 * 5.2688 = 5551.6` RON — de peste 5 ori valoarea reala. ### Fix — scopat strict pe stocare, fara sa ating dialogul `frm_articol_factura`/`ofacturare.vc2` **nu sunt in proprietatea acestei livrari** (partajate cu tot restul suitei), si a face dialogul sa accepte pret direct in valuta ar cere `poDate.id_valuta`/`poDate.zi_curs` populate corect (altfel validarea interna a dialogului respinge la "Terminare" cu mesaj — verificat pe cod, `ofacturare.vc2:9484,9490`) — netestabil headless (`Show()` e modal) si risc pe o clasa mare, necunoscuta. Fix ales, minim si sigur: 1. **`tvanz` extins** cu `in_valuta`, `id_valuta`, `nume_val` (join nou pe `nom_valute` in `IncarcaVanzareNota`, `ofacturare_editare.prg:150-176`; `CreeazaCursorTvanzGol` la fel). 2. **`AdaugaLinieTvdDinArticol` (`omodificari.vc2:12901-12934`) converteste** pretul/discountul calculate de dialog (RON) in valuta documentului, inainte de a le scrie in `tvd`: `pret = pretRON * tvanz.multiplicator / tvanz.curs` (si simetric pentru `discount_unitar`) — inversul exact al formulei din bara de totaluri, deci **no-op pe documente RON** (`curs=multiplicator=1`, verificat neregresat pe suita existenta). 3. **`id_valuta`/`nume_val` ale liniei noi preiau valorile de pe `tvanz`** (`tvanz.id_valuta`, `tvanz.nume_val`) in loc de `.Null.`/gol — simetric cu liniile existente, care le au populate din `VVANZARI_ARTICOLE`. **Limitare ramasa, de raportat explicit**: dialogul **tot lucreaza in RON** — utilizatorul introduce pretul in RON chiar si pe un document in valuta, iar conversia se face "in spate", la scriere. STOCAREA e acum corecta valutar (bara de totaluri calculeaza corect regardless de valuta documentului), dar UX-ul de introducere a pretului direct in valuta nu e implementat — ar cere atingerea `ofacturare.vc2`, in afara scope-ului primit ("nu era ceruta multi-valuta in briefing" — decizie mostenita din sub-blocul B partea 2, `rec_s4_runda3b2.md`). ### Testat - `test_page3_articole.prg`: **14 PASS / 2 FAIL** (identic cu baseline). - `test_incarca_vanzare_din_nota.prg`: **5 PASS / 0 FAIL**. - `test_adauga_linie_articol.prg` (existent, caz RON): **20 PASS / 0 FAIL** — confirma ca conversia e no-op cand `curs=multiplicator=1`, nicio regresie pe calea deja acoperita. - **Suita noua** `test_adauga_linie_valuta.prg`: foloseste un document real cu articole (descoperit dinamic, nu ancorat pe `cod`), dar suprascrie manual `tvanz.curs=5.2688`, `multiplicator=1`, `in_valuta=1`, `id_valuta=2`, `nume_val='EURO'` dupa `Show()` — nu exista in schema un caz descoperibil simultan "cu articole" si "in valuta reala" (`id_vanzare=1037`, EURO, are o singura linie, nefolosibila pentru un test de "adaugare langa liniile existente" fara ambiguitate). Construieste `poArticol` cu `pretftva=1053.76` (echivalentul RON a 200 EUR la cursul testat) si verifica: **6 PASS / 0 FAIL** — `tvd.pret` convertit la `200.00`, `id_valuta=2`, `nume_val='EURO'`, `tvd.valoare=200.00`, iar bara de totaluri creste cu exact `1053.76` RON (echivalentul corect, nu suma bruta). ## Cens de octeti si write-back Baseline la inceputul sesiunii: `2 aa / 2 e3 / 2 fe`, zero `EF BF BD`. Primul `Edit` pe `omodificari.vc2` (adaugarea `nrgbrow = 0`) a reencodat tot fisierul, stricand din nou cele doua linii `Caption = "CTRL+F = Terminare; ESC = Renunțare; CTRL+N = Adăugare; CTRL+D = Ștergere"` (capcana deja cunoscuta, lovita si in sesiunile precedente) — cens dupa: `6 ef / 6 bf / 6 bd`. Reparat byte-cu-byte cu Perl (pozitional: octetul 1 din fiecare tripleta `EF BF BD` -> `0xFE` (ț), octetul 2 -> `0xE3` (ă), octetul 3 -> `0xAA` (Ș), ciclic pe cele 2 linii x 3 caractere). Cens final, verificat inainte si dupa `txt2vcx.ps1`: **identic cu baseline, `2 aa/2 e3/2 fe`, zero `EF BF BD`**. `ofacturare_editare.prg` e ASCII pur (verificat, zero octeti >0x7F) — fara risc de encoding, nu necesita reparare. **Fidelity-check trecut din prima incercare** (`txt2vcx.ps1 -AllowComun`, un singur fisier regenerat) — spre deosebire de sesiunile precedente, nu a fost nevoie sa adopt textul regenerat din `\verify\`. ## Fisiere atinse, stare write-back | Fisier | Stare | |---|---| | `COMUN\clase\omodificari.vc2` | Editat (`nrgbrow=0` pe grid + `AdaugaLinieTvdDinArticol` rescrisa), write-back FACUT, fidelity check OK din prima, cens OK | | `COMUN\clase\omodificari.vcx` / `.VCT` | Scrise, mtime sincron cu `.vc2` (16:59) | | `COMUN\clase\omodificari.vc2.pre_fix_culoare.bak` | Backup, luat la inceputul sesiunii | | `COMUN\programe\ofacturare_editare.prg` | Editat (`CreeazaCursorTvanzGol` + `IncarcaVanzareNota` extinse cu valuta documentului, antet rescris), ASCII pur | | `COMUN\programe\ofacturare_editare.prg.pre_fix_culoare.bak` | Backup, luat la inceputul sesiunii | | `COMUN\utile\Teste\editare_factura\test_adauga_linie_valuta.prg` | Nou, testat, 6/6 PASS | | `COMUN\utile\Teste\editare_factura\test_ui_culoare_contrast.prg` | Nou, testat, captura + esantionare pixeli | | `COMUN\utile\Teste\editare_factura\screenshots_after_fix_culoare\` | Nou — 2 capturi, dovada pe pixeli | | `COMUN\utile\Teste\editare_factura\screenshots_before_fix_culoare\` | Capturile "before" din sesiunea anterioara, mutate din `screenshots\` ca sa nu se piarda | | `docs\diff_s4_fix_culoare_valuta.patch` | Nou — `omodificari.vc2` | | `docs\diff_s4_fix_culoare_valuta_ofacturare_editare.patch` | Nou — `ofacturare_editare.prg` | | `docs\progres.md` | Actualizat | **Fara commit** — asteapta review, conform regulii. ## Ce NU e acoperit 1. **Dialogul `frm_articol_factura` tot lucreaza in RON** — utilizatorul introduce pretul in RON chiar si pe un document in valuta; conversia se aplica la scriere, nu la intrare. A schimba asta ar cere atingerea `ofacturare.vc2` (`poDate.in_valuta=1` + `id_valuta`/`zi_curs` populate), in afara proprietatii/scope-ului acestei livrari. 2. **`Show(1)` (dialogul modal chiar deschis)** — netestat automat (netestabil headless), ca in sesiunile precedente; acoperit doar prin analiza de cod si testare manuala recomandata. 3. **Editarea unei linii existente prin acelasi dialog** (dublu-clic) — ramane nefacuta, in afara scope-ului primit in aceasta sesiune. 4. **Decizia de arhitectura despre `_grd_base.vc2`**: fixul defectului 1 s-a facut fara sa ating `_grd_base.vc2` (clasa comuna) — `nrgbrow` era deja o proprietate expusa pentru exact acest caz, nu a fost nevoie de nicio modificare acolo.