Files
comun/skills/roa-vfp-frx-report/SKILL.md
2026-09-24 23:07:59 +03:00

198 lines
14 KiB
Markdown

---
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\<schema>\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 <model>.frx TO <nou>.frx` si la fel `.frt`, apoi
`USE <nou>.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 <nou>.frx` trebuie sa produca `.fr2`.
6. Randare + numaratoare (REPORTBEHAVIOR **80**): script headless cu cursor de test,
`REPORT FORM <raport> TO FILE <txt> 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 <model>.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 <script.prg>` (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 <raport>.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 <raport> TO FILE <txt> ASCII NOCONSOLE`; in `<txt>`: 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 <fisier>`; directia din
extensie).
- `scripts/structura_frx.prg` - dump benzi + obiecte (VPOS -> banda) prin `FFC\_FRXCursor.vcx`;
headless: `vfp9 -A "<...>\structura_frx.FXP" "<raport>.frx" ["<iesire>.txt"]` (se termina cu QUIT).
- `scripts/randare_preview.ps1` - PNG cu pagina din PREVIEW (`-Fxp <script.FXP> -Png <png>
[-TitleMatch <nume>]`); 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/`).