docs: cercetarile trans-proiect din ROAFACTURARE trec in COMUN
Paisprezece rapoarte care nu erau ale ROAFACTURARE traiau in docs\cercetare\ al acelui proiect: valuta si curs in ofacturare, TVA calculat vs salvat plus denormalizarea VANZARI, inventarul consumatorilor VANZARI din toata suita ROA, integrarile de contracte/politici/nomenclator (#10, #11, #12), watchdog-ul VFP de testare, proiectarea Oracle a scrierii din S5 si view-ul VVANZARI_ARTICOLE. Motivul mutarii, nu doar al pastrarii: planurile #10, #11 si #12 sunt amanate, iar indexul lor spune explicit ca se reiau din aceste rapoarte - deci sunt punct de plecare, nu istoric. Iar tinta lor e COMUN plus ROAPRETURI/ROACONTRACTE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
35
docs/cercetare/ff_view_articole_vanzare.sql
Normal file
35
docs/cercetare/ff_view_articole_vanzare.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- 08.08.2026 Marius Mutu
|
||||
-- VVANZARI_ARTICOLE: liniile unei vanzari (VANZARI_DETALII) cu denumirea articolului, a gestiunii
|
||||
-- si a valutei, pentru gridul de articole din formularul de modificare a notei. Valori brute, fara
|
||||
-- conversie valutara si fara calcule - editarea scrie inapoi exact ce a citit. Filtrul STERS ramane
|
||||
-- pe seama apelantului, ca la VACT_TOT / VRUL_TOT / VRUL_OBINV_TOT.
|
||||
|
||||
CREATE OR REPLACE VIEW VVANZARI_ARTICOLE AS
|
||||
select vd.id_vanzare,
|
||||
vd.id_vanzare_det,
|
||||
vd.id_articol,
|
||||
vd.cantitate,
|
||||
vd.pret,
|
||||
vd.pret_cu_tva,
|
||||
vd.proc_tvav,
|
||||
vd.discount_unitar,
|
||||
vd.id_gestiune,
|
||||
vd.cont,
|
||||
vd.id_valuta,
|
||||
vd.id_jtva_coloana,
|
||||
vd.serie,
|
||||
vd.explicatie,
|
||||
vd.taxcode,
|
||||
vd.lot,
|
||||
vd.sters,
|
||||
na.denumire,
|
||||
na.codmat,
|
||||
ng.nume_gestiune,
|
||||
nv.nume_val
|
||||
from vanzari_detalii vd
|
||||
left join nom_articole na on na.id_articol = vd.id_articol
|
||||
left join nom_gestiuni ng on ng.id_gestiune = vd.id_gestiune
|
||||
left join nom_valute nv on nv.id_valuta = vd.id_valuta;
|
||||
|
||||
exec pack_migrare.UpdateVersiune('ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE');
|
||||
commit;
|
||||
127
docs/cercetare/handoff_watchdog_vfp.md
Normal file
127
docs/cercetare/handoff_watchdog_vfp.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Handoff — watchdog VFP + bisectie blocaj "View Parameter" gnAn
|
||||
|
||||
Predare la peste 310k context (Regula zero). **Doar stare, fara analize noi.** Raport analitic
|
||||
complet (dovezi, log-uri, discutie): `docs\cercetare\rec_watchdog_vfp.md`.
|
||||
|
||||
## 1. Inventar livrabile, cu fisier:linie
|
||||
|
||||
**Utilitar**: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1`. Linia de comanda completa:
|
||||
|
||||
```
|
||||
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1" -Script "<cale.prg>" -TimeoutSec 90 -AutoDismiss -MaxDialogs 10
|
||||
```
|
||||
|
||||
Lanseaza `vfp9.exe -A -T "<script>"`, detecteaza orice fereastra noua diferita de
|
||||
`Process.MainWindowHandle`, o captureaza (PNG + `WM_GETTEXT` pe titlu si controale copil) in
|
||||
`<folderul scriptului>\watchdog_out\`, si cu `-AutoDismiss` incearca s-o inchida STRICT prin mesaje
|
||||
Windows tintite pe handle (`BM_CLICK` pe buton Cancel/Anulare -> `WM_COMMAND IDCANCEL` ->
|
||||
`WM_KEYDOWN`/`WM_KEYUP` ca mesaj -> `WM_CLOSE`) - **FARA input real** (vezi sectiunea 3). Omoara
|
||||
procesul mereu la iesire (`finally`).
|
||||
|
||||
**Fisiere de test noi**:
|
||||
- `COMUN\utile\Teste\watchdog_selftest.prg` - caz banal de validare (MESSAGEBOX), folosit doar ca
|
||||
sa confirme ca watchdog-ul detecteaza/captureaza/dismite corect, inainte de cazul real.
|
||||
- `COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg` - experimentul B (izolat): DOAR
|
||||
`test_init_env_auto` + `update_jtva_coloane("", "crsJtvaTemp", 6)`, fara nimic din S4. NU
|
||||
reproduce blocajul - dovada ca `update_jtva_coloane`/`updateserver.prg` sunt nevinovate.
|
||||
|
||||
**Fisier de test modificat**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`:
|
||||
- linia 176: `update_jtva_coloane("", "crsJtvaTemp", 6)` (parametrul schimbat din `0` in `6` -
|
||||
necesar pentru indexul `id_jtva` cerut de `Column63.ControlSource` din `omodificari.vc2:9199`;
|
||||
independent de defectul principal, fix cunoscut deja din `date_test_nnir.md`);
|
||||
- `PROCEDURE bisect_log_gnan` (noua, la coada fisierului, ~linia 236) + apeluri `DO bisect_log_gnan
|
||||
WITH <tag>, lcLog` inserate: in programul principal intre fiecare apel de nivel superior, si ca
|
||||
PRIMA linie in interiorul fiecarei proceduri (`verifica_vanzare_nota`, `verifica_coliziune_cod`,
|
||||
`verifica_pagecount_form`) - infrastructura de bisectie, ramasa in cod ca dovada;
|
||||
- **liniile 37, 39, 50 - remediul APLICAT DE TEAM-LEAD** (nu de mine, sub pragul lui de editare
|
||||
directa): `gnAn`/`gnLuna` parantezate - `(gnAn)`, `(gnLuna)` - in apelurile `DO verifica_vanzare_nota
|
||||
WITH ...` (x2) si `DO verifica_pagecount_form WITH ...` (primul), cu comentariu explicativ deasupra
|
||||
primei aparitii (liniile 34-36).
|
||||
|
||||
**Log-uri si capturi**: `test_page3_articole_log.txt` (log-ul aplicatiei, contine liniile `[BISECT]`),
|
||||
`watchdog_out\*.png` + `watchdog_out\*_watchdog.log` (capturi + jurnal watchdog, per fisier de test),
|
||||
toate in `COMUN\utile\Teste\editare_factura\`.
|
||||
|
||||
## 2. Ce e terminat, ce nu
|
||||
|
||||
**Terminat**:
|
||||
- Watchdog-ul: scris, testat pe caz banal, testat pe cazul real, corectat (heuristica de fereastra
|
||||
principala, bug de indexare PowerShell pe array cu 1 element, input real scos complet).
|
||||
- Bisectia: COMPLETA, cauza CONFIRMATA cu date brute (nu deductie) - vezi sectiunea 4.
|
||||
- Remediul: APLICAT de team-lead (liniile 37/39/50).
|
||||
|
||||
**Neterminat**: remediul NU a fost re-testat dupa aplicare. Ultima rulare a suitei (a mea) a fost
|
||||
INAINTE de remediul team-lead-ului. Team-lead ruleaza el insusi suita, separat - **eu nu mai rulez
|
||||
nimic** (interdictie explicita primita).
|
||||
|
||||
## 3. Stare fisiere - write-back
|
||||
|
||||
**Toate fisierele atinse in aceasta sesiune sunt `.prg`/`.ps1` (text simplu)** - NU exista
|
||||
`.vc2`/`.sc2`/`.vcx`/`.vct` atinse, deci **NU exista niciun write-back nefacut**.
|
||||
|
||||
Confirmare explicita: `COMUN\clase\omodificari.vc2/.vcx/.vct` si `COMUN\programe\updateserver.prg`
|
||||
sunt **NEATINSE** in aceasta sesiune (nici de mine, nici - din cate stiu - de altcineva). Fara
|
||||
commit git/svn facut sau initiat.
|
||||
|
||||
## 4. Ce s-a stabilit deja - NU relua
|
||||
|
||||
- **Cauza CONFIRMATA, nu ipoteza**: `test_page3_articole.prg` (inainte de remediu) trecea
|
||||
`gnAn`/`gnLuna` NEPARANTEZAT in `DO...WITH` (liniile 34, 36, 47 - numerotare dinainte de remediu).
|
||||
`DO proc WITH var` paseaza variabile de memorie BY REFERENCE implicit in VFP; `LPARAMETERS`
|
||||
primitor devine alias direct pe storage-ul original, iar numele original (`gnAn`) devine
|
||||
inaccesibil (`TYPE()='U'`) STRICT pe durata acelui apel, revenind valid imediat dupa `RETURN`.
|
||||
Confirmat simetric pe 3 apeluri afectate vs 2 neafectate (vezi tabelul din `rec_watchdog_vfp.md`
|
||||
sectiunea 5).
|
||||
- **`updateserver.prg`/`update_jtva_coloane` sunt nevinovate** - experimentul B (izolat, fara nimic
|
||||
din S4) NU reproduce; functia merge perfect cand `gnAn` e vizibil normal.
|
||||
- **`gnAn` NU e eliberata niciodata** - ramane valida (`TYPE()='N'`, 2026) la FIECARE checkpoint din
|
||||
scope-ul PRINCIPAL, fara exceptie. Nu exista `CLEAR ALL`/`CLEAR MEMORY`/`RELEASE ALL EXTENDED` pe
|
||||
lantul executat.
|
||||
- **Nu e coliziune de nume in corpul procedurii**: `verifica_pagecount_form` NU are `gnAn` in
|
||||
`LOCAL` (linia ei 148/159), si NU exista `PRIVATE gnAn`/`LOCAL gnAn` nicaieri in `COMUN\programe`.
|
||||
Umbrirea vine din SINTAXA APELULUI (`DO...WITH` neparantezat), nu din declaratiile callee-ului.
|
||||
- **Handler-ul de eroare** (`test_error_handler`/`ON ERROR`) doar logheaza (`STRTOFILE`) si continua
|
||||
- nu ascunde nimic, util pentru diagnostic.
|
||||
- **Incidentul de focus**: ESC-ul trimis de o versiune veche a watchdog-ului (cu `SetForegroundWindow`
|
||||
+ `keybd_event`, SCOASA complet acum) a aterizat de fapt in PROPRIUL nostru proces de test, NU in
|
||||
sesiunea utilizatorului - dar mecanismul tot nu are tinta si regula "fara input real" ramane
|
||||
obligatorie indiferent (documentat in `rec_watchdog_vfp.md` sectiunea 1, corectat acolo dupa o
|
||||
formulare initiala gresita).
|
||||
|
||||
## 5. Ce e interzis
|
||||
|
||||
- Input real de tastatura/mouse in watchdog (`keybd_event`, `SetForegroundWindow`, `SendInput`,
|
||||
`mouse_event`) - masina e PARTAJATA. Deja scos din cod, verificat cu grep (zero hit-uri de cod,
|
||||
doar comentarii care explica interdictia).
|
||||
- Atingerea `COMUN\clase\omodificari.vc2/.vcx/.vct` si `COMUN\programe\updateserver.prg`.
|
||||
- Commit git/svn.
|
||||
- **Eu nu mai rulez suita** - team-lead o ruleaza separat dupa remediul lui.
|
||||
|
||||
## 6. Capcane de mediu platite in aceasta sesiune
|
||||
|
||||
- **Heuristica "prima fereastra vazuta = principala" e o cursa pierduta**: un dialog poate aparea in
|
||||
aceeasi fractiune de secunda cu fereastra principala. Solutia corecta: `Process.MainWindowHandle`
|
||||
(.NET), NU ordinea de aparitie si NU numele clasei (VFP foloseste clase `vfp9...` si pentru shell,
|
||||
si pentru dialogurile proprii - `vfp99400000` vs `vfp994000002`).
|
||||
- **Dialogurile proprii VFP (ex. "View Parameter") sunt owner-drawn** - `EnumChildWindows` intoarce
|
||||
ZERO controale copil (butoanele sunt desenate, nu HWND-uri reale). `BM_CLICK` e imposibil pentru
|
||||
ele; chiar si `WM_CLOSE` poate "reusi" aparent (`IsWindow` -> fals) FARA sa deblocheze de fapt
|
||||
motorul VFP din spate (SQLEXEC ramas agatat, fara linie noua in log, pana la timeout) - un fals
|
||||
"succes" de retinut daca cineva reia mecanismul de dismiss.
|
||||
- **Bug PowerShell subtil**: un `List[string]` cu UN SINGUR element e "descompus" de PowerShell la
|
||||
scalar string simplu la `return` din functie (fara `,` unar) - indexarea ulterioara `$x[0]` citeste
|
||||
atunci primul CARACTER, nu primul element. Prins la dump-ul de text al dialogului "View Parameter"
|
||||
(fara controale copil = un singur element in listă). Fix: `return , $lista.ToArray()`.
|
||||
- **`.fxp` vechi blocat**: sterge intotdeauna `.fxp`-ul inainte de fiecare rulare (watchdog-ul o face
|
||||
singur) - altfel VFP ruleaza tacut codul vechi compilat.
|
||||
- **Bisectia**: `TRANSFORM(gnAn)` pe o variabila `TYPE()='U'` ARUNCA eroare catchabila ("Variable
|
||||
'GNAN' is not found") - helper-ul `bisect_log_gnan` verifica `TYPE()<>'U'` INAINTE de a apela
|
||||
`TRANSFORM()`, ca sa nu produca o eroare noua care ar fi intrerupt bisectia la primul checkpoint
|
||||
"gol".
|
||||
|
||||
## Confirmare finala
|
||||
|
||||
**PID 16548 nu mai exista** (verificat cu `Get-Process -Id 16548` - inexistent la momentul acestui
|
||||
handoff) si **niciun `vfp9.exe` nu ruleaza** (verificat cu `Get-Process vfp9` - lista goala). Nimic
|
||||
in stare periculoasa: fara editare pe jumatate, fara cursor/tranzactie Oracle deschisa (doar
|
||||
citiri), fara fisier binar atins. Sesiunea se opreste aici.
|
||||
210
docs/cercetare/import_roris_roaacnpro.md
Normal file
210
docs/cercetare/import_roris_roaacnpro.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Anatomia butonului "Import Roris & Contracte" (ROAACNPRO)
|
||||
|
||||
Cercetare read-only in `D:\ROA\ROAACNPRO`, ca model pentru un buton generic
|
||||
"importa articole din sursa" intr-un formular de facturare unificat in ROAFACTURARE.
|
||||
Nicio editare de fisiere, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit — doar investigatie.
|
||||
|
||||
## 1. Localizare
|
||||
|
||||
**Concluzie**: Butonul e `cmdImport` (clasa `cmd_executa`) pe formularul `frm_factura`, definit in
|
||||
`Clase\oacnpro.vc2`. Nu are `Click` propriu — mosteneste `Click` din clasa de baza a butoanelor,
|
||||
care ruleaza metoda numita in proprietatea `caction`; `cmdImport` nu suprascrie `caction`, deci
|
||||
ramane valoarea implicita `do_executa` mostenita de la clasa `cmd_executa`
|
||||
(`COMUN\clase\cmd_butoane.vc2:474`). Codul efectiv e in `PROCEDURE do_executa` a formularului
|
||||
`frm_factura`.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:6894-6905` — definirea controlului:
|
||||
```
|
||||
ADD OBJECT 'cmdImport' AS cmd_executa WITH ... Caption = "\<Import Roris & Contracte", ... Left = 597, Top = 65, Width = 169
|
||||
```
|
||||
- `COMUN\clase\cmd_butoane.vc2:474` — `caction = do_executa` (mostenit de `cmd_executa`).
|
||||
- `COMUN\clase\_cmd_base.vc2:120-159` — `PROCEDURE Click` a clasei de baza: construieste
|
||||
`lcCommand = [this.Parent.] + lcAction + ...` si il executa cu macro (`&lcCommand`).
|
||||
- `Clase\oacnpro.vc2:7814-7836` — `PROCEDURE do_executa` a lui `frm_factura` (clasa incepe la
|
||||
`oacnpro.vc2:6398 DEFINE CLASS frm_factura`, se termina la `8383`).
|
||||
|
||||
## 2. Preconditii
|
||||
|
||||
**Concluzie**: Butonul e activ doar daca (a) factura nu e inca salvata (`llFacturaEditabila`) si
|
||||
(b) tipul facturii (`poDate.cTip`, ales din `cboTip`) e unul din
|
||||
`TRANZIT, CHEIAJ, CHIRII, APA, PENALITATI`. In plus, la Click, `do_executa` verifica separat ca a
|
||||
fost ales clientul.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:8117-8145`:
|
||||
```
|
||||
llFacturaEditabila = !This.lFacturaSalvata
|
||||
llRorisSauContracte = INLIST(poDate.cTip, 'TRANZIT', 'CHEIAJ', 'CHIRII', 'APA', 'PENALITATI') && butonul import RORIS&Contracte este activ doar pentru RORIS sau contracte
|
||||
...
|
||||
.cmdImport.Enabled = m.llFacturaEditabila and m.llRorisSauContracte
|
||||
```
|
||||
- `Clase\oacnpro.vc2:7820-7823`:
|
||||
```
|
||||
IF EMPTY(NVL(this.nIdClient,0))
|
||||
AMESSAGEBOX('Alegeti clientul!',0+48,_screen.Caption)
|
||||
RETURN
|
||||
ENDIF
|
||||
```
|
||||
- Fara client ales, butonul e vizibil/activ dar Click-ul se opreste cu acest mesaj.
|
||||
|
||||
## 3. Ce face efectiv
|
||||
|
||||
**Concluzie**: `do_executa` -> `factura_import()` (`Programe\proceduri_acnpro.prg:3151`) ramifica
|
||||
dupa `poDate.cTip`:
|
||||
- TRANZIT/CHEIAJ: `vizualizare_tranzit()` -> deschide dialogul `frm_tranzit` (selectie convoaie din
|
||||
vederea Oracle `ips_vvoyages`) -> la confirmare, `calcul_tranzit()`/`calcul_cheiaj()` interogheaza
|
||||
direct (SELECT-uri simple, nu pachete PL/SQL) tabelele/vederile `ips_vvoyage_members_calcul`,
|
||||
`ips_vvoyage_locks`, apoi deschide un al doilea dialog de editare tarife (`frm_calcul_tranzit`).
|
||||
- CHIRII/APA: `vizualizare_contract()` -> `calcul_contract()` -> SELECT din vederea
|
||||
`vctr_articole2` (articole de contract) -> dialog `frm_calcul_contract`.
|
||||
- PENALITATI: `vizualizare_penalitati()` -> `frm_penalitati` cu cursorul `crsCalculPenalitati`
|
||||
calculat in VFP din vederile `vanzari`/`ireg_parteneri`/`penalitati`.
|
||||
|
||||
Dupa confirmarea dialogului (validat prin variabila globala `gnButon = 1`, standard framework
|
||||
pentru formulare modale), se apeleaza `make_factura_tranzit` / `make_factura_cheiaj` /
|
||||
`make_factura_contracte` / `make_factura_penalitati`, care insereaza direct in `crsFactura`.
|
||||
|
||||
**Important**: nicio procedura Oracle stocata (`pack_*`) nu e apelata in etapa de import — acestea
|
||||
(`pack_facturare.*`, `pack_acn.salveaza_regdoc`, `pack_contafin.*`) sunt apelate abia la
|
||||
**salvarea** finala a facturii (`factura_salvare_db`, `Programe\proceduri_acnpro.prg:3290+`), nu
|
||||
la import.
|
||||
|
||||
**Dovezi**:
|
||||
- `Programe\proceduri_acnpro.prg:3151-3212` (`Procedure factura_import`), `740-771`
|
||||
(`calcul_tranzit`, sursa `ips_vvoyage_members_calcul`), `1127-1161` (`calcul_contract`, sursa
|
||||
`vctr_articole2`).
|
||||
- `Programe\proceduri_acnpro.prg:939-943`:
|
||||
`loFrmTranzit = Crea("frm_calcul_tranzit", ...); loFrmTranzit.Show(1); llSucces = (m.gnButon = 1)`.
|
||||
- `Programe\proceduri_acnpro.prg:3331-3467` — apelurile `pack_facturare.*`/`pack_acn.salveaza_regdoc`
|
||||
sunt in `factura_salvare_db`, nu in `factura_import`.
|
||||
|
||||
## 4. Cum populeaza factura
|
||||
|
||||
**Concluzie**: Scrie **direct** in cursorul de articole al facturii, `crsFactura`, prin
|
||||
`INSERT INTO crsFactura(...)` in fiecare `make_factura_*` — nu trece prin vreo metoda generica
|
||||
"adauga articol" a formularului (exista o metoda separata `do_adauga_articol` pentru adaugare
|
||||
manuala de linie, dar importul n-o foloseste). Liniile se **adauga** peste cele existente (nu
|
||||
sterge/inlocuieste `crsFactura` inainte). Protectia la dublu-import e partiala:
|
||||
- dupa un import reusit, `This.cmdImport.Enabled = .F.` dezactiveaza butonul (nu se mai poate
|
||||
importa a doua oara in aceeasi sesiune de editare a facturii);
|
||||
- pentru TRANZIT/CHEIAJ exista un avertisment (nu blocaj) daca pe convoaiele alese exista deja
|
||||
facturi (`ips_vvoyages_vanzari.numar_act IS NOT NULL`) — utilizatorul poate continua oricum.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:7827-7834`:
|
||||
```
|
||||
llSucces = factura_import(m.tlImportCalculat)
|
||||
IF m.llSucces
|
||||
This.cmdImport.Enabled = .F. && nu mai import altceva
|
||||
This.chkIntern.Enabled = .F.
|
||||
This.ActiveazaSalvare()
|
||||
...
|
||||
```
|
||||
- `Programe\proceduri_acnpro.prg:3601` (TRANZIT) / `3677-3678` (CHEIAJ) / `3764-3765`
|
||||
(CHIRII/APA): `Insert Into crsFactura(nrcrt, id_articol, denumire, ...)`.
|
||||
- `Clase\oacnpro.vc2:14915-14930` — avertisment dublu-import in `frm_tranzit.do_executa`:
|
||||
```
|
||||
SELECT numar_act, data_act, client FROM ips_vvoyages_vanzari WHERE vye_id in (...) AND numar_act is NOT null ...
|
||||
IF RECCOUNT('cFacturiTranzitTemp') > 0
|
||||
lcMesaj = 'Atentie! Exista facturi pe tranzitele alese!' + CHR(13)+CHR(10) + ...
|
||||
AMESSAGEBOX(m.lcMesaj,0+48,_screen.Caption)
|
||||
ENDIF
|
||||
```
|
||||
(mesaj informativ, apoi continua oricum cu `calcul_tranzit`/`calcul_cheiaj`).
|
||||
|
||||
## 5. Feedback catre utilizator
|
||||
|
||||
**Concluzie**: Mesaje punctuale prin `AMESSAGEBOX`, nu un sistem unitar de raportare:
|
||||
- Client lipsa: `'Alegeti clientul!'` (`oacnpro.vc2:7821`).
|
||||
- Convoi/liniile de import lipsa dupa dialog: `'Alegeti un convoi!'` in `make_factura_tranzit`
|
||||
daca `poDate.uuid` (id-urile alese) e gol (`proceduri_acnpro.prg:3530`); analog pentru
|
||||
CHIRII/APA prin `factura_salvare` (`'Alegeti convoiul'`/`'Adaugati prestatii'`,
|
||||
`proceduri_acnpro.prg:3221`, dar aceasta e verificare la *salvare*, nu la import).
|
||||
- Facturi deja existente pe convoaiele alese: avertisment neblocant (vezi punctul 4).
|
||||
- Zero rezultate in dialogul de selectie (`frm_tranzit`/`frm_calcul_contract`): niciun mesaj
|
||||
explicit gasit — grila ramane goala, iar la `Calculare`/`Terminat` fara nimic bifat `lcVyeIds`
|
||||
ramane vid si `IF !EMPTY(m.lcVyeIds)` (`oacnpro.vc2:14912`) sare peste tot calculul, fara mesaj
|
||||
catre utilizator (comportament implicit: butonul pare sa nu faca nimic).
|
||||
- Eroare Oracle: nu exista tratare `TRY/CATCH` explicita in `factura_import`/`calcul_tranzit`/
|
||||
`make_factura_*`; erorile SQL se propaga prin returul `.F.` al lui
|
||||
`goExecutor.oExecuta`/`oSelecteaza2Value`, iar afisarea erorii tine de comportamentul standard
|
||||
al `goExecutor` (framework comun, neexaminat in detaliu aici).
|
||||
|
||||
**Dovezi**: citatele de mai sus; pentru "zero rezultate fara mesaj" — `Clase\oacnpro.vc2:14898-14943`.
|
||||
|
||||
## 6. Selectivitate
|
||||
|
||||
**Concluzie**: Da, exista dialoage intermediare cu selectie, diferite pe tip:
|
||||
- **TRANZIT/CHEIAJ**: `frm_tranzit` (`oacnpro.vc2:14492-15003`) — grid `grdTranzit` cu coloane
|
||||
`Declaratia`, `Data decl.`, `Convoi`, `Origin`, `Destinatie` si o coloana checkbox `cAles`
|
||||
(`ControlSource='crsConvoaie.ales'`), plus criterii de cautare (`Cb_tx_cautare1`,
|
||||
`But_start_criterii1`/`But_reset_criterii1`) si buton `cmdCalculeaza` ("Calculare Tarife").
|
||||
Utilizatorul bifeaza unul sau mai multe convoaie; daca nu bifeaza niciunul, se ia randul curent
|
||||
(`lcVyeId` din `crsConvoaie` la pozitia curenta). Dupa `Calculare Tarife`, se deschide **al
|
||||
doilea nivel** — `frm_calcul_tranzit`/`frm_calcul_cheiaj` — pentru editarea/confirmarea
|
||||
tarifelor calculate, inainte de a reveni in factura.
|
||||
- **CHIRII/APA**: `calcul_contract()` deschide `frm_calcul_contract` cu cursorul
|
||||
`crsArticoleContract` (articol, perioada, um, pret, cantitate, valoare, valuta, locatie) din
|
||||
vederea `vctr_articole2` filtrata pe `id_ctr` — practic toate articolele contractului curent,
|
||||
editabile in acel dialog.
|
||||
- **PENALITATI**: `frm_penalitati` cu grid `grdPenalitati` (`crsCalculPenalitati`), coloane
|
||||
vizibile precum `cNrDoc`, `cSuma` ("Sold restant" in modul evaluare), `cDataDoc`.
|
||||
|
||||
Deci nu e un "importa tot" fara interventie — e mereu mediat de un dialog de vizualizare/calcul/
|
||||
confirmare (`gnButon = 1` = utilizatorul a confirmat).
|
||||
|
||||
**Dovezi**: `Clase\oacnpro.vc2:14500-14515` (obiecte grid + `cAles`), `14801`
|
||||
(`ControlSource='crsConvoaie.ales'`), `14594-14601` (`cmdCalculeaza`);
|
||||
`Programe\proceduri_acnpro.prg:1137-1153` (`crsArticoleContract` din `vctr_articole2`);
|
||||
`Programe\proceduri_acnpro.prg:545-555` (`frm_penalitati`, `grdPenalitati.cSuma`, `.cNrDoc`,
|
||||
`.cDataDoc`).
|
||||
|
||||
## 7. Butoanele vecine
|
||||
|
||||
**Concluzie**: Pe `frm_factura`, `cmdImport` e plasat in zona antetului facturii
|
||||
(`Left=597, Top=65`), langa `cboTip` (`Left=507, Top=67` — combo cu tipul facturii) si
|
||||
`chkIntern` (`Left=412, Top=70`) — nu langa bara principala de butoane. Bara principala de sus
|
||||
(`Top=0`) contine, in ordinea `Left`: `but_factura_noua` ("Nou", `Left=688`), `But_salveaza1`
|
||||
("Salveaza", `Left=719`), `But_renunt1` ("Renunta", `Left=749`). Separat, langa grila
|
||||
`grdFactura`, exista butoane de linie (`Top=120`): `But_nou1` ("Adauga linie", `Left=707`) si
|
||||
`But_sterge1` ("Sterge linie", `Left=737`). Mai sunt `but_cautare_client`/`but_cautare_beneficiar`
|
||||
(lupa cautare partener, langa campurile de client/beneficiar).
|
||||
|
||||
**Dovezi**: `Clase\oacnpro.vc2:6779-6818` (definitiile `but_factura_noua`, `But_nou1`,
|
||||
`But_renunt1`, `But_salveaza1`, `But_sterge1` cu `Left`/`Top`), `6836-6851` (`cboTip`),
|
||||
`6853-6863` (`chkIntern`), `6894-6905` (`cmdImport`).
|
||||
|
||||
## Ce e transferabil in ROAFACTURARE
|
||||
|
||||
Tiparul general merita copiat ca **model de arhitectura pentru un buton generic "importa
|
||||
articole din sursa"**:
|
||||
1. Buton activ conditionat de (a) factura editabila (nesalvata) si (b) tip de document care are o
|
||||
sursa de import — exact ca `llFacturaEditabila and llRorisSauContracte`.
|
||||
2. O metoda de intrare unica (`do_executa`/`factura_import`) care ramifica dupa tipul documentului
|
||||
catre functii specializate de "vizualizare/selectie" — fiecare deschide un dialog modal de
|
||||
selectie (grid cu coloana checkbox `ales`, criterii de cautare), returneaza succes/esec prin
|
||||
variabila globala tip `gnButon = 1`.
|
||||
3. Populare prin `INSERT INTO <cursor_articole>` direct, aditiv (nu sterge liniile existente), cu
|
||||
dezactivarea butonului dupa import reusit ca protectie minimala la dublu-import — plus
|
||||
(optional) un avertisment neblocant daca sursa a mai fost deja folosita in alta factura.
|
||||
4. Separarea neta intre etapa de **calcul/import** (SELECT-uri simple pe vederi Oracle, fara
|
||||
proceduri stocate) si etapa de **salvare** (unde intra pachetele PL/SQL) — util ca principiu:
|
||||
importul nu trebuie sa scrie definitiv in baza, doar sa populeze cursorul local.
|
||||
|
||||
Ce e strict specific ACN/RORIS si **nu** se transfera: sursele de date (`ips_vvoyages`,
|
||||
`ips_voyage_members_vanzari`, `ips_vberthing_details_vanzari`, `vctr_articole2`), logica de calcul
|
||||
tarife/ecluzari/porturi, formularele `frm_tranzit`/`frm_calcul_tranzit`/`frm_calcul_contract`/
|
||||
`frm_penalitati` ca atare, si valorile `poDate.cTip` (`TRANZIT/CHEIAJ/CHIRII/APA/PENALITATI`).
|
||||
|
||||
## Necunoscute ramase
|
||||
|
||||
- Comportamentul exact al `goExecutor.oExecuta`/`oSelecteaza2Value` la eroare Oracle (mesaj
|
||||
afisat, retry, log) — n-a fost verificat in profunzime (functie framework comun).
|
||||
- Continutul complet al dialogului `frm_calcul_tranzit`/`frm_calcul_cheiaj` (al doilea nivel,
|
||||
editare tarife) — am confirmat doar ca exista si ca `gnButon=1` marcheaza confirmarea, dar nu am
|
||||
detaliat coloanele/campurile lui.
|
||||
- Detaliile `frm_calcul_contract` (butoane, posibilitate de deselectare articole individuale) nu
|
||||
au fost citite integral — doar sursa cursorului.
|
||||
- Nu am verificat daca exista vreo validare suplimentara de "acelasi convoi importat de doua ori
|
||||
in aceeasi factura" dincolo de avertismentul cross-factura mentionat la punctul 4.
|
||||
220
docs/cercetare/rec_consumatori_vanzari.md
Normal file
220
docs/cercetare/rec_consumatori_vanzari.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Inventar consumatori `VANZARI.AVIZE`/`CURS`/`ID_VALUTA`/`MULTIPLICATOR` in suita ROA
|
||||
|
||||
Cercetare STRICT de citire (fara modificari de cod, fara DDL, fara conexiuni DB, fara rulare de
|
||||
conversii noi `vcx2txt.ps1`/`git_sync.ps1`) — masoara riscul de comportament schimbat dupa
|
||||
corectiile S4 (AVIZE, WHERE lipsa) si S8/curs (garda `nin_valuta`) aplicate in `PACK_FACTURARE`
|
||||
(vezi `rec_s4_aplicare.md`). Executata cu 3 subagenti in paralel: (a) ROAFACTURARE+COMUN, (b) cele
|
||||
8 produse cu cache text deja generat, (c) restul suitei (~56 directoare).
|
||||
|
||||
## RISC REAL
|
||||
|
||||
**1. Referinta la aviz dispare de pe factura retiparita (tip=4).**
|
||||
`COMUN\programe\oproceduri_facturare.prg` (`listeaza_formular`, ~:1125/:1189/:1265) si dublura ei
|
||||
`COMUN\clase\ofacturare_comun.vc2` (`frm_facturi.do_listeaza_formular`, ~:4103/:4204) — cod
|
||||
partajat, prezent in tot ecosistemul ROA*. Construiesc `crsFacturaListare` din `FACT_VFACTURI2`
|
||||
(coloana `altele` = alias pentru `vanzari.avize` cand `tip in (4,24,8,9)`), apoi
|
||||
`poDate.descriere = Alltrim(altele)` specific pentru `tip=4` ("factura din aviz"). `poDate.descriere`
|
||||
ajunge pe formularul tiparit ca referinta "nr. aviz" (layout exact in `.frx`, neconvertit — vezi
|
||||
gap mai jos). **Ce se schimba**: la retiparirea/relistarea unei facturi `tip=4` mai vechi, unde
|
||||
`vanzari.avize` era populat gresit inainte (smearat de UPDATE-ul fara WHERE) si acum e gol pentru
|
||||
majoritatea facturilor, referinta la aviz dispare vizibil de pe document. E in calea de reprint,
|
||||
nu de emitere — deci afecteaza utilizatorul la orice reeditare/retiparire ulterioara corectiei.
|
||||
|
||||
**2. Nota "Curs: X RON/valuta" pe factura electronica/PDF, generata fara verificare `in_valuta`.**
|
||||
`xmlefactura.prg` (COMUN partajat, cod identic — verificat prin hash — in ROAEFACTURA, ROASITFIN,
|
||||
ROACONIMPORT, ROAPRINT, ROAPRODUCTIE, ROACONT/OUTPUT, si prezent si in ROAFACTURARE/COMUN):
|
||||
```
|
||||
IF !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
lcTextAditional = ... + "Curs: " + ALLTRIM(STR(loDate.curs,10,4)) + " RON/" + ... loDate.cValuta ...
|
||||
ENDIF
|
||||
```
|
||||
Conditia verifica doar `curs` nenul, nu si `loDate.in_valuta` (spre deosebire de blocul de cateva
|
||||
linii mai jos, `DocumentCurrencyCode`, care testeaza corect `in_valuta=1`). Inainte, facturile in
|
||||
LEI aveau deja `curs` populat gresit (bug), deci nota aparea eronat, dar cu o valoare "plauzibila".
|
||||
**Ce se schimba**: dupa corectie, `curs=1` pe facturile in lei — `!EMPTY(1)` tot trece, deci nota
|
||||
tot apare, dar acum arata constant `"Curs: 1.0000 RON/"` cu sufix de valuta gol (`id_valuta=0`).
|
||||
Vizibil pe fiecare factura/aviz in lei exportata ca e-factura sau tiparita, in toate produsele de
|
||||
mai sus.
|
||||
|
||||
## RISC POSIBIL
|
||||
|
||||
3. **`ofacturare_comun.vc2:4156-4157`** (si dublura `oproceduri_facturare.prg:1225-1226`): dupa
|
||||
blocul `If poDate.in_valuta = 1 ... Else ... Endif`, `poDate.Curs`/`poDate.multiplicator` sunt
|
||||
suprascrise necondiționat din valorile citite din view. Calculul de discount e corect protejat
|
||||
de `in_valuta=1`, dar nu s-a putut confirma daca FRX-ul (neconvertit) afiseaza aceste campuri
|
||||
necondiționat de `in_valuta`. De verificat in layout-ul tiparit.
|
||||
4. **`vizualizare_facturi2` (`oproceduri_facturare.prg:431-479`)**: grid-ul de cautare/listare
|
||||
facturi expune coloanele `altele`, `curs`, `multiplicator`, `valuta`, `id_valuta`, `nume_val`
|
||||
direct din `FACT_VFACTURI2`. Nu s-a putut confirma legarea la `ControlSource` in `.scx`
|
||||
(neconvertit) — daca vreuna e legata vizibil intr-un grid, utilizatorii ar vedea coloane
|
||||
goale/"1" unde inainte vedeau valori (gresite).
|
||||
5. **ROAACNPRO** `Programe\proceduri_acnpro.prg:1180-1229`, `calcul_penalitati` (calea activa):
|
||||
`Select v.Curs, v.id_Valuta ... left join vnom_valute vl on v.id_valuta = vl.id_valuta From
|
||||
vanzari v`, afisat probabil intr-un grid de review inainte de generarea facturilor de
|
||||
penalizare. Calculul numeric al penalitatii NU foloseste `curs` (confirmat), deci fara risc de
|
||||
calcul; ramane risc de **afisare**: `id_valuta=0` ar putea sa nu se potriveasca in
|
||||
`left join vnom_valute` (tabela de valute proprie ACNPRO, distincta de `NOM_VALUTE`), lasand
|
||||
coloana `valuta` goala. Nu s-a putut confirma daca `vnom_valute` are un rand pentru
|
||||
`id_valuta=0` (spre deosebire de `NOM_VALUTE`, unde existenta randului `id_valuta=0` a fost deja
|
||||
confirmata in `rec_s4_aplicare.md`, punctul 3 din sectiunea S8/curs — deci pentru view-urile
|
||||
`FACT_VFACTURI*` din COMUN acest risc e deja exclus, ramane specific tabelei proprii ACNPRO).
|
||||
6. **ROAAUTO** `Programe\oproceduri_devize.prg:1486-1532`, `relisteaza_factura_deviz`: citeste
|
||||
`curs`/`multiplicator`/`altele` din `fact_vfacturi` la reafisarea unei facturi, dar cursorul se
|
||||
inchide imediat fara ca valorile sa fie propagate mai departe — risc redus, de reverificat doar
|
||||
daca un apelant viitor se bazeaza pe acelasi cursor.
|
||||
7. **`CONTAFIN2ORA\VFP2ORA\Programe\acn.prg:~1247-1319`** (unealta de migrare, nu produs curent de
|
||||
vanzare): scrie direct `curs`/`id_valuta`/`multiplicator` in `INSERT INTO VANZARI` +
|
||||
`MERGE INTO VANZARI_CURSURI`, cu propria regula de zero-ing independenta de `PACK_FACTURARE` —
|
||||
probabil neafectata functional, dar merita o verificare separata daca unealta mai e folosita
|
||||
activ.
|
||||
|
||||
## FARA RISC (rezumat, nu enumerate)
|
||||
|
||||
- **ROAFACTURARE+COMUN**: restul celor ~930 potriviri brute pentru `AVIZE`/`CURS`/`ID_VALUTA`/
|
||||
`MULTIPLICATOR`/`ALTELE` — module NIR/import, balante/parteneri/compensari, salarii, curs
|
||||
valutar BNR, sau citiri deja corect protejate de `in_valuta`/`tip_valuta`
|
||||
(`ofacturare.prg::listeaza_ofacturare` la emitere, `ofacturare_stoc.prg`, `anaf_efactura.prg` cu
|
||||
`decode(in_valuta,1,curs,1)`, `makexmlfacturaelectronica.prg` cu garda `mmoneda<>"RON"`). Niciun
|
||||
hit `.avize` (acces direct de camp) in afara celor doua raportate mai sus.
|
||||
- **ROACONT**: `Programe\saft_d406.prg` (declaratia fiscala D406, `oSalesInvoices`) — foloseste
|
||||
`DECODE(v.in_valuta, 0, 0, ...)` explicit, output SAF-T neschimbat de corectie. Restul hit-urilor
|
||||
pe tabele proprii (`ireg_parteneri`, `act`, `rul`) sau text necorelat.
|
||||
- **ROAACNPRO** (restul), **ROACONTRACTE**, **ROAGEST**, **ROAIMOB**, **ROAREGISTRATURA**,
|
||||
**ROASTART**: zero hit relevant legat de `VANZARI` in afara COMUN (verificat, nu doar negasit).
|
||||
- **~40 de produse din restul suitei** (ROAEFACTURA si variante, ROASITFIN, ROAPRINT,
|
||||
ROACONIMPORT, ROAPRODUCTIE, ROAHOTEL si variante, ROASAL, ROAMANAGER, ROADECL, ROAPRETURI,
|
||||
ROABAZA, ROAAPROV, ROADEVIZE, etc.): hit-uri pe "AVIZE" = conceptul de business "aviz de
|
||||
expeditie" (tip document), nu coloana; hit-uri "CURS"/"ID_VALUTA" pe alte tabele proprii sau in
|
||||
`oDateFactura` (populate de apelant, protejate de `in_valuta`). `COMUNROA` (depozitul central)
|
||||
contine doar scripturi de deployment, fara logica de facturare.
|
||||
|
||||
## Rezumat acoperire
|
||||
|
||||
- **ROAFACTURARE + COMUN local**: acoperire completa `.prg` + `.vc2`/`.sc2` via `vfp_symbols.ps1
|
||||
-CodeOnly` (index deja construit). **Gap**: 207 `.frx` si 30 `.mnx` fara `.fr2`/`.mn2` in cache —
|
||||
layout-ul efectiv tiparit (unde `poDate.descriere`/`Curs`/`cValuta` chiar apar pe hartie) nu e
|
||||
verificabil fara conversie (interzisa in acest task). Riscurile REAL #1/#2 si POSIBIL #3/#4
|
||||
depind partial de acest layout.
|
||||
- **8 produse cu cache text existent** (ROAACNPRO, ROAAUTO, ROACONT, ROACONTRACTE, ROAGEST,
|
||||
ROAIMOB, ROAREGISTRATURA, ROASTART): acoperire completa `.prg` + `.vc2`/`.sc2` via
|
||||
`vfp_symbols.ps1 -CodeOnly`. **Gap**: proprietati/metadata (`ControlSource` de grid legat direct
|
||||
de o coloana, fara linie de cod explicita) nu apar in `-CodeOnly` — dar un asemenea caz s-ar
|
||||
clasifica oricum FARA RISC (simpla afisare).
|
||||
- **Restul suitei** (~56 directoare, inclusiv `COMUNROA`): acoperire **doar** pe fisierele `.prg`
|
||||
(text simplu, nu necesita conversie); zero cache text pentru `.vcx`/`.scx` — **niciun cod din
|
||||
clase/formulare compilate al acestor produse nu a fost verificat**, conform interdictiei de a
|
||||
genera cache nou. ~35 produse identificate ca in afara domeniului (unelte/infrastructura: SSH,
|
||||
server, telefonie, criptare, declaratii D1xx/D3xx/D406 etc.) fara nicio potrivire pe termenii
|
||||
cautati.
|
||||
- Nicio conexiune la baza de date folosita; cifrele despre distributia datelor (ex. randul
|
||||
`id_valuta=0` din `NOM_VALUTE`) sunt preluate din `rec_s4_aplicare.md`, deja documentate.
|
||||
|
||||
## Concluzie
|
||||
|
||||
Doua locuri cu **risc real** confirmat, ambele in codul COMUN partajat (deci efect cross-project):
|
||||
disparitia referintei la aviz de pe facturile `tip=4` retiparite, si textul "Curs: 1.0000 RON/"
|
||||
afisat gresit pe nota facturii electronice/PDF pentru facturile in lei. Restul e risc posibil,
|
||||
dependent de layout-uri `.frx`/`.scx` neconvertite (gap de acoperire cunoscut, nu absenta de risc).
|
||||
Nu s-a gasit niciun consumator cu risc real de **calcul** (sume/discounturi) — toate caile de
|
||||
calcul verificate sunt deja protejate corect de `in_valuta`.
|
||||
|
||||
---
|
||||
|
||||
## Corectie aplicata — riscul REAL #2 (nota "Curs:" din `xmlefactura.prg`)
|
||||
|
||||
Modificare de cod, ceruta explicit de team-lead dupa livrarea inventarului de mai sus. Domeniu:
|
||||
**doar** `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg`. Nicio alta copie din suita nu a
|
||||
fost atinsa.
|
||||
|
||||
### Ce s-a schimbat
|
||||
|
||||
Linia 374 (acum singura diferenta fata de fisierul dinainte de editare):
|
||||
```diff
|
||||
- IF !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
+ IF loDate.in_valuta = 1 AND !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
lcTextAditional = m.lcTextAditional + Iif(!Empty(m.lcTextAditional), ' # ', '') + "Curs: " + ...
|
||||
ENDIF
|
||||
```
|
||||
Garda adaugata (`loDate.in_valuta = 1 AND`) e preluata **identic** din modelul deja corect din
|
||||
acelasi fisier, la 9 linii mai jos (acum `:385`, era `:384`): `If loDate.in_valuta = 1` /
|
||||
`oinvoice.lastchild.Text = m.mmoneda`, folosit pentru `DocumentCurrencyCode`. Nicio forma noua
|
||||
inventata. Fara comentarii adaugate (regula 2, rezolvari de erori). Diff complet:
|
||||
`D:\ROA\ROAFACTURARE\docs\diff_xmlefactura_garda_valuta.patch`.
|
||||
|
||||
### Verificare pe cazuri (dupa modificare)
|
||||
|
||||
1. **Factura in lei (`in_valuta=0`), `curs=1`** (comportamentul nou, generalizat, al facturilor in
|
||||
lei dupa corectia `PACK_FACTURARE`): inainte, `!EMPTY(NVL(1,0))=.T.` -> nota aparea cu
|
||||
`"Curs: 1.0000 RON/"` (valuta goala). Acum, `loDate.in_valuta = 1` e `.F.` -> intreg `AND`-ul e
|
||||
fals -> nota NU se mai adauga. **Singurul caz care isi schimba comportamentul** (cel vizat).
|
||||
2. **Factura in valuta reala (`in_valuta=1`)**, curs populat normal: `loDate.in_valuta = 1` e
|
||||
`.T.`, restul conditiei neschimbat -> nota apare identic ca inainte. Neschimbat.
|
||||
3. **Factura veche cu `curs` NULL** (orice `in_valuta`): `NVL(NULL,0)=0` -> `!EMPTY(0)=.F.` ->
|
||||
conditia era deja falsa inainte de garda si ramane falsa (garda adaugata doar restrange in plus
|
||||
cazul `in_valuta=0`, nu schimba nimic pe ramura `curs` NULL). Neschimbat.
|
||||
|
||||
Confirmat: doar cazul 1 (facturi in lei cu `curs` efectiv nenul) isi schimba comportamentul, exact
|
||||
riscul identificat.
|
||||
|
||||
### Verificare encoding (byte-level)
|
||||
|
||||
Fisierul contine octeti cp1252 `>=0x80` (`0xEE` = "î", la liniile 1203 si 1330 — "puteti încarca
|
||||
prin SPV"), deci risc de corupere la scriere cu tool care nu pastreaza octetii nativ. Editare
|
||||
facuta cu `perl` in mod raw (`s/.../.../ `), nu cu Edit/Write:
|
||||
- Backup pre-editare: `xmlefactura.prg.pre_runda.bak` (md5 `e800c71bd08e6c472b6fe912437f37f8`,
|
||||
62093 octeti).
|
||||
- Dupa editare: md5 `c9c9f4774346b2e62bfe9c12ea02dfb3`, 62118 octeti (+25 = exact lungimea
|
||||
textului `loDate.in_valuta = 1 AND ` inserat).
|
||||
- Comparatie linie-cu-linie (raw bytes) intre backup si fisierul editat: **1364 linii in ambele,
|
||||
o singura linie diferita (374)** — restul fisierului byte-identic.
|
||||
- Octetii `0xEE` de la liniile 1203/1330 confirmati neschimbati dupa editare.
|
||||
- Scanare pentru markeri de corupere (`EF BF BD` = U+FFFD reincodat): zero potriviri.
|
||||
|
||||
Encoding intact, nicio corupere.
|
||||
|
||||
### Copii identice/asemanatoare in suita — corectie fata de raportul initial
|
||||
|
||||
Raportul initial (sectiunea RISC REAL #2) afirma ca fisierul e "identic prin hash" in ROAEFACTURA,
|
||||
ROASITFIN, ROACONIMPORT, ROAPRINT, ROAPRODUCTIE si ROACONT/OUTPUT — verificare hash directa acum
|
||||
(md5 pe tot arborele `D:\ROA`, exclus `DATABASE`) arata ca afirmatia era **partial gresita**: doar
|
||||
`ROACONIMPORT` si `ROAPRINT` sunt byte-identice intre ele; restul au fiecare continut propriu,
|
||||
divergent de `ROAFACTURARE`. Ce e adevarat si ramane valabil: **toate contin acelasi tipar de cod
|
||||
vulnerabil** (linia cu conditia fara garda pe `in_valuta`), verificat cu grep, o singura aparitie
|
||||
in fiecare fisier.
|
||||
|
||||
**Grup A — fisier byte-identic cu `ROAFACTURARE/COMUN` INAINTE de editarea de azi**
|
||||
(md5 `e800c71bd08e6c472b6fe912437f37f8`, deci acelasi patch de o linie de la `:374` se aplica
|
||||
identic, byte cu byte, in toate):
|
||||
`ROAACNPRO`, `ROAAUTO`, `ROACONT`, `ROACONTRACTE`, `ROADEF`, `ROAGEST`, `ROAIMOB`,
|
||||
`ROAREGISTRATURA`, `ROARES`, `ROASTART` — cale `D:\ROA\<PRODUS>\COMUN\programe\xmlefactura.prg`.
|
||||
(In plus, acelasi hash apare si in foldere de backup istoric fara relevanta:
|
||||
`_backup_comun_conflicts\*`, `_backup_roaimob_comun_conflict\*` — nu sunt copii vii.)
|
||||
|
||||
**Grup B — fisier divergent (alt continut/alte linii in rest), dar cu ACELASI tipar vulnerabil**
|
||||
(conditie identica textual, gasita o singura data per fisier, doar la alt numar de linie):
|
||||
| Produs | Cale | md5 | Linia conditiei |
|
||||
|---|---|---|---|
|
||||
| OUTPUT/ROACONT | `D:\ROA\OUTPUT\ROACONT\COMUN\programe\xmlefactura.prg` | `504dc24e771f6d66e4f028d1347671a8` | 350 |
|
||||
| ROACONIMPORT | `D:\ROA\ROACONIMPORT\COMUN\programe\xmlefactura.prg` | `fa1e389839109ae46da1ef815e102259` | 356 |
|
||||
| ROAEFACTURA | `D:\ROA\ROAEFACTURA\COMUN\programe\xmlefactura.prg` | `200442ad86e180c2484c96367ac9514a` | 356 |
|
||||
| ROAPRINT | `D:\ROA\ROAPRINT\COMUN\programe\xmlefactura.prg` | `fa1e389839109ae46da1ef815e102259` | 356 |
|
||||
| ROAPRODUCTIE | `D:\ROA\ROAPRODUCTIE\COMUN\programe\xmlefactura.prg` | `b71c1d9284d8ee78d02429b04c968a35` | 326 |
|
||||
| ROASITFIN | `D:\ROA\ROASITFIN\COMUN\programe\xmlefactura.prg` | `47bb2e49770713b396e855e8af0ae2ea` | 356 |
|
||||
|
||||
**Grup C — NU are acest bloc de cod deloc** (verificat, nu doar negasit — nu construiesc nota de
|
||||
curs valutar in e-factura): `ROADECL`, `ROAMANAGER`, `ROAPRETURI`, `ROASAL`. Fara risc, fara
|
||||
propagare necesara.
|
||||
|
||||
**Fisierul modificat azi**: doar `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg` (md5 nou
|
||||
`c9c9f4774346b2e62bfe9c12ea02dfb3`). Toate celelalte 16 cai listate mai sus (Grup A + Grup B)
|
||||
raman NEATINSE, cu bug-ul inca prezent — propagarea e decizie de proces a lui Marius, nu s-a facut
|
||||
aici.
|
||||
|
||||
### Livrabile
|
||||
|
||||
- Modificare: `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg` (linia 374, +garda `in_valuta`).
|
||||
- Patch de review: `D:\ROA\ROAFACTURARE\docs\diff_xmlefactura_garda_valuta.patch`.
|
||||
- Backup pre-editare (ramas pe disc, netracked): `xmlefactura.prg.pre_runda.bak` in acelasi folder.
|
||||
- Aceasta sectiune.
|
||||
|
||||
Fara commit git/svn — asteapta aprobarea patch-ului.
|
||||
358
docs/cercetare/rec_integrari.md
Normal file
358
docs/cercetare/rec_integrari.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# Cercetare: integrare CONTRACTE, politici de preturi, nomenclator ca lista de preturi
|
||||
|
||||
Metoda: `vfp_symbols.ps1` (cache text ROAFACTURARE deja la zi) + Grep pe `.prg`/`.vc2`/`.mn2`.
|
||||
Fapte cu `fisier:linie`; ipoteze marcate `IPOTEZA:`.
|
||||
|
||||
## SUBIECT A - Integrare pagina CONTRACTE (todo #10)
|
||||
|
||||
### 1. Unde exista azi contractele
|
||||
|
||||
Produs separat, working copy completa: `D:\ROA\ROACONTRACTE` (`.git` + `.svn`, `roaContracte.pjx`,
|
||||
`roacontracte.exe`). Structura: `Clase\` (`ofundal.vcx`, `onom_clienti.vcx`, `oOptiuni.vcx`,
|
||||
`roaclienti.vcx`, `ferestre_contracte.vcx` - probabil formularele CRUD de contracte),
|
||||
`Ferestre\`, `Programe\`, `Rapoarte\`, `Meniuri\`, `COMUN\` (propria copie a librariei partajate),
|
||||
`Teste\`, `docs\`.
|
||||
|
||||
**Important**: ROAFACTURARE NU e izolat de contracte azi - are deja o integrare de facturare
|
||||
partiala "pe baza de contract" (vezi punctul 4), care citeste direct din schema Oracle a
|
||||
ROACONTRACTE prin view-uri (`vcontracte`, `fact_vcontracte`, `tipuri_contracte`), fara pagina de
|
||||
editare. `ferestre_contracte.vcx` din ROACONTRACTE contine probabil formularele CRUD care ar
|
||||
trebui aduse in ROAFACTURARE (nu au fost deschise/citite - binar, necesita conversie separata daca
|
||||
se trece la implementare).
|
||||
|
||||
Exista deja urme ale unei integrari partiale de UI in ROAFACTURARE:
|
||||
- `Meniuri\contracte.mnx`/`.mn2` (`D:\ROA\ROAFACTURARE\Meniuri\contracte.mn2:1`) - NU e un meniu de
|
||||
administrare contracte, ci un shortcut-popup cu 3 optiuni de facturare ("Factura fiscala lei /
|
||||
Invoice / Factura fiscala valuta") - cf. continut citit integral.
|
||||
- `Grafice\icon_contracte1.png`, `icon_contracte2.png`, `Grafice\Originale\contracte.png` -
|
||||
iconite deja pregatite in ROAFACTURARE.
|
||||
|
||||
### 2. Cum e integrata azi COMENZI in ROAFACTURARE (sablonul de urmat)
|
||||
|
||||
COMENZI **nu** e produs separat legat prin exe, ci cod montat direct in ROAFACTURARE din
|
||||
`COMUN\` (librarie partajata `gitea.romfast.ro:romfast/comun.git`, cf. CLAUDE.md). Exista si un
|
||||
produs stand-alone `D:\ROA\ROACOMENZI` (cu `.pjx` propriu), dar in ROAFACTURARE comenzile sunt
|
||||
o **pagina/panou montat direct in formularul principal (fundal)**, nu un exe separat lansat.
|
||||
|
||||
Reteta pas cu pas (comenzi ca model pentru contracte):
|
||||
|
||||
1. **Clasa container** `ct_comenzi` din `COMUN\clase\ocomenzi.vcx` (`.vc2` cache:
|
||||
`COMUN\clase\ocomenzi.vc2`) - contine formulare/containere CRUD comenzi
|
||||
(`frm_optiuni_comenzi`, cursoare `vcomenzi_elemente` etc.).
|
||||
2. **Montare in formularul principal**: containerul e plasat ca obiect copil in
|
||||
`Clase\ofundal_facturare.vc2` (form fundal), cu comentariul `< END OBJECT:
|
||||
ClassLib="..\comun\clase\ocomenzi.vcx" BaseClass="container" />` in jurul liniei
|
||||
`Clase\ofundal_facturare.vc2:831`; obiectul se numeste `lb_comenzi`
|
||||
(`Clase\ofundal_facturare.vc2:824`).
|
||||
3. **Butoane de actiune** ("Cw" = clase de tip buton-cu-drept, vezi punctul 3) legate la proceduri
|
||||
business, ex. `Page2.Cw3.do_actiune` -> `DO facturare_comenzi IN oproceduri_facturare.prg`
|
||||
(`Clase\ofundal_facturare.vc2:899-902`).
|
||||
4. **Inregistrare in `Programe\roafacturare.prg`** (entry point):
|
||||
- `SET CLASSLIB TO ocomenzi ADDITIVE` sub comentariul `*** COMENZI`
|
||||
(`Programe\roafacturare.prg:180-181`);
|
||||
- `SET PROCEDURE TO orap_comenzi.prg / onom_comenzi.prg / update_comenzi.prg ADDITIVE`
|
||||
(`Programe\roafacturare.prg:239-242`), tot sub `*** COMENZI`;
|
||||
- variabile module: `PRIVATE pocomenzi,pocomenzielemente,polucrari,pocomenzi2,polucrarielemente`
|
||||
(`Programe\roafacturare.prg:246-247`).
|
||||
- Toate cele 3 `.prg` (`orap_comenzi.prg`, `onom_comenzi.prg`, `update_comenzi.prg`) si clasa
|
||||
`ocomenzi.vcx`/`.vct` locuiesc fizic in `COMUN\programe\` / `COMUN\clase\`, dar sunt
|
||||
inregistrate ca membri ai proiectului `roafacturare.pjx` (confirmat prin Grep pe `.pjx`:
|
||||
`COMUN\clase\ocomenzi.vcx`, `COMUN\programe\orap_comenzi.prg` etc. apar in el).
|
||||
5. **Business logic de facturare din comenzi**: `Procedure facturare_comenzi` in
|
||||
`COMUN\programe\oproceduri_facturare.prg:139-141` - un simplu `factureaza(3)` (tip document 3
|
||||
= "din comanda", motorul central `factureaza()` face restul).
|
||||
6. **Meniu**: nu exista `Meniuri\comenzi.mnx` separat in ROAFACTURARE - comenzile nu au intrare de
|
||||
meniu proprie, ci doar butonul `Cw3` de pe pagina fundal (Page2 = "Facturare"). Contractele au
|
||||
deja `Meniuri\contracte.mnx` dar cu alt continut (shortcut factura), deci pentru pagina noua de
|
||||
contracte ar trebui fie extins acest fisier, fie creat altul.
|
||||
|
||||
Concluzie sablon: pentru CONTRACTE ar insemna (a) o clasa container tip `ct_contracte` (posibil
|
||||
adaptata din `ferestre_contracte.vcx` al ROACONTRACTE, mutata/duplicata in `COMUN\clase\`), (b)
|
||||
montarea ei ca obiect in `ofundal_facturare.vc2` langa `lb_comenzi`, (c) inregistrare `SET
|
||||
CLASSLIB`/`SET PROCEDURE` in `roafacturare.prg` sub un bloc nou `*** CONTRACTE`, (d) adaugare in
|
||||
`roafacturare.pjx`.
|
||||
|
||||
### 3. Mecanismul de DREPTURI pe obiecte
|
||||
|
||||
Sursa: `COMUN\programe\acces_meniu.prg` (fisier citit integral).
|
||||
|
||||
- **Sursa de date**: view Oracle `contafin_oracle.vdef_util_obiecte`, interogat cu
|
||||
`select cheie,id_firma from contafin_oracle.vdef_util_obiecte where id_util=?gnIdUtil and
|
||||
id_program=?gnIdProgram and id_firma=?gnIdFirma` (`acces_meniu.prg:29-31`), rezultat in cursorul
|
||||
`crsdrepturi` (o singura coloana cheie relevanta: `cheie`, string).
|
||||
- **Codificarea cheii**: concatenare de "caractere de nivel" - `Chr(lnKey)` pentru fiecare nivel de
|
||||
pageframe/pagina (`dezactiveaza_obiecte_pageframe`, `acces_meniu.prg:103-165`, recursiv pe
|
||||
subpageframe-uri), plus un cod de 2 cifre pentru fiecare buton `Cw*`:
|
||||
`lcCheie = lcKey + Padl(Alltrim(Str(.Objects(l).nid_cw)), 2, '0')` (`acces_meniu.prg:138`).
|
||||
Fiecare obiect `Cw*` are proprietatea `nid_cw` (numarul lui in cadrul paginii) si la runtime i se
|
||||
seteaza `ccheie` (`acces_meniu.prg:140`) si `coptiuni_active` (lista de operatii CRUD permise,
|
||||
citita din caracterele urmatoare cheii - `acces_meniu.prg:141-149`). Butonul apeleaza
|
||||
`.Objects(l).activeaza()` / `.dezactiveaza()` in functie de gasire (`acces_meniu.prg:150-152`).
|
||||
- **Pentru imagini/iconite** (nivel diferit, folosit pe alte forme): proprietate `ccod` pe obiect
|
||||
(`acces_meniu.prg:48`), aceeasi logica de cautare in `crsdrepturi`.
|
||||
- **Cod de meniu (pad-uri)**: `GetAccesByCod(tcCod, tcAccesDefault)` (`acces_meniu.prg:236-276`)
|
||||
cauta o cheie explicita (cod optiune meniu, ex. "ZA01") in `crsdrepturi` si intoarce lista de
|
||||
operatii permise (ex. "1;2;3;4").
|
||||
- **Punct de intrare**: `verifica_drepturi(tcObiectFundal, tcPageFrame)`
|
||||
(`acces_meniu.prg:9-15`) apelat din formularul fundal (`Ferestre\fundal.sc2:699`:
|
||||
`verifica_drepturi('gofundal','_pgfrmbase1')`), care incarca `crsdrepturi` o singura data per
|
||||
firma (cache in memorie, `citeste_drepturi`, `acces_meniu.prg:17-37`) si dezactiveaza in cascada
|
||||
paginile/butoanele/meniurile fara drept.
|
||||
- **Administrare drepturi** (unde se declara catalogul de obiecte si se atribuie pe grupuri):
|
||||
`COMUN\clase\drept_grupuri.vc2` - `frm_grupuri`, apel catre pachetul Oracle
|
||||
`PACK_DREPTURI.grupdreptmodproc` (`COMUN\clase\drept_grupuri.vc2:39`) si `citeste_drepturi
|
||||
(loRec.id_grup)` (`COMUN\clase\drept_grupuri.vc2:205`).
|
||||
|
||||
IPOTEZA: catalogul efectiv de "obiecte disponibile pentru ROAFACTURARE" (denumirile/codurile
|
||||
`nid_cw`/`ccod`/coduri de meniu, ex. cele pentru COMENZI) e definit **partial in designerul VFP**
|
||||
(proprietatea `nid_cw` seteaza pe fiecare buton la design-time in `.scx`/`.vcx`) si **partial
|
||||
server-side** in schema Oracle `contafin_oracle` (tabelul din spatele view-ului
|
||||
`vdef_util_obiecte`, populat probabil printr-un script de instalare/migrare, nu vazut in sursa
|
||||
VFP). Pentru "comasarea" drepturilor ROACONTRACTE + ROAFACTURARE mentionata in cerere, ar trebui
|
||||
inspectat acest tabel server-side (in afara sursei VFP disponibile aici) plus alocarea de noi
|
||||
`nid_cw` pentru butoanele noi de contracte, fara sa coincida cu cele deja folosite de COMENZI/
|
||||
lista de preturi/avize pe aceeasi pagina.
|
||||
|
||||
Exemplu concret COMENZI: butonul `Page2.Cw3` (facturare din comenzi) foloseste automat cheia
|
||||
`<cheie_pagina>+'03'` (Cw3 => `nid_cw=3`); pentru un buton nou de contracte pe aceeasi pagina ar
|
||||
trebui un `nid_cw` neutilizat (ex. 11+, dat fiind ca Page2 are deja Cw1..Cw9 conform
|
||||
`Clase\ofundal_facturare.vc2:882-926`).
|
||||
|
||||
### 4. Facturarea pe baza de comanda / pe baza de contract
|
||||
|
||||
**Comanda -> factura**: `Procedure facturare_comenzi` (`COMUN\programe\oproceduri_facturare.prg:
|
||||
139-141`) => `factureaza(3)`. Cautarea comenzii disponibile pentru facturare:
|
||||
`Function caut_comanda_gestiune` (`COMUN\programe\oproceduri_facturare.prg:1961-1983`), citeste
|
||||
din view-ul `vcomenzi` (`... FROM ] + gcS + [.vcomenzi`), filtru
|
||||
`facturat = 0 and interna = 3 ... ` (linia 1977).
|
||||
|
||||
**"Pe baza de contract" EXISTA DEJA**, mai complet decat comenzile pe alocuri:
|
||||
- `Procedure facturare_contracte(tcTip)` (`COMUN\programe\oproceduri_facturare.prg:119-136`) -
|
||||
primeste tipul de document ("FACTURA LEI"/"INVOICE"/"FACTURA VALUTA") si apeleaza
|
||||
`factureaza(2)`/`factureaza(6)`/`factureaza(52)`.
|
||||
- Buton pe pagina fundal: `Page2.Cw2.do_actiune` (`Clase\ofundal_facturare.vc2:886-897`) - meniu
|
||||
`xmenu` cu cele 3 optiuni, cheama `facturare_contracte`.
|
||||
- **Cautare contract**: `Function caut_contract_facturare(tnIdPart, tcSirTipFacturare)`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:1986-2021`) - citeste din view-ul `fact_vcontracte`
|
||||
(`select id_ctr, contract, numar, data, denumire, scadenta_incasare, opt_facturare,
|
||||
text_standard, afisare_scadenta FROM fact_vcontracte`, linia 2001), filtrat pe
|
||||
`opt_facturare in (...)` si `id_part`.
|
||||
- **Alegerea contractului la factura**: `frm_date_factura.do_cauta_contract`
|
||||
(`COMUN\clase\ofacturare.vc2:9067-9115`) si `frm_date_aviz.do_cauta_contract`
|
||||
(`COMUN\clase\ofacturare.vc2:7049-7051`) apeleaza `caut_contract_facturare`.
|
||||
- **Editorul de articole pe factura** are un tab/grid dedicat contractelor:
|
||||
`frm_facturare_articole` cu controale `grd_contracte`, `cb_contracte` (combobox cu ratele /
|
||||
contractele), populate din cursorul `crscontracte` (`COMUN\clase\ofacturare.vc2:15069-15107` si
|
||||
in jur). Optiunea `opt_facturare` din `fact_vcontracte`/`crsfactura` marcheaza randurile "din
|
||||
contract" (`COMUN\programe\oproceduri_facturare.prg:176-177`, `Inlist(opt_facturare,1,2)` in alt
|
||||
context legat de seturi).
|
||||
- **Aviz pe baza de contract**: exista si un tip de aviz "26 - catre clienti din contract"
|
||||
(`COMUN\programe\oproceduri_facturare.prg:207`, enumerat si in `caut_avize`,
|
||||
`COMUN\programe\oproceduri_facturare.prg:2045`), apelat din `emitere_aviz_clienti(tnTip=3)`.
|
||||
|
||||
Concluzie: **motorul de facturare din contract e deja complet functional** in ROAFACTURARE (citire
|
||||
din schema ROACONTRACTE prin view-uri Oracle `vcontracte`/`fact_vcontracte`/`tipuri_contracte`).
|
||||
Ce lipseste conform cererii e (a) o **pagina de editare CRUD a contractelor** in ROAFACTURARE
|
||||
(azi doar in exe-ul separat ROACONTRACTE) si (b) **rapoarte de contracte** in ROAFACTURARE, plus
|
||||
(c) unificarea drepturilor. Nu a fost gasit niciun raport de contracte in
|
||||
`ROAFACTURARE\Rapoarte\` (glob `*contract*` nu a dat `.frx` in Rapoarte, doar meniu/iconite).
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT B - Politici de preturi (todo #11)
|
||||
|
||||
### 5. Unde sunt azi definite/editate
|
||||
|
||||
Produs separat, mic, dedicat: `D:\ROA\ROAPRETURI` (`.pjx` propriu, `roapreturi.exe`). Structura:
|
||||
`Programe\onom_preturi.prg`, `Programe\update_preturi.prg`, `Programe\update_nomenclator.prg`,
|
||||
`Clase\opreturi.vcx`, `Clase\onom_preturi.vcx`, `Clase\ofundal_preturi.vcx`,
|
||||
`Clase\ofundal_roapreturi.vcx`, `Ferestre\fundal.scx`. (Continutul acestor clase nu a fost convertit
|
||||
in text - ROAPRETURI nu are un cache text propriu generat in aceasta sesiune; doar structura de
|
||||
fisiere a fost inspectata.)
|
||||
|
||||
Interfata de editare pare sa fie un produs desktop de sine statator, distinct de ROAFACTURARE si de
|
||||
ROACONT, focalizat strict pe politici/liste de preturi si pe actualizarea nomenclatorului
|
||||
(`update_nomenclator.prg` sugereaza ca ROAPRETURI scrie si in nomenclatorul comun de articole).
|
||||
|
||||
### 6. Tabele/view-uri implicate (identificate din ROAFACTURARE)
|
||||
|
||||
Din codul ROAFACTURARE care CITESTE politici de preturi (nu editeaza), gasite:
|
||||
- `vcrm_politici_preturi` - view folosit in cautare dupa drepturi utilizator
|
||||
(`COMUN\clase\baza.vc2:10087`, `:10157`, `:10502-10512`). Interogare efectiva:
|
||||
`select nume_lista_preturi, id_pol from crm_vpolpretcurutil` (`COMUN\clase\baza.vc2:10504`) -
|
||||
deci exista si view-ul `crm_vpolpretcurutil` ("politica de pret curenta pentru utilizator"),
|
||||
cheie `id_pol`.
|
||||
- `vvanzari_detalii` - contine coloana `nume_lista_preturi` folosita in rapoarte de marfa
|
||||
(`COMUN\clase\configurare.vc2:3915-3972`, `frm_raport_marfa`).
|
||||
- Meniu dedicat facturarii pe lista de preturi: `Meniuri\politica.mnx`/`.mn2`/`.MPR` in
|
||||
ROAFACTURARE; procedura `facturare_lista_de_preturi` = `Do politica.mpr`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:113-116`), buton `Page2.Cw1.do_actiune`
|
||||
(`Clase\ofundal_facturare.vc2:882-884`).
|
||||
- Prefixul `crm_`/`CRM` in numele tabelelor/view-urilor (`vcrm_politici_preturi`,
|
||||
`crm_vpolpretcurutil`) sugereaza schema/modul Oracle numit "CRM", separat de schema principala
|
||||
de facturare (`gcS`). IPOTEZA: politicile de pret sunt un modul Oracle transversal (folosit si
|
||||
de ROAGEST, ROAPRETURI, ROACONTRACTE), nu proprietatea exclusiva a unui singur produs VFP.
|
||||
- Comenzile (ROACOMENZI/`ocomenzi.vcx`) au propriul mecanism de asociere pret-din-comanda:
|
||||
globale `gnIdPoliticaPret`, `gnId_lista_preturi_PV` (`Programe\roafacturare.prg:467,469`),
|
||||
folosite si in `frm_optiuni_comenzi` (`COMUN\clase\ocomenzi.vc2:6488-6686`, variabila
|
||||
`gnID_LISTA_PRETURI_PV` = politica de pret "de productie" folosita la generarea automata a
|
||||
comenzilor). Cursorul de articole al comenzii are coloanele `id_pol`, `nume_lista_preturi`,
|
||||
`pret`, `pret_cu_tva`, `ptva` direct in el (`COMUN\clase\ocomenzi.vc2:1227-1229`, cursor creat
|
||||
din view-ul `vcomenzi_elemente`).
|
||||
|
||||
Nu a fost gasita nicio schema DBF/DDL explicita pentru "politici de preturi" / "liste de preturi"
|
||||
in sursa VFP (tabelele reale sunt Oracle, definite server-side; VFP le vede doar prin view-uri
|
||||
enumerate mai sus). N-a fost identificat un tabel separat de "note contabile asociate politicii de
|
||||
pret" in codul cercetat - contul contabil de vanzare pare sa vina din nomenclatorul de articole
|
||||
(`nom_articole.cont`, vezi punctul 10), nu dintr-o tabela separata legata de politica.
|
||||
|
||||
### 7. Ce foloseste ROAFACTURARE azi din aceste date
|
||||
|
||||
- **Facturare pe lista de preturi** (`Do politica.mpr`) - flux complet de vanzare pe baza unei
|
||||
politici de pret selectate (analog cu vanzarea din stoc/comenzi/contract), tip document
|
||||
distinct in motorul central `factureaza()`.
|
||||
- **Cautare/afisare politica dupa drepturi utilizator** (`COMUN\clase\baza.vc2:10502-10512`) -
|
||||
ROAFACTURARE citeste `crm_vpolpretcurutil` pentru a limita politicile vizibile la cele pe care
|
||||
utilizatorul are drept (alt strat de drepturi, distinct de `acces_meniu.prg` - specific pe
|
||||
politici de pret, posibil gestionat tot server-side prin pachetul `PACK_DREPTURI`).
|
||||
- **Rapoarte de vanzari pe lista de preturi** (`frm_raport_marfa`,
|
||||
`COMUN\clase\configurare.vc2:3915-3972`) - grupare/însumare pe `nume_lista_preturi`.
|
||||
- Nu editeaza politici/liste - doar le CITESTE si le foloseste ca sursa de pret la facturare/
|
||||
raportare. Editarea (adaugare politica, adaugare articole in politica, preturi) ramane in
|
||||
ROAPRETURI.
|
||||
|
||||
Concluzie pentru migrare: ce ar trebui **mutat efectiv in ROAFACTURARE** (conform cererii - "se
|
||||
folosesc numai in programul ROAFACTURARE") e interfata de editare (`opreturi.vcx`/
|
||||
`onom_preturi.vcx` din ROAPRETURI), nu structura de date (Oracle, deja partajata/citita corect).
|
||||
Rapoartele si drepturile pe liste de preturi trebuie de asemenea aduse ca pagina/panou in
|
||||
ROAFACTURARE, dupa acelasi sablon COMENZI descris la punctul 2.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT C - Nomenclatorul de articole ca lista de preturi virtuala (todo #12)
|
||||
|
||||
### 8. Structura tabelei de nomenclator
|
||||
|
||||
Tabela Oracle **`catalog_articole`**, expusa prin view-ul **`vnom_articole`** (si `vnom_articole2`
|
||||
pentru un al doilea tip - vezi `nom_articole2_nou`, `COMUN\programe\onomenclatoare.prg:1376-1396`,
|
||||
tabela `catalog_articole2`).
|
||||
|
||||
Coloane identificate din interogari/scatter (nu e o lista exhaustiva - vin din SELECT-uri
|
||||
punctuale, nu din DDL):
|
||||
`id_articol, denumire, codmat, codmatf (cod furnizor), codbare, um, grupa, subgrupa, id_grupa,
|
||||
id_subgrupa, dnf, cont (cont contabil, 3-4 caractere), acont, inactiv, sters, in_stoc, in_crm,
|
||||
tip (ex. 1 = manopera pt. ROAACNPRO), id_part/partener (pt. articole legate de furnizor/client)`.
|
||||
Surse: `COMUN\clase\ocriterii.vc2:1531` (`select denumire, codmat, um, grupa, subgrupa, id_grupa,
|
||||
id_subgrupa, dnf, cont, acont, inactiv, id_articol from vnom_articole`),
|
||||
`COMUN\programe\onomenclatoare.prg:1345-1353` (`in_crm`, `in_stoc`, `tip`),
|
||||
`COMUN\clase\ointroduceri.vc2:9631` (`in_stoc, cont`).
|
||||
|
||||
**Nicio coloana de pret** nu a fost gasita direct pe `nom_articole`/`catalog_articole` (grep
|
||||
`pret_v|pretv|pret_lista` in `onomenclatoare.prg` = fara rezultate; scatter-ul din
|
||||
`nom_articole_nou` nu populeaza niciun camp de pret). Confirma punctul 11 mai jos.
|
||||
|
||||
**Formular de editare**: `frm_catalog_articole` (grid/cautare) si `frm_catalog_articole_nou`
|
||||
(fisa), ambele in `COMUN\clase\onom_articole.vc2` (`:531-599`, `:1655-1733`), salvare prin
|
||||
`cus_odata_catalog_articole.salvare` si `Adauga_Modifica_Inregistrare('catalog_articole', ...)`
|
||||
(`COMUN\programe\onomenclatoare.prg:1367,1443`). Deschidere din meniu:
|
||||
`Procedure viz_catalog_articole` (`COMUN\programe\oproceduri_articole.prg:62-122`).
|
||||
|
||||
**Important pentru subiectul B/C**: `nom_articole_nou` (`COMUN\programe\onomenclatoare.prg:
|
||||
1343-1353`) marcheaza acelasi articol cu `in_crm = 1` cand programul curent e ROAPRETURI sau
|
||||
ROACONTRACTE, respectiv `in_stoc = 1` in rest (inclusiv ROAFACTURARE) - **nomenclatorul de
|
||||
articole e deja UNIC/PARTAJAT** intre ROAFACTURARE, ROAPRETURI, ROACONTRACTE si ROAACNPRO (aceeasi
|
||||
tabela `catalog_articole`), flagurile `in_stoc`/`in_crm`/`tip` fiind doar clasificari de
|
||||
utilizare, nu tabele separate. Asta simplifica mult todo #12: nomenclatorul nu trebuie replicat,
|
||||
doar completat cu campuri de pret/tva/cont-vanzare si folosit direct ca sursa de pret la
|
||||
facturare.
|
||||
|
||||
### 9. Mecanismul actual de "lista de articole din stoc ca lista de preturi virtuala"
|
||||
|
||||
Confirmat: e mecanismul de **"vanzare din stoc/gestiune"**, un tip de facturare paralel cu
|
||||
"lista de preturi"/"contract"/"comanda", identificat prin variabila globala `gnTipGest`:
|
||||
- `Procedure vanzare_materii_prime` -> `gnTipGest = 2`, `Do vanzare1.mpr`
|
||||
- `Procedure vanzare_produse` -> `gnTipGest = 4`, `Do vanzare2.mpr`
|
||||
- `Procedure vanzare_marfa_pret_achi` -> `gnTipGest = 5`, `Do vanzare3.mpr` (marfa la pret de
|
||||
achizitie)
|
||||
- `Procedure vanzare_marfa_pret_vanz` -> `gnTipGest = 6`, `Do vanzare4.mpr` (marfa la pret de
|
||||
**vanzare**)
|
||||
- `Procedure vanzare_marfa_pret_achi_vanz` -> `gnTipGest = 7`, `Do vanzare5.mpr`
|
||||
(toate in `COMUN\programe\oproceduri_facturare.prg:1505-1534`; butoane `Page2.Cw5..Cw9` in
|
||||
`Clase\ofundal_facturare.vc2:908-926`).
|
||||
|
||||
Gestiunile disponibile per tip se filtreaza prin `Procedure selecteaza_gestiuni`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:1536-1565`), pe view-ul `vnom_GESTIUNI` filtrat
|
||||
`nr_pag = ?gnTipGest`, plus un al doilea nivel de drept pe gestiuni (view-urile
|
||||
`vgest_coresp_grupe_gestiuni` / `vgest_coresp_util_grupe`, linia 1552-1555) - deci "lista de
|
||||
preturi virtuala" = stocul unei gestiuni, cu control de acces pe gestiune (nu pe politica de
|
||||
pret).
|
||||
|
||||
Motorul de scriere: `Function oscrie_vanzare_din_stoc` in `COMUN\programe\ofacturare_stoc.prg:
|
||||
104-...`, apelat din `initializeaza_vanzare_din_stoc` (`ofacturare_stoc.prg:31-99`). Comentariu
|
||||
explicit in cod: *"in vanzari_detalii scriu pretul cu tva daca am marfa la pret de vanzare, daca
|
||||
nu scriu pretul de vanzare fara tva"* (`ofacturare_stoc.prg:107`), cu
|
||||
`lnPretCuTva = Iif(INLIST(gnTipGest,6,7), 1, 0)` (linia 111) - deci flagul "pret cu TVA" e
|
||||
determinat de TIPUL de vanzare din stoc ales (6/7 = la pret de vanzare), nu citit dintr-o coloana
|
||||
a nomenclatorului.
|
||||
|
||||
Cursorul-cheie `crsvanztemp` (`ofacturare_stoc.prg:137-139`) are coloanele:
|
||||
`id_articol, Pret, proc_tvav, id_jtva_coloana, cantitate, discount_unitar, id_gestiune, Cont
|
||||
(c4), pret_cu_tva, serie, id_valuta, codmat, Curs, multiplicator, pret_achizitie, pretd,
|
||||
id_valuta_d, id_rul_aux, taxcode, lot`.
|
||||
|
||||
### 10. Lantul de cod: pret, valuta, %TVA, pret_cu_tva, cont vanzare - la facturare "din stoc"
|
||||
|
||||
Din structura `crsvanztemp` (punctul 9) rezulta explicit lantul folosit azi cand se factureaza
|
||||
"virtual" din stoc (echivalentul cerut pentru nomenclator-ca-lista-de-preturi):
|
||||
- **Pret**: coloana `Pret` in `crsvanztemp`, luata din inregistrarea de gestiune/stoc (miscarea de
|
||||
intrare), nu din nomenclator - `pret_achizitie` separat pentru pretul de achizitie.
|
||||
- **Valuta**: `id_valuta`, `Curs`, plus varianta in alta valuta `pretd`/`id_valuta_d` (pret dublu,
|
||||
pentru afisare in a doua valuta).
|
||||
- **%TVA**: `proc_tvav` + `id_jtva_coloana` (coloana de defalcare TVA in jurnal) + `taxcode` (cod
|
||||
fiscal pt. integrari, ex. eFactura).
|
||||
- **Flag pret_cu_tva**: `pret_cu_tva` in cursor, calculat din `gnTipGest` (`ofacturare_stoc.prg:
|
||||
111`), NU citit dintr-o coloana persistenta a articolului.
|
||||
- **Cont vanzare (echivalent 4111=7xx)**: coloana `Cont c(4)` in `crsvanztemp` - cont contabil pe
|
||||
4 caractere (ex. "707x"/"701x"), scris explicit in cursor la nivel de linie de vanzare. Sursa lui
|
||||
cea mai probabila (nu confirmata cu linie exacta de SELECT in aceasta cercetare, cursorul e
|
||||
populat mai jos in fisier, dincolo de zona citita) e coloana `nom_articole.cont` (confirmata ca
|
||||
existenta la punctul 8) sau contul gestiunii (`nom_gestiuni`) - **IPOTEZA**: trebuie verificat
|
||||
punctual restul lui `oscrie_vanzare_din_stoc` (fisierul continua dupa linia 140, necitit
|
||||
integral in aceasta trecere) pentru sursa exacta linie-cu-linie a lui `Cont`.
|
||||
|
||||
Pentru comparatie, la facturarea pe **lista de preturi/politica** (nu pe stoc), pretul/valuta/TVA
|
||||
vin din politica de pret (view `crm_vpolpretcurutil`/`vcrm_politici_preturi`, punctul 6), deci
|
||||
lantul e diferit dupa tipul de facturare ales (`gnTipGest` vs. `id_pol`).
|
||||
|
||||
### 11. Coloane de pret existente/partial folosite in nomenclator
|
||||
|
||||
**Nu exista azi nicio coloana de pret pe `nom_articole`/`catalog_articole`** (cautare explicita
|
||||
fara rezultate). Exista insa deja doua campuri reutilizabile direct pentru scenariul din cerere:
|
||||
- `cont` (cont contabil de vanzare/achizitie, deja pe articol - vezi punctul 8) - poate fi folosit
|
||||
ca "nota contabila" fara tabel separat.
|
||||
- Flagurile `in_stoc` / `in_crm` - clasifica deja fiecare articol dupa modul de utilizare (stoc
|
||||
vs. politica de pret / CRM), un precedent direct pentru un viitor flag suplimentar de tipul
|
||||
"articol cu pret propriu in nomenclator" daca se implementeaza todo #12.
|
||||
|
||||
Nu au fost gasite coloane de tipul `pret`, `pret_vanzare`, `valuta_pret`, `ptva` direct pe
|
||||
nomenclator - toate preturile de vanzare vin azi fie din politici de pret (Oracle, schema CRM),
|
||||
fie din miscarile de gestiune/stoc (nu din articolul insusi). Implementarea todo #12
|
||||
("nomenclatorul direct ca lista de preturi, fara politica + nota contabila asociate") ar necesita
|
||||
adaugarea a cel putin: pret (+valuta), procent TVA, flag pret_cu_tva pe `catalog_articole`/
|
||||
`nom_articole` - camp nou, nu o coloana ascunsa deja existenta.
|
||||
|
||||
---
|
||||
|
||||
## Rezumat surse cheie (fisier:linie)
|
||||
|
||||
- Sablon COMENZI: `Programe\roafacturare.prg:180-181,239-247`; `Clase\ofundal_facturare.vc2:
|
||||
760-926`; `COMUN\clase\ocomenzi.vc2`.
|
||||
- Drepturi: `COMUN\programe\acces_meniu.prg` (tot fisierul, 306 linii).
|
||||
- Facturare contract: `COMUN\programe\oproceduri_facturare.prg:119-136,1986-2021`;
|
||||
`COMUN\clase\ofacturare.vc2:9067-9115,15069-15107`.
|
||||
- Politici de pret: `COMUN\clase\baza.vc2:10087,10157,10453-10512`;
|
||||
`COMUN\programe\oproceduri_facturare.prg:113-116`.
|
||||
- Vanzare din stoc (lista virtuala): `COMUN\programe\oproceduri_facturare.prg:1505-1534`;
|
||||
`COMUN\programe\ofacturare_stoc.prg:31-140`.
|
||||
- Nomenclator articole: `COMUN\programe\onomenclatoare.prg:1302-1373`;
|
||||
`COMUN\clase\onom_articole.vc2:531-599,1655-1733`.
|
||||
243
docs/cercetare/rec_pret_lazy.md
Normal file
243
docs/cercetare/rec_pret_lazy.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# Cercetare: lant pret (#12) + editare politici pret (#11/#10) + lazy loading
|
||||
|
||||
## REZUMAT (max 30 linii, focus A2 + C2)
|
||||
|
||||
**A2 — NU exista niciun fallback la nomenclator azi.** Confirmat din chiar view-ul care alimenteaza
|
||||
majoritatea ramurilor lui `cursor_preturi` (`fact_vpreturi_utilizator`, definit in
|
||||
`D:\ROA\DATABASE\SCRIPTURI\2009\4\ff_2009_04_21_03_FACTURARE.sql:11-50`): clauza
|
||||
`and d.id_pol is not null` (linia 49) e o conditie **obligatorie** pe `crm_politici_pret_art d` —
|
||||
un articol nu apare deloc in lista de vanzare daca nu are un rand in `CRM_POLITICI_PRET_ART` pentru
|
||||
politica activa a utilizatorului. `NOM_ARTICOLE` e joinat doar pentru `um/denumire/codmat/codbare/
|
||||
in_stoc` (descriptive), niciodata pentru `pret/valuta/proc_tvav`. In `PACK_FACTURARE.cursor_preturi`
|
||||
insusi (`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:2121-2627`) acelasi tipar se repeta in toate
|
||||
ramurile (2, 45, 1/2, 5/6/10/52, 7, else/aviz): driver-ul e mereu `CRM_POLITICI_PRET_ART`/politica,
|
||||
niciun `NVL`/`COALESCE` catre `catalog_articole`/`nom_articole` pentru pret. Nu exista deloc referinte
|
||||
la `catalog_articole` in tot pachetul `PACK_FACTURARE` (grep pe fisierul de 16948 linii: 0 hit-uri).
|
||||
**Concluzie: fallback-ul de #12 chiar trebuie construit de la zero.** Locul ieftin de inserat: in
|
||||
`fact_vpreturi_utilizator` si/sau direct in `cursor_preturi`, un `UNION ALL`/`LEFT JOIN` suplimentar
|
||||
care aduce articolele din `catalog_articole` **fara** rand in `CRM_POLITICI_PRET_ART` pentru
|
||||
politica curenta, cu pret/valuta/tva **NULL sau valori implicite din optiuni firma** — deoarece
|
||||
`catalog_articole` nu are nicio coloana de pret/valuta/tva (confirmat de echipa, si verificat: n-am
|
||||
gasit `PRET`/`ID_VALUTA`/`PROC_TVAV` pe `NOM_ARTICOLE`/`CATALOG_ARTICOLE` in DDL). Fallback-ul e deci
|
||||
"articolul apare, dar utilizatorul trebuie sa completeze pretul manual" — nu un pret implicit real.
|
||||
|
||||
**C2 — Exista deja un sablon de lazy loading, complet si reutilizabil**, in
|
||||
`COMUN\clase\_ct_base.vc2:278-281` (`do_activeaza_container` → `This.actualizeaza_cursoare()`), legat
|
||||
la `PageX.Activate` (`Clase\ofundal_facturare.vc2:1006-1008` pentru comenzi). Implementarea reala e
|
||||
in `COMUN\clase\ocomenzi.vc2:1088-1104`: cursorul se creeaza o singura data (guard
|
||||
`!Used('crscomenzi') Or gcS!=This.cSchema Or ...schimbare sectie`), cu un `WHERE` imposibil
|
||||
(`id_comanda = -9999999`) — deci structura se creeaza dar nu se aduc date. Datele reale vin abia la
|
||||
`do_cauta()` (`ocomenzi.vc2:1484-1510`), care trimite un `WHERE` filtrat prin `gencursor`/
|
||||
`ca_baza1.afisare()` — deci cautarea e deja server-side (Oracle), nu filtrare locala. **Acesta e
|
||||
sablonul de refolosit pentru #11/#10, exact cum indica deja `plan_10_integrare_contracte.md` S3.**
|
||||
Atentie: baza `_ct_base.actualizeaza_cursoare` (linia 231-232) e un stub gol — clasa container noua
|
||||
(`ct_preturi`/`ct_contracte`) trebuie sa suprascrie explicit `actualizeaza_cursoare`, dupa modelul
|
||||
`ocomenzi`, nu doar sa mosteneasca `_ct_base`. `ct_contracte` din ROACONTRACTE **nu** face asta azi
|
||||
(vezi C4) — deci nu e lazy, desi mosteneste acelasi `_ct_base`.
|
||||
|
||||
---
|
||||
|
||||
## A. Lantul de determinare a pretului
|
||||
|
||||
### A1. `cursor_preturi` — surse pe ramura (spec `:335-343`, body `:2121-2627`,
|
||||
`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`)
|
||||
|
||||
Toate ramurile pornesc din `pack_facturare.initializeaza_facturare` + `completare_politica_stoc` +
|
||||
`verifica_cursuri_valute` (:2132-2136), apoi `CASE V_TIP`:
|
||||
|
||||
- **V_TIP=45 (restaurant)**, `:2143-2247`: subselect A = politica activa a utilizatorului
|
||||
(`utilizatori_rol_intern` → `politici_grupuri` → `crm_politici_preturi` → `crm_note_vanzari` →
|
||||
`note_contabile`, filtrat pe `datai/datas` fata de luna curenta si `id_sucursala`), apoi
|
||||
`LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL=B.ID_POL`, `LEFT JOIN NOM_ARTICOLE C`. **Pret**:
|
||||
`B.PRET`/`B.DISCOUNT_UNITAR` convertite prin curs (`F.CURS`) daca valuta politicii ≠ valuta
|
||||
nationala. **Valuta**: `B.ID_VALUTA` → `NOM_VALUTE G`. **TVA%**: `B.PROC_TVAV` (direct din
|
||||
`CRM_POLITICI_PRET_ART`, nicio referinta la `ID_JTVA_COLOANA` in acest cursor). **Flag
|
||||
pret_cu_tva**: `A.PRETURI_CU_TVA` (de pe `crm_politici_preturi`, nu per-articol). **Cont**:
|
||||
`'371' AS CONT` — **hardcodat**, nu vine din nicio tabela.
|
||||
- **V_TIP IN (1,2) (factura lei)**, `:2248-2372`: acelasi tipar (A/B/C), plus `LEFT JOIN` pe `STOC`
|
||||
pentru cantitate gestionabila (E) si pe `CURS`/`NOM_VALUTE` (F/G). Nicio coloana `CONT` in select.
|
||||
- **V_TIP IN (5,6,10,52) (valuta)** si **V_TIP=7 (credit note)**, `:2374-2524`: sursa e view-ul
|
||||
`FACT_VPRETURI_UTILIZATOR A` (nu mai construieste politica inline), plus `STOC` (C) si `CURS`/
|
||||
`NOM_VALUTE` (D/E). Filtru `A.ID_VALUTA=V_ID_VALUTA AND A.IN_VALUTA=1` (sau `A.ID_POL=
|
||||
pack_facturare.nid_politica_stoc` la tip 5/6/10/52). Nicio coloana `CONT`.
|
||||
- **ELSE (aviz)**, `:2526-2624`: tot din `FACT_VPRETURI_UTILIZATOR A`, fara filtru pe valuta. Nicio
|
||||
coloana `CONT`.
|
||||
|
||||
**Definitia `FACT_VPRETURI_UTILIZATOR`** (cea mai recenta gasita, `D:\ROA\DATABASE\SCRIPTURI\2009\4\
|
||||
ff_2009_04_21_03_FACTURARE.sql:11-50`; nu a mai fost modificata in `SCRIPTURI_CLAR`, deci pare
|
||||
stabila de atunci):
|
||||
```
|
||||
utilizatori_rol_intern a
|
||||
left join politici_grupuri b on a.id_grup=b.id_grup
|
||||
left join crm_politici_preturi c on b.id_politica=c.id_pol
|
||||
left join crm_politici_pret_art d on b.id_politica=d.id_pol
|
||||
left join nom_articole e on d.id_articol=e.id_articol -- doar um/denumire/codmat/codbare/in_stoc
|
||||
left join crm_note_vanzari f on c.id_nota=f.id_nota
|
||||
left join note_contabile g on f.id_set=g.id_set
|
||||
where ... and d.id_pol is not null -- <- gate-ul, vezi A2
|
||||
```
|
||||
Coloane expuse: `id_pol, preturi_cu_tva, nume_lista_preturi, id_articol, pret, discount_unitar,
|
||||
proc_tvav, um, denumire, codmat, codbare, gestionabil, id_valuta, in_valuta, nota_discount`. **Nicio
|
||||
coloana `cont`.**
|
||||
|
||||
**`ID_JTVA_COLOANA`**: nu apare deloc in `cursor_preturi`. Grep pe tot pachetul arata ca se
|
||||
foloseste doar mai tarziu, la scriere/postare (`scrie_in_vanzari`, `descarca_gestiune`,
|
||||
`contabilizeaza_tva`, in jur de liniile 3700-4130, 4660-5400, 12250-13700 din pachet) — deci cota de
|
||||
TVA "oficiala" (coloana `JTVA_COLOANE`) se rezolva separat de `PROC_TVAV`-ul afisat la selectarea
|
||||
pretului, probabil prin cautare dupa procent in `citeste_id_jtva_coloana`-tip logica (nu am urmarit
|
||||
in detaliu — in afara bugetului A, marcat ca zona neexplorata).
|
||||
|
||||
**Cont de vanzare, de fapt**: parametrul `V_CONT` din `PACK_FACTURARE.adauga_articol_factura`
|
||||
(`:4972-4998`) e trimis de VFP la adaugarea liniei (valoare `'XXXX'` = "fara override" -> `V_CONT2`
|
||||
ramane NULL). Nu am gasit el sa fie citit din `CRM_POLITICI_PRET_ART`/`NOM_ARTICOLE` in acest flux;
|
||||
in schimb exista deja `initializeaza_date_gestiune(V_ID_GESTIUNE, V_ID_TIPGEST, V_CONT, V_ACONT)`
|
||||
(spec `:311-314`) care leaga contul de **gestiune**, nu de articol/politica. Separat, pachetul
|
||||
`PACK_PRETURI` (editorul din ROAPRETURI) scrie `NOM_ARTICOLE.CONT` direct la
|
||||
`adauga_articol`/`modifica_articol` (`D:\ROA\DATABASE\SCRIPTURI\2008\7\ff_2008_07_31_01_PRETURI.sql
|
||||
:107-130, 300-330, 386-424`) — deci **contul de vanzare al articolului exista deja pe
|
||||
`NOM_ARTICOLE.CONT`** (= `catalog_articole.cont` din nomenclatura curenta), independent de orice
|
||||
lista de preturi. Asta inseamna ca, pentru #12, contul NU are nevoie de fallback nou — poate fi citit
|
||||
direct din nomenclator, exact ce cere planul (ramane de confirmat unde/cum VFP populeaza azi `V_CONT`
|
||||
la trimiterea catre `adauga_articol_factura`, cautare separata, in afara bugetului acestei runde).
|
||||
|
||||
### A2. Fallback existent — **NU exista**. Detaliat mai sus (rezumat) si in A1
|
||||
(`d.id_pol is not null`, zero hit-uri `catalog_articole` in `PACK_FACTURARE`). Singurul loc unde
|
||||
`NOM_ARTICOLE`/nomenclatorul intra in calculul de pret e ca sursa a campurilor descriptive
|
||||
(um/denumire/codmat/codbare/in_stoc), niciodata pentru pret/valuta/tva/cont.
|
||||
|
||||
### A3. `citeste_setari_pol_pret` (`:2025-2065`)
|
||||
Nu seteaza `pret_cu_tva`/TVA. Citeste doar **care politica e implicita** pentru un `V_ID_UTIL`, in
|
||||
functie de `V_TIP`: `OPTIUNI.VARNAME` = `'ID_POL_PRET_TR'` (tip 23/30/41), `'ID_POL_PRET_STOC'`
|
||||
(tip 1), `'IDPOLPRETFACTK'` (tip 48/49), fiecare cu `LEFT JOIN CRM_VPOLPRETCURUTIL B ON ... AND
|
||||
B.ID_UTIL=V_ID_UTIL`. Deci `pret_cu_tva`/TVA vin exclusiv din randurile `crm_politici_preturi`/
|
||||
`crm_politici_pret_art` selectate ulterior de `cursor_preturi`, nu din aceasta procedura.
|
||||
|
||||
### A4. Tabelele politicii de pret (coloane confirmate din uz + din
|
||||
`ff_2008_07_31_01_PRETURI.sql:10-95`, ADD/FK, si `create or replace view vcrm_politici_preturi`):
|
||||
|
||||
- **`CRM_POLITICI_PRETURI`**: `ID_POL, NUME_LISTA_PRETURI, DATAI, DATAS, ID_VALUTA, ID_UTIL,
|
||||
DATAORA, NOTA_ONLINE, ID_NOTA, PRETURI_CU_TVA, ID_SUCURSALA, STERS`. FK: `ID_VALUTA->NOM_VALUTE`,
|
||||
`ID_NOTA->CRM_NOTE_VANZARI`, `ID_SUCURSALA->CONTAFIN_ORACLE.NOM_FIRME`.
|
||||
- **`CRM_POLITICI_PRET_ART`**: `ID_POL, ID_ARTICOL, ID_VALUTA, PRET, PRETFTVA, PRETCTVA, PROC_TVAV,
|
||||
DISCOUNT_UNITAR, STERS` (din `modificare_politica_stoc`, `:2105-2119`, si din selectii). **Nicio
|
||||
coloana CONT.**
|
||||
- Nu am gasit `CREATE TABLE` originar pentru aceste doua tabele in `SCRIPTURI_CLAR` (probabil
|
||||
create inainte de 2006, in afara arhivei "clare"); coloanele de mai sus sunt derivate din uz activ
|
||||
in cod, nu dintr-un DDL complet — de tratat ca aproape sigure, nu 100% exhaustive (pot exista
|
||||
coloane suplimentare needitate de codul cercetat).
|
||||
- View-uri asociate: `VCRM_POLITICI_PRETURI` (`ff_2008_07_31_01_PRETURI.sql:42-65`),
|
||||
`VPOLITICI_GRUPURI` (`:67-95`), `CRM_VPOLPRETCURUTIL` (folosit pretutindeni, definitia lui nu a
|
||||
fost cautata — in afara bugetului).
|
||||
|
||||
---
|
||||
|
||||
## B. Cine mai foloseste listele de preturi
|
||||
|
||||
- **ROAFACTURARE**: doar citeste. `crm_vpolpretcurutil` la `COMUN\clase\baza.vc2:10087,10157,
|
||||
10502-10512` (filtrare politici dupa dreptul utilizatorului); `facturare_lista_de_preturi` =
|
||||
`Do politica.mpr` (`COMUN\programe\oproceduri_facturare.prg:113-116`); rapoarte grupate pe
|
||||
`nume_lista_preturi` din `vvanzari_detalii` (`COMUN\clase\configurare.vc2:3915-3972`).
|
||||
(Sursa: `docs\plan_11_integrare_politici_preturi.md:15-22`, deja in `D:\ROA\ROAFACTURARE\docs\`.)
|
||||
- **ROAPRETURI** (`D:\ROA\ROAPRETURI`, exe separat `roapreturi.exe`): **editeaza** politicile —
|
||||
`Clase\opreturi.vcx`, `Clase\onom_preturi.vcx`, `Clase\ofundal_preturi.vcx`,
|
||||
`Programe\update_preturi.prg`, `Programe\update_nomenclator.prg`. Pachet Oracle `PACK_PRETURI`
|
||||
(`adauga_articol`/`modifica_articol`/`verifica_articol` — scriu si pe `NOM_ARTICOLE`, nu doar pe
|
||||
politica). **Nu are cache text (.vc2/.sc2) generat** — nu s-a putut inspecta clasa in detaliu (vezi
|
||||
C4); confirmat prin `Glob D:\ROA\ROAPRETURI\**\*.vc2` = 0 fisiere, doar `.vcx` binare.
|
||||
- **ROACONTRACTE** (`D:\ROA\ROACONTRACTE\Clase\ferestre_contracte.vc2`): citeste direct
|
||||
`vcrm_politici_preturi` / `vcrm_politici_pret_art` prin SQL Pass Through (`goExecutor.oExecute`),
|
||||
la cautarea/atasarea unui articol pe linie de contract
|
||||
(`Programe\oproceduri_roacontracte.prg:213-239`: `select id_pol,... from vcrm_politici_preturi`,
|
||||
apoi `select id_articol, id_pol_art, ... from ]+gcS+[.vcrm_politici_pret_art where id_pol=...`).
|
||||
Nu editeaza politica insasi, doar o consuma pentru a atasa articole+pret pe contract.
|
||||
- **ROAGEST**: 0 hit-uri directe pe `CRM_POLITICI_PRET*`/`cursor_preturi`/`PACK_PRETURI` in cod VFP
|
||||
(`.vc2`/`.prg`) — foloseste probabil acelasi `PACK_FACTURARE` server-side prin
|
||||
`Programe\ofactureaza.prg` (fisier gasit la cautarea `id_pol`, dar doar in comentarii vechi de
|
||||
schema cursor, nu apel activ identificat in bugetul alocat).
|
||||
- **ROAACNPRO**: la fel, 0 hit-uri directe; `id_pol` apare doar intr-un `Text To lcSchema`/
|
||||
`lcSelect` ascuns (`Omitted long matching line` — continut necunoscut, de investigat separat daca
|
||||
devine relevant).
|
||||
- **ROAIMOB**: 0 hit-uri (`.vc2`).
|
||||
- **COMUNROA**: 0 hit-uri (`.vc2`) — surprinzator pentru un modul "CRM transversal"; posibil ca
|
||||
accesul se face exclusiv server-side (proceduri Oracle), nu prin VFP direct in produsele
|
||||
verificate, sau ROAGEST/ROAACNPRO folosesc cod VFP montat din `COMUN` (acelasi `ocomenzi.vcx` etc.)
|
||||
fara sa atinga politica de pret local.
|
||||
- **Nomenclatorul comun** (`update_nomenclator.prg` in ROAPRETURI) scrie in tabela de articole
|
||||
folosita de toate produsele — punct de atentie deja notat in `plan_11...md:82-84` (S2, perimetru).
|
||||
|
||||
**Concluzie B**: politica de pret (structura de date) e deja un modul transversal minim
|
||||
(ROAFACTURARE citeste, ROAPRETURI editeaza, ROACONTRACTE citeste), consistent cu ce spune deja
|
||||
`plan_11...md`. Nu s-a gasit inca un al patrulea consumator activ in cod VFP (ROAGEST/ROAACNPRO
|
||||
probabil trec prin acelasi `PACK_FACTURARE` server-side, fara sa atinga tabelele direct din VFP).
|
||||
|
||||
---
|
||||
|
||||
## C. Incarcare lazy si cautare
|
||||
|
||||
### C1. Cum incarca azi paginile mari
|
||||
|
||||
- **`ct_comenzi`** (`COMUN\clase\ocomenzi.vc2`): montat ca `Page5.Ct_comenzi1`
|
||||
(`Clase\ofundal_facturare.vc2:604-606`). `PROCEDURE Page5.Activate` (`:1006-1008`) apeleaza
|
||||
`this.ct_comenzi1.do_activeaza_container()`. Aceasta e mostenita din `_ct_base.vc2:278-281`
|
||||
(`do_activeaza_container` → `bind keypress` + `This.actualizeaza_cursoare()`).
|
||||
`ocomenzi.actualizeaza_cursoare` (`:1088-1104`) **nu incarca date** — creeaza doar structura
|
||||
cursorului prin `creeaza_cursoare()` (`:1191-1245`), cu `lcFiltru = [id_comanda = -9999999]`
|
||||
(`:1216`) — deci 0 randuri reale la deschidere/activare de pagina.
|
||||
- **`onom_articole.vc2`**: editorul de articol individual (`PROCEDURE Init`, `:1704-1772`) e per-
|
||||
inregistrare (`toRec`), nu o grila; incarca doar cursoarele mici de grupe/subgrupe pentru combo-uri
|
||||
(`crs_grupe_art`, `crs_subgrupe_art`, `:1717-1729`). Nu s-a gasit/verificat in bugetul alocat cum
|
||||
se incarca lista/grila principala de articole a nomenclatorului (formularul-parinte, nu editorul) —
|
||||
**neacoperit**, marcat ca zona pentru o runda ulterioara daca devine relevanta pentru #12/#11.
|
||||
|
||||
### C2. Sablon de lazy loading existent — **DA**, detaliat in rezumat.
|
||||
Cheia: `_ct_base.do_activeaza_container` (hook legat de `Page.Activate`) → `actualizeaza_cursoare()`
|
||||
**suprascrisa** in clasa concreta cu un guard `!Used(cursor) Or <context s-a schimbat>` →
|
||||
`creeaza_cursoare()` cu `WHERE` fals → date reale doar la `do_cauta()`. `_ct_base` insusi ofera doar
|
||||
stub-uri goale (`:231-232` pentru `actualizeaza_cursoare`, la fel `do_adauga/do_cauta/...` la
|
||||
`:283-322`) — **fiecare container concret trebuie sa implementeze explicit lazy-load-ul**, nu vine
|
||||
gratis din mostenire.
|
||||
|
||||
### C3. Cautare server-side vs. filtrare locala
|
||||
- **Server-side (tiparul de urmat)**: `ocomenzi.do_cauta` (`:1484-1510`) construieste `lcFiltru`
|
||||
(`id_sectie = ...` + `filtru_pretty`) si il trimite prin `actualizeaza_grid1(lcFiltru)` →
|
||||
`pocomenzi.ca_baza1.cfiltru=lcFiltru` → `pocomenzi.ca_baza1.afisare()` (`:1109-1123`) — clasa
|
||||
`ca_baza1` (generata de `gencursor`) trimite `WHERE`-ul catre Oracle prin `goExecutor`, nu
|
||||
filtreaza un cursor deja incarcat integral.
|
||||
- **Local (contra-exemplu)**: `actualizeaza_grid1` in continuare (`:1119-1124`) face si operatii pur
|
||||
locale pe cursor deja adus (`SELECT * FROM crscomenzi WITH (Buffering=.T.) WHERE selectat=1 INTO
|
||||
CURSOR crstempcomenzi`) — dar asta e pentru pastrarea selectiei intre reincarcari, nu pentru
|
||||
cautare/filtrare initiala.
|
||||
|
||||
### C4. ROAPRETURI / ROACONTRACTE — incarca tot la deschidere?
|
||||
|
||||
- **ROAPRETURI**: **nu se poate stabili** — lipseste cache-ul text (`.vc2`/`.sc2`), doar `.vcx`
|
||||
binare (`Clase\opreturi.vcx`, `Clase\ofundal_preturi.vcx`); conform regulilor primite, nu am
|
||||
convertit nimic. Precondiitia e deja documentata ca blocanta in `plan_11...md:66-70` (S1: inrolare
|
||||
in fluxul text, procedura `COMUN\docs\inrolare-proiect-git-text.md`).
|
||||
- **ROACONTRACTE**: `ferestre_contracte.vc2` are cache text si e vizibil. Descoperire importanta:
|
||||
clasa container `ct_contracte` (`:9`, `DEFINE CLASS ct_contracte AS _ctfrmbase OF
|
||||
"..\comun\clase\_ct_base.vcx"`) **mosteneste acelasi `_ct_base`** ca `ct_comenzi`, si are propriul
|
||||
`filtru_pretty`/`but_start_criterii1` (vazut in `But_reset_criterii1.Click`, `:1551-1567`) — deci
|
||||
infrastructura de cautare exista. **Dar nu suprascrie `actualizeaza_cursoare`/`creeaza_cursoare`**
|
||||
(0 hit-uri in fisier) — inseamna ca foloseste stub-ul gol din `_ct_base` pentru acel hook, iar
|
||||
cursorul principal (`cContracte`, vazut deja populat la `PROCEDURE Init` a ferestrei,
|
||||
`:1518-1527`, `Select cContracte / If Reccount()>0`) se incarca **altundeva, probabil eager, la
|
||||
deschiderea formularului** — nu s-a gasit punctul exact de populare in bugetul alocat (cautare
|
||||
separata necesara: `cContracte` INTO CURSOR / gencursor pentru contracte). **Concluzie: ROACONTRACTE
|
||||
NU e azi lazy**, desi are "schela" pentru a deveni (acelasi `_ct_base`). Pentru #10/#11, sablonul
|
||||
de copiat e strict cel din `ocomenzi.vc2`, nu cel din `ferestre_contracte.vc2`.
|
||||
|
||||
---
|
||||
|
||||
## Neacoperit / de reluat intr-o runda viitoare
|
||||
- `CRM_VPOLPRETCURUTIL` — definitia view-ului (coloane exacte, drepturi).
|
||||
- `ID_JTVA_COLOANA` — cum se leaga procentul afisat in `cursor_preturi` (`PROC_TVAV`) de coloana TVA
|
||||
oficiala folosita la postare.
|
||||
- Unde/cum populeaza azi VFP parametrul `V_CONT` la `adauga_articol_factura` (ipoteza: din gestiune,
|
||||
nu din articol) — relevant pentru a confirma daca planul #12 poate refolosi direct
|
||||
`NOM_ARTICOLE.CONT` fara alta lucrare.
|
||||
- Incarcarea listei/grilei principale de articole din `onom_articole.vc2` (formularul-parinte, nu
|
||||
editorul per-inregistrare).
|
||||
- Unde exact se populeaza `cContracte` in `ferestre_contracte.vc2` (cautare punctuala, nu facuta).
|
||||
- ROAGEST/ROAACNPRO: confirmarea ca folosesc `PACK_FACTURARE` exclusiv server-side pentru preturi
|
||||
(ipoteza, nu verificata prin citirea completa a `ofactureaza.prg`/`proceduri_acnpro.prg`).
|
||||
110
docs/cercetare/rec_s10_s12.md
Normal file
110
docs/cercetare/rec_s10_s12.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# S10 + S12 — versiune_db.txt, curatare VERSIUNE, propunere changelog 2.11.13
|
||||
|
||||
## S10 — `versiune_db.txt`
|
||||
|
||||
Regula exacta, confirmata in `COMUN\docs\scripturi-migrare-db.md` ("Numerotare si versiune_db.txt"):
|
||||
in `versiune_db.txt` se trece **doar versiunea ultimului script `ff_`** (nu `co_`/`sys_`/`rf_`/`ris_`),
|
||||
pentru ca programele se conecteaza pe schema firmei, nu pe `CONTAFIN_ORACLE`.
|
||||
|
||||
Verificat pe disc, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\`: cinci scripturi `ff_2026_08_06_*`
|
||||
aplicate azi pe `MARIUSM_AUTO` — `_02` (`PACK_FACTURARE`, S4), `_03` (`PACK_FACTURARE`, S7), `_04`
|
||||
(`VANZARI_COMANDA_CONTRACT`, S6), `_05` (`FACT_VFACTURI`, S8), `_06` (`VANZARI_BACKFILL`, S5).
|
||||
Confirmat si prin interogare pe `VERSIUNE` (`order by data_script desc, seq_script desc`): ultimul
|
||||
`ff_` aplicat e `ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql`.
|
||||
|
||||
**Scris in `versiune_db.txt`: `2026_08_06_06`** (fara newline la final, pastrand conventia
|
||||
fisierului existent — verificat byte-level, 13 octeti, identic ca lungime cu vechea valoare
|
||||
`2026_08_02_01`).
|
||||
|
||||
## S10 — starea tabelei `VERSIUNE`
|
||||
|
||||
Interogata direct pe `MARIUSM_AUTO` (`docs\cercetare\s10_curata_versiune.sql`, pasul 1). Numar de
|
||||
inregistrari per script, azi:
|
||||
|
||||
| Script | Inregistrari |
|
||||
|---|---|
|
||||
| `ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql` (S4) | 2 |
|
||||
| `ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql` (S7) | 1 |
|
||||
| `ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql` (S6) | 2 |
|
||||
| `ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql` (S8) | 4 |
|
||||
| `ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql` (S5) | 5 |
|
||||
|
||||
14 randuri in total pentru cele 5 scripturi de azi, 9 in plus fata de cate 1 per script. Confirma
|
||||
tiparul semnalat: `_02`, `_05`, `_06` (si, in plus fata de ce era asteptat, `_04`) au fost aplicate
|
||||
de mai multe ori pe masura extinderii lor in cursul zilei; `_03` are deja un singur rand, nimic de
|
||||
curatat acolo.
|
||||
|
||||
**Propunere, nerulata**: `docs\cercetare\s10_curata_versiune.sql`. Pastreaza per script randul cu
|
||||
`ID_VERSIUNE` maxim (ultima aplicare = starea finala reala a scriptului), sterge restul — scoped
|
||||
strict pe cele 5 nume de script din lista de mai sus, cu `select` de verificare inainte si dupa,
|
||||
`delete` la mijloc, `commit` comentat (de dat manual). Fara impact functional indiferent daca se
|
||||
ruleaza sau nu: nici `versiune_db.txt`, nici aplicarea DDL nu depind de numarul de randuri din
|
||||
`VERSIUNE`. **Nu s-a rulat** — stergerea de istoric ramane decizie de om.
|
||||
|
||||
## S12 — propunere changelog
|
||||
|
||||
Livrat: `docs\propunere_changelog_2.11.13.txt` (neaplicat in `changelog_roafacturare.txt`).
|
||||
|
||||
```
|
||||
<!--
|
||||
06/08/2026
|
||||
ROAFACTURARE - 2.11.13
|
||||
|
||||
:eroare:
|
||||
Referinta catre aviz de pe facturi era gresita - toate facturile aratau acelasi aviz, indiferent de cel real. Acum se afiseaza avizul corect acolo unde exista, iar unde nu exista referinta, campul ramane necompletat.
|
||||
|
||||
Unele facturi mai vechi ramasesera fara total salvat la emitere si aparea fara valoare la listare sau retiparire. Totalurile lipsa au fost completate.
|
||||
|
||||
Cursul valutar afisat pe facturile emise in lei era uneori inregistrat gresit. Facturile in lei nu mai afiseaza curs valutar strain.
|
||||
|
||||
La facturile de retur-transfer numele clientului afisat in lista de facturi era uneori gresit. Acum se afiseaza clientul corect.
|
||||
|
||||
:modificare:
|
||||
S-a uniformizat textul explicativ (comanda/contract) afisat pe facturi.
|
||||
-->
|
||||
```
|
||||
|
||||
### Motivare inclusiune/excludere
|
||||
|
||||
- **Aviz, totaluri, curs valutar** (`:eroare:`) — toate trei sunt defecte confirmate ca fiind
|
||||
efectiv pe date de productie (`VENDING`), vizibile pe facturi reale (lista principala si
|
||||
retiparire), acum corectate. Se incadreaza clar la `:eroare:`.
|
||||
- **`CLIENT` pe retur-transfer** (`:eroare:`) — `fact_vfacturi`, folosit de gridul principal de
|
||||
listare, calcula gresit numele clientului pe cele 23 de facturi `tip=41`/`tip=-6`; view-ul
|
||||
corect (`fact_vfacturi2`) confirma sursa deliberata din cod. E o valoare gresita afisata in
|
||||
productie, nu doar o diferenta cosmetica intre doua liste — de aceea l-am pus la `:eroare:` si
|
||||
nu la `:modificare:`, desi in mesajul initial parea doar "etichete neuniforme".
|
||||
- **`EXPLICATIE`** (`:modificare:`) — diferenta e strict de formatare a textului
|
||||
("COMERCIAL - FACTURARE" vs "COMERCIAL-FACTURARE" etc.), nu o valoare gresita; l-am tinut separat
|
||||
si mai jos in prioritate, la `:modificare:`.
|
||||
- **Denormalizarea comanda/contract (S6) — EXCLUSA din changelog.** Harnessul de regresie da
|
||||
aceleasi cifre inainte si dupa aplicarea scriptului; nu exista nimic vizibil pentru utilizator.
|
||||
O intrare de changelog ar fi inselatoare (ar sugera o schimbare functionala inexistenta).
|
||||
- **Incasarea lipsa pe facturile din devize auto — EXCLUSA din `changelog_roafacturare.txt`.**
|
||||
Corectia e integral in cod VFP din **ROAAUTO** (`Programe\oproceduri_devize.prg`,
|
||||
`factureaza_deviz`) plus un parametru nou cu `DEFAULT` in `PACK_FACTURARE.scrie_incasari`
|
||||
(pachet Oracle comun, dar ramura noua e apelata **doar** din ROAAUTO). Recompilarea
|
||||
`roafacturare.exe` singura, fara recompilarea ROAAUTO, nu produce niciun efect vizibil pentru
|
||||
utilizatorul ROAFACTURARE — deci intrarea nu apartine acestui changelog, chiar daca facturile
|
||||
respective ar putea fi vazute si din listele ROAFACTURARE dupa ce ROAAUTO e recompilat si el.
|
||||
**Propunere de text pentru `changelog_roaauto.txt` (needitat, doar propus aici)**:
|
||||
|
||||
```
|
||||
<!--
|
||||
06/08/2026
|
||||
ROAAUTO - 2.5.5
|
||||
|
||||
:eroare:
|
||||
La facturile emise din deviz auto nu se inregistra incasarea (chitanta/bon), desi factura era platita. S-a corectat.
|
||||
-->
|
||||
```
|
||||
|
||||
## Fisiere livrate
|
||||
|
||||
- `versiune_db.txt` — actualizat la `2026_08_06_06` (editat direct, marcaj de proiect).
|
||||
- `docs\cercetare\s10_curata_versiune.sql` — propunere de curatare, **nerulata**.
|
||||
- `docs\propunere_changelog_2.11.13.txt` — propunere, **neaplicata** in `changelog_roafacturare.txt`.
|
||||
- Acest raport.
|
||||
|
||||
**Fara commit** (git/SVN). Nu s-au atins scripturile de migrare, nu s-a rulat DDL, nu s-a scris in
|
||||
`VERSIUNE`.
|
||||
780
docs/cercetare/rec_s5_proiectare_oracle.md
Normal file
780
docs/cercetare/rec_s5_proiectare_oracle.md
Normal file
@@ -0,0 +1,780 @@
|
||||
# Cercetare S5 — proiectarea partii Oracle (plan #6, editare factura emisa)
|
||||
|
||||
Data: 09.08.2026. Sursa: export PROASPAT din `MARIUSM_AUTO@ROA_CENTRAL` (`all_source`), facut in
|
||||
aceasta sesiune, in scratchpad (nu in proiect):
|
||||
|
||||
- `PACK_FACTURARE.pck` — 16160 linii
|
||||
- `PACK_CONTAFIN.pck` — 8550 linii
|
||||
|
||||
**Toate numerele de linie de mai jos sunt din ACEST export.** Raportul precedent
|
||||
(`rec_s5_oracle_vanzari.md`, 08.08.2026) declara `PACK_FACTURARE.pck = 17010` linii; offset-urile
|
||||
procedurilor coincid totusi exact (`scrie_in_vanzari` la `:13491`, `actualizeaza_vanzari` la
|
||||
`:16015`), deci diferenta e de formatare a spool-ului, nu de continut. Concluziile lui A.1-A.2, C si
|
||||
D raman valabile pe sursa de azi si nu se repeta aici.
|
||||
|
||||
**Doua corectii de fond fata de raportul precedent** (detaliate la 3 si 4):
|
||||
|
||||
1. agregarea NU citeste `VANZARI_SETURI_TEMP`, ci tabela **persistenta `VANZARI_SETURI`**
|
||||
(`:13916`) — deci liniile de set se pot reconstitui integral la editare;
|
||||
2. recomandarea „procedura noua apelata din interiorul `finalizeaza_modificare_nota`" e
|
||||
**incompatibila cu ordinea corecta de scriere** si trebuie abandonata.
|
||||
|
||||
---
|
||||
|
||||
## 1. Baza de plecare e la zi? DA (pentru `ff_`)
|
||||
|
||||
Comparatie `VERSIUNE` (4151 randuri) vs. `D:\ROA\DATABASE\SCRIPTURI_CLAR` (3030 fisiere `.sql`):
|
||||
|
||||
- **`ff_` pe disc dar neaplicate in `MARIUSM_AUTO`: 0.** Schema e la zi pe tot ce o priveste.
|
||||
- Ultimul `ff_` inregistrat: `ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE.sql`, identic cu
|
||||
`versiune_db.txt` din radacina proiectului (`2026_08_08_01`).
|
||||
- Cele 26 de scripturi din 2026 care lipsesc din `VERSIUNE` sunt **exclusiv `co_` si `sys_`** — se
|
||||
aplica pe `CONTAFIN_ORACLE`, respectiv `SYS`, nu pe schema firmei; `VERSIUNE` din `MARIUSM_AUTO`
|
||||
contine doar 3 randuri `co_`, deci absenta lor e normala, nu o restanta.
|
||||
|
||||
**Verdict: se poate construi propunerea peste sursa exportata azi.**
|
||||
|
||||
### Doua anomalii de semnalat (nu blocheaza S5)
|
||||
|
||||
- **`ff_2026_08_08_01_COMUN_VVD_TOT.sql` e inregistrat in `VERSIUNE` dar NU exista nicaieri pe
|
||||
disc** (cautat recursiv in tot `SCRIPTURI_CLAR`). Un obiect aplicat in dev fara script salvat nu
|
||||
ajunge niciodata la clienti. De verificat cu Marius daca e un script de lucru abandonat sau daca
|
||||
lipseste din SVN.
|
||||
- Acelasi `NN` (`2026_08_08_01`) e folosit de doua scripturi `ff_` (`VVANZARI_ARTICOLE` si
|
||||
`VVD_TOT`), contra regulii „NN e secventa unica pe zi".
|
||||
|
||||
---
|
||||
|
||||
## 2. Blocul de agregare din `scrie_in_vanzari` — integral
|
||||
|
||||
Procedura: `PACK_FACTURARE.pck:13491-13956`. Blocul de agregare + `UPDATE VANZARI` e
|
||||
`:13762-13954`, citat integral:
|
||||
|
||||
```
|
||||
13762 -- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
13763 begin
|
||||
13764 select DISC_TVA_VAL AS DISCOUNT_TVA,
|
||||
13765 VALOARE_ACHIZITIE,
|
||||
13766 a.suma_fara_tva_ron - a.disc_fara_tva_ron as TOTAL_FARA_TVA,
|
||||
13767 a.suma_tva_ron - a.disc_tva_ron as TOTAL_TVA,
|
||||
13768 a.suma_fara_tva_ron - a.disc_fara_tva_ron + a.suma_tva_ron -
|
||||
13769 a.disc_tva_ron as TOTAL_CU_TVA,
|
||||
13770 a.suma_fara_tva_val - a.disc_fara_tva_val as VALVAL,
|
||||
13771 a.suma_tva_val - a.disc_tva_val as TVAVAL,
|
||||
13772 a.suma_fara_tva_val - a.disc_fara_tva_val + a.suma_tva_val -
|
||||
13773 a.disc_tva_val as TOTVAL,
|
||||
13774 id_valuta,
|
||||
13775 curs,
|
||||
13776 multiplicator,
|
||||
13777 pack_facturare.cserie_act_incasare as SERIE_INCASAT,
|
||||
13778 pack_facturare.nnumar_act_incasare as NR_INCASAT,
|
||||
13779 pack_facturare.nsuma_incasare AS SUMA_INCASAT,
|
||||
13780 pack_facturare.ntip_doc_incasare as TIP_INCASAT
|
||||
13781 INTO lnDiscountTVA,
|
||||
13782 lnValoareAchizitie,
|
||||
13783 lnTotalFaraTVA,
|
||||
13784 lnTotalTVA,
|
||||
13785 lnTotalCuTVA,
|
||||
13786 lnValVal,
|
||||
13787 lnTVAVal,
|
||||
13788 lnTotVal,
|
||||
13789 lnIdValuta,
|
||||
13790 lnCurs,
|
||||
13791 lnMultiplicator,
|
||||
13792 lnSerieIncasat,
|
||||
13793 lnNrIncasat,
|
||||
13794 lnSumaIncasat,
|
||||
13795 lnTipIncasat
|
||||
13796 FROM (select MAX(decode(pack_facturare.nin_valuta,
|
||||
13797 1,
|
||||
13798 ROUND(a1.curs * NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
13799 a1.multiplicator,
|
||||
13800 lnPreciziePretV),
|
||||
13801 NVL(V_DISCOUNT_FACTURA, 0))) as DISC_FARA_TVA_RON,
|
||||
13802 NVL(V_DISCOUNT_FACTURA, 0) as DISC_FARA_TVA_VAL,
|
||||
13803 pack_facturare.nin_valuta AS IN_VALUTA,
|
||||
13804 MAX(ROUND(decode(pack_facturare.nin_valuta,
|
||||
13805 1,
|
||||
13806 ROUND(a1.curs *
|
||||
13807 NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
13808 a1.multiplicator,
|
||||
13809 lnPreciziePretV),
|
||||
13810 NVL(V_DISCOUNT_FACTURA, 0)) *
|
||||
13811 (a1.proc_tvav - 1),
|
||||
13812 lnPreciziePretV)) as DISC_TVA_RON,
|
||||
13813 MAX(ROUND(NVL(V_DISCOUNT_FACTURA, 0) *
|
||||
13814 (a1.proc_tvav - 1),
|
||||
13815 lnPreciziePretV)) as DISC_TVA_VAL,
|
||||
13816 sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_ron,
|
||||
13817 0,
|
||||
13818 1,
|
||||
13819 NVL(a1.discount_unitar_ron,
|
||||
13820 0),
|
||||
13821 pack_facturare.ndiscount_evidentiat,
|
||||
13822 a1.cantitate,
|
||||
13823 a1.pret_cu_tva,
|
||||
13824 a1.proc_tvav)) AS SUMA_FARA_TVA_RON,
|
||||
13825 sum(pack_facturare.calculeaza_total_tva_fact(a1.pret_ron,
|
||||
13826 0,
|
||||
13827 1,
|
||||
13828 NVL(a1.discount_unitar_ron,
|
||||
13829 0),
|
||||
13830 pack_facturare.ndiscount_evidentiat,
|
||||
13831 a1.cantitate,
|
||||
13832 a1.pret_cu_tva,
|
||||
13833 a1.proc_tvav)) as SUMA_TVA_RON,
|
||||
13834 sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_val,
|
||||
13835 0,
|
||||
13836 1,
|
||||
13837 NVL(a1.discount_unitar_val,
|
||||
13838 0),
|
||||
13839 pack_facturare.ndiscount_evidentiat,
|
||||
13840 a1.cantitate,
|
||||
13841 a1.pret_cu_tva,
|
||||
13842 a1.proc_tvav)) AS SUMA_FARA_TVA_VAL,
|
||||
13843 sum(pack_facturare.calculeaza_total_tva_fact(a1.pret_val,
|
||||
13844 0,
|
||||
13845 1,
|
||||
13846 NVL(a1.discount_unitar_val,
|
||||
13847 0),
|
||||
13848 pack_facturare.ndiscount_evidentiat,
|
||||
13849 a1.cantitate,
|
||||
13850 a1.pret_cu_tva,
|
||||
13851 a1.proc_tvav)) as SUMA_TVA_VAL,
|
||||
13852 sum(round(a1.cantitate * a1.pret_achizitie,
|
||||
13853 lnPrecizieCalcul)) AS VALOARE_ACHIZITIE,
|
||||
13854 max(decode(pack_facturare.nin_valuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
13855 max(decode(pack_facturare.nin_valuta, 1, a1.curs, 1)) as curs,
|
||||
13856 max(decode(pack_facturare.nin_valuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
13857 from (select vd.id_vanzare_set,
|
||||
13858 (case
|
||||
13859 when (pack_facturare.nin_valuta = 1 or
|
||||
13860 vd.id_valuta <>
|
||||
13861 pack_def.GetIdMonedaNationala()) then
|
||||
13862 ROUND(vc.curs * vd.pret / vc.multiplicator,
|
||||
13863 lnPreciziePretV)
|
||||
13864 else
|
||||
13865 vd.pret
|
||||
13866 end) as pret_ron,
|
||||
13867 vd.pret as pret_val,
|
||||
13868 vd.proc_tvav,
|
||||
13869 vd.cantitate,
|
||||
13870 vd.diferenta,
|
||||
13871 (case
|
||||
13872 when (pack_facturare.nin_valuta = 1 or
|
||||
13873 vd.id_valuta <>
|
||||
13874 pack_def.GetIdMonedaNationala()) then
|
||||
13875 ROUND(vc.curs * vd.discount_unitar /
|
||||
13876 vc.multiplicator,
|
||||
13877 lnPreciziePretV)
|
||||
13878 else
|
||||
13879 vd.discount_unitar
|
||||
13880 end) as discount_unitar_ron,
|
||||
13881 vd.discount_unitar as discount_unitar_val,
|
||||
13882 vd.id_valuta,
|
||||
13883 vd.pret_cu_tva,
|
||||
13884 vd.pret_achizitie,
|
||||
13885 vc.curs,
|
||||
13886 vc.multiplicator
|
||||
13887 from (select a.id_vanzare_set,
|
||||
13888 a.pret,
|
||||
13889 a.proc_tvav,
|
||||
13890 a.cantitate,
|
||||
13891 a.diferenta,
|
||||
13892 a.discount_unitar,
|
||||
13893 a.id_valuta,
|
||||
13894 a.pret_cu_tva,
|
||||
13895 a.pret_achizitie
|
||||
13896 from VANZARI_DETALII_TEMP a
|
||||
13897 where nvl(a.id_vanzare_set, 0) = 0
|
||||
13898 union all
|
||||
13899 select b.id_vanzare_set,
|
||||
13900 b.pret,
|
||||
13901 max(c.proc_tvav) as proc_tvav,
|
||||
13902 b.cantitate,
|
||||
13903 0 as diferenta,
|
||||
13904 b.discount_unitar,
|
||||
13905 decode(pack_facturare.nin_valuta,
|
||||
13906 0,
|
||||
13907 pack_def.GetIdMonedaNationala(),
|
||||
13908 c.id_valuta) as id_valuta,
|
||||
13909 b.pret_cu_tva,
|
||||
13910 sum(decode(b.cantitate,
|
||||
13911 0,
|
||||
13912 0,
|
||||
13913 c.pret_achizitie * c.cantitate /
|
||||
13914 b.cantitate)) as pret_achizitie
|
||||
13915 from vanzari_detalii_temp c
|
||||
13916 left join vanzari_seturi b
|
||||
13917 on b.id_vanzare_set = c.id_vanzare_set
|
||||
13918 where nvl(c.id_vanzare_set, 0) <> 0
|
||||
13919 and nvl(pack_facturare.nin_valuta, -1) > -1
|
||||
13920 group by b.id_vanzare_set,
|
||||
13921 b.pret,
|
||||
13922 b.cantitate,
|
||||
13923 b.discount_unitar,
|
||||
13924 b.pret_cu_tva,
|
||||
13925 decode(pack_facturare.nin_valuta,
|
||||
13926 0,
|
||||
13927 pack_def.GetIdMonedaNationala(),
|
||||
13928 c.id_valuta)) vd
|
||||
13929 left join vanzari_cursuri vc
|
||||
13930 on vc.id_vanzare = V_ID_VANZARE
|
||||
13931 and vd.id_valuta = vc.id_valuta) a1) a;
|
||||
13932
|
||||
13933 update vanzari
|
||||
13934 set discount_tva = lnDiscountTVA,
|
||||
13935 valoare_achizitie = lnValoareAchizitie,
|
||||
13936 total_fara_tva = lnTotalFaraTVA,
|
||||
13937 total_tva = lnTotalTVA,
|
||||
13938 total_cu_tva = lnTotalCuTVA,
|
||||
13939 valval = lnValVal,
|
||||
13940 tvaval = lnTVAVal,
|
||||
13941 totval = lnTotVal,
|
||||
13942 id_valuta = lnIdValuta,
|
||||
13943 curs = lnCurs,
|
||||
13944 multiplicator = lnMultiplicator,
|
||||
13945 serie_incasat = lnSerieIncasat,
|
||||
13946 nr_incasat = lnNrIncasat,
|
||||
13947 suma_incasat = lnSumaIncasat,
|
||||
13948 tip_incasat = lnTipIncasat
|
||||
13949 where id_vanzare = V_ID_VANZARE;
|
||||
13950
|
||||
13951 exception
|
||||
13952 when NO_DATA_FOUND then
|
||||
13953 null;
|
||||
13954 end;
|
||||
```
|
||||
|
||||
Variabilele locale relevante, declarate la `:13501-13523`:
|
||||
|
||||
```
|
||||
13522 lnPrecizieCalcul NUMBER(2) := pack_sesiune.getOptiuneFirma('PC');
|
||||
13523 lnPreciziePretV NUMBER(2) := pack_sesiune.getOptiuneFirma('PPRETV');
|
||||
```
|
||||
|
||||
### Inventarul dependentelor blocului
|
||||
|
||||
| Dependenta | Linii | Sursa la emitere | Echivalent PERSISTENT la editare | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| `V_DISCOUNT_FACTURA` | 13798, 13801, 13802, 13807, 13810, 13813 | parametru al procedurii | **`VANZARI.DISCOUNT`** — scrisa la INSERT din exact acelasi parametru (`:13621` / `:13682`) | reconstituibil |
|
||||
| `pack_facturare.nin_valuta` | 13796, 13803, 13804, 13854-13856, 13859, 13872, 13905, 13919, 13925 | stare de sesiune pe pachet | **`VANZARI.IN_VALUTA`** (`NUMBER(1)`, NOT NULL) — scrisa la INSERT din aceeasi variabila (`:13625` / `:13686`) | reconstituibil |
|
||||
| `pack_facturare.ndiscount_evidentiat` | 13821, 13830, 13839, 13848 | stare de sesiune pe pachet | **`VANZARI.DISCOUNT_EVIDENTIAT`** — scrisa la INSERT din aceeasi variabila (`:13622` / `:13683`) | reconstituibil |
|
||||
| `cserie_act_incasare`, `nnumar_act_incasare`, `nsuma_incasare`, `ntip_doc_incasare` | 13777-13780 | stare de sesiune, setata numai in fluxul de emitere (`:13102-13144`; resetate la `:1882-1886`) | **NICIUNUL** | vezi 6 |
|
||||
| `pack_def.GetIdMonedaNationala()` | 13861, 13874, 13907, 13927 | functie pura: `SELECT MIN(ID_VALUTA) FROM NOM_VALUTE WHERE STERS=0 AND MONEDA_NATIONALA=1` (PACK_DEF body `:214-230`) | idem — fara stare | fara probleme |
|
||||
| `pack_sesiune.getOptiuneFirma('PC'/'PPRETV')` | 13522-13523, 13853 | `SELECT varvalue FROM optiuni WHERE ...` (PACK_SESIUNE body `:119-141`) — **lookup pe tabela, fara stare de pachet** | idem | fara probleme |
|
||||
| `VANZARI_DETALII_TEMP` (liniile directe) | 13896-13897 | GTT `ON COMMIT DELETE ROWS` | **`VANZARI_DETALII WHERE ID_VANZARE = :id AND STERS = 0 AND NVL(ID_VANZARE_SET,0) = 0`** | vezi 3 |
|
||||
| `VANZARI_DETALII_TEMP` (liniile de set, alias `c`) | 13915, 13918 | GTT | **`VANZARI_DETALII ... AND NVL(ID_VANZARE_SET,0) <> 0`** | vezi 3 |
|
||||
| `vanzari_seturi` (alias `b`) | 13916 | **tabela PERSISTENTA, deja** | ea insasi | vezi 3 |
|
||||
| `vanzari_cursuri vc` | 13929-13930 | tabela persistenta, populata cu `id_vanzare`-ul curent la `:13704` (`scrie_cursuri`) | ea insasi, deja legata pe `ID_VANZARE` | fara probleme |
|
||||
|
||||
Verificat pe `all_tab_columns`: **toate cele 9 coloane citite din `VANZARI_DETALII_TEMP` de
|
||||
agregare** (`id_vanzare_set, pret, proc_tvav, cantitate, diferenta, discount_unitar, id_valuta,
|
||||
pret_cu_tva, pret_achizitie`) exista, cu acelasi nume, in `VANZARI_DETALII`. Singurele coloane pe
|
||||
care TEMP le are in plus si care nu exista in tabela reala sunt `CURS, MULTIPLICATOR, EXPLICATIA,
|
||||
ID_COMANDA, NUMAR_ACT, ID_TEMP, ID_UTIL, IN_STOC, PRETV_ORIG, ID_GESTIUNE_DEST, ID_LUCRARE_REZ,
|
||||
ID_PART_REZ` — **niciuna nu e folosita de blocul de agregare** (`curs`/`multiplicator` vin din
|
||||
`vanzari_cursuri vc`, nu din linie).
|
||||
|
||||
**Concluzie: blocul se reproduce 1:1 pe surse persistente, schimband doar clauzele `FROM` si
|
||||
inlocuind cele 3 valori de stare cu cele 3 coloane din `VANZARI`.** Nu e nevoie de nicio
|
||||
reformulare a formulei — inclusiv `calculeaza_total_fara_tva_fact` / `calculeaza_total_tva_fact`
|
||||
(`PACK_FACTURARE.pck:15858-16013`, semnaturi in spec la `:1167-1185`) raman apelate identic.
|
||||
|
||||
Doua observatii de detaliu:
|
||||
- `a1.diferenta` e selectata (`:13870`, `:13891`) dar **nu e folosita nicaieri** in agregarea din
|
||||
`scrie_in_vanzari` (parametrul `V_DIFERENTA` e `0` hard-codat la `:13817`, `:13826`, `:13835`,
|
||||
`:13844`). Se poate pastra pentru fidelitate sau elimina; recomand pastrarea, ca diff-ul fata de
|
||||
original sa ramana citibil.
|
||||
- Handler-ul `WHEN NO_DATA_FOUND THEN NULL` (`:13951-13953`) e defensiv si practic inaccesibil:
|
||||
subinterogarea are agregate fara `GROUP BY`, deci intoarce mereu exact un rand. **Dar** pe zero
|
||||
linii agregatele intorc `NULL`, nu `0` — la emitere cazul nu poate aparea, la editare da (vezi
|
||||
riscul R4).
|
||||
|
||||
---
|
||||
|
||||
## 3. Liniile din seturi — EXISTA echivalent persistent
|
||||
|
||||
**Corectia raportului precedent.** Agregarea nu citeste `VANZARI_SETURI_TEMP`, ci
|
||||
**`VANZARI_SETURI`** (`PACK_FACTURARE.pck:13916`), care e o tabela **normala, persistenta**
|
||||
(verificat pe `all_tables`: `VANZARI_SETURI temporary=N`; doar `VANZARI_SETURI_TEMP` si
|
||||
`VANZARI_DETALII_TEMP` sunt `temporary=Y duration=SYS$TRANSACTION`).
|
||||
|
||||
Trecerea TEMP -> persistent o face `pack_facturare.scrie_seturi` (`:13993-14028`), apelata la
|
||||
`:13706`, **inainte** de agregare: insereaza fiecare rand din `VANZARI_SETURI_TEMP` in
|
||||
`VANZARI_SETURI` (`ID_VANZARE_SET` din `SEQ_VANZARI_SETURI`, prin trigger
|
||||
`TRG_VANZARI_SET_BEFOINS`) si reface pointerul in `VANZARI_DETALII_TEMP.ID_VANZARE_SET`. De aceea
|
||||
agregarea de la `:13915-13928` face join intre TEMP (componentele) si tabela persistenta (capul de
|
||||
set).
|
||||
|
||||
Structura `VANZARI_SETURI` (`all_tab_columns`), identica cu a TEMP-ului:
|
||||
|
||||
```
|
||||
ID_VANZARE_SET NUMBER(10) NOT NULL -- PK, din SEQ_VANZARI_SETURI
|
||||
DENUMIRE VARCHAR2(100)
|
||||
EXPLICATIE VARCHAR2(100)
|
||||
CANTITATE NUMBER(10,4)
|
||||
UM VARCHAR2(10)
|
||||
SERIE VARCHAR2(100)
|
||||
PRET NUMBER(20,4)
|
||||
DISCOUNT_UNITAR NUMBER(20,4)
|
||||
PRET_CU_TVA NUMBER(1) NOT NULL
|
||||
```
|
||||
|
||||
Nu are `ID_VANZARE` si nu are `STERS`: legatura cu documentul e **exclusiv** prin
|
||||
`VANZARI_DETALII.ID_VANZARE_SET`. Deci filtrarea pe document se face tot din `VANZARI_DETALII`.
|
||||
|
||||
**Precedent care confirma reconstituirea**: `pack_facturare.citeste_vanzari_seturi`
|
||||
(`:16619-16698`) reface deja exact acest lucru pe surse persistente — `VANZARI` (`cod`, `sters=0`)
|
||||
+ `VANZARI_DETALII` (`sters=0`, `id_vanzare_set is not null`) + `VANZARI_CURSURI` +
|
||||
`VANZARI_SETURI`, cu acelasi `GROUP BY b.id_vanzare_set`. Nu inventam un tipar nou.
|
||||
|
||||
**Raspuns la intrebarea din brief: DA, recalculul poate acoperi si liniile de set, fara pierdere de
|
||||
informatie.** Transformarea necesara in ramura de set:
|
||||
|
||||
```
|
||||
from vanzari_detalii c -- in loc de vanzari_detalii_temp c
|
||||
left join vanzari_seturi b on b.id_vanzare_set = c.id_vanzare_set
|
||||
where nvl(c.id_vanzare_set, 0) <> 0
|
||||
and c.id_vanzare = V_ID_VANZARE
|
||||
and c.sters = 0
|
||||
```
|
||||
|
||||
Date (test, `MARIUSM_AUTO`): 4 randuri in `VANZARI_SETURI`, 4 documente cu linii de set. **Nu e o
|
||||
dovada** — validarea ramane pe cod, nu pe volum.
|
||||
|
||||
### Ce NU se poate face din interfata (limitare de semnalat pentru S4)
|
||||
|
||||
View-ul `VVANZARI_ARTICOLE` (sursa lui `IncarcaArticoleFactura`,
|
||||
`COMUN\programe\ofacturare_editare.prg:288-330`) **nu expune `ID_VANZARE_SET` si nici
|
||||
`PRET_ACHIZITIE`**:
|
||||
|
||||
```
|
||||
select vd.id_vanzare, vd.id_vanzare_det, vd.id_articol, vd.cantitate, vd.pret, vd.pret_cu_tva,
|
||||
vd.proc_tvav, vd.discount_unitar, vd.id_gestiune, vd.cont, vd.id_valuta,
|
||||
vd.id_jtva_coloana, vd.serie, vd.explicatie, vd.taxcode, vd.lot, vd.sters,
|
||||
na.denumire, na.codmat, ng.nume_gestiune, nv.nume_val
|
||||
from vanzari_detalii vd
|
||||
left join nom_articole na on na.id_articol = vd.id_articol
|
||||
left join nom_gestiuni ng on ng.id_gestiune = vd.id_gestiune
|
||||
left join nom_valute nv on nv.id_valuta = vd.id_valuta
|
||||
```
|
||||
|
||||
Consecinte concrete:
|
||||
|
||||
1. componentele de set apar in grid ca linii obisnuite, dar formularul **nu poate sti** ca sunt
|
||||
componente si nu poate edita capul de set (`VANZARI_SETURI.PRET`/`CANTITATE`), care e ceea ce
|
||||
intra efectiv in totaluri. Un utilizator care schimba pretul unei componente **nu schimba
|
||||
totalul documentului** — recalculul ia pretul din `VANZARI_SETURI`. Divergenta tacuta.
|
||||
2. `PRET_ACHIZITIE` lipseste din cursor, deci VFP nu-l poate rescrie la un `UPDATE` de linie: la
|
||||
scriere trebuie **omis din `SET`**, ca sa ramana valoarea existenta (altfel `VALOARE_ACHIZITIE`
|
||||
se pierde). Pentru liniile **noi** insa nu exista sursa — vor intra cu `PRET_ACHIZITIE` NULL si
|
||||
vor contribui cu NULL la `SUM(round(cantitate * pret_achizitie))`, deci **`VALOARE_ACHIZITIE`
|
||||
devine NULL pe tot documentul** daca fie si o singura linie noua are NULL. Vezi riscul R3.
|
||||
|
||||
---
|
||||
|
||||
## 4. Capcana de ordonare — punctul central
|
||||
|
||||
### Ce ruleaza azi, in ce ordine
|
||||
|
||||
Punctul de intrare nou (deja scris in ramura curenta):
|
||||
`COMUN\clase\ofacturare_comun.vc2`, `do_editare_factura`, blocul de salvare:
|
||||
|
||||
```
|
||||
3798 If Thisform.do_deschide_tranzactie() && SQLSetprop(gnHandle,"Transactions",2)
|
||||
3800 lnSucces = OSCRIE_IN_FISIERE(2,.T.,.T.) && marcheaza STERS=1 nota veche
|
||||
...
|
||||
3820 lnSucces = OSCRIE_IN_FISIERE(0,.T.,.T.) && scrie nota noua (COD nou din SCRIE_IN_ACT)
|
||||
3823 If lnSucces > 0
|
||||
3824 lcSql = [begin pack_contafin.finalizeaza_modificare_nota(...); end]
|
||||
3826 lnSucces = Iif(goExecutor.oExecuta(lcSql),1,-1)
|
||||
3827 Endif
|
||||
3828 If Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1)) && SQLCOMMIT / SQLROLLBACK
|
||||
```
|
||||
|
||||
`do_deschide_tranzactie` / `do_inchide_tranzactie`: `COMUN\clase\_frm_base.vc2:251-269` si
|
||||
`:278-302` — `SQLSetprop(gnHandle,"Transactions",2)` / `Sqlcommit(gnHandle)`.
|
||||
|
||||
`finalizeaza_modificare_nota` (`PACK_CONTAFIN.pck:8601-8651`) apeleaza `actualizeaza_vanzari` la
|
||||
`:8616`, gardat de `SELECT COUNT(*) FROM vanzari WHERE cod = tnCod > 0` (`:8613-8615`).
|
||||
|
||||
`actualizeaza_vanzari` (`PACK_FACTURARE.pck:16015-16025`):
|
||||
|
||||
```
|
||||
16018 -- de modificat in caz ca il las sa stearga manual inregistrari din VANZARI_DETALII
|
||||
16019 -- acum se marcheaza cu STERS = 1 doar cand se sterge toata factura
|
||||
16020 UPDATE VANZARI_DETALII
|
||||
16021 SET STERS = 0
|
||||
16022 WHERE ID_VANZARE IN
|
||||
16023 (SELECT ID_VANZARE FROM VANZARI WHERE COD = V_COD_VECHI);
|
||||
16024 UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI;
|
||||
```
|
||||
|
||||
### De ce exista `STERS = 0` acolo (nu e cod mort)
|
||||
|
||||
E perechea lui `sterge_din_vanzari` -> `sterge_factura`, care marcheaza documentul sters:
|
||||
|
||||
```
|
||||
5549 UPDATE VANZARI_DETALII
|
||||
5550 SET STERS = V_STERS, ID_UTILS = V_ID_UTIL, DATAORAS = V_DATAORA
|
||||
5551 WHERE ID_VANZARE = V_ID_VANZARE
|
||||
5552 AND STERS = V_NESTERS
|
||||
```
|
||||
|
||||
Reeditarea unei note sterse trebuie sa readuca la viata si randul din `VANZARI`, si liniile lui.
|
||||
**Scoaterea reset-ului ar rupe fluxul „stergi nota, apoi o reeditezi" pe toata suita ROA.**
|
||||
|
||||
### Consecinta pentru S5 — mai grava decat „stergerile se pierd"
|
||||
|
||||
Reset-ul e **in bloc, fara nicio garda**: nici `AND STERS = 1` (irelevant), nici o discriminare
|
||||
intre „stersa ca linie" si „stersa odata cu documentul". `sterge_factura` marcheaza doar liniile
|
||||
active (`AND STERS = V_NESTERS`, `:5552`), deci o linie stearsa la o editare anterioara ramane
|
||||
`STERS=1` — si e **inviata** de `actualizeaza_vanzari` la urmatoarea salvare a notei.
|
||||
|
||||
Deci daca liniile se scriu inainte de `finalizeaza_modificare_nota`:
|
||||
|
||||
- stergerile din editarea CURENTA se pierd tacut;
|
||||
- **si**, independent de ce face utilizatorul acum, orice stergere facuta la o editare
|
||||
ANTERIOARA e anulata la fiecare salvare ulterioara — inclusiv la o salvare care nu atinge deloc
|
||||
articolele. Stergerea de linie **nu s-ar fixa niciodata**.
|
||||
|
||||
`UPDATE`-urile de pret/cantitate si `INSERT`-urile de linii noi ar supravietui — deci esecul e
|
||||
partial si asimetric, exact tipul care trece de un test superficial.
|
||||
|
||||
### Variantele de ordonare
|
||||
|
||||
**(a) VFP scrie detaliile inainte, `actualizeaza_vanzari` neatinsa.**
|
||||
Respinsa. Motivul complet e cel de mai sus: nu doar ca stergerile din sesiunea curenta se pierd,
|
||||
dar mecanismul de stergere de linie devine structural imposibil, fiindca fiecare salvare a notei
|
||||
reseteaza tot documentul la `STERS = 0`. In plus, dupa `actualizeaza_vanzari` `VANZARI.COD` s-a
|
||||
schimbat, deci orice scriere ulterioara ancorata pe `cod` ar rata randul (`ID_VANZARE` ramane —
|
||||
vezi A.1 din raportul precedent, reconfirmat la `:16024`).
|
||||
|
||||
**(a') VFP scrie inainte + `actualizeaza_vanzari` primeste o garda.**
|
||||
Respinsa. Ar cere un discriminator „stearsa ca linie" vs „stearsa cu documentul", care nu exista in
|
||||
schema (`VANZARI_DETALII` nu are alt marcaj decat `STERS`/`ID_UTILS`/`DATAORAS`); orice euristica
|
||||
pe `DATAORAS` e fragila. Si e Varianta A din raportul precedent — modificare in-place a unei
|
||||
proceduri apelate de **orice** editare de nota cu `cod` in `vanzari`, din toata suita ROA.
|
||||
|
||||
**(b) VFP scrie detaliile DUPA ce `finalizeaza_modificare_nota` s-a intors, apoi apeleaza
|
||||
`pack_facturare.recalculeaza_totaluri_vanzari(id_vanzare, discount)`, in aceeasi tranzactie.**
|
||||
**RECOMANDATA.** Verificat, nu presupus:
|
||||
|
||||
- **tranzactia e inca deschisa** cand se intoarce `finalizeaza_modificare_nota`: aceasta ruleaza la
|
||||
`ofacturare_comun.vc2:3826`, iar `do_inchide_tranzactie` abia la `:3828`; tranzactia e manuala pe
|
||||
`gnHandle` (`_frm_base.vc2:255`), aceeasi conexiune ODBC pe care ruleaza `goExecutor`. Deci
|
||||
scrierile de dupa intra in acelasi `COMMIT`/`ROLLBACK`, fara fereastra de inconsistenta.
|
||||
- `actualizeaza_vanzari` ramane **neatinsa** — zero regresie pe ROAGEST/ROACONT.
|
||||
- `PACK_CONTAFIN` ramane **neatins** — nu se recompileaza pachetul central de scriere a
|
||||
documentelor.
|
||||
- reset-ul `STERS = 0` ruleaza primul, iar VFP re-aplica dupa el starea autoritativa a liniilor.
|
||||
- `VANZARI.COD` e deja rescris cand VFP scrie — de aceea toate scrierile se ancoreaza pe
|
||||
`ID_VANZARE` (`lnIdVanzare`, citit din `crsfacturi` la `ofacturare_comun.vc2:3735`, stabil).
|
||||
|
||||
**Atentie — (b) naiv are un defect.** `IncarcaArticoleFactura` incarca **doar `sters = 0`**
|
||||
(`ofacturare_editare.prg:302`), deci liniile sterse la o editare anterioara nu sunt in
|
||||
`crsArticoleFactura`: reset-ul le-a inviat, iar VFP nu le atinge, deci raman active. Corectia e in
|
||||
**forma scrierii**, nu in Oracle — VFP scrie o stare completa, nu un delta:
|
||||
|
||||
```
|
||||
1. UPDATE VANZARI_DETALII SET STERS = 1, ID_UTILS = ?, DATAORAS = SYSDATE
|
||||
WHERE ID_VANZARE = <id> AND STERS = 0 -- marcheaza tot
|
||||
2. pentru fiecare linie pastrata din cursor, cu id_vanzare_det > 0:
|
||||
UPDATE VANZARI_DETALII SET STERS = 0, CANTITATE = ?, PRET = ?, PRET_CU_TVA = ?, ...
|
||||
WHERE ID_VANZARE_DET = <det> -- invie doar ce ramane
|
||||
3. pentru fiecare linie noua: INSERT INTO VANZARI_DETALII (...) -- ID_VANZARE_DET din trigger
|
||||
4. pack_facturare.recalculeaza_totaluri_vanzari(<id>, <discount>)
|
||||
```
|
||||
|
||||
Idiomul „marcheaza tot, invie ce ramane" e idempotent, nu are limita de 1000 de elemente intr-un
|
||||
`IN`, nu cere lista separata de linii sterse, si pastreaza sterse liniile scoase la editari
|
||||
anterioare. La pasul 2, `PRET_ACHIZITIE` se **omite** din `SET` (nu e in cursor — vezi 3).
|
||||
|
||||
**(c) recalcul apelat din interiorul `finalizeaza_modificare_nota`** (recomandarea raportului
|
||||
precedent, sectiunea B). **De abandonat.** Este incompatibila cu (b): daca recalculul ruleaza
|
||||
inauntrul lui `finalizeaza_modificare_nota`, el vede liniile **vechi** (VFP nu le-a scris inca,
|
||||
pentru ca nu poate scrie inainte de reset), deci ar produce exact totalurile de dinainte de editare
|
||||
— tacut corecte ca formula, tacut gresite ca valoare. Nu e o varianta mai riscanta, e o varianta
|
||||
gresita.
|
||||
|
||||
**(c') procedura Oracle care primeste si liniile** (prin `VANZARI_DETALII_TEMP` sau o colectie) si
|
||||
face si scrierea, si recalculul. Respinsa: reintroduce calea TEMP pe care planul a exclus-o explicit
|
||||
(`plan_06_editare_factura.md:106-114`), cere o forma de parametru incomoda prin ODBC, si dubleaza
|
||||
scrierea per-linie pe care planul a decis-o deja in VFP, dupa modelul `modifica_explicatie_articol`
|
||||
(`PACK_FACTURARE.pck:14514-14522`).
|
||||
|
||||
### Recomandare
|
||||
|
||||
**Varianta (b), cu scrierea in forma „marcheaza tot, invie ce ramane".** Argumentul decisiv nu e
|
||||
comoditatea, ci ca e **singura ordine in care reset-ul `STERS = 0` din `actualizeaza_vanzari` ramane
|
||||
inofensiv fara sa modificam procedura** — iar procedura aceea e folosita azi de toata suita, pentru
|
||||
un scop legitim (reeditarea unei note sterse) pe care nu-l putem sacrifica.
|
||||
|
||||
---
|
||||
|
||||
## 5. Discountul de document (`VANZARI.DISCOUNT`)
|
||||
|
||||
**Cine il scrie azi: nimeni, dupa emitere.** Verificat pe toata schema, nu doar pe `PACK_FACTURARE`:
|
||||
|
||||
- `all_source` (`PACKAGE BODY`/`PROCEDURE`/`FUNCTION`/`TRIGGER`, `MARIUSM_AUTO`), cautand
|
||||
`discount\s*=` exclusiv coloanele `discount_unitar|discount_tva|discount_evidentiat`: **niciun
|
||||
`UPDATE ... SET DISCOUNT = ...` pe `VANZARI`.** Rezultatele sunt toate pe alte tabele
|
||||
(`PACK_COMENZI.PROC_DISCOUNT`, `PACK_CRM.val_discount`, `PACK_OFERTARE.valdiscount`, ...).
|
||||
- Toate cele 8 instructiuni `UPDATE VANZARI` din `PACK_FACTURARE` (`:5485, :5493, :5516, :5615,
|
||||
:14817, :15387, :15397, :15509`) plus `modifica_date_factura` (`:14463-14512`, singura procedura
|
||||
de „modifica antetul facturii" existenta) ating `STERS`/`FACTURAT`/`ID_FACT`/`AVIZE`/
|
||||
`SERIE_ACT`/`NUMAR_ACT`/`DATA_ACT`/`DATA_SCAD` — **niciuna `DISCOUNT`**.
|
||||
- In VFP: cautare pe `COMUN\` dupa `set discount =` / `vanzari set ... discount` — **niciun
|
||||
rezultat**.
|
||||
- Trigger-ele pe `VANZARI` nu-l ating: `TRG_VANZARI_BEFOUPD` face doar audit pe
|
||||
`NR_ACT`/`SERIE_ACT`/`DATA_ACT`/`DATA_SCAD` (`pack_audit.verifica_val`).
|
||||
|
||||
Deci `VANZARI.DISCOUNT` e scris **o singura data in viata documentului**, la `INSERT`-ul din
|
||||
`scrie_in_vanzari` (`:13621` in lista de coloane, `:13682` in `VALUES`, din `V_DISCOUNT_FACTURA`).
|
||||
Nu exista nici procedura de modificare, nici cale VFP.
|
||||
|
||||
Pe partea VFP valoarea e deja disponibila in memorie: cursorul `tvanz` are coloana `discount`
|
||||
(`ofacturare_editare.prg:152`, `CreeazaCursorTvanzGol`), populata din `VANZARI` de
|
||||
`IncarcaVanzareNota` (`:176`).
|
||||
|
||||
### Recomandare: **parametru al procedurii noi**, nu `UPDATE` separat din VFP
|
||||
|
||||
```
|
||||
recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER, V_DISCOUNT IN NUMBER DEFAULT NULL)
|
||||
```
|
||||
|
||||
cu semantica `V_DISCOUNT IS NULL` = „pastreaza valoarea curenta". Motive:
|
||||
|
||||
1. **discountul si totalurile nu pot diverge.** Cu doua instructiuni separate din VFP, un esec pe
|
||||
a doua (sau o omisiune la un viitor call-site) lasa `DISCOUNT` nou si totaluri calculate pe cel
|
||||
vechi. Cu un parametru, e imposibil sa recalculezi cu un discount invechit.
|
||||
2. procedura ramane utilizabila ca **recalcul pur** (fara al doilea argument) de oriunde altundeva
|
||||
— de exemplu dintr-un script de backfill.
|
||||
3. e o instructiune ODBC in minus pe drumul critic.
|
||||
|
||||
Implementare: `V_DISCOUNT` se aplica in acelasi `UPDATE vanzari` final,
|
||||
`discount = NVL(V_DISCOUNT, discount)`, iar valoarea folosita in agregare se citeste **inainte**,
|
||||
ca `NVL(V_DISCOUNT, (select discount from vanzari where id_vanzare = V_ID_VANZARE))` — altfel
|
||||
agregarea ar lucra pe discountul vechi.
|
||||
|
||||
---
|
||||
|
||||
## 6. Coloanele care NU se recalculeaza — reconfirmat pe sursa proaspata
|
||||
|
||||
`SERIE_INCASAT` / `NR_INCASAT` / `SUMA_INCASAT` / `TIP_INCASAT` (`:13777-13780`, scrise la
|
||||
`:13945-13948`) vin din `pack_facturare.cserie_act_incasare` / `nnumar_act_incasare` /
|
||||
`nsuma_incasare` / `ntip_doc_incasare`.
|
||||
|
||||
Cautare exhaustiva a atribuirilor catre aceste 4 variabile in `PACK_FACTURARE.pck` — **7 rezultate,
|
||||
toate in fluxul de emitere**:
|
||||
|
||||
```
|
||||
1882-1886 cserie_act_incasare := NULL; nnumar_act_incasare := NULL;
|
||||
ntip_doc_incasare := NULL; nsuma_incasare := NULL; (initializeaza_date_factura)
|
||||
13102-13105 cserie_act_incasare := V_SERIE_ACT_INCASARE; nnumar_act_incasare := V_NUMAR_ACT_INCASARE;
|
||||
ntip_doc_incasare := nTipIncasareChitanta; nsuma_incasare := 0;
|
||||
13136 nsuma_incasare := nsuma_incasare + ...
|
||||
13140,13144 ntip_doc_incasare := V_TIP / nTipIncasareBonFiscal;
|
||||
```
|
||||
|
||||
Sursele lor (`V_SERIE_ACT_INCASARE`, `V_NUMAR_ACT_INCASARE`) sunt parametri ai emiterii unei
|
||||
facturi-cu-incasare combinata. **Nu exista nicio tabela din care sa fie reconstituite la o editare
|
||||
ulterioara** — singura urma persistenta sunt chiar cele 4 coloane din `VANZARI`, care ar fi
|
||||
suprascrise cu `NULL` de o copiere naiva a `UPDATE`-ului.
|
||||
|
||||
**Concluzie reconfirmata: procedura noua scrie 11 coloane** (`discount_tva`, `valoare_achizitie`,
|
||||
`total_fara_tva`, `total_tva`, `total_cu_tva`, `valval`, `tvaval`, `totval`, `id_valuta`, `curs`,
|
||||
`multiplicator`), **plus optional `discount`** (vezi 5). Cele 4 de incasare raman la valoarea de la
|
||||
emitere.
|
||||
|
||||
---
|
||||
|
||||
## 7. Semnatura propusa si scripturile de migrare
|
||||
|
||||
### Declaratia din PACKAGE SPEC
|
||||
|
||||
Se adauga imediat dupa `actualizeaza_vanzari` (`PACK_FACTURARE.pck:1187-1188`), langa procedurile
|
||||
surori:
|
||||
|
||||
```sql
|
||||
PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER,
|
||||
V_DISCOUNT IN NUMBER DEFAULT NULL);
|
||||
```
|
||||
|
||||
`recalculeaza_totaluri_vanzari` = **29 de caractere**, sub limita de 30 a lui Oracle 10.2/11
|
||||
(peste 30 ar da `ORA-00972`). Nu mai lungi numele.
|
||||
|
||||
### Corpul (schita, compatibila 10.2)
|
||||
|
||||
Constructii folosite: `SELECT INTO`, `UPDATE`, `DECODE`, `CASE`, `NVL`, `ROUND`, `MAX`, `SUM`,
|
||||
`LEFT JOIN`, `UNION ALL`, `GROUP BY`, subinterogari inline. **Nimic din tabelul de incompatibilitati
|
||||
din `scripturi-migrare-db.md`** — fara `LISTAGG`, `CONTINUE`, `REGEXP_COUNT`, `FETCH FIRST`,
|
||||
`PIVOT`, `secventa.NEXTVAL` in atribuire.
|
||||
|
||||
```sql
|
||||
PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER,
|
||||
V_DISCOUNT IN NUMBER DEFAULT NULL) IS
|
||||
lnDiscountFactura VANZARI.DISCOUNT%TYPE;
|
||||
lnInValuta VANZARI.IN_VALUTA%TYPE;
|
||||
lnDiscountEvidentiat VANZARI.DISCOUNT_EVIDENTIAT%TYPE;
|
||||
lnDiscountTVA VANZARI.DISCOUNT_TVA%TYPE;
|
||||
lnValoareAchizitie VANZARI.VALOARE_ACHIZITIE%TYPE;
|
||||
lnTotalFaraTVA VANZARI.TOTAL_FARA_TVA%TYPE;
|
||||
lnTotalTVA VANZARI.TOTAL_TVA%TYPE;
|
||||
lnTotalCuTVA VANZARI.TOTAL_CU_TVA%TYPE;
|
||||
lnValVal VANZARI.VALVAL%TYPE;
|
||||
lnTVAVal VANZARI.TVAVAL%TYPE;
|
||||
lnTotVal VANZARI.TOTVAL%TYPE;
|
||||
lnIdValuta VANZARI.ID_VALUTA%TYPE;
|
||||
lnCurs VANZARI.CURS%TYPE;
|
||||
lnMultiplicator VANZARI.MULTIPLICATOR%TYPE;
|
||||
lnPrecizieCalcul NUMBER(2) := pack_sesiune.getOptiuneFirma('PC');
|
||||
lnPreciziePretV NUMBER(2) := pack_sesiune.getOptiuneFirma('PPRETV');
|
||||
BEGIN
|
||||
SELECT NVL(V_DISCOUNT, discount), in_valuta, NVL(discount_evidentiat, 0)
|
||||
INTO lnDiscountFactura, lnInValuta, lnDiscountEvidentiat
|
||||
FROM vanzari
|
||||
WHERE id_vanzare = V_ID_VANZARE;
|
||||
|
||||
-- acelasi bloc de agregare ca in scrie_in_vanzari (:13764-13931), cu trei substitutii:
|
||||
-- VANZARI_DETALII_TEMP a -> VANZARI_DETALII a WHERE a.id_vanzare = V_ID_VANZARE AND a.sters = 0
|
||||
-- vanzari_detalii_temp c -> vanzari_detalii c WHERE c.id_vanzare = V_ID_VANZARE AND c.sters = 0
|
||||
-- pack_facturare.nin_valuta -> lnInValuta
|
||||
-- pack_facturare.ndiscount_evidentiat -> lnDiscountEvidentiat
|
||||
-- V_DISCOUNT_FACTURA -> lnDiscountFactura
|
||||
-- fara coloanele de incasare (serie/nr/suma/tip)
|
||||
SELECT ... INTO lnDiscountTVA, lnValoareAchizitie, lnTotalFaraTVA, lnTotalTVA,
|
||||
lnTotalCuTVA, lnValVal, lnTVAVal, lnTotVal,
|
||||
lnIdValuta, lnCurs, lnMultiplicator
|
||||
FROM ( ... );
|
||||
|
||||
UPDATE vanzari
|
||||
SET discount = lnDiscountFactura,
|
||||
discount_tva = lnDiscountTVA,
|
||||
valoare_achizitie = lnValoareAchizitie,
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
total_tva = lnTotalTVA,
|
||||
total_cu_tva = lnTotalCuTVA,
|
||||
valval = lnValVal,
|
||||
tvaval = lnTVAVal,
|
||||
totval = lnTotVal,
|
||||
id_valuta = lnIdValuta,
|
||||
curs = lnCurs,
|
||||
multiplicator = lnMultiplicator
|
||||
WHERE id_vanzare = V_ID_VANZARE;
|
||||
END recalculeaza_totaluri_vanzari;
|
||||
```
|
||||
|
||||
Idempotenta: `SELECT` + `UPDATE ... WHERE id_vanzare = :id`, fara `INSERT` — rularea de doua ori pe
|
||||
acelasi id da acelasi rezultat.
|
||||
|
||||
**Fara handler `WHEN NO_DATA_FOUND THEN NULL`** pe modelul originalului: la editare, un `id_vanzare`
|
||||
inexistent e o eroare reala care trebuie sa opreasca tranzactia, nu sa fie inghitita. (Primul
|
||||
`SELECT INTO`, pe cheia primara, e singurul care poate ridica `NO_DATA_FOUND`.)
|
||||
|
||||
### Scripturile de migrare
|
||||
|
||||
Azi, 09.08.2026, in `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08` nu exista niciun script cu data de azi
|
||||
(ultimele sunt `..._2026_08_08_01` si `..._2026_08_08_02`), deci **`NN` porneste de la `01`**.
|
||||
`NN` e secventa unica pe zi, comuna tuturor prefixelor.
|
||||
|
||||
**Un singur script**, fiindca S5 atinge un singur pachet si nimic altceva:
|
||||
|
||||
```
|
||||
ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql
|
||||
```
|
||||
|
||||
- prefix `ff_` — pachetul sta pe schema fiecarei firme;
|
||||
- **un pachet sta singur in scriptul lui**: fara DDL de tabele si fara DML alaturi (nu e nevoie de
|
||||
niciunul — nu se adauga coloane si nu se curata date);
|
||||
- contine SPEC + BODY (SPEC-ul se schimba: declaratia noua), CRLF obligatoriu, antet de 4-5 randuri,
|
||||
fara `select` de raportare, si se incheie cu
|
||||
`exec pack_migrare.UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql');` + `commit;`;
|
||||
- `versiune_db.txt` din radacina proiectului se muta pe `2026_08_09_01` (fara newline final).
|
||||
|
||||
**Nu e nevoie de un script separat pentru `VVANZARI_ARTICOLE`** pentru S5 asa cum e proiectat aici
|
||||
— dar vezi intrebarea 3 de mai jos, care ar cere unul (`NN = 02`, si atunci **inainte** de cel al
|
||||
pachetului daca pachetul l-ar folosi; aici nu-l foloseste, deci ordinea e libera).
|
||||
|
||||
---
|
||||
|
||||
## 8. Riscuri si intrebari deschise
|
||||
|
||||
**R1 — Componentele de set sunt editabile in grid dar nu influenteaza totalurile. (mediu)**
|
||||
`VVANZARI_ARTICOLE` nu expune `ID_VANZARE_SET`, deci S4 nu poate distinge componentele de liniile
|
||||
normale; recalculul ia insa pretul/cantitatea din `VANZARI_SETURI`, nu din componente. Utilizatorul
|
||||
modifica o componenta, apasa salvare, totalul nu se schimba. Tacut.
|
||||
*Recomandarea mea:* pentru #6, **adauga `ID_VANZARE_SET` in `VVANZARI_ARTICOLE`** (script separat,
|
||||
`ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`) si fa liniile cu `id_vanzare_set` nenul
|
||||
**needitabile in grid**, cu o eticheta („linie din set"). Editarea seturilor e o functionalitate
|
||||
proprie, nu o extindere gratuita a lui S4. Alternativa minimala, daca Marius nu vrea scriptul de
|
||||
view: blocheaza intreaga factura care contine seturi — dar asta contrazice decizia din
|
||||
`plan_06_editare_factura.md:183-186` („fara blocarea facturilor care le contin").
|
||||
|
||||
**R2 — Stergerea unei componente de set. (mic, dar urat)**
|
||||
Cu idiomul „marcheaza tot, invie ce ramane", stergerea in grid a unei componente lasa capul de set
|
||||
in `VANZARI_SETURI` si celelalte componente pe loc: totalul ramane neschimbat (vine din cap), dar
|
||||
`VALOARE_ACHIZITIE` scade (se pierde `pret_achizitie`-ul componentei). Divergenta partiala.
|
||||
*Recomandare:* acoperit de R1 — daca liniile de set sunt needitabile, cazul dispare.
|
||||
|
||||
**R3 — `PRET_ACHIZITIE` NULL pe liniile noi anuleaza `VALOARE_ACHIZITIE` pe tot documentul. (mediu)**
|
||||
`SUM(round(cantitate * pret_achizitie))` cu un singur operand NULL nu da NULL pe total (SUM ignora
|
||||
NULL-urile), dar **linia noua nu contribuie deloc** — deci `VALOARE_ACHIZITIE` (baza de calcul a
|
||||
marjei) subestimeaza sistematic dupa fiecare adaugare de linie. Coloana nu e in cursorul din grid si
|
||||
nu are sursa la editare (la emitere vine din stoc/politica de pret).
|
||||
*Recomandare:* la `INSERT`-ul liniei noi, VFP scrie `PRET_ACHIZITIE` cu pretul de achizitie curent
|
||||
al articolului (acelasi lookup pe care il face `adauga_articol_factura_stoc`), sau, daca nu se poate
|
||||
determina, cu `0` explicit si o avertizare in verificarile din S4b. **Nu lasa NULL tacut.**
|
||||
|
||||
**R4 — Factura fara nicio linie da totaluri NULL, nu 0. (mic)**
|
||||
Agregatele pe zero randuri intorc NULL; la emitere cazul nu poate aparea, la editare da.
|
||||
*Recomandare:* nu modifica formula (ar diverge de original). Interzice in S4b salvarea unei facturi
|
||||
cu zero linii active — e oricum un document invalid.
|
||||
|
||||
**R5 — Linie cu valuta fara rand in `VANZARI_CURSURI`. (mic)**
|
||||
`left join vanzari_cursuri vc on vc.id_vanzare = V_ID_VANZARE and vd.id_valuta = vc.id_valuta`
|
||||
(`:13929-13930`): daca o linie ajunge cu o valuta pentru care documentul nu are curs, `vc.curs` e
|
||||
NULL si `pret_ron` iese NULL. `CreeazaPoArticolNouTvd` primeste explicit valuta documentului
|
||||
(`tnIdValutaDoc`, `ofacturare_editare.prg:356`), deci in fluxul proiectat nu ar trebui sa apara.
|
||||
**NEVERIFICAT** ca S4 chiar transmite acel parametru pe toate caile de adaugare.
|
||||
*Recomandare:* verificare in S4b, nu garda in Oracle.
|
||||
|
||||
**R6 — `pack_sesiune.getOptiuneFirma` intoarce `''` la orice eroare. (mic)**
|
||||
`PACK_SESIUNE` body `:128-131`: `WHEN OTHERS THEN lcValue := ''`. Atribuit intr-un `NUMBER(2)`, `''`
|
||||
devine NULL, iar `ROUND(x, NULL)` da NULL. Comportament identic cu cel de la emitere — deci nu e o
|
||||
regresie introdusa de S5, dar merita stiut daca apar totaluri NULL inexplicabile.
|
||||
*Recomandare:* nimic de facut in S5; consemnat pentru depanare.
|
||||
|
||||
**R7 — `ff_2026_08_08_01_COMUN_VVD_TOT.sql` aplicat in dev fara script pe disc. (de clarificat)**
|
||||
Obiectul nu exista in `all_objects` sub niciun nume `VVD%`, deci probabil scriptul a creat altceva
|
||||
sau a fost anulat ulterior. Fisierul lipseste din `SCRIPTURI_CLAR` -> nu ajunge la clienti.
|
||||
*Recomandare:* intrebare pentru Marius, nu blocheaza S5.
|
||||
|
||||
### Intrebari pentru Marius
|
||||
|
||||
1. **Liniile de set (R1)** — le facem needitabile in grid, cu `ID_VANZARE_SET` adaugat in
|
||||
`VVANZARI_ARTICOLE` printr-un al doilea script? *Recomandarea mea: da.* Fara asta, editarea unei
|
||||
facturi cu seturi arata ca merge si nu merge.
|
||||
2. **Discountul de document** — parametru al procedurii (`V_DISCOUNT ... DEFAULT NULL`), sau
|
||||
`UPDATE VANZARI SET DISCOUNT` separat din VFP? *Recomandarea mea: parametru*, ca discountul si
|
||||
totalurile sa nu poata diverge (vezi 5).
|
||||
3. **`PRET_ACHIZITIE` pe linia noua (R3)** — se citeste din nomenclator/stoc la adaugare, sau se
|
||||
scrie `0` cu avertizare? *Recomandarea mea: citit din nomenclator*, cu `0` doar ca ultima
|
||||
rezerva, niciodata NULL.
|
||||
4. **`VALVAL`/`TVAVAL`/`TOTVAL`** — raman in recalcul? *Recomandarea mea: da* (cost zero, fac parte
|
||||
din acelasi `SELECT`; excluderea lor ar lasa facturile in valuta inconsistente). Confirmare
|
||||
ceruta si in raportul precedent, inca neconfirmata.
|
||||
5. **`ff_2026_08_08_01_COMUN_VVD_TOT.sql`** — script de lucru abandonat, sau lipseste din SVN?
|
||||
|
||||
---
|
||||
|
||||
## Ce a ramas neverificat
|
||||
|
||||
- **R5**: nu am verificat pe codul S4 in lucru ca `CreeazaPoArticolNouTvd` primeste efectiv valuta
|
||||
documentului pe toate caile de adaugare de linie.
|
||||
- Nu am rulat nimic in Oracle in afara de `SELECT`-uri de dictionar si de export — **niciun DDL,
|
||||
niciun DML, niciun test de executie a procedurii propuse.** Corpul propus la 7 e schita, nu cod
|
||||
compilat.
|
||||
- Numarul de documente cu seturi in `MARIUSM_AUTO` (4) e din date de test si **nu constituie
|
||||
dovada** pentru niciuna dintre afirmatiile de mai sus; toate concluziile sunt din cod.
|
||||
296
docs/cercetare/rec_s5_script_oracle.md
Normal file
296
docs/cercetare/rec_s5_script_oracle.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Verificare script Oracle S5 — PACK_FACTURARE.recalculeaza_totaluri_vanzari
|
||||
|
||||
Data: 09.08.2026. Livrabil: `docs/ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (823433 octeti,
|
||||
17231 randuri; 17010->17231 fata de `.pck`-ul sursa, +221 randuri de continut adaugat).
|
||||
|
||||
Construit programatic dintr-un script PowerShell (nu retastat): citeste `PACK_FACTURARE.pck`
|
||||
(export proaspat, scratchpad, 810960 octeti, 17011 randuri) ca octeti ASCII, insereaza declaratia
|
||||
noua in SPEC dupa `actualizeaza_vanzari` si corpul nou in BODY dupa `actualizeaza_vanzari`, adauga
|
||||
`CREATE OR REPLACE` pe cele doua linii de start (absente in exportul brut din `all_source`),
|
||||
antetul si coada, si scrie rezultatul CRLF. Fiecare punct de insertie e verificat printr-un
|
||||
assert pe textul exact al ancorei (throw daca nu se potriveste) — scriptul s-a oprit si a fost
|
||||
corectat de doua ori in timpul lucrului (vezi „Erori prinse" mai jos), rularea finala a trecut
|
||||
toate ancorele.
|
||||
|
||||
## Ce s-a facut
|
||||
|
||||
1. **PACKAGE SPEC** (`.pck:1187-1189`): dupa declaratia `actualizeaza_vanzari`, s-a inserat
|
||||
`PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER, V_DISCOUNT IN NUMBER DEFAULT
|
||||
NULL);` — text identic cu cel din brief.
|
||||
2. **PACKAGE BODY** (`.pck:16015-16025`, dupa `END actualizeaza_vanzari;`): s-a inserat procedura
|
||||
noua, corpul fiind blocul de agregare din `scrie_in_vanzari` (`.pck:13762-13954`) cu substitutiile
|
||||
cerute, plus SELECT-ul de discount/in_valuta/discount_evidentiat inainte, plus `discount` in
|
||||
UPDATE, fara handler de exceptie — toate exact ca in sectiunea 7 a specificatiei.
|
||||
3. Antet de 6 randuri (4 randuri text + 1 rand `--` gol + titlu), fara referinte la planuri/rapoarte.
|
||||
4. Coada: `exec pack_migrare.UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql'); commit;`
|
||||
|
||||
## Verificari cerute, cu cifre
|
||||
|
||||
**CRLF** — octeti LF fara CR inainte in fisierul final: **0**.
|
||||
|
||||
**Nume sub 30 caractere** — `'recalculeaza_totaluri_vanzari'.Length` = **29**. Confirmat.
|
||||
|
||||
**Diff-ul blocului de agregare** — original (`.pck:13762-13954`, 193 randuri, citat integral in
|
||||
`rec_s5_proiectare_oracle.md` sectiunea 2) vs. blocul nou din procedura (extras din fisierul
|
||||
livrat). Generat cu `diff -u -b` (ignora *doar* diferentele de cantitate de spatiu — necesar
|
||||
pentru ca tot blocul a fost mutat cu un nivel de indentare mai putin, vezi nota de mai jos; `-b`
|
||||
nu ascunde nicio diferenta de continut). Diff-ul integral:
|
||||
|
||||
```diff
|
||||
--- orig_block.txt (PACK_FACTURARE.pck:13762-13954)
|
||||
+++ new_block.txt (recalculeaza_totaluri_vanzari, corpul agregarii)
|
||||
@@ -1,5 +1,4 @@
|
||||
- -- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
- begin
|
||||
+-- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
select DISC_TVA_VAL AS DISCOUNT_TVA,
|
||||
VALOARE_ACHIZITIE,
|
||||
a.suma_fara_tva_ron - a.disc_fara_tva_ron as TOTAL_FARA_TVA,
|
||||
@@ -12,11 +11,7 @@
|
||||
a.disc_tva_val as TOTVAL,
|
||||
id_valuta,
|
||||
curs,
|
||||
- multiplicator,
|
||||
- pack_facturare.cserie_act_incasare as SERIE_INCASAT,
|
||||
- pack_facturare.nnumar_act_incasare as NR_INCASAT,
|
||||
- pack_facturare.nsuma_incasare AS SUMA_INCASAT,
|
||||
- pack_facturare.ntip_doc_incasare as TIP_INCASAT
|
||||
+ multiplicator
|
||||
INTO lnDiscountTVA,
|
||||
lnValoareAchizitie,
|
||||
lnTotalFaraTVA,
|
||||
@@ -27,29 +22,25 @@
|
||||
lnTotVal,
|
||||
lnIdValuta,
|
||||
lnCurs,
|
||||
- lnMultiplicator,
|
||||
- lnSerieIncasat,
|
||||
- lnNrIncasat,
|
||||
- lnSumaIncasat,
|
||||
- lnTipIncasat
|
||||
- FROM (select MAX(decode(pack_facturare.nin_valuta,
|
||||
+ lnMultiplicator
|
||||
+ FROM (select MAX(decode(lnInValuta,
|
||||
1,
|
||||
- ROUND(a1.curs * NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
+ ROUND(a1.curs * NVL(lnDiscountFactura, 0) /
|
||||
a1.multiplicator,
|
||||
lnPreciziePretV),
|
||||
- NVL(V_DISCOUNT_FACTURA, 0))) as DISC_FARA_TVA_RON,
|
||||
- NVL(V_DISCOUNT_FACTURA, 0) as DISC_FARA_TVA_VAL,
|
||||
- pack_facturare.nin_valuta AS IN_VALUTA,
|
||||
- MAX(ROUND(decode(pack_facturare.nin_valuta,
|
||||
+ NVL(lnDiscountFactura, 0))) as DISC_FARA_TVA_RON,
|
||||
+ NVL(lnDiscountFactura, 0) as DISC_FARA_TVA_VAL,
|
||||
+ lnInValuta AS IN_VALUTA,
|
||||
+ MAX(ROUND(decode(lnInValuta,
|
||||
1,
|
||||
ROUND(a1.curs *
|
||||
- NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
+ NVL(lnDiscountFactura, 0) /
|
||||
a1.multiplicator,
|
||||
lnPreciziePretV),
|
||||
- NVL(V_DISCOUNT_FACTURA, 0)) *
|
||||
+ NVL(lnDiscountFactura, 0)) *
|
||||
(a1.proc_tvav - 1),
|
||||
lnPreciziePretV)) as DISC_TVA_RON,
|
||||
- MAX(ROUND(NVL(V_DISCOUNT_FACTURA, 0) *
|
||||
+ MAX(ROUND(NVL(lnDiscountFactura, 0) *
|
||||
(a1.proc_tvav - 1),
|
||||
lnPreciziePretV)) as DISC_TVA_VAL,
|
||||
sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_ron,
|
||||
@@ -57,7 +48,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_ron,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) AS SUMA_FARA_TVA_RON,
|
||||
@@ -66,7 +57,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_ron,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) as SUMA_TVA_RON,
|
||||
@@ -75,7 +66,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_val,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) AS SUMA_FARA_TVA_VAL,
|
||||
@@ -84,18 +75,18 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_val,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) as SUMA_TVA_VAL,
|
||||
sum(round(a1.cantitate * a1.pret_achizitie,
|
||||
lnPrecizieCalcul)) AS VALOARE_ACHIZITIE,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.curs, 1)) as curs,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
+ max(decode(lnInValuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
+ max(decode(lnInValuta, 1, a1.curs, 1)) as curs,
|
||||
+ max(decode(lnInValuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
from (select vd.id_vanzare_set,
|
||||
(case
|
||||
- when (pack_facturare.nin_valuta = 1 or
|
||||
+ when (lnInValuta = 1 or
|
||||
vd.id_valuta <>
|
||||
pack_def.GetIdMonedaNationala()) then
|
||||
ROUND(vc.curs * vd.pret / vc.multiplicator,
|
||||
@@ -108,7 +99,7 @@
|
||||
vd.cantitate,
|
||||
vd.diferenta,
|
||||
(case
|
||||
- when (pack_facturare.nin_valuta = 1 or
|
||||
+ when (lnInValuta = 1 or
|
||||
vd.id_valuta <>
|
||||
pack_def.GetIdMonedaNationala()) then
|
||||
ROUND(vc.curs * vd.discount_unitar /
|
||||
@@ -132,8 +123,10 @@
|
||||
a.id_valuta,
|
||||
a.pret_cu_tva,
|
||||
a.pret_achizitie
|
||||
- from VANZARI_DETALII_TEMP a
|
||||
+ from VANZARI_DETALII a
|
||||
where nvl(a.id_vanzare_set, 0) = 0
|
||||
+ and a.id_vanzare = V_ID_VANZARE
|
||||
+ and a.sters = 0
|
||||
union all
|
||||
select b.id_vanzare_set,
|
||||
b.pret,
|
||||
@@ -141,7 +134,7 @@
|
||||
b.cantitate,
|
||||
0 as diferenta,
|
||||
b.discount_unitar,
|
||||
- decode(pack_facturare.nin_valuta,
|
||||
+ decode(lnInValuta,
|
||||
0,
|
||||
pack_def.GetIdMonedaNationala(),
|
||||
c.id_valuta) as id_valuta,
|
||||
@@ -151,17 +144,19 @@
|
||||
0,
|
||||
c.pret_achizitie * c.cantitate /
|
||||
b.cantitate)) as pret_achizitie
|
||||
- from vanzari_detalii_temp c
|
||||
+ from vanzari_detalii c
|
||||
left join vanzari_seturi b
|
||||
on b.id_vanzare_set = c.id_vanzare_set
|
||||
where nvl(c.id_vanzare_set, 0) <> 0
|
||||
- and nvl(pack_facturare.nin_valuta, -1) > -1
|
||||
+ and c.id_vanzare = V_ID_VANZARE
|
||||
+ and c.sters = 0
|
||||
+ and nvl(lnInValuta, -1) > -1
|
||||
group by b.id_vanzare_set,
|
||||
b.pret,
|
||||
b.cantitate,
|
||||
b.discount_unitar,
|
||||
b.pret_cu_tva,
|
||||
- decode(pack_facturare.nin_valuta,
|
||||
+ decode(lnInValuta,
|
||||
0,
|
||||
pack_def.GetIdMonedaNationala(),
|
||||
c.id_valuta)) vd
|
||||
@@ -170,7 +165,8 @@
|
||||
and vd.id_valuta = vc.id_valuta) a1) a;
|
||||
|
||||
update vanzari
|
||||
- set discount_tva = lnDiscountTVA,
|
||||
+ set discount = lnDiscountFactura,
|
||||
+ discount_tva = lnDiscountTVA,
|
||||
valoare_achizitie = lnValoareAchizitie,
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
total_tva = lnTotalTVA,
|
||||
@@ -180,14 +176,5 @@
|
||||
totval = lnTotVal,
|
||||
id_valuta = lnIdValuta,
|
||||
curs = lnCurs,
|
||||
- multiplicator = lnMultiplicator,
|
||||
- serie_incasat = lnSerieIncasat,
|
||||
- nr_incasat = lnNrIncasat,
|
||||
- suma_incasat = lnSumaIncasat,
|
||||
- tip_incasat = lnTipIncasat
|
||||
+ multiplicator = lnMultiplicator
|
||||
where id_vanzare = V_ID_VANZARE;
|
||||
-
|
||||
- exception
|
||||
- when NO_DATA_FOUND then
|
||||
- null;
|
||||
- end;
|
||||
```
|
||||
|
||||
Diff-ul contine **exact**: cele 6 substitutii din tabelul din brief (`nin_valuta`->`lnInValuta` x11,
|
||||
`ndiscount_evidentiat`->`lnDiscountEvidentiat` x4, `NVL(V_DISCOUNT_FACTURA, 0)`->
|
||||
`NVL(lnDiscountFactura, 0)` x6, cele doua perechi FROM/WHERE), eliminarea celor 4 coloane de
|
||||
incasare din SELECT/INTO/UPDATE (cu fixarea virgulei ramase), adaugarea `discount =
|
||||
lnDiscountFactura,` in UPDATE si eliminarea wrapper-ului `begin ... exception ... end;` (cerut
|
||||
explicit: „Fara handler WHEN NO_DATA_FOUND THEN NULL"). **Nicio alta diferenta de continut.**
|
||||
|
||||
Nota pe metoda: `-b` a fost necesar (nu `diff` simplu) pentru ca tot blocul, o data scos din
|
||||
`begin...end;`-ul intern, a coborat cu un nivel de indentare (2 spatii) — o consecinta mecanica,
|
||||
uniforma, a aplatizarii cerute de sectiunea 7, nu o modificare de continut. Fara `-b`, diff-ul ar
|
||||
fi aratat *toate* liniile ca schimbate, desi doar spatiul de inceput difera pe liniile
|
||||
neatinse de tabelul de substitutii (verificat separat: liniile fara nicio substitutie, ex.
|
||||
`select DISC_TVA_VAL AS DISCOUNT_TVA,`, `VALOARE_ACHIZITIE,`, nu apar deloc in diff-ul de mai sus).
|
||||
|
||||
**`;` in comentarii `--` in interiorul unei instructiuni** — 11 aparitii ale tiparului `--.*;` in
|
||||
tot fisierul, **toate preexistente** in codul neatins (`scrie_incasari`, `contabilizeaza_articol`
|
||||
etc., linii 5908-14852 din script), **niciuna** introdusa de mine (verificat separat: 0 in antetul
|
||||
nou, 0 in declaratia SPEC noua, 0 in corpul noii proceduri). Riscul descris in
|
||||
`scripturi-migrare-db.md` (SP2-0734) se aplica instructiunilor SQL terminate cu `;`
|
||||
(`CREATE VIEW` etc.); intregul script de fata e un singur bloc `CREATE OR REPLACE PACKAGE`/
|
||||
`PACKAGE BODY` terminat cu `/`, deci riscul nu se aplica structural — dar cifra e cea ceruta.
|
||||
|
||||
**Constructii peste Oracle 10.2** — scanat corpul noii proceduri pentru
|
||||
`LISTAGG|CONTINUE|REGEXP_COUNT|FETCH FIRST|PIVOT|NEXTVAL`: **0 aparitii** pentru fiecare.
|
||||
Identificatori: cel mai lung e `recalculeaza_totaluri_vanzari` (29) si `lnDiscountEvidentiat` (20)
|
||||
— niciunul peste 30.
|
||||
|
||||
**Numarul de linii** — fisier final: **17231** (17230 dupa convenția `wc -l`, care nu numara
|
||||
ultimul rand fiindca fisierul, la fel ca sursa, nu are newline final); `.pck` original:
|
||||
**17011** (17010 `wc -l`). Delta: +221 randuri adaugate (antet 7 + insert SPEC 3 + rand gol inainte
|
||||
de `CREATE OR REPLACE PACKAGE BODY` 1 + procedura noua in BODY 205 + coada 4 + `CREATE OR REPLACE`
|
||||
adaugat pe 2 linii existente, fara linii noi acolo).
|
||||
|
||||
**Coloanele de incasare** — `serie_incasat`, `nr_incasat`, `suma_incasat`, `tip_incasat`: **0**
|
||||
aparitii in SELECT/INTO/UPDATE-ul noii proceduri (verificat separat, izolat pe textul extras al
|
||||
procedurii). Apar in continuare de 5 ori fiecare **in restul fisierului** — in `scrie_in_vanzari`,
|
||||
neatinsa, unde e corect sa ramana (comportamentul de emitere nu se schimba).
|
||||
|
||||
## Erori prinse si corectate in timpul lucrului (nu au ajuns in fisierul livrat)
|
||||
|
||||
1. Prima rulare a omis complet linia `discount = lnDiscountFactura,` din `UPDATE` (am copiat doar
|
||||
eliminarea coloanelor de incasare, am uitat adaugarea cerincetei separat in brief). Prins de
|
||||
verificarea numerica (`lnDiscountFactura` aparea de 8 ori in loc de 9) inainte de a scrie
|
||||
raportul; corectat si re-rulat.
|
||||
2. Prima rulare a scris `PACKAGE "PACK_FACTURARE" is` si `PACKAGE BODY PACK_FACTURARE is` fara
|
||||
prefixul `CREATE OR REPLACE` pe **linia de SPEC** (l-am adaugat doar pe linia de BODY). Ar fi
|
||||
dat eroare de sintaxa la aplicare — `PACKAGE ... is` singur nu e o instructiune DDL valida.
|
||||
Prins prin citirea directa a antetului fisierului scris; corectat si re-rulat.
|
||||
|
||||
## Ce NU am facut / neverificat
|
||||
|
||||
- Nu am rulat nimic in Oracle — niciun `sqlplus`, niciun DDL, niciun test de compilare a
|
||||
pachetului. Corectitudinea sintactica dincolo de verificarile de mai sus (paranteze, virgule,
|
||||
cuvinte cheie) nu e garantata decat prin inspectie si prin construirea mecanica din blocul
|
||||
original deja compilat.
|
||||
- Coada scriptului foloseste `UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql')` **cu**
|
||||
extensia `.sql`, asa cum cere explicit brief-ul si `scripturi-migrare-db.md` („script_final =
|
||||
numele fisierului, cu tot cu .sql"). Modelul citat (`ff_2026_08_06_10_...sql:17019`) foloseste
|
||||
de fapt numele **fara** `.sql` — o inconsistenta intre precedent si regula scrisa. Am urmat
|
||||
regula scrisa si instructiunea explicita, nu precedentul; semnalez discrepanta, nu am
|
||||
„reparat-o" in modelul vechi (nu era in scop).
|
||||
- Nu am atins `versiune_db.txt`, nu am scris in `D:\ROA\DATABASE\SCRIPTURI_CLAR`, nu am dat
|
||||
commit — conform interdictiilor din brief.
|
||||
341
docs/cercetare/rec_tva_vanzari.md
Normal file
341
docs/cercetare/rec_tva_vanzari.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Cercetare: TVA calculat vs salvat (#7) + denormalizare VANZARI (#8)
|
||||
|
||||
Sursele principale sunt fisierele "curate" din arhiva de migrare Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\...` (nu in working copy VFP git — DDL/PL-SQL nu e versionat in
|
||||
`ROAFACTURARE`), plus `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare.vc2` (working copy VFP, text
|
||||
`.vc2` deja la zi).
|
||||
|
||||
Cea mai recenta definitie **completa** a pachetului `PACK_FACTURARE` (COMUN, folosit de toate
|
||||
produsele ROA care factureaza) e in
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (16948 linii).
|
||||
Toate liniile citate mai jos fara alta mentiune sunt din acest fisier.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT A — TVA calculat, nu salvat (#7)
|
||||
|
||||
### 1. Structura VANZARI / VANZARI_DETALII (coloane pret/TVA)
|
||||
|
||||
Nu exista `CREATE TABLE VANZARI_DETALII` in arhiva cautata (tabela e mai veche decat
|
||||
`SCRIPTURI_CLAR`, care incepe efectiv din 2009; originea e probabil in
|
||||
`D:\ROA\DATABASE\SCRIPTURI\2006-2008`, nu am parcurs exhaustiv acel arhiv de migrari vechi).
|
||||
Coloanele efective au fost confirmate prin utilizarea lor in cod (view-uri + SQL dinamic VFP):
|
||||
|
||||
**VANZARI_DETALII** (per linie articol):
|
||||
- `PRET` — pret unitar (fara/cu TVA, in functie de flag)
|
||||
- `DIFERENTA` — ajustare pret (folosita in `calculeaza_total_*_fact`)
|
||||
- `DISCOUNT_UNITAR`
|
||||
- `CANTITATE`
|
||||
- `PRET_CU_TVA` — flag NUMBER(1): 1 = pretul de mai sus e cu TVA inclus, 0 = fara TVA
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:25,43` — `b.pret_cu_tva`)
|
||||
- `PROC_TVAV` — procent TVA ca multiplicator (ex. 1.19), nu procent brut
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:26` — `b.proc_tvav`)
|
||||
- `PRET_ACHIZITIE` (`ff_2026_07_27_01_FACTURARE.sql:105,130`)
|
||||
- `ID_ARTICOL`, `ID_VALUTA`, `ID_POL` (politica de pret), `SERIE`, `STERS`, `ID_VANZARE`,
|
||||
`ID_VANZARE_DET` (`ff_2026_07_27_01_FACTURARE.sql:11-76`)
|
||||
- `TAXCODE` — adaugata `2022\03\ff_2022_03_24_01_COMUN_SAFT.sql:6` (cod taxa SAFT)
|
||||
- `ID_JTVA_COLOANA_EX` — adaugata `2019\08\ff_2019_08_20_04_COMUN.sql:7`
|
||||
- `LOT` — adaugata `2024\06\ff_2024_06_04_01_COMUN_FACTURARE.sql:3`
|
||||
|
||||
**NU exista** o coloana de TVA calculata/salvata per linie (nici `valoare_tva`, nici
|
||||
`pret_fara_tva` rezultat) — doar inputurile (`pret`, `pret_cu_tva` flag, `proc_tvav`), confirmand
|
||||
exact ce zice todo #7: TVA e derivat, nu stocat.
|
||||
|
||||
**VANZARI** (antet factura) — coloane denormalizate relevante TVA, toate adaugate prin
|
||||
`2017\03\ff_2017_03_28_01_FACTURARE.sql:9-38`:
|
||||
- `DISCOUNT_TVA` — "TVA DISCOUNT (DISCOUNT * MAX(PROC_TVA) ARTICOLE)"
|
||||
- `TOTAL_FARA_TVA`, `TOTAL_TVA`, `TOTAL_CU_TVA` — totaluri pe document (denormalizate)
|
||||
- `VALOARE_ACHIZITIE`
|
||||
- vezi si Subiect B pt. `SERIE_INCASAT`/`NR_INCASAT`/`SUMA_INCASAT`/`TIP_INCASAT`/`AVIZE`
|
||||
|
||||
### 2. Unde se calculeaza TVA-ul pe linie — toate locurile
|
||||
|
||||
Calculul e centralizat in doua straturi Oracle, apelate de fiecare punct de consum (VFP nu face
|
||||
calcul TVA local — doar construieste SQL dinamic care cheama functiile Oracle):
|
||||
|
||||
- **`PACK_SESIUNE.calculeaza_pret_fara_tva/tva/cu_tva`** — motorul de rotunjire per-unitate de
|
||||
pret (nu e in `PACK_FACTURARE`; pachetul `PACK_SESIUNE` nu a fost gasit definit separat in
|
||||
arhiva cautata — cautare punctuala pe cateva luni din 2025/2026, fara succes; probabil e intr-un
|
||||
script `COMUN_PACK_SESIUNE` mai vechi, netestat exhaustiv).
|
||||
- **`PACK_FACTURARE.calculeaza_pret`** (linia 14557) — wrapper subtire peste cele 3 functii
|
||||
`PACK_SESIUNE` de mai sus.
|
||||
- **`PACK_FACTURARE.calculeaza_sume`** (linia 14631, spec 980) — calculeaza suma_fara_tva /
|
||||
suma_tva / suma_cu_tva pe linie (cantitate * pret), delegand catre:
|
||||
- **`calculeaza_total_fara_tva`** (linia 15745/15769) — delega la `pack_sesiune.calculeaza_total_fara_tva`
|
||||
- **`calculeaza_total_tva`** (linia 15850/15874) — delega la `pack_sesiune.calculeaza_total_tva`
|
||||
- **`calculeaza_total_cu_tva`** (linia 15638/15662) — delega la `pack_sesiune.calculeaza_total_cu_tva`
|
||||
- variantele `_fact` (`calculeaza_total_fara_tva_fact` 15794, `calculeaza_total_tva_fact` 15899,
|
||||
`calculeaza_total_cu_tva_fact` 15687) au **formula proprie inline** (nu delega la
|
||||
`pack_sesiune`), documentata explicit in cod ca fiind "folosita in view-ul
|
||||
fact_vrap_centralizator_fact" (comentarii 15686, 15744, 15793, 15848, 15897).
|
||||
|
||||
Formula `_fact` (exemplu `calculeaza_total_tva_fact`, 15899-15949) e explicit ROUND-based, cu
|
||||
ramuri separate pentru `discount_evidentiat` — aici e punctul central de rotunjire per linie.
|
||||
|
||||
- **Puncte de CONSUM ale acestor functii** (unde efectiv se calculeaza TVA afisat/raportat):
|
||||
- Views `fact_vrap_fact_articole` si `fact_vrap_articole_vandute`
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:18-44, 131-196` — apeleaza
|
||||
`calculeaza_total_fara_tva`/`calculeaza_total_tva` direct in `SELECT`)
|
||||
- Raport VFP dinamic: `COMUN\programe\oproceduri_rapoarte_fact.prg:581-590` — SQL construit in
|
||||
VFP apeleaza direct `pack_sesiune.calculeaza_pret_cu_tva(...)` si
|
||||
`pack_sesiune.calculeaza_total_cu_tva(...)` pentru listare/relistare rapoarte articole vandute.
|
||||
- `PACK_FACTURARE.scrie_in_vanzari` (13468) si `finalizeaza_avize_lucrare` (14807) — calculeaza
|
||||
si scriu `VANZARI.TOTAL_FARA_TVA`/`TOTAL_CU_TVA` folosind `calculeaza_total_fara_tva_fact` in
|
||||
subquery-uri (13716-13786, 14965-15008).
|
||||
- `verifica_total_document` (16009) — vezi punctul 5, verifica totalul calculat vs. cel din
|
||||
notele contabile si genereaza o corectie.
|
||||
|
||||
Nu am gasit un apel VFP direct catre `calculeaza_pret(` sau `calculeaza_sume(` in working copy
|
||||
`ROAFACTURARE`/`COMUN` (grep pe nume exact, fara rezultate) — deci formularul de introducere
|
||||
factura in VFP nu recalculeaza local pretul/TVA-ul linie cu linie prin apel explicit de
|
||||
procedura; cel mai probabil articolele + preturile lor (deja cu `pret`, `pret_cu_tva`,
|
||||
`proc_tvav`) vin dintr-un cursor Oracle (`PACK_FACTURARE.cursor_preturi`, spec 335, body 2121)
|
||||
populat cand se adauga articolul, iar TVA vizibila in grid e calculata la afisare din acele
|
||||
coloane brute — nu am gasit expresia VFP exacta (`cursor_preturi` e ~500 linii, nu am parcurs
|
||||
linie cu linie in bugetul acestei cercetari).
|
||||
|
||||
### 3. Ce inseamna "preturi_cu_tva" / `pret_cu_tva`
|
||||
|
||||
- Pe linie: `VANZARI_DETALII.PRET_CU_TVA` — NUMBER(1), 0/1, transmis ca parametru `V_PRET_CU_TVA`
|
||||
/ `V_PRET_ARE_TVA` la toate functiile de calcul de mai sus (controleaza care ramura de formula
|
||||
se aplica — pornind de la pret cu TVA sau fara TVA).
|
||||
- In grid-ul VFP exista o coloana **checkbox** `cPret_cu_tva` in
|
||||
`D:\ROA\ROAFACTURARE\Clase\ofundal_facturare.vc2:696-699` (`_grdrow2.cPret_cu_tva...`) — probabil
|
||||
afiseaza/permite editarea flagului per linie in formular.
|
||||
- **Nu am gasit** coloana `PRET_CU_TVA` (sau similara) direct pe `CRM_POLITICI_PRETURI` in
|
||||
scripturile de migrare cautate (am cautat `ALTER TABLE CRM_POLITICI_PRETURI ADD`, fara hit
|
||||
pe acest nume) — deci originea exacta a valorii implicite per politica de pret ("politica are
|
||||
bifat 'preturi fara TVA'", cum descrie todo #7) nu e confirmata cu `fisier:linie`; e probabil
|
||||
citita/propagata in `PACK_FACTURARE.cursor_preturi` (2121) sau
|
||||
`citeste_setari_pol_pret` (spec 324, body 2025) cand se adauga articolul pe factura — nu am
|
||||
parcurs acele corpuri in detaliu (buget de cercetare).
|
||||
|
||||
### 4. Locuri de atins daca s-ar salva `pret_cu_tva`/`valoare_tva` per linie real (nu calculat)
|
||||
|
||||
Enumerare pe baza punctelor de consum gasite mai sus:
|
||||
|
||||
1. **DDL**: `ALTER TABLE VANZARI_DETALII ADD (valoare_tva, pret_fara_tva_calculat, ...)` — coloane
|
||||
noi + migrare date istorice.
|
||||
2. **`PACK_FACTURARE`** (Oracle, `ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`):
|
||||
- `adauga_articol_factura` (spec 529, body 4972) — punctul unde se insereaza linia in
|
||||
`VANZARI_DETALII_TEMP`; ar trebui sa scrie valoarea (eventual editata de user) in loc s-o
|
||||
lase doar pe `pret`/`pret_cu_tva`/`proc_tvav`.
|
||||
- `calculeaza_sume` (14631) — daca valoarea e editabila, aceasta procedura ar trebui sa
|
||||
citeasca valoarea salvata in loc s-o recalculeze, sau sa fie ocolita.
|
||||
- `scrie_in_vanzari` (13468) si `finalizeaza_avize_lucrare` (14807) — subquery-urile care insumeaza
|
||||
`calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact` pe toate liniile
|
||||
(13716-13786, 14965-15008) ar trebui sa insumeze coloana noua in loc s-o recalculeze.
|
||||
- `verifica_total_document` (16009) — logica de reconciliere total-vs-note-contabile ar trebui
|
||||
ajustata (vezi punct 5) daca totalul nu se mai deriva strict din formula.
|
||||
3. **Views Oracle**: `fact_vrap_fact_articole`, `fact_vrap_articole_vandute`
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:10-257`), `fact_vfacturi`/`fact_vfacturi2` (vezi Subiect B #11)
|
||||
— toate ar trebui sa citeasca noua coloana in loc de `calculeaza_total_*`.
|
||||
4. **VFP**: `COMUN\programe\oproceduri_rapoarte_fact.prg:581-590` — SQL-ul de raport care apeleaza
|
||||
direct `pack_sesiune.calculeaza_pret_cu_tva`/`calculeaza_total_cu_tva`.
|
||||
5. **VFP grid formular**: `D:\ROA\ROAFACTURARE\Clase\ofundal_facturare.vc2` (coloana
|
||||
`cPret_cu_tva` + coloanele de pret/valoare din grid — nu identificate exhaustiv, cautare
|
||||
punctuala doar pe flag).
|
||||
6. **Rapoarte `.frx`** care afiseaza TVA pe linie de factura (ex. `usr_factura*.fr2` — 25 de
|
||||
fisiere gasite cu referinta la `PACK_FACTURARE`, listate la cautarea initiala; nu au fost
|
||||
deschise individual).
|
||||
|
||||
Estimare: minim **6 zone distincte** (DDL, pachet Oracle ~4 proceduri, 3-4 views, 1 program raport
|
||||
VFP, grid formular, rapoarte `.frx`) — consistent cu observatia ca e o schimbare "de volum", nu
|
||||
locala.
|
||||
|
||||
### 5. Mecanism existent de ajustare/rotunjire
|
||||
|
||||
**Da, exista, dar la nivel contabil, nu la nivel de linie facturata / vizibil userului.**
|
||||
|
||||
`PACK_FACTURARE.verifica_total_document` (linia 16009-16188+) compara totalul FTVA/TVA asteptat
|
||||
(`pack_facturare.ntotftva`, populat din VFP la `pnTotalFtva`/`Thisform.nbazaron`) cu suma reala din
|
||||
notele contabile generate (`ACT_TEMP`, grupate pe conturi `4111`/`4427`/`418`/`4428`). Daca difera
|
||||
(16082-16083: `NVL(pack_facturare.ntotftva,0) <> V_TOTFTVA_VER`), calculeaza diferenta
|
||||
`pack_facturare.ndifftva := pack_facturare.ntotftva - V_TOTFTVA_VER` (16085) si **insereaza automat
|
||||
o linie de corectie in `ACT_TEMP`** (16086-16188, INSERT cu `suma => pack_facturare.ndifftva`) —
|
||||
adica ajustarea de rotunjire se absoarbe silentios in nota contabila, nu apare ca linie editabila
|
||||
pe factura si nu se reflecta in `VANZARI_DETALII`. Aceasta e probabil exact mecanismul care face ca
|
||||
utilizatorul sa nu poata corecta manual TVA-ul afisat: rotunjirea se "repara" doar in contabilitate,
|
||||
dupa fapt.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT B — Denormalizare VANZARI, bug facturi din avize + numerar (#8)
|
||||
|
||||
### 6. Locatia `PACK_FACTURARE`
|
||||
|
||||
Nu exista fisier `.pck`/`.sql` cu acest pachet in working copy `ROAFACTURARE` (nici in `COMUN/`
|
||||
local) — **e in afara working copy-ului VFP**, in arhiva DDL/PL-SQL Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\`. Cea mai recenta versiune completa (16948 linii):
|
||||
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`
|
||||
|
||||
(exista si un diff mai nou, doar pe view-uri, `2026\07\ff_2026_07_27_01_FACTURARE.sql` — vezi #11).
|
||||
Istoricul complet de modificari e in `SCRIPTURI_CLAR\<an>\<luna>\ff_..._COMUN_PACK_FACTURARE.sql`
|
||||
respectiv `..._FACTURARE.sql` (zeci de fisiere, 2006-2026).
|
||||
|
||||
### 7. Procedura care scrie in VANZARI valorile denormalizate
|
||||
|
||||
**`PACK_FACTURARE.scrie_in_vanzari`** (spec linia 894, body **linia 13468-13908**) — apelata din
|
||||
`finalizeaza_factura` (linia **14740**). Face `UPDATE VANZARI SET ...` cu (13886-13898):
|
||||
|
||||
```
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
...
|
||||
total_cu_tva = lnTotalCuTVA,
|
||||
...
|
||||
serie_incasat = lnSerieIncasat,
|
||||
nr_incasat = lnNrIncasat,
|
||||
suma_incasat = lnSumaIncasat,
|
||||
tip_incasat = lnTipIncasat
|
||||
```
|
||||
|
||||
unde `lnSerieIncasat`/`lnNrIncasat`/`lnSumaIncasat`/`lnTipIncasat` sunt populate (13727-13730) din
|
||||
variabilele **de pachet** `pack_facturare.cserie_act_incasare`, `.nnumar_act_incasare`,
|
||||
`.nsuma_incasare`, `.ntip_doc_incasare` (declarate 179-182).
|
||||
|
||||
O a doua ramura echivalenta exista in **`finalizeaza_avize_lucrare`** (spec 1011, body
|
||||
**14807-15176**), care face acelasi UPDATE (15124-15130) — pentru ramura "avize pe lucrare".
|
||||
|
||||
Variabilele de pachet `cserie_act_incasare`/`nnumar_act_incasare`/`ntip_doc_incasare`/
|
||||
`nsuma_incasare` sunt setate **doar** de **`PACK_FACTURARE.scrie_incasari`** (spec 864, body
|
||||
**13070-13139**), apelata condiționat (`IF V_LISTA_INCASARE IS NOT NULL`) din:
|
||||
- `scrie_factura2` — linia **6178** (ramura factura normala / comanda / contract)
|
||||
- `scrie_factura_avize` — linia **7008** (ramura **factura din aviz**)
|
||||
|
||||
Sunt resetate la `NULL` in `initializeaza_date_factura` (comentariu 1384-1386, cod la
|
||||
**1876-1880**) — fix explicit din 03.07.2020: *"ramaneau completate pe facturile urmatoare de la o
|
||||
factura anterioara"*.
|
||||
|
||||
`VANZARI.AVIZE` (seria/numarul avizelor facturate) e scris de
|
||||
**`scrie_corespondente_vanzari`** (spec 1057, body **15421-15457**, comentariu la linia **15445**:
|
||||
*"Completez VANZARI.AVIZE redundant, pentru a nu face mai rapid fact_vfacturi"*) — nu am confirmat
|
||||
in acest buget de cercetare din ce apeleaza `finalizeaza_factura` daca `scrie_corespondente_vanzari`
|
||||
ruleaza necondiționat pe toate tipurile sau doar pe unele (`V_TIP` e parametru) — merita verificat
|
||||
punctual daca se investigheaza bug-ul mai departe.
|
||||
|
||||
### 8. Coloanele denormalizate din VANZARI si ramura care le populeaza
|
||||
|
||||
Toate adaugate prin `2017\03\ff_2017_03_28_01_FACTURARE.sql:9-38` (comentariu fisier, linia 1-4:
|
||||
*"adaugare coloane in vanzari pentru denormalizarea join-urilor din fact_vfacturi"*):
|
||||
|
||||
| Coloana | Comentariu DB (linia 44-54 din script) | Populata de |
|
||||
|---|---|---|
|
||||
| `DISCOUNT_TVA` | TVA DISCOUNT (DISCOUNT * MAX(PROC_TVA) ARTICOLE) | `scrie_in_vanzari` |
|
||||
| `VALOARE_ACHIZITIE` | Valoare la pret achizitie articole | `scrie_in_vanzari` |
|
||||
| `TOTAL_FARA_TVA` | Total fara TVA lei | `scrie_in_vanzari` (13886), `finalizeaza_avize_lucrare` (15124) |
|
||||
| `TOTAL_TVA` | Total TVA lei | idem |
|
||||
| `TOTAL_CU_TVA` | Total cu TVA lei | `scrie_in_vanzari` (13888), `finalizeaza_avize_lucrare` (15126) |
|
||||
| `SERIE_INCASAT` | Seria chitanta/bon fiscal | `scrie_in_vanzari` (13895), din `pack_facturare.cserie_act_incasare` (setat de `scrie_incasari`) |
|
||||
| `NR_INCASAT` | Nr chitanta/bon fiscal | idem (13896) |
|
||||
| `SUMA_INCASAT` | Suma chitanta/bon fiscal | idem (13897) |
|
||||
| `TIP_INCASAT` | 11=CHITANTA, 2=BON FISCAL | idem (13898); vezi si `nTipIncasareCardBancar`=3, `nTipIncasareTichete`=5 |
|
||||
| `AVIZE` | Serie/numar avize facturate (max 1000 caractere, comentariu la linia 1490) | `scrie_corespondente_vanzari` (15421) |
|
||||
|
||||
### 9. Tipurile de factura/aviz si ramificarea codului
|
||||
|
||||
Din `VANZARI.TIP` (CASE explicit in view `fact_vfacturi2`,
|
||||
`ff_2017_03_28_01_FACTURARE.sql:89-171`) — cateva valori cheie:
|
||||
`1`=POLITICA PRETURI, `2`=CONTRACT, `3`=COMANDA, **`4`=FACT. DIN AVIZ**, `21`=AVIZ PE BAZA DE
|
||||
COMANDA, `24`=AVIZ DE RETUR, `43`=BON FISCAL MAGAZIN, etc. (lista completa la liniile citate).
|
||||
|
||||
Ramificare in VFP, `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare.vc2`, metoda
|
||||
**`do_scrie_factura`** — exista **doua copii aproape identice** ale acestei logici, in doua clase
|
||||
diferite (confirmat via `vfp_symbols.ps1 -Where`):
|
||||
- `frm_facturare_articole.do_scrie_factura` — **liniile 13981-14339**
|
||||
- `frm_facturare_articole2.do_scrie_factura` — **liniile 18004-18331**
|
||||
|
||||
Structura `DO CASE` (identica in ambele, ex. clasa 1 la liniile 14067-14174):
|
||||
- `poDate.eProforma = 1` → `{call pack_facturare.scrie_proforma(...)}` (14071)
|
||||
- `poDate.Tip = 4` → **`{call pack_facturare.scrie_factura_avize(...)}`** (14103) — "facturare din
|
||||
aviz" (comentariu 14086-14087)
|
||||
- `poDate.Tip IN (3,21,25,28,42,47)` → `{call pack_facturare.scrie_factura2(...)}` (14130) —
|
||||
"facturare pe baza de comanda / avize pe baza de comanda"
|
||||
- `Otherwise` → `{call pack_facturare.scrie_factura2(...)}` (14158) — politica de preturi/contract
|
||||
|
||||
Inainte de asta, lista de incasare `lcListaIncasare` se construieste (14012-14038) din
|
||||
`poDate.ntip_incasare` (11=chitanta, 2=bon fiscal, 3=POS/card), comun tuturor ramurilor.
|
||||
|
||||
### 10. Ramura "factura din aviz" + "achitata numerar" — ce ar trebui sa scrie si ipoteza de cauza
|
||||
|
||||
**Ce se intampla, pas cu pas** (`Tip = 4`, `ntip_incasare` in (11,2,3)):
|
||||
|
||||
1. VFP (`ofacturare.vc2:14012-14038`) construieste `lcListaIncasare` de forma
|
||||
`'11|<suma>|<id_casa>;'` (chitanta) sau `'2|...'`/`'3|...'` (bon fiscal/card) — **daca**
|
||||
`ntip_incasare` nu e in (11,2,3), `lcListaIncasare = [NULL]` (literal SQL NULL).
|
||||
2. VFP construieste `lcSql = {call pack_facturare.scrie_factura_avize(pnDiscount, serie_chit,
|
||||
nr_incasare, lcListaIncasare, id_delegat, id_masina, id_facturare, listare_detaliata,
|
||||
dataora_exp, id_agent, text_aditional, discount_evidentiat, param_aditional, ?@nid_vanzare)}`
|
||||
(14103-14115) — semnatura corespunde exact cu procedura Oracle activa
|
||||
`scrie_factura_avize` (linia **6675-7041**, care NU are `V_TOTFTVA`/`V_TOTTVA` ca prime
|
||||
argumente, spre deosebire de `scrie_factura2` — asta e corect, nu e discrepanta de parametri).
|
||||
3. In Oracle, `scrie_factura_avize` (7007-7012): `IF V_LISTA_INCASARE IS NOT NULL THEN
|
||||
pack_facturare.scrie_incasari(...)` — deci **doar daca** `lcListaIncasare` nu e literalul
|
||||
`NULL` se seteaza variabilele de pachet `cserie_act_incasare`/etc.
|
||||
4. `scrie_factura_avize` seteaza `pack_facturare.clistaid_avize := ...V_LISTAID` (7020) — lista de
|
||||
ID-uri de avize facturate, folosita ulterior de `scrie_corespondente_vanzari` pentru
|
||||
`VANZARI.AVIZE`.
|
||||
5. `scrie_factura_avize` cheama `finalizeaza_factura` (7026-7034), care cheama `scrie_in_vanzari`
|
||||
(14740) → scrie `serie_incasat`/`nr_incasat`/`suma_incasat`/`tip_incasat` din variabilele de
|
||||
pachet setate la pasul 3.
|
||||
|
||||
**La nivel de cod citit, lantul pare corect cablat** — parametrii trec prin, semnaturile se
|
||||
potrivesc, iar cele doua clase VFP (`frm_facturare_articole` si `frm_facturare_articole2`) au
|
||||
logica identica pentru aceasta ramura.
|
||||
|
||||
**IPOTEZA (cauza posibila a bug-ului, nedemonstrata prin citire de cod — necesita investigatie
|
||||
suplimentara/testare)**:
|
||||
- Daca ordinea reala de operatii difera de `ordine_operatii_facturare.txt` (ex. daca intre pasul 3
|
||||
si pasul 5 se mai apeleaza `initializeaza_date_factura` pentru alt document/lucru din aceeasi
|
||||
sesiune, inainte ca `finalizeaza_factura` sa apuce sa citeasca variabilele de pachet), fix-ul din
|
||||
03.07.2020 (linia 1876-1880, reset la NULL) ar putea sterge datele de incasare **inainte** ca
|
||||
`scrie_in_vanzari` sa le foloseasca — dar nu am gasit in codul citit un asemenea apel intercalat.
|
||||
- Alternativ: daca pe ramura aviz `scrie_corespondente_vanzari` (care scrie `VANZARI.AVIZE`) nu e
|
||||
apelata necondiționat din `finalizeaza_factura` pentru `TIP=4` (nu am verificat corpul complet al
|
||||
`finalizeaza_factura`, liniile 14723-14807, dincolo de apelul la `scrie_in_vanzari`) — merita
|
||||
verificat explicit daca acel apel exista si e neconditionat de tip.
|
||||
- Nu am verificat daca exista vreo diferenta intre `scrie_factura_avize` si
|
||||
`scrie_factura_avize_retur` (spec 667, alta ramura pentru tip retur din aviz) in privinta
|
||||
apelului `scrie_incasari`/`scrie_in_vanzari` — posibil ca bug-ul sa fie specific ramurii de retur,
|
||||
nu celei simple.
|
||||
|
||||
Recomandare pentru pasul urmator (daca se investigheaza mai departe): instrumentare/breakpoint pe
|
||||
`scrie_in_vanzari` (13468) intr-un mediu de test, pentru o factura Tip=4 platita numerar, verificand
|
||||
valorile efective ale `pack_facturare.cserie_act_incasare`/`nnumar_act_incasare`/`nsuma_incasare`/
|
||||
`ntip_doc_incasare` chiar inainte de UPDATE (13886-13898).
|
||||
|
||||
### 11. Cele doua view-uri de facturi
|
||||
|
||||
- **`fact_vfacturi`** — view-ul "original" (pe model relational, cu join-uri catre `VANZARI_DETALII`
|
||||
/ `ACT` / etc., fara denormalizare). Redefinit de multe ori de-a lungul timpului; cea mai recenta
|
||||
gasita: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\11\ff_2023_11_17_01_COMUN.sql` (si
|
||||
`2023\02\ff_2023_02_08_01_COMUN.sql`) — nu am deschis continutul exact in acest buget.
|
||||
- **`fact_vfacturi2`** — view-ul cu totaluri **denormalizate din VANZARI** (`total_fara_tva`,
|
||||
`total_tva`, `total_cu_tva`, `serie_incasat`, `nr_incasat`, `suma_incasat`, `tip_incasat`,
|
||||
`avize` citite direct din coloanele `VANZARI`, nu recalculate din `VANZARI_DETALII`/`ACT`).
|
||||
Definit initial in **`D:\ROA\DATABASE\SCRIPTURI_CLAR\2017\03\ff_2017_03_28_01_FACTURARE.sql:59-236`**,
|
||||
cu comentariul explicit (linia 3): *"View fact_vfacturi2 - temporar, urmand a inlocui view-ul
|
||||
fact_vfacturi, dupa completarea coloanelor din vanzari pentru datele existente deja"*.
|
||||
|
||||
Separat, exista si o a doua pereche de view-uri, mai recenta si mai granulara (pe linie de
|
||||
articol, nu pe factura): **`fact_vrap_fact_articole`** / **`fact_vrap_articole_vandute`**, definite
|
||||
in `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_27_01_FACTURARE.sql:10-257` — acestea NU
|
||||
citesc din coloane denormalizate, ci calculeaza TVA live prin `pack_facturare.calculeaza_total_*`
|
||||
(vezi Subiect A #2). Nu sunt aceleasi cu perechea mentionata in cerinta (care pare sa se refere la
|
||||
`fact_vfacturi`/`fact_vfacturi2`), dar sunt relevante daca se cauta "view-ul cu totaluri din
|
||||
vanzari" generic.
|
||||
|
||||
---
|
||||
|
||||
## Note metodologice
|
||||
|
||||
- `PACK_SESIUNE` (motorul de rotunjire per-pret, apelat de `PACK_FACTURARE`) nu a fost gasit
|
||||
definit ca fisier separat in cautarile punctuale facute (cateva luni din 2025-2026); daca se
|
||||
continua investigatia pe rotunjire, merita o cautare dedicata `create or replace package
|
||||
pack_sesiune` in `D:\ROA\DATABASE\SCRIPTURI_CLAR`.
|
||||
- Arhiva `D:\ROA\DATABASE\SCRIPTURI_CLAR` e foarte mare (mii de fisiere, 2009-2026); cautarile de
|
||||
mai sus au fost tintite (nume de coloane/proceduri cunoscute), nu exhaustive — un `grep` complet
|
||||
pe intreg arhivul repetat timeout la 20s in unele incercari initiale.
|
||||
- Nu am gasit apeluri catre `PACK_FACTURARE` din `D:\ROA\ROAFACTURARE` propriu-zis (in afara
|
||||
`COMUN\clase\ofacturare.vc2`) — pachetul e consumat exclusiv prin acea clasa si prin
|
||||
`COMUN\programe\oproceduri_rapoarte_fact.prg`.
|
||||
164
docs/cercetare/rec_view_articole_vanzare.md
Normal file
164
docs/cercetare/rec_view_articole_vanzare.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Proiectare view Oracle pentru liniile de articole ale unei vanzari (VVANZARI_ARTICOLE)
|
||||
|
||||
Cercetare + proiectare, read-only pe baza. Decizia lui Marius (08.08.2026): view dedicat, varianta B
|
||||
din `rec_review_ancorare_s4.md` (respinsa atunci doar pentru ca insemna migrare de schema - acum
|
||||
aprobata). Inlocuieste lista de 20 coloane + join pe 4 tabele din `IncarcaArticoleFactura`
|
||||
(`COMUN\programe\ofacturare_editare.prg:201-208`).
|
||||
|
||||
## A. Precoditia VERSIUNE
|
||||
|
||||
`MARIUSM_AUTO` e la zi **pentru schema firmei (`ff_`)** pe orice ar putea atinge `VANZARI_DETALII`/
|
||||
facturare: toate scripturile `ff_` din 2015 incoace apar in `VERSIUNE`, inclusiv cele mai recente
|
||||
patru (`ff_2026_08_06_09/10/11/12`, comanda/contract, `PACK_FACTURARE`, `FACT_VFACTURI`,
|
||||
`VANZARI_BACKFILL`). Diferenta gasita fata de `SCRIPTURI_CLAR` (452 de fisiere, din care 14 `ff_`)
|
||||
e integral din 2009-2014 (dinainte de generalizarea `UpdateVersiune`) plus scripturi de diagnostic
|
||||
fara prefix de tracking (`DIAG_SPATIU_*`, ad-hoc-uri fara nume standard) - niciunul din ele nu
|
||||
atinge `VANZARI`/`VANZARI_DETALII`/facturare. Verdict: **precoditia trece**, sursa `MARIUSM_AUTO` e
|
||||
de incredere pentru acest DDL.
|
||||
|
||||
## B. Ce exista deja - niciun candidat direct refolosibil, dar un precedent util
|
||||
|
||||
Cautat in `all_views` (MARIUSM_AUTO) orice view pe `VANZARI_DETALII`/liniile unei vanzari:
|
||||
`VVANZARI_DETALII`, `VVANZARI_DETALII_TOT`, `FACT_VFACTURI_DETALII` sunt cele relevante.
|
||||
|
||||
- **`FACT_VFACTURI_DETALII`** (exportat integral) e cel mai apropiat candidat structural - selecteaza
|
||||
direct din `vanzari_detalii`, cu join-uri catre `nom_articole`/`nom_gestiuni`/`nom_valute` ca in
|
||||
proiectarea de mai jos. **Nu e reutilizabil ca atare**: `pret` si `discount_unitar` sunt
|
||||
RECALCULATE la cursul valutar curent (`ROUND(NVL(g.curs,1)*NVL(a.pret,0)/NVL(g.multiplicator,1),
|
||||
pack_sesiune.getoptiunefirma('PPRETV'))`, apel de functie PL/SQL per coloana per rand), construit
|
||||
pentru **afisare/tiparire** facturi, nu pentru editare. Pagina de editare (#6) trebuie sa lucreze
|
||||
pe valorile RAW stocate, ca la salvare (S5) sa scrie inapoi exact ce a citit - un `pret` deja
|
||||
convertit ar strica exact acest contract. Confirma insa tiparul de join corect (articol/gestiune/
|
||||
valuta) si ca genul asta de view exista deja in familia `FACT_*` pentru alt scop.
|
||||
- **`VVANZARI_DETALII`/`VVANZARI_DETALII_TOT`**: alt scop (afisare generica vanzari + utilizatori +
|
||||
politici de pret), nu au `pret_cu_tva`, `cont`, `id_valuta`, `id_gestiune`, `taxcode`, `lot` -
|
||||
nepotrivite.
|
||||
|
||||
Niciunul nu inlocuieste nevoia unui view nou - se continua cu proiectarea.
|
||||
|
||||
## C. Numele: `VVANZARI_ARTICOLE`
|
||||
|
||||
Verificat liber in `all_objects` (MARIUSM_AUTO). 17 caractere, sub limita de 30 (Oracle 10.2).
|
||||
|
||||
**Criteriul lui Marius (08.08.2026)**: numele trebuie sa pastreze prefixul familiei de vanzari,
|
||||
`vvanzari_*`, pentru ca el se uita la view-uri **alfabetic** si vrea sa vada dintr-o privire ca tine
|
||||
de vanzari. La o listare alfabetica: `VVANZARI_ARTICOLE`, `VVANZARI_DETALII`,
|
||||
`VVANZARI_DETALII_TOT`, `VVANZARI_TOT`.
|
||||
|
||||
Numele analog conventiei surorilor de pe acelasi formular (`vact_tot`, `vrul_tot`,
|
||||
`vrul_obinv_tot`) ar fi fost `vvanzari_detalii_tot`, dar e **deja ocupat** (alt view, B mai sus).
|
||||
|
||||
Numele de lucru din prima varianta a fost `VVD_TOT` (`v` + abreviere + `_tot`, aliniat cu aliasul
|
||||
VFP `tvd`), respins de Marius pe criteriul de mai sus: nu se citea ca facand parte din familie si
|
||||
cadea in alta parte a listei alfabetice.
|
||||
|
||||
## D. Coloanele - RAW, fara filtru STERS in view
|
||||
|
||||
Cele 20 de coloane consumate azi + **`id_vanzare` adaugat**: lista din
|
||||
`ofacturare_editare.prg:201-203` filtreaza pe `vd.id_vanzare` dar nu-l intoarce niciodata ca si
|
||||
coloana - omisiune reala, semnalata in misiune, acum corectata in view.
|
||||
|
||||
- **Fara conversie valutara, fara valori calculate** (spre deosebire de `FACT_VFACTURI_DETALII`) -
|
||||
editarea scrie inapoi exact ce citeste.
|
||||
- **`STERS` ramane in view ca si coloana, dar FARA filtru `WHERE sters=0` in definitie** - acelasi
|
||||
tipar ca `VACT_TOT`/`VRUL_TOT`/`VRUL_OBINV_TOT` (niciunul nu filtreaza `sters` in view, apelantul
|
||||
o face explicit). Apelantul de azi (`IncarcaArticoleFactura`) deja are `WHERE ... vd.sters = 0` -
|
||||
ramane neschimbat, doar `FROM vanzari_detalii vd ... WHERE vd.id_vanzare=... AND vd.sters=0` devine
|
||||
`FROM vvanzari_articole vd WHERE vd.id_vanzare=... AND vd.sters=0`.
|
||||
|
||||
## E. Ce s-a respins: valorile calculate `CALCULEAZA_TOTAL_FARA_TVA_FACT`/`_TVA_FACT`
|
||||
|
||||
Ambele sunt functii **in `PACK_FACTURARE`**, semnatura `(V_PRET, V_DIFERENTA, V_CURS,
|
||||
V_DISCOUNT_UNITAR, V_DISCOUNT_EVIDENTIAT, V_CANTITATE, V_PRET_CU_TVA, V_PROC_TVAV) RETURN NUMBER` -
|
||||
strict `NUMBER`, deci **apelabile din SQL** (confirmat prin `all_arguments`, fara tip PL/SQL-only).
|
||||
Exista precedent local pentru functii de pachet apelate per-rand intr-un view din aceeasi familie
|
||||
(`VRUL_TOT` cheama `PACK_SESIUNE.SUMA_RON` pe aproape fiecare coloana numerica), deci performanta nu
|
||||
e un argument respingator prin ea insasi pe un view filtrat pe `id_vanzare` (cateva linii/factura).
|
||||
|
||||
**Respins totusi, pentru acum**:
|
||||
1. `V_DISCOUNT_EVIDENTIAT` **nu e coloana pe `VANZARI_DETALII`** - e pe antet (`VANZARI.
|
||||
DISCOUNT_EVIDENTIAT`, confirmat in `all_tab_columns`). A include totalul calculat per linie ar
|
||||
cere fie un join suplimentar la `VANZARI` (umfla view-ul cu o dependenta noua doar pentru un
|
||||
parametru), fie parametrul trimis din VFP - oricum, in afara scopului Rundei 1 (doar afisare).
|
||||
2. Runda 1 (deja implementata si testata, `docs\progres.md`) **nu consuma** aceste valori - grid-ul
|
||||
e readonly, fara subtotal calculat.
|
||||
3. Nevoia reala apare la S4b/Runda 4 (`plan_06_s4_proiectare.md`, C.1-C.3) - dar acolo e vorba de
|
||||
**totalul pe DOCUMENT** (suma peste toate liniile), comparat cu `ACT`/`RUL`, nu de o coloana pe
|
||||
fiecare rand din grid. O interogare de agregare separata (sau o functie Oracle dedicata) e o
|
||||
potrivire mai buna pentru "totalul de control" decat 2 coloane calculate in view-ul de grid.
|
||||
4. **Costul amanarii e mic**: extinderea unui VIEW (`CREATE OR REPLACE`) nu e o migrare de schema in
|
||||
sensul de risc/coordonare al unei modificari de tabela - se poate adauga oricand printr-un script
|
||||
nou, fara sa afecteze datele sau consumatorii existenti ai coloanelor deja definite. Deci nu e
|
||||
nevoie sa se "prevada" acum ca sa se evite un al doilea script peste o saptamana.
|
||||
|
||||
**Recomandare pentru Runda 4**: cand S4b ajunge la implementare, se adauga fie doua coloane noi in
|
||||
`VVANZARI_ARTICOLE` (dupa un join la `VANZARI` pentru `DISCOUNT_EVIDENTIAT`), fie - preferabil - o interogare
|
||||
de agregare separata in Oracle care calculeaza direct SUMA pe document, fara sa treaca prin grid.
|
||||
Decizia ramane a lui Marius/sesiunii care implementeaza S4b, pe baza planului deja scris in
|
||||
`plan_06_s4_proiectare.md` C.1.
|
||||
|
||||
## F. Validare pe cele doua cazuri de regresie
|
||||
|
||||
Rulat direct ca `SELECT` (fara `CREATE VIEW`), corpul propus vs. interogarea de azi din
|
||||
`IncarcaArticoleFactura`, pe `MARIUSM_AUTO`:
|
||||
|
||||
- **`id_vanzare = 1050`** (`cod=1140888`, `tip=1`): **4 randuri**, valori identice rand-cu-rand intre
|
||||
interogarea veche si corpul noului view (`id_vanzare_det` 1584-1587). Coincide cu baza de regresie
|
||||
din `docs\progres.md`.
|
||||
- **`id_vanzare = 1047`** (`cod=1140885`, `tip=-12`): **2 randuri** (1578, 1579), identice intre cele
|
||||
doua interogari.
|
||||
|
||||
Ambele cazuri: zero divergenta, inclusiv pe coloanele cu `NULL` (ex. `id_gestiune`/`nume_gestiune`
|
||||
pe liniile nestocate din `id_vanzare=1047`).
|
||||
|
||||
## G. Numele coloanelor la iesire
|
||||
|
||||
Fara alias-uri noi - toate coloanele pastreaza numele lor de baza din tabelele sursa (Oracle
|
||||
foloseste automat numele coloanei cand nu exista `AS`):
|
||||
|
||||
```
|
||||
ID_VANZARE, ID_VANZARE_DET, ID_ARTICOL, CANTITATE, PRET, PRET_CU_TVA, PROC_TVAV, DISCOUNT_UNITAR,
|
||||
ID_GESTIUNE, CONT, ID_VALUTA, ID_JTVA_COLOANA, SERIE, EXPLICATIE, TAXCODE, LOT, STERS,
|
||||
DENUMIRE, CODMAT, NUME_GESTIUNE, NUME_VAL
|
||||
```
|
||||
|
||||
**Zero schimbari fata de azi** pentru `ControlSource`-urile existente ale `grdArticoleFactura`
|
||||
(`tvd.denumire`, `tvd.codmat`, etc.) - toate numele coincid cu ce se foloseste azi. Singura
|
||||
schimbare vizibila e coloana noua `tvd.id_vanzare`, disponibila dar neconsumata inca de grid.
|
||||
|
||||
## H. Numerotare script
|
||||
|
||||
`SCRIPTURI_CLAR\2026\08\` exista deja, ultimul numar folosit azi (08.08.2026, la momentul
|
||||
verificarii) e **niciunul** - nu exista fisiere `_2026_08_08_` in director (ultimele sunt din
|
||||
06.08.2026, `NN` pana la 12). **`NN=01` e liber pentru 08.08.2026**, comun tuturor prefixelor -
|
||||
de reverificat la momentul aplicarii, pentru ca sesiunea are mai multi agenti activi in paralel azi
|
||||
care ar putea consuma acelasi numar inaintea acestui script.
|
||||
|
||||
Nume final propus: **`ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE.sql`** - scriptul livrat foloseste deja acest
|
||||
nume in `exec pack_migrare.UpdateVersiune(...)`; daca sesiunea principala aloca alt `NN`, fisierul
|
||||
**si** acel apel trebuie schimbate impreuna (altfel `VERSIUNE` inregistreaza un nume care nu exista
|
||||
pe disc).
|
||||
|
||||
## I. Format script
|
||||
|
||||
`CREATE OR REPLACE VIEW` (fara `FORCE`) - verificat pe modelul cel mai recent din `SCRIPTURI_CLAR`
|
||||
(`ff_2026_08_06_11_COMUN_FACT_VFACTURI.sql`, care creeaza doua view-uri fara `FORCE`); `FORCE` nu e
|
||||
necesar oricum, toate tabelele sursa (`VANZARI_DETALII`, `NOM_ARTICOLE`, `NOM_GESTIUNI`,
|
||||
`NOM_VALUTE`) exista deja. CRLF verificat byte-safe dupa scriere (44 CRLF, 0 LF singur). Fara `;` in
|
||||
comentarii inaintea instructiunii. Se incheie cu `UpdateVersiune` + `commit`.
|
||||
|
||||
## Livrabil
|
||||
|
||||
`D:\ROA\ROAFACTURARE\docs\cercetare\ff_view_articole_vanzare.sql` - scriptul complet, **neaplicat**.
|
||||
|
||||
## Ce trebuie schimbat in VFP ca urmare (doar cand se aplica scriptul)
|
||||
|
||||
1. `COMUN\programe\ofacturare_editare.prg:201-208` (`IncarcaArticoleFactura`): inlocuieste
|
||||
`FROM vanzari_detalii vd LEFT JOIN nom_articole na ... LEFT JOIN nom_gestiuni ng ... LEFT JOIN
|
||||
nom_valute nv ...` cu `FROM vvanzari_articole vd`, pastrand neschimbat `WHERE vd.id_vanzare = ... AND
|
||||
vd.sters = 0` si toata lista de coloane din `SELECT` (numele raman identice).
|
||||
2. Fallback-ul `CREATE CURSOR tvd (...)` (`:214-216`) si duplicatul din `omodificari.vc2` (~14076,
|
||||
semnalate deja ca duplicare in `rec_review_ancorare_s4.md`) **nu se ating** de aceasta schimbare -
|
||||
raman neschimbate structural, doar sursa `SELECT`-ului se simplifica.
|
||||
3. **Opional**, daca se doreste sa se profite de coloana noua `id_vanzare`: nu e necesar niciun
|
||||
consum imediat in VFP - grid-ul nu are nevoie de ea (id-ul e deja cunoscut din parametrul functiei).
|
||||
207
docs/cercetare/rec_watchdog_vfp.md
Normal file
207
docs/cercetare/rec_watchdog_vfp.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Watchdog VFP + diagnostic blocaj "View Parameter" (test_page3_articole.prg)
|
||||
|
||||
## 1. Utilitarul: `COMUN\utile\Teste\watchdog_vfp.ps1`
|
||||
|
||||
Lanseaza `vfp9.exe -A -T "<script>"`, polleaza la ~700ms ferestrele TOP-LEVEL ale procesului
|
||||
(`EnumWindows`+`GetWindowThreadProcessId`, filtrate pe PID). Fereastra principala VFP se identifica
|
||||
prin `Process.MainWindowHandle` (.NET) - **nu** dupa numele clasei: VFP inregistreaza clase diferite
|
||||
prefixate `vfp9...` atat pentru shell-ul principal (`vfp99400000`) cat si pentru dialogurile lui
|
||||
proprii (`vfp994000002` pentru "View Parameter") - o clasificare pe clasa a lasat "View Parameter"
|
||||
nedetectat la prima incercare (corectat).
|
||||
|
||||
Pentru fiecare fereastra noua (diferita de `MainWindowHandle`): captura PNG (`PrintWindow`, fallback
|
||||
`CopyFromScreen` daca iese neagra) + dump text (titlu, clasa, si textul/clasa fiecarui control copil
|
||||
via `WM_GETTEXT`, cross-proces). Cu `-AutoDismiss`: cauta un buton copil "Cancel"/"Anulare" si
|
||||
trimite `BM_CLICK`; altfel incearca, in ordine, `WM_COMMAND IDCANCEL` -> ESCAPE ca MESAJ
|
||||
(`WM_KEYDOWN`/`WM_KEYUP`, tintit pe handle) -> `WM_CLOSE`.
|
||||
|
||||
**REGULA OBLIGATORIE, incalcata initial si corectata**: watchdog-ul NU are voie sa foloseasca INPUT
|
||||
REAL de tastatura/mouse (`keybd_event`, `SetForegroundWindow`, `SendInput`, `mouse_event`) - masina e
|
||||
PARTAJATA cu utilizatorul. Prima versiune folosea `SetForegroundWindow`+`keybd_event(ESCAPE)` ca
|
||||
fallback pentru dialogurile proprii VFP owner-drawn (fara controale copil reale) - **acest input NU
|
||||
are tinta, ajunge in orice fereastra are focus pe masina in acel moment, indiferent al cui proces
|
||||
e**. **Fapt clarificat de team-lead, dupa investigare**: in acest caz concret, ESC-ul a aterizat de
|
||||
fapt in PROPRIUL NOSTRU proces de test (eroarea "Variable 'GNAN' is not found" vazuta imediat inainte
|
||||
de "Execution was canceled by the user" e chiar semnatura testului nostru) - **nu in sesiunea lui
|
||||
Marius**, deci de fapt nu s-a intrerupt munca nimanui de data asta. Asta NU schimba regula: mecanismul
|
||||
tot nu are tinta si putea la fel de usor sa ajunga in aplicatia de productie (`roafacturare.exe`/
|
||||
`roacont.exe`) daca utilizatorul avea focus acolo in acea clipa - o formulare anterioara aici spunea
|
||||
gresit ca ar fi afectat sesiunea utilizatorului, corectata acum. **Scos complet** din cod (nu mai
|
||||
exista nicio linie `keybd_event`/`SetForegroundWindow` in `watchdog_vfp.ps1`). Consecinta: pentru
|
||||
dialogurile owner-drawn care nu raspund la mesaje tintite, `-AutoDismiss` **esueaza cinstit** (dialogul
|
||||
ramane deschis pana la `-TimeoutSec`, apoi procesul e omorat) - captura (PNG+dump text) ramane de incredere,
|
||||
dismiss-ul nu e garantat pentru acest tip de dialog. Procesul e omorat mereu la iesire (`finally`),
|
||||
nu ramane niciodata viu.
|
||||
|
||||
Validat intai pe caz banal: `COMUN\utile\Teste\watchdog_selftest.prg` (`MESSAGEBOX` cu OK/Cancel) -
|
||||
detectie, captura, dump text si auto-dismiss confirmate corecte inainte de a-l rula pe cazul real.
|
||||
|
||||
Utilizare: `powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "<test.prg>" -AutoDismiss [-TimeoutSec 90] [-MaxDialogs 10] [-OutDir <cale>]`.
|
||||
|
||||
## 2. Dialogurile capturate pe `test_page3_articole.prg`
|
||||
|
||||
**Dialog 0** - nativ VFP, clasa `vfp994000002`, titlu **"View Parameter"**, text **"Enter the value
|
||||
for gnAn:"** (fara controale copil reale - owner-drawn). Screenshot:
|
||||
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog0.png`.
|
||||
|
||||
**Dialog 1** (apare doar cand dialog 0 e inchis prin ESCAPE real, care functioneaza) - "Open" Win32
|
||||
standard, filtru "Table/DBF (*.dbf)", folder implicit `ROACONT` (working directory-ul mediului de
|
||||
test, mostenit din `test_init_env_auto.prg`). Screenshot:
|
||||
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog1.png`.
|
||||
|
||||
## 3. PROGRAM()/LINENO() din log (dupa Cancel pe dialog 0)
|
||||
|
||||
Din `test_page3_articole_log.txt`, PROGRAM()=`VERIFICA_PAGECOUNT_FORM` (procedura de test) pe
|
||||
liniile din jurul apelului `loForm = Createobject([frm_modific2024], lnIdSet)`:
|
||||
|
||||
```
|
||||
EROARE 1 [VERIFICA_PAGECOUNT_FORM:165] File 'crsjtvatemp.dbf' does not exist.
|
||||
EROARE 1924 [VERIFICA_PAGECOUNT_FORM:166..173] LOFORM is not an object. (cascada, zgomot)
|
||||
```
|
||||
|
||||
## 4. Experimente de izolare (cerute de team-lead) - **INFIRMA ipoteza initiala**
|
||||
|
||||
Ipoteza initiala din aceasta sectiune ("`SQLEXEC` din `update_jtva_coloane` nu rezolva `?gnAn`") era
|
||||
o **deductie**, nu o masuratoare - team-lead a cerut-o verificata direct, corect. Trei experimente
|
||||
ieftine, in ordine:
|
||||
|
||||
**Experiment A - "chiar exista in acel moment?"** `TYPE('gnAn')`/`TRANSFORM(gnAn)` puse imediat
|
||||
**INAINTE** de apelul `update_jtva_coloane` (linia 161 curenta, nu inainte de `Createobject` cum
|
||||
fusese verificat prima data):
|
||||
|
||||
```
|
||||
EROARE 12 [VERIFICA_PAGECOUNT_FORM:161] Variable 'GNAN' is not found.
|
||||
```
|
||||
|
||||
- Eroare aparuta DOAR la primul apel al `verifica_pagecount_form` (`cod=1140888`), inainte sa se
|
||||
ajunga la `update_jtva_coloane`. **Rezultat: `gnAn` e deja invizibila INAINTE ca `update_jtva_coloane`
|
||||
sa fie apelata** - markerele `?gnAn`/`?gnLuna` din `updateserver.prg:597` nu pot fi (macar nu
|
||||
singure) cauza, contrazice ipoteza initiala.
|
||||
|
||||
**Experiment B - "se reproduce izolat, fara nimic din S4?"** Script nou,
|
||||
`COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg`: DOAR `test_init_env_auto` +
|
||||
`update_jtva_coloane("", "crsJtvaTemp", 6)`, fara `IncarcaCursoareModificareNota`, fara
|
||||
`frm_modific2024`, fara nimic din PAGE3. Rezultat:
|
||||
|
||||
```
|
||||
TYPE(gnAn)=N TRANSFORM(gnAn)=2026 TYPE(gnLuna)=N TRANSFORM(gnLuna)=8
|
||||
dupa update_jtva_coloane: Used(crsJtvaTemp)=.T. Reccount=120
|
||||
done
|
||||
```
|
||||
|
||||
**NU reproduce.** Zero dialog, exit curat, cursorul se creeaza corect cu 120 randuri.
|
||||
`update_jtva_coloane` singura, chemata imediat dupa initul mediului, functioneaza perfect -
|
||||
**nu e o capcana preexistenta a harness-ului si nici o vina proprie a functiei in izolare**.
|
||||
Rerulat identic dupa scoaterea input-ului real din watchdog (vezi sectiunea 1) - acelasi rezultat,
|
||||
deci reconfirmat, nu era un artefact al mecanismului de dismiss.
|
||||
|
||||
**Experiment C - oprit inainte de a-l rula.** Premisa lui ("copiaza corpul lui `update_jtva_coloane`
|
||||
cu concatenare in loc de `?param`, ca sa confirmi mecanismul") presupune ca vina e in legarea
|
||||
`SQLEXEC` a functiei - exact ce B tocmai a infirmat. Nu are sens sa continue in forma ceruta initial
|
||||
fara o noua directie.
|
||||
|
||||
## 5. Cauza CONFIRMATA prin bisectie (nu doar deductie)
|
||||
|
||||
Bisectie ceruta de team-lead: `TYPE('gnAn')`/`TRANSFORM(gnAn)` logat in **doua straturi** - (a) in
|
||||
programul principal, intre fiecare apel de nivel superior, si (b) ca **prima linie** in interiorul
|
||||
fiecarei proceduri (`verifica_vanzare_nota`, `verifica_coliziune_cod`, `verifica_pagecount_form`).
|
||||
Helper `bisect_log_gnan` (nou, la coada `test_page3_articole.prg`) - TYPE() e sigur necoditionat,
|
||||
TRANSFORM() doar daca TYPE()<>'U', ca sa nu produca o eroare noua care ar intrerupe bisectia.
|
||||
|
||||
Rezultat brut (`test_page3_articole_log.txt`):
|
||||
|
||||
```
|
||||
[BISECT] main: dupa test_init_env_auto :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] main: dupa verifica_vanzare_nota #1 (cod=1140888) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1140885 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] main: dupa verifica_vanzare_nota #2 (cod=1140885) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1125486 :: TYPE(gnAn)=N gnAn=2026 <- diferit!
|
||||
[BISECT] main: dupa verifica_vanzare_nota #3 (cod=1125486) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_coliziune_cod ENTRY :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] main: dupa verifica_coliziune_cod :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] main: inainte de verifica_pagecount_form :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_pagecount_form ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] verifica_pagecount_form inainte de update_jtva_coloane :: TYPE(gnAn)=U gnAn=(U)
|
||||
```
|
||||
|
||||
**Tiparul e limpede si consecvent**: `gnAn` e INTOTDEAUNA valid (`N`, `2026`) in scope-ul PRINCIPAL,
|
||||
la fiecare checkpoint, fara exceptie - deci **NU e "eliberata"** (nu e `CLEAR ALL`/`CLEAR MEMORY`/
|
||||
`RELEASE ALL EXTENDED` pe undeva). E **`'U'` STRICT la intrarea in proceduri apelate cu `DO ... WITH`
|
||||
in care `gnAn`/`gnLuna` sunt trecute NEPARANTEZATE ca argumente**, si redevine valid imediat ce
|
||||
procedura respectiva se termina si controlul revine in principal. Corelatia e exacta cu sintaxa
|
||||
apelului, nu cu ce face procedura pe dinauntru:
|
||||
|
||||
- `verifica_vanzare_nota` apelul #1/#2 (`gnAn` -> `U`): call-site-urile trec `gnAn, gnLuna` DIRECT -
|
||||
`test_page3_articole.prg:34` (`DO verifica_vanzare_nota WITH 1140888, gnAn, gnLuna, ...`) si
|
||||
`test_page3_articole.prg:36` (`... WITH 1140885, gnAn, gnLuna, ...`).
|
||||
- `verifica_vanzare_nota` apelul #3 (`gnAn` ramane `N`): `test_page3_articole.prg:38` trece
|
||||
`2008, 2` LITERAL, nu `gnAn`/`gnLuna`.
|
||||
- `verifica_coliziune_cod` (`gnAn` ramane `N`): `test_page3_articole.prg:42` nu trece deloc
|
||||
`gnAn`/`gnLuna` (doar `lcLog`).
|
||||
- `verifica_pagecount_form` primul apel (`gnAn` -> `U`, **exact scenariul blocat**):
|
||||
**`test_page3_articole.prg:47`** - `DO verifica_pagecount_form WITH 1140888, gnAn, gnLuna, 'A (factura reala)', lcLog, 3, .T.`.
|
||||
|
||||
**Mecanismul**: `DO <procedura> WITH <arg1>, <arg2>, ...` (stilul vechi, folosit peste tot in acest
|
||||
script) trece variabilele de memorie **BY REFERENCE** implicit (`SET UDFPARMS` e `REFERENCE` in mod
|
||||
implicit VFP) - `LPARAMETERS tnAn, tnLuna` din procedura primitoare devin ALIAS-uri directe pe
|
||||
storage-ul lui `gnAn`/`gnLuna`, iar numele ORIGINAL devine inaccesibil (`TYPE()='U'`) **pe toata
|
||||
durata apelului**, exact cat tine executia procedurii - confirmat empiric de simetria perfecta
|
||||
"intra U, revine N" la fiecare din cele 3 perechi de apeluri afectate.
|
||||
|
||||
**Clasificare in termenii cerutii de team-lead**: e **"umbrire de scope"** (categoria 2), NU
|
||||
"eliberare" (categoria 1) - dar mecanismul exact nu e o coliziune de nume `PRIVATE`/`LOCAL` in corpul
|
||||
procedurii (team-lead avea deja dreptate: `verifica_pagecount_form` nu are `gnAn` in `LOCAL`, si nu
|
||||
exista `PRIVATE gnAn`/`LOCAL gnAn` nicaieri in `COMUN\programe`) - **umbrirea vine din SINTAXA
|
||||
APELULUI** (`DO...WITH` fara paranteze in jurul lui `gnAn`/`gnLuna`), nu din declaratiile procedurii
|
||||
apelate.
|
||||
|
||||
**Statement-ul vinovat exact, cu fisier:linie**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg:47`.
|
||||
|
||||
**IMPLICATIE IMPORTANTA**: acesta e un tipar din **SCRIPTUL DE TEST**, nu din fluxul real al
|
||||
aplicatiei - `do_editare_factura` (codul de productie) nu trece prin `verifica_pagecount_form`
|
||||
(helper propriu testului). Team-lead a confirmat verdictul: **e strict un defect de harness** -
|
||||
`updateserver.prg`, `omodificari.*` si codul S4 sunt toate nevinovate.
|
||||
|
||||
**Remediu APLICAT de team-lead** (3 linii, sub pragul lui de editare directa): argumentele
|
||||
`gnAn`/`gnLuna` sunt acum parantezate - `(gnAn)`, `(gnLuna)` - la liniile 37, 39 si 50 din
|
||||
`test_page3_articole.prg` (parantezele forteaza trecere PRIN VALOARE in loc de PRIN REFERINTA),
|
||||
cu un comentariu explicativ deasupra primei aparitii. Nerulat inca de mine (interzis explicit -
|
||||
suita o ruleaza team-lead-ul dupa eliberarea `.fxp`-ului).
|
||||
|
||||
**Remediul din rundele anterioare ale acestui raport (concatenare in loc de `?gnAn`/`?gnLuna` in
|
||||
`updateserver.prg:597`) ramane infirmat** - nu era cauza. `updateserver.prg` nu a fost si nu e atins.
|
||||
|
||||
## 6. Fisiere atinse
|
||||
|
||||
- **Nou**: `COMUN\utile\Teste\watchdog_vfp.ps1`, `COMUN\utile\Teste\watchdog_selftest.prg`,
|
||||
`COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg` (experimentul B, izolat).
|
||||
- **Modificat**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`
|
||||
- linia 176 (`update_jtva_coloane(..., 6)`, ramane - fix necesar pt. indexul `id_jtva`, independent
|
||||
de defectul de mai jos);
|
||||
- `PROCEDURE bisect_log_gnan` (noua, la coada fisierului) + apeluri `DO bisect_log_gnan WITH ...`
|
||||
inserate in principal (intre apelurile de nivel superior) si ca prima linie in fiecare procedura
|
||||
- ramase in cod, sunt dovada bisectiei;
|
||||
- **liniile 37, 39, 50 - remediul APLICAT de team-lead**: `gnAn`/`gnLuna` parantezate (`(gnAn)`,
|
||||
`(gnLuna)`), forteaza trecere prin valoare in loc de prin referinta in `DO...WITH`.
|
||||
- **Neatins**: `COMUN\clase\omodificari.vc2/.vcx/.vct`, `COMUN\programe\updateserver.prg` (ambele
|
||||
infirmate ca posibila cauza, vezi sectiunea 5).
|
||||
- Artefacte de rulare (`watchdog_out\*.png/.log`, `*_log.txt`) raman pe disc ca dovada; se pot sterge
|
||||
cu `curatenie.ps1` la finalul lucrarii.
|
||||
|
||||
## 7. Stare la data acestui raport
|
||||
|
||||
**Cauza confirmata prin bisectie (sectiunea 5) si remediul APLICAT de team-lead** (paranteze la
|
||||
liniile 37/39/50). **Nerulat inca** de nimeni dupa aplicarea remediului - team-lead ruleaza suita
|
||||
separat, dupa eliberarea `.fxp`-ului (nu s-a mai relansat testul in aceasta sesiune, per interdictia
|
||||
primita). Ramane deschis, pentru cine continua:
|
||||
|
||||
1. Confirma cu o rulare ca remediul chiar elimina dialogul si `verifica_pagecount_form` trece PASS
|
||||
pe `PageCount=3`/`lAreArticoleVanzari=.T.` pentru cod=1140888.
|
||||
2. Optional: verifica daca fluxul REAL de productie (`do_editare_factura` in `ofacturare_comun.vc2`)
|
||||
are undeva acelasi tipar `DO...WITH <variabila PUBLIC>` NEPARANTEZAT inainte de un `?param` in
|
||||
SQLEXEC - team-lead a verdictuit "defect de harness", dar asta ramane neverificat exhaustiv pe
|
||||
codul de productie.
|
||||
3. Watchdog-ul (`watchdog_vfp.ps1`) ramane instrumentul de verificat orice ipoteza noua fara sa se
|
||||
agate procesul si FARA input real (regula obligatorie, sectiunea 1) - reutilizabil pentru orice
|
||||
alt blocaj similar in suita.
|
||||
55
docs/cercetare/s10_curata_versiune.sql
Normal file
55
docs/cercetare/s10_curata_versiune.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- S10 - curatare istoric TABELA VERSIUNE, MARIUSM_AUTO/ROA_CENTRAL
|
||||
-- Scop: cele 5 scripturi ff_2026_08_06_* au fiecare mai multe inregistrari in VERSIUNE,
|
||||
-- din aplicari succesive pe masura ce au fost extinse in cursul zilei de 06.08.2026.
|
||||
-- Fara impact functional (versiune_db.txt si aplicarea DDL nu depind de numarul de randuri),
|
||||
-- doar istoric zgomotos. Pastreaza UN singur rand per script (cel cu ID_VERSIUNE maxim, adica
|
||||
-- ultima aplicare - starea finala reala a scriptului), sterge restul.
|
||||
--
|
||||
-- NU S-A RULAT. Propunere pentru aprobare - stergerea de istoric e decizie de om, iar
|
||||
-- MARIUSM_AUTO e schema de dezvoltare partajata.
|
||||
|
||||
-- 1) Verificare inainte de stergere: cate randuri per script, azi
|
||||
select script_final, count(*) as nr_inregistrari
|
||||
from versiune
|
||||
where script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
group by script_final
|
||||
order by script_final;
|
||||
|
||||
-- Stare masurata 06.08.2026: _02=2, _03=1 (nimic de sters), _04=2, _05=4, _06=5 (14 randuri total,
|
||||
-- 9 de sters, ramanand 5 - unul per script).
|
||||
|
||||
-- 2) Stergere: pastreaza doar randul cu ID_VERSIUNE maxim per script (ultima aplicare)
|
||||
delete from versiune v
|
||||
where v.script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
and v.id_versiune < (
|
||||
select max(v2.id_versiune)
|
||||
from versiune v2
|
||||
where v2.script_final = v.script_final
|
||||
);
|
||||
|
||||
-- 3) Verificare dupa stergere: fiecare script din lista trebuie sa aiba exact 1 rand
|
||||
select script_final, count(*) as nr_inregistrari
|
||||
from versiune
|
||||
where script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
group by script_final
|
||||
order by script_final;
|
||||
|
||||
-- commit; -- de dat manual, dupa verificarea pasului 3
|
||||
296
docs/cercetare/valuta_si_curs.md
Normal file
296
docs/cercetare/valuta_si_curs.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Cercetare: `poDate.in_valuta`, `Clb_zi_curs` si cele doua concepte de valuta
|
||||
|
||||
Investigatie read-only (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit). Sursa: cache
|
||||
text `.vc2`/`.prg` din `COMUN\clase\ofacturare.vc2`, `COMUN\programe\ofacturare.prg`,
|
||||
`COMUN\programe\ofacturare_comun.prg`, `COMUN\programe\ofacturare_stoc.prg`,
|
||||
`COMUN\programe\oproceduri_curs.prg`, plus pachetul Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (copie pe disc,
|
||||
alta numerotare decat baza). Reutilizeaza si confirma cercetari anterioare din
|
||||
`docs\cercetare\rec_cale_vanzari_detalii.md` si `docs\cercetare\rec_consumatori_vanzari.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. `poDate.in_valuta` — unde se seteaza si din ce
|
||||
|
||||
**Concluzie**: `in_valuta` e determinat **o singura data**, la `Init`, exclusiv din **tipul
|
||||
documentului** (`tnTip`) plus o lista fixa de `id_set` — niciodata din alegerea manuala a unei
|
||||
valute in antet si niciodata resetat ulterior. E o proprietate a *tipului de factura* (Invoice /
|
||||
credit note / retur valuta / factura fiscala valuta pe contract), nu a faptului ca articolele au
|
||||
preturi in valuta.
|
||||
|
||||
**Dovezi**:
|
||||
- Valoare implicita `0`: `COMUN\programe\ofacturare_comun.prg:165` (`in_valuta = 0`, in blocul de
|
||||
proprietati al obiectului `poDate`).
|
||||
- Singurul loc care il seteaza pe `1`, in `Procedure Init`:
|
||||
```
|
||||
ofacturare_comun.prg:248-250
|
||||
If Inlist(tnIdSet,223,225,226,227,271) OR INLIST(m.tnTip, 5,6,7,9,10,52)
|
||||
.in_valuta = 1
|
||||
Endif
|
||||
```
|
||||
- Legenda tipurilor (comentariu din `frm_date_factura.Init`), `ofacturare.vc2:9571-9582`:
|
||||
`1`=lista preturi, `2`=contract, `3`=comanda, `4`=aviz, `5`=Invoice lista preturi, `6`=Invoice
|
||||
contract, `7`=credit note, `8`=retur lei, `9`=retur valuta, `48`=avize valoric, `52`=Factura
|
||||
fiscala valuta contract. Tipul `10` nu are comentariu explicit in acest bloc (adaugat separat,
|
||||
v2.0.56, ca variatie de tip 1/5).
|
||||
- Nicio alta cale de scriere pe `in_valuta` in `.vc2`/`.prg` din COMUN — verificat cu grep
|
||||
(`\.in_valuta\s*=\s*[01]`) pe intreg `ofacturare.vc2` si `ofacturare.prg`: restul potrivirilor
|
||||
sunt toate **citiri** (`If poDate.in_valuta = 1 ...`), nu atribuiri. `ofacturare_stoc.prg:549`
|
||||
(`poDate.in_valuta = in_valuta`) e o cale alternativa (facturare "din stoc") care primeste
|
||||
parametrul `in_valuta` deja calculat in amonte, cu aceeasi semantica.
|
||||
- Consecinta directa, confirmata de cod: alegerea manuala a unei valute pentru document (control
|
||||
`ct_clb_valuta`) nu poate schimba `in_valuta` — controlul insusi e eliminat din formular cand
|
||||
`in_valuta = 0` (`ofacturare.vc2:9725-9728`), deci userul nu are cum sa-l "aleaga" pe o factura
|
||||
care nu e deja de tip valuta.
|
||||
|
||||
---
|
||||
|
||||
## 2. Campul „Data curs valutar" (`Clb_zi_curs`) — unde e definit si cand e eliminat azi
|
||||
|
||||
**Concluzie**: campul e eliminat **doar** pentru facturi de retur (`tip` 8 sau 9), niciodata pe
|
||||
baza de `in_valuta`. **Da, azi campul apare si pe facturile in lei** (`in_valuta=0`) de orice alt
|
||||
tip — comportament confirmat explicit prin simetrie de cod: imediat dupa blocul de retur, exista un
|
||||
bloc separat care elimina `ct_clb_valuta` cand `in_valuta=0`, dar echivalentul lipseste pentru
|
||||
`clb_zi_curs`.
|
||||
|
||||
**Dovezi** (identificarea claselor facuta cu `vfp_symbols.ps1 -Where`):
|
||||
|
||||
| Clasa | Interval | `Clb_zi_curs` definit | Eliminat azi? |
|
||||
|---|---|---|---|
|
||||
| `frm_date_aviz` | `ofacturare.vc2:6566-7618` | `:6727-6740` (caption "Data cursului valutar") | Niciodata — zero `RemoveObject`/conditionare pe `in_valuta` in toata clasa (verificat exhaustiv) |
|
||||
| `frm_date_aviz_lucrare` | `:7620-8201` | `:7787-7801` | Niciodata; in plus, validare **necondiționata**: `:8076` `Case Empty(Nvl(poDate.zi_curs,{}))` fara nicio garda pe `in_valuta` (spre deosebire de `frm_date_factura`, vezi mai jos) |
|
||||
| `frm_date_factura` | `:8482-9869` | `:8701-8714` (caption "Data curs valutar") | **Doar** pentru `tip` 8/9, in `Init`: |
|
||||
|
||||
```
|
||||
ofacturare.vc2:9717-9722 (frm_date_factura.Init)
|
||||
*!* modificare v 2.0.56
|
||||
If Inlist(poDate.tip, 8, 9)
|
||||
lnHeight = lnHeight - .clb_zi_curs.Height
|
||||
laPozitii(.clb_zi_curs.TabIndex, 2) = 1
|
||||
.RemoveObject('clb_zi_curs')
|
||||
Endif
|
||||
*!* modificare v 2.0.56 ^
|
||||
|
||||
ofacturare.vc2:9725-9728 (imediat dupa, in ACEEASI metoda)
|
||||
If poDate.in_valuta = 0
|
||||
lnHeight = lnHeight - .ct_clb_valuta.Height
|
||||
laPozitii(.ct_clb_valuta.TabIndex, 2) = 1
|
||||
.RemoveObject('ct_clb_valuta')
|
||||
```
|
||||
|
||||
Cele doua blocuri sunt adiacente si scrise dupa acelasi tipar (inaltime, `laPozitii`,
|
||||
`RemoveObject`) — dovada ca autorul original a tratat `ct_clb_valuta` (selectorul de valuta) ca
|
||||
dependent de `in_valuta`, dar **nu** a facut acelasi lucru pentru `clb_zi_curs`. Validarea de
|
||||
completare e de asemenea asimetrica: `ofacturare.vc2:9484`
|
||||
(`Case poDate.in_valuta = 1 And Empty(Nvl(poDate.zi_curs,{})) And ...`) cere data curs **doar** cand
|
||||
`in_valuta=1`, in timp ce `frm_date_aviz_lucrare` (`:8076`) o cere mereu — inconsistenta intre cele
|
||||
doua forme, de retinut daca regula noua trebuie aplicata uniform.
|
||||
|
||||
Nota: `frm_facturare_articole2` (clasa separata, `:15741-19355`) are propriul label "Curs valutar"
|
||||
(control diferit, `:16285-16299`), tratat la punctul 3/5 — nu e acelasi camp cu `Clb_zi_curs` din
|
||||
antet, dar citeste aceeasi `poDate.zi_curs`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Cele doua concepte, in cod — confirmate distinct
|
||||
|
||||
**Concluzie**: da, distinctia exista clar in cod, pe doua axe independente:
|
||||
- **(a) valuta de document**: `poDate.in_valuta` / `poDate.Curs` / `poDate.multiplicator` /
|
||||
`poDate.id_valuta` — un singur curs, al documentului intreg, folosit cand tot documentul e emis
|
||||
in valuta.
|
||||
- **(b) articol cu pret in valuta pe document in lei**: `poArticol.tip_valuta = 1` (proprietate a
|
||||
politicii de pret a articolului, independenta de `poDate.in_valuta`), cu preturile brute in
|
||||
valuta pastrate in `poArticol.pretftva_val`/`pretctva_val`/`pretd` + `id_valuta_d`, convertite in
|
||||
lei **client-side, in VFP**, folosind cursul zilei `poDate.zi_curs`.
|
||||
|
||||
**Dovezi — conversia in lei pentru cazul (b)**:
|
||||
```
|
||||
ofacturare.vc2:1989 poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
ofacturare.vc2:2953 poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
ofacturare.vc2:2017 poArticol.pretctva = Round(poArticol.pretctva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
```
|
||||
`poArticol.Curs`/`multiplicator` (nu e alt curs decat cel al zilei documentului) provin din cursorul
|
||||
`crscursuri`, incarcat explicit **cand documentul e in lei** — exact pentru articolele cu preturi in
|
||||
valuta:
|
||||
```
|
||||
ofacturare.prg:407-418 (identic si la :941-947)
|
||||
If poDate.in_valuta = 1
|
||||
Select Distinct Curs, nume_val, id_valuta, multiplicator From (lcCursor) ... Where tip_valuta = 1 ... Into Cursor crscursuri
|
||||
Select crscursuri
|
||||
poDate.Curs = Curs
|
||||
poDate.multiplicator = multiplicator
|
||||
Else
|
||||
citeste_cursuri_zi(poDate.zi_curs)
|
||||
If Reccount('crscursuri') = 0
|
||||
Use In crscursuri
|
||||
Endif
|
||||
Endif
|
||||
```
|
||||
`citeste_cursuri_zi` (`COMUN\programe\oproceduri_curs.prg:113-124`) construieste `crscursuri` cu
|
||||
**toate** cursurile valabile la `tdDataCurs` (`= poDate.zi_curs`):
|
||||
```
|
||||
oproceduri_curs.prg:117-118
|
||||
lcSql = [select nume_val,curs,id_valuta,multiplicator from ] + gcS + [.vcurs where data <= ... and data2 >= ...]
|
||||
```
|
||||
Cursorul e apoi afisat direct pe grila de selectie a articolelor, cu eticheta construita din
|
||||
`poDate.zi_curs`, **indiferent de `in_valuta`**:
|
||||
```
|
||||
ofacturare.vc2:15097-15098 si 19004-19005 (frm_facturare_articole2)
|
||||
If !Empty(Nvl(poDate.zi_curs, {}))
|
||||
Thisform.lb_cursuri.Caption = "Curs valutar " + Iif(!Inlist(poDate.tip, 8, 9), "(" + Dtoc(poDate.zi_curs) + ")", "")
|
||||
```
|
||||
Grila/eticheta apar doar daca `crscursuri` are randuri (`ofacturare.vc2:15092`/`19000`), ceea ce se
|
||||
intampla si pe un document in lei, daca exista macar un articol cu politica in valuta.
|
||||
|
||||
**`VANZARI` vs `VANZARI_DETALII` — ce se stocheaza**:
|
||||
- `VANZARI` (document): coloane proprii `CURS`/`ID_VALUTA`/`MULTIPLICATOR` — cursul/valuta **doar
|
||||
ale documentului**, populate din `poDate.Curs`/`id_valuta`/`multiplicator` (relevante cand
|
||||
`in_valuta=1`).
|
||||
- `VANZARI_DETALII` (linie): **nu are coloana `CURS`** — confirmat din `all_tab_columns`
|
||||
(`docs\cercetare\rec_cale_vanzari_detalii.md:171-175`) si din SQL-ul live al pachetului, care
|
||||
reface cursul liniei prin JOIN, nu dintr-o coloana proprie:
|
||||
```
|
||||
PACK_FACTURARE.sql:3773-3778 (identic la :4034-4036, :6393-6396, :6571-6573)
|
||||
LEFT JOIN VANZARI_CURSURI B1 ON A1.ID_VANZARE = B1.ID_VANZARE AND A1.ID_VALUTA = B1.ID_VALUTA
|
||||
```
|
||||
Linia stocheaza insa `PRET` (pretul efectiv, in lei, folosit pe factura) **si** `PRETD` +
|
||||
`ID_VALUTAD` (pretul brut in valuta si valuta originii, pastrate ca referinta/audit) — vezi
|
||||
INSERT-ul din `adauga_articol_factura`
|
||||
(`docs\cercetare\rec_cale_vanzari_detalii.md:59-65`, coloanele `PRET`, `PRETD`, `ID_VALUTAD`,
|
||||
`ID_VALUTA` alaturi). Deci **da**, se pastreaza si urma valutei originale pe linie, dar valoarea
|
||||
de facturare efectiva e mereu in lei pe `VANZARI_DETALII.PRET`.
|
||||
|
||||
**La ce se foloseste `VANZARI_CURSURI`**: e un instantaneu al cursurilor pentru **orice valuta
|
||||
straina aparuta printre liniile documentului** (nu doar valuta proprie a documentului), scris o
|
||||
singura data la emitere, indiferent de `in_valuta`:
|
||||
```
|
||||
PACK_FACTURARE.sql:14491-14501
|
||||
PROCEDURE scrie_cursuri(V_ID_VANZARE IN NUMBER) IS
|
||||
BEGIN
|
||||
INSERT INTO VANZARI_CURSURI (ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR)
|
||||
SELECT DISTINCT V_ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR
|
||||
FROM VANZARI_DETALII_TEMP
|
||||
WHERE ID_VALUTA <> pack_facturare.nid_moneda_nationala;
|
||||
END scrie_cursuri;
|
||||
```
|
||||
Rulat necondiționat la fiecare emitere (apelat din `scrie_in_vanzari`, vezi
|
||||
`docs\cercetare\rec_cale_vanzari_detalii.md:98-102`) — deci **si pe o factura in lei** cu articole
|
||||
in valuta se scrie cate un rand `VANZARI_CURSURI` pentru fiecare valuta straina aparuta pe linii,
|
||||
cu cursul citit la `poDate.zi_curs`. Tabela e folosita ulterior la reafisare/reprint/retur, ca sursa
|
||||
a cursului per-linie (JOIN-urile de mai sus).
|
||||
|
||||
---
|
||||
|
||||
## 4. Ce se strica daca ascundem campul cand "nu are sens"
|
||||
|
||||
**Concluzie**: nu exista risc de "factura fara curs deloc" — `poDate.zi_curs` primeste mereu o
|
||||
valoare implicita la `Init` (data documentului) si nu poate ramane `NULL`. Riscul real e altul:
|
||||
**userul pierde controlul asupra datei de curs pentru cazul (b)** — o factura in lei cu articole in
|
||||
valuta ar folosi mereu cursul zilei curente/implicite, fara sa poata fi corectat manual, exact in
|
||||
situatia in care lipseste cursul pentru acea data (eroarea Oracle 20005, punctul 5) sau cand userul
|
||||
vrea sa aliniaze cursul cu data unui document sursa (aviz, contract).
|
||||
|
||||
**Consumatori confirmati ai `poDate.zi_curs`** (grep exhaustiv `poDate\.zi_curs`, fisiere
|
||||
`ofacturare.vc2`, `ofacturare.prg`, `ofacturare_stoc.prg` — singurele 3 din COMUN):
|
||||
|
||||
| Loc | Ce primeste | Conditionat de `in_valuta`? |
|
||||
|---|---|---|
|
||||
| `ofacturare.vc2:6105-6107`, `:13994-13996`, `:18030-18032` | parametru `to_date(...)` catre `pack_facturare.initializeaza_date_factura(...)` la fiecare finalizare de antet | **Nu** — trimis pentru orice tip |
|
||||
| `ofacturare.prg:272,276,281,297,303` | primul parametru al `pack_facturare.cursor_articole_k` / `cursor_preturi` / `cursor_gestiune` / `cursor_lucrare` — SQL-ul care aduce lista de articole/preturi afisata userului | **Nu** — inclusiv pentru `tip=1` (lista de preturi, lei) |
|
||||
| `ofacturare_stoc.prg:79,335` | acelasi rol, pe calea alternativa "facturare din stoc" | Nu |
|
||||
| `ofacturare.vc2:15097-15098`, `:19004-19005` | eticheta grilei "Curs valutar (data)" din `frm_facturare_articole2` | Nu — apare oricand `crscursuri` are randuri |
|
||||
| `ofacturare.vc2:9484` | validare "Nu ati completat ziua cursului valutar" | **Da**, doar `in_valuta=1` (`frm_date_factura`) |
|
||||
| `ofacturare.vc2:8076` | aceeasi validare | **Nu**, necondiționata (`frm_date_aviz_lucrare`) |
|
||||
|
||||
**Riscul concret**: daca regula noua ascunde/dezactiveaza campul strict cand `poDate.in_valuta=0`,
|
||||
pe `frm_date_factura` (tip 1, lista de preturi) userul nu va mai putea edita `zi_curs` inainte de a
|
||||
deschide grila de articole — dar `ofacturare.prg:279-282` tot va trimite acel `poDate.zi_curs`
|
||||
(neschimbat, ramas pe data initializata la `Init`) catre `cursor_preturi`, iar daca acolo Oracle
|
||||
ridica eroarea 20005 (curs lipsa pentru acea data/valuta), fluxul de recuperare
|
||||
(`vizualizeaza_curs`, punctul 5) va porni oricum, dar userul nu va (mai) avea camp pe antet ca sa
|
||||
retina/corecteze data dupa ce inchide formularul de curs. Pe `frm_date_aviz_lucrare`, ascunderea ar
|
||||
intra chiar in conflict cu validarea necondiționata de la `:8076`, care ar bloca finalizarea
|
||||
antetului cerand un camp care nu mai exista pe ecran — de reparat impreuna, nu doar ascuns.
|
||||
|
||||
---
|
||||
|
||||
## 5. Formularul de curs valutar din antet — localizare si traseu
|
||||
|
||||
**Concluzie**: nu exista niciun buton/dblclick pe `Clb_zi_curs` care sa deschida formularul de curs
|
||||
(verificat exhaustiv — zero hit pentru `Clb_zi_curs.DblClick/RightClick/Click` sau `but_curs` in
|
||||
`ofacturare.vc2`). Formularul se deschide **automat**, ca recuperare de eroare, dupa ce antetul
|
||||
(`frm_date_factura`/`frm_date_aviz`) s-a inchis deja si codul incearca sa incarce lista de articole.
|
||||
|
||||
**Traseu**:
|
||||
1. `ofacturare.prg:228-235` — `ofrmceredate = Createobject(lcObiect, toFactura)` (`lcObiect` =
|
||||
`frm_date_factura` sau `frm_date_aviz`), `.Show()` modal; la inchidere, `Release ofrmceredate`
|
||||
(`:248`).
|
||||
2. `ofacturare.prg:266-308` — pe baza tipului, se construieste apelul catre
|
||||
`pack_facturare.cursor_preturi(?poDate.zi_curs, ...)` (sau `cursor_articole_k`/`cursor_gestiune`/
|
||||
`cursor_lucrare`) si se executa (`:311` `goExecutor.oExecute(lcSqlCursor, lcCursor)`).
|
||||
3. Daca esueaza cu eroarea Oracle **20005** (curs lipsa pentru data/valuta ceruta):
|
||||
```
|
||||
ofacturare.prg:313-317 (identic la :828-832)
|
||||
If lnSucces < 0
|
||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
||||
If goExecutor.nEroare = 20005
|
||||
vizualizeaza_curs(poDate.zi_curs)
|
||||
ENDIF
|
||||
```
|
||||
4. `vizualizeaza_curs(tdDataCurs)` — `COMUN\programe\oproceduri_curs.prg:8-42` — construieste
|
||||
cursorul `crscurs` si deschide `loFrmCurs = Createobject("frm_curs", tdDataCurs)` /
|
||||
`.Show(1)` (modal).
|
||||
5. Clasa `frm_curs` e definita in **`COMUN\clase\onom_curs.vc2:537`** (`DEFINE CLASS frm_curs AS
|
||||
_frmbase ...`), cu formulare-satelit pentru adaugare curs: `frm_curs_nou`
|
||||
(`onom_curs.vc2:1066`) si `frm_curs_nou_multiplu` (`onom_curs.vc2:1330`, apelat de la
|
||||
`onom_curs.vc2:941`).
|
||||
6. A doua cale catre acelasi formular: `citeste_cursuri_stoc` (`oproceduri_curs.prg:45-110`, folosit
|
||||
pe fluxul de facturare din stoc) cheama `vizualizeaza_curs()` **fara parametru de data**
|
||||
(`oproceduri_curs.prg:94`) cand gaseste valute fara curs pentru ziua ceruta, in bucla `Do While
|
||||
llVerificare` (retry pana userul completeaza sau renunta).
|
||||
|
||||
Aceasta e exact situatia din `COMUN\docs\todos.txt:45` (punctul 16): "la finalizare se verifica
|
||||
cursul valutar necesar pentru politicile de preturi care se factureaza [...] la revenire din
|
||||
formularul de curs valutar, focusul revine [...] pe TIP DOCUMENT, si la iesire din serie se
|
||||
regenereaza numar act" — confirmat ca fenomenul are loc **dupa** ce antetul (`frm_date_factura`) e
|
||||
deja inchis si eliberat (`Release ofrmceredate`, pasul 1), deci orice regenerare de numar/focus
|
||||
vizibila dupa inchiderea `frm_curs` tine de ecranul/starea care ramane activa in spate (nu s-a
|
||||
localizat mai departe — cere depanare separata, in afara scopului "doar localizare" cerut aici).
|
||||
|
||||
---
|
||||
|
||||
## Cand are sens campul „Data curs valutar" — propunere de regula
|
||||
|
||||
Campul are sens de aratat/editabil pe antet exact cand exista **vreo** sansa ca cursul zilei sa fie
|
||||
folosit la facturare — nu doar cand documentul insusi e in valuta:
|
||||
|
||||
> Arata (si activeaza) `Clb_zi_curs` cand `poDate.tip` NU e retur (`!Inlist(poDate.tip,8,9)`) **SI**
|
||||
> (`poDate.in_valuta = 1` **SAU** exista macar o politica de pret in valuta accesibila tipului
|
||||
> curent de document — adica exact conditia care astazi populeaza `crscursuri` cu randuri, verificata
|
||||
> deja empiric la `ofacturare.vc2:15092`/`:19000`: `Used('crscursuri') And Reccount('crscursuri') > 0`
|
||||
> dupa incarcarea listei de articole). Pe retur (8/9) ramane ascuns ca azi.
|
||||
|
||||
Pe `frm_date_aviz`/`frm_date_aviz_lucrare`, unde `crscursuri` nu e inca disponibil la momentul
|
||||
antetului (se incarca dupa, la fel ca la factura), regula echivalenta practicabila **pe antet** (nu
|
||||
dupa) ar fi: arata campul daca tipul documentului admite politici de pret in valuta pentru
|
||||
sucursala/gestiunea curenta (verificare care azi nu exista pe antet, doar mai tarziu pe grila de
|
||||
articole) — necesita fie mutarea verificarii mai devreme, fie acceptarea ca antetul nu poate sti cu
|
||||
certitudine inainte de a incarca articolele, caz in care campul ramane vizibil implicit si doar
|
||||
eticheta/relevanta lui se clarifica ulterior (ca azi, in `frm_facturare_articole2`).
|
||||
|
||||
---
|
||||
|
||||
## Necunoscute ramase
|
||||
|
||||
- Nu s-a confirmat daca exista si alte politici (`CRM_POLITICI_PRET*`) sau reguli de sucursala care
|
||||
determina *inainte* de deschiderea grilei de articole daca vor exista linii `tip_valuta=1` — utile
|
||||
pentru a decide vizibilitatea campului chiar pe antet, nu doar reactiv dupa incarcarea articolelor.
|
||||
- Nu s-a urmarit complet "focusul sare pe tip document / regenerare numar act" (punctul 5/todos #16)
|
||||
dupa inchiderea `frm_curs` — s-a localizat doar traseul de deschidere, nu si ecranul/handler-ul
|
||||
care ramane activ in spate si cauzeaza simptomul; cere sesiune separata de depanare (posibil in
|
||||
`poGeneratorNumere`/`clb_serie_act`, cf. `ofacturare.vc2:9735-9737`, needatate aici).
|
||||
- Layout-urile `.frx` (neconvertite) care afiseaza `poDate.Curs`/`cValuta` pe hartie nu au fost
|
||||
verificate — gap deja semnalat in `rec_consumatori_vanzari.md` (risc posibil #3), ramane valabil.
|
||||
- Nu s-a confirmat empiric (interogare Oracle) daca exista azi in productie facturi `tip=1` (lei) cu
|
||||
linii `ID_VALUTA <> moneda_nationala` in `VANZARI_DETALII` — dovada ar intari direct concluzia
|
||||
punctului 3, dar cercetarea a fost strict pe cod (fara conexiuni DB, conform mandatului).
|
||||
Reference in New Issue
Block a user