Files
roafacturare/docs/cercetare/s10_pret_rederivat.md
2026-09-09 22:19:22 +03:00

24 KiB

S10 — Pretul re-derivat in adauga_articol_factura, ramura cu ramura

Nota pe sursa folosita

docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql nu exista (nici in docs\, nici urme in istoricul git — docs\ e integral netracked). Fisierul cu acelasi nume exista insa la D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql si a fost folosit ca sursa. Continutul adauga_articol_factura coincide structural cu ce descrie planul (aceeasi ordine de ramuri, aceleasi coduri de eroare FACT-012/013/018, acelasi DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)), dar liniile sunt deplasate cu +17 fata de citatele din plan (corp 4989-5284 aici vs. 4972-5268 in plan; ramura de contract 5146-5185 aici vs. :5129-5168 in plan) — probabil diferente de comentarii de antet intre cele doua exporturi. Toate citatele de mai jos sunt pe fisierul din SCRIPTURI_CLAR, verificat direct.

Verdict (raspuns la intrebarea 4 — reemiterea)

Da, exista o ramura care suprascrie tacut pretul la orice apel, inclusiv la o eventuala reemitere: liniile provenite dintr-un contract cu OPT_FACTURARE = 3 (sau NULL, care se implicit-eaza tot la 3). Procedura nu are nicio notiune de "reemitere" — la fiecare apel cu acelasi V_ID_CTR, daca articolul are in CTR_ARTICOLE.PRET_UNITAR o valoare diferita de 0, acea valoare inlocuieste pretul trimis de VFP (V_PRET_TEMP), necontidionat de faptul ca linia a fost sau nu atinsa de utilizator pe ecran. Riscul se materializeaza doar daca pretul din contract s-a schimbat intre emiterea initiala si reemitere — daca n-a scazut, rezultatul e identic si nimeni nu observa diferenta pana cand se schimba. Pe toate celelalte ramuri (implicita, restaurant), pretul trimis de VFP e respectat ca atare. Nu exista niciun parametru sau flag care sa opreasca re-derivarea — raspunsul la intrebarea 5 e "nu exista".

1. Semnatura completa (corp, nu spec)

PACK_FACTURARE:4989-5015 (spec identica la :537-563, verificat doar ca prezenta, nu citat):

PROCEDURE adauga_articol_factura(V_ID_TEMP             IN NUMBER,
                                 V_ID_ARTICOL          IN NUMBER,
                                 V_SERIE               IN VARCHAR2,
                                 V_EXPLICATIE          IN VARCHAR2,
                                 V_ID_POL              IN NUMBER,
                                 V_ID_GESTIUNE         IN NUMBER,
                                 V_PRET_ACHIZITIE_TEMP IN NUMBER,
                                 V_PRETD               IN NUMBER,
                                 V_ID_VALUTAD          IN NUMBER,
                                 V_PRET_TEMP           IN NUMBER,
                                 V_ID_VALUTA_TEMP      IN NUMBER,
                                 V_PRETURI_CU_TVA_TEMP IN NUMBER,
                                 V_IN_STOC_TEMP        IN NUMBER,
                                 V_CANTITATE           IN NUMBER,
                                 V_DISCOUNT_UNITAR     IN NUMBER,
                                 V_CONT                IN VARCHAR2,
                                 V_CURS                IN NUMBER,
                                 V_MULTIPLICATOR       IN NUMBER,
                                 V_ID_JTVA_COLOANA     IN NUMBER,
                                 V_ID_PART_REZ         IN NUMBER,
                                 V_ID_LUCRARE_REZ      IN NUMBER,
                                 V_PRETV_ORIG          IN NUMBER,
                                 V_ID_VANZARE_SET      IN NUMBER,
                                 V_ID_CTR              IN NUMBER,
                                 V_ID_UTIL             IN NUMBER,
                                 V_TAXCODE             IN NUMBER DEFAULT NULL,
                                 V_LOT                 IN VARCHAR2 DEFAULT NULL) IS

Toti parametrii sunt IN, niciunul OUT/IN OUT — nu exista un canal de intoarcere a pretului recalculat catre VFP la momentul apelului (linia se citeste ulterior, la re-interogarea grilei).

V_OPT_FACTURARE nu e parametru. E o variabila locala (:5029), calculata in interiorul procedurii din CONTRACTE.OPT_FACTURARE, folosind parametrul V_ID_CTR primit de la VFP (:5039-5050):

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;

VFP nu trimite si nu poate influenta V_OPT_FACTURARE altfel decat prin V_ID_CTR (poArt.id_ctr, trimis NULL cand linia nu vine de pe contract). Pentru orice pack_facturare.ntip in afara lui (2, 6, 26, 52), V_OPT_FACTURARE ramane neinitializat (blocul IF nu ruleaza deloc) — echivalent cu "nu conteaza", ramura de contract nu se poate nimeri.

2. Ce inseamna V_OPT_FACTURARE si cum se coreleaza cu VFP

Din PACK_FACTURARE (alte proceduri, aceeasi coloana CONTRACTE.OPT_FACTURARE) si din COMUN\clase\ofacturare.vc2 / COMUN\programe\oproceduri_facturare.prg (proprietatea locala poArt.opt_facturare, populata la citirea contractului, in oglinda cu coloana Oracle):

Valoare Tip facturare Dovada
0 (sau lipsa contract) Articol normal, nelegat de contract ofacturare.vc2:13055 If poArticol.opt_facturare = 0
1, 2 Rata (scadentar contract, CTR_SCADENTAR) — linia nu trece prin adauga_articol_factura, ci prin adauga_rata_factura (ofacturare.vc2:14041-14050) ofacturare.vc2:14041 If Inlist(poArt.opt_facturare,1,2); PACK_FACTURARE:2898,2915 (CTR_SCADENTAR, doar pt. OPT_FACTURARE IN (1,2))
3 Articol de pe contract, pret din CTR_ARTICOLE PACK_FACTURARE:2699,2820 (CTR_ARTICOLE doar pt. OPT_FACTURARE = 3); ofacturare.vc2:14750 (poDate.tip = 2 And poArticol.opt_facturare = 3)
4 Fallback intern cand contractul nu (mai) exista (NO_DATA_FOUND) PACK_FACTURARE:5047-5048

VFP nu trimite V_OPT_FACTURARE ca parametru al adauga_articol_factura — coreleaza doar indirect, prin faptul ca proprietatea locala poArt.opt_facturare (citita din acelasi CONTRACTE.OPT_FACTURARE cand grila s-a populat) decide ce RPC se apeleaza (adauga_rata_factura pentru 1/2, adauga_articol_factura pentru orice altceva, inclusiv 3), nu ce ramura Oracle se executa in interior — aceea o recalculeaza serverul singur, din V_ID_CTR.

3. Ramura cu ramura pe V_OPT_FACTURARE (si pe pack_facturare.ntip, care are prioritate)

CASE la PACK_FACTURARE:5052-5220. Ramurile 1-3 sunt selectate dupa pack_facturare.ntip, nu dupa V_OPT_FACTURARE — ramura 4 (contract) se testeaza doar daca niciuna din primele trei nu s-a potrivit.

Selector Tip facturare Pretul Sursa re-derivarii Citat
ntip IN (3,21,28,42,47) Facturare/aviz din comenzi Re-derivat, dar constrans sa se potriveasca cu ce a trimis VFP — SELECT A.PRET ... WHERE A.PRET = V_PRET_TEMP COMENZI_ELEMENTE + CRM_POLITICI_PRETURI/PRET_ART, filtrat pe A.PRET = V_PRET_TEMP; daca nu exista rand cu exact acel pret, NO_DATA_FOUND nu e tratat — procedura pica cu eroare Oracle netratata :5053-5078
ntip = 4 Facturare din avize Re-derivat necondiționat, din documentul sursa — SELECT DISTINCT A.PRET ... INTO V_PRET fara filtru pe pretul trimis VANZARI_DETALII (linia din avizul sursa), filtrata pe articol/politica/gestiune/discount/cont, dar nu pe pret :5080-5103
ntip = 45 Facturare restaurant Respectat — V_PRET := V_PRET_TEMP setat inainte de SELECT (:5109); SELECT-ul recalculeaza doar V_PROC_TVAV si V_PRET_ACHIZITIE (cost, nu pret de vanzare) n/a pentru pret; TVA din JTVA_COLOANE, cost din CRM_POLITICI_PRET_ART.PRETFTVA :5104-5145
V_OPT_FACTURARE = 3 (deci ntip IN (2,6,26,52) si contract cu OPT_FACTURARE=3/NULL) Facturare/aviz de pe contract Re-derivat daca contractul are pret setat — DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR); daca PRET_UNITAR = 0 sau nu exista rand CTR_ARTICOLE/politica potrivita (NO_DATA_FOUND), cade pe V_PRET_TEMP CTR_ARTICOLE.PRET_UNITAR (prin CRM_POLITICI_PRET_ART, ID_POL_ART) :5146-5185 (branch), :5181-5184 (fallback V_PRET_TEMP in exceptie)
ELSE (implicit — orice altceva, inclusiv linii normale fara contract) Standard Respectat — V_PRET := V_PRET_TEMP n/a; TVA din JTVA_COLOANE dupa V_ID_JTVA_COLOANA trimis de VFP :5187-5203

Observatie de prioritate: daca o linie e simultan "din comanda" (ntip IN (3,21,28,42,47)) si are si V_ID_CTR completat, ramura de comenzi castiga — V_OPT_FACTURARE nici nu se calculeaza (blocul de la :5039 ruleaza doar pentru ntip IN (2,6,26,52)). Ramurile nu se pot suprapune in productie curenta, dar asta inseamna ca orice extindere viitoare a lui ntip (de ex. daca #13 introduce un tip nou de document care refoloseste adauga_articol_factura pentru reemitere) trebuie verificata explicit fata de acest CASE — un ntip nou care nu intra in niciuna din listele (3,21,28,42,47) / 4 / 45 cade automat fie pe ramura de contract (daca are V_ID_CTR), fie pe ELSE.

4. Reemiterea — detaliu (vezi si verdictul de mai sus)

Procedura nu primeste si nu poate primi vreun semnal de tip "acesta e un apel de reemitere, nu recalcula". Comportamentul e identic la emiterea initiala si la orice apel ulterior cu aceiasi parametri. Riscul descris in plan (sectiunea H) se confirma integral pentru ramura de contract (V_OPT_FACTURARE = 3): daca CTR_ARTICOLE.PRET_UNITAR s-a modificat intre emiterea initiala si reemitere, factura reemisa va lua pretul curent din contract, nu pretul confirmat pe ecran de utilizator inainte de reemitere — chiar daca utilizatorul n-a atins linia.

Pentru ramurile "avize" (ntip = 4) si "comenzi" (ntip IN (3,21,28,42,47)), acelasi risc structural exista (pretul se re-citeste din documentul sursa la fiecare apel), dar nu sunt cazul confirmat de #13 — planul S10 spune explicit "se verifica pe factura din contract, care e cazul confirmat" — nu s-a cerut si nu s-a facut inventar suplimentar pentru daca fluxul de reemitere din #13 (etapa II, stergere+regenerare cu aceleasi linii) ar trece vreodata prin aceste doua ramuri. De retinut daca #13 extinde reemiterea si la facturi/avize provenite din comanda sau din alt aviz — pe ambele, re-derivarea e chiar mai stricta decat pe contract (ramura de comenzi cade cu eroare netratata daca pretul nu se potriveste exact; ramura de avize suprascrie necondiționat).

5. Mecanism de "nu re-deriva"

Nu exista. Niciun parametru boolean, nicio valoare santinela, nicio ramura in CASE (:5052-5220) care sa respecte pretul primit pentru ca i s-a cerut explicit. Ramurile "implicita" si "restaurant" respecta pretul doar pentru ca structural nu au de unde re-deriva altceva (nu exista document sursa de recitit), nu pentru ca ar exista o comutare intentionata. Orice solutie de tip "pretul vine din formular, nu se re-deriva" (cum sugereaza planul, sectiunea H) ar trebui implementata in VFP inainte de apel (de exemplu, nu retrimite V_ID_CTR la reemitere daca vrei sa eviti ramura de contract) — nu exista un parametru in pachet pe care VFP l-ar putea seta ca sa dezactiveze re-derivarea, iar pachetul nu se modifica (decizia 27-bis, respectata — aceasta e o constatare, nu o propunere).

6. Discountul si TVA-ul

  • Discountul (V_DISCOUNT_UNITAR) nu e niciodata re-derivat. Parametrul intra direct in INSERT INTO VANZARI_DETALII_TEMP (..., DISCOUNT_UNITAR, ...) VALUES (..., V_DISCOUNT_UNITAR, ...) (:5236, :5266) — nicio ramura din CASE il citeste sau il modifica. Tratament diferit de pret: discountul e mereu respectat, indiferent de ramura.
  • TVA-ul (V_PROC_TVAV) e mereu recalculat server-side, pe toate ramurile — dar din surse diferite, nu dintr-un parametru brut. Nu exista parametru V_PROC_TVAV_TEMP in semnatura; VFP trimite doar V_ID_JTVA_COLOANA (un identificator de coloana de cota, nu cota insasi). Pe ramura implicita si pe cea de contract-fara-potrivire, cota se cauta in JTVA_COLOANE dupa acel ID (:5188-5198, :5169-5179); pe ramurile comenzi/avize/contract-cu-potrivire/restaurant, cota vine din documentul sursa sau din politica de pret (:5058, :5083, :5150, :5114). Deci TVA-ul are un tratament mai strict decat pretul: niciodata "pass-through" direct, intotdeauna o cautare, doar sursa cautarii difera pe ramura.

7. Cursul valutar si pretul in valuta

  • Cursul (V_CURS) e mereu respectat ca valoare, pe toate ramurile. Singura procesare e DECODE(V_CURS, 0, 1, V_CURS) la INSERT (:5270) — inlocuieste 0 cu 1, altfel scrie exact ce a trimis VFP. Procedura nu interogheaza niciodata tabela CURS pentru un curs "de azi" — nu are de unde sa recalculeze cursul chiar daca ar vrea.
  • Identitatea valutei (V_ID_VALUTA, variabila locala, nu parametrul V_ID_VALUTAD) urmeaza acelasi tipar ca pretul, ramura cu ramura: re-derivata din sursa pe comenzi (C.ID_VALUTA, :5059) si avize (A.ID_VALUTA, :5084), re-derivata din contract pe ramura V_OPT_FACTURARE=3 (B.ID_VALUTA, :5151, cu fallback V_ID_VALUTA_TEMP in exceptie, :5182), respectata pe implicita si restaurant (V_ID_VALUTA := V_ID_VALUTA_TEMP, :5110, :5201).
  • Consecinta pentru reemiterea unui document in valuta pe contract: daca politica de pret a contractului (CRM_POLITICI_PRET_ART, prin CTR_ARTICOLE.ID_POL_ART) a fost schimbata intre timp la o alta valuta, reemiterea ar scrie articolul in noua valuta a politicii, dar cu cursul trimis de VFP (posibil cursul valutei vechi, daca VFP nu a fost actualizat sa retrimita cursul corect pentru noua valuta) — o sursa suplimentara de neconcordanta, nesemnalata explicit in plan. Nu s-a gasit nicio validare in procedura care sa verifice ca V_CURS corespunde valutei re-derivate V_ID_VALUTA.

Completare: nota contabila a politicii

Sursa: aceeasi, contabilizeaza_articol la PACK_FACTURARE:7173-7547 (in fisierul din SCRIPTURI_CLAR; corpul relevant pentru articol simplu — nu compus — e la :7391-7544), plus scrie_nota la :12329-12561.

1. Cum ajunge de la id_pol la nota contabila — ia toate randurile setului, fara distributie

cursor_articol (:7218-7271):

FROM CRM_POLITICI_PRET_ART A
LEFT JOIN CRM_POLITICI_PRETURI B ON A.ID_POL = B.ID_POL
LEFT JOIN CRM_NOTE_VANZARI C ON B.ID_NOTA = C.ID_NOTA
LEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SET
WHERE A.ID_POL = detalii_articol.id_pol AND A.ID_ARTICOL = detalii_articol.id_articol

E un JOIN simplu pe ID_SET, fara ROWNUM, fara agregare, fara filtru suplimentar pe NOTE_CONTABILE. Daca setul are N randuri, cursorul intoarce N randuri pentru aceeasi combinatie id_pol/id_articol. Bucla care il consuma (:7393-7542, OPEN cursor_articol; FETCH ...; WHILE cursor_articol%FOUND LOOP ... FETCH ...; END LOOP;) executa intregul corp — scrie_nota, descarca_gestiune, scrie_discount — o data pentru fiecare rand din set, cu aceeasi cantitate si acelasi pret intreg de fiecare data, nu impartite intre randuri. Nu exista nicio coloana ORDINE in tot pachetul (cautare ORDINE in fisier: zero rezultate) si nicio logica de distributie procentuala intre randurile unui set.

Consecinta directa pentru reteta aprobata (J-quater): daca politica tehnica pentru contul de venit ar ajunge sa foloseasca o nota al carei ID_SET are mai mult de un rand in NOTE_CONTABILE (cazul general — pana la 30 de randuri pe unele seturi din baza, per verificarea ta pe date vii), contabilizeaza_articol ar scrie de N ori aceeasi suma in contabilitate si ar apela descarca_gestiune de N ori pentru aceeasi linie de vanzare — dublare de venit si dublare de descarcare de gestiune, nu doar zgomot. Pasul 2 al retetei ("cauta o politica a carei nota are deja SCC-ul calculat") trebuie sa garanteze un singur rand NOTE_CONTABILE per ID_SET ales, nu doar un rand cu SCC-ul potrivit — verificarea facuta de tine pe cele 7 note active (exact un rand per set) e conditia care face reteta sigura azi, dar nu e impusa de cod, e o coincidenta a datelor curente.

2. NOTE_CONTABILE.PTVA — nu e citit deloc de contabilizeaza_articol

Lista de coloane a cursor_articol (:7230-7260) selecteaza explicit ID_NOTA, PRETURI_CU_TVA, ID_VENCHELT, ID_SECTIE, ID_SET, EXPLICATIE, SCD, ASCD, SCC, ASCC, CU_TVA, IN_VALUTA din lantul CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE — D.PTVA nu apare in acest SELECT. Coloana exista pe tabel (confirmat de datele tale vii — 21, 5, 0), dar contabilizeaza_articol n-o citeste niciodata.

Cota de TVA folosita efectiv in scriere e alta — vine din apelul catre scrie_nota (:7444-7467), unde parametrul V_PTVA primeste detalii_articol.proc_tvav * 100 - 100 (:7464), adica cota calculata deja in adauga_articol_factura (din JTVA_COLOANE sau din documentul sursa, vezi sectiunea 3 de mai sus), nu din nota. Deci raspunsul direct la intrebarea ta: daca articolul are 21% dar nota politicii are PTVA=5, nu se intampla nimic — PTVA de pe nota e complet ignorat, cota folosita e cea a articolului/documentului, nu a notei. Coloana NOTE_CONTABILE.PTVA e moarta din perspectiva acestei proceduri (posibil folosita in alt modul, neverificat aici).

3. CU_TVA si IN_VALUTA de pe nota

Ambele se citesc din NOTE_CONTABILE (crs_rand_articol.cu_tva, crs_rand_articol.in_valuta, :7253, :7254-7260) si se transmit mai departe la scrie_nota ca parametri (V_CU_TVA, V_IN_VALUTA), unde controleaza mecanic, nu semantic tot ce e legat de pret:

  • V_CU_TVA (scrie_nota:12416-12422): daca = 0, suma acumulata in V_INCASAT_CALCUL e V_SUMA_FARA_TVA; daca = 1, e V_SUMA_CU_TVA. Separat, la :12537-12558, doar cand V_CU_TVA = 1 se scrie si o linie separata de TVA (pack_facturare.scrie_tva(...), cont 4427/etc.). Deci CU_TVA decide daca acest rand din notă genereaza o inregistrare contabila separata pentru TVA sau nu — nu suprascrie si nu recalculeaza cota, doar comuta daca linia de TVA se scrie.
  • V_IN_VALUTA (scrie_nota:12391-12404, :12492-12507): daca = 1, se calculeaza si V_SUMA_VAL/V_SUMA_FARA_TVA_VAL/etc. (sumele in valuta straina) si randul ACT_TEMP scrie ID_VALUTA = V_ID_VALUTA (valuta reala a articolului) si CURS = V_CURS; daca = 0, randul scrie ID_VALUTA = pack_facturare.nid_moneda_nationala si CURS = 0, indiferent de valuta reala a articolului. Raspuns la "ce se intampla daca IN_VALUTA=1 pe nota dar documentul e in lei": nu apare nicio eroare si nicio validare incrucisata intre IN_VALUTA de pe nota si valuta reala a documentului — procedura calculeaza pur si simplu V_SUMA_VAL folosind V_ID_VALUTA si V_CURS primite din detalii_articol (care, pe un document in lei, sunt deja moneda nationala si curs implicit 1). Rezultatul practic: ACT_TEMP.SUMA_VAL se populeaza redundant (cu aceeasi valoare ca SUMA, scalata la curs 1) in loc sa ramana 0, iar ID_VALUTA de pe randul contabil ramane oricum moneda nationala (pentru ca asta e detalii_articol.id_valuta pe un document in lei) — o inconsistenta cosmetica in ACT_TEMP (rand cu SUMA_VAL populat desi CURS real e 1), nu o eroare de suma. Relevant pentru politicile SERVICII VALUTA / COMISION INTERMEDIERE (SCC=704, candidate la pasul 2 al retetei): daca oricare document in lei ar ajunge sa foloseasca una din ele, ar produce astfel de randuri cosmetic-inconsistente, fara sa strice suma facturata.

4. ASCD/ASCC si ID_PARTD/ID_PARTC

  • ASCD/ASCC nu sunt obligatorii — au fallback automat cand sunt nule, exact pe ramura care se aplica pe facturi (:7409-7428):
    V_ASCD := NVL(crs_rand_articol.ascd,
                  PACK_FACTURARE.GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCD));
    ...
    V_ASCC := NVL(crs_rand_articol.ascc,
                  PACK_FACTURARE.GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCC));
    
    Cand nota nu are analitic explicit (cazul majoritatii notelor reale, per verificarea ta), se deriva unul din grupul de utilizatori al celui care factureaza, functie de contul SCD/SCC. Nu s-a verificat in aceasta sesiune ce intoarce GetAnaliticByGrupUtilizatori cand nici grupul de utilizatori nu are un analitic configurat (posibil NULL mai departe, netratat explicit aici).
  • ID_PARTD/ID_PARTC nu sunt citite de la nota deloc. cursor_articol nu le selecteaza (nu exista D.ID_PARTD/D.ID_PARTC nicaieri in fisier — verificat). Cele doua coloane de pe ACT_TEMP se calculeaza in scrie_nota/scrie_tva direct din codul contului (V_SCD, V_SCC) si din variabile de sesiune ale pachetului (pack_facturare.nid_part, nid_part_rez, nid_partc), nu din nota: ID_PARTD e un CASE pe prefixul lui V_SCD (41%/46%/45%/357) la :12510-12515; ID_PARTC e un DECODE pe V_SCC (419/4111/357) la :12523-12530. Daca NOTE_CONTABILE are coloane ID_PARTD/ID_PARTC, ele sunt irelevante pentru contabilizeaza_articol — partenerul contabil vine intotdeauna din contextul documentului (pack_facturare.nid_part), nu din configurarea notei.

5. Politica fara ID_NOTA (HOTEL TAXE, HOTEL CAZARE)

Nu exista un cod FACT-0xx dedicat pentru acest caz — spre deosebire de articolul care lipseste din politica (FACT-024, :7278-7302), aici procedura nu detecteaza si nu semnaleaza explicit situatia. Lantul de LEFT JOIN din cursor_articol (:7261-7271) e construit sa supravietuiasca oricarui inel lipsa: A (politica-articol, gasita — altfel s-ar fi luat deja FACT-024 mai devreme) se pastreaza chiar daca B.ID_NOTA e NULL — C si D devin pur si simplu toate NULL pe acel rand, cursorul tot intoarce un rand (cursor_articol%FOUND = TRUE), nu zero.

In bucla (:7396-7541), asta inseamna:

  • crs_rand_articol.scd, .ascd, .scc, .ascc, .cu_tva, .in_valuta, .explicatie — toate NULL.
  • Ramura de calcul V_ASCD := NVL(NULL, GetAnaliticByGrupUtilizatori(nid_util, NULL)) — analitic calculat pe un cont NULL, deci probabil tot NULL (netestat aici ce intoarce functia pe argument NULL).
  • scrie_nota e apelata cu V_SCD = NULL, V_SCC = NULL — insereaza in ACT_TEMP un rand cu conturile de debit/credit nule.

Daca ACT_TEMP.SCD/ACT_TEMP.SCC au constrangere NOT NULL pe schema, INSERT-ul ar pica cu o eroare Oracle generica (ORA-01400), nu cu un mesaj FACT-0xx prietenos ca la celelalte cazuri tratate explicit — neverificat in aceasta sesiune (DDL-ul ACT_TEMP nu e in exportul PACK_FACTURARE). In orice caz, comportamentul e cel putin la fel de riscant ca lipsa unui articol din politica, dar fara plasa de siguranta explicita din cod — de tratat cu atentie daca reteta aprobata ajunge sa produca vreodata o politica fara id_nota (nu pare sa fie cazul retetei J-quater, care cere explicit gasirea unei note existente, dar HOTEL TAXE/HOTEL CAZARE arata ca starea "politica fara nota" chiar exista azi in baza vie, pe alte fluxuri).

Ce nu s-a putut stabili si de ce

  • Comportamentul exact al viitorului flux de reemitere din #13 (etapa II) fata de aceasta procedura — planul S10 cere verificarea "pe factura din contract", nu inventarul complet pentru comenzi/avize; nu s-a extins cercetarea acolo (in afara observatiei de la punctul 4, care ramane o constatare structurala, nu un verdict testat pe fluxul de reemitere, care inca nu exista in cod — decizia 30, nimic implementat).
  • Ce se intampla la eroarea netratata din ramura de comenzi (NO_DATA_FOUND cand A.PRET = V_PRET_TEMP nu gaseste rand, :5057-5078) — daca propaga ca eroare Oracle bruta pana la utilizator sau daca goExecutor o intercepteaza generic; n-a fost verificat (nu era in perimetrul celor 7 intrebari, dar e un risc adiacent gasit din citirea codului).
  • Cate contracte reale au azi CTR_ARTICOLE.PRET_UNITAR diferit de pretul ultimei facturi emise — verificare pe date vii, nefacuta (cercetare read-only, fara acces la interogari live in aceasta sesiune).
  • Discrepanta de numerotare fata de docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql (fisierul cerut in prompt nu exista pe disc) — semnalata la inceputul raportului, nu blocheaza verdictul pentru ca fisierul gasit in SCRIPTURI_CLAR are acelasi nume/data si continut structural identic.