Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
190 lines
11 KiB
Markdown
190 lines
11 KiB
Markdown
# 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
|
|
<cac:AllowanceCharge>
|
|
... <cbc:AllowanceChargeReason>Discount</cbc:AllowanceChargeReason>
|
|
<cbc:Amount currencyID="RON">539.82</cbc:Amount>
|
|
<cac:TaxCategory><cbc:ID>Z</cbc:ID><cbc:Percent>0</cbc:Percent>...
|
|
</cac:AllowanceCharge>
|
|
<cac:TaxTotal><cbc:TaxAmount currencyID="RON">0.00</cbc:TaxAmount>
|
|
<cac:TaxSubtotal><cbc:TaxableAmount currencyID="RON">-539.82</cbc:TaxableAmount> <!-- grupul orfan -->
|
|
<cbc:TaxAmount currencyID="RON">0.00</cbc:TaxAmount>
|
|
<cac:TaxCategory><cbc:ID>Z</cbc:ID><cbc:Percent>0</cbc:Percent>...
|
|
<cac:TaxSubtotal><cbc:TaxableAmount currencyID="RON">4410.00</cbc:TaxableAmount> <!-- liniile reale, NEnete -->
|
|
<cbc:TaxAmount currencyID="RON">0.00</cbc:TaxAmount>
|
|
<cac:TaxCategory><cbc:ID>E</cbc:ID><cbc:Percent>0</cbc:Percent>
|
|
<cbc:TaxExemptionReason>Scutit cu drept de deducere</cbc:TaxExemptionReason>...
|
|
</cac:TaxTotal>
|
|
```
|
|
|
|
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 <config> -v FACT1 <in.xml> <out.txt>`, 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
|
|
<cac:AllowanceCharge> ... <cbc:Amount currencyID="RON">539.82</cbc:Amount>
|
|
<cbc:AllowanceTotalAmount currencyID="EUR">539.82</cbc:AllowanceTotalAmount>
|
|
```
|
|
|
|
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.
|