# Nota contabila pentru articol fara politica de pret (proiect #13) Cercetare pe cod, fara modificari. Continua raportul anterior `cont_venit_corespondente.md` (SCC vine prin lantul `CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI.ID_NOTA -> CRM_NOTE_VANZARI.ID_SET -> NOTE_CONTABILE`, citit de `cursor_articol` din `pack_facturare.contabilizeaza_articol`). Sursa: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` (pachet `pack_facturare`, 17020 linii) si `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg` + `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql` (pachet `pack_auto`). --- ## 1. Ce face `contabilizeaza_articol` cand `id_pol` e NULL/0 Raspuns: **ridica exceptia FACT-024 si intreruce functia, inainte sa ajunga la cursorul care citeste SCC**. Nu exista o ramura de default si nu se scrie nota cu SCC NULL pentru acest caz. Corpul complet e la `ff_...:7182-7556`. Primul lucru pe care il face (inainte de `cursor_articol`, inainte de orice `scrie_nota`) e: ``` ff_...:7286-7311 BEGIN SELECT COMPUS, ID_POL_ART INTO V_COMPUS, V_ID_POL_ART FROM VCRM_POLITICI_PRET_ART WHERE ID_ARTICOL = detalii_articol.id_articol AND ID_POL = detalii_articol.id_pol; EXCEPTION WHEN NO_DATA_FOUND THEN SELECT DENUMIRE INTO lcArticol FROM NOM_ARTICOLE WHERE id_articol = detalii_articol.id_articol; SELECT NUME_LISTA_PRETURI INTO lcPolitica FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol; RAISE_APPLICATION_ERROR(-20000, 'Articolul ' || detalii_articol.id_articol || '|' || lcArticol || ' nu este definit in politica de preturi ' || detalii_articol.id_pol || '|' || lcPolitica || '! (FACT-024)'); END; ``` `ID_POL = detalii_articol.id_pol` cu `id_pol` NULL nu poate potrivi niciun rand in SQL standard (`X = NULL` e necunoscut, nu adevarat) — deci pentru `id_pol` NULL sau 0 (presupunand ca nu exista o politica cu `ID_POL = 0`), `SELECT INTO` intra mereu pe `NO_DATA_FOUND`. **Observatie suplimentara, neceruta explicit dar relevanta**: in ramura de exceptie, al doilea `SELECT ... INTO lcPolitica FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol` sufera de aceeasi problema — cu `id_pol` NULL, si acest `SELECT INTO` da tot `NO_DATA_FOUND`, care **nu e tratat** in acest bloc (nu exista un al doilea handler). Deci mesajul FACT-024 "curat" (cu numele politicii) nu s-ar afisa niciodata pentru cazul `id_pol IS NULL` — s-ar propaga o exceptie `NO_DATA_FOUND` neprinsa din interiorul handler-ului, cu alt mesaj Oracle generic, nu FACT-024 cu text. Codul articolului (`lcArticol`) apuca sa fie rezolvat inaintea acestui eșec, deci identificarea articolului nu s-ar pierde in log/eroare, dar textul final ar fi ORA-01403, nu mesajul FACT-024 formatat. Pentru `id_pol = 0` (nu NULL), daca exista vreo politica cu id 0, acel `SELECT` ar reusi si mesajul FACT-024 ar iesi corect. Cursorul `cursor_articol` (care citeste SCD/ASCD/SCC/ASCC din `NOTE_CONTABILE` prin lant) **nu se mai deschide** — funcția a ridicat deja exceptia mai sus. Deci intrebarea "cursorul intoarce zero randuri, ce face codul mai departe" nu se pune in acest caz: nu ajunge acolo. Concluzie Q1: **orice linie de vanzare cu `id_pol` NULL/0 trimisa la `contabilizeaza_articol` in starea actuala a codului arunca o eroare si opreste tranzactia** (nu scrie nota cu cont NULL, nu pune default). Cerinta proiectului #13 ("articol din nomenclator fara politica de pret, pret tastat manual, pe orice tip de document") ar lovi FACT-024 (sau exceptia neprinsa descrisa mai sus) daca linia respectiva e trecuta prin acest cod neschimbat. --- ## 2. Validare la `scrie_nota` pentru cont NULL Raspuns: **nu exista**. Corpul complet e la `ff_...:12338-12570`. - Singura verificare care ar fi atins asta e comentata: ``` ff_...:12441-12453 /* IF V_SUMA IS NULL THEN RAISE_APPLICATION_ERROR(...) ... END IF;*/ ``` Si oricum verifica `V_SUMA` (suma calculata), nu `V_SCD`/`V_SCC`. - `INSERT INTO ACT_TEMP (... SCD, ASCD, SCC, ASCC ...)` (`ff_...:12458-12544`) insereaza direct `V_SCD`/`V_ASCD`/`V_SCC`/`V_ASCC` primite ca parametri, fara niciun `NVL`/`CASE`/verificare de NULL pe ele. - Nu exista `NOT NULL` verificat in cod pentru aceste coloane in acest fisier (constrangerea ar fi la nivel de DDL al tabelei `ACT`/`ACT_TEMP`, care nu face parte din pachetul PL/SQL cercetat — vezi sectiunea Neverificat). Deci daca cineva ar ocoli blocul de la punctul 1 (de ex. printr-un `id_pol` care *exista* in `CRM_POLITICI_PRET_ART` dar al carui lant `CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` e incomplet, asa incat `D.SCC` iese NULL din LEFT JOIN), `scrie_nota` ar scrie nota cu `SCC = NULL` fara sa protesteze. Asta e insa un scenariu diferit de "fara politica de pret" (aici politica exista, dar lantul de sub ea e incomplet) — nu e cazul cerut de proiectul #13 asa cum a fost formulat. --- ## 3. Toti apelantii lui `contabilizeaza_articol` in fisier Cautare exhaustiva `Grep 'contabilizeaza_articol'` pe tot fisierul: 3 apeluri (in afara de propria definitie si un comentariu): | Linie | Procedura apelanta | Domeniu / flux | |---|---|---| | `ff_...:6150` | `scrie_factura2` (corp `ff_...:6029-6272`) | **Fluxul principal de scriere** — pentru orice document ale carui randuri nu cad in ramurile speciale ale `CASE`-ului de la `ff_...:6081-6151`: transfer intre subunitati (`ntip IN (23,25,30,41)`), factura/aviz custodie (`ntip IN (42,47)`), rata cu `id_rata<>0` (`ntip IN (2,6,52)`). Toate celelalte tipuri — **factura normala** (`ntip<=20`), hotel, restaurant, nota de plata, vanzare retail, tipurile 48/49/51/52, aviz simplu, aviz catre clienti debitori, si **aviz retur (tip 24)** cand e scris prin `scrie_factura2` — ajung la `contabilizeaza_articol` aici. Aceasta e apelarea "de baza" pe care se sprijina interpretarea corpului functiei facuta la punctul 1 (ramurile `CASE` din interiorul `contabilizeaza_articol`, `ff_...:7407-7432`, mapeaza direct pe tipurile de document tratate de acest apelant). | | `ff_...:6867` | `scrie_factura_avize_retur` (corp `ff_...:6273-6666`) | Flux **factura scrisa pe baza de avize + retur simultan**. Apelul e in interiorul buclei pe `articole_aviz` (`ff_...:6841-6960` zona), pentru randurile cu `custodie = 1` (marfa in custodie ce se descarca din avize). | | `ff_...:7149` | `scrie_aviz_retur` (corp `ff_...:7094-7180`) | Procedura dedicata **aviz retur** (`V_ID_DELEGAT, V_ID_MASINA, ...`). Apelul e in ramura `ELSE` a testului `pack_facturare.nfactavizcust = 1` (`ff_...:7124-7150`) — deci pentru randurile care **nu** sunt in custodie. | Concluzie Q3: **da**, `contabilizeaza_articol` e apelata pe fluxul principal de facturare (`scrie_factura2`, care acopera factura normala si majoritatea celorlalte tipuri de document), nu doar din `scrie_aviz_retur`. Raportul anterior lasase asta neverificat; e confirmat aici cu cele 3 puncte de apel gasite si citate mai sus — nu exista un al 4-lea apel in fisier. --- ## 4. Precedentul ROAAUTO "Alte servicii" — **nu e de fapt un precedent pentru cazul cerut** Asta contrazice premisa din cerere. Firul (`crsalteserv -> crsvanztemp -> adauga_articol_factura_deviz`) duce spre un flux care **ocoleste complet `contabilizeaza_articol`**, deci nu demonstreaza ce se intampla in `pack_facturare` pentru un articol fara politica — demonstreaza doar ca exista un *alt* mecanism de contabilizare, in afara pachetului `pack_facturare`. Pasii, cu citate: 1. **`crsalteserv -> crsvanztemp`** (`D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1040-1041`): ``` Insert Into (lcCursorDeviz)(id_articol,denumire,explicatie,cantitate,um,id_jtva_coloana,proc_Tvav,taxcode,pret,discount_unitar) ; Select id_articol,denumire,"",cantitate,um,CAST(lnIdJTvaColoana as N(6) null) as id_jtva_coloana,CAST(lnProcTvav as N(7,2) null) as proc_tvav,CAST(lnTaxCode as N(6) null) as taxcode,pretftva,0 From crsalteserv Where !Deleted() ``` Nu exista coloana `id_pol` in `crsalteserv`, in `lcCursorDeviz`, sau in cursorul `crsvanztemp` creat la `oproceduri_devize.prg:1190-1192` (schema explicita a cursorului nu include `id_pol`). 2. **`crsvanztemp -> adauga_articol_factura_deviz`** (`oproceduri_devize.prg:1240-1257`): apelul RPC trimite `id_articol, explicatie, serie, pret_achizitie, pretd, id_valuta_d, pret, id_valuta, curs, multiplicator, proc_tvav, id_jtva_coloana, cantitate, discount_unitar, id_gestiune, cont, pret_cu_tva, NULL, NULL, taxcode` — **fara `id_pol`**, pentru ca procedura insasi nu are un parametru `id_pol`. 3. **Semnatura `adauga_articol_factura_deviz`** (spec `ff_...:468-488`, corp `ff_...:4675-4745`): nu exista `V_ID_POL IN NUMBER` printre parametri. `INSERT INTO VANZARI_DETALII_TEMP` la `ff_...:4697-4744` **nu include coloana `ID_POL`** in lista de coloane inserate — deci `ID_POL` ramane pe valoarea implicita a coloanei (probabil NULL; DDL-ul tabelei nu face parte din pachetul cercetat, vezi Neverificat). **Asta confirma partea (a) a intrebarii: da, liniile "alte servicii" ajung cu `id_pol` gol** in `VANZARI_DETALII_TEMP`. 4. **Dar acele randuri nu trec niciodata prin `contabilizeaza_articol`.** Apelantul din `oproceduri_devize.prg` nu cheama `scrie_factura2` / `scrie_factura_avize` / `scrie_aviz_retur` (singurele proceduri care cheama `contabilizeaza_articol`, vezi punctul 3). In schimb cheama, in ordine (`oproceduri_devize.prg:1211-1310`): - `pack_facturare.initializeaza_date_factura(...)` - bucla `pack_facturare.adauga_articol_factura_deviz(...)` (doar insert in `VANZARI_DETALII_TEMP`) - opțional `pack_facturare.scrie_incasari(...)` (incasare, nu venit din vanzare) - `pack_facturare.scrie_in_vanzari(0, ...)` (`ff_...:13497-13962`) - `pack_auto.actualizeaza_deviz(...)` (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) **`scrie_in_vanzari` nu scrie nici o nota contabila de venit.** Corpul complet (`ff_...:13497-13962`) face: `INSERT INTO VANZARI` (antetul documentului), `scrie_cursuri`, `scrie_seturi`, apoi ``` ff_...:13714-13766 INSERT /*+ APPEND */ INTO VANZARI_DETALII (..., ID_POL, ...) SELECT ..., ID_POL, ... FROM VANZARI_DETALII_TEMP; ``` (copiaza `ID_POL` — care e NULL pentru aceste randuri — direct in `VANZARI_DETALII`, fara nicio validare), si la final actualizeaza totalurile cache pe `VANZARI`. **Nu apare niciun apel catre `contabilizeaza_articol`, `scrie_nota`, `cumuleaza_note_act` sau `finalizeaza_factura` in acest corp** (verificat citind procedura cap-coada). `pack_auto.actualizeaza_deviz` (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) face doar: ``` SELECT MAX(ID_FACT) INTO lnIdFact FROM ACT WHERE COD = pack_contafin.get_cod() AND SCD NOT LIKE '5%'; UPDATE DEV_ORDL SET PROC_TVAV = ... WHERE ID_ORDL IN (...); UPDATE RUL SET ID_FACT = lnIdFact WHERE ...; UPDATE NOM_LUCRARI SET ID_FACT = lnIdFact WHERE ... (doar pt anumite id_set); ``` Adica **presupune ca exista deja un rand in `ACT`** (nu `ACT_TEMP`) cu `SCD NOT LIKE '5%'` pentru codul documentului curent, si doar propaga `ID_FACT` gasit acolo spre `DEV_ORDL`/`RUL`/`NOM_LUCRARI`. Nu insereaza el insusi in `ACT` sau `NOTE_CONTABILE`, si nu contine nicio referinta la `SCC`, `NOTE_CONTABILE` sau `CRM_POLITICI*` (cautat explicit, zero rezultate in acest fisier). **Concluzie Q4, corectand premisa din cerere**: liniile "Alte servicii" chiar ajung cu `id_pol` gol (confirmat), dar **nu demonstreaza ca `contabilizeaza_articol` tolereaza `id_pol` gol** — pentru ca nu trec niciodata prin el. Ele demonstreaza doar ca exista deja, pentru fluxul deviz ROAAUTO, o cale de facturare paralela (`scrie_in_vanzari` + `pack_auto.actualizeaza_deviz`) care **nu genereaza deloc nota contabila de venit prin `pack_facturare`**. Daca acele randuri capata totusi un cont de venit in `ACT`/`NOTE_CONTABILE` in productie, mecanismul nu e vizibil in codul cercetat (posibil un trigger pe tabel, un job batch, sau o scriere manuala/alt modul neexaminat — vezi Neverificat). **Nu se poate folosi acest precedent ca raspuns de fapt la intrebarea 1.** --- ## 5. Vreun mecanism care sa foloseasca `NOM_ARTICOLE.CONT` drept cont de venit Raspuns: **NU**, in `pack_facturare`. Cautat explicit `Grep -i 'NOM_ARTICOLE\.CONT'` pe intregul fisier `ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` (17020 linii) — **zero potriviri**. Singurele utilizari ale `NOM_ARTICOLE` gasite in `contabilizeaza_articol` insusi sunt pentru `DENUMIRE` (mesajul de eroare FACT-024, `ff_...:7295-7298`), nu pentru `CONT`. `crs_rand_articol.scc` (folosit ca `V_SCC` la `ff_...:7434`) vine exclusiv din `D.SCC` din join-ul `CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` (`ff_...:7261, 7275-7276`) — nu exista ramura alternativa care sa substituie `NOM_ARTICOLE.CONT` cand politica lipseste. (Raportul anterior confirmase deja separat ca `NOM_ARTICOLE.CONT` e folosit doar ca **cont de gestiune**, transmis catre `descarca_gestiune`, `ff_...:7499`.) --- ## Neverificat - **Continutul efectiv al `NOTE_CONTABILE`/`CRM_POLITICI_PRET_ART`** (daca exista politici cu `ID_POL = 0`, ce SCC au liniile din productie pentru documente "alte servicii" existente) — necesita interogare pe baza de date, nu doar cod. - **Constrangerile DDL** ale `VANZARI_DETALII_TEMP.ID_POL` si `ACT_TEMP.SCC`/`ACT.SCC` (NOT NULL? default?) — scripturile DDL ale acestor tabele nu au fost cautate/citite in aceasta cercetare (am cautat doar in pachetele PL/SQL `ff_...COMUN_PACK_FACTURARE.sql` si `..._AUTO_PACK_AUTO.sql`). - **Cine scrie efectiv contul de venit (`ACT`/`NOTE_CONTABILE`) pentru documentele "alte servicii" din ROAAUTO**, daca se scrie deloc. `pack_auto.actualizeaza_deviz` presupune ca exista deja un rand `ACT` cu `SCD NOT LIKE '5%'` pentru codul documentului curent — sursa acelui rand nu a fost gasita in `oproceduri_devize.prg` sau in corpul `scrie_in_vanzari`/`actualizeaza_deviz` cercetate. Ar trebui cautat: alte proceduri `pack_auto` (fisierul `ff_2026_03_31_02_AUTO_PACK_AUTO.sql` are si alte proceduri necercetate aici), triggere pe `VANZARI`/`VANZARI_DETALII`/`ACT`, sau apeluri din alte forme VFP (`.scx`/`.vcx`) din ROAAUTO care preced acest apel RPC. Nu am cautat exhaustiv triggere in afara folderului `SCRIPTURI_CLAR\2026` (cautare limitata, zero rezultate acolo). - **Ce se intampla exact daca `id_pol = 0` si exista o politica reala cu `ID_POL = 0`** (caz teoretic, neexclus explicit din schema) — ar schimba concluzia de la "eroare neprinsa" la "FACT-024 cu mesaj complet", dar tot ramane o eroare, nu un default silentios.