Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
490 lines
34 KiB
Markdown
490 lines
34 KiB
Markdown
# 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)
|
|
|
|
1. 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`).
|
|
2. Toate cheama `Thisform.do_calculeaza_discount(valoare, tip)` — **`frm_articol_factura`**
|
|
(`:1874-1976`, contract detaliat in sectiunea 2) — scrie in `poArticol`:
|
|
`discount_unitar`, `discount_unitar_val`, `discount_unitar_ctva`, `discount_unitar_ctva_val`.
|
|
3. `do_calculeaza_discount` cheama la final `Thisform.do_calculeaza_totaluri()` (`:1974`,
|
|
`frm_articol_factura`, `:2068-2179`) care delega la **`calculeaza_totaluri(poArticol)`**
|
|
(`oproceduri_facturare.prg:2258-2381`, functie globala) — aceasta scrie in `poArticol`:
|
|
`valdiminuatftva`, `valdiminuattva`, `valdiminuatctva`, `vvaldiminuatftva`, `vvaldiminuattva`,
|
|
`vvaldiminuatctva`, plus `valdiscountftva/ctva`, `vvaldiscountftva/ctva`, `valftva/ctva/tva` etc.
|
|
4. La inchiderea dialogului, `do_adauga_articol` (`ofacturare.vc2:12813-13167`) scrie **tot obiectul
|
|
deja calculat** in `crsfactura`, in doua pasi:
|
|
- `Gather Name poArticol Fields Like ... valdiminuatftva, valdiminuattva, valdiminuatctva,
|
|
vvaldiminuatftva, vvaldiminuattva, vvaldiminuatctva ...` (`:12945-12950`) — campurile agregate,
|
|
**deja calculate de `calculeaza_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 lista `Gather` (doar variantele `val*` agregate sunt).
|
|
5. `do_modifica` (`:13746-13914`, editarea unei linii existente) urmeaza acelasi tipar —
|
|
`Gather`/`Replace` cu aceleasi campuri (`:13837-13881`), dupa ce redeschide dialogul pe rand.
|
|
6. **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** — scrie `vdiscountftva` direct in `crsfactura` prin
|
|
binding-ul standard de grid, dar **nu recalculeaza** `vvaldiminuatftva`/`vvaldiminuatctva`
|
|
(confirmat cautat explicit, `discount_verificare2.md` punctul 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): daca `tip_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. Daca `tip_valuta=1`: `discount_unitar_val` se calculeaza intai (`gnPVal`), apoi
|
|
`discount_unitar = Round(discount_unitar_val * Curs / multiplicator, gnPPretV)`.
|
|
- `tnTip=2` (lei -> procent): `discount_unitar = tnValoare` direct, procentul se deriva
|
|
(`Round(discount_unitar/pretftva*100, 2)`).
|
|
- `tnTip=3` (valuta -> procent + lei): `discount_unitar_val = tnValoare`, procentul deriva din
|
|
valuta, apoi `discount_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.
|
|
- Daca `tip_valuta=1`, simetric: `discount_unitar_ctva_val` derivat din `discount_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) — `ControlSource` pe 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.ReadOnly` trece din `.T.` in `.F.` — devine
|
|
editabila, simetric cu ce azi doar prototipul `frm_facturare_articole2` face.
|
|
- **`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:
|
|
- **`InteractiveChange` fireste pe fiecare tasta** — ar recalcula la fiecare caracter tastat intr-un
|
|
numar cu zecimale (comportament vazut la coloana `procent` in 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`/`nSumaValOld`
|
|
ca sa nu recalculeze cand valoarea nu s-a schimbat efectiv — semnaleaza ca autorii au evitat
|
|
deliberat recalculul pe fiecare tasta chiar si la `Valid`.
|
|
- **`LostFocus` e 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 `Valid` pe coloana insasi in mod uzual folosit aici — handlerele existente sunt
|
|
pe `<Coloana>.Text1.LostFocus`, deci noile handlere trebuie sa fie
|
|
`grd_factura.cDiscountCTva.Text1.LostFocus` si `grd_factura.cVdiscountftva.Text1.LostFocus`
|
|
(plus coloana noua de procent, daca implementata ca `Text1` editabil).
|
|
|
|
### 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:
|
|
1. **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).
|
|
2. **Adauga coloana editabila de procent, needitabila-pe-model** — ca in `frm_articol_factura`
|
|
(`Clb_procent_discount`), scrie tot in `discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva`
|
|
prin 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 de
|
|
`LostFocus` si inca un camp de lucru in cursorul local (`AddProperty` la `do_initializeaza_articol`,
|
|
dupa tiparul `id_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"
|
|
|
|
1. **Extrage functia de calcul reciproc** din `frm_articol_factura.do_calculeaza_discount`
|
|
(`:1874-1976`), fara liniile de `Thisform.clb_*`/`Thisform.nprocent`, ca functie noua in
|
|
`oproceduri_facturare.prg`, parametrizata pe obiect + valoare + tip (semnatura similara cu
|
|
`calculeaza_totaluri(toArticol)`).
|
|
*Gata cand*: pentru fiecare din cele 3 `tnTip` si ambele ramuri `preturi_cu_tva`, functia noua
|
|
produce exact aceleasi `discount_unitar`/`discount_unitar_ctva`/`_val` ca metoda originala, testat
|
|
pe acelasi set de intrari (procent, lei, valuta) — comparatie directa, nu doar citire de cod.
|
|
2. **Deschide `Column5`/`cDiscountCTva` la editare** (`ReadOnly = .F.`,
|
|
`ofacturare.vc2:12311-12317`), pastrand `RemoveObject` pe valuta neschimbat.
|
|
*Gata cand*: pe factura in lei, celula de discount unitar cu TVA e editabila din grid (nu doar
|
|
afisata).
|
|
3. **Adauga handler `Text1.LostFocus` pe `cDiscountCTva` si pe `cVdiscountftva`**, 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, si
|
|
`discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva` **si**
|
|
`valdiminuatftva`/`valdiminuatctva`/`vvaldiminuatftva`/`vvaldiminuatctva` pe randul curent,
|
|
verificat prin `Browse`/inspectie cursor, nu doar pe ecran.
|
|
4. **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.
|
|
5. **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 variantele
|
|
`val`) se schimba imediat, fara sa fie nevoie de o alta actiune (adaugare/stergere de linie) care
|
|
sa le forteze.
|
|
6. **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_UNITAR` dupa 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).
|
|
7. **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):
|
|
1. Adauga o linie in grid (fara discount).
|
|
2. Editeaza direct in grid coloana de discount (valoare, apoi pe alt rand procent) — verifica pe
|
|
ecran ca celelalte coloane afisate (`cPretFtva`/`cVpretftva`, `cValdiminuatctva`/`cVvaldiminuatftva`
|
|
daca ramase vizibile) se actualizeaza imediat.
|
|
3. **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".
|
|
4. **Finalizeaza factura** (`do_scrie_articole`) si interogheaza Oracle (schema `MARIUSM_AUTO`,
|
|
conventia din `COMUN\docs\scripturi-migrare-db.md:36-40`, prin `goExecutor`, doar `SELECT`):
|
|
```sql
|
|
SELECT discount_unitar FROM vanzari_detalii WHERE id_vanzare = :id_factura AND id_articol = :id_articol;
|
|
```
|
|
trebuie sa fie egal cu valoarea tastata in grid.
|
|
5. **Retipareste factura** (nu doar priveste pe ecranul de compunere — deschide efectiv raportul,
|
|
`factura.fr2` sau echivalentul in lei/valuta) si verifica pe pagina tiparita ca `pretftva`/`valftva`
|
|
pe 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).
|
|
6. **Genereaza XML-ul de eFactura** pentru aceeasi factura si verifica in fisier
|
|
`cbc:LineExtensionAmount`/`cbc:PriceAmount` pe linia editata — trebuie sa corespunda cu pretul net
|
|
nou, nu cu cel dinaintea editarii.
|
|
7. 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 `Text1` al unei coloane, tab/enter pentru
|
|
`LostFocus`) — 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
|
|
cursorului `crsfacttemp` in 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
|
|
`LostFocus` se 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_discount` are 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`/`Scatter` pe rand cu campuri MEMO**: `do_adauga_articol` foloseste `Gather ... MEMO`
|
|
(`:12945-12950`) — handler-ul nou de coloana (sectiunea 4.5) trebuie sa faca la fel
|
|
(`Scatter Name ... Memo` / `Gather Name ... Memo`), altfel campul `explicatie` (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 cu `explicatie` populata, verifica ca
|
|
`explicatie` ramane neschimbata dupa `LostFocus`.
|
|
- **Coloana `procdisc` din 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_articole2`** ramane 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**.
|
|
- `_checkbox1` e un control generic (mostenit din clasa de baza), folosit in `frm_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.
|
|
- `chkDetaliat` e un checkbox cu nume propriu, legat de fluxul de incasare (`actualizeaza_tipincasare`,
|
|
`:2728-2853`) — vizibil doar cand `opt_incasat` indica un anumit tip de incasare (POS/detaliat),
|
|
ascuns/zero altfel; pe ramura `Else` a lui `Init` (`: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 `crsarticole` cu discountul din politica de pret
|
|
(`CRM_POLITICI_PRET_ART`) — cod in afara `ofacturare.vc2`, netrasat (mostenit din
|
|
`discount_verificare2.md`, sectiunea "Necunoscute ramase").
|
|
- Valorile implicite ale flag-urilor `gnEFACTURA_XML_DISC_PLISTA_LINIE`/`_ART` (mostenit din
|
|
`discount_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.
|