sync SVN r18202
This commit is contained in:
@@ -21,6 +21,7 @@ atingi zona X, citeste fisierul Y"). Restul se deschid doar la declansator.
|
||||
| **VFP** | |
|
||||
| `cautare_vcx_vct.md` | cauti cod in `.vcx`/`.scx`/`.frx`/`.mnx` -> skill `roa-vfp-text-edit` |
|
||||
| `flux-editare-vfp-text.md` | editezi `.vc2`/`.sc2` + write-back; invariantele si `*p:`/`*m:` -> skill `roa-vfp-text-edit` |
|
||||
| `creare-editare-rapoarte-frx.md` | creezi/editezi un raport `.frx`/`.frt` sau verifici unul nou (structura DBF, band-uri, grupare, totaluri) -> skill `roa-vfp-frx-report` |
|
||||
| `conventie_encoding_cp1252.md` | fisierul editat are diacritice (cp1250, nu cp1252) -> skill `roa-vfp-text-edit` |
|
||||
| `inventar-comun.md` | inainte de cod nou: exista deja o functie/clasa comuna? |
|
||||
| `conventie_ux_formulare.md` | adaugi/rearanjezi controale pe formular |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# rec_skills - skill-uri de agent pentru suita ROA
|
||||
|
||||
Data: 2026-09-16. Status: aplicat (10 skill-uri create, rutare + backstop puse in docs).
|
||||
Data: 2026-09-16. Status: aplicat (11 skill-uri create, rutare + backstop puse in docs).
|
||||
|
||||
## Decizie
|
||||
|
||||
@@ -25,6 +25,7 @@ Data: 2026-09-16. Status: aplicat (10 skill-uri create, rutare + backstop puse i
|
||||
|---|---|
|
||||
| `roa-vfp-text-edit` | editare `.vcx/.scx` pe text (`.vc2/.sc2`), cautare in binare, encoding cp1250, write-back |
|
||||
| `roa-vfp-headless-test` | rulare/depanare teste VFP headless, harness UI + screenshots |
|
||||
| `roa-vfp-frx-report` | creezi/editezi/verifici un raport `.frx`/`.frt` (layout, benzi, grupare, totaluri, preview) |
|
||||
| `roa-git-svn-commit` | commit / `svn update` / `roa_sync` in COMUN |
|
||||
| `roa-oracle-migration` | scriere + publicare scripturi de migrare, re-export pachet PL/SQL |
|
||||
| `roa-oracle-diagnostic` | update ROA blocat la client, tablespace/disc plin |
|
||||
@@ -47,7 +48,7 @@ Data: 2026-09-16. Status: aplicat (10 skill-uri create, rutare + backstop puse i
|
||||
|
||||
## Verificari la data aplicarii
|
||||
|
||||
- 10/10 skill-uri: ASCII pur, fara BOM, `name` = folder, descriere <= 1024, exact 1 bullet sub
|
||||
- 11/11 skill-uri: ASCII pur, fara BOM, `name` = folder, descriere <= 1024, exact 1 bullet sub
|
||||
`## Referinta (doar la nevoie)`.
|
||||
- 0 rute reziduale zona -> doc in `CLAUDE.md`, `AGENTS.md`, `reguli_lucru.md`, `README.md`.
|
||||
- `leaga_skills.ps1`: 4 create apoi idempotent (0 create, 9 ok); director real cu acelasi nume
|
||||
|
||||
95
docs/creare-editare-rapoarte-frx.md
Normal file
95
docs/creare-editare-rapoarte-frx.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Rapoarte VFP (.frx/.frt): citire, creare, verificare
|
||||
|
||||
Un `.frx` este un DBF cu 75 de campuri fixe (identice la toate rapoartele) + memo-ul `.frt`.
|
||||
`goExport.export2frx(cursor, "nume", ...)` il lanseaza; `getRepPath` (oexport.prg) cauta
|
||||
`nume.frx` in `SET PATH` (`Rapoarte\`) sau in `USERREPORTS\<schema>\USR_nume.frx`. Cursorul
|
||||
selectat inainte de `Report Form` este sursa expresiilor din raport.
|
||||
|
||||
## Campuri cheie
|
||||
|
||||
- `OBJTYPE`: 1=raport (OBJCODE=53), 9=banda, 5=eticheta, 8=expresie/camp, 6=linie,
|
||||
7=dreptunghi (OBJCODE=4), 18=variabila de raport, 25=dataenvironment.
|
||||
- `OBJCODE` pe `OBJTYPE=9`: 0=Title, 1=Page Header, 2=Column Header, 3=Group Header,
|
||||
4=Detail, 5=Group Footer, 6=Column Footer, 7=Page Footer, 8=Summary.
|
||||
- `EXPR`: textul (intre ghilimele) la etichete, expresia la campuri, **expresia de grupare la
|
||||
banda OBJCODE=3**. `PAGEBREAK=".T."` pe OBJCODE=3 => pagina noua per grup.
|
||||
- `FONTFACE`/`FONTSIZE`/`FONTSTYLE` (1=bold); obiectele au `MODE=1`.
|
||||
- Aliniere: la **etichete** (OBJTYPE=5) `PICTURE` = `""` stanga, `"@I"` centrat, `"@J"` dreapta;
|
||||
la **campuri** (OBJTYPE=8) `OFFSET` = 0 stanga, 1 dreapta, 2 centrat.
|
||||
- Numere: `EXPR = transform(camp, get_mask(14, gnpa))` (ca rapoartele existente).
|
||||
`_PAGENO`, `goFirma.Antet`, `get_mask` sunt disponibile la randare.
|
||||
|
||||
## Asocierea obiect -> banda (capcana centrala)
|
||||
|
||||
VFP NU asociaza obiectele dupa ordinea inregistrarilor si NU foloseste VPOS relativ la banda.
|
||||
`VPOS` este **absolut pe pagina**, iar banda fiecarui obiect se determina cu intervalele
|
||||
calculate de `FFC\_FRXCursor.vcx`: fiecarei benzi i se adauga un **separator de 2083.333 FRU**:
|
||||
|
||||
```
|
||||
band_START = suma(inaltimilor benzilor precedente) + 2083.333 * (numarul de benzi precedente + 1)
|
||||
band_STOP = band_START + HEIGHT
|
||||
```
|
||||
|
||||
Obiectul apartine primei benzi cu `band_START <= VPOS <= band_STOP` si se deseneaza la `VPOS`
|
||||
absolut. Deci, ca sa pui un obiect intr-o banda, calculeaza `VPOS = band_START + offset` (vezi
|
||||
exemplu mai jos). Verificarea exacta: ruleaza `_FRXCursor` si citeste `BANDS.START/STOP` +
|
||||
`OBJECTS.BANDLABEL` (vezi scriptul de mai jos).
|
||||
|
||||
**Capcana: continutul benzii Group Header nu se randeaza** (nici cu `PAGEBREAK` pe ea, nici fara;
|
||||
obiectele sunt asociate corect dar nu apar). Solutia care merge: pune continutul per-grup
|
||||
(antet, tabel document, texte, cap de tabel) in **Page Header**, cu `VPOS` in intervalul benzii
|
||||
Page Header, si lasa banda Group Header **goala (HEIGHT=0) doar cu `PAGEBREAK=.T.`** ca sa
|
||||
declanseze pagina noua per grup. Campurile de grup (`nrdoc` etc.) se evalueaza corect in Page
|
||||
Header, pentru ca pagina noua incepe cu primul rand al grupului.
|
||||
|
||||
## Crearea unui raport nou programatic (fara IDE)
|
||||
|
||||
`COPY STRUCTURE` NU e suficient: produce un `.frx` pe care motorul il raporteaza "is invalid"
|
||||
(lipseste formatul de raport). Corect:
|
||||
|
||||
1. `COPY FILE <model>.frx TO <nou>.frx` si `COPY FILE <model>.frt TO <nou>.frt` (pastreaza
|
||||
antetul/memo-ul exact).
|
||||
2. `USE <nou>.frx EXCLUSIVE` apoi `ZAP`.
|
||||
3. `APPEND BLANK` + `REPLACE` campurile, per obiect (incepe cu un `FUNCTION addrec` cu valorile
|
||||
implicite, suprascrie per tip).
|
||||
4. Benzile: `HEIGHT` si `OBJCODE`; Title/PageFooter pot avea HEIGHT=0.
|
||||
|
||||
Exemplu de calcul VPOS pentru benzi Title(0), PageHeader(24000), GroupHeader(0), Detail(1800),
|
||||
GroupFooter(15000), PageFooter(0): `PH_START=2083.333`, `GH_START=28166.666`,
|
||||
`DETAIL_START=30250`, `GF_START=34133.333`. Obiectele din Page Header: `VPOS=2083.333+offset`.
|
||||
|
||||
## Rulare headless (obligatoriu .FXP)
|
||||
|
||||
`vfp9.exe -A "<script.prg>"` deschide des editorul si nu scrie log (proces viu, log gol).
|
||||
Corect: precompileaza intai (`COMUN\utile\Teste\_precompile.ps1 -Prg <script.prg>`), apoi
|
||||
`vfp9.exe -A "<script.FXP>"` (fara `-T`). Prima linie: `SET SAFETY OFF`. Pune
|
||||
`ON ERROR DO ... WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care scrie in log si `QUIT`,
|
||||
altfel orice eroare deschide un dialog nativ si blocheaza pana la timeout.
|
||||
|
||||
## Verificarea ca raportul e valid si arata corect
|
||||
|
||||
- Validitate: `FoxBin2Prg.EXE "<raport>.frx" "" "" "" 1 0 1` trebuie sa produca `.fr2`.
|
||||
- Continut: `SELECT <cursor>` + `REPORT FORM <raport> TO FILE <txt> ASCII NOCONSOLE` si cauta in
|
||||
`<txt>` antetul, coloanele, totalurile (`TO FILE` fara `ASCII` scrie binar).
|
||||
- Aspect: `REPORT FORM <raport> PREVIEW NOWAIT`, apoi muta fereastra copil "Print Preview"
|
||||
(MoveWindow) ca sa nu acopere pagina si fa screenshot pe fereastra copil cu titlul raportului
|
||||
(`PrintWindow`). Fara mutarea barei, jumatatea de sus a paginii e mascata de umbra ei.
|
||||
|
||||
## Capcane
|
||||
|
||||
- **`COPY STRUCTURE` da un `.frx` invalid**; foloseste `COPY FILE` + `ZAP` (punctul de mai sus).
|
||||
- **Continutul benzii Group Header nu se randeaza**; muta-l in Page Header (punctul de mai sus).
|
||||
- `LOCAL laFields` + `AFIELDS(laFields, ...)` da eroarea 232 "not an array"; declara
|
||||
`LOCAL ARRAY laFields[1,1]`.
|
||||
- Fara `ON ERROR ... QUIT`, o eroare intr-un script headless deschide un dialog nativ si pare
|
||||
"agatat" (proces viu, CPU ~0, log gol).
|
||||
- `vfp9.exe -A "<script>"` pe `.prg` (nu `.FXP`) deschide editorul; ruleaza `.FXP`.
|
||||
- Diacriticele se scriu ca octeti cp1250 (in `.prg` le construiesti cu `CHR()`), nu cu Edit/Write.
|
||||
Setul de caractere se declara pe fiecare obiect text/camp prin `DOUBLE=.T.` + `RESOID=238`
|
||||
(Central European); pentru rapoarte fara diacritice, `RESOID=0` si texte ASCII.
|
||||
- Fundal pe un cap de tabel: `FILLRED/GREEN/BLUE` + `FILLPAT=1` **si `MODE=0`** - la `MODE=1`
|
||||
(transparent) fundalul nu se deseneaza.
|
||||
- Cursorul referit de raport trebuie sa fie cel selectat la `Report Form`; altfel expresiile dau
|
||||
"Variable not found".
|
||||
- `VFP 9` nu face word-wrap la etichete: liniile lungi se taie la latime; pune `CHR(13)` explicit
|
||||
in textul etichetei (intre ghilimele) pentru linii multiple.
|
||||
@@ -23,6 +23,7 @@ opreste lane-urile celuilalt si le poate muta directorul de lucru (verificat 15.
|
||||
|---|---|
|
||||
| ROAFACTURARE | 4096 |
|
||||
| ROACONT | 4196 |
|
||||
| ROAIMOB | 4296 |
|
||||
|
||||
Proiect nou: urmatorul port liber din tabel (+100), trecut aici inainte de pornire. In API trimite si `?directory=<radacina proiectului>`.
|
||||
| Actiune | Apel |
|
||||
|
||||
@@ -28,6 +28,19 @@ cand primesti un mesaj nou, il aplici imediat; are prioritate fata de sarcina in
|
||||
date sau istoric.
|
||||
- Fisierele `.sql` pentru `SCRIPTURI_CLAR` se scriu cu CRLF.
|
||||
|
||||
## Rulare VFP (obligatoriu)
|
||||
- Orice rulare vfp9 (test, randare raport, compilare) trece prin watchdog, niciodata vfp9.exe direct:
|
||||
`powershell -File COMUN\utile\Teste\watchdog_vfp.ps1 -Script <prg> -TimeoutSec 120 -AutoDismiss -OutDir <dir>`.
|
||||
Tu nu vezi ecranul: dialogurile modale (erori VFP, MessageBox, "Fatal error") blocheaza procesul
|
||||
fara sa apara in log. Watchdog-ul le captureaza (PNG + text) si le inchide.
|
||||
- Rularea nu s-a terminat sau logul e gol: CITESTI intai captura PNG si dump-ul text din `-OutDir`,
|
||||
apoi decizi. Nu relua aceeasi comanda fara sa stii ce dialog a blocat-o.
|
||||
- `Fatal error: Exception code=C0000005` = crash VFP (acces invalid la memorie), nu eroare de logica;
|
||||
linia e in "Called from". Nu repeta instructiunea: izoleaz-o intr-un script minim si schimba
|
||||
abordarea (tipic: `.frx` deschis cu `USE` si apoi `REPORT FORM` pe el, `_FRXCursor`, obiect distrus).
|
||||
Noteaza cazul in raport, la capcane.
|
||||
- La final si dupa fiecare blocaj: niciun `vfp9` pornit de tine ramas viu (opresti doar PID-urile tale).
|
||||
|
||||
## Raport (obligatoriu, pe disc)
|
||||
Scrii `docs\raport_<lane>.md` (numele lane-ului il primesti in sarcina), scurt:
|
||||
1. Facut: fisier:linie pentru fiecare modificare.
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
ce se intoarce si modelul de lucru (backlog de story-uri mici): `orchestrare-subagenti.md`.
|
||||
7. Conventii obligatorii, de citit cand atingi zona respectiva:
|
||||
- editezi `.vc2`/`.sc2` sau `.prg` cu diacritice -> skill roa-vfp-text-edit (el detine procedura; criteriile lui blocheaza "gata");
|
||||
- creezi sau editezi un raport `.frx`/`.frt` (structura DBF, band-uri, grupare, totaluri, preview) -> skill roa-vfp-frx-report (el detine procedura; criteriile lui blocheaza "gata");
|
||||
- adaugi o PROPRIETATE sau o METODA noua intr-o clasa `.vc2`/`.sc2` -> skill roa-vfp-text-edit (membrul nou si `*p:`/`*m:` sunt in procedura lui; criteriile lui blocheaza "gata");
|
||||
- adaugi/rearanjezi controale pe formulare sau coloane in grid -> `conventie_ux_formulare.md`;
|
||||
- grid needitabil pe formular cu 2+ grid-uri -> `capcana_grid_controlsource.md`;
|
||||
|
||||
197
skills/roa-vfp-frx-report/SKILL.md
Normal file
197
skills/roa-vfp-frx-report/SKILL.md
Normal file
@@ -0,0 +1,197 @@
|
||||
---
|
||||
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/`).
|
||||
29
skills/roa-vfp-frx-report/references/editare-iterativa.md
Normal file
29
skills/roa-vfp-frx-report/references/editare-iterativa.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Editarea iterativa a unui raport existent
|
||||
|
||||
Doua cai; alege una si tine-o ca **sursa unica de adevar**.
|
||||
|
||||
## A. Generator `.prg` reutilizabil (reasezare / raport construit programatic)
|
||||
|
||||
Un singur `.prg` cu `COPY FILE <model>.frx TO <nou>.frx` (+ `.frt`), `USE ... EXCLUSIVE`, `ZAP`,
|
||||
apoi `APPEND BLANK` + `REPLACE` per obiect printr-un singur `FUNCTION addrec` (valori implicite,
|
||||
suprascrise per tip). Sursa de adevar e `.prg`-ul: il re-rulezi si regenereaza `.frx`/`.frt`.
|
||||
Potrivit cand layout-ul se schimba mult (latimi, benzi, orientare) sau raportul se construieste de la
|
||||
zero; fiecare runda porneste de la generatorul rundei precedente (r6: `build_r6.prg` derivat din
|
||||
`build_final.prg`).
|
||||
|
||||
## B. Editarea `.fr2` + `frx_convert`
|
||||
|
||||
Pentru modificari mici de text/camp pe un raport existent:
|
||||
`frx_convert.ps1 -Path r.frx` -> editezi `r.fr2` -> `frx_convert.ps1 -Path r.fr2` regenereaza
|
||||
`r.frx`+`r.frt`. Editezi doar textul vizat (nu rescrie antetul), altfel risti sa strici formatul.
|
||||
|
||||
## Reguli
|
||||
|
||||
- **Nu edita ambele**: daca rulezi generatorul peste un `.frx` editat manual din `.fr2`, pierzi
|
||||
editarea; daca editezi `.fr2` dupa ce ai rulat generatorul, `.frx` nu mai corespunde. Alege o
|
||||
sursa si lucreaza numai in ea.
|
||||
- `FoxBin2Prg.EXE` (prin `frx_convert.ps1`) lasa un `r.fr2.bak` cand `r.fr2` exista deja; nu face
|
||||
parte din livrare - sterge-l.
|
||||
- Modelul de pornire trebuie sa aiba toate benzile (Title/PageHeader/GroupHeader/Detail/GroupFooter/
|
||||
PageFooter). `Rapoarte\listmfcota.frx` e valid; `COPY FILE` pastreaza si formatul de raport, pe
|
||||
cand `COPY STRUCTURE` produce un `.frx` "invalid" (lipseste formatul).
|
||||
43
skills/roa-vfp-frx-report/references/orientare-si-latimi.md
Normal file
43
skills/roa-vfp-frx-report/references/orientare-si-latimi.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Orientare si latimi (portret / landscape)
|
||||
|
||||
## Campurile din .frx
|
||||
|
||||
In inregistrarea REPORT (`OBJTYPE=1`, `OBJCODE=53`), memo-ul `expr`:
|
||||
|
||||
- `ORIENTATION`: 0 = portret, 1 = landscape.
|
||||
- `PAPERSIZE=9` (A4), `COLOR=2`.
|
||||
- Referinta portret valid: `Rapoarte\listmf_inv.fr2` (`ORIENTATION=0 PAPERSIZE=9 COLOR=2`, fara
|
||||
`PAPERLENGTH`/`PAPERWIDTH`). pvcasare_mf portret a pastrat si `PAPERLENGTH=2794 PAPERWIDTH=2159`;
|
||||
`ORIENTATION` decide oricum orientarea.
|
||||
|
||||
## Latimea utila
|
||||
|
||||
- Portret A4: ~**74000 FRU** (pvcasare_mf r5 foloseste W raport 74000; `listmf_inv` are 74667, cu
|
||||
max `HPOS+WIDTH=74166.666`).
|
||||
- Landscape: ~**80000 FRU** (pvcasare_mf r2-r4).
|
||||
- La schimbarea orientarii reaseaza TOATE HPOS/WIDTH pe latimea noua (antet, casete, titlu, cap de
|
||||
tabel, detaliu, GF), apoi verifica `max(HPOS+WIDTH) <= latimea paginii` (r5: max 73600 <= 74000),
|
||||
cu un mic program care parcurge obiectele si tipareste `HPOS+WIDTH` per obiect.
|
||||
|
||||
## Trunchierea de camp: W (FRU) vs FONT, nu valoarea
|
||||
|
||||
Un camp trunchiat cu "..." (nu `***`) are W prea mic pentru **fontul** lui, nu pentru valoare (r6):
|
||||
TOTAL amortizare avea W=9900 ca detaliul, dar font 9 bold fata de font 8 in detaliu, iar
|
||||
`transform(...)` -> "3..." nu incapea. Latimea in FRU e independenta de font; un font cu un punct mai
|
||||
mare cere ~12% mai mult W. Remediu: mareste coloana si **aliniaza W-ul TOTAL la W-ul din detaliu**
|
||||
(r6: W=14800). Verifica pe PNG - ASCII-ul nu trunchiaza la fel.
|
||||
|
||||
## Cap de coloana care nu incape
|
||||
|
||||
Un LABEL de cap de coloana care nu incape in W-ul coloanei **NU se taie la marginea coloanei**:
|
||||
textul iese peste linia verticala si peste `RECT`-ul gri, pana la marginea paginii, cu aspect de
|
||||
"fundal gri dincolo de linie" (r6). Rupe textul manual pe 2-3 randuri (VPOS diferit) care incap in W.
|
||||
Arial font 8 ~540 FRU/caracter, font 9 ~600 (masurat: "scoaterea din functiune", 23 caractere
|
||||
~12400 FRU).
|
||||
|
||||
## Blocuri care nu incap in subsol
|
||||
|
||||
Cand un bloc din Group Footer (ex. COMISIA) nu incape, asaza-l pe mai multe randuri si/sau sub tabel
|
||||
si **mareste HEIGHT-ul GF** cat sa-l acopere (r5: H=15300 -> r6: H=16800), altfel continutul se taie
|
||||
pe pagina. `RECT`-ul gri se opreste la marginea tabelului (r6: W=73000, X max 73500), nu dincolo de
|
||||
linia verticala.
|
||||
43
skills/roa-vfp-frx-report/references/preview-png.md
Normal file
43
skills/roa-vfp-frx-report/references/preview-png.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Preview PNG (REPORTBEHAVIOR 90)
|
||||
|
||||
Fereastra de capturat este **"Report Preview - <raport>.frx [- Page N]"**, copil al shell-ului VFP.
|
||||
NU "Report Designer" si NU fereastra Command Window. Captura pe fereastra gresita = certifici
|
||||
designerul/desktop-ul, nu pagina.
|
||||
|
||||
## Cum gasesti fereastra
|
||||
|
||||
- Preview-ul NU e top-level: `EnumWindows` pe proces intoarce doar shell-ul VFP ("Microsoft Visual
|
||||
FoxPro"), iar fereastra de pagina e un **copil** al lui. Captura corecta = `EnumChildWindows` pe
|
||||
shell si filtrare dupa titlul care contine `Report Preview`.
|
||||
- Fereastra de pagina are titlul `Report Preview - <raport>.frx - Page N`. Bara de unelte e o
|
||||
fereastra mica (aceeasi radacina de titlu, inaltime mica) si se exclude prin filtrul de dimensiune
|
||||
(`>200x200`); daca umbreste pagina, muta-o cu `MoveWindow`.
|
||||
|
||||
## Runner
|
||||
|
||||
`.prg` (precompilat) cu: cursor de test + `SET REPORTBEHAVIOR 90` +
|
||||
`REPORT FORM <frx> PREVIEW NOWAIT` + `INKEY(<sec>)` + `QUIT`; `ON ERROR` scrie in log si `QUIT`.
|
||||
Apoi:
|
||||
|
||||
```
|
||||
powershell -File scripts\randare_preview.ps1 -Fxp <ui.FXP> -Png <png> [-TitleMatch <nume>]
|
||||
```
|
||||
|
||||
Scriptul lanseaza vfp9, capteaza fereastra copil de preview si **opreste procesul** la final (nu lasa
|
||||
vfp9 viu).
|
||||
|
||||
## Watchdog
|
||||
|
||||
`randare_preview.ps1` este runnerul controlat pentru preview: are nevoie de procesul VIV in timpul
|
||||
`INKEY`, deci nu poate fi asteptat de watchdog-ul care asteapta iesirea procesului. Toate celelalte
|
||||
rulari vfp9 (build, randare ASCII, structura) trec prin
|
||||
`COMUN\utile\Teste\watchdog_vfp.ps1 -Script <prg> -TimeoutSec 120 -AutoDismiss -OutDir <dir>`.
|
||||
|
||||
## Capcane de preview
|
||||
|
||||
- `_REPORTPREVIEW = "ZOOM 100"` NU e zoom: VFP cauta un fisier `zoom 100.prg` (eroare "File 'zoom
|
||||
100.prg' does not exist"). Zoom-ul e doar buton owner-drawn in toolbar, nu se seteaza programatic.
|
||||
- Preview-ul nativ la 100% afiseaza **pagina intreaga** (r6): marginea paginii se vede, restul e
|
||||
desktop gri. Un cap de coloana care pare taiat e text care iese din coloana, nu pagina (nota
|
||||
inversa din r5 era inselatoare). Confirma intai `max(HPOS+WIDTH) <= latimea paginii`, apoi citeste
|
||||
PNG-ul.
|
||||
28
skills/roa-vfp-frx-report/references/reportbehavior-80-90.md
Normal file
28
skills/roa-vfp-frx-report/references/reportbehavior-80-90.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# REPORTBEHAVIOR 80 vs 90 (rapoarte ROA)
|
||||
|
||||
Aplicatia randeaza rapoartele cu **REPORTBEHAVIOR 90**: `FoxyPreviewerSimple`
|
||||
(`COMUN\programe\oexport.prg:1733-1738`) seteaza `gnReportBehaviour` daca e numeric, altfel 90,
|
||||
apoi lanseaza `REPORT FORM ... PREVIEW` (`oexport.prg:1746-1747`). **Verificarea finala se face la
|
||||
90**, pentru ca la 90 randeaza aplicatia.
|
||||
|
||||
## Diferente verificate (aceeasi sursa .frx, r3)
|
||||
|
||||
- **Obiectele din Group Header cu `SUPRPCOL=0/SUPGROUP=0` NU se randeaza la 90** (pe primul grup),
|
||||
dar se randeaza la 80. Cu `SUPRPCOL=3` + `SUPGROUP=6` (grupul 1), cum le scrie Designer-ul, GH
|
||||
randeaza normal la 90 (dovedit pe `pvcasare_mf`, 24.09.2026). Continutul per grup poate sta in GH
|
||||
cu aceste valori, sau in Page Header cu GH `HEIGHT=0`, `PAGEBREAK=.T.`.
|
||||
- **Obiectele plasate in regiunea separatorului** (VPOS intre `START+HEIGHT` al benzii si `START`-ul
|
||||
benzii urmatoare, adica in cei 2083.333 FRU) se randeaza la 80, dar NU la 90. La 90 motorul taie
|
||||
strict la continutul benzii.
|
||||
- Test izolat: label la VPOS 29500 (in separatorul de dupa Page Header) - apare la 80, dispare la 90.
|
||||
|
||||
## Dovada numerica vs dovada de aspect
|
||||
|
||||
- **La 90 NU exista cale text curata headless**: `TO FILE ... ASCII` trece prin listener si scrie
|
||||
BINAR (nu agata, dar nu e text lizibil); `OBJECT TYPE 1` se BLOCHEAZA headless.
|
||||
- Dovada numerica (randuri, totaluri) se face cu **80** +
|
||||
`REPORT FORM ... TO FILE <txt> ASCII NOCONSOLE`; dovada de aspect si verificarea finala se face cu
|
||||
**90** + `PREVIEW` -> PNG (vezi `preview-png.md`).
|
||||
- `TO FILE ... ASCII` (80) **trunchiaza la 80 de caractere/linie** (r6): capetele din dreapta pot
|
||||
aparea taiate ("Amortizarea pana l") fara ca raportul sa fie defect - e limita grilei ASCII, nu a
|
||||
paginii. Nu confunda cu trunchierea de camp ("..."/"***"), care se vede doar pe PNG.
|
||||
49
skills/roa-vfp-frx-report/references/stil-rapoarte.md
Normal file
49
skills/roa-vfp-frx-report/references/stil-rapoarte.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Stilul rapoartelor ROA (aprobat de Marius pe `Rapoarte/pvcasare_mf`)
|
||||
|
||||
Model de referinta: `Rapoarte/pvcasare_mf.fr2` (portret, grupare per document, tabel cu linii).
|
||||
Aplica stilul de la inceput; fiecare punct de mai jos a costat o runda de corectura.
|
||||
|
||||
## Pagina si fonturi
|
||||
- Portret (`ORIENTATION=0`, A4) daca incape; tabelul pe ~74000 FRU, toate obiectele cu acelasi X max.
|
||||
- Firma (`goFirma.Antet`): Arial 10 bold. Titlul: Arial 12 bold, centrat.
|
||||
- Restul (texte, cap de tabel, detaliu, total, comisie): **Arial Narrow 10** (bold la capete si total).
|
||||
Subsolul de pagina: Arial Narrow 8.
|
||||
- Texte fixe fara diacritice, sentence case ("Numar document", nu "NUMAR DOCUMENT").
|
||||
|
||||
## Grupare
|
||||
- Un document (PV, factura etc.) per grup, cu pagina noua: GH cu `EXPR` = cheia documentului
|
||||
(ex. `ALLTRIM(NVL(nrdoc,'')) + '|' + DTOS(NVL(datadoc,{}))`), `PAGEBREAK=.T.`.
|
||||
- Continutul per document (numar/data, texte, cap de tabel) sta in **Group Header**, nu in Page
|
||||
Header; toate obiectele din GH cu `SUPRPCOL=3`, `SUPGROUP=6`. Page Header doar firma + titlu.
|
||||
- Nr. crt. si totalurile se reseteaza pe grup: variabile de raport (Count / Sum, `RESETTOTAL=6`),
|
||||
nu precalcul in cursor, nu `recno()` (`totaluri.md`).
|
||||
|
||||
## Ordonare
|
||||
- Ordinea o da cursorul, nu raportul: `INDEX ON <cheia grupului> + <sortarea din grup>` (ex.
|
||||
`Alltrim(nrdoc) + '|' + Dtos(datadoc) + denumire`). Cheia din INDEX = exact `EXPR`-ul GH, altfel
|
||||
grupul se rupe in mai multe pagini.
|
||||
- Campuri din Oracle: NULL si tipuri reale (nrdoc numeric, datadoc datetime) -> `Nvl`, `Iif(Isnull...)`,
|
||||
`Str()`; testeaza cu tipurile reale, nu doar cu cursori sintetici.
|
||||
|
||||
## Tabel
|
||||
- Cap de tabel pe **un singur rand**, texte scurte ("Amortizare", nu "Amortizarea pana la ..."),
|
||||
centrate (`@I`), fundal gri (`FILL*=200`, `MODE=0`) doar pana la marginea tabelului, linie sub cap.
|
||||
- Coloana de text lunga (Denumirea) lata; coloanele numerice cat valoarea maxima cu masca.
|
||||
Text la stanga, numere la dreapta (`OFFSET=1`), masca `get_mask(14,gnpa)` peste tot (si la total).
|
||||
- Linii verticale in detaliu: `OBJTYPE=6`, `STRETCH=.T.`, aceleasi `HPOS` ca in capul de tabel.
|
||||
- Campul de text care se poate lungi (Denumirea): `STRETCH=.T.` (se rupe pe mai multe randuri,
|
||||
nu se trunchiaza).
|
||||
- **Linie orizontala sub fiecare rand de detaliu**, pe toata latimea tabelului, la baza benzii
|
||||
Detail, cu **`FLOAT=.T.`** (`STRETCH=.F.`, `TOP=.F.`) - altfel ramane sus cand randul se lungeste.
|
||||
- Tabelul se inchide doar cu linia ultimului rand: **fara linie suplimentara la inceputul GF**
|
||||
(se dubleaza si iese o linie groasa).
|
||||
- Fara spatiu gol intre capul de tabel si primul rand (detaliul incepe imediat sub linia capului).
|
||||
|
||||
## Total si subsol de document (Group Footer)
|
||||
- Randul "TOTAL" fara chenar, sub tabel, valorile aliniate sub coloanele lor, aceeasi masca.
|
||||
- Comisia: tabel cu perechi "Nume si prenume | Semnatura" alaturate, pe latimea tabelului, un
|
||||
singur rand gol pentru semnaturi (nu 3).
|
||||
|
||||
## Verificare specifica stilului
|
||||
- Test cu o denumire lunga (3-4 randuri): linia orizontala coboara sub rand, verticalele se intind.
|
||||
- Doua documente -> doua pagini, nr. crt. reincepe de la 1, totalurile per document.
|
||||
50
skills/roa-vfp-frx-report/references/totaluri.md
Normal file
50
skills/roa-vfp-frx-report/references/totaluri.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Totaluri pe grup: variabile de raport (standard ROA)
|
||||
|
||||
In rapoartele ROA totalurile se calculeaza **in raport**, cu variabile de raport resetate pe grup -
|
||||
nu se precalculeaza in cursor. Randurile numerice: `EXPR = transform(camp, get_mask(14, gnpa))`
|
||||
(masca cu SPATIU, ca rapoartele existente).
|
||||
|
||||
## Reteta (dovedita pe `Rapoarte\pvcasare_mf.frx`, 24.09.2026, beh 90, 2 randuri/PV)
|
||||
|
||||
Variabila = inregistrare `OBJTYPE=18` in .frx (creata de Marius in IDE, Report > Variables):
|
||||
|
||||
| camp | valoare | sens |
|
||||
|---|---|---|
|
||||
| `NAME` | `tot_valoare` | numele variabilei |
|
||||
| `EXPR` | `valoare` | expresia NUMERICA acumulata (camp brut, nu `transform`) |
|
||||
| `TOTALTYPE` | `2` Sum / `1` Count | Calculate |
|
||||
| `RESETTOTAL` | `6` | reset la grupul 1 (= nr. grup + 5); `1` = sfarsit raport, `2` = pagina |
|
||||
| `TAG` | `0` | valoarea initiala |
|
||||
| `PENRED` | `0` | (asa o scrie IDE-ul) |
|
||||
|
||||
Afisarea in Group Footer: camp `OBJTYPE=8` cu `EXPR = transform(tot_valoare, get_mask(14,gnpa))`,
|
||||
**`TOTALTYPE=0`** (variabila deja acumuleaza).
|
||||
|
||||
**Nr. crt. pe grup**: variabila Count (`TOTALTYPE=1`, `RESETTOTAL=6`, `EXPR=id_mf`), afisata cu
|
||||
`alltrim(str(nrcrt_pv))`. **NU `recno()`**: cursorul se parcurge pe index (`SET ORDER`), deci
|
||||
`recno()` e ordinea fizica (a dat 5,3 / 4,1 in loc de 1,2 / 1,2) si nici nu se reseteaza pe grup.
|
||||
|
||||
## Capcane
|
||||
|
||||
- **Nume de variabila = nume de camp din cursor**: campul castiga (rezolvarea VFP a numelor
|
||||
necalificate), deci raportul afiseaza campul, nu variabila. Scoate campul din cursor sau da-i
|
||||
variabilei alt nume.
|
||||
- **Creare programatica**: `APPEND BLANK` + `REPLACE` pe campuri alese de mana a dat **Fatal error
|
||||
C0000005** la `REPORT FORM` (r4, `verify80b.prg`). Ce merge: copiaza o variabila facuta in IDE
|
||||
(`LOCATE FOR objtype=18` + `SCATTER MEMO NAME lo` + modifici `name/expr/totaltype` + `APPEND BLANK`
|
||||
+ `GATHER MEMO NAME lo`) - randat OK. Daca raportul nu are inca o variabila, cere-o facuta in IDE
|
||||
sau copiaz-o din `COMUN\Rapoarte\factura.frx`.
|
||||
- `EXPR = transform(camp, ...)` + `TOTALTYPE=2` pe campul de afisare -> `****` (motorul nu suma
|
||||
string-uri). Suma se face pe variabila numerica, `transform` doar la afisare.
|
||||
- `PICTURE` e un **sir literal de format**, nu expresie: `PICTURE = "get_mask(14,gnpa)"` tipareste
|
||||
textul. Nu pune masca in PICTURE.
|
||||
- Test obligatoriu cu **cel putin 2 randuri pe grup** si randuri inserate fizic in afara ordinii
|
||||
indexului - un singur rand pe grup nu dovedeste nici suma, nici resetul, nici nr. crt.
|
||||
|
||||
## Masca numerica `get_mask`
|
||||
|
||||
`get_mask` real (`COMUN\programe\oproceduri_comune.prg:1719`; `proceduri_comune.prg:1090`)
|
||||
construieste masca cu **SPATIU**: `transform(3000, "99 999 999 999 999.99")` => `"3 000.00"`.
|
||||
`SET SEPARATOR` nu schimba asta. **Stub-ul din harness trebuie sa intoarca masca reala** (cu spatiu);
|
||||
un stub cu virgula (runda 2) a facut preview-ul sa arate virgule - adica preview-ul minte. Definiti
|
||||
`FUNCTION get_mask` identic cu cel real (vezi sablonul din SKILL.md, la Scripturi).
|
||||
20
skills/roa-vfp-frx-report/scripts/frx_convert.ps1
Normal file
20
skills/roa-vfp-frx-report/scripts/frx_convert.ps1
Normal file
@@ -0,0 +1,20 @@
|
||||
# frx_convert.ps1 - converteste un raport VFP .frx <-> .fr2 cu FoxBin2Prg (fara IDE).
|
||||
# .frx -> .fr2 (BIN2PRG): textul editabil/diffabil. .fr2 -> .frx + .frt (PRG2BIN): regenerare
|
||||
# dupa editarea textului. Extensia fisierului dat decide directia; verifica iesirea asteptata.
|
||||
# Utilizare:
|
||||
# powershell -File frx_convert.ps1 -Path Rapoarte\raport.frx # scrie raport.fr2
|
||||
# powershell -File frx_convert.ps1 -Path Rapoarte\raport.fr2 # scrie raport.frx + .frt
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Path,
|
||||
[string]$Exe = 'D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { Write-Output ('frx_convert: nu gasesc ' + $Path); exit 2 }
|
||||
if (-not (Test-Path -LiteralPath $Exe -PathType Leaf)) { Write-Output ('frx_convert: nu gasesc FoxBin2Prg ' + $Exe); exit 2 }
|
||||
$ext = [IO.Path]::GetExtension($Path).ToLower()
|
||||
if ($ext -eq '.frx') { $out = [IO.Path]::ChangeExtension($Path, '.fr2') }
|
||||
elseif ($ext -eq '.fr2') { $out = [IO.Path]::ChangeExtension($Path, '.frx') }
|
||||
else { Write-Output ('frx_convert: extensie neasteptata ' + $ext + ' (doar .frx/.fr2)'); exit 2 }
|
||||
$p = Start-Process -FilePath $Exe -ArgumentList ('"' + $Path + '"') -Wait -WindowStyle Hidden -PassThru
|
||||
if ($p.ExitCode -eq 0 -and (Test-Path -LiteralPath $out)) { Write-Output ('frx_convert: OK -> ' + $out); exit 0 }
|
||||
Write-Output ('frx_convert: ESUAT (exit ' + $p.ExitCode + '), nu a aparut ' + $out); exit 1
|
||||
96
skills/roa-vfp-frx-report/scripts/randare_preview.ps1
Normal file
96
skills/roa-vfp-frx-report/scripts/randare_preview.ps1
Normal file
@@ -0,0 +1,96 @@
|
||||
# randare_preview.ps1 - deschide un raport VFP in PREVIEW si salveaza pagina intr-un PNG, fara sa
|
||||
# fure focus (PrintWindow pe fereastra copil de preview, nu captura de ecran).
|
||||
# Captureaza strict fereastra "Report Preview - <raport>.frx [- Page N]" (copil al shell-ului VFP);
|
||||
# NU "Report Designer" si NU fereastra Command Window.
|
||||
# Scriptul VFP dat (-Fxp, precompilat) trebuie sa pregateasca cursorul de test, apoi:
|
||||
# REPORT FORM "<raport>" PREVIEW NOWAIT
|
||||
# =INKEY(<secunde>, 'H') && cat timp scriptul e viu, fereastra randeaza si poate fi capturata
|
||||
# Utilizare:
|
||||
# powershell -File randare_preview.ps1 -Fxp ui_render.FXP -Png preview.png -TitleMatch pvcasare_mf
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Fxp,
|
||||
[string]$Png,
|
||||
[int]$WaitSec = 20,
|
||||
[string]$TitleMatch = '',
|
||||
[string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if (-not (Test-Path -LiteralPath $Fxp -PathType Leaf)) { Write-Output ('randare_preview: nu gasesc ' + $Fxp); exit 2 }
|
||||
if (-not $Png) { $Png = Join-Path (Split-Path -Parent $Fxp) 'preview.png' }
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
public class W32Prev {
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT { public int Left, Top, Right, Bottom; }
|
||||
public delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
|
||||
[DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr hWnd, EnumProc cb, IntPtr lParam);
|
||||
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder s, int max);
|
||||
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT r);
|
||||
[DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdc, uint flags);
|
||||
[DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
|
||||
public static string Text(IntPtr h) { var sb = new StringBuilder(512); GetWindowText(h, sb, 512); return sb.ToString(); }
|
||||
public static List<IntPtr> Children(IntPtr parent) {
|
||||
var list = new List<IntPtr>();
|
||||
EnumChildWindows(parent, delegate(IntPtr h, IntPtr l) { list.Add(h); return true; }, IntPtr.Zero);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
$proc = Start-Process -FilePath $Vfp -ArgumentList @('-A', ('"' + $Fxp + '"')) -PassThru
|
||||
$page = [IntPtr]::Zero
|
||||
$deadline = (Get-Date).AddSeconds($WaitSec)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
if ($proc.HasExited) { break }
|
||||
$proc.Refresh()
|
||||
$main = $proc.MainWindowHandle
|
||||
if ($main -ne [IntPtr]::Zero) {
|
||||
foreach ($h in [W32Prev]::Children($main)) {
|
||||
if (-not [W32Prev]::IsWindowVisible($h)) { continue }
|
||||
$t = [W32Prev]::Text($h)
|
||||
if (-not $t) { continue }
|
||||
if ($t -like '*Print Preview*') { [W32Prev]::MoveWindow($h, 2100, 0, 320, 90, $true) | Out-Null; continue }
|
||||
if ($t -like '*Report Designer*' -or $t -like '*Command Window*') { continue }
|
||||
if ($t -notlike '*Report Preview*') { continue }
|
||||
$match = if ($TitleMatch) { $t -like ('*' + $TitleMatch + '*') } else { $true }
|
||||
if ($match) {
|
||||
$r = New-Object W32Prev+RECT
|
||||
[W32Prev]::GetWindowRect($h, [ref]$r) | Out-Null
|
||||
if (($r.Right - $r.Left) -gt 200 -and ($r.Bottom - $r.Top) -gt 200) { $page = $h; break }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($page -ne [IntPtr]::Zero) { break }
|
||||
Start-Sleep -Milliseconds 400
|
||||
}
|
||||
if ($page -eq [IntPtr]::Zero) {
|
||||
if (-not $proc.HasExited) { $proc.Kill() }
|
||||
Write-Output 'randare_preview: nu am gasit fereastra de preview (TitleMatch gresit sau raportul nu s-a deschis)'
|
||||
exit 1
|
||||
}
|
||||
[W32Prev]::MoveWindow($page, 0, 0, 1450, 980, $true) | Out-Null
|
||||
Start-Sleep -Seconds 3
|
||||
$r = New-Object W32Prev+RECT
|
||||
[W32Prev]::GetWindowRect($page, [ref]$r) | Out-Null
|
||||
$w = $r.Right - $r.Left; $h = $r.Bottom - $r.Top
|
||||
if ($w -le 0 -or $h -le 0) { $w = 1450; $h = 980 }
|
||||
$bmp = New-Object System.Drawing.Bitmap $w, $h
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$hdc = $g.GetHdc()
|
||||
$ok = [W32Prev]::PrintWindow($page, $hdc, 2)
|
||||
$g.ReleaseHdc($hdc); $g.Dispose()
|
||||
if (-not $ok) {
|
||||
$g2 = [System.Drawing.Graphics]::FromImage($bmp); $hdc2 = $g2.GetHdc()
|
||||
[W32Prev]::PrintWindow($page, $hdc2, 0) | Out-Null
|
||||
$g2.ReleaseHdc($hdc2); $g2.Dispose()
|
||||
}
|
||||
$bmp.Save($Png, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bmp.Dispose()
|
||||
if (-not $proc.HasExited) { $proc.Kill() }
|
||||
Write-Output ('randare_preview: SAVED ' + $Png + ' (' + $w + ' x ' + $h + ')')
|
||||
60
skills/roa-vfp-frx-report/scripts/structura_frx.prg
Normal file
60
skills/roa-vfp-frx-report/scripts/structura_frx.prg
Normal file
@@ -0,0 +1,60 @@
|
||||
* structura_frx.prg - listeaza structura unui raport VFP (.frx): benzile si obiectele lor,
|
||||
* exact cum le asociaza motorul de raportare (prin FFC\_FRXCursor.vcx).
|
||||
* Utilizare headless: precompileaza intai, apoi
|
||||
* vfp9.exe -A "<...>\structura_frx.FXP" "<raport>.frx" ["<iesire>.txt"]
|
||||
* Fara al doilea parametru scrie langa raport, in "<raport>_structura.txt". Se termina cu QUIT.
|
||||
LPARAMETERS tcReport, tcOut
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
PRIVATE pcReport, pcOut
|
||||
LOCAL lnF, i, lcLine
|
||||
LOCAL ARRAY laF[1, 1]
|
||||
pcReport = IIF(VARTYPE(tcReport) = "C", tcReport, "")
|
||||
IF EMPTY(pcReport) OR NOT FILE(pcReport)
|
||||
STRTOFILE("structura_frx: raport inexistent: " + pcReport + CHR(13)+CHR(10), FORCEEXT(pcReport, "structura.txt"))
|
||||
QUIT
|
||||
ENDIF
|
||||
pcOut = IIF(EMPTY(tcOut), FORCEEXT(pcReport, "structura.txt"), tcOut)
|
||||
ON ERROR DO errhand WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
|
||||
STRTOFILE("Raport: " + pcReport + CHR(13)+CHR(10), pcOut)
|
||||
SET CLASSLIB TO (HOME() + "FFC\_FRXCursor.vcx") ADDITIVE
|
||||
USE (pcReport) IN 0 ALIAS FRX
|
||||
SELECT FRX
|
||||
loFRX = CREATEOBJECT("FRXCursor")
|
||||
loFRX.CreateObjectCursor()
|
||||
IF USED("BANDS")
|
||||
SELECT BANDS
|
||||
lnF = AFIELDS(laF, "BANDS")
|
||||
STRTOFILE("=== BENZI ===" + CHR(13)+CHR(10), pcOut, 1)
|
||||
SCAN
|
||||
lcLine = ""
|
||||
FOR i = 1 TO lnF
|
||||
IF INLIST(laF[i,1], "BANDLABEL", "START", "STOP", "HEIGHT")
|
||||
lcLine = lcLine + laF[i,1] + "=" + TRANSFORM(EVALUATE(laF[i,1])) + " "
|
||||
ENDIF
|
||||
NEXT
|
||||
STRTOFILE(lcLine + CHR(13)+CHR(10), pcOut, 1)
|
||||
ENDSCAN
|
||||
ENDIF
|
||||
IF USED("OBJECTS")
|
||||
SELECT OBJECTS
|
||||
lnF = AFIELDS(laF, "OBJECTS")
|
||||
STRTOFILE("=== OBIECTE (VPOS -> banda) ===" + CHR(13)+CHR(10), pcOut, 1)
|
||||
SCAN
|
||||
lcLine = ""
|
||||
FOR i = 1 TO lnF
|
||||
IF INLIST(laF[i,1], "OBJTYPE", "OBJCODE", "VPOS", "HPOS", "HEIGHT", "WIDTH", "BANDLABEL", "START_BAND_ID", "EXPR")
|
||||
lcLine = lcLine + laF[i,1] + "=" + TRANSFORM(EVALUATE(laF[i,1])) + " "
|
||||
ENDIF
|
||||
NEXT
|
||||
STRTOFILE(lcLine + CHR(13)+CHR(10), pcOut, 1)
|
||||
ENDSCAN
|
||||
ENDIF
|
||||
USE IN FRX
|
||||
STRTOFILE("gata" + CHR(13)+CHR(10), pcOut, 1)
|
||||
QUIT
|
||||
|
||||
PROCEDURE errhand
|
||||
LPARAMETERS tnErr, tcMsg, tcProg, tnLine
|
||||
STRTOFILE("ERROR " + TRANSFORM(tnErr) + " " + tcMsg + " @ " + tcProg + ":" + TRANSFORM(tnLine) + CHR(13)+CHR(10), pcOut, 1)
|
||||
ENDPROC
|
||||
@@ -5,6 +5,7 @@ procedura si criteriile care blocheaza "gata"); docurile sunt doar referinta con
|
||||
skill-ului.
|
||||
|
||||
- editezi `.vc2`/`.sc2`/`.prg` cu diacritice, cauti in `.vcx`/`.scx`, adaugi membri de clasa -> skill `roa-vfp-text-edit`
|
||||
- creezi sau editezi un raport `.frx`/`.frt` (layout, band-uri, grupare, totaluri, preview) -> skill `roa-vfp-frx-report`
|
||||
- rulezi sau depanezi teste headless ori UI -> skill `roa-vfp-headless-test`
|
||||
- commit / `svn update` / `roa_sync` in COMUN -> skill `roa-git-svn-commit`
|
||||
- scrii sau publici scripturi de migrare Oracle, reexporti un pachet -> skill `roa-oracle-migration`
|
||||
|
||||
Reference in New Issue
Block a user