Files
comun/docs/depanare_testare_vfp.md
Marius Mutu 95bb2b5b74 Borderou eFactura: import manual arhiva zip descarcata din SPV
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
2026-07-24 10:43:28 +03:00

226 lines
14 KiB
Markdown

# 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 al `COMUN\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.prg` si `COMUN\utile\Teste\test.prg` - teste ad hoc istorice.
- Mediu de test standard: server `CENTRAL`, schema `MARIUSM_AUTO`, parola schemei
`ROMFASTSOFT`; 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", apoi `ENDWITH` da
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;
- `QUIT` obligatoriu 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() }`
- `-A` ignora config, `-T` fara 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:
1. `git show <rev_vechi>:clase/fisier.vcx > temp\fisier.vcx` (+ .vct identic);
2. convertit la text pentru diff: `vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`;
3. in scriptul de test, `SET CLASSLIB TO temp\fisier.vcx ADDITIVE` (varianta veche)
vs. cea din COMUN (varianta noua) - A/B pe acelasi flux;
4. 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 CURSOR` NU mosteneste indecsii cursorului sursa. Orice
`Seek(..., 'cursor', 'tag')` pe copie crapa. `update_jtva_coloane` creeaza
`INDEX 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.attachtogrid` copiaza 0 coloane, apoi `calctotal` da
"Property <cColWithLabel> is not found" la `Getpem(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
verb` daca linia incepe cu un apel de metoda (`goExecutor.oExecuta("<sql lung>", 'c')`),
`Command contains unrecognized phrase/keyword` la o atribuire (`lc = "<sir lung>"`).
Insidios: restul programului ruleaza normal, doar linia aceea lipseste din `.fxp` si 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 cu `TEXT TO <var> [TEXTMERGE] NOSHOW`
... `ENDTEXT` sau 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 cu `round(...)` (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 ... ENDDEFINE` NU poate sta la mijlocul programului principal.**
Daca il pui intre alte instructiuni de top-level (ex. clasa dummy pentru un
`goExecutor` de test, definita devreme "ca sa fie la indemana"), VFP compileaza
gresit: TOATE liniile de DUPA `ENDDEFINE` dau "Statement is not in a procedure"
(vezi `<script>.ERR` generat la compilare). Simptom in productie: `vfp9.exe`
pare 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 dinaintea `DEFINE CLASS` si s-a oprit silentios dupa eroarea de
compilare, fara sa ajunga la `QUIT`. Fix: muta orice `DEFINE CLASS` auxiliar
(dummy executor, helpere) la FINALUL fisierului, dupa `QUIT`, langa celelalte
`PROCEDURE` - 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>.ERR` langa `.prg` (log de compilare,
vezi si `roagest.ERR`/`log.txt` in 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-Type` cu `EnumWindows`/`GetWindowText`/
`GetWindowThreadProcessId` din `user32.dll`, filtrat pe PID-ul lui `vfp9.exe`.
Daca vezi doar fereastra principala + "Command", nu e dialog - cauta alta cauza
(vezi punctul de mai sus).
- **`STRTOFILE` inainte de `SET SAFETY OFF` = dialog modal "Overwrite?"** daca
fisierul de log exista deja (sub `-A`, SAFETY porneste ON). Pune `SET SAFETY OFF`
PRIMA linie sau sterge log-ul inainte de fiecare rulare.
- **`-A -T` sunt obligatorii impreuna** la lansare headless (`-A` ignora config.fpw,
`-T` sare peste splash screen-ul de start) - fara `-T` procesul 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 apeleaza
`goExecutor.oExecute(...)`/`oExecuta(...)`, dummy-ul trebuie sa fie o clasa
proprie (`DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute`/
`oExecuta` reale), nu un `Custom` cu proprietati adaugate.
- **Erorile de instantiere (`CREATEOBJECT`) raportate la linia CREATEOBJECT, nu la
`.Init`**: cand `ON ERROR`/`CATCH` arata `Procedure` = scriptul apelant (nu
`NUMECLASA.INIT`), inseamna ca eroarea vine din evaluarea proprietatilor/
obiectelor din DEFINITIA clasei (`ADD OBJECT ... WITH RowSource = ...`,
`ControlSource` de coloana grid evaluat ca expresie, etc.) - controale native
VFP (ex. combobox cu `RowSourceType=6` Fields) isi rezolva bind-ul la
CONSTRUCTIE, inainte sa apuce sa ruleze `PROCEDURE Init` a 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.fpw` propriu cu `SAFETY=OFF` si `RESOURCE=OFF`, pasat cu `-c<cale>`** (lipit de
flag, fara spatiu). Fara el, `COMPILE` peste un `.err`/`.fxp` existent si `STRTOFILE` peste
un log existent scot dialogul modal "overwrite?" si procesul pare agatat. `SET SAFETY OFF`
ca prima linie in script e complementar, nu inlocuitor (`-A` singur porneste cu SAFETY ON).
Verificare ca a fost preluat: logheaza `SET("SAFETY")` / `SET("RESOURCE")` la start.
- **`-c<config>` explicit are prioritate si merge impreuna cu `-A`**: desi `-A` ignora
config.fpw-ul implicit, un `-c` explicit tot e preluat (verificat 24.07.2026:
`vfp9.exe -c<config> -A -T script.prg` a raportat `SAFETY=[OFF] RESOURCE=[OFF]`).
- **Compileaza o COPIE a fisierului, in scratchpad, nu sursa din working copy**: `COMPILE`
scrie `.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 de `COMPILE`**, altfel nu poti distinge un `.err` ramas 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).