Files
roafacturare/docs/cercetare/s4b_bara_butoane_meniu.md
Marius Mutu b5a7108f34 docs: cercetarile comune trec in COMUN, reziduul #6 dispare, progres.md la zi
Curatenie ceruta dupa inchiderea lui #6. Folderul cercetare\ NU se putea sterge
in bloc: plan_13 se sprijina pe el cu 85 de trimiteri, deci e baza de dovezi a
planului aflat in lucru. Impartirea:

- 14 cercetari trans-proiect trec in COMUN\docs\cercetare\ - valuta si curs,
  TVA/VANZARI, consumatorii VANZARI din toata suita, integrarile #10/#11/#12,
  watchdog VFP, proiectarea Oracle a lui S5, view-ul VVANZARI_ARTICOLE. Nu sunt
  ale ROAFACTURARE, iar #10/#11/#12 se reiau chiar din ele.
- 20 de rapoarte de executie ale lui #6, nereferite de nimic viu, sterse.
- 91 raman, neatinse.

Trimiterile catre handoff-urile si diff-urile intermediare deja desfiintate au
fost curatate peste tot (39 de fisiere): 50 catre handoff-uri, 37 catre diff-uri
aplicate, plus caile celor mutate in COMUN. Zero trimiteri rupte ramase.

progres.md preia rolul de predare: ce ramane din #6 (cele sase documente
parazite, cele doua esecuri reale din S8 pe factura din aviz), cifrele de test
citite din log, si cele doua capcane de mediu platite - .FXP vechi executat in
locul .prg-ului, si GETFONT() care atarna un formular instantiat fara goApp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
2026-08-11 22:31:42 +03:00

607 lines
41 KiB
Markdown

# 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); "\<X" in fata unei litere = accelerator de tastatura
&& TNBAR = optiunea initial selectata (default 1)
...
RETURN IIF( LASTKEY()=27, 0, m.NSELECT ) && 0 la ESC sau la orice tastatura care nu alege
ENDPROC
```
- Popup-ul apare la pozitia mouse-ului (`Mrow()/Mcol()`), cu `DEFINE BAR` cate unul per optiune.
- Selectia se prinde in `GETCHOICE` (`oproceduri_comune.prg:1660-1666`): `m.NSELECT = Bar()`.
- **Returul e 1-based** (numarul barei alese), **`0` daca s-a apasat ESC** (`Lastkey()=27`) — verificat
prin `Empty(m.lnOptiune)` in tot codul (0 e Empty pentru numeric).
- Nu exista parametru de "titlu" separat — primul item e primul rand din popup, nu un header.
**Trei exemple reale, gata de copiat:**
1. **`inainte_de_do_modifica`** (`ofacturare_comun.vc2:4928-4935`) — meniu static, doua optiuni,
exact tiparul pe care planul il citeaza ca precedent pentru butonul unic:
```
PROCEDURE inainte_de_do_modifica
Local lnOptiune
lnOptiune = xmenu('Modificare \<date factura;Editare \<factura (articole, cantitati, preturi)')
Do Case
Case lnOptiune = 1
This.do_modifica()
Case lnOptiune = 2
This.do_editare_factura()
Endcase
ENDPROC
```
2. **`do_verifica`** (`ofacturare_comun.vc2:4879-4886`) — meniu static, trei optiuni, cu accelerator
pe fiecare literal:
```
lnOptiune = xmenu('Verifica codurile fiscale la fiecare \<data a facturilor;Verifica doar la \<inceputul lunii;Verifica doar la \<sfarsitul lunii')
IF EMPTY(m.lnOptiune)
RETURN
ENDIF
```
3. **Meniul de borderouri** (`ofacturare_comun.vc2:5006-5029`) — meniu **construit dinamic**, cu
optiuni suplimentare adaugate conditionat si un separator vizual `\-`:
```
lcMeniuPlus = ''
IF !(EMPTY(m.lnOptiuniPlus) OR ... )
FOR lnOptiunePlus = 1 TO m.lnOptiuniPlus
lcTitlu = ALLTRIM(GETWORDNUM(m.lcListaMeniu, m.lnOptiunePlus, '|'))
lcMeniuPlus = m.lcMeniuPlus + ';' + m.lcTitlu
ENDFOR
ENDIF
lcMeniu = m.lcMeniu + IIF(!EMPTY(m.lcMeniuPlus), ';\-' + m.lcMeniuPlus, '')
lnOptiune = xmenu(m.lcMeniu)
If Empty(m.lnOptiune)
Return
Endif
DO CASE
CASE m.lnOptiune = 1
...
```
Acesta e tiparul direct aplicabil la S4b: `lcMeniu` se construieste cu `Do Case poDate.tip` (vezi
sectiunea 3), apoi un singur `xmenu(lcMeniu)` si un `Do Case lnOptiune = N` care ramifica pe
pozitia in lista construita — **nu** pe numar fix, ca la exemplele 1-2, pentru ca lista variaza pe
tip. Trebuie tinut sincron `lcMeniu` (textul optiunii) cu ordinea in care se evalueaza `lnOptiune`
in `Do Case`, altfel un `Case lnOptiune = 3` nimereste alta optiune decat cea afisata — riscul
concret al unui meniu dinamic, absent la cele statice.
**Ce NU ofera `xmenu()`**: niciun mecanism de dezactivare a unei optiuni individuale (doar prezenta/
absenta din lista construita), nicio pictograma pe item, niciun submeniu. Pentru meniul din decizia 13
(4-5 optiuni pe sursa, variabile) e suficient — nu e nevoie de mai mult.
---
## 3. Conditiile de vizibilitate ale meniului, pe tip de document
Sursa: `frm_facturare_articole.Init`, `Do Case poDate.eProforma / poDate.lCopiere / poDate.tip`,
`ofacturare.vc2:15109-15248` (citit integral, nu esantion).
| Ramura (`Case`) | `tip`(uri) | Titlu | `but_urmator_tot1.Visible` | `but_retur.Visible` | `fisier:linie` |
|---|---|---|---|---|---|
| `eProforma = 1` | proforma (orice tip) | "PROFORMA" | **.T.** | — | `:15110-15113` |
| `lCopiere` | copiere factura/aviz | "COPIERE FACTURA/AVIZ ..." | **.T.** | — | `:15115-15120` |
| `Inlist(tip,1,5,7,10)` | lista de preturi (lei/valuta/credit note/fiscala valuta) | — | — | **.T.** | `:15122-15127` |
| `Inlist(tip,2,6)` | **contract** (lei/valuta) | "FACTURA PE CTR. ..." | — (nesetat, ramane `.F.`) | — | `:15129-15143` |
| `tip = 3` | comanda | "FACTURA LA COMANDA ..." | **.T.** | — | `:15144-15150` |
| `tip = 4` | din avize | "FACTURA DIN AVIZE" | **.T.** | — | `:15151-15167` |
| `Inlist(tip,21,28,42,47)` | aviz catre clienti din comanda | "AVIZ DE EXPEDITIE DIN COMANDA" | **.T.** | — | `:15168-15177` |
| `Inlist(tip,22,29)` | aviz din lista de preturi | "AVIZ DE EXPEDITIE DIN LISTA DE PRETURI" | — | — | `:15178-15186` |
| `tip = 23` | transfer subunitati | "TRANSFER INTRE SUBUNITATI" | — | — | `:15187-15195` |
| `tip = 41` | retur transfer | "RETUR TRANSFER" | — | — | `:15196-15205` |
| `tip = 25` | transfer din comanda | "TRANSFER INTRE SUBUNITATI PE BAZA DE COMANDA" | **.T.** | — | `:15206-15215` |
| `tip = 26` | **aviz din contract** | "AVIZ DE EXPEDITIE DIN CONTRACTUL ..." | — (nesetat) | — | `:15216-15234` |
| `Inlist(tip,8,9)` | factura retur lei/valuta | — | **.T.** | — | `:15236-15241` |
| `tip = 24` | aviz retur | "RETUR AVIZ DE EXPEDITIE" | **.T.** | — | `:15242-15248` |
| *(niciun Case)* | **`tip = 52`** | **nesetat — ramane titlul implicit al formularului** | **nesetat** | — | — |
**Corectie fata de plan** (`plan_13...md:924-926`, `:1882-1883`): planul spune "tipurile de contract
(2, 6, 26, 52) nu sunt in lista [de vizibilitate a `but_urmator_tot1`]". E adevarat pentru 2/6/26, dar
**incomplet pentru 52**: `poDate.tip = 52` nu apare in **niciun** `Case` al acestui `Do Case` — nu
doar ca nu primeste `but_urmator_tot1.Visible = .T.`, ci **nu primeste nimic**: nu i se seteaza
titlul (`lb_titlu_alb_b121.Caption` ramane cel implicit al formularului, probabil gol sau invechit),
nu i se scoate coloana `cSerie` din grid (ramane vizibila, desi contractul in valuta n-are serie de
lot relevanta aici), nu i se schimba eticheta coloanei de cantitate. Pe cod, tip 52 se comporta azi ca
un tip necunoscut care a ajuns totusi sa deschida formularul (posibil pentru ca fluxul Oracle il
recunoaste — `Inlist(tnTip,2,26,6,52)` la `cursor_contract`, `ofacturare.prg:283` — dar formularul de
articole nu l-a "prins" niciodata in `Do Case`-ul lui local). **Consecinta pentru S4b**: reparatia
corecta nu e "adauga 52 la linia lui 2/6" (ar ramane fara titlu/fara eliminare `cSerie`), ci
**adauga-l explicit ca al treilea membru al ramurii `Inlist(poDate.tip, 2, 6)`** de la `:15129`,
identic cu cum a fost tratat deja in `frm_date_factura.Init` (`ofacturare.vc2:9530`:
`Case Inlist(poDate.tip, 2, 6, 52)`, si inca o data la `:9632`, `:9670`) — **acolo tip 52 e deja
grupat corect cu 2/6**, doar in acest formular de articole a fost omis.
**Ce inseamna gruparea 2/6/26/52 pentru continutul meniului**: toate patru sunt "contract" — 2/6 =
factura pe contract (lei/valuta), 26 = aviz pe contract, 52 = factura fiscala in valuta pe contract
(`COMUN\docs\tipuri_documente_facturare.md:23,27,38,50`). Optiunile din tabelul J raman aceleasi
pentru toate patru (schimba doar cuvantul "Adauga"/"Alege" vs. eventual "Avizeaza" pe 26, daca se
pastreaza conventia de limbaj factura/aviz existenta in restul formularului — `lcTipDoc` calculeaza
deja acest cuvant in alte ramuri ale aceluiasi `Do Case`, e.g. `:15175,15185,15194`, dar **nu e setat
deloc pe ramura contract** — inca o mica omisiune, `lcTipDoc` ramane la valoarea implicita "factura"
si pe aviz de contract).
**Meniul propus, per sursa** (reluat din tabelul J, cu corectiile de mai sus):
| Sursa (`tip`) | Optiunile din `xmenu()` |
|---|---|
| lista de preturi (1,5,7,10) | Cauta in lista de preturi… · Alege din nomenclator… · Retur de articole… |
| **contract, `OPT_FACTURARE=3`** (2,6,26,52) | Adauga tot din contract · Alege articolele din contract… · Cauta in lista de preturi… · Alege din nomenclator… |
| **contract, `OPT_FACTURARE IN (1,2)`** (2,6,26,52) | Adauga toate ratele · **Alege ratele de facturat…** · Cauta in lista de preturi… · Alege din nomenclator… |
| comanda (3) | Adauga tot din comanda · Alege din comanda… · Cauta in lista de preturi… · Alege din nomenclator… |
| avize (4) / avize din comanda (21,28,42,47) / transfer din comanda (25) | Adauga tot din avize · Alege avizele… · Alege liniile din avize… · Cauta in lista de preturi… · Alege din nomenclator… |
| retur (8,9,24) | Adauga tot din facturile alese · Alege liniile de returnat… · Cauta in lista de preturi… · Alege din nomenclator… |
Diferenta fata de tabelul din plan e explicata in sectiunea 4 (contractul are doua meniuri posibile,
nu unul) si in sectiunea 9 (retur nu are azi un pas separat "alege facturile" la nivelul acestui
formular — vezi mai jos).
---
## 4. Dialogul de alegere selectiva
### 4.1 Nu porni de la `frm_tranzit` (RORIS) — porneste de la `cauta_alfa`
Raportul de referinta (`COMUN\docs\cercetare\import_roris_roaacnpro.md`) descrie corect **arhitectura
generala** (buton conditionat -> 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.