--- name: roa-vfp-frx-report description: Creeaza, editeaza si verifica rapoarte VFP 9 (.frx/.frt) fara IDE - layout, benzi, grupare, totaluri, chenare, aliniere, orientare, preview PNG. Foloseste-l OBLIGATORIU cand utilizatorul cere un raport nou sau modificarea unuia existent ("fa un proces-verbal / o situatie / o lista tiparita", "modifica raportul X", "schimba layout-ul raportului", "adauga o coloana in raport", "pune total pe document", "trece-l pe portret", "raportul se rupe / nu se randeaza / iese invalid"), cand verifica un .frx nou, cand vrea preview sau PNG al unui raport, sau cand lucreaza cu .frx/.fr2 (creare, conversie, structura DBF, band-uri). Nu improviza structura: VPOS e absolut, banda unui obiect se calculeaza cu separatorul de 2083.333, la REPORTBEHAVIOR 90 obiectele din Group Header se randeaza doar cu SUPRPCOL=3 + SUPGROUP=nr.grup+5, iar totalurile pe grup se fac cu variabile de raport - procedura e mai jos. --- # Rapoarte VFP (.frx/.frt): creare, editare, verificare fara IDE Un `.frx` e un DBF cu 75 de campuri fixe + memo-ul `.frt`. Se lanseaza cu `goExport.export2frx(cursor, "nume")`; `getRepPath` (oexport.prg) cauta `nume.frx` in `SET PATH` (`Rapoarte\`) sau in `USERREPORTS\\USR_nume.frx`. Cursorul selectat inainte de `REPORT FORM` e sursa expresiilor. **Aplicatia randeaza cu REPORTBEHAVIOR 90** (`COMUN\programe\oexport.prg:1733-1738`); verificarea finala se face la 90, nu la 80. La 90 obiectele din Group Header cu `SUPRPCOL=0/SUPGROUP=0` nu se randeaza (pe primul grup) si obiectele din zona separatorului sunt taiate; la 80 se randeaza. Detalii: `references/reportbehavior-80-90.md`. ## Cand se foloseste - "fa un proces-verbal / o situatie / o lista tiparita", raport nou de la zero sau pornit din model. - "modifica raportul X" (layout, coloane, totaluri, antet, subsol), "schimba alinierea", "trece-l pe portret". - "raportul iese invalid", "nu se randeaza", "continutul nu apare", "se rupe pe pagini", "campul iese trunchiat". - verificare: "e valid raportul?", preview/PNG, numaratoare de pagini, totaluri. - orice atingere de `.frx`/`.frt`/`.fr2` (conversie, structura, editare fara IDE). Nu pentru rapoarte generate de alt motor (`.lbx` etichete, Excel) si nu pentru `goExport` in sine. ## Pasi (secventa cea mai scurta care a mers) 0. Alege un model `.frx` valid care se randeaza deja (ex. `Rapoarte\listmfcota.frx`); pleci de la antetul/memo-ul lui, nu de la zero. 1. Creeaza fisierul: `COPY FILE .frx TO .frx` si la fel `.frt`, apoi `USE .frx EXCLUSIVE` + `ZAP`. `COPY STRUCTURE` NU merge (vezi Capcane). 2. Scrie benzile, in ordinea de sus in jos, cu un `addrec` implicit + suprascriere per tip: `OBJTYPE=9` + `OBJCODE` (0 Title, 1 Page Header, 3 Group Header, 4 Detail, 5 Group Footer, 7 Page Footer) + `HEIGHT`. Retine `band_start` pentru fiecare banda (formula mai jos). 3. Scrie obiectele, fiecare in banda ei: `VPOS = band_start + offset`. Tipuri: `OBJTYPE=5` eticheta, `8` camp/expresie, `6` linie, `7` dreptunghi (`OBJCODE=4`). 4. Verifica structura: `scripts/structura_frx.prg` (ruleaza `FFC\_FRXCursor.vcx`) si confirma ca fiecare obiect are `BANDLABEL`-ul dorit si ca benzile au `START/STOP` asteptate. 5. Verifica validitatea: `scripts/frx_convert.ps1 -Path .frx` trebuie sa produca `.fr2`. 6. Randare + numaratoare (REPORTBEHAVIOR **80**): script headless cu cursor de test, `REPORT FORM TO FILE ASCII NOCONSOLE`; verifica totalurile numeric. 7. Aspect + verificare finala (REPORTBEHAVIOR **90**): `scripts/randare_preview.ps1` (PNG) si citeste PNG-ul inainte de "gata" (`references/preview-png.md`). ## Retete - **Stilul casei (OBLIGATORIU la orice raport nou/modificat)**: fonturi, grupare, ordonare, linii intre randuri cu `FLOAT`, cap de tabel pe un rand, total, comisie -> `references/stil-rapoarte.md`. - **Banda unui obiect (regula centrala)**: `VPOS` e absolut pe pagina, NU relativ la banda. `band_START = suma(HEIGHT benzi anterioare) + 2083.333 * numar benzi anterioare` (verificat pe `pvcasare_mf`: Detail = 0 + 8646 + 12729 + 3 * 2083.333 = 27625); fiecare banda adauga un separator de 2083.333 FRU. Obiectul apartine primei benzi cu `START <= VPOS <= STOP`. Pune `VPOS = band_START + offset` si obiectul cade in banda corecta. - **Grupare cu pagina noua per grup**: banda `OBJCODE=3`, `EXPR` = expresia de grupare, `HEIGHT=0`, `PAGEBREAK=.T.`. Continutul per grup (antet, cap de tabel, texte) poate sta in **Group Header** daca FIECARE obiect din GH are `SUPRPCOL=3` + `SUPGROUP=6` (grupul 1; = nr. grup + 5), cum le scrie Designer-ul. Cu `0/0` (obiecte generate programatic sau mutate din Page Header) nu apar (pe `pvcasare_mf` lipseau pe primul PV). Alternativ: continut in Page Header, GH cu `HEIGHT=0`. - **Plafon cap de tabel -> detaliu**: cu continut in Page Header si o banda Group Header goala intre, spatiul minim dintre capul de tabel si primul rand de detaliu este `2 * 2083.333 = 4166.667 FRU`. Nu se elimina complet fara scoaterea benzii GH (necesara pentru grupare + page break per document); minimizeaza-l prin `HEIGHT`-ul Page Header si offseti. - **Total / nr. crt. pe grup**: variabile de raport `OBJTYPE=18` (`EXPR` camp numeric, `TOTALTYPE=2` Sum / `1` Count, `RESETTOTAL=6`, `TAG=0`), afisate in GF cu `transform(var, get_mask(14,gnpa))` si `TOTALTYPE=0`. NU precalcul in cursor, NU `recno()` pentru nr. crt., NU nume de variabila egal cu un camp din cursor. Programatic doar copiind o variabila facuta in IDE (`SCATTER/GATHER MEMO`). Detalii: `references/totaluri.md`. - **Fundal gri pe cap de tabel**: `FILLRED/GREEN/BLUE=200` + `FILLPAT=1` **si `MODE=0`** (la `MODE=1`, transparent, fundalul nu se deseneaza). `RECT`-ul gri se opreste la marginea tabelului, nu dincolo de linia verticala. Linie sub cap: `OBJTYPE=6`, `PENPAT=8`. - **Linii/chenare**: `OBJTYPE=6` (`PENPAT=8`, `PENSIZE=1`); chenar = `OBJTYPE=7`, `OBJCODE=4`. - **Aliniere**: la etichete (`OBJTYPE=5`) `PICTURE` = `""` stanga, `"@I"` centrat, `"@J"` dreapta; la campuri (`OBJTYPE=8`) `OFFSET` = 0 stanga, 1 dreapta, 2 centrat. Text la stanga, numere la dreapta. - **Masca numerica**: `EXPR = transform(camp, get_mask(14, gnpa))`. `get_mask` real da separator SPATIU (`transform(3000,"99 999 999 999 999.99")` => `"3 000.00"`); `SET SEPARATOR` nu-l schimba. `get_mask`, `_PAGENO`, `gnpa` sunt disponibile la randare; in harness trebuie definite cu masca reala (stub cu virgula => preview mincinos). - **Orientare portret/landscape**: in memo-ul inregistrarii REPORT, `ORIENTATION` (0=portret, 1=landscape), `PAPERSIZE=9`, `COLOR=2`. Latime utila ~74000 FRU portret, ~80000 landscape; referinta portret `Rapoarte\listmf_inv.fr2`. Reaseaza HPOS/WIDTH pe latimea noua si verifica `max(HPOS+WIDTH) <= latimea paginii`. Trunchierea de camp tine de W vs FONT, nu de valoare. Detalii: `references/orientare-si-latimi.md`. - **Antet firma**: `EXPR = alltrim(goFirma.Antet)` (si `gofirma.cod_fiscal` etc.). - **Standard de layout cerut**: texte fixe **fara diacritice** (ASCII simplu) si **sentence case** (doar prima litera mare); datele din baza raman cum vin. Daca totusi trebuie diacritice: `RESOID=238` + `DOUBLE=.T.` (Central European) pe fiecare obiect text/camp. - **Text pe mai multe linii**: VFP 9 nu face word-wrap la etichete; pune `CHR(13)` explicit in text sau rupe pe mai multe obiecte cu `VPOS` diferit (cap de coloana care nu incape in W). - **Editarea unui raport existent**: generator `.prg` reutilizabil (reasezare) SAU editare `.fr2` + `frx_convert` (modificari mici) - **o singura sursa de adevar**, nu ambele. Detalii: `references/editare-iterativa.md`. ## Capcane (simptom -> cauza -> remediu) - **"Report file ... is invalid"** dupa creare -> `COPY STRUCTURE` (lipseste formatul de raport) -> `COPY FILE .frx` + `ZAP` (punctul 1); modelul trebuie sa aiba toate benzile. - **"Report file ... is invalid"** la un raport altfel corect -> lipseste banda **Page Footer** (`OBJCODE=7`) -> adaug-o (poate avea `HEIGHT=0`). - **Continutul benzii Group Header nu se randeaza** (la 90; tipic doar pe primul grup) -> obiectele au `SUPRPCOL=0/SUPGROUP=0` -> pune `SUPRPCOL=3`, `SUPGROUP=6` pe fiecare obiect din GH (sau in IDE: Print When, ca obiectele care se vad). - **Obiectul apare in alta banda / dispare la 90 / page break in loc gresit** -> obiectele se asociaza dupa `VPOS` absolut, iar la 90 zona separatorului e taiata -> `VPOS = band_START + offset` (Retete); verifica `BANDS.START/STOP` cu `structura_frx.prg`. - **Totalul iese `****`** -> `TOTALTYPE=2` pe `transform(...)` (string) -> suma pe variabila de raport numerica, `transform` doar in campul de afisare cu `TOTALTYPE=0` (`references/totaluri.md`). - **Fatal error C0000005 la `REPORT FORM`** -> variabile de raport `OBJTYPE=18` construite de mana (`APPEND BLANK` + `REPLACE`) -> copiaza o variabila facuta in IDE (`SCATTER/GATHER MEMO`). Nu repeta instructiunea: izoleaz-o intr-un script minim si schimba abordarea. - **Camp trunchiat cu "..."** -> W prea mic pentru fontul campului (nu pentru valoare) -> mareste coloana / aliniaza W-ul la detaliu (`references/orientare-si-latimi.md`). - **Cap de coloana care iese peste linia verticala / "fundal gri dincolo de linie"** -> labelul nu incape in W-ul coloanei si nu se taie la margine -> rupe-l pe 2-3 randuri care incap. - **Fundalul nu se vede** -> `MODE=1` (transparent) -> `MODE=0`. - **`TO FILE` scrie binar** -> lipseste `ASCII` sau rulezi la 90 (listener) -> `ASCII NOCONSOLE` la 80 (`references/reportbehavior-80-90.md`). - **Rulare headless agatata, log gol** -> `vfp9 -A ` (deschide editorul), lipsa `ON ERROR ... QUIT`, `OBJECT TYPE 1` (se blocheaza) sau un vfp9 strain inca viu -> ruleaza intotdeauna prin `watchdog_vfp.ps1`; citeste intai PNG + dump text din `-OutDir`. - **`Variable 'lcX' is not found` intr-o PROCEDURE** -> variabila declarata `LOCAL` in programul apelant -> declara `PRIVATE` ce folosesc procedurile auxiliare (ex. `errhand`, log). - **Eroarea 232 `'laFields' is not an array`** -> `LOCAL laFields` fara dimensiune -> `LOCAL ARRAY laFields[1,1]`. - **`Alias 'GOFIRMA' is not found` / pagina alba in preview** -> raportul foloseste `goFirma`/`gnpa`/ `get_mask` -> defineste-le in scriptul de test (`PUBLIC goFirma` + `Antet`, `gnpa`, `FUNCTION get_mask` cu masca reala). - **`File 'zoom 100.prg' does not exist`** -> `_REPORTPREVIEW = "ZOOM 100"` nu e zoom -> scoate linia (`references/preview-png.md`). - **Diacritice corupte** -> `.prg` cu diacritice scris cu Edit/Write -> construieste textele cu `CHR()` in `.prg` ASCII, sau declara `RESOID=238` pe obiect. - **Linia dintre randuri ramane sus cand Denumirea trece pe 2+ randuri** -> linia orizontala din Detail nu e `FLOAT` (sau a mostenit `STRETCH=.T.` copiata dintr-o verticala) -> `FLOAT=.T.`, `STRETCH=.F.`, `TOP=.F.`; campul de text lung cu `STRETCH=.T.`. - **Linie groasa/dubla sub ultimul rand** -> linie de detaliu + linie la inceputul GF -> sterge-o pe cea din GF. - **`r.fr2.bak` langa raport** -> il lasa FoxBin2Prg cand `.fr2` exista deja -> sterge-l (nu e livrabil). ## Criterii de acceptare si dovada (GATA) Fara dovezile de mai jos, raportul NU se raporteaza ca terminat. **Build fara eroare NU e GATA.** 1. **Valid**: `frx_convert.ps1 -Path .frx` produce `.fr2` fara eroare. 2. **Structura**: `structura_frx.prg` arata fiecare obiect in banda dorita (`BANDLABEL`) si benzile cu `START/STOP/HEIGHT` asteptate. 3. **Randare + numaratoare** (REPORTBEHAVIOR 80): script headless cu cursor de test (2 documente x 2 randuri), `REPORT FORM TO FILE ASCII NOCONSOLE`; in ``: numarul de pagini (o aparitie a titlului per document), randurile si **totalurile numerice egale cu suma manuala** (ex. doc 1001 -> 4200/3400, doc 2002 -> 5800/4600). 4. **Aspect** (REPORTBEHAVIOR 90): `randare_preview.ps1` -> PNG cu **pagina intreaga**; pentru FIECARE cerinta de layout, dovada vizuala pe PNG. Verifica explicit: - trunchiere de camp ("..." / "***") si capete de coloana care ies din tabel; - fundalul gri peste marginea tabelului / dincolo de linia verticala; - totalurile numerice (masca, coloane, sume) si alinierea (text stanga / numere dreapta); - `max(HPOS+WIDTH) <= latimea paginii` (portret ~74000, landscape ~80000). Nu confunda clipul grilei ASCII (80 car./linie) sau un cap de coloana iesit din coloana cu depasirea paginii. 5. **Curatenie**: niciun `vfp9` pornit de tine ramas viu. ## Scripturi - `scripts/frx_convert.ps1` - `.frx` <-> `.fr2` cu FoxBin2Prg (`-Path `; directia din extensie). - `scripts/structura_frx.prg` - dump benzi + obiecte (VPOS -> banda) prin `FFC\_FRXCursor.vcx`; headless: `vfp9 -A "<...>\structura_frx.FXP" ".frx" [".txt"]` (se termina cu QUIT). - `scripts/randare_preview.ps1` - PNG cu pagina din PREVIEW (`-Fxp -Png [-TitleMatch ]`); captureaza fereastra "Report Preview", nu "Report Designer"/Command Window; scriptul VFP dat face `SET REPORTBEHAVIOR 90` + `REPORT FORM ... PREVIEW NOWAIT` + `INKEY`. - Existent: `COMUN\utile\Teste\_precompile.ps1` (precompilare izolata), `COMUN\utile\Teste\watchdog_vfp.ps1` (rulare cu watchdog), `D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE`. - Scriptul de test/preview se scrie dupa modelul: `SET SAFETY OFF` / cursor de test / `gnpa`, `goFirma`, `get_mask` (masca reala, cu spatiu) / `REPORT FORM` / `QUIT`. ## Buget de context Lucreaza raportul in story-uri mici (o cerinta de layout per rulare), nu intr-o singura sesiune lunga; la finalul fiecarui bloc scrie handoff-ul pe disc cu o sectiune "Pentru skill" (simptom -> cauza -> remediu, doar ce s-a verificat). Rapoartele reale se itereaza pe mai multe runde; fara handoff, runda urmatoare reia investigatia. ## Referinta (doar la nevoie) - `references/reportbehavior-80-90.md` - diferentele 80 vs 90, ce dovada la ce behavior. - `references/totaluri.md` - totaluri pe grup cu variabile de raport (reteta + capcane), `get_mask`. - `references/orientare-si-latimi.md` - portret/landscape, latimi, trunchiere de font, cap de coloana, subsol. - `references/preview-png.md` - captura ferestrei "Report Preview", runner, watchdog, zoom. - `references/editare-iterativa.md` - generator `.prg` vs editare `.fr2`, sursa unica de adevar. - `references/stil-rapoarte.md` - stilul aprobat (fonturi, grupare, ordonare, linii, total, comisie). - `COMUN\docs\creare-editare-rapoarte-frx.md` - tabelul complet de campuri DBF si structura `_FRXCursor` (istoric; pentru comportamentul 90 vezi `references/`).