Scoate documentele de cercetare rec_* din COMUN\docs\cercetare

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aroafxp4z8bmM5oVECZRXY
This commit is contained in:
2026-09-09 22:00:19 +03:00
parent 940bb39701
commit 7cf0e10a2e
11 changed files with 0 additions and 3076 deletions

View File

@@ -1,220 +0,0 @@
# 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.

View File

@@ -1,124 +0,0 @@
# Propuneri de directie ROACONT (09.2026)
Propunerile finale dupa cercetarea de piata si backtestul pe 23 de scheme de productie. Cifrele
marcate "masurat" vin din backtest cronologic pe date reale, comparat cu ce a confirmat operatorul;
restul sunt marcate "nemasurat". Dovezile si metoda: `ROACONT\docs\cercetare\piata_ai_2026_09\`.
Premisa: fara clienti noi, fara rescriere, datele raman in Oracle-ul clientului.
## 1. Import eFactura primite
Ecranul `frm_import_efactura` (`COMUN\clase\anaf_efactura.vcx`, lansat din
`COMUN\programe\import_efactura.prg:167`). Aici e concentrata munca manuala repetitiva a lunii:
13.418 facturi de intrare si 33.844 linii de articol pe an la un cabinet de 21 de firme, din care
15.565 linii primesc cont de la operator. Restul fluxului lunar e deja automatizat.
**1.1 Cheie de articol normalizata.** `GetArticolEFByPartDenumire`
(`COMUN\programe\oproceduri_comune.prg:7362`) cauta in `ANAF_VEFACTURA_DETALII` cu
`TRIM(UPPER(d.articol)) = ?pcDenumire` - litera cu litera, deci orice cifra din denumire strica
potrivirea ("Abonament 08/2026" vs "09/2026"). Propunere: cheie fara cifre + prefix configurabil per
firma. Masurat: 52,0% -> 72,3% recunoscute, ~3.150 linii/an in plus; precizia scade 95,0% -> 93,4%.
**1.2 Contabilizare in lot.** Semafor pe factura (verde = ultimele 5 facturi ale furnizorului contate
identic; galben = istoric variabil; rosu = furnizor nou), buton pentru tot ce e verde, anulare in
bloc. Masurat pe cont sintetic: 41,3% din facturi pe verde cu 93,8% precizie, nicio firma sub 85% -
circa 5.200 facturi/an fara interventie. Anularea in bloc nu e optionala.
**1.3 LLM doar pe rest.** ~28% din linii nu au precedent, plus 7% furnizori noi. Se trimite doar
textul articolului si lista de conturi - fara sume, fara parteneri. Sub 20 USD/luna pentru toti
clientii. Obligatoriu: eticheta vizibila ca sugestia vine de la AI (regula UE, august 2026).
## 2. Import extrase de banca
Flux: `Programe\ocont2003.prg:845` -> fabrica `ImportNote` (`Programe\oproceduri_import.prg:57`) ->
parser per format -> imperechere in `ExtrasBanca::CreeazaNote` (`oproceduri_import.prg:312-926`) ->
corectie manuala in `frm_modific2024` -> scriere in `ACT` cu `id_set = 90023`.
**2.1 Cheie de partener invatata din istoric.** Azi partenerul se ia de pe o pozitie fixa in
descriere (`oproceduri_import.prg:3013`) si se cauta exact in nomenclator
(`oproceduri_comune.prg:6623-6645`). Nu exista cheie disponibila la toate formatele: codul fiscal
lipseste la majoritatea (la BT e fixat gol, `oproceduri_import.prg:3015`), iar IBAN-ul nu are pe ce
lucra - doar 4,5% din parteneri au `CONT_BANCA` completat. Propunere: se retine din istoric ce
partener a fost confirmat pentru un IBAN si pentru un text de descriere normalizat, si se propune
doar cand acea cheie a dus **de fiecare data** la acelasi partener.
Masurat pe 3.490 de linii, 7 firme, 12 luni: **4,6% -> 28,5% acoperire la 97,9% precizie**.
Pe firme: 79,5% (BT) ... 10,5% (banci care trimit doar numere de referinta) - regula tace singura
acolo unde textul nu spune nimic, deci nu cere configurare per banca.
**Garda de unicitate e esentiala**: fara ea, 58,3% acoperire dar 56,4% precizie.
**2.2 Salvarea IBAN-ului pe partener.** Parserul extrage IBAN-ul (`oproceduri_import.prg:3014`, scris
in `explicatia4` la `:902`) dar nu ajunge in baza: gol la 878 din 879 de linii. Daca s-ar salva pe
partener la confirmarea operatorului, nomenclatorul s-ar umple singur si cheia cea mai sigura ar
deveni utilizabila.
**2.3 Factura: nu se automatizeaza, se scurteaza cautarea.** Masurat pe 3.074 de linii legate de o
factura: numarul acelei facturi apare in textul bancii doar in **25,0%** din cazuri (0% - 56,6% pe
firma). Potrivirea dupa suma pe documentele deschise acopera 21,2% cu 89,9% precizie, prea slaba
pentru completare automata. Plafonul e structural - informatia lipseste din fisier. Efortul merita
mutat pe: dupa ce partenerul e cunoscut, arata lista scurta a documentelor lui deschise ordonate
dupa potrivirea sumei.
**2.4 Defecte confirmate (de reparat oricum, independent de propuneri).**
- Pozitiile fixe din descrierea BT nu sunt validate (`oproceduri_import.prg:3013-3014`): la "Plata OP
inter" si "Comision plata OP" descrierea are un camp in plus, deci denumirea se citeste ca numar de
OP si IBAN-ul ca denumire. Dovedit prin test headless pe fisiere reale.
- Bucla de asociere document iese neconditionat dupa primul numar incercat
(`EXIT`, `oproceduri_import.prg:736`), contrar comentariului de la `:701-706` - o plata care acopera
mai multe facturi ramane nepereche.
- La numar de document ambiguu, `GetDocumentByContPartenerAct` doar scrie in log
(`oproceduri_comune.prg:7038`) fara sa arate operatorului candidatii.
- **`VerifIBAN` (`COMUN\programe\oproceduri_comune.prg:7885`) intoarce raspuns gresit** cand apelantul
are selectat un cursor cu camp `iban`: variabilele nu sunt declarate si citirile neprefixate se
rezolva la CAMP, nu la parametru. Afecteaza si validarea IBAN-ului la introducerea partenerului
(`onomenclatoare.vcx`, `oparteneri.vcx`). Corectie: `LOCAL` + prefix `m.` pe toate referintele.
**2.5 Adoptie.** Doar 7 din 21 de firme au linii venite din import de extras. Inainte de a imbunatati
algoritmul, merita aflat de ce nu se foloseste la celelalte.
## 3. Restul, in ordinea recomandata (nemasurate)
1. **Drepturi in modulul web** - blocant: pana nu se rezolva, nimic nu se poate arata unui client.
2. **Punte MCP peste Oracle** - intrebi in Claude Code, el ruleaza interogari pregatite; modelul
primeste rezultatul, nu baza. Singura cale prin care AI-ul intra adanc in ROA fara rescriere.
3. **Tablou de bord multi-firma cu alerte** (luna neinchisa, token SPV expirat, facturi
necontabilizate) - **singurul candidat de abonament nou** din toata cercetarea.
4. **Inchiderea de luna ca flux unic** - azi 13 comenzi in doua meniuri, fara ordine si fara bifare.
5. **Sablon de import la preluarea unui client** - maparea de conturi produsa o data si refolosita.
6. **Asistent de suport pe documentatie** - butonul de chat exista, ii lipseste continutul.
7. **e-Transport** - azi nu exista nimic.
## 4. Respinse, ca sa nu fie repropuse
- **Bon fiscal digital**: QR-ul contine doar identificatori (data/ora, numar, seria AMEF, optional CUI
cumparator), nu valoare/TVA/cote; nu exista serviciu ANAF pentru descarcarea bonurilor **primite**;
obligatiile cad pe producatorii de case de marcat. Zero efort in ROACONT.
- **Codul de articol din eFactura**: furnizorii nu-l trimit (34 linii din 4.426).
- **GPU local pentru AI**: costa cat 30 de ani de abonament la volumele actuale.
- **Rescriere in alt limbaj / mutare in browser**: un an fara actualizari legislative = pierderea
clientilor. Nici SAGA nu face asta.
- **Cloud propriu cu datele clientilor**: cost de operare imposibil pentru doi oameni si sterge
singurul avantaj fata de SmartBill/Oblio/Keez.
- **Intrebari in limbaj natural direct pe baza de date**: 31% raspunsuri corecte pe baze reale.
## 5. Capcane de masurare (platite)
- Numarul de linii cu `ID_FACTD/ID_FACTC` completat **nu** masoara automatizarea - imperecherea o face
operatorul. Nu exista tabel de audit: corectiile din `frm_modific2024` se fac inainte de scrierea in
`ACT` (`ocont2003.prg:1245-1257`). Singurul backtest valid: se reia regula cronologic pe
`ACT.EXPLICATIA` al liniilor cu `id_set = 90023` si se compara cu ce e inregistrat pe linie.
- Pe o linie de banca, partenerul se ia **de pe latura contului 401/411**, nu cu
`nvl(id_partd, id_partc)` - pe latura 512 partenerul e contul bancar propriu si cifrele ies fals
de bune.
- Subinterogarile corelate peste `ACT` omoara serverul; se scriu cu JOIN si hint `materialize`.
## 6. Surse externe pentru afirmatiile de piata
Verificate 03.09.2026.
- SmartBill a livrat preluarea si prelucrarea automata a eFacturilor primite din SPV la 28.08.2026:
ajutor.smartbill.ro/article/1256 si /1128. Extrase bancare prin open banking: /article/1073.
- Obligatia de transparenta AI (Regulament UE, Art. 50) se aplica din 02.08.2026 - utilizatorul trebuie
informat ca interactioneaza cu AI: bchlaw.eu/articles/regulamentul-european-privind-ia-etapa-de-la-2-august-2026/
- Pret GPU la 08.2026: RTX 5090 ~28.000 lei, RTX PRO 6000 96GB ~76.000 lei (techpowerup.com/351549).
- Intrebari in limbaj natural pe baze de date: 82% pe BIRD, 31% pe Spider - de aceea e respins.
- Bon fiscal digital: sursele legale, in `ROACONT\docs\cercetare\piata_ai_2026_09\BON_FISCAL_DIGITAL.md`.

View File

@@ -1,358 +0,0 @@
# 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`.

View File

@@ -1,233 +0,0 @@
# Precompletarea la importul de eFacturi
Directia, dupa masuratorile din 07-08.09.2026 pe toata productia (cabinetul `ROA_ROMFAST`, 21 de
scheme, si `VENDING`), read-only, backtest cronologic fata de ce a facut operatorul in realitate.
Principiul, si singurul criteriu de acceptare a oricarei propuneri de aici: **liniile vin completate,
fara niciun click, ecran sau formular nou.** Importul nu e adoptat pentru ca nu aduce inca un
beneficiu; orice adaugam trebuie sa scada munca, nu s-o mute.
Dovezile: `ROACONT\docs\backtest_precompletare_cont.md`, `backtest_precompletare_runda2.md`,
`inventar_campuri_de_retinut.md`, `masuratori_romfast_efactura.md`, `masuratori_vending_efactura.md`,
`profilare_semafor_vending.md`. Scripturi: `ROACONT\docs\cercetare\piata_ai_2026_09\sql\bt_26*`, `bt_27*`.
## 0. Ce se arunca
**Unde**: `COMUN\programe\semafor_contabilizare_ef.prg`, coloana de semafor din `frm_import_efactura`.
**Cum e azi**: o interogare mare calculeaza per factura o culoare (verde/galben/gri/articol nou) din
stabilitatea istoricului de conturi al furnizorului.
**Ce se schimba**: dispare, cu totul.
**Ce se castiga**: 36s per rulare la VENDING, 4,2s pe cabinet, la deschiderea ecranului si la fiecare
cautare (`masurat`). Profilarea arata ca timpul nu vine din scanarea lui `ACT` (0,6s) ci din combinatia
catalog + cascada normalizata din aceeasi interogare (35,3s izolat).
**Ce risc**: niciunul functional. Culoarea nu schimba nicio decizie a operatorului: pe cabinet 77,7%
din facturi ies "articol nou" si doar 3,8% verzi, iar relaxarea pragului de stabilitate de la 5 la 3
documente aduce +13 facturi din 924 (`masurat`).
Ramane din runda 18 doar verificarea locala a lipsurilor (`CoadaContabilizareEF.LipsuriDetalii`, fara
Oracle), ca sa nu iasa tacit documente cu cont gol pe ruta `ImportGeneral`.
**Coada de import ramane**, dar numai pentru facturile complet completate: o factura careia ii lipseste
ceva nu intra in lot, se debifeaza singura si motivul se vede pe rand. Regula e deja scrisa (runda 18) si
nu depinde de semafor - lipsurile se calculeaza local, pe liniile facturii. Dupa precompletare lotul
devine util de la sine: 9,0% -> 66,5% din facturi fara nicio lipsa pe cabinet (`masurat`).
## 1. Contul si analiticul pe linie
**Unde**: `COMUN\programe\recunoastere_articol_ef.prg`, folosit din
`frm_import_efactura.completeazaDetaliiFactura` (`COMUN\clase\anaf_efactura.vc2`).
**Cum e azi**: contul se cauta in catalogul de articole (denumire/codbare/codmat/codmatf,
`oproceduri_comune.prg:7267`) si in istoricul **aceluiasi furnizor pe acelasi articol**, text exact si
cheie normalizata. Ce nu prinde, ramane gol.
**Ce se schimba**: doua surse noi, dupa cele existente, doar pentru liniile ramase goale:
1. **articolul contat oriunde in firma**, indiferent de furnizor, pe cheia normalizata, cu garda de
unicitate la 70% si fereastra **ultima factura**;
2. **contul dominant al furnizorului**, indiferent de articol, cu garda la 90%, fereastra **ultimele
trei facturi**, si **doar contul sintetic** - analiticul ramane al operatorului;
3. ce mai ramane ia **contul majoritar al celorlalte linii ale aceleiasi facturi**, fara prag.
Analiticul liniei (`3xxx.xxx`, `6xxx.xxxx`) se propune doar din sursa 1 (94,3% precizie), niciodata din
sursa 2 (79,3%).
Analiticul contului de furnizor si de client din antet (`401.xxxx`, `4111.xxxx`) nu intra: exista ca
mecanism (`IREG_PARTENERI`/`BALANTA_PARTENERI`, cu partener + cont + analitic), dar nu e folosit - vezi
respinse.
**Ce se castiga** (`masurat`, cabinet, 15.828 linii/an): acoperire pe linie 77,8% -> 97,7%; facturi fara
nicio lipsa de cont 9,0% -> 66,5%; la VENDING 2,5% -> 69,2%. Fereastra conteaza mult: pe sursa 1,
ultima factura da 85,0% acoperire cu 93,7% precizie, fata de 60,5% / 90,1% la 36 de luni, iar castigul
e cel mai mare la furnizorii care factureaza lunar (>=8 facturi/an).
**Ce risc**: precizia propunerilor scade de la 89,5% la 84,9% (`masurat` cu ferestrele vechi; de
recalculat la implementare, cifra e un plafon pesimist - treptele noi masurate cu fereastra corecta dau
93-94%). Practic: din 100 de linii precompletate circa 15 se corecteaza, fata de 22 care azi se scriu
de la zero. Mitigare fara UI nou: `sursa_cont` exista deja pe linie si arata de unde vine propunerea.
## 2. Gestiunea pe linie
**Unde**: aceeasi cascada; azi gestiunea vine doar din istoricul (furnizor, articol), altfel din optiunea
globala `EFACTURA_ID_GESTIUNE_P`.
**Cum e azi**: la firmele cu mai multe gestiuni, optiunea globala e practic inutila - niciuna din cele
21 de firme nu are o singura gestiune activa (au intre 2 si 467), deci nu exista implicit cinstit.
**Ce se schimba**: gestiunea trece prin aceleasi surse noi, cheia fiind partener+articol, fereastra
ultima factura.
**Ce se castiga** (`masurat`): facturi complet rezolvate la VENDING 23,3% -> 86,0% (acoperire 97,2%,
precizie 98,1%); pe cabinet 53,0% -> 89,1%. Conteaza aproape numai la firmele cu stoc: acolo gestiunea
e 38,7% din blocaje, la cabinet 4,2%.
**Ce risc**: stabilitatea per (partener, articol) e 64% - mai mica decat la cont. Ramane peste alternativa
de azi (o gestiune unica pe toata firma), dar merita marcata ca propunere, nu ca certitudine.
## 3. Data scadentei
**Unde**: `crsFacturi.data_scad`, `frm_import_efactura.completeazaFactura`.
**Cum e azi**: se scrie de mana la fiecare factura.
**Ce se schimba**: se propune scadenta de pe ultima factura a aceluiasi partener.
**Ce se castiga** (`masurat`): se completeaza la 86,1% din facturi si coincide cu ultima factura a
aceluiasi partener in 87,3% din cazuri.
**Ce risc**: cel mai mare din tot pachetul. O scadenta gresita nu se vede pe ecran, dar ajunge in balanta
si in raportarile de plati. Se propune doar cand campul din eFactura e gol, niciodata peste o valoare
venita din XML.
## 4. Contractul
**Unde**: `crsFacturi.Id_Ctr`.
**Cum e azi**: ales de mana, cand se foloseste.
**Ce se schimba**: se propune contractul de pe ultima factura a aceluiasi partener.
**Ce se castiga** (`masurat`): se completeaza rar (16,8% din facturi), dar cand se completeaza e acelasi
in 93,8% din cazuri - deci ieftin si sigur, pentru firmele care lucreaza pe contracte.
**Ce risc**: mic; camp fara efect contabil direct.
## 5. Alegerile din antet care azi se pierd: deducerea, discountul, contul si analiticul partenerului
**Unde**: `cboDeducere` (`COMUN\clase\anaf_efactura.vc2:9672`, folosit la `:13153`),
`chkDistribuieDiscount` (`:12254`), si campurile de antet `Cont`/`ACont` (`txtCont`/`txtAcont`,
cursorul `vizImportEFactura` in `COMUN\programe\import_efactura.prg:44`).
**Cum e azi**: toate patru sunt **tranzitorii**. `Cont` si `acont` vin goale din interogare (`'' as Cont,
'' as acont`), se completeaza la rulare si nu se scriu inapoi; deducerea si discountul merg direct in
nota contabila. In DDL (`anaf_efactura.sql`) nu exista nicio coloana pentru ele. Deci la fiecare factura
de la acelasi furnizor se aleg din nou: analiticul lui `401`, deducerea 50%, distribuirea discountului.
**Ce se schimba**: patru coloane noi pe `ANAF_EFACTURA` in care se salveaza ce a ales operatorul la
import (`cont`, `acont`, deducerea, distribuirea discountului), si repropunerea valorilor de la ultima
factura a aceluiasi partener - furnizor sau client, cu aceeasi cheie.
Asta acopera si analiticul `401.xxxx` / `4111.xxxx`: nu se deduce din balanta (vezi respinse), ci se
**tine minte ce a scris operatorul** si se repropune la acelasi partener. Nu depinde de faptul ca azi
nimeni nu foloseste analitice pe aceste conturi - daca operatorul scrie unul, se retine.
**Ce se castiga**: `nemasurat` - stabilitatea nu se poate masura azi, pentru ca valoarea nu se salveaza.
Se masoara dupa prima luna de date.
**Ce risc**: cere script de migrare si versiune noua de `.exe`. Deducerea gresita are efect fiscal, deci
se propune, nu se aplica tacit: valoarea propusa ramane vizibila in acelasi loc in care se alege azi.
## 6. Facturile emise (importate din SPV)
**Cum e azi**: masuratorile s-au facut doar pe primite; 15 din 20 de scheme au si emise importate
(1.680 linii / 1.456 facturi pe 12 luni).
**Ce se schimba**: aceeasi cascada, cu clientul in locul furnizorului. Codul e deja simetric
(`lPrimite`, perechile de optiuni `_E`/`_P`).
**Ce se castiga** (`masurat`): acoperire 98,8%, precizie 97,7% - dar spatiul de conturi de vanzare e
foarte ingust (unele firme folosesc un singur cont pe an), deci cifra e reala si usoara, nu comparabila
cu achizitiile. La VENDING sunt 22.457 de facturi emise, dar prin alt flux, fara conturi pe linii -
nemasurabil.
## 7. Configurarea, care azi nu se deschide niciodata
**Unde**: `frm_import_efactura.Cmd_executa2.Click` si `frm_configurare_efactura`
(`COMUN\clase\anaf_efactura.vc2:14615`). Analiza completa:
`ROACONT\docs\analiza_configurare_import_efactura.md`.
**Cum e azi**: 15 optiuni `EFACTURA_*`, dintre care doar patru schimba cu adevarat rezultatul importului
(contul implicit de articole si gestiunea implicita, pe primite si pe emise). Restul fie umplu un camp
fara nicio verificare, fie sunt comutatoare ale cascadei al caror raspuns corect e mereu acelasi.
Masurat pe cabinet: `EFACTURA_CONT_ART_P` e setata la **0 din 21** de firme, gestiunea implicita la 0
efectiv (trei firme au valoarea 0), iar comutatoarele cascadei n-au fost atinse de nimeni. Ecranul de
Configurare nu se deschide, deci singurele optiuni care conteaza sunt goale peste tot.
**Ce se schimba**, in aceasta ordine si abia **dupa** precompletare, pentru ca ea sterge cea mai mare
parte a nevoii de configurare:
1. **Ecranul se subtiaza**: raman doar cele patru optiuni care conteaza; cele fara efect dispar din
interfata, iar comutatoarele cascadei devin constante in cod (raman citite, ca sa nu se strice
singura firma care si-a oprit manual preluarea din facturi anterioare).
2. **Ce ramane urca pe ecranul de import**, pe randul liber deja masurat (`Left 130-530`, `Top 57-84`),
cu tiparul de camp + buton de cautare deja folosit pe forma. Dispare motivul de a deschide un ecran
separat.
3. **Optiunea se naste din lucru**: cand operatorul completeaza manual un camp pentru care exista o
optiune, langa el apare "foloseste asta de fiecare data", care scrie optiunea prin `scrie_optiune`
(`oinit_optiuni.prg:827`). Nu se cere nimic inainte si nu apare niciun ecran nou.
**Ce se castiga**: gestiunea implicita globala devine inutila dupa precompletare - nicio firma din 21 nu
are o singura gestiune activa, deci un implicit global nu putea fi corect (`masurat`); ramane doar contul
implicit, ca plasa de siguranta pentru liniile pe care nimic altceva nu le rezolva, si acolo se aplica
punctul 3.
**Ce risc**: un implicit scris din greseala se propaga tacit. Se previne cu eticheta explicita pe
alegere si cu faptul ca valoarea e mereu la vedere pe ecranul de import, nu ascunsa.
## Respinse, cu motivul
- **Potrivirea fuzzy a articolelor** (Jaro-Winkler, edit distance, `UTL_MATCH`): 80% precizie la
acoperire mica, sub cascada actuala (93-95%), si batuta de regula fara text "ultimele trei conturi ale
furnizorului", care e chiar sursa 2 de mai sus.
- **Deducerea analiticului `401.xxxx` / `4111.xxxx` din datele existente**: mecanismul exista
(`IREG_PARTENERI`/`BALANTA_PARTENERI`), dar e nefolosit - la cabinet doar 2 din 21 de firme au analitic
pe aceste conturi, si acolo e o categorie sau un proiect, nu identitatea partenerului (la una, un
singur cod acopera 98,6% din linii); la VENDING completarea e 0% si nici nu exista coduri analitice
definite pentru ele. Nu exista legatura partener -> analitic din care sa se deduca ceva.
**Respinsa e doar deducerea din balanta**; retinerea valorii scrise de operator si repropunerea ei la
acelasi partener sunt in plan, la punctul 5.
- **Valori implicite propuse din date pentru Configurare**: nicio firma din 21 nu are o singura gestiune
activa, iar contul de achizitie dominant e in medie 37,8% si niciodata peste 50%.
- **TVA la incasare**: nu e o decizie a operatorului, se verifica la ANAF; o valoare memorata ar putea
fi gresita.
- **Explicatia documentului**: completata la 18,1% din facturi si stabila doar in 50,9%.
- **Sectia, venitul/cheltuiala, responsabilul, lucrarea, felul documentului**: constante ale firmei, nu
decizii per factura - locul lor e in Configurare.
- **Un ecran de completare grupata pe articol** (macheta din 07.09): ar fi dus lotul de la 9% la 62%,
dar cu pretul unui formular in plus. Precompletarea de mai sus ajunge la 66,5% fara niciun ecran.
- **Index nou pe `ACT`** pentru semafor: bucata care scaneaza `ACT` costa 0,6s din 36s, deci nu ea e
problema; iar semaforul dispare.
## Capcane de masurare platite
- Facturile de test nu se recunosc doar dupa numar: 16 facturi aveau `TEST=1` si numar gol, si faceau ca
lunile recente sa para pline de date reale.
- `TRIM(x) = ''` e mereu fals in Oracle - a falsificat o prima rulare a backtestului.
- Fereastra de istoric e un parametru cu efect mare, nu un detaliu: 36 de luni fata de ultima factura
inseamna 25 de puncte de acoperire in minus pe sursa de articol.
- Timpul unei interogari nu se judeca dupa volumul de date: pe VENDING citirea costa nimic, iar 35 din
36 de secunde vin din combinarea a doua bucati care, separat, ruleaza sub 4s fiecare.
## Ordinea de implementare
1. Scoaterea semaforului (castig imediat de viteza, sterge cod).
2. Contul si analiticul pe linie, cu ferestrele masurate. Se masoara timpul de deschidere a unei
facturi inainte si dupa: **precompletarea trebuie sa fie instantanee**, altfel nu s-a rezolvat nimic.
3. Gestiunea, pe aceleasi surse.
4. Data scadentei si contractul.
5. Deducerea si distribuirea discountului (cer script de migrare).
6. Configurarea: subtiere, urcarea pe ecranul de import, alegerea care scrie optiunea. Abia acum, cand
se stie ce a mai ramas necesar dupa precompletare.
7. Emise, dupa ce primitele sunt livrate si verificate.

View File

@@ -1,243 +0,0 @@
# 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`).

View File

@@ -1,110 +0,0 @@
# 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`.

View File

@@ -1,780 +0,0 @@
# 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.

View File

@@ -1,296 +0,0 @@
# 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.

View File

@@ -1,341 +0,0 @@
# 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`.

View File

@@ -1,164 +0,0 @@
# 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).

View File

@@ -1,207 +0,0 @@
# 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.