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