From c047ff339a3e6a6b307b1f3c2cfd18a22a5b07ab Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Tue, 22 Sep 2026 19:43:55 +0300 Subject: [PATCH] sync SVN r18196 --- docs/handoff_comenzi_luna_curenta.md | 72 ++++ docs/handoff_cost_materiale_manopera.md | 156 ++++++++ docs/propuneri_coloane_manopera_piese_viz.md | 371 +++++++++++++++++++ 3 files changed, 599 insertions(+) create mode 100644 docs/handoff_comenzi_luna_curenta.md create mode 100644 docs/handoff_cost_materiale_manopera.md create mode 100644 docs/propuneri_coloane_manopera_piese_viz.md diff --git a/docs/handoff_comenzi_luna_curenta.md b/docs/handoff_comenzi_luna_curenta.md new file mode 100644 index 0000000..29e782b --- /dev/null +++ b/docs/handoff_comenzi_luna_curenta.md @@ -0,0 +1,72 @@ +# Handoff: cercetare "ecran comenzi in luna curenta" (read-only) + +Sarcina: identificare ecran ROAAUTO pentru lista de comenzi (work orders) din luna curenta, +la cererea team-lead. Cercetare READ-ONLY — nu s-a modificat niciun fisier din proiect. +Toate constatarile au fost deja transmise integral catre `team-lead` prin doua mesaje +SendMessage (cu citate exacte de cod si file:line). Acest fisier e doar predarea pe disc, +conform REGULA ZERO — pentru cazul in care mesajele s-ar pierde. + +## Stare: COMPLET pentru intrebarile puse pana acum (2 runde) + +Nu exista lucru neterminat, nu exista fisiere editate, nu exista tranzactii deschise, +nu exista date de test consumate. Subagentul nu a scris/modificat cod. + +## Inventar constatari (file:line) + +### Runda 1 — identificarea ecranului +- Punct de intrare: tile dashboard "Comenzi în luna curentă" + - `Clase/ofundal_dev.vc2:266` — caption `Label_item1.Caption` + - `Clase/ofundal_dev.vc2:681-682` — `DO vizualizare_comenzi IN oproceduri_vizualizare.prg` +- Procedura care construieste cursorul: `Programe/oproceduri_vizualizare.prg:649-678` + (`Procedure vizualizare_comenzi`), sursa = view Oracle `auto_normare_comenzi`, + cursor `crscomenzi`, porneste cu `pcfiltru=[1=2]` (grid gol la deschidere), + deschide formularul `frm_viz_comenzi`. +- Filtrul real de "luna curenta" = `gcCondLuna`, aplicat in formular la `do_cauta` + (`Clase/oviz_devize.vc2:15148`, clasa `frm_viz_comenzi` incepe la `:14503`). +- `gcCondLuna` definit in `Programe/ovariabile_globale.prg:14-18` — NU e "creat luna asta", + ci "luna de lucru curenta (gnLuna/gnAn) SAU comenzi mai vechi inca deschise/nefacturate/ + nevalidate". +- Coloane grid (13) + Oracle view sursa + lipsa camp manopera/piese calculat — detaliate in + mesajul 1 catre team-lead. + +### Runda 2 — detalii cerute explicit (puncte 1-4) +1. Grid `frm_viz_comenzi`: `Clase/oviz_devize.vc2:14745` (ADD OBJECT 'grdcomenzi', + ColumnCount=13, RecordSource="crscomenzi"), coloane 1-13 la liniile 14760-14818, + headere la 14827-14962. Coloana noua → extinde `ColumnCount` + bloc `ColumnN` acolo, + plus camp adaugat in `pcschema`/`pcselect` din `oproceduri_vizualizare.prg:656-666`. +2. Preferinte grid per utilizator: mecanism `GridExtras` + (`COMUN/utile/GridExtras/gridextras.vc2:337-1434`, clasa `gridextra`), instantiat ca + `Gridextra1` (`Clase/oviz_devize.vc2:15065`), pornit din `Init` (`:15438-15441`) prin + `this.gridextra1.setup()`. `restoregridpreferences` (`gridextras.vc2:1051`) si + `savegridpreferences` (`:1130`) salveaza perechi `ColumnOrder,Width` **pozitional pe + index de coloana** (nu pe nume/ControlSource) in `%APPDATA%\...\gridprefs.tmp`, cheie + `SYS(1272, grid)`. Confirma capcana din `COMUN/docs/capcana_grid_preferinte_utilizator.md`: + coloana noua trebuie adaugata la coada (ultimul index), nu in mijloc. +3. `gencursor()`: `COMUN/programe/gencursor.prg:1` — creeaza `NEWOBJECT('deca_baza')` + + `.ADDOBJECT('ca_baza1','ca_baza')`. Clasa `ca_baza` (`COMUN/clase/decabaza.vc2:7`) e + bazata pe `cursoradapter` (via `_cabase`/`_ca_base.vcx` — FetchSize nu am gasit sa fie + suprascris explicit, ramane implicit din `_ca_base.vcx`, neverificat). `afisare()` + (`decabaza.vc2:36-74`) taie SQL-ul de la `WHERE` incolo si il reconstruieste cu + `cfiltru` curent — aici se inlocuieste `1=2` cu filtrul real. +4. Ecran cu coloanele `AUTO_ISTORIC_COMENZI` (VAL_MANOPERA_FACTURA etc.): DA, exista deja — + `frm_istoric_comenzi` (`Clase/oviz_devize.vc2:9115-10506`), grid cu `ColumnCount=25`, + coloanele 19-25 (`:9579-9622`) afiseaza exact `valctva_factura, val_manopera_factura, + val_materiale_factura, val_materiale_ach, val_materiale_vz, ore_manopera, val_manopera`. + Filtrul de luna NU e `gcCondLuna` — `do_cauta` (`:10190`) porneste cu `lcFiltru=[2=2]` + (fara restrictie de luna), filtrare doar optionala prin checkbox-uri UI. + +## Ce s-a stabilit deja (sa nu se reia) +- Ecranul-tinta pt "comenzi in luna curenta" = `frm_viz_comenzi` (nu `frm_istoric_comenzi`, + nu `vizualizare_manopera`). +- Query-ul de baza si mecanismul de filtrare (gencursor + ca_baza.afisare) sunt clarificate, + nu mai cauta SQL-ul de la zero. +- Capcana pozitionala GridExtras e confirmata in cod, nu doar in doc. + +## Interzis / neverificat +- Nu am citit `_ca_base.vcx` — FetchSize exact al CursorAdapter-ului ramane neconfirmat daca + devine relevant (ex. pentru performanta pe volum mare). +- Nu am cautat DDL-ul view-urilor Oracle (`auto_normare_comenzi`, `auto_istoric_comenzi`) — + nu exista in acest repo VFP; ar necesita acces Oracle (`roa-oracle-production-access`). + +## Comanda de rulare teste +N/A — sarcina a fost strict cercetare/cautare in cod, fara modificari, fara teste rulate. diff --git a/docs/handoff_cost_materiale_manopera.md b/docs/handoff_cost_materiale_manopera.md new file mode 100644 index 0000000..1b29bef --- /dev/null +++ b/docs/handoff_cost_materiale_manopera.md @@ -0,0 +1,156 @@ +# Handoff: cercetare "cost materiale / rulaj + manoperă" (read-only) + +Sarcina (subagent `lane-cost`): găsește și documentează cum se determină azi (a) costul de +achiziție al unei piese ieșite pe o comandă/factură și (b) valoarea + orele de manoperă, +pentru cererea clientului de a afișa aceste valori în listele de comenzi/facturi. Cercetare +READ-ONLY — **niciun fișier din proiect nu a fost modificat.** + +## Stare: COMPLET + +Toate constatările au fost transmise integral către `team-lead` prin două mesaje SendMessage +(cu citate exacte de cod și file:line). Acest fișier e doar predarea pe disc, conform REGULA +ZERO — pentru cazul în care mesajele s-ar pierde. Nu există lucru neterminat, fișiere editate, +tranzacții deschise sau date de test consumate. + +**Important**: în timp ce scriam handoff-ul am găsit că exista deja pe disc +[`docs/propuneri_coloane_manopera_piese_viz.md`](propuneri_coloane_manopera_piese_viz.md) — +un document mult mai complet (scris cu acces direct la Oracle: `EXPLAIN PLAN`, `autotrace`, +măsurători reale pe schema `MARIUSM_AUTO`/`ROA_CENTRAL`), care **acoperă și depășește** tot ce +am găsit eu static din cod. Acela e documentul de referință pentru implementare, nu acesta. +Constatările mele (fără acces DB, doar citire de cod) coincid cu ale lui la toate punctele +verificabile static. + +## Inventar constatări (file:line) + +### 1. Tabelul RUL (rulaj) — coloane relevante +Schema cursorului `viz_rulaje`: `COMUN/programe/oproceduri_rulaje.prg:15-25`. +`PRET n(16,4)` = preț achiziție/cost per rând de mișcare, `PRETV n(16,4)` = preț vânzare, +`CANT`/`CANTE` = cantitate, `COD` = document sursă, `ID_LUCRARE`, `ID_GESTIUNE`, `STERS`. + +### 2. Cost achiziție — DEJA STOCAT pe linia de vânzare/factură (nu recalculat la citire) +- Schema cursor linii deviz: `Programe/oproceduri_devize.prg:882` — `pret_achizitie N(20,4)`. +- Populat din lotul de stoc consumat: `COMUN/clase/ofacturare.vc2:4233,13131,13511` — + `poArticol.pret_achizitie = Pret`. +- Scris efectiv prin `pack_facturare.adauga_articol_factura_deviz` + (`Programe/oproceduri_devize.prg:1218-1244`). +- Persistă și la editare facturi: `COMUN/programe/ofacturare_editare.prg:225,547,652,775,807-817`; + schema tabel `vanzari_det`: `COMUN/programe/oproceduri_facturare.prg:378-491,1114-1133`. +- Rapoarte care citesc costul direct de pe linie, fără recalcul: + `COMUN/programe/oproceduri_rapoarte_fact.prg:579-590` (view `fact_vfacturi_detalii`). + +### 3. Algoritm de determinare a costului la ieșire — FIFO pe LOT, din tabela STOC +Sursă (în afara repo-ului git ROAAUTO, în `D:\ROA\DATABASE\SCRIPTURI_CLAR\` — istoric SVN al +schemei Oracle COMUN, actualizat curent, ultimul fișier relevant la 2026-09-16): +`PACK_FACTURARE.cursor_gestiuni_articol` +(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_16_04_COMUN_PACK_FACTURARE.sql:4513-4597`). +Selectează loturi din **`STOC`** (nu din `RUL`), filtrate pe `LUNA`/`AN`/`ID_ARTICOL`/ +`ID_GESTIUNE`, cantitate rămasă = `CANTS + CANT - CANTE > 0`, ordonate **`ORDER BY DATAIN`** +(FIFO pe data intrării lotului). `PRET` = costul lotului ales, scris o singură dată pe linia +de vânzare (punctul 2). Rapoartele nu re-rulează FIFO, doar re-însumează ce s-a scris deja. + +### 4. Manoperă — ore și valoare +DDL `DEV_OPER`: `Scripturi_instalare/tabele.sql:674-693` — `TIMPN NUMBER(8,3)` (ore), +`PRET NUMBER(17,4)` (tarif orar), `ID_ORDL` (FK spre `DEV_ORDL`, fără index explicit pe +`DEV_OPER.ID_ORDL` în DDL). `DEV_ORDL` are index pe `ID_LUCRARE` (`IDX_ORDL_00001`, +`Scripturi_instalare/tabele.sql:636`). +Valoare manoperă = `TIMPN * PRET`, agregată în raport: +`Programe/oproceduri_listari.prg:1608-1621` (`facturi_emise_pe_sectii`). + +### 5. Raportul cerut de client EXISTĂ DEJA — view Oracle `AUTO_ISTORIC_COMENZI` +Versiune curentă (ultima modificare găsită, 2025-06-30, nimic mai nou): +`D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\06\ff_2025_06_30_01_AUTO.sql:37-95`. +```sql +(select sum(round(pret * cante, 2)) from rul where sters = 0 and id_lucrare = a.id_lucrare) as val_materiale_ach, +(select sum(round(pretv * cante, 2)) from rul where sters = 0 and id_lucrare = a.id_lucrare) as val_materiale_vz, +(select sum(b1.timpn) from dev_ordl a1 left join dev_oper b1 on a1.id_ordl=b1.id_ordl where a1.id_lucrare=a.id_lucrare and a1.sters=0 and b1.sters=0) as ore_manopera, +(select sum(round(b1.timpn*b1.pret,2)) from dev_ordl a1 left join dev_oper b1 on a1.id_ordl=b1.id_ordl where a1.id_lucrare=a.id_lucrare and a1.sters=0 and b1.sters=0) as val_manopera, +``` +Subselecte **corelate pe `id_lucrare`** (nu scan fără filtru), susținute de indexul +`IDX_RUL_001 on RUL(ID_LUCRARE, STERS)` (creat `D:\ROA\DATABASE\SCRIPTURI\2009\1\ff_2009_01_21_01_GESTIUNI.sql:63`, +încă folosit activ cu hint în producție — ultima apariție +`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql:711`). +Folosit deja în VFP: `Programe/oproceduri_vizualizare.prg:441-457` (`vizualizare_ist_comenzi`), +afișat în grid `Clase/oviz_devize.vc2:9585-9607` (`ControlSource`: `val_materiale_ach`, +`val_materiale_vz`, `val_materiale_factura`, `val_manopera_factura`, `ore_manopera`). + +### 6. Raportul care CHIAR scanează tot RUL fără scopare pe lucrare (risc real, dar altă bucată de cod) +`Programe/oproceduri_listari.prg:1607-1625` (`facturi_emise_pe_sectii`): +```sql +left join (select sum(round(pret*cante,2)) as valoarea, sum(round(pretv*cante,2)) as valoarev, + id_lucrare, id_sectie from rul where sters=0 group by id_lucrare, id_sectie) e + on a.id_lucrare = e.id_lucrare and a.id_sectie = e.id_sectie +``` +`GROUP BY` peste tot `RUL` (fără filtru pe `id_lucrare` în where), join-ul pe perioadă se face +abia după — spre deosebire de `AUTO_ISTORIC_COMENZI` (punctul 5), care e corelat per lucrare. +Acesta e adevăratul candidat de "selecție pe toată baza de date", dacă vreodată devine relevant +de optimizat. + +## Ce s-a stabilit deja (să nu se reia) +- Costul de achiziție e stocat pe linia de vânzare/factură (`pret_achizitie`), nu recalculat FIFO + la fiecare citire — FIFO rulează o singură dată, la facturare, pe tabela `STOC`. +- View-ul `AUTO_ISTORIC_COMENZI` deja livrează exact coloanele cerute de client și e deja folosit + în ecranul „Istoric comenzi"; documentul de propuneri recomandă copierea acelorași subinterogări + în `AUTO_NORMARE_COMENZI`/`AUTO_FACTURI_EMISE` pentru ecranele „Vizualizare comenzi"/"Facturi". +- Sursa de adevăr pentru DDL/PL-SQL Oracle (istoric complet de migrări, inclusiv versiuni curente + ale pachetelor și view-urilor) NU e în `Scripturi_instalare\` din ROAAUTO, ci în + `D:\ROA\DATABASE\SCRIPTURI\` (brut, cronologic) și `D:\ROA\DATABASE\SCRIPTURI_CLAR\` (reformatat) — + repo SVN separat, partajat între toate aplicațiile ROA. + +## Interzis / nu s-a făcut +Nicio modificare de cod, niciun script Oracle rulat, niciun write-back `.vc2`/`.sc2`. Cercetare +pur read-only, conform mandatului primit. + +## Runda 2 — întrebări punctuale de la team-lead (trimise, nu doar aici) +Sursă: `PACK_FACTURARE.descarca_gestiune`, în +`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_16_04_COMUN_PACK_FACTURARE.sql` (versiune +curentă, 2026-09-16). + +1. **`RUL.PRET` la ieșire — SIGUR**: nu se recalculează în `descarca_gestiune`. Procedura primește + `V_PRET_ACHIZITIE IN NUMBER` ca parametru, selectează din `STOC` exact lotul cu acel preț + (`AND A.PRET = V_PRET_ACHIZITIE`, linia 8766 și analog 8659/8899/10642), apoi + `V_PRET := tab_stoc(i).PRET` (linia 8975) și `V_PRET as PRET` în `INSERT INTO RUL_TEMP` + (linia 9456). FIFO-ul (alegerea lotului) se face separat, mai devreme, în + `cursor_gestiuni_articol` (`ORDER BY DATAIN`, raportat runda 1). + **Incert**: n-am găsit niciun `INSERT INTO RUL` literal (fără `_TEMP`) în acest fișier — nu am + urmărit cum `RUL_TEMP` ajunge fizic în `RUL`. Nu schimbă răspunsul (valoarea `PRET` e fixată la + `V_PRET` oricum), dar rămâne un gol de investigat dacă cineva vrea firul complet. +2. **RUL pe `id_lucrare` = doar piese, niciodată manoperă — SIGUR**: gardă la intrarea în + `descarca_gestiune` (liniile 8264-8270): + ```sql + SELECT MAX(IN_STOC) INTO lnInStoc FROM NOM_ARTICOLE WHERE ID_ARTICOL = V_ID_ARTICOL; + if lnInStoc = 0 then GOTO SFARSIT; end if; + ``` + Articolele cu `IN_STOC=0` (servicii/manoperă) nu ajung niciodată să scrie în `RUL_TEMP`/`RUL`. + Replicat pe VFP prin `gestionabil = Iif(Nvl(in_stoc,0)<>0,1,0)` + (`COMUN/programe/ofacturare_editare.prg:242`). +3. **`CANTE` vs `CANT` — SIGUR**: `CANTE`=ieșire, `CANT`=intrare, din formula soldului + `CANTS+CANT-CANTE` (folosită consecvent, ex. `cursor_gestiuni_articol`). La retur (ramura + `WHEN pack_facturare.ntip IN (8,9)` din `descarca_gestiune`, ~linia 8355) nu se folosesc + cantități negative — ieșirea originală (`a.cante`) devine `cant` (intrare) pe rândul nou: + `SELECT 1 as tip, ..., a.cante as cant, 0 as cante, ... FROM RUL A ... WHERE A.CANTE<>0`. +4. **`DEV_OPER.TIMPN` = ore — SIGUR pe bază de uz**, nicio conversie de unitate întâlnită: + `Programe/oproceduri_listari.prg:1620` (`sum(...timpn) as ore`), `:2005` (`sum(timpn) as ore`), + caption `Clase/odevize.vc2:6310` (`"Total ore"`). Niciun `COMMENT ON COLUMN` găsit în DDL — + eticheta vine din convenția de cod, nu dintr-un comentariu de schemă. +5. **`VANZARI_DETALII.PRET_ACHIZITIE` — mecanism SIGUR, procentul de 95% zero INCERT**: scris de + același flux (`descarca_gestiune`/finalizare factură), doar pentru linii gestionabile + (`IN_STOC<>0`), cu `V_PRET` din `STOC` (condiție de MERGE, linia 12741: + `AND A.PRET_ACHIZITIE = V_PRET ...`). Pentru linii de manoperă (`IN_STOC=0`) rămâne 0 **prin + design** (codul nici nu rulează pentru ele — punctul 2). Nu am verificat empiric (fără acces + Oracle) dacă cele ~95% zero din schema de test chiar corespund liniilor de manoperă sau ascund + și piese needescărcate din alt motiv — am recomandat team-lead-ului un count pe + `IN_STOC × (PRET_ACHIZITIE=0)` pentru confirmare. + +**Concluzie rundă 2**: `pret_achizitie` de pe `VANZARI_DETALII` e de încredere ca sursă a costului +pe factură pentru liniile de piese; un 0 pe linie de manoperă e corect, nu semnal de problemă. + +**CORECTARE ulterioară (din `docs/propuneri_coloane_manopera_piese_viz.md`, actualizat de altcineva +cu acces Oracle, după ce am trimis răspunsul de mai sus)**: pe schema de test, `PRET_ACHIZITIE = 0` +nu doar pe liniile de manoperă (`IN_STOC=0`: 2475/2526 = 98%), ci **și pe 96% din liniile de piese +gestionabile** (`IN_STOC=1`: 8561/8889 = 96%). Deci concluzia mea "e de încredere pentru piese" nu +se confirmă empiric — mecanismul de scriere există (descris mai sus), dar pe date reale coloana e +goală aproape peste tot, inclusiv acolo unde ar trebui completată. Motivul exact nu e stabilit +(posibil: câmpul se completează doar pe un anumit flux de facturare, sau pe majoritatea documentelor +din baza de test descărcarea de gestiune nu s-a mai executat din alt motiv). **Nu folosiți +`VANZARI_DETALII.PRET_ACHIZITIE` ca sursă de cost fără verificare suplimentară — `RUL.PRET` (via +`AUTO_ISTORIC_COMENZI`) rămâne sursa confirmată empiric.** diff --git a/docs/propuneri_coloane_manopera_piese_viz.md b/docs/propuneri_coloane_manopera_piese_viz.md new file mode 100644 index 0000000..8058f1f --- /dev/null +++ b/docs/propuneri_coloane_manopera_piese_viz.md @@ -0,0 +1,371 @@ +# Propunere: ore/valoare manoperă + valoare piese (facturare și achiziție) în „Vizualizare comenzi" și „Facturi emise" + +Cercetare + plan. Data: 22.09.2026. Dovezile de plan/IO sunt luate pe schema `MARIUSM_AUTO` de pe +`ROA_CENTRAL`. + +## 0. Stare — predare către o sesiune nouă + +**Acesta este fișierul de implementat.** Planul e la §5, aprobat de Marius pe 22.09.2026. + +- **Nimic implementat.** Zero fișiere de cod atinse, zero scripturi rulate pe Oracle, zero commit-uri. + `git status` are doar fișiere noi în `docs/`. +- **Nimic într-o stare periculoasă**: fără editări de binar fără write-back, fără tranzacții deschise, + fără procese rămase vii, fără date de test consumate. +- **Primul pas într-o sesiune nouă**: `git_sync.ps1` (obligatoriu la început de sesiune), apoi §5 pas 1. +- **Decizii deja luate, nu le relua**: §4 varianta 1 (ecranul de comenzi trece pe + `AUTO_ISTORIC_COMENZI`, nu se modifică `AUTO_NORMARE_COMENZI`); manopera se livrează în ambele + forme (deviz + facturat); costul se ia din `RUL.PRET`, nu din `VANZARI_DETALII.PRET_ACHIZITIE`. +- **Interzis**: să atingi `AUTO_NORMARE_COMENZI` (§4); să inserezi coloane noi la mijlocul gridurilor + (§6.1); să rulezi scriptul de bază fără cererea explicită a lui Marius (§5 pas 1); să dai commit + fără ca Marius să vadă întâi diff-ul complet. +- **Rămâne de aflat de la client** (§7.1-7.3): rânduri + timp actual pe luna curentă la baza cea mai + mare, dimensiunile `RUL`/`ACT`/`DEV_OPER`, existența `IDX_RUL_001`. Nu blochează pașii 1-3. +- Conectare la Oracle de dev: vezi `COMUN\docs\conventie_mediu_oracle.md` §4. +- `docs/handoff_comenzi_luna_curenta.md` și `docs/handoff_cost_materiale_manopera.md` sunt predări de + subagenți, integral acoperite de acest document — de ignorat sau de șters. + +## 1. Cererea clientului + +> „în vizualizare comenzi în luna curentă (la fel și pentru facturile în luna curentă), am nevoie +> să apară și orele manoperă, valoare manoperă, valoare piese facturate și valoare piese achiziție" + +Îngrijorarea ta: comenzile deschise în luna curentă pot fi foarte vechi, deci calculul valorii și +costului materialelor din `RUL` ar însemna o selecție pe toată baza. Constrângeri: Oracle Express +(fără view-uri materializate), compatibil Oracle 10g. + +## 2. Descoperirea care schimbă tot: funcționalitatea există deja + +Ecranul **„Istoric comenzi"** (meniu Comenzi, butonul de lângă „Vizualizare comenzi" — +`Clase/ofundal_dev.vc2:685` → `vizualizare_ist_comenzi`) afișează **deja exact cele patru valori +cerute**, plus încă două. + +Cursorul vine din view-ul Oracle `AUTO_ISTORIC_COMENZI` +(`Programe/oproceduri_vizualizare.prg:443-476`), iar gridul din `frm_istoric_comenzi` +(`Clase/oviz_devize.vc2:9115`) are coloanele: + +| Coloană grid | Coloană view | Ce e | +|---|---|---| +| `cOreManopera` | `ORE_MANOPERA` | ore manoperă (normate, din deviz) | +| `cValManopera` | `VAL_MANOPERA` | valoare manoperă (din deviz) | +| `cValManoperaFactura` | `VAL_MANOPERA_FACTURA` | manoperă efectiv facturată (din contabilitate, cont 704) | +| `cValMaterialeVz` | `VAL_MATERIALE_VZ` | **valoare piese la preț de vânzare** | +| `cValMaterialeAch` | `VAL_MATERIALE_ACH` | **valoare piese la preț de achiziție** | +| `cValMaterialeFactura` | `VAL_MATERIALE_FACTURA` | materiale efectiv facturate (cont 707/419) | + +Diferența față de ce cere clientul este **doar filtrul**: „Istoric comenzi" pornește de la +`lcFiltru = [2=2]` (`Clase/oviz_devize.vc2:10193`) cu filtre opționale pe interval de `datai`, în +timp ce „Vizualizare comenzi" pornește de la `gcCondLuna` +(`Clase/oviz_devize.vc2:15187`, definit în `Programe/ovariabile_globale.prg:16`). + +**Deci munca reală nu e „de calculat ceva nou", ci „de mutat 4-6 coloane deja existente și +verificate în producție pe alte două ecrane".** + +## 3. Analiza de cost pe bază de date + +### 3.1 Costul de achiziție al pieselor NU se recalculează — e fixat la bonul de consum + +Pe fluxul auto, piesele **ies din gestiune la bonul de consum**, nu la facturare. În momentul +emiterii bonului, pe rândul de ieșire din `RUL` se scriu deodată: + +- `RUL.PRET` = preț de achiziție +- `RUL.PRETV` = preț de vânzare +- `RUL.ADAOS` / `VALOARE_ADAOS` = adaosul +- `RUL.CANTE` = cantitatea ieșită + +Exemplu real (`id_lucrare=14`): `pret=27.88`, `pretv=32.062`, `cante=1`. + +**Facturarea comenzii auto nu mai descarcă nimic** — doar citește din `RUL` ce s-a consumat deja și +calculează valoarea de vânzare a materialelor, alături de manoperă. +`frm_emitere_facturi.do_factureaza_final` (`Clase/oviz_devize.vc2:2358-2366`): + +```sql +select b.denumire, b.um, a.cante as cant, a.pretv, a.pret, a.dataact, a.id_rul, ... + from (select id_articol, id_lucrare, pretv, ..., SUM(cante) as cante, ... + from rul where sters = 0 and id_lucrare = + group by id_lucrare, id_articol, pretv) a + left join nom_articole b on a.id_articol = b.id_articol + where a.cante <> 0 +``` + +Deci `PACK_FACTURARE.cursor_gestiuni_articol` (descărcarea FIFO din `STOC` la emiterea facturii) +**nu e pe traseul comenzii auto** — acela e fluxul de facturare direct din stoc, al celorlalte +aplicații ROA. Pentru ROAAUTO, sursa de adevăr pentru ambele prețuri este rândul de ieșire din +`RUL`, scris la bonul de consum. + +Consecința pentru cerere: `val_materiale_ach = sum(pret*cante)` și +`val_materiale_vz = sum(pretv*cante)` citesc exact valorile de pe bonurile de consum ale comenzii. +**Nicio reconstrucție de cost la raportare** — nici FIFO, nici medie ponderată. Îngrijorarea legată +de recalculul costului nu se confirmă. + +`RUL` nu conține niciodată manoperă: descărcarea sare articolele cu `NOM_ARTICOLE.IN_STOC = 0` +(`SCRIPTURI_CLAR\2026\09\ff_2026_09_16_04_COMUN_PACK_FACTURARE.sql:8264-8270`): + +```sql +SELECT MAX(IN_STOC) INTO lnInStoc FROM NOM_ARTICOLE WHERE ID_ARTICOL = V_ID_ARTICOL; +if lnInStoc = 0 then GOTO SFARSIT; end if; +``` + +Confirmat și pe date: ieșirile cu `id_lucrare` au doar conturi de stoc (371, 303, 345, 302x), +niciodată 704. Deci suma nu amestecă manopera cu piesele. + +**De ce NU se folosește `VANZARI_DETALII.PRET_ACHIZITIE` ca sursă a costului.** Tocmai pentru că, pe +fluxul auto, factura nu descarcă gestiunea, coloana aceea nu se populează. Pe schema de test e 0 pe +aproape tot, inclusiv pe liniile de piese: + +| `NOM_ARTICOLE.IN_STOC` | linii | din care `PRET_ACHIZITIE = 0` | +|---|---|---| +| 0 (servicii/manoperă) | 2 526 | 2 475 (98 %) | +| 1 (piese, gestionabile) | 8 889 | **8 561 (96 %)** | + +`RUL.PRET` rămâne singura sursă corectă — și e oricum cea folosită deja de `AUTO_ISTORIC_COMENZI`. + +### 3.2 Subinterogarea pe `RUL` merge pe index, nu pe toată baza + +Formularea din `AUTO_ISTORIC_COMENZI`: + +```sql +(select sum(round(pret * cante, 2)) from rul where sters = 0 and id_lucrare = a.id_lucrare) as val_materiale_ach, +(select sum(round(pretv * cante, 2)) from rul where sters = 0 and id_lucrare = a.id_lucrare) as val_materiale_vz +``` + +Există indexul **`IDX_RUL_001 (ID_LUCRARE, STERS)`**, creat încă din +`D:\ROA\DATABASE\SCRIPTURI\2009\1\ff_2009_01_21_01_GESTIUNI.sql:63` și folosit explicit, cu hint, +în `pack_auto.actualizeaza_deviz` +(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql:711`, acum șase luni) +— deci e viu în producție, la toți clienții actualizați. `EXPLAIN PLAN` confirmă că Oracle împinge +predicatul și accesează `RUL` prin: + +``` +VIEW PUSHED PREDICATE VW_SSQ_4 + SORT GROUP BY + TABLE ACCESS BY INDEX ROWID BATCHED RUL + INDEX RANGE SCAN IDX_RUL_001 (access "ID_LUCRARE"=... AND "STERS"=0) +``` + +Costul scalează cu **numărul de rânduri afișate × rândurile de rulaj ale acelei comenzi**, nu cu +mărimea istoricului. O comandă veche nu costă mai mult decât una nouă. + +### 3.3 Ce chiar costă — și costă DEJA, azi, fără nicio modificare + +`AUTO_NORMARE_COMENZI` (sursa ecranului „Vizualizare comenzi") face deja +`left join auto_vordl_facturi` → `auto_vordl_facturate` → **`MV_ORDL_SUME_ACT`**, care e un view +normal (nu materializat, în ciuda numelui) ce agregă tabela `ACT` fără filtru de dată. + +La fel `AUTO_FACTURI_EMISE`, care are `join mv_ordl_sume_act`. + +Adică partea „scumpă" e deja plătită la fiecare deschidere a celor două ecrane. În plan, și aceasta +primește predicat împins (`INDEX RANGE SCAN IDX_ORDL_AVANS` pe `ACT(ID_LUCRARE,STERS,ID_SET,SCD)`). + +### 3.4 Măsurători (schema de test — cifre relative, nu absolute) + +`autotrace` pe `MARIUSM_AUTO`, toate comenzile din istoric (375 rânduri), respectiv toate facturile +(154 rânduri): + +| Variantă | consistent gets | Δ | +|---|---|---| +| A — `AUTO_NORMARE_COMENZI`, lista de coloane de azi | 660 | — | +| B — A + cele 4 coloane, formularea din `AUTO_ISTORIC_COMENZI` | 1 149 | +74 % | +| C — ca B, dar cu `/*+ NO_UNNEST */` pe subinterogările de manoperă | 2 107 | +219 % (mai rău) | +| D — ca B, dar manopera corelată direct pe `a.id_ordl` | 1 050 | +59 % | +| E — `AUTO_FACTURI_EMISE`, lista de coloane de azi | 1 726 | — | +| F — E + cele 4 coloane | 1 703 | ≈ 0 | + +Concluzii: + +- Pe **facturi** adăugarea coloanelor e practic gratuită (diferența e zgomot de măsurare): view-ul + plătește deja agregarea peste `ACT`. +- Pe **comenzi** creșterea e de ordinul a +60-75 % din I/O al ecranului, pe o interogare care azi + costă sub o secundă. Absolut: aici baza de test e mică (`RUL` 10 320 rânduri, `ACT` 70 723, + `DEV_OPER` 2 414). **Cifra trebuie reconfirmată la client** (punctul 7). +- Partea de manoperă (`DEV_ORDL` ⋈ `DEV_OPER` pe `id_lucrare`) este singura care, în planul implicit, + se „dez-corelează" într-un `HASH GROUP BY` peste tabelele întregi — deci singura care scalează cu + istoricul. Varianta D o evită, dar schimbă semantica dacă o lucrare poate avea mai multe `ordl` + (în baza de test raportul e 1:1 pe toate cele 375 de lucrări — de verificat la client). + +### 3.5 Capcană de date + +`RUL.ID_LUCRARE` **nu e null niciodată** — mișcările care nu țin de o lucrare au `ID_LUCRARE = 0` +(4 110 din 7 358 rânduri în baza de test, adică 56 %). La un client real acolo stau milioanele de +mișcări de stoc obișnuite. O comandă auto are întotdeauna un `id_lucrare > 0` din `NOM_LUCRARI`, deci +subinterogarea nu ar trebui să nimerească niciodată „găleata" 0 — dar merită o gardă explicită +(`and a.id_lucrare > 0`), altfel un singur rând cu `id_lucrare = 0` scanează tot. + +### 3.6 Unde chiar există problema de care te temeai (dar în alt loc) + +`Programe/oproceduri_listari.prg:1607-1625`, procedura `facturi_emise_pe_sectii`: + +```sql +left join (select sum(round(pret * cante, 2)) as valoarea, + sum(round(pretv * cante, 2)) as valoarev, id_lucrare, id_sectie + from rul where sters = 0 group by id_lucrare, id_sectie) e + on a.id_lucrare = e.id_lucrare and a.id_sectie = e.id_sectie +``` + +Aici subselectul chiar grupează **tot** `RUL` (singurul filtru e `sters = 0`), iar join-ul pe +perioadă se face abia după. Ăsta e „selecția pe toată baza" reală — dar în raportul de facturi +emise pe secții, nu în ecranele din cerere. De reținut ca anti-pattern (formularea propusă aici, +corelată pe `id_lucrare`, îl evită) și ca loc de reparat separat dacă raportul acela e lent la +client. + +## 4. Variante + +### Varianta 0 — niciun cod: clientul folosește „Istoric comenzi" +Zero efort, zero risc. Dezavantaj: filtrul e pe interval de `datai`, nu „comenzi deschise în luna +curentă", și nu acoperă deloc ecranul de facturi. **Merită propusă clientului ca soluție imediată**, +cât timp se lucrează la restul. + +### Varianta 1 (APROBATĂ) — ecranul de comenzi trece pe `AUTO_ISTORIC_COMENZI` +Ideea lui Marius, mai bună decât varianta inițială: nu se copiază subinterogările în +`AUTO_NORMARE_COMENZI`, ci **`vizualizare_comenzi` își schimbă sursa** pe `AUTO_ISTORIC_COMENZI`, +păstrând filtrul `gcCondLuna`. + +Comparația coloanelor celor două view-uri (rulată pe `MARIUSM_AUTO`) arată că +`AUTO_ISTORIC_COMENZI` conține tot ce folosește azi `frm_viz_comenzi`, cu **o singură excepție**: +`INCH_VALIDARE`, de care are nevoie `gcCondLuna`. Se adaugă din `dev_tip_deviz`, deja joinat acolo +(`i.inch_validare`). + +De ce e mai bună: + +- **un singur view modificat, cu o singură coloană** — în loc de patru subinterogări în două view-uri; +- `AUTO_NORMARE_COMENZI` rămâne **neatins**, iar el mai e folosit în **10 alte locuri** + (`Programe/oproceduri_devize.prg:233` și `:1632`, `Programe/oproceduri_listari.prg:998`, + `Programe/oproceduri_vizualizare.prg:387` și `:515`, `Clase/oviz_devize.vc2:1579`, `:2316`, + `:4481`, `:11724`, `:18751`), inclusiv un `select *`. Varianta inițială le-ar fi pus pe toate să + plătească subinterogările degeaba; +- SQL-ul de calcul rămâne exact cel rulat deja în producție pe „Istoric comenzi" → risc semantic zero; +- compatibil Oracle 10g, fără obiecte noi, fără indecși noi, fără MV. + +Ecranul de facturi rămâne pe `AUTO_FACTURI_EMISE` (altă formă — un rând per factură, cu `nrcrt`), +deci acolo cele patru coloane se adaugă în view. + +### Varianta 2 — optimizarea manoperei (corelare pe `id_ordl`) +Subinterogările de manoperă scrise direct pe `DEV_OPER` prin `a.id_ordl` (index `IDX_OPER_01`), în +loc de `DEV_ORDL ⋈ DEV_OPER` pe `id_lucrare`. Scapă de singurul acces care scalează cu istoricul. +Marius a confirmat că **o lucrare are exact un `dev_ordl`**, deci e aplicabilă — dar rămâne o +optimizare ulterioară, de făcut doar dacă măsurătorile la client o cer. Nu intră în livrarea curentă. + +### Varianta 3 — tabelă de totaluri întreținută la validare/facturare +Un `AUTO_COMENZI_TOTALURI(id_lucrare, ore_manopera, val_manopera, val_mat_ach, val_mat_vz)` scris de +`PACK_DEVIZE`/`PACK_FACTURARE` la validare și la facturare. Citire O(1) per rând. +**Nerecomandat acum**: cost mare (pachet + migrare + backfill + risc de desincronizare) pentru o +problemă care, după măsurători, nu există încă. De ținut în sertar dacă varianta 1 se dovedește +lentă la un client cu volum mare. + +## 5. Plan de implementare — APROBAT de Marius, 22.09.2026 + +Ordinea contează: întâi baza, apoi VFP — altfel cursorul cere coloane inexistente. + +**Pas 1 — script de bază de date.** Versiunile curente ale celor trei view-uri sunt în +`D:\ROA\DATABASE\SCRIPTURI_CLAR\` (istoricul de migrări, SVN, partajat între aplicațiile ROA — nu +în `Scripturi_instalare\` din ROAAUTO, care e doar install-ul inițial): + +- `AUTO_ISTORIC_COMENZI` — `2025\06\ff_2025_06_30_01_AUTO.sql:37-95` +- `AUTO_FACTURI_EMISE` — de localizat în același arbore + +Se scrie un script nou cu `CREATE OR REPLACE VIEW`, necalificat, fără prefix de schemă: + +- **`AUTO_ISTORIC_COMENZI`**: se adaugă o singură coloană, `i.inch_validare`, din `dev_tip_deviz` + deja joinat. Restul view-ului rămâne neschimbat. +- **`AUTO_FACTURI_EMISE`**: se adaugă `ORE_MANOPERA`, `VAL_MANOPERA`, `VAL_MATERIALE_ACH`, + `VAL_MATERIALE_VZ`, copiate literal din `AUTO_ISTORIC_COMENZI`, cu garda `a.id_lucrare > 0`. + (`MANOPERA` și `MATERIALE` — cele facturate — există deja acolo.) + +`AUTO_NORMARE_COMENZI` **nu se atinge.** + +Se rulează **doar la cererea ta explicită**, pe `MARIUSM_AUTO` de pe `ROA_CENTRAL` întâi. Se publică +apoi prin fluxul obișnuit de scripturi de actualizare (vezi skill-ul `roa-oracle-migration`). + +**Pas 2 — cursoarele VFP** (`Programe/oproceduri_vizualizare.prg`): + +- `vizualizare_comenzi` (`:649`) — sursa se schimbă din `auto_normare_comenzi` în + **`auto_istoric_comenzi`**; `pcschema` și `pcselect` se extind cu `ORE_MANOPERA`, `VAL_MANOPERA`, + `VAL_MANOPERA_FACTURA`, `VAL_MATERIALE_VZ`, `VAL_MATERIALE_ACH`, `VAL_MATERIALE_FACTURA` + (`N(20,4)`, ca în `vizualizare_ist_comenzi`). Filtrul rămâne `gcCondLuna`. +- `vizualizare_facturi` (`:231`) — `pcschema`/`pcselect` extinse cu cele 4 coloane noi. + +**Pas 3 — gridurile** (`Clase\oviz_devize.vc2`, write-back prin `txt2vcx.ps1`): + +- `frm_viz_comenzi` (`:14503`, grid `grdcomenzi`) — coloane noi **la coadă**, copiate ca definiție + (format, aliniere, `InputMask`) din coloanele omoloage ale lui `frm_istoric_comenzi` (`:9115`, + `Column19`-`Column25`). +- `frm_viz_facturi` (`:16762`, grid `grdfacturi`) — idem. + +`do_excel` iterează generic peste `grid.Columns` pe ambele formulare +(`Clase/oviz_devize.vc2:15208` și `:17472`) — coloanele noi apar automat în export, fără cod. + +`do_listare` diferă între ecrane: +- la comenzi (`:15381`) nu atinge gridul — nimic de făcut; +- la facturi (`:17570`) exportă prin raportul `.frx` **`rap_facturi_clienti`** + (`goExport.export2frx`). Dacă se vrea coloana și pe listarea tipărită, raportul se editează + **doar în IDE-ul VFP** (fără write-back din text). + +**Pas 4 (opțional) — totaluri pe ecranul de facturi.** `frm_viz_facturi.refreshdata` +(`Clase/oviz_devize.vc2:17610`) calculează deja sumele afișate sub grid pe `valctva`, `manopera`, +`materiale`, cu `WHERE nrcrt=1` pentru deduplicarea liniilor multiple pe aceeași factură. Totalurile +pentru coloanele noi se adaugă aici, cu aceeași gardă `nrcrt=1`, altfel se dublează. + +**Pas 5 (opțional) — listarea facturilor.** `do_listare` la facturi (`:17570`) exportă prin raportul +`.frx` `rap_facturi_clienti`. Coloanele noi se adaugă acolo **doar în IDE-ul VFP** (fără write-back +din text). La comenzi, `do_listare` (`:15381`) nu atinge gridul — nimic de făcut. + +**Pas 6 — testare.** Pe lângă cronometrarea pe lună curentă la un client cu volum mare: +- valorile din ecranul de comenzi trebuie să coincidă cu cele din „Istoric comenzi" pentru aceleași + comenzi (aceeași sursă, deci trebuie să fie identice); +- `do_executa` și `do_listare` din `frm_viz_comenzi` fac `Scatter Name ocomanda` pe cursor și pasează + obiectul la `vizualizeaza_detalii_comanda` / `listare_comanda_deviz`. Câmpurile folosite + (`id_ordl`, `id_lucrare`, `proc_tvav`, `validat`) există în ambele view-uri, dar **schimbarea + sursei cursorului trebuie verificată la rulare** — e singurul punct unde se poate rupe ceva. + +## 6. Capcane de reținut la implementare + +1. **GridExtras — coloanele noi se adaugă OBLIGATORIU la coadă.** Ambele formulare apelează + `this.gridextra1.setup()` (`Clase/oviz_devize.vc2:15441` și `:17607`), deci preferințele de + coloane sunt salvate per utilizator în `gridprefs.tmp`. `restoregridpreferences` + (`COMUN/utile/GridExtras/gridextras.vc2:1051`) aplică perechile `ColumnOrder,Width` + **pozițional**, pe index de coloană: + `loColumn = toGridObject.Columns((lnCounter + 1)/2)`, cu + `lnMax = Min(ColumnCount_nou * 2, Alen(preferinte_vechi))`. + Consecințe: + - coloană adăugată **la coadă** (`Column14`, `Column13`): preferințele vechi 1..N se aplică + corect, iar coloana nouă ajunge vizual ultima (acceptabil — vezi soluția de repoziționare + runtime din `COMUN\docs\capcana_grid_preferinte_utilizator.md`, aplicată pe `frm_modific2024`); + - coloană **inserată la mijloc**: preferințele vechi se aplică peste coloane deplasate → lățimi + și ordini greșite pentru toți utilizatorii existenți. **De evitat.** +2. **`ALTER TABLE` pe cursorul de la `goExecutor`** nu merge — coloanele noi trebuie să fie în + `SELECT` de la început (`COMUN\docs\conventie_goexecutor_alter_table.md`). Aici e respectat by + design, pentru că se modifică `pcselect`. +3. **Encoding CP1252** la editarea `.vc2` (`COMUN\docs\conventie_encoding_cp1252.md`); write-back doar + prin `txt2vcx.ps1`, skill `roa-vfp-text-edit`. +4. `CursorFill` aduce **tot** setul de rezultate în cursor local (`COMUN\clase\decabaza.vc2:36`), deci + costul e proporțional cu numărul de rânduri returnate — filtrul de lună chiar contează. +5. Filtrele sunt diferite pe cele două ecrane: facturi = strict `dataact` în luna curentă + (`Clase/oviz_devize.vc2:17425`); comenzi = `gcCondLuna`, care include **și comenzi mai vechi + nefacturate/nevalidate** (`Programe/ovariabile_globale.prg:16`). Asta e sursa reală a comenzilor + „foarte vechi" — dar, cum s-a arătat la 3.2, vechimea nu scumpește subinterogările. + +## 7. De verificat înainte de implementare + +1. **La clientul cu cea mai mare bază**: câte rânduri întoarce azi „Vizualizare comenzi" pe luna + curentă, și cât durează deschiderea acum. Fără numărul ăsta, procentele de la 3.4 nu spun nimic + despre experiența reală. +2. Dimensiunile reale ale `RUL`, `ACT`, `DEV_OPER` la acel client. +3. Există `IDX_RUL_001 (ID_LUCRARE, STERS)` și la client? Aproape sigur da (creat în 2009, folosit + cu hint în `pack_auto` în 2026), dar merită confirmat cu o interogare pe `USER_INDEXES` la + clientul respectiv. Dacă lipsește, ăsta e **singurul** caz în care apare cu adevărat scanarea pe + toată baza de care te temeai — și se rezolvă cu un `CREATE INDEX`, nu cu rescrierea logicii. +4. ~~Poate o lucrare să aibă mai multe `dev_ordl`?~~ **Închis**: Marius a confirmat — o + `NOM_LUCRARI` are exact un `DEV_ORDL`. Deschide varianta 2 ca optimizare ulterioară. +5. ~~„Valoare manoperă" = deviz sau facturat?~~ **Închis**: se livrează amândouă, ca în „Istoric + comenzi" — `VAL_MANOPERA` (din `DEV_OPER`, `timpn*pret`) și `VAL_MANOPERA_FACTURA` (cont 704 din + `ACT`), respectiv `VAL_MATERIALE_VZ` și `VAL_MATERIALE_FACTURA`. La comenzile încă nefacturate + există doar prima; la cele facturate, diferența dintre ele e chiar semnalul util. + +## 8. Recomandare + +Varianta 1, aprobată de Marius pe 22.09.2026: ecranul de comenzi trece pe `AUTO_ISTORIC_COMENZI` +(cu `INCH_VALIDARE` adăugat), ecranul de facturi primește cele patru coloane în +`AUTO_FACTURI_EMISE`, cu garda `id_lucrare > 0`. `AUTO_NORMARE_COMENZI` rămâne neatins. + +Este cel mai mic diff posibil care rezolvă cererea, reutilizează SQL deja rulat în producție și nu +adaugă niciun obiect nou în bază. Până la livrare, clientul poate folosi „Istoric comenzi" +(varianta 0).