# R6 - butonul "Modificare articol" din frm_facturi + explicatie TVA - diagnostic Raspuns la cerinta: pe formularul de facturare `frm_facturi` (lista facturi/avize/proforme, NU `frm_modific2024`), butonul care edita azi `explicatie`+`taxcode` sa expuna si "explicatie TVA", cu sincronizare automata `explicatie TVA -> taxcode`. Fiecare afirmatie e **VERIFICAT** (citit din sursa, `fisier:linie`) - nu apare nimic marcat NESTABILIT, tot ce s-a cerut s-a putut confirma din cod. --- ## A. Localizarea `frm_facturi` Clasa `frm_facturi` (nu form `.scx` separat) - definita in `COMUN\clase\ofacturare_comun.vc2:1168`: ``` DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx" ``` Lant de mostenire: `frm_facturi -> _frmbase (_frm_base.vc2:7) -> _form (_baza.vc2:157) -> form`. Instantiat din `roafacturare.prg` (meniul principal), nu are `.scx` propriu - toata definitia e in binarul `ofacturare_comun.vcx`. --- ## B. Butonul "Modificare articol" Obiect **`But_modifica2`**, definit in `frm_facturi` la `ofacturare_comun.vc2:1436-1444`: ``` ADD OBJECT 'But_modifica2' AS but_modifica WITH ; Anchor = 12, ; caction = do_modifica_explicatie, ; Caption = "", ; Left = 710, ; Name = "But_modifica2", ; TabIndex = 27, ; ToolTipText = "Modificare explicatie articol", ; Top = 341 *< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" /> ``` `Caption` e gol (e un buton cu picture, nu text); `ToolTipText` = "Modificare explicatie articol". Clasa `but_modifica` (`cmd_butoane.vc2:184-197`) nu are `Click` propriu - mosteneste `buton.Click` din `_cmd_base.vc2:41-71`, care e un dispatcher generic: ``` PROCEDURE Click Local lcAction, lcCommand, lcListaParametri lcAction = This.cAction ... If Type('this.parent') = 'O' And Pemstatus(This.Parent,lcAction,5) lcCommand = [this.Parent.] + lcAction + lcListaParametri &lcCommand Else If Type('this.parent.PARENT') = 'O' And Pemstatus(This.Parent.Parent,lcAction,5) lcCommand = [this.Parent.Parent.] + lcAction + lcListaParametri &lcCommand Else If Pemstatus(Thisform,lcAction,5) lcCommand = [thisform.] + lcAction + lcListaParametri &lcCommand Endif Endif Endif ENDPROC ``` Cu `caction = do_modifica_explicatie` si parintele `But_modifica2` fiind direct `frm_facturi` (fara container intermediar cu aceeasi metoda), rezultatul e efectiv `thisform.do_modifica_explicatie()`. Corpul integral al metodei (`ofacturare_comun.vc2:4642-4659`): ``` PROCEDURE do_modifica_explicatie If Reccount('crsDetalii') > 0 update_saft_taxtable() Select crsDetalii Scatter Name poRec Memo poRec.explicatie = NVL(poRec.explicatie, ' ') If poRec.sters = 0 ofrmmodificare = Createobject("frm_modifica_articol_factura") ofrmmodificare.Show() If gnButon = 1 Thisform.actualizeaza_grid2() Endif Endif Endif ENDPROC ``` --- ## C. Ce deschide butonul Un formular modal separat: **`frm_modifica_articol_factura`** (`ofacturare_comun.vc2:5129-5253`), clasa `AS frm_termin_renunt OF "_frm_child.vcx"` (dialog copil standard, cu `But_termin1`/`But_renunt1`). Titlu: `Lb_titlu_alb_b121.Caption = "Modifică explicație articol"` (`:5163`). Campuri editabile azi, cu `ControlSource` si linii: | control | tip | ControlSource | label | linie | |---|---|---|---|---| | `Ed_tx_simplu1._edbase1` | editbox | `porec.explicatie` | "Explicație" (+ "( maxim N caractere )" adaugat in `Init`) | `:5213-5223` | | `cbo_saft` | combobox | `poRec.taxcode` | "Cod taxa" (`lbSaft`) | `:5185-5199` | `cbo_saft` e legat direct pe `poRec.taxcode` (BoundColumn=4), cu: ``` RowSource = "Select taxname, tip, procent_taxa, taxcode From saft_taxtable where tva = 1 order by taxcode Into Cursor crsTaxTableX" ``` adica lista tuturor codurilor SAF-T (cursor local `saft_taxtable`, populat de `update_saft_taxtable()` apelat inainte de deschidere) - **nu exista azi nicio legatura cu explicatia TVA**; userul alege direct un `taxcode` dintr-o lista plata de coduri fiscale. Niciun alt camp (cantitate, pret, articol, id_jtva_coloana) nu e pe acest dialog. --- ## D. Cum se scrie inapoi - si confirmarea premisei "nu modifica valorile" Salvare in `inainte_de_do_termin` (`ofacturare_comun.vc2:5225-5233`): ``` PROCEDURE inainte_de_do_termin Local llReturn If amessagebox("Doriti sa salvati modificarile?",4+32,"Confirmare salvare explicatie") = 6 lcSql = [begin pack_facturare.modifica_explicatie_articol(] + Alltrim(Str(poRec.id_vanzare_det)) + [,] + ; ['] + Nvl(Alltrim(OracleSpecialCharacters(poRec.explicatie)),'') + [',?gnIdUtil,?poRec.taxcode); end;] llReturn = goExecutor.oExecuta(lcSql) Else llReturn = .F. Endif Return llReturn ENDPROC ``` Scrierea e **direct in Oracle**, prin `pack_facturare.modifica_explicatie_articol(id_vanzare_det, explicatie, id_util, taxcode)`. Cercetare anterioara deja confirmase corpul acestei proceduri (`docs\cercetare\rec_cale_vanzari_detalii.md:195-199`, citat identic aici pentru trasabilitate): ```sql UPDATE VANZARI_DETALII SET EXPLICATIE = V_EXPLICATIE, TAXCODE = V_TAXCODE WHERE ID_VANZARE_DET = V_ID_VANZARE_DET ``` **Confirmat: doar 2 coloane se scriu** (`explicatie`, `taxcode`). Nu se ating `cantitate`, `pret`, `pret_cu_tva`, `proc_tvav`, `id_jtva_coloana` si nu exista niciun apel de recalcul de totaluri (`gcs.vact_tot`/`vrul_tot`, note contabile, rulaje) in acest flux - premisa utilizatorului ("campuri care nu modifica valorile") **e corecta pentru starea de azi a acestor doua campuri**. --- ## E. "Explicatie TVA" ca data + sincronizarea cu taxcode - sablonul deja aplicat in frm_modific2024 Campul din baza este **`id_jtva_coloana`** (nu `taxcode`) - nomenclator `vjtva_coloane` (`id_jtva_coloana, denumire, cota_tva`). `taxcode` e un cod SAF-T/e-Factura separat, derivat din `id_jtva_coloana` + context (partener, data, tip TVA), nu ales direct de nomenclatorul de explicatii. Sablonul de sincronizare cerut de utilizator **exista deja**, aplicat in runda 5 pe `frm_modific2024` / `pgfArticole.PAGE3.grdArticoleFactura` (cursor `tvd`), documentat in `docs\raport_runda5_omodificari.md` si `docs\propunere_runda4_note_sincronizare_tva.md` (sectiunea 5, "varianta B"). Cod citit direct din sursa curenta (`COMUN\programe\ofacturare_editare.prg`, `ArticoleNotaEditor.ModificaNomenclator`, `:1100-1108`): ``` CASE m.lcCamp == 'id_jtva_coloana' *!* cota vine din explicatia aleasa, ca la randul de nota; taxcode-ul SAF-T se recoreleaza dupa ea REPLACE id_jtva_coloana WITH loCauta.id_jtva_coloana, ; proc_tvav WITH (Nvl(loCauta.cota_tva, 0) + 100) / 100 This.oForm.UpdateExplicatieSAFTArt() This.oForm.calculeaza_valori_articol() ``` `UpdateExplicatieSAFTArt` (`omodificari.vc2:15049-15074`) e "jumatatea taxcode" a sincronizarii - deriva `taxcode` din `id_jtva_coloana` prin functia comuna `GetTaxCodeIdPart`: ``` PROCEDURE updateexplicatiesaftart IF !m.gl406 RETURN ENDIF ... lnIdJtva = tvd.id_jtva_coloana lnIdPart = Nvl(tAct.id_partc, tAct.id_partd) ... lnTaxCode = GetTaxCodeIdPart(m.gnAn, m.gnLuna, m.ldDataAct, m.lnIdJtva, m.lnIdPart, m.llN50, m.llN100, m.llNeexigibil) Replace taxcode With m.lnTaxCode In tvd This.pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Refresh() ENDPROC ``` Relatia exacta explicatie TVA -> taxcode: **`id_jtva_coloana` (+ an/luna/data act/partener/steaguri N50-N100/neexigibil) -> `GetTaxCodeIdPart()` -> `taxcode`**. Nu e un `Do Case` simplu pe `id_jtva_coloana`; `GetTaxCodeIdPart` (functie globala, `COMUN\programe\oproceduri_comune.prg:6059-6125`, NEschimbata pentru aceasta cerere) incapsuleaza toata logica SAF-T 406. **Garda `gl406`**: `UpdateExplicatieSAFTArt` (si perechile ei `Rul`/simplu) nu fac nimic daca `gl406 = .F.` (`COMUN\programe\oinit_optiuni.prg:524`, flag global de optiuni). Adica sincronizarea taxcode <- explicatie TVA e activa doar cand raportarea SAF-T 406 e activata pentru firma curenta - de verificat la implementare daca `frm_facturi` are acces la acelasi `gl406`. --- ## F. Cum se deschide azi nomenclatorul de explicatie TVA Doua rutine **globale, refolosibile din orice formular** (nu legate de contextul lui `frm_modific2024`): 1. **`caut_explicatie_tva(tnIdJtva, tnPornire, tlDesktop, tlTipEx, tnCotaTva)`** (`COMUN\programe\ocautare.prg:3174-3238`) - deschide dialogul de cautare pe `select id_jtva_coloana, denumire, cota_tva from vjtva_coloane`, filtrat implicit pe `id_jtva_coloana > 0` (cand `tlTipEx` e gol) si, daca `tnCotaTva > 0`, suplimentar pe `cota_tva = tnCotaTva` (`:3218-3220`, filtru adaugat tot in runda asta). Returneaza obiectul `loCauta` din `cauta_alfa` (are `.id_jtva_coloana`, `.denumire`, `.cota_tva`). 2. **`GetTaxCodeIdPart(an, luna, data_act, id_jtva, id_part, n50, n100, neexigibil)`** (`COMUN\programe\oproceduri_comune.prg:6059-6125`) - returneaza `taxcode`-ul SAF-T. Apelul concret cu filtrul de cota (`ArticoleNotaEditor.CautaExplicatieTva`, `ofacturare_editare.prg:1142-1147`): ``` PROCEDURE CautaExplicatieTva LOCAL lnCotaFiltru lnCotaFiltru = Round((Nvl(tvd.proc_tvav, 0) - 1) * 100, 2) RETURN caut_explicatie_tva(Nvl(tvd.id_jtva_coloana, -1), , , , m.lnCotaFiltru) ENDPROC ``` Ambele functii sunt `.prg`-uri globale incarcate `ADDITIVE` la pornirea aplicatiei (deci apelabile si din `frm_facturi`) - **nu cer nicio extragere de cod**, doar apel direct. --- ## G. `frm_facturi` deja afiseaza explicatia TVA - doar needitabil Grila `grid_detalii` din `frm_facturi` are deja doua coloane needitabile pentru asta (`ofacturare_comun.vc2:1679-1687`): ``` Column24.ControlSource = "jtva_coloana", ; Column24.Name = "cExplicatieTVA", ; Column24.ReadOnly = .T., ; ... Column25.ControlSource = "jtva_coloana_ex", ; Column25.Name = "cExplicatieTVAEx", ; Column25.ReadOnly = .T., ; ``` cu header-ele "Explicatie TVA" / "Explicatie TVA 2" (`:1920-1946`). `jtva_coloana`/`jtva_coloana_ex` sunt (dupa tiparul din restul fisierului, ex. `:2107-2109`) denumirile derivate din `jtva_coloane`/`jtva_coloane_explicatii`, nu ID-uri brute - afisare, nu editare. Nu exista alt traseu in `frm_facturi` unde utilizatorul alege interactiv o explicatie TVA (nu la adaugare de linie - acest formular nu are adaugare de linii, e formular de listare/editare metadate, nu de compunere a facturii). Concluzie G: campul exista deja ca **read-only** in grid; nu trebuie adaugata coloana in grid, doar expunerea editabila in dialogul de modificare (punctul C). --- ## H. Riscul - taxcode NU e neutru daca se copiaza tot sablonul **Rezultat cheie, marcat apasat:** sablonul din E (runda 5, `frm_modific2024`) **nu e neutru valoric**. La alegerea unei explicatii TVA, pe langa `taxcode`, se rescrie explicit **`proc_tvav`** (cota TVA a liniei) si se cheama **`calculeaza_valori_articol()`**, care recalculeaza valoarea liniei si bara de totaluri (`docs\raport_runda5_omodificari.md`, linia 41 din tabelul de apeluri `ActualizeazaBaraTotaluri`, si citatul de la punctul E de mai sus). Adica in acel context, "explicatie TVA" **schimba** valorile documentului daca explicatia aleasa are alta cota decat cea curenta - filtrul de cota din `caut_explicatie_tva` (punctul F) doar restrange lista, nu blocheaza o alegere cu cota diferita daca userul o cauta explicit sau daca linia are `proc_tvav` gol/0 (caz in care filtrul nu se aplica deloc, `ocautare.prg:3218`: `If ... tnCotaTva > 0`). Pentru `taxcode` insusi, in fluxul actual: `taxcode` **nu e citit nicaieri in calculul totalurilor VFP** - cautare in `COMUN\programe\ofacturare_comun.prg` si in schema campurilor din `ofacturare_comun.vc2` (punctul D) nu arata `taxcode` folosit in nicio expresie de `valctva`, `valtva`, `total_cu_tva` sau similar; acelea se calculeaza din `proc_tvav`/`pret`/`cantitate`, coloane distincte. `taxcode` e strict o eticheta de raportare SAF-T/e-Factura, stocata pe linie. **Deci verdictul depinde strict de ce se implementeaza:** - **Daca se reface `pack_facturare.modifica_explicatie_articol` sa scrie DOAR `id_jtva_coloana` (nou parametru) + `taxcode` derivat, FARA sa atinga `proc_tvav`** - si daca dialogul de `frm_facturi` foloseste `caut_explicatie_tva` cu filtrul de cota calculat din `poRec.proc_tvav` curent (ca sa nu se poata alege o explicatie de alta cota) - atunci operatia ramane neutra valoric, exact ce cere utilizatorul. Aceasta e o **varianta redusa** a sablonului din E, nu o copiere 1:1. - **Daca se copiaza sablonul din E exact cum e** (inclusiv rescrierea `proc_tvav` + recalcul) - operatia **NU mai e neutra**: ar schimba cota TVA si valorile unei facturi **deja emise**, fara mecanismul de recalcul de totaluri/note/rulaje care exista in `frm_modific2024` (acolo editarea e pe cursoare locale, salvata printr-un flux dedicat de recalcul; `frm_facturi` nu are asa ceva - e un UPDATE punctual pe o singura linie, deja emisa). Risc fiscal si de coerenta (SAF-T/e-Factura ar raporta un `taxcode` care nu mai corespunde cu `proc_tvav`/totalurile deja emise, sau invers, daca doar taxcode se sincronizeaza fara proc_tvav). **Recomandare de proiectare** (schita, fara cod aplicat): varianta redusa de mai sus - dialog `frm_modifica_articol_factura` capata un al treilea camp (combo pe explicatie TVA, `caut_explicatie_tva` filtrat pe cota curenta a liniei, ca in `CautaExplicatieTva`), la alegere se recalculeaza doar `taxcode` local (`GetTaxCodeIdPart`, acelasi apel ca `UpdateExplicatieSAFTArt` dar fara `REPLACE proc_tvav`), iar la salvare `pack_facturare.modifica_explicatie_articol` capata un parametru nou `id_jtva_coloana` (schimbare PL/SQL, in afara acestui repo VFP - de coordonat separat) pe langa `explicatie`/`taxcode` existente. Cod nou estimat: mic pe partea VFP (~30-40 linii: un combo nou + un handler de alegere + un parametru in apelul SQL), dar cere **schimbare de pachet Oracle** (`pack_facturare.modifica_explicatie_articol`) care nu exista in acest working copy si trebuie facuta/aprobata separat in `DATABASE`. --- ## Rezumat surse | ce | fisier:linie | |---|---| | clasa `frm_facturi` | `COMUN\clase\ofacturare_comun.vc2:1168` | | butonul `But_modifica2` | `COMUN\clase\ofacturare_comun.vc2:1436-1444` | | dispatcher `buton.Click` | `COMUN\clase\_cmd_base.vc2:41-71` | | `do_modifica_explicatie` | `COMUN\clase\ofacturare_comun.vc2:4642-4659` | | dialog `frm_modifica_articol_factura` | `COMUN\clase\ofacturare_comun.vc2:5129-5253` | | salvare (`inainte_de_do_termin`) | `COMUN\clase\ofacturare_comun.vc2:5225-5233` | | grid `cExplicatieTVA`/`cExplicatieTVAEx` (read-only) | `COMUN\clase\ofacturare_comun.vc2:1679-1687`, `:1920-1946` | | sablon sincronizare (runda 5) | `COMUN\programe\ofacturare_editare.prg:1100-1147`, `COMUN\clase\omodificari.vc2:15049-15074` | | `caut_explicatie_tva` | `COMUN\programe\ocautare.prg:3174-3238` | | `GetTaxCodeIdPart` | `COMUN\programe\oproceduri_comune.prg:6059-6125` | | `gl406` | `COMUN\programe\oinit_optiuni.prg:524` | | procedura Oracle (citata din cercetare anterioara) | `docs\cercetare\rec_cale_vanzari_detalii.md:195-199` |