18 KiB
Cercetare: calea VFP->Oracle la emitere si calea propusa pentru editare (#6, punctul E.4)
Sursa: interogari SELECT proaspete pe MARIUSM_AUTO@ROA_CENTRAL (08.08.2026), acelasi mediu si
aceeasi versiune de baza ca in rec_s5_oracle_vanzari.md (nu s-a schimbat nimic intre cele doua
cercetari din aceeasi zi). Numerele de linie PL/SQL de mai jos sunt dintr-un export propriu, facut
in aceasta sesiune, din all_source pentru PACK_FACTURARE (schema/pachet identic cu cel din
raportul anterior). Partea VFP e din cache-ul text .vc2 din proiect (nu binarul).
Aceasta cercetare inchide golul lasat de rec_s5_oracle_vanzari.md, sectiunea E.4: cum ajung
liniile facturii in Oracle la emitere si care e calea corecta pentru editare.
1. Calea de azi, capat la capat
1.1 VFP: cine populeaza VANZARI_DETALII_TEMP
Raspuns: Oracle, prin apeluri per-linie facute din VFP, nu VFP direct prin INSERT.
frm_facturare_articole.do_scrie_articole(COMUN\clase\ofacturare.vc2:13967-14195)::13981-13999cheamapack_facturare.initializeaza_date_factura(...)(antetul documentului, tine minte parametrii in stare de sesiune pe pachet:pack_facturare.ntip,nid_sucursala,nid_utiletc. — folosite mai jos deadauga_articol_factura).:14036-14115:SELECT crsfactura->SCAN, cate unScatter Name poArt MEMOper linie, apoi pentru fiecare linie fara rata de contract (ELSEla:14051) construieste text PL/SQL literal (nu{call}cu parametri legati) si apeleazapack_facturare.adauga_articol_factura(id_temp, id_articol, serie, explicatie, id_pol, id_gestiune, pret_achizitie, pretd, id_valutad, pret, id_valuta, cu_tva, gestionabil, cantitate, discount, cont, curs, multiplicator, id_jtva_coloana, id_part_rez, id_lucrare_rez, pretv_orig, id_set_fact, id_ctr, gnIdUtil, taxcode, lot)(:14069-14091), executat imediat pringoExecutor.oExecute(lcSql)(:14105) — un apel Oracle per linie de factura, in bucla, nu un singur apel cu tot cursorul.- Pentru liniile din seturi:
pack_facturare.initializeaza_seturi_temp+ un apelpack_facturare.adauga_articol_set(...)per linie de set (:14117-14154), scrie inVANZARI_SETURI_TEMP.
frm_facturare_articole.do_scrie_factura(:14197-14554): dupa ce toate liniile au fost urcate in TEMP prin apelurile de mai sus, apeleaza finalizarea documentului —pack_facturare.scrie_factura2(...)(facturi,:14345/:14373) saupack_facturare.scrie_proforma(...)(:14286) sauscrie_factura_avize(...)/scrie_factura_avize_retur(...)dupa tipul documentului — un singur apel, fara sa mai transmita liniile (ele sunt deja inVANZARI_DETALII_TEMP, populata de bucla anterioara, in ACEEASI sesiune/tranzactie Oracle).
1.2 Oracle: adauga_articol_factura — nu doar INSERT, RECALCULEAZA din sursa originala
Verificat sursa completa a procedurii publice pack_facturare.adauga_articol_factura (semnatura cu
V_ID_TEMP ca prim parametru, cea apelata de VFP la :14069) — nu e un simplu INSERT cu
valorile primite de la VFP. Ramifica pe pack_facturare.ntip (starea de sesiune setata la
initializeaza_date_factura) si re-deriva V_PRET, V_PROC_TVAV, V_ID_VALUTA,
V_PRETURI_CU_TVA, V_IN_STOC direct din documentul-sursa:
ntip IN (3,21,28,42,47)(facturare din comenzi):SELECT ... FROM COMENZI_ELEMENTE ...ntip = 4(facturare din avize):SELECT ... FROM VANZARI_DETALII ...(avizul deja scris)ntip = 45(restaurant): calcul dinJTVA_COLOANE+CRM_POLITICI_PRET_ARTV_OPT_FACTURARE = 3(contract):SELECT ... FROM CTR_ARTICOLE ...- fallback (
ELSE): doar cota TVA dinJTVA_COLOANE, restul (V_PRET,V_ID_VALUTA,V_PRETURI_CU_TVA,V_IN_STOC) preluate ca atare din parametrii transmisi de VFP.
Abia dupa acest bloc CASE urmeaza INSERT-ul propriu-zis:
INSERT INTO VANZARI_DETALII_TEMP
(ID_TEMP, ID_ARTICOL, SERIE, LOT, EXPLICATIA, ID_POL, PRET_ACHIZITIE, PRETD, ID_VALUTAD,
PRET, PRET_CU_TVA, PROC_TVAV, CANTITATE, DISCOUNT_UNITAR, ID_GESTIUNE, CONT, ID_VALUTA,
CURS, MULTIPLICATOR, ID_JTVA_COLOANA, IN_STOC, ID_VANZARE_SET, ID_PART_REZ, ID_LUCRARE_REZ,
PRETV_ORIG, CUSTODIE, ID_CTR, ID_UTIL, TAXCODE)
VALUES (...)
(am confirmat integral corpul, INSERT-ul e ultimul bloc din procedura, imediat dupa END CASE).
Consecinta directa pentru #6: adauga_articol_factura NU poate fi reutilizata ca atare pentru
editare — depinde de starea de sesiune pack_facturare.ntip/clistaid/id_ctr care descrie
DOCUMENTUL SURSA de la emitere (comanda/aviz/contract), stare care nu exista si nu are sens la o
editare ulterioara a facturii deja emise. Orice procedura noua pentru editare trebuie sa scrie
direct in VANZARI_DETALII (tabela reala), nu prin acest drum.
Exista si sterge_articol_factura(V_ID_TEMP, V_ID_UTIL) — DELETE FROM VANZARI_DETALII_TEMP WHERE ID_TEMP = V_ID_TEMP — folosita doar cat timp factura e in curs de compunere (inainte de emitere),
nu dupa.
1.3 Oracle: scrie_factura2 -> finalizeaza_factura -> scrie_in_vanzari — trecerea TEMP -> real
scrie_factura2 (PACK_FACTURARE, corpul activ, nu varianta comentata din cod):
:70pack_contafin.sterge_temp_actrul(), seteazapack_facturare.ntotftva/ntottvadin parametri (valori calculate in VFP).:84SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP— citeste TOATA tabela temp intr-un array PL/SQL, fara filtru pe sesiune (GTT-ul e oricum izolat per sesiune/tranzactie).- Bucla pe
tab_detalii, ramificata tot pentip(transfer intre subunitati, restaurant etc.) — pentru cazul standard de factura apeleaza mai departe sprepack_facturare.finalizeaza_factura.
finalizeaza_factura (nu comentata):
pack_facturare.initializeaza_scriere_actrul(V_DATAORA);
pack_facturare.scrie_in_vanzari(V_DISCOUNT_FACTURA, V_ID_DELEGAT, V_ID_MASINA, V_ID_FACTURARE,
V_LISTARE_DETALIATA, V_DATAORA_EXP, V_ID_AGENT, V_TEXT_ADITIONAL, pack_facturare.nid_vanzare);
pack_facturare.finalizeaza_scriere_actrul();
-- update vanzari set id_fact = ... (completare id_fact dupa scrierea in contabilitate)
scrie_in_vanzari — gasit exact mecanismul care lipsea din raportul anterior:
INSERT INTO VANZARI (...) VALUES (...) RETURNING ID_VANZARE INTO pack_facturare.nid_vanzare(randul parinte, un rand per document din bucla petab_vanz, tabela interna cu documentele de scris — cazul normal e un singur document).pack_facturare.scrie_cursuri(pack_facturare.nid_vanzare).- Trecerea efectiva TEMP -> real:
Cheia de potrivire intre randul nou din
INSERT /*+ APPEND */ INTO VANZARI_DETALII (ID_VANZARE, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, ID_JTVA_COLOANA) SELECT pack_facturare.nid_vanzare, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, ID_JTVA_COLOANA FROM VANZARI_DETALII_TEMP WHERE ID_COMANDA = tab_vanz(i).id_comanda AND NUMAR_ACT = tab_vanz(i).numar_act ORDER BY ID_TEMP;VANZARIsi liniile lui din TEMP e(ID_COMANDA, NUMAR_ACT)— nuID_TEMPdirect — pentru ca un singur apel poate scrie mai multe documenteVANZARIdeodata (facturare pe mai multe comenzi/numere de act simultan); fiecare document isi ia doar liniile care se potrivesc. - Coloanele COPIATE in
VANZARI_DETALIIsunt un subset dinVANZARI_DETALII_TEMP(16 coloane) —ID_TEMP,ID_CTR,ID_UTIL,TAXCODE,LOT,ID_VANZARE_SET,ID_PART_REZ,ID_LUCRARE_REZ,PRETV_ORIG,CUSTODIE,NUMAR_ACT,ID_COMANDA,ID_GESTIUNE_DESTraman DOAR in TEMP, nu se copiaza in tabela reala (VANZARI_DETALIInu are coloaneleID_TEMP,NUMAR_ACT,ID_COMANDAetc. — confirmat dinall_tab_columns, vezi 2.2). - Urmeaza blocul de agregare (
SELECT INTOdinVANZARI_DETALII_TEMP, deja documentat inrec_s5_oracle_vanzari.mdA.3-A.4) siUPDATE VANZARI SET total_fara_tva=..., ...cu totalurile denormalizate.
PK-ul VANZARI_DETALII.ID_VANZARE_DET se genereaza automat: trigger TRG_VANZARI_DET_BEFOINS
(BEFORE INSERT ... FOR EACH ROW, verificat prin dbms_metadata.get_ddl) face exclusiv
SELECT SEQ_VANZARI_DETALII.NEXTVAL INTO :NEW.ID_VANZARE_DET FROM DUAL — nu seteaza alte coloane.
Deci orice INSERT nou in VANZARI_DETALII (inclusiv unul scris pentru #6, la adaugare de linie
noua la editare) primeste automat PK-ul corect fara sa fie nevoie de secventa apelata manual din
codul de editare.
2. Natura tabelei temp si structura
2.1 VANZARI_DETALII_TEMP — Global Temporary Table, ON COMMIT DELETE ROWS
Confirmat din all_tables:
| TABLE_NAME | TEMPORARY | DURATION |
|---|---|---|
| VANZARI | N | — |
| VANZARI_DETALII | N | — |
| VANZARI_DETALII_TEMP | Y | SYS$TRANSACTION |
| VANZARI_SETURI_TEMP | Y | SYS$TRANSACTION |
DURATION = SYS$TRANSACTION inseamna GTT cu ON COMMIT DELETE ROWS — randurile dispar la commit
(sau rollback), nu doar la sfarsit de sesiune. Consecinta directa: orice solutie care ar
reutiliza VANZARI_DETALII_TEMP pentru editare (#6) trebuie sa umple tabela SI sa consume
rezultatul in ACEEASI tranzactie — nu poate fi umpluta intr-un pas si citita in altul, ca in fluxul
de emitere unde umplere (bucla adauga_articol_factura) si consum (scrie_factura2) se intampla
deja in aceeasi conexiune/tranzactie, inainte de commit.
2.2 Coloane: VANZARI_DETALII vs VANZARI_DETALII_TEMP
VANZARI_DETALII (35 coloane, din all_tab_columns) — cheie primara ID_VANZARE_DET (NOT NULL,
generata din secventa prin trigger), FK logic ID_VANZARE (NOT NULL). Coloane relevante pentru
editare: PRET (NOT NULL), CANTITATE, PRET_CU_TVA, DISCOUNT_UNITAR, PROC_TVAV, STERS
(NOT NULL), VALIDAT/DATAORA_VALID/ID_UTIL_VALID, ID_UTILS/DATAORAS (audit),
DIFERENTA (NOT NULL), CUSTODIE/DESCARCAT (NOT NULL) — ultimele patru nu au valoare implicita
vizibila in trigger, deci probabil DEFAULT la nivel de coloana (nu s-a verificat separat, nu era
in scope).
VANZARI_DETALII_TEMP (28 coloane) — fara PK real (ID_TEMP e generat in VFP, folosit doar ca
identificator temporar de linie in cadrul sesiunii curente de compunere a facturii), fara
ID_VANZARE/ID_VANZARE_DET/STERS/VALIDAT (nu se aplica inainte de a exista documentul
parinte). Are in schimb coloane specifice etapei de compunere, care NU exista in tabela reala:
ID_TEMP, NUMAR_ACT, ID_COMANDA, ID_GESTIUNE_DEST, ID_UTIL (vs ID_UTILS in cea reala).
Coloanele comune folosite de editare (S4): ID_ARTICOL, PRET, CANTITATE, PRET_CU_TVA,
DISCOUNT_UNITAR, PROC_TVAV, ID_GESTIUNE, CONT, ID_VALUTA, CURS (doar in TEMP; in
VANZARI_DETALII nu exista CURS per linie — cursul e doar pe document, in VANZARI_CURSURI/
VANZARI.CURS), ID_JTVA_COLOANA, SERIE, EXPLICATIE/EXPLICATIA (nume diferit!), TAXCODE,
LOT.
3. Stergerea si adaugarea de linii
3.1 Stergere — mecanism EXISTENT azi doar la nivel de document intreg, NU per linie
Cautat explicit orice UPDATE VANZARI_DETALII SET STERS in PACK_FACTURARE — gasite doar doua
locuri, ambele sterg TOATE liniile unui document, niciodata o singura linie:
sterge_factura(V_ID_VANZARE, ...):UPDATE VANZARI_DETALII SET STERS = V_STERS, ID_UTILS = V_ID_UTIL, DATAORAS = V_DATAORA WHERE ID_VANZARE = V_ID_VANZARE AND STERS = V_NESTERS(V_STERS variaza 0/1 dupa tipul documentului, cazul normal fiind 1).sterge_proforma(V_ID_VANZARE, ...): acelasi tipar, exclusiv pe proforme.
Nu exista azi un mecanism Oracle sau VFP de stergere per-linie in VANZARI_DETALII — orice
"stergere de linie la editare" ceruta de S4/S4b e functionalitate NOUA, de adaugat la #6, nu o
reutilizare a ceva existent.
Exista insa un precedent apropiat de UPDATE punctual per linie, util ca sablon:
modifica_explicatie_articol(V_ID_VANZARE_DET, V_EXPLICATIE, V_ID_UTIL, V_TAXCODE) —
UPDATE VANZARI_DETALII SET EXPLICATIE = V_EXPLICATIE, TAXCODE = V_TAXCODE WHERE ID_VANZARE_DET = V_ID_VANZARE_DET — exact modelul de "UPDATE tintit prin goExecutor" mentionat ca varianta in
planul S5/S6, deja folosit azi din frm_modifica_articol_factura (mostenire de la #7, vezi
plan_06_editare_factura.md).
3.2 Adaugare de linie noua — nu cere nimic special in plus fata de un INSERT direct
ID_VANZARE_DET vine automat din SEQ_VANZARI_DETALII prin trigger la orice INSERT INTO VANZARI_DETALII, indiferent de cine face insert-ul (nu doar fluxul de emitere) — vezi 1.3. Deci
adaugarea unei linii noi la editare nu cere obtinerea manuala a unui ID din secventa; e suficient
un INSERT INTO VANZARI_DETALII (ID_VANZARE, ID_ARTICOL, PRET, CANTITATE, PRET_CU_TVA, ...) VALUES (...) (fara ID_VANZARE_DET in lista de coloane) intr-o procedura noua, apelata din
finalizeaza_modificare_nota alaturi de recalculul de totaluri propus in rec_s5_oracle_vanzari.md
punctul B.
4. Propunerea pentru S4/S5/S6 — cum trimite editarea liniile modificate in Oracle
Optiuni comparate
A. Refolosirea VANZARI_DETALII_TEMP + o "mini scrie_in_vanzari" pentru editare — ar insemna ca
formularul de editare (S4) sa populeze TEMP la fel ca la emitere (apeluri per linie), apoi o
procedura noua sa faca INSERT-urile/UPDATE-urile in VANZARI_DETALII din TEMP, in aceeasi
tranzactie (impusa de ON COMMIT DELETE ROWS, vezi 2.1). Risc: reproduce complexitatea lui
adauga_articol_factura (ramificare pe ntip/sursa document) fara sa aiba sens la editare — acolo
nu exista comanda/aviz/contract "curent" din care sa se re-deriva pretul; ar trebui un mod nou,
simplificat, de populare a TEMP doar pentru editare, ceea ce complica inutil un cod deja incarcat.
Risc mediu-mare de regresie pe calea de emitere daca vreo modificare atinge accidental
adauga_articol_factura/scrie_in_vanzari partajate.
B. UPDATE/INSERT/soft-DELETE punctual direct in VANZARI_DETALII, prin goExecutor, dintr-o
procedura noua dedicata editarii — fara sa treaca deloc prin VANZARI_DETALII_TEMP. Model deja
existent si folosit: modifica_explicatie_articol (UPDATE tintit pe ID_VANZARE_DET). Pentru cele
trei operatii cerute de S4/S4b:
- Editare cantitate/pret/
pret_cu_tvape o linie existenta:UPDATE VANZARI_DETALII SET CANTITATE=..., PRET=..., PRET_CU_TVA=..., DISCOUNT_UNITAR=... WHERE ID_VANZARE_DET = :id. - Stergere linie:
UPDATE VANZARI_DETALII SET STERS=1, ID_UTILS=:util, DATAORAS=SYSDATE WHERE ID_VANZARE_DET = :id— acelasi tipar casterge_factura, dar tintit pe o singura linie (nou, nu exista azi, vezi 3.1). - Adaugare linie noua:
INSERT INTO VANZARI_DETALII (ID_VANZARE, ID_ARTICOL, PRET, CANTITATE, PRET_CU_TVA, DISCOUNT_UNITAR, PROC_TVAV, ID_GESTIUNE, CONT, ID_VALUTA, ID_JTVA_COLOANA, SERIE, EXPLICATIE, TAXCODE, LOT) VALUES (...)— PK automat din trigger (vezi 3.2). Apoi, in ACEEASI tranzactie (deschisa deja dedo_deschide_tranzactiein fluxul de editare a notei, cf.rec_s5_oracle_vanzari.mdB "Idempotenta si tranzactionalitate"), se cheama procedura de recalcul a totalurilor propusa inrec_s5_oracle_vanzari.md(recalculeaza_totaluri_vanzari), care citeste direct dinVANZARI_DETALII WHERE ID_VANZARE=:id AND STERS=0— fara nicio dependenta deVANZARI_DETALII_TEMP.
Recomandare: Varianta B
Argumentul principal e riscul de regresie asupra emiterii: Varianta A ar obliga fie la extinderea
lui adauga_articol_factura cu o ramura noua "editare" (cod partajat cu emiterea, risc direct pe
calea critica), fie la duplicarea partiala a logicii lui in altă procedura (risc de divergenta,
exact problema pe care #8 a corectat-o deja pentru calculul de totaluri). Varianta B nu atinge deloc
adauga_articol_factura/scrie_in_vanzari/VANZARI_DETALII_TEMP — cod nou, izolat, apelat doar
din calea de editare (finalizeaza_modificare_nota), cu acelasi profil de risc "mic" motivat deja
pentru recalculeaza_totaluri_vanzari in rec_s5_oracle_vanzari.md. In plus, B se potriveste
natural cu S4b (verificare/sincronizare explicita, nu silentioasa): fiecare rand modificat/sters/
adaugat poate fi tratat ca o comanda separata, usor de enumerat utilizatorului inainte de aplicare
("linia X: cantitate 5 -> 8, pret 10 -> 12"), pe cand Varianta A ar produce un singur bloc opac de
recalcul din care nu se pot extrage usor liniile individuale schimbate.
Observatie tehnica pentru implementare: la editarea preturilor, NU se recalculeaza V_PROC_TVAV
din JTVA_COLOANE/comanda/contract ca la emitere (asta ar reintroduce dependenta de sursa
documentului, respinsa mai sus) — cota de TVA ramane cea deja persistata pe linie
(VANZARI_DETALII.PROC_TVAV), coerent cu felul in care pret_cu_tva/proc_tvav sunt deja tratate
ca date proprii ale liniei si nu recalculate la fiecare atingere (cf. #7).
5. Ce ramane neconfirmat / in afara scopului acestei cercetari
- Coloanele
STERS,VALIDAT,DIFERENTA,CUSTODIE,DESCARCATdinVANZARI_DETALIIsuntNOT NULLfara sa aiba valoare din trigger — probabil auDEFAULTla nivel de coloana (all_tab_columns.data_defaultnu a fost interogat, nu era necesar pentru concluziile de mai sus); de verificat explicit inainte de a scrie INSERT-ul de linie noua din Varianta B, ca sa nu fie nevoie sa le populeze manual. - Valorile implicite exacte pentru
DEFAULT(daca exista) pe coloanele NOT NULL de mai sus. - Comportamentul
VANZARI_SETURI_TEMP/liniile din seturi la editare (#6 nu pare sa acopere editarea seturilor, doar articolele individuale — de clarificat cu Marius daca seturile sunt in scope).