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
This commit is contained in:
2026-08-20 16:35:03 +03:00
parent b5a7108f34
commit ca3c5d7eea
26 changed files with 5316 additions and 103 deletions

View File

@@ -0,0 +1,327 @@
# 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` |