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

14 KiB

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)

  1. Alege un model .frx valid care se randeaza deja (ex. Rapoarte\listmfcota.frx); pleci de la antetul/memo-ul lui, nu de la zero.
  2. 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).
  3. 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).
  4. Scrie obiectele, fiecare in banda ei: VPOS = band_start + offset. Tipuri: OBJTYPE=5 eticheta, 8 camp/expresie, 6 linie, 7 dreptunghi (OBJCODE=4).
  5. 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.
  6. Verifica validitatea: scripts/frx_convert.ps1 -Path <nou>.frx trebuie sa produca .fr2.
  7. Randare + numaratoare (REPORTBEHAVIOR 80): script headless cu cursor de test, REPORT FORM <raport> TO FILE <txt> ASCII NOCONSOLE; verifica totalurile numeric.
  8. 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/).