Paisprezece rapoarte care nu erau ale ROAFACTURARE traiau in docs\cercetare\ al acelui proiect: valuta si curs in ofacturare, TVA calculat vs salvat plus denormalizarea VANZARI, inventarul consumatorilor VANZARI din toata suita ROA, integrarile de contracte/politici/nomenclator (#10, #11, #12), watchdog-ul VFP de testare, proiectarea Oracle a scrierii din S5 si view-ul VVANZARI_ARTICOLE. Motivul mutarii, nu doar al pastrarii: planurile #10, #11 si #12 sunt amanate, iar indexul lor spune explicit ca se reiau din aceste rapoarte - deci sunt punct de plecare, nu istoric. Iar tinta lor e COMUN plus ROAPRETURI/ROACONTRACTE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
13 KiB
Watchdog VFP + diagnostic blocaj "View Parameter" (test_page3_articole.prg)
1. Utilitarul: COMUN\utile\Teste\watchdog_vfp.ps1
Lanseaza vfp9.exe -A -T "<script>", polleaza la ~700ms ferestrele TOP-LEVEL ale procesului
(EnumWindows+GetWindowThreadProcessId, filtrate pe PID). Fereastra principala VFP se identifica
prin Process.MainWindowHandle (.NET) - nu dupa numele clasei: VFP inregistreaza clase diferite
prefixate vfp9... atat pentru shell-ul principal (vfp99400000) cat si pentru dialogurile lui
proprii (vfp994000002 pentru "View Parameter") - o clasificare pe clasa a lasat "View Parameter"
nedetectat la prima incercare (corectat).
Pentru fiecare fereastra noua (diferita de MainWindowHandle): captura PNG (PrintWindow, fallback
CopyFromScreen daca iese neagra) + dump text (titlu, clasa, si textul/clasa fiecarui control copil
via WM_GETTEXT, cross-proces). Cu -AutoDismiss: cauta un buton copil "Cancel"/"Anulare" si
trimite BM_CLICK; altfel incearca, in ordine, WM_COMMAND IDCANCEL -> ESCAPE ca MESAJ
(WM_KEYDOWN/WM_KEYUP, tintit pe handle) -> WM_CLOSE.
REGULA OBLIGATORIE, incalcata initial si corectata: watchdog-ul NU are voie sa foloseasca INPUT
REAL de tastatura/mouse (keybd_event, SetForegroundWindow, SendInput, mouse_event) - masina e
PARTAJATA cu utilizatorul. Prima versiune folosea SetForegroundWindow+keybd_event(ESCAPE) ca
fallback pentru dialogurile proprii VFP owner-drawn (fara controale copil reale) - acest input NU
are tinta, ajunge in orice fereastra are focus pe masina in acel moment, indiferent al cui proces
e. Fapt clarificat de team-lead, dupa investigare: in acest caz concret, ESC-ul a aterizat de
fapt in PROPRIUL NOSTRU proces de test (eroarea "Variable 'GNAN' is not found" vazuta imediat inainte
de "Execution was canceled by the user" e chiar semnatura testului nostru) - nu in sesiunea lui
Marius, deci de fapt nu s-a intrerupt munca nimanui de data asta. Asta NU schimba regula: mecanismul
tot nu are tinta si putea la fel de usor sa ajunga in aplicatia de productie (roafacturare.exe/
roacont.exe) daca utilizatorul avea focus acolo in acea clipa - o formulare anterioara aici spunea
gresit ca ar fi afectat sesiunea utilizatorului, corectata acum. Scos complet din cod (nu mai
exista nicio linie keybd_event/SetForegroundWindow in watchdog_vfp.ps1). Consecinta: pentru
dialogurile owner-drawn care nu raspund la mesaje tintite, -AutoDismiss esueaza cinstit (dialogul
ramane deschis pana la -TimeoutSec, apoi procesul e omorat) - captura (PNG+dump text) ramane de incredere,
dismiss-ul nu e garantat pentru acest tip de dialog. Procesul e omorat mereu la iesire (finally),
nu ramane niciodata viu.
Validat intai pe caz banal: COMUN\utile\Teste\watchdog_selftest.prg (MESSAGEBOX cu OK/Cancel) -
detectie, captura, dump text si auto-dismiss confirmate corecte inainte de a-l rula pe cazul real.
Utilizare: powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "<test.prg>" -AutoDismiss [-TimeoutSec 90] [-MaxDialogs 10] [-OutDir <cale>].
2. Dialogurile capturate pe test_page3_articole.prg
Dialog 0 - nativ VFP, clasa vfp994000002, titlu "View Parameter", text "Enter the value
for gnAn:" (fara controale copil reale - owner-drawn). Screenshot:
COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog0.png.
Dialog 1 (apare doar cand dialog 0 e inchis prin ESCAPE real, care functioneaza) - "Open" Win32
standard, filtru "Table/DBF (*.dbf)", folder implicit ROACONT (working directory-ul mediului de
test, mostenit din test_init_env_auto.prg). Screenshot:
COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog1.png.
3. PROGRAM()/LINENO() din log (dupa Cancel pe dialog 0)
Din test_page3_articole_log.txt, PROGRAM()=VERIFICA_PAGECOUNT_FORM (procedura de test) pe
liniile din jurul apelului loForm = Createobject([frm_modific2024], lnIdSet):
EROARE 1 [VERIFICA_PAGECOUNT_FORM:165] File 'crsjtvatemp.dbf' does not exist.
EROARE 1924 [VERIFICA_PAGECOUNT_FORM:166..173] LOFORM is not an object. (cascada, zgomot)
4. Experimente de izolare (cerute de team-lead) - INFIRMA ipoteza initiala
Ipoteza initiala din aceasta sectiune ("SQLEXEC din update_jtva_coloane nu rezolva ?gnAn") era
o deductie, nu o masuratoare - team-lead a cerut-o verificata direct, corect. Trei experimente
ieftine, in ordine:
Experiment A - "chiar exista in acel moment?" TYPE('gnAn')/TRANSFORM(gnAn) puse imediat
INAINTE de apelul update_jtva_coloane (linia 161 curenta, nu inainte de Createobject cum
fusese verificat prima data):
EROARE 12 [VERIFICA_PAGECOUNT_FORM:161] Variable 'GNAN' is not found.
- Eroare aparuta DOAR la primul apel al
verifica_pagecount_form(cod=1140888), inainte sa se ajunga laupdate_jtva_coloane. Rezultat:gnAne deja invizibila INAINTE caupdate_jtva_coloanesa fie apelata - markerele?gnAn/?gnLunadinupdateserver.prg:597nu pot fi (macar nu singure) cauza, contrazice ipoteza initiala.
Experiment B - "se reproduce izolat, fara nimic din S4?" Script nou,
COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg: DOAR test_init_env_auto +
update_jtva_coloane("", "crsJtvaTemp", 6), fara IncarcaCursoareModificareNota, fara
frm_modific2024, fara nimic din PAGE3. Rezultat:
TYPE(gnAn)=N TRANSFORM(gnAn)=2026 TYPE(gnLuna)=N TRANSFORM(gnLuna)=8
dupa update_jtva_coloane: Used(crsJtvaTemp)=.T. Reccount=120
done
NU reproduce. Zero dialog, exit curat, cursorul se creeaza corect cu 120 randuri.
update_jtva_coloane singura, chemata imediat dupa initul mediului, functioneaza perfect -
nu e o capcana preexistenta a harness-ului si nici o vina proprie a functiei in izolare.
Rerulat identic dupa scoaterea input-ului real din watchdog (vezi sectiunea 1) - acelasi rezultat,
deci reconfirmat, nu era un artefact al mecanismului de dismiss.
Experiment C - oprit inainte de a-l rula. Premisa lui ("copiaza corpul lui update_jtva_coloane
cu concatenare in loc de ?param, ca sa confirmi mecanismul") presupune ca vina e in legarea
SQLEXEC a functiei - exact ce B tocmai a infirmat. Nu are sens sa continue in forma ceruta initial
fara o noua directie.
5. Cauza CONFIRMATA prin bisectie (nu doar deductie)
Bisectie ceruta de team-lead: TYPE('gnAn')/TRANSFORM(gnAn) logat in doua straturi - (a) in
programul principal, intre fiecare apel de nivel superior, si (b) ca prima linie in interiorul
fiecarei proceduri (verifica_vanzare_nota, verifica_coliziune_cod, verifica_pagecount_form).
Helper bisect_log_gnan (nou, la coada test_page3_articole.prg) - TYPE() e sigur necoditionat,
TRANSFORM() doar daca TYPE()<>'U', ca sa nu produca o eroare noua care ar intrerupe bisectia.
Rezultat brut (test_page3_articole_log.txt):
[BISECT] main: dupa test_init_env_auto :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
[BISECT] main: dupa verifica_vanzare_nota #1 (cod=1140888) :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1140885 :: TYPE(gnAn)=U gnAn=(U)
[BISECT] main: dupa verifica_vanzare_nota #2 (cod=1140885) :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1125486 :: TYPE(gnAn)=N gnAn=2026 <- diferit!
[BISECT] main: dupa verifica_vanzare_nota #3 (cod=1125486) :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_coliziune_cod ENTRY :: TYPE(gnAn)=N gnAn=2026
[BISECT] main: dupa verifica_coliziune_cod :: TYPE(gnAn)=N gnAn=2026
[BISECT] main: inainte de verifica_pagecount_form :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_pagecount_form ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
[BISECT] verifica_pagecount_form inainte de update_jtva_coloane :: TYPE(gnAn)=U gnAn=(U)
Tiparul e limpede si consecvent: gnAn e INTOTDEAUNA valid (N, 2026) in scope-ul PRINCIPAL,
la fiecare checkpoint, fara exceptie - deci NU e "eliberata" (nu e CLEAR ALL/CLEAR MEMORY/
RELEASE ALL EXTENDED pe undeva). E 'U' STRICT la intrarea in proceduri apelate cu DO ... WITH
in care gnAn/gnLuna sunt trecute NEPARANTEZATE ca argumente, si redevine valid imediat ce
procedura respectiva se termina si controlul revine in principal. Corelatia e exacta cu sintaxa
apelului, nu cu ce face procedura pe dinauntru:
verifica_vanzare_notaapelul #1/#2 (gnAn->U): call-site-urile trecgnAn, gnLunaDIRECT -test_page3_articole.prg:34(DO verifica_vanzare_nota WITH 1140888, gnAn, gnLuna, ...) sitest_page3_articole.prg:36(... WITH 1140885, gnAn, gnLuna, ...).verifica_vanzare_notaapelul #3 (gnAnramaneN):test_page3_articole.prg:38trece2008, 2LITERAL, nugnAn/gnLuna.verifica_coliziune_cod(gnAnramaneN):test_page3_articole.prg:42nu trece delocgnAn/gnLuna(doarlcLog).verifica_pagecount_formprimul apel (gnAn->U, exact scenariul blocat):test_page3_articole.prg:47-DO verifica_pagecount_form WITH 1140888, gnAn, gnLuna, 'A (factura reala)', lcLog, 3, .T..
Mecanismul: DO <procedura> WITH <arg1>, <arg2>, ... (stilul vechi, folosit peste tot in acest
script) trece variabilele de memorie BY REFERENCE implicit (SET UDFPARMS e REFERENCE in mod
implicit VFP) - LPARAMETERS tnAn, tnLuna din procedura primitoare devin ALIAS-uri directe pe
storage-ul lui gnAn/gnLuna, iar numele ORIGINAL devine inaccesibil (TYPE()='U') pe toata
durata apelului, exact cat tine executia procedurii - confirmat empiric de simetria perfecta
"intra U, revine N" la fiecare din cele 3 perechi de apeluri afectate.
Clasificare in termenii cerutii de team-lead: e "umbrire de scope" (categoria 2), NU
"eliberare" (categoria 1) - dar mecanismul exact nu e o coliziune de nume PRIVATE/LOCAL in corpul
procedurii (team-lead avea deja dreptate: verifica_pagecount_form nu are gnAn in LOCAL, si nu
exista PRIVATE gnAn/LOCAL gnAn nicaieri in COMUN\programe) - umbrirea vine din SINTAXA
APELULUI (DO...WITH fara paranteze in jurul lui gnAn/gnLuna), nu din declaratiile procedurii
apelate.
Statement-ul vinovat exact, cu fisier:linie: COMUN\utile\Teste\editare_factura\test_page3_articole.prg:47.
IMPLICATIE IMPORTANTA: acesta e un tipar din SCRIPTUL DE TEST, nu din fluxul real al
aplicatiei - do_editare_factura (codul de productie) nu trece prin verifica_pagecount_form
(helper propriu testului). Team-lead a confirmat verdictul: e strict un defect de harness -
updateserver.prg, omodificari.* si codul S4 sunt toate nevinovate.
Remediu APLICAT de team-lead (3 linii, sub pragul lui de editare directa): argumentele
gnAn/gnLuna sunt acum parantezate - (gnAn), (gnLuna) - la liniile 37, 39 si 50 din
test_page3_articole.prg (parantezele forteaza trecere PRIN VALOARE in loc de PRIN REFERINTA),
cu un comentariu explicativ deasupra primei aparitii. Nerulat inca de mine (interzis explicit -
suita o ruleaza team-lead-ul dupa eliberarea .fxp-ului).
Remediul din rundele anterioare ale acestui raport (concatenare in loc de ?gnAn/?gnLuna in
updateserver.prg:597) ramane infirmat - nu era cauza. updateserver.prg nu a fost si nu e atins.
6. Fisiere atinse
- Nou:
COMUN\utile\Teste\watchdog_vfp.ps1,COMUN\utile\Teste\watchdog_selftest.prg,COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg(experimentul B, izolat). - Modificat:
COMUN\utile\Teste\editare_factura\test_page3_articole.prg- linia 176 (
update_jtva_coloane(..., 6), ramane - fix necesar pt. indexulid_jtva, independent de defectul de mai jos); PROCEDURE bisect_log_gnan(noua, la coada fisierului) + apeluriDO bisect_log_gnan WITH ...inserate in principal (intre apelurile de nivel superior) si ca prima linie in fiecare procedura- ramase in cod, sunt dovada bisectiei;
- liniile 37, 39, 50 - remediul APLICAT de team-lead:
gnAn/gnLunaparantezate ((gnAn),(gnLuna)), forteaza trecere prin valoare in loc de prin referinta inDO...WITH.
- linia 176 (
- Neatins:
COMUN\clase\omodificari.vc2/.vcx/.vct,COMUN\programe\updateserver.prg(ambele infirmate ca posibila cauza, vezi sectiunea 5). - Artefacte de rulare (
watchdog_out\*.png/.log,*_log.txt) raman pe disc ca dovada; se pot sterge cucuratenie.ps1la finalul lucrarii.
7. Stare la data acestui raport
Cauza confirmata prin bisectie (sectiunea 5) si remediul APLICAT de team-lead (paranteze la
liniile 37/39/50). Nerulat inca de nimeni dupa aplicarea remediului - team-lead ruleaza suita
separat, dupa eliberarea .fxp-ului (nu s-a mai relansat testul in aceasta sesiune, per interdictia
primita). Ramane deschis, pentru cine continua:
- Confirma cu o rulare ca remediul chiar elimina dialogul si
verifica_pagecount_formtrece PASS pePageCount=3/lAreArticoleVanzari=.T.pentru cod=1140888. - Optional: verifica daca fluxul REAL de productie (
do_editare_facturainofacturare_comun.vc2) are undeva acelasi tiparDO...WITH <variabila PUBLIC>NEPARANTEZAT inainte de un?paramin SQLEXEC - team-lead a verdictuit "defect de harness", dar asta ramane neverificat exhaustiv pe codul de productie. - Watchdog-ul (
watchdog_vfp.ps1) ramane instrumentul de verificat orice ipoteza noua fara sa se agate procesul si FARA input real (regula obligatorie, sectiunea 1) - reutilizabil pentru orice alt blocaj similar in suita.