Files
roafacturare/docs/cercetare/rec_s5_goluri_test.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

205 lines
12 KiB
Markdown

# S5 — inchiderea golurilor de acoperire (rollback, al doilea punct de intrare, linii de set)
Continua `docs\livrare_s5.md`, sectiunea 5 „Ce NU e acoperit". Trei goluri, in ordinea cerută:
calea de ROLLBACK la esec partial, al doilea punct de intrare (`comun.vc2:2491`), liniile din
seturi de articole. Suite noi: `COMUN\utile\Teste\editare_factura\test_s5_rollback_real.prg`,
`COMUN\utile\Teste\editare_factura\test_s5_al_doilea_intrare.prg`. Niciun cod de productie atins.
## A. Calea de ROLLBACK la esec partial — INCHIS, cu o descoperire importanta
Suita: `test_s5_rollback_real.prg`. Log: `test_s5_rollback_real_log.txt` (rulare 10.08.2026,
01:20:31-01:20:32). **13 PASS / 0 FAIL.**
**PARTEA M (mock, fara Oracle)** — completeaza `test_s5_validari_articole.prg`/B3 (care opreste
esecul la a DOUA comanda, in mijlocul buclei de UPDATE): aici esecul e la PRIMA comanda a
helperului (pasul 1, „marcheaza tot sters"). Dovedit: functia intoarce `.F.` imediat, **o singura**
comanda incercata (`nApeluri=1`) — nicio comanda de UPDATE/INSERT/recalcul nu se mai declanseaza.
**PARTEA R (real, Oracle)** — pe `id_vanzare=1049`, prin `SQLEXEC` brut (nu prin
`ScrieArticoleFacturaEditate`/`goExecutor.oExecuta` — vezi descoperirea de mai jos), reproduce
EXACT secventa SQL pe care helperul o genereaza pentru un caz cu esec la INSERT (linie noua cu
`id_articol=999999999`, verificat inainte ca articolul chiar nu exista in `NOM_ARTICOLE`):
| Pas | Comanda | Rezultat |
|---|---|---|
| 1 | `UPDATE ... SET STERS=1` (marcheaza tot) | reuseste |
| 2 | 3x `UPDATE` (invie+actualizeaza liniile pastrate 1581/1583/1588) | reuseste |
| 3a | `INSERT` linie noua VALIDA | reuseste |
| 3b | `INSERT` linie noua cu `id_articol` inexistent | **ORA-02291** (FK_VANZARE_DET002) |
Dovedit, cu citiri **necomise** pe aceeasi conexiune, INTRE pasi (proba de executie PARTIALA
reala, nu doar teoretica): dupa pasii 1-2, cele 3 linii pastrate erau deja active si actualizate
(necomis); dupa insertul valid, documentul avea deja 5 linii (necomis). Insertul invalid a oprit
secventa exact acolo (simetric cu `EXIT` din `SCAN`-ul helperului) — `recalculeaza_totaluri_vanzari`
nu s-a mai apelat. Tranzactia s-a inchis cu **ROLLBACK** (tiparul apelantilor:
`lnSucces=Iif(...,1,-1)` → `MyInchideTranzactie(Iif(lnSucces<0,2,1))`).
Verificat **independent prin sqlplus**, dupa terminarea procesului VFP: `VANZARI.COD` neschimbat
(`1140898`), toate cele 4 randuri din `VANZARI_DETALII` identice octet-cu-octet cu starea de
dinainte (`id_vanzare_det:sters:cantitate:pret:pret_achizitie`), zero randuri cu
`id_articol=999999999`, zero tranzactii deschise.
### Descoperire, NEREPARATA (cod de productie interzis pentru acest agent) — de raportat separat
`goExecutor.oExecuta(<sql>)` — calea Oracle pe care `ScrieArticoleFacturaEditate` o foloseste
pentru **toate** comenzile ei — **agata procesul VFP headless la o eroare Oracle REALA**, sub
tranzactie manuala (`SQLSetprop(...,"Transactions",2)`), chiar daca Oracle a raspuns deja cu
eroarea. Constatat de doua ori, izolat, cu un script minimal (~15 linii, sters dupa investigatie):
- sesiunea Oracle arata `INACTIVE`/`SQL*Net message from client` (Oracle a raspuns, asteapta
urmatoarea comanda de la client) — **nu** e o incuietoare (`v$lock`/`v$transaction`: nimic
blocant);
- `EnumWindows` pe procesul VFP arata **doar** fereastra principala — **nu** e un dialog Windows
care asteapta input;
- `SQLEXEC` brut (fara `goExecutor`) pe **exact aceeasi comanda** intoarce eroarea instant
(`lnRes=-1`, `AERROR` populat corect, `alen=7`, `[1]=1526`) — deci nu e o problema a driverului
Oracle/OLEDB in sine.
### CAUZA GASITA de orchestrator, 10.08.2026 — e artefact headless, NU un defect de productie
Suspiciunea pe `goLog.Log()` de mai sus e **gresita**. Cauza reala, citita in cod:
`ORA-02291` ajunge in `oproceduri_comune.prg` ca eroare ODBC, deci `lnEroare1 = 1526` si
`lnEroare2 = 2291`. `2291` nu e in lista de reconectare (`:385`), deci executia intra pe ramura
`Otherwise` (`:421-424`):
```foxpro
If llShowError
lnRaspuns = amessagebox('Eroare necunoscuta' + Chr(13) + lcTextEroare, 0, 'Eroare')
Endif
```
**Acolo se agata: un `AMESSAGEBOX` modal, intr-un proces headless in care nimeni nu apasa OK.**
`goLog.Log()` (`:427`) vine **dupa** el si nici nu apuca sa ruleze. Explica si de ce `SQLEXEC` brut
intoarce eroarea instant — el nu trece prin ramura asta.
`llShowError` vine din `This.lShowError` cand apelantul nu-l paseaza (`:234` vs `:243`), iar
`ScrieArticoleFacturaEditate` cheama `goExecutor.oExecuta(m.lcSql)` fara al doilea argument pe toate
cele patru cai (`ofacturare_editare.prg:491, 502, 531, 547`) — deci mesajul chiar se afiseaza.
**Consecinta reala pentru livrare, inversa fata de ce scria mai sus**: in productie, cu utilizator
in fata ecranului, comportamentul e **corect** — se afiseaza mesajul, `Exit` iese din bucla,
helperul intoarce `.F.`, apelantul inchide tranzactia cu ROLLBACK. Aplicatia **nu** ramane agatata.
Agatarea e capcana headless deja documentata in `docs\progres.md` („Dialogurile native VFP blocheaza
testul headless la infinit"), lovita aici de un script minimal care nu avea mock pe `amessagebox`.
Ce ramane, ca **wart preexistent** al infrastructurii comune (nu al S5, si neatins): textul afisat e
`Eroare necunoscuta` + SQL-ul brut + `GETCALLSTACK()` — corect functional, urat pentru utilizator.
Priveste tot ROA, nu doar acest lant.
## B. Al doilea punct de intrare (`comun.vc2:2491`, `afisjurcom.do_modifica`) — INCHIS
Suita: `test_s5_al_doilea_intrare.prg`. Log: `test_s5_al_doilea_intrare_log.txt` (rulare
10.08.2026, 01:29:51-01:29:52, a doua trecere — vezi „Corectie" mai jos). **17 PASS / 0 FAIL.**
Reproduce integral secventa `comun.vc2:2222-2569` (garzile, cursoarele notei, salvarea), mai putin
partea de UI (gridul `actjur`, `Createobject([frm_modific2024], lnIdSet)` — sarit deliberat, risc
de blocaj headless; verificarea vizuala ramane la Marius, per briefing). Pe **aceeasi nota**
folosita de `test_s5_scriere_reala.prg`/`test_s5_discount_valuta.prg` (`id_vanzare=1049`) —
singurul document de test cunoscut care are simultan rand real in `VANZARI` **si** nota in luna
curenta (cerinta specifica a lui `afisjurcom.do_modifica`, `comun.vc2:2265-2268`, pe care
`do_editare_factura` nu o are in aceeasi forma).
Diferenta functionala fata de primul punct de intrare, singura care conteaza pentru acest test:
`afisjurcom.do_modifica` **nu** apeleaza `IncarcaVanzareDinNota` direct — apeleaza
`PregatesteArticoleFacturaEditare('tact')` (`comun.vc2:2436`), care populeaza `tvanz` **si**
`crsArticoleFactura` pe alta cale de cod. Dovedit REAL (nu static):
- `PregatesteArticoleFacturaEditare` gaseste documentul (`.T.`);
- garda noua (`Used('tvanz') And Reccount('tvanz')=1`) se satisface REAL prin aceasta cale;
- `tvanz.id_vanzare` corect (`1049`), `crsArticoleFactura` precarcat;
- blocul nou (`comun.vc2:2491-2493`) **s-a executat** (flag verificat direct in cod, nu prin
scanare de log);
- lantul complet a reusit (`lnSucces=1`), tranzactia s-a inchis cu **COMMIT**
(`Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1))`, `comun.vc2:2541`) — spre deosebire de
suita A, aici nu s-a provocat niciun esec, deci lantul chiar reuseste si comite, ca in fluxul
real.
Fara nicio modificare de articole (ca blocul C din `test_s5_discount_valuta.prg`): `COD` s-a
realocat (`1140899 → 1140900`, efect normal al `finalizeaza_modificare_nota` la orice salvare,
chiar fara modificari), dar discount/totaluri/numar de linii active raman identice, verificat
**independent prin sqlplus** dupa terminarea procesului.
### Corectie facuta IN TIMPUL scrierii acestui test (nu a ajuns in fisierul final, dar a scris real in baza)
O prima varianta a testului apela `ScrieArticoleFacturaEditate` **fara** sa incarce intai `tvd`
(in fluxul real, `Load()`-ul lui `frm_modific2024` e cel care il populeaza din
`crsArticoleFactura` — sarit aici deliberat). Garda EXTERNA
(`Used('tvanz') And Reccount('tvanz')=1`) s-a satisfacut, dar garda INTERNA de no-op a helperului
(`!Used('tvd')`) a transformat apelul intr-un **no-op tacut** (intoarce tot `.T.`) — fara sa
corecteze reset-ul `STERS=0` pe care `pack_facturare.actualizeaza_vanzari` il face pe TOT
documentul (acelasi mecanism documentat in `rec_s5_scriere_reala.md`). Tranzactia a **comis**
reset-ul necorectat: linia `det=1582`, definitiv stearsa in `test_s5_scriere_reala.prg`
(09.08.2026), a fost reinviata real in Oracle (`STERS: 1 → 0`).
**Reparat imediat**, inainte de orice alta actiune: `UPDATE vanzari_detalii SET sters=1 WHERE
id_vanzare_det=1582; COMMIT;`, verificat prin `sqlplus` ca restul coloanelor (cantitate, pret,
pret_achizitie) au ramas neatinse. Varianta finala a testului incarca `tvd` cu
`IncarcaArticoleFactura(tnIdVanzare,'tvd')` chiar dupa `PregatesteArticoleFacturaEditare` (ca
Load()-ul formularului), deci helperul chiar corecteaza reset-ul — noua rulare confirma explicit,
prin asertia 17, ca `det=1582` ramane `sters=1` dupa salvare. Consemnat in antetul suitei finale,
ca precautie pentru cine reia/adapteaza testul.
Aceasta e o capcana de test (guard-ul `!Used('tvd')` functioneaza exact cum trebuie, prevenind un
no-op periculos sa arunce eroare — problema a fost ca testul nu a reprodus fidel ce face UI-ul real
inainte de acest punct), **nu** un defect de productie.
## C. Liniile din seturi de articole (`id_vanzare_set` nenul)
**Partea Oracle (agregarea pe seturi din `recalculeaza_totaluri_vanzari`) — NEACOPERIBILA pe datele
curente, confirmat prin interogare, nu presupus:**
```sql
select count(*) from vanzari v join vanzari_detalii vd on vd.id_vanzare=v.id_vanzare and vd.sters=0
where vd.id_vanzare_set is not null
and extract(year from v.data_act)=2026 and extract(month from v.data_act)=8;
-- 0 randuri
```
Cele mai recente documente cu linii de set active sunt din 2026-03 (`id_vanzare=1027`) si mai
vechi — niciunul din luna curenta (august 2026), cerinta obligatorie a gardei de editare
(`(pnAn*12)+pnLuna = (gnAn*12)+gnLuna`, verificata direct in suitele A si B de mai sus). Fara
fabricare de date (interzisa explicit in briefing): golul ramane deschis, ca risc cunoscut, pana
apare un document real editabil cu linii de set.
**Partea helper VFP (`ScrieArticoleFacturaEditate` trateaza corect o linie cu `id_vanzare_set`
nenul) — DEJA ACOPERITA, verificat prin recitirea suitei existente, fara munca noua necesara:**
`test_s5_validari_articole.prg`, sectiunea B1, randul 6 („componenta de set, pastrata"
`id_vanzare_set=999`, `det=558`) — asertia „clasificare: exact 3 UPDATE (liniile pastrate 555/556/
558, inclusiv nemodificata si componenta de set)" dovedeste ca helperul trateaza o linie cu
`id_vanzare_set` nenul identic cu orice alta linie pastrata (UPDATE normal, fara ramura speciala in
codul VFP — `id_vanzare_set` nu apare in nicio conditie din `ScrieArticoleFacturaEditate`,
confirmat si prin citirea sursei). Nu era nevoie de mock nou.
## Cifre PASS/FAIL, numarate din log
| Suita | Cifra | Verdict |
|---|---|---|
| `test_s5_rollback_real.prg` (nou) | **13 PASS / 0 FAIL** | Rollback la esec partial: contract (mock) + stare DB (real, SQLEXEC) |
| `test_s5_al_doilea_intrare.prg` (nou) | **17 PASS / 0 FAIL** | Al doilea punct de intrare, executie reala prin `comun.vc2:2491-2493` |
| `test_s5_validari_articole.prg` (existent, doar recitit) | 35 PASS / 0 FAIL (neschimbat) | Confirma acoperirea existenta pentru linia de set (B1/rand 6) |
## Date de test consumate
Pe `id_vanzare=1049` (acelasi document folosit de toate suitele S5 anterioare):
- **`cod` realocat: `1140898 → 1140899 → 1140900`** (ambele din suita B — prima trecere, cu
corectia de mai sus, apoi a doua trecere finala). Cine reia testele citeste `cod`-ul curent din
`VANZARI`, nu-l presupune.
- **Continutul liniilor si totalurile documentului raman identice** cu starea de dinaintea acestei
lucrari (verificat exhaustiv, prin sqlplus, dupa fiecare pas): `det=1581/1583/1588` active cu
aceleasi valori, `det=1582` ramane `sters=1`.
- Suita A (rollback) **nu a consumat nimic** — ambele parti (mock si real) se inchid fara COMMIT.
- Zero linii noi ramase, zero tranzactii deschise, zero procese `vfp9.exe` ramase — verificat prin
`sqlplus`/`tasklist` la finalul lucrarii.
## Fisiere atinse
- `COMUN\utile\Teste\editare_factura\test_s5_rollback_real.prg` (nou)
- `COMUN\utile\Teste\editare_factura\test_s5_al_doilea_intrare.prg` (nou)
- Niciun fisier de productie (`.vc2`/`.prg` de aplicatie) atins. `omodificari.vc2`,
`ofacturare_comun.vc2`, `comun.vc2`, `ofacturare_editare.prg`, `COMUN\clase\ofacturare.vc2` —
neatinse, conform interdictiei din briefing.
Diff (fisiere noi): `docs\diff_s5_goluri_test.patch`.