#13: formular de facturare unificat - etapa curenta
Squash al branch-ului de lucru plan13-s2. Cod: clasa noua ofacturare_util (geometrie si utilitare de formular), inrolata in roafacturare.prg; meniul "Facturare (nou)" plat, cu reparatia literalului peste limita VFP de 255 de caractere care impiedica compilarea metodei. Livrare: changelog 2.11.19, marcajul de versiune DB pentru scripturile finale din SCRIPTURI_CLAR\2026\09, inventarul livrarii in docs/livrare_13.md si registrul de erori deschise. Documentatia de lucru a etapei (cercetare, rapoarte, handoff-uri, snapshot-uri de scripturi si backup-uri de rollback) a fost scoasa din arbore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
This commit is contained in:
@@ -1,189 +0,0 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user