# S4b — Bara de butoane si meniul de adaugare Proiectare pe cod, READ-ONLY (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit), pentru povestea **S4b** din `docs\plan_13_unificare_formular_facturare.md:1890-1899` (textul decis in sectiunea J, `:911-1101`). Nu s-a atins `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` (perimetrul #6) — doar citite, cand au aparut in cautari. ## Verdict (esenta, 10 randuri) Cele trei bucati din decizia 13 sunt implementabile fara cod nou major, dar cu **trei corectii fata de textul din plan**, toate in favoarea utilizatorului: (1) golul de contract nu e doar `but_urmator_tot1.Visible` lipsa pe tip 2/6/26 — pe **tip 52 formularul nu intra deloc in ramura lui**, `Do Case` nu are niciun `Case` care sa-l prinda, deci pierde si titlul, si eliminarea coloanei `cSerie`, nu doar butonul "tot"; (2) tiparul RORIS (`frm_tranzit`, dialog bespoke) **nu e cel mai ieftin de refolosit** — suita are deja, folosit chiar in acest formular pentru retur (`do_cauta_facturi`), un mecanism generic de selectie multipla cu bifare, `cauta_alfa(..., tnTipReturn=1)`, mai aproape de cerinta („dialog modal cu coloana de bifat si criterii de cautare") decat un formular nou; (3) „unitatea de selectie e rata" pe contract **nu e valabil pentru toate contractele** — cursorul `crsarticole1` are doua ramuri disjuncte, `OPT_FACTURARE=3` (articole reale) si `OPT_FACTURARE IN (1,2)` (rate de scadentar), iar un contract e mereu pe una singura; eticheta „Alege ratele de facturat…" e corecta doar pe ramura a doua. Echivalenta „tot" = „alege total" tine azi pe o singura rutina comuna, `do_adauga_articol`, dar `do_adauga_tot` **nu parcurge `crsarticole1` deloc** — pe contract, „adauga tot" ar trebui sa fie scris, nu doar facut vizibil. Detaliile, cu `fisier:linie`, mai jos. --- ## 1. Inventarul butoanelor de azi ### 1.1 `frm_facturare_articole` (formularul de compunere in productie, `ofacturare.vc2:10968-15739`) Grid sursa (comanda/lista preturi) = `grd_articole` (`RecordSource=crsarticole`, `ofacturare.vc2:11570`). Grid sursa-contract = `grd_contracte` (`RecordSource=crsarticole1`, `:11891`). Grid destinatie (linii factura) = `grd_factura` (`RecordSource=crsfactura`, `:12263`). | Control | Clasa | Caption/Picture | L/T/W/H | ToolTipText | `fisier:linie` | |---|---|---|---|---|---| | `But_modifica1` | `but_modifica` | (fara caption) `modific_sus.bmp` | 773/61/30/27 | "Modificare (CTRL+M)" | `:11192` | | `But_sterge1` | `but_sterge` | (fara caption) `sterg_sus.bmp` | 811/61/30/27 | "Stergere (CTRL+D)" | `:11229` | | `But_renunt1` | `but_renunt` | (fara caption) `renunt_sus.bmp` | 792/1/30/27 | "Renuntare (ESC)" | `:11202` | | `But_reset1` | `but_reset` | (fara caption) `reset_sus.bmp` | 303/295/30/27 | "Reseteaza" | `:11213` | | `But_urmator1` | `but_urmator`, `caction=do_urmator` | `urmator1.bmp` | 343/348/30/27 | "Verificare" | `:11237` | | `But_urmator2` | `but_urmator`, `caction=do_urmator2` | `urmator1.bmp` | 343/143/30/27 | "Verificare" | `:11247` | | **`But_urmator_tot1`** | `but_urmator_tot`, `caction=do_adauga_tot` | `urmator1_tot.bmp`, **fara Caption, fara ToolTipText** (clasa insasi nu are niciuna din ele — `cmd_butoane.vc2:414-425`) | 343/378/30/27, `Visible=.F.` implicit | — | `:11257` | | `But_retur` | `but_retur`, `caction=do_retur` | `retur1.bmp`, `Visible=.F.` implicit | 343/407/30/27 | "Retur" | `:11221` | Niciun buton "linie noua" (`but_nou`) — o linie se adauga alegand un rand din `grd_articole` / `grd_contracte` si apeland `do_adauga_articol` (dublu-click / Enter, mecanism de grid standard, nu citit exhaustiv aici), nu prin `APPEND BLANK` pe `crsfactura`. "Detalii linie" nu exista ca buton separat — `But_modifica1` **e** azi echivalentul: deschide `frm_articol_factura` pe randul curent din `crsfactura` (`do_modifica`, `:13746-13914`), reface plafonul de cantitate din cursorul sursa (`crsarticole` sau `crsarticole1`, ales prin `poArticol.opt_facturare`, `:13778`) si reconstruieste proprietatile de discount/pret in valuta. **"Adauga tot" — `But_urmator_tot1.Click -> do_adauga_tot`** (`:13169-13198`): ``` PROCEDURE do_adauga_tot If Used('crsarticole') And Reccount('crsarticole') > 0 Select crsarticole Scan ... Thisform.do_adauga_articol(.T.) && tlContract = omis => .F. implicit ... Endscan Else aMessageBox("Nu exista articole de adaugat!", 48, "Atentie") Endif ENDPROC ``` **Parcurge exclusiv `crsarticole`.** Nu exista nicio ramura care sa parcurga `crsarticole1` — deci chiar daca s-ar face vizibil pe tip 2/6/26/52, "adauga tot" **nu ar aduce nimic** pe un contract al carui `grd_contracte`/`crsarticole1` are randuri (rate sau articole de contract), pentru ca butonul citeste doar cursorul celalalt. E un gol de **cod**, nu doar de vizibilitate — vezi sectiunea 5. **Conventia de clase de butoane** (confirmat, `inventar_controale_formulare.md`): clasa de baza `buton` (`_cmd_base.vc2:16`) are `Caption=""` implicit — butoanele sunt doar-imagine 30x27px prin design. `but_nou` (`cmd_butoane.vc2:214-227`, `caption=do_adauga`, `ToolTipText="Adaugare (CTRL+N)"`) si `but_sterge` (`:354-366`, `caption=inainte_de_do_sterge`, tot fara Caption propriu la nivel de clasa) au deja `ToolTipText`, dar niciodata `Caption` — "eticheta, nu iconita muta" ceruta de decizia 13 e o schimbare reala, nu o simpla refolosire a clasei: fie se seteaza `Caption` pe instanta (clasa `buton` are proprietatea, pur si simplu nefolosita azi), fie se creeaza o varianta de clasa cu `Caption` implicit. ### 1.2 `frm_facturare_articole2` (prototip, `ofacturare.vc2:15741-19355`) Arhitectura diferita: **un singur grid** `grd_factura` cu editare inline prin combo-uri in celule (`cCodMat.cboCodmat`, `cDenumire.cCboDenumire`) — nu exista `grd_articole`/`crsarticole` separat. | Control | Clasa | Caption/Picture | L/T/W/H | ToolTipText | `fisier:linie` | |---|---|---|---|---|---| | **`But_nou1`** | `but_nou` (caption suprascris pe instanta, vezi mai jos) | `nou_sus.bmp` | 719/175/30/27 | "Adaugare (CTRL+N)" | `:15936` | | `But_sterge1` | `but_sterge` | `sterg_sus.bmp` | 747/175/30/27 | "Stergere (CTRL+D)" | `:15955` | | `But_renunt1` | `but_renunt` | `renunt_sus.bmp` | 716/1/30/27 | "Renuntare (ESC)" | `:15944` | **Sunt deja unde trebuie**: `Top=175`, gridul la `Top=204` (`:15936`, `:15955`) — **deasupra tabelului**, exact pozitionarea ceruta de decizia 13. **`But_nou1.Click -> do_adauga`** (`:17118-17122`, override propriu pe instanta, nu `caction` mostenit de la clasa `but_nou`): ``` PROCEDURE do_adauga Select crsFactura APPEND BLANK this.grd_factura.SetFocus() ENDPROC ``` Adauga direct un rand gol si da focus in grid — **nu** cheama `do_adauga_articol`, nu verifica stoc, nu deschide niciun dialog. Completarea articolului se face **dupa**, prin editare inline in celule (`cboCodmat`/`cCboDenumire`) — acesta e tiparul care se leaga direct de S4 (cautare pe server, vezi sectiunea 6), nu de meniul de adaugare in masa. Nu exista `But_modifica`/`detalii linie` in acest prototip — editarea se face inline, in celula. ### 1.3 Tabel comparativ | | `frm_facturare_articole` (productie) | `frm_facturare_articole2` (prototip) | |---|---|---| | Pozitia butoanelor de linie | lateral, intre cele doua griduri (`Left=773+`) | deasupra gridului (`Top=175`, gridul la `Top=204`) — cerinta decizie 13 | | "Linie noua" | nu exista — se alege dintr-un grid sursa | `But_nou1` -> `APPEND BLANK` + focus | | "Sterge linie" | `But_sterge1`, lateral | `But_sterge1`, deasupra | | "Detalii linie" | `But_modifica1` -> `frm_articol_factura` (dialog complet) | nu exista — editare inline | | Sursa de articole | 2 griduri separate, `crsarticole`/`crsarticole1` | niciunul — combo pe cod/denumire in celula | | Caption pe butoane | fara, peste tot (doar iconite) | fara, peste tot (doar iconite) — nici prototipul nu rezolva "eticheta" | Concluzie pentru implementare: **pozitionarea** se ia din prototip, **comportamentul de adaugare in masa** (`do_adauga_tot`/dialoage de alegere) se ia din formularul de productie (prototipul nu are deloc aceasta logica — n-a fost construit pentru documente cu sursa), iar **eticheta pe buton** nu exista azi in niciunul din cele doua, e de adaugat explicit in ambele cazuri. --- ## 2. `xmenu()` — contract si exemple **Definitie**: `COMUN\programe\proceduri_comune.prg:755-822` (identica, byte-cu-byte in structura, cu `COMUN\programe\oproceduri_comune.prg:1550-1617` — a doua e cea incarcata efectiv, SET PROCEDURE ADDITIVE peste prima, dar comportamentul e acelasi). ``` Procedure XMENU Lparameters TCITEMS, TNBAR && TCITEMS = optiuni separate prin ";" ; "\-" = separator vizual (bara, nu optiune selectabila, && nu se trece prin ALLTRIM); "\ metoda unica -> dialog cu bifare si criterii -> populare aditiva prin `INSERT`, fara scriere Oracle pana la salvare) — dar `frm_tranzit` insusi e un formular **specific ROAACNPRO**, care nu exista in ROAFACTURARE si n-ar trebui portat ca formular. **ROAFACTURARE are deja, in productie, in acelasi `frm_facturare_articole` care e subiectul acestei povesti, mecanismul cerut** — `cauta_alfa()` (`COMUN\programe\cauta_alfa.prg:16-260`), functia de cautare generica folosita de zeci de `do_cauta_*` din toata suita. Are deja tot ce cere decizia 13: - **coloana de bifat**: cand `tnTipReturn=1`, `cauta_alfa` adauga singura o coloana `ales` peste cursorul de rezultate (`cauta_alfa.prg:124`: `Select *, 0 As ales From &lcCursort ... Into Cursor &lcCursor`) si titlul devine explicit "Alegeti ... (mouse-click pe numar sau apasati SPACE)" (`oproceduri_facturare.prg:2104`); - **criterii de cautare**: parametrul `tcStringCriterii` deschide `cauta_alfa_form_plus`, varianta cu bara de criterii (`cauta_alfa.prg:22`); fara el, cautarea alfa/browse standard tot filtreaza interactiv pe coloanele afisate; - **populare aditiva, fara scriere in baza**: returul (`tnTipReturn=1`) e un XML cu randurile bifate, convertit local prin `Xmltocursor(...)` (exemplu direct, `ofacturare.vc2:9196`) — nimic nu ajunge la Oracle in acest pas; - **e deja folosit in acest exact formular**, pentru un caz de "alege documentul sursa": vezi 4.2. **Recomandare de proiectare**: dialoagele "Alege..." din meniul S4b se construiesc pe reteta `cauta_alfa(..., tnTipReturn=1)`, cu `tcselect`/`tcfiltru` diferite pe sursa (comanda/contract/avize/ retur), **nu** pe un formular nou stilizat dupa `frm_tranzit`. Ramane de portat din RORIS doar **ideea**, nu codul: buton unic condiționat, metoda de intrare unica, populare aditiva — toate trei deja adevarate si pentru `cauta_alfa`. ### 4.2 Precedentul direct: retur multi-factura `frm_date_factura.do_cauta_facturi` (`ofacturare.vc2:9173-9212`) foloseste deja exact acest tipar, pentru cazul "alege mai multe facturi sursa de retur": ``` lcXMLFacturi = caut_facturi_multiple_client(poDate.id_client, poDate.in_valuta, poDate.id_valuta, .T.) If !Empty(lcXMLFacturi) and gnButon = 1 Xmltocursor(lcXMLFacturi, "crsFacturiTemp") ... poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",") ``` si `caut_facturi_multiple_client` (`oproceduri_facturare.prg:2091-2121`) cheama direct `cauta_alfa(..., lnTipReturn = Iif(tlFacturiMultiple, 1, 0))`. **Important pentru sectiunea 9**: acest pas ruleaza azi la **antet** (`frm_date_factura`), inainte ca formularul de articole (si deci bara de butoane S4b) sa existe — `poDate.listaid` e fixat o singura data, cursorul `crsarticole` se incarca o singura data din el (`cursor_retur_document`, cu `V_LISTAID`). Optiunea "Adauga tot din facturile alese" din meniul S4b **poate** refolosi direct `do_adauga_tot`/`crsarticole` asa cum e azi (sursa e deja bounded). O optiune noua "Alege facturile de returnat…" **la nivelul barei de butoane** ar insemna insa a permite schimbarea setului de facturi sursa **dupa** ce formularul de articole s-a deschis — azi nu exista acest flux (odata setat, `poDate.listaid` nu se rescrie din articole). E o decizie de proiectare noua, nu o simpla mutare a codului existent (vezi 9.3). ### 4.3 Ce cursor-sursa pe fiecare tip, si ce inseamna "linie" acolo | Sursa | Cursor(oare) populat(e) la intrarea in `factureaza`/`factureaza2` | Ce e o "linie" pentru dialogul de alegere | `fisier:linie` | |---|---|---|---| | **comanda** (3, 21, 25, 28, 42, 47) | `crsarticole` <- `pack_facturare.cursor_comanda` | un articol de pe `COMENZI_ELEMENTE`, cu `id_pol` mostenit direct de pe linia comenzii | `ofacturare.prg:292-293` | | **contract, `OPT_FACTURARE=3`** (2,6,26,52) | `crsarticole1` <- `pack_facturare.cursor_contract`, ramura `CTR_ARTICOLE` | un articol al contractului, `id_pol` derivat prin `CTR_ARTICOLE.ID_POL_ART -> CRM_POLITICI_PRET_ART` | `ff_...COMUN_PACK_FACTURARE.sql:2752-2836` | | **contract, `OPT_FACTURARE IN (1,2)`** (2,6,26,52) | `crsarticole1` <- acelasi cursor, ramura `CTR_SCADENTAR` (`UNION ALL`) | o **rata** de scadentar (`ID_RATA`, `NR_RATA`, `DATA_RATA`, `DATA_SCADENTA`, `DEN_RATA`); `id_articol` e **NULL**, `cantitate` e mereu 1, `gestionabil=0` | `ff_...COMUN_PACK_FACTURARE.sql:2836-2924` | | **contract, oricare din cele doua** — cautare libera | `crsarticole` <- acelasi apel, dar cu `pack_facturare.cursor_preturi` legat in interior | lista de preturi normala a operatorului (nu e "libera de politica", vezi `idpol_comanda_contract.md` E.3) | `ff_...COMUN_PACK_FACTURARE.sql:2940-2948` | | **avize** (4) | `crsarticole` <- `pack_facturare.cursor_avize` | un rand de pe avizul sursa | `ofacturare.prg:294-295` | | **retur** (8, 9, 24) | `crsarticole` <- `pack_facturare.cursor_retur_document`, filtrat pe `V_LISTAID` (facturile alese la antet, 4.2) | un articol al facturii/facturilor deja alese; **nicio coloana `id_vanzare`/`id_vanzare_det` in cursor** — legatura cu factura sursa se pierde inainte sa ajunga in VFP (`docs\cercetare\legatura_linie_retur.md:8-20`) | `ff_...COMUN_PACK_FACTURARE.sql:3949-4062` | **Pe contract, `crsarticole1` e populat printr-un singur `UNION ALL`, dar cele doua ramuri sunt disjuncte pe `OPT_FACTURARE`**: pentru un contract cu `OPT_FACTURARE=3`, `WHERE A.OPT_FACTURARE = 3` elimina ramura de rate; pentru `OPT_FACTURARE IN (1,2)`, `WHERE ... IN (1,2)` elimina ramura de articole. **Un contract nu produce niciodata ambele tipuri de rand in acelasi `crsarticole1`.** Deci eticheta din meniu ("Adauga tot din contract" vs. "Adauga toate ratele") **trebuie sa depinda de `crsarticole1.opt_facturare`** (coloana exista in cursor, `:2751,2893` — poate fi citita din primul rand dupa incarcare), nu poate fi un text static ca in tabelul din plan. **Daca `crsarticole1` iese gol** (contract fara articole/rate configurate), formularul de azi **elimina complet** `grd_contracte`/`But_urmator2` si redistribuie inaltimea catre `grd_articole` (`ofacturare.vc2:15294-15324`, `If !Used('crsarticole1') ... Thisform.RemoveObject('grd_contracte')`) — documentul se comporta ca o factura "libera" din lista de preturi. Meniul S4b trebuie sa verifice aceeasi conditie inainte sa ofere "Adauga tot din contract"/"Alege ratele…": daca cursorul nu exista sau e gol, optiunea nu apare deloc (nu apare goala). ### 4.4 Populare aditiva si "nicio scriere pana la salvare" Deja adevarat prin constructie, pentru toata familia `do_adauga_articol`: fiecare rand ales (fie prin `do_adauga_tot`, fie prin rezultatul unui `cauta_alfa`) trece prin **`Thisform.do_adauga_articol(...)`**, care termina cu `Insert Into crsfactura(...)` (cursor local, `.T. Buffering`) — niciun apel Oracle in acest pas (Oracle e atins abia la salvarea documentului, `finalizeaza_factura`/`scrie_factura2`, in afara acestei povesti). Un dialog nou de alegere trebuie doar sa **produca lista de randuri bifate** (cursor XML din `cauta_alfa`) si sa **cheme aceeasi rutina** rand cu rand — vezi sectiunea 5, e punctul care garanteaza si echivalenta cu "tot". ### 4.5 Zero rezultate — mesaj, nu tacere RORIS nu spune nimic la zero rezultate (`import_roris_roaacnpro.md`, punctul 5: butonul "pare ca nu face nimic"). `cauta_alfa` insasi nu are un mesaj dedicat de "zero rezultate" (e un browse generic — daca cursorul de rezultate e gol, grila apare goala, fara alt semnal, cf. structurii ei standard). **Reteta pentru S4b**: verificarea "zero rezultate" se face **inainte** de a deschide `cauta_alfa` (similar cu `do_cauta_facturi`, care verifica intai `Empty(poDate.id_client)` inainte de a cauta), printr-un `SELECT COUNT(*)` (sau verificare pe cursorul deja incarcat, `Reccount(...)=0`) urmat de `AMESSAGEBOX` care spune **de ce**: "Contractul nu are rate de facturat neincasate" / "Comanda nu are articole ramase de facturat" / etc., in loc sa lase dialogul sa se deschida gol. Exact tiparul deja folosit de `do_adauga_tot` insusi pe ramura fara date (`:13195-13197`, `"Nu exista articole de adaugat!"`) — se extinde acelasi obicei la dialogul de alegere. --- ## 5. Echivalenta „tot" vs. „alege" **Garantia structurala exista, dar e incompleta azi.** Toate cele trei cai de populare a `crsfactura` — `do_adauga_tot` (SCAN + apel), un viitor dialog de alegere (bifare + apel pe fiecare rand bifat), si adaugarea manuala rand cu rand — converg spre **aceeasi rutina**, `Thisform.do_adauga_articol(tlImplicit, tlContract, tlRetur)` (`ofacturare.vc2:12813-...`). Cata vreme toate trei cheama exact aceasta rutina, cu acelasi cursor sursa pozitionat pe randul corect, rezultatul per linie in `crsfactura` e identic prin constructie — nu exista o a doua cale de `INSERT` in `crsfactura` in afara acestei rutine (confirmat prin cautarea `Insert Into crsfactura` — singurul alt loc e ramura specifica `tip=30`/aviz din NIR, `ofacturare.prg:357-376`, care nu intra in perimetrul S4b). **Ce lipseste, concret, ca sa fie adevarat si pe contract**: `do_adauga_tot` (`:13169-13198`) parcurge **doar `crsarticole`**. Pentru ca "Adauga tot din contract" / "Adauga toate ratele" sa functioneze, `do_adauga_tot` are nevoie de o ramura noua (sau un al doilea parametru `tlContract`, simetric cu `do_adauga_articol`) care sa faca `SCAN` peste `crsarticole1` si sa cheme `Thisform.do_adauga_articol(.T., .T.)`. Fara aceasta completare, "tot" pe contract fie nu exista, fie (daca cineva ar face butonul vizibil fara sa atinga metoda) ar aduce tacut liniile gresite din `crsarticole` (lista de preturi) in loc de `crsarticole1` (articolele/ratele contractului) — o eroare mai grava decat lipsa vizibilitatii, pentru ca n-ar da nicio eroare, doar rezultat gresit. **Alegerea selectiva** trebuie sa respecte aceeasi regula: dupa ce `cauta_alfa` intoarce XML-ul cu randurile bifate, bucla care le proceseaza trebuie sa pozitioneze cursorul sursa (`crsarticole` sau `crsarticole1`, dupa caz) pe `id_c`-ul corespunzator inainte de fiecare apel `do_adauga_articol`, **nu** sa reconstruiasca linia din campurile XML direct — altfel cele doua cai ("tot" vs. "alege") ar avea doua implementari diferite ale "ce inseamna sa adaug randul X", exact riscul pe care criteriul de gata din plan il exclude explicit ("rezultatul in `crsfactura` e identic pe cele doua cai cand selectia e totala"). **Pe comanda/avize/retur, criteriul e deja adevarat prin constructie** — `do_adauga_tot` foloseste azi `crsarticole`, care e cursorul lor unic; singurul de completat e contractul (cele doua cazuri de mai sus). --- ## 6. Interactiunea cu S4 (cautarea articolelor pe server) Decizia din S4 (`plan_13...md:1879-1888`) e explicita: `crsarticole` **nu se mai incarca in masa** pentru facturarea libera (lista de preturi/nomenclator), inlocuita cu `combosql` pe cod/denumire care completeaza randul pe loc — **dar** "se pastreaza adauga tot pentru tipurile care au document sursa (comanda, aviz, contract) — acolo setul e marginit si incarcarea lui e legitima". Consecinta directa pentru meniul S4b: - **Optiunile "Cauta in lista de preturi…" si "Alege din nomenclator…"** (constante peste tot, cf. deciziilor 16/18/20) **nu deschid un dialog de tip `cauta_alfa`** — ele sunt fatada meniului pentru mecanismul S4: `combosql` pe o singura linie, populata pe loc, fara incarcare in masa. In termeni de UI, alegerea uneia din aceste doua optiuni echivaleaza cu ce face azi `But_nou1.do_adauga` din prototip (`APPEND BLANK` + focus pe celula de cod/denumire) — deci **"linie noua" (sectiunea 1) si aceste doua optiuni de meniu ajung la acelasi gest UI**, doar ca "linie noua" il porneste direct (fara meniu), iar cele doua optiuni de meniu il pornesc din context (dupa ce operatorul a vazut si restul optiunilor sursei). Nu sunt cai de cod diferite — sunt doua puncte de intrare in acelasi `APPEND BLANK` + editare inline. - **Toate optiunile "Adauga tot din X…" / "Alege X…"** (comanda, contract, avize, retur) opereaza pe cursoarele bounded (`crsarticole`/`crsarticole1`) pe care **S4 le pastreaza neschimbate** — pentru aceste tipuri, nimic din mecanismul de cautare pe server nu se aplica; secțiunile 3-5 de mai sus raman valabile ca atare. - **Punct de atingere real intre cele doua povesti**: campul `id_pol` pe randul adaugat prin `combosql` (S4, cautare din nomenclator liber) — subiectul blocajului `FACT-024` documentat pe larg in plan (J-ter/J-quater) si in `idpol_comanda_contract.md`. S4b **nu rezolva** acest blocaj (e decizia de produs deschisa la J-quater, intre reteta in 4 pasi / nota pe antet / nota pe antet ca fallback) — doar il mosteneste: optiunea "Alege din nomenclator…" din meniul S4b va ajunge la aceeasi eroare `FACT-024` la salvare, indiferent de cum arata butonul, pana cand acea decizie separata se ia. De mentionat explicit in criteriul de gata al S4b, ca sa nu se creada ca butonul rezolva problema. --- ## 7. Ordinea de executie in pasi 1. **Bara de linie (`but_nou`, `but_sterge`, "detalii linie")**, mutata deasupra gridului, cu `Caption` explicit pe fiecare instanta (clasele `but_nou`/`but_sterge` au deja `ToolTipText`, nu au niciodata `Caption` — de setat pe instanta, nu de asteptat de la clasa). *Verificabil*: cele trei butoane sunt vizibile deasupra gridului de linii, fiecare cu text vizibil (nu doar iconita), pe orice tip de document deschis. 2. **Corectarea `Do Case` din `Init`** (`ofacturare.vc2:15109-15248`): adauga `52` la ramura `Inlist(poDate.tip, 2, 6)` (linia 15129) astfel incat sa primeasca titlu, eliminare `cSerie`, si sa fie tratat identic cu 2/6 in tot restul metodei — corecteaza golul mai mare decat cel semnalat in plan (sectiunea 3 de mai sus). Adauga `26` la lista tipurilor care primesc `but_urmator_tot1.Visible = .T.` candva in pasul 3, nu aici (26 ramane azi cu titlu corect, doar fara butonul "tot"). *Verificabil*: un document nou de tip 52 arata acelasi titlu si acelasi grid (fara `cSerie`) ca un document de tip 2/6. 3. **Extinderea `do_adauga_tot`** cu ramura pe `crsarticole1` (parametru sau ramura separata dupa `poDate.tip`/`crsarticole1.opt_facturare`), plus vizibilitatea butonului/optiunii pe 2/6/26/52 — **cei doi pasi impreuna**, nu separat (vizibilitate fara continut ar aduce un buton mut; continut fara vizibilitate nu s-ar vedea niciodata). *Verificabil*: pe un contract cu `OPT_FACTURARE=3` si articole configurate, "adauga tot" produce in `crsfactura` exact liniile din `crsarticole1`; pe un contract cu `OPT_FACTURARE IN (1,2)`, produce cate o linie per rata neincasata. 4. **Butonul unic "Adauga articole" cu `xmenu()`**, inlocuind `But_urmator_tot1` (fara caption) si orice buton separat de alegere — construit dinamic dupa tabelul din sectiunea 3, cu eticheta pe ramura de contract aleasa in functie de `crsarticole1.opt_facturare` (sectiunea 4.3). *Verificabil*: pe fiecare sursa, meniul deschis contine exact optiunile din tabelul sectiunii 3, in ordinea specificata, iar alegerea `ESC` (`xmenu()` intoarce 0) nu declanseaza nimic. 5. **Dialogul de alegere selectiva**, pe reteta `cauta_alfa(..., tnTipReturn=1)` (sectiunea 4), cu mesaj explicit la zero rezultate (4.5) si populare prin `do_adauga_articol` rand cu rand (5) — cate o instantiere per sursa (comanda/contract-articole/contract-rate/avize/retur-linii), cu `tcselect`/`tcfiltru` proprii. *Verificabil*: pe fiecare sursa, bifarea a N randuri din M disponibile adauga exact acele N randuri in `crsfactura`, cu aceleasi valori ca daca ar fi fost adaugate individual; bifarea tuturor produce acelasi rezultat ca "adauga tot" (criteriul de gata al plan-ului, rescris verificabil). *Depinde de*: pasul 3 (aceeasi rutina `do_adauga_articol` trebuie sa functioneze deja pe `crsarticole1` inainte ca dialogul de alegere sa se poata baza pe ea). 6. **Reconcilierea cu selectia de facturi la antet** (sectiunea 4.2, 9.3) — decizie separata, nu blocanta pentru pasii 1-5: daca "Alege facturile de returnat…" ramane doar la antet (cum e azi) sau devine posibila si din bara de butoane. *Gata cand (rescris)*: pe fiecare din cele sase surse (lista de preturi, contract-articole, contract-rate, comanda, avize, retur), meniul "Adauga articole" ofera exact optiunile tabelate in sectiunea 3; pentru fiecare sursa cu "tot"/"alege" ambele prezente, o selectie totala prin dialogul de alegere produce in `crsfactura` acelasi set de randuri (aceleasi valori, nu doar acelasi numar) ca "adauga tot"; zero rezultate in orice dialog de alegere produce un mesaj care spune sursa si motivul, nu un dialog gol; tip 52 se comporta identic cu tip 2/6 in titlu si structura de grid. --- ## 8. Ce NU se poate testa headless - **Continutul si comportamentul meniului `xmenu()`** — `Activate Screen` + `Define Popup ... Bar` e UI nativa Windows/VFP (shortcut popup la pozitia mouse-ului); nu exista API de citire a itemilor unui popup activ prin automatizare headless. Verificarea "meniul contine optiunile N" se poate face doar **indirect**, citind stringul `lcMeniu` construit inainte de apelul `xmenu()` (verificabil headless, ca text), nu popup-ul afisat efectiv. - **Coloanele griduri (`grd_articole`, `grd_contracte`, `grd_factura`, si viitorul grid al dialogului de alegere)** — capcana deja cunoscuta si confirmata pe acest proiect: sub `-A -T` (rulare headless) `ColumnCount=0` si `RecordSource` raman artefacte, coloanele nu se materializeaza. Exista un harness UI **vizibil** (nu headless) care le citeste corect — orice verificare vizuala a meniului/dialogului de alegere trebuie sa treaca prin acel harness, nu prin rulare `-A -T`. - **`cauta_alfa`/`cauta_alfa_form_plus`** — dialog modal cu `.Show(1)`, blocheaza thread-ul UI pana la interactiune; testarea automata ar necesita fie injectare de input (interzisa pe aceasta masina, partajata cu Marius — vezi memoria `masina-partajata-fara-input-real`), fie apelarea directa a functiilor Oracle/cursor din spatele dialogului, ocolind UI-ul (verifica *datele*, nu *dialogul*). - **`AMESSAGEBOX` la zero rezultate** — verificabil ca text al mesajului in cod (string literal), nu ca aparitie reala pe ecran, din acelasi motiv. - **Pozitionarea vizuala "deasupra gridului"** (`Top`/`Height` relative) — se poate verifica numeric (comparand `Top`-ul butoanelor cu `Top`-ul gridului in `.vc2`), dar nu si "arata bine"/aliniere vizuala — necesita captura de ecran prin harness-ul UI vizibil. Ce **se poate** verifica headless, direct: continutul cursoarelor (`crsarticole`, `crsarticole1`, `crsfactura`) dupa apelul rutinelor de adaugare, apelate direct (nu prin click) cu parametri de test; stringul `lcMeniu` construit inainte de `xmenu()`; valorile `Visible`/`Caption`/`ToolTipText` setate pe obiectele din `.vc2` (proprietati statice, nu comportament de runtime). --- ## 9. Riscuri, capcane de UX, si ce ramane de decis de Marius ### 9.1 Regulile de formulare/griduri (`COMUN\docs\reguli_lucru.md`, punctul 7) Aplicabile direct aici (citite, respectate in proiectarea de mai sus): - **`GO` pe `Recno()`** — orice bucla de tip `do_adauga_tot`/dialog de alegere care itereaza cursorul sursa (`crsarticole`/`crsarticole1`) trebuie sa retina si sa refaca pozitia (`Recno()`) dupa fiecare apel `do_adauga_articol`, exact cum face deja `do_adauga_tot` azi (`lnNrInregistrare = Recno()` ... `Go lnNrInregistrare`, `:13175,13185,13193`) — pastrat si in ramura noua pe `crsarticole1`. - **UX formulare/griduri** — butoanele fara `Caption` sunt azi conforme cu restul suitei (toate butoanele suita sunt icon-only); a le adauga `Caption` e o schimbare vizibila la nivelul intregii bare, nu doar a celor trei butoane noi — de discutat daca `But_renunt1`/`But_reset1` raman mute langa butoane cu text (inconsistenta vizuala posibila, nu doar tehnica). ### 9.2 Contractul are doua meniuri, nu unul (sectiunea 4.3) Tabelul din plan (J) trateaza "contract" ca o singura sursa cu un singur set de optiuni. Pe cod, un contract e **fie** OPT_FACTURARE=3 (articole), **fie** 1/2 (rate) — niciodata ambele. Eticheta "Alege ratele de facturat…" din decizia explicita a lui Marius e corecta doar pe ramura a doua; pe prima, echivalentul e "Alege articolele din contract…". **De decis**: se pastreaza o singura eticheta generica ("Alege din contract…") care acopera ambele cazuri fara sa distinga in text, sau se diferentiaza explicit (mai clar pentru operator, dar mai mult cod de verificare a lui `crsarticole1.opt_facturare` inainte de a construi meniul)? ### 9.3 "Alege facturile de returnat…" — la antet sau la bara de butoane? Azi (sectiunea 4.2), selectia facturilor sursa pentru retur se face **o singura data**, la deschiderea formularului de antet (`frm_date_factura.do_cauta_facturi`), inainte ca formularul de articole sa existe. Meniul S4b, asa cum e cerut de decizia 13, ar pune "Alege facturile de returnat…" **in bara de butoane a formularului de articole** — adica dupa ce `poDate.listaid` a fost deja fixat. Doua optiuni, de decis de Marius: 1. Optiunea din meniul S4b **nu schimba** setul de facturi sursa — e doar un rascolitor spre inapoi, catre acelasi dialog de la antet, dar utilizatorul trebuie sa iasa din pasul de articole ca sa-l foloseasca (comportament posibil confuz: optiunea apare in meniul de articole, dar actioneaza pe alt ecran). 2. Se adauga un mecanism nou: alegerea de facturi suplimentare **din formularul de articole**, care extinde `poDate.listaid` si reincarca aditiv `crsarticole` (`cursor_retur_document` apelat a doua oara, cu filtru pe facturile noi, `INSERT INTO crsarticole` in loc de `SELECT INTO`) — cere cod nou in VFP, dar nu in `pack_facturare` (procedura Oracle ramane aceeasi, doar apelata de mai multe ori). "Alege liniile de returnat…" (per articol, din facturile deja alese) e diferita si **nu are conflict** cu fluxul de azi — e un dialog de alegere clasic peste `crsarticole`, ca pe comanda/avize. ### 9.4 `id_pol` pe rata de contract — `NULL` prin constructie, dar validat corect Rata de scadentar (`crsarticole1`, ramura `OPT_FACTURARE IN (1,2)`) are **`id_pol` NULL** prin constructia SQL insasi (`NULL AS ID_POL`, `:2840`) — dar asta nu ajunge niciodata la `FACT-024`, pentru ca ratele nu trec prin `contabilizeaza_articol`, ci prin `contabilizeaza_rata` (functie separata in `pack_facturare`, `idpol_comanda_contract.md` §0c), care citeste nota din `CONTRACTE.ID_NOTA`. **De verificat inainte de implementare**: `do_adauga_articol`/`do_scrie_articole` scriu randul de rata in `crsfactura` cu ce anume in coloana `id_pol` (probabil `NULL`, mostenit din `poArticol`) — daca salvarea foloseste ramura corecta (`contabilizeaza_rata`, nu `contabilizeaza_articol`) pe baza altui semnal decat `id_pol` (probabil `id_ctr`/`opt_facturare` pe linie), nu pe baza lui `id_pol` fiind gol. Nu s-a verificat cine alege intre cele doua functii la salvare — e in afara perimetrului S4b (tine de scriere, nu de bara de butoane), dar merita un pointer explicit ca sa nu se presupuna gresit ca "rata fara `id_pol`" e acelasi caz cu "articol din nomenclator fara `id_pol`" (J-quater) — sunt cai de cod diferite, cu tratament diferit. ### 9.5 Ce ramane strict de decis de Marius 1. Eticheta pe contract (9.2) — generica sau diferentiata pe `OPT_FACTURARE`. 2. Fluxul "Alege facturile de returnat…" (9.3) — raman la antet sau se adauga incarcare aditiva. 3. Daca `But_renunt1`/`But_reset1` primesc si ele `Caption`, pentru consistenta vizuala cu bara nou- etichetata, sau raman icon-only (9.1). 4. Ordinea si formularea exacta a optiunilor in `xmenu()` per sursa (tabelul din sectiunea 3 e o propunere, nu o decizie finala de text). --- ## Handoff Cercetare incheiata, fara nicio problema de context intalnita in aceasta runda — nu a fost necesara predarea. Toate cele noua sectiuni cerute de brief sunt completate mai sus, cu `fisier:linie` verificat direct pe fisierele text reale (nu `.bak`), plus SQL-ul din `PACK_FACTURARE` exportat curent (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`). Nicio editare de cod, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit.