Files
roafacturare/docs/cercetare/nota_contabila_fara_politica.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

14 KiB

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.