#13: formular de facturare unificat - etapa curenta

Squash al branch-ului de lucru plan13-s2.

Cod: clasa noua ofacturare_util (geometrie si utilitare de formular), inrolata in
roafacturare.prg; meniul "Facturare (nou)" plat, cu reparatia literalului peste
limita VFP de 255 de caractere care impiedica compilarea metodei.

Livrare: changelog 2.11.19, marcajul de versiune DB pentru scripturile finale din
SCRIPTURI_CLAR\2026\09, inventarul livrarii in docs/livrare_13.md si registrul de
erori deschise. Documentatia de lucru a etapei (cercetare, rapoarte, handoff-uri,
snapshot-uri de scripturi si backup-uri de rollback) a fost scoasa din arbore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
This commit is contained in:
2026-09-09 21:30:51 +03:00
parent 87063b06a8
commit 104c24ec20
80 changed files with 5585 additions and 27852 deletions

View File

@@ -1,239 +0,0 @@
# 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:
```sql
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.