# Cercetare R6 — nomenclatoare in gridul Articole (frm_modific2024) Zona: `COMUN\clase\omodificari.vc2` (formularul `frm_modific2024`), gridul `pgfArticole.PAGE3.grdArticoleFactura`. Dispecerul comun e in `COMUN\programe\ofacturare_editare.prg`, clasa `ArticoleNotaEditor`. ## A. Inventarul coloanelor gridului Articole Bloc `ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura'` incepe la `omodificari.vc2:12330`; lista completa a coloanelor (`ControlSource` + `Name`) e la `omodificari.vc2:12349-12471`. | Coloana (Name) | ControlSource | Linie ControlSource | Legata de nomenclator? | |---|---|---|---| | cDenumireArt (Column1) | `tvd.denumire` | `omodificari.vc2:12349` | DA — articol | | cCodmatArt (Column2) | `tvd.codmat` | `omodificari.vc2:12356` | nu | | cSerieArt (Column3) | `tvd.serie` | `omodificari.vc2:12363` | nu | | cLotArt (Column4) | `tvd.lot` | `omodificari.vc2:12370` | nu | | cCantitateArt (Column5) | `tvd.cantitate` | `omodificari.vc2:12377` | nu | | cPretArt (Column6) | `tvd.pret` | `omodificari.vc2:12386` | nu | | cPretAchizitieArt (Column7) | `tvd.pret_achizitie` | `omodificari.vc2:12395` | nu | | cPretCuTvaArt (Column8) | `tvd.pret_cu_tva` | `omodificari.vc2:12404` | nu (checkbox propriu) | | cProcTvavArt (Column9) | `tvd.proc_tvav` | `omodificari.vc2:12412` | nu | | cDiscountUnitarArt (Column10) | `tvd.discount_unitar` | `omodificari.vc2:12421` | nu | | **cGestiuneArt (Column11)** | `tvd.nume_gestiune` | `omodificari.vc2:12430` | **DA — gestiune** | | **cValutaArt (Column12)** | `tvd.nume_val` | `omodificari.vc2:12437` | **DA — valuta** | | cExplicatieArt (Column13) | `tvd.explicatie` | `omodificari.vc2:12444` | nu (text liber) | | **cTaxcodeArt (Column14)** | `tvd.taxcode` | `omodificari.vc2:12451` | **DA — cod TVA SAF-T** | | cValoareArt (Column15) | `tvd.valoare` | `omodificari.vc2:12458` | nu (calculat) | | **cExplicatieTvaArt (Column16)** | `Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')` | `omodificari.vc2:12467` | **DA — explicatie TVA / id_jtva_coloana** | Cele 5 coloane legate de un nomenclator sunt exact cele acceptate de dispecer — vezi `ArticoleNotaEditor::AreNomenclator` (`ofacturare_editare.prg:978-981`): ``` PROCEDURE AreNomenclator LPARAMETERS tcControl RETURN Inlist(This.NumeCamp(m.tcControl), 'denumire', 'nume_gestiune', 'nume_val', 'taxcode', 'id_jtva_coloana') ENDPROC ``` Observatie importanta pentru cauza de la punctul D: **`cTaxcodeArt` si `cExplicatieTvaArt` au Column.ReadOnly = .F.** (`omodificari.vc2:12379` / linia `Column14.ReadOnly = .F.` la `omodificari.vc2` in blocul 12451-12457, respectiv `Column16.ReadOnly = .F.` in blocul 12467-12471), dar **controlul `Text1` din interiorul lor are `ReadOnly = .T.`** — vezi `omodificari.vc2:12759` (cTaxcodeArt.Text1) si `omodificari.vc2:12594` (cExplicatieTvaArt.Text1). Diferenta reala dintre ele nu e ReadOnly-ul (identic), ci **daca `ControlSource` e camp direct sau expresie** — vezi punctul D. ## B. Ce evenimente exista azi pe fiecare coloana cu nomenclator | Coloana | InteractiveChange | DblClick | Buton lateral "Modificare" (`But_modificaR`) | |---|---|---|---| | cDenumireArt | DA — `omodificari.vc2:16710-16714` | **nu exista** | DA (comun, vezi E) | | cGestiuneArt | DA — `omodificari.vc2:16739-16743` | **nu exista** | DA (comun) | | cValutaArt | DA — `omodificari.vc2:16826-16830` | **nu exista** | DA (comun) | | cTaxcodeArt | DA — `omodificari.vc2:16815-16819` | **nu exista** | DA (comun) | | cExplicatieTvaArt | DA — `omodificari.vc2:16728-16732` (exista, dar nu se declanseaza — vezi D) | **nu exista** | DA (comun) | Corpul e identic pentru toate cele 5 (exemplu, taxcode): ``` PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Text1.InteractiveChange Local loEditor loEditor = Createobject('ArticoleNotaEditor', Thisform) loEditor.ModificaNomenclator(Thisform.pccontrol) ENDPROC ``` Confirmare ca `DblClick` nu exista deloc pe `grdArticoleFactura` sau pe coloanele lui: cautare `InteractiveChange|DblClick` in tot `omodificari.vc2` — singurele `DblClick` din fisier sunt pe alt grid, `grdRequest` (`omodificari.vc2:390` si `omodificari.vc2:408`, vezi F). ## C. Dispecerul comun — `ArticoleNotaEditor::ModificaNomenclator` Definit in `COMUN\programe\ofacturare_editare.prg:1069-1137`, clasa `ArticoleNotaEditor` (`ofacturare_editare.prg:947-1165`). Semnatura: `PROCEDURE ModificaNomenclator LPARAMETERS tcControl` — primeste un string de forma `alias.camp` (ex. `tvd.taxcode`, `tvd.id_jtva_coloana`). Garda de intrare (`ofacturare_editare.prg:1073-1076`): ``` lcCamp = This.NumeCamp(m.tcControl) IF !This.Editabil() OR Reccount('tvd') = 0 OR !This.AreNomenclator(m.lcCamp) OR Nvl(tvd.id_vanzare_set, 0) <> 0 RETURN .F. ENDIF ``` `Do Case` de deschidere a nomenclatorului (`ofacturare_editare.prg:1078-1089`): ``` DO CASE CASE m.lcCamp == 'denumire' loCauta = caut_articol() CASE m.lcCamp == 'nume_gestiune' loCauta = caut_gestiune() CASE m.lcCamp == 'nume_val' loCauta = caut_valuta() CASE m.lcCamp == 'id_jtva_coloana' loCauta = This.CautaExplicatieTva() OTHERWISE loCauta = This.CautaCodTva() ENDCASE ``` Discrimineaza dupa **numele campului** extras din `tcControl` (nu dupa numele coloanei din grid, nu dupa un parametru separat) — `NumeCamp` (`ofacturare_editare.prg:966-974`) taie tot ce e inainte de primul `.`. `taxcode` cade pe ramura `OTHERWISE` (`CautaCodTva`, `ofacturare_editare.prg:1152-1158`). Dupa alegere, al doilea `Do Case` (`ofacturare_editare.prg:1096-1132`) scrie valorile alese inapoi in `tvd`, cu ramura dedicata pentru `id_jtva_coloana` (`ofacturare_editare.prg:1124-1129`, recalculeaza `proc_tvav` si cheama `This.oForm.UpdateExplicatieSAFTArt()` + `calculeaza_valori_articol()`). ## D. De ce "explicatie TVA" nu se declanseaza pe InteractiveChange **Nu e o garda care blocheaza si nu e o ramura lipsa in dispecer** — `id_jtva_coloana` e in `AreNomenclator` (`ofacturare_editare.prg:980`) si are ramura proprie in `Do Case` (`ofacturare_editare.prg:1085-1086`). Handlerul `InteractiveChange` exista si e corect (`omodificari.vc2:16728-16732`). **Cauza e ca evenimentul `InteractiveChange` pur si simplu nu se produce niciodata din tastare pe aceasta coloana**, pentru ca `ControlSource`-ul ei e o expresie, nu un camp direct: ``` Column16.ControlSource = "Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')" ``` (`omodificari.vc2:12467`) versus `cTaxcodeArt`: ``` Column14.ControlSource = "tvd.taxcode" ``` (`omodificari.vc2:12451`) In VFP, un `Textbox` intr-o coloana de grid cu `ReadOnly = .T.` (asa cum sunt ambele, `omodificari.vc2:12759` si `omodificari.vc2:12594`) nu accepta editare directa de text, dar cand `Column.ControlSource` e un **camp real dintr-un cursor navigabil** ("bound field"), gridul VFP declanseaza cautarea incrementala nativa la tastare (type-ahead / seek pe recordset): tasta apasata muta recordul curent la prima potrivire, ceea ce schimba valoarea afisata legata de acel camp — iar aceasta schimbare de `Value` e cea care declanseaza `InteractiveChange`. Cand `ControlSource` e o **expresie** (ca la `cExplicatieTvaArt`, care afiseaza denumirea din `crsJtvaTemp` via `Seek`, nu campul `tvd.id_jtva_coloana` in sine), gridul nu are un camp indexabil pe care sa faca seek — mecanismul de cautare incrementala nu se activeaza, deci `Value`-ul textboxului nu se schimba niciodata din tastare, deci `InteractiveChange` nu are cum sa se declanseze prin tastare pe aceasta coloana. Chiar comentariul din cod confirma asta, la `GotFocus`-ul aceleiasi coloane (`omodificari.vc2:16723`): ``` *!* coloana afiseaza denumirea din crsJtvaTemp, deci ControlSource e expresie - campul editat se da explicit ``` adica autorul stia deja ca `ControlSource` e o expresie si de-aia a scris explicit campul real (`tvd.id_jtva_coloana`) in `Thisform.pccontrol` la `GotFocus`, in loc sa se bazeze pe `This.ControlSource` (cum fac celelalte coloane). Explicatia asta e chiar mecanismul care face ca butonul lateral sa functioneze — vezi E. Dublu-click nu se declanseaza pe nicio coloana pentru ca **pur si simplu nu exista niciun handler `DblClick`** pe `grdArticoleFactura` (vezi B) — nu e o problema specifica de "explicatie TVA", e valabil identic si pentru `taxcode`, doar ca la `taxcode` utilizatorul are alternativa tastarii, care functioneaza (camp direct). ## E. Cum ajunge butonul lateral "Modificare" sa deschida explicatie TVA Handler: `PROCEDURE But_modificaR.Click`, `omodificari.vc2:15319-15326`: ``` PROCEDURE But_modificaR.Click Local lcRul, loEditor IF thisform.pgfArticole.ActivePage = 3 loEditor = Createobject('ArticoleNotaEditor', Thisform) loEditor.ModificaNomenclator(thisform.pccontrol) RETURN ENDIF ... ENDPROC ``` Cheia e ca butonul **nu citeste `ControlSource`-ul coloanei curente** — foloseste direct `Thisform.pccontrol`, o proprietate a formularului scrisa de fiecare `GotFocus` al coloanei active, inainte ca utilizatorul sa apuce sa tasteze ceva. Pentru `cExplicatieTvaArt`, `GotFocus` scrie explicit campul real (`omodificari.vc2:16722-16726`): ``` PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cExplicatieTvaArt.Text1.GotFocus *!* coloana afiseaza denumirea din crsJtvaTemp, deci ControlSource e expresie - campul editat se da explicit Thisform.pccontrol = 'tvd.id_jtva_coloana' Thisform.pncolumnorder = This.Parent.ColumnOrder ENDPROC ``` Pentru celelalte 4 coloane, `GotFocus` foloseste `Alltrim(This.ControlSource)` direct (ex. `cTaxcodeArt.Text1.GotFocus`, `omodificari.vc2:16810-16813`), ceea ce functioneaza pentru ele pentru ca acolo `ControlSource` chiar e campul real. Deci calea functioneaza pentru ca **`GotFocus` — care se declanseaza la orice intrare in celula, prin click simplu, tab sau navigare cu sagetile, indiferent de tipul de `ControlSource` — a populat deja `Thisform.pccontrol` corect inainte ca butonul sa fie apasat.** Butonul nu depinde deloc de `InteractiveChange`; de-aia merge la explicatie TVA desi tastarea nu merge. ## F. Cost estimat pentru DblClick pe fiecare coloana cu nomenclator **Exista deja un sablon direct aplicabil**, chiar in cadrul modelului de "coloana Text1 in grid cu InteractiveChange care deschide ceva" — `grdRequest`, alt grid din acelasi formular: ``` PROCEDURE grdRequest.cLabelItem.Text1.DblClick Thisform.lEditMode = .T. ENDPROC ``` (`omodificari.vc2:390-392`) ``` PROCEDURE grdRequest.cValoareDefaultText.Text1.DblClick If !Empty(Nvl(crsRequest.fis_lista, '')) Thisform.Selectfromlookup() Endif ENDPROC ``` (`omodificari.vc2:408-412`) Confirmare pentru VFP: intr-adevar, evenimentul se pune pe **controlul din interiorul coloanei** (`Column.Text1.DblClick`), exact cum arata cele doua exemple de mai sus si exact cum sunt puse deja `InteractiveChange` si `GotFocus` pe `grdArticoleFactura` (`Column.Text1.InteractiveChange`, `Column.Text1.GotFocus`) — nu exista niciun `DblClick` pus pe nivelul `Column` insusi in tot fisierul. Modelul `grdRequest` e potrivit ca forma (unde se pune evenimentul), dar nu ca continut — acolo `DblClick` face lucruri specifice acelui grid (comuta un mod de editare / arata un lookup din alta zona), nu apeleaza un dispecer de nomenclator. Sablonul de continut potrivit e chiar `InteractiveChange`-urile deja existente pe `grdArticoleFactura` (`omodificari.vc2:16710-16714`, `16728-16732`, `16739-16743`, `16815-16819`, `16826-16830`) — toate identice, 3 linii: ``` Local loEditor loEditor = Createobject('ArticoleNotaEditor', Thisform) loEditor.ModificaNomenclator(Thisform.pccontrol) ``` Costul e mic si uniform: adaugarea a 5 metode noi `DblClick` (una per coloana cu nomenclator), fiecare cu acelasi corp de 3 linii de mai sus. Pentru ca toate cele 5 `GotFocus` seteaza deja `Thisform.pccontrol` corect — fie din `ControlSource` (denumire, gestiune, valuta, taxcode), fie explicit (explicatie TVA, `omodificari.vc2:16724`) — folosirea lui `Thisform.pccontrol` in `DblClick` functioneaza uniform pe toate cele 5, inclusiv pe explicatie TVA, fara sa depinda de mecanismul de seek care lipseste la coloana cu `ControlSource` expresie. Practic acelasi apel pe care il face deja `But_modificaR.Click` (`omodificari.vc2:15319-15326`, vezi E). Nu e nevoie de nicio schimbare in dispecer (`ModificaNomenclator`) sau in `AreNomenclator` — ele deja accepta toate cele 5 campuri.