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
287 lines
14 KiB
Markdown
287 lines
14 KiB
Markdown
# Runda 4 - totalul notelor, dialogul de sincronizare, explicatia TVA, codul de TVA
|
|
|
|
Diagnostic pe semnalarile din proba pe factura din aviz (document fara rulaje).
|
|
Fiecare punct: constatarea, dovada (fisier:linie), propunerea.
|
|
|
|
## Stare: aplicat in text si scris in binar, necomis
|
|
|
|
Decizii luate: la explicatia TVA - **varianta B** (dialogul `caut_explicatie_tva`, cu
|
|
corelarea codului SAF-T si filtrarea pe cota liniei); la punctul 2 - **fara** avertisment
|
|
suplimentar la salvare.
|
|
|
|
| ce | unde | write-back |
|
|
|---|---|---|
|
|
| 1. Total note se reface la orice recalculare a notei | `COMUN\clase\omodificari.vc2` | facut, `.vcx` la zi |
|
|
| 3. capete de coloana dinamice + labelul de explicatii eliminat | `COMUN\clase\omodificari.vc2` | facut, `.vcx` la zi |
|
|
| 4. codul de TVA nu mai vine ca Memo | `COMUN\programe\ofacturare_editare.prg` | n/a (`.prg`) |
|
|
| 5. explicatia TVA pe linia de articol + corelare taxcode | ambele fisiere | facut, `.vcx` la zi |
|
|
| 6. salvarea liniilor existente pastreaza toate campurile editabile | `COMUN\programe\ofacturare_editare.prg` | n/a (`.prg`) |
|
|
|
|
Diff-uri pentru review: `docs\diff_runda4_omodificari.patch`,
|
|
`docs\diff_runda4_ofacturare_editare.patch`. **Necomis** - nici git, nici SVN.
|
|
Verificat dupa write-back: textul reconvertit din `.vcx` e identic cu `.vc2` (0 diferente),
|
|
zero caractere corupte.
|
|
|
|
---
|
|
|
|
## 1. "Total note" nu se actualizeaza cand modific valoarea notei
|
|
|
|
**Nu e din cauza rulajelor.** Bara de totaluri se recalculeaza doar din evenimente de pe
|
|
partea de **articole**, niciodata din grila de note.
|
|
|
|
`ActualizeazaBaraTotaluri` (`COMUN\clase\omodificari.vc2:12959`) calculeaza `nTotalLiniiRon`
|
|
/ `nTotalNetRon` si apeleaza `ActualizeazaVerdictActRul` (`:13001`), care pune
|
|
`This.nTotalActRon` - adica exact valoarea afisata in "Total note"
|
|
(`txtTotalActArt.ControlSource = "thisform.nTotalActRon"`, `:12878-12880`).
|
|
|
|
Apelurile ei in productie sunt doar acestea:
|
|
|
|
| linie | context |
|
|
|---|---|
|
|
| `omodificari.vc2:13488` | `calculeaza_valori_articol` (editare in grila de articole) |
|
|
| `omodificari.vc2:14920` | `Show` (o data, la deschidere) |
|
|
| `omodificari.vc2:16604` | `pgfArticole.PAGE3.Activate` (la intrarea pe pagina Articole) |
|
|
| `omodificari.vc2:16698` | `txtDiscountArt.Valid` |
|
|
| `omodificari.vc2:16946` | `frm_sincronizare_articole.inainte_de_do_termin` |
|
|
| `ofacturare_editare.prg:1023` | editorul de articole din nota |
|
|
|
|
Editarea sumei pe randul de nota trece prin `Grid1.Column9.Text1.Valid`
|
|
(`omodificari.vc2:16044`), care apeleaza `Thisform.CalculeazaTotal()` -
|
|
`calculeazatotal` (`:13458`) recalculeaza numai `This.nSuma` (totalul setului de note) si
|
|
face `txtSuma.Refresh()`. Bara de jos ramane cu valoarea de la ultimul eveniment de articol.
|
|
|
|
Bara se reimprospateaza indirect daca iesi si reintri pe pagina Articole (`PAGE3.Activate`) -
|
|
de aceea uneori pare ca "s-a actualizat singura".
|
|
|
|
### Propunere 1 - o linie
|
|
|
|
La finalul lui `calculeazatotal` (`omodificari.vc2:13458-13475`), dupa
|
|
`Select (m.lcSelect)`:
|
|
|
|
```foxpro
|
|
This.ActualizeazaBaraTotaluri()
|
|
```
|
|
|
|
`ActualizeazaBaraTotaluri` are deja garda proprie la inceput
|
|
(`IF !This.lAreArticoleVanzari OR !Used('tvd') OR !Used('tvanz') OR Reccount('tvanz') = 0`),
|
|
deci pe notele fara articole nu face nimic. `calculeazatotal` e apelata din 8 locuri
|
|
(inclusiv `Show`, `:14878`), toate momente in care bara oricum trebuie sa fie corecta.
|
|
|
|
Alternativa mai ingusta (doar `Grid1.Column9.Text1.Valid`) acopera doar suma, nu si
|
|
stergerea/adaugarea de randuri de nota - nu o recomand.
|
|
|
|
---
|
|
|
|
## 2. Nu a aparut mesajul de verificare la salvare
|
|
|
|
**Aici da, e din cauza rulajelor** - si e comportament voit.
|
|
|
|
`SemnaturaDivergenteSincronizare` (`omodificari.vc2:14805`) iese devreme cu semnatura goala
|
|
cand nu exista randuri active in `trul`:
|
|
|
|
```foxpro
|
|
*!* fara rulaje nu exista cu ce compara: propunerea ar marca toate articolele ca "Semnalare"
|
|
SELECT trul
|
|
COUNT FOR Nvl(sters,0) <> 1 TO lnRanduriRul
|
|
IF m.lnRanduriRul = 0
|
|
... RETURN m.lcSemnatura && ''
|
|
```
|
|
|
|
iar declansatorul de la salvare (`:14449-14458`) porneste dialogul numai daca semnatura e
|
|
nevida si difera de cea de la deschidere. Deci: document fara rulaje -> niciun dialog.
|
|
Acelasi lucru il spune si verdictul din bara: *"nu se aplica (documentul nu are rulaje)"*
|
|
(`:13089`).
|
|
|
|
**Important, ca sa nu ramana o asteptare gresita:** mecanismul de sincronizare compara
|
|
**rulaje vs articole**, niciodata **note vs articole**. Chiar daca documentul ar fi avut
|
|
rulaje, modificarea sumei de pe randul de nota nu ar fi deschis dialogul - divergenta
|
|
note/articole e semnalata exclusiv informativ, prin verdictul din bara
|
|
(`ActualizeazaVerdictActRul`, `:13001`), si acolo, la 305.00 vs 306.00, ar fi trebuit sa
|
|
scrie "divergent" imediat dupa editare. Nu a scris-o din cauza punctului 1.
|
|
|
|
**Nicio schimbare de comportament** aici, decis: dialogul de sincronizare n-are ce aplica
|
|
fara rulaje, iar avertismentul simplu la salvare nu se face. Punctul 1, odata reparat, aduce
|
|
singurul semnal care lipsea (verdictul devine "divergent" pe loc).
|
|
|
|
---
|
|
|
|
## 3. Dialogul de sincronizare: coloane clare, fara labelul de explicatii
|
|
|
|
Starea de acum (`omodificari.vc2:16703` - `frm_sincronizare_articole`):
|
|
|
|
- capete de coloana fixe: `Cantitate veche` / `Cantitate noua` / `Pret vechi` / `Pret nou`
|
|
(`:16838-16867`);
|
|
- `lblAvertisment` (`:16871-16882`) explica ce inseamna: *"Vechi = valorile care se
|
|
salveaza acum; Nou = valorile din sursa aleasa mai sus..."*.
|
|
|
|
Semantica reala, verificata in `ConstruiestePropunereSincronizare`
|
|
(`ofacturare_editare.prg:707-710`): `vechi = tinta` (ce se suprascrie),
|
|
`nou = sursa` (de unde se preia). Directia se alege din `optDirectie` (`:16888`):
|
|
`RUL_SURSA` (rulajul e sursa) sau `ARTICOLE_SURSA`.
|
|
|
|
### Propunere 3 - capete dinamice pe directie + eliminarea labelului
|
|
|
|
In `ConstruiesteEnumerare` (`:16913`), care ruleaza deja la deschidere si la fiecare
|
|
schimbare de directie, se seteaza captiunile:
|
|
|
|
| coloana | `RUL_SURSA` | `ARTICOLE_SURSA` |
|
|
|---|---|---|
|
|
| 2 (`cCantVecheProp`) | `Cantitate in factura (se inlocuieste)` | `Cantitate in rulaj (se inlocuieste)` |
|
|
| 3 (`cCantNouaProp`) | `Cantitate in rulaj (se preia)` | `Cantitate in factura (se preia)` |
|
|
| 4 (`cPretVechiProp`) | `Pret in factura (se inlocuieste)` | `Pret in rulaj (se inlocuieste)` |
|
|
| 5 (`cPretNouProp`) | `Pret in rulaj (se preia)` | `Pret in factura (se preia)` |
|
|
|
|
Se sterge `lblAvertisment`; singura informatie din el care nu intra in capete este
|
|
*"daca renuntati, salvarea continua neschimbata"* - aceea exista deja ca ToolTip pe
|
|
`But_renunt1` (`:16752`, "Inchide fara sa schimbe nimic - salvarea continua"). Propun sa o
|
|
mut in ToolTipText-ul gridului sau sa o las doar pe buton; spune tu care variantă.
|
|
|
|
Ajustari de layout necesare: `HeaderHeight` 22 -> 32 (capetele au `WordWrap = .T.`, doua
|
|
randuri), latimile coloanelor 2-5 de la 88 la ~108, si gridul se poate inalta cu ~36 px
|
|
in locul labelului eliminat.
|
|
|
|
**Atentie la write-back:** `frm_sincronizare_articole` e clasa in `omodificari.vc2`, deci
|
|
modificarea e pe text + `txt2vcx.ps1`, nu in IDE.
|
|
|
|
---
|
|
|
|
## 4. Alegerea codului de TVA arata "Memo"
|
|
|
|
Confirmat, si cauza e exact cea spusa.
|
|
|
|
`ArticoleNotaEditor.CautaCodTva` (`COMUN\programe\ofacturare_editare.prg:1094-1100`):
|
|
|
|
```foxpro
|
|
lcSelect = [select taxname, procent_taxa, taxcode from vsaft_taxtable]
|
|
RETURN cauta_alfa(m.lcSelect, [1=2], [], [taxname], [taxname,procent_taxa], ;
|
|
[Alegeti codul de TVA], [Denumire,Procent], [], .F., [1=1], [taxname], 1, 0, [taxcode])
|
|
```
|
|
|
|
`cauta_alfa` trimite select-ul mai departe la `gencursor` (`COMUN\programe\cauta_alfa.prg:151`),
|
|
care il pune pe `SELECTCMD`-ul unui CursorAdapter legat de `goConn.nHandle`
|
|
(`COMUN\programe\gencursor.prg:27-31`) - **deci e SQL Oracle, executat pe server**.
|
|
`taxname` din view depaseste latimea declarata de 254 de caractere, iar driverul mapeaza
|
|
coloana pe Memo; grila din `cauta_alfa_form` afiseaza literalmente "Memo".
|
|
|
|
**Latimea declarata, nu cea reala** - asta e detaliul care conteaza. Definitia view-ului
|
|
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2022\03\ff_2022_03_14_01_COMUN_SAFT.sql:566-582`):
|
|
|
|
```sql
|
|
create or replace view vsaft_taxtable as
|
|
select ...
|
|
substr(a.taxcode || '-' || a.descriere, 1, 250) as taxname,
|
|
```
|
|
|
|
cu `descriere VARCHAR2(250)` in tabela (`ff_2022_03_08_01_COMUN_SAFT.sql:28`). Concatenarea
|
|
`taxcode || '-' || descriere` are latime declarata ~291 (NUMBER convertit implicit + 1 +
|
|
250), iar **`SUBSTR` nu micsoreaza latimea declarata a rezultatului** - ramane cea a
|
|
intrarii. De aceea `substr(...,1,250)` deja existent in view nu a rezolvat nimic, si de
|
|
aceea nici un `substr(taxname,1,200)` in clientul nostru nu ar rezolva singur: driverul tot
|
|
ar vedea o coloana > 254 si tot ar da Memo. Trebuie **`CAST`**, care e singurul care fixeaza
|
|
latimea declarata.
|
|
|
|
`LEFT` nu exista in Oracle - `SUBSTR` e idiomul folosit deja in cod
|
|
(`COMUN\programe\updateserver.prg:1357`, `substr(a.adresa,1,250) as adresa`).
|
|
|
|
### Propunere 4 - CAST, in tabela derivata
|
|
|
|
Nu merge simplu la nivelul de sus: `deca_baza.afisare` (`COMUN\clase\decabaza.vc2:36-73`)
|
|
concateneaza `WHERE` si `ORDER BY` **la acelasi nivel de query**, iar Oracle nu accepta
|
|
aliasul de coloana in `WHERE` (ORA-00904). Filtrul de cautare si ordinea sunt ambele pe
|
|
`taxname` (parametrii 4 si 11 din apel), deci forma sigura e:
|
|
|
|
```foxpro
|
|
lcSelect = [select * from (select cast(substr(taxname,1,200) as varchar2(200)) as taxname, ] + ;
|
|
[procent_taxa, taxcode from vsaft_taxtable)]
|
|
```
|
|
|
|
Restul apelului ramane neschimbat. De testat dupa aplicare: deschiderea dialogului, cautarea
|
|
"incepe cu" pe denumire (verifica ca `WHERE` merge pe aliasul din tabela derivata) si
|
|
returnarea corecta a `taxcode`.
|
|
|
|
**Varianta alternativa, doar pe client:** `cauta_alfa` primeste ca parametru 3 o
|
|
`CURSORSCHEMA` (acum `[]`, vezi apelul). Trecand
|
|
`[taxname C(200), procent_taxa N(6,2), taxcode N(6)]` s-ar forta tipul in cursorul VFP fara
|
|
sa se atinga SQL-ul. Merge, dar leaga apelul de structura exacta a view-ului; prefer CAST-ul.
|
|
|
|
**De reparat separat, daca vrei:** acelasi `substr` fara CAST din view il mosteneste si
|
|
`typename` (`:569`) - orice alt loc care afiseaza `vsaft_taxtable.typename` are aceeasi
|
|
problema. Nu am verificat unde se mai foloseste.
|
|
|
|
**Nota:** celelalte alegeri de taxcode (randul de nota, `omodificari.vc2:3926` / `:8491`,
|
|
si rulajele, `rulaje.vc2:5289`) citesc cursorul **local** `saft_taxtable`, unde `taxname` e
|
|
C(250) - acolo nu apare Memo. Problema e strict pe calea Oracle din `CautaCodTva`.
|
|
|
|
---
|
|
|
|
## 5. Explicatia TVA in editarea articolelor
|
|
|
|
Lipseste, si se poate adauga - campul exista deja in cursor.
|
|
|
|
`tvd` are `id_jtva_coloana I NULL` de la creare (`omodificari.vc2:14735`) si e completat
|
|
din articol la adaugare (`AdaugaLinieTvdDinArticol`, `:13144`). Ce lipseste:
|
|
|
|
1. **coloana in grila** `grdArticoleFactura` - coloanele actuale sunt 1-15
|
|
(`:12345-12454`), fara nimic pe `id_jtva_coloana`;
|
|
2. **editorul** - `ArticoleNotaEditor` (`ofacturare_editare.prg:1060-1090`) trateaza doar
|
|
`denumire`, `nume_gestiune`, `nume_val` si taxcode.
|
|
|
|
Sablon existent, de copiat: randul de rulaj face exact asta -
|
|
afisare prin `Iif(Seek(trul.id_jtva_coloana,'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')`
|
|
(`omodificari.vc2:9253`) si editare prin combo cu
|
|
`RowSource = "select denumire, id_jtva_coloana from crsJtvaTemp order by denumire into cursor crsJtvaTempX"`
|
|
(`:9652-9655`). Randul de nota foloseste dialogul `caut_explicatie_tva`
|
|
(`COMUN\programe\ocautare.prg:3174`), apelat din `do_modifica_explicatie_tva`
|
|
(`omodificari.vc2:14049`), care pune `id_jtva_coloana`, `explicatie_tva` si `proc_tva`.
|
|
|
|
### Aplicat - varianta B (dialogul `caut_explicatie_tva`)
|
|
|
|
1. **Coloana** `cExplicatieTvaArt` in `grdArticoleFactura` (ColumnCount 15 -> 16), readonly,
|
|
care afiseaza denumirea prin
|
|
`Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')` -
|
|
acelasi tipar ca pe randul de rulaj. **Coloana e ultima in grila**, dupa Valoare: ordinea
|
|
naturala e cea a indicilor, iar mutarea ei langa Taxcode ar fi cerut `ColumnOrder` pe toate
|
|
celelalte. Daca o vrei langa Taxcode, e o a doua iteratie, doar de asezare.
|
|
2. **Text1.GotFocus** pune explicit `Thisform.pccontrol = 'tvd.id_jtva_coloana'` - coloana are
|
|
ControlSource expresie, deci `Alltrim(This.ControlSource)` (tiparul celorlalte coloane) n-ar
|
|
fi dat un nume de camp utilizabil.
|
|
3. **`ArticoleNotaEditor.CautaExplicatieTva`** apeleaza `caut_explicatie_tva` cu filtrul de cota
|
|
calculat din linie: `Round((Nvl(tvd.proc_tvav,0) - 1) * 100, 2)` - `proc_tvav` e in forma
|
|
1.21, exact ca `tact.proc_tva` la randul de nota. Cota 0/goala = lista completa.
|
|
`tlTipEx` ramane gol (toate explicatiile cu `id_jtva_coloana > 0`), ca in ramura `Else` a
|
|
randului de nota; daca vrei restrangerea la TVA exigibil din JV, e un parametru in plus.
|
|
4. **Corelarea codului SAF-T**: alegerea scrie `id_jtva_coloana` si `proc_tvav`
|
|
(`(cota_tva + 100) / 100`), apoi cheama `UpdateExplicatieSAFTArt` - metoda noua in
|
|
`frm_modific2024`, copie a lui `UpdateExplicatieSAFTRul` cu `tvd` in loc de `trul`
|
|
(acelasi `GetTaxCodeIdPart`, aceeasi garda `gl406`). La final `calculeaza_valori_articol`,
|
|
ca valoarea liniei si bara de totaluri sa urmeze noua cota.
|
|
|
|
---
|
|
|
|
## 6. Defect gasit pe drum: salvarea pierde tacut alegerile de pe liniile existente
|
|
|
|
`ScrieArticoleFacturaEditate` (`ofacturare_editare.prg:471`) actualiza liniile deja salvate
|
|
**doar** cu `sters`, `cantitate`, `pret`, `pret_cu_tva`. Restul campurilor apareau doar in
|
|
INSERT-ul liniilor noi. Consecinta: pe o linie existenta, codul de TVA ales din nomenclator,
|
|
gestiunea, valuta, articolul, pretul de achizitie si discountul se pierdeau la salvare fara
|
|
niciun mesaj - defect care exista **inainte** de explicatia TVA, nu introdus de ea.
|
|
|
|
UPDATE-ul acopera acum toate campurile pe care grila si nomenclatoarele le pot schimba:
|
|
`pret_achizitie`, `proc_tvav`, `discount_unitar`, `id_articol`, `id_gestiune`, `id_valuta`,
|
|
`id_jtva_coloana`, `taxcode`, `cont`.
|
|
|
|
---
|
|
|
|
## De probat
|
|
|
|
1. **Total note**: modifica suma pe un rand de nota -> "Total note" se schimba pe loc, iar
|
|
verdictul trece pe "divergent" daca nu mai bate cu articolele.
|
|
2. **Cod TVA**: pe pagina Articole, coloana Taxcode + butonul de modificare -> lista trebuie
|
|
sa arate denumirile, nu "Memo"; cautarea "incepe cu" pe denumire trebuie sa filtreze.
|
|
3. **Explicatie TVA**: pe o linie cu cota 21%, lista trebuie sa contina doar explicatiile de
|
|
21%; dupa alegere, coloana Taxcode se schimba singura.
|
|
4. **Persistenta**: alege cod TVA / explicatie TVA pe o linie **deja salvata**, salveaza,
|
|
redeschide - valorile trebuie sa fie acolo.
|
|
5. **Dialogul de sincronizare** (document cu rulaje): capetele se schimba cand comuti directia,
|
|
iar legenda rosie de jos nu mai exista.
|