# 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`).
