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
11 KiB
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:
<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 cuefactura = 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
AllowanceChargede document pe care le-am examinat sunt, dupa toate semnele, discounturi pe linie cudiscount_evidentiat = 1, nu discounturi de document: cel de la2024_07\...CIA_TRADE_POSSIBLEare cote 9% si 19%, iar alocarea unica e pe 9% — adica pe cota minima, ceea ce regulaMax(proc_tvav)nu poate produce; iar cel de la2024_01\...BEST_POWER_DANCEare 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.zipe a unei incarcari anterioare si e pentru alte doua reguli (BR-CO-15siBR-CL-04— cod de moneda invalid, probabilEUROin loc deEUR, ceea ce explica linia de corectiexmlefactura.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:
- Pseudo-liniile de discount trebuie sa primeasca
id_jtva_coloanaal grupului pe care il reduc, nu doar cota. Cota singura nu ajunge — cheia de grupare are cinci campuri, iarexpltvase completeaza tot prinid_jtva_coloana. O implementare care seteaza doarproc_tvaar lasa grupul orfan exact acolo unde e azi. - Regula traieste in doua locuri — VFP (
Calculate Max(proc_tvav)) si PL/SQL (MAX(ROUND(discount * (proc_tvav - 1), ...))inrecalculeaza_totaluri_vanzari). Daca se schimba doar una,VANZARI.TOTAL_TVAsi 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
IIFmasurata. O confirmare ar cere o factura scutita cu discount de document, introdusa manual. - Ordinea randurilor in
C_TVA_FACTURAdupaGROUP BY(deci ce alegeagettipcota(1)intreEsiZ) nu e garantata de VFP si nu am fixat-o experimental. Am presupusscutit=0inaintea luiscutit=1; daca e invers, categoria alocarii ieseEin loc deZ— 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.