29 KiB
S10 (runda 3) — Proiectare: pretul re-derivat din contract la reemiterea #13
Agent de proiectare (research), read-only. Nicio editare de cod, nicio scriere Oracle in afara de
SELECT. Sursa SQL: D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql
(17217 linii — de data asta liniile citate coincid exact cu cele din raportul vechi
s10_pret_rederivat.md, fara offset). Date verificate pe MARIUSM_AUTO@ROA_CENTRAL, doar
SELECT, 11.08.2026 — baza de dezvoltare, esantion mic (vezi sectiunea 2), nu productie.
0. Rezumat
Intrebare adaugata de team-lead, raspuns scurt: NU. cursor_retur_document (procedura folosita
la INCARCAREA liniilor unui document existent, apelata din VFP cu V_COPIERE=1) citeste pretul
ca atare din VANZARI_DETALII.PRET, nu il re-deriva — nu exista niciun JOIN catre
CTR_ARTICOLE/CRM_POLITICI_PRET_ART in toata procedura (:3949-4062). Presupunerea din S8b se
confirma pe cod. Detaliu complet, cu dovezi, in sectiunea 1bis (nou adaugata, tratata ca prioritara
fata de restul raportului, conform cererii).
Faptul portant (scrierea, nu citirea) se reconfirma integral (:5146-5185) si se extinde cu
doua descoperiri noi:
- TVA-ul si identitatea valutei sunt re-derivate din exact acelasi
JOIN/SELECTca pretul, pe aceeasi ramura de contract — nu doar pretul e in pericol la reemitere, ci pretul + cota de TVA + valuta articolului, impreuna, tot-sau-nimic (sectiunea 3). - Nu exista nicio a doua re-derivare mai departe in lant —
PRETtrece neschimbat de laVANZARI_DETALII_TEMPinVANZARI_DETALII(scrie_in_vanzari,:13705-13757, coloanaPRETcopiata direct, faraDECODE/recalcul) sicontabilizeaza_articolnu scrie niciodata peVANZARI_DETALII.PRET(scrie doarACT_TEMP, nota contabila). Singurul loc de re-derivare eadauga_articol_factura:5146-5185(sectiunea 1) — la scriere, niciodata la citire (sectiunea 1bis).
Pe date reale (esantion mic, baza de dezvoltare): 3 din 11 linii deja facturate pe contract au azi un pret de contract diferit de pretul efectiv facturat — divergenta nu e ipotetica, e deja prezenta (sectiunea 2).
Recomandare (detaliata in sectiunea 4): varianta (c) avertizare + confirmare explicita,
implementabila integral in VFP cu goExecutor (fara sa ating pack_facturare, conform deciziei
27-bis), cu optiunea de a trece la (b) blocare stricta daca Marius prefera zero schimbari
tacite vreodata. Varianta (d) ocolire a ramurii e posibila mecanic dar produce o regresie reala
(pierderea legaturii VANZARI_DETALII.ID_CTR) — nu o recomand.
1. Reverificarea faptului portant
Cod citit direct din sursa (:5039-5220), nu preluat din raportul vechi.
-- :5039-5050 — V_OPT_FACTURARE se calculeaza intern, din V_ID_CTR primit de la VFP
IF pack_facturare.ntip IN (2, 6, 26, 52) THEN
BEGIN
SELECT NVL(OPT_FACTURARE, 3) INTO V_OPT_FACTURARE FROM CONTRACTE WHERE ID_CTR = V_ID_CTR;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_OPT_FACTURARE := 4;
END;
END IF;
-- :5146-5185 — ramura de contract, in interiorul CASE-ului principal (:5052-5220)
WHEN V_OPT_FACTURARE = 3 THEN
BEGIN
SELECT DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR),
B.PROC_TVAV,
B.ID_VALUTA,
A.PRET_CU_TVA,
C.IN_STOC
INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC
FROM CTR_ARTICOLE A
LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL_ART = B.ID_POL_ART
LEFT JOIN NOM_ARTICOLE C ON B.ID_ARTICOL = C.ID_ARTICOL
WHERE B.ID_ARTICOL = V_ID_ARTICOL AND A.ID_CTR = V_ID_CTR AND B.ID_POL = V_ID_POL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
... V_PRET := V_PRET_TEMP; V_ID_VALUTA := V_ID_VALUTA_TEMP; V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP; V_IN_STOC := V_IN_STOC_TEMP;
END;
Confirmat, cuvant cu cuvant fata de runda 9: cu OPT_FACTURARE = 3 (sau NULL, implicit 3),
daca CTR_ARTICOLE.PRET_UNITAR <> 0, acea valoare inlocuieste V_PRET_TEMP (pretul trimis de
VFP), necondiționat de faptul ca operatorul a atins sau nu linia. Procedura nu are nicio notiune
de reemitere — parametrii sunt toti IN (:4989-5059, verificat), niciun canal care sa spuna
"nu recalcula". NO_DATA_FOUND (politica/articol lipsa) e singurul caz care respecta pretul
trimis.
Nu exista alt loc care suprascrie pretul
scrie_in_vanzari(:13488+), care muta liniile dinVANZARI_DETALII_TEMPinVANZARI_DETALII(tabela finala) la finalizarea documentului:PRETe in lista de coloane copiate neschimbat (:13705-13757,SELECT ... PRET ... FROM VANZARI_DETALII_TEMP→INSERT INTO VANZARI_DETALII (..., PRET, ...)), faraDECODE/recalcul. Cautare exhaustiva pe fisier: zeroINSERT INTO VANZARI_DETALII(tabela finala, nu_TEMP) in afara de acest punct.contabilizeaza_articol(:7173-7547, reconfirmat structural fata de runda 9) citestedetalii_articol.pret(randul deja scris inVANZARI_DETALII_TEMP) doar ca sa calculeze suma notei contabile — nu scrie niciodata inapoi peVANZARI_DETALII.PRET.modificare_politica_stoc(:2122-2135) face unUPDATE CRM_POLITICI_PRET_ART SET PRET=0, ...— e singurul altSET PRETgasit in tot fisierul, dar reseteaza politica la schimbarea monedei stocului, complet in afara lantului de facturare a unei linii; nu atinge vreo linie de document.
Concluzie sectiune: raspunsul din runda 9 se confirma integral, fara nicio corectie, si se inchide explicit intrebarea "mai exista si alte locuri" — nu mai exista.
1bis. Citirea la incarcare: cursor_retur_document re-deriva pretul?
Adaugat la cererea team-lead-ului — critic pentru S8b, tratat ca prioritar.
Verdict: NU. Pretul se citeste ca atare din VANZARI_DETALII.PRET, fara nicio re-derivare.
Corp complet, PACK_FACTURARE:3949-4062:
PROCEDURE cursor_retur_document(V_IN_VALUTA IN NUMBER,
V_LISTAID IN VARCHAR2,
V_COPIERE IN NUMBER,
V_PROFORMA IN NUMBER,
V_ID_UTIL IN NUMBER,
V_CURSOR OUT cursor_facturare) IS
...
BEGIN
pack_facturare.initializeaza_facturare(V_ID_UTIL);
OPEN V_CURSOR FOR
WITH CRS AS (SELECT X as ID_VANZARE FROM table(charn2collection(V_LISTAID, V_SEPARATOR)))
SELECT ROWNUM AS ID_C, A.ID_ARTICOL, A.LOT, A.SERIE, A.ID_POL, A.ID_VALUTA,
... DISCOUNT_UNITAR (derivat din A.DISCOUNT_UNITAR) ...,
B.CODMAT, B.CODBARE, B.DENUMIRE, NVL(B.UM,'') AS UM,
... GESTIONABIL ..., A.CANTITATE, A.PROC_TVAV, A.ID_JTVA_COLOANA,
A.PRET_CU_TVA AS PRETURI_CU_TVA, A.CURS, A.MULTIPLICATOR,
(CASE WHEN NVL(C.MONEDA_NATIONALA,1)<>1
THEN ROUND(A.CURS * ROUND(A.PRET, pack_sesiune.nzecimale_pretvval) / NVL(A.MULTIPLICATOR,1), pack_sesiune.nzecimale_pretv)
ELSE ROUND(A.PRET, pack_sesiune.nzecimale_pretv)
END) + A.DIFERENTA AS PRET,
...
FROM (SELECT A1.ID_ARTICOL, A1.LOT, A1.SERIE, A1.ID_POL, A1.DISCOUNT_UNITAR, A1.DIFERENTA,
A1.CANTITATE, A1.PROC_TVAV, A1.ID_JTVA_COLOANA,
NVL2(A1.ID_GESTIUNE,1,0) AS GESTIONABIL, A1.PRET_CU_TVA, A1.PRET, A1.ID_VALUTA,
NVL(A2.CURS,0) AS CURS, NVL(A2.MULTIPLICATOR,1) AS MULTIPLICATOR,
A1.EXPLICATIE, A1.ID_GESTIUNE, A1.PRET_ACHIZITIE, A1.PRETD, A1.ID_JTVA_COLOANA_EX
FROM VANZARI_DETALII A1
LEFT JOIN VANZARI_CURSURI A2 ON A1.ID_VANZARE = A2.ID_VANZARE AND A1.ID_VALUTA = A2.ID_VALUTA
WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)) A
LEFT JOIN NOM_ARTICOLE B ON A.ID_ARTICOL = B.ID_ARTICOL
LEFT JOIN NOM_VALUTE C ON A.ID_VALUTA = C.ID_VALUTA
ORDER BY B.DENUMIRE;
END cursor_retur_document;
Analiza surselor, coloana cu coloana:
A.PRET(:4009-4015, alias finalPRET) vine din subquery-ulAcare eVANZARI_DETALII A1direct (A1.PRET,:4041) — nicio referinta laCTR_ARTICOLE/CRM_POLITICI_PRET_ART/CRM_POLITICI_PRETURIin toata procedura (cautare exhaustiva pe corpul de la:3949-4062: zero hit-uri pentru oricare din cele trei tabele). SingureleJOIN-uri sunt:VANZARI_CURSURI A2(:4051-4053), peID_VANZARE+ID_VALUTA— aduceCURS/MULTIPLICATORstocate pe documentul insusi (cursul valutar de la momentul cand a fost scris, nu un curs "de azi" recalculat — tabelaVANZARI_CURSURI, nuCURS).NOM_ARTICOLE B(:4056-4057) — doar campuri descriptive (CODMAT,CODBARE,DENUMIRE,UM), acelasi tipar confirmat deja inrec_pret_lazy.mdA2 pentru alte cursoare din pachet — niciodata sursa de pret.NOM_VALUTE C(:4058-4059) — doarMONEDA_NATIONALA/NUME_VAL, folosit inCASEca sa decida cum se formateaza afisarea (lei vs. valuta), nu ca sa aduca un pret alternativ.- Expresia finala pe
PRETe o transformare de afisare a luiA.PRET(rotunjire + conversie in valuta folosindA.CURS/A.MULTIPLICATORstocate pe document) plusA.DIFERENTA(coloana proprie peVANZARI_DETALII, un ajustaj deja persistat pe linie, nu o recalculare live) — nu o re-derivare dintr-o sursa externa.
- Acelasi tipar pe
DISCOUNT_UNITAR,PROC_TVAV,ID_VALUTA,PRET_CU_TVA— toate citite direct dinA1.*(VANZARI_DETALII), fara vreunJOINcatre politica/contract.
Confirmare suplimentara pe partea VFP — cursor_retur_document cu V_COPIERE=1 e chiar calea
folosita la reincarcarea unui document existent, cu prioritate fata de ramura de contract:
COMUN\programe\ofacturare.prg:266-283
Do Case
Case m.llCopiere
lcSqlCursor = [{call pack_facturare.cursor_retur_document(?poDate.in_valuta,?poDate.listaid,1,?poDate.eProforma,?gnIdUtil)}]
Case Inlist(tnTip, 48, 49)
...
Case tnTip = 45
...
Case Inlist(tnTip, 1, 22, 5, 29, 7, 10, 23) && lista de preturi
...
Case Inlist(tnTip, 2, 26, 6, 52) && contract
lcSqlCursor = [{call ]+gcS+[.pack_facturare.cursor_contract(...)}]
Do Case in VFP executa doar prima ramura adevarata. Cand llCopiere e activ (incarcarea unui
document existent — exact scenariul "editare prin regenerare" descris de team-lead), procedura
apelata e intotdeauna cursor_retur_document, indiferent de tnTip — ramura de contract
(cursor_contract, :283+) nu se mai ajunge deloc, chiar daca documentul e de tip 2/6/26/52.
cursor_retur (:3934-3947) e doar un wrapper subtire peste cursor_retur_document cu
V_COPIERE=0/V_PROFORMA=0, pentru returul propriu-zis — nu schimba concluzia.
Consecinta pentru S8b
Presupunerea "re-derivarea e o proprietate a scrierii, nu a citirii" se confirma pe cod, fara
rezerve. Deschiderea formularului de editare (etapa II, incarcarea liniilor existente) nu
declanseaza nicio re-derivare de pret/TVA/valuta — documentul apare "neschimbat" la simpla
deschidere, exact cum a presupus S8b. Mecanismul de detectare a schimbarii (compara starea
incarcata cu starea la confirmare) poate functiona ca premisa — riscul de suprascriere tacita
descris in restul acestui raport (sectiunile 1-6) apare doar la re-scriere (regenerare efectiva
prin adauga_articol_factura), nu la incarcare. Cele doua momente (citire la deschidere, scriere
la regenerare) sunt guvernate de cursoare Oracle complet diferite (cursor_retur_document vs.
adauga_articol_factura), fara cod comun — o modificare facuta doar pe unul din ele (de exemplu o
garda adaugata la scriere, variantele (b)/(c) din sectiunea 4) nu afecteaza comportamentul
celuilalt.
2. Tipurile afectate si frecventa in date
Tipurile de document: pack_facturare.ntip IN (2, 6, 26, 52) (:5039), confirmat identic cu
tabelul din rec_editare_factura.md:176 (contract = tip 2/6/26/52, ramura de scriere
scrie_factura2, la fel ca lista de preturi — nu are RPC separat).
Frecventa in date (schema MARIUSM_AUTO, doar SELECT, 11.08.2026):
-- VANZARI_DETALII cu ID_CTR populat, active: 74
-- din care, articolul are un rand corespunzator in CTR_ARTICOLE (id_ctr+id_articol): 11
-- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI diferit de VANZARI_DETALII.PRET: 3
-- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI egal cu VANZARI_DETALII.PRET: 8
-- din acelea, CTR_ARTICOLE.PRET_UNITAR = 0 (nu suprascrie): 0
Interpretare, cu grija la marimea esantionului: baza e de dezvoltare, cu doar 27 randuri totale
in CTR_ARTICOLE — nu extrapolez procentul (27%) la volumul de productie. Dar 3 cazuri reale,
nu zero, e dovada suficienta ca fenomenul chiar se intampla, nu doar teoretic: daca oricare
din cele 3 facturi ar fi reemisa azi prin regenerare (#13 etapa II), suma ei s-ar schimba tacit,
fara nicio actiune a operatorului legata de pret. Simetric, cele 8 cazuri "egal" arata ca in
majoritatea situatiilor curente reemiterea ar fi azi inofensiva — ceea ce face problema mai
insidioasa, nu mai putin reala: cand se produce, nimeni nu se astepta, pentru ca de obicei nu se
intampla nimic vizibil.
Nu exista coloana de audit pe CTR_ARTICOLE (confirmat din user_tab_columns: ID_CTR_ART, ID_CTR, ID_POL_ART, PRET_UNITAR, CANT, COEF_DISCOUNT, VAL_DISCOUNT, ID_VALUTA, EXPLICATIE, UM, ID_ARTICOL, PRET_CU_TVA, ID_LOCATIA, PROC_TVAV, VALOARE — nicio DATA_MODIF/ID_UTIL_MODIF) —
nu exista cale de a masura cat de des se schimba PRET_UNITAR in timp, doar cate cazuri
divergente exista azi. Raspunsul la "cat de des" ramane nedeterminabil din date; ce se poate
spune e doar ca divergenta exista deja, azi, pe un esantion mic.
3. Descoperire noua: TVA si valuta sunt re-derivate din aceeasi interogare ca pretul
Din citatul de la sectiunea 1: SELECT DECODE(A.PRET_UNITAR, ...), B.PROC_TVAV, B.ID_VALUTA, ...
— un singur SELECT, trei valori. Asta inseamna ca varianta de raspuns nu poate trata pretul
izolat: daca politica de pret a contractului (CRM_POLITICI_PRET_ART, aceeasi B) si-a schimbat
si cota de TVA sau valuta intre timp, reemiterea le suprascrie pe amandoua, prin acelasi
mecanism, in aceeasi conditie (NO_DATA_FOUND → respecta ce a trimis VFP; altfel → suprascrie).
Comparativ cu discountul si cursul (reconfirmare fata de runda 9, sectiunile 6-7 ale raportului vechi, verificate din nou pe sursa curenta):
| Camp | Tratament pe ramura de contract | Risc la reemitere |
|---|---|---|
Pret (V_PRET) |
DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR) — suprascris daca PRET_UNITAR<>0 |
Da — faptul portant |
TVA (V_PROC_TVAV) |
B.PROC_TVAV, din acelasi SELECT, fara fallback conditionat separat |
Da — aceeasi conditie ca pretul |
Valuta articol (V_ID_VALUTA) |
B.ID_VALUTA, idem |
Da — aceeasi conditie |
Discount (V_DISCOUNT_UNITAR) |
Parametru trimis de VFP, scris direct in INSERT (:5266), nicio ramura din CASE il citeste |
Nu — mereu respectat, indiferent de ramura |
Curs (V_CURS) |
DECODE(V_CURS, 0, 1, V_CURS) la INSERT (:5270) — inlocuieste doar 0 cu 1 |
Nu direct — dar vezi mai jos |
Consecinta combinata pret+valuta+curs: daca V_ID_VALUTA re-derivat difera de valuta pentru
care VFP a calculat V_CURS (de ex. politica a trecut de la EUR la USD intre emitere si
reemitere), documentul reemis scrie noua valuta cu vechiul curs trimis de VFP — nicio
validare incrucisata intre cele doua (reconfirmat, :5270 nu verifica V_ID_VALUTA). Aceeasi
observatie ca in runda 9, dar acum cu dovada ca sursa comuna (pret+TVA+valuta) inseamna ca orice
gardă construita pentru pret trebuie sa verifice si TVA si valuta in acelasi pas, nu doar pretul
— altfel o factura reemisa poate trece garda de pret nemodificata si totusi sa iasa cu alta cota
de TVA sau alta valuta.
Discountul nu ridica aceeasi problema — e mereu respectat ca atare, indiferent de ramura, deci nu are nevoie de nicio garda la reemitere.
4. Variantele de raspuns
Toate patru presupun ca infrastructura de "sterge + reemite" din #13 etapa II trimite, pentru
liniile de pe un document-contract, acelasi V_ID_CTR care era pe linia originala (asta e
premisa explicita a sarcinii: "acelasi drum ca la emitere" — daca V_ID_CTR nu s-ar retrimite,
linia nici n-ar mai fi "de pe contract").
(a) Se accepta re-derivarea — nicio garda
Ce se face: nimic — regenerarea apeleaza adauga_articol_factura exact ca la emitere, cu
acelasi V_ID_CTR. Comportamentul existent (sectiunea 1) se aplica neschimbat.
Ce se strica: criteriul din plan ("reemitere identica produce exact aceleasi sume" — vezi sectiunea 6) esueaza silentios pe orice linie de contract al carei pret/TVA/valuta a fost modificat intre timp. Dovedit sa se intample azi, pe date reale (sectiunea 2, 3 din 11). Utilizatorul care corecteaza o cantitate pe o factura veche poate vedea, fara sa i se spuna, totalul facturii schimbat pentru un articol la care nici nu s-a uitat.
Cod atins: zero. Regresie pe emiterea normala: zero (comportamentul de azi ramane identic).
(b) Se blocheaza reemiterea cand pretul curent difera
Ce se face: inainte de a porni stergerea+regenerarea, VFP ruleaza un SELECT (prin
goExecutor, exact tiparul deja folosit in ofacturare.prg pentru alte verificari punctuale —
goExecutor.oSelecteaza2Value(...), :2628, :2661) care compara, pentru fiecare linie a
documentului cu ID_CTR populat, pretul/TVA/valuta stocate pe linie
(VANZARI_DETALII.PRET/PROC_TVAV/ID_VALUTA) fata de ce ar recalcula azi ramura de contract
(CTR_ARTICOLE.PRET_UNITAR/CRM_POLITICI_PRET_ART.PROC_TVAV/.ID_VALUTA, acelasi JOIN ca in
sectiunea 1, dar rulat direct din VFP, fara sa ating pack_facturare). Daca gaseste macar o
divergenta, blocheaza regenerarea cu mesaj ("Pretul de pe contract s-a schimbat pentru
articolul X: -> . Actualizati contractul sau anulati regenerarea.") si nu porneste
deloc stergerea.
Ce se strica: orice regenerare pe un document cu cel putin o linie de contract cu pret schimbat e blocata integral, chiar daca utilizatorul voia sa corecteze cu totul altceva (ruta, o cantitate pe alta linie). Nu exista cale de a "accepta noul pret si continua" fara o a doua interactiune — varianta (c) rezolva exact asta.
Cod atins: doar VFP (o interogare noua, read-only, plus un dialog de blocare), inaintea
apelului la infrastructura de stergere+regenerare din #13. Zero atingere pack_facturare
(decizia 27-bis respectata). Regresie pe emiterea normala: zero — garda ruleaza doar pe
calea de regenerare, nu la emiterea unui document nou.
(c) Se avertizeaza si se cere confirmare, cu diferenta afisata
Ce se face: aceeasi interogare de detectie ca la (b), dar in loc sa blocheze, afiseaza un dialog cu lista liniilor divergente si delta (pret vechi vs. nou, si TVA/valuta daca difera — vezi sectiunea 3, toate trei trebuie aratate, nu doar pretul) si cere confirmare explicita ("Continuati cu noile preturi de pe contract?" Da/Nu). La "Nu", regenerarea se opreste ca la (b). La "Da", regenerarea continua normal si ramura de contract face exact ce face azi (suprascrie).
Ce se strica: nu opreste suprascrierea insasi — doar o face vizibila si asumata inainte sa se produca, exact criteriul cerut de plan ("un rezultat explicit decis, nu accidental"). Nu ofera o cale de a pastra vechiul pret in timp ce se accepta restul modificarii — pentru asta ar trebui combinata cu (d), cu costul ei (vezi mai jos). Risc de "warning fatigue": daca divergenta e frecventa in productie (nu se poate masura azi, sectiunea 2), utilizatorii pot invata sa dea click pe "Da" fara sa citeasca.
Cod atins: identic cu (b) plus un dialog cu doua butoane in loc de unul singur. Regresie pe emiterea normala: zero.
(d) Se ocoleste ramura — reemiterea trimite pretul documentului pe o cale care nu trece prin DECODE
Posibil mecanic, cu un cost real. Ramura de contract se selecteaza din doua conditii, ambele in afara controlului direct al apelantului per-linie:
-
pack_facturare.ntip IN (2,6,26,52)— variabila de sesiune (:1882,pack_facturare.ntip := V_TIP), setata o singura data la initializarea documentului (tipul documentului insusi), nu per linie. A o schimba ar insemna sa reclasifici tot documentul (afecteaza si alte ramuriCASEcare depind dentip—:1905,:5053,:6056-6124,:14820— cu efecte necunoscute si necontrolate) — contrazice premisa "acelasi drum ca la emitere" data explicit in sarcina. Nu recomand aceasta sub-varianta. -
V_ID_CTR— parametru per linie, trimis explicit de VFP la fiecare apeladauga_articol_factura. Daca VFP trimiteV_ID_CTR = NULLpentru o linie la reemitere, blocul de la:5039-5050gasesteNO_DATA_FOUND(cautareaWHERE ID_CTR = NULLnu potriveste nimic) →V_OPT_FACTURARE := 4→ ramuraWHEN V_OPT_FACTURARE = 3nu se mai potriveste → cade peELSE(:5187-5203) →V_PRET := V_PRET_TEMP(pretul documentului, respectat ca atare, la fel TVA prinJTVA_COLOANEdupaV_ID_JTVA_COLOANA).Costul:
V_ID_CTRe si coloana scrisa inVANZARI_DETALII_TEMP/VANZARI_DETALII.ID_CTR(:5280, copiata neschimbat mai departe la:13730/:13755). TrimitandNULLla reemitere, linia pierde definitiv legatura cu contractul in tabela finala — orice raport care grupeaza/filtreaza peVANZARI_DETALII.ID_CTR(regasire vanzari pe contract, situatii contractuale) nu va mai gasi acea linie dupa reemitere, desi articolul a fost, de fapt, livrat pe acel contract. E o regresie permanenta si greu de observat (nu da eroare, doar dispare tacit dintr-un raport), pe un camp folosit azi activ (rec_pret_lazy.md/idpol_comanda_contract.mdconfirma ca legatura cu contractul e cheie de raportare in tot lantulPACK_FACTURARE). In plus, o data pierduta legatura, o a doua reemitere ulterioara nu ar mai putea re-deriva pretul din contract nici daca s-ar dori — comutarea e ireversibila per linie, nu un flag comutabil.
Nu recomand varianta (d) — costul (pierderea trasabilitatii contract-linie, permanenta) e mai mare decat problema pe care o rezolva, si nici macar nu rezolva complet criteriul "reemitere identica" (rezolva doar pretul/TVA/valuta acelei linii, cu pretul unei regresii pe alt camp).
5. Discount, TVA, curs valutar — verificare fata de sectiunile 6-7 din raportul vechi
Deja tratat integral in sectiunea 3 de mai sus (descoperirea principala a acestei runde). Rezumat scurt pentru trasabilitate fata de cererea explicita:
- Discountul (sectiunea 6 raport vechi): reconfirmat — niciodata re-derivat, indiferent de ramura. Nu ridica problema de reemitere.
- TVA-ul (sectiunea 6 raport vechi): reconfirmat ca "mereu recalculat, din surse diferite pe
ramura" — dar cu precizarea noua ca pe ramura de contract, sursa coincide exact cu sursa
pretului (acelasi
SELECT, aceeasi conditieNO_DATA_FOUND). Ridica aceeasi problema ca pretul, prin acelasi mecanism — orice varianta (b)/(c) trebuie sa acopere si TVA, nu doar pret. - Cursul valutar (sectiunea 7 raport vechi): reconfirmat —
V_CURSmereu respectat ca atare (DECODE(V_CURS,0,1,V_CURS)), nu se re-deriva direct. Dar identitatea valutei (V_ID_VALUTA) se re-deriva pe aceeasi ramura de contract, din acelasiSELECT— daca politica a schimbat valuta intre timp, reemiterea scrie noua valuta cu vechiul curs trimis de VFP, fara nicio validare incrucisata (reconfirmat,:5270nu verificaV_ID_VALUTA). Ridica aceeasi problema, prin acelasi mecanism, si trebuie acoperita de aceeasi garda.
6. Cazul "reemitere identica"
Criteriul din plan (S12) cere ca o reemitere fara nicio modificare intentionata sa produca
exact aceleasi sume. Verificat pe fiecare ramura a CASE-ului de la :5052-5220 (nu doar
ramura de contract):
Ramura (ntip) |
Garantat identic la reemitere? | De ce |
|---|---|---|
ELSE (lista de preturi, fara contract) |
Da, cu o rezerva minora | V_PRET := V_PRET_TEMP — passthrough direct (:5200). TVA vine din JTVA_COLOANE dupa V_ID_JTVA_COLOANA (:5188-5198) — identic doar daca acea coloana de TVA nu a fost ea insasi editata intre timp (posibil, dar alt tip de risc, nu cel cerut de aceasta sarcina). |
45 (restaurant) |
Da, cu aceeasi rezerva | V_PRET := V_PRET_TEMP setat inainte de orice SELECT (:5109) — respectat necondiționat. TVA vine tot din JTVA_COLOANE (:5114-5139), aceeasi rezerva ca mai sus. |
3,21,28,42,47 (comenzi) |
Nu garantat, dar esueaza tare, nu tacit | SELECT ... WHERE A.PRET = V_PRET_TEMP (:5077) — daca pretul din comanda-sursa nu mai coincide exact cu ce trimite VFP, NO_DATA_FOUND netratat → eroare Oracle bruta, regenerarea pica integral, nu produce o suma gresita silentios. Diferit structural de ramura de contract. |
4 (aviz) |
Nu garantat, silentios | SELECT DISTINCT A.PRET ... FROM VANZARI_DETALII (:5082-5103), fara filtru pe pret — re-citeste pretul curent al liniei din avizul-sursa necondiționat. Daca avizul insusi a fost modificat intre emiterea initiala si reemitere, sau daca exista mai multe randuri candidate (DISTINCT pe o potrivire ambigua), reemiterea poate lua alt pret, tacit — acelasi tipar de risc ca la contract, dar pe alt document-sursa. |
V_OPT_FACTURARE=3 (contract) |
Nu garantat, silentios | Sectiunea 1 — faptul portant. |
Observatie in afara perimetrului cerut, dar direct relevanta: daca #13 etapa II ajunge sa
regenereze si facturi emise initial din aviz (ntip=4), nu doar din contract, acelasi tip
de risc silentios exista si acolo, prin alt mecanism (re-citire necondiționata din
VANZARI_DETALII a avizului-sursa, fara filtru pe pret). Raportul vechi (s10_pret_rederivat.md,
sectiunea 4) semnalase deja asta ca "neverificat, in afara bugetului" — de data asta o marchez
explicit ca decizie deschisa: daca reemiterea din #13 se aplica si documentelor provenite din
aviz, aceeasi discutie (a/b/c/d) trebuie purtata si pentru acea ramura, cu propriul cost (sursa
de comparat e alt document, nu CTR_ARTICOLE). Nu am extins cercetarea de date pe aceasta ramura
(in afara cererii explicite a sarcinii, care a cerut "cazul confirmat", adica cel de contract).
Concluzie sectiune: criteriul "reemitere identica" e garantat azi doar pe ramurile
ELSE/restaurant (fara contract, fara comanda, fara aviz). Pe comenzi, o divergenta produce
eroare vizibila (rau, dar nu tacit). Pe contract si pe aviz, o divergenta produce o suma
gresita fara nicio semnalare — acestea sunt cele doua ramuri care au nevoie de o decizie explicita
de tipul (a)-(d) inainte ca #13 etapa II sa poata pretinde ca satisface criteriul S12.
7. Ce ramane de decis de Marius
- (b) blocare stricta vs. (c) avertizare+confirmare — (c) satisface literal criteriul "decis explicit, nu accidental" cerut de plan si nu intrerupe fluxul quasi-niciodata (doar cand chiar exista divergenta); (b) e mai sigur (niciodata nu se schimba nimic fara interventie manuala pe contract) dar mai frustrant daca divergentele sunt frecvente in productie — lucru nemasurabil azi (sectiunea 2). Recomand (c) ca implicit, cu mesajul aratand explicit delta de pret/TVA/valuta (sectiunea 3) — nu doar pretul.
- Garda trebuie sa acopere pret + TVA + valuta impreuna, nu doar pretul — confirmat la
sectiunea 3 ca vin din acelasi
SELECT. O implementare care verifica doar pretul ar lasa trecerea tacuta a unei schimbari de TVA sau valuta. - Varianta (d) (ocolire) — nu o recomand, din cauza pierderii permanente a legaturii
VANZARI_DETALII.ID_CTR. Daca Marius considera totusi ca merita costul (de ex. daca trasabilitatea contract-linie pe documentele reemise nu conteaza in practica), e o decizie de produs explicita, nu tehnica — semnalez aici, nu decid. - Ramura de aviz (
ntip=4) are acelasi tip de risc silentios (sectiunea 6) — de decis daca intra in perimetrul #13 etapa II acum sau se trateaza separat, cu propriul studiu de fezabilitate pentru garda (sursa de comparat difera fata de contract). - Frecventa reala in productie a divergentei
CTR_ARTICOLE.PRET_UNITARramane nemasurabila din baza de dezvoltare (27 randuri total) — daca decizia (b) vs (c) depinde de cat de des s-ar declansa garda, ar trebui repetata interogarea din sectiunea 2 pe o baza cu volum de productie inainte de implementare.
STARE / CE RAMANE
Livrabil complet — toate cele 6 puncte cerute in brief sunt acoperite (sectiunile 1-6), plus recomandare argumentata (sectiunea 4, cu tabel comparativ) si lista de decizii deschise (sectiunea 7).
Nimic ramas in lucru; nicio verificare intrerupta. Singurele limitari explicite:
- esantionul de date (27 randuri
CTR_ARTICOLE, baza de dezvoltare) nu se extrapoleaza la productie (sectiunea 2); - ramura de aviz (
ntip=4) e semnalata ca avand acelasi tip de risc, dar nu a primit acelasi nivel de cercetare (nu era in perimetrul cerut) — vezi sectiunea 6, ultimul paragraf inainte de concluzie.