diff --git a/docs/README.md b/docs/README.md index 7d1e371..82884dc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ Notite concise despre structura interna a proiectului (fluxuri, pachete, tabele) - [CLAUDE.md](CLAUDE.md) — modul de lucru Claude ↔ Marius (propuneri de cod pentru VFP IDE, acces Oracle direct). - [flux-achizitie-import.md](flux-achizitie-import.md) — lantul achizitie import: gestiuni.vcx → achizitie_import → import_nota → import_nir → PACK_CONTAFIN; capcane (ptva bruta vs proc_tva, import_nota_original mort, 4427/401). +- [rapoarte-sql-predefinite.md](rapoarte-sql-predefinite.md) — meniul „Rapoarte SQL": tabelele RAPOARTE/_FILTRE/_UTILIZATORI, parametrii `<%=AN%>`, sablonul de script de instalare; capcane (drepturi lipsa, `commit` lipsa). - [git-svn-crlf.md](git-svn-crlf.md) — modificari fantoma in git dupa svn update (CRLF/mtime); fix cu `.gitattributes` (`* -text`) la radacina fiecarui repo. - Credentialele Oracle stau in `COMUN\docs\local\oracle.md` (neversionat in git, exista in SVN). diff --git a/docs/rapoarte-sql-predefinite.md b/docs/rapoarte-sql-predefinite.md new file mode 100644 index 0000000..74bc8bf --- /dev/null +++ b/docs/rapoarte-sql-predefinite.md @@ -0,0 +1,113 @@ +# Rapoarte SQL (meniu Rapoarte → „Rapoarte SQL") + +Mecanism de raportare **fără `.frx`**: SQL-ul stă în baza de date, aplicația îl execută și afișează +rezultatul într-un grid (cu grupare/ordonare/export Excel). Se instalează printr-un script de +migrare, nu prin VFP IDE. + +## Lansare și drepturi + +- `Meniuri\rapoarte.mn2:36-37` → `DO alege_raport WITH 2, .T. IN orapoarte_dinamice.prg`. +- `alege_raport` (`COMUN\programe\orapoarte_dinamice.prg:5-79`): al doilea parametru `tlDrepturi=.T.` + ⇒ lista se face cu **`join rapoarte_utilizatori ru on ... and ru.id_utilizator = gnIdUtil`**. + **Un raport fără linii în `RAPOARTE_UTILIZATORI` nu apare deloc în meniu**, oricât de corect ar fi. +- `executa_raport(tnIdFormularRaport, tnIdRaport)` (`:85-107`) deschide direct un raport după id, + fără lista de alegere. + +`ID_FORMULAR_RAPORT` alege formularul: + +| valoare | formular | ce e | +|---|---|---| +| 1 | `frm_date_rapoarte_rulaje` | rapoarte bazate pe rulaje | +| **2** | **`frm_date_rapoarte_sql`** (`COMUN\clase\orapoarte.vc2:4076`) | **rapoarte SQL** | +| 3 | `frm_date_rapoarte_balpart` | balanță parteneri | + +## Tabele + +- **`RAPOARTE`** — `CSQL` (CLOB, textul SQL), `CSQL_PARAMETRI` (XML, definiția parametrilor), + `DENUMIRE` (numele din listă, VARCHAR2(1000)), `TITLU` (titlul listării, 150), `ID_FORMULAR_RAPORT`, + `STERS`. Secvență: `SEQ_RAPOARTE`. View de listare: `VRAPOARTE`. +- **`RAPOARTE_FILTRE`** — o linie per raport, `VALOARE` = șir concatenat + `titlu;camp;pozitie_semn;;valoare|` (ex. `AN;AN;1;;0|LUNA;LUNA;1;;0|`). Generat de + `parametri_rapoarte_sql` (`orapoarte_dinamice.prg:186-206`), citit la deschidere + (`orapoarte.vc2:2709`). Marcajul `luna_curenta|` e înlocuit la citire cu luna/anul de lucru. +- **`RAPOARTE_UTILIZATORI`** — `(ID_RAPORT, ID_UTILIZATOR)`, drepturile. FK-uri către `RAPOARTE`: + doar `RAPOARTE_FILTRE` și `RAPOARTE_UTILIZATORI`. + +## Parametri `<%=NUME%>` + +În `CSQL` parametrii se scriu `<%=AN%>`, `<%=LUNA%>`. La rulare, +**`ct_selectie_sql.do_instructiune_sql`** (`COMUN\clase\orapoarte.vc2:1552-1637`) înlocuiește +**textual** (`STUFFC`) fiecare marcaj cu valoarea din criteriile de selecție — **nu sunt bind +variables**, deci valoarea intră brut în SQL. + +Cuvinte rezervate, completate automat din globale (`orapoarte.vc2:1578-1586`): + +| parametru | sursă | +|---|---| +| `AN` | `gnAn` | +| `LUNA` | `gnLuna` | +| `SUCURSALA` | `gcCondSucursala` | + +Acestea **trebuie declarate cu `tipcamp = 'E'`**, iar `campid` = numele parametrului; altfel valoarea +nu se rezolvă și apare „Nu este completata valoarea parametrului X!". + +`CSQL_PARAMETRI` = XML `VFPData` (encoding `Windows-1252`), câte un nod `crsparametri` cu +`parametru / titlu / camp / tipcamp / campid` (+ opțional `campcautare`); citit cu `XMLTOCURSOR` în +`criterii_rapoarte_sql` și `parametri_rapoarte_sql` (`orapoarte_dinamice.prg:109-251`). +`tipcamp`: `N` numeric, `C` caracter, `D` dată, `D1` perioadă (se traduce în `AN*12+LUNA`), +`E` cuvânt rezervat. + +## Instalare printr-un script de migrare + +Șablon de urmat: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql` +(raportul `CHELTUIELI CUMULAT - REG JURNAL + RULAJE`, peste view-ul `VRUL_ACT_CHELTUIELI`) și +`...\2026\08\ff_2026_08_21_01_GESTIUNE_RAPOARTE.sql` (`VENITURI SI CHELTUIELI - REG JURNAL + +RULAJE`, view `VRUL_ACT_VEN_CHELT`). Structura: + +1. `CREATE OR REPLACE VIEW` cu logica raportului (SQL-ul din `CSQL` rămâne un `select` simplu peste + view — mai ușor de depanat și de refolosit). +2. Bloc anonim idempotent: `IF (select count(*) from rapoarte where upper(denumire) = '...') = 0` + → `SEQ_RAPOARTE.nextval` + `MERGE` în `RAPOARTE` + `MERGE` în `RAPOARTE_FILTRE` + `INSERT` în + `RAPOARTE_UTILIZATORI` din `syn_vutilizatori` ⋈ `syn_vdef_util_firme` ⋈ + `syn_nom_firme where schema = user and nvl(sters,0) = 0`, cu `inactiv = 0`. +3. `exec pack_migrare.UpdateVersiune(''); commit;` + +### Capcane + +- **Commit.** Blocurile anonime nu comit singure. Rulat dintr-un client care nu comite la ieșire, + view-urile rămân create (DDL = commit implicit) dar raportul lipsește din `RAPOARTE` — arată exact + ca o problemă de drepturi. Verificare rapidă: `SEQ_RAPOARTE.last_number` a avansat, dar + `select * from rapoarte` nu are linia ⇒ lipsește `commit`. +- Drepturile nu sunt opționale (vezi mai sus). +- Ordinea coloanelor din `select`-ul din `CSQL` = ordinea coloanelor din grid. +- `ORDER BY` se scrie în `CSQL`; coloanele tehnice de sortare pot fi selectate sau nu, dar trebuie să + existe în view. + +### Linii de total în același raport + +Pentru totaluri la finalul listării (fără al doilea raport): `UNION ALL` cu rânduri sintetice care au +coloana de grupare **`NULL`** (Oracle sortează `NULLS LAST` la `ASC`, deci ajung ultimele) și un +`R_TIP` mai mare decât cele de detaliu; eticheta se pune într-o coloană text lată (`EXPLICATIA`), +valoarea în coloana de sumă. Exemplu: `VRUL_ACT_VEN_CHELT`, `R_TIP` 9-15. + +### Performanță: `WITH` materializat blochează filtrul de perioadă + +Într-un view de raport filtrat pe `an`/`luna`, o subinterogare `WITH` **referită de două ori** e +materializată de Oracle într-un tabel temporar (`TEMP TABLE TRANSFORMATION` în plan). O subinterogare +materializată **nu mai primește predicatul exterior**: se construiește tot istoricul și abia la final +se aplică `an = ... and luna = ...`. Pe date de producție asta a însemnat 7.4M rânduri și un plan de +3h15m pentru o lună (`VRUL_ACT_VEN_CHELT`, versiunea inițială). + +- `/*+ INLINE */` în CTE **nu** a rezolvat-o când CTE-ul e un `UNION ALL`. +- Ce funcționează: **view-uri separate în loc de CTE-uri referite de mai multe ori** — view-urile se + îmbină (view merging) și primesc predicatul. Vezi `VRUL_ACT_VEN_CHELT_DET` (detaliul) + + `VRUL_ACT_VEN_CHELT` (detaliu `UNION ALL` totaluri, referă `_DET` de două ori) în + `SCRIPTURI_CLAR\2026\08\ff_2026_08_21_01_GESTIUNE_RAPOARTE.sql`. +- Subinterogările auxiliare (ancore de dată, teste de existență, totaluri de control) citesc + **tabelele de bază `ACT` / `RUL`**, nu `VACT_TOT` / `VRUL_TOT`: view-urile astea nu filtrează + rânduri (doar left join-uri de nomenclator), dar `VRUL_TOT` are `FDOC`/`ID_FDOC` ca **subinterogări + scalare pe `ACT` per rând**. Indecși utili: `ACT` `IDX_ACT_006 (AN,LUNA,STERS,…)`, + `IDX_ACT_002 (LUNA,AN,SCD,SCC,STERS,…)`; `RUL` `IDX_RUL_003 (AN,LUNA,COD)`. +- Verificare rapidă înainte de livrare: `EXPLAIN PLAN` pe interogarea din `CSQL` cu an/luna + concrete — dacă apare `TEMP TABLE TRANSFORMATION` sau predicatul `AN=`/`LUNA=` nu se vede la + accesele pe `ACT`/`RUL`, raportul va scana tot istoricul.