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