# Custodie (tipuri 48/49): stergere + reemitere intoarce curat descarcarea din custodie? Cercetare read-only. Raspunde la intrebarea daca editarea prin regenerare (STERS=1 pe documentul vechi + document nou, in aceeasi tranzactie, mecanismul proiectat la S9 din `docs\plan_13_unificare_formular_facturare.md:3576-3660`) pentru facturile de marfa in custodie (tipurile 48/49) lasa stocul de custodie neschimbat. Sursa principala citata: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (prescurtat `EXPORT` mai jos, `EXPORT:linie`). Verificat direct in aceasta runda: numerotarea din export **coincide** cu cea folosita in materialele anterioare citate de team-lead (`scrie_fact_aviz_custodie` la `:10089-10325` pentru corpul functiei si `:7521-7536` pentru locul de unde e apelata; `sterge_factura` la `:5432-5607`) — nu s-a gasit niciun offset. **Rezultat central, care rescrie premiza cererii**: `scrie_fact_aviz_custodie` **nu se apeleaza niciodata pentru documente de tip 48/49**. Se apeleaza exclusiv cand `pack_facturare.ntip = 4` ("factura din avize" — alt tip de document, complet diferit). Tipurile 48/49 sunt o ramura separata, care **nu atinge stocul deloc** la emitere (nu doar "il descarca prin alta cale"). Detalii mai jos. ## 1. Ce sunt exact tipurile 48 si 49 Confirmat pe cod, nu doar pe indiciul din materiale: - **Denumirile** ("custodie cu descarcare K" / "custodie fara descarcare K") vin din `COMUN\docs\tipuri_documente_facturare.md`, deja verificate pe cod de cercetarea anterioara `docs\cercetare\s5_acoperire_tipuri.md` (tabelul de la liniile 102-103 de acolo). - **Reachable din meniu**: `Meniuri\politica.mn2`, submeniul `Marfaincus` — tip 48 la linia 29, tip 49 la linia 26 (citat deja corect in `s5_acoperire_tipuri.md:22,102-103,146-147`). - **Amandoua sunt facturi de sine statatoare** (categoria "Facturi", nu "Avize" — spre deosebire de 42/47, care sunt avize catre custodie), sursa **VFP** de articole fiind aceeasi pentru ambele: `Case Inlist(tnTip, 48, 49) -> pack_facturare.cursor_articole_k(...)` (`COMUN\programe\ofacturare.prg:271-272` pe formularul standard, identic la `:750-752` pe prototip). - **`cursor_articole_k`** (`EXPORT:3595-3701`, definitia activa; exista si o declaratie in spec la `:382`) e o interogare de **lista de preturi**, nu o interogare de stoc/aviz: alege articolele dintr-o **politica de pret dedicata** (`A.ID_POL = to_number(pack_sesiune.getoptiunefirma('IDPOLPRETFACTK'))`, `EXPORT:3681-3682`) si **filtreaza explicit doar articole negestionabile**: `WHERE C.IN_STOC = 0 AND C.IN_CRM = 1 AND C.STERS = 0 AND C.INACTIV = 0` (`EXPORT:3695-3698`). Cu alte cuvinte: **48 si 49 sunt aceeasi sursa de articole** ("K" = politica de pret speciala pentru custodie, optiunea de firma `IDPOLPRETFACTK`), restransa explicit la articole **care nu sunt gestionate in stoc** (`IN_STOC=0` in `NOM_ARTICOLE`). - **Nu s-a gasit in aceasta runda ce anume distinge 48 de 49** ("cu"/"fara descarcare K") — pe tot codul examinat (`adauga_articol_factura`, `contabilizeaza_articol`, `cursor_articole_k`, `sterge_factura`), cele doua tipuri sunt tratate **identic**, fara nicio ramura care sa le separe. Diferenta pare sa fie doar de conventie de utilizare (business), nu de cod — semnalat la sectiunea "Neacoperit". ## 2. Ce scrie emiterea pe un document 48/49 **Nu se atinge stocul deloc, prin design, nu prin accident.** Lant de dovezi: 1. `adauga_articol_factura` (`EXPORT:4989-5220`) nu are ramura proprie pentru `ntip IN (48,49)` — cade in `ELSE` (`:5187-5203`), care preia `V_IN_STOC` direct din `V_IN_STOC_TEMP`, valoarea trimisa de VFP din grid — la randul ei populata din `cursor_articole_k.GESTIONABIL` (`C.IN_STOC AS GESTIONABIL`, `EXPORT:3634`), deci **intotdeauna 0** pentru articolele oferite pe aceste doua tipuri. 2. `contabilizeaza_articol` (`EXPORT:7173-7547`): tip 48/49 intra in bucketul "factura normala" pentru determinarea `SCD`/`ASCD` (`WHEN pack_facturare.ntip <= 20 or ntip IN (... 48, 49, 51, 52)`, `EXPORT:7400-7412`) — scrie o nota contabila normala prin `scrie_nota` (`:7443-7467`), ca orice factura obisnuita, folosind conturile din politica de pret K. 3. **Apelul catre `descarca_gestiune` e conditionat** de `detalii_articol.in_stoc = 1` (`EXPORT:7472-7475`, ramura `IF pack_facturare.ntip <> 4`, care e adevarata pentru 48/49). Cum `in_stoc` vine intotdeauna 0 pentru articolele acestor doua tipuri (pasul 1), **conditia nu se indeplineste niciodata** — `descarca_gestiune` nu se executa. 4. **Confirmare independenta, in interiorul lui `descarca_gestiune` insusi**: chiar daca cineva ar reusi sa forteze apelul, functia are propria garda de iesire timpurie: ``` EXPORT:7789-7797 -- NU SE DESCARCA GESTIUNEA PENTRU ARTICOLELE NEGESTIONABILE -- ASTFEL INCAT SA SE POATA FACE OPERATII GEN AVIZ DIN CUSTODIE INCLUSIV CU ARTICOLE NEGESTIONABILE SELECT MAX(IN_STOC) INTO lnInStoc FROM NOM_ARTICOLE WHERE ID_ARTICOL = V_ID_ARTICOL; if lnInStoc = 0 then GOTO SFARSIT; end if; ``` Comentariul insusi confirma intentia de design: articolele negestionabile (exact categoria din care se alimenteaza 48/49) sunt **explicit excluse** din descarcarea de gestiune, tocmai pentru ca fluxul de custodie sa poata folosi articole care nu au stoc urmarit. 5. **`scrie_fact_aviz_custodie` nu se apeleaza pentru 48/49.** Singurul apel din tot pachetul (`EXPORT:7520-7537`) e in interiorul aceleiasi functii `contabilizeaza_articol`, pe ramura `ELSE` a testului `IF pack_facturare.ntip <> 4` (`:7472`) — adica **doar cand `ntip = 4`** ("factura din avize"). Pentru 48/49, `ntip` e 48 sau 49, niciodata 4, deci acest cod **nu se executa niciodata** pe aceasta ramura. Functia `scrie_fact_aviz_custodie` insasi (`EXPORT:10089-10325`) nu scrie in `STOC`/`RUL` — cauta un rand deja existent in `RUL` (cu `ID_TIP_RULAJ = 0`, `EXPORT:10175`) pentru a calcula valori de cost, si scrie doar **note contabile** (`scrie_nota`, conturi `371/357/607/378/4428` — conturi de marfuri in custodie / cheltuieli, `EXPORT:10229-10324`) — e o functie de **conversie contabila**, nu de miscare de stoc. **Concluzie sectiune**: premiza din cerere ("pe ramura custodiei se cheama `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`") descrie corect fluxul pentru **tip 4** (factura emisa dintr-un aviz, posibil un aviz catre custodie 42/47 emis anterior), **nu** pentru tipurile 48/49 cerute explicit. Pentru 48/49, niciuna din cele doua proceduri nu scrie nimic in stoc — documentul e din start "fara efect de stoc", pentru ca sursa lui de articole (`cursor_articole_k`) e restransa la articole negestionabile. ## 3. Ce face stergerea (`sterge_factura`) pe un document 48/49 `sterge_factura` (`EXPORT:5432-5607`) nu are nicio ramura proprie pentru `V_TIP IN (48,49)`. Constantele speciale verificate (`EXPORT:78-86`): `nTipVanzareRetail=43`, `nTipFacturaHotel=44`, `nTipFacturaRestaurant=45`, `nTipNotaPlata=46`, `nTipFacturaACN=51` — 48 si 49 nu sunt printre ele. `CASE`-ul principal (`EXPORT:5501-5551`) evalueaza `V_TIP` astfel pentru 48/49: - `V_TIP = 24`? Nu. - `V_TIP > 20 AND V_TIP NOT IN (44,45,46)`? **Da** — 48 si 49 cad in aceasta ramura generica ("alte tipuri de avize", `EXPORT:5512-5523`), care face: ```sql UPDATE VANZARI_CANTITATI SET STERS = 1 WHERE ID_VANZARE_DET_AVIZ IN (SELECT ID_VANZARE_DET FROM VANZARI_DETALII WHERE ID_VANZARE = V_ID_VANZARE AND STERS = 0); ``` `VANZARI_CANTITATI` e tabelul de bookkeeping "cantitate ramasa din comanda/aviz" (Rol A, documentat in `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md`, citat si in `s5_acoperire_tipuri.md` coloana "Rol"). **48/49 nu au niciodata acest rol** (`s5_acoperire_tipuri.md:103-104`: coloana Rol = "—" pentru ambele) — articolele lor vin din `cursor_articole_k` (lista de preturi K), nu din `cursor_comanda`. Deci acest `UPDATE` **actioneaza pe zero randuri** pentru documente 48/49 (nu exista randuri `VANZARI_CANTITATI` legate de ele) — e un no-op inofensiv, nu o eroare, dar nici o actiune custodie-specifica. - Restul ramurilor CASE (`V_TIP=4`, `V_TIP=nTipFacturaRestaurant`) nu se aplica. Restul procedurii e comun tuturor tipurilor (nimic specific 48/49): - `UPDATE VANZARI_DETALII SET STERS=1 ... WHERE ID_VANZARE = V_ID_VANZARE` (`:5560-5564`) — marcheaza liniile facturii ca sterse. - `IF V_TIP IN (2,6,52)` pentru `CTR_RATE_FACTURI` (`:5566-5580`) — nu se aplica (48/49 nu sunt in lista). - `UPDATE VANZARI_CORESP SET STERS=1 ...` (`:5582-5585`) — sterge corespondentele factura<->aviz/comanda, generic. - CASE-ul final pe pachete externe (restaurant/hotel/ACN, `:5587-5605`) — nu se aplica pentru 48/49, cad in `ELSE NULL`. **Nu exista, nicaieri in `sterge_factura`, vreun apel care sa reverseze explicit efectul lui `descarca_gestiune`** (nu s-a gasit `incarca_gestiune` sau echivalent, pentru niciun tip de document, nu doar 48/49 — cautare `grep -n "UPDATE STOC"` in tot pachetul, zero rezultate; singurele scrieri gasite in zona lui `descarca_gestiune` sunt in `RUL_TEMP`, `EXPORT:9464,10006`, tabel tranzitoriu care se descarca in `RUL` mai departe in flux, nu direct in acest apel). Mecanismul prin care stocul "revine" la stergerea unei facturi normale (`ntip<=20`) **nu a fost identificat in aceasta runda** — posibil printr-un filtru `WHERE VANZARI_DETALII.STERS=0` in interogarile de stoc disponibil, posibil prin alt pachet (`PACK_STOC`?) sau printr-un trigger, in afara `PACK_FACTURARE` si a perimetrului citit. Marcat explicit la "Neacoperit" — **e o intrebare generala a sistemului, nu specifica custodiei**, pentru ca `sterge_factura` trateaza identic (fara reversare explicita) orice tip de document. ## 4. Garzi existente la stergere — prind si cazul custodiei? Cele trei garzi de la inceputul lui `sterge_factura`, verificate pe liniile citate de team-lead (coincid exact, fara offset): - `EXPORT:5452-5462` — blocheaza daca exista **facturi de retur** (`VANZARI_CORESP.TIP=3`) pe documentul curent. - `EXPORT:5466-5476` — blocheaza daca exista **facturi/avize de retur** (`TIP IN (1,2)`) pe un aviz curent. - `EXPORT:5480-5494` — blocheaza daca exista **avize de retur** pe avizele care au generat o factura din aviz curenta. **Niciuna nu mentioneaza custodie sau `ntip IN (48,49)`** — toate trei filtreaza exclusiv pe `VANZARI_CORESP.TIP IN (1,2,3)` (relatii factura<->retur / aviz<->retur), independent de tipul documentului curent (`V_ID_VANZARE`). Pentru un document 48/49 fara retur emis pe el, **niciuna nu se declanseaza** — stergerea trece liber, exact ca pentru o factura normala fara retur. ## 5. Verdict **Regenerarea (stergere + reemitere) e SIGURA pentru documentele 48/49 in privinta stocului de custodie — dar dintr-un motiv diferit de cel presupus in cerere.** Nu pentru ca stergerea "intoarce curat" o descarcare de custodie — ci pentru ca **emiterea unui document 48/49 nu descarca nimic din stoc/custodie in primul rand** (sectiunea 2: sursa de articole e restransa structural la articole negestionabile, `IN_STOC=0`, iar `descarca_gestiune` are doua garzi independente care o opresc pentru astfel de articole). Stergerea (sectiunea 3) nu are nimic custodie-specific de reversat, si nici nu are nevoie sa aiba, pentru ca nimic custodie-specific n-a fost scris la emitere. **`ntip=4` (factura din avize) e ramura care foloseste efectiv `scrie_fact_aviz_custodie`, si e un tip de document diferit de 48/49** — premiza din cerere ("pe ramura custodiei se cheama `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`") descrie corect tip 4, nu 48/49. **Conditie sub care verdictul s-ar putea rasturna, ne-exclusa 100% in aceasta runda**: siguranta de mai sus depinde de invariantul "un document 48/49 contine numai articole cu `IN_STOC=0`". Daca articolele adaugate pe un document 48/49 ar veni vreodata **printr-o alta cale** decat `cursor_articole_k` (o cautare libera de articole, nerestransa la politica K), un articol gestionabil (`IN_STOC=1`) ar trece garda de la `adauga_articol_factura`/`contabilizeaza_articol` (sectiunea 2, pasul 1: `V_IN_STOC_TEMP` ar deveni 1) si **`descarca_gestiune` s-ar executa normal** — caz in care `sterge_factura`, care nu reverseaza explicit stocul pentru niciun tip (sectiunea 3), ar lasa un decalaj real. **Nu s-a gasit in aceasta runda** o cale VFP care sa permita asta pentru 48/49 (grid-ul de articole al acestor doua tipuri e populat o singura data, la deschiderea formularului, direct din `cursor_articole_k` — `COMUN\programe\ofacturare.prg:271-272`; nu exista o cautare separata de articole vazuta in codul citit), dar nu s-a verificat exhaustiv toate punctele de intrare posibile in `VANZARI_DETALII_TEMP` pentru aceste doua tipuri. ## Verificat direct / Dedus / Neacoperit **Verificat direct pe cod (fisier:linie citat in sectiunile 1-4):** - Tip 48/49 = facturi (nu avize), sursa unica de articole `cursor_articole_k`, restransa la `IN_STOC=0`. - `scrie_fact_aviz_custodie` se apeleaza exclusiv pentru `ntip=4`, niciodata pentru 48/49. - `descarca_gestiune` are doua garzi independente (`in_stoc=1` la apelant, plus garda proprie pe `NOM_ARTICOLE.IN_STOC`) care o opresc pentru articolele din 48/49. - `sterge_factura` nu are ramura proprie pentru 48/49 (cade in bucketul generic ">20, exclus hotel/restaurant/notaplata"), iar actiunea acelui bucket (`VANZARI_CANTITATI`) nu are ce sa actioneze pentru aceste doua tipuri (fara rol in acel tabel). - Cele trei garzi de refuz al stergerii nu disting custodia, si nu se declanseaza pentru un document 48/49 fara retur emis pe el. **Dedus, nu verificat exhaustiv:** - Ca operatorul nu poate adauga pe un document 48/49 un articol gestionabil pe alta cale decat `cursor_articole_k` — bazat pe faptul ca grid-ul se populeaza o singura data la deschidere, dar n-am urmarit tot codul de interactiune al grid-ului (`adauga la lista`/editare inline) pentru cele doua tipuri. - Ca diferenta reala dintre tip 48 si tip 49 e doar de conventie/business, nu de cod — n-am gasit nicio ramura care sa le separe, dar nici n-am cautat in afara `PACK_FACTURARE`/`ofacturare.prg` (de exemplu in rapoarte sau in alte proceduri VFP care ar putea trata diferit "cu descarcare K" vs "fara"). **Neacoperit in aceasta runda:** - Mecanismul general prin care stocul "revine" la stergerea unei facturi **normale** (`ntip<=20`) — nu s-a gasit niciun `UPDATE STOC`/reversare explicita in `PACK_FACTURARE`; posibil calculat prin interogari care filtreaza `VANZARI_DETALII.STERS=0`, posibil in alt pachet Oracle sau prin trigger, in afara perimetrului citit in aceasta runda. Nu e o intrebare specifica custodiei — se aplica identic la orice tip de document — dar ramane deschisa. - Semnificatia exacta si diferenta functionala/de raportare intre "custodie cu descarcare K" (48) si "custodie fara descarcare K" (49) — n-am gasit in cod ce anume descarca "K" daca nu e stoc fizic (posibil un calcul contabil de adaos comercial specific comertului cu amanuntul, coeficientul K, dar nu confirmat pe cod in aceasta runda). - Testare pe date reale (baza de dev) a unui ciclu emitere->stergere->reemitere pe un document 48/49 — cercetarea a fost exclusiv pe cod si `SELECT`-uri simple, fara sa ruleze fluxul.