# Date de test - NNIR editabil in Registru Jurnal

Baza: `MARIUSM_AUTO@ROA_CENTRAL` (test, nu productie). Verificare: `verif_nnir_cod.sql` in acest
folder (`sqlplus ... @verif_nnir_cod.sql <cod>`).

| Caz | COD | AN | LUNA | ID_SET | NNIR curent | ACT randuri | RUL randuri | RUL_OBINV randuri |
|---|---|---|---|---|---|---|---|---|
| NIR/achizitie (principal) | 1140841 | 2026 | 7 | 209 | 3 | 7 (doar 1 cu nnir=3, restul 0) | 1 (nnir=3) | 0 |
| Obiecte de inventar | 1140835 | 2026 | 6 | 211 | 2026060001 | 1 | 0 | 2 (nnir=2026060001) |
| Fara rulaje | 1140848 | 2026 | 7 | 209 | 124 | 2 (ambele nnir=124) | 0 | 0 |
| Nota de inventar (blocare) | 1140716 | 2026 | 3 | 90101 | 2026030004 | 1 | 1 (nnir=2026030004) | 0 |

Note:
- ACT e pe linii (nu pe antet) - la COD 1140841 doar linia efectiv legata de rulaj are NNIR-ul
  real, restul liniilor documentului au NNIR=0. Sincronizarea (`WHERE Nvl(nnir,0) = vechi`) trebuie
  sa lase liniile cu 0 neatinse.
- Nu exista nicio nota cu `id_set=90101` sau cu rulaje in `RUL_OBINV` in luna curenta de lucru
  (AN=2026, LUNA=7) - alese din perioade anterioare. Editarea reala prin `afisjurcom.do_modifica`
  e limitata la luna curenta (`comun.vc2:2265-2268`); testul instantiaza `frm_modific2024` direct
  (ca `test_repro_cnrcrt_footer.prg`), cu cursoare construite pe AN/LUNA propriu notei, ocolind
  acel filtru - la fel ca precedentul deja acceptat in `achizitie_import`.

## Stare implementare (livrata, 03.08.2026)

`COMUN\clase\omodificari.vc2` (frm_modific2024):
- `Grid1.ColumnCount = 44`, `Column44.ControlSource = "nnir"` (la runtime apare calificat automat
  de VFP cu aliasul cursorului: `tact.nnir`), `ReadOnly = .F.` - confirmat prin instantiere reala
  (nu doar citire de sursa).
- `PROCEDURE sincronizeaza_nnir(tnNnirVechi, tnNnirNou)` (`omodificari.vc2:13762`) - update pe
  `tact`/`trul`/`trul_obinv` unde `Nvl(nnir,0) = vechi`, cu revenire pe recno. Propaga si valoarea
  0 (stergerea NNIR) - nu exista garda `EMPTY` pe valoarea noua.
- Legare pe coloana: `Grid1.Column44.Text1.When` retine valoarea veche in `thisform.nnnirvechi`;
  **`Grid1.Column44.Text1.LostFocus`** apeleaza `sincronizeaza_nnir` si `Thisform.Grid1.Refresh()`
  - `Valid` a fost eliminat (la textbox valoarea nu ajunge in ControlSource decat DUPA ce Valid
    returneaza .T., deci nu propaga). Testul declanseaza `LostFocus`.
- Blocarea pentru `id_set = 90101` (nota de inventar): in `Init` (`omodificari.vc2:13654`) SI
  reimpusa in `Grid1.AfterRowColChange` (14289-14292), dupa `SetAll("ReadOnly", ...)` - fara a doua,
  `SetAll` anuleaza blocarea la prima navigare in grid.
- `ColumnOrder = 7` in clasa, plus repozitionare la runtime in `Show()`, dupa `gridextra1.setup()`
  (13729-13738) - preferintele de grid salvate per utilizator impingeau coloana noua la coada
  (vezi `COMUN\docs\capcana_grid_preferinte_utilizator.md`). Pozitia NU e o asertiune stabila intre
  masini; asertat tare in test: `ControlSource = "nnir"`, `ReadOnly` (.F. normal / .T. pe
  id_set 90101), propagarea valorii.

## Cerinte de mediu descoperite la construirea harness-ului (deja tratate in scripturile livrate)

- `COMUN\utile\Teste\test_init_env_auto.prg` (generic) e de fapt specific ROACONT (`gcAppPath`,
  classlib-uri si proceduri hardcodate `D:\ROA\ROACONT\`) - inutilizabil pentru ROAGEST. S-a scris
  `COMUN\utile\Teste\test_init_env_auto_roagest.prg`, cu SET PATH/CLASSLIB/PROCEDURE reconstruite
  din `Programe\roagest.prg`.
- `gcUserName` trebuie setat explicit inainte de `optiuni_firma` (`oinit_optiuni.prg`) - altfel
  `SCRIE_OPTIUNI(gcUserName)` da "Operator/operand type mismatch" (PUBLIC nedeclarat ramane
  logic .F.). **`gcUserName` e schema/userul de conectare Oracle** (`Programe\roagest.prg:560,575`)
  - `SCRIE_OPTIUNI` il foloseste direct in `from <gcUserName>.optiuni`, deci trebuie
  `gcUserName = m.gcS`, NU un nume de persoana (ex. 'MARIUS M' da ORA-00907, identificator invalid
  - incercare gresita initiala). Numele de persoana e `gcUserNameApp` (variabila separata).
- `frm_modific2024` face `Seek(..,'crsJtvaTemp','id_jtva')` in ControlSource-ul coloanei de
  explicatie TVA a rulajelor (Column63) - evaluat la CONSTRUCTIA formularului. Fara cursorul
  deschis, `CREATEOBJECT('frm_modific2024',...)` pica cu "File 'crsjtvatemp.dbf' does not exist".
  Fix: `update_jtva_coloane('', 'crsJtvaTemp', 6)` (in `COMUN\programe\updateserver.prg:553`)
  inainte de instantiere - construieste direct cursorul cu indexul cerut, fara pasii intermediari
  folositi de exemplul ROACONT (`update_jtva_coloana`/`crsExplicatiiTVATemp`, care oricum nu
  exista in ROAGEST).
- Mock-ul `amessagebox` trebuie incarcat **INAINTE** de `test_init_env_auto_roagest.prg` (nu doar
  dupa, cum sugereaza exemplele din `achizitie_import`) - `optiuni_firma` poate afisa un dialog
  real daca `SCRIE_OPTIUNI` raporteaza eroare, inainte sa apuce sa se incarce mock-ul "la coada".
- La citirea unei coloane de grid prin ControlSource dintr-un cursor legat ca RecordSource, VFP
  calificat automat campul simplu cu aliasul (`nnir` -> `tact.nnir`) - comparatiile exacte
  `== 'NNIR'` trebuie sa accepte si forma calificata.
- Coloanele de grid nu sunt garantat numite `Column<N>` - unele au `Name` custom (ex. `cAles` pe
  pozitia 41 in acest grid); accesul dinamic la coloana trebuie facut prin colectia
  `Grid1.Columns(i)`, nu prin constructia textuala a numelui `"column" + TRANSFORM(i)`.
- `frm_modific2024.Grid1.AfterRowColChange` are propria logica de blocare pe sucursala (SetAll
  ReadOnly pe toate coloanele daca `Nvl(gnIdSucursala,0) <> Nvl(tAct.id_sucursala,0)`) - independenta
  de blocarea 90101, se declanseaza automat la pozitionare in grid. COD 1140835 (cazul obiecte de
  inventar) are `id_sucursala` diferit de sesiunea de test (`gnIdSucursala = NULL`), deci Column44
  iese `ReadOnly=.T.` in acest scenariu - nu e o eroare, e comportament existent, neplanificat de
  NNIR; sincronizarea tot functioneaza (apelul e direct, nu prin UI).
- **Termin reatribuie COD nou**: `afisjurcom.do_modifica` (comun.vc2:2444-2487) cheama
  `oscrie_in_fisiere` de DOUA ori - intai `(2,.T.,llRul)` (STERGE, marcheaza `sters=1` pe COD-ul
  ORIGINAL, din `actactan` neatins de editare), apoi `(0,.T.,llRul)` (SCRIE varianta editata sub
  COD nou, generat de `pack_contafin`, sub AN/LUNA de sesiune curenta - nu pastreaza COD-ul/AN/LUNA
  notei editate). Verificarea si curatenia testului se fac dupa NNIR (marcaj distinct), nu dupa
  COD, si readuc nota originala la `sters=0` dupa test (pasul STERGE o dezactiveaza altfel permanent).
- Salvarea reala cere tranzactie manuala explicita: `SQLSetProp(gnHandle,'Transactions',2)` inainte
  de blocul STERGE+SCRIE+finalizeaza, apoi `SQLCOMMIT(gnHandle)`/`SQLROLLBACK` +
  `SQLSetProp(...,1)` dupa - echivalentul direct al `thisform.do_deschide_tranzactie()`/
  `do_inchide_tranzactie()` din `afisjurcom` (`_frm_base.vc2:252-305`, mostenite, apelate pe
  formularul de jurnal - nu pe `frm_modific2024`, deci invizibile la o privire doar pe clasa notei).
  Fara acest wrapping, `oscrie_in_fisiere`/`finalizeaza_modificare_nota` raporteaza succes dar nu
  se vede nimic persistat, nici in aceeasi sesiune.

## Capcana: .FXP stale ascunde scenariile noi

VFP nu recompileaza .prg-ul daca exista un .FXP mai vechi - ruleaza tacut versiunea veche a
scriptului. Simptom: suita raporteaza PASS-uri corecte, dar scenariile adaugate dupa ultima
compilare nu apar deloc in log (verifica numarul de assert-uri, nu doar `0 FAIL`). Sterge
.FXP-ul inainte de fiecare rulare.

## Capcana: dialog modal "Enter the value for <var>" in loc de eroare catchabila

O variabila memorie lipsa/umbrita folosita ca `?param` intr-un SQL pass-through (SQLEXEC/
`goExecutor.oExecuta`) NU da o eroare VFP catchabila (ON ERROR) - deschide dialogul nativ
"View Parameter" ("Enter the value for <var>"), MODAL, care blocheaza tot procesul headless la
nesfarsit si nu lasa nimic in log (spre deosebire de un `REPLACE`/expresie cu aceeasi variabila
lipsa, care DA eroare catchabila - vezi cazul `gnIdUtil` scapat o data ca `PRIVATE` neasignat).
Simptom: proces `vfp9.exe` care nu mai raspunde, fara `.ERR`, fara linie noua in log.

Remediu: **garda explicita inainte de orice rulare care ajunge la cod ce foloseste `?param`** -
verifica `TYPE(<nume>) == 'U'` pe toate variabilele relevante (nu doar cele proprii testului, ci
si cele din codul apelat: `oscrie_in_fisiere`, `pack_contafin`/`pack_sesiune`, `oinit_optiuni.prg`)
si opreste testul cu FAIL clar in log daca lipseste ceva, inainte sa ajunga la SQLEXEC. Implementat
in `test_nnir_sincronizare.prg` (`NnirGardaVariabile`, apelata imediat dupa init mediu). `TYPE()`
returneaza `'X'` (nu `'U'`) pentru NULL - o valoare NULL e un bind valid, doar variabila absenta
e problema.

## Rezultat test complet (03.08.2026, pe binarul livrat)

**33 PASS, 0 FAIL** pe cele 6 scenarii: NIR, obiecte de inventar si nota fara rulaje (toate cu
salvare reala prin Termin + verificare in Oracle), blocare `id_set=90101`, `NNIR -> 0`, si blocare
`id_set=90101` pe sucursala curenta. Baza de test verificata dupa rulare: cele 4 note originale
(1140841, 1140835, 1140848, 1140716) raman intacte (NNIR si `sters` la valorile initiale); notele
noi create de Termin (COD reatribuit) au fost marcate `sters=1` de curatenia din test.

Scenariul 6 (`INVENTAR2`) exista pentru ca scenariul 4 ruleaza pe o nota din ALTA sucursala, unde
`AfterRowColChange` blocheaza oricum tot gridul - trecea din motivul gresit. Scenariul 6 aliniaza
`gnIdSucursala` la nota (`llReadOnly` iese `.F.`) si asertesteaza si ca restul coloanelor raman
editabile, altfel blocarea NNIR nu dovedeste nimic.
