Files
roafacturare/docs/cercetare/discount_document_cota_tva.md
2026-09-09 22:19:22 +03:00

44 KiB
Raw Blame History

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): ramura ofacturare_comun.prg:1177-1203. Liniile de articol primesc 0 As discountftva (:1188), deci nu genereaza pseudo-linii; doar randul ZZZZ (reinserat separat prin Where denumire = Replicate('Z',20), :1193-1203) pastreaza discountftva -> o singura pseudo-linie de discount, cea de document.
  • discount_evidentiat = 1: ramura :1157-1173, un singur Insert fara WHERE, discountftva pastrat per articol (coloana 20 din group 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 din C_TVA_FACTURA.tip, adica din This.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) * 100 al 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 un Max(), 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)

  • currencyID hardcodat "RON" la :778, in timp ce tot restul documentului foloseste mmoneda (:820, 827, 873, 876, ..., definit la :266-267). Pe o factura in valuta cu discount de document, AllowanceCharge/cbc:Amount iese cu currencyID="RON" iar LegalMonetaryTotal/cbc:AllowanceTotalAmount cu currencyID="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 agettipcota fara garda pe _Tally (:782-786): daca gruparea nu gaseste randul (egalitate pe numere in virgula mobila: campul C_TVA_FACTURA.proc_tva e stocat rotunjit, comparatia se face cu (lnProcTva-1)*100 calculat 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 TaxSubtotal orfan cu baza negativa si categorie ambigua (E sau Z). 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:

  1. Atribuirea intregului discount cotei maxime trece validarea — deci nu e o eroare pe care ANAF sa o prinda. E o eroare tacuta.
  2. Ipoteza mea ca o TaxableAmount negativa ar fi respinsa e infirmata de validatorul local.
  3. Ipoteza ca neconcordanta de moneda (currencyID="RON" pe factura in EUR) ar fi respinsa e tot infirmata de validatorul local.
  4. 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 ZZZZ si 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:4495 si :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_TVA derivate din el), :16214-16227 (update vanzari)
  • euristica de detectie in eFactura — xmlefactura.prg:230
  • generarea AllowanceCharge de document, cu GROUP BY proc_tva — xmlefactura.prg:756-795
  • sursa lui TaxCategory/ID (GetTipTaxa, xmlefactura.prg:1103-1143) si a lui Percent
  • 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, nu anaf_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 + filtrul tip_doc_394 din xmlefactura.prg:276-279
  • semantica LEFT JOIN + IIF/INLIST peste .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:2094 vs. :2108, 2129, sectiunea 3.2)
  • sursa categoriei Z a grupului orfan, ramura cu ramura in GetTipTaxa — xmlefactura.prg:1116-1140 (sectiunea 3.2)
  • incarcarea cJTVAVanzariTemp doar cu id_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 AllowanceCharge de 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, plus control_stricat ca 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_FACTURA dupa GROUP BY (deci daca agettipcota(1) alege E sau Z in 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 campurilor v* 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

  1. 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?
  2. 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?
  3. 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.
  4. 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?
  5. Campul de motiv: il vrei per document (un singur text), sau e suficient sa ramana constanta "Discount" si sa nu adaugam UI?
  6. 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.
  7. 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_TVA si TVA-ul din eFactura vor diferi pe facturile cu cote mixte si discount.
  8. Discount de document negativ (majorare): e posibil din UI? Daca da, MAX-ul din Oracle alege cota cea mai mica, iar Max(proc_tvav) din VFP tot pe cea mai mare — adica cele doua implementari ar diverge deja azi, inainte de orice modificare.