Files
roafacturare/docs/cercetare/rec_s5_oracle_vanzari.md
Marius Mutu d9f5ca4226 docs: planurile, proiectarile si rapoartele de lucru intra in versionare
Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git,
nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de
cercetare pe care se sprijina modificarile din cod. O stergere acolo era
definitiva.

Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au
fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele
fisierelor de test la care se refereau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
2026-08-11 22:17:17 +03:00

15 KiB

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.