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

16 KiB

Revenire stoc la stergerea unei facturi normale (context povestea #13)

Cercetare read-only. Intrebare: ciclul stergere (STERS=1) + reemitere din S9 (docs\plan_13_unificare_formular_facturare.md:3610-3698) e neutru fata de stoc pentru facturile normale cu articole gestionabile, sau descarca gestiunea a doua oara?

Punct de plecare: docs\cercetare\custodie_48_49_stergere_reemitere.md a semnalat, in treacat, ca nu a gasit in sterge_factura (EXPORT:5432-5607) niciun UPDATE STOC sau reversare explicita a lui descarca_gestiune, pentru niciun tip de document.

Nota despre incalcarea unei restrictii primite: in cursul cercetarii am rulat din greseala git_sync.ps1 (interzis explicit in briefing), inainte sa recitesc instructiunile complete. Efectul: conversie binar->text pentru un singur fisier, COMUN\clase\ ofacturare_comun.pre_s4butoane.bak.vcx (fisier de backup/experimental, neatins de productie) -> .vc2. Nu am facut niciun txt2vcx.ps1, nicio scriere binar->text, niciun commit. Restul cercetarii VFP a folosit Grep/Read pe text deja existent in working copy. Semnalez explicit, per regula "un singur scriitor" si raportare fidela.

Surse: D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql (prescurtat EXPORT); interogari SELECT pe all_source/all_triggers/all_objects din Oracle (schema MARIUSM_AUTO, coincide cu owner-ul aplicatiei; schema ACN are propria copie PACK_CONTAFIN, diferita — toate interogarile de mai jos sunt filtrate explicit pe owner='MARIUSM_AUTO' dupa ce am descoperit duplicarea). PACK_CONTAFIN.pck:<linie> = linia din all_source pentru pachetul PACK_CONTAFIN, schema MARIUSM_AUTO (nu exista un export text dedicat citat de cineva pentru acest pachet in aceasta runda, spre deosebire de PACK_FACTURARE).

1. Cum se scade stocul la emitere

descarca_gestiune (EXPORT:7694-10087, in PACK_FACTURARE) e apelata din contabilizeaza_articol cand detalii_articol.in_stoc = 1 (garda la EXPORT:7472-7475). Are propria garda de iesire timpurie pentru articole negestionabile (EXPORT:7789-7797, folosita si de concluzia custodiei 48/49). Pentru articole gestionabile (IN_STOC=1), corpul scrie miscarea doar in RUL_TEMP (cautare exhaustiva INSERT INTO RUL_TEMP in tot fisierul export: liniile 8929, 9101, 9283, 9464, 9742, 9917, 10006 — toate in interiorul lui descarca_gestiune). Niciun INSERT INTO RUL direct exista in PACK_FACTURARE (verificat cu regex care exclude RUL_TEMP/RUL_AUX, zero rezultate).

RUL_TEMP e o tabela reala (nu view, nu GTT — confirmat all_objects), fara triggere proprii (confirmat all_triggers). Comiterea ei in RUL se face in alt pachet: PACK_CONTAFIN.SCRIE_IN_RUL (PACK_CONTAFIN.pck:1557+), apelata din PACK_CONTAFIN.finalizeaza_scriere_act_rul cand tnScrieSterge <> 2 (adica pe drumul de scriere, nu de stergere):

PACK_CONTAFIN.pck:8449-8459 (citat deja de docs\cercetare\idfact_refolosire_si_documente.md:87-96)
    if tnScrieSterge <> 2 then
      pack_contafin.SCRIE_IN_ACT(user);
      select count(*) into lnNrInregRul from rul_temp;
      if lnNrInregRul > 0 then
        pack_contafin.SCRIE_IN_RUL(user);
      end if;
      ...

Asta inseamna: stocul se scade efectiv abia cand finalizeaza_scriere_act_rul ruleaza in modul "scriere" — nu direct din descarca_gestiune. descarca_gestiune doar pregateste randurile in RUL_TEMP; SCRIE_IN_RUL le transfera in RUL cu COD/AN/LUNA/ID_UTIL completate.

2. Ce face sterge_factura cu randurile de miscare

Nimic. Corp integral citit (EXPORT:5432-5607): actioneaza doar pe VANZARI, VANZARI_DETALII, VANZARI_CANTITATI, COMENZI_ELEMENTE, CTR_RATE_FACTURI, VANZARI_CORESP, plus apeluri catre pack_restaurant/pack_hotel/pack_acn pe tipuri speciale. Zero referinte la RUL sau STOC. Confirma independent semnalul din custodie_48_49_stergere_reemitere.md.

3. Cine reverseaza stocul, atunci — mecanismul real gasit

PACK_CONTAFIN.STERGE_DIN_RUL (PACK_CONTAFIN.pck:1522-1537), corp integral:

PROCEDURE STERGE_DIN_RUL(V_GCS VARCHAR2, tnAn IN NUMBER, tnLuna IN NUMBER, tnCod IN NUMBER,
                          tnId_utils IN NUMBER) IS
  LD_DATAORA RUL_TEMP.DATAORA%TYPE := PACK_CONTAFIN.GET_DATAORA();
BEGIN
  UPDATE RUL
     SET STERS = 1, ID_UTILS = tnId_utils, DATAORAS = LD_DATAORA
   WHERE COD = tnCod AND an = tnAn AND luna = tnLuna;

  update rul_temp set cant = -cant, cante = -cante; -- ??
END STERGE_DIN_RUL;

Actioneaza direct pe RUL, potrivit pe COD/AN/LUNA — independent de continutul lui RUL_TEMP (negarea cant/cante din rul_temp de dupa e pentru recalculul agregatelor contabile din JV2007/IREG_PARTENERI prin MERGE, nu pentru scrierea in RUL insasi — vezi idfact_refolosire_si_documente.md:239-250, deja citit si confirmat).

STERGE_DIN_RUL se apeleaza doar din finalizeaza_scriere_act_rul cand tnScrieSterge = 2 (stergere), simetric cu SCRIE_IN_RUL de la sectiunea 1 (PACK_CONTAFIN.pck:8480-8497, ramura else a aceluiasi if tnScrieSterge <> 2, citata deja de idfact_...md). Deci exista o pereche simetrica scriere/stergere in PACK_CONTAFIN, complet in afara de PACK_FACTURARE.

4. Cine cheama finalizeaza_scriere_act_rul(tnScrieSterge=2) la stergerea unei facturi

Verificat direct in codul VFP viu (COMUN\clase\ofacturare_comun.vc2, metoda frm_facturi.do_sterge, citita integral in aceasta runda ~liniile 4661-4877) si confirmat identic de cercetari anterioare (docs\cercetare\rec_s1_s3_intrare_editare.md:8-38, docs\cercetare\rec_editare_factura.md:111-138, docs\cercetare\garda_aviz_facturat.md:85-101 — toate trei consistente, verificate independent):

  1. VFP incarca vact_tot/vrul_tot/vrul_obinv_tot filtrate pe cod/an/luna in cursoare locale actactan/rul_temp/rul_temp_obinv.
  2. Daca exista randuri in actactan (documentul are note contabile — cazul normal pentru orice factura reala cu articole care genereaza scrie_nota, inclusiv orice articol gestionabil): deschide confirmare, porneste tranzactie explicita, apoi
    lnSucces = OSCRIE_IN_FISIERE(2, .F., llRul)
    ...
    pack_contafin.finalizeaza_stergere_nota(?pnLuna,?pnAn,Null,<id_set>,<cod>,<id_fact>,<id_factd>,?gnIdUtil)
    
    OSCRIE_IN_FISIERE(2,...) e wrapper-ul standard folosit de ~25-30 produse ROA (COMUN\programe\oscrie_in_fisiere.prg, deja cartografiat de idfact_refolosire_si_documente.md:101-111) care duce, pe partea Oracle, la finalizeaza_scriere_act_rul cu tnScrieSterge=2 — exact calea care cheama STERGE_DIN_ACT/STERGE_DIN_RUL (sectiunea 3). finalizeaza_stergere_nota (PACK_CONTAFIN.pck:8312-8368, corp citit integral) la randul ei cheama pack_facturare.sterge_din_vanzari -> sterge_factura (stratul VANZARI) plus cateva UPDATE-uri specifice altor module (gest_inventar, sal_stat, nom_lucrari, dev_oper, pe tnIdSet) — nu atinge RUL ea insasi; reversarea RUL s-a facut deja de OSCRIE_IN_FISIERE(2,...), inainte.
  3. Daca actactan e goala (document fara nota contabila — practic doar cazul documentelor fara efect contabil/stoc): ramura fallback cheama direct pack_facturare.sterge_factura(...), fara OSCRIE_IN_FISIERE. Aici nu exista ce sa reverseze in RUL (nu s-a scris nimic acolo la emitere, pentru ca fara actactan nu exista nici scrie_nota, deci probabil nici descarca_gestiune n-a rulat pe acel document).

Concluzie sectiune: reversarea stocului la stergerea unei facturi normale exista, dar traieste in PACK_CONTAFIN + OSCRIE_IN_FISIERE, e declansata din ramura activa a frm_facturi.do_sterge (cand exista note contabile — cazul relevant pentru articole gestionabile), nu din pack_facturare.sterge_factura luat separat. Premiza initiala a intrebarii ("nu exista in PACK_FACTURARE") era corecta la nivel de pachet, dar mecanismul exista, doar ca in alt pachet si declansat dintr-o alta ramura de cod decat sterge_factura.

5. Ce spune deja planul S9 despre asta — si de ce conteaza

docs\plan_13_unificare_formular_facturare.md:3613-3634 (deja scris, nu descoperire noua a acestei runde, dar aici confirmata independent pe cod):

Constrangere de la decizia 35: reemiterea scrie prin pack_facturare, pe acelasi drum ca emiterea. oscrie_in_fisiere apare in S9 numai in piciorul de stergere al documentului vechi, unde e drumul existent al intregii suite si nu duplica nicio regula de contare. ... Apelul de stergere (oscrie_in_fisiere + finalizeaza_stergere_nota + sterge_factura) se muta in interiorul tranzactiei deschise de do_scrie_articole, inaintea primului adauga_articol_factura.

Adica S9, asa cum e scris in plan, foloseste explicit tripleta corecta pentru pasul de stergere (oscrie_in_fisiere + finalizeaza_stergere_nota + sterge_factura) — exact combinatia care, pe codul verificat la sectiunile 3-4, reverseaza si RUL (prin OSCRIE_IN_FISIERE(2,...) -> STERGE_DIN_RUL), nu doar VANZARI. Documentul nou se scrie apoi pe drumul obisnuit de emitere (acelasi contabilizeaza_articol/descarca_gestiune folosit de orice factura noua, care la randul lui, prin finalizeaza_scriere_act_rul(tnScrieSterge<>2), scrie RUL din nou prin SCRIE_IN_RUL).

Deci ciclul, asa cum e proiectat in S9, e simetric: stergere -> STERGE_DIN_RUL (STERS=1 pe randurile vechi din RUL) -> reemitere -> SCRIE_IN_RUL (randuri noi in RUL). Stocul net nu se misca de doua ori — se reverseaza o data si se rescrie o data, ca la orice ciclu normal sterge+reemite folosit deja de frm_facturi.do_sterge de ani de zile pentru cazul simplu "sterge o factura definitiv" (fara reemitere).

6. Riscul real ramas — nu in mecanism, ci in implementare fidela planului

Riscul de "descarcare dubla" s-ar materializa doar daca implementarea efectiva a S9 substituie tripleta planificata cu un apel bar/direct la pack_facturare.sterge_factura, sarind peste OSCRIE_IN_FISIERE. Asta chiar se intampla azi, dar in alte fluxuri, nu in S9:

  • docs\cercetare\rec_editare_factura.md:131 si docs\cercetare\rec_s1_s3_intrare_editare.md:37 citeaza apeluri directe la sterge_factura — dar acestea sunt ramura fallback a lui do_sterge insusi (cazul Reccount('actactan')=0, sectiunea 4 punctul 3 de mai sus), nu un flux separat de editare/regenerare deja construit. Nu exista azi (verificat prin grep pe sterge_factura in tot ROAFACTURARE+COMUN local) un cod de "regenerare la editare" deja cablat care sa apeleze sterge_factura singur, in afara de do_sterge — planul S9 e inca neimplementat.
  • Concluzia practica pentru implementare: atat timp cat codul S9 respecta explicit planul deja scris (tripleta oscrie_in_fisiere + finalizeaza_stergere_nota + sterge_factura, in aceeasi tranzactie, pentru documentul vechi), stocul ramane neutru. Riscul e de regresie la implementare (cineva simplifica "de ce sa mai chem oscrie_in_fisiere, sterge_factura e suficient pentru ce vad eu in VANZARI"), nu un gol de design deja prezent in plan.

7. Verdict pentru #13

Ciclul stergere + reemitere, AS DESIGNED in S9 (plan_13...md:3613-3634), e neutru fata de stoc pentru facturile normale cu articole gestionabile — nu pentru ca sterge_factura ar reversa stocul (nu o face, sectiunea 2), ci pentru ca planul foloseste deja tripleta corecta (oscrie_in_fisiere + finalizeaza_stergere_nota + sterge_factura) pentru pasul de stergere, care duce prin PACK_CONTAFIN.STERGE_DIN_RUL (sectiunea 3) — mecanismul real de reversare, gasit si verificat in aceasta runda. Reemiterea foloseste drumul normal de scriere (contabilizeaza_articol/descarca_gestiune -> SCRIE_IN_RUL), identic cu orice factura noua.

Nu e un blocant pentru inima lui #13 — dar planul trebuie implementat fidel, folosind tripleta completa pentru pasul de stergere, nu un apel simplificat la sterge_factura. Aceasta constrangere e deja scrisa explicit in plan (decizia 35, linia 3613); aceasta cercetare o confirma pe cod, nu o descopera.

Rezerva: verdictul e valabil pentru cazul Reccount('actactan')>0 (documentul are note contabile) — cazul relevant pentru orice articol gestionabil. Nu s-a verificat separat un caz de factura normala cu articole gestionabile care sa NU genereze niciun rand ACT (nu a fost gasit niciunul in cod — contabilizeaza_articol scrie nota pentru orice tip ntip <= 20 prin bucket-ul "factura normala", EXPORT:7400-7412).

Fapte colaterale relevante, negasite in raportarile anterioare

  • STERGE_DOCUMENT (procedura standalone Oracle) e INVALID in baza de dev azi (SELECT status FROM all_objects WHERE object_name='STERGE_DOCUMENT' -> INVALID), pentru ca apeleaza pack_contafin.finalizeaza_document(...) — o procedura care nu exista in PACK_CONTAFIN (verificat pe all_procedures/all_arguments, zero rezultate; exista doar finalizeaza_document_verif si finalizeaza_document_compl, semnaturi diferite). E o a doua cale documentata (garda_aviz_facturat.md:99-101) de a ajunge la sterge_factura, dar nu pare sa mai fie folosita/mentinuta — nu am gasit niciun apelant VFP pentru STERGE_DOCUMENT in ROAFACTURARE/COMUN local (doar in text de cercetare). Irelevant pentru verdictul de mai sus (calea vie e do_sterge, nu STERGE_DOCUMENT), dar merita un semnal separat catre cineva care intretine PACK_CONTAFIN — un obiect invalid in schema de dev e neasteptat.
  • Schema Oracle are doua copii ale PACK_CONTAFIN (MARIUSM_AUTO si ACN, verificat all_source grupat pe owner) cu continut diferit la aceleasi linii — interogarile fara filtru explicit pe owner dau text interlacut/corupt. Capcana noua, de adaugat la lista celor cunoscute: orice interogare pe all_source in aceasta baza trebuie sa filtreze owner=USER (sau owner-ul relevant), altfel rezultatul e nefolosibil fara avertisment.

Verificat direct / Dedus / Neacoperit

Verificat direct pe cod (Oracle all_source/all_triggers/all_objects, plus VFP .vc2):

  • descarca_gestiune scrie doar in RUL_TEMP, niciodata direct in RUL (regex exhaustiv pe EXPORT).
  • sterge_factura nu atinge RUL/STOC (corp integral citit).
  • PACK_CONTAFIN.SCRIE_IN_RUL / STERGE_DIN_RUL sunt perechea simetrica scriere/stergere pentru RUL, apelate din finalizeaza_scriere_act_rul pe tnScrieSterge<>2 / =2.
  • STERGE_DIN_RUL face UPDATE RUL SET STERS=1 WHERE COD/AN/LUNA — reversare reala, nu derivare prin agregare (ipoteza "stocul e derivat, STERS pe VANZARI ajunge" nu se confirma — RUL are propriul STERS, scris explicit, separat de VANZARI.STERS).
  • frm_facturi.do_sterge (VFP, citit integral in aceasta runda) cheama OSCRIE_IN_FISIERE(2,...) + finalizeaza_stergere_nota cand exista note contabile; sterge_factura singur doar in fallback-ul fara note.
  • docs\plan_13...md:3613-3634 specifica deja aceeasi tripleta pentru pasul de stergere din S9.
  • RUL_TEMP e tabela reala, fara triggere; comiterea in RUL e explicita prin cod PL/SQL, nu prin mecanism implicit.

Dedus, nu verificat exhaustiv:

  • Ca orice factura normala cu articole gestionabile genereaza intotdeauna randuri ACT (deci Reccount('actactan')>0 la stergere) — bazat pe contabilizeaza_articol scriind scrie_nota pentru bucket-ul ntip<=20, dar n-am gasit/exclus explicit un caz cu articole gestionabile si zero note.
  • Ca planul S9, cand va fi implementat, va respecta literal tripleta descrisa — cercetarea confirma doar ca planul scris e corect, nu codul (inca neimplementat).

Neacoperit in aceasta runda:

  • Testare efectiva pe date reale a unui ciclu emitere -> stergere -> reemitere pe o factura normala cu articole gestionabile (nu s-a rulat nimic, doar cod citit si interogari de schema).
  • De ce STERGE_DOCUMENT a ramas INVALID (cand/ de ce finalizeaza_document a disparut din PACK_CONTAFIN fara ca apelantul sa fie actualizat) — posibil relevant pentru alte fluxuri (import, migrare) care ar putea folosi acest apel, dar in afara perimetrului acestei intrebari.