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
12 KiB
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); EnumWindowspe procesul VFP arata doar fereastra principala — nu e un dialog Windows care asteapta input;SQLEXECbrut (faragoExecutor) pe exact aceeasi comanda intoarce eroarea instant (lnRes=-1,AERRORpopulat 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):
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):
PregatesteArticoleFacturaEditaregaseste documentul (.T.);- garda noua (
Used('tvanz') And Reccount('tvanz')=1) se satisface REAL prin aceasta cale; tvanz.id_vanzarecorect (1049),crsArticoleFacturaprecarcat;- 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:
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):
codrealocat:1140898 → 1140899 → 1140900(ambele din suita B — prima trecere, cu corectia de mai sus, apoi a doua trecere finala). Cine reia testele citestecod-ul curent dinVANZARI, 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/1588active cu aceleasi valori,det=1582ramanesters=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.exeramase — verificat prinsqlplus/tasklistla 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/.prgde 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.