Files
roafacturare/docs/cercetare/rec_r5_editare_inline_articole.md
Marius Mutu ca3c5d7eea docs: runda 6 - cercetari, propuneri si conventia mediului Oracle
Cercetarile si propunerile rundelor 4-6 pe editarea facturii emise. Starea
rundei 6 si ce s-a stabilit intra in progres.md.

Nou: docs/conventii_mediu_oracle.md - pe dev/test se lucreaza numai cu
ROA_CENTRAL si schemele CONTAFIN_ORACLE si MARIUSM_AUTO; schema ACN nu se
foloseste. Scripturile de pachet sunt necalificate, deci schema tinta o
decide sirul de conectare, nu fisierul.

roafacturare.pj2 regenerat de git_sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gYCNE26G1G7UoGu2ju7aS
2026-08-20 16:35:03 +03:00

23 KiB

Runda 5 - editare inline pe pagina Articole (proiectare, fara aplicare)

Document de proiectare pentru cerinta: pe formularul unificat frm_modific2024, pagina 3 "Articole factura" (pgfArticole.PAGE3.grdArticoleFactura, cursor tvd), sa devina editabile inline serie, lot, explicatie si proc_tvav, iar coloanele cu nomenclator (denumire, nume_gestiune, nume_val, taxcode, id_jtva_coloana) sa deschida dialogul de cautare si pe InteractiveChange, nu doar din But_modificaR.Click.

Linii citate din COMUN\clase\omodificari.vc2 (17059 linii) si COMUN\programe\ofacturare_editare.prg (1140 linii), stare de pe disc la data cercetarii - fisierul e in lucru, reconfirma numerele de linie cu vfp_symbols.ps1 inainte de orice txt2vcx.ps1.

Fiecare bloc de cod de mai jos e marcat VERIFICAT (citit direct din sursa) sau PRESUPUS (judecata de proiectare, nu extrasa din cod existent).


0. Constatare care schimba premisa punctului A din brief

VERIFICAT. Intrebarea "cCantitateArt/cPretArt marcheaza lmodificat si conteaza asta la salvare" are raspuns clar: lmodificat nu e citit nicaieri ca filtru de salvare.

ScrieArticoleFacturaEditate (ofacturare_editare.prg:473-571) are doar doua SCAN-uri pe tvd:

  • ofacturare_editare.prg:502 - SCAN FOR id_vanzare_det > 0 AND Nvl(sters,0) <> 1 (UPDATE liniilor existente)
  • ofacturare_editare.prg:534 - SCAN FOR id_vanzare_det = 0 AND Nvl(sters,0) <> 1 (INSERT liniilor noi)

Niciunul nu conditioneaza pe lmodificat. Comentariul de la ofacturare_editare.prg:498 o spune explicit: "invie si actualizeaza liniile pastrate - toate cele active din alias, nu doar lmodificat, pasul de mai sus le-a marcat pe toate". Singurele scrieri ale campului (AplicaAdaugareTvd:857, ComutaSters:1034, DuplicaLinie:1053, ModificaNomenclator:1108, calculeaza_valori_articol in omodificari.vc2:13521) nu au nicio citire-pereche - lmodificat e scris dar niciodata citit. E camp vestigial (poate util pentru o extensie viitoare, gen indicator vizual "linie modificata"), nu un defect de blocat livrarea.

Consecinta pentru A: coloanele noi (serie, lot, explicatie, proc_tvav) nu au nevoie sa seteze lmodificat ca sa se salveze - se salveaza oricum, ca toate liniile active. Recomand totusi sa-l seteze, din consecventa cu restul coloanelor editabile si ca sa nu ramana singurele exceptii daca cineva incepe sa-l citeasca in viitor - cost zero, un REPLACE in plus.


1. Defect real gasit pe drum: UPDATE-ul nu scrie serie/lot/explicatie

VERIFICAT - trebuie reparat in aceeasi livrare, exact cum a semnalat brief-ul.

UPDATE-ul liniilor existente (ofacturare_editare.prg:505-517):

lcSql = [update vanzari_detalii set sters = 0, cantitate = ] + Alltrim(Str(Nvl(cantitate,0),18,3)) + ;
    [, pret = ] + Alltrim(Str(Nvl(pret,0),18,4)) + [, pret_cu_tva = ] + Alltrim(Str(Nvl(pret_cu_tva,0))) + ;
    [, pret_achizitie = ] + Alltrim(Str(Nvl(pret_achizitie,0),18,4)) + ;
    [, proc_tvav = ] + Alltrim(Str(Nvl(proc_tvav,0),18,4)) + ;
    [, discount_unitar = ] + Alltrim(Str(Nvl(discount_unitar,0),18,4)) + ;
    [, id_articol = ] + Alltrim(Str(Nvl(id_articol,0))) + ;
    [, id_gestiune = ] + Iif(Isnull(id_gestiune),[NULL],Alltrim(Str(id_gestiune))) + ;
    [, id_valuta = ] + Iif(Isnull(id_valuta),[NULL],Alltrim(Str(id_valuta))) + ;
    [, id_jtva_coloana = ] + Iif(Isnull(id_jtva_coloana),[NULL],Alltrim(Str(id_jtva_coloana))) + ;
    [, taxcode = ] + Iif(Isnull(taxcode),[NULL],Alltrim(Str(taxcode))) + ;
    [, cont = ] + Iif(Empty(Nvl(cont,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(cont,''))) + [']) + ;
    [, id_utils = ] + Alltrim(Str(gnIdUtil)) + [, dataoras = sysdate ] + ;
    [where id_vanzare_det = ] + Alltrim(Str(id_vanzare_det)) + [ and id_vanzare = ] + Alltrim(Str(m.tnIdVanzare))

proc_tvav e deja in lista (linia 508) - editarea cotei TVA persista deja corect pe linii existente, nimic de reparat acolo. Lipsesc doar serie, lot, explicatie - prezente in INSERT-ul liniilor noi (ofacturare_editare.prg:535-544, aceeasi forma Iif(Empty(Nvl(...,'')),[NULL],['] + OracleSpecialCharacters(...) + [']) dar absente din UPDATE. Fara reparatie, editarea inline propusa la punctul A de mai jos s-ar pierde tacut la salvare pe orice linie deja salvata (id_vanzare_det > 0) - exact scenariul cel mai comun (factura cu articole sincronizate din rulaj).

Propunere - ofacturare_editare.prg, dupa linia 515 (, cont = ...), inainte de linia 516

(, id_utils = ...)

    [, cont = ] + Iif(Empty(Nvl(cont,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(cont,''))) + [']) + ;
    [, serie = ] + Iif(Empty(Nvl(serie,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(serie,''))) + [']) + ;
    [, lot = ] + Iif(Empty(Nvl(lot,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(lot,''))) + [']) + ;
    [, explicatie = ] + Iif(Empty(Nvl(explicatie,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(explicatie,''))) + [']) + ;
    [, id_utils = ] + Alltrim(Str(gnIdUtil)) + [, dataoras = sysdate ] + ;

Trei linii noi, tipar identic cu cel deja folosit pentru cont pe acelasi UPDATE si pentru serie/explicatie/lot pe INSERT (:538-540). Comentariul de la :503-504 ("toate campurile pe care grila si nomenclatoarele le pot schimba") ramane valabil fara modificare.


2. Coloane care devin editabile inline (serie, lot, explicatie)

VERIFICAT - definitiile actuale, omodificari.vc2:

coloana linie definitie ControlSource ReadOnly azi
cSerieArt (Column3) :12359-12365 tvd.serie .T.
cLotArt (Column4) :12366-12372 tvd.lot .T.
cExplicatieArt (Column13) :12441-12447 tvd.explicatie .T.

Niciuna nu are azi Text1.When/Text1.Valid propriu (singurele evenimente pe pagina 3 sunt cele listate la omodificari.vc2:16678-16770, verificat prin citire directa a intervalului).

Garda de editare - condifia de baza vs. cea extinsa a lui cPretAchizitieArt

VERIFICAT. Doua garde diferite exista azi pe pagina 3:

* cCantitateArt.Text1.When si cPretArt.Text1.When - garda de baza
IF Thisform.lArticoleReadOnly OR Nvl(tvd.id_vanzare_set,0) <> 0
    RETURN .F.
ENDIF

* cPretAchizitieArt.Text1.When - garda extinsa, cu o conditie in plus
IF Thisform.lArticoleReadOnly OR Nvl(tvd.id_vanzare_set,0) <> 0 OR Nvl(tvd.id_vanzare_det,0) <> 0
    RETURN .F.
ENDIF

PRESUPUS (nu exista comentariu care sa explice; judecata de proiectare pe baza codului inconjurator): conditia suplimentara Nvl(tvd.id_vanzare_det,0) <> 0 de pe pret de achizitie blocheaza editarea manuala pe liniile deja legate de o vanzare persistata (id_vanzare_det nenul inseamna linie care exista deja in VANZARI_DETALII, deci a venit dintr-o miscare de stoc reala). Sustinut de garda de la salvare (omodificari.vc2:14457-14460): un avertisment de "pret de achizitie 0" apare doar pentru linii noi (id_vanzare_det = 0) - semn ca liniile vechi au deja o valoare de incredere, mostenita din stoc, si nu trebuie rescrisa manual dupa fapt (ar strica marja/COGS calculat la vremea vanzarii). E o garda de integritate financiara pe o valoare derivata, nu una generica de editare.

serie/lot/explicatie sunt campuri descriptive/text, nu valori derivate din stoc - corectarea unei greseli de tastare pe o linie deja salvata (serie gresita, lot gresit, o explicatie de corectat) e exact scenariul uzual de folosit al acestei livrari, inclusiv pe linii vechi. Recomand garda de baza (fara conditia id_vanzare_det) pentru toate trei. Daca exista un motiv de trasabilitate (serie/lot leaga factura de un lot fizic expediat, iar schimbarea lui dupa livrare ar fi o problema de conformitate) - e o decizie de business, nu una pe care o pot confirma din cod; semnalez-o explicit ca punct de validat cu tine inainte de aplicare.

Propunere - omodificari.vc2, dupa cPretArt.Text1.Valid (:16759, inainte de

cPretArt.Text1.When)

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Text1.When
    IF Thisform.lArticoleReadOnly OR Nvl(tvd.id_vanzare_set,0) <> 0
        RETURN .F.
    ENDIF
    Thisform.oldvalue = This.Value
ENDPROC

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Text1.Valid
    IF NVL(Thisform.oldvalue,'') <> NVL(This.Value,'')
        REPLACE lmodificat WITH .T. IN tvd
        Thisform.oldvalue = This.Value
    ENDIF
ENDPROC

Identic (nume schimbat) pentru cLotArt (tvd.lot) si cExplicatieArt (tvd.explicatie). Coloana 3 din Column3.ReadOnly = .T., 4 din Column4.ReadOnly = .T., 13 din Column13.ReadOnly = .T. trec pe .F. (omodificari.vc2:12365, :12372, :12447).


3. Cazul delicat: proc_tvav (Column9, cProcTvavArt)

3.1 Forma stocata - VERIFICAT, nu presupus

tvd.proc_tvav e in forma multiplicativa (1.21, nu 21), confirmat in trei locuri independente:

  • CreeazaPoArticolNouTvd... calculeaza_valori_articol (omodificari.vc2:13511-13521): lnProcTvav = NVL(proc_tvav, 1) si lnValoare = ... * lnPretNet * lnProcTvav - daca ar fi forma "21", valoarea liniei ar fi de 21x mai mare, evident gresit. ei
  • ArticoleNotaEditor.ModificaNomenclator, ramura id_jtva_coloana (ofacturare_editare.prg:1099-1101): proc_tvav WITH (Nvl(loCauta.cota_tva, 0) + 100) / 100 - cota_tva vine din nomenclator ca "21", transformarea explicita in "1.21" e in cod.
  • CautaExplicatieTva (ofacturare_editare.prg:1117-1120): lnCotaFiltru = Round((Nvl(tvd.proc_tvav, 0) - 1) * 100, 2) - transformarea inversa, "1.21" -> "21".

3.2 Precedent direct in acelasi grid family: editare libera in forma "1.xx"

VERIFICAT. Coloana cProc_tva de pe grila de rulaje (pgfArticole.PAGE1.grdRulaje, omodificari.vc2:8956-8963, si dubletul ei pe PAGE2.grdRulajeObinv) e deja editabila direct, in forma bruta:

Column24.ControlSource = "proc_tva", ;
Column24.Format = "R", ;
Column24.InputMask = "9.99", ;
Column24.Name = "cProc_tva", ;
Column24.ReadOnly = .F., ;

cu evenimentul (omodificari.vc2:16337-16343):

PROCEDURE pgfArticole.PAGE1.grdRulaje.cProc_tva.Text1.Valid
    IF NVL(thisform.oldvalue, 0) <> NVL(this.Value, 0)
        Thisform.calculeaza_valori_rul(this.ControlSource)
        thisform.oldvalue = this.Value
    ENDIF
ENDPROC

Utilizatorul tasteaza deja "1.21", nu "21", pe grila de rulaje din acelasi formular. cProcTvavArt de pe grila de articole are deja azi Column9.InputMask = "9.99" (omodificari.vc2:12417, mostenit inca de cand coloana era doar de afisare) - acelasi format, gata pregatit.

3.3 Recomandare: pastreaza forma "1.21", nu converti la "21"

Argument: forma "1.21" e cea stocata in proc_tvav, cea folosita direct de calculeaza_valori_articol, si cea deja tastata de utilizatori pe grila de rulaje din acelasi formular - e conventia existenta, nu una noua. Varianta "utilizatorul tasteaza 21" ar cere:

  • o proprietate ajutatoare separata pe care sa se afiseze/editeze (Text1 legat printr-o expresie de transformare, ControlSource nu mai poate fi direct tvd.proc_tvav), pentru ca grid column text boxes nu pot face transformarea la afisare fara sa piarda editarea directa pe ControlSource;
  • conversia dus-intors (/100+1 la citire, (x-1)*100 la scriere) e exact riscul semnalat - o greseala de semn sau de ordine ar strica toate liniile la urmatoarea recalculare, silentios (nu exista validare care sa prinda "1900" in loc de "19" introdus gresit);
  • ar fi singura coloana din tot formularul cu conventia asta, cand exact aceeasi valoare, pe aceeasi pagina, la un tab distanta (rulaje), se editeaza deja in forma "1.21".

Nu recomand conversia. Ramane InputMask = "9.99", neschimbat.

3.4 Riscul real: corelarea cu id_jtva_coloana/taxcode se poate dezincroniza

VERIFICAT ca risc, nu ca deja tratat. Spre deosebire de cProc_tva de pe rulaje (unde trul nu are corelare SAF-T de taxcode legata de cota), pe tvd cota TVA e legata explicit de id_jtva_coloana si de taxcode:

  • ModificaNomenclator, ramura id_jtva_coloana (ofacturare_editare.prg:1099-1104): alegerea unei explicatii TVA scrie impreuna id_jtva_coloana si proc_tvav, apoi cheama UpdateExplicatieSAFTArt() care recoreleaza taxcode din id_jtva_coloana (omodificari.vc2:15049-15073, verificat: lnIdJtva = tvd.id_jtva_coloana, GetTaxCodeIdPart(...), Replace taxcode With m.lnTaxCode In tvd).
  • Daca utilizatorul editeaza proc_tvav direct (tasteaza "1.19" peste "1.21"), fara sa treaca si prin nomenclatorul de explicatie TVA, id_jtva_coloana si taxcode raman la cota veche - cExplicatieTvaArt (coloana 16) ar continua sa arate explicatia de 21% langa o cota de 19%, iar taxcode-ul folosit la raportarea SAF-T ar fi cel corelat cu explicatia gresita. E o inconsistenta silentioasa, nu doar cosmetica - taxcode alimenteaza raportarea SAF-T 406.

Pe grila de rulaje, editarea directa a lui proc_tva/proc_tvav nu recoreleaza nimic legat de TVA (trul nu poarta taxcode/id_jtva_coloana in acelasi fel) - acolo riscul asta nu exista, deci precedentul de la 3.2 nu acopera si problema asta.

3.5 Doua variante, cu recomandare

Varianta A (recomandata) - blocheaza corelarea veche in loc s-o lase gresita

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Text1.When
    IF Thisform.lArticoleReadOnly OR Nvl(tvd.id_vanzare_set,0) <> 0
        RETURN .F.
    ENDIF
    Thisform.oldvalue = This.Value
ENDPROC

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Text1.Valid
    IF NVL(Thisform.oldvalue, 0) <> NVL(This.Value, 0)
        SELECT tvd
        REPLACE id_jtva_coloana WITH NULL, taxcode WITH NULL
        Thisform.calculeaza_valori_articol()
        Thisform.oldvalue = This.Value
        This.Parent.Parent.Refresh()
    ENDIF
ENDPROC

Dupa editare manuala a cotei, cExplicatieTvaArt si cTaxcodeArt raman goale pe randul respectiv - semnal vizibil, imediat, ca utilizatorul trebuie sa aleaga din nou explicatia TVA (punctul 4 de mai jos ii da exact calea: click sau tastare pe coloana Explicatie TVA deschide dialogul, filtrat deja pe noua cota introdusa). taxcode WITH NULL inseamna insa ca linia nu mai are cod SAF-T pana la re-alegere - de verificat cu tine daca exista vreo validare la salvare care sa avertizeze pe taxcode nul (nu am gasit una explicita pentru tvd, doar cea de pret de achizitie 0 de la :14457); daca nu exista, merita adaugata odata cu asta, ca sa nu scape o factura fara cod SAF-T la salvare.

Varianta B (respinsa) - lasa corelarea veche neatinsa, ca pe rulaje

Doar Thisform.calculeaza_valori_articol() in Valid, fara sa atinga id_jtva_coloana/taxcode. Simetrica cu precedentul de la 3.2, dar pe tvd inseamna taxcode SAF-T incorect ramas silentios dupa o editare manuala de cota - risc pe raportare fiscala, nu doar UX. Nu o recomand.

Nu am incercat o a treia varianta ("re-coreleaza automat") - ar insemna sa caut in crsJtvaTemp o explicatie care sa aiba exact noua cota si sa o aplic fara dialog; las-o deoparte pentru ca poate exista mai mult de o explicatie pe aceeasi cota (JC vs JV, exigibil vs neexigibil - vezi parametrii tlTipEx din caut_explicatie_tva, ocautare.prg:3174-3181), iar alegerea automata a uneia ar fi arbitrara exact acolo unde conteaza sa fie corecta.


4. Nomenclatoarele pe InteractiveChange

VERIFICAT - sablonul de referinta indicat de tine (Grid1.Column15/16/17 pe grila de note, omodificari.vc2:5890-5940) e construit pe do_modifica generic (parametri pcx_item, pccontrol, pnid_item, cautare in cursorul xitems) - un mecanism diferit fata de ArticoleNotaEditor.ModificaNomenclator, care primeste doar pccontrol si decide singur ce cautare deschide printr-un DO CASE pe numele campului (ofacturare_editare.prg:1073-1090). Sablonul se preia doar partial: pattern-ul GotFocus seteaza pccontrol+pncolumnorder, InteractiveChange cheama dialogul - dar apelul e catre ArticoleNotaEditor.ModificaNomenclator(pccontrol), fara pcx_item/pnid_item (nu exista in semnatura ei).

Cele 5 coloane cu nomenclator (AreNomenclator, ofacturare_editare.prg:1004-1007: 'denumire', 'nume_gestiune', 'nume_val', 'taxcode', 'id_jtva_coloana') au deja Text1.GotFocus care seteaza pccontrol+pncolumnorder (omodificari.vc2:16700-16704 cDenumireArt, :16723-16727 cExplicatieTvaArt cu pccontrol explicit 'tvd.id_jtva_coloana' pentru ca ControlSource e o expresie, :16729-16733 cGestiuneArt, :16750-16754 cTaxcodeArt, :16755-16759 cValutaArt) - functioneaza deja azi desi coloanele sunt ReadOnly = .T. (un textbox readonly primeste GotFocus la navigare cu sagetile, doar nu accepta tastare). Asta e mecanismul care tine But_modificaR sincronizat cu coloana curenta (punctul 5).

Propunere - adauga Text1.InteractiveChange pe fiecare, ReadOnly -> .F.

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cDenumireArt.Text1.InteractiveChange
    LOCAL loEditor
    loEditor = Createobject('ArticoleNotaEditor', Thisform)
    loEditor.ModificaNomenclator(Thisform.pccontrol)
ENDPROC

Identic (nume schimbat) pentru cGestiuneArt, cValutaArt, cTaxcodeArt, cExplicatieTvaArt - Thisform.pccontrol e deja setat corect de GotFocus-ul existent al fiecareia. Column1.ReadOnly (:12354), Column11.ReadOnly (:12428), Column12.ReadOnly (:12434), Column14.ReadOnly (:12453), Column16.ReadOnly (:12474) trec pe .F..

Risc: caracterul tastat inainte de deschiderea dialogului

VERIFICAT ca exista deja acelasi risc, neremediat, in sablonul citat. In do_modifica (omodificari.vc2:13960 si dublurile ei), REPLACE-ul cu valoarea aleasa se face doar in ramura IF gnButon = 1 (omodificari.vc2:14019-14025 pentru tact) - la anulare (gnButon <> 1 sau formular inchis fara alegere), campul nu e restaurat. Cum coloana e ReadOnly = .F., InteractiveChange se declanseaza pe prima tasta apasata (Value s-a schimbat deja cu acel caracter inainte ca evenimentul sa ruleze) - daca utilizatorul apasa o litera si apoi renunta la dialog, acel caracter ramane in camp.

Pe ArticoleNotaEditor.ModificaNomenclator e identic: REPLACE-ul final (ofacturare_editare.prg:1093-1107) e dupa IF gnButon <> 1: RETURN .F. ENDIF (:1082-1084) - la anulare, campul nu se atinge, deci caracterul parazit ramane in tvd.denumire (sau alt camp editat). Fara refresh explicit pe ramura de anulare (RefreshGrid() e apelat doar dupa REPLACE, in afara ramurii de RETURN timpuriu), gridul ramane cu textul modificat vizual pana la urmatoarea reimprospatare.

Nota: lmodificat nu se atinge pe ramura de anulare, dar cum am aratat la punctul 0, asta nu opreste totusi salvarea - daca linia era oricum activa (sters<>1), caracterul parazit ar fi scris in Oracle la urmatoarea salvare a documentului, indiferent de lmodificat.

Propunere de atenuare (in plus fata de sablon, nu cerinta din brief): in ArticoleNotaEditor.ModificaNomenclator, muta This.RefreshGrid() inainte de RETURN .F. din garda de anulare, ca sa readuca vizual valoarea corecta din cursor peste orice caracter tastat:

IF gnButon <> 1
    This.RefreshGrid()
    RETURN .F.
ENDIF

RefreshGrid() doar re-deseneaza gridul din cursor (This.oForm.pgfArticole.PAGE3.grdArticoleFactura.Refresh(), ofacturare_editare.prg:1136-1138) - nu scrie nimic, deci sigur de adaugat si pe ramura de anulare. Nu rezolva 100% (daca utilizatorul apasa doua taste rapid inainte ca dialogul sa apuce sa se deschida, a doua tasta tot ar intra), dar acopera cazul uzual (o tasta, apoi Esc pe dialog).

But_modificaR ramane functional neschimbat: click-ul lui cheama tot ArticoleNotaEditor.ModificaNomenclator(thisform.pccontrol) (omodificari.vc2:15319-15322, verificat), acelasi pccontrol pe care acum si InteractiveChange-ul il foloseste - nu se dubleaza deschiderea dialogului, sunt doua declansatoare catre aceeasi metoda, niciodata concurente (butonul se apasa explicit, InteractiveChange doar la tastare in celula).


5. BeforeRowColChange/AfterRowColChange - raman neschimbate

VERIFICAT.

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.AfterRowColChange
    Lparameters nColIndex
    DODEFAULT(m.nColIndex)
    Thisform.but_modificaR.Enabled = (Thisform.pncolumnorder = m.nColIndex) AND !Thisform.lArticoleReadOnly
ENDPROC

PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.BeforeRowColChange
    LPARAMETERS nColIndex
    IF Thisform.pncolumnorder # m.nColIndex
        STORE [] TO Thisform.pccontrol
    ENDIF
ENDPROC

Nu au nevoie de nicio schimbare:

  • Coloanele cu nomenclator continua sa seteze pncolumnorder in GotFocus (neschimbat la punctul 4) - But_modificaR ramane activat/dezactivat exact ca azi.
  • Coloanele noi editabile fara nomenclator (cSerieArt, cLotArt, cExplicatieArt, cProcTvavArt) nu primesc GotFocus care sa seteze pccontrol/pncolumnorder - la fel ca cCantitateArt/cPretArt/cPretAchizitieArt azi (verificat: niciuna dintre astea trei nu are Text1.GotFocus in intervalul :16678-16770). Cand utilizatorul navigheaza pe una din ele, pncolumnorder ramane la valoarea din ultima coloana cu nomenclator vizitata, deci Thisform.pncolumnorder = m.nColIndex e fals si But_modificaR ramane dezactivat - corect, pentru ca aceste patru coloane n-au nomenclator si nu trebuie sa activeze butonul.

Nu propun sa le adaug GotFocus - ar activa gresit But_modificaR pe coloane fara cautare.


6. Ordinea coloanelor - doar de retinut, nu de aplicat acum

cExplicatieTvaArt (Column16) e ultima coloana din grid, dupa cValoareArt (Column15) - ColumnOrder-ul azi urmeaza indicii (1-16). Mutarea ei langa cTaxcodeArt (Column14) ar cere renumerotarea ColumnOrder pe toate cele 16 coloane (nu doar schimbarea pozitiei uneia), cost deja notat in docs\propunere_runda4_note_sincronizare_tva.md:242-244. Ramane a doua iteratie, separata de livrarea asta.


Rezumat - ce e usor, ce e delicat

Usor, cu incredere mare:

  • serie/lot/explicatie editabile inline (punctul 2) - tipar identic cu cPretArt, fara recalcul necesar.
  • UPDATE-ul din ScrieArticoleFacturaEditate (punctul 1) - trei linii, tipar deja folosit alaturi (cont pe acelasi UPDATE, serie/lot/explicatie pe INSERT-ul de doua ori mai jos).
  • Nomenclatoarele pe InteractiveChange (punctul 4) - GotFocus deja exista pe toate cele 5 coloane, doar ReadOnly si InteractiveChange lipsesc.
  • BeforeRowColChange/AfterRowColChange (punctul 5) - zero schimbari, verificat ca raman corecte.

Delicat, cere o decizie a ta inainte de aplicare:

  • proc_tvav (punctul 3) - forma "1.21" e clar cea corecta de pastrat (precedent direct pe aceeasi pagina, la rulaje), dar corelarea cu id_jtva_coloana/taxcode la editare manuala e un risc real de raportare SAF-T netratat de niciun precedent existent. Recomand Varianta A (goleste corelarea, forteaza re-alegere) - confirma daca vrei asta sau preferi sa nu atingi id_jtva_coloana/taxcode deloc (Varianta B, mai simpla dar cu riscul asumat).
  • Garda de editare pe serie/lot/explicatie - am recomandat garda de baza (fara restrictia id_vanzare_det<>0 de la pret de achizitie), dar e o decizie de business (trasabilitate lot pe linii deja livrate), nu una pe care am putut-o confirma din cod.
  • Riscul caracterului parazit la deschiderea nomenclatorului pe InteractiveChange (punctul 4) - exista deja, neremediat, in sablonul do_modifica de pe grila de note; am propus o atenuare de o linie (RefreshGrid() pe ramura de anulare) care nu era in cerinta ta, spune daca o vrei in livrare sau ramane pentru alta runda.