Achizitie import unificata (ROAGEST): ointroduceri, teste UI, documentatie

- 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
This commit is contained in:
2026-07-20 00:50:18 +03:00
parent 0fe9b25fe6
commit 53d9f1c64a
46 changed files with 16907 additions and 457 deletions

View File

@@ -0,0 +1,15 @@
# Conventie: GO pe Recno() capturat
`SELECT-SQL ... FROM cursor` lasa pointerul cursorului sursa pe EOF, deci un `Recno()` capturat dupa aceea poate fi `Reccount()+1` -> `GO` da "Record is out of range".
Regula: nu folosi `GO` direct pe un recno primit ca parametru sau capturat anterior; impacheteaza-l:
```foxpro
TRY
GO tnRecNo
CATCH
GO TOP
ENDTRY
```
Preferabil, unde exista cheie de rand (ex. doc_key): repozitionare prin `LOCATE` pe cheie in loc de recno absolut.

View File

@@ -18,6 +18,9 @@ max o linie, modificari minime).
- `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).
@@ -76,6 +79,15 @@ Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge
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
@@ -93,8 +105,64 @@ Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge
- 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 cu `round(...)` (ROAGEST 16.07.2026); model corect: Grid1.cTaxCode
(`Bound = .F.` + expresie, functional de ani).
## 6. Studiu de caz: "Property CNRCRT is not found" (15.07.2026)
## 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. 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

View File

@@ -1,7 +1,7 @@
# Flux editare .vcx/.scx pe text (comun proiectelor VFP)
Textul .vc2/.sc2 e sursa de editare; write-back in binar cu `txt2vcx.ps1`, fara VFP IDE.
Detalii complete: `D:\ROA\UTIL\foxbin2prg\CLAUDE.md`. Parametrii per proiect: in `docs\` proiectului.
Detalii complete: `D:\ROA\UTIL\foxbin2prg\CLAUDE.md`. Parametrii per proiect: sectiunea de mai jos.
**Proiecte migrate (ROACONT, COMUN, …): flux in-arbore.** Textul .??2 sta langa binare, il
improspateaza `git_sync.ps1 -ProjectRoot <root>` (nu cache extern) — la inceput de sesiune si
@@ -10,6 +10,12 @@ in IDE. `txt2vcx.ps1` cu `-CacheRoot` = `-ProjectRoot` (implicit). Pasul 0 de ma
rularea `git_sync.ps1`; restul pasilor raman la fel. Fluxul cu cache extern (`vcx2txt.ps1` +
`-CacheRoot` separat) ramane doar pentru proiectele nemigrate.
## Parametri per proiect (nemigrate, cache extern)
- **ROAGEST**: `-Project 'D:\ROA\ROAGEST\roagest.pjx' -ProjectRoot 'D:\ROA\ROAGEST'
-CacheRoot 'D:\ROA\_vfp_textcache\roagest'`; procese de verificat inainte de write-back:
`vfp9`, `roagest`; patch-uri de review in `docs/diff_runda<N>_<subiect>.patch`.
## Pasii unei runde
0. Refresh cache (obligatoriu dupa orice sesiune IDE — staleness pe mtime):
@@ -23,11 +29,25 @@ rularea `git_sync.ps1`; restul pasilor raman la fel. Fluxul cu cache extern (`vc
(exit 1 = normal). Utilizatorul revizuieste FISIERUL de patch si aproba; write-back-ul
(pasul 5) se face DOAR dupa aprobare. Commit doar dupa confirmare pe patch + test in IDE.
Comentariile in cod: maxim o linie (`*!* DD.MM.YYYY autor - motiv scurt`), fara explicatii lungi.
Obligatoriu inainte de a preda patch-ul la review: skill-ul de code-review rulat pe bucatile
de cod din diff (nu doar citire manuala) - prinde defecte gen IIF cu numar gresit de
argumente, deduplicari care nu se declanseaza niciodata, interogari mai largi decat e nevoie.
Review-ul verifica si compactitatea/reutilizarea: cod care refoloseste functiile si clasele
comune existente in loc sa le reinventeze (ex. cursor2lista()/cursor2listac() pentru liste
dintr-un cursor in loc de Scan manual; butoane din cmd_butoane.vcx in loc de butoane ad-hoc).
Inventarul comunelor: COMUN\docs\inventar-comun.md - se consulta si la pasul de plan/arhitectura,
inainte de a scrie cod nou.
4. Preconditie write-back: niciun proces vfp9/<exe proiect> (tin lock pe binar); nu se omoara.
5. Write-back: `txt2vcx.ps1 -TextFile <vc2> -ProjectRoot <root> -CacheRoot <cache>`
(+ `-AllowComun` cu aprobare explicita pentru tinte COMUN — afecteaza toate aplicatiile ROA).
Succes = fidelity-check trecut: binar cu mtime nou; `vcx2txt.ps1` ulterior il vede la zi.
6. Backup-urile `.pre_runda*.bak` si patch-urile se sterg la curatenia de dupa commit.
6. Teste finale pe cerinta initiala (cerinta Marius, valabila in toate proiectele VFP):
dupa ultima runda, teste e2e (harness UI, pe fluxul real al utilizatorului) care verifica
ca rezultatul construit *arata si se comporta* conform cerintei initiale — scenariul concret
dat de Marius, cu valorile lui asteptate — nu doar teste per-fix. Assert-uri pe continutul
rezultatului (sume, campuri, randuri), nu pe efecte de suprafata ("a aparut un rand" nu e
suficient).
7. Backup-urile `.pre_runda*.bak` si patch-urile se sterg la curatenia de dupa commit.
## Capcane
@@ -37,5 +57,14 @@ rularea `git_sync.ps1`; restul pasilor raman la fel. Fluxul cu cache extern (`vc
- Metoda noua de clasa cere `*m: nume` in `*<DefinedPropArrayMethod>`; fara ea, prima salvare
din IDE o arunca tacut, iar refresh-ul urmator absoarbe pierderea in cache (si in .bak-uri).
Override-urile de metode de baza (Init, Show, hook-uri) nu au nevoie de `*m:`.
- Salvarea din VFP IDE peste un binar scris de txt2vcx poate PIERDE si definitii `*m:` deja
existente, desi corpurile PROCEDURE raman (patit pe ROAGEST: `import_adauga_factura.recalc_tva`,
`import_nota.do_adauga/do_copie/do_reface`, 07.2026); simptom: crash la Createobject cu
"Data type mismatch" raportat la linia apelanta. Dupa orice sesiune IDE pe clasa: refresh
cache + compara lista `*m:` cu starea de dinainte.
- Nu folosi *Project > Build* ca sa recompilezi un binar vizat: rebuild-ul recompileaza toate
`.vcx`/`.vct` din proiect si le rescrie antetul (timestamp compilare) si posibil p-code-ul
intern si la librarii neatinse efectiv, aparand fals ca "modificate" in diff — de aici
fluxul text->bin punctual pe binarul vizat.
- Subagentii delegati primesc regulile 2-5 in prompt si raporteaza unicitatea sirurilor
inlocuite + rezultatul fidelity-check-ului.

315
docs/inventar-comun.md Normal file
View File

@@ -0,0 +1,315 @@
# Inventar COMUN - functii/clase reutilizabile (ROAGEST)
Scop: referinta rapida pt. plan/code-review, ca sa refoloseasca comunele in loc sa se reinventeze
(ex: Scan manual pt. lista dintr-un cursor, cand exista deja cursor2lista()). Generat prin grep pe
COMUN/programe/*.prg + cache text D:\ROA\_vfp_textcache\roagest (nu presupuneri). Numerele = nr.
aparitii grep in tot proiectul (indiciu de cat de "canonic" e functia).
## 1. Functii comune - COMUN\programe\oproceduri_comune.prg (clasa Oracle/executor + utilitare)
| Functie | Semnatura scurta | Ce face | Uz |
|---|---|---|---|
| oExecute | (cSql,cCursor,lProgress,cTitlu,nHandle,lShowError,lQuitOnError,lReconnect) | ruleaza SQL prin Oracle, deschide cursor. Retur: <0 eroare (vezi oPrelucrareEroare), 0 fara randuri, >0 nr. randuri | 851 |
| oExecuta | idem oExecute, wrapper | ca oExecute dar arata singur amessagebox la eroare si intoarce .T./.F. | 256 |
| oPrelucrareEroare | () | intoarce mesajul de eroare Oracle formatat, dupa un oExecute cu retur <0 | 93 |
| oFunction2Value | (cSql/cFunctie,...) | executa o functie Oracle si intoarce direct valoarea scalara | 31 |
| oSelect2Value | (cSql,...) | SELECT cu 1 coloana/1 rand -> valoare scalara | 58 |
| oSelecteaza2Value | variantul "Selecteaza" al lui oSelect2Value | idem, alt nume istoric | 20 |
| oSelecteaza2Object | (cSql,...) | SELECT 1 rand -> obiect cu proprietati = coloane | 23 |
| Connect / Disconnect / Oreset() | - | (re)conectare/deconectare goConn, reset stare executor | 33/10/119 |
| ProcessError | - | procesare interna eroare conexiune | 14 |
| amessagebox | (cMesaj,nIcon,cTitlu,...) | MESSAGEBOX wrapper standard ROA (icon/titlu/localizare) - **functia cea mai folosita din tot COMUN** | 1308 |
| amessage | - | varianta simpla fara icon/butoane multiple | 14 |
| cursor2lista | (cCursor,cColumn,cSeparator,cFiltru) | Scan cursor -> lista valori separate (";" default), pt. coloane necaracter | 36 |
| cursor2listac | (cCursor,cColumn,cSeparator,cFiltru) | ca cursor2lista dar incadreaza itemii intre apostrof (pt. SQL IN ('a','b')) - de folosit in loc de Scan manual | 2 |
| lista2array | (cLista,@aArray,cSeparator) | sparge o lista delimitata intr-un array | 35 |
| lista2cursor | (cLista,...) | lista delimitata -> cursor | 21 |
| get_mask | (nInt,nDec) | intoarce masca de format numeric (ex. "999,999.9999") dupa nr. intregi/zecimale | 24 |
| formateaza | - | formatare valoare dupa masca/tip | 18 |
| NEW_ID | (tabela,...) | genereaza urmatorul ID secvential pt. o tabela | 22 |
| NRORD | - | numar de ordine (contor afisare grid/raport) | 217 |
| NRCRT | - | numar curent (similar NRORD, alt istoric) | 108 |
| Get_Ora | (nTip) | ora curenta formatata dupa tip | 150 |
| ULTIMAZI / ultima_zi_din_luna | (an,luna) | ultima zi calendaristica a lunii | 16/25 |
| C_LUNA / CLUNA3 / cluna | (nLuna) | nume luna (lung/prescurtat) in romana | - |
| get_trimestru / get_semestru | (nLuna) | trimestrul/semestrul din numar luna | - |
| SAVE_GRID / RESTORE_GRID | (oGrid,cCheie) | salveaza/restaureaza layout coloane grid (latimi/ordine) in ini/tabela | 15/15 |
| SAVE_GRID_COMMENT / RESTORE_GRID_COMMENT | idem | variante cu comentarii coloane | - |
| SAVE_GRID_TAG / RESTORE_GRID_TAG | idem | variante cu tag/sortare curenta | - |
| snr_grid | - | switch_and_restore_grid - schimba sursa gridului si reaplica layout salvat | - |
| XMENU / XMENUP | - | meniu contextual generic (popup) | 22 |
| GETCHOICE | - | dialog alegere dintr-o lista | - |
| Get_Version | (lNoRevision,lInfo,cFileName) | citeste versiunea din resursele .exe | 15 |
| Get_Hexa | (nDeca) | conversie zecimal->hex | 12 |
| format_msg | - | formateaza mesaj cu parametri (gen sprintf) | - |
| GetHash | - | hash generic (verificare integritate/duplicate) | 48 |
| export_xls / ExportToXMLExcel | - | export cursor -> Excel (functii libere aici, la 1234/5502; NU sunt metoda goExport.export_xls din oexport.prg - cod si semnatura diferite, doar nume identic) | 24 |
| copiaza_structura_cursor | (cSursa,cDest) | copiaza doar structura unui cursor (fara date) | 27 |
| GETCALLSTACK | - | stack de apel curent, pt. debug/log erori | 24 |
| RemoveCharacters / RemoveCharactersDB | (cStr,cChars) | elimina caractere dintr-un string / varianta safe pt. query | 56/9 |
| XmlSpecialCharacters / WindowsSpecialCharacters / OracleSpecialCharacters | (cStr) | escapare caractere speciale pe context (XML/Windows/Oracle) | 19/11/- |
| EncryptDecrypt | (cStr,cCheie) | criptare/decriptare simetrica simpla | 23 |
| StringAgg | (cCursor,cColumn,cSeparator) | agregare valori coloana intr-un singur string (gen LISTAGG) | 18 |
| GetNrFromString / GetDigitsFromString / GetIBANFromString | (cStr) | extrage numere/cifre/IBAN dintr-un text liber | 18/- |
| Atasament2File / File2Atasament | - | conversie fisier <-> blob-atasament (baza de date) | 12/12 |
| romfast_suport / DownloadSupportFile | - | trimite/descarca fisier suport catre ROA | 18 |
| SaveBackupSet / RestoreBackupSet / ExistsBackupSet / DeleteBackupSet / UpdateBackupSet | - | set de proceduri pt. backup/restaurare setari private utilizator | 22/12/12/12/10 |
| GetOptiuneFirma / citeste_optiune_firma | (cCod) | citeste o optiune de configurare per firma | 20 |
| param_listari | (cRaport,cTitlu,cSemnaturi,lFaraTab) | pregateste parametri standard pt. rapoarte (titlu/semnaturi) | 19 |
| ceretitlu_rap | - | dialog cerere titlu raport | 16 |
| aInputBox | - | InputBox generic cu validare | 12 |
| GetPdfPath | - | cale generare/salvare PDF | 13 |
| GetDefaultValue / HasProperty / GetValue / SetValue | pe clase interne (ex. optiuni) | pattern generic proprietate-cu-default | 14/11/464/540 |
| verificare_note_contabile | - | valideaza notele contabile generate din import (echilibrare debit/credit) | 11 |
| verifica_partener / verificare_parteneri / verificare_parteneri_istoric | (cCUI/cCod) | valideaza/cauta partener in nomenclator dupa CIF/cod, inclusiv istoric | 10/4/6 |
| VerificaCodFiscal / GetMeniuVerificareCodFiscal / GetOptiuneVerificareCodFiscal | - | validare CUI/CIF (ANAF-like) | 10 |
| VerifCF / cValidCNP / ValidCNP / GetDataCNP | (cCod) | validare CIF/CNP + extragere data nastere din CNP | 9/- |
| VerifIBAN / verificare_iban / GetSumaControlIBAN | (cIban) | validare IBAN (cifra de control) | 8/4 |
| TVA2PROCTVA / GetProcTvaStandard / GetCoeficientTvaStandard / ProcentTva2IdJtva / GetIdJtvaStandard | - | conversii cota TVA <-> procent <-> cod TVA Oracle (gcS) | 6/11/-/-/5 |
| GetTaxCodeIdPart / GetTaxCode / GetPaymentCode | - | coduri taxare/plata pt. e-Factura ANAF | 12/-/- |
| GetArticolByCodDenumire / GetArticolById / GetArticolEFByPartDenumire | - | cautare articol nomenclator dupa cod/denumire/id | 10/6/- |
| GetPartenerByCodFiscal / GetPartenerByDenumire / GetPartenerByContBanca / GetPartenerByUtil / GetIdPartenerBy... / GetDenumirePartenerById / GetCodFiscalPartenerById | - | familie de cautari partener (id<->cod fiscal<->denumire<->cont banca) - **cauta_* pt. nomenclatoare, prioritar fata de SELECT ad-hoc** | 2 fiecare |
| GetSectieByCsectie / GetSectieById / GetIdSectie / GetGestiuneById | - | cautari sectie/gestiune | 2 fiecare |
| GetVencheltById / GetIdVenchelt | - | cautare venit/cheltuiala | 2 |
| GetDocumentByContPartenerAct | - | cautare document existent dupa partener+cont+act | 4 |
| GetFdocById | - | cautare tip document (fdoc) | - |
| GetValutaByISO | - | cautare valuta dupa cod ISO | - |
| NrLuni2AnLuna / GetDataIreg | - | conversii perioada an/luna | - |
| sir2array | - | split string -> array (generic, non-cursor) | - |
| nvl2 / nvl2Default | (val,default) | echivalent NVL Oracle in VFP | 11 |
| TooManyInstances | (nInstante) | verifica daca ruleaza deja alta instanta a exe-ului | - |
| MyUnzip / UnzipQuickShell / MyUnzipCallBack | - | dezarhivare (update-uri, atasamente) | - |
| HTTPDownloadFile / DownloadFile | - | descarcare fisier prin HTTP | - |
| lansare_help / lansare_help_meniu / arata_manual | - | deschide manualul/help-ul aplicatiei | - |
| viz_prg_instalat / versiune / CheckNewVersion / CheckVersion / UpdateVersion | - | verificare versiune instalata vs. server update | 57 (versiune) |
| xdir / xdelete / DeleteFiles | - | utilitare fisiere (listare/stergere) | 14/- |
| ImportTable | - | import tabel extern (DBF/CSV) | - |
| GET_SCHEMA_GRID | - | genereaza schema coloane pt. grid dinamic | - |
| CloneObj / CopyProps | (oSursa,[oDest]) | clonare obiect / copiere proprietati intre obiecte | 10/- |
| Get_zileluc | - | zile lucratoare | - |
| GetUserDocPath | - | folder documente utilizator curent | - |
| aleg_document_pereche | (vezi ooperatii_comune) | vezi sectiune 2 | - |
Nota: multe din functiile de mai sus exista **dublat** in `proceduri_comune.prg` (varianta veche,
non-obiect) - ex. NEW_ID, SAVE_GRID*, XMENU, cursor2lista*, lista2array/cursor, get_mask, Get_Version,
Get_Hexa, format_msg, C_LUNA/cluna, get_trimestru/semestru, GETCALLSTACK, CloneObj/CopyProps,
ceretitlu_rap. La duplicat castiga fisierul adaugat **cel mai recent** cu SET PROCEDURE ADDITIVE -
in roagest.prg OPROCEDURI_COMUNE.PRG (linia 238) vine dupa proceduri_comune (linia 218), deci
varianta activa e cea din `oproceduri_comune.prg`. Din varianta veche raman folosite doar 2 functii
unice: `extrage_optiuni(cLista,nId)` (valoare dintr-o lista "id::val;id::val", 11 uz) si
`myScatter(tcBlank)` (SCATTER generic in obiect, 3 uz); restul functiilor unice de acolo = moarte.
## 2. Functii comune - COMUN\programe\ooperatii_comune.prg (contabilitate/analitice/balanta)
| Functie | Ce face | Uz |
|---|---|---|
| viz_act | vizualizare document (act) generic | - |
| aleg_document_pereche | alege documentul pereche (ex. factura<->aviz) la asociere | - |
| exista_cont_in_plan_conturi | verifica daca un cont exista in planul de conturi | - |
| verific_partener | valideaza/completeaza partener pe o inregistrare contabila | 7 |
| verific_analitic / verific_analitic_ | valideaza analitic contabil (cont+subcont) | 7/2 |
| verificare_note_contabile | valideaza echilibrarea notelor contabile generate | 11 |
| sterge_nota / scrie_nota_import | sterge/scrie nota contabila generata din import | - |
| do_assign_analitic | atribuie analitic automat pe o linie | - |
| verific_cont | verifica existenta/valabilitate cont contabil | 9 |
| viz_balanta_contabilitate / viz_balanta / make_tbalanta / Eval_balanta | construire/vizualizare balanta de verificare | - |
| completare_gruputil_analitice / completare_tva_analitice / completare_taxname / completare_paymentname / completare_explicatie_tva | completeaza campuri derivate pe linie contabila (grup util, TVA, taxname, explicatie) | - |
## 3. Export si listare - COMUN\programe\oexport.prg (clasa oExportConfig -> obiect global goExport)
Pattern canonic: `goExport.export2frx(...)` / `goExport.export2xlsx(...)` / `goExport.export2pdf(...)`.
Preview rapoarte: FoxyPreviewer (`gcReportPreviewer = "FoxyPreview"` in roagest.prg).
| Functie/metoda | Semnatura scurta | Ce face | Uz |
|---|---|---|---|
| export2frx | (tcAlias,tcRaport,tlCereTitlu,...,tcReportPreviewer,toPreviewerConfig) | REPORT FORM cu preview/print (FoxyPreview), logo, imprimanta din setari - **listarea standard, cea mai folosita** | 155 |
| export2xls | (tcAlias,tcNumeFisier,tcListaColoane,tcFiltru) | EXPORT TO ... TYPE XL5 (.xls vechi) + deschide fisierul | 30 |
| export2xlsx | (tcAlias,tcNumeFisier,tcListaColoane,tcFiltru) | copytoxlsx() - XLSX nativ modern, **preferat pt. cod nou** | 7 |
| export2xlsxGrid | (toGrid,tcNumeFisier,tcWhere,tcOrder) | schema din grid (get_schema_grid) -> SELECT -> export2xlsx | 3 |
| exportMultiTable2Xlsx | (tcTableList,tcFileName) | mai multe tabele in acelasi workbook xlsx (clasa VFPxWorkbookXLSX) | 3 |
| Export2XmlXls | (toGrid,tcFileName,tcSheetName,tlOpen) | XML SpreadsheetML via clasa excelxml, direct din grid | 2 |
| export2pdf | (tcAlias,tcRaport,tlPreview,tcCursorSalvare) | REPORT FORM OBJECT pdflistener2 -> PDF; optional continutul in cursor | 26 |
| export2html | (tcTargetTables,tcNumeFisier,tcTableTitles,tlJustTables) | HTML/Excel-HTML din unul/mai multe tabele (TEXTMERGE) | 9 |
| export2xml_efactura | (toDateFactura,tcCursorLinii,tlSilentios) | wrapper getXmlEFactura() - XML e-Factura ANAF | 6 |
| export2frx_paginaregrup | (...) | listare cu numerotare pagini per grup (crsPaginiRaportGrup) | 6 |
| getRepPath / getLogoPath | (tcRaport / tcLogo) | rezolva USR_<raport>.frx / logo per schema (DIRGEN\USERREPORTS, atas_atasamente) | 24/10 |
| OPEN_DEFAULT_APP | (tcNumeFisier) | ShellExecute "open" cu aplicatia implicita | 70 |
| citesteSetare / modificaSetare (+ *Ini/*Registru) | (cCheie[,cVal]) | setari registru/ini dupa setTip/setPath/setSectiune | 13/11 |
| verificaDate | (tcAlias,tnTip,tlSilentios) | valideaza alias deschis/cu date inainte de export | 16 |
| listareUserReport / export_frx / export_xls | - | dispatcher + variante vechi (marcate de inlocuit); metoda export_xls != functia libera export_xls din oproceduri_comune.prg | 22/13/26 |
| FoxyPreview (functie libera, nu pe goExport) | (tcRaport,toPreviewerConfig) | preview FoxyPreviewer.App direct pe .frx, apelata din formulare | 19 |
| FoxyPreviewerCaller (clasa) | AddReport(tcReport,tcClauses) / RunReport | combina mai multe .frx intr-un singur preview | 7 |
| PreviewerConfig (clasa) | SetValue/GetValue | property-bag optiuni FoxyPreviewer (param toPreviewerConfig la export2frx) | - |
### Export Excel - 3 mecanisme paralele (atentie la confuzii)
1. `goExport.export2xls/export2xlsx` (mai sus) - preferat pt. cod nou (export2xlsx).
2. `export_excel` / `export_excel_grid` / `exportare` / `LIST_EXCEL` (proceduri_excel.prg) - OLE
Automation, scrie celula cu celula, necesita Excel instalat; uz mic (2-12). `get_excel_mask` de
acolo = conversie masca VFP -> format Excel (nu confunda cu `get_mask` din sect. 1).
3. Clasa `excelxml` (excelxml.vcx, COMUN\UTILE\excel) - Excel XML direct din `gridobject`, fara
Excel instalat (proprietati: file, sheetname, openaftersaving; metoda `save`); 73 uz.
Plus functiile libere `export_xls`/`ExportToXMLExcel` din oproceduri_comune.prg (sect. 1) - cod
diferit de metoda `goExport.export_xls`, doar nume identic.
## 4. Utilitare transversale (COMUN\programe, fisiere neinventariate initial)
### gencursor.prg
| Functie | Semnatura scurta | Ce face | Uz |
|---|---|---|---|
| gencursor | (cNume,cAlias,cSelect,cFiltru,cSchema,cOrder,lAfisare,cGroup,...) | obiect deca_baza/ca_baza cu cursor "lazy" (select/filtru/order pe goConn) - **baza grilelor cu sursa dinamica; nu construi manual ca_baza** | 403 |
### validare.prg - validare CIF/CNP cu ANAF/MFIN/VIES (vs. VerifCF/ValidCNP offline din sect. 1)
| Functie/clasa | Ce face | Uz |
|---|---|---|
| VerificareCod (clasa) | validare completa cu webservice: metode VERIFICARE_CIF/VERIFICARE_CNP/ANAF/MFIN/VIES/validare/GetError | 169 |
| VerificareANAF (clasa) | sincronizare lista CIF-uri cu ANAF (VerificaListaCIF, ANAF_SincronWebService_PlatitorTva) | 26 |
| test_cif / VALIDARE_CIF / test_cnp / VALIDARE_CNP | validari format + cifra control, offline | 27/19/9/15 |
| VERIFICARE_PLATITORTVA / VIES_VAT / ONRC | platitor TVA / verificare VIES / interogare ONRC | 5/5/9 |
| IntroducereCod (clasa Form) | formular introducere + validare cod | 25 |
### ocautare.prg - dialoguri standard de cautare/alegere (de refolosit in loc de SELECT+grid manual)
| Functie | Ce alege | Uz (prg/forme) |
|---|---|---|
| caut_gestiune / caut_gestiune_dest | gestiune (sursa/destinatie) | 25/324, 12/37 |
| caut_fdoc | tip document | 7/170 |
| caut_explicatie_tva | explicatie TVA (filtrare exigibil JC/JV) | 9/143 |
| caut_partener / CautPartenerContabilitate | partener (generic / dupa cont contabil) | 13/104, 16/16 |
| caut_valuta / caut_valuta_iso | valuta | 11/104 |
| cauta_nom_articole / caut_articol | articol (filtru text / multiplu) | 8/87, 20/42 |
| caut_sectie / caut_facturi | sectie / facturi dupa cont+partener+interval | 8/71, 12/34 |
| caut_lucrare, caut_venchelt, caut_contract, caut_delegati, ... | ~40 alte dialoguri de nomenclator, uz mic in ROAGEST | 2-17 |
### oheader.prg
| Clasa | Ce face | Uz |
|---|---|---|
| PSHeader (As Header) | dublu-click pe header = sortare (cHOrder), evidentiaza coloana activa, apeleaza cCallMethod (default Refresh) pe parintele gridului | 37 in forme |
### regex.prg
| Functie | Ce face | Uz |
|---|---|---|
| strtranx | STRTRAN cu regex (grupuri $1, evaluare =expr) | 14 |
| GetRegExp / GetRegExpAll / OccursRegExp / StrtranRegExp | prima/toate potrivirile, numarare, inlocuire simpla | 15/4/4/2 |
| StripNonAscii | elimina caractere non-ASCII dintr-un string (whitelist) | 11 |
### ini.prg
| Functie | Semnatura | Ce face | Uz |
|---|---|---|---|
| getini / setini | (cIniFile,cSection,cVar[,cVal]) | citire/scriere fisiere .ini low-level (FOPEN) - distinct de GetOptiuneFirma (Oracle) si goExport.citesteSetareIni (wwApi) | 28/9 |
### altele (o functie-doua per fisier)
| Functie (fisier) | Ce face | Uz |
|---|---|---|
| get_oluna (oproceduri_util.prg) | (an,luna) -> obiect cu intervalul calendaristic disponibil (lunamin/anmin/lunamax/anmax) din tabela calendar | 22 |
| ReferinteDocumenteNota / ReferinteDocument (odocumente.prg) | verifica daca un document e referentiat de altul (pack_documente Oracle) - inainte de stergere/modificare | 4/4 |
| anunta_rezultat / scrie_in_mesaje / raport_mesaje (mesaje.prg) | log mesaje de verificare in tabel + raport; uz de nisa | 2-3 |
## 5. Clase UI comune (COMUN\clase, cache text D:\ROA\_vfp_textcache\roagest\comun\clase)
### Butoane - cmd_butoane.vcx (parinte: _cmd_base.vcx -> buton/cmd_buton)
Mecanism: fiecare buton seteaza doar proprietatea `caction` (nume metoda) + `cpictureup/down`.
`buton.Click` (in `_cmd_base.vcx`) cauta metoda `caction` pe `this.Parent`, apoi `this.Parent.Parent`,
apoi `Thisform`, si o executa cu `clistaparametri` ca argument daca exista (Pemstatus + macro &).
**Nu suprascrie Click pe butoane noi - seteaza doar caction pe o instanta din cmd_butoane.vcx.**
| Clasa buton | caction | Rol |
|---|---|---|
| but_nou / cmd_adauga | do_adauga | adauga inregistrare noua |
| but_copiaza | do_copiaza | copiaza inregistrarea curenta |
| but_sterge / cmd_sterge | inainte_de_do_sterge | sterge (cu hook de confirmare) |
| but_modifica / cmd_modifica | inainte_de_do_modifica | intra in modificare |
| but_termin / but_da / cmd_termin | do_termin | confirma/salveaza si inchide |
| but_renunt / but_nu / cmd_renunt | do_renunt | renunta/inchide fara salvare |
| but_salveaza | do_salvare | salveaza fara inchidere |
| but_reset / cmd_reset | do_reset | reseteaza formularul/filtrul |
| but_cautare / cmd_cauta | do_cauta | deschide cautare |
| but_compara | do_compara | comparare inregistrari |
| but_verifica | do_verifica | validare |
| but_retur | do_retur | operatie retur |
| but_precedent / but_urmator / but_urmator_tot | do_precedent / do_urmator / do_urmator_tot | navigare inregistrari |
| but_excel | inainte_de_do_excel | export Excel |
| but_listare | inainte_de_do_listare | listare/raport |
| but_factura / but_listfactura | do_factura / do_listfact | facturare/listare factura |
| but_grafic | do_grafic | afisare grafic |
| but_help | do_help | help contextual |
| but_modiparam | do_modiparam | modificare parametri |
| but_attach | do_ataseaza | ataseaza fisier |
| but_reface | inainte_de_do_reface | reface/reia operatie |
| cmd_login / cmd_logoff | do_login / do_logoff | autentificare |
| cmd_executa / cmd_select | do_executa / do_select | executie/selectie generica |
| but_sus / but_jos | do_scrollup / do_scrolldown | scroll |
### Container label+text - lb_tx.vcx (parinte: _container, folosesc _lbbase/_txbase din _lb_base.vcx/_tx_base.vcx)
| Clasa | Rol |
|---|---|
| clb_tx_simplu / cb_tx_simplu | label + textbox simplu |
| cb_tx_autofill | label + textbox cu autocompletare (cautare in nomenclator) |
| cb_tx_cautare | label + textbox + buton cautare integrat |
| clb_tx_data | label + textbox cu masca data |
| clb_an_luna | pereche an/luna |
| clb_luna_intre / clb_nr_intre | interval luna/numar (de-la, pana-la) |
| clb_n1n2_simplu | pereche 2 numere simple |
| ct_cb_tx | container compus text+combo |
| ed_tx_simplu | label + editbox |
| op_tx_simplu | label + optiongroup |
Proprietate cheie comuna: `pcobj` (leaga containerul de camp/proprietate din obiectul sursa).
### Baze formular - _frm_base.vcx / _frm_child.vcx
| Clasa | Rol |
|---|---|
| _frmbase | baza standard formular; implementeaza deja `do_adauga/do_copiaza/do_modifica/do_renunt/do_sterge/do_termin` (override doar daca logica difera de default) |
| _frmnotitle | ca _frmbase, fara bara de titlu; are propriile do_adauga/do_modifica/do_renunt/do_sterge/do_termin |
| frm_termin | subclasa _frmbase, expune direct do_termin |
| frm_termin_renunt | subclasa frm_termin, adauga do_renunt (fara override suplimentar) |
| frm_gol | subclasa _frmbase "goala", cu propriile do_adauga/do_modifica/do_sterge |
| _pgfrmbase | baza pageframe |
### Grid - _grd_base.vcx
| Clasa | Rol |
|---|---|
| _grdbase | grid de baza (subclasa _grid) |
| _grdrow | subclasa _grdbase, randuri custom |
| _grdfooter | grid separat "atasat" la un grid sursa prin metoda `attachtogrid` (copiaza coloane/fonturi de la oSrcGrd) - pt. randuri de total/footer sub un grid principal |
## 6. Conventii transversale
- Prefixe variabile: `g`/`gc`/`gn`/`gl` (globale char/numeric/logic), `p`/`pc`/`pn`/`pl` (proprietati
obiect), `t`/`tc`/`tn`/`tl` (parametri PARAMETERS/LPARAMETERS), `l`/`lc`/`ln`/`ll` (locale), `m.`
(scoping explicit memvar quand hay coliziune cu camp).
- SQL pass-through: mereu prin `goExecutor.oExecute`/`oExecuta`, niciodata SQLEXEC direct in cod nou;
schema calificata cu `gcS` (ex. `gcS + ".STOC"`); parametri legati cu `?param` (bind variable VFP->Oracle).
- Retur executor: oExecute -> numeric (<0 eroare/`oPrelucrareEroare()`, 0 fara randuri, >0 nr. randuri);
oExecuta -> logic (.T./.F., arata singur eroarea).
- Cursoare temporare: prefix `crs*` (ex. `crsListaTemp` in cursor2lista/cursor2listac).
- Mesaje utilizator: mereu `amessagebox()`/`amessage()`, nu `MESSAGEBOX()` direct.
- Formatare numerica: `get_mask()` + `formateaza()`, nu masti hardcodate in cod nou.
- Fisiere `.vc2`/`.sc2`: TAB-uri semnificative, proprietatile in blocul `*<PropValue>` sunt
alfabetizate - orice editare manuala trebuie sa respecte formatul (vezi CLAUDE.md, flux editare text).
## 7. Ce am lasat pe dinafara (intentionat)
- `proceduri_comune.prg` (varianta veche) - acoperita acum in nota de la sect. 1 (2 functii unice
folosite, restul dublate sau moarte); functiile ei unice cu 0 aparitii (caut_nume_politica,
make_sectii_utilizator, recompune_optiuni, cere*/compara*, debug_*_eventtracking, ...) - omise.
- `oproceduri_comune_imob.prg` - varianta specifica ROAIMOB, nu ROAGEST.
- `xdate.prg` - script procedural (incarca cursoarele xsets/xrequest/xnote la DO), nu expune functii.
- Metoda `getUserRepPath` din oExportConfig - complet comentata (moarta); varianta activa e functia
libera omonima din `proceduri_rapoarte.prg` (cod diferit).
- Clase de nomenclatoare mari (`onomenclatoare.vcx`, `onom_articole.vcx`, `stocuri.vcx`, `rulaje.vcx`,
`ointroduceri*.vcx`, `oinventar.vcx`) - business logic specific gestiune, prea voluminos pt. acest
inventar (candidat pt. un inventar separat, per-flux, daca devine necesar).
- Clasele de configurare/serii (`configurare.vcx`, `serii_numere.vcx`, `ferestre_oracle.vcx`,
`bon_fisc.vcx`) si utilitarele (`GridExtras`, `ctl32`, `hpdf`) - neinventariate, uz mai rar
in fluxurile curente de lucru (`excelxml.vcx` e acoperita la sect. 3).
- Functiile din `COMUN\programe` fara nicio aparitie gasita prin grep in ROAGEST (posibil folosite
doar de alte aplicatii ROA) - omise.
## 8. Cum se foloseste
- **La plan**: inainte de a scrie cod/obiecte noi, scaneaza acest fisier + un grep tintit
(`grep -rn "<nume_functie_candidata>" COMUN/programe D:\ROA\_vfp_textcache\roagest`) ca sa
confirmi ca nu exista deja o comuna echivalenta.
- **La code-review**: daca un diff introduce Scan manual pt. liste, MESSAGEBOX direct, SQLEXEC direct,
un buton cu Click suprascris in loc de `caction`, un export Excel/listare construite manual in loc
de `goExport.export2*`/FoxyPreview, sau un dialog de alegere nomenclator facut din SELECT+grid in
loc de `caut_*` (ocautare.prg), semnaleaza reutilizarea comunei corespunzatoare din tabelele de mai sus.

View File

@@ -0,0 +1,40 @@
# Orchestrare cu subagenti: context mic, misiuni descompuse
Regula (valabila in toate proiectele ROA/VFP): un singur subagent care duce o misiune lunga
(iteratii de teste, investigatii, mai multe sarcini inlantuite) acumuleaza context urias
(s-a ajuns la ~800k tokens) — calitate degradata, cost mare. Nu se lucreaza asa.
## Cum se lucreaza
- Sesiunea principala (sau un agent primar) e ORCHESTRATOR: imparte planul pe sarcini
independente (un test, un audit, un scenariu = o sarcina), monitorizeaza, transmite
constatarile intre agenti, verifica rezultatele.
- Fiecare sarcina merge la un SUBAGENT PROASPAT cu prompt autosuficient: context complet
in prompt + trimitere la un fisier de handoff, nu context acumulat in conversatie.
- HANDOFF compact pe disc (docs\handoff_*.md, stil telegrafic): inventar livrabile,
stare, ce ramane, detaliile ne-evidente pe care un agent proaspat nu le-ar putea
reconstitui usor. Se actualizeaza la fiecare predare de stafeta.
- PRAG: maxim ~200-250k tokens per subagent. Orchestratorul urmareste dimensiunea
contextului; la atingerea pragului: cere handoff, inchide agentul, respawneaza unul
proaspat pe sarcina urmatoare.
- Sarcinile independente ruleaza in PARALEL (lane-uri), ex. audit read-only + editare teste.
- Orchestratorul da utilizatorului UPDATE-URI SCURTE de progres, regulat (monitor pe
artefacte: loguri/fisiere/screenshots; o propozitie-doua la fiecare schimbare relevanta,
fara naratiune la evenimente fara continut).
- Orchestratorul MONITORIZEAZA ACTIV subagentii sa nu se blocheze: monitor persistent pe
artefactele lor + verificare la lipsa de progres (~10 min fara schimbare = suspect):
procese agatate (vfp9 zombi, dialoguri modale), semafoare orfane, timeout-uri de tool
(120s implicit) care omoara procese. La blocaj: diagnostic direct (procese, fisiere,
loguri), mesaj de deblocare cu constatarea concreta, si abia apoi restart daca e cazul.
- Modele: subagentii care DOAR ruleaza/editeaza teste si alte sarcini de volum/rutina ->
Sonnet; ORCHESTRATORUL subagentilor (cel care imparte, monitorizeaza, deblocheaza,
decide) -> Opus (sau sesiunea principala). Commit doar dupa review-ul diff-ului de
catre om (vezi reguli_lucru.md).
## Semne ca ai gresit impartirea
- Acelasi agent primeste "inca o sarcina" a treia oara -> trebuia agent nou + handoff.
- Agentul re-descopera capcane deja documentate -> handoff-ul/docs-ul e incomplet;
completeaza-l, nu repeta investigatia.
- Orchestratorul face el insusi munca de volum -> deleaga si pastreaza-si contextul
pentru decizii, verificari si deblocari.

View File

@@ -13,3 +13,6 @@
Metoda de depanare (log.txt, cascade ON ERROR, bisectie clase): `depanare_testare_vfp.md`.
5. Editare .vcx/.scx pe text (cache, cp1252 byte-safe, fidelity-check): `flux-editare-vfp-text.md`.
Cautare in binare: `cautare_vcx_vct.md`.
6. Misiuni lungi (teste, investigatii, sarcini inlantuite): NU un singur subagent cu context
acumulat — orchestrator + subagenti proaspeti per sarcina (max ~200-250k tokens/subagent),
handoff compact pe disc: `orchestrare-subagenti.md`.

132
docs/testare-ui-vfp.md Normal file
View File

@@ -0,0 +1,132 @@
# Testare UI VFP headless cu harness + screenshots
Harness reutilizabil pentru teste care afiseaza un formular REAL (fara Oracle/UI complet),
executa actiuni si valideaza vizual prin capturi de ecran. Valabil pentru toate proiectele
VFP din suita ROA; exemplele sunt pe ROAGEST. Alternativa la
`testare-vfp-mcp.md` (care ruleaza programul principal intreg + windows-mcp): aici instantiem o
singura clasa cu dependinte dummy, deci pornire rapida si izolata.
Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, parametrizat),
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri VFP de handshake),
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele de
teste stau grupate pe subiect in subfoldere (ex. `achizitie_import\`). Exemplu complet:
`achizitie_import\test_import_nota_ui.prg` + `.ps1` (wrapper).
**REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia (ex. "Adauga repere",
do_adauga_factura prin dialogul real), NU introducerea directa a randurilor in cursoare/tabele
(ex. Append in rul_temp/introdc). Popularea directa sare peste validari/filtre/efecte laterale
si da PASS fals (caz real: articol cont 212 "adaugat" direct in rul_temp trecea testul, dar prin
fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
dialoguri modale), nu pe pasii fluxului testat.
## Test UI nou in 5 pasi
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET TALK OFF` (obligatoriu, vezi cap. c);
`SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
`SET PROCEDURE TO ...\ui_harness.prg ADDITIVE` si (daca ai dialoguri)
`...\mock_amessagebox.prg ADDITIVE`.
2. Creeaza dummy-urile minime (`goExecutor`, `goApp`, etc.) + cursoarele pe care le cere
Init-ul clasei (RecordSource-urile grilelor).
3. Instantiaza clasa, `WindowType=0`, `Show()`, `WindowState=2`.
4. Pe fiecare pas: fa actiunea, verifica efectul in date (loghezi PASS/FAIL cu un helper
local), apoi `DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la
orchestrator sau auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
5. Ruleaza: `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
PNG-urile ies in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
## Capcane / deblocari (toate confirmate in sesiune)
a. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
finalul pasilor. Ruleaza-l in background (run_in_background) + asteapta `done.txt`, sau
timeout mare (300-600s). NU porni VFP separat de orchestrator - si-l lanseaza singur.
b. **Asteptarea ready_0 prea scurta** omoara VFP exact inainte sa scrie semaforul. Cu `.fxp`
cald START apare in ~2s, la rece mult mai mult (zeci de classlib-uri). Foloseste
`-ReadyTimeoutSec` generos (implicit 180s). Retry NUMAI la "start ratat", nu la "start lent".
c. **`SET SAFETY OFF` + `SET TALK OFF` ca PRIMUL lucru, inainte de primul `STRTOFILE`.**
Cu SAFETY ON, prima scriere a log-ului (daca fisierul exista deja) scoate dialogul modal
"...already exists, overwrite it?" care BLOCHEAZA rularea headless (VFP pare "instanta moarta"
- proces viu, blocat pe dialog). Cu TALK ON, `SUM`/`CALCULATE` (ex. din `recalculeaza`) isi
echo-eaza rezultatele pe desktop, ca text parazit peste formular in screenshots. Harness-ul
are `HarnessInit`/`SET SAFETY OFF` defensiv in procedurile de scriere, dar testul trebuie
sa faca `SET SAFETY OFF` oricum INAINTE de scrierea lui proprie de "START".
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log ramane gol).
Lanseaza `.fxp`-ul PRECOMPILAT (`vfp9 -A test.fxp`) - se executa mereu.
e. **Precompilarea (`COMPILE`+`QUIT`) atarna** dupa COMPILE; rulata in ACEEASI sesiune
powershell care lanseaza apoi testul, otraveste lansarile (nu executa). Ruleaza precompilarea
intr-un proces copil izolat (`Start-Process -Wait _precompile.ps1`).
f. **Detectia "a pornit" pe mtime-ul log-ului > momentul lansarii**, nu pe "log ne-gol": un log
stale tinut deschis de un `vfp9` zombi pacaleste verificarea. Omoara zombii inainte de fiecare
lansare.
g. **Formulare modale** (`WindowType`) - `Show()` nu returneaza si blocheaza testul. Seteaza
`goObj.WindowType = 0` (modeless) pentru afisare non-modala automatizata.
h. **Dummy minimali**: `goExecutor` (oExecute/oExecuta creeaza cursorul cerut), `goApp`
(`ReadIni` pt. font din settings.ini), `poAct`/`oact` (curs valutar). Init-ul recreeaza
cursoarele goale din Oracle daca nu-s pre-populate - pre-populeaza-le INAINTE de instantiere.
i. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded cu spatii nu gaseste nimic** (`==` e
exact) - foloseste `ALLTRIM(camp) == 'literal'`.
j. **Messagebox-uri de confirmare blocheaza testul headless**. Mock: `FUNCTION amessagebox`
care returneaza direct valoarea butonului (6=Da). Incarca-l cu `SET PROCEDURE ... ADDITIVE`
**PRIMUL** (inaintea `oproceduri_comune.prg`): la nume duplicat de procedura, VFP foloseste
fisierul cautat PRIMUL in lista (verificat empiric: incarcat ultimul PIERDE, primul CASTIGA).
Mock-ul umbreste `amessagebox` si din apelurile din metodele de clasa (ex. `do_sterge_articol`).
k. **Cursor bound la o grila, recreat de Init -> grila pierde coloanele (eroare 1925).** Ex.:
`import_nota.Init` recreeaza `rul_temp` din Oracle daca e gol la instantiere; GridArt ramane
fara coloanele denumite. Fix: pune 1 rand placeholder in cursor INAINTE de instantiere (ca Init
sa nu-l recreeze), apoi **`ZAP`** dupa `Show()` (NU `DELETE ALL`: acesta lasa un rand fizic
sters si pointerul la EOF, iar cod gen `do_calculeaza_diferente` face `GO recno_EOF` -> eroare 5).
l. **CREATEOBJECT/ADDPROPERTY se comporta gresit apelate DINTR-O PROCEDURA in acest runtime headless**
(obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din programul PRINCIPAL
merg. Construieste obiectele (ex. `toDlg` pt. `do_adauga_factura`) INLINE in main, nu intr-un
helper. (Cauza exacta neclara; reproductibil.)
m. **`gl406=.T.` (SAFT) activeaza cod care are nevoie de mock-uri suplimentare**, altfel crapa sau
taxcode ramane mereu NULL: `GetTaxCode`/`GetTaxCodeIdPart` (oproceduri_comune.prg) apeleaza
`update_jtva_coloane()` care ruleaza `goExecutor.oExecute("select * from vjtva_coloane ...", <cursor>)`
- mock-ul de `oExecute` trebuie sa intercepteze `'VJTVA_COLOANE' $ UPPER(tcSql)` si sa returneze
un cursor cu structura reala a view-ului (`id_jtva_coloana, denumire, jc, jv, coloana_jc,
coloana_jv, cota_tva, id_tva, taxcode, taxcode_neexigibil, taxcode_n50, taxcode_n50_tvai,
taxcode_n100, taxcode_regularizare, taxcode_tvai, afisat, ordine`) - codul apelant face singur
`INDEX ON id_jtva_coloana TAG id_jtva` dupa, nu trebuie indexat in mock. Cand `id_partc`/`id_partd`
pe rand nu e gol, `GetTaxCodeIdPart` cauta si codul fiscal al partenerului prin
`goExecutor.oSelect2Value(sql, @toValue)` (`GetCodFiscalPartenerById`) - metoda lipseste din
dummyexecutor-ul de baza, trebuie adaugata (returneaza cod fiscal gol -> `VERIFICA_RTVAI` sare
peste apelul Oracle real de validare CIF).
n. **Explicatia TVA de pe randul T ("in tandem" cu baza) NU se scrie prin cod separat** - e pur si
simplu o A DOUA intrare in `jtva_coloane2`/`vjtva_coloane` (coloana `id_tva` de pe randul bazei
punctand spre `id_jtva_coloana` al randului TVA); `aplica_explicatie_tva` (ointroduceri.prg
~9807) citeste acea a doua intrare si o scrie pe rand T. Populeaza mock-ul cu ambele randuri
(baza + TVA), nu doar cu unul si nu concatena manual "TVA " + denumirea bazei in test.
o. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` (captura ecranului intreg) fura
focus si se corupe daca utilizatorul lucreaza in paralel - NU se mai foloseste. Capturile se fac
cu `PrintWindow` (user32, P/Invoke) pe HANDLE-ul ferestrei vfp9 (`Process.MainWindowHandle`),
flag `2` = `PW_RENDERFULLCONTENT` (fallback flag `0` daca iese goala); merge cu fereastra
acoperita/in fundal, dar NU minimizata. Fereastra e mutata OFF-SCREEN (`SetWindowPos` cu
`HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce apare handle-ul si RE-IMPINSA la fiecare
pas (VFP isi reactiveaza singur fereastra la `Show()`/dialoguri modale - o singura impingere la
lansare nu e suficienta). Toate consolele powershell copil (precompilare) sunt `-WindowStyle
Hidden` (sigur pentru consola; NU si pentru GUI VFP, care trebuie doar off-screen, nu ascuns -
PrintWindow pe fereastra minimizata/ascunsa poate reda gol). Implementat in
`vfp_ui_harness.ps1` (Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si
`_precompile.ps1` (acelasi Push-Offscreen pe fereastra tranzitorie de compilare).
p. **Cursorul `introdc` FARA indexul `ord_doc` schimba documentul "principal" la sincronizari
repetate** (sincronizeaza() apelat de mai multe ori, ex. la adaugarea a 2+ articole succesiv
prin fluxul real): identificarea documentului principal (`Go Top` + `Locate For
Inlist(tip_rand,'B','S') And !Empty(doc_key)`) e stabila in productie doar pentru ca
`achizitie_import` (COMUN\programe\ointroduceri.prg:1616-1618) creeaza, INAINTE de
`CREATEOBJECT('import_nota')`, indexul `ord_doc` (`Str(Nvl(nr_doc,0),8) +
Icase(tip_rand='B','1', tip_rand='S','2', tip_rand='T','3', tip_rand='D','4', '5') +
Padr(Nvl(scd,''),10)`) + `SET ORDER TO ord_doc`, care ordoneaza dupa `nr_doc` (S-urile
principalei mostenesc nr_doc-ul ei) - documentul cu `nr_doc` minim ramane mereu acelasi. Un
test care creeaza `introdc` fara acest index ramane pe ordinea FIZICA (recno): dupa ce
principala e sparta (randul B sters, randurile S adaugate la coada), urmatorul apel de
sincronizeaza() gaseste gresit ca "principal" documentul cu cel mai mic recno ramas si il
sparge si pe el - cascada se repeta la fiecare apel ulterior. Fix: dupa popularea `introdc`,
INAINTE de `CREATEOBJECT('import_nota', ...)`, adauga exact acelasi `INDEX ON ... TAG ord_doc`
+ `SET ORDER TO ord_doc` ca in `achizitie_import`.
q. **Suitele de teste NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 |
Stop-Process -Force` (omoara TOATE instantele vfp9 de pe masina), deci un test lansat in
paralel ucide testul aflat in curs si corupe rezultatele. Ruleaza suitele strict secvential.
r. **Fara SELECT-SQL pe cursorul legat de grid**: SELECT ... FROM introdc INTO CURSOR intr-un
test, cat timp introdc e RecordSource-ul unui grid viu, poate omori procesul vfp9 silentios
(fara eroare catchabila, fara semafor). Foloseste xBase nativ (COUNT FOR ... TO,
CALCULATE, LOCATE) care nu comuta zona de lucru a grid-ului.

40
docs/testare-vfp-mcp.md Normal file
View File

@@ -0,0 +1,40 @@
# Testare automata VFP prin IDE + MCP (windows-mcp)
Valabil pentru toate proiectele VFP din suita ROA; exemplele de mai jos sunt pe ROAGEST —
inlocuieste caile, numele programului principal si titlul ferestrei pentru alt proiect.
Pentru verificare vizuala dupa modificari pe `.vc2`/`.sc2`, nu se porneste `roagest.exe`
(ar cere rebuild complet — vezi `COMUN\docs\flux-editare-vfp-text.md`), ci se lanseaza `vfp9.exe`
interpretat peste `Programe\roagest.prg`:
```powershell
Start-Process -FilePath "C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" `
-ArgumentList '-c"D:\ROA\ROAGEST\config.fpw"','D:\ROA\ROAGEST\Programe\roagest.prg' `
-WorkingDirectory "D:\ROA\ROAGEST"
```
- Gate-ul de pornire (`gcNumeProgram = 'ROAGEST'` in `roagest.prg`) verifica
`JUSTSTEM(SYS(16,0))` — numele programului de la nivelul 0 al stivei, nu numele
procesului `.exe`. Rulat interpretat, `SYS(16,0)` = `roagest.prg`, deci gate-ul trece.
- Fereastra rezultata are titlul `ROA - Gestiuni` (setat din `NUMEPROGRAM`).
## Capcana: focus furat de terminal
Daca sesiunea Claude Code afiseaza output in timp ce comanda de lansare ruleaza,
fereastra terminalului/IDE-ului ramane in prim-plan si VFP porneste in fundal —
`Screenshot`/`Snapshot` din `windows-mcp` pot raporta "No windows found" desi procesul
si fereastra exista (verificabil cu `Get-Process vfp9 | Select MainWindowTitle`).
Fix: inainte de screenshot, adu explicit fereastra in prim-plan dupa titlu:
```
mcp__windows-mcp__App mode=switch name="ROA - Gestiuni"
```
apoi `Screenshot`. Nu te baza pe enumerarea implicita de ferestre a `windows-mcp` cand
ai lansat procesul chiar in runda curenta de comenzi.
## Curatare dupa test
Proces de omorat la final: `mcp__windows-mcp__Process mode=kill pid=<pid>` (gasit prin
`mode=list name=vfp9` sau `Get-Process vfp9`).