Files
comun/docs/view-uri-efactura.md
2026-08-28 13:21:37 +03:00

8.3 KiB

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\<an>\<luna>\. 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.