# 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.**