- import_nota / import_adauga_factura: flux unificat note contabile + articole, sincronizare automata, total factura automat, TVA impartit pe conturile creditoare ale notelor (rundele 1-31) - runda 32: totalurile import_nota pe _label/_textbox din _baza.vcx (Arial 10), fara containerele clb_tx_simplu si fara bifa "Recalculeaza TVA" (recalcul mereu automat); "Sincronizeaza facturile secundare" mutata sub butoane - utile/Teste: harness UI VFP headless (vfp_ui_harness.ps1, ui_harness.prg, precompilare, mock-uri) in radacina; suitele e2e pentru achizitia din import grupate in utile/Teste/achizitie_import/ - docs: flux editare text vcx/scx, testare-ui-vfp si testare-vfp-mcp (mutate din ROAGEST, general valabile), inventar comun, orchestrare subagenti, conventie GO recno, depanare testare VFP Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DGibQKa5uK4P2TNSFFQNh
12 KiB
Depanare si testare VFP fara IDE (headless)
Metoda folosita la depanarea erorii "Property CNRCRT is not found" (ROACONT 2.11.61,
15.07.2026) - reutilizabila pentru orice bug runtime VFP. Complementara cu
flux-editare-vfp-text.md (editare .vcx/.scx pe text) si cautare_vcx_vct.md (cautare).
0. Reguli de lucru
Vezi reguli_lucru.md (diff ca fisier + aprobare inainte de write-back, comentarii
max o linie, modificari minime).
Fisiere de testare (foloseste-le la orice depanare/testare viitoare)
COMUN\utile\Teste\test_init_env_auto.prg- initializare completa de mediu FARA interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO). Echivalentul headless alCOMUN\programe\test_init_env.prg(care ramane varianta interactiva, cu INPUTBOX si meniu de lansare formulare).COMUN\utile\Teste\test_repro_cnrcrt_footer.prg- exemplu complet de harness: init mediu -> replicare flux (cursoarele din modificanote) -> instantiere forma reala -> executie pas cu pas cu logare -> verdict OK/BUG. Copiaza-i structura pentru bug-uri noi.COMUN\utile\Teste\test_repro_import_nota.prg- harness instantiere import_nota (ROAGEST) fara Oracle: cursori dummy + goExecutor dummy; parametrul LPARAMETERS incarca o copie de clasa din scratchpad in loc de cea reala (bisectie A/B).COMUN\utile\Teste\teste_roacont.prgsiCOMUN\utile\Teste\test.prg- teste ad hoc istorice.- Mediu de test standard: server
CENTRAL, schemaMARIUSM_AUTO, parola schemeiROMFASTSOFT; utilizator de aplicatie "MARIUS M" / parola "123" (unde e nevoie de login).
1. Citirea log-ului aplicatiei (log.txt in radacina proiectului)
ErrorHandler (roacont.prg) scrie pentru fiecare eroare: nErrror, cMethod,
line (0 daca exe-ul e compilat fara debug info) si stack-ul complet.
Semantica ON ERROR (esentiala la interpretare):
- Dupa ce handlerul face RETURN, executia CONTINUA de la linia urmatoare din metoda care a crapat. O singura cauza produce deci CASCADE de erori.
- Tipar clasic: eroare pe linia
WITH <expr>=> urmatoarele linii.prop = ...dau 1940 "Expression is not valid outside of WITH/ENDWITH", apoiENDWITHda 1939 "WITH/ENDWITH mismatch". PRIMA eroare din secventa e cea reala; restul e zgomot. - Erorile din interiorul TRY/CATCH nu ajung in log (inghitite). Un CATCH gol poate ascunde ani de zile un defect latent, care "explodeaza" cand codul din jur se repara (exact cazul typo-ului oMinHeaderHeight din cusfonthandler).
- In stack, liniile "ON..." sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
2. Rulare headless a scripturilor VFP
Sablonul unui script de diagnostic (vezi utile\Teste\test_repro_cnrcrt_footer.prg):
- logare in fisier cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()care logheaza si continua - mimeaza comportamentul ErrorHandler din aplicatie;QUITobligatoriu la final (altfel ramane vfp9.exe agatat);- lansare din PowerShell cu timeout:
$p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru; $p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() } -Aignora config,-Tfara ecran de start; parametrul dupa .prg ajunge in LPARAMETERS.- LINENO() din handler e relativ la inceputul metodei (utile pentru localizare in .vc2: linia din metoda = linia raportata + linia PROCEDURE din .vc2).
Precautii: nu rula cu VFP IDE sau exe-ul aplicatiei deschis (lock pe binare); sterge .fxp-ul vechi de langa .prg daca ai editat scriptul intre rulari.
3. Mediu complet cu Oracle, fara interactiune
utile\Teste\test_init_env_auto.prg = varianta neinteractiva a
programe\test_init_env.prg (fara INPUTBOX/xmenu): primeste host/schema/parola ca
parametri, seteaza PATH/CLASSLIB/PROCEDURE ca roacont.prg, instantiaza
goConn/goExecutor/goApp/goLog, incarca goFirma/goCalendar/optiuni si pachetele de
sesiune Oracle. Dupa apel poti instantia forme reale (CREATEOBJECT('frm_xxx', ...))
si apela metodele lor punctual, cu cursoarele pregatite ca in fluxul real.
Pentru forme care se deschid modal (Show(1) blocheaza), nu apela Show: replica in script doar liniile relevante din Show (attach, calcule) si logheaza starea intre ele.
4. Bisectia versiunilor de clase (regresii)
Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge SVN:
git show <rev_vechi>:clase/fisier.vcx > temp\fisier.vcx(+ .vct identic);- convertit la text pentru diff:
vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt; - in scriptul de test,
SET CLASSLIB TO temp\fisier.vcx ADDITIVE(varianta veche) vs. cea din COMUN (varianta noua) - A/B pe acelasi flux; - cand difera rezultatul, cauza e in diff-ul acelei clase (sau intr-un defect latent pe care noul cod il activeaza - vezi capcanele de mai jos).
Varianta pe backup-uri de runda (fara git): copiaza *.vc2.pre_rundaN.bak din cache
intr-un folder temp ca ointroduceri.vc2, regenereaza binarul direct cu
FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1 (pune ..\include\foxpro.h langa,
altfel .ERR), apoi in harness incarca DOAR copia (nu ADDITIVE peste cea reala -
rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ).
Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource ->
camp simplu, sterge linii de proprietate din mijlocul blocului) - NU sterge
obiecte/coloane din ADD OBJECT (strica structura: "Cannot add this object to a Grid").
5. Capcane VFP descoperite (de verificat la bug-uri asemanatoare)
SELECT ... INTO CURSORNU mosteneste indecsii cursorului sursa. OriceSeek(..., 'cursor', 'tag')pe copie crapa.update_jtva_coloanecreeazaINDEX ON id_jtva_coloana TAG id_jtva; daca ii copiezi rezultatul, recreeaza indexul.- Un grid isi pierde TOATE coloanele (ColumnCount devine 0, fara nicio eroare in log)
daca un ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse
programatic (ex. FontName/FontSize per coloana in cusfonthandler.ApplyFontToGrid,
la Init-ul formei prin _frmbase.SetFont). Simptome tipice in aval:
_grdfooter.attachtogridcopiaza 0 coloane, apoicalctotalda "Property is not found" laGetpem(This, This.cColWithLabel). - ControlSource-uri cu campuri inexistente in RecordSource sunt tolerate la afisare, dar orice expresie care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie nedefinita) declanseaza comportamentul de mai sus.
- Fix-ul unui typo intr-o clasa partajata poate activa defecte latente in aval (codul care nu rulase niciodata incepe sa ruleze). La regresii dupa fix-uri "inofensive" in COMUN, cauta defectul latent, nu doar diff-ul.
- Coloana de grid cu ControlSource EXPRESIE (nu camp) cere
Bound = .F.. Fara el: eroare 9 "Data type mismatch" la CREATEOBJECT-ul formei (faza de constructie, raportata la linia apelanta, Details gol). Caz: import_nota.GridArt Column7/8/9 curound(...)(ROAGEST 16.07.2026); model corect: Grid1.cTaxCode (Bound = .F.+ expresie, functional de ani).
6. Capcane la SCRIEREA scriptului de test (nu ale bug-ului investigat)
Descoperite depanand harness-ul pentru "Data type mismatch" la CREATEOBJECT('IMPORT_nota',...)
(ROAGEST, 16.07.2026) - de verificat oricand un script de test nou pare "agatat".
DEFINE CLASS ... ENDDEFINENU poate sta la mijlocul programului principal. Daca il pui intre alte instructiuni de top-level (ex. clasa dummy pentru ungoExecutorde test, definita devreme "ca sa fie la indemana"), VFP compileaza gresit: TOATE liniile de DUPAENDDEFINEdau "Statement is not in a procedure" (vezi<script>.ERRgenerat la compilare). Simptom in productie:vfp9.exepare agatat la nesfarsit - CPU ~0, nicio fereastra de dialog vizibila (verificat si prin enumerare Win32 a ferestrelor procesului), pentru ca de fapt a rulat doar liniile de dinainteaDEFINE CLASSsi s-a oprit silentios dupa eroarea de compilare, fara sa ajunga laQUIT. Fix: muta oriceDEFINE CLASSauxiliar (dummy executor, helpere) la FINALUL fisierului, dupaQUIT, langa celelaltePROCEDURE- e locul standard pentru cod suport intr-un script de test.- Diagnostic rapid compilare vs blocaj real: cand un script "nu se termina",
verifica INTAI daca a aparut
<script>.ERRlanga.prg(log de compilare, vezi siroagest.ERR/log.txtin radacina proiectului) - inseamna ca s-a oprit la compilare, nu ca ruleaza/asteapta ceva. Daca nu exista.ERR, verifica CPU-ul procesului (Get-Process | select CPU): ~0 dupa cateva secunde inseamna ca asteapta un dialog modal (foloseste enumerarea Win32 de ferestre de mai jos ca sa confirmi), nu ca proceseaza ceva lent. - Enumerare ferestre Win32 pentru un proces "agatat" (utila cand nu esti sigur
daca exista un dialog modal ascuns):
Add-TypecuEnumWindows/GetWindowText/GetWindowThreadProcessIddinuser32.dll, filtrat pe PID-ul luivfp9.exe. Daca vezi doar fereastra principala + "Command", nu e dialog - cauta alta cauza (vezi punctul de mai sus). STRTOFILEinainte deSET SAFETY OFF= dialog modal "Overwrite?" daca fisierul de log exista deja (sub-A, SAFETY porneste ON). PuneSET SAFETY OFFPRIMA linie sau sterge log-ul inainte de fiecare rulare.-A -Tsunt obligatorii impreuna la lansare headless (-Aignora config.fpw,-Tsare peste splash screen-ul de start) - fara-Tprocesul ramane agatat pe ecranul de intampinare, identic ca simptom cu o eroare de compilare (CPU ~0, fara log). Confirma mereu care e cauza inainte de a schimba scriptul.goExecutor/alte dependinte "dummy":CREATEOBJECT('Custom')+AddProperty()poate adauga doar PROPRIETATI, nu METODE. Daca clasa testata apeleazagoExecutor.oExecute(...)/oExecuta(...), dummy-ul trebuie sa fie o clasa proprie (DEFINE CLASS dummyexecutor AS CustomcuPROCEDURE oExecute/oExecutareale), nu unCustomcu proprietati adaugate.- Erorile de instantiere (
CREATEOBJECT) raportate la linia CREATEOBJECT, nu la.Init: candON ERROR/CATCHarataProcedure= scriptul apelant (nuNUMECLASA.INIT), inseamna ca eroarea vine din evaluarea proprietatilor/ obiectelor din DEFINITIA clasei (ADD OBJECT ... WITH RowSource = ...,ControlSourcede coloana grid evaluat ca expresie, etc.) - controale native VFP (ex. combobox cuRowSourceType=6Fields) isi rezolva bind-ul la CONSTRUCTIE, inainte sa apuce sa rulezePROCEDURE Inita formei. Cand reproduci un astfel de bug cu un mediu minimal, o "Alias is not found" pe un cursor pe care nu l-ai deschis tu confirma mecanismul (faza de instantiere), chiar daca nu e mesajul EXACT din bug-ul original.
7. Studiu de caz: "Property CNRCRT is not found" (15.07.2026)
Lant complet: frm_import_extrase_banca.modificanote copia crsJTVATemp fara index
(defect latent) -> coloana cExplicatieTVA din frm_modific2024.grdRulaje face
Seek(trul.id_jtva_coloana,'crsJtvaTemp','id_jtva') in ControlSource -> fix-ul
typo-ului oMinHeaderHeight->nMinHeaderHeight in cusfonthandler (r17869) a facut
ca ApplyFontToGrid sa atinga coloanele la Init -> grid-ul a ramas cu 0 coloane ->
footer-ul a crapat in calctotal. Fix: INDEX ON id_jtva_coloana TAG id_jtva dupa
SELECT INTO in modificanote. Repro/regresie: utile\Teste\test_repro_cnrcrt_footer.prg
(mod FARAINDEX reproduce bug-ul, mod CUINDEX valideaza fix-ul).