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

218 lines
15 KiB
Markdown

# 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.