Files
roafacturare/docs/cercetare/s4f_retur_formular_unificat.md
2026-09-09 22:19:22 +03:00

52 KiB

S4f — Returul in formularul unificat

Proiectare pe cod, READ-ONLY (fara editari, fara git_sync.ps1/txt2vcx.ps1, fara commit, fara scriere Oracle — doar SELECT), pentru povestea S4f din docs\plan_13_unificare_formular_facturare.md:2287-2330 (decizia 17, proiectata in sectiunea N, :1574-1655). Nu s-a atins COMUN\clase\ofacturare_comun.vc2 si COMUN\programe\ofacturare_editare.prg — doar citite, cand au aparut in cautari.

Surse de adevar deja stabilite, citate nu reinvestigate: docs\cercetare\legatura_linie_retur.md, docs\cercetare\factura_retur_document.md, docs\cercetare\retur_si_lista_preturi.md, docs\cercetare\s4b_bara_butoane_meniu.md.

Verdict (esenta, 10 randuri)

Corectat dupa avertismentul S4e (docs\cercetare\s4e_lista_preturi_pe_sursa.md, sectiunile 3 si 7): mecanismul APPEND FROM din decizia 16 (ofacturare.prg:454-473) e respins pentru orice cursor care serveste si drept registru citit de do_sterge, crsarticole fiind exact acel cursor pe documentele de retur (Inlist(poDate.tip,8,9,24) => Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c, ofacturare.vc2:14658-14659) — riscul e coliziune de id_c intre cursor_retur_document si cursor_preturi, ambele numerotate ROWNUM independent. Sectiunea 5 de mai jos e rescrisa pe solutia validata de S4e: liniile libere nu intra deloc in crsarticole, se adauga direct in crsfactura prin APPEND BLANK + combosql (tiparul deja in productie la frm_facturare_articole2.But_nou1.do_adauga), iar campul de distinctie e crsfactura.id_c (0 implicit pe o linie libera, valoare pe care Oracle n-o produce niciodata pentru id_c), nu o coloana lipsa in crsarticole.

Partea grea nu e ce parea: N.1 (documentul de retur, tip 8/9/24) merge deja cap-coada — alegere multipla de facturi, populare din cursor_retur, gestiune si pret de achizitie mostenite, stergere si retur partial. Munca reala e in trei bucati inguste. (1) Nu se muta dialogul de alegere a facturilor in bara de butoane a gridului — s-a verificat pe cod ca alegerea ramane la nivelul antetului (sectiunea pliata), exact cum recomanda deja s4b_bara_butoane_meniu.md §9.3/tabelul "puncte marunte", randul h; ce se muta in meniul "Adauga articole" e rezultatul alegerii (liniile deja aduse), ca "Adauga tot din facturile alese" / "Alege liniile de returnat…", simetric cu comanda/contract/avize. (2) Ridicarea lui But_retur la nivel de document e cod nou real, nu o mutare de buton — azi alegerea e per-articol si niciodata scrisa ca legatura persistata. (3) Liniile libere pe retur folosesc reteta S4e ("APPEND BLANK pe crsfactura, niciodata pe crsarticole"), plus un gol nou de proiectat, gasit in aceasta sesiune si necunoscut de S4e (care nu are dialog de gestiune pe comanda/avize): mecanismul de alegere a gestiunii (frm_articol_gest_factura, .lRetur=.T.) suprascrie pretul de vanzare cu pretul din stoc (ofacturare.vc2:4094-4103) — corect pentru o linie mostenita dintr-o factura sursa, dar gresit pentru o linie libera, a carei pret de vanzare trebuie sa vina din lista de preturi (decizia 16), nu din costul de stoc; in plus, acel dialog se intra azi doar dintr-un poArticol scatter-uit din crsarticole (do_adauga_articol/do_alege_stoc), pe care o linie libera din crsfactura nu-l are — e nevoie de un punct de intrare nou in dialog, nu doar de reparat pretul. Vezi sectiunea 5 si riscurile R1/R7.


1. Inventarul celor doua cai de retur de azi

1.1 N.1 — factura de retur ca document (tip 8, 9, aviz 24)

Pas Ce face fisier:linie
Intrare Tile Page2.Cw1 -> politica.mpr -> factureaza(8)/factureaza(9) din meniul shortcut COMUN\clase\ofundal_facturare.vc2:882-884, Meniuri\politica.mn2:14-15,45-46
Antet nIdTipDoc=5, formular frm_date_factura, aviz retur (24) foloseste frm_date_aviz cu nIdTipDoc=6 COMUN\programe\ofacturare.prg:192-196,222-226
Alegere facturi sursa frm_date_factura.do_cauta_facturi -> caut_facturi_multiple_client(id_client, in_valuta, id_valuta, .T.) -> cauta_alfa(..., tnTipReturn=1), selectie multipla, exclude tipurile de retur din sursa COMUN\clase\ofacturare.vc2:9173-9212; COMUN\programe\oproceduri_facturare.prg:2091-2121
Validare obligatorie Fara alegere, antetul nu se poate inchide (inainte_de_do_termin) ofacturare.vc2:9523-9526
Populare linii pack_facturare.cursor_retur(V_IN_VALUTA,V_LISTAID,V_ID_UTIL) -> cursor_retur_document(...,V_COPIERE=0,...), SELECT direct din VANZARI_DETALII, filtrat pe ID_VANZARE IN (V_LISTAID) ofacturare.prg:306-307; ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:3943-3956,3958-4071
Gestiune + pret achizitie ID_GESTIUNE/PRET_ACHIZITIE copiate neschimbate din linia originala; GESTIONABIL = NVL2(A1.ID_GESTIUNE,1,0) ff_...sql:4034-4035,4055-4056,4048
Stergere linie Permisa, cantitatea reintra in cursorul sursa ofacturare.vc2:14608-14693, ramura :14658-14659
Retur partial Permis, validat fata de maximul = CANTITATE a liniei originale, fara agregare cu retururi anterioare ofacturare.vc2:14743-14754, ff_...sql:4036
Adaugare libera Nu se poate azi — do_adauga_articol ia articolul mereu din crsarticole, care pe tip 8/9/24 este rezultatul lui cursor_retur ofacturare.vc2:12843-12851

1.2 N.2 — But_retur per-articol, in factura normala (tip 1, 5, 7, 10)

Pas Ce face fisier:linie
Vizibilitate Doar pe tip 1/5/7/10; pe documentele de retur (8/9/24) butonul nu exista in UI ofacturare.vc2:15122-15127
Declansare caction=do_retur -> do_adauga_articol(.F.,.F.,.T.) cmd_butoane.vc2:324-338; ofacturare.vc2:13963-13965
Alegere factura sursa Per articol, la fiecare adaugare: caut_facturi_multiple_client_articol(id_client,in_valuta,id_valuta,.F.,id_articol) -> cauta_alfa cu titlu simplu "Alegeti factura" (nu selectie multipla) oproceduri_facturare.prg:2124-2159
Acumulare thisform.cListaIdArticoleRetur acumuleaza CSV de perechi id_articol:id_vanzare, cate una per articol adaugat; poDate.listaid e folosit transient (salvat in lnListaIdOld si restaurat imediat dupa) doar cat dureaza dialogul per-articol ofacturare.vc2:12882-12892
Gestiune Se alege, nu se mosteneste — pack_facturare.cursor_gestiuni_articol_retur, prin do_alege_stoc(...,tlRetur=.T.) -> frm_articol_gest_factura ofacturare.vc2:13230,13353-13385
Pret Suprascris cu pretul din stoc (pretv), nu cel din lista de preturi — vezi sectiunea 5 ofacturare.vc2:4094-4103
Semn cantitate Explicit poArticol.cantitate = (-1) * cantitate cand Thisform.lRetur ofacturare.vc2:4103
Trimitere Oracle poDate.listaid = thisform.cListaIdArticoleRetur la scriere finala ofacturare.vc2:13977-13979
Legatura persistata Nicio legatura scrisa — listaid e folosit doar ca filtru in calculul de disponibil pe rulaj (RUL), nu scrie nimic in VANZARI_CORESP sau pe linia noua ff_...sql:8142-8212

1.3 Ce au in comun si unde diverg

Comun: excluderea returului dintr-un retur (acelasi filtru tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11) in ambele functii de cautare), conventia de semn negativ pe cantitate, frm_articol_gest_factura ca formular de alegere a gestiunii pentru linia gestionabila.

Diverg pe tot restul: N.1 alege facturile o singura data, la nivel de document, inainte ca articolele sa existe; N.2 alege per articol, in orice moment, fara limita de cate ori. N.1 mosteneste gestiune+pret_achizitie fara alegere; N.2 le cere explicit. N.1 scrie VANZARI_CORESP(TIP=3); N.2 nu scrie nimic persistat. Zero cod comun intre cele doua cai — ofacturare.vc2:12813-12900 (N.2, do_adauga_articol) si ofacturare.prg:306-311 (N.1, cursor_retur) nu se ating.


2. do_cauta_facturi / cauta_alfa(..., tnTipReturn=1) — contractul exact

2.1 Cine il cheama si cand

Pe frm_date_factura exista un control generic de cautare, Ct_clb_altele (clasa ct_clb_cautare), a carui iconita de lupa e legata de cprocedura = thisform.do_cauta_altele (ofacturare.vc2:8721-8729). do_cauta_altele e un dispecer pe nid_tip:

Case Inlist(Thisform.nid_tip,8,9)
    Thisform.do_cauta_facturi()

(ofacturare.vc2:8953-8954)

Secventa confirmata pe cod (factureaza(), ofacturare.prg:187-311): frm_date_factura se creeaza si se afiseaza (.Show(), :230-235) inainte ca Do Case tnTip de la :266-308 sa ruleze cursor_retur, si mult inainte ca lcObject = [frm_facturare_articole] sa fie ales (:395). Executia continua abia dupa ce ofrmceredate (antetul) se elibereaza (:248), iar pnButon (setat la inchiderea antetului) e verificat imediat dupa. Deci azi dialogul de alegere a facturilor ruleaza pe formularul de antet, complet inainte ca formularul de articole sa existe — crsarticole se incarca o singura data, cu poDate.listaid deja fixat.

Control observat, semnificativ pentru sectiunea 3: Ct_clb_altele are cconditie = EMPTY(NVL(poDate.listaid,'')) (ofacturare.vc2:8722) — sugereaza ca iconita de cautare e activa doar cat timp nimic nu a fost inca ales; neverificat insa exact ce proprietate citeste cconditie (clasa ct_clb_cautare nu a fost gasita in text in acest repo, posibil intr-un .vcx neconvertit sau in COMUNROA), deci nu se poate confirma daca astazi re-alegerea e blocata explicit sau doar descurajata vizual. De tratat ca ipoteza, nu ca fapt confirmat.

2.2 Contractul functiei

caut_facturi_multiple_client(tnIdPart, tnInValuta, tnIdValuta, tlFacturiMultiple) (oproceduri_facturare.prg:2091-2121):

  • Intrare: client (obligatoriu — dialogul nu porneste fara el), valuta (doar daca tnInValuta), tlFacturiMultiple=.T. la acest apel -> lnTipReturn=1 in cauta_alfa.
  • Sursa: select serie_act,numar_act,data_act,dataora,id_vanzare from fact_vfacturi where sters=0 and tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11) [+ conditie sucursala] [+ client] [+ valuta] — fara filtru SQL pe perioada/serie (filtrarea pe acele coloane e comportament generic al cauta_alfa, nu parametru dedicat).
  • Iesire: XML cu randurile bifate (ales=1), convertit local prin Xmltocursor(..., "crsFacturiTemp"); nimic nu ajunge la Oracle in acest pas.
  • Populare aditiva a rezultatului in VFP: poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",") — inlocuieste intreg continutul (=, nu +=); daca dialogul se apeleaza a doua oara azi, a doua rulare suprascrie complet prima (dar azi asta nu se intampla niciodata pentru ca dialogul ruleaza o singura data, inainte de orice alta actiune pe document — vezi 2.1).
  • cauta_alfa(...,tnTipReturn=1) (COMUN\programe\cauta_alfa.prg:16-260, mecanismul generic folosit peste tot in suita): adauga singur coloana ales (cauta_alfa.prg:124), titlu explicit "Alegeti ... (mouse-click pe numar sau apasati SPACE)", filtrare interactiva pe coloanele afisate. Nu are mecanism de dezactivare a unei optiuni individuale, nici submeniu — doar bifare/debifare.

2.3 Varianta per-articol

caut_facturi_multiple_client_articol(tnIdPart, tnInValuta, tnIdValuta, tlFacturiMultiple, tnIdArticol) (oproceduri_facturare.prg:2124-2159) — aceeasi functie de baza, cu doua diferente: filtru suplimentar b.id_articol = ?pnIdArticol (join pe vanzari_detalii) si tlFacturiMultiple=.F. la apelul din do_adauga_articol (:12884), deci lnTipReturn=0 -> selectie simpla, nu multipla. Titlu simplu "Alegeti factura". Rezultatul e un obiect cu un singur id_vanzare, nu un cursor.


3. Proiectarea punctului 1 — alegerea facturilor sursa ca sursa in meniul de adaugare

Ce NU se schimba: alegerea propriu-zisa a facturilor sursa (dialogul cu bifare multipla) ramane la nivelul antetului — in formularul unificat, sectiunea pliata (decizia 7). Verificat impotriva tabelului "puncte marunte" din plan (plan_13...md, randul h): „Alege facturile de returnat…" ramane doar la antet in etapa I; mutarea (in bara de butoane) e o poveste separata — si confirmat independent de tabelul de meniu al lui s4b_bara_butoane_meniu.md §3: pe randul "retur (8,9,24)", meniul "Adauga articole" propus contine "Adauga tot din facturile alese" · "Alege liniile de returnat…" · "Cauta in lista de preturi…" · "Alege din nomenclator…" — nu si "Alege facturile de returnat…". Punctul 1 al lui S4f nu contrazice asta: ce se muta in meniul de adaugare e rezultatul alegerii deja facute la antet (liniile din facturile alese), simetric cu comanda/ contract/avize, nu dialogul de alegere a facturilor insusi.

Ce SE schimba, si e proiectarea reala: azi secventa e rigida — antet modal -> cursor_retur -> formular de articole (sectiunea 2.1). In formularul unificat exista un singur formular, mereu deschis; sectiunea de antet (pliata) si gridul de articole coexista. Trei consecinte de proiectat:

  1. Cand se declanseaza cursor_retur_document? Nu mai poate fi legat de "inchiderea antetului" (nu mai exista acel eveniment). Trebuie legat de confirmarea dialogului de alegere (butonul de cautare din sectiunea pliata, echivalentul lui Ct_clb_altele/do_cauta_facturi): imediat ce utilizatorul bifeaza facturile si confirma, poDate.listaid se seteaza si cursor_retur_document ruleaza pe loc, populand/repopuland crsarticole. Documentul poate porni cu tipul deja pe 8/9/24 (ales din combo-ul de tip document) inainte ca vreo factura sa fie aleasa — grid-ul de articole ramane gol pana la prima alegere, exact ca azi cand Reccount(crsarticole)=0 da mesajul "Nu exista articole pentru optiunile alese!" (ofacturare.prg:325).

  2. Poate opera la orice moment, nu doar o data. Spre deosebire de azi (dialogul ruleaza o singura data, inainte ca gridul sa existe), in formularul unificat butonul din sectiunea pliata e apasabil oricand documentul e deschis. Trebuie decis explicit comportamentul la a doua apasare — vezi punctul urmator.

  3. Alegere aditiva sau inlocuitoare, la a doua apasare. Azi poDate.listaid = ... inlocuieste (nu +=), dar azi nu conteaza pentru ca nu se poate apasa a doua oara dupa ce gridul exista. In formularul unificat, daca utilizatorul a ales deja factura A, a adaugat cateva linii din ea in crsfactura, si apoi vrea sa adauge si din factura B, doua variante:

    • (a) Inlocuire — a doua alegere reseteaza poDate.listaid/crsarticole la noul set ales; liniile deja mutate in crsfactura raman (crsfactura e independent de crsarticole dupa mutare), dar orice linie inca needitata din factura A dispare din crsarticole fara avertisment. Simplu de implementat (identic cu azi), dar surprinzator pentru operator.
    • (b) Aditiv — a doua alegere extinde poDate.listaid cu facturile noi (evitand duplicate) si ruleaza cursor_retur_document filtrat doar pe facturile noi, cu INSERT INTO crsarticole (nu SELECT INTO), exact reteta deja propusa de s4b_bara_butoane_meniu.md §9.3, optiunea 2 — cere cod VFP nou (bucla de filtrare + INSERT), dar zero cod nou in pack_facturare (aceeasi procedura, apelata de mai multe ori). Consistent cu filozofia deciziei 16 ("sursa umple documentul, nu il inchide").

    Recomandare: varianta (b), aditiv — e coerenta cu tratamentul lista-de-preturi (S4e) si cu comanda/contract (unde alegerea suplimentara nu sterge ce exista deja), si evita pierderea tacuta de linii needitate. Ramane decizie de confirmat de Marius — nici plan-ul, nici s4b_bara_butoane_meniu.md nu au tranzat-o explicit (§9.5, punctul 2 acolo o listeaza tot ca deschisa).

Consecinta pentru validarea de antet: garda de azi (inainte_de_do_termin, descriere obligatoriu pentru tip 8/9, ofacturare.vc2:9523-9526) nu mai are un moment natural "inainte de a inchide antetul" — se muta la garda echivalenta din Termina (aceeasi verificare, alt declansator).


4. Proiectarea punctului 2 — ridicarea But_retur la nivel de document

Ce se pastreaza din calea per-articol: alegerea gestiunii ramane per-articol, prin cursor_gestiuni_articol_retur si frm_articol_gest_factura — decizia 22/N.3 spune explicit ca gestiunea si pretul de achizitie se aleg, nu se mostenesc, pentru orice linie fara factura sursa unica precisa (si N.2 nu are niciodata o factura "sursa unica a documentului", are cel mult factura aleasa pentru articolul curent). Punctul 2 nu schimba asta.

Ce se schimba: alegerea facturii sursa nu mai e per-articol (un dialog cauta_alfa cu selectie simpla la fiecare do_retur), ci la nivel de document, dupa modelul N.1 — un singur dialog cu selectie multipla (caut_facturi_multiple_client, acelasi ca la N.1, dar filtrat pe articolul curent doar daca ramane necesar; de decis daca filtrul pe articol dispare complet odata cu ridicarea la document). Consecinta directa: poDate.listaid (sau un camp nou, vezi mai jos) ar fi populat o singura data pentru intreg documentul, nu reconstruit per articol prin caut_facturi_multiple_client_articol.

Capcana de proiectare, gasita pe cod: poDate.listaid e deja partajat intre N.1 si N.2 azi — N.2 il suprascrie transient (lnListaIdOld = poDate.listaid ... poDate.listaid = m.lnListaIdOld, ofacturare.vc2:12882,12892) doar cat dureaza alegerea per-articol, apoi il restaureaza. Daca N.2 ridicat la document incepe sa scrie permanent in poDate.listaid (ca N.1), cele doua mecanisme intra in conflict pe acelasi camp — un document normal (tip 1/5/7/10) cu retur ridicat la nivel de document ar avea poDate.listaid populat cu facturile de retur alese, camp care pe alte tipuri (comanda/contract/avize) e deja folosit cu alt sens (id-uri de comanda/contract/aviz sursa, vezi ofacturare.prg:266-308). Nu e conflict pe tip 1/5/7/10 — acele tipuri nu folosesc azi poDate.listaid pentru altceva (sunt populate cu cursor_preturi, care nu ia V_LISTAID ca parametru) — deci reutilizarea e sigura tehnic, dar merita un camp cu nume propriu (poDate.listaid_retur sau similar) ca sa nu se ambiguizeze semantic ce inseamna listaid pe un document normal. De decis la implementare, nu blocant.

Ce arata in bara de butoane / meniu (S4b): tabelul din s4b_bara_butoane_meniu.md §3 nu are azi un rand pentru "retur intr-o factura normala" — doar pentru documentele de retur propriu-zise (8,9,24). Ridicarea lui N.2 la nivel de document introduce o optiune noua in meniul "Adauga articole" pentru tipurile 1,5,7,10: "Retur de articole…" (deja anticipata in tabelul §3 al raportului S4b, coloana lista de preturi: "Cauta in lista de preturi… · Alege din nomenclator… · Retur de articole…"). Aceasta optiune deschide dialogul de alegere multipla la nivel de document (prima data), apoi pe alegerile ulterioare acelasi comportament aditiv/inlocuitor discutat la sectiunea 3 se aplica identic.

Ce dispare: caut_facturi_multiple_client_articol (filtrul per-articol) ramane cod mort daca alegerea trece integral la nivel de document — sau se pastreaza ca alegere secundara optionala ("mai am nevoie de o factura sursa doar pentru articolul X", nefolosita azi in acest scenariu). Nu e clar din decizia 17/N.3 daca trebuie eliminata explicit sau doar nu mai e calea principala — de decis de Marius (risc R3).


5. Proiectarea punctului 3 — liniile libere pe document de retur

Corectie fata de premisa initiala a brief-ului ("prin acelasi APPEND FROM ca la S4e"): S4e a gasit ca reteta literala a deciziei 16 (ofacturare.prg:454-473) nu e sigura pe niciun tip unde crsarticole e citit ca registru de do_sterge/do_scrie_factura, si a documentat asta explicit pentru retur, in tabelul sau de la §7:

"retur ca document (8,9,24) | crsarticole = registru citit de do_scrie_factura? Nu — cad pe Otherwise, fara Sum(cantitate)/pnParametruAditional | do_sterge ajusteaza crsarticole pe id_c? Da, partial — Inlist(poDate.tip,8,9,24) => Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c (:14658-14659, semn opus, urmareste maximul returnabil, nu inchiderea) | Concluzie: risc de coliziune id_c ramane, dar fara poluarea Sum: o linie libera adaugata literal prin APPEND FROM in crsarticole pe un document de retur ar putea, la stergere, atinge tacut cantitatea maxima returnabila a unei linii reale — de tratat cu aceeasi solutie (linii libere in afara crsarticole) si in S4f." (s4e_lista_preturi_pe_sursa.md, §7, randul "retur ca document")

Solutia S4e (verificata acolo pe comanda/avize) se aplica identic aici — sectiunile de mai jos sunt rescrise pe ea, nu pe APPEND FROM.

5.1 Mecanismul corect: APPEND BLANK pe crsfactura, niciodata pe crsarticole

Liniile libere nu intra in crsarticole. Se adauga direct in crsfactura, prin acelasi gest deja in productie la frm_facturare_articole2.But_nou1.do_adauga (ofacturare.vc2:17118-17122):

PROCEDURE do_adauga
    Select crsFactura
    APPEND BLANK
    this.grd_factura.SetFocus()
ENDPROC

urmat de editare inline prin combosql pe celula de cod/denumire (mecanismul construit de S4/S4e pentru cautarea filtrata pe server, pack_facturare.cursor_preturi cu V_FILTRU_COD/V_FILTRU_DEN). Optiunea de meniu "Cauta in lista de preturi…" (deja in tabelul S4b §3, pe randul retur) declanseaza exact acest gest — nu un cursor_retur_document suplimentar si nu un APPEND FROM peste crsarticole.

5.2 Cum se distinge o linie libera de una din factura sursa

Campul corect e crsfactura.id_c, nu o coloana pe crsarticole. O linie mostenita ajunge in crsfactura prin do_adauga_articol, care face Select (lcCursor) / Scatter Name poArticol pe randul ales din crsarticole (populat de cursor_retur_document, cu id_c = ROWNUM, pornind de la

  1. si copiaza acel id_c in randul nou din crsfactura. O linie libera, adaugata prin APPEND BLANK direct pe crsfactura (5.1), nu trece niciodata prin acest Scatter — id_c ramane la valoarea implicita a campului (N(20), fara Null, deci 0, creeaza_facturacrs, COMUN\programe\ofacturare_comun.prg:1775-1785, citat si verificat de S4e §3 punctul 3). Oracle nu produce niciodata id_c = 0 (ROWNUM incepe la 1) — deci crsfactura.id_c = 0 e semnalul curat, fara camp nou, care distinge o linie libera de una mostenita. Acelasi tipar e deja in productie pentru linia de discount (ofacturare.vc2:14531-14537, Append Blank fara id_c setat).

Consecinta directa, fara cod suplimentar: do_sterge, ramura de retur (Case Inlist(poDate.tip,8,9,24) => Select crsarticole / Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c, ofacturare.vc2:14658-14659) cauta in crsarticole un rand cu id_c = poArticol.id_c. Pentru o linie libera, poArticol.id_c = 0, iar crsarticole nu are niciodata un rand cu acel id_c — REPLACE ... FOR devine no-op prin constructie. Stergerea unei linii libere de pe un document de retur nu atinge maximul returnabil al nici unei linii mostenite — exact ce cerea brief-ul la punctul 5, si exact acelasi mecanism (nu doar aceeasi idee) ca solutia S4e pentru comanda.

5.3 Pretul de vanzare — riscul gasit (R1, ramane valabil)

Nu e doar despre gestiune/pret de achizitie, si nu se rezolva prin mutarea in crsfactura. Cand o linie ajunge la do_alege_stoc cu Inlist(poDate.tip,8,9,24) adevarat (orice linie pe un document de retur, indiferent de origine — conditia e pe poDate.tip, nu pe un flag de linie), se deschide frm_articol_gest_factura. In acel formular, la schimbarea coloanei de gestiune (grd_gestiuni.RowColChange), daca Thisform.lRetur e adevarat:

If Thisform.lRetur
    poArticol.pretftva = pretv          && pret DIN STOC, nu din lista de preturi
    ...
    poArticol.cantitate = (-1) * cantitate
Endif

(ofacturare.vc2:4094-4103)

Pentru o linie mostenita (N.1), asta e corect prin constructie — returnezi marfa la pretul cu care a fost vanduta. Pentru o linie libera (decizia 22 — articol niciodata vandut clientului, deci fara "pret de retur" de mostenit), suprascrierea cu pretul de stoc e o eroare de pret: linia libera ar iesi cu costul de stoc in loc de pretul din lista de preturi ales prin combosql la 5.1.

Solutie propusa, actualizata: frm_articol_gest_factura primeste un al doilea semnal, distinct de tlRetur ("esti pe flux de retur"), care sa insemne "aceasta linie are factura sursa" — derivat acum din crsfactura.id_c <> 0 (5.2), nu dintr-o coloana lipsa in crsarticole. Ramura de suprascriere pret (:4094-4103) se activeaza doar cand acest al doilea semnal e adevarat.

5.4 Golul nou, nu tratat de S4e: punctul de intrare in dialogul de gestiune

S4e nu are acest gol pentru ca nu are dialog de gestiune pe comanda/avize — liniile libere de acolo se completeaza integral prin combosql (pret, TVA, valuta) fara sa mai treaca prin niciun dialog de alegere a gestiunii. Pe retur, decizia 22 cere explicit ca gestiunea si pretul de achizitie sa se aleaga pentru o linie libera — mecanismul care stie sa faca asta e cursor_gestiuni_articol_retur + frm_articol_gest_factura, dar acel dialog se intra azi doar din do_alege_stoc, apelat de do_adauga_articol pe baza unui poArticol scatter-uit dintr-un rand deja incarcat in crsarticole (ofacturare.vc2:12843-12851,12891). O linie libera adaugata prin APPEND BLANK pe crsfactura (5.1) nu are un asemenea rand sursa — exact acelasi gol pe care S4e l-a semnalat, netratat, pentru validarea de cantitate pe comanda (s4e_lista_preturi_pe_sursa.md §6: "do_verifica_articol cere un poArticol scatter-uit dintr-un cursor sursa deja incarcat... o linie libera... nu are un asemenea rand sursa").

Consecinta pentru S4f, mai stricta decat pe comanda: pe comanda/avize (S4e), lipsa unui poArticol de sursa afecteaza doar validarea de cantitate (un gol de acoperit, dar linia se poate scrie si fara gestiune aleasa — comanda/avize nu cer gestiune diferita de cea din lista de preturi). Pe retur, lipsa aceluiasi poArticol blocheaza si alegerea gestiunii, care e obligatorie prin decizia 22. Trebuie proiectat un punct de intrare nou in frm_articol_gest_factura/do_alege_stoc, care sa porneasca de la randul deja inserat in crsfactura (dupa combosql, cu id_articol si cantitate deja completate) in loc de la un rand din crsarticole — practic o a doua cale de apel, cu acelasi dialog la capat, dar poArticol construit ad-hoc din crsfactura in loc de Scatter din crsarticole. Aceeasi problema structurala, aceeasi solutie propusa (poArticol construit ad-hoc) ca varianta (b) de la S4e §6 pentru do_verifica_articol — de unificat la implementare daca se poate, nu de rezolvat de doua ori independent.

5.5 Maximul returnabil — nu se aplica, si acum e clar de ce

Confirmat structural: azi maximul returnabil pe N.1 nu e un calcul de "cat s-a mai returnat", e pur si simplu CANTITATE a liniei originale (ff_...sql:4036, ofacturare.vc2:15236-15239, do_verifica_articol:14743-14754) — validarea respinge doar semnul gresit (tnCantitate >= 0 in mod retur), nu o depasire fata de un istoric. Pentru o linie libera, intrebarea nici nu se pune in termenii de azi: do_verifica_articol, ca si dialogul de gestiune (5.4), asteapta un poArticol din crsarticole — o linie libera nu are unul, deci nu poate trece prin comparatia de maxim existenta nici macar din greseala. Validarea proprie a liniei libere (cantitate/stoc, construita ad-hoc, 5.4) nu are nimic de comparat cu un "maxim returnabil" — verifica doar disponibilul de stoc curent, ca pe o linie normala de lista de preturi (acelasi gol deschis de S4e §6 pentru comanda, aceeasi solutie).


6. Efectul asupra VANZARI_CORESP

Scrierea nu depinde de continutul liniilor, ci de alegerea facuta la antet. scrie_corespondente_vanzari(3) (ff_...sql:14834-14836, apelata din finalizeaza_factura) scrie direct din pack_facturare.clistaid (variabila de sesiune, = poDate.listaid trimis la initializeaza_date_factura), independent de ce a ajuns efectiv in VANZARI_DETALII la finalizare:

INSERT INTO VANZARI_CORESP (ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP)
  SELECT pack_facturare.nid_vanzare, ID_VANZARE, 3
    FROM VANZARI WHERE ID_VANZARE IN (SELECT X FROM table(charn2collection(V_LISTAID,',')));

(ff_...sql:15481-15516, citat integral in legatura_linie_retur.md:45-53)

Consecinta directa pentru liniile libere: daca documentul de retur contine si linii libere (din lista de preturi), corespondenta tot se scrie, si scrie exact aceleasi facturi alese la antet — liniile libere nu adauga si nu scot randuri din VANZARI_CORESP, pentru ca sursa e poDate.listaid (fixat la antet), nu continutul crsfactura/VANZARI_DETALII. Un document de retur cu 3 linii mostenite din factura A si 2 linii libere scrie tot un singur rand in VANZARI_CORESP (ID_VANZARE_FACT=documentul nou, ID_VANZARE_AVIZ=factura A, TIP=3) — corespondenta descrie "acest document a folosit ca sursa factura A", nu "aceste linii vin din factura A".

Ce se scrie cand nu s-a ales nicio factura sursa: nu e un caz posibil pentru documentul de retur insusi — validarea de antet (inainte_de_do_termin, descriere obligatoriu, sectiunea 3) impune cel putin o factura aleasa inainte ca documentul sa poata continua. Deci pentru N.1/S4f (documente 8/9/24) clistaid nu e niciodata gol la scriere. Pentru N.2 ridicat la document (punctul 2) raspunsul e diferit: acolo documentul e de tip 1/5/7/10, iar poDate.listaid/campul nou echivalent (sectiunea 4) nu e obligatoriu — un document normal fara nicio linie de retur nu are ce sa scrie. Intrebare deschisa, verificata partial: daca scrie_corespondente_vanzari(3) e apelata azi doar cand ntip in (8,9) (confirmat de legatura_linie_retur.md:113: "TIP=3: factura de retur (scrie_corespondente_vanzari(3), :14834-14836, la ntip in (8,9))"), atunci ridicarea lui N.2 la nivel de document NU va scrie automat VANZARI_CORESP — documentul ramane tip 1/5/7/10, gardat de ntip, nu de continutul listaid. Linia exacta a acestui IF/CASE in exportul curent (ff_2026_08_09_01...sql) nu a fost re-verificata caracter cu caracter in aceasta sesiune (citata din raportul sursa, care a folosit un export anterior cu alta numerotare) — de reverificat la implementare, dar concluzia structurala (gating pe ntip, nu pe listaid) e solida.

Decizie de proiectare care rezulta: ridicarea lui N.2 la nivel de document (punctul 2) nu capata, prin simpla ridicare, o corespondenta persistata in VANZARI_CORESP — comportamentul raman identic cu azi (nicio legatura scrisa), cu exceptia cazului in care Marius decide explicit ca merita cod Oracle nou (extinderea gardei ntip in (8,9) la un al treilea semnal, ex. "are listaid populat"). Decizia 35 (un singur cod de scriere contabila, cod Oracle nou doar cand strict necesar) face ca varianta "fara schimbare in pack_facturare" sa fie implicita — de confirmat cu Marius (risc R4), nu de presupus.


7. Semnul cantitatii / al valorii pe retur

Doua mecanisme diferite, verificate separat pe cod:

  • N.1 (documente 8/9/24): cantitatea vine deja cu semnul corect direct din cursor_retur_document — CANTITATE a liniei originale (pozitiva ca valoare de referinta, dar interpretata cu semn de retur prin poDate.tip); semnul efectiv de scriere in VANZARI_DETALII e controlat de codul Oracle care insereaza documentul de retur (nu urmarit aici, in afara perimetrului VFP), iar in UI validarea do_verifica_articol (llRetur = Inlist(poDate.tip,8,9,24)) respinge tnCantitate >= 0 in mod retur — deci utilizatorul introduce cantitatea ca valoare pozitiva ("cat returnez"), iar semnul negativ e o conventie aplicata la nivel de tip de document, nu per linie.
  • N.2 (But_retur): semnul negativ e aplicat explicit in cod VFP, in frm_articol_gest_factura — poArticol.cantitate = (-1) * cantitate (ofacturare.vc2:4103), declansat de Thisform.lRetur (adevarat cand tlRetur a fost trimis la Init, indiferent de poDate.tip). Aici semnul nu vine din poDate.tip (documentul e tip 1/5/7/10, pozitiv prin design), ci e o negatie explicita facuta pentru ca acea linie anume e un retur intr-un document altfel normal.

Implicatie pentru liniile libere pe document de retur (S4f punctul 3): pentru ca documentul intreg e tip 8/9/24, orice linie — libera sau mostenita — trece prin acelasi tratament de tip de document (semnul negativ e o proprietate a documentului, nu a liniei, in acest caz). Nu exista risc de "linie libera cu semn pozitiv gresit" atata timp cat validarea ramane gatata pe poDate.tip si nu pe originea liniei — de verificat explicit ca ramura noua a validarii (5.4, sarirea peste maximul returnabil) nu atinge si sare peste verificarea de semn, care trebuie sa ramana activa identic pe linii libere si mostenite.

Implicatie pentru N.2 ridicat la document (punctul 2): aici semnul ramane o proprietate de linie (nu de document, pentru ca documentul e tip normal 1/5/7/10) — mecanismul de la ofacturare.vc2:4103 se pastreaza neschimbat, declansat de acelasi tlRetur/Thisform.lRetur, indiferent daca alegerea facturii sursa se face per-articol (ca azi) sau la nivel de document (punctul 2). Nimic de schimbat aici — doar de confirmat ca noul flux (alegere la document) tot seteaza tlRetur=.T. la apelul catre do_alege_stoc/frm_articol_gest_factura pentru fiecare linie de retur adaugata.


8. Unde se aseaza informatia de provenienta in UI

Inchis pe cod, cf. legatura_linie_retur.md: legatura linie-la-linie nu se poate afisa in niciun caz — nici cursor_retur_document nu aduce ID_VANZARE/ID_VANZARE_DET in crsarticole (ff_...sql:3965-4028, coloanele lipsesc din SELECT), nici INSERT-ul final in VANZARI_DETALII nu are coloana de sursa (PACK_FACTURARE:13705-13757, listata explicit, fara camp de provenienta).

Ce se poate afisa, verificat: la nivel de document, cand exact o singura factura sursa a fost aleasa, VANZARI_CORESP(TIP=3) da fara ambiguitate factura sursa:

SELECT v.serie_act, v.numar_act
  FROM VANZARI_CORESP c JOIN VANZARI v ON v.ID_VANZARE = c.ID_VANZARE_AVIZ
 WHERE c.ID_VANZARE_FACT = :id_document_retur AND c.TIP = 3 AND c.STERS = 0;

(legatura_linie_retur.md:174-178, interogare formulata, neverificata pe date live in acel raport)

Propunere concreta de text si conditie (in romana, pentru sectiunea pliata / antetul formularului unificat):

  • O singura factura sursa aleasa: eticheta fixa de tip "Provine din factura: {serie} {numar}" langa campul de descriere existent (poDate.text_aditional, deja populat azi cu "RETUR FACTURA {numere}" — factura_retur_document.md:65), afisata read-only, imediat sub sau langa campul de tip document.
  • Mai multe facturi sursa alese: eticheta devine "Provine din facturile: {lista serie+numar}" — aceeasi sursa de date (poDate.descriere, deja populata cu lista, nu doar VANZARI_CORESP), fara sa se sugereze vreo distributie pe linii.
  • Niciodata pe linie — nici coloana in grid, nici tooltip per rand care sa pretinda o sursa exacta. Pentru liniile libere in particular, nu exista nimic de afisat (nu au sursa) — nu se introduce o valoare "N/A" vizibila care ar sugera ca alte linii AU o sursa unica atunci cand de fapt (cu selectie multipla) nici ele nu o au cu certitudine.
  • Conditie tehnica: textul se construieste din poDate.descriere (deja in memorie, fara interogare suplimentara) — nu necesita interogarea VANZARI_CORESP de mai sus decat daca se doreste reafisarea la redeschiderea unui document deja emis (regenerare, etapa II) — caz in care interogarea de mai sus e necesara pentru ca poDate.descriere nu mai exista in memorie.

9. Pasi de implementare ordonati

  1. Confirmarea comportamentului aditiv/inlocuitor la alegerea facturilor sursa (sectiunea 3, punctul 3) — decizie de produs, nu cod. Gata cand: Marius a ales (a) sau (b); fara asta pasii 2-3 nu se pot implementa fara risc de refacere. Verificabil: niciunul (decizie, nu cod).
  2. Butonul/actiunea de alegere a facturilor sursa, mutat in sectiunea pliata a formularului unificat, pastrand do_cauta_facturi/caut_facturi_multiple_client neschimbate — doar punctul de declansare se muta din antetul modal in sectiunea pliata a formularului unic, si cursor_retur_document se declanseaza la confirmarea dialogului, nu la inchiderea unui formular separat (sectiunea 3, punctele 1-2). Gata cand: pe un document nou de tip 8/9/24, deschis in formularul unificat, alegerea facturilor sursa din sectiunea pliata populeaza crsarticole cu exact aceleasi randuri ca azi (gestiune, pret achizitie, pret vanzare identice), verificabil prin comparatie directa a cursorului.
  3. Meniul "Adauga articole" pe tipurile 8/9/24, cu optiunile "Adauga tot din facturile alese" / "Alege liniile de returnat…" peste crsarticole deja populat (reteta comuna S4b, sectiunile 4-5 ale acelui raport) — fara alegere de facturi in acest meniu (sectiunea 3). Gata cand: meniul contine exact aceste doua optiuni pe tip 8/9/24, plus "Cauta in lista de preturi…"/"Alege din nomenclator…" (punctul urmator).
  4. Liniile libere — APPEND BLANK pe crsfactura, semnalul de distinctie pe id_c (sectiunile 5.1-5.2): optiunea de meniu "Cauta in lista de preturi…" pe tip 8/9/24 declanseaza Select crsFactura / APPEND BLANK + combosql, exact tiparul S4e (ofacturare.vc2:17118-17122) — nu APPEND FROM peste crsarticole. id_c ramane implicit (0) pe randul nou. Gata cand: dupa adaugarea unei linii libere pe un document de test, crsfactura.id_c = 0 pe acel rand si crsarticole ramane neschimbat (acelasi Reccount/continut ca inainte de adaugare).
  5. Punct de intrare nou in dialogul de gestiune, pornind de la randul din crsfactura (sectiunea 5.4) — frm_articol_gest_factura/do_alege_stoc primesc o a doua cale de apel, cu poArticol construit ad-hoc din randul deja inserat in crsfactura (dupa combosql), nu din Scatter pe un rand din crsarticole. Fara acest pas, o linie libera nu poate ajunge deloc la alegerea gestiunii ceruta de decizia 22. Gata cand: pe o linie libera adaugata pe un document de retur, dialogul de gestiune se deschide si returneaza ID_GESTIUNE/PRET_ACHIZITIE alese de operator, fara sa fi trecut prin crsarticole.
  6. Fixarea pretului de vanzare pe liniile libere (sectiunea 5.3, riscul R1) — ramura de suprascriere din frm_articol_gest_factura (ofacturare.vc2:4094-4103) primeste al doilea semnal ("are sursa" vs. "e libera"), derivat din crsfactura.id_c <> 0 (pasul 4), si nu suprascrie pretftva cand linia e libera. Gata cand: o linie libera adaugata pe un document de retur pastreaza pretul din lista de preturi dupa alegerea gestiunii, verificabil comparand poArticol.pretftva inainte si dupa RowColChange in dialogul de gestiune.
  7. Validarea de cantitate/stoc pe liniile libere (sectiunea 5.5) — mecanism nou, nu do_verifica_articol ca atare (care cere un poArticol din crsarticole); verifica disponibilul de stoc curent, fara nicio comparatie cu un maxim returnabil. Unificabil cu golul echivalent deschis de S4e §6 pentru comanda/avize, daca implementarea alege aceeasi varianta (poArticol construit ad-hoc, parametru explicit). Gata cand: pe o linie libera, orice cantitate <= stocul curent trece validarea, cu mesaj propriu (nu "cantitate maxima de returnat"); pe o linie mostenita, comportamentul de azi ramane neschimbat.
  8. Ridicarea But_retur la nivel de document (punctul 2) — dialog de alegere multipla la deschiderea primei linii de retur pe un document normal (1/5/7/10), populare cu un camp nou sau reutilizarea controlata a lui poDate.listaid (sectiunea 4), pastrand calea per-articol existenta ca fallback sau eliminand-o explicit (decizie separata, risc R3). Gata cand: pe o factura normala, alegerea facturilor sursa se face o singura data, iar liniile de retur adaugate ulterior (mai multe articole) folosesc acel set fara sa redeschida dialogul de cautare la fiecare articol.
  9. VANZARI_CORESP pentru N.2 ridicat la document (sectiunea 6) — de decis daca se adauga cod Oracle nou sau ramane fara corespondenta persistata (risc R4); implementat doar dupa decizia lui Marius. Gata cand: comportamentul ales (cu sau fara corespondenta) e implementat consistent si testat pe un document cu retur ridicat la nivel de document si mai multe facturi sursa.
  10. Textul de provenienta la antet (sectiunea 8) — afisare read-only din poDate.descriere, condusa de numarul de facturi alese (0/1/multiple). Gata cand: eticheta arata corect in toate trei cazurile (nicio factura inca, o factura, mai multe), fara sa apara pe grid/linie.

Depinde de: S2 (formular unificat de baza), S4e (mecanismul APPEND BLANK + combosql pentru liniile libere pe document cu sursa, NU APPEND FROM) — exact ca in plan (plan_13...md:2330), corectat fata de reteta literala a deciziei 16.


10. Cum se verifica

Proba din plan (plan_13...md:2327-2329): "un document de retur deschis in formularul unificat aduce liniile facturilor alese cu aceleasi valori ca azi — inclusiv gestiunea si pretul de achizitie —, permite stergere si cantitate partiala, iar pe o factura normala se poate face retur alegand facturile o singura data."

Pasi concreti:

  1. Paritate N.1: pe date de test, emite un document de retur (tip 8) pe calea veche (formularul actual) si retine crsarticole rezultat (gestiune, pret achizitie, pret vanzare, cantitate per linie). Repeta aceeasi alegere de facturi sursa in formularul unificat si compara crsarticole camp cu camp — trebuie sa fie identic, pentru ca sursa Oracle (cursor_retur_document) nu se schimba, doar punctul VFP care o declanseaza.
  2. Stergere si retur partial: pe formularul unificat, sterge o linie mostenita si verifica ca cantitatea reintra in crsarticole (acelasi test ca azi, do_sterge neschimbat); introdu o cantitate mai mica decat maximul pe o linie mostenita si verifica validarea.
  3. Linie libera: adauga o linie din lista de preturi pe documentul de retur deschis (prin APPEND BLANK + combosql, nu APPEND FROM); verifica (a) crsfactura.id_c = 0 pe acel rand si crsarticole neschimbat; (b) pretul de vanzare = pretul din lista de preturi, nu din stoc; (c) gestiunea/pretul de achizitie sunt cerute prin dialogul de gestiune (intrat pe calea noua, 5.4), nu completate automat; (d) cantitatea nu e limitata de niciun maxim istoric, doar de stocul curent; (e) semnul cantitatii ramane negativ (consistent cu documentul); (f) stergerea liniei libere nu modifica crsarticole (verifica direct: Reccount/continut identic inainte si dupa stergere).
  4. VANZARI_CORESP neschimbat de liniile libere: dupa emiterea documentului din pasul 3, interogheaza:
    SELECT ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP FROM VANZARI_CORESP
     WHERE ID_VANZARE_FACT = :id_document_nou AND TIP = 3;
    
    — trebuie sa arate exact facturile alese la antet, indiferent de cate linii libere s-au adaugat.
  5. N.2 ridicat la document: pe o factura normala, adauga doua linii de retur pentru articole diferite folosind acelasi set de facturi sursa ales o singura data (nu redeschide dialogul la fiecare articol); verifica semnul cantitatii (negativ) si absenta oricarei scrieri in VANZARI_CORESP (daca decizia de la punctul 8/risc R4 pastreaza comportamentul de azi).
  6. Provenienta: deschide un document de retur cu o singura factura sursa — verifica textul de antet; alege doua facturi sursa — verifica ca textul listeaza ambele si nu implica o sursa unica.

11. Ce nu se poate testa headless

Reluat din capcanele deja confirmate pe acest proiect (s4b_bara_butoane_meniu.md §8), plus specificul retur:

  • cauta_alfa cu tnTipReturn=1 — dialog modal (.Show(1)), blocheaza thread-ul UI; testarea automata a bifarii multiple nu se poate face prin injectare de input pe aceasta masina (partajata, memoria masina-partajata-fara-input-real). Se verifica indirect: apeland direct caut_facturi_multiple_client/cursor_retur_document cu parametri de test si citind cursorul rezultat, ocolind UI-ul.
  • frm_articol_gest_factura — acelasi tip de dialog modal; verificarea suprascrierii de pret (risc R1, sectiunea 5.3) si a noului punct de intrare pornit din crsfactura (sectiunea 5.4) se poate face doar citind codul metodei RowColChange/noua metoda de intrare si, separat, apeland direct logica de calcul cu date de test simulate — nu prin click real in grid.
  • Coloanele griduri (grd_articole, gridul dialogului de alegere) — capcana deja cunoscuta: ColumnCount=0 sub -A -T; verificare vizuala necesita harness UI vizibil, nu headless. (memoria grid-coloane-nu-se-materializeaza-headless)
  • Textul de provenienta din sectiunea pliata (sectiunea 8) — verificabil headless ca continut al proprietatii (Caption/Value a controlului), nu ca "arata bine" vizual.
  • Secventa reala de deschidere a dialogului din sectiunea pliata (declansarea la click, nu doar codul din spatele ei) — necesita harness UI vizibil pentru captura de interactiune.

Ce se poate verifica headless, direct: continutul crsarticole/crsfactura dupa apeluri directe ale rutinelor (cursor_retur_document, do_adauga_articol, do_verifica_articol, APPEND BLANK pe crsfactura) cu parametri de test; valoarea crsfactura.id_c pe randurile noi (semnalul de la 5.2, 0 pe liniile libere, valoare reala pe cele mostenite); continutul real din VANZARI_CORESP/VANZARI_DETALII dupa emiterea unui document de test pe schema Oracle (doar SELECT, verificare post-factum).


12. Riscuri si ce ramane de decis de Marius

R1 — Pretul de vanzare pe liniile libere se suprascrie cu pretul de stoc, daca nu se trateaza explicit (sectiunea 5.3). Cel mai concret risc gasit in aceasta cercetare, netratat in plan pana acum. Recomandare: al doilea semnal ("linie cu sursa" vs. "linie libera") pe langa tlRetur, derivat din crsfactura.id_c <> 0 (5.2), care sa dezactiveze suprascrierea de pret doar pentru liniile libere. Necesita o mica modificare in frm_articol_gest_factura (nu in pack_facturare).

R7 — Punctul de intrare in dialogul de gestiune pentru o linie libera nu exista azi (sectiunea 5.4). Gol nou, mai strict decat echivalentul lui pe comanda/avize (S4e §6, doar validare de cantitate) — pe retur blocheaza si alegerea gestiunii/pretului de achizitie, obligatorie prin decizia 22. do_alege_stoc/frm_articol_gest_factura se intra azi doar dintr-un poArticol scatter-uit din crsarticole; o linie libera (APPEND BLANK pe crsfactura, 5.1) nu are asa ceva. Recomandare: a doua cale de apel, cu poArticol construit ad-hoc din randul crsfactura deja completat prin combosql, unificata daca se poate cu solutia aleasa pentru golul echivalent de validare (R7 aici, S4e §6/varianta b acolo) — un singur mecanism de "construieste poArticol fara sursa in crsarticole", nu doua independente.

R2 — Alegere aditiva sau inlocuitoare la a doua apasare a dialogului de facturi sursa (sectiunea 3, punctul 3). Nu e tratat de nicio decizie existenta. Recomandare: aditiv, consistent cu filozofia deciziei 16, dar cere cod VFP nou (bucla de filtrare pe facturi noi + INSERT INTO crsarticole).

R3 — Ce se intampla cu caut_facturi_multiple_client_articol (alegerea per-articol) dupa ridicarea lui But_retur la nivel de document. Ramane ca optiune secundara sau se elimina? Nefolosit oriunde altundeva in cod (verificat implicit — apelul e doar din do_adauga_articol, ramura tlRetur). Recomandare: se elimina, pentru ca pastrarea ambelor cai ar insemna doua UX-uri pentru aceeasi actiune, exact riscul pe care unificarea vrea sa il elimine.

R4 — VANZARI_CORESP pentru N.2 ridicat la document. Gardat azi pe ntip in (8,9), nu pe continutul listaid — reconfirmat independent in aceasta sesiune, pe exact acelasi export (ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:14834-14836): WHEN pack_facturare.ntip in (8, 9) THEN pack_facturare.scrie_corespondente_vanzari(3);, in interiorul unui CASE fara alta conditie pe listaid. Daca ramane asa, un document normal cu retur ridicat la document nu va avea nicio legatura persistata catre facturile sursa alese — exact ca azi cu But_retur per-articol, deci nu e o regresie, dar nici un castig. Recomandare: se lasa neschimbat (fara cod Oracle nou), consistent cu decizia 35 (economie de cod de intretinut) — de confirmat explicit cu Marius, pentru ca punctul 2 al S4f ("ridicarea la nivel de document") ar putea crea asteptarea implicita ca acum exista si o legatura persistata, cand de fapt nu exista.

R5 — Ct_clb_altele.cconditie — semantica exacta neconfirmata (sectiunea 2.1). Daca acest camp chiar dezactiveaza azi re-alegerea dupa prima selectie, design-ul nou (sectiunea 3) trebuie sa decida explicit sa elimine aceasta restrictie (pentru varianta aditiva) — altfel comportamentul nou ar fi mai permisiv decat parea sa fie azi, fara sa fi fost o decizie constienta. De verificat la implementare, cand clasa ct_clb_cautare devine accesibila (posibil in COMUNROA, in afara acestui repo).

R6 — Semnul cantitatii pe liniile libere, verificare explicita ceruta de brief (sectiunea 7): nu e un risc nou — analiza arata ca semnul e o proprietate a documentului (tip 8/9/24), nu a liniei, deci liniile libere il mostenesc automat corect. Se listeaza aici doar ca sa fie explicit ca punctul a fost verificat, nu omis.


Handoff

Cercetare incheiata fara sa fie nevoie de predare de context — un subagent de verificare (ab0be8e27f0fb97f3, Explore/general-purpose) a esuat de doua ori sa returneze rezultate, apoi a raspuns complet la a treia incercare (reluat prin SendMessage), confirmand independent 4 puncte deja scrise pe cod propriu in raport: (1) do_cauta_facturi e declansat explicit de user, prin Ct_clb_altele/caut_ora.vc2:787-798,839-844 -> do_cauta_altele -> do_cauta_facturi (ofacturare.vc2:8940-8961,9173), inainte de orice deschidere a frm_facturare_articole; (2) scrie_corespondente_vanzari(3) e gardat exact pe ntip in (8,9) (ff_...sql:14834-14836) — folosit sa inchid definitiv R4; (3) thisform.cListaIdArticoleRetur poate acumula ID_VANZARE diferit per articol (ofacturare.vc2:12882-12892); (4) poDate.listaid e acelasi camp la N.1 si N.2, dar cu rol tranzitoriu la N.2 (salvat/restaurat) si definitiv doar la do_scrie_articole (ofacturare.vc2:13977-13979). Niciuna din aceste confirmari nu a schimbat vreo concluzie a raportului, doar le-a intarit sursa.

Corectie aplicata dupa livrare, la avertismentul team-lead (bazat pe docs\cercetare\s4e_lista_preturi_pe_sursa.md, sectiunile 3 si 7): sectiunea 5 (liniile libere) a fost rescrisa integral — reteta APPEND FROM peste crsarticole (premisa initiala a brief-ului) e inlocuita cu mecanismul validat de S4e (APPEND BLANK pe crsfactura + combosql, niciodata pe crsarticole), campul de distinctie devine crsfactura.id_c = 0 (nu absenta PRET_ACHIZITIE pe crsarticole), si s-a adaugat un gol nou, negasit de S4e (care nu are dialog de gestiune pe comanda/avize): punctul de intrare in frm_articol_gest_factura pentru o linie fara rand sursa in crsarticole (risc R7, nou). Verdictul de la inceputul raportului, pasii de implementare (9), verificarea (10) si sectiunile headless (11) au fost actualizate in consecinta. Restul raportului (sectiunile 1-4, 6-8, 12 minus R1/R7) ramane neschimbat fata de livrarea initiala.

Fisierul SQL folosit: D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql (cel mai recent din director). Nicio editare de cod, niciun git_sync.ps1/txt2vcx.ps1, niciun commit, nicio scriere Oracle.