# Cercetare Oracle S5/S6/S7 (plan #6 - editare factura emisa) Sursa: export proaspat din `MARIUSM_AUTO@ROA_CENTRAL` (`all_source`), facut in aceasta sesiune (08.08.2026), NU copia veche de pe disc din martie. Inainte de export s-a verificat tabela `VERSIUNE`: ultimul script aplicat e `ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`, identic cu `versiune_db.txt` din radacina proiectului (`2026_08_06_12`) - MARIUSM_AUTO e la zi. Numerele de linie de mai jos sunt din exportul acestei sesiuni (`PACK_FACTURARE.pck` = 17010 linii, `PACK_CONTAFIN.pck` = 9041 linii, `PACK_DOCUMENTE.pck` = 96 linii), NU din `ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` de pe disc, care e vechi si nu contine modificarile de la #8. Fisierele exportate au ramas in scratchpad-ul sesiunii, nu sunt commise. ## A. Starea reala de azi ### A.1 `actualizeaza_vanzari` (PACK_FACTURARE.pck:16015-16025) Confirmat: face EXCLUSIV realinierea `cod` + `STERS=0`, nimic altceva. ``` UPDATE VANZARI_DETALII SET STERS = 0 WHERE ID_VANZARE IN (SELECT ID_VANZARE FROM VANZARI WHERE COD = V_COD_VECHI); UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI; ``` Important, nu era explicit in plan: al doilea `UPDATE` schimba `COD` pe ACELASI rand din `VANZARI` (filtrat dupa vechiul cod) - `ID_VANZARE` (cheia primara) NU se schimba niciodata la editare. Asta conteaza direct pentru sectiunea C. Comentariul inline de la `:16018` ("de modificat in caz ca il las sa stearga manual inregistrari din VANZARI_DETALII") confirma ca extinderea era anticipata. ### A.2 `PACK_CONTAFIN.finalizeaza_modificare_nota` / `finalizeaza_stergere_nota` `finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601-8651): cheama `pack_facturare.actualizeaza_vanzari(tnCod, lnCodNou)` DOAR daca `SELECT COUNT(*) FROM vanzari WHERE cod = tnCod` > 0 (`:8613-8617`). Daca `cod`-ul vechi nu exista in `vanzari` (nota nu vine dintr-o factura), pasul e sarit tacut, fara eroare - comportament corect pentru orice alt tip de nota (ROAGEST/ROACONT). `finalizeaza_stergere_nota` (`:8653-8709`) e simetric: cheama `sterge_din_vanzari` doar daca `cod`-ul exista in vanzari. Spre deosebire de `actualizeaza_vanzari`, `sterge_din_vanzari` (PACK_FACTURARE.pck:16027-16042) cauta `ID_VANZARE` dupa `cod` si, daca nu-l gaseste, ridica `RAISE_APPLICATION_ERROR(-20000, ... FACT-017 ...)` - dar acest caz nu poate aparea in fluxul normal, fiindca apelul e deja gardat de count-ul de mai sus. ### A.3-A.4 `scrie_in_vanzari` (PACK_FACTURARE.pck:13491-13956) - formula de calcul Extrasa integral (bloc `SELECT INTO` la `:13763-13931`, `UPDATE VANZARI` la `:13933-13949`). Coloane denormalizate scrise: `discount_tva, valoare_achizitie, total_fara_tva, total_tva, total_cu_tva, valval, tvaval, totval, id_valuta, curs, multiplicator, serie_incasat, nr_incasat, suma_incasat, tip_incasat` (14 coloane). Sursa datelor: **`VANZARI_DETALII_TEMP`** (tabela globala temporara de sesiune, NU `VANZARI_DETALII`) + `VANZARI_SETURI_TEMP` (pentru linii-set) + `VANZARI_CURSURI` (deja scrisa cu `id_vanzare`-ul curent, la `:13704`, prin `scrie_cursuri`). `V_DISCOUNT_FACTURA` e parametru explicit al procedurii (discountul de pe document), nu o coloana citita din `VANZARI`. Formula per-linie e deja extrasa in doua FUNCTII REUTILIZABILE, pure, cu parametri expliciti - NU inline: `calculeaza_total_fara_tva_fact` / `calculeaza_total_tva_fact` (PACK_FACTURARE.pck:15858-16013). Ambele ramifica explicit pe `V_PRET_CU_TVA` (`:15871`, `:15976`), apeland `calculeaza_total_cu_tva_fact` cand flagul e 1. Deci **partea de calcul per linie e deja partajabila** - nu trebuie reinventata. Ce NU e extras e blocul de AGREGARE (suma pe toate liniile documentului, tratarea liniilor din seturi, alegerea `curs`/`multiplicator`/`id_valuta` din `vanzari_cursuri`), care e inline in `scrie_in_vanzari` si depinde de: - `VANZARI_DETALII_TEMP` ca sursa (nu `VANZARI_DETALII`); - stare de sesiune pe pachet: `pack_facturare.nin_valuta`, `ndiscount_evidentiat`, `cserie_act_incasare` / `nnumar_act_incasare` / `nsuma_incasare` / `ntip_doc_incasare` (acestea din urma populate DOAR la emiterea unei facturi-cu-incasare combinata - nu au sens la o editare ulterioara, vezi C); - `pack_def.GetIdMonedaNationala()`. **Cine mai apeleaza `scrie_in_vanzari`**: doar 2 locuri, ambele INSERT-then-populate cu `RETURNING ID_VANZARE`: `scrie_proforma` (`:5643`) si `finalizeaza_factura` (`:14790`). Extragerea blocului de agregare intr-o functie interna parametrizata pe sursa (TEMP la emitere, `VANZARI_DETALII` real la editare) e SIGURA fata de ambii apelanti, daca varianta "sursa=TEMP" pastreaza exact comportamentul actual. **Risc concret de reutilizare naiva pentru S5**: coloanele `serie_incasat/nr_incasat/ suma_incasat/tip_incasat` NU trebuie recalculate la editare - vin din stare de sesiune specifica emiterii unei facturi-cu-incasare, fara nicio sursa persistenta din care sa fie reconstruite la o editare ulterioara. O procedura noua care ar copia tot `UPDATE`-ul din `scrie_in_vanzari` le-ar suprascrie cu `NULL` la fiecare editare, rupand legatura cu incasarea. Procedura propusa pentru S5 trebuie sa scrie DOAR cele 11 coloane de totaluri/curs, nu si aceste 4. ### A.5 Flagul `VANZARI_DETALII.PRET_CU_TVA` Deja parte a calculului in Oracle, nu doar in VFP: valoarea intra in agregare din `a1.pret_cu_tva <- vd.pret_cu_tva <- VANZARI_DETALII_TEMP.PRET_CU_TVA` pentru liniile directe (`:13883`) sau din `VANZARI_SETURI_TEMP.pret_cu_tva` pentru liniile din seturi (`:13909`), apoi `calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact` ramifica pe el (`:15871`, `:15976`). Orice extragere pentru S5 trebuie sa citeasca acelasi `VANZARI_DETALII.PRET_CU_TVA` per linie (deja persistat, cf. #7) - nu exista alta sursa de adevar pentru el. ## B. Propunerea pentru S5 ### Varianta A vs B `actualizeaza_vanzari` e apelata NECONDITIONAT de `finalizeaza_modificare_nota` pentru ORICE editare de nota al carei `cod` exista in `vanzari` - nu doar facturi din #6, ci orice tip de document din VANZARI editat azi prin `frm_modific2024`/`afisjurcom.do_modifica`, folosit deja de ROAGEST/ROACONT in registrul jurnal. Daca **Varianta A** (extinderea in-place a lui `actualizeaza_vanzari` cu recalcul de totaluri) e aleasa, recalculul ar rula automat la ORICE editare de nota cu `cod` in vanzari, inclusiv editari care azi nu ating deloc sumele (ex. doar `explicatie`/`cont`). Risc de regresie: **mediu-mare**, extins la toata suita ROA, nu doar la #6. **Varianta B** (procedura sora noua, ex. `recalculeaza_totaluri_vanzari`, apelata explicit din `finalizeaza_modificare_nota` doar cand editarea a atins efectiv `VANZARI_DETALII`): risc **mic** - `actualizeaza_vanzari` ramane neschimbata (zero impact pe restul aplicatiilor ROA care o folosesc azi), noua procedura e un pas suplimentar, opt-in. **Recomandare: Varianta B**, motivata exclusiv de riscul de regresie asupra editarilor de note care NU sunt facturi si trec prin acelasi `finalizeaza_modificare_nota`. ### Schita procedurii propuse ``` PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER) IS -- reia blocul de agregare din scrie_in_vanzari (:13763-13931), cu sursa -- VANZARI_DETALII WHERE ID_VANZARE = V_ID_VANZARE AND STERS = 0 -- in loc de VANZARI_DETALII_TEMP; V_DISCOUNT_FACTURA citit din VANZARI.DISCOUNT -- (coloana existenta pe randul curent, populata la INSERT din acelasi parametru, :13682) BEGIN SELECT discount, ... INTO lnDiscountFactura, ... FROM vanzari WHERE id_vanzare = V_ID_VANZARE; -- acelasi SELECT agregat ca in scrie_in_vanzari, sursa VANZARI_DETALII in loc de _TEMP UPDATE vanzari SET discount_tva = ..., valoare_achizitie = ..., total_fara_tva = ..., total_tva = ..., total_cu_tva = ..., valval = ..., tvaval = ..., totval = ..., id_valuta = ..., curs = ..., multiplicator = ... -- FARA serie_incasat/nr_incasat/suma_incasat/tip_incasat, vezi A.4 WHERE id_vanzare = V_ID_VANZARE; END; ``` Apelata din `finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601), dupa `actualizeaza_vanzari`. ### Mecanismul de scriere VFP in `VANZARI_DETALII` la emitere Cautare in cache-ul text `COMUN` pentru `VANZARI_DETALII_TEMP` / `DETALII_TEMP` (case-insensitive): **niciun rezultat**, inclusiv in `oscrie_in_fisiere.prg`. Tabela temp e populata probabil printr-un helper generic de upload cursor->tabela (nume de tabela asamblat dinamic sau printr-un mecanism care nu apare ca literal in sursa text). Nu am putut confirma static calea exacta - de cercetat separat, pe partea VFP, inainte de proiectarea S4 (ce cursor/tabela temp foloseste editarea: reutilizeaza `VANZARI_DETALII_TEMP` sau una noua). ### Idempotenta si tranzactionalitate Fluxul de editare a notei ruleaza deja intr-o singura tranzactie manuala: `Thisform.do_deschide_tranzactie()` (comun.vc2:2448) ... `oscrie_in_fisiere` + apelul catre `finalizeaza_modificare_nota` (`:2484-2486`) ... `Thisform.do_inchide_tranzactie(...)` (`:2535`, `COMMIT`/`ROLLBACK` in functie de succes). Procedura noua, apelata DIN INTERIORUL `finalizeaza_modificare_nota`, mosteneste automat aceeasi tranzactie: la esec pe jumatate, `ROLLBACK`-ul anuleaza tot (nota + `vanzari` + noile totaluri) - nu exista fereastra de inconsistenta. `recalculeaza_totaluri_vanzari` propusa e ea insasi idempotenta ca `UPDATE ... WHERE id_vanzare = :id` (poate rula de mai multe ori pe acelasi id fara efect cumulativ, spre deosebire de un `INSERT`). ## C. S6 - legaturile care depind de `cod` Verificat pe cod (nu date, dat fiind ca `MARIUSM_AUTO` are date de test): | Legatura | Cheie reala | Ramane valida? | |---|---|---| | `vanzari_coresp` | `ID_VANZARE_FACT`/`ID_VANZARE_AVIZ` (coloane confirmate din `all_tab_columns`) | DA - `actualizeaza_vanzari` nu schimba `ID_VANZARE` (vezi A.1), doar `COD` pe acelasi rand | | `facturat` (aviz/comanda, `marcheaza_facturat`) | `ID_VANZARE`, exclusiv (PACK_FACTURARE.pck:15384-15421, foloseste doar `pack_facturare.nid_vanzare`, niciodata `cod`) | DA | | chitanta/incasare (`SERIE_INCASAT`/`NR_INCASAT`/`SUMA_INCASAT`/`TIP_INCASAT`) | denormalizate direct pe randul `VANZARI` (nu FK), scrise o singura data la `scrie_in_vanzari` (`:13777-13780`) | DA - `actualizeaza_vanzari` nu le atinge; raman la valoarea de la emitere, ceea ce e comportamentul corect (nu se recalculeaza la editare de sume, vezi A.4/B) | | `ReferinteDocumenteNota`/`ReferinteDocument` (PACK_DOCUMENTE.pck:25-93) | `ACT.id_factd`/`id_factc` = `id_fact`-ul documentului, filtrat `cod <> tnCod` | DA - e o VERIFICARE PRE-EDITARE (gate, apelata inainte sa se schimbe ceva), nu o legatura persistenta; nu depinde de `cod`-ul rezultat dupa editare | | `anaf_efactura` | `ID_FACT` (coloana confirmata) | DA - `id_fact` nu se schimba la editare (by design, deja stabilit in plan) | | `documente` | `DOCUMENTE.ID_DOC = ACT.ID_FACT` (confirmat din `MERGE INTO DOCUMENTE ... ON A.ID_DOC = B.ID_DOC` unde `B.ID_DOC` vine din `ACT_TEMP.ID_FACT`, PACK_CONTAFIN.pck:858-880) | DA - si se REACTUALIZEAZA automat (SERIE_ACT/NRACT/DATAACT) la fiecare scriere de nota, inclusiv editare, prin acelasi `MERGE` care ruleaza in `cumuleaza_note_act` | **Concluzie generala**: toate legaturile intre note contabile trec prin `ID_FACT` (`ACT.id_factd/id_factc`, `DOCUMENTE.id_doc`, `ANAF_EFACTURA.id_fact`), niciodata prin `cod`. Cum #6 pastreaza `id_fact` neschimbat by design, toate raman valide fara nicio interventie suplimentara. Singura legatura care trece prin altceva (`ID_VANZARE`, PK-ul randului `VANZARI`) e `vanzari_coresp`/`marcheaza_facturat`, si acesta ramane la fel neschimbat (doar `COD` se rescrie pe acelasi rand, vezi A.1). **S6 se poate inchide ca "confirmat pe cod, fara lucru suplimentar necesar"**, nu doar ca "de verificat". ## D. S7 - rotunjirea la reeditare `verifica_total_document` (PACK_FACTURARE.pck:16073-...) insereaza o linie de corectie in `ACT_TEMP` cand suma recalculata din `ACT_TEMP` insusi (`V_TOTFTVA_VER`/`V_TOTTVA_VER`) difera de `pack_facturare.ntotftva`/`ntottva` (valori calculate in VFP, trecute prin stare de sesiune inainte de scriere). `ACT_TEMP` e complet repopulat la fiecare scriere - procedura nu "tine minte" nimic intre apeluri, in Oracle. **Risc identificat pe partea VFP**: la editare, `afisjurcom.do_modifica` (comun.vc2:2352-2366) incarca in cursorul `tact` TOATE randurile curente ale notei (`SELECT * FROM vact_tot WHERE cod = ... [AND STERS = 0] ORDER BY id_act`) - asta include si linia de corectie inserata la salvarea anterioara (e un rand `ACT` normal, fara marcaj special care sa o distinga). La resalvare, acest rand curge inapoi prin `oscrie_in_fisiere` in `ACT_TEMP` impreuna cu liniile editate de utilizator, iar `verifica_total_document` ruleaza din nou pe noul `ACT_TEMP` (care deja contine vechea corectie). **Nu se poate decide static** daca rezultatul e o a doua corectie suprapusa peste prima, sau daca mecanismul e self-consistent (adica `V_TOTFTVA_VER` deja include vechea corectie in suma agregata, iar `pack_facturare.ntotftva` recalculat de VFP coincide, deci nu se mai adauga nimic) - depinde de cum recalculeaza VFP `ntotftva`/`ntottva` la editare, cod care nu a fost verificat in aceasta trecere (afara de scope-ul Oracle-only al acestei cercetari). De notat: acest mecanism NU e nou pentru #6 - e folosit azi neschimbat de orice editare de nota prin `do_modifica` (ROAGEST/ROACONT registru jurnal), pe orice tip de document, de ani de zile. Daca ar acumula corectii sistematic la editari repetate, ar fi deja o problema cunoscuta pe editari non-factura. Asta scade riscul, dar nu-l elimina pentru cazul specific facturii, unde S4/S5 introduc o cale noua de a schimba cantitati/preturi care nu exista azi in `do_modifica` generic (pe notele generice azi de regula nu se schimba baza de calcul TVA in acelasi fel). **Raspuns**: nu se poate decide din cod - testul deja propus in plan (S7: trei editari consecutive, verifica nr. de linii de corectie in `ACT`) e calea corecta si suficienta; nu exista scurtatura statica. ## E. Intrebari deschise 1. **Semnatura procedurii noi**: apel neconditionat din `finalizeaza_modificare_nota` oricand `cod`-ul exista in `vanzari` (simplu, cost mic - un `SELECT`+`UPDATE` in plus si pe editari care nu ating articolele), sau flag explicit `tnAtinsArticole` trecut din VFP (evita lucru inutil, dar risc de flag uitat/gresit)? **Recomandare: apel neconditionat** - cost neglijabil, elimina o clasa de bug. 2. **Discountul de document la editare**: `V_DISCOUNT_FACTURA` la emitere e parametru explicit din VFP; planul S4/S4b nu mentioneaza editarea discountului de pe document, doar cantitate/pret/ `pret_cu_tva` pe linie. La S5, discountul se citeste din `VANZARI.DISCOUNT` (neschimbat) - de confirmat cu Marius ca asta e comportamentul dorit (discountul de document ramane needitabil in #6). 3. **`VALVAL`/`TVAVAL`/`TOTVAL`** (totaluri in valuta): planul S5 mentioneaza explicit doar `total_fara_tva`/`total_tva`/`total_cu_tva`/`valoare_achizitie`/`discount_tva`. Fac parte din acelasi bloc de agregare din `scrie_in_vanzari` - **recomandare: le includem in recalcul**, cost suplimentar zero, evita inconsistenta pe documentele in valuta straina. 4. **Mecanismul de populare al `VANZARI_DETALII_TEMP`** la emitere n-a putut fi gasit static in cache-ul text `COMUN` (cautare literal, fara rezultate) - necesita cercetare VFP separata inainte de a proiecta calea exacta de scriere pentru editarea din S4 (aceeasi tabela temp, sau una noua). 5. **S7**: raspunsul cere test dinamic (3 editari consecutive), nu poate fi confirmat static - de pastrat explicit in scope-ul S8, nu doar "de verificat" generic in text.