# 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.