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

438 lines
29 KiB
Markdown

# S4g — Adaugarea de articole la modificarea oricarui document (proiectare)
Proiectare, nu implementare. Zero fisiere de cod atinse, zero write-back, zero commit. Pe Oracle
doar `SELECT`. Continua planul `docs\plan_13_unificare_formular_facturare.md` liniile 2494-2530 si
cercetarile `canal_cont_venit_fara_politica.md` (sectiunea "Proiectarea parametrului de cont
contabil"), `parametru_cont_contabilizeaza_articol.md`, `coresp_cont_venchelt.md`,
`optiune_firma_cont_debit.md`.
**STARE: cercetare/proiectare incheiata.**
Sursa PL/SQL verificata direct in aceasta runda: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`
(17217 linii, confirmat `wc -l`). Cod VFP citit: `COMUN\clase\ofacturare.vc2`,
`COMUN\programe\ofacturare_comun.prg` (ambele permise). Neatinse: `COMUN\clase\ofacturare_comun.vc2`,
`COMUN\programe\ofacturare_editare.prg` (perimetrul sesiunii #6).
---
## 0. Premisele deja decise (nu se redeschid)
- Povestea: a doua sursa de articole in meniu, "Alege din nomenclator...", disponibila si la
modificarea oricarui document deja emis, inclusiv auto (tip = -12, dar livrat separat, dupa).
- Decizia 20: din nomenclator se ofera si gestionabile, si negestionabile — nu se copiaza filtrul
`in_stoc = 0` al lui ROAAUTO.
- Decizia 21: contul de gestiune are fallback, nu NULL, nu refuz — mecanism separat de cel de aici
(priveste `id_gestiune`/`Cont` de gestiune al liniei, nu contul de venit), presupus deja rezolvat
cand derivarea din sectiunea 3 porneste.
- Decizia 27 (transportul inlocuit de decizia 34): contul de venit se **deriva** in VFP —
`CORESP_CONT_VENCHELT` pentru gestionabile (pe contul de gestiune al liniei), `NOM_ARTICOLE.CONT`
daca e 6xx/7xx pentru negestionabile, altfel `704`.
- Decizia 34: transportul e parametru nou, direct — nu prin `id_pol`/politica tehnica. Ocolul prin
`pack_preturi.adauga_politica_pret_art` e abandonat.
- Decizia 35: acelasi drum serveste si editarea prin regenerare — nu se proiecteaza a doua ruta de
contare.
- Decizia 36 (noua, 10.08.2026): `SCD` pe ramura fara politica nu e hardcodat `'4111'` literal —
vine dintr-o optiune de firma (`getoptiunefirma`), cu `4111` ca implicit dublu (rand in `OPTIUNI`
+ fallback hardcodat in PL/SQL), tiparul deja folosit de `RF_CONT_INCASARE_*` in acelasi pachet.
- "Alte servicii" ROAAUTO ocoleste complet `contabilizeaza_articol` — exceptie, nu jumatate de
mecanism.
- Regula deja adoptata in #13: liniile libere nu intra deloc in `crsarticole` — `APPEND BLANK` +
`combosql` direct in `crsfactura`, ca `id_c` sa ramana `0` si `do_sterge` sa fie no-op prin
constructie (verificat aici ca acelasi tipar se aplica si campului `id_pol`, sectiunea 4).
- Ordine: intai ROAFACTURARE, apoi tip = -12.
---
## 1. Punctul central: parametru vs politica, cine castiga
### 1.1 Ce face azi codul, verificat direct pe sursa (nu preluat din rapoarte)
`contabilizeaza_articol` (`ff_...:7173-7547`) primeste un singur parametru,
`detalii_articol VANZARI_DETALII_TEMP%ROWTYPE`. Primul lucru pe care il face (`:7275-7302`):
```sql
BEGIN
SELECT COMPUS, ID_POL_ART
INTO V_COMPUS, V_ID_POL_ART
FROM VCRM_POLITICI_PRET_ART
WHERE ID_ARTICOL = detalii_articol.id_articol
AND ID_POL = detalii_articol.id_pol;
EXCEPTION
WHEN NO_DATA_FOUND THEN
...
RAISE_APPLICATION_ERROR(-20000, 'Articolul ... nu este definit in politica de preturi ... (FACT-024)');
END;
```
Daca `detalii_articol.id_pol` e `NULL`, comparatia `ID_POL = NULL` nu se potriveste niciodata —
`NO_DATA_FOUND` garantat, FACT-024 garantat. Acelasi rezultat daca `id_pol` e populat dar articolul
nu e in acea politica. **`cursor_articol`** (`:7218-7271`), care face tot lucrul real (`scrie_nota`,
`descarca_gestiune`, discount), e filtrat **pe exact aceeasi cheie** (`A.ID_POL = detalii_articol.id_pol
AND A.ID_ARTICOL = detalii_articol.id_articol`, `:7270-7271`) — daca `SELECT INTO` de mai sus a picat
in `NO_DATA_FOUND`, cursorul ar gasi tot zero randuri (aceeasi cauza). Deci punctul de intrare in
politica e unic si dublu-verificat (o data prin exceptie explicita, o data structural prin cursor).
Design-ul deja facut (`canal_cont_venit_fara_politica.md:132-138`) propune sa infasoare **toata
functia** intr-un switch la nivel de metoda:
```
IF detalii_articol.cont_venit IS NOT NULL THEN
<ramura noua: SCC := cont_venit, SCD := optiune de firma (decizia 36), fara cursor, fara SELECT INTO de mai sus>
ELSE
<tot codul de azi, neschimbat, inclusiv SELECT INTO + FACT-024 + cursor_articol>
END IF;
```
**Aceasta e deja, prin constructie, varianta "parametrul castiga necondiionat cand e populat"** —
cand `cont_venit` nu e `NULL`, executia nu se mai uita deloc la `id_pol`/politica, indiferent daca
acestea ar fi rezolvat sau nu. Team-lead-ul cere explicit sa se decida asta ca punct de proiectare,
nu sa ramana o consecinta implicita a formei codului — mai jos analiza celor 3 variante realiste.
### 1.2 Cele 3 variante
**A. Parametrul castiga mereu cand e populat** (= design-ul existent, ca atare).
- *Ce se strica*: daca, dintr-un motiv oarecare (bug VFP, sau — mai realist — decizia 35: la
**regenerare**, daca logica de derivare a lui `cont_venit` ar rula necondiionat pe toate liniile
documentului, inclusiv cele scrise initial prin "Cauta in lista de preturi..." cu `id_pol` real),
o linie ajunge la Oracle cu **ambele** populate, `SCC`-ul politicii reale e inlocuit tacut cu
contul derivat generic, `SCD` cu optiunea de firma (posibil diferita de `NOTE_CONTABILE.SCD` al
notei reale), `ASCD`/`ASCC`/`EXPLICATIE`/`CU_TVA` cu variantele generice ale ramurii noi. **Fara
nicio eroare** — factura se emite, dar cu alta contare decat cea configurata prin politica. Cel
mai periculos tip de defect: tace si trece verificarea vizuala (suma corecta, cont plauzibil).
**B. Politica castiga mereu cand `id_pol` e populat** (indiferent daca rezolva sau nu).
- Ar cere schimbarea conditiei de switch din `cont_venit IS NOT NULL` in ceva de forma
`id_pol IS NULL AND cont_venit IS NOT NULL` — adica: daca exista `id_pol` pe linie, mergi pe
ramura veche necondiionat, chiar daca acel `id_pol` nu rezolva (caz FACT-024 de azi).
- *Ce se strica*: exact scenariul in care mecanismul nou ar fi cel mai util — o linie cu `id_pol`
populat gresit/stale (nu neaparat imposibil, doar improbabil in fluxul normal, vezi 1.3) —
ar continua sa primeasca FACT-024 desi VFP a trimis explicit un cont de rezerva. Varianta cea mai
fragila: defineste "castigatorul" pe **prezenta** unui camp, nu pe **rezolvarea** lui.
**C. Parametrul e folosit doar cand politica nu rezolva** (fallback real, nu switch pe camp).
- Cere ca `SELECT INTO ... FROM VCRM_POLITICI_PRET_ART` (1.1) sa ramana **necondiionat**, exact ca
azi (deja rulat pe fiecare linie, cost zero suplimentar), dar `EXCEPTION WHEN NO_DATA_FOUND` sa
verifice `cont_venit`: daca e populat, ramura noua; daca nu, `RAISE FACT-024` ca azi. Semantic
corect — politica, cand exista si rezolva, nu e niciodata inlocuita tacut; parametrul e strict ce
a fost gandit sa fie: o plasa pentru cazul in care politica lipseste.
- *Cost real*: restructurare interna a functiei, nu doar un `IF` la inceput. Codul de dupa blocul
`EXCEPTION` verifica azi `IF V_COMPUS = 1 THEN ... ELSE <deschide cursor_articol> END IF`
(`:7305,7391`) — `V_COMPUS` ramane `NULL` daca s-a intrat pe `EXCEPTION`, deci "cade" oricum pe
ramura `ELSE` care deschide `cursor_articol` (gaseste zero randuri, nu declanseaza nimic, dar nici
ramura noua). Trebuie introdus un flag explicit (`V_ARE_POLITICA`/similar) propagat din interiorul
`EXCEPTION` pana la punctul de decizie, marind suprafata modificata fata de varianta A (care
atinge doar granita functiei, cu restul intact).
### 1.3 Ar putea sa apara vreodata, in fluxul normal, ambele populate?
Verificat direct in aceasta runda (nu presupus): `crsfactura` (cursorul VFP din care se scrie orice
linie) are `id_pol N(20) Null` (`COMUN\programe\ofacturare_comun.prg:1775`) — camp nullable, deci la
`APPEND BLANK` (mecanismul liniilor libere, confirmat de S4e ca acelasi gest se foloseste si pentru
"Alege din nomenclator...") ramane `.NULL.` implicit, nu `0`. La scriere, `poArt.id_pol` (scatter
din randul respectiv) ajunge in apelul RPC ca `Nvl(Alltrim(Str(poArt.id_pol)),[NULL])`
(`ofacturare.vc2:14072`, identic la `:18107`) — literal SQL `NULL` cand campul e `.NULL.`. **O linie
"din nomenclator" trimite deci `id_pol = NULL` la Oracle prin constructie, nu doar prin conventie de
utilizare** — cursorul de cautare al nomenclatorului (`caut_articol`, `ocautare.prg`, deja confirmat
de `coresp_cont_venchelt.md` sectiunea 9d) nu are coloana `id_pol` in output, deci nu exista niciun
punct in care combosql-ul ar putea popula acest camp cu o valoare reala pentru o astfel de linie.
Ramane un singur scenariu real de ambiguitate: **regenerarea** (decizia 35). La regenerare, toate
liniile documentului (cele scrise initial prin "Cauta in lista de preturi...", cu `id_pol` real, **si**
cele adaugate prin "Alege din nomenclator...", fara `id_pol`) trec din nou prin acelasi drum de
scriere. Daca logica VFP care calculeaza `cont_venit` (sectiunea 3) ar rula **necondiionat** pe toate
liniile din grid, in loc sa fie conditionata explicit de "linia nu are `id_pol`", ar produce exact
combinatia periculoasa din varianta A. **Asta nu e un risc Oracle — e un risc de implementare VFP**,
dar exact tipul de risc pe care design-ul Oracle trebuie sa nu-l agraveze printr-un switch care il
face invizibil.
### 1.4 Recomandare
**Niciuna dintre cele 3 variante pure, ci varianta A (deja proiectata, cea mai simpla) plus o garda
explicita pe combinatia ambigua**, nu o rezolvare tacita in orice directie:
```sql
IF detalii_articol.cont_venit IS NOT NULL THEN
IF detalii_articol.id_pol IS NOT NULL THEN
RAISE_APPLICATION_ERROR(-20000,
'Articolul ' || detalii_articol.id_articol ||
' are simultan politica de pret (' || detalii_articol.id_pol ||
') si cont de venit calculat — conflict netratat (FACT-0xx)');
END IF;
<ramura noua, ca in design>
ELSE
<tot codul de azi, neschimbat>
END IF;
```
Motivare, in ordine:
1. **Combinatia nu trebuie sa apara niciodata in fluxul normal** (1.3) — `id_pol` ramane `NULL` prin
constructie pentru orice linie scrisa prin gestul "linie libera". Daca totusi apare, e un semnal
ca ceva e stricat in populate-ul liniei (VFP a trimis ambele campuri, posibil din cauza logicii
de regenerare descrisa la 1.3) — situatie de bug de investigat, nu de "rezolvat" silentios.
2. **Variantele B si C rezolva combinatia in cate o directie fixa** — ambele ascund o eroare de date
in loc sa o semnaleze: B ar putea reintroduce FACT-024 pe o linie unde VFP a oferit deja o
solutie; A fara garda ar inlocui tacut o politica reala. O garda explicita e singurul comportament
care nu presupune ca stie mai bine decat datele ce s-a intamplat.
3. **Cost de implementare minim**: un singur `IF` suplimentar, la intrarea in ramura deja proiectata
— nu restructureaza funcia (spre deosebire de varianta C), nu schimba conditia switch-ului
principal (spre deosebire de B).
4. **Compatibilitate/regresie zero pe apelantii de azi**: cand `cont_venit` e `NULL` (toti apelantii
existenti, care nu cunosc inca acest parametru), executia intra direct pe `ELSE` — garda nu se
evalueaza niciodata, comportamentul e identic bit-cu-bit cu azi. Cand `cont_venit` e populat si
`id_pol` e `NULL` (cazul intentionat, "din nomenclator"), garda trece nevazuta, ramura noua
ruleaza normal.
**Numele codului de eroare** (`FACT-0xx` in schita de mai sus) ramane de ales de Marius, distinct de
`FACT-024` (care ramane, neschimbat, pentru cazul "niciuna din cele doua" — linie fara politica si
fara cont).
---
## 2. Suprafata de schimbare pe Oracle
Deja proiectata si verificata adversarial in `canal_cont_venit_fara_politica.md` si
`parametru_cont_contabilizeaza_articol.md`; rezumat + completarea cu decizia 36 si garda de la
sectiunea 1.4:
1. **Coloana noua**: `VANZARI_DETALII_TEMP.CONT_VENIT VARCHAR2(4) NULL` (fara `NOT NULL`, fara
`CHECK` — acelasi tipar ca `ACT_TEMP.SCC`). Optional, recomandat pentru trasabilitate:
`VANZARI_DETALII.CONT_VENIT`, plus extinderea listei explicite de coloane din
`scrie_in_vanzari` (`PACK_FACTURARE:13705-13757`, confirmat ca listeaza 24 coloane explicit, nu
`SELECT *` — de extins cu o a 25-a daca se alege trasabilitatea).
2. **Parametru nou pe `adauga_articol_factura`** (`ff_...:4989-5015`), la coada, dupa `V_LOT`:
`V_CONT_VENIT IN VARCHAR2 DEFAULT NULL` — apelul VFP existent (pozitional, se opreste la
`V_LOT`, confirmat la doua locuri, `ofacturare.vc2:14069-14091` si `:18089-18114`, doua clase
distincte cu aceeasi metoda, nu o duplicare) ramane neschimbat, echivalent cu "trimite NULL".
Plumbing identic cu `V_CONT`/`V_CONT2`, o coloana in plus in `INSERT INTO VANZARI_DETALII_TEMP`
(`ff_...:5222-5282`).
3. **`contabilizeaza_articol` insasi NU primeste parametru nou** — ramane `VANZARI_DETALII_TEMP%ROWTYPE`;
coloana noua ajunge automat prin `SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP`
(`:6063`), fara nicio schimbare la cei 3 apelanti interni (`scrie_factura2`,
`scrie_factura_avize_retur`, `scrie_aviz_retur`).
4. **Ramura noua in `contabilizeaza_articol`**, cu garda de la sectiunea 1.4:
- `SCC := detalii_articol.cont_venit`.
- `SCD` (decizia 36): `PACK_SESIUNE.getoptiunefirma('FACT_SCD_ARTFPRET')`, cu fallback hardcodat
`'4111'` cand optiunea lipseste/e goala — optiune noua in tabelul `OPTIUNI`
(`VARTYPE='CHARACTER'`, script de migrare idempotent, tiparul exact al `RF_CONT_INCASARE_*`,
deja folosit in acelasi pachet, `optiune_firma_cont_debit.md` sectiunea 3.4). Ramurile de aviz
raman `'418'`/`'461'`, neschimbate.
- `ASCD`/`ASCC` din `GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCD/V_SCC)` — acelasi
fallback deja folosit necondiionat pe ramurile de aviz azi.
- `EXPLICATIE` din `detalii_articol.explicatia` (parametru deja existent, `V_EXPLICATIE`).
- `ID_VENCHELT`/`ID_SECTIE` din `pack_facturare.nid_venchelt`/`nid_sectie_stoc` (fallback de
sesiune deja folosit ca prioritate azi).
- `IN_VALUTA` din `pack_facturare.nin_valuta` (parametru obligatoriu, fara `DEFAULT`, mereu
curent — nu o presupunere, sursa solida).
- `CU_TVA`: hardcodat `1`, **cu efect colateral real si masurat, nu "inofensiv"** —
`parametru_cont_contabilizeaza_articol.md` sectiunea 2b arata ca pe combinatia specifica "linie
fallback cu TVA 0% + discount global pe factura + acea linie e prima/singura vazuta" valoarea
forteaza `nproc_tva_max`/`nid_jtva_coloana` pentru toata linia de discount a facturii. Ramane
"de confirmat cu Marius", cu argumentul mai tare decat in propunerea initiala.
- Apeluri o singura data (nu in bucla): `scrie_nota(...)`, `descarca_gestiune(...)` (garda
identica, `nscadere_stoc=1 AND id_gestiune<>-1000 AND in_stoc=1`), discount (garda identica).
- Articole compuse (`V_COMPUS=1`) exclus structural — o linie fara `id_pol` nu poate avea
`ID_POL_ART`, deci intrebarea "e compus" nu se poate pune pe aceasta ramura (confirmat pe
schema view-ului, nu presupus).
5. **Ordinea de deploy**: DB inainte de EXE. Parametrul nou are `DEFAULT NULL`, deci EXE-ul vechi
ruland pe DB-ul nou functioneaza neschimbat (nu trimite parametrul). EXE-ul nou ruland pe DB-ul
vechi (fara coloana/parametru) ar esua la primul apel cu al 27-lea argument — deci EXE-ul nou
**nu** poate merge inaintea migrarii DB. Ordine standard, fara surpriza.
6. **Regenerarea (decizia 35)**: `initializeaza_date_factura` reseteaza starea de sesiune
(`DELETE FROM VANZARI_DETALII_TEMP`, `nid_act := 0`) la fiecare emitere — nimic din ramura noua
citeste vreo stare presupunand "documentul e nou". Calea Oracle e identica la regenerare, **cu o
exceptie separata, in alt pachet**: reemiterea cu acelasi `ID_FACT` ar da azi `ORA-00001` pe
`PK_DOCUMENTE` in `PACK_CONTAFIN.SET_IDFACT` — problema deja proiectata separat
(`idfact_refolosire_si_documente.md`), nu intersecteaza si nu invalideaza design-ul de aici, dar
ramane o bucata de lucru distincta, necesara pentru ca regenerarea sa fie completa.
7. **Gol neadresat inca**: ramura `ntip = 4` ("factura din avize", `:7520-7537`) apeleaza
`scrie_fact_aviz_custodie` in loc de `descarca_gestiune`+discount — design-ul nu specifica ce se
intampla pe fallback aici. In practica improbabil (o linie de aviz sursa fara politica n-ar fi
ajuns la factura pe acest drum, `adauga_articol_factura:5096` cere deja `A.ID_POL = V_ID_POL` pe
avizul sursa), dar de exclus explicit inainte de implementare, nu de presupus tacit.
---
## 3. Derivarea contului in VFP
### 3.1 Sursele, in ordine (decizia 27)
1. **Articol gestionabil** (are `id_gestiune` valid, `in_stoc=1`): `CORESP_CONT_VENCHELT.CONT_VENIT`,
cautat pe **contul de gestiune al liniei** (`poArt.Cont`, camp deja gathered pe `poArticol` in
ambele metode `do_scrie_articole`, confirmat la `ofacturare.vc2:12945,13835` si echivalentele in
`frm_facturare_articole2`). Cheia de join e mereu `CONT` (nu `ID_GESTIUNE`, nu `TIP_DOC`) —
confirmat pe 4+ consumatori Oracle reali ai tabelului (`pack_vin`, `pack_devize`,
`gestiune_pack_gest_import`, un raport din 2026), niciodata pentru `CONT_VENIT` insa — aceasta
propunere ar fi **primul consumator real** al coloanei, desi populata din 2023 (20 conturi de
gestiune uzuale: 301-303/3021-3028, 331/332, 341, 345/346/348, 361, 371, 381). View-ul VFP-safe e
`VCORESP_CONT_VENCHELT` (`STERS=0` deja filtrat in view).
2. **Articol negestionabil**, sau gestionabil dar corespondenta nu rezolva (rand lipsa pentru acel
`CONT`): `NOM_ARTICOLE.CONT`, **doar daca** primul caracter e `6` sau `7` (`verific_cont` nu
restrange campul la o clasa, deci poate contine legitim orice cont valid, inclusiv 6xx/7xx pe un
articol negestionabil).
3. **Fallback final**: literal `'704'`. Fara reteta de cod deja scrisa in `pack_facturare` pentru
aceasta valoare, dar cu un precedent independent de "704 = cont implicit client" in alt subsistem
(`anaf_efactura.vc2:8376`, `cconte = 704`) — nu o inventie, dar cod nou.
### 3.2 Unde sta codul si cand ruleaza
Locul natural: chiar in `do_scrie_articole` (ambele clase, `frm_facturare_articole` si
`frm_facturare_articole2`, confirmat ca 2 locuri distincte de atins, nu 1), imediat inainte de
construirea textului RPC pentru fiecare linie, **conditionat explicit de `Empty(Nvl(poArt.id_pol,0))`**
— nu la momentul adaugarii liniei in grid. Doua motive:
- **Regenerarea (decizia 35)** re-parcurge toate liniile la fiecare scriere; derivarea la momentul
scrierii (nu la adaugare) garanteaza ca valoarea reflecta starea curenta a corespondentelor/
nomenclatorului, nu una inghetata la momentul in care linia a fost adaugata initial in grid.
- **Conditionarea explicita pe `id_pol` gol** (nu pe "linia vine din nomenclator" ca marcaj separat)
e chiar garda ceruta la sectiunea 1.3-1.4: o linie cu `id_pol` populat nu trebuie sa primeasca
niciodata o valoare pe `cont_venit`, indiferent de sursa ei — un singur punct de decizie, in
oglinda exacta cu conditia pe care Oracle o va verifica la randul lui (sectiunea 1.4).
### 3.3 Ce se intampla cand derivarea esueaza
- **Corespondenta nu are rand pentru acel `CONT`** (gestiune fara corespondenta configurata): cade pe
pasul 2 (`NOM_ARTICOLE.CONT`), nu pe eroare.
- **`NOM_ARTICOLE.CONT` nu incepe cu 6/7** (cont de gestiune sau alt tip pe un articol negestionabil,
configurare atipica): cade pe pasul 3 (`704`), nu pe eroare.
- **Niciodata `NULL`/gol trimis la Oracle pentru o linie "din nomenclator"** — ultimul pas e un
literal, nu o interogare care poate esua. Aceasta e proprietatea care face garda de la 1.4
inofensiva pe fluxul normal: `cont_venit` e *intotdeauna* populat cand `id_pol` e gol, deci
ramura noua din Oracle ruleaza mereu cand ar trebui, iar FACT-024 (ramura veche) nu mai poate fi
atinsa de o linie din nomenclator dupa implementare — dispare exact problema pe care povestea o
rezolva.
- **Nu e proiectata aici** (ramane de decis la implementare, in afara acestei povesti): daca vreo
validare suplimentara ar trebui sa verifice ca valorile derivate (`CONT_VENIT` din corespondenta,
`NOM_ARTICOLE.CONT`) sunt conturi valide in planul de conturi al anului curent — azi
`verific_cont` exista ca mecanism (`oproceduri_comune.prg:2389-2415`) dar nu e cablat automat pe
acest drum nou.
---
## 4. Fluxul in formularul unificat
1. Utilizatorul deschide un document deja emis la modificare (`frm_modific2024`/formularul unificat,
in afara perimetrului #6/omodificari.vc2, care nu se atinge aici) si alege din meniu "Alege din
nomenclator..." (a doua sursa, langa "Cauta in lista de preturi...", ambele reduse la acelasi
gest UI conform S4b/S4e).
2. Gestul UI: `Select crsfactura / APPEND BLANK` (tiparul deja in productie la
`frm_facturare_articole2.But_nou1.do_adauga`, `ofacturare.vc2:17118-17122`), focus pe celula de
cautare, `combosql` legat pe cursorul filtrat pe nomenclator (`caut_articol`/echivalent, fara
coloana `id_pol` in output — confirmat, sectiunea 1.3).
3. **`crsfactura.id_c` ramane `0`** (valoarea implicita a campului `N(20)` fara `Null`, la
`APPEND BLANK`) — valoare pe care Oracle nu o produce niciodata pentru `id_c`. `do_sterge`
(potriveste pe `id_c`) e no-op prin constructie pe aceasta linie, fara nicio modificare de cod —
exact regula deja adoptata in #13, reconfirmata aici pentru sursa "nomenclator" (S4e o stabilise
pentru sursa "lista de preturi pe comanda"; acelasi cursor de scriere `crsfactura`, acelasi
mecanism, doar alt cursor de cautare in fata).
4. **`crsfactura.id_pol` ramane `.NULL.`** (camp `N(20) Null`, `ofacturare_comun.prg:1775`), pentru
ca sursa de cautare (nomenclator) nu are aceasta coloana in output — confirmat direct in aceasta
runda (sectiunea 1.3), nu presupus.
5. Utilizatorul completeaza cantitate/pret (campuri editabile inline pe grid, tipar deja existent).
6. La `do_scrie_articole` (salvarea documentului), pentru fiecare linie cu `Empty(Nvl(poArt.id_pol,0))`,
se ruleaza derivarea din sectiunea 3 si se populeaza al 27-lea argument pozitional al apelului RPC
catre `adauga_articol_factura` cu valoarea calculata; pentru restul liniilor (cele cu `id_pol`
populat, "din lista de preturi"), argumentul ramane `NULL` — comportament identic cu azi.
7. Documentul se salveaza; pe Oracle, `contabilizeaza_articol` ruleaza ramura noua (sectiunea 1-2)
pentru liniile fara politica, ramura veche neschimbata pentru restul.
8. **Editarea** (regenerare, decizia 35): acelasi `do_scrie_articole`, aceeasi conditie pe `id_pol`,
acelasi rezultat — nu exista o a doua cale de contare pentru liniile deja existente pe document.
---
## 5. Ce ramane in afara (partea auto)
- `tip = -12` (facturare auto, ROAAUTO) — livrat separat, dupa ce mecanismul de mai sus e stabil pe
documentele ROAFACTURARE (ordinea deja decisa in plan).
- "Alte servicii" din ROAAUTO ramane exceptia ei — ocoleste complet `contabilizeaza_articol`, nu
foloseste si nu va fi migrata sa foloseasca acest mecanism ca parte a acestei povesti; daca se
decide vreodata unificarea, e o poveste separata.
- Gridul read-only din `frm_modific2024` (al #6) — S4g incepe dupa ce #6 se termina (decizia 30),
nicio proiectare de aici nu presupune sau modifica acel perimetru.
- Validarea de cantitate/stoc pentru o linie liberă la modificare (analogul sectiunii 6 din
`s4e_lista_preturi_pe_sursa.md`, dar pentru nomenclator, nu lista de preturi) — nu re-proiectata
aici, acelasi gol semnalat deja de S4e se aplica identic si pe sursa "nomenclator"; de rezolvat cu
acelasi mecanism (varianta (b), reutilizare `do_verifica_articol` cu `poArticol` explicit).
---
## 6. Teste minime
Pe langa cele deja listate in `canal_cont_venit_fara_politica.md` sectiunea 6 si
`nota_contabila_fara_politica.md`, specifice acestei povesti:
1. **Factura normala cu politica, parametru NULL** — verifica ACT identic cu azi (regresie de baza).
2. **Aviz cu articol adaugat din nomenclator (fara politica)** — `SCD` ramane `'461'`/`'418'`
(ramurile de aviz raman hardcodate independent de ramura noua/veche), nu `getoptiunefirma`.
3. **`ntip = 46`** (daca exista pe fluxul de modificare vizat) — `scrie_nota` nu se cheama pe acea
ramura; de confirmat ca ramura noua nu e atinsa deloc pentru acest tip.
4. **Articol gestionabil din nomenclator, cu corespondenta configurata** — un singur rand `ACT`,
`SCC` = `CORESP_CONT_VENCHELT.CONT_VENIT` pentru contul de gestiune al liniei, `SCD` = optiunea
de firma (sau `4111` daca optiunea lipseste), linie TVA scrisa separat.
5. **Articol negestionabil din nomenclator, fara corespondenta aplicabila** — `SCC` = `NOM_ARTICOLE.CONT`
daca incepe cu 6/7, altfel `'704'`.
6. **Articol negestionabil** (`id_gestiune=-1000`) cu `cont_venit` populat — `descarca_gestiune` NU
ruleaza.
7. **Discount pe o linie din nomenclator** — foloseste `V_ASCD`/`V_CU_TVA` calculate in ramura noua.
8. **Document in valuta** (`nin_valuta=1`) cu linie din nomenclator — `IN_VALUTA=1` pe nota,
`SUMA_VAL` completat.
9. **Linie fara politica si fara cont derivat** (nu ar trebui sa se poata construi prin UI, dar de
testat direct pe Oracle) — FACT-024 tot apare, regresie negativa: garda nu s-a slabit.
10. **Linie cu ambele populate** (construita direct la nivel de apel Oracle, nu prin UI — testeaza
garda de la sectiunea 1.4, nu fluxul normal) — noua eroare explicita apare, nu o rezolvare
tacita in nicio directie.
11. **Regenerare pe un document mixt** (o linie din lista de preturi cu `id_pol`, o linie din
nomenclator fara `id_pol`) — dupa regenerare, prima linie tot cu `SCC` din politica, a doua tot
cu `SCC` derivat; niciuna nu trece pe ramura celeilalte.
12. **`id_c` si `do_sterge`** (mostenit din tiparul S4e, de re-verificat pentru sursa nomenclator):
o linie din nomenclator adaugata la modificare, apoi stearsa inainte de salvare — no-op pe
`crsarticole`, fara efect asupra vreunei linii reale a documentului.
---
## 7. Riscuri si de decis de Marius
1. **Garda explicita pe combinatia `id_pol` + `cont_venit` ambele populate** (sectiunea 1.4) — e o
recomandare de proiectare a acestui raport, nu o decizie deja luata de Marius; codul exact al
erorii si numarul `FACT-0xx` raman de ales.
2. **`CU_TVA` hardcodat `1`** — are un efect colateral masurat (nu doar teoretic), prin
`nproc_tva_max`, pe combinatia specifica linie-scutita + discount global de factura. De confirmat
explicit, nu de presupus inofensiv.
3. **Decizia 36 (`SCD` prin optiune de firma)** — numele exact al cheii (`FACT_SCD_ARTFPRET` propus),
daca se adauga validare de cont la citire (niciun precedent existent nu valideaza), si
`PROGRAME` (restrans la ROAFACTURARE sau extins ca `RF_CONT_INCASARE_*`) raman decizii deschise
in `optiune_firma_cont_debit.md` sectiunea 4.
4. **Ramura `ntip=4`/"factura din avize" pe fallback** (sectiunea 2 punctul 7) — probabil imposibil
de atins prin acest drum (avizul sursa cere deja `id_pol`), dar nu exclus explicit prin cod sau
test — de confirmat inainte de implementare.
5. **Suprafata de regresie in restul suitei** (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB) pentru
`adauga_articol_factura` — pe baza cercetarilor existente, aceste produse folosesc proceduri
separate (`_deviz`/`_stoc`) sau alt pachet complet (`pack_acn`), deci risc asteptat zero, dar
cautarea directa in `COMUN`-urile lor pentru un apel simplu la `adauga_articol_factura(` nu s-a
terminat in nicio runda anterioara — de re-rulat inainte de implementare, nu de presupus incheiata.
6. **Validarea de cantitate/stoc pentru linia libera la modificare** (sectiunea 5) — gol mostenit
de la S4e, nu inchis aici, de rezolvat cu acelasi mecanism pe ambele surse (lista de preturi si
nomenclator).
7. **Trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII`** (sectiunea 2 punctul 1) — optionala pentru
ca mecanismul sa functioneze, dar fara ea coloana nu se pastreaza dupa fapt; de decis daca merita
extinderea listei explicite de coloane din `scrie_in_vanzari`.
8. **`idfact_refolosire_si_documente.md`** — obstacol real pentru ca regenerarea (decizia 35) sa fie
completa (reemitere cu acelasi `ID_FACT`), in `PACK_CONTAFIN`, nu in `pack_facturare` — nu
blocheaza design-ul de aici, dar e o bucata de lucru separata, necesara inainte ca "editare =
regenerare" sa functioneze end-to-end.
---
## STARE / CE RAMANE
Cercetare/proiectare incheiata pentru toate cele 6 puncte cerute in brief, cu punctul central
(sectiunea 1) tratat explicit ca decizie de proiectare (nu doar consecinta implicita a codului
existent deja schitat in rundele anterioare). Nicio editare de cod, niciun `git_sync.ps1`/
`txt2vcx.ps1`, niciun commit, nicio scriere pe Oracle. Context consumat moderat in aceasta runda —
nu a fost necesara predarea de mijloc de sesiune.
**Ce nu s-a putut inchide complet, de reluat separat**:
- Cautarea exhaustiva a apelantilor `adauga_articol_factura` simplu in restul suitei (risc 5,
sectiunea 7) — de re-rulat, nu s-a terminat in nicio runda anterioara din lipsa de timp, nu din
cauza unei erori.
- Confirmarea explicita a lui Marius pe hardcodarile/optiunile ramase deschise (`CU_TVA=1`, numele
cheii de optiune, garda de la sectiunea 1.4) — sunt recomandari argumentate, nu decizii finale.