Files
roafacturare/docs/cercetare/rec_s5_goluri_test.md
Marius Mutu b5a7108f34 docs: cercetarile comune trec in COMUN, reziduul #6 dispare, progres.md la zi
Curatenie ceruta dupa inchiderea lui #6. Folderul cercetare\ NU se putea sterge
in bloc: plan_13 se sprijina pe el cu 85 de trimiteri, deci e baza de dovezi a
planului aflat in lucru. Impartirea:

- 14 cercetari trans-proiect trec in COMUN\docs\cercetare\ - valuta si curs,
  TVA/VANZARI, consumatorii VANZARI din toata suita, integrarile #10/#11/#12,
  watchdog VFP, proiectarea Oracle a lui S5, view-ul VVANZARI_ARTICOLE. Nu sunt
  ale ROAFACTURARE, iar #10/#11/#12 se reiau chiar din ele.
- 20 de rapoarte de executie ale lui #6, nereferite de nimic viu, sterse.
- 91 raman, neatinse.

Trimiterile catre handoff-urile si diff-urile intermediare deja desfiintate au
fost curatate peste tot (39 de fisiere): 50 catre handoff-uri, 37 catre diff-uri
aplicate, plus caile celor mutate in COMUN. Zero trimiteri rupte ramase.

progres.md preia rolul de predare: ce ramane din #6 (cele sase documente
parazite, cele doua esecuri reale din S8 pe factura din aviz), cifrele de test
citite din log, si cele doua capcane de mediu platite - .FXP vechi executat in
locul .prg-ului, si GETFONT() care atarna un formular instantiat fara goApp.

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

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);
  • 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):

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:

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): diff aplicat (sters).