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

217 lines
14 KiB
Markdown

# Discount pe linie (DISCOUNT_UNITAR) in afara formularului de facturare — rapoarte .frx, eFactura, SAF-T
Cercetare read-only pentru S4c (plan_13). Nu s-a modificat niciun fisier, nu s-a rulat git_sync.
## Verdict (5-10 randuri)
Niciun raport tiparit de factura (`factura.fr2`, `facturatip*.fr2`, `factura_val*.fr2`,
`invoice*.fr2`, `proforma*.fr2`) nu afiseaza discountul pe linie ca coloana separata — nici ca
valoare, nici ca procent. Rapoartele tiparesc `pretftva` (pret unitar) si `valftva` (valoare linie)
care sunt **deja nete de discount** (calculate ca `pretftva-discountftva` / `Sum(valdiminuatftva)`
in cursorul intermediar `crsfacttemp`, construit de `prelucreaza_factura`/`creeaza_crsfacttemp` din
`ofacturare_comun.prg`). eFactura (`xmlefactura.prg`) foloseste **exact acelasi cursor** —
comentariul din cod chiar spune asta explicit — deci `LineExtensionAmount`/`PriceAmount` trimise la
ANAF sunt aceleasi valori nete, cu optiunea (dezactivata implicit, din flag-uri globale) de a
adauga si un `cac:AllowanceCharge` informativ pe linie. SAF-T (D406): **nu exista niciun generator
D406/SAF-T in ROAFACTURARE** — exista doar tabele de nomenclator cu prefix `saft_` (coduri TVA/plata)
folosite pe partea de achizitii, fara legatura cu `DISCOUNT_UNITAR`.
**Raspuns la intrebarea care conteaza**: daca discountul pe linie devine editabil direct in grid,
**nu se strica nimic in codul rapoartelor/eFacturii** — ele citesc oricum campurile finale
(`valdiminuatftva`/`discountftva`/`discount_unitar`) din cursor, indiferent cum au fost populate
(dialog separat vs. editare in grid). **Riscul real e in alta parte**: exista deja azi o cale prin
care discountul e editabil direct in grid (`vdiscountftva`, pe factura in valuta) care **nu
declanseaza recalcularea** lui `valdiminuatftva`/`valdiminuatctva` — vezi `docs\cercetare\discount_verificare2.md`
punctul 3, ultimul paragraf. Daca S4c extinde editarea in grid la toate coloanele de discount fara
sa cablaje si recalculul, rapoartele si eFactura vor tipari/trimite **valori vechi** (stale), pentru
ca ambele citesc `valdiminuatftva`, nu `discount_unitar` direct.
## 1. Rapoartele .frx/.fr2 tiparite
**Cautare**: `Grep 'DISCOUNT'` si apoi `Grep 'disc'` (case-insensitive) in toate `.fr2` din
`COMUN\Rapoarte` cu glob `*factur*.fr2`, `*proforma*.fr2`, `*invoice*.fr2` — **zero potriviri** in
toate cele (factura.fr2, facturatip.fr2, facturatip_a5.fr2, facturatip_cuchit.fr2, factura_a5.fr2,
factura_chit.fr2, factura_val.fr2, factura_val_a5.fr2, invoice.fr2, invoice_a5.fr2, proforma.fr2,
proforma_orig.fr2, proforma_val.fr2, proforma_val_orig.fr2). Singurele 2 fisiere `.fr2` din toata
`COMUN\Rapoarte` care contin "DISCOUNT" sunt `rap_nir_materiale.fr2` si `rap_nir_marfuri.fr2` —
rapoarte de NIR (receptie), nu facturi emise.
**Ce se tipareste per linie** (confirmat in `factura.fr2`):
```
1002: <expr><![CDATA[formateaza(cantitate,30,gnPCant)]]>
1014: <expr><![CDATA[formateaza(pretftva,14,gnPPretV)]]>
1026: <expr><![CDATA[formateaza(valftva,14,gnPc)]]>
1062: <expr><![CDATA[PADL(ALLTRIM(STR((proc_tva-1)*100)),2,[ ])+'%']]> -- procent TVA, nu discount
```
Deci: cantitate, pret unitar, valoare linie, procent TVA. Nici discount valoric, nici procent
discount.
**De unde vin `pretftva`/`valftva` — si de ce sunt deja nete**: cursorul de tiparire
(`crsfacttemp`) e construit de `Procedure prelucreaza_factura` (`COMUN\programe\ofacturare_comun.prg:1055-1059`),
apelata din `COMUN\programe\ofacturare.prg:1887`:
```
prelucreaza_factura([crsfactura], [crsfacturaset], [crsfacturafinala], poDate.discount_evidentiat, poDate.in_valuta, lnPretListAviz, poDate.nListareDetaliata)
```
In interior, `Do Case` pe `tnDiscountEvidentiat`/`lnPretListAviz` (`ofacturare_comun.prg:1156-1248`).
Cazul comun, `tnDiscountEvidentiat = 0` (checkbox-ul "Se pune in evidenta discount-ul..." NEBIFAT —
comportamentul implicit):
```
1182: Sum(cantitate) As cantitate,pretftva-discountftva As pretftva,;
...
1186: Sum(valdiminuatftva) As valftva,Sum(valdiminuattva) As valtva,;
```
adica pretul tiparit = pret brut minus discountul pe unitate, iar valoarea tiparita = suma
`valdiminuatftva` (deja calculata la adaugarea articolului ca `pretftva - discount_unitar`, cf.
`ofacturare.vc2:13126`, deja documentat in `discount_pe_articol.md` sectiunea 3). Simetric pentru
varianta cu TVA la `:1226-1230`.
**Cazul `tnDiscountEvidentiat = 1`** (checkbox bifat — discountul "se pune in evidenta"):
```
1165: pretftva,Nvl(codbare,... -- pretftva RAMANE brut, nu se scade discountftva
1166-1167: Sum(valftva) As valftvai,... Sum(valftva) As valftva,Sum(valtva) As valtva,
1168: discountftva,Sum(valdiscountftva) As valdiscountftva,Sum(valdiscounttva) As valdiscounttva,
```
Aici `pretftva`/`valftva` tiparite raman **brute** (neta de discount), iar `discountftva`/
`valdiscountftva`/`valdiscounttva` sunt calculate in cursor **dar nu sunt tiparite de niciun .frx**
(confirmat, zero hit pe "disc" in .fr2). Practic, cand discountul e "pus in evidenta", pretul si
valoarea linie tiparite pe factura NU reflecta discountul per linie — discountul apare doar ca linie
separata de document (randul-sentinela `denumire = Replicate('Z',20)`, populat din
`Thisform.nbazafdiscount`/`nbazafdiscountval` la `ofacturare.vc2:14534,14591,18532,18602` si
`ofacturare_comun.prg:1891`) — asta e insa discountul **de document** (`VANZARI.DISCOUNT`), nu
`DISCOUNT_UNITAR` pe linie. Nu am gasit nicio dovada ca acest mod (`discount_evidentiat=1`) ar fi
folosit uzual — e o optiune existenta, documentata deja in `discount_pe_articol.md` punctul 3 ca
schimband "doar mecanismul aritmetic", dar aici se vede consecinta suplimentara: **si ce se
tipareste**.
**Fara impartiri la pret zero pe randul de discount**: unde raportul calculeaza procent
(`proc_disc`), sursa e protejata explicit:
```
1184-1185 (ofacturare_comun.prg): Iif(cu_tva=0,Iif(pretftva<>0,Round(discountftva/pretftva*100,2),0),;
Iif(pretctva<>0,Round(discountctva/pretctva*100,2),0))
```
deci nu exista risc de impartire la zero — dar, ca observat mai sus, `proc_disc` nu e tiparit
nicaieri in .frx-urile de factura verificate (e calculat doar pentru consum intern/eFactura).
## 2. eFactura (ANAF/UBL) — `xmlefactura.prg`
**Concluzie**: eFactura foloseste **acelasi cursor de linii ca cel de la tiparire**, explicit
documentat in cod:
```
oexport.prg:1590: * tcCursorLiniiFactura = numele cursorului cu liniile facturii, asa cum arata la listare
```
Lantul: `ofacturare.prg:2126` -> `goExport.export2xml_efactura(poDate, m.lcCursoreFactura, ...)` cu
`lcCursoreFactura` = `lcCursorFacturaTemp` (= `'crsFacturaFinala'`, `ofacturare.prg:1892`, sau
`'crsfacturafinalaval'` pentru valuta) -> `xmlefactura.prg:231`:
```
Select a.*, ... From (m.tcCursorLiniiFactura) a Left Join cJTVAVanzariTemp b ... Into Cursor C_IES_FORM Readwrite
```
Deci `C_IES_FORM` (sursa liniilor XML) e construit direct din cursorul de tiparire — aceleasi
`pretftva`/`pretftvai`/`valftva`/`valftvai`/`proc_disc` descrise la punctul 1, cu aceeasi dependenta
de `poDate.discount_evidentiat`.
**Ce se trimite pe linie**:
- `cbc:LineExtensionAmount` (BT-131) = `C_IES_FORM.valftva` — `xmlefactura.prg:935-937`.
- `cbc:PriceAmount` = `C_IES_FORM.pretftva` — `xmlefactura.prg:1043-1046`.
- Optional (doar daca flag global `gnEFACTURA_XML_DISC_PLISTA_LINIE = 1` SI `pretftvai > pretftva`):
un `cac:AllowanceCharge` la nivel de linie cu `Amount = valftvai-valftva`,
`BaseAmount = valftvai`, `MultiplierFactorNumeric = proc_disc` — `xmlefactura.prg:952-971`.
- Optional (flag separat `gnEFACTURA_XML_DISC_PLISTA_ART = 1`): un `cac:AllowanceCharge` la nivel de
`cac:Price` cu discountul unitar (`ABS(pretftvai-pretftva)`) — `xmlefactura.prg:1054-1067`.
Ambele flag-uri sunt verificate cu `TYPE(...) = 'N' AND ... = 1` — daca variabila globala nu exista
sau e 0 (implicit), **nu se trimite niciun `AllowanceCharge` pe linie**; discountul e vizibil pentru
ANAF doar implicit, prin faptul ca `PriceAmount * InvoicedQuantity = LineExtensionAmount` (adica
pretul e deja net). Nu am gasit unde sunt setate global aceste doua variabile (`gnEFACTURA_XML_DISC_PLISTA_LINIE`/`_ART`) —
posibil optiuni de firma necautate in acest fisier; nu afecteaza concluzia principala.
**Discount de document, separat**: exista o eticheta euristica in `C_IES_FORM`
(`Iif('DISCOUNT' $ Upper(a.denumire) And a.pretftva < 0, 1, 0) As discount`, `xmlefactura.prg:230`)
care detecteaza randuri-pseudo-articol de discount/taxa (denumire contine "DISCOUNT", pret negativ)
si le exclude din liniile normale (`Scan For discount = 0`, `:901`), agregandu-le separat intr-un
`cac:AllowanceCharge` la nivel de document (`:756-793`, motiv "Discount", cod 95). Acesta e
discountul de document, nu `DISCOUNT_UNITAR` pe linie — mecanism diferit de randul-sentinela
`Replicate('Z',20)` folosit la tiparire (punctul 1).
**Nicio validare care ar respinge discount > pret**: n-am gasit nicio verificare explicita in
`xmlefactura.prg` care sa blocheze o linie cu `discount_unitar` mai mare ca pretul (ar rezulta
`pretftva` negativ pe linie individuala; codul are doar o corectie generala pentru
`pretftva < 0` la nivel de linie completa, `xmlefactura.prg:236-239`, care inverseaza semnul
cantitate/pret — nu specifica pentru discount).
## 3. SAF-T (D406)
**Concluzie: nu exista implementare SAF-T/D406 in ROAFACTURARE.** Cautat explicit:
- `Grep 'SAF-T|SAFT|D406|declaratie406|GenereazaSAFT|GenereazaD406'` in tot `COMUN\programe` si
`COMUN` — nicio potrivire pe un generator de fisier D406/SAF-T XML.
- `Glob '**/*saft*'` pe tot proiectul si pe `COMUN` — zero fisiere cu "saft" in nume.
- Singurele aparitii reale (nu fals-pozitive de tip "safe") sunt:
- `oinit_optiuni.prg:523-524`: `gl406 = (TYPE('gnD406') = 'N' and m.gnD406 = 1)` — un flag "firma
are activata SAFT 406", comentat "Firma are activata SAFT 406".
- `oproceduri_comune.prg:889-896,6083,6151`, `ointroduceri.prg:1597`: `saft_taxtable` si
`saft_mecanisme_plati` — tabele de nomenclator (coduri de TVA/mecanism de plata compatibile
SAF-T) folosite pe **partea de achizitii** (limitare deducere TVA la introducerea facturilor de
achizitie), nu pe vanzari/facturare.
- `oproceduri_comune.prg:6066-6067,6137-6138,6195`: comentarii care mentioneaza "Taxa SAFT tip"
ca denumire pentru codurile de TVA, tot pe achizitii.
- Niciuna din aceste aparitii nu are legatura cu `DISCOUNT`/`DISCOUNT_UNITAR` — cautarea `DISCOUNT`
in fisierele care contin "saft" (`oproceduri_comune.prg`, `ointroduceri.prg`, `pmenu.prg`,
`oinit_optiuni.prg`) nu a gasit nicio intersectie intre cele doua seturi de rezultate.
**Interpretare**: `gl406` pare sa activeze doar validari/coduri suplimentare pentru conformitate
SAF-T pe partea de achizitii (poate pentru ca alt produs din suita, ROACONT, genereaza efectiv
D406 din datele contabile) — ROAFACTURARE nu produce singur un fisier D406.
## 4. Alte consumatori care ar presupune discount uniform pe document
Nu am gasit vreun raport/export care sa presupuna un discount unic pe tot documentul in sensul de
"aceeasi valoare/procent pe toate liniile" — mecanismul de discount de document
(`VANZARI.DISCOUNT`, randul `Replicate('Z',20)`) e tratat ca **valoare agregata separata**, adaugata
ca linie proprie (in tiparire) sau ca `AllowanceCharge` de document (in eFactura), nu redistribuita
implicit pe liniile existente — cu o exceptie: `xmlefactura.prg` are logica de **distribuire**
explicita a discountului/taxelor de document pe articole cand bifa `chkDistribuieDiscount`/
`llDistribuieDiscountTaxe` e activa (`xmlefactura.prg:12189-12320`, `:12534-12790`) — asta insa
opereaza pe discountul de document, nu presupune ca discountul de linie (`DISCOUNT_UNITAR`) e
uniform; dimpotriva, distribuie proportional cu valoarea fiecarei linii, deci suporta discount
diferit per linie din start.
## Ce ar strica S4c, concret
**Nu se strica codul rapoartelor sau al eFacturii** — ambele citesc valori finale din cursor
(`valdiminuatftva`, `discountftva`, `pretftva` deja net), indiferent de sursa UI a discountului.
**Se poate strica invariantul pe care se bazeaza**, daca editarea in grid nu recalculeaza:
- **Dovada ca gaura exista deja azi**: pe factura in valuta, coloana `vdiscountftva` e editabila
direct in grid (`ofacturare.vc2:12340-12345`, fara `ReadOnly`) dar **fara niciun handler
`Valid`/`InteractiveChange`** care sa recalculeze `vvaldiminuatftva`/totalurile — confirmat cautat
explicit in `docs\cercetare\discount_verificare2.md` punctul 3, ultimul paragraf ("editarea directa
in grid NU declanseaza recalcularea automata a `vvaldiminuatftva`/totaluri, doar modifica valoarea
bruta in `crsfactura`").
- **De ce conteaza pentru rapoarte/eFactura**: `valdiminuatftva`/`valdiminuatctva` (nu
`discount_unitar` brut) sunt campurile citite de `prelucreaza_factura`/`creeaza_crsfacttemp`
pentru `valftva` tiparit si trimis la ANAF (punctele 1 si 2 de mai sus). Daca operatorul modifica
discountul direct in grid si acel camp nu se recalculeaza, factura tiparita si XML-ul ANAF vor
arata **valoarea veche** (dinainte de editare) — o discrepanta reala intre ce vede operatorul in
grid si ce se tipareste/trimite.
- **Concluzie pentru plan**: daca S4c extinde editarea de discount la toate coloanele din grid
(inclusiv `discountctva`, azi read-only pe lei), trebuie cablat un recalcul echivalent cu
`frm_articol_factura.do_calculeaza_discount` (`ofacturare.vc2:1874-1976`) pe evenimentul de editare
din grid — altfel riscul de mai sus (deja prezent azi doar pe valuta) se extinde la toate
facturile in lei.
## Ramas de verificat
- Valorile implicite ale flag-urilor globale `gnEFACTURA_XML_DISC_PLISTA_LINIE` si
`gnEFACTURA_XML_DISC_PLISTA_ART` (unde sunt setate ca optiune de firma) — nu am cautat sursa lor,
doar am confirmat ca cu `TYPE(...) <> 'N'` (nedefinite) comportamentul e "fara AllowanceCharge pe
linie".
- Nu am verificat pe date reale (Oracle/XML generat) un caz cu `discount_evidentiat=1` si
`DISCOUNT_UNITAR` populat simultan, ca sa confirm empiric (nu doar din citirea codului) ca factura
tiparita arata pretul brut.
- Nu am urmarit `crsfacturafinalaval` (varianta valuta a cursorului final) linie cu linie — am
presupus ca urmeaza acelasi patron ca `crsfacturafinala`/`crsfacttemp` (cursorul in lei), pe baza
simetriei campurilor `vpretftva`/`vvalftva`/`vdiscountftva` gasite deja documentate in
`discount_verificare2.md` si `discount_pe_articol.md`; n-am reverificat separat sursa SQL pentru
varianta valuta.
- Nu am identificat un al doilea produs (ROACONT?) care sa genereze efectiv D406 din datele scrise
de ROAFACTURARE — presupunerea din sectiunea 3 e speculativa, nu confirmata cu cod.