Files
roagest/docs/rapoarte-sql-predefinite.md

8.0 KiB

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!".

Valoarea tastată de utilizator pentru un parametru rezervat este ignorată — se pune global. Deci un interval (ex. lunile 1..7) cere doi parametri tipcamp = 'N' cu alt nume, iar numele nu are voie să înceapă cu AN/LUNA/SUCURSALA: potrivirea din do_instructiune_sql e = VFP, iar cu SET EXACT OFF un LUNA_DE_LA s-ar potrivi cu LUNA. Ex. DE_LA_LUNA / PANA_LA_LUNA, folosite în SQL ca luna between <%=DE_LA_LUNA%> and <%=PANA_LA_LUNA%>.

Parametrul e citit din expresia de filtru spartă pe AND și apoi pe =, deci merge doar operatorul „egal" (implicit la N) și numele nu poate conține AND.

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_24_03_GESTIUNE_RAPOARTE.sql (VENITURI SI CHELTUIELI - REG JURNAL + RULAJE, view-urile VRUL_ACT_VEN_CHELT_DET + VRUL_ACT_VEN_CHELT_TOT). 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('<nume_script>'); 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).
  • ACT.ID_PARTD / ACT.ID_PARTC sunt 0 când nu există partener, niciodată NULL. Un MIN(ID_PARTD) peste notele unui document întoarce 0 și numele partenerului iese gol; corect e MIN(NULLIF(ID_PARTD, 0)). Numele se iau din NOM_PARTENERI.NUME (în VACT_TOT sunt deja rezolvate ca PARTD/PARTC).
  • 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_TOT, R_TIP 9-15.

Când raportul merge pe un interval de perioade, totalurile nu pot sta în view: view-ul le poate da doar pe perioadă (altfel rândul agregat are an/luna NULL și cade la filtrul exterior). Deci view-ul dă totalurile pe lună, iar UNION ALL-ul din CSQL le însumează pe intervalul cerut (group by r_tip, tip, explicatia) — toate liniile fiind sume, adunarea pe luni e validă. Ramura de totaluri pune NULL pe an/luna, deci blocul iese o singură dată, la finalul listării.

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_TOT (totalurile pe lună, agregă _DET) în SCRIPTURI_CLAR\2026\08\ff_2026_08_24_03_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.