Files
roafacturare/docs/cercetare/rec_s4_fix_culoare_valuta.md
Marius Mutu d9f5ca4226 docs: planurile, proiectarile si rapoartele de lucru intra in versionare
Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git,
nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de
cercetare pe care se sprijina modificarile din cod. O stergere acolo era
definitiva.

Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au
fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele
fisierelor de test la care se refereau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
2026-08-11 22:17:17 +03:00

222 lines
13 KiB
Markdown

# 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 `<staging>\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.