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

375 lines
27 KiB
Markdown

# S5C — Factura din proforma (proiectare)
Status: INCHEIAT
## Sarcina
Cerinta noua (decizia 43, runda 12): dintr-o proforma emisa sa se poata genera o
FACTURA ca document NOU (nu prin comutarea tipului pe acelasi document — comutarea
PROFORMA -> FACTURA cu linii e deja blocata cu mesaj). Mecanismul pare sa existe deja
pe calea de copiere (`do_copiaza`).
## (a) Poate fi azi o proforma aleasa ca sursa de copiere?
**Da, fara nicio excludere.** Trei dovezi convergente:
1. `COMUN\clase\ofacturare_comun.vc2:4964-4974` — `frm_facturi.IsCopy(tnTip)`:
```
RETURN .T. && POT SA COPIEZ ORICE, TRATEZ TIPURILE IN DO_COPIAZA
*!* RETURN INLIST(m.lnTip, 1,5,7,10,22,23,43) && pot sa copii doar avize si facturi pret de lista, ...
```
Varianta veche (restrictiva, comentata) verifica doar `tip` (1-52) — niciodata `eproforma`. Varianta
activa returneaza necondiționat `.T.`. Nu exista, si n-a existat vreodata in acest cod, o excludere
pe `eproforma`.
2. `COMUN\clase\ofacturare_comun.vc2:5110` — vizibilitatea butonului de copiere pe randul selectat:
`Thisform.but_copiaza1.Visible = Thisform.IsCopy(crsFacturi.tip)` — acelasi apel, acelasi rezultat
necondiționat.
3. `COMUN\clase\ofacturare_comun.vc2:5082-5083` — gridul de facturi are un filtru dedicat pe
`eproforma`, cu proforma tratata ca subset normal, nu ascuns:
```
"Facturi&Avize\nofiled\E\(eproforma = 0)\" + crlf + ;
"Proforme\nofiled\E\(eproforma=1)\" + crlf + ;
```
Chiar filtrul "Proforme" arata explicit ca proformele sunt navigabile si selectabile normal in
acelasi grid — butonul de copiere ramane vizibil identic pe orice rand selectat din acest filtru.
**Concluzie**: azi orice utilizator poate selecta o proforma in grid si apasa "Copiere (CTRL+K)"
(tooltip-ul insusi anunta explicit acest caz de folosire — vezi citatul din
`s5b_proiectare_proforma_copiere.md` §"1.4"/`proforma_copiere_puncte_intrare.md:41-47`). Nu exista
nicio garda de blocat, la niciun nivel (`IsCopy`, vizibilitate buton, filtru grid).
## (b) Ce tip rezulta din copierea unei proforme?
**Rezultatul e intotdeauna `nIdTipDoc=5` (FACTURA)** — corect pentru o factura fiscala — dar tipul de
business (`VANZARI.TIP`, 1-52) degradeaza dupa tabelul deja existent din `do_copiaza`, **nemodificat
pentru cazul proforma**: nu exista nicio ramura speciala pe `eproforma` in `do_copiaza`
(`COMUN\clase\ofacturare_comun.vc2:3628-3713`) — degradarea se face **strict dupa `loFactura.tip`**,
indiferent daca documentul sursa era proforma sau nu.
**Pasul 1 — degradarea de tip business** (`:3693-3708`, tabel deja confirmat in
`s5b_proiectare_proforma_copiere.md` §2.1):
- Proforma poate exista doar pe tipuri "de factura" (combo-ul `Ct_clb_fdoc` traieste in
`frm_date_factura`, nu si in `frm_date_aviz` — cf. `proforma_copiere_puncte_intrare.md` §1). Setul
posibil de `loFactura.tip` pentru o proforma e deci printre `{1,2,3,4,5,6,7,8,9,10,43,44,45,47,48,49,51}`
(grupul "Facturi" din `COMUN\docs\tipuri_documente_facturare.md`).
- Din acest set: `{1,5,7,10}` raman neschimbate (`CASE INLIST(loFactura.tip,T1,T5,T7,T10,T22,T23)`,
`:3694`); `{2,3,4,8,43,44,45,47,48,49,51}` degradeaza la `T1` (`:3696-3697`); `{6}` la `T10`
(`:3698-3699`); `{9}` la `T5` (`:3700-3701`).
- Deci, pentru orice proforma emisa astazi (indiferent de tipul de business original), documentul
copiat va avea `TIP` in `{1,5,7,10}` — nucleul "lista de preturi" (lei/valuta) sau credit note.
**Pasul 2 — `nIdTipDoc` NU se propaga** (confirmat deja in
`s5b_proiectare_proforma_copiere.md` §2.3, `COMUN\programe\ofacturare_comun.prg:370`:
`*.nIdTipDoc = toDateAnterior.nIdTipDoc` — comentata). Documentul nou primeste `nIdTipDoc` implicit
dupa tipul degradat: `Do Case` la `COMUN\programe\ofacturare.prg:187-196`, toate valorile din
`{1,5,7,10}` cad sub `5` (FACTURA) → `poDate.eProforma = 0` pe documentul nou, **indiferent ca sursa
era proforma**.
**Concluzie pentru (b)**: mecanismul de copiere transforma azi orice proforma intr-o **FACTURA reala
(`nIdTipDoc=5`, `eproforma=0`)**, de tip business `1` (lista de preturi, cel mai frecvent caz — orice
tip din `{2,3,4,8,43,44,45,47,48,49,51}` cade tot pe `T1`), `5`/`10` (daca sursa era deja pe lista de
preturi in valuta/factura valuta), sau `7` (credit note, ramas neschimbat). Tipul rezultat **e cel bun
pentru o factura fiscala** din perspectiva `nIdTipDoc`/`eproforma` (nu ramane proforma), dar tipul de
business e intotdeauna **degradat catre "lista de preturi"** — proforma emisa dintr-o comanda (`tip=3`)
sau dintr-un aviz (`tip=4`) devine, dupa copiere, o factura "banala" `tip=1`, **la fel ca la orice alta
copiere non-proforma** (comportament deja documentat si acceptat in S5b §7 punctul 5: "degradarea de
tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare" — nicio schimbare ceruta aici
fata de acel raport).
## (c) Legatura proforma -> factura (VANZARI_CORESP.TIP)
### Structura tabelei (confirmata pe schema vie, `MARIUSM_AUTO`)
```
ID_VANZARE_CORESP NUMBER NOT NULL (PK, populat de trigger BEFORE INSERT din SEQ_VANZARI_CORESP)
ID_VANZARE_FACT NUMBER NOT NULL
ID_VANZARE_AVIZ NUMBER NOT NULL (nume generic mostenit — nu neaparat un aviz, vezi mai jos)
STERS NUMBER NULL
TIP NUMBER NOT NULL
```
Singurul trigger gasit pe tabela (`TRG_VANZARI_CORESP_BEFOINS`, prezent identic in schemele `ACN` si
`MARIUSM_AUTO`) doar genereaza PK-ul din secventa — nu atinge/valideaza `TIP`.
### Cine scrie in tabela — un singur punct, in tot codul Oracle
`grep` pe `all_source` (schema vie) dupa `VANZARI_CORESP` gaseste **un singur writer**:
`pack_facturare.scrie_corespondente_vanzari` (`PACK_FACTURARE:15481-15516`, singurul `INSERT INTO
VANZARI_CORESP` din toata baza). Nu exista alt pachet, trigger sau produs ROA (ROAGEST, ROAIMOB,
ROACONTRACTE, ROAACNPRO etc.) care sa scrie in aceasta tabela — `PACK_FACTURARE` traieste in `COMUN`,
partajat de toata suita, deci **orice** consumator ar trece prin acelasi punct.
### Valorile de `TIP` deja folosite — confirmate cod + date
**Cod** (singurele 3 apeluri la `scrie_corespondente_vanzari` din tot `PACK_FACTURARE`,
`:14818-14839`, in `finalizeaza_factura`):
- `TIP=1`: `WHEN pack_facturare.ntip = 4 THEN ... scrie_corespondente_vanzari(1)` — factura scrisa
dintr-un aviz (`ntip=4` = "FACT. DIN AVIZ"). Foloseste `pack_facturare.clistaid_avize` (lista
separata, populata pentru facturare-din-aviz cu selectie multipla).
- `TIP=2`: `WHEN pack_facturare.ntip = 24 THEN ... scrie_corespondente_vanzari(2)` — aviz de retur
(`ntip=24`).
- `TIP=3`: `WHEN pack_facturare.ntip IN (8, 9) THEN ... scrie_corespondente_vanzari(3)` — factura de
retur.
Toate trei folosesc `pack_facturare.nid_vanzare` (documentul curent, tocmai scris) ca
`ID_VANZARE_FACT`; pentru `TIP=1` sursa vine din `clistaid_avize`, pentru `TIP=2`/`TIP=3` (ramura
`ELSE` din `scrie_corespondente_vanzari`, `:15490-15491`) din `pack_facturare.clistaid` generic.
**Date** (schema `MARIUSM_AUTO`, interogare directa `SELECT tip, COUNT(*) FROM vanzari_coresp GROUP BY
tip`): `TIP=1` → 6 randuri, `TIP=2` → 1, `TIP=3` → 1. **Zero randuri cu alt `TIP`.** Coerent cu codul
(nu exista alt loc care sa scrie alte valori) — dovada dubla (cod + date), nu doar una din ele (per
memoria de proiect "zero cazuri in date nu e dovada": aici avem *si* absenta din date, *si* absenta
structurala in cod, ceea ce e o dovada tare, nu doar un data point izolat).
### Valoare noua libera
**`TIP=4` e liber**, confirmat pe ambele fronturi (cod: niciun apel existent la
`scrie_corespondente_vanzari(4)`; date: zero randuri `TIP=4` in schema testata). Recomandare: **`TIP=4`
= "factura scrisa dintr-o proforma"**, urmatorul numar disponibil in secventa deja folosita (1,2,3),
fara conflict cu niciun consumator existent (nu exista alt produs ROA care sa scrie sau sa citeasca
`VANZARI_CORESP` in afara de `PACK_FACTURARE`). Structura tabelei **nu se schimba** — doar o noua
valoare de enum in coloana `TIP`, exact cum a cerut misiunea.
### Unde s-ar scrie corespondenta — punct de intrare, cu rezerva importanta
Tiparul de azi (`TIP=1/2/3`) scrie corespondenta **din interiorul** `finalizeaza_factura`, intr-un
`CASE` cheie **`pack_facturare.ntip`** — semnalul de business-type al documentului nou scris. Aceasta
cheie **nu poate distinge** "factura rezultata dintr-o copiere de proforma" de "orice alta factura
obisnuita cu acelasi tip degradat" (vezi (b): rezultatul copierii unei proforme cade intotdeauna in
`{1,5,7,10}`, niciodata `4`/`24`/`8`/`9` — deci nu exista azi nicio ramura `WHEN` pe care s-o extinda,
si niciuna nu s-ar putea scrie corect doar din `ntip`, pentru ca acelasi `ntip=1` rezulta si dintr-o
copiere obisnuita de factura, nelegata de nicio proforma). Vezi Capcana (i) pentru raspunsul complet la
"de unde stie Oracle ca sursa a fost o proforma" si propunerea de proiectare pentru rezolvare.
## Capcana (i): supravietuieste id_fact/id_vanzare al sursei pana la scrierea corespondentei?
**Da, `id_vanzare` al proformei supravietuieste — dovedit pas cu pas — dar faptul ca sursa "era o
proforma" (nu doar "era un document oarecare") NU supravietuieste nicaieri azi. Asta e golul real.**
**Partea care functioneaza deja, neschimbata:**
1. `do_copiaza` (`COMUN\clase\ofacturare_comun.vc2:3690-3691`): `SELECT crsFacturi / SCATTER NAME
loFactura MEMO` — `loFactura` e o copie completa a randului sursa din grid, **inclusiv
`loFactura.id_vanzare`** (campul cheie) si `loFactura.eproforma` (coloana de grid confirmata la
`ofacturare_comun.vc2:2494`, `Column37.ControlSource = "eproforma"`) — **ambele disponibile in
acest moment**, inainte ca degradarea de tip (`:3693-3708`) sa ruleze.
2. `copiere_factura` (`COMUN\programe\oproceduri_facturare.prg:150-153`) → `factureaza(loFactura.tip,
loFactura)` — `loFactura` intreg (cu `id_vanzare` si `eproforma` inca pe el) devine `toFactura`,
parametrul opțional.
3. `completeaza_setari_document(toDateAnterior, .T.)`
(`COMUN\programe\ofacturare_comun.prg:362-412`), ramura de copiere: **`.listaid =
toDateAnterior.id_vanzare`** (`:387`) — **aici** `id_vanzare` al proformei ajunge in `poDate`,
proprietate care **nu e atinsa de degradarea de tip** (aceea opereaza doar pe `loFactura.tip`,
variabila locala din `do_copiaza`, complet separata de `poDate`).
4. `poDate.listaid` calatoreste neschimbat prin toata sesiunea (folosit si la §2.5 din
`s5b_proiectare_proforma_copiere.md` pentru `cursor_retur_document`), pana la scriere:
`do_scrie_articole` (`COMUN\clase\ofacturare.vc2:6104`): `Alltrim(Nvl(poDate.listaid,''))` e trimis
ca parametru `V_LISTAID` catre `pack_facturare.initializeaza_date_factura`, care il pune in
`pack_facturare.clistaid := V_LISTAID` (`PACK_FACTURARE:1883`) — **inainte** ca `do_scrie_factura`
sa aleaga `scrie_factura2`/`finalizeaza_factura`. La momentul in care `scrie_corespondente_vanzari`
ar rula, `pack_facturare.clistaid` **este deja** `id_vanzare`-ul proformei (ca text), exact valoarea
pe care ramura `ELSE` a lui `scrie_corespondente_vanzari` (`:15490-15491`, folosita azi de `TIP=2` si
`TIP=3`) o citeste.
**Partea care NU exista azi — golul real:** `completeaza_setari_document` (pasul 3 de mai sus) copiaza
explicit `id_lucrare`, `nrord`, `id_sectie`, `sectie`, `id_agent`, `nume_agent`, `id_delegat`,
`nume_delegat`, `BIdelegat`, `CNPdelegat`, `nrinmat`, `id_masina`, `listaid`, `descriere`, `id_client`,
`nume_client` de pe `toDateAnterior` — **dar niciodata `.eproforma`**. Documentul nou stie "din ce
`id_vanzare` a fost copiat" (`.listaid`), dar **nu stie daca acel document sursa era o proforma sau o
factura obisnuita** — informatia se pierde exact la acest pas, inainte sa ajunga la Oracle.
**De ce conteaza**: `finalizeaza_factura` (Oracle) decide azi ce `TIP` de corespondenta sa scrie
uitandu-se **doar** la `pack_facturare.ntip` (business-type-ul documentului nou). Cum am stabilit la
(b), rezultatul unei copieri de proforma cade intotdeauna in `{1,5,7,10}` — **acelasi interval** in
care cade si o copiere obisnuita (proforma sau nu). Oracle **nu poate reconstitui** din `ntip` singur
daca documentul curent a fost copiat dintr-o proforma sau dintr-o factura normala — nu exista niciun
semnal in `pack_facturare` care sa poarte aceasta distinctie.
**Ce trebuie adaugat, minimal** (propunere, nu implementare):
1. **VFP**: la `completeaza_setari_document:387`, langa `.listaid = toDateAnterior.id_vanzare`, adauga
o proprietate noua, de exemplu `.lProformaSursa = (toDateAnterior.eproforma = 1)` — un simplu boolean
client-side, capturat in acelasi moment si din acelasi obiect sursa unde `listaid` e deja capturat
(zero risc suplimentar de "se pierde intre timp", pentru ca foloseste exact acelasi tipar dovedit la
pasul 3-4 de mai sus).
2. **Scrierea corespondentei, NU prin `finalizeaza_factura`/`ntip`**: pentru ca `ntip` nu poate purta
distinctia (vezi mai sus), cea mai sigura ruta e un apel Oracle **explicit, separat**, facut din VFP
imediat dupa ce `do_scrie_factura` confirma succesul scrierii documentului nou, gardat de
`poDate.lProformaSursa`:
```
IF poDate.lCopiere AND poDate.lProformaSursa
lcSql = [{call pack_facturare.scrie_corespondente_vanzari(4)}]
* ... goExecutor.oExecute(lcSql) ...
ENDIF
```
Aceasta reutilizeaza **exact** procedura Oracle existenta, neschimbata (ramura `ELSE`,
`pack_facturare.clistaid`/`pack_facturare.nid_vanzare` inca valide in sesiune la acel moment, per
pasul 4 de mai sus) — **zero cod PL/SQL nou**, doar un nou punct de apel VFP si o noua valoare de
`TIP`. Alternativa (adaugarea unei ramuri noi in `CASE`-ul din `finalizeaza_factura`, cheie pe un
parametru nou trimis prin `V_PARAMETRU_ADITIONAL` sau similar) ar fi mai fragila: acel `CASE` e
punctul comun al **oricarei** facturi/aviz/retur scrise in tot codul, folosit de toate produsele ROA
prin `PACK_FACTURARE` partajat — o ramura noua acolo, keyed pe o combinatie de `ntip` + un flag nou,
ar creste suprafata unui switch deja incarcat, pentru un caz care oricum nu poate fi exprimat corect
doar din `ntip`. Apelul explicit izolat e mai simplu si nu atinge deloc `finalizeaza_factura`.
3. **Ordinea conteaza**: apelul explicit trebuie sa ruleze **inainte** ca orice alt cod Oracle din
aceeasi sesiune sa resetteze `pack_facturare.nid_vanzare`/`clistaid` (de exemplu, o alta scriere de
document in acelasi batch) — de plasat imediat dupa `do_scrie_factura`, inainte de listare/atasamente
(care oricum nu ating Oracle pentru partea de scriere). Daca se prefera robustete completa (fara
dependenta de stare de sesiune Oracle), varianta alternativa e sa se paseze explicit
`V_ID_VANZARE_FACT` (= `poDate.id_vanzare`, cunoscut client-side dupa scriere) si
`V_ID_VANZARE_PROFORMA` (= `poDate.listaid`, deja cunoscut) direct ca parametri, printr-o mica
procedura Oracle noua care ocoleste `clistaid`/`nid_vanzare` cu totul — cost: un nou obiect PL/SQL in
loc de zero, beneficiu: independenta de ordinea altor apeluri din sesiune. **Alegere ramasa lui
Marius** (vezi propunerea finala).
## Capcana (ii): `marcheaza_facturat` pe proforma — corect sau nu?
**Nu trebuie chemat pe proforma. Confirmat cu dovada, pe patru argumente convergente.**
**Ce face, exact** (`PACK_FACTURARE:15381-15418`):
```sql
PROCEDURE marcheaza_facturat(V_VERIFICARE IN NUMBER) IS
BEGIN
IF V_VERIFICARE = 0 THEN
UPDATE VANZARI SET FACTURAT = 1, ID_UTILFACT = pack_facturare.nid_util
WHERE ID_VANZARE IN (SELECT id_vanzare_aviz FROM vanzari_coresp
WHERE id_vanzare_Fact = pack_facturare.nid_vanzare
AND sters = 0 AND tip <> 3)
AND FACTURAT = 0;
ELSE
-- varianta "verificare cantitate ramasa": marcheaza FACTURAT=1 doar daca toata cantitatea
-- din VANZARI_DETALII a documentului sursa a fost deja consumata in VANZARI_CANTITATI
...
END IF;
END;
```
Flipeaza `VANZARI.FACTURAT=1` (+`ID_UTILFACT`) pe **documentul(ele) sursa** legate prin
`VANZARI_CORESP` de documentul tocmai scris — fie neconditionat (`V_VERIFICARE=0`), fie doar cand
cantitatea documentului sursa a fost epuizata (`V_VERIFICARE=1`, mecanismul de **facturare partiala** a
avizelor, bazat pe `VANZARI_CANTITATI`).
**Argumentul 1 — nu exista un `TIP` cu care sa se cupleze corect azi.** `marcheaza_facturat` se cheama
azi **doar** din `finalizeaza_factura`, in aceleasi doua ramuri `WHEN pack_facturare.ntip = 4` si
`WHEN pack_facturare.ntip = 24` care scriu si corespondenta (`:14823-14831`) — cuplate mereu impreuna cu
`scrie_corespondente_vanzari(1)`/`(2)`, niciodata separat. Cum am stabilit la Capcana (i), calea propusa
pentru `TIP=4` (factura din proforma) e un **apel explicit separat**, in afara acestui `CASE` — deci
n-ar exista niciun loc "natural" unde `marcheaza_facturat` sa se agate fara sa introduca exact acelasi
tip de cod nou-scris ca la corespondenta insasi.
**Argumentul 2 — mecanismul de "cantitate ramasa" (`V_VERIFICARE=1`) nu are pe ce sa opereze pentru o
proforma.** Verificarea citeste `VANZARI_CANTITATI` (populat **doar** de
`scrie_cantitati_vanzari_avize`, apelata **doar** in ramura `ntip=4` a lui `finalizeaza_factura`).
Proforma nu trece niciodata prin `finalizeaza_factura` (confirmat in
`s5b_proiectare_proforma_copiere.md`, "Descoperire centrala": `scrie_proforma` cheama doar
`scrie_in_vanzari`) — deci **nu exista niciodata randuri `VANZARI_CANTITATI` pentru o proforma**.
Varianta `V_VERIFICARE=1` a lui `marcheaza_facturat` ar gasi mereu "cantitate ramasa = cantitate totala"
(nimic consumat), fie nu ar marca niciodata `FACTURAT=1` (comportament inutil), fie (daca s-ar folosi
gresit `V_VERIFICARE=0`) ar marca neconditionat, ca la punctul urmator.
**Argumentul 3 — asimetrie la stergere: proforma ar ramane blocata `FACTURAT=1` definitiv.**
`sterge_factura` (`PACK_FACTURARE:5501-5533`) reface `FACTURAT=0` pe documentele sursa **doar** pentru
`V_TIP=24` (aviz retur, `:5502-5510`) si `V_TIP=4` (factura din aviz, `:5525-5533`) — tipurile care azi
chiar cheama `marcheaza_facturat`. Rezultatul copierii unei proforme cade intotdeauna in `{1,5,7,10}`
(per (b)) — **niciuna din aceste valori nu are ramura in `CASE`-ul de stergere**. Daca s-ar chema
`marcheaza_facturat` la scrierea facturii din proforma, iar utilizatorul ar sterge ulterior acea
factura, **proforma sursa ar ramane cu `FACTURAT=1` pentru totdeauna** — o stare orfana, fara niciun
cod care s-o repare, introdusa exact de acest apel. Corespondenta `VANZARI_CORESP` insasi nu are aceeasi
problema (nimic n-o citeste ca sa se strice daca ramane "orfana" dupa stergerea facturii — cel mult
devine o legatura catre un document sters, inofensiv).
**Argumentul 4 — nimic nu filtreaza azi proformele dupa `FACTURAT`, deci n-ar exista niciun beneficiu
de blocat.** Spre deosebire de avize (`cursor_avize`/candidatii de facturat, filtrati implicit prin
fluxul dedicat "Factura din aviz") si comenzi (`VCOMENZI.FACTURAT=0`, folosit explicit in
`cauta_date_comanda`/`cauta_date_comanda_gest`, `PACK_FACTURARE:15659,15685`, ca sa nu ofere din nou o
comanda deja facturata), **nimic in codul citit** filtreaza dupa `FACTURAT` cand se alege o proforma ca
sursa de copiere — confirmat la (a): `IsCopy`/vizibilitatea butonului/filtrul de grid nu se uita
niciodata la `facturat`. Marcarea n-ar preveni nicio re-copiere accidentala a aceleiasi proforme (care
oricum ramane posibila, vezi propunerea finala, punctul de decizie 2).
**Concluzie**: `marcheaza_facturat` **nu trebuie chemat** pentru "factura din proforma". Se scrie
**doar** corespondenta (`VANZARI_CORESP`, `TIP=4`), fara actualizare de `VANZARI.FACTURAT` pe proforma.
Confirma explicit suspiciunea din misiune ("proforma nu e document de livrare") cu evidenta concreta:
proforma n-are urma in `VANZARI_CANTITATI` (Argumentul 2), n-are ramura de reversare la stergere
(Argumentul 3), si n-are niciun consumator care sa citeasca `FACTURAT` pe ea (Argumentul 4).
## Propunere de proiectare
Mecanismul de baza **ramane copierea existenta** (`do_copiaza`/`copiere_factura`/`factureaza`,
neschimbata in structura ei) — cerinta "document nou, nu comutare pe acelasi document" e deja
satisfacuta de calea de azi (confirmat la (a)/(b)). Ce lipseste e strict **legatura persistata**
proforma → factura si excluderea explicita a lui `marcheaza_facturat`. Pasi, in ordine:
1. **Captureaza `eproforma` al sursei la copiere.** In `completeaza_setari_document`
(`COMUN\programe\ofacturare_comun.prg:387`, ramura `tlFactura=.T.`), langa
`.listaid = toDateAnterior.id_vanzare`, adauga o proprietate noua pe `poDate`
(ex. `.lProformaSursa`), citind `toDateAnterior.eproforma` — singurul loc unde informatia mai e
disponibila, inainte sa se piarda (Capcana (i)). *Gata cand:* dupa copierea unei proforme,
`poDate.lProformaSursa = .T.`; dupa copierea oricarui alt document, `.F.`.
2. **Rezerva `TIP=4`** in `VANZARI_CORESP` pentru "factura scrisa dintr-o proforma" — doar o conventie
documentata (ca la 1/2/3), zero schimbare de schema (confirmat la (c): tabela ramane
`ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP, STERS`).
3. **Scrie corespondenta cu un apel Oracle explicit, separat de `finalizeaza_factura`.** Imediat dupa
ce `do_scrie_factura` confirma succesul (acelasi punct unde azi se decid listarea/atasamentele),
daca `poDate.lCopiere AND poDate.lProformaSursa`: `{call
pack_facturare.scrie_corespondente_vanzari(4)}` — **reutilizeaza procedura Oracle existenta,
neschimbata** (ramura `ELSE`, deja scrisa pentru `TIP=2`/`3`). Zero cod PL/SQL nou pentru scriere.
*Alternativa mai robusta, cu cost:* o mica procedura Oracle noua care primeste explicit
`V_ID_VANZARE_FACT`/`V_ID_VANZARE_PROFORMA` ca parametri (nu se bazeaza pe
`pack_facturare.clistaid`/`nid_vanzare` inca valide in sesiune) — de ales intre simplitate (varianta
de mai sus) si robustete fata de ordinea apelurilor (varianta cu parametri expliciti); vezi punctul
de decizie 1 mai jos.
4. **NU cheama `marcheaza_facturat`.** Confirmat cu 4 argumente independente la Capcana (ii) — proforma
nu are `VANZARI_CANTITATI`, nu are ramura de reversare la stergere, nimic n-o filtreaza dupa
`FACTURAT`, si oricum n-ar exista un `WHEN pack_facturare.ntip=...` natural de unde s-o cheme (calea
aleasa la pasul 3 e explicit separata de `finalizeaza_factura`).
5. **Afisare optionala "provine din proforma X"** pe factura noua — se poate construi din
`VANZARI_CORESP` (`TIP=4`) la fel ca tiparul deja folosit pentru retur (`legatura_linie_retur.md`
§5): la nivel de **document**, nu de linie (nicio schimbare fata de tiparul deja acceptat pentru
retur — nu exista niciun tabel de legatura la nivel de linie in tot codul citit, per acelasi raport).
Nu e cerut explicit de misiune, dar e disponibil "gratis" din legatura scrisa la pasul 3.
**Ce NU se schimba** (confirmat, fara nevoie de atingere):
- `IsCopy`, vizibilitatea `But_copiaza1`, filtrul de grid "Facturi&Avize / Proforme" — proforma ramane
selectabila ca sursa exact ca azi (a).
- Degradarea de tip din `do_copiaza` (`{1,5,7,10}` pentru orice proforma) si alocarea de numar nou —
raman neschimbate, deja produc `nIdTipDoc=5`/`eproforma=0` corect pentru documentul nou (b).
- `cursor_retur_document`, restaurarea `GESTIONABIL=B.IN_STOC` la copiere — neschimbat, mecanism deja
corect pentru orice copiere (mostenit din `s5b_proiectare_proforma_copiere.md` §7).
- `scrie_corespondente_vanzari` insasi — zero cod PL/SQL nou (varianta recomandata la pasul 3).
### Riscuri si ce ramane de decis de Marius
1. **Apel explicit pe stare de sesiune (`clistaid`/`nid_vanzare`) vs. procedura noua cu parametri
expliciti** (pasul 3) — simplitate (zero cod Oracle nou, dar depinde ca nimic altceva sa nu resetteze
starea pachetului intre scrierea facturii si apelul de corespondenta) vs. robustete (un obiect
PL/SQL nou, insensibil la ordine). Recomandare: varianta simpla, **daca** apelul se plaseaza imediat
dupa `do_scrie_factura`, inainte de orice alt apel Oracle din acelasi flux (listare/atasamente nu
ating Oracle pentru scriere) — de verificat pe cod exact la implementare ca nu exista un apel Oracle
intercalat care ar reseta `pack_facturare.nid_vanzare`.
2. **Poate fi copiata de mai multe ori aceeasi proforma?** Azi nu exista nicio garda (FACTURAT nu se
marcheaza — decizia de la Capcana (ii)), deci un utilizator poate genera N facturi din aceeasi
proforma, fiecare cu propriul rand `TIP=4` in `VANZARI_CORESP` catre aceeasi proforma sursa. E
comportamentul implicit al oricarei copieri azi (nimic n-o limiteaza nici pentru facturi/avize
normale) — de confirmat daca e acceptabil sau daca se doreste un avertisment (nu o blocare, pentru
ca ar contrazice tiparul existent de copiere liber-repetabila).
3. **Guard simetric la stergere?** `sterge_factura` blocheaza azi stergerea unui aviz/unei facturi care
are deja o factura de retur legata (`TIP=3`) sau facturi/avize-retur legate (`TIP IN (1,2)`,
`PACK_FACTURARE:5450-5476`). Nu exista cerinta explicita in misiune pentru un guard simetric pe
`TIP=4` (ex. "nu poti sterge o proforma care are deja o factura generata din ea") — de decis daca se
doreste, sau daca proforma ramane liber stersa oricand (comportament actual, neschimbat daca nu se
adauga nimic).
4. **Afisarea "provine din proforma X"** (pasul 5) — optionala, nu ceruta explicit; de decis daca
merita implementata acum sau ramane pentru o poveste ulterioara (costul e mic, data fiind legatura
deja scrisa la pasul 3).
5. **`TIP=4` ca valoare aleasa** — libera si fara conflict azi (confirmat cod+date), dar e o alocare
ireversibila din punct de vedere al datelor odata folosita in productie; de confirmat explicit de
Marius inainte de implementare (nu doar acceptata tacit).
## STARE / CE RAMANE
**Cercetare + proiectare incheiate.** Toate cele trei intrebari (a/b/c) si ambele capcane (i/ii) au
raspuns cu dovada `fisier:linie`, verificat pe fisierele text reale (`ofacturare_comun.vc2`,
`ofacturare.vc2`, `ofacturare_comun.prg`, `ofacturare.prg` — nu `.bak`) si pe `PACK_FACTURARE`
(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`), plus verificare directa pe schema Oracle vie
(`MARIUSM_AUTO`: structura `VANZARI_CORESP`, distributia `TIP` din date, singurul trigger existent).
Descoperirea centrala a acestei cercetari: mecanismul de copiere de azi rezolva deja "document nou" si
"tip corect" (a, b) fara nicio schimbare, dar **pierde tacit** informatia "sursa era o proforma" chiar
in metoda care ar trebui s-o pastreze (`completeaza_setari_document:387`, care copiaza `listaid` dar nu
si `eproforma`) — motiv pentru care legatura `VANZARI_CORESP` nu se poate agata de `CASE`-ul existent
din `finalizeaza_factura` (cheie doar pe `ntip`, care nu poarta aceasta distinctie) si are nevoie de un
semnal nou, client-side, plus un apel Oracle explicit separat (pasii 1 si 3 din propunere).
Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, niciun commit, nicio scriere pe
Oracle (doar `SELECT`, rulat cu `sqlplus.exe` pe schema `MARIUSM_AUTO`).