AnafeFacturaServer.ImportZipLocal (programe/anaf_efactura.prg): dezarhiveaza arhiva aleasa de utilizator, ia xml-ul facturii (ignora semnatura MFinante), ParseEFactura, decide FACTURA PRIMITA/TRIMISA dupa codul fiscal al firmei curente, copiaza arhiva in directorul local de raspunsuri si scrie in Oracle prin acelasi lant ca descarcarea din SPV (cursor temporar canaf_efactura_temp + cUpdateFactura + UpdateDb -> pack_anaf.AdaugaRaspunsFactura si anaf_efactura_detalii). Fara SQL scris de mana si fara duplicarea maparii de campuri. clase/anaf_efactura.vcx (clasa anaf_efactura, metoda citesteraspunsuri): optiunea 8 in meniul butonului "Raspunsuri" - "Import arhiva zip de pe disc...". Necesar cand mesajul nu mai e in lista de raspunsuri ANAF (expira dupa 60 zile) si factura primita nu a ajuns in borderou. Testare (CENTRAL/MARIUSM_AUTO, ianuarie 2026, 12 PASS / 0 FAIL): utile/Teste/efactura_import/ - garda pe cod fiscal, import complet cu verificarea campurilor si a liniilor in anaf_efactura_detalii, factura vizibila in grila reala "Facturi primite in SPV". utile/Teste/test_init_env_auto.prg: adauga goFirma.codfiscalfro ca in start_firma (ostartfirma.prg) - proprietatea nu e coloana in v_firme, iar fara ea orice cod care o citeste nepazit crapa in mediul headless. docs/depanare_testare_vfp.md: verificarea de sintaxa prin compilare headless (config.fpw cu SAFETY=OFF, copie in scratchpad) si capcana literalelor string mai lungi de 255 de caractere (eroare de compilare, nu de runtime). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqJ7D5ftMbChDyz5mpVkCd
14 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.
- Literal string de peste 255 de caractere = eroare de COMPILARE, nu de runtime (masurat
24.07.2026: 255 trece, 256 pica). Mesajul difera dupa forma liniei:
Unrecognized command verbdaca linia incepe cu un apel de metoda (goExecutor.oExecuta("<sql lung>", 'c')),Command contains unrecognized phrase/keywordla o atribuire (lc = "<sir lung>"). Insidios: restul programului ruleaza normal, doar linia aceea lipseste din.fxpsi da eroare 16 cand se ajunge la ea - deci un test poate raporta "FAIL" pe altceva decat cauza reala. De aceea tot SQL-ul lung din codebase e scris cuTEXT TO <var> [TEXTMERGE] NOSHOW...ENDTEXTsau concatenat din bucati, nu ca literal unic. Verificare rapida:awk 'length($0) > 260 {print NR": "length($0)}' fisier.prg. - 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. Verificare de sintaxa prin compilare headless (dupa orice editare de .prg)
Cel mai ieftin test dupa o modificare intr-un .prg: COMPILE headless, care prinde erorile
de sintaxa fara sa porneasca aplicatia si fara Oracle. Reguli:
config.fpwpropriu cuSAFETY=OFFsiRESOURCE=OFF, pasat cu-c<cale>(lipit de flag, fara spatiu). Fara el,COMPILEpeste un.err/.fxpexistent siSTRTOFILEpeste un log existent scot dialogul modal "overwrite?" si procesul pare agatat.SET SAFETY OFFca prima linie in script e complementar, nu inlocuitor (-Asingur porneste cu SAFETY ON). Verificare ca a fost preluat: logheazaSET("SAFETY")/SET("RESOURCE")la start.-c<config>explicit are prioritate si merge impreuna cu-A: desi-Aignora config.fpw-ul implicit, un-cexplicit tot e preluat (verificat 24.07.2026:vfp9.exe -c<config> -A -T script.prga raportatSAFETY=[OFF] RESOURCE=[OFF]).- Compileaza o COPIE a fisierului, in scratchpad, nu sursa din working copy:
COMPILEscrie.fxp-ul langa.prg, iar.fxp-urile din SVN sunt artefacte de build - regenerate ad-hoc, ar aparea ca modificari in working copy. VFP compileaza fara sa rezolve simbolurile (clase, functii din alte fisiere), deci copia izolata e suficienta pentru sintaxa. - Sterge
.err-ul vechi inainte deCOMPILE, altfel nu poti distinge un.errramas de la rularea precedenta de unul nou. Verdictul se citeste din existenta<nume>.err: exista => logheaza continutul lui; nu exista => compilare curata.
Sablon (rulat dupa fiecare runda de editare pe anaf_efactura.prg, 24.07.2026):
SET SAFETY OFF
lcDir = '<scratchpad>\'
STRTOFILE('START ' + TTOC(DATETIME()) + ' SAFETY=' + SET("SAFETY") + CRLF, lcLog)
IF FILE(lcDir + 'copie.err')
DELETE FILE (lcDir + 'copie.err')
ENDIF
COMPILE (lcDir + 'copie.prg')
IF FILE(lcDir + 'copie.err')
STRTOFILE('ERR:' + CRLF + FILETOSTR(lcDir + 'copie.err'), lcLog, 1)
ELSE
STRTOFILE('OK: compilare fara erori', lcLog, 1)
ENDIF
QUIT
Nu inlocuieste testul functional: COMPILE valideaza sintaxa, nu apeluri catre metode
inexistente sau proprietati gresite (acelea apar abia la runtime).
8. 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).