# View-urile eFactura: ANAF_VEFACTURA_* Cele sapte view-uri prin care ROA citeste facturile electronice descarcate/incarcate la ANAF, plus regula de potrivire cu registrul de TVA. Suna simplu si nu e: potrivirea are doua ramuri, cateva invariante de performanta si un istoric de fals-pozitive platite scump. Cine le atinge citeste intai aici. ## Tabelele reale si view-urile peste ele Tabelele sunt `ANAF_EFACTURA` (antetul, un rand per factura) si `ANAF_EFACTURA_DETALII` (liniile). Coloana `FACTURA_EMISA` desparte cele doua lumi: `0` = achizitie (primita), `1` = vanzare (trimisa). Restul sunt view-uri: | View | Peste ce | La ce foloseste | |---|---|---| | `ANAF_VEFACTURA_PRIMIT` | `ANAF_EFACTURA` + `JC2007` + `NOM_PARTENERI` | achizitii, cu potrivirea in jurnalul de cumparari | | `ANAF_VEFACTURA_TRIMIS` | `ANAF_EFACTURA` + `JV2007` + `NOM_PARTENERI` | vanzari, cu potrivirea in jurnalul de vanzari | | `ANAF_VEFACTURA_EMIS` | + `VANZARI`, `VNOM_VALUTE` | facturile emise din ROA, legate de documentul intern | | `ANAF_VEFACTURA_DETALII` | `ANAF_EFACTURA_DETALII` + `NOM_ARTICOLE`, `NOM_GESTIUNI` | liniile, cu articolul si gestiunea ROA potrivite | | `..._PRIMIT_DETALIU`, `..._TRIMIS_DETALIU`, `..._EMIS_DETALIU` | detaliile filtrate pe view-ul de antet respectiv | griduri de detaliu | Unde se consuma: **borderoul eFactura**, `COMUN\programe\import_efactura.prg`, `PROCEDURE vizImportEFactura` - alege `anaf_vefactura_primit` sau `anaf_vefactura_trimis` dupa parametrul `PRIMITE`/`TRIMISE` (linia 27) si coloreaza randurile dupa diferenta fata de jurnal. Detaliile se iau din `anaf_vefactura_detalii` (linia 60). Vizualizarea XML e in `COMUN\programe\anaf_efactura.prg`, `PROCEDURE viz_efactura_xml` / `viz_spv`. Pe ecran, `jtotctva` e coloana `cJtotcTVA` din `COMUN\clase\anaf_efactura.vc2`, prezenta pe toate cele trei griduri ale formularului: `pgfeFactura.Page1.grdFacturiEmise` (`anaf_efactura.vc2:1240`), `Page2.grdFacturiPrimite` (`:2713`) si `Page3.grdFacturiTrimise` (`:4029`). ## Potrivirea cu registrul de TVA: jtotctva si jtva_ti `jtotctva` = totalul cu TVA gasit in jurnal pentru factura respectiva. **`jtotctva IS NULL` inseamna "documentul nu e in registrul de TVA"** - nu exista o coloana separata cu numarul de potriviri, nulul e semnalul. `jtva_ti` (doar pe `PRIMIT`) e TVA-ul de taxare inversa, ca borderoul sa nu coloreze fals facturile cu 4426=4427; pe `TRIMIS` e constanta 0. Potrivirea are **doua ramuri legate prin `COALESCE`**, in ordinea asta: 1. **dupa numar** - aceeasi zi, acelasi partener, iar `NRACT` din jurnal e unul din grupurile de cifre ale lui `XNUMAR_ACT` (sau concatenarea tuturor cifrelor). Ramura normala, acopera aproape tot. 2. **rezerva pe valoare** - aceeasi zi, acelasi partener, aceeasi suma cu TVA. Ruleaza numai pentru randurile unde prima a intors NULL. ## Ce NU se atinge - fiecare regula e platita - **`COALESCE`, nu `NVL` si nu `OR`.** `COALESCE` isi evalueaza argumentele lene, deci a doua ramura ruleaza doar unde prima n-a gasit nimic. Unite prin `OR` in aceeasi subinterogare: peste 10 minute pe acelasi set de date, si potrivire nediferentiata a oricarui document cu aceeasi suma la acelasi partener in aceeasi zi (masurat pana la 49 de documente pentru o singura factura). - **Forma predicatului pe numar.** Grupurile de cifre se extrag din `XNUMAR_ACT` (coloana din exterior) si se compara cu `j.nract` prin `IN`, ca `NRACT` sa intre in `access()` pe `IDX_JC2007_003` / `IDX_JV2007_003`. Scris invers, ca `REGEXP_LIKE` peste `j.nract`, nu mai e sargabil: 97 de secunde in loc de 1,4 pe acelasi lot. - **`avg`, nu `sum`, pe rezerva pe valoare.** Doua facturi cu aceeasi valoare, acelasi partener, aceeasi zi potriveau fiecare ambele randuri din jurnal, iar `sum()` dadea valoare dubla. - **`sum(NVL(j.totctva,0))`, nu `sum(j.totctva)`**, ca `sum()` sa fie NULL doar cand nu s-a potrivit niciun rand - altfel `jtotctva` si `jtva_ti` pot alege ramuri diferite. - **`HAVING`-ul de anti-ghicire pe rezerva pe valoare.** Se potriveste doar daca numarul de eFacturi din aceeasi zi, cu acelasi cod fiscal si aceeasi valoare, nu depaseste numarul de randuri de jurnal cu acea valoare. Mai multi pretendenti decat randuri = nu se ghiceste. **`having count(*) = 1` e interzis**: exista cazul real a doua facturi cu aceeasi valoare, acelasi partener, aceeasi zi, ambele in jurnal. - **`ID_FACT` nu e utilizabil la achizitii** - se completeaza doar cand factura e inregistrata din wizardul de import (13 randuri din 970 pe schema de test). - **`SERIE_ACT` nu e cheie** - exista in `JC2007`/`JV2007`, dar e populat ~3%. - Un document apare in `JC2007` cu mai multe randuri (`an`/`luna` diferite, reportarea neexigibilului), de aceea view-urile filtreaza `j.an`/`j.luna` dupa `xdata_act` si ramura pe numar foloseste `sum()`. - **Fara obiecte noi si fara indecsi noi.** S-au respins deja: view ajutator cu numerele candidate, `not exists` de anti-revendicare, ramura pe `ID_FACT`. - Scris compatibil **Oracle 11g**. ## Codul fiscal: cand se cere egalitate si cand nu Codul fiscal se normalizeaza pe **ambele** parti (`REGEXP_REPLACE(upper(...), '[^[:digit:]]','')`), nu doar cel din `NOM_PARTENERI`. La persoanele fizice care nu isi declara CNP-ul, XML-ul duce la ANAF `CompanyID`-ul cumparatorului completat cu **13 de zero** (`0000000000000`) - eticheta nu poate lipsi - in timp ce partenerul din `NOM_PARTENERI` are `COD_FISCAL` NULL. Egalitatea stricta nu se poate implini niciodata, deci toate facturile catre persoane fizice ieseau cu `jtotctva` NULL. La un client de vending: 20780 din 47369 de facturi emise. De aceea, **numai pe ramura pe numar din `ANAF_VEFACTURA_TRIMIS`**, pe langa egalitate se accepta si cazul in care **ambele** parti sunt "fara cod fiscal" (normalizat: NULL, gol sau numai zerouri). Garda pe ambele parti e esentiala: o factura cu cod fiscal real nu poate prinde randul unui partener fara cod fiscal, si invers. Relaxarea **nu costa indecsi**: conditia de cod fiscal era si inainte un filtru dupa jonctiune, nu un predicat de acces - are `REGEXP_REPLACE` peste ambele coloane, deci nu putea intra niciodata in `access()`. `EXPLAIN PLAN` pe productie da acelasi plan hash (`1870929383`) si acelasi cost inainte si dupa: `NRACT` ramane in `access()` pe indexul din `JV2007`, `AN`/`LUNA` raman filtru pe tabela, iar `OR`-ul nou apare doar in `filter()` peste randurile deja aduse. Ce alege randurile e tot numarul plus data. **Rezerva pe valoare ramane stricta**, si `ANAF_VEFACTURA_PRIMIT` la fel: - la un client cu vanzare catre persoane fizice, valorile identice in aceeasi zi sunt frecvente, iar la acelasi client 17034 din 24006 parteneri nu au cod fiscal - relaxata acolo, rezerva ar potrivi documente nediferentiat; - la achizitii nu exista niciun `COD_FISCAL_EMITENT` nul sau cu zerouri, furnizorii sunt persoane juridice. ## Unde stau si cum se livreaza View-urile se recreeaza integral din scripturile `ff_*_COMUN_EFACTURA.sql` din `D:\ROA\DATABASE\SCRIPTURI_CLAR\\\`. Fiecare script **isi poarta motivatia in antet** - acolo sunt masuratorile si cazurile reale care au dus la forma actuala; se citeste inainte de orice modificare si nu se rescrie fara motiv. Ultimele: `ff_2026_07_29_01_COMUN_EFACTURA.sql` (potrivirea dupa numar, sargabila), `ff_2026_08_28_01_COMUN_EFACTURA.sql` (`HAVING`-ul de anti-ghicire + cazul persoanelor fizice). Rollback = se ruleaza pur si simplu scriptul anterior. Sursa de referinta pentru DDL e schema `MARIUSM_AUTO` de pe `ROA_CENTRAL`, nu productia - vezi `scripturi-migrare-db.md`. Publicarea la clienti: `COMUN\utile\publicare_scripturi.ps1` (`publicare-scripturi-db.md`); fisierele trebuie **CRLF si ASCII pur**. **Changelog: nu.** O modificare numai de baza de date, fara versiune noua de executabil, nu intra in `changelog_roacont.txt` - changelog-ul insoteste livrarea unui `.exe`. ## Cum se verifica o modificare inainte de livrare Comparatie rand-cu-rand intre view-ul curent si expresia corectata, pe o schema de productie cu volum real, numarand patru lucruri: **reparate**, **pierdute**, **valoare schimbata**, **ramase NULL**. "Pierdute" si "valoare schimbata" trebuie sa fie **zero** - orice altceva inseamna ca modificarea atinge si cazuri la care nu te asteptai. Sablonul de interogare e in `D:\ROA\ROACONT\docs\analiza_efactura_cnp_zerouri.md`. Conectarea la productie prin tunel: `conexiuni-tunel-ssh-odbc.md`. Pe productie **doar `SELECT`**.