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
41 KiB
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()), cuDEFINE BARcate unul per optiune. - Selectia se prinde in
GETCHOICE(oproceduri_comune.prg:1660-1666):m.NSELECT = Bar(). - Returul e 1-based (numarul barei alese),
0daca s-a apasat ESC (Lastkey()=27) — verificat prinEmpty(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:
-
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 -
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 -
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:
lcMeniuse construieste cuDo Case poDate.tip(vezi sectiunea 3), apoi un singurxmenu(lcMeniu)si unDo Case lnOptiune = Ncare ramifica pe pozitia in lista construita — nu pe numar fix, ca la exemplele 1-2, pentru ca lista variaza pe tip. Trebuie tinut sincronlcMeniu(textul optiunii) cu ordinea in care se evalueazalnOptiuneinDo Case, altfel unCase lnOptiune = 3nimereste 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_alfaadauga singura o coloanaalespeste 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
tcStringCriteriideschidecauta_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 prinXmltocursor(...)(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:combosqlpe o singura linie, populata pe loc, fara incarcare in masa. In termeni de UI, alegerea uneia din aceste doua optiuni echivaleaza cu ce face aziBut_nou1.do_adaugadin 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 acelasiAPPEND 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_polpe randul adaugat princombosql(S4, cautare din nomenclator liber) — subiectul blocajuluiFACT-024documentat pe larg in plan (J-ter/J-quater) si inidpol_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 eroareFACT-024la 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
- Bara de linie (
but_nou,but_sterge, "detalii linie"), mutata deasupra gridului, cuCaptionexplicit pe fiecare instanta (claselebut_nou/but_stergeau dejaToolTipText, nu au niciodataCaption— 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. - Corectarea
Do CasedinInit(ofacturare.vc2:15109-15248): adauga52la ramuraInlist(poDate.tip, 2, 6)(linia 15129) astfel incat sa primeasca titlu, eliminarecSerie, 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). Adauga26la lista tipurilor care primescbut_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 (faracSerie) ca un document de tip 2/6. - Extinderea
do_adauga_totcu ramura pecrsarticole1(parametru sau ramura separata dupapoDate.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 cuOPT_FACTURARE=3si articole configurate, "adauga tot" produce incrsfacturaexact liniile dincrsarticole1; pe un contract cuOPT_FACTURARE IN (1,2), produce cate o linie per rata neincasata. - Butonul unic "Adauga articole" cu
xmenu(), inlocuindBut_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 decrsarticole1.opt_facturare(sectiunea 4.3). Verificabil: pe fiecare sursa, meniul deschis contine exact optiunile din tabelul sectiunii 3, in ordinea specificata, iar alegereaESC(xmenu()intoarce 0) nu declanseaza nimic. - Dialogul de alegere selectiva, pe reteta
cauta_alfa(..., tnTipReturn=1)(sectiunea 4), cu mesaj explicit la zero rezultate (4.5) si populare prindo_adauga_articolrand cu rand (5) — cate o instantiere per sursa (comanda/contract-articole/contract-rate/avize/retur-linii), cutcselect/tcfiltruproprii. Verificabil: pe fiecare sursa, bifarea a N randuri din M disponibile adauga exact acele N randuri incrsfactura, 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 rutinado_adauga_articoltrebuie sa functioneze deja pecrsarticole1inainte ca dialogul de alegere sa se poata baza pe ea). - 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 ... Bare 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 stringullcMeniuconstruit inainte de apelulxmenu()(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=0siRecordSourceraman 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 memoriamasina-partajata-fara-input-real), fie apelarea directa a functiilor Oracle/cursor din spatele dialogului, ocolind UI-ul (verifica datele, nu dialogul).AMESSAGEBOXla 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/Heightrelative) — se poate verifica numeric (comparandTop-ul butoanelor cuTop-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):
GOpeRecno()— orice bucla de tipdo_adauga_tot/dialog de alegere care itereaza cursorul sursa (crsarticole/crsarticole1) trebuie sa retina si sa refaca pozitia (Recno()) dupa fiecare apeldo_adauga_articol, exact cum face dejado_adauga_totazi (lnNrInregistrare = Recno()...Go lnNrInregistrare,:13175,13185,13193) — pastrat si in ramura noua pecrsarticole1.- UX formulare/griduri — butoanele fara
Captionsunt azi conforme cu restul suitei (toate butoanele suita sunt icon-only); a le adaugaCaptione o schimbare vizibila la nivelul intregii bare, nu doar a celor trei butoane noi — de discutat dacaBut_renunt1/But_reset1raman 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:
- 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).
- Se adauga un mecanism nou: alegerea de facturi suplimentare din formularul de articole, care
extinde
poDate.listaidsi reincarca aditivcrsarticole(cursor_retur_documentapelat a doua oara, cu filtru pe facturile noi,INSERT INTO crsarticolein loc deSELECT INTO) — cere cod nou in VFP, dar nu inpack_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
- Eticheta pe contract (9.2) — generica sau diferentiata pe
OPT_FACTURARE. - Fluxul "Alege facturile de returnat…" (9.3) — raman la antet sau se adauga incarcare aditiva.
- Daca
But_renunt1/But_reset1primesc si eleCaption, pentru consistenta vizuala cu bara nou- etichetata, sau raman icon-only (9.1). - 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.