# Supliment: gruparea pseudo-liniei de discount in `C_TVA_FACTURA` Completare la `discount_document_cota_tva.md`, scrisa de al doilea agent pe aceeasi intrebare. **Nu repeta** raportul principal — acopera exact punctul pe care acela il lasa deschis la finalul sectiunii 3 („ipoteza gruparii separate — nu am atins-o deloc, o verifica alt agent"), plus doua verificari prin rulare pe care raportul principal nu le are. Cercetare read-only: niciun fisier de cod atins, zero write-back, pe Oracle numai `SELECT`, niciun apel catre ANAF (validarea s-a facut **offline**, cu DUKIntegrator din `D:\ROA\COMUNROA\dist_efactura`). ## Verdict in 5 randuri Ipoteza pe care o ridicasem — ca pseudo-linia de discount de document, avand `id_jtva_coloana = 0`, iese din `LEFT JOIN` cu `coloana_jv` NULL si formeaza propriul grup in `C_TVA_FACTURA`, cu `TaxableAmount` negativ — este **infirmata pentru factura obisnuita** si **confirmata pentru factura scutita / cu taxare inversa / intracomunitara**. In al doilea caz XML-ul chiar iese cu doua `TaxSubtotal`-uri pe cota 0, unul cu baza negativa si categoria `Z`. **Dar validatorul nu-l respinge** — l-am rulat. Deci: comportament gresit ca modelare, nu defect care blocheaza factura. --- ## 1. Ce am masurat, nu dedus: `IIF()` peste NULL in VFP Toata ipoteza atarna de o singura intrebare: ce intoarce `Iif(Left(.Null.,2) = 'CE', 1, 0)`. Daca ar intoarce `.NULL.`, cheia de grupare a pseudo-liniei ar diferi de a liniilor reale si gruparea s-ar rupe pe **orice** factura. Am rulat o sonda headless (`vfp9.exe -A -T`) care reproduce exact `LEFT JOIN`-ul si `GROUP BY`-ul din `xmlefactura.prg:226-248`, cu o pseudo-linie de discount pe `id_jtva_coloana = 0`: ``` 1) IIF(.NULL.="CE",1,0) -> tip=N isnull=.F. 2) IIF(LEFT(.NULL.,2)="CE",1,0) -> tip=N isnull=.F. 3) IIF(INLIST(.NULL.,...),1,0) -> tip=N isnull=.F. linie=ARTICOL A | coloana_jv nul=.F. | intracom=0 | scutit=0 linie=ARTICOL B | coloana_jv nul=.F. | intracom=0 | scutit=0 linie=Discount 10.00 % Factura | coloana_jv nul=.T. | intracom=0 | scutit=0 4) grupuri in C_TVA_FACTURA = 1 grup: cota=21 baza=1350 tva=283.50 (1500 - 150, corect) ``` **`IIF()` intoarce ramura falsa, nu `.NULL.`**, chiar daca conditia e nula. Deci pe o factura interna obisnuita pseudo-linia primeste `intracomunitar=0, taxare_inversa=0, scutit=0` — exact ca liniile reale — si **se contopeste in grupul cotei maxime**. Sectiunile 3 si 4 din raportul principal raman valabile. Sonda: `...\scratchpad\probe_null.prg`. Nu depinde de baza de date si se poate reface oricand. ## 2. Unde ipoteza se confirma totusi: facturile scutite / taxare inversa / intracomunitare Egalitatea de mai sus tine doar cat timp **si liniile reale** au `scutit=0` si `expltva` gol. Pe o factura scutita nu e asa: | camp din cheia `Group By` (`xmlefactura.prg:246`) | liniile reale (scutite) | pseudo-linia de discount de document | |---|---|---| | `id_jtva_coloana` | id real (ex. 11) | **0** — `prelucreaza_facturacrs` nu-l seteaza niciodata pe randul `ZZZZ` (`ofacturare_comun.prg:1891-1896`) | | `coloana_jv` (din `LEFT JOIN`) | `'WRSCDD'` | `.NULL.` — `cJTVAVanzariTemp` e incarcat cu `where afisat > 0 and id_jtva_coloana > 0` (`updateserver.prg:578`), deci **nu exista rand cu id 0** | | `scutit` | **1** | **0** | | `expltva` | `'Scutit cu drept de deducere'` | **gol** | Ultimul rand merita explicat, pentru ca e contraintuitiv: `completeaza_explicatie_tva` (`ofacturare_comun.prg:2077-2137`) chiar **include** pseudo-linia in lista de id-uri cautate — scanul de la `:2094` e `For proc_tva = 1 And Nvl(id_jtva_coloana,-1) <> -1`, iar pseudo-linia are `proc_tva = 1` (cota 0) si `id_jtva_coloana = 0`, deci trece filtrul. Dar interogarea de la `:2108` e `where id_jtva_coloana in (...)` pe `jtva_coloane`, unde id-ul 0 nu exista, asa ca `Replace expltva ... For NVL(id_jtva_coloana,0) = m.lnIdJtva` (`:2129`) nu o atinge niciodata. Ramane goala. Doua campuri diferite din cinci => **doua grupuri**. XML-ul iese cu: ```xml ... Discount 539.82 Z0... 0.00 -539.82 0.00 Z0... 4410.00 0.00 E0 Scutit cu drept de deducere... ``` Categoria `Z` a grupului orfan vine din `GetTipTaxa` ramura cu ramura (`xmlefactura.prg:1116-1140`): `intracomunitar=0` sare peste `'K'`, `taxare_inversa=0` sare peste `'AE'`, `scutit=0` sare peste `'E'`, si cade pe `Case m.lcTipFactura = 'F' And m.lnProcTva = 0` -> **`'Z'`** (`:1128-1129`). In plus, cautarea categoriei pentru `AllowanceCharge` (`xmlefactura.prg:782-786`) devine ambigua: `Select tip From C_TVA_FACTURA Where proc_tva = 0 Into Array agettipcota` gaseste acum **doua** randuri (`E` si `Z`), iar codul ia neconditionat `agettipcota(1)` — primul in ordinea cursorului, nu cel corect prin vreun criteriu. **Nu am reprodus scenariul end-to-end prin program** (ar fi cerut o factura scutita cu discount de document, si in baza de dev nu exista — vezi punctul 4). Il stabilesc din citirea codului plus semantica `IIF`/NULL masurata la punctul 1. ## 3. Validare offline: XML-ul gresit **nu** e respins Am construit XML-urile si le-am trecut prin DUKIntegrator (`java -jar DUKIntegrator.jar -c -v FACT1 `, exact apelul din `xmlefactura.prg:1192`), pornind de la o factura reala acceptata de ANAF ca schelet: | scenariu | fisier | rezultat | |---|---|---| | scutit + discount de document, **cu grupul orfan** (ce produce codul azi) | `scutit_bug.xml` | **ok** | | scutit + discount, un singur `TaxSubtotal` net (varianta corecta) | `scutit_corect.xml` | **ok** | | cote mixte 21%+11%, tot discountul pe 21% (ce produce codul azi) | `mixt_azi.xml` | **ok** | | cote mixte 21%+11%, discount repartizat proportional | `mixt_proportional.xml` | **ok** | | discount mai mare decat baza cotei maxime -> `TaxableAmount = -400.00` si `TaxAmount = -84.00` pe grupul 21% | `mixt_baza_negativa.xml` | **ok** | | **control negativ**: `TaxExclusiveAmount` stricat intentionat | `control_stricat.xml` | **eroare BR-CO-13 + BR-CO-15** | Controlul negativ conteaza: fara el, cele cinci „ok" n-ar dovedi nimic. Validatorul chiar valideaza. **De ce trec**: regulile EN16931 pe categorii (BR-S-08, BR-E-08, BR-Z-08) cer ca baza declarata pe o categorie sa fie egala cu *suma liniilor din acea categorie minus alocarile din aceeasi categorie*. Grupul orfan respecta asta trivial (`0 - 539.82 = -539.82`), iar grupul `E` la fel (`4410.00 - 0`). Aritmetica inchide; **modelarea fiscala e cea gresita**, si asta niciun schematron nu prinde. Avertisment onest: validatorul local e din 2022 (`ro16931-ubl-1.0.8`, jar-ul din ianuarie 2022). Validatorul online curent al ANAF poate fi mai strict. **Nu l-am apelat** — interdictie explicita. ## 4. De ce nu am putut proba pe date reale - In baza accesibila (`MARIUSM_AUTO@ROA_CENTRAL`) exista **3** facturi cu discount de document, toate cu **o singura cota** si toate anterioare eFacturii (2008, 2014 x2), niciuna cu `efactura = 1`. Separat exista 34 de facturi cu cote mixte, dar **niciuna** cu discount de document. Deci intersectia care ne intereseaza e goala. **Asta nu inseamna ca nu apare in productie** — baza de dev are 712 facturi in total. - Cele 4 XML-uri de productie cu `AllowanceCharge` de document pe care le-am examinat sunt, dupa toate semnele, **discounturi pe linie** cu `discount_evidentiat = 1`, nu discounturi de document: cel de la `2024_07\...CIA_TRADE_POSSIBLE` are cote 9% si 19%, iar alocarea unica e pe **9%** — adica pe cota **minima**, ceea ce regula `Max(proc_tvav)` nu poate produce; iar cel de la `2024_01\...BEST_POWER_DANCE` are **doua** alocari (9% si 19%), ceea ce discountul de document, fiind o singura pseudo-linie, nu poate produce nici el. Nu am gasit niciun XML de productie care sa fie sigur discount de **document**. - Firmele din `D:\ROA\Efactura` (VENDING_MASTER, CLEVER_MOTORS, ...) nu au scheme in baza accesibila (`select ... from all_users` — zero potriviri), deci nu am putut confrunta XML-ul cu antetul lui. ## 5. Un lucru dovedit pe un fisier real, nu dedus Pe `D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml`: ``` BT-5 DocumentCurrencyCode = EUR ... 539.82 539.82 ``` Aceeasi suma apare o data ca `RON` si o data ca `EUR` — `currencyID` e hardcodat `"RON"` la `xmlefactura.prg:778`, in timp ce restul documentului foloseste `mmoneda`. Doua precizari care schimba interpretarea: - fisierul de pe disc este **identic pe SHA256** cu cel din `...\TRIMISE\4172939206_3250292036.zip`, deci e exact ce a plecat la ANAF; - si a fost **acceptat**. Respingerea din `...\ERORI\4172675286_3249814520.zip` e a unei incarcari *anterioare* si e pentru alte doua reguli (`BR-CO-15` si `BR-CL-04` — cod de moneda invalid, probabil `EURO` in loc de `EUR`, ceea ce explica linia de corectie `xmlefactura.prg:267`). Deci `currencyID="RON"` pe factura in valuta e o **neconformitate reala si activa in cod azi**, dar nu am dovada ca ar cauza o respingere. ## 6. Ce inseamna pentru #13 Repartizarea proportionala pe cote — recomandarea din raportul principal — **rezolva si problema de aici**, fara nicio garda in plus: daca discountul se sparge pe cotele care exista efectiv pe factura, fiecare bucata mosteneste `id_jtva_coloana` si `expltva` ale grupului ei, grupul orfan dispare, iar `agettipcota(1)` nu mai poate fi ambiguu. Subscriu, cu doua completari: 1. **Pseudo-liniile de discount trebuie sa primeasca `id_jtva_coloana` al grupului pe care il reduc**, nu doar cota. Cota singura nu ajunge — cheia de grupare are cinci campuri, iar `expltva` se completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` ar lasa grupul orfan exact acolo unde e azi. 2. **Regula traieste in doua locuri** — VFP (`Calculate Max(proc_tvav)`) si PL/SQL (`MAX(ROUND(discount * (proc_tvav - 1), ...))` in `recalculeaza_totaluri_vanzari`). Daca se schimba doar una, `VANZARI.TOTAL_TVA` si TVA-ul din XML vor diferi pe facturile cu cote mixte. ## 7. Ce ramane deschis - Scenariul de la punctul 2 **nu e reprodus prin program**, ci dedus din cod + semantica `IIF` masurata. O confirmare ar cere o factura scutita cu discount de document, introdusa manual. - Ordinea randurilor in `C_TVA_FACTURA` dupa `GROUP BY` (deci ce alege `agettipcota(1)` intre `E` si `Z`) nu e garantata de VFP si nu am fixat-o experimental. Am presupus `scutit=0` inaintea lui `scutit=1`; daca e invers, categoria alocarii iese `E` in loc de `Z` — tot gresita, dar altfel. - Validatorul ANAF **online** curent nu a fost testat (interdictie). Tot ce spun despre „nu e respins" se refera la DUKIntegrator 2022 de pe disc. - Nu am verificat calea in **valuta** (`prelucreaza_factura_valuta`) linie cu linie.