44 KiB
Cota de TVA a discountului de DOCUMENT (VANZARI.DISCOUNT) — program + eFactura
Cercetare read-only. Nu s-a modificat niciun fisier, nu s-a rulat git_sync.ps1, pe Oracle doar
SELECT.
STATUS: complet.
Acest document e rezultatul imbinarii a doua rapoarte de cercetare pe aceeasi intrebare, facute de
doi agenti separati. Raportul suplimentar, discount_document_cota_tva_b.md, ramane pe disc ca
sursa a partii adaugate aici (sectiunile 3.1-3.3 si observatiile din 2.3 si 4.1).
Raspunsul scurt
Discountul de document NU se sparge pe cote. El devine o singura pseudo-linie negativa
numita "Discount <procent> % Factura", careia i se atribuie cota maxima de TVA de pe factura
(Calculate Max(proc_tvav) To lnProcTvav). eFactura chiar genereaza cac:AllowanceCharge la nivel
de document, cu TaxCategory/ID + Percent + TaxScheme corecte si AllowanceChargeReason = "Discount", ReasonCode = 95 — dar cota e cea maxima, aleasa prin Max(), nu de
utilizator si nici proportionala, iar "explicatia" e literalul hardcodat "Discount".
Regula "cota maxima" e scrisa de doua ori, independent: in VFP (Calculate Max(proc_tvav)) si in
PL/SQL (PACK_FACTURARE.recalculeaza_totaluri_vanzari, care din ea deriva VANZARI.DISCOUNT_TVA,
TOTAL_TVA si TOTAL_CU_TVA). Pentru #13 asta e informatia operationala: se schimba in doua
locuri, nu in unul (sectiunea 1.5).
Pe factura obisnuita (cote mixte, fara scutire), XML-ul rezultat e intern coerent (TaxSubtotal-urile includ deja pseudo-linia de discount) si trece validarea — verificat cu validatorul local DUKIntegrator, inclusiv pe cazul mixt si pe unul cu baza impozabila negativa. Deci defectul pe care l-am putut dovedi nu se vede: e o eroare tacuta de atribuire fiscala. Pe o factura cu 1000 lei la 21% si 1000 lei la 11% cu discount de document 10%, se declara cu 10 lei mai putin TVA decat ar trebui (sectiunea 4.4), mereu in acelasi sens — TVA colectat subdeclarat. Pe factura scutita XML-ul e si mai putin coerent decat atat — vezi paragraful urmator.
Sectiunea 3 s-a inchis intre timp, cu un rezultat impartit. Pe o factura obisnuita (toate
liniile scutit=0, expltva gol), pseudo-linia de discount se contopeste corect in grupul cotei
maxime — verificat prin masuratoare, nu doar dedus (sectiunea 3.1). Dar pe o factura scutita,
cu taxare inversa sau intracomunitara, gruparea chiar se rupe: pseudo-linia formeaza un
TaxSubtotal orfan, cu baza negativa si categorie gresita (sectiunea 3.2). Validatorul local nu
respinge nici aceasta forma (sectiunea 3.3) — deci ramane, ca si defectul de atribuire fiscala, o
eroare tacuta, nu una care blocheaza factura.
1. Cum se aplica azi discountul de document in program
Cine il aplica: amandoua, fiecare pentru consumatorul lui. Oracle stocheaza VANZARI.DISCOUNT
(valoare absoluta, in moneda documentului) si VANZARI.DISCOUNT_EVIDENTIAT, si isi calculeaza
singur TVA-ul discountului in PACK_FACTURARE.recalculeaza_totaluri_vanzari (sectiunea 1.5);
prelucrarea pentru listare si eFactura se face separat, in cursoare VFP (sectiunile 1.1-1.4).
Ambele folosesc aceeasi regula — cota maxima — dar o implementeaza independent.
1.1. Randul-sentinela ZZZZ... in crsfactura
COMUN\programe\ofacturare_comun.prg:1886-1897 (Procedure prelucreaza_facturacrs):
1886 If tnDiscount<> 0
1888 Calculate Sum(valdiminuatftva),Sum(vvaldiminuatftva),Max(id_temp) To lnTotalBaza,lnTotalBazaVal,lnIdTemp
1890 Append Blank
1891 Replace denumire With Replicate('Z',20),cantitate With 1,;
1892 pretftva With lnTotalBaza,discountftva With tnDiscount,valdiscountftva With tnDiscount,;
1893 valdiscounttva With Round(tnDiscount * (tnProcTvav - 1),gnPc),;
1894 vpretftva With lnTotalBazaVal,vdiscountftva With tnDiscountVal,vvaldiscountftva With tnDiscountVal,;
1895 vvaldiscounttva With Round(tnDiscountVal * (tnProcTvav - 1),gnPVal),;
1896 proc_Tvav With tnProcTvav,id_temp With lnIdTemp+1
1897 Endif
Observatii portante:
- baza discountului =
Sum(valdiminuatftva)peste toate liniile, indiferent de cota; - TVA-ul discountului =
tnDiscount * (tnProcTvav - 1)— o singura cota,tnProcTvav; - randul e un singur rand de document, nu o repartizare pe linii.
1.2. De unde vine tnProcTvav — cota maxima de pe factura
Toate cele trei cai de apel calculeaza acelasi lucru:
| apelant | linia | cod |
|---|---|---|
| listare factura emisa (din lista de facturi) | COMUN\clase\ofacturare_comun.vc2:4494-4498 (frm_facturi.do_listeaza_formular, 4188-4539) |
Calculate Max(proc_tvav) To lnProcTvav -> prelucreaza_facturacrs([crsdetalii],[crsfactura],lnProcTvav,lnDiscount,lnDiscountVal) |
| listare proforma | COMUN\clase\ofacturare_comun.vc2:7293-7298 (frm_proforme.do_listare, 7233-7315) |
idem |
listare din oproceduri_facturare |
COMUN\programe\oproceduri_facturare.prg:1386-1391 |
Select crsDetaliiListare / Calculate Max(proc_tvav) To lnProcTvav |
| facturare din stoc | COMUN\programe\ofacturare_stoc.prg:582, 728 |
Calculate Max(proc_tvav) To lnProcTvav |
Deci cota de TVA a discountului de document = MAX(cotele de pe liniile facturii). Nu e nici
aleasa de utilizator, nici derivata din structura pe cote, nici proportionala. Nu exista niciun
control in formular pentru ea si nicio coloana in VANZARI care sa o retina.
1.3. Randul ZZZZ... devine pseudo-linia "Discount NN.NN % Factura"
COMUN\programe\ofacturare_comun.prg:1273-1392 (in prelucreaza_factura, scan peste
crsfacttemp):
1279 If loArticol.denumire <> Replicate('Z',20)
... (linia normala de articol se copiaza in cursorul destinatie)
1361 Else
1362 loArticol.denumire = [FACTURA]
1363 Endif
1365 If lnPretListAviz = 2 && pret fara tva
1367 If loArticol.discountftva <> 0
1368 Append Blank
1369 lnProcentDiscount = loArticol.discountftva * 100 / loArticol.pretftva
1370 Replace denumire With [Discount ]+Alltrim(Str(lnProcentDiscount,10,2))+[ % ]+Alltrim(Proper(loArticol.denumire)),;
1374 pretftva With (-1) * loArticol.discountftva,valftva With (-1) * loArticol.valdiscountftva,...;
1375 ... valtva With (-1) * loArticol.valdiscounttva,;
1376 proc_tva With loArticol.proc_Tvav,id_jtva_coloana With loArticol.id_jtva_coloana,...
1378 Endif
Randul ZZZZ... nu ajunge niciodata in cursorul final ca atare — la :1361-1363 doar i se schimba
denumirea in FACTURA, iar la :1367-1377 se creeaza pseudo-linia
"Discount 10.00 % Factura" cu pretftva si valftva negative si proc_tva = tnProcTvav.
Coloanele atinse in cursorul final (crsFacturaFinala / crsfacturafinalaval): denumire,
cantitate, pretftva (negativ), valftva (negativ), valtva (negativ), proc_tva,
id_jtva_coloana, id_jtva_coloana_ex. Deci discountul de document mosteneste si coloana de
jurnal TVA (id_jtva_coloana) a randului-sentinela — care nu e setata la :1891-1896, deci
ramane 0/blank pe randul ZZZZ. (Consecinta in eFactura: sectiunea 2.3.)
1.4. discount_evidentiat schimba cate pseudo-linii "Discount" apar
discount_evidentiat = 0(implicit): ramuraofacturare_comun.prg:1177-1203. Liniile de articol primesc0 As discountftva(:1188), deci nu genereaza pseudo-linii; doar randulZZZZ(reinserat separat prinWhere denumire = Replicate('Z',20),:1193-1203) pastreazadiscountftva-> o singura pseudo-linie de discount, cea de document.discount_evidentiat = 1: ramura:1157-1173, un singurInsertfaraWHERE,discountftvapastrat per articol (coloana 20 dingroup by 2,3,...,20,...) -> fiecare articol cu discount unitar produce propria pseudo-linie"Discount X % <articol>", plus cea de document. Aici cotele chiar sunt cele ale articolelor respective.
1.5. Regula "cota maxima" e implementata A DOUA OARA, in Oracle
Corectie la afirmatia de la inceputul sectiunii 1 ("Oracle stocheaza doar VANZARI.DISCOUNT"):
este incompleta. PACK_FACTURARE.recalculeaza_totaluri_vanzari reface acelasi rationament
independent, in PL/SQL —
D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql, procedura de la
:16024:
16081 MAX(ROUND(decode(lnInValuta, 1,
16082 ROUND(a1.curs * NVL(lnDiscountFactura,0) / a1.multiplicator, lnPreciziePretV),
16085 NVL(lnDiscountFactura,0)) *
16088 (a1.proc_tvav - 1), lnPreciziePretV)) as DISC_TVA_RON,
16090 MAX(ROUND(NVL(lnDiscountFactura,0) * (a1.proc_tvav - 1), lnPreciziePretV)) as DISC_TVA_VAL,
Precizare pe forma exacta: nu e discount * MAX(proc_tvav), ci MAX(discount * (proc_tvav-1))
— maximul se ia peste produsele rotunjite. Cat timp lnDiscountFactura >= 0 cele doua coincid
(acelasi rand castiga), deci efectul e identic cu al VFP-ului. Ar diverge doar pe un discount
negativ (o majorare), unde MAX ar alege cota cea mai mica — n-am verificat daca un discount
negativ e posibil in UI.
Ce se scrie cu asta (:16049-16062 -> :16214-16227): nu doar VANZARI.DISCOUNT_TVA, ci si
totalurile salvate ale documentului:
16052 a.suma_tva_ron - a.disc_tva_ron as TOTAL_TVA,
16053 a.suma_fara_tva_ron - a.disc_fara_tva_ron + a.suma_tva_ron - a.disc_tva_ron as TOTAL_CU_TVA,
...
16214 update vanzari set discount = lnDiscountFactura, discount_tva = lnDiscountTVA, ...
16218 total_tva = lnTotalTVA, total_cu_tva = lnTotalCuTVA, ... where id_vanzare = V_ID_VANZARE;
Consecinta pentru #13: regula de repartizare a discountului pe cote trebuie schimbata in doua
locuri, nu unul — prelucreaza_facturacrs (VFP, pentru listare/eFactura/nota contabila) si
recalculeaza_totaluri_vanzari (Oracle, pentru VANZARI.DISCOUNT_TVA / TOTAL_TVA /
TOTAL_CU_TVA, adica pentru ce se vede in liste, rapoarte de sinteza si sold). Daca se schimba doar
unul, VANZARI.TOTAL_TVA si TVA-ul din XML vor diverge. Nu am verificat care dintre cele doua
valori e considerata azi "cea buna" acolo unde ambele sunt disponibile.
1.6. Ramura "pret cu TVA" (lnPretListAviz = 1) — nu atinge facturile
ofacturare_comun.prg:1380-1391 (ramurile Otherwise / tnDiscountEvidentiat=1 And lnPretListAviz=1) seteaza pe pseudo-linia de discount numai pretctva/valctva/valtva,
nu si pretftva/valftva — care raman 0. Am verificat daca asta poate ajunge in eFactura:
nu poate. lnPretListAviz e initializat = 2 (COMUN\programe\ofacturare.prg:1112) si e pus
pe 1 intr-un singur loc, in ramura de aviz de transfer (lcRaport = [AVIZ],
ofacturare.prg:1856-1861, conditionat de gnPretListAviz = 1), iar avizele nu trec de filtrul
tip_doc_394 IN ('F','S','M','U','H') din xmlefactura.prg:276-279. Deci nu e un defect eFactura.
2. Ce ajunge in XML-ul eFactura
Generatorul viu e COMUN\programe\xmlefactura.prg (getXmlEFactura), apelat prin
goExport.export2xml_efactura (COMUN\programe\oexport.prg:1586-1594) din
COMUN\programe\ofacturare.prg:2126, cu cursorul crsFacturaFinala (sau crsfacturafinalaval in
valuta) — exact cursorul de listare (ofacturare.prg:2108-2114).
COMUN\clase\anaf_efactura.vc2 este UI-ul/transportul ANAF (trimitere, validare, preview), nu
generatorul de XML.
2.1. Da — se genereaza cac:AllowanceCharge la nivel de document
Detectia e euristica pe denumire + semn, xmlefactura.prg:230:
230 Iif('DISCOUNT' $ Upper(a.denumire) And a.pretftva < 0, 1, 0) As discount ;
Pseudo-linia "Discount 10.00 % Factura" cu pretftva < 0 (sectiunea 1.3) satisface ambele
conditii. Randurile marcate discount = 1 sunt excluse din liniile normale (Scan For discount = 0, :901) si emise ca alocari de document, xmlefactura.prg:756-795:
757 If mliniireducere > 0
758 SELECT SUM(-1*valftva) as valftva, proc_tva ;
759 FROM C_IES_FORM ;
760 WHERE discount = 1 ;
761 GROUP BY proc_tva ;
762 ORDER BY proc_tva ;
763 INTO CURSOR cDiscounturiTemp
765 Select cDiscounturiTemp
766 SCAN
770 oallowancecharge = oinvoice.appendchild(oxml.createelement("cac:AllowanceCharge"))
771 ... ChargeIndicator = "false"
773 ... cbc:AllowanceChargeReasonCode = "95"
775 ... cbc:AllowanceChargeReason = "Discount"
777 odocallowancecharge = ... ("cbc:Amount") && BT-92
778 odocallowancecharge.setattribute("currencyID", "RON")
779 odocallowancecharge.Text = Alltrim(Str(m.lnValftva, 15, 2))
780 otaxcategoryallowance = ... ("cac:TaxCategory")
782 Select tip From C_TVA_FACTURA Where proc_tva = (m.lnProcTva - 1) * 100 Into Array agettipcota
786 otaxcategoryallowance.lastchild.Text = Alltrim(agettipcota(1))
787 ... ("cbc:Percent")
788 otaxcategoryallowance.lastchild.Text = Alltrim(Str((m.lnProcTva - 1) * 100, 2, 0))
789 otaxschemeallowance = ... ("cac:TaxScheme") / cbc:ID = "VAT"
792 ENDSCAN
Raspunsurile punctuale:
cac:TaxCategory/cbc:ID(S,AE,Z,E,K,O): nu e hardcodat — se ia dinC_TVA_FACTURA.tip, adica dinThis.GetTipTaxa(mtipfactura, intracomunitar, taxare_inversa, scutit, proc_tva, @lcExplicatie, @lcMotiv, expltva)(xmlefactura.prg:298), pe grupul cu aceeasi cota ca pseudo-linia de discount.cbc:Percent:(proc_tva - 1) * 100al pseudo-liniei — adica cota maxima de pe factura (sectiunea 1.2). Aici e raspunsul la intrebarea lui Marius: cota exista in XML, dar sursa ei e unMax(), nu o alegere.cbc:AllowanceChargeReason: literalul"Discount"(:776), hardcodat;cbc:AllowanceChargeReasonCode:"95"(:774), hardcodat. Textul real din program ("Discount 10.00 % Factura") nu ajunge in XML — ramane doar in cursorul de listare. Deci "explicatia" pe care o cauta Marius nu exista: e o constanta.
2.2. Cote mixte: se sparge in mai multe AllowanceCharge?
Mecanismul exista (GROUP BY proc_tva, :761 -> cate un AllowanceCharge per cota), dar
nu se activeaza pentru discountul de document, pentru ca acesta e prin constructie o singura
pseudo-linie cu o singura cota (Max). Gruparea foloseste efectiv doar cand
discount_evidentiat = 1, unde exista mai multe pseudo-linii de discount pe articol, fiecare cu
cota articolului ei.
Deci, pe o factura cu 21% si 11% si un discount de document: un singur AllowanceCharge, cu
Percent = 21.
2.3. Riscuri identificate in acest bloc (nedovedite pe rulare)
currencyIDhardcodat"RON"la:778, in timp ce tot restul documentului folosestemmoneda(:820, 827, 873, 876, ..., definit la:266-267). Pe o factura in valuta cu discount de document,AllowanceCharge/cbc:Amountiese cucurrencyID="RON"iarLegalMonetaryTotal/cbc:AllowanceTotalAmountcucurrencyID="EUR". Acelasi tipar la acciza (:803). Testat cu validatorul local (sectiunea 4.3): DUKIntegrator nu respinge neconcordanta. Ramane o inconsecventa de cod, dar nu doar teoretica: sectiunea 4.1 arata un XML real de productie cu exact aceasta neconcordanta, confirmat pe SHA256 ca a plecat la ANAF si a fost acceptat.Select ... Into Array agettipcotafara garda pe_Tally(:782-786): daca gruparea nu gaseste randul (egalitate pe numere in virgula mobila: campulC_TVA_FACTURA.proc_tvae stocat rotunjit, comparatia se face cu(lnProcTva-1)*100calculat la rulare),agettipcota(1)da eroare de variabila inexistenta. Risc teoretic — nu l-am putut reproduce; il semnalez ca "de verificat", nu ca defect.Str((lnProcTva-1)*100, 2, 0)— latime 2. Pentru cotele romanesti (0, 5, 9, 11, 19, 21) e in regula; o cota de trei cifre ar da**.
3. Coerenta cu TaxTotal / TaxSubtotal
Da, baza impozabila din XML tine cont de discount — si o face corect aritmetic.
C_TVA_FACTURA se construieste peste toate randurile din C_IES_FORM, inclusiv
pseudo-linia de discount (nu exista WHERE discount = 0), xmlefactura.prg:242-248:
242 Select(proc_tva - 1) * 100 As proc_tva, intracomunitar, taxare_inversa, scutit, expltva, ;
243 Sum(valtva) As tva, ;
244 Sum(valftva) As valoare, ... ;
245 From C_IES_FORM ;
246 Group By proc_tva, intracomunitar, taxare_inversa, scutit, expltva ;
247 Into Cursor C_TVA_FACTURA
valftva/valtva ale pseudo-liniei sunt negative -> grupul cotei maxime iese deja net de
discount. cbc:TaxableAmount = C_TVA_FACTURA.valoare (:828), cbc:TaxAmount =
C_TVA_FACTURA.tva (:831).
Totalurile inchid corect (:863-898):
864 Sum valftva To mtotalnet For discount = 0 && numai liniile reale
867 mtotalnetliniifactura = mtotalnet && BT-106 LineExtensionAmount
869 mtotalnet = mtotalnet + mtotalcharges - mtotalallowances && BT-109 TaxExclusiveAmount
870 mtotalbrut = mtotalnet + mtotaltva && BT-112 TaxInclusiveAmount
882 ... AllowanceTotalAmount = mtotalallowances && BT-107
cu mtotalallowances = mdiscounturi = Sum(-1*valftva) For discount = 1 (:282-287).
Verificare: Σ TaxSubtotal/TaxableAmount = suma peste toate randurile = mtotalnetliniifactura − mdiscounturi = mtotalnet = TaxExclusiveAmount. Deci BR-CO-13 (TaxExclusiveAmount = LineExtensionAmount − AllowanceTotalAmount + ChargeTotalAmount) si BR-CO-15 se respecta.
Concluzie: pe factura obisnuita, suma liniilor minus discount da TaxableAmount-ul
declarat; discrepanta nu e aritmetica, ci de atribuire pe cote (sectiunea 4). Pe factura
scutita / cu taxare inversa / intracomunitara, concluzia nu tine — vezi 3.1-3.3.
Rationamentul de mai sus presupune ca pseudo-linia de discount cade in acelasi grup cu liniile
de la cota maxima. Asta e adevarat doar daca cele patru chei suplimentare din GROUP BY
(intracomunitar, taxare_inversa, scutit, expltva, xmlefactura.prg:246) ies egale pe
pseudo-linie si pe liniile reale. Subsectiunile care urmeaza verifica exact asta, prin masuratoare,
nu prin deductie.
3.1. Verificat prin masuratoare: IIF() peste NULL nu se propaga ca .NULL. in VFP
Pseudo-linia are id_jtva_coloana = 0 (randul-sentinela e Append Blank la
ofacturare_comun.prg:1890 si campul nu e setat la :1891-1896). Daca LEFT JOIN-ul de la
xmlefactura.prg:226-232 nu gaseste rand in cJTVAVanzariTemp pentru id = 0, b.coloana_jv iese
.NULL., si intrebarea e ce intoarce Iif(Left(.Null.,2) = 'CE', 1, 0) — daca s-ar propaga ca
.NULL., cheia de grupare a pseudo-liniei ar diferi de a liniilor reale pe orice factura.
Am rulat o sonda headless (vfp9.exe -A -T, probe_null.prg) care reproduce exact acest LEFT JOIN si GROUP BY, 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. Sonda ramane pe disc in
%TEMP%\claude\...\scratchpad\probe_null.prg, nu depinde de baza de date si se poate reface
oricand.
3.2. Dar pe factura scutita / taxare inversa / intracomunitara, gruparea chiar se rupe
Contopirea de la 3.1 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. Ordinea nu e garantata de VFP si nu a fost fixata experimental;
categoria alocarii poate iesi E sau Z dupa caz — oricare din ele e gresita ca modelare, doar in
mod diferit.
Nu s-a reprodus scenariul end-to-end prin program (ar fi cerut o factura scutita cu discount de
document, iar in baza de dev nu exista — sectiunea 4.2). Ramane stabilit din citirea codului plus
semantica IIF/NULL masurata la 3.1, nu dintr-o factura rulata efectiv prin program.
3.3. Validare offline: XML-ul cu grup orfan nu e respins, dar controlul negativ functioneaza
XML-urile de mai sus au fost construite pornind de la o factura reala acceptata de ANAF ca schelet
si trecute prin validatorul local DUKIntegrator.jar (acelasi apel ca la 4.3):
| scenariu | ce contine | rezultat |
|---|---|---|
scutit_bug |
scutit + discount de document, cu grupul orfan (ce produce codul azi) | ok |
scutit_corect |
scutit + discount, un singur TaxSubtotal net (varianta corecta) |
ok |
control_stricat |
TaxExclusiveAmount stricat intentionat, ca martor ca validatorul chiar valideaza |
eroare BR-CO-13 + BR-CO-15 |
Controlul negativ conteaza: fara el, un „ok" pe scutit_bug n-ar dovedi nimic — ar putea insemna
ca validatorul nu verifica deloc coerenta totalurilor. Cu el, e clar ca validatorul chiar
verifica, si totusi trece grupul orfan.
De ce trece: 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.
Aceeasi rezerva ca in 4.3: validatorul local e din ianuarie 2022 (ro16931-ubl-1.0.8); validatorul
online curent al ANAF nu a fost apelat (interdictie explicita).
Verdict sectiunea 3: contopirea corecta descrisa mai sus e confirmata prin masuratoare pentru factura obisnuita (3.1) si infirmata pentru factura scutita / taxare inversa / intracomunitara (3.2), unde discountul de document formeaza un
TaxSubtotalorfan cu baza negativa si categorie ambigua (EsauZ). Pe niciuna din cele doua forme validatorul local nu respinge XML-ul (3.3, si 4.3 pentru cazul cu cote mixte) — deci ramane, ca si defectul din sectiunea 4, o eroare tacuta, nu una care blocheaza factura.
4. Defect real sau gol de proiectare
Verdict: NU s-a putut dovedi un defect de VALIDARE — nici pe factura obisnuita, nici pe cea scutita cu grupul orfan (sectiunea 3.2-3.3), validatorul local nu respinge XML-ul. S-a dovedit in schimb un defect de ATRIBUIRE FISCALA: pe facturi cu cote mixte, tot TVA-ul discountului de document se scade din cota maxima, si pe facturi scutite/taxare inversa/intracomunitare, o modelare fiscala gresita (grup orfan) care trece neobservata. Adica exact ce banuia Marius, dar consecinta stabilita nu e "factura respinsa", ci "TVA colectat gresit sau baza modelata gresit, tacut".
Nuanta ramasa, dupa inchiderea ipotezei din sectiunea 3: ipoteza grupului orfan s-a confirmat pentru facturile scutite/taxare inversa/intracomunitare (3.2), dar validatorul local nu o respinge (3.3) — deci nu devine, cum s-ar fi putut anticipa, un defect de validare propriu-zis, ci ramane in aceeasi categorie cu restul sectiunii 4: o eroare de modelare care nu blocheaza factura. Testele din 4.3 (cote mixte) si cele din 3.3 (factura scutita) acopera acum ambele forme.
4.1. Dovada ca mecanismul chiar produce AllowanceCharge de document in productie
Am cautat in cele ~250 de XML-uri eFactura salvate pe disc (D:\ROA\Efactura, D:\ROA\ROAEFACTURA,
D:\ROA\ROAFACTURARE\Utile\efactura) blocurile cac:AllowanceCharge care contin cac:TaxCategory
(marca alocarii de document; cele de linie nu au TaxCategory, au MultiplierFactorNumeric).
Exemple reale:
| fisier | Amount | TaxCategory | Percent |
|---|---|---|---|
D:\ROA\Efactura\2024_01\VENDING_MASTER_SRL\efactura_20240125_1526_BEST_POWER_DANCE_S_R_L_.xml |
13.84 / 25.02 (doua) | S / S | 9 / 19 |
D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml |
539.82 | E | 0 |
D:\ROA\Efactura\2024_03\VENDING_MASTER_SRL\efactura_20240301_3736_BRIANA_ALIMENT_SRL.xml |
411.01 | S | 9 |
D:\ROA\Efactura\2024_07\VENDING_MASTER_SRL\efactura_20240426_7446_CIA_TRADE_POSSIBLE_SRL.xml |
176.15 | S | 9 |
Forma emisa (exemplu real, ...2885...):
<cac:AllowanceCharge><cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Discount</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="RON">539.82</cbc:Amount>
<cac:TaxCategory><cbc:ID>E</cbc:ID><cbc:Percent>0</cbc:Percent>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme></cac:TaxCategory></cac:AllowanceCharge>
Precizare de onestitate: niciunul dintre aceste patru nu pot sa-l atribui sigur discountului de
document. Dimpotriva — la ...7446... factura are linii pe 9% (2016.51) si pe 19% (1477.11),
iar unica alocare cade pe 9%; daca ar fi fost discount de document, Max(proc_tvav) ar fi dat
19%. Deci acolo sunt discounturi pe linie cu discount_evidentiat = 1 (sectiunea 1.4). La
fel ...1526..., unde cele doua alocari sunt exact 2.00% din baza fiecarei cote. Nu am gasit pe
disc un XML in care sa pot identifica pozitiv un discount de document. Ce dovedesc aceste fisiere
e ca traseul cod -> AllowanceCharge cu TaxCategory chiar functioneaza in productie si ca
gruparea pe cote e reala cand exista mai multe pseudo-linii de discount.
Al treilea indiciu, pe acelasi fisier ...2885..., in sens invers. Factura e integral scutita
cu drept de deducere: toate cele trei linii sunt E / Percent 0 cu TaxExemptionReason = "Scutit cu drept de deducere", DocumentCurrencyCode = EUR, si are AllowanceCharge la nivel de
document de 539.82 cu TaxCategory ID = E. Are un singur TaxSubtotal: TaxableAmount = 3870.18, adica exact 4410.00 − 539.82 — net de discount, fara grup orfan.
Asta e semnificativ dupa sectiunea 3.2: expltva face parte din cheia de grupare, iar grupul unic
de aici poarta TaxExemptionReason completat. Daca pseudo-linia de discount ar fi avut expltva
gol si id_jtva_coloana = 0 (cazul discountului de document, descris la 3.2), gruparea nu s-ar
fi putut contopi — ar fi iesit doua grupuri, ca in scutit_bug (3.3). Contopirea observata aici
dovedeste deci ca pseudo-linia purta un id_jtva_coloana real, adica era un discount pe linie
(discount_evidentiat = 1), nu de document — un al treilea indiciu, independent de cele doua de mai
sus (alocare pe cota minima la ...7446...; doua alocari la ...1526...), care coroboreaza aceeasi
concluzie: cele patru XML-uri de productie gasite sunt discounturi pe linie, nu de document.
Fisierul arata insa pozitiv forma corecta pentru o factura scutita cu discount de document: cand
pseudo-linia poarta id_jtva_coloana corect, grupul iese unul singur si net — exact forma
recomandata la sectiunea 6, punctul 1. Deci recomandarea are un precedent real in productie, nu doar
o constructie sintetica de test. Rezerva onesta: fisierul e din februarie 2024, iar inferenta
presupune ca traseul de cod e neschimbat de atunci; ramane adevarat ca nu exista niciun XML de
productie identificat pozitiv ca discount de DOCUMENT.
Identitatea fisierului, verificata pe SHA256.
D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml (5922
octeti) are acelasi SHA256 (3303AFE34BC9E3E84DA9B3527C435DD9B8CB0BC7881D7D2F660A250237E23C64) cu
4172939206.xml din ...\TRIMISE\4172939206_3250292036.zip — deci e exact ce a plecat la ANAF, si
a fost acceptat. Continutul din ...\ERORI\4172675286_3249814520.zip e un mesaj de eroare de
446 octeti pentru o incarcare anterioara, cu alt index de incarcare, si listeaza exact doua
reguli: BR-CO-15 si BR-CL-04 (cod de moneda invalid). Deci respingerea aceea nu e a
fisierului cu currencyID="RON" discutat la sectiunea 2.3 — acela a trecut.
4.2. Datele din baza de dev — putine, dar arata ca situatia e posibila
MARIUSM_AUTO@ROA_CENTRAL, doar SELECT:
- facturi nesterse cu
VANZARI.DISCOUNT <> 0: 3 (id_vanzare 165 / 575 / 576;discount_evidentiat= 1 / 1 / 0). Toate trei au o singura cota pe linii (1.19, 1.24, 1.24). - facturi nesterse cu cote mixte pe linii: 34.
Deci in dev nu exista intersectia "discount de document + cote mixte". Asta nu dovedeste nimic — volumele de dev sunt mici, iar ambele ingrediente exista separat. In productie combinatia e banala (orice comerciant cu alimente 9%/11% + nealimentare 21% care da un discount global).
4.3. Ce am testat efectiv cu validatorul ANAF, offline
Am folosit validatorul local D:\ROA\COMUNROA\dist_efactura\DUKIntegrator.jar (-v FACT1,
reguli ro16931-ubl-1.0.8), acelasi pe care il apeleaza xmlefactura.prg:1166-1194. Nu s-a
trimis nimic catre ANAF; nu s-a apelat niciun serviciu web. XML-urile de test sunt in
%TEMP%\claude\...\scratchpad\ (base/scenA/scenB/scenC_*), construite plecand de la un XML real
valid.
| test | ce contine | rezultat |
|---|---|---|
base |
XML-ul real ...7446..., nemodificat (martor) |
ok |
scenA |
cote mixte 21% + 11%, discount 200 lei atribuit integral cotei 21% (exact ce genereaza programul) | ok |
scenB |
100 lei @21% + 5000 lei scutit, discount 510 lei la 21% -> TaxableAmount = -410.00, TaxAmount = -86.10 |
ok |
scenC_corect |
acelasi ca scenA, in EUR, cu AllowanceCharge/Amount currencyID="EUR" |
ok |
scenC_bug |
idem, dar cu currencyID="RON" pe alocare (ce face codul la :778) |
ok |
Concluzii, inclusiv cele care imi infirma ipotezele:
- Atribuirea intregului discount cotei maxime trece validarea — deci nu e o eroare pe care ANAF sa o prinda. E o eroare tacuta.
- Ipoteza mea ca o
TaxableAmountnegativa ar fi respinsa e infirmata de validatorul local. - Ipoteza ca neconcordanta de moneda (
currencyID="RON"pe factura in EUR) ar fi respinsa e tot infirmata de validatorul local. - Rezerva: validatorul instalat e din ianuarie 2022 (
DUKIntegrator.jar, 19.01.2022, reguli 1.0.8). Regulile ANAF s-au inasprit de atunci. Punctele 2 si 3 raman "neinfirmate de validatorul disponibil local", nu "garantat acceptate azi de ANAF".
4.4. Scenariul reproductibil al defectului REAL (fiscal)
Factura: client intern, in lei, discount_evidentiat = 0.
- Linia 1: marfa cota standard, 1 buc x 1000.00 lei, 21%
- Linia 2: marfa cota redusa, 1 buc x 1000.00 lei, 11%
- Discount de document: 10% ->
VANZARI.DISCOUNT = 200.00
Ce face programul:
Max(proc_tvav)= 1.21 -> pseudo-linia de discount primeste 21% (oproceduri_facturare.prg:1387/ofacturare_comun.vc2:4495)valdiscounttva = Round(200 * 0.21, 2) = 42.00(ofacturare_comun.prg:1893)
Ce iese in XML (verificat ca valid — scenA):
AllowanceCharge: Amount 200.00, TaxCategory S, Percent 21
TaxSubtotal S/21: TaxableAmount 800.00 TaxAmount 168.00
TaxSubtotal S/11: TaxableAmount 1000.00 TaxAmount 110.00
TaxAmount total 278.00 ; LineExtension 2000.00 ; TaxExclusive 1800.00 ; TaxInclusive 2078.00
Ce ar fi trebuit (discount repartizat proportional cu baza: 100 lei pe fiecare cota):
AllowanceCharge #1: 100.00, S, 21 AllowanceCharge #2: 100.00, S, 11
TaxSubtotal S/21: 900.00 / 189.00
TaxSubtotal S/11: 900.00 / 99.00
TaxAmount total 288.00 ; TaxInclusive 2088.00
Diferenta: 10.00 lei TVA colectat in minus, adica 5% din TVA-ul facturii — si creste cu ecartul
dintre cote si cu marimea discountului. Sensul e mereu acelasi: cota maxima absoarbe tot
discountul, deci TVA-ul declarat e prea mic. Riscul e al emitentului (TVA colectat
subdeclarat), nu al ANAF-ului care respinge documentul. Aceeasi valoare gresita ajunge si in nota
contabila si in jurnalul de TVA, pentru ca valdiscounttva e acelasi camp.
Caz-limita, tot valid dupa validator dar clar absurd (scenB): factura cu 100 lei la 21% si
5000 lei scutit, discount de document 10% (510 lei). Tot discountul intra pe cota 21%, unde baza e
100 lei -> TaxSubtotal S/21 iese cu TaxableAmount = -410.00 si TaxAmount = -86.10. Factura
declara TVA negativ pe o livrare normala. Nu e nevoie de cote diferite de zero pe ambele parti:
ajunge o factura in care liniile de la cota maxima sunt o mica parte din total.
4.5. Gol de proiectare, distinct de defect
Explicatia ("reason") nu exista ca notiune in program. In XML AllowanceChargeReason e literalul
"Discount" si ReasonCode e "95", ambele hardcodate (xmlefactura.prg:774-776). Textul construit
in VFP — "Discount 10.00 % Factura" (ofacturare_comun.prg:1370) — nu ajunge in XML. Nu exista
nicio coloana pe VANZARI pentru motivul discountului si niciun control in formular. Deci raspunsul
la a doua jumatate a intrebarii lui Marius: nu, discountul de document nu are azi explicatie —
are o constanta.
5. Recomandare pentru formularul unificat (#13)
Sa NU ceara utilizatorului cota. Sa sparga discountul automat, proportional cu baza fiecarei cote,
si sa expuna doar un camp optional de motiv. Argumentul e ca discountul de document nu are o cota
proprie de ales: prin definitie e un procent aplicat bazei intregii facturi (lnTotalBaza = Sum(valdiminuatftva) peste toate liniile, ofacturare_comun.prg:1888), deci natura lui de TVA e
determinata de liniile pe care le reduce, nu de o optiune. A pune operatorul sa aleaga o cota inseamna
a-i cere sa ia o decizie fiscala pe care datele o dau deja, si a deschide o a doua cale de a gresi —
azi Max() greseste consecvent, un camp liber ar greseste imprevizibil. Repartizarea proportionala e
si singura care satisface modelul EN16931, unde TaxableAmount-ul fiecarei categorii se calculeaza ca
net de linii al categoriei minus alocarile de document ale aceleiasi categorii. Costul de
implementare e mic, dar atinge doua locuri, nu unul (sectiunea 1.5): prelucreaza_facturacrs
(ofacturare_comun.prg:1886-1897) trebuie sa insereze cate un rand-sentinela per cota in loc de
unul singur, cu tnDiscount repartizat pe baza fiecarei cote (ultima cota preia diferenta de
rotunjire, ca suma sa fie exact VANZARI.DISCOUNT), si recalculeaza_totaluri_vanzari
(ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092) trebuie sa inlocuiasca MAX(...) cu suma
repartizarii, altfel VANZARI.TOTAL_TVA ramane pe regula veche si diverge de XML; eFactura nu are
nevoie de nicio modificare structurala —
xmlefactura.prg:758-792 grupeaza deja GROUP BY proc_tva si emite cate un AllowanceCharge per
cota, iar C_TVA_FACTURA include deja pseudo-liniile in TaxSubtotal. Doua consecinte de decis
explicit cu Marius: factura tiparita va arata N randuri "Discount X % Factura" in loc de unul (pe
cote mixte), si nota contabila primeste TVA-ul discountului spart pe cote. Pentru "explicatie",
recomand un singur camp text optional pe document, folosit ca AllowanceChargeReason in locul
constantei "Discount" (ReasonCode ramane 95) — util si pe hartie, si e singura bucata care chiar
cere UI nou.
Precizare, dupa sectiunea 3.2: "eFactura nu are nevoie de modificare" e adevarat doar daca
fiecare pseudo-linie noua, per cota, primeste si id_jtva_coloana al grupului pe care il reduce,
nu doar proc_tva al lui. Cota singura nu ajunge — cheia de grupare din xmlefactura.prg:246 are
cinci campuri, iar expltva se completeaza tot prin id_jtva_coloana (sectiunea 3.2). O
implementare care seteaza doar proc_tva pe randul-sentinela ar lasa grupul orfan exact acolo unde
e azi, pe facturile scutite / cu taxare inversa / intracomunitare. Fisierul ...2885... de la
sectiunea 4.1 e dovada pozitiva ca, atunci cand id_jtva_coloana e setat corect, forma iese unul
singur si net — deci reparatia are deja un precedent real in productie, nu doar o constructie de
test.
Verificat direct vs. dedus
Verificat direct (citit in cod, cu fisier:linie)
- randul-sentinela
ZZZZsi campurile lui —ofacturare_comun.prg:1886-1897 - transformarea in pseudo-linia
"Discount % Factura"—ofacturare_comun.prg:1361-1392 Max(proc_tvav)pe toate cele patru cai de apel —oproceduri_facturare.prg:1387,ofacturare_comun.vc2:4495si:7294,ofacturare_stoc.prg:582- a doua implementare a aceleiasi reguli, in PL/SQL, si campurile pe care le scrie —
ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16024(procedura),:16081-16092(MAX(...)),:16049-16062(TOTAL_TVA/TOTAL_CU_TVAderivate din el),:16214-16227(update vanzari) - euristica de detectie in eFactura —
xmlefactura.prg:230 - generarea
AllowanceChargede document, cuGROUP BY proc_tva—xmlefactura.prg:756-795 - sursa lui
TaxCategory/ID(GetTipTaxa,xmlefactura.prg:1103-1143) si a luiPercent - includerea pseudo-liniei in
C_TVA_FACTURA->TaxSubtotal—xmlefactura.prg:242-248, 822-851 - inchiderea totalurilor
LegalMonetaryTotal—xmlefactura.prg:863-898 - ca generatorul viu e
xmlefactura.prg, nuanaf_efactura—oexport.prg:1586-1594,ofacturare.prg:2107-2126 - ca ramura "pret cu TVA" (unde pseudo-linia iese cu
valftva = 0) nu poate ajunge in eFactura —ofacturare.prg:1112, 1856-1861+ filtrultip_doc_394dinxmlefactura.prg:276-279 - semantica
LEFT JOIN+IIF/INLISTpeste.NULL.in cheia de grupare (sectiunea 3.1/3.2) —xmlefactura.prg:226-248 - de ce grupul orfan nu poate fi completat prin
completeaza_explicatie_tva— filtrul de scan vs. filtrul de update (ofacturare_comun.prg:2094vs.:2108, 2129, sectiunea 3.2) - sursa categoriei
Za grupului orfan, ramura cu ramura inGetTipTaxa—xmlefactura.prg:1116-1140(sectiunea 3.2) - incarcarea
cJTVAVanzariTempdoar cuid_jtva_coloana > 0, motivul pentru care randul 0 nu se gaseste —updateserver.prg:578(sectiunea 3.2)
Verificat prin rulare
- 4 XML-uri reale de productie cu
AllowanceChargede document (sectiunea 4.1) - 3 facturi cu discount in baza de dev, 34 cu cote mixte (
SELECT, sectiunea 4.2) - 5 validari DUKIntegrator offline pe cote mixte (sectiunea 4.3) — au infirmat doua dintre ipotezele initiale
- semantica
IIF()peste.NULL.masurata cu o sonda headless (probe_null.prg, sectiunea 3.1) — intoarce ramura falsa, nu.NULL. - 3 validari DUKIntegrator offline pe scenariul grupului orfan (
scutit_bug,scutit_corect, pluscontrol_stricatca martor negativ, sectiunea 3.3) — grupul orfan nu e respins - identitatea SHA256 a XML-ului EUR de productie fata de fisierul chiar trimis la ANAF, si citirea
celor doua zip-uri (
TRIMISE/ERORI) care arata ca respingerea gasita e a unei incarcari anterioare, pentru alte reguli (sectiunea 4.1)
Dedus, NEverificat prin rulare
- calculul numeric din scenariul 4.4 (aritmetica pe formulele citite, nu o factura rulata efectiv prin program)
- riscul
agettipcota(1)fara garda pe_Tally(xmlefactura.prg:782-786) — n-am reusit sa-l provoc si nu-l afirm ca defect - ce se intampla in nota contabila / jurnalul de TVA cu
valdiscounttva— am dedus din faptul ca e acelasi camp, n-am urmarit consumatorii - scenariul "factura scutita + discount de document" end-to-end prin program (sectiunea 3.2):
gruparea separata e stabilita din cod + semantica
IIF/NULL masurata, nu dintr-o factura reala rulata prin program — in baza de dev nu exista intersectia (sectiunea 4.2) - ordinea randurilor in
C_TVA_FACTURAdupaGROUP BY(deci dacaagettipcota(1)alegeEsauZin cazul grupului orfan) — nu e garantata de VFP si nu a fost fixata experimental
Neacoperit — si stiut ca neacoperit
- daca
VANZARI.TOTAL_TVA(Oracle, sectiunea 1.5) si TVA-ul din XML sunt confruntate undeva si care primeaza — am constatat doar ca ambele exista si folosesc aceeasi regula - daca un discount de document negativ e posibil din UI (ar inversa
MAX-ul din Oracle, 1.5) - calea in valuta (
crsfacturafinalaval/prelucreaza_factura_valuta,ofacturare_comun.prg:1540+) — am presupus simetrie cu cea in lei pe baza campurilorv*paralele de la:1894-1895, n-am parcurs-o linie cu linie - daca
Max(proc_tvav)e cota corecta si pentru proforme in vreun sens special - comportamentul validatorului ANAF curent (cel local e din 2022, atat pentru cote mixte cat si pentru grupul orfan)
Intrebari ramase pentru Marius
- Repartizarea proportionala se aplica retroactiv la relistare? O factura veche relistata sau retrimisa in eFactura ar genera alt XML decat cel trimis initial. Se accepta, sau noul comportament se leaga de o data / de un flag?
- Pe factura tiparita: e in regula sa apara N randuri "Discount X % Factura" (unul pe cota), sau vrei un singur rand vizibil si spargerea doar in XML / nota contabila?
- Rotunjirea: pe cine cade diferenta de rotunjire cand discountul nu se imparte exact (ex. 100 lei pe trei cote)? Propunerea mea: pe cota cu baza cea mai mare.
- Discountul care depaseste baza unei cote (cazul din 4.4, factura majoritar scutita): dupa repartizarea proportionala problema dispare de la sine — confirmi ca nu mai e nevoie de nicio garda separata?
- Campul de motiv: il vrei per document (un singur text), sau e suficient sa ramana constanta
"Discount"si sa nu adaugam UI? discount_evidentiat: mai e folosit in productie? Schimba semnificativ ce se tipareste (sectiunea 1.4) si e a doua sursa de pseudo-linii "Discount" in XML.- Cele doua implementari ale regulii (VFP + Oracle, sectiunea 1.5): se schimba amandoua in
aceeasi livrare, sau Oracle ramane pe regula veche o vreme? Daca raman desincronizate,
VANZARI.TOTAL_TVAsi TVA-ul din eFactura vor diferi pe facturile cu cote mixte si discount. - Discount de document negativ (majorare): e posibil din UI? Daca da,
MAX-ul din Oracle alege cota cea mai mica, iarMax(proc_tvav)din VFP tot pe cea mai mare — adica cele doua implementari ar diverge deja azi, inainte de orice modificare.