34 KiB
S4c — Discountul pe linie, mutat din dialog in grid — proiectare implementabila
Cercetare + proiectare READ-ONLY pentru docs\plan_13_unificare_formular_facturare.md, #### S4c
(:2156-2190). Nu s-a modificat niciun fisier, nu s-a rulat git_sync.ps1/txt2vcx.ps1, nu s-a dat
commit, nu s-a scris nimic pe Oracle (numai SELECT, niciunul rulat de fapt — toata cercetarea a
fost pe cod VFP). Nu s-a atins COMUN\clase\ofacturare_comun.vc2 si COMUN\programe\ofacturare_editare.prg.
Surse de adevar deja stabilite, citate ca atare (nu se re-verifica aici):
docs\cercetare\discount_in_rapoarte_si_efactura.md (verdictul principal — niciun raport/eFactura nu
tipareste discountul, dar amandoua citesc valdiminuatftva/discountftva din cursorul de tiparire),
docs\cercetare\discount_verificare2.md (structura reala a coloanelor din grd_factura si a
VANZARI_DETALII). docs\cercetare\discount_pe_articol.md NU e citat ca sursa — task-ul mi-a
semnalat ca are doua afirmatii gresite, corectate deja in discount_verificare2.md sectiunea finala
("Ce era gresit in afirmatiile de mai sus").
Verdict (10 randuri)
S4c e implementabila cu cod nou moderat, nu cu redesign. Contractul de calcul exista deja, complet si
reciproc, in frm_articol_factura.do_calculeaza_discount (ofacturare.vc2:1874-1976) si
frm_articol_factura.do_calculeaza_totaluri (:2068-2179, delegand la functia partajata
calculeaza_totaluri() din oproceduri_facturare.prg:2258-2381) — aceasta din urma e deja scrisa
generic, pe orice obiect cu proprietatile potrivite, deci se poate rula direct pe un rand din
crsfactura (via Scatter/Gather Name), fara sa mai treaca prin dialog. Tiparul de eveniment
pentru coloane editabile de grid exista deja in acelasi fisier, pe alt formular din aceeasi
familie de clase (frm_avizare_lucrare.grd_articole.cCantitate/cPret.Text1.LostFocus,
:6549-6562) — LostFocus care cheama Thisform.do_calculeaza_totaluri(), nu InteractiveChange.
Capcana reala e alta decat pare din titlul poveste: exista doua cai de scriere independente
catre destinatii diferite, si doar una e azi corect legata. do_scrie_articole trimite spre Oracle
(pack_facturare.adauga_articol_factura, ofacturare.vc2:14081-14083) discountul citit direct
din discountftva/discountctva/vdiscountftva/vdiscountctva — deci VANZARI_DETALII.DISCOUNT_UNITAR
e mereu corect, indiferent de bug, pentru ca aceste campuri sunt chiar campurile editate in grid.
Riscul e strict local, in sesiunea VFP curenta: prelucreaza_factura (apelata pentru tiparire/eFactura,
ofacturare.prg:1887) construieste cursorul de tiparire din acelasi crsfactura deja in memorie,
citind valdiminuatftva/valdiminuatctva/vvaldiminuatftva/vvaldiminuatctva — campuri agregate
(pret-discount)*cantitate, care nu se recalculeaza singure cand se editeaza discountul brut. Deci
Oracle poate avea DISCOUNT_UNITAR corect si totusi factura tiparita/eFactura sa arate valoarea veche,
in aceeasi sesiune, pana la reincarcare. Solutia: coloanele noi de discount trebuie sa scrie, pe
LostFocus, atat campurile brute cat si campurile agregate — vezi sectiunea 4.
1. Lantul complet al campurilor de discount, cu fisier:linie
1.1 Structura cursorului local crsfactura
Definit in creeaza_facturacrs (COMUN\programe\ofacturare_comun.prg:1779-1782):
1779 valdiscountctva N(20,max(gnPc,4)),valdiminuatftva N(20,max(gnPc,4)),valdiminuattva N(20,max(gnPc,4)),valdiminuatctva N(20,max(gnPc,4)),proc_Tvav N(20,4),cu_tva N(1), lot C(20) null, serie c(100) Null,;
1782 vvaldiscountctva N(20,max(gnPVal,4)),vvaldiminuatftva N(20,max(gnPVal,4)),vvaldiminuattva N(20,max(gnPVal,4)),vvaldiminuatctva N(20,max(gnPVal,4)),id_set_fact N(20) Null,explicatie M Null,;
(discountftva, discountctva, vdiscountftva, vdiscountctva, valdiscountftva, valdiscountctva,
vvaldiscountftva, vvaldiscountctva sunt definite pe liniile adiacente, aceeasi procedura —
nu recitate individual, tiparul N(20,...) e identic).
Nomenclatura confirmata pe cod (nu doar dedusa din nume) — opt campuri distincte, trei niveluri:
| Camp | Nivel | Moneda | Cu/fara TVA | Sens |
|---|---|---|---|---|
discountftva |
pe unitate | lei | fara TVA | discount unitar, sursa in lei |
discountctva |
pe unitate | lei | cu TVA | discount unitar, derivat |
vdiscountftva |
pe unitate | valuta | fara TVA | discount unitar, sursa in valuta |
vdiscountctva |
pe unitate | valuta | cu TVA | discount unitar, derivat |
valdiscountftva/valdiscountctva |
pe linie (x cantitate) | lei | ambele | discount agregat lei |
vvaldiscountftva/vvaldiscountctva |
pe linie (x cantitate) | valuta | ambele | discount agregat valuta |
valdiminuatftva/valdiminuatctva |
pe linie (x cantitate) | lei | ambele | valoare neta (pret-discount)*cant — cea tiparita/eFactura |
vvaldiminuatftva/vvaldiminuatctva |
pe linie (x cantitate) | valuta | ambele | valoare neta in valuta |
1.2 De la tastare la crsfactura — calea de azi (prin dialog)
- Operator tasteaza in dialogul
frm_articol_factura(ofacturare.vc2:1108-2659), pe unul din 3 perechi de campuri:Clb_procent_discount.Text_simplu1(InteractiveChange,:2646),Clb_discount_unitar.tx_suma_nat/tx_suma_val(Valid,:2614/:2620),Clb_discountctva.tx_suma_nat/tx_suma_val(Valid,:2602/:2608). - Toate cheama
Thisform.do_calculeaza_discount(valoare, tip)—frm_articol_factura(:1874-1976, contract detaliat in sectiunea 2) — scrie inpoArticol:discount_unitar,discount_unitar_val,discount_unitar_ctva,discount_unitar_ctva_val. do_calculeaza_discountcheama la finalThisform.do_calculeaza_totaluri()(:1974,frm_articol_factura,:2068-2179) care delega lacalculeaza_totaluri(poArticol)(oproceduri_facturare.prg:2258-2381, functie globala) — aceasta scrie inpoArticol:valdiminuatftva,valdiminuattva,valdiminuatctva,vvaldiminuatftva,vvaldiminuattva,vvaldiminuatctva, plusvaldiscountftva/ctva,vvaldiscountftva/ctva,valftva/ctva/tvaetc.- La inchiderea dialogului,
do_adauga_articol(ofacturare.vc2:12813-13167) scrie tot obiectul deja calculat incrsfactura, in doua pasi:Gather Name poArticol Fields Like ... valdiminuatftva, valdiminuattva, valdiminuatctva, vvaldiminuatftva, vvaldiminuattva, vvaldiminuatctva ...(:12945-12950) — campurile agregate, deja calculate decalculeaza_totaluri, nu recalculate aici.Replace ... discountftva With poArticol.discount_unitar, discountctva With poArticol.discount_unitar_ctva, vdiscountftva With Nvl(poArticol.discount_unitar_val,0), vdiscountctva With Nvl(poArticol.discount_unitar_ctva_val,0)(:12952-12957) — campurile pe unitate, separat, pentru ca nu sunt in listaGather(doar varianteleval*agregate sunt).
do_modifica(:13746-13914, editarea unei linii existente) urmeaza acelasi tipar —Gather/Replacecu aceleasi campuri (:13837-13881), dupa ce redeschide dialogul pe rand.- Cale suplimentara, deja existenta, cu bug documentat: pe factura in valuta, coloana
cVdiscountftva(ControlSource="vdiscountftva",ofacturare.vc2:12340-12345) e editabila direct in grid, fara niciun handler — scrievdiscountftvadirect incrsfacturaprin binding-ul standard de grid, dar nu recalculeazavvaldiminuatftva/vvaldiminuatctva(confirmat cautat explicit,discount_verificare2.mdpunctul 3).
1.3 De la crsfactura la Oracle (VANZARI_DETALII.DISCOUNT_UNITAR)
do_scrie_articole (ofacturare.vc2:13916-...), la finalizarea facturii, trimite spre
pack_facturare.adauga_articol_factura un singur parametru de discount, ales direct din campurile
pe unitate ale randului curent din crsfactura (:14081-14083):
14081 IIF(poArt.cu_tva = 0,;
14082 Iif(poArt.tip_valuta = 0,Alltrim(Str(poArt.discountftva,18,gnPPretV)),Alltrim(Str(poArt.vdiscountftva,18,gnPVal))), ;
14083 IIF(poArt.tip_valuta = 0,Alltrim(Str(poArt.discountctva,18,gnPPretV)),Alltrim(Str(poArt.vdiscountctva,18,gnPVal))))
Nu foloseste valdiminuatftva. Deci Oracle primeste intotdeauna valoarea curenta din
discountftva/discountctva/vdiscountftva/vdiscountctva — cele patru campuri pe care coloanele
noi de grid le-ar edita direct. VANZARI_DETALII.DISCOUNT_UNITAR NUMBER(22,6) e singura coloana
Oracle de discount (confirmat discount_verificare2.md punctul 4) — nu exista coloana de procent pe
Oracle.
1.4 Inapoi, pentru tiparire/eFactura — crsfacttemp
prelucreaza_factura (ofacturare_comun.prg:1055-1059, apelata din ofacturare.prg:1887) NU
reincarca din Oracle — primeste ca parametru acelasi crsfactura deja in memorie (cel scris la
pasii 1.2/1.3), si construieste cursorul de tiparire prin agregare SQL locala:
1182 Sum(cantitate) As cantitate,pretftva-discountftva As pretftva,;
1186 Sum(valdiminuatftva) As valftva,Sum(valdiminuattva) As valtva,;
(ofacturare_comun.prg:1182-1186, cazul comun discount_evidentiat=0). eFactura foloseste acelasi
cursor de iesire (xmlefactura.prg:231, LineExtensionAmount = valftva, PriceAmount = pretftva,
xmlefactura.prg:935-937, :1043-1046).
Consecinta directa: pretftva-discountftva de la 1182 citeste discountftva (mereu proaspat,
pentru ca e campul editat direct), dar Sum(valdiminuatftva) de la 1186 citeste campul agregat,
care ramane vechi daca nu a fost recalculat explicit dupa editare. Rezultat: randul tiparit poate
avea pretftva corect (net, recalculat corect din discountftva) dar valftva (valoarea liniei)
gresit — o discrepanta pret x cantitate ≠ valoare, vizibila chiar pe hartie, nu doar o valoare
veche uniforma. Asta e mai grav decat "arata vechi" — arata inconsistent.
2. Contractul frm_articol_factura.do_calculeaza_discount (ofacturare.vc2:1874-1976)
Semnatura: Lparameters tnValoare, tnTip — tnTip: 1 = s-a modificat procentul, 2 =
discount in lei (fara TVA daca preturi_cu_tva=0, cu TVA altfel), 3 = discount in valuta.
Ramura principala — If poArticol.preturi_cu_tva = 0 (pretul de referinta e fara TVA, cazul
uzual): sursa de adevar e discount_unitar(_val).
tnTip=1(procent -> valoare): dacatip_valuta=0,discount_unitar = Round(pretftva * procent/100, gnPPretV), apoi procentul se re-normalizeaza din valoarea rotunjita (:1888-1889) — nu se pastreaza procentul brut tastat, ci cel rezultat din rotunjire. Dacatip_valuta=1:discount_unitar_valse calculeaza intai (gnPVal), apoidiscount_unitar = Round(discount_unitar_val * Curs / multiplicator, gnPPretV).tnTip=2(lei -> procent):discount_unitar = tnValoaredirect, procentul se deriva (Round(discount_unitar/pretftva*100, 2)).tnTip=3(valuta -> procent + lei):discount_unitar_val = tnValoare, procentul deriva din valuta, apoidiscount_unitar = Round(discount_unitar_val * Curs / multiplicator, gnPPretV).- Dupa
Do Case, neconditionat: `discount_unitar_ctva = discount_unitar + Round(discount_unitar- (proc_tvav-1), gnPPretV)
(:1913-1914`) — varianta cu TVA e mereu derivata din cea fara TVA in aceasta ramura, niciodata sursa.
- (proc_tvav-1), gnPPretV)
- Daca
tip_valuta=1, simetric:discount_unitar_ctva_valderivat dindiscount_unitar_val.
Ramura alternativa — Else (preturi_cu_tva=1, pretul de referinta e cu TVA): rolurile se
inverseaza complet — discount_unitar_ctva(_val) e sursa (calculata direct din tnValoare/pretctva
in cele 3 cazuri, simetric cu ramura de mai sus), iar discount_unitar(_val) fara TVA e derivat
la final (:1958, Round(discount_unitar_ctva / proc_tvav, gnPPretV)).
Rotunjiri: gnPPretV pentru valorile unitare in lei, gnPVal pentru cele in valuta, 2 fix
pentru procent — niciodata gnPc (precizia de linie/document) in aceasta metoda.
La final, neconditionat (:1972-1974): Thisform.clb_tva_discount.Refresh(),
Thisform.clb_pret_diminuat.Refresh(), Thisform.do_calculeaza_totaluri() — deci orice apel
recalculeaza si campurile agregate de linie, nu doar cele pe unitate.
In valuta, ce ramane needitat de aceasta metoda: campurile agregate (valdiminuatftva etc.) NU
sunt scrise aici — do_calculeaza_totaluri (sectiunea urmatoare) le calculeaza separat din
discount_unitar/cantitate.
3. Starea de azi in grid — tabel
| Coloana | ControlSource |
Formular | ReadOnly |
Editabil azi | Recalculeaza la editare |
|---|---|---|---|---|---|
Column5/cDiscountCTva |
discountctva |
frm_facturare_articole (productie), :12311-12317 |
.T. explicit |
Nu | — |
Column9/cVdiscountftva |
vdiscountftva |
frm_facturare_articole, :12340-12345 |
nesetat (implicit .F.) |
Da, pe factura in valuta | Nu — niciun Valid/LostFocus/InteractiveChange propriu, cautat explicit in 10968-15739 |
Column5/cDiscountCTva |
discountctva |
frm_facturare_articole2 (prototip, neinstantiat in productie) |
.F. explicit, :16657 |
Da (prototip) | Nesigur — prototip, nu s-a cautat handler dedicat |
Column9/cVdiscountftva |
vdiscountftva |
frm_facturare_articole2 |
.F. explicit, :16688 |
Da (prototip) | idem |
Column14/procdisc |
procdisc |
frm_facturare_articole2 numai, :16721 |
nesetat | scaffold, fara scriere | Nu are corespondent Oracle, nu are Gather/Replace nicaieri in fisier (discount_verificare2.md sectiunea 5) |
Excludere pe valuta (ofacturare.vc2:15269-15278, in frm_facturare_articole.Init): cand
poDate.in_valuta = 0 se elimina cVpretFtva, cVdiscountftva, cVvaldiminuatftva; cand
in_valuta <> 0 se elimina cPretFtva, cDiscountctva (si simetricele lor). Deci azi, pe orice
factura, doar una din cele doua coloane de discount e vizibila — cea in lei, needitabila, sau cea
in valuta, editabila-dar-fara-recalcul. Nu exista azi nicio coloana de procent de discount in
grd_factura in productie (doar discountctva/vdiscountftva, valori, nu procent) — pentru procent,
azi operatorul trebuie sa deschida dialogul.
4. Proiectarea
4.1 Ce coloane se adauga/deschid
Nu se sterge nimic din crsfactura (modelul de date ramane neschimbat, conform cerintei). Se
lucreaza cu campurile deja existente:
- Coloana procent discount (noua in grid, in ambele monede) —
ControlSourcepe un camp calculat, nu direct pe un camp Oracle (nu exista coloana Oracle de procent) — vezi 4.4 pentru optiunea recomandata. cDiscountCTva(discountctva, lei):Column5.ReadOnlytrece din.T.in.F.— devine editabila, simetric cu ce azi doar prototipulfrm_facturare_articole2face.cVdiscountftva(vdiscountftva, valuta): ramane editabila ca azi, dar castiga handler-ul care azi lipseste.
Se pastreaza RemoveObject pe valuta (:15269-15278) neschimbat — excluderea reciproca deja
implementeaza cerinta "se pastreaza excluderea pe in_valuta".
4.2 Pe ce eveniment se cableaza calculul reciproc
Recomandare: Text1.LostFocus, dupa tiparul deja folosit in acest fisier pentru coloane
editabile de grid — frm_avizare_lucrare.grd_articole.cCantitate.Text1.LostFocus si
.cPret.Text1.LostFocus (ofacturare.vc2:6549-6562), ambele in aceeasi clasa de baza de grid
(_grdrow, _grd_base.vc2:445) folosita si de grd_factura. Motivele, nu teoretice ci din cod:
InteractiveChangefireste pe fiecare tasta — ar recalcula la fiecare caracter tastat intr-un numar cu zecimale (comportament vazut la coloanaprocentin dialog,:2646, dar acolo controlul e un camp simplu de dialog, nu o celula de grid cu re-randare de coloane vecine la fiecare tasta — in grid ar fi vizibil costisitor si ar zgaltai focusul).Valid-urile din dialog (:2602-2624) folosesc explicit un guard<> nSumaNatOld/nSumaValOldca sa nu recalculeze cand valoarea nu s-a schimbat efectiv — semnaleaza ca autorii au evitat deliberat recalculul pe fiecare tasta chiar si laValid.LostFocuse tiparul deja validat pentru grid-uri de articole in acest fisier, pe doua coloane numerice diferite (cantitate, pret), amandoua cu acelasi tip de nevoie (schimbarea unei valori declanseaza recalculul liniei si al totalului documentului).- Grid-ul VFP nu are
Validpe coloana insasi in mod uzual folosit aici — handlerele existente sunt pe<Coloana>.Text1.LostFocus, deci noile handlere trebuie sa fiegrd_factura.cDiscountCTva.Text1.LostFocussigrd_factura.cVdiscountftva.Text1.LostFocus(plus coloana noua de procent, daca implementata caText1editabil).
4.3 Rutina de recalcul — reutilizare, nu reimplementare
calculeaza_totaluri() (oproceduri_facturare.prg:2258-2381) e deja generica: primeste orice
obiect (toArticol) cu proprietatile preturi_cu_tva/discount_unitar/pretftva/cantitate/...
(le adauga singura, prin AddProperty, daca lipsesc — :2264-2272, mapand numele de camp
crsfactura-stil, discountftva/vdiscountftva, pe numele poArticol-stil,
discount_unitar/discount_unitar_val) si scrie inapoi campurile agregate. Poate rula direct pe
un Scatter Name al randului curent din crsfactura, fara sa deschida dialogul:
Select crsfactura
Scatter Name loArt Memo
loArt = calculeaza_totaluri(loArt)
Gather Name loArt Memo
Aceasta acopera pasul "recalculeaza campurile agregate din discountul unitar deja stabilit"
(valdiminuatftva, valdiminuatctva, vvaldiminuatftva, vvaldiminuatctva,
valdiscountftva/ctva, vvaldiscountftva/ctva), exact campurile care azi raman vechi
(sectiunea 1.4).
Ce calculeaza_totaluri() NU face: conversia reciproca procent<->valoare — aceea e logica din
do_calculeaza_discount (sectiunea 2), care azi scrie in poArticol si actualizeaza controale de
dialog (Thisform.clb_*.Refresh()) care nu exista in grid. Recomandare: se extrage o functie noua,
fara referinte la Thisform.clb_* (partea de calcul pur, liniile :1878-1970 minus liniile de
Refresh), reutilizabila atat din dialog (daca dialogul de articol individual mai exista undeva —
nu la S4c, dialogul dispare) cat si din handler-ul de grid — parametrizata pe rand
(toArticol/Scatter Name) in loc de poArticol global. Aceasta functie noua intra la fel ca
calculeaza_totaluri(), in oproceduri_facturare.prg, ca sa fie apelabila din handler-ul de coloana
fara sa depinda de poArticol-ul dialogului disparut.
4.4 Coloana de procent — implementare recomandata
Nu exista coloana Oracle de procent (sectiunea 1.3) si nici coloana persistenta pe crsfactura
pentru asta (spre deosebire de discountftva etc, care sunt in creeaza_facturacrs). Doua optiuni:
- Adauga camp calculat, needitabil, alaturi de coloana editabila de valoare — afiseaza procentul
derivat (
Round(discountftva/pretftva*100,2)), needitabil direct — evita sa se mai adauge o coloana Oracle noua si o cale de editare in plus (mai putin cod nou, mai putina suprafata de bug). - Adauga coloana editabila de procent, needitabila-pe-model — ca in
frm_articol_factura(Clb_procent_discount), scrie tot indiscountftva/discountctva/vdiscountftva/vdiscountctvaprin acelasi calcul reciproc, dar camp de UI, nu de Oracle. Mai aproape de comportamentul de azi din dialog (operatorul poate tasta fie procentul, fie valoarea), dar cere si al treilea handler deLostFocussi inca un camp de lucru in cursorul local (AddPropertylado_initializeaza_articol, dupa tiparulid_jtva_coloana/valdiminuatftva,:13666-13681).
Cerinta din plan ("cele doua campuri de discount ale lui — procent si valoare unitara — devin coloane in grid") cere explicit ambele campuri editabile — deci optiunea 2 e cea care respecta litera cerintei; optiunea 1 e o simplificare de discutat cu Marius (vezi sectiunea 10).
4.5 Unde se cheama exact recalculul, pas cu pas (handler propus)
Pentru coloana cDiscountCTva (discountctva, lei, tip=2 in nomenclatura do_calculeaza_discount):
PROCEDURE grd_factura.cDiscountCTva.Text1.LostFocus
Select crsfactura
Scatter Name loArt Memo
* recalcul reciproc procent<->valoare, tip=2 -- functia noua din 4.3, nu do_calculeaza_discount
loArt = recalc_discount_linie(loArt, This.Value, 2) && scrie discountftva/discountctva(_val)
loArt = calculeaza_totaluri(loArt) && scrie valdiminuat*/vvaldiminuat*
Gather Name loArt Memo
Thisform.do_calculeaza_totaluri() && resumeaza totalurile documentului
ENDPROC
Simetric pentru cVdiscountftva (tip=3) si pentru coloana de procent, daca implementata editabil
(tip=1). Guard-ul <> valoare veche (ca la Valid-urile din dialog, :2602-2624) se pastreaza ca
sa nu se recalculeze la simplu tab-through fara modificare.
5. Interactiunea cu discountul din politica de pret
Azi: do_initializeaza_articol (ofacturare.vc2:13618 si urm.) preia
toArticol.discount_unitar = Nvl(toArticol.discount_unitar, 0) (:13630) din crsarticole
(cursorul de stoc/oferta, populat inainte de deschiderea dialogului — sursa SQL exacta, in afara
ofacturare.vc2, ramasa necercetata si in raportul precedent). Daca politica de pret a populat deja
un discount, acela apare preincarcat in campurile dialogului (Clb_discount_unitar etc.),
operatorul il poate suprascrie tastand — suprascrierea intra prin acelasi do_calculeaza_discount
ca orice alta tastare, fara distinctie intre "valoare din politica" si "valoare tastata manual".
Dupa S4c: nu se schimba nimic in mecanismul de preincarcare — do_adauga_articol inca scrie
discountftva/discountctva/vdiscountftva/vdiscountctva in crsfactura la adaugarea liniei
(sectiunea 1.2, pasul 4), inainte ca operatorul sa apuce sa editeze coloana de grid. Valoarea din
politica ramane vizibila in celula, exact ca azi in dialog, iar editarea manuala in grid o
suprascrie la fel — singura diferenta e ca suprascrierea se intampla acum pe LostFocus de celula,
nu pe Valid de camp de dialog. Nu exista azi o distinctie de tip "flag discount din politica vs.
discount manual" in crsfactura (nu am gasit un camp discount_din_politica sau similar) — deci
S4c nu pierde nicio informatie care exista deja, dar nici nu castiga vreo trasabilitate noua.
6. Refacerea totalurilor documentului
Totalurile documentului (Thisform.nbazaron, ntotalron, ndiscron, variantele *val) se
recalculeaza in frm_facturare_articole.do_calculeaza_totaluri (ofacturare.vc2:13423-13520) —
re-sumeaza direct din crsfactura (si crsfacturaset daca exista), citind exact campurile
agregate din sectiunea 1.1/1.4:
13456 Select Sum(Nvl(valdiminuatctva,0)) As Total,Sum(Nvl(valdiminuatftva,0)) As Baza,;
13457 Sum(Nvl(valdiminuattva,0)) As Tva,;
13458 Sum(Nvl(valdiscountftva,0)) As discount From crsfactura Into Cursor crstotalurifact
Simetric pentru valuta (vvaldiminuat*) mai jos in aceeasi procedura. Consecinta directa pentru
proiectare: daca handler-ul de coloana (sectiunea 4.5) actualizeaza corect valdiminuatftva/
valdiminuatctva/vvaldiminuatftva/vvaldiminuatctva/valdiscountftva/vvaldiscountftva pe randul
editat inainte de a chema Thisform.do_calculeaza_totaluri(), totalurile documentului se refac
automat, corect, din acelasi mecanism care azi refece totalurile la adaugare/stergere de linie
(do_adauga_articol:13160, do_sterge:14676) — nu trebuie cod nou pentru pasul de resumare, doar
apelul, la finalul handler-ului de coloana. Thisform.do_calculeaza_totaluri e deja legat prin
Bindevent de actualizeaza_total_mod (:15265) — orice apel al lui declanseaza si actualizarea de
ecran a etichetelor de total, fara cablaj suplimentar.
7. Pasi de implementare, ordonati, cu criteriu de "gata"
- Extrage functia de calcul reciproc din
frm_articol_factura.do_calculeaza_discount(:1874-1976), fara liniile deThisform.clb_*/Thisform.nprocent, ca functie noua inoproceduri_facturare.prg, parametrizata pe obiect + valoare + tip (semnatura similara cucalculeaza_totaluri(toArticol)). Gata cand: pentru fiecare din cele 3tnTipsi ambele ramuripreturi_cu_tva, functia noua produce exact aceleasidiscount_unitar/discount_unitar_ctva/_valca metoda originala, testat pe acelasi set de intrari (procent, lei, valuta) — comparatie directa, nu doar citire de cod. - Deschide
Column5/cDiscountCTvala editare (ReadOnly = .F.,ofacturare.vc2:12311-12317), pastrandRemoveObjectpe valuta neschimbat. Gata cand: pe factura in lei, celula de discount unitar cu TVA e editabila din grid (nu doar afisata). - Adauga handler
Text1.LostFocuspecDiscountCTvasi pecVdiscountftva, dupa modelul din sectiunea 4.5: recalcul reciproc (pasul 1) +calculeaza_totaluri()(deja existent,oproceduri_facturare.prg:2258) +Gather+Thisform.do_calculeaza_totaluri(). Gata cand: editarea oricareia din cele doua coloane schimba, in acelasi moment, sidiscountftva/discountctva/vdiscountftva/vdiscountctvasivaldiminuatftva/valdiminuatctva/vvaldiminuatftva/vvaldiminuatctvape randul curent, verificat prinBrowse/inspectie cursor, nu doar pe ecran. - Adauga coloana de procent (decizie 4.4 de confirmat cu Marius — recomandare: optiunea 2,
editabila, ca sa respecte litera cerintei din plan), cu acelasi handler,
tip=1. Gata cand: tastarea unui procent produce aceeasi valoare de discount ca tastarea valorii echivalente, pe ambele monede. - Verifica totalurile documentului dupa editare in grid — nu ar trebui sa fie nevoie de cod nou
(sectiunea 6), doar de apelul din pasul 3.
Gata cand: dupa editarea discountului pe o linie,
Thisform.nbazaron/ntotalron(si varianteleval) se schimba imediat, fara sa fie nevoie de o alta actiune (adaugare/stergere de linie) care sa le forteze. - Verifica scrierea in Oracle (
do_scrie_articole,:14081-14083) — nu ar trebui sa fie nevoie de nicio schimbare, pentru ca citeste deja campurile pe unitate direct (sectiunea 1.3). Gata cand:VANZARI_DETALII.DISCOUNT_UNITARdupa salvare = valoarea tastata in grid, pe o factura testata cu discount editat exclusiv din grid (fara sa fi trecut prin dialog, care oricum dispare la unificare). - Verifica tiparirea si eFactura — cea mai importanta proba, cea ceruta explicit de plan. Gata cand: vezi sectiunea 8.
8. Cum se verifica — concret
Pasii, pe o factura de test (lei si separat valuta):
- Adauga o linie in grid (fara discount).
- Editeaza direct in grid coloana de discount (valoare, apoi pe alt rand procent) — verifica pe
ecran ca celelalte coloane afisate (
cPretFtva/cVpretftva,cValdiminuatctva/cVvaldiminuatftvadaca ramase vizibile) se actualizeaza imediat. - Interogheaza direct cursorul (nu doar ecranul) — din Command Window/breakpoint, pe randul
editat:
? discountftva, discountctva, vdiscountftva, vdiscountctva, valdiminuatftva, valdiminuatctva, vvaldiminuatftva, vvaldiminuatctva— toate opt trebuie sa reflecte editarea, nu doar cele patru "brute". - Finalizeaza factura (
do_scrie_articole) si interogheaza Oracle (schemaMARIUSM_AUTO, conventia dinCOMUN\docs\scripturi-migrare-db.md:36-40, pringoExecutor, doarSELECT):trebuie sa fie egal cu valoarea tastata in grid.SELECT discount_unitar FROM vanzari_detalii WHERE id_vanzare = :id_factura AND id_articol = :id_articol; - Retipareste factura (nu doar priveste pe ecranul de compunere — deschide efectiv raportul,
factura.fr2sau echivalentul in lei/valuta) si verifica pe pagina tiparita capretftva/valftvape linia editata reflecta discountul nou (pretul unitar net si valoarea liniei trebuie sa fie consistente intre ele:valftva = pretftva * cantitate, altfel exact bug-ul din sectiunea 1.4). - Genereaza XML-ul de eFactura pentru aceeasi factura si verifica in fisier
cbc:LineExtensionAmount/cbc:PriceAmountpe linia editata — trebuie sa corespunda cu pretul net nou, nu cu cel dinaintea editarii. - Repeta 1-6 pe factura in valuta, ca sa acoperi ramura
vdiscountftva/vvaldiminuatftva(ramura azi cu bug-ul confirmat).
9. Ce nu se poate testa headless
- Editarea propriu-zisa a celulei de grid (tastare in
Text1al unei coloane, tab/enter pentruLostFocus) — headless-ul VFP (-A -T) nu materializeaza interactiunea de tastatura intr-un grid; cf.docs\cercetare\...grid-coloane-nu-se-materializeaza-headless(memorie de proiect) — coloanele de grid sunt artefacte needitabile sub-A -T; verificarea reala a comportamentului UI cere harness-ul cu UI vizibil sau un test manual asistat. - Tiparirea efectiva a raportului (
.frx) — generarea unui PDF/preview real, nu doar constructia cursoruluicrsfacttempin memorie, cere motorul de raportare VFP, care nu ruleaza util headless pentru verificare vizuala (se poate verifica campurile cursorului sursa, dar nu pagina tiparita). - Trimiterea efectiva catre ANAF (SPV) — se poate genera si inspecta XML-ul local, dar validarea reala (acceptare/respingere) cere mediul de test ANAF, in afara acestei sarcini.
- Comportamentul focus/tab-order al noilor coloane in grid (ordinea de tab intre celule, daca
LostFocusse declanseaza corect la navigare cu tastatura vs. mouse) — cere sesiune interactiva.
10. Riscuri si ce ramane de decis de Marius
- Coloana de procent — editabila sau doar afisata (sectiunea 4.4). Recomandare: editabila (optiunea 2), pentru ca respecta litera deciziei din plan ("cele doua campuri... devin coloane"), dar costa un handler si un camp de lucru in plus. Daca simplitatea conteaza mai mult decat paritatea cu dialogul vechi, optiunea 1 (doar afisaj) reduce suprafata de cod fara sa piarda functionalitate reala (operatorul tot poate obtine orice discount tastand valoarea).
- Riscul de rotunjire in lant:
do_calculeaza_discountare cel putin 3 rotunjiri succesive pe drumul procent->valoare->procent (sectiunea 2) — comportament deja existent, nu introdus de S4c, dar mutarea in grid (editare mai frecventa, rand cu rand, fara sa mai treaca prin "confirmare" de dialog) ar putea face vizibile discrepante mici de rotunjire care azi treceau neobservate. Nu e un motiv sa se schimbe rotunjirile (ar strica alte fluxuri), doar un risc de UX de semnalat. Zero cazuri gasite in cod care sa demonstreze deja o problema — semnalat preventiv, nu confirmat. Gather/Scatterpe rand cu campuri MEMO:do_adauga_articolfolosesteGather ... MEMO(:12945-12950) — handler-ul nou de coloana (sectiunea 4.5) trebuie sa faca la fel (Scatter Name ... Memo/Gather Name ... Memo), altfel campulexplicatie(M) s-ar putea goli la fiecare editare de discount — de verificat explicit la implementare, nu doar presupus. Recomand un test dedicat: editeaza discountul pe o linie cuexplicatiepopulata, verifica caexplicatieramane neschimbata dupaLostFocus.- Coloana
procdiscdin prototip (frm_facturare_articole2,:16721) — scaffold mort, fara scriere (sectiunea 3). Recomandare: nu se reutilizeaza ca atare pentru coloana noua de procent — se porneste curat, cu functia noua din sectiunea 4.3, nu cu acest camp neconectat. - Formularul
frm_facturare_articole2ramane prototip separat, ne-instantiat in productie — S4c nu are nevoie sa il atinga, dar daca exista intentia sa devina formularul unificat, coloanele lui de discount (deja editabile,:16657/:16688) ar trebui auditate separat pentru acelasi bug de recalcul (nu verificat aici — in afara perimetrului cerut).
11. Punct deschis din S1 — _checkbox1 vs chkDetaliat
Nu apartine povestii S4c (nu am gasit nicio legatura cu discountul pe linie sau grd_factura), dar
am dat peste ambele controale pe cale laterala, in frm_alte_date (ferestre_cere_date.vc2), asa ca
inchid ieftin: sunt doua controale distincte, nu o duplicare de nume.
_checkbox1e un control generic (mostenit din clasa de baza), folosit infrm_alte_date.Init(ferestre_cere_date.vc2:3188-3202) doar ca reper de layout — pozitia lui determina inaltimea ferestrei si offset-ul altor controale, fara logica de business proprie vizibila in acest formular.chkDetaliate un checkbox cu nume propriu, legat de fluxul de incasare (actualizeaza_tipincasare,:2728-2853) — vizibil doar candopt_incasatindica un anumit tip de incasare (POS/detaliat), ascuns/zero altfel; pe ramuraElsea luiInit(:3187-3205, cand alt tip de context nu implica incasare deloc) e eliminat explicit din formular (Thisform.RemoveObject('chkDetaliat'),:3201), spre deosebire de_checkbox1, care ramane (e folosit chiar pe linia urmatoare pentru calculul inaltimii finale,:3202).
Concluzie: nu e o inconsecventa de cod, sunt doua controale cu roluri diferite pe acelasi
formular — punctul se poate inchide ca "nu e bug", cu rezerva ca n-am cercetat de ce chkDetaliat
exista ca si checkbox separat de opt_incasat (ce reprezinta exact "detaliat") — nu era in
perimetrul S4c si nu am aprofundat.
Necunoscute ramase (mostenite din rapoartele-sursa, nu re-investigate aici)
- Interogarea SQL exacta care populeaza
crsarticolecu discountul din politica de pret (CRM_POLITICI_PRET_ART) — cod in afaraofacturare.vc2, netrasat (mostenit dindiscount_verificare2.md, sectiunea "Necunoscute ramase"). - Valorile implicite ale flag-urilor
gnEFACTURA_XML_DISC_PLISTA_LINIE/_ART(mostenit dindiscount_in_rapoarte_si_efactura.md). - Comportamentul
crsfacturafinalaval(varianta valuta a cursorului final de tiparire) — presupus simetric cu varianta lei, nu reverificat linie cu linie separat pentru S4c.