# Cercetare — doua puncte deschise inainte de implementarea S4 Investigatie READ-ONLY, doua intrebari inchise din `docs\plan_13_unificare_formular_facturare.md`, `#### S4`, sectiunea "De inchis inainte de implementare" (`:2092-2095`). Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara scriere pe Oracle (numai `SELECT`). Status: **GATA**. Ambele intrebari au raspuns confirmat pe cod, cu `fisier:linie`. Amandoua raspunsurile de baza confirma "nu schimba nimic", dar fiecare are o rezerva concreta, nu triviala, de pus in proiectarea S4 (nu doar de bifat). Sursa Oracle: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17217 linii — liniile citate mai jos sunt verificate direct pe acest fisier, corpul pachetului). Sursa VFP: `COMUN\clase\ofacturare.vc2`, `COMUN\programe\ofacturare.prg` (perimetrul `frm_facturare_articole` / `factureaza`; `frm_facturare_articole2` / `factureaza2` citite doar pentru comparatie, nu atinse). --- ## Intrebarea 1 — `id_jtva_coloana` lipseste din `cursor_preturi`? ### 1.1 Ce e si cine o consuma `id_jtva_coloana` identifica randul din `JTVA_COLOANE` (explicatia/coloana de TVA folosita la raportare si SAFT) asociat unei linii de factura. E scris pe fiecare linie in `VANZARI_DETALII_TEMP` (Oracle, prin `adauga_articol_factura`) si citit inapoi de UI-ul VFP pentru dialogul de "explicatie TVA" (`Cb_explicatie_Tva`, `frm_articol_factura`). ### 1.2 Chiar lipseste din `cursor_preturi`? — confirmat pe SQL Toate cele **cinci ramuri** ale `cursor_preturi` (`WHEN V_TIP = 45`, `WHEN V_TIP IN (1,2)`, `WHEN V_TIP IN (5,6,10,52)`, `WHEN V_TIP = 7`, `ELSE` aviz — corpul la `ff_...PACK_FACTURARE.sql:2138-2644`) au liste de coloane complete verificate direct — **niciuna nu returneaza `id_jtva_coloana`** (`:2163-2221`, `:2268-2329`, `:2394-2427`, `:2470-2504`, `:2545-2603`). `cursor_facturare` e un `REF CURSOR` slab tipat (`:28`), deci lista de coloane a fiecarei ramuri e literal ce se vede in `SELECT`, fara camp implicit. Acelasi lucru e adevarat si pentru **`cursor_gestiune`** (`:4158-4347`, coloanele la `:4207-4343`) — relevant pentru intrebarea 2, tipul 41. `cursor_contract` (`:2646-2950`) delega `V_CURSOR` la `cursor_preturi` (deci mosteneste lipsa), iar `V_CURSOR2` (rate + articole `OPT_FACTURARE=3`, `:2718-2938`) **de asemenea nu are `id_jtva_coloana`** in niciuna din cele doua ramuri `UNION ALL`. Prin contrast, **`cursor_avize`** (`:3703-3874`) **are** `A.ID_JTVA_COLOANA` explicit (`:3748`, plus `GROUP BY`-urile de la `:3781`, `:3804`, `:3822`, `:3843`) si `cursor_comanda` (`:2952-3171`) **nu are**, pe niciuna din cele doua ramuri (factura/aviz, `:2994-3170`). Deci impartirea reala e: *are* — doar `cursor_avize`; *nu are* — `cursor_preturi`, `cursor_gestiune`, `cursor_contract` (ambele cursoare), `cursor_comanda`. ### 1.3 E completat in alt pas, sau ramane gol? — DA, e completat, printr-un mecanism in doi pasi Pasul 1 — **valoare implicita, nu `NULL`**. `do_initializeaza_articol` (`ofacturare.vc2:13681-13683`, identic la `:17896-17897` pe prototip): ``` If Type('toArticol.id_jtva_coloana') = 'U' AddProperty(toArticol,'id_jtva_coloana',0) Endif ``` Daca `Scatter Name poArticol` dintr-un cursor fara coloana `id_jtva_coloana` produce un obiect fara acea proprietate (`Type(...) = 'U'`), se adauga cu valoarea **`0`** (numeric, nu `NULL`). Pasul 2 — **derivare reala din `proc_tvav`, neconditionata**. `frm_articol_factura.Init` (`ofacturare.vc2:2344-2371`) suprascrie *intotdeauna* `id_jtva_coloana`, indiferent de ce a venit din cursor: ``` Select jtva_coloane If !Isnull(poArticol.proc_tvav) Set Filter To cota_tva = poArticol.proc_tvav * 100 - 100 ... poArticol.id_jtva_coloana = id_jtva_coloana Else ... poArticol.proc_tvav = (cota_tva + 100)/100 poArticol.id_jtva_coloana = id_jtva_coloana EndIf ``` `proc_tvav` **este** returnat de toate cele cinci ramuri ale `cursor_preturi` (coloana comuna, confirmata la 1.2), deci lookup-ul are mereu ce derivea, indiferent daca `id_jtva_coloana` a lipsit din cursorul sursa. Acest `Init` ruleaza pentru **orice** linie adaugata prin `do_adauga_articol` (`ofacturare.vc2:12813-13167`), pe ambele ramuri ale `Do Case` de la `:12870-12896`: - articol negestionabil / `gnScadereStoc=0` / restaurant (`tip=45`) → `Createobject("frm_articol_factura", ...)` direct (`:12873`); - articol gestionabil (`Otherwise`, `:12881-12895`) → `do_alege_stoc` → `Createobject("frm_articol_gest_factura", ...)` (`:13353`/`:13361`/`:13375`) — clasa care **mosteneste** `frm_articol_factura` (`vfp_symbols -Class`: `frm_articol_gest_factura -> frm_articol_factura -> _frmbase -> _form`) si al carei `Init` (`:3986-3989`) cheama `DoDefault(tnCantitate,tlAscunde)` — deci ruleaza acelasi `Init` de la 2315-2371, cu aceeasi derivare. **Confirmare ca derivarea chiar functioneaza si nu doar exista in cod:** `do_alege_stoc` (`:13330-13340`) avea, intr-o versiune anterioara (comentata, `v 2.2.18`), un scurtcircuit care seta direct `id_jtva_coloana` fara sa mai arate formularul cand exista o singura optiune de TVA; azi acel scurtcircuit e dezactivat si se creeaza intotdeauna obiectul `frm_articol_gest_factura` (deci `Init` ruleaza mereu, nu doar in cazuri rare). Safety-net-ul `Case Empty(poArticol.id_jtva_coloana)` din `inainte_de_do_termin` (`:2205-2208`, mostenit si de `frm_articol_gest_factura`) e o verificare reziduala pentru cazul in care nici `proc_tvav` n-are match in `jtva_coloane` — nu dovada ca `id_jtva_coloana` ramane gol azi pe fluxul normal. ### 1.4 Concluzia care conteaza — cu o rezerva reala, nu ipotetica **Pe fluxul de azi (`do_adauga_articol`), lipsa lui `id_jtva_coloana` din `cursor_preturi`/ `cursor_gestiune` NU e un bug** — e completat corect, de doua ori (default 0, apoi derivat real din `proc_tvav`), pe ambele ramuri (gestionabil/negestionabil), inainte ca linia sa ajunga in `crsfactura` (`Gather ... id_jtva_coloana ...`, `:12945-12950`/`:12992-12995`) si, de acolo, in Oracle (`do_scrie_articole` → `adauga_articol_factura(...)`, `Alltrim(Str(poArt.id_jtva_coloana))`, `:14085`). **Rezerva: raspunsul "filtrarea nu schimba nimic" e adevarat DOAR daca implementarea S4 pastreaza trecerea prin `do_adauga_articol`/`frm_articol_factura.Init` pentru linia aleasa.** Cercetarea anterioara (`docs\cercetare\s4_cautare_articole_server.md`, sectiunea 5) arata ca **singurul exemplu real existent de cautare-pe-server** (`combosql`, `grd_factura.cCodMat.cboCodmat.LostFocus`, `ofacturare.vc2:19288-19306`) **nu trece prin `do_adauga_articol` deloc** — face `REPLACE` direct in `crsFactura`: ``` REPLACE codmat WITH crsCodMat.codmat, denumire WITH crsCodmat.denumire, id_articol WITH crsCodmat.id_articol IN crsFactura ``` Acelasi tipar identic pe coloana `cDenumire` (`:19312-19317`). Daca S4 extinde acest `REPLACE` cu restul campurilor din cursorul filtrat — asa cum recomanda raportul anterior la sectiunea 5, punctul 4 ("sa extinda REPLACE-ul... cu restul campurilor") — **`id_jtva_coloana` nu mai trece prin niciun `do_initializeaza_articol`/`frm_articol_factura.Init`**, pentru ca acel `REPLACE` nu creeaza obiectul `poArticol` si nu instantiaza formularul. Cursorul filtrat tot n-are `id_jtva_coloana` (varianta filtrata a `cursor_preturi` pastreaza aceeasi lista de coloane — vezi `s4_cautare_articole_server.md:220`), deci randul din `crsFactura` ar ramane cu valoarea implicita de camp (`Append Blank`, nu exista un `REPLACE` explicit pe acea coloana) — **fara nicio derivare din `proc_tvav`**. La scriere, `adauga_articol_factura` (Oracle) cauta `ID_JTVA_COLOANA = V_ID_JTVA_COLOANA` in `JTVA_COLOANE` pe ramura `ELSE` a `CASE`-ului sau (`ff_...PACK_FACTURARE.sql:5187-5198` — ramura care se aplica exact tipurilor de lista de preturi 1/5/7/10/22/29, tipurile de transfer 23/45/48/49 si altele care nu intra in ramurile speciale comanda/aviz/restaurant/contract-cu-pret-fix) si, daca nu gaseste o potrivire, **arunca `RAISE_APPLICATION_ERROR(-20000, 'Nu a fost gasita cota de TVA! (FACT-013 : ...)')`**. O valoare implicita de camp (tipic `0`, posibil `NULL` dupa `Append Blank`, de verificat pe structura reala a `crsfactura`) ar produce fie acest -20000, fie (daca exista un rand `JTVA_COLOANE.ID_JTVA_COLOANA=0`) o cota de TVA gresita scrisa tacut — amandoua ar fi un **bug nou, introdus de S4**, nu unul preexistent. **Recomandare concreta pentru proiectarea S4 (nu doar constatare):** varianta filtrata a cautarii (oricare ar fi mecanismul concret — `combosql` extins sau altceva) trebuie sa continue sa treaca prin `do_adauga_articol`/`frm_articol_factura.Init` pentru linia aleasa (sau sa reproduca explicit derivarea din `proc_tvav` daca se alege un `REPLACE` direct), nu doar sa extinda `REPLACE`-ul de azi cu campurile brute din cursor. De pus explicit ca cerinta in proiectarea S4, nu ca detaliu care se rezolva de la sine. --- ## Intrebarea 2 — `cursor_gestiune` (tipurile 23/41) fara buton "adauga tot"? ### 2.1 Vizibilitatea butonului pe cod — confirmata `but_urmator_tot1` are **`Visible = .F.` la design-time** (`ofacturare.vc2:11257-11266`, `ADD OBJECT 'But_urmator_tot1' AS but_urmator_tot WITH ... Visible = .F. ...`). Devine vizibil doar prin `This.but_urmator_tot1.Visible = .T.` explicit, in 8 locuri din `Do Case poDate.tip` din `Init` (`:15108-15248`): | Tip | Vizibil? | Linia care il seteaza | |---|---|---| | `eProforma=1` | da | `:15113` | | `lCopiere` | da | `:15120` | | `1,5,7,10` (lista de preturi) | **nu** (fara linie) | — | | `2,6` (contract) | **nu** | — | | `3` (comanda) | da | `:15150` | | `4` (avize) | da | `:15166` | | `21,28,42,47` (aviz din comanda) | da | `:15177` | | `22,29` (aviz din lista de preturi) | **nu** | — | | **`23`** (transfer subunitati) | **nu** — are propriul `Case` (`:15187-15195`), dar niciun `Visible=.T.` in el | — | | **`41`** (retur transfer) | **nu** — are propriul `Case` (`:15197-15205`), dar niciun `Visible=.T.` in el | — | | `25` (transfer din comanda) | da | `:15215` | | `26` (aviz din contract) | **nu** | — | | `8,9` (retur) | da | `:15240` | | `24` (aviz retur) | da | `:15245` | **Confirmat: 23 si 41 au fiecare `Case` propriu in `Do Case`** (nu lipsesc din el, spre deosebire de tipul 52, verificat separat intr-o cercetare anterioara ca "nu intra in niciun `Case`") — dar niciunul din cele doua `Case`-uri nu seteaza `Visible = .T.`, deci butonul ramane la valoarea implicita ascunsa. Concluzie identica cu lista de preturi (1/5/7/10): "adauga tot" e ascuns azi, nu doar "neconfirmat". ### 2.2 Exista alt mecanism de adaugare in masa pe 23/41? **Nu unul dedicat — dar exista mecanismul de baza, "adauga rand cu rand", disponibil pe orice tip.** `But_urmator1` (singular, nu "tot") e `ADD OBJECT`-at neconditionat in definitia clasei (`ofacturare.vc2:11237`), fara vreun `Visible=.F.` la design-time si fara sa fie atins de `Do Case` de la 15108-15248 (acolo se atinge doar `but_urmator2`, aferent `grd_contracte`/contract, scos prin `RemoveObject` pentru tipurile non-contract, `:15322-15323` — nu are legatura cu 23/41). Lantul e `But_urmator1.Click` → `do_urmator()` → `Thisform.do_adauga_articol()` (`:14715`) — exact metoda cu derivarea de TVA confirmata la intrebarea 1. Deci pe 23/41, ca si pe lista de preturi, operatorul adauga **o linie o data**, prin acelasi buton/metoda folosit si azi pe tipurile de lista de preturi — nu exista un "adauga tot" separat de recreat, pentru ca nu exista nici azi. ### 2.3 Cine mai foloseste `cursor_gestiune` — CORECTIE fata de premisa din plan Rutarea reala e in `ofacturare.prg`, procedura `factureaza` (formularul **standard**, `frm_facturare_articole` — cel lansat implicit), `Do Case` de la `:266-308`: ``` Case Inlist(tnTip, 1, 22, 5, 29, 7, 10, 23) && lista de preturi -> cursor_preturi (:279-282) Case Inlist(tnTip, 2, 26, 6, 52) && contract -> cursor_contract (:283-290) Case Inlist(tnTip, 3, 21, 25, 28, 42, 47) && comenzi -> cursor_comanda (:292-293) Case tnTip = 4 && din avize -> cursor_avize (:294-295) Case Inlist(tnTip, 41) && transfer/retur -> cursor_gestiune (:296-298) Case tnTip = 30 -> cursor_aviz_nir (:299-300) Case tnTip = 27 -> cursor_lucrare (:302-304) Case Inlist(tnTip, 8, 9, 24) -> cursor_retur (:306-307) ``` plus, mai sus in acelasi `Do Case` (`:271-278`): `Case Inlist(tnTip, 48, 49)` → **`cursor_articole_k`** (procedura complet diferita, nu una din cele cinci) si `Case tnTip = 45` → **`cursor_preturi`**. **Pe formularul standard: doar tipul 41 foloseste `cursor_gestiune`.** Tipul **23 foloseste de fapt `cursor_preturi`**, grupat direct cu tipurile de lista de preturi (1,22,5,29,7,10) — asta explica de ce, la 2.1, tipul 23 se comporta identic cu lista de preturi (acelasi cursor sursa). Tipurile **45 si 48/49 nu ating deloc `cursor_gestiune`**: 45 → `cursor_preturi`, 48/49 → `cursor_articole_k` (iese din perimetrul celor cinci cursoare din S4). Premisa din plan ("`cursor_gestiune` — tipurile 23/41") si mentiunea "45, 48, 49" (`s4_cautare_articole_server.md:344`, tabelul de vizibilitate) **descriu corect vizibilitatea butonului** (toate aceste tipuri au "adauga tot" ascuns), dar **nu si sursa lor de cursor** — nu toate trec prin `cursor_gestiune`. **Pe formularul prototip** (`frm_facturare_articole2`/`factureaza2`, accesibil live doar cu optiunea `gnFacturareNou=1` **si** alegerea explicita a operatorului la un dialog de confirmare, `ofacturare.prg:88-93` — deci reachable in productie, nu cod mort, dar opt-in), rutarea **difera**: `Case Inlist(tnTip, 23, 41)` (`ofacturare.prg:776-778`) cheama **impreuna** `cursor_gestiune` pentru **ambele** tipuri. Pe acest formular, premisa din plan e exacta. Divergenta intre cele doua formulare (23 pe `cursor_preturi` in cel standard, pe `cursor_gestiune` in prototip) nu pare intentionata — nu exista niciun comentariu care s-o justifice — dar e in afara perimetrului acestei cercetari (read-only, fara editare) si nu afecteaza concluzia de mai jos, valabila pe oricare din cei doi cursori (niciunul nu are `id_jtva_coloana`, ambii sunt tratati identic de `do_adauga_articol`). ### 2.4 Concluzia care conteaza **Dupa S4, pe tipurile 23/41 nu se pierde nimic functional.** "Adauga tot" e deja absent azi pe ambele (confirmat pe cod, 2.1), iar adaugarea se face deja rand-cu-rand prin acelasi mecanism folosit si pe lista de preturi (`but_urmator1`/`do_urmator`/`do_adauga_articol`, 2.2) — mecanism pe care S4 nu il atinge (S4 schimba doar sursa cursorului incarcat la deschidere, nu calea de adaugare a unei linii individuale). Aceeasi rezerva de la intrebarea 1 se aplica identic aici: valabil doar daca varianta filtrata a cautarii trece prin `do_adauga_articol`, nu daca reproduce `REPLACE`-ul direct din `combosql`. --- ## Handoff Nu e necesara predare — ambele intrebari sunt inchise, cu dovada pe cod. Rezervele de la 1.4 si 2.4 (derivarea `id_jtva_coloana` trebuie sa treaca prin `do_adauga_articol`/`frm_articol_factura.Init`, nu prin `REPLACE` direct tip `combosql`) sunt de dus mai departe in proiectarea S4, nu doar de arhivat aici. Corectia de la 2.3 (doar tipul 41, nu si 23, foloseste `cursor_gestiune` pe formularul standard) merita reflectata daca planul S4 mai citeaza undeva "cursor_gestiune (23/41)" ca sursa unica.