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:
125
docs/cercetare/rec_cantitate_negativa_date.md
Normal file
125
docs/cercetare/rec_cantitate_negativa_date.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Cercetare pe date: `cantitate < 0` / `cantitate = 0` pe articole de vânzări (validarea de la `omodificari.vc2:14386`)
|
||||
|
||||
Schema verificată: `MARIUSM_AUTO`@`ROA_CENTRAL` (schema de dezvoltare, folosită și pentru rulări de
|
||||
teste automate — vezi caveat la final). Toate interogările: `SET TRANSACTION READ ONLY`, încheiate
|
||||
cu `ROLLBACK` (confirmat: „Rollback complete." la fiecare rulare, zero scrieri).
|
||||
|
||||
Sursă date: view-ul `vvanzari_articole` (JOIN `VANZARI_DETALII`+articol, expune deja `denumire`,
|
||||
`pret_achizitie`), filtrat `sters = 0` (= liniile active), înjumătățit cu `VANZARI` pe `id_vanzare`
|
||||
pentru `tip`/`cod`/`data_act`. Nu trag concluzii de produs — doar cifre și exemple, cerute punct cu punct.
|
||||
|
||||
## 1. Câte linii, pe `VANZARI.TIP`
|
||||
|
||||
**`cantitate < 0`** (linii active, sparte pe tip):
|
||||
|
||||
| TIP | linii | documente | etichetă (dacă am confirmat-o în cod) |
|
||||
|---|---|---|---|
|
||||
| -13 | 1 | 1 | necunoscută — vezi caveat |
|
||||
| -12 | 9 | 8 | necunoscută — vezi caveat |
|
||||
| -8 | 1 | 1 | necunoscută — vezi caveat |
|
||||
| -7 | 1 | 1 | necunoscută — vezi caveat |
|
||||
| -6 | 25 | 24 | necunoscută — vezi caveat |
|
||||
| -4 | 1 | 1 | necunoscută — vezi caveat |
|
||||
| -2 | 1 | 1 | necunoscută — vezi caveat |
|
||||
| -1 | 3 | 2 | necunoscută — vezi caveat |
|
||||
| 1 | 3 | 2 | lista de preturi (confirmat de team-lead) |
|
||||
| 3 | 10 | 4 | necunoscută — nu am verificat-o în cod |
|
||||
| 8 | 2 | 1 | necunoscută — nu am verificat-o în cod |
|
||||
| 24 | 1 | 1 | necunoscută — nu am verificat-o în cod |
|
||||
| 41 | 4 | 4 | necunoscută — nu am verificat-o în cod |
|
||||
| 51 | 3 | 1 | ROAACNPRO / import (menționat în `omodificari.vc2:13125-13127`, cercetarea `rec_s8_rulaje_tip4.md`) |
|
||||
|
||||
Total: **65 linii active cu `cantitate < 0`, pe 52 documente distincte.**
|
||||
|
||||
**`cantitate = 0`** (linii active, sparte pe tip):
|
||||
|
||||
| TIP | linii | documente | etichetă |
|
||||
|---|---|---|---|
|
||||
| -6 | 2 | 2 | necunoscută — vezi caveat |
|
||||
| 2 | 1 | 1 | contract (confirmat de team-lead) |
|
||||
| 22 | 5 | 5 | aviz (confirmat în cod, `oproceduri_facturare.prg` etc.) |
|
||||
|
||||
Total: **8 linii active cu `cantitate = 0`, pe 8 documente distincte.**
|
||||
|
||||
Tabelele de mai sus conțin `TIP = -6`, nu `TIP = 6` — sunt valori distincte, nu le-am confundat.
|
||||
|
||||
> **CORECTIE a sesiunii principale, 12.08.2026 — afirmatia „doar `TIP = 4` trece prin validarea de la
|
||||
> `:14386`" era GRESITA si a fost stearsa de aici si din §6.** Contaminare din misiunea anterioara a
|
||||
> aceluiasi agent, care era chiar despre tip 4.
|
||||
>
|
||||
> Pagina de articole **nu e conditionata de tip**. `omodificari.vc2:14842`:
|
||||
> `IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Reccount('tact') > 0`, apoi
|
||||
> `IF Reccount('tvanz') = 1` -> `lAreArticoleVanzari = .T.`. `nTipVanzare` e doar **retinut**
|
||||
> (`:14841`), niciodata folosit ca poarta. Deci **orice document cu rand in `VANZARI`** primeste `tvd`
|
||||
> si trece prin `:14386`.
|
||||
>
|
||||
> Dovedit si empiric de matricea S8 (`rec_s8_matrice.md`): asertia „tvd incarcat cu liniile active
|
||||
> reale" a trecut pe `1048` (tip 1), `1055` (tip 2), `1052` (tip 22) **si** `1054` (tip 4).
|
||||
>
|
||||
> **Consecinta**: absenta lui `TIP = 4` din tabele **nu e o veste buna** si nu arata ca validarea n-a
|
||||
> respins nimic real. Cele 52 de documente cu `cantitate < 0` sunt exact populatia pe care validarea o
|
||||
> blocheaza. Tipurile `-6` si `41` sunt **retur-transfer** (`docs\progres.md`, sectiunea #8/S8), adica
|
||||
> fix cazul semnalat de Marius.
|
||||
|
||||
## 2. Exemple concrete (3 + 3, cele mai recente după `data_act`)
|
||||
|
||||
**`cantitate < 0`:**
|
||||
|
||||
| id_vanzare | tip | cod | data_act | denumire | cantitate | pret | id_articol | pret_achizitie |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| 1035 | 3 | 1140727 | 26-MAR-26 | DISCOUNT | -3 | 4.8 | 4294507213 | 0 |
|
||||
| 1035 | 3 | 1140727 | 26-MAR-26 | DISCOUNT | -1 | 3.15 | 4294507213 | 0 |
|
||||
| 1035 | 3 | 1140727 | 26-MAR-26 | DISCOUNT | -3 | 3.3 | 4294507213 | 0 |
|
||||
|
||||
**`cantitate = 0`:**
|
||||
|
||||
| id_vanzare | tip | cod | data_act | denumire | cantitate | pret | id_articol | pret_achizitie |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| 1051 | 22 | 1140907 | 10-AUG-26 | SERVICII PER BUC | 0 | 0 | 4294507490 | 0 |
|
||||
| 1057 | 2 | 1140913 | 10-AUG-26 | SERVICII PER BUC | 0 | 0 | 4294507490 | 0 |
|
||||
| 1056 | 22 | 1140912 | 10-AUG-26 | SERVICII PER BUC | 0 | 0 | 4294507490 | 0 |
|
||||
|
||||
(Toate cele 3 exemple de `cantitate=0` provin sunt pe `id_articol` completat, `pret = 0`.)
|
||||
|
||||
## 3. Recență
|
||||
|
||||
| categorie | `MAX(data_act)` | linii în ultimele 12 luni |
|
||||
|---|---|---|
|
||||
| `cantitate < 0` | 26-MAR-26 | 10 din 65 |
|
||||
| `cantitate = 0` | 10-AUG-26 | 6 din 8 |
|
||||
|
||||
## 4. `pret = 0` pe linii active
|
||||
|
||||
**19 linii, pe 17 documente distincte**, `MAX(data_act) = 10-AUG-26`. Toate cele 19 au `id_articol`
|
||||
completat (`linii_cu_articol = 19` din `19` — deci nu sunt rânduri-rest fără articol).
|
||||
|
||||
## 5. `pret IS NULL` pe linii active
|
||||
|
||||
**0 linii.** Confirmă structural ce ați bănuit: `VANZARI_DETALII.PRET` e `NOT NULL` (verificat cu
|
||||
`DESC vanzari_detalii` — coloana `PRET NUMBER(22,6) NOT NULL`), deci interogarea pe `pret IS NULL`
|
||||
nu putea da altceva decât 0.
|
||||
|
||||
## 6. `cantitate < 0` pe documente cu pagină de articole (validarea `:14386`)
|
||||
|
||||
Agentul a raportat aici ca „doar `TIP = 4` trece prin validare" si ca `TIP = 4` lipsind din date ar
|
||||
insemna ca validarea n-a respins nimic real. **Ambele sunt gresite** — vezi caseta de corectie din §1.
|
||||
|
||||
Raspunsul corect, stabilit de sesiunea principala din cod: pagina de articole **nu are filtru pe
|
||||
tip** (`omodificari.vc2:14842`), deci **toate** cele 52 de documente cu `cantitate < 0` si cele 8 cu
|
||||
`cantitate = 0` trec prin `:14386` la editare, si toate ar fi respinse.
|
||||
|
||||
## Caveat pe date
|
||||
|
||||
- Schema e cea de **dezvoltare** (`MARIUSM_AUTO`), folosită și pentru rulările automate de teste
|
||||
(vezi `docs/cercetare/rec_s8_rulaje_tip4.md` — același `id_vanzare` a fost editat de un test).
|
||||
Recența (`10-AUG-26` = practic azi) poate reflecta rulări de test, nu neapărat uz curent real.
|
||||
- **Valorile negative de `TIP`** (-1, -2, -4, -6, -7, -8, -12, -13) nu corespund niciunui tip de
|
||||
document cunoscut din codul citit până acum — `VANZARI.TIP` e `NUMBER(2) NOT NULL`, dar coloana nu
|
||||
are o constrângere de semn în schema Oracle, deci acceptă tehnic valori negative. N-am cercetat
|
||||
originea lor (ar necesita căutare suplimentară în cod, în afara scopului acestei misiuni) — le
|
||||
raportez ca atare, fără să presupun că sunt date de test sau eroare.
|
||||
|
||||
## Fișiere atinse
|
||||
|
||||
Niciunul (misiune read-only). Interogări Oracle read-only pe `MARIUSM_AUTO`@`ROA_CENTRAL`, cu
|
||||
`ROLLBACK` la final pe toate scripturile.
|
||||
176
docs/cercetare/rec_fact008_pret_achizitie_zecimale.md
Normal file
176
docs/cercetare/rec_fact008_pret_achizitie_zecimale.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# FACT-008 la vending: pretul de achizitie se trimite cu 3 zecimale, stocul il tine cu 4
|
||||
|
||||
Investigatie pe productia VENDING (tunel SSH, strict citiri). Articol reclamat:
|
||||
`ZAHAR STICK BRUN 4G 200BUC/SET`, `id_articol = 5155`.
|
||||
|
||||
## Simptom
|
||||
|
||||
La salvarea facturii: `Articolul ZAHAR STICK BRUN 4G 200BUC/SET nu mai e in stoc! (FACT-008)`,
|
||||
desi articolul are 500 buc pe stoc si e in lista de preturi.
|
||||
|
||||
## Cauza
|
||||
|
||||
`pack_facturare.descarca_gestiune` cauta randul din `STOC` prin egalitate exacta pe pretul de
|
||||
achizitie (productie, `PACK_FACTURARE` body linia **7186**, ramura `ELSE`):
|
||||
|
||||
```sql
|
||||
AND A.PRET = V_PRET_ACHIZITIE
|
||||
```
|
||||
|
||||
Daca `BULK COLLECT` nu aduce niciun rand, se ridica FACT-008 (linia 7247).
|
||||
|
||||
Datele din productie:
|
||||
|
||||
| sursa | valoare |
|
||||
|---|---|
|
||||
| `STOC.PRET` (id_articol 5155, gest. 1001, cont 371, luna 8 / 2026) | **8.5586** |
|
||||
| `RUL` receptia din 06.08.2026 (id_rul 568464) | cant 500, valoare 4279.28 -> 4279.28/500 = 8.55856 -> **8.5586** |
|
||||
| `OPTIUNI.PPRET` (precizia pretului de achizitie) | **3** |
|
||||
|
||||
Clientul VFP tine pretul corect (cursorul e definit `pret_achizitie N(20, max(gnPPret,4))`, deci 4
|
||||
zecimale), dar **il serializeaza in SQL cu `gnPPret` zecimale**:
|
||||
|
||||
- `COMUN\clase\ofacturare.vc2:14073` — `frm_facturare_articole.do_scrie_articole`
|
||||
- `COMUN\clase\ofacturare.vc2:18108` — `frm_facturare_articole2.do_scrie_articole`
|
||||
- `COMUN\programe\ofacturare_stoc.prg:360` — `adauga_articol_factura_stoc`
|
||||
|
||||
```foxpro
|
||||
Alltrim(Str(poArt.pret_achizitie,18,gnPPret))
|
||||
```
|
||||
|
||||
`Str(8.5586, 18, 3)` = `8.559`. In `VANZARI_DETALII_TEMP.PRET_ACHIZITIE` (NUMBER(22,6)) ajunge
|
||||
8.559, iar predicatul `A.PRET = V_PRET_ACHIZITIE` compara 8.5586 cu 8.559 -> 0 randuri -> FACT-008.
|
||||
|
||||
Verificat pe productie:
|
||||
|
||||
```
|
||||
pret = 8.559 -> 0 randuri
|
||||
pret = 8.5586 -> 1 rand
|
||||
```
|
||||
|
||||
Ramura `tip = 3` (articole fara stoc) nu salveaza situatia: `OPTIUNI.RF_FACTURARE_FARA_STOC = 0`.
|
||||
|
||||
## Intinderea problemei
|
||||
|
||||
Nu e un caz izolat. In `STOC` exista **15 randuri** cu pret pe mai mult de 3 zecimale, toate in
|
||||
luna 8 / 2026, si **toate vor da FACT-008** la facturare:
|
||||
|
||||
```
|
||||
1070 CAFEA COVIM OROCREMA 1KG 42.3203 360 buc
|
||||
1367 PULBERE ALBA -ADAOS BAUTURI CALDE 25.3857 120 buc
|
||||
1433 ZAHAR STICK 5G 200BUC/SET 7.2072 1000 buc
|
||||
1537 CONTOR VOLUMETRIC NECTA 19.8347 30 buc
|
||||
1677 GARNITURA BUCSA MIXER NECTA NEW .4958 60 buc
|
||||
1965 MOTOREDUCTOR GRUP CAFEA NECTA 152.0667 3 buc
|
||||
2020 LAVAZZA BBE EXPERT GUSTO FORTE 55.8815 1188 buc (cants)
|
||||
2057 LAVAZZA BBE EXPERT GUSTO PIENO 61.5397 396 buc
|
||||
2261 FURTUN SILICONIC 6X9 6.6116 50 buc
|
||||
2934 BUCSA TEFLON MIXER NECTA 4.7107 60 buc
|
||||
3680 ZAHAR MARGARITAR 1 KG 3.3243 100 buc
|
||||
4282 GARNITURA NECTA OR2037 WITT 9100 1.6528 60 buc
|
||||
5133 ZAHAR STICK MXA 5G 100BUC/SET 3.6036 1000 buc
|
||||
5155 ZAHAR STICK BRUN 4G 200BUC/SET 8.5586 500 buc
|
||||
```
|
||||
|
||||
In `RUL`, preturile cu peste 3 zecimale apar **doar din 09.07.2026** incoace (20 randuri, ultimul
|
||||
14.08.2026). Inainte de aceasta data nu exista niciunul — deci comportamentul a aparut recent, pe
|
||||
calea de receptie (pret unitar = valoare / cantitate, rotunjit la scale-ul coloanei `STOC.PRET`,
|
||||
care e 4).
|
||||
|
||||
## Solutii
|
||||
|
||||
**Rapid, fara cod (productie):** `OPTIUNI.PPRET` de la 3 la 4. `Str()` incepe sa emita 4 zecimale
|
||||
si predicatul se potriveste. Aliniaza precizia de transport cu scale-ul real al `STOC.PRET`.
|
||||
|
||||
**Durabil, in cod:** in cele 3 puncte de mai sus, serializarea sa foloseasca aceeasi precizie ca
|
||||
definitia cursorului:
|
||||
|
||||
```foxpro
|
||||
Alltrim(Str(poArt.pret_achizitie,18,Max(gnPPret,4)))
|
||||
```
|
||||
|
||||
Nu se recomanda relaxarea predicatului in `descarca_gestiune` (`round(A.PRET, ...)`): pe gestiuni
|
||||
cu mai multe loturi ale aceluiasi articol ar putea potrivi randul gresit.
|
||||
|
||||
## Risc similar, neatins inca
|
||||
|
||||
`pretv_orig` se trimite cu `gnPPretV` (= 2 la vending), iar `STOC.PRETV` are scale 4, cu acelasi
|
||||
predicat de egalitate exacta (`A.PRETV = V_PRETV_ALES`). La vending toate randurile din stoc au
|
||||
`pretv = 0`, deci nu se manifesta; pe o gestiune tinuta la pret de vanzare cu pret pe 3-4 zecimale
|
||||
ar da acelasi FACT-008.
|
||||
|
||||
## De unde vin zecimalele: importul eFactura din ROACONT (confirmat)
|
||||
|
||||
Ipoteza s-a confirmat pe date. Receptia care a creat stocul articolului 5155 vine din eFactura:
|
||||
|
||||
```
|
||||
ANAF_EFACTURA_DETALII / ANAF_EFACTURA
|
||||
furnizor NAVIS PROD CONCEPT SRL, NVS 1427, 06.08.2026
|
||||
id_articol 5155, cantitate 500, PRET = 8.5586, VALOAREFARATVA = 4279.28
|
||||
```
|
||||
|
||||
Sunt **doua** surse de zecimale, ambele ocolind `gnPPret`:
|
||||
|
||||
**1. Pretul din XML-ul furnizorului are el insusi 4 zecimale.** Cazul 5155 (8.5586) si 1433
|
||||
(7.2072, NVS 1406). Se preia ca atare in `ANAF_EFACTURA_DETALII.PRET` si de acolo in NIR.
|
||||
|
||||
**2. Importul recalculeaza pretul unitar dupa distribuirea discounturilor, cu numarul de zecimale
|
||||
scris in cod (4, respectiv 6), nu cu `gnPPret`** — `frm_import_efactura.importmodifica`,
|
||||
`D:\ROA\ROACONT\COMUN\clase\anaf_efactura.vc2`:
|
||||
|
||||
| linie | cod | efect |
|
||||
|---|---|---|
|
||||
| 12727 | `Set pret = ROUND(ROUND(pret / lnTotalFaraTVAx, 6) * lnTotalCuTVAx, 6)` | 6 zecimale |
|
||||
| 12744 | `lnPret = pret - ROUND(lnDiscount / cantitate, 4)` | 4 zecimale — discount pe linie |
|
||||
| 12786 | `Set Pret = Round(Pret * lnProcent, 4)` | 4 zecimale — discount/transport global |
|
||||
|
||||
Verificare pe date, cazuri unde XML-ul avea 2 zecimale si stocul a ajuns cu 4 (deci recalculul de
|
||||
la 12744 e cel care a produs zecimalele):
|
||||
|
||||
| articol | XML: cant / pret / valoare | valoare/cant | `STOC.PRET` |
|
||||
|---|---|---|---|
|
||||
| 1677 GARNITURA BUCSA MIXER | 60 / 0.50 / 29.75 | 0.4958333 | **0.4958** |
|
||||
| 1965 MOTOREDUCTOR GRUP CAFEA | 3 / 152.07 / 456.20 | 152.066666 | **152.0667** |
|
||||
| 1537 CONTOR VOLUMETRIC | 30 / 19.83 / 595.04 | 19.834666 | **19.8347** |
|
||||
| 2934 BUCSA TEFLON MIXER | 30 / 4.71 / 141.32 | 4.7106666 | **4.7107** |
|
||||
|
||||
Mai departe, `frm_import_efactura.importmodifica` (`anaf_efactura.vc2:12810`) duce `d.Pret` in
|
||||
cursorul NIR-ului **fara nicio rotunjire**, desi toate valorile vecine din acelasi `SELECT` trec
|
||||
prin `Round(..., gnPC)`.
|
||||
|
||||
### De ce rotunjirea la import nu e solutia buna
|
||||
|
||||
Daca la import s-ar rotunji pretul la `gnPPret = 3`, pentru 5155 ar rezulta 8.559 x 500 = 4279.50,
|
||||
fata de valoarea reala a facturii, 4279.28 — o diferenta de 0.22 lei intre valoarea receptionata si
|
||||
cea care se descarca ulterior din stoc. Exact de aceea `STOC.PRET` are scale 4 si cursoarele din
|
||||
facturare sunt declarate `N(20, max(gnPPret,4))`.
|
||||
|
||||
Concluzia ramane: reparatia corecta e la **transportul din facturare** (`Str(..., 18, gnPPret)` ->
|
||||
`Max(gnPPret,4)`), sau, ca masura imediata, `OPTIUNI.PPRET = 4`. Importul eFactura e sursa
|
||||
zecimalelor, dar zecimalele in sine sunt legitime.
|
||||
|
||||
## Al patrulea punct de emisie, in afara COMUN (ROAGEST)
|
||||
|
||||
`D:\ROA\ROAGEST\Programe\ofactureaza.prg`, liniile **267, 268, 280**, construieste acelasi apel
|
||||
`pack_facturare.adauga_articol_factura(...)` cu aceeasi trunchiere (`gnPPret`, `gnPPretVal`,
|
||||
`gnPPretV`). E cod propriu ROAGEST, nu in `COMUN`, deci reparatia din `COMUN` nu-l acopera. Acelasi
|
||||
tipar, aceeasi solutie.
|
||||
|
||||
In plus, copiile `COMUN` din ROAGEST / ROACONT / ROAACNPRO sunt in urma celei din ROAFACTURARE
|
||||
(`ofacturare.vc2` are acolo liniile la 13857 / 17889) — primesc reparatia la urmatorul `svn update`
|
||||
plus recompilare.
|
||||
|
||||
## Ce mai atinge PPRET, in afara transportului
|
||||
|
||||
Nu e doar precizie de transport. Aceeasi optiune conduce:
|
||||
|
||||
- **rotunjirea pretului unitar la NIR-ul introdus manual**: `COMUN\clase\ointroduceri.vc2`, liniile
|
||||
1459, 1495, 15533, 19159 — `Round(valoare / cantitate, gnPPret)`. Deci calea manuala de receptie
|
||||
respecta deja `gnPPret`; doar importul eFactura o ocoleste. Cu `PPRET = 4`, si NIR-urile
|
||||
introduse manual vor stoca preturi cu 4 zecimale, nu 3.
|
||||
- **mastile de editare/afisare** ale pretului de achizitie: `ofacturare.vc2:3490`,
|
||||
`ofacturare_comun.vc2:1643`, `ointroduceri.vc2:10071, 16390, 19638` (`get_mask(..., gnPPret)`).
|
||||
|
||||
De aici si concluzia despre scriptul global: dupa ce intra versiunile cu `Max(gnPPret,4)`, ridicarea
|
||||
lui PPRET la 4 nu mai repara nimic, dar schimba rotunjirea receptiilor manuale si afisarea la toti
|
||||
clientii.
|
||||
451
docs/cercetare/rec_r5_editare_inline_articole.md
Normal file
451
docs/cercetare/rec_r5_editare_inline_articole.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# 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`):
|
||||
|
||||
```foxpro
|
||||
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 = ...`)
|
||||
|
||||
```foxpro
|
||||
[, 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:
|
||||
|
||||
```foxpro
|
||||
* 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`)
|
||||
|
||||
```foxpro
|
||||
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:
|
||||
|
||||
```foxpro
|
||||
Column24.ControlSource = "proc_tva", ;
|
||||
Column24.Format = "R", ;
|
||||
Column24.InputMask = "9.99", ;
|
||||
Column24.Name = "cProc_tva", ;
|
||||
Column24.ReadOnly = .F., ;
|
||||
```
|
||||
|
||||
cu evenimentul (`omodificari.vc2:16337-16343`):
|
||||
|
||||
```foxpro
|
||||
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**
|
||||
|
||||
```foxpro
|
||||
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.`
|
||||
|
||||
```foxpro
|
||||
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:
|
||||
|
||||
```foxpro
|
||||
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.**
|
||||
|
||||
```foxpro
|
||||
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.
|
||||
427
docs/cercetare/rec_r5_linii_fara_articol_contract.md
Normal file
427
docs/cercetare/rec_r5_linii_fara_articol_contract.md
Normal file
@@ -0,0 +1,427 @@
|
||||
# Diagnostic — linii fara articol pe facturi emise din CONTRACT (rate)
|
||||
|
||||
**Scop.** Doua erori pe facturi emise din contract (explicatie "CONTRACT"), pe formularul unificat
|
||||
`frm_modific2024` (`COMUN\clase\omodificari.vc2`) + `COMUN\programe\ofacturare_editare.prg`:
|
||||
|
||||
- **EROAREA 1** (la deschidere): `Field ID_ARTICOL does not accept null values`,
|
||||
`CONSTRUIESTEPROPUNERESINCRONIZARE`, linia 652 (INSERT in `agg_tvd`).
|
||||
- **EROAREA 2** (la salvare): `Linia '' nu are articol asociat.`
|
||||
|
||||
Cazul de test: factura 10/08/2026, SSS 549, ROMFAST S.R.L., explicatie "CONTRACT", contract
|
||||
"1/21.07.2020" — grid cu linia "RATA 2" (fara codmat, fara pret de achizitie) si linia "A2" (cu
|
||||
codmat 8003510000203). Doar diagnostic — nu s-a atins niciun fisier de cod.
|
||||
|
||||
## Concluzia scurta
|
||||
|
||||
**Nu e un defect de generare.** Liniile de tip "rata" dintr-un contract sunt, prin proiectare, linii
|
||||
`VANZARI_DETALII` fara articol de nomenclator — `id_articol` chiar e `NULL` in Oracle pe randul
|
||||
respectiv, iar denumirea vizibila ("RATA 2") vine din `explicatie`, nu din `nom_articole.denumire`.
|
||||
Asta era deja documentat in `docs\plan_13_unificare_formular_facturare.md:2275` inainte sa apara
|
||||
eroarea curenta — planul #13 stia despre ele, dar guarda de la salvare (`omodificari.vc2:14450`) si
|
||||
cursoarele de agregare pentru sincronizare RUL<->TVD (`ofacturare_editare.prg:617,640`) au fost scrise
|
||||
pornind de la premisa contrara, ca `id_articol` "vine mereu completat din sursa"
|
||||
(`docs\progres.md:166`). Premisa aia era gresita exact pentru liniile de rata, si cele doua erori sunt
|
||||
consecinta directa.
|
||||
|
||||
---
|
||||
|
||||
## 1. De unde vine linia fara articol — proiectare, nu defect
|
||||
|
||||
Generarea facturii din contract (butonul de facturare al `ROACONTRACTE`, `goContract`, tipurile de
|
||||
document `2/6/52` — `ofacturare_comun.prg:261-297`) construieste liniile prin
|
||||
`creeaza_facturacrs` (`ofacturare_comun.prg:1793`, cursorul `crsfacturacrs`, camp
|
||||
`id_articol N(20) null` — **explicit nullable**) si le populeaza prin `prelucreaza_facturacrs`
|
||||
(`ofacturare_comun.prg:1836-1840`):
|
||||
|
||||
```
|
||||
Insert Into (tcCursorDestinatie) (id_articol, ...) ;
|
||||
SELECT CAST(IIF(TYPE(tcCursorSursa+".id_articol")='U',0,id_articol) as N(20)) as id_articol, ...
|
||||
```
|
||||
|
||||
`TYPE(...)='U'` verifica daca **exista coloana** `id_articol` in cursorul sursa (0 doar cand lipseste
|
||||
complet), nu daca valoarea e NULL — deci daca sursa are coloana `id_articol` si pe randul de rata ea
|
||||
e NULL, NULL trece mai departe neschimbat, nu devine 0.
|
||||
|
||||
Structura de rate a contractului insasi confirma asta — cursorul istoric pentru rate
|
||||
(`ofacturare_comun.prg:1905-1908`, comentat, dar pastrat ca document al formei) nu are deloc coloana
|
||||
`id_articol`:
|
||||
|
||||
```
|
||||
*!* Create Cursor crsfactura(id_rata N(20), id_temp N(20), den_rata c(100), ..., denumire c(100), ...)
|
||||
```
|
||||
|
||||
**Deja documentat inainte de eroarea curenta.** Cercetarea S4 din planul #13 a stabilit exact acelasi
|
||||
lucru cand a analizat `cursor_contract`:
|
||||
|
||||
> `docs\plan_13_unificare_formular_facturare.md:2273-2275`:
|
||||
> „`cursor_contract` produce deja doua cursoare — `V_CURSOR` (`crsarticole`, prin delegare la
|
||||
> `cursor_preturi`) si `V_CURSOR2` (`crsarticole1`, articole **sau** rate). Filtrarea se aplica curat
|
||||
> doar pe jumatatea `crsarticole`; **randurile de rata n-au `id_articol`** si raman needitate."
|
||||
|
||||
Concluzie punct 1: **legitim, prin proiectare**. Randul de rata reprezinta o transa de plata dintr-un
|
||||
contract (esalonare), nu un articol din nomenclator — nu exista ce `id_articol` sa i se puna, si codul
|
||||
de generare stie asta de multa vreme.
|
||||
|
||||
## 2. Ce accepta baza de date
|
||||
|
||||
**Confirmat pe Oracle** (`MARIUSM_AUTO@ROA_CENTRAL`, interogare proprie `ALL_TAB_COLUMNS`, SELECT
|
||||
strict, plus datele masurate de team-lead pe `VANZARI`/`VANZARI_DETALII`):
|
||||
|
||||
| Coloana | Tip | Nullable | Observatie |
|
||||
|---|---|---|---|
|
||||
| `ID_ARTICOL` | NUMBER | **Y** | are FK `FK_VANZARE_DET002 -> NOM_ARTICOLE.PK_ARTICOL`; in `NOM_ARTICOLE` **nu exista niciun articol cu `id_articol = 0`** (count = 0) |
|
||||
| `PRET` | NUMBER | **N** | singura coloana NOT NULL din setul verificat |
|
||||
| `PRET_CU_TVA` | NUMBER | Y | flag 0/1, nu pret |
|
||||
| `PRET_ACHIZITIE` | NUMBER | Y | |
|
||||
| `PROC_TVAV` | NUMBER | Y | multiplicator TVA (ex. 1.21), nu procent |
|
||||
| `DISCOUNT_UNITAR` | NUMBER | Y | |
|
||||
| `CANTITATE` | NUMBER | Y | |
|
||||
| `SERIE` / `LOT` / `EXPLICATIE` | VARCHAR2 | Y | |
|
||||
| `ID_VANZARE_SET` / `ID_GESTIUNE` | NUMBER | Y | |
|
||||
|
||||
Confirmare directa pe date reale (interogare team-lead): factura `VANZARI.id_vanzare=1055` (SSS 549,
|
||||
tip=2, `id_ctr=234`), linia `id_vanzare_det=1603` are efectiv `id_articol = NULL`,
|
||||
`id_gestiune = NULL`, `pret_achizitie = NULL`, `explicatie = 'RATA 2'`, `cantitate=1`, `pret=100`,
|
||||
`proc_tvav=1.21`, `id_valuta=3` — deja salvata asa, deci coloana accepta NULL in productie (nu doar
|
||||
teoretic in DDL). In toata baza exista deja **20 de linii active** (`sters=0`) cu `id_articol IS
|
||||
NULL`, din 1124 in total — starea nu e un caz izolat.
|
||||
|
||||
Concluzie punct 2: `ID_ARTICOL` e **nullable, cu FK** catre `NOM_ARTICOLE`. Asta e critic pentru
|
||||
reparatie (punctul 6, mai jos): daca s-ar scrie `0` in loc de `NULL` pe o linie fara articol, FK-ul ar
|
||||
respinge scrierea (`ORA-02291`), pentru ca `id_articol = 0` nu exista in `NOM_ARTICOLE`.
|
||||
|
||||
## 3. De unde se incarca `tvd` si de ce `denumire` iese goala
|
||||
|
||||
`tvd` se incarca prin `IncarcaArticoleFactura` (`ofacturare_editare.prg:305-`), din view-ul
|
||||
`VVANZARI_ARTICOLE`, definit recent chiar pentru acest formular:
|
||||
|
||||
```sql
|
||||
-- D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql:7-34
|
||||
CREATE OR REPLACE VIEW VVANZARI_ARTICOLE AS
|
||||
select vd.id_vanzare, ..., vd.id_articol, ..., vd.explicatie, ...,
|
||||
na.denumire, na.codmat, ng.nume_gestiune, nv.nume_val
|
||||
from vanzari_detalii vd
|
||||
left join nom_articole na on na.id_articol = vd.id_articol
|
||||
...
|
||||
```
|
||||
|
||||
Spre deosebire de `fact_vfacturi_detalii` (punctul 2), **acest view nou nu are fallback** pe
|
||||
`vd.explicatie` cand `vd.id_articol` e NULL — `na.denumire` vine direct dintr-un `LEFT JOIN` pe
|
||||
`nom_articole`, iar cand `id_articol` e NULL, join-ul nu gaseste nimic si `denumire` iese **NULL**.
|
||||
Cursorul `tvd` insusi e declarat cu `id_articol N(20) NULL` (`ofacturare_editare.prg:278`,
|
||||
`omodificari.vc2:14769`), asa ca incarcarea nu crapa aici — doar `denumire` ramane goala pe randul de
|
||||
rata.
|
||||
|
||||
Asta explica exact mesajul din EROAREA 2: `Alltrim(Nvl(denumire,''))` (`omodificari.vc2:14450`) da
|
||||
sir gol pentru ca `tvd.denumire` e cu adevarat NULL pe acel rand, nu pentru ca s-a gasit randul gresit.
|
||||
In grid, textul vizibil "RATA 2" **nu vine din coloana Denumire** (`Column1.ControlSource =
|
||||
"tvd.denumire"`, `omodificari.vc2:12349`), ci din coloana **Explicatie** de mai la dreapta
|
||||
(`Column13.ControlSource = "tvd.explicatie"`, `omodificari.vc2:12444`) — coloana Denumire e goala pe
|
||||
acel rand, exact ca in mesajul de eroare.
|
||||
|
||||
Concluzie punct 3: `denumire` **poate** iesi NULL pe linia de rata, si chiar iese — cauza e absenta
|
||||
fallback-ului pe `explicatie` in `VVANZARI_ARTICOLE`, spre deosebire de view-ul mai vechi
|
||||
`fact_vfacturi_detalii` care il are.
|
||||
|
||||
## 4. Cine a pus garda `Nvl(id_articol,0) = 0` si de ce
|
||||
|
||||
Garda de la `omodificari.vc2:14450` (in `frm_modific2024.inainte_de_do_termin`) a intrat in
|
||||
`COMUN` prin commit-ul `1c42ae0` — *"#6 editare factura emisa: S5 - scrierea sumelor editate in
|
||||
Oracle"*, 10.08.2026 — inainte de S4 (cautarea articolelor pe server) si inainte de decizia 18/S4b
|
||||
despre liniile sintetice. Nu exista niciun commit ulterior care sa modifice acel `IF`.
|
||||
|
||||
**Nu era gandita ca plasa pentru rate.** Documentatia proprie a proiectului o descrie explicit ca
|
||||
ramura considerata **imposibil de declansat** pe fluxul normal:
|
||||
|
||||
> `docs\progres.md:157,166`:
|
||||
> | `:14402` | `Nvl(id_articol,0) = 0` | blocheaza |
|
||||
> ...
|
||||
> „Din cele trei, `Isnull(pret)` e ramura moarta si `id_articol` vine **mereu completat din sursa**,
|
||||
> deci singura care ar fi putut pica e chiar cea de cantitate."
|
||||
|
||||
Asta e in contradictie directa cu ce arata investigatia S4 din acelasi plan (`plan_13:2275`, punctul 1
|
||||
de mai sus), scrisa in aceeasi fereastra de timp — **liniile de rata nu au `id_articol` de la sursa**.
|
||||
Contradictia nu a fost observata pentru ca cele doua fire de lucru (S5/garda de salvare, si S4/cautarea
|
||||
articolelor din `cursor_contract`) nu s-au intersectat pana acum, pe date reale de contract cu rate.
|
||||
|
||||
Concluzie punct 4: garda **nu** a fost pusa pentru ca cineva stia ca `VANZARI_DETALII.ID_ARTICOL` e
|
||||
NOT NULL in Oracle (ar fi contrazis chiar codul de generare din `ofacturare_comun.prg`, punctul 1) —
|
||||
a fost pusa ca validare generica de continut ("linia trebuie sa aiba un articol"), pe premisa (falsa
|
||||
pentru rate) ca `id_articol` vine intotdeauna populat. E o garda prea stricta pentru liniile de rata,
|
||||
nu o reflectare a unei constrangeri de baza de date.
|
||||
|
||||
## 5. Ce mai crapa in aval, cu `id_articol` NULL pe un rand `tvd`/`trul`
|
||||
|
||||
Lista completa, `fisier:linie`, a locurilor care folosesc `id_articol` fara `Nvl` sau il compara cu
|
||||
`=` (deci sensibile la NULL):
|
||||
|
||||
| Loc | Cod | Efect cu `id_articol` NULL |
|
||||
|---|---|---|
|
||||
| `ofacturare_editare.prg:617` | `CREATE CURSOR agg_rul (id_articol N(20), ...)` | camp declarat **fara** `NULL` — pe hartie acelasi tipar ca la `agg_tvd`, dar **confirmat inaccesibil in practica** (vezi punctul 7: `RUL.ID_ARTICOL` e `NOT NULL` in Oracle, 0 randuri active cu NULL) |
|
||||
| `ofacturare_editare.prg:626,630-631` | `LOCATE FOR id_articol = trul.id_articol` apoi `INSERT INTO agg_rul ... VALUES (trul.id_articol, ...)` | fara risc practic — `trul.id_articol` nu poate fi NULL (punctul 7) |
|
||||
| `ofacturare_editare.prg:640,647,651-652` | `CREATE CURSOR agg_tvd (id_articol N(20), ...)` + `INSERT INTO agg_tvd ... VALUES (tvd.id_articol, ...)` | **cauza directa a EROAREA 1** — camp NOT NULL, valoare NULL |
|
||||
| `ofacturare_editare.prg:660-663` | `SELECT id_articol FROM agg_rul UNION SELECT id_articol FROM agg_tvd INTO CURSOR crs_articole_unite` | **verificat empiric (punctul 7)**: `UNION` trateaza NULL=NULL ca echivalent la deduplicare — oricate randuri NULL ar aduce `agg_tvd`, `crs_articole_unite` primeste **un singur** rand NULL |
|
||||
| `ofacturare_editare.prg:671,687` | `LOCATE FOR id_articol = crs_articole_unite.id_articol` (o data pe `agg_rul`, o data pe `agg_tvd`) | **verificat empiric (punctul 7), corecteaza ipoteza initiala**: `LOCATE FOR camp = NULL` **nu gaseste niciodata**, in VFP, nici cand campul chiar contine NULL pe randul cautat — nu doar cand valorile difera. Efectul nu e o pereche falsa „Adaugare”+„Semnalare” (ipoteza initiala, neconfirmata) — e mai simplu si mai tacut: randul NULL din `crs_articole_unite` iese cu `llGasitSursa=.F.` **si** `llGasitTinta=.F.` pe ambele ramuri, cade in `OTHERWISE` cu 0=0, si **nu genereaza nicio linie in `propunere_sincronizare`** — dispare complet din sincronizare, fara eroare, fara semnalare |
|
||||
| `ofacturare_editare.prg:849,887,923` | `LOCATE FOR id_articol = m.tnIdArticol` in `AplicaModificareTvd`, `AplicaAdaugareTvd`, `AplicaModificareTrul` | irelevant in practica daca se aplica reparatia recomandata la punctul 7 (Varianta B) — `propunere_sincronizare` nu mai ajunge sa contina randuri cu `id_articol` NULL, deci aceste functii nu sunt niciodata chemate cu `tnIdArticol` NULL |
|
||||
| `omodificari.vc2:14450` | `IF Nvl(id_articol,0) = 0` | **cauza directa a EROAREA 2** — garda generica, prea stricta pentru rate |
|
||||
|
||||
Concluzie punct 5: reparatia nu se opreste la `agg_tvd` (linia care a crapat primul) — `agg_rul` are
|
||||
aceeasi lipsa de `NULL` pe declaratie, dar dovedit inaccesibila (punctul 7). Mecanismul de potrivire
|
||||
pe `id_articol =` din sincronizarea RUL<->TVD (`:671`, `:687`) e **verificat empiric ca nu functioneaza
|
||||
pentru NULL**, cu efect de disparitie tacuta din propunere, nu de eroare — vezi analiza completa si
|
||||
reparatia recomandata la punctul 7.
|
||||
|
||||
## 6. `Nvl(...,0)` la scriere in `ScrieArticoleFacturaEditate` — campuri care distrug informatie
|
||||
|
||||
Chiar daca EROAREA 1 si EROAREA 2 s-ar repara (cursoare + garda), `ScrieArticoleFacturaEditate`
|
||||
(`ofacturare_editare.prg`) tot ar strica un rand de rata la prima salvare reusita, pentru ca **atat
|
||||
UPDATE-ul liniilor pastrate (`:505-517`), cat si INSERT-ul liniilor noi (`:535-547`)** trec mai multe
|
||||
campuri prin `Nvl(camp,0)` inainte sa le scrie in Oracle — convertind orice NULL legitim in `0`
|
||||
literal:
|
||||
|
||||
```
|
||||
-- UPDATE (linii existente), ofacturare_editare.prg:505-510
|
||||
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))) + ;
|
||||
...
|
||||
|
||||
-- INSERT (linii noi), ofacturare_editare.prg:535-546
|
||||
lcSql = [insert into vanzari_detalii (id_vanzare, id_articol, cantitate, pret, pret_cu_tva, proc_tvav, discount_unitar, ] + ;
|
||||
[id_gestiune, cont, id_valuta, id_jtva_coloana, serie, explicatie, taxcode, lot, pret_achizitie, id_utils, dataoras) values (] + ;
|
||||
Alltrim(Str(m.tnIdVanzare)) + [,] + Alltrim(Str(Nvl(id_articol,0))) + [,] + Alltrim(Str(Nvl(cantitate,0),18,3)) + [,] + ;
|
||||
Alltrim(Str(Nvl(pret,0),18,4)) + [,] + Alltrim(Str(Nvl(pret_cu_tva,0))) + [,] + Alltrim(Str(Nvl(proc_tvav,0),18,4)) + [,] + ;
|
||||
Alltrim(Str(Nvl(discount_unitar,0),18,4)) + [,] + Iif(Isnull(id_gestiune),[NULL],Alltrim(Str(id_gestiune))) + [,] + ;
|
||||
...
|
||||
Alltrim(Str(Nvl(pret_achizitie,0),18,4)) + [,] + Alltrim(Str(gnIdUtil)) + [, sysdate)]
|
||||
```
|
||||
|
||||
Observatie de proiectare: **`id_gestiune`, `id_valuta`, `id_jtva_coloana`, `taxcode`, `cont`, `serie`,
|
||||
`explicatie`, `lot` sunt deja tratate corect**, cu `Iif(Isnull(...),[NULL],...)` — modelul corect
|
||||
exista deja in acelasi bloc de cod, doar ca nu s-a aplicat si celor sase campuri de mai jos.
|
||||
|
||||
Evaluare camp cu camp, cu nullabilitatea confirmata la punctul 2:
|
||||
|
||||
| Camp | Nullable in Oracle | `Nvl(...,0)` e corect? | Motiv |
|
||||
|---|---|---|---|
|
||||
| **`id_articol`** | Y, **cu FK** spre `NOM_ARTICOLE` | **NU — critic** | `0` nu exista in `NOM_ARTICOLE` (count=0) — pe o linie de rata cu `id_articol` real NULL, scrierea ar da **`ORA-02291`** (violare FK), nu doar ar corupe tacut o valoare. Chiar daca EROAREA 2 s-ar repara la nivel de garda VFP, salvarea tot ar crapa aici, doar cu un mesaj Oracle mai putin clar decat "nu are articol asociat". **Singurul din lista care produce eroare dura, nu doar corupere tacuta.** |
|
||||
| **`pret_achizitie`** | Y | **NU** | Pe randul real `id_vanzare_det=1603` e deja NULL in productie — "cost de achizitie necunoscut/nu se aplica" (rata n-are cost de achizitie, nu e marfa). `Nvl(...,0)` il transforma in "cost de achizitie efectiv zero", care e o valoare falsa pentru orice calcul de marja/profit facut ulterior direct din `VANZARI_DETALII` (in afara VFP) |
|
||||
| **`proc_tvav`** | Y | **Probabil nu, cu rezerva** | E un multiplicator TVA (1.21, nu 21%) — `0` nu inseamna "fara TVA" in aceasta conventie, ci ar corupe orice calcul care-l inmulteste (baza * 0 = 0). Pe randul de test `proc_tvav` e deja completat (1.21), deci `Nvl` nu se declanseaza acolo — dar daca exista/ar exista vreun rand cu `proc_tvav` NULL, scrierea lui ca `0` e o valoare periculoasa, nu neutra. **NEVERIFICAT**: daca exista azi randuri reale cu `proc_tvav` NULL (n-am interogat) |
|
||||
| **`discount_unitar`** | Y | **Risc scazut** | NULL si 0 sunt aproape echivalente semantic ("fara discount") — conversia schimba tipul valorii, nu sensul ei practic. Recomandat de aliniat la tiparul `Isnull` din acelasi bloc, mai mult pentru consistenta decat pentru un bug observat |
|
||||
| **`cantitate`** | Y | **Risc scazut, deja filtrat in amonte** | Garda `omodificari.vc2:14386` (`Nvl(cantitate,0) <= 0` -> blocheaza cu "are cantitatea 0") ruleaza **inaintea** lui `ScrieArticoleFacturaEditate` si opreste salvarea pe orice rand cu cantitate NULL sau <= 0. Pana la aceasta functie, `cantitate` e deja garantat non-NULL si non-zero pe calea normala — `Nvl(...,0)` de aici e defensiv, nu activ distructiv |
|
||||
| **`pret`** | **N (NOT NULL)** | **DA — corect** | Coloana Oracle nu accepta NULL; garda `omodificari.vc2:14394` (`Isnull(pret)` -> blocheaza) opreste deja NULL inainte de scriere. `Nvl(pret,0)` e aici plasa de siguranta potrivita pentru o coloana NOT NULL, nu o corupere |
|
||||
| **`pret_cu_tva`** | Y | **Risc scazut** | E flag boolean 0/1 (comentariu `:501` in acelasi fisier: "pret_cu_tva e flag (0/1), nu pret"), nu o valoare cu semnificatie de "lipsa" — `Nvl(...,0)` echivaleaza NULL cu "fara TVA in pret", o valoare implicita rezonabila pentru un flag |
|
||||
|
||||
Concluzie punct 6: din cele sase campuri, **doar `id_articol` produce o eroare Oracle dura** (FK) daca
|
||||
nu se repara — e blocajul real, dincolo de garda VFP. **`pret_achizitie` corupe tacut date reale deja
|
||||
existente** (randul 1603 chiar are NULL azi) fara sa arunce nicio eroare. `proc_tvav` e risc teoretic,
|
||||
neconfirmat pe date. Restul (`discount_unitar`, `cantitate`, `pret_cu_tva`) sunt scrieri defensive
|
||||
fara efect practic distructiv, iar `pret` e deja corect (coloana NOT NULL + garda VFP dedicata).
|
||||
|
||||
## 7. Sarim liniile fara articol la sursa, sau reparam matching-ul cu NULL — analiza cu dovada VFP
|
||||
|
||||
**Date suplimentare masurate de team-lead pe Oracle**: `RUL.ID_ARTICOL` e **NOT NULL** in Oracle
|
||||
(`all_tab_columns.nullable = 'N'`), si exista **0** randuri `RUL` active cu `id_articol IS NULL`.
|
||||
Documentul de test (SSS 549, cod 1140920) are **un singur rulaj**: `RUL.id_rul=10947,
|
||||
id_articol=4294507173, cant=0, cante=1, pretvtva=121, id_tip_rulaj=0` — si **doua** linii `tvd`
|
||||
(rata fara articol + articolul 4294507173). Deci pe acest document, `agg_rul` n-ar primi niciodata
|
||||
un rand NULL — doar `agg_tvd` (din `tvd`) aduce randul de rata.
|
||||
|
||||
**Concluzie directa**: rândul `ofacturare_editare.prg:617` (`agg_rul` declarat fara `NULL`) e nesigur
|
||||
pe hartie, dar **dovedit inaccesibil** — `trul`, incarcat din `RUL`, nu poate aduce NULL, constrangerea
|
||||
Oracle il blocheaza la sursa. Merita uniformizat pentru consistenta cu `tvd`/`agg_tvd` (defensiv,
|
||||
cost zero), dar nu e un bug activ.
|
||||
|
||||
### Testat empiric, nu presupus
|
||||
|
||||
Am rulat un test izolat in VFP (`vfp9.exe -A -T`, fara Oracle, fara formulare), reproducand exact
|
||||
structura din `ConstruiestePropunereSincronizare` — `test_null_locate_union.prg`, pastrat in
|
||||
scratchpad, log complet in `test_null_locate_union.log` (acelasi director). Rezultate:
|
||||
|
||||
| Test | Ce verifica | Rezultat masurat |
|
||||
|---|---|---|
|
||||
| 1 | `SELECT id_articol FROM agg_rul UNION SELECT id_articol FROM agg_tvd` cu 1 rand NULL in `agg_tvd` | **2 randuri** in rezultat: unul NULL, unul cu valoare — `UNION` pastreaza NULL-ul, nu-l pierde |
|
||||
| 2 | Acelasi UNION, dar cu **2** randuri NULL in `agg_tvd` (simuleaza doua rate pe acelasi document) | Tot **2 randuri** in rezultat — `UNION` trateaza cele doua NULL-uri **ca echivalente** la deduplicare (comportament SQL standard de grupare, diferit de semantica lui `=`) |
|
||||
| 3 | `LOCATE FOR id_articol = m.lnTest`, cu `m.lnTest = .NULL.`, pe un cursor care chiar are un rand cu `id_articol` NULL | **`FOUND() = .F.`** — nu gaseste randul, desi acesta exista |
|
||||
| 4 | `LOCATE FOR id_articol = crs_articole_unite.id_articol`, exact tiparul de la `:671`/`:687`, cu ambele parti NULL | **`FOUND() = .F.`** — confirma acelasi lucru intre doua cursoare, nu doar cu un memvar |
|
||||
|
||||
**Interpretare, aplicata pe `ConstruiestePropunereSincronizare`**: daca EROAREA 1 s-ar repara doar prin
|
||||
adaugarea `NULL` la declaratiile `agg_rul`/`agg_tvd` (Varianta A propusa initial), randul NULL din
|
||||
`crs_articole_unite` (Test 1/2 arata ca **exista**, indiferent de cate rate sunt) ar ajunge in bucla
|
||||
principala de la `:665-773`. Acolo, `LOCATE FOR id_articol = crs_articole_unite.id_articol` **pe
|
||||
`agg_rul` si pe `agg_tvd`, ambele** (Test 3/4) **nu gaseste nimic**, chiar daca `agg_tvd` chiar contine
|
||||
randul cautat. Rezultatul: `lnNrandRul=0` si `lnNrandTvd=0` raman la valorile implicite, deci
|
||||
`llGasitSursa=.F.` si `llGasitTinta=.F.` **pe ambele ramuri** — nu se potriveste niciun `CASE` din
|
||||
`DO CASE` (nici „Adaugare", nici „Semnalare"), cade in `OTHERWISE` cu `0=0`, si **liniei de rata nu i
|
||||
se genereaza nicio linie in `propunere_sincronizare`**. **Corectez aici ipoteza din punctul 5 al
|
||||
raportului initial** („pereche falsa Adaugare+Semnalare") — nu e o pereche falsa, e o **disparitie
|
||||
tacuta**, mai greu de observat: randul nu genereaza nici eroare, nici avertizare, doar lipseste din
|
||||
propunere, din `SemnaturaDivergenteSincronizare` (`omodificari.vc2:14839`, filtreaza pe aceleasi trei
|
||||
actiuni) si deci din dialogul de sincronizare.
|
||||
|
||||
### Raspunsul la intrebare: Varianta B (excludere la sursa) e cea corecta
|
||||
|
||||
Team-lead a propus alternativa: `ConstruiestePropunereSincronizare` sare complet peste liniile `tvd`
|
||||
(si, defensiv, `trul`) fara `id_articol`, cu `SCAN FOR Nvl(sters,0) <> 1 AND !Isnull(id_articol)` in
|
||||
loc de `SCAN FOR Nvl(sters,0) <> 1` la `:619` (trul->agg_rul) si `:642` (tvd->agg_tvd).
|
||||
|
||||
**E varianta corecta**, din trei motive, toate confirmate mai sus:
|
||||
|
||||
1. **`id_articol` e chiar cheia de comparatie a mecanismului** — comentariul din capul fisierului
|
||||
(`ofacturare_editare.prg:14`: "agregate pe id_articol, in ambele sensuri") o spune explicit. O
|
||||
linie fara cheie nu poate, prin definitie, sa aiba corespondent — nu e un caz special de tratat cu
|
||||
grija, e un rand care nu apartine acestui mecanism.
|
||||
2. **Rezultatul practic al Variantei B e identic cu ce se intampla azi accidental prin Varianta A**
|
||||
(randul de rata nu ajunge in `propunere_sincronizare`, deci nu apare in dialog, nu declanseaza
|
||||
„Aplica" pe el) — dar **prin filtrare explicita**, nu prin efectul secundar, neverificat de nimeni
|
||||
pana acum, al lui `LOCATE FOR ... = NULL`. Daca cineva „repara" mai tarziu comparatiile cu
|
||||
`Nvl(id_articol,-1) = Nvl(...,-1)` (Varianta A din reparatia initiala, sau orice alt developer care
|
||||
descopera si "repara" fara sa stie de aceasta analiza), comportamentul s-ar schimba brusc de la
|
||||
"dispare tacut" la "participa la matching" — cu riscul semnalat deja in reparatia initiala, ca mai
|
||||
multe rate distincte de pe acelasi document s-ar agrega/compara laolalta (confirmat acum de Testul
|
||||
2: `UNION` le trateaza deja ca un singur grup NULL). Varianta B evita complet acest risc, pentru ca
|
||||
liniile de rata nici nu ajung sa fie agregate.
|
||||
3. **Varianta B rezolva si EROAREA 1 la radacina**, fara sa mai fie nevoie sa se adauge `NULL` la
|
||||
declaratiile `agg_rul`/`agg_tvd` — daca linia cu `id_articol` NULL nu mai intra in bucla de `SCAN`
|
||||
care face `INSERT INTO agg_tvd`, nu mai exista nicio incercare de a insera NULL intr-un camp NOT
|
||||
NULL. (Adaugarea `NULL` la declaratii ramane totusi recomandata, ca plasa de siguranta ieftina,
|
||||
independent de asta.)
|
||||
|
||||
**Efect pe cazul concret SSS 549, cu Varianta B**: `agg_rul` are 1 rand (`4294507173`), `agg_tvd` are
|
||||
1 rand (`4294507173`, linia de rata fiind sarita la `SCAN`). `crs_articole_unite` are 1 rand. Randul
|
||||
de rata nu apare nicaieri in `propunere_sincronizare` — nici „Adaugare", nici „Semnalare", nici „N-A".
|
||||
La `SemnaturaDivergenteSincronizare`, semnatura nu contine nimic despre rata — deschiderea/inchiderea
|
||||
dialogului de sincronizare nu e afectata de ea, la deschidere sau la salvare. La „Aplica" (daca
|
||||
utilizatorul il apasa pentru articolul real), `AplicaModificareTrul`/`AplicaAdaugareTvd`/
|
||||
`AplicaModificareTvd` nu sunt niciodata chemate cu `tnIdArticol` NULL, pentru ca randul de rata nu
|
||||
ajunge in `propunere_sincronizare` ca sa fie scanat de `AplicaSincronizareArticole`
|
||||
(`ofacturare_editare.prg:785-822`, bucla `SCAN FOR Inlist(Alltrim(actiune), 'Modificare',
|
||||
'Adaugare')` la `:802`) — deci ingrijorarea din punctul 5 despre `:849/887/923` nu se mai
|
||||
materializeaza.
|
||||
|
||||
---
|
||||
|
||||
## Reparatie propusa (NU aplicata)
|
||||
|
||||
### EROAREA 1 — cursoarele de agregare
|
||||
|
||||
**Recomandare finala (dupa analiza si testul din punctul 7): Varianta B — exclude liniile fara
|
||||
`id_articol` la sursa**, in `ConstruiestePropunereSincronizare`:
|
||||
|
||||
```
|
||||
SELECT trul
|
||||
SCAN FOR Nvl(sters,0) <> 1 AND !Isnull(id_articol)
|
||||
...
|
||||
```
|
||||
(`ofacturare_editare.prg:619`, azi `SCAN FOR Nvl(sters,0) <> 1`)
|
||||
|
||||
```
|
||||
SELECT tvd
|
||||
SCAN FOR Nvl(sters,0) <> 1 AND !Isnull(id_articol)
|
||||
...
|
||||
```
|
||||
(`ofacturare_editare.prg:642`, azi `SCAN FOR Nvl(sters,0) <> 1`)
|
||||
|
||||
E coerent cu faptul ca `id_articol` e chiar cheia de comparatie a mecanismului (comentariul din capul
|
||||
fisierului, `:14`) — o linie fara cheie n-are cum sa aiba corespondent, la fel cum deja se trateaza
|
||||
separat cazul "Articol nestocat, fara corespondent in rulaje" (`:730-732`). Filtrul de mai sus **repara
|
||||
si EROAREA 1** — nu mai ajunge nicio valoare NULL la `INSERT INTO agg_tvd`/`agg_rul`, deci declaratiile
|
||||
cursoarelor n-ar mai avea nevoie sa accepte `NULL` ca sa nu crape. Recomandat totusi, ca plasa de
|
||||
siguranta ieftina si pentru consistenta cu `tvd`:
|
||||
|
||||
```
|
||||
CREATE CURSOR agg_rul (id_articol N(20) NULL, denumire C(100), codmat C(30), cant N(12,3), val N(16,4), nrand I)
|
||||
CREATE CURSOR agg_tvd (id_articol N(20) NULL, denumire C(100), codmat C(30), cant N(12,3), val N(16,4), nrand I, in_stoc I)
|
||||
```
|
||||
(`ofacturare_editare.prg:617`, `:640`)
|
||||
|
||||
**Varianta alternativa, respinsa**: doar adauga `NULL` la declaratii si repara comparatiile `id_articol
|
||||
= X` cu o forma NULL-safe (`Nvl(id_articol,-1) = Nvl(m.tnIdArticol,-1)`, acelasi tipar folosit in
|
||||
`ofacturare_comun.prg:1334,1349`). Tehnic ar functiona, dar **verificat empiric (punctul 7, Test 2)**:
|
||||
`UNION`-ul deja trateaza toate liniile NULL ca **un singur grup** — cu aceasta varianta, doua sau mai
|
||||
multe rate distincte de pe acelasi document s-ar agrega si compara laolalta, ca si cum ar fi acelasi
|
||||
"articol". Nu aduce niciun beneficiu fata de Varianta B (rezultatul pentru randul de rata tot nu
|
||||
trebuie sa apara in sincronizare), doar risc suplimentar — de aceea nu e recomandata.
|
||||
|
||||
### EROAREA 2 — garda de la salvare
|
||||
|
||||
Trei variante, e o decizie de produs:
|
||||
|
||||
- **Varianta A — restrange garda la liniile cu articol real posibil**: cere articol doar cand linia
|
||||
nu e de tip rata — de exemplu cand `id_vanzare_set = 0` **si** linia are `pret_achizitie` (semn ca
|
||||
vine dintr-un flux de articole reale), sau invers, sare garda cand `Isnull(id_articol) AND
|
||||
!Empty(explicatie)` (semnul unei linii "sintetice" descrise prin `explicatie`, ca ratele).
|
||||
- **Varianta B — scoate garda complet** pentru randuri cu `id_articol` NULL de la incarcare (nu
|
||||
adaugate manual in sesiunea curenta) — presupune sa distingi in `tvd` intre "NULL de la Oracle" si
|
||||
"NULL pentru ca utilizatorul a adaugat un rand si n-a ales inca un articol" (al doilea caz chiar
|
||||
trebuie blocat).
|
||||
- **Varianta C — transforma in intrebare (confirmare), nu blocaj** — la fel ca gardul de pret de
|
||||
achizitie 0 de doua linii mai jos (`:14458-14464`), las utilizatorul sa decida daca salveaza cu
|
||||
randul fara articol.
|
||||
|
||||
Recomandarea de continut (nu de aplicat acum): **Varianta A**, pentru ca pastreaza garda utila pe
|
||||
cazul ei real (rand adaugat manual din nomenclator, fara articol ales din greseala), fara sa oblige
|
||||
verificarea de tip "e linie de rata veche" peste tot.
|
||||
|
||||
### Scrierea in Oracle — `ScrieArticoleFacturaEditate` (punctul 6)
|
||||
|
||||
Minim, pe ambele blocuri (UPDATE `:505-510`, INSERT `:535-546`), aliniaza `id_articol` si
|
||||
`pret_achizitie` la tiparul `Iif(Isnull(...),[NULL],...)` deja folosit pentru `id_gestiune`/
|
||||
`id_valuta`/`id_jtva_coloana`/`taxcode` in acelasi bloc:
|
||||
|
||||
```
|
||||
[, id_articol = ] + Iif(Isnull(id_articol),[NULL],Alltrim(Str(id_articol))) + ;
|
||||
...
|
||||
[, pret_achizitie = ] + Iif(Isnull(pret_achizitie),[NULL],Alltrim(Str(pret_achizitie,18,4))) + ;
|
||||
```
|
||||
|
||||
**`id_articol` e obligatoriu de facut** — altfel, chiar dupa ce EROAREA 1 si EROAREA 2 s-ar repara,
|
||||
prima salvare a unei facturi cu linie de rata ar cadea pe `ORA-02291` (FK spre `NOM_ARTICOLE`, care
|
||||
n-are rand cu `id=0`). `pret_achizitie` e recomandat, ca sa nu se scrie tacut "cost de achizitie 0" pe
|
||||
un rand care azi are NULL. `proc_tvav` — de decis dupa ce se clarifica NEVERIFICAT-ul de mai jos (daca
|
||||
poate fi NULL pe un rand real, acelasi tipar se aplica si lui). `discount_unitar`, `cantitate`,
|
||||
`pret_cu_tva` — opional, doar pentru consistenta cu restul blocului, fara bug observat.
|
||||
|
||||
### `VVANZARI_ARTICOLE` — fallback pe `explicatie`
|
||||
|
||||
Independent de cele doua erori, view-ul (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`)
|
||||
ar putea capata acelasi fallback ca `fact_vfacturi_detalii`:
|
||||
`NVL2(vd.id_articol, na.denumire, vd.explicatie) as denumire` — ca sa nu mai iasa coloana Denumire
|
||||
goala pe randurile de rata (in prezent doar coloana Explicatie arata ceva, iar Denumire ramane goala
|
||||
in grid, ceea ce probabil nu era intentionat cand s-a proiectat view-ul).
|
||||
|
||||
## NEVERIFICAT
|
||||
|
||||
- **Constrangerea DDL exacta pe `VANZARI_DETALII.ID_ARTICOL` — REZOLVAT.** Confirmat direct pe Oracle
|
||||
(`ALL_TAB_COLUMNS`, `MARIUSM_AUTO@ROA_CENTRAL`): `ID_ARTICOL` e `NULLABLE = Y`, cu FK
|
||||
`FK_VANZARE_DET002` spre `NOM_ARTICOLE.PK_ARTICOL`; `NOM_ARTICOLE` n-are niciun rand cu `id_articol =
|
||||
0`. Vezi tabelul din punctul 2. (Scriptul original de `CREATE TABLE` tot nu e in
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR` — dar nu mai e nevoie de el, DDL-ul curent s-a citit direct din
|
||||
dictionarul de date.)
|
||||
- **Daca exista azi randuri reale in `VANZARI_DETALII` cu `proc_tvav` NULL** — n-am interogat asta
|
||||
direct (doar am confirmat ca *poate* fi NULL, `NULLABLE=Y`). Pe randul de test `proc_tvav=1.21`, deci
|
||||
nu se declanseaza acolo. Daca nu exista niciodata NULL pe randuri reale, `Nvl(proc_tvav,0)` din
|
||||
punctul 6 e o plasa de siguranta fara efect, la fel ca la `pret`/`cantitate`; daca exista, e periculos
|
||||
(baza * 0 = 0 in orice recalcul din afara VFP). Se poate lamuri cu un `SELECT count(*) FROM
|
||||
vanzari_detalii WHERE sters=0 AND proc_tvav IS NULL`.
|
||||
- **De ce n-a crapat `agg_rul` — REZOLVAT.** `RUL.ID_ARTICOL` e **NOT NULL** in Oracle
|
||||
(`all_tab_columns.nullable = 'N'`, masurat de team-lead), si exista **0** randuri `RUL` active cu
|
||||
`id_articol IS NULL`. Documentul de test (SSS 549) are un singur rulaj, cu articolul real
|
||||
`4294507173` — rata n-are corespondent in `RUL`. Deci `trul` nu poate aduce niciodata NULL, iar
|
||||
declaratia `agg_rul (id_articol N(20), ...)` fara `NULL` (`:617`), desi nesigura pe hartie, e
|
||||
dovedit inaccesibila pe calea normala — nu doar "n-a crapat inca", ci "nu poate crapa" atat timp cat
|
||||
constrangerea Oracle ramane in vigoare. Vezi punctul 7.
|
||||
- **Comportamentul VFP la NULL in `UNION` si `LOCATE FOR` — REZOLVAT, testat empiric.** Vezi punctul 7:
|
||||
test izolat `vfp9.exe -A -T`, script pastrat in
|
||||
`C:\Users\mmari\AppData\Local\Temp\claude\D--ROA-ROAFACTURARE\81fbd7c8-41d4-4341-a83a-95736e6dc6d3\scratchpad\test_null_locate_union.prg`,
|
||||
log in acelasi director (`test_null_locate_union.log`) — fisiere temporare de sesiune, nu in
|
||||
arborele proiectului. `UNION` trateaza NULL=NULL ca echivalent la deduplicare; `LOCATE FOR camp =
|
||||
NULL` nu gaseste niciodata, nici cand campul chiar contine NULL pe randul cautat.
|
||||
- **Cate linii de rata existente in productie ar fi afectate** de reparatia recomandata (Varianta B) —
|
||||
nu am interogat Oracle pentru un numar total (team-lead a masurat deja 20 de linii active cu
|
||||
`id_articol IS NULL` in toata baza, punctul 2 — dar nu si cate din ele sunt pe facturi cu rulaje
|
||||
care ar trece prin `ConstruiestePropunereSincronizare`).
|
||||
411
docs/cercetare/rec_r5_totaluri_factura_din_aviz.md
Normal file
411
docs/cercetare/rec_r5_totaluri_factura_din_aviz.md
Normal file
@@ -0,0 +1,411 @@
|
||||
# Cercetare: totaluri goale pe factura editata din aviz (LISTA FACTURI, AVIZE SI PROFORME)
|
||||
|
||||
Simptom: dupa editarea din formularul unificat a unei facturi emise DIN AVIZ, randul facturii in
|
||||
lista principala apare cu **Total fara TVA / Total TVA / Total cu TVA GOALE** (NULL, nu 0.00).
|
||||
Randul avizului de dedesubt ramane corect. Regresie fata de comportamentul anterior.
|
||||
|
||||
Diagnostic STRICT — nu s-a modificat niciun fisier de cod, nu s-a facut write-back, nu s-a comis
|
||||
nimic.
|
||||
|
||||
> **Depasit partial.** Acest raport se opreste la "cauza probabila, NEVERIFICAT pe date".
|
||||
> Interogarea pe `MARIUSM_AUTO@ROA_CENTRAL` a inchis intre timp intrebarea: linia facturii are
|
||||
> `ID_VALUTA = 2` (EURO) fara rand corespunzator in `VANZARI_CURSURI`, iar ramura de conversie
|
||||
> valutara a procedurii produce NULL. Concluzia si reparatia sunt in
|
||||
> `docs\propunere_runda5_articole_valuta_rate.md`, punctul 2. Ce ramane valabil aici: analiza
|
||||
> lantului de scriere si infirmarea ipotezei cu `id_vanzare_set`/`id_vanzare_det`.
|
||||
|
||||
## 1. De unde vin coloanele Total fara TVA / Total TVA / Total cu TVA
|
||||
|
||||
Gridul `grid_facturi` din `frm_facturi` (`COMUN\clase\ofacturare_comun.vc2:1172` obiectul,
|
||||
`:1203-1208` coloanele `cTotal_cu_tva`/`cTotal_fara_tva`/`cTotal_tva`) e legat pe cursorul
|
||||
`crsfacturi` (`RecordSource = "crsFacturi"`, `COMUN\clase\ofacturare_comun.vc2:2238`), populat prin
|
||||
`gencursor('poFacturi','crsfacturi', lcSelect, ...)`.
|
||||
|
||||
Cursorul e umplut din UNA din doua proceduri Oracle, **alese la RUNTIME de utilizator**, printr-un
|
||||
prompt (`Clase\ofundal_facturare.vc2:944-953`, `Page4.Cw1.do_actiune`):
|
||||
|
||||
```
|
||||
lnOptiune = xmenu('Vizualizare \<standard;Vizualizare \<experimentala (mai rapida)')
|
||||
If m.lnOptiune = 1
|
||||
Do vizualizare_facturi In oproceduri_facturare.prg && FACT_VFACTURI
|
||||
Else
|
||||
Do vizualizare_facturi2 In oproceduri_facturare.prg && FACT_VFACTURI2
|
||||
Endif
|
||||
```
|
||||
|
||||
Cele doua proceduri (`COMUN\programe\oproceduri_facturare.prg:314` si `:398`) selecteaza
|
||||
`total_fara_tva, total_tva, total_cu_tva` din **doua view-uri Oracle diferite** cu semantica
|
||||
diferita — asta e cheia problemei:
|
||||
|
||||
- **`FACT_VFACTURI`** ("standard"): coloanele sunt **calculate live**, printr-un `SUM(...)`
|
||||
peste `vanzari_detalii` (si `vanzari_seturi` pentru liniile de tip set). Nu citeste niciodata
|
||||
coloanele stocate din `VANZARI`.
|
||||
Definitie: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_06_11_COMUN_FACT_VFACTURI.sql:192-194`
|
||||
(`a.suma_fara_tva - a.disc_fara_tva_ron as total_fara_tva`, etc.), agregarea la
|
||||
`:408-460` (subquery `vd`, `LEFT JOIN` intre `v.id_vanzare` si rezultatul agregat).
|
||||
- **`FACT_VFACTURI2`** ("experimentala"): coloanele sunt citite **direct** din `VANZARI` (coloane
|
||||
stocate), fara recalcul la interogare — comentariul din cod spune explicit de ce:
|
||||
"*totalurile ... sunt calculate in vanzari, in loc sa fie luate din vanzari_detalii, pentru
|
||||
rapiditate; totalurile sunt completate in vanzari la insert into vanzari*"
|
||||
(`COMUN\programe\oproceduri_facturare.prg:397-399`).
|
||||
Definitie view: `ff_2026_08_06_11_COMUN_FACT_VFACTURI.sql:682-684`
|
||||
(`a.total_fara_tva, a.total_tva, a.total_cu_tva` din `vanzari a`).
|
||||
|
||||
**NEVERIFICAT**: nu stiu ce optiune a ales Marius la momentul screenshot-ului (`standard` sau
|
||||
`experimentala`) — promptul e interactiv, nu am gasit un default hard-codat. Concluzia de mai jos
|
||||
e valabila pentru amandoua variantele, din motive diferite (vezi §3).
|
||||
|
||||
## 2. Ce scrie salvarea din formularul unificat pe partea de factura
|
||||
|
||||
`ScrieArticoleFacturaEditate` (`COMUN\programe\ofacturare_editare.prg:473-571`), apelata cu
|
||||
`tvanz.id_vanzare` (id-ul corect al FACTURII, verificat — `tvanz` vine din
|
||||
`IncarcaVanzareNota`/`IncarcaVanzareDinNota`, care interogheaza `VANZARI` dupa
|
||||
`cod/nract/serie_act/data_act` ale notei editate, deci nu e confuzie aviz/factura; apelurile reale
|
||||
sunt in `COMUN\clase\comun.vc2:2492` si `COMUN\clase\ofacturare_comun.vc2:3829`, ambele in ramura
|
||||
`"OFACTURARE_EDITARE" $ Upper(Set("Procedure"))`):
|
||||
|
||||
1. **Marcheaza sters=1 TOATE liniile active** din `vanzari_detalii` pentru acel `id_vanzare`
|
||||
(`:492-495`).
|
||||
2. **Reinvie/actualizeaza** liniile pastrate, cele cu `id_vanzare_det > 0` din cursorul `tvd`
|
||||
(`:498-518`) — UPDATE pe cheia `id_vanzare_det`, camp cu camp: `sters, cantitate, pret,
|
||||
pret_cu_tva, pret_achizitie, proc_tvav, discount_unitar, id_articol, id_gestiune, id_valuta,
|
||||
id_jtva_coloana, taxcode, cont, id_utils, dataoras`. **NU** atinge `id_vanzare_set` — coloana nu
|
||||
apare in lista SET, deci valoarea existenta in DB ramane neschimbata la UPDATE.
|
||||
3. **Insereaza** liniile noi (`id_vanzare_det = 0`, `:522-540`) — la fel, **fara** `id_vanzare_set`
|
||||
in lista de coloane (linii noi ies cu `id_vanzare_set` implicit NULL in DB).
|
||||
4. **Doar daca 1-3 au reusit complet** (`IF m.llSucces`, verificat dupa fiecare pas, `EXIT` la
|
||||
primul esec Oracle — deci un esec partial NU ajunge la pasul urmator), cheama procedura Oracle
|
||||
de recalcul totaluri (`:559-563`, vezi §3).
|
||||
|
||||
Pe o factura din aviz **fara** articole compuse ("seturi"), toate liniile din `tvd` au
|
||||
`id_vanzare_set = 0/NULL` — confirmat de cercetarea anterioara `docs/livrare_s5.md:117-118`:
|
||||
"*Liniile din seturi de articole (id_vanzare_set nenul) — neacoperibil pe datele actuale, nu din
|
||||
omisiune: interogare pe MARIUSM_AUTO, zero documente cu id_vanzare_set nenul*". Deci pentru cazul
|
||||
din screenshot (SSS 100037), branch-ul de "seturi" din view/procedura Oracle (vezi §3) probabil nu
|
||||
se activeaza — **ramane insa NEVERIFICAT direct pe acest document**, n-am rulat SQL pe schema
|
||||
reala.
|
||||
|
||||
**Nu am gasit nicio scriere directa pe antetul `VANZARI` in `ScrieArticoleFacturaEditate` in afara
|
||||
apelului la procedura Oracle de la pasul 4** — totalurile de antet nu sunt niciodata calculate in
|
||||
VFP, sunt lasate integral pe seama Oracle.
|
||||
|
||||
## 3. Procedura Oracle de recalcul — cea mai probabila cauza
|
||||
|
||||
`ScrieArticoleFacturaEditate` (`ofacturare_editare.prg:559-563`) cheama necondiționat:
|
||||
|
||||
```
|
||||
lcSql = [begin pack_facturare.recalculeaza_totaluri_vanzari(] + Alltrim(Str(m.tnIdVanzare)) + [,] + ;
|
||||
Iif(m.llDiscountNul,[NULL],Alltrim(Str(m.lnDiscount,18,4))) + [); end;]
|
||||
llSucces = goExecutor.oExecuta(m.lcSql)
|
||||
```
|
||||
|
||||
Aceasta procedura este **cod nou**, introdus in exact aceeasi runda de modificari ca bug-ul
|
||||
raportat:
|
||||
|
||||
- Script-ul care o documenteaza in `SCRIPTURI_CLAR` e
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`
|
||||
(comentariu la inceput: "*adauga procedura recalculeaza_totaluri_vanzari*").
|
||||
- Apelul din VFP a fost adaugat in commit-ul `1c42ae0` din `COMUN`
|
||||
("*#6 editare factura emisa: S5 - scrierea sumelor editate in Oracle*"), **10.08.2026**, adica o
|
||||
zi dupa ce procedura Oracle a fost capturata in script (09.08.2026) — coerent cu "procedura noua
|
||||
in DB, apoi codul VFP care o cheama".
|
||||
|
||||
Corpul procedurii (`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16024-16228`) face un
|
||||
`SELECT ... INTO` cu agregare (SUM) peste vanzari_detalii/vanzari_seturi — **aceeasi structura**
|
||||
ca formula din `FACT_VFACTURI` (§1): UNION ALL intre liniile normale
|
||||
(`nvl(a.id_vanzare_set,0)=0 and a.id_vanzare=V_ID_VANZARE and a.sters=0`) si liniile "set"
|
||||
(`vanzari_seturi` join `vanzari_detalii`), apoi `SUM(pack_facturare.calculeaza_total_fara_tva_fact(...))`
|
||||
etc. Rezultatul e scris **necondiționat**, fara nicio garda `NVL`:
|
||||
|
||||
```
|
||||
update vanzari
|
||||
set discount = lnDiscountFactura,
|
||||
...
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
total_tva = lnTotalTVA,
|
||||
total_cu_tva = lnTotalCuTVA,
|
||||
...
|
||||
where id_vanzare = V_ID_VANZARE;
|
||||
```
|
||||
(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16211-16223`)
|
||||
|
||||
Contrast direct cu scriptul de backfill istoric din aceeasi runda
|
||||
(`ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`), care recalculeaza aceleasi coloane dar explicit
|
||||
**"fill-only"**: `v.total_fara_tva = NVL(v.total_fara_tva, src.c_ftva)`, cu comentariu propriu care
|
||||
spune raspicat ca scrierea trebuie sa fie doar completare de goluri, "*niciodata peste o valoare
|
||||
deja scrisa*". `recalculeaza_totaluri_vanzari` **nu are aceasta garda** — daca `SELECT INTO`
|
||||
calculeaza `NULL` pe oricare coloana (SUM peste zero randuri, sau peste randuri toate NULL),
|
||||
UPDATE-ul suprascrie o valoare corecta deja existenta cu NULL.
|
||||
|
||||
**Cand poate iesi NULL din SUM**: in Oracle, `SUM()` peste un set de randuri produs de un
|
||||
`LEFT JOIN` fara nicio potrivire da NULL (nu 0), iar daca TOATE liniile relevante ale documentului
|
||||
ies cu pret/discount NULL din subquery (de ex. `vanzari_seturi` fara rand pentru
|
||||
`id_vanzare_set`-ul cerut, in branch-ul de "seturi"), rezultatul agregat pe acel document e NULL.
|
||||
Pentru un document fara linii de tip "set" (cazul obisnuit, §2), acest branch nu ar trebui sa se
|
||||
activeze — dar **nu am verificat pe date reale** daca liniile facturii SSS 100037 au ramas cu
|
||||
`sters=0` dupa salvare sau daca vreo alta conditie (curs valutar lipsa in `vanzari_cursuri` pentru
|
||||
`id_valuta`-ul liniei, de ex.) a produs NULL in agregat.
|
||||
|
||||
## 4. Regresia, concret — NEVERIFICAT complet, dar convergenta puternica
|
||||
|
||||
Nu exista fisiere `.bak.vc2` pentru `ofacturare_editare.prg` (bak-urile mentionate in cerere —
|
||||
`omodificari.pre_cantitate.bak.vc2` etc. — sunt pentru `omodificari.vc2`, o clasa diferita; codul
|
||||
de scriere efectiva a articolelor si totalurilor traieste in `ofacturare_editare.prg`, care nu are
|
||||
un `.bak`). Istoricul git (`COMUN`) arata insa clar ca **intreg mecanismul de recalcul al
|
||||
totalurilor pe factura editata e cod nou din 09-10.08.2026** (S5), introdus in acelasi pachet cu
|
||||
extinderea UPDATE-ului mentionata in cerere (`id_articol, id_gestiune, id_valuta, proc_tvav,
|
||||
taxcode, cont, pret_achizitie, discount_unitar, id_jtva_coloana`) — commit-urile ulterioare
|
||||
(S4b etapa 2, 11.08.2026) nu ating aceasta zona.
|
||||
|
||||
**Ipoteza suspectata explicit de cerere** (UPDATE-ul extins scrie campuri NULL peste linii
|
||||
existente si strica `id_vanzare_set`/`id_vanzare_det`) **NU se confirma din citirea codului**:
|
||||
UPDATE-ul de la pasul 2 (§2) nu atinge deloc coloana `id_vanzare_set`, iar `id_vanzare_det` e
|
||||
folosit doar in clauza `WHERE`, niciodata scris. Round-trip-ul cursorului `tvd` (incarcat din
|
||||
`VVANZARI_ARTICOLE`, view 1:1 pe `vanzari_detalii` — vezi
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`, fara nicio
|
||||
agregare) e coerent camp cu camp.
|
||||
|
||||
**Cauza cea mai probabila, cu incredere medie-mare**: nu o eroare in codul VFP de scriere a
|
||||
liniilor, ci procedura Oracle noua `pack_facturare.recalculeaza_totaluri_vanzari`
|
||||
(§3) — cod introdus in aceeasi runda, apelat necondiționat dupa fiecare salvare de factura editata
|
||||
din formularul unificat, care scrie fara garda NULL peste coloanele de total din `VANZARI`. Pe
|
||||
"vizualizarea standard" (`FACT_VFACTURI`), acelasi tip de agregare NULL-propagabila se repeta live
|
||||
la fiecare afisare a listei, deci simptomul ar aparea indiferent care view e activ.
|
||||
|
||||
## 5. Trigger/procedura declansata la INSERT — nu se aplica aici
|
||||
|
||||
Nu exista trigger Oracle care sa recalculeze totalurile la INSERT in `vanzari_detalii` — scrierea
|
||||
e facuta explicit, o singura data, prin apelul manual la `recalculeaza_totaluri_vanzari` de la
|
||||
finalul lui `ScrieArticoleFacturaEditate` (§3). Punctul 4 din cererea initiala nu se aplica.
|
||||
|
||||
## Reparatie propusa (fara aplicare)
|
||||
|
||||
Minimal, in `D:\ROA\DATABASE\SCRIPTURI_CLAR\...\PACK_FACTURARE.sql` /
|
||||
`recalculeaza_totaluri_vanzari`: inlocuit UPDATE-ul necondiționat cu varianta "fill-safe" folosita
|
||||
deja in scriptul de backfill — fie NVL pe fiecare coloana (`total_fara_tva = NVL(lnTotalFaraTVA,
|
||||
total_fara_tva)` etc., pastreaza valoarea veche daca recalculul da NULL), fie un `RAISE`/log
|
||||
explicit cand agregatul iese NULL, ca sa nu treaca neobservat. Inainte de asta, e nevoie de o
|
||||
verificare directa pe schema reala (SQL pe `vanzari_detalii`/`vanzari_seturi` pentru
|
||||
`id_vanzare`-ul facturii SSS 100037) ca sa se confirme ce ramura produce NULL — recomand asta ca
|
||||
prim pas al oricarei remedieri, nu modificarea pe ghicite.
|
||||
|
||||
## Ce ramane NEVERIFICAT (runda 1)
|
||||
|
||||
- Ce optiune de vizualizare (standard/experimentala) a fost activa la momentul screenshot-ului.
|
||||
- Continutul real al `vanzari_detalii`/`vanzari_seturi` pentru factura SSS 100037 dupa editare —
|
||||
**verificat in runda 2** (mai jos), pe baza interogarilor SQL facute de team-lead + interogari
|
||||
proprii read-only, cu `sqlplus`.
|
||||
|
||||
---
|
||||
|
||||
# Runda 2 — cauza confirmata prin SQL pe date reale
|
||||
|
||||
Team-lead a interogat direct Oracle (`MARIUSM_AUTO@ROA_CENTRAL`) si a stabilit cauza imediata:
|
||||
linia `VANZARI_DETALII.id_vanzare_det = 1602` (singura linie activa a facturii `id_vanzare = 1054`,
|
||||
SSS 100037) are `ID_VALUTA = 2` (EURO), desi documentul e `IN_VALUTA = 0` si linia-sursa din aviz
|
||||
avea `ID_VALUTA = 3` (RON). `VANZARI_CURSURI` nu are niciun rand pentru `id_vanzare = 1054`, deci
|
||||
`recalculeaza_totaluri_vanzari` calculeaza `pret_ron = NULL` pentru acea linie (branch-ul de
|
||||
conversie valutara, fara curs) si `total_fara_tva/total_tva/total_cu_tva` ies `NULL`. Am confirmat
|
||||
independent, cu SQL read-only propriu (`sqlplus`, vezi mai jos), amploarea si mecanismul.
|
||||
|
||||
## 1. Cine scrie `id_valuta` pe linia de articol — CONFIRMAT
|
||||
|
||||
Singura cale prin care `tvd.id_valuta` se schimba pe o linie **existenta** e nomenclatorul de
|
||||
valuta din grid: `ArticoleNotaEditor.ModificaNomenclator`, ramura `nume_val`
|
||||
(`COMUN\programe\ofacturare_editare.prg:1093-1094`):
|
||||
|
||||
```
|
||||
CASE m.lcCamp == 'nume_val'
|
||||
REPLACE nume_val WITH loCauta.nume_val, id_valuta WITH loCauta.id_valuta
|
||||
```
|
||||
|
||||
`loCauta = caut_valuta()` — nomenclatorul standard de valute, deschis fara nicio conditie legata
|
||||
de `tvanz.in_valuta`. Singura garda de la inceputul procedurii (`:1069`) e
|
||||
`Nvl(tvd.id_vanzare_set,0) <> 0` (blocheaza doar liniile din seturi de articole) — **nu exista
|
||||
nicio garda legata de tipul documentului**, deci utilizatorul poate alege orice valuta pe orice
|
||||
linie normala, indiferent daca factura e in RON sau in valuta.
|
||||
|
||||
UPDATE-ul care duce `id_valuta` in `VANZARI_DETALII` e in
|
||||
`ScrieArticoleFacturaEditate` (`COMUN\programe\ofacturare_editare.prg:512`):
|
||||
|
||||
```
|
||||
[, id_valuta = ] + Iif(Isnull(id_valuta),[NULL],Alltrim(Str(id_valuta))) + ;
|
||||
```
|
||||
|
||||
**Confirmat prin diff cu `COMUN\programe\ofacturare_editare.prg.pre_runda_butoane.bak:501-505`**:
|
||||
inainte de runda curenta, UPDATE-ul pe liniile existente scria doar
|
||||
`sters, cantitate, pret, pret_cu_tva, id_utils, dataoras` — **`id_valuta` nu era in lista**. Deci
|
||||
inainte, o alegere de valuta pe o linie existenta (facuta prin `nume_val`) se pierdea silentios la
|
||||
salvare (UI arata schimbarea, dar UPDATE-ul n-o scria) — inofensiv, dar si fara efect. Extinderea
|
||||
UPDATE-ului (runda curenta, aceeasi runda care a adaugat si `recalculeaza_totaluri_vanzari`, vezi
|
||||
runda 1 §3-4) e cea care face ca alegerea gresita de valuta sa ajunga acum in `VANZARI_DETALII` si
|
||||
sa strice totalurile. **Simptomul e nou din exact acest motiv — confirmat pe cod, nu presupunere.**
|
||||
|
||||
## 2. Ce ar trebui sa fie corect pe un document `in_valuta = 0` — CONFIRMAT
|
||||
|
||||
`VANZARI_CURSURI` e scris o singura data, la EMITERE, de `pack_facturare.scrie_cursuri`
|
||||
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:14538-14548`):
|
||||
|
||||
```sql
|
||||
PROCEDURE scrie_cursuri(V_ID_VANZARE IN NUMBER) IS
|
||||
BEGIN
|
||||
INSERT INTO VANZARI_CURSURI (ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR)
|
||||
SELECT DISTINCT V_ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR
|
||||
FROM VANZARI_DETALII_TEMP
|
||||
WHERE ID_VALUTA <> pack_facturare.nid_moneda_nationala;
|
||||
END scrie_cursuri;
|
||||
```
|
||||
|
||||
Sursa e tabela de STAGING folosita la emitere (`VANZARI_DETALII_TEMP`), nu `VANZARI_DETALII` live —
|
||||
deci **nu exista niciun mecanism care sa adauge/actualizeze `VANZARI_CURSURI` cand se schimba
|
||||
`id_valuta` pe o linie DUPA emitere**, din editarea unificata sau din oricare alt punct. Raspuns
|
||||
direct la intrebare: **da, e conceptual posibil ca o linie sa aiba alta valuta decat RON pe un
|
||||
document in lei** (mecanismul de facturare mixta exista, cu curs propriu per linie in
|
||||
`VANZARI_CURSURI`) — dar **doar daca acea alegere s-a facut la emitere**, cand `scrie_cursuri`
|
||||
inca ruleaza si scrie perechea `(id_vanzare, id_valuta) -> curs`. O schimbare **post-emitere**,
|
||||
prin editarea unificata, nu are nicio cale sa creeze acel rand — deci orice `id_valuta` diferit de
|
||||
RON ales dupa emitere e, prin constructie, orfan (fara curs), garantand `pret_ron = NULL` in
|
||||
`recalculeaza_totaluri_vanzari`.
|
||||
|
||||
## 3. Toate caile prin care agregatele pot iesi NULL — enumerare + masurare pe date reale
|
||||
|
||||
Din citirea corpului `recalculeaza_totaluri_vanzari`
|
||||
(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16024-16228`):
|
||||
|
||||
1. **Zero randuri active in `VANZARI_DETALII`** pentru `id_vanzare` (toate `sters=1`, sau
|
||||
document fara nicio linie) — `SUM()`/`MAX()` peste zero randuri = NULL pentru toate coloanele.
|
||||
2. **Linie cu `id_valuta` diferit de moneda nationala, fara rand in `VANZARI_CURSURI`** pentru acea
|
||||
pereche `(id_vanzare, id_valuta)` — cazul masurat mai jos (Marius). `LEFT JOIN vc` nu gaseste
|
||||
nimic, `vc.curs`/`vc.multiplicator` = NULL, `pret_ron` = NULL pentru acea linie. Daca **toate**
|
||||
liniile documentului sunt afectate, `SUM()` total iese NULL; daca doar unele, `SUM()` ignora
|
||||
randurile NULL si totalul iese **trunchiat, nu NULL** (lipseste contributia liniei respective,
|
||||
fara sa fie vizibil ca eroare).
|
||||
3. **Linie din "set de articole" (`id_vanzare_set <> 0`) fara rand corespunzator in
|
||||
`VANZARI_SETURI`** — acelasi tipar ca #2, prin `LEFT JOIN vanzari_seturi b`. Neconfirmat pe date
|
||||
reale (branch aproape neutilizat — vezi `docs/livrare_s5.md:117-118`, zero documente cu
|
||||
`id_vanzare_set` nenul cunoscute anterior).
|
||||
4. **`VANZARI.in_valuta` NULL** — `nvl(lnInValuta,-1) > -1` devine fals, exclude tot branch-ul de
|
||||
"seturi"; nu produce NULL pe branch-ul normal, dar poate goli branch-ul 2 pe un document compus
|
||||
doar din linii de set.
|
||||
5. **Argument NULL in `pack_facturare.calculeaza_total_fara_tva_fact`/`_tva_fact`** (ex.
|
||||
`proc_tvav` sau `cantitate` NULL pe o linie) — acelasi tipar trunchiat/total, dupa cate linii
|
||||
sunt afectate.
|
||||
|
||||
**Masurat pe schema reala** (`MARIUSM_AUTO@ROA_CENTRAL`, SELECT-uri read-only, script-urile
|
||||
folosite raman in `scratchpad`, nu s-a scris nimic in baza):
|
||||
|
||||
| Categorie (din cele 235 `VANZARI.total_cu_tva IS NULL`) | Numar documente |
|
||||
|---|---|
|
||||
| Total `VANZARI` cu `total_cu_tva IS NULL` | **235** |
|
||||
| Fara nicio linie activa in `VANZARI_DETALII` (cauza #1, veche — carve-out cunoscut, vezi
|
||||
`ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`, "facturile fara nicio linie activa raman
|
||||
neatinse, prin decizie de produs") | **120** |
|
||||
| Linie cu `id_valuta` diferit de RON si fara rand in `VANZARI_CURSURI` (cauza #2 — **exact
|
||||
bug-ul curent**) | **1** (chiar SSS 100037 / `id_vanzare=1054`, confirmat `IN_VALUTA=0`) |
|
||||
| Neexplicat de #1 sau #2 (alta cauza, preexistenta, in afara scopului acestei cercetari) |
|
||||
**114** — din acestea, 109 au `VANZARI.discount_evidentiat IS NULL` (posibil o cauza separata,
|
||||
nelegata de `id_valuta`; neinvestigat in continuare, iese din scopul cererii) |
|
||||
|
||||
**Concluzie importanta**: bug-ul de `id_valuta` orfan e **izolat la 1 singur document in toata
|
||||
baza**, in acest moment — consistent cu faptul ca UPDATE-ul extins care il face vizibil e cod de
|
||||
cateva zile (runda curenta). Nu e nevoie de o reparare in masa; celelalte 234 de documente cu total
|
||||
NULL au alta cauza (majoritar #1, cunoscuta si acceptata prin design) si nu trebuie atinse de
|
||||
reparatia acestui bug.
|
||||
|
||||
## 4. Reparatie propusa, in trei straturi (fara aplicare)
|
||||
|
||||
### Client (VFP)
|
||||
|
||||
In `ArticoleNotaEditor.ModificaNomenclator`
|
||||
(`COMUN\programe\ofacturare_editare.prg:1069`), extinde garda existenta ca sa blocheze
|
||||
nomenclatorul de valuta pe liniile unui document care nu e in valuta:
|
||||
|
||||
```
|
||||
IF !This.Editabil() OR Reccount('tvd') = 0 OR !This.AreNomenclator(m.lcCamp) OR Nvl(tvd.id_vanzare_set, 0) <> 0 ;
|
||||
OR (m.lcCamp == 'nume_val' AND Nvl(tvanz.in_valuta,0) <> 1)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
```
|
||||
|
||||
(presupune ca `tvanz` e vizibil din contextul clasei — de verificat la implementare; alternativ,
|
||||
o proprietate `This.oForm.lDocInValuta` populata la incarcare). Efect: pe o factura in RON,
|
||||
coloana `nume_val` devine needitabila, la fel ca liniile din seturi — elimina posibilitatea de a
|
||||
introduce `id_valuta` orfan prin UI.
|
||||
|
||||
### DB — `recalculeaza_totaluri_vanzari`
|
||||
|
||||
Doua schimbari, ambele minime:
|
||||
|
||||
1. **Restrange conditia de conversie** la cazul in care documentul insusi e in valuta, nu cand
|
||||
linia are o valuta diferita de RON pe un document RON (elimina exact vulnerabilitatea gasita):
|
||||
|
||||
```sql
|
||||
-- inainte:
|
||||
(case when (lnInValuta = 1 or vd.id_valuta <> pack_def.GetIdMonedaNationala())
|
||||
then ROUND(vc.curs * vd.pret / vc.multiplicator, lnPreciziePretV)
|
||||
else vd.pret end) as pret_ron
|
||||
-- dupa:
|
||||
(case when lnInValuta = 1
|
||||
then ROUND(NVL(vc.curs,1) * vd.pret / NVL(vc.multiplicator,1), lnPreciziePretV)
|
||||
else vd.pret end) as pret_ron
|
||||
```
|
||||
|
||||
Motivatie: pe un document `in_valuta=0`, `vd.pret` e prin definitie deja in RON (asa scrie
|
||||
`ScrieArticoleFacturaEditate` liniile, indiferent de `id_valuta` afisat) — conversia n-are ce sa
|
||||
faca acolo, indiferent ce `id_valuta` a ajuns (corect sau nu) pe linie. Pastrez `NVL(vc.curs,1)`
|
||||
doar ca ultima plasa de siguranta pe ramura `lnInValuta=1` (document CHIAR in valuta) — daca
|
||||
acolo lipseste cursul e o eroare de date reala care merita semnalata, nu ascunsa; de discutat cu
|
||||
Marius daca `NVL(...,1)` e acceptabil acolo sau daca ar trebui sa opreasca salvarea cu eroare in
|
||||
loc sa scrie o suma gresita tacut. **Nu propun `NVL` necondiționat pe ramura de conversie
|
||||
reala** — ar masca o factura in valuta cu curs lipsa, scriind un total fals dar nenul, mai greu
|
||||
de observat decat un NULL.
|
||||
|
||||
2. **Plasa finala de siguranta la UPDATE**, dupa modelul "fill-safe" deja folosit in
|
||||
`ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`, ca sa nu se mai poata suprascrie tacut o valoare
|
||||
corecta cu NULL, indiferent ce cauza noua ar aparea in viitor:
|
||||
|
||||
```sql
|
||||
update vanzari
|
||||
set total_fara_tva = NVL(lnTotalFaraTVA, total_fara_tva),
|
||||
total_tva = NVL(lnTotalTVA, total_tva),
|
||||
total_cu_tva = NVL(lnTotalCuTVA, total_cu_tva),
|
||||
...
|
||||
where id_vanzare = V_ID_VANZARE;
|
||||
```
|
||||
|
||||
Cu un `dbms_output`/log undeva (tabela de erori aplicative, daca exista una) cand recalculul
|
||||
iese NULL, ca sa nu treaca neobservat un caz nou.
|
||||
|
||||
### Date (script propus, NU RULAT)
|
||||
|
||||
```sql
|
||||
-- 1. Corectie punctuala pentru SSS 100037 (id_vanzare=1054): id_valuta-ul liniei 1602 revine la
|
||||
-- RON, aliniat cu documentul (in_valuta=0) si cu linia-sursa din aviz (id_vanzare_det=1599,
|
||||
-- care avea deja id_valuta=3).
|
||||
UPDATE VANZARI_DETALII
|
||||
SET ID_VALUTA = pack_def.GetIdMonedaNationala()
|
||||
WHERE ID_VANZARE_DET = 1602
|
||||
AND ID_VANZARE = 1054;
|
||||
COMMIT;
|
||||
|
||||
-- 2. Retrigger recalcul dupa fix (acelasi apel pe care il face si formularul la salvare)
|
||||
BEGIN
|
||||
pack_facturare.recalculeaza_totaluri_vanzari(1054);
|
||||
END;
|
||||
/
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
Restrans STRICT la acest document — cele 234 de alte randuri cu total NULL au alta cauza (§3) si nu
|
||||
intra in scopul acestei reparatii. Daca se doreste o baleiere completa dupa ce fix-ul de la client
|
||||
e livrat (sa nu mai apara alte cazuri noi), propun mai intai o interogare de monitorizare
|
||||
periodica (varianta de query C/F de mai sus), nu o corectie automata in masa.
|
||||
|
||||
## Ce ramane NEVERIFICAT (runda 2)
|
||||
|
||||
- Cauza celor 114 documente "neexplicate" (mult mai vechi, posibil legate de
|
||||
`discount_evidentiat IS NULL`) — iese din scopul cererii, semnalez doar ca exista.
|
||||
- Daca `tvanz` e efectiv vizibil ca alias in contextul `ArticoleNotaEditor.ModificaNomenclator` la
|
||||
momentul apelului (necesar pentru fix-ul de client propus) — de verificat la implementare, nu am
|
||||
urmarit domeniul complet de vizibilitate al claselor `.vc2`.
|
||||
|
||||
327
docs/cercetare/rec_r6_buton_modificare_frm_facturi.md
Normal file
327
docs/cercetare/rec_r6_buton_modificare_frm_facturi.md
Normal 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` |
|
||||
236
docs/cercetare/rec_r6_meniu_editare_factura.md
Normal file
236
docs/cercetare/rec_r6_meniu_editare_factura.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# Cercetare: eticheta care duce la editarea facturii emise
|
||||
|
||||
Sarcina: gasirea locului (locurilor) unde apare textul care duce la editarea facturii emise
|
||||
(formularul `frm_modific2024`, `COMUN\clase\omodificari.vc2`), pentru a evalua propunerea
|
||||
utilizatorului de a-l redenumi in **"editare factura (note, rulaje, articole)"**.
|
||||
|
||||
Concluzie scurta: **nu exista un bar de meniu `.mnx` clasic** cu acest text. Punctul real de
|
||||
intrare e un **popup dinamic construit din `xmenu(...)` intr-un fisier `.vc2`** (deci
|
||||
write-back-abil prin `txt2vcx.ps1`, nu editare manuala in IDE cum s-a presupus initial in brief).
|
||||
Vezi punctul C pentru corectia asta.
|
||||
|
||||
---
|
||||
|
||||
## A. Toate locurile unde apare eticheta / unde s-ar putea afla
|
||||
|
||||
Cautari facute (rezultate zero pentru textul cerut in `.mnx`/`.mn2`):
|
||||
|
||||
- `grep -in "modific\|editeaz\|editare" Meniuri\vanzare1.mn2 ... vanzare5.mn2` -> **zero hit-uri**.
|
||||
- `grep -ln "modific\|editeaz\|editare" Meniuri\*.mn2 COMUN\meniuri\*.mn2` -> doar
|
||||
`Meniuri\model.mn2` si `Meniuri\roafacturare.mn2`, ambele **irelevante**:
|
||||
- `Meniuri\roafacturare.mn2:50` — `DEFINE BAR 3 OF Ajutor PROMPT "\<Jurnal modificari"` (jurnal
|
||||
de audit al aplicatiei, nu editare factura).
|
||||
- `Meniuri\model.mn2:14` — `DEFINE BAR 2 OF Shortcut PROMPT "\<Modificare raport"` (editare
|
||||
raport definit de utilizator, nu factura).
|
||||
- Niciun `.mnx`/`.mn2` din proiect sau din `COMUN\meniuri\` nu contine referinta la
|
||||
`frm_modific2024` sau `ofacturare_editare` (verificat cu grep pe toate cele 43 de fisiere unde
|
||||
apare `frm_modific2024` — niciunul e `.mn2`).
|
||||
|
||||
**Locul real** e un buton de toolbar generic + un popup `xmenu()` construit in cod, in
|
||||
`COMUN\clase\ofacturare_comun.vc2`, pe formularul `frm_facturi` (lista de facturi emise):
|
||||
|
||||
1. **Butonul de toolbar** — clasa `but_modifica`, definita generic in
|
||||
`COMUN\clase\cmd_butoane.vc2:182-197`:
|
||||
```
|
||||
DEFINE CLASS but_modifica AS buton OF "_cmd_base.vcx"
|
||||
caction = inainte_de_do_modifica
|
||||
...
|
||||
ToolTipText = "Modificare (CTRL+M)"
|
||||
```
|
||||
Acest buton e adaugat **direct in clasa de baza a tuturor formularelor**,
|
||||
`COMUN\clase\_frm_base.vc2:1103` (`ADD OBJECT 'But_modifica1' AS but_modifica ...`), deci apare
|
||||
pe **toate** formularele-lista din suita ROA (parteneri, personal, stocuri, note contabile,
|
||||
facturi etc.), nu doar pe cel de facturi. Textul lui (`"Modificare (CTRL+M)"`) e generic si
|
||||
hardcodat, nu vine din `Locale\` (vezi punctul C).
|
||||
|
||||
2. **Popup-ul specific facturii** — `frm_facturi` (definit in `ofacturare.vcx`/`ofacturare_comun.vc2`)
|
||||
suprascrie handler-ul butonului cu propriul sau meniu contextual:
|
||||
```
|
||||
COMUN\clase\ofacturare_comun.vc2:4928-4937
|
||||
PROCEDURE inainte_de_do_modifica
|
||||
Local lnOptiune
|
||||
lnOptiune = xmenu('Modificare \<date factura;Editare \<factura (articole, cantitati, preturi)')
|
||||
Do Case
|
||||
Case lnOptiune = 1
|
||||
This.do_modifica()
|
||||
Case lnOptiune = 2
|
||||
This.do_editare_factura()
|
||||
Endcase
|
||||
ENDPROC
|
||||
```
|
||||
Textul exact de azi al liniei (verificat byte cu byte, ASCII curat, fara diacritice corupte):
|
||||
`xmenu('Modificare \<date factura;Editare \<factura (articole, cantitati, preturi)')` —
|
||||
`COMUN\clase\ofacturare_comun.vc2:4930`.
|
||||
|
||||
Deci utilizatorul care apasa butonul "Modificare (CTRL+M)" pe lista de facturi vede un
|
||||
mini-meniu cu **doua optiuni**:
|
||||
- "Modificare date factura" (accelerator D) → `This.do_modifica()` — ruta generica
|
||||
`afisjurcom.do_modifica` din `comun.vc2:2222-2572` (aceeasi metoda partajata de foarte multe
|
||||
tipuri de documente din suita, cu ramuri Do Case pentru fiecare).
|
||||
- "Editare factura (articole, cantitati, preturi)" (accelerator F) →
|
||||
`This.do_editare_factura()` — **asta e ruta directa catre `frm_modific2024`**.
|
||||
|
||||
## B. Lantul real: eticheta -> comanda -> `frm_modific2024`
|
||||
|
||||
```
|
||||
frm_facturi (lista facturi emise)
|
||||
-> but_modifica1 (mostenit din _frm_base, ToolTipText "Modificare (CTRL+M)")
|
||||
-> click -> inainte_de_do_modifica() [ofacturare_comun.vc2:4928]
|
||||
-> xmenu('Modificare \<date factura;Editare \<factura (articole, cantitati, preturi)')
|
||||
[ofacturare_comun.vc2:4930]
|
||||
-> optiunea 2 -> This.do_editare_factura() [ofacturare_comun.vc2:3715]
|
||||
-> seteaza tipul, verifica drepturi (This.lactiv3), Reccount('crsfacturi')...
|
||||
-> Omodif = Createobject([frm_modific2024], lnIdSet) [ofacturare_comun.vc2:3796]
|
||||
-> Omodif.Show()
|
||||
```
|
||||
|
||||
Confirmare independenta a clasei tinta prin `vfp_symbols.ps1 -Where "ofacturare_comun.vc2:4935"`:
|
||||
`frm_facturi.inainte_de_do_modifica [method] ofacturare_comun.vc2:4928-4937`.
|
||||
|
||||
**Alte cai catre acelasi formular**, gasite prin `grep -rn frm_modific2024` (43 fisiere), toate
|
||||
insa in alt context, nu din meniul principal de facturare al utilizatorului final:
|
||||
|
||||
- `COMUN\clase\comun.vc2:2439` — `afisjurcom.do_modifica` (metoda generica, ramura
|
||||
`IF gnAn >= ... ELSE Createobject([frm_modific])`) — e ruta **veche/generica** de "nota
|
||||
contabila", partajata cu toate produsele ROA (nu specifica facturii).
|
||||
- `COMUN\clase\anaf_efactura.vc2:13087` — deschidere `frm_modific2024` din fluxul e-Factura ANAF
|
||||
(verificare/validare, nu din butonul de lista).
|
||||
- `COMUN\ferestre\frm_import_note_facturi_clienti.sc2:898` si
|
||||
`COMUN\ferestre\frm_initializare_facturi_balanta.sc2:1806` — fluxuri de import/initializare,
|
||||
nu editarea unei facturi deja emise, deci **nu intra in scopul cerut**.
|
||||
|
||||
Pentru cererea utilizatorului ("editarea facturii emise"), **singurul loc relevant si cu text
|
||||
apropiat de ce isi doreste** este `ofacturare_comun.vc2:4930` (optiunea 2 din popup).
|
||||
|
||||
## C. Cum se schimba textul in practica
|
||||
|
||||
- **Nu e in `.mnx`/`.mn2`.** Nu exista un `DEFINE BAR ... PROMPT` cu acest text nicaieri in
|
||||
proiect. Deci nu e nevoie de editare manuala in IDE-ul de meniuri.
|
||||
- **E literal, hardcodat intr-o metoda `.vc2`**: `COMUN\clase\ofacturare_comun.vc2:4930`, in
|
||||
interiorul clasei `frm_facturi`, metoda `inainte_de_do_modifica`. Fisierul `.vc2` **este**
|
||||
write-back-abil prin `txt2vcx.ps1` (regula generala din `CLAUDE.md`: doar `.vc2`/`.sc2` accepta
|
||||
scriere inapoi in binar). Deci schimbarea propusa de utilizator **se poate face prin editare
|
||||
text + `txt2vcx.ps1`**, nu necesita interventie manuala in IDE.
|
||||
- Text propus de utilizator ca inlocuitor pentru optiunea 2: in loc de
|
||||
`"Editare \<factura (articole, cantitati, preturi)"` ar deveni ceva de forma
|
||||
`"Editare \<factura (note, rulaje, articole)"` — pastrand acceleratorul `\<F`. (Nu am facut
|
||||
modificarea — doar raportez unde s-ar face, conform interdictiilor primite.)
|
||||
- **`ToolTipText = "Modificare (CTRL+M)"`** din `cmd_butoane.vc2:194` **NU trebuie schimbat** — e
|
||||
folosit de zeci de formulare din toata suita ROA (am numarat >100 instantieri `but_modifica` in
|
||||
`.vc2`/`.sc2`, majoritatea in `onomenclatoare.vc2`, `onom_sal.vc2`, `oparteneri.vc2` etc.,
|
||||
niciunul specific facturii). Un text de forma "editare factura..." pus acolo ar minti pe toate
|
||||
celelalte ecrane (parteneri, personal, stocuri...).
|
||||
- **Nu exista mecanism `.mnx` compilat separat de regenerat** pentru aceasta schimbare — nefiind
|
||||
vorba de un `.mnx`, nu se aplica `git_sync.ps1`/recompilare de meniu; e nevoie doar de rebuild-ul
|
||||
normal al proiectului dupa modificarea `.vcx` (Project > Build din VFP IDE), conform fluxului
|
||||
standard descris in `CLAUDE.md`.
|
||||
|
||||
## C-bis. Localizare (`Locale\`)
|
||||
|
||||
- Directorul `Locale\` **exista dar e complet gol** in aceasta copie de lucru
|
||||
(`find D:\ROA\ROAFACTURARE\Locale -type f` → 0 fisiere). Nu exista tabele DBF de traducere
|
||||
incarcate aici.
|
||||
- Nu exista niciun apel `Traduc(...)`/`goApp.Traduc(...)` in jurul `ToolTipText`-ului `but_modifica`
|
||||
sau al liniei `xmenu(...)` de mai sus — am cautat in `cmd_butoane.vc2` si `_frm_base.vc2`
|
||||
(`grep -n "Traduc\b"` → zero hit-uri in ambele). Singura potrivire pentru "traduc" in tot
|
||||
proiectul e in `comun.vc2:2157-2158`, cod comentat (`*!* IF THIS.filtru_traducere()#""`),
|
||||
nefolosit si nelegat de acest buton.
|
||||
- **Concluzie**: textul e o constanta Romana hardcodata direct in `.vc2`, nu vine din nicio sursa
|
||||
de traducere. Nu exista alte limbi de verificat pentru acest text.
|
||||
|
||||
## D. Captions reale ale paginilor din `frm_modific2024`
|
||||
|
||||
Premisa utilizatorului ("trei pagini: Note, Rulaje si Articole") **nu se potriveste exact cu
|
||||
Caption-urile din formular**. Am verificat cu:
|
||||
```
|
||||
vfp_symbols.ps1 -Find frm_modific2024 -> class frm_modific2024 omodificari.vc2:6375-16836
|
||||
awk 'NR==6375,NR==16836' omodificari.vc2 | grep -n "AS _pageframe\|PAGE.\.Caption"
|
||||
```
|
||||
Rezultat: **exista un singur `PageFrame`** in formular, `pgfArticole`
|
||||
(`omodificari.vc2:8714-8732`, adica linia absoluta ≈ `6375+2341` in indexare relativa), cu
|
||||
**3 pagini**, niciuna captionata "Note":
|
||||
|
||||
```
|
||||
COMUN\clase\omodificari.vc2:8725 PAGE1.Caption = "Rulaje materii prime, materiale, marfuri, obiecte inventar (303)"
|
||||
COMUN\clase\omodificari.vc2:8728 PAGE2.Caption = "Rulaje obiecte inventar in folosinta (8039)"
|
||||
COMUN\clase\omodificari.vc2:8731 PAGE3.Caption = "Articole factura"
|
||||
```
|
||||
|
||||
Nu exista nicio pagina/tab captionat literal **"Note"**. Zona de "note" (liniile notei contabile —
|
||||
coloane precum "Explicatie C", "Cont", "Cota TVA" etc., verificate prin
|
||||
`grep -n "Caption = \"" ` in intervalul clasei) e **corpul principal al formularului** (grid-ul de
|
||||
baza `grid1`), nu o pagina din pageframe. Deci structura reala e:
|
||||
|
||||
- corp principal (nepaginat) = liniile notei contabile ("Note" e denumirea informala folosita in
|
||||
discutii, nu un Caption efectiv);
|
||||
- `pgfArticole.PAGE1` + `PAGE2` = doua variante de "Rulaje" (materiale 303 / obiecte inventar 8039);
|
||||
- `pgfArticole.PAGE3` = "Articole factura".
|
||||
|
||||
Titlul formularului insusi e generic: `Lb_titlu_alb_b121.Caption = "MODIFICARE"`
|
||||
(`omodificari.vc2:6934`, in Init-ul clasei `frm_modific2024`), nu mentioneaza nici el "factura" sau
|
||||
"note/rulaje/articole".
|
||||
|
||||
**Recomandare pentru textul de meniu**: formularea "note, rulaje, articole" e conceptual corecta
|
||||
(reflecta cele 3 zone reale), dar nu e un citat al Caption-urilor efective de pe ecran — daca se
|
||||
doreste consistenta stricta cu ce vede utilizatorul, ar trebui fie sa se ajusteze si Caption-urile
|
||||
paginilor (schimbare separata, in afara scopului acestei cercetari), fie sa se accepte ca eticheta
|
||||
de meniu descrie zonele functional, nu litera Caption-urilor.
|
||||
|
||||
## E. Pagina Articole e conditionata
|
||||
|
||||
**Da, e strict conditionata** — nu apare intotdeauna. Dovezi:
|
||||
|
||||
```
|
||||
COMUN\clase\omodificari.vc2:14927 This.lAreArticoleVanzari = .F.
|
||||
COMUN\clase\omodificari.vc2:14928 IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Reccount('tact') > 0
|
||||
COMUN\clase\omodificari.vc2:14929 IncarcaVanzareDinNota('tact')
|
||||
COMUN\clase\omodificari.vc2:14930 IF Reccount('tvanz') = 1
|
||||
COMUN\clase\omodificari.vc2:14931 This.lAreArticoleVanzari = .T.
|
||||
COMUN\clase\omodificari.vc2:14932 This.nIdVanzare = tvanz.id_vanzare
|
||||
COMUN\clase\omodificari.vc2:14933 This.nTipVanzare = tvanz.tip
|
||||
COMUN\clase\omodificari.vc2:14934 This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact,0))
|
||||
COMUN\clase\omodificari.vc2:14935 ENDIF
|
||||
COMUN\clase\omodificari.vc2:14936 ENDIF
|
||||
...
|
||||
COMUN\clase\omodificari.vc2:14941 IF This.lAreArticoleVanzari
|
||||
COMUN\clase\omodificari.vc2:... This.pgfArticole.PageCount = 3
|
||||
COMUN\clase\omodificari.vc2:... ELSE
|
||||
COMUN\clase\omodificari.vc2:14958 This.pgfArticole.PageCount = 2
|
||||
COMUN\clase\omodificari.vc2:14959 This.but_nouR.Enabled = .F.
|
||||
COMUN\clase\omodificari.vc2:14960 ENDIF
|
||||
```
|
||||
|
||||
Conditia exacta pentru ca pagina "Articole factura" sa fie vizibila:
|
||||
1. formularul a fost deschis din contextul de editare factura (`"OFACTURARE_EDITARE" $
|
||||
Upper(Set("Procedure"))` — adica din lantul `frm_facturi -> do_editare_factura`, nu din ruta
|
||||
generica `do_modifica`);
|
||||
2. nota are cel putin o linie de tip articol (`Reccount('tact') > 0`);
|
||||
3. exista **exact o** vanzare (factura) legata de nota (`Reccount('tvanz') = 1`).
|
||||
|
||||
Daca oricare din conditii lipseste, `pgfArticole.PageCount = 2` — pagina "Articole factura" **nu
|
||||
apare deloc**, ramanand doar cele doua pagini de "Rulaje". In plus, chiar cand pagina apare, poate
|
||||
fi **doar-citire** (`This.lArticoleReadOnly = EsteInEFactura(...)`) daca factura a fost deja
|
||||
trimisa in e-Factura ANAF (`omodificari.vc2:14934`, plus dezactivari vizibile in
|
||||
`omodificari.vc2:14943-14947`: `but_nouR.Enabled`, `txtDiscountArt.ReadOnly`,
|
||||
`lblArticoleReadOnly.Visible`, `cmdSincronizeazaArticole.Enabled`).
|
||||
|
||||
**Implicatie pentru textul de meniu**: un text care promite mereu "articole" (ex. "editare factura
|
||||
(note, rulaje, articole)") poate **minti in unele cazuri** — cand nota nu are exact o vanzare
|
||||
asociata sau nu are linii de tip articol, utilizatorul nu vede pagina de articole deloc, desi
|
||||
eticheta i-a promis-o. E o observatie de continut, nu un blocaj — decizia de a accepta acest risc
|
||||
(eticheta descrie "ce se poate edita in general", nu "ce va vedea de fiecare data") apartine
|
||||
utilizatorului/echipei.
|
||||
|
||||
## Sumar raspunsuri la intrebarile din brief
|
||||
|
||||
| Intrebare | Raspuns |
|
||||
|---|---|
|
||||
| Exista eticheta `.mnx` literala? | **NU** — cautare completa in toate `.mn2`-urile proiectului si `COMUN\meniuri\`, zero hit-uri pe "editare factura" sau variante |
|
||||
| Unde e reala eticheta? | `COMUN\clase\ofacturare_comun.vc2:4930`, in `xmenu()`, clasa `frm_facturi`, metoda `inainte_de_do_modifica` |
|
||||
| Textul de azi | `Editare \<factura (articole, cantitati, preturi)` |
|
||||
| Vine din `Locale\`? | Nu — `Locale\` e goala, nu exista apel `Traduc()` in jurul acestui text |
|
||||
| Se schimba in `.mnx` sau in cod? | In cod (`.vc2`), write-back prin `txt2vcx.ps1` — **nu** necesita editare manuala in IDE |
|
||||
| Paginile se numesc Note/Rulaje/Articole? | Nu literal — un singur pageframe cu 3 pagini: "Rulaje materii prime..." / "Rulaje obiecte inventar..." / "Articole factura"; zona de note e corpul principal, nepaginat |
|
||||
| Pagina Articole e mereu prezenta? | Nu — conditionata de 3 conditii (`omodificari.vc2:14927-14936`), poate lipsi sau fi doar-citire |
|
||||
|
||||
Nimic din NESTABILIT — toate afirmatiile au dovada `fisier:linie` verificata direct in text.
|
||||
255
docs/cercetare/rec_r6_nomenclatoare_grid.md
Normal file
255
docs/cercetare/rec_r6_nomenclatoare_grid.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Cercetare R6 — nomenclatoare in gridul Articole (frm_modific2024)
|
||||
|
||||
Zona: `COMUN\clase\omodificari.vc2` (formularul `frm_modific2024`), gridul
|
||||
`pgfArticole.PAGE3.grdArticoleFactura`. Dispecerul comun e in
|
||||
`COMUN\programe\ofacturare_editare.prg`, clasa `ArticoleNotaEditor`.
|
||||
|
||||
## A. Inventarul coloanelor gridului Articole
|
||||
|
||||
Bloc `ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura'` incepe la
|
||||
`omodificari.vc2:12330`; lista completa a coloanelor (`ControlSource` + `Name`) e la
|
||||
`omodificari.vc2:12349-12471`.
|
||||
|
||||
| Coloana (Name) | ControlSource | Linie ControlSource | Legata de nomenclator? |
|
||||
|---|---|---|---|
|
||||
| cDenumireArt (Column1) | `tvd.denumire` | `omodificari.vc2:12349` | DA — articol |
|
||||
| cCodmatArt (Column2) | `tvd.codmat` | `omodificari.vc2:12356` | nu |
|
||||
| cSerieArt (Column3) | `tvd.serie` | `omodificari.vc2:12363` | nu |
|
||||
| cLotArt (Column4) | `tvd.lot` | `omodificari.vc2:12370` | nu |
|
||||
| cCantitateArt (Column5) | `tvd.cantitate` | `omodificari.vc2:12377` | nu |
|
||||
| cPretArt (Column6) | `tvd.pret` | `omodificari.vc2:12386` | nu |
|
||||
| cPretAchizitieArt (Column7) | `tvd.pret_achizitie` | `omodificari.vc2:12395` | nu |
|
||||
| cPretCuTvaArt (Column8) | `tvd.pret_cu_tva` | `omodificari.vc2:12404` | nu (checkbox propriu) |
|
||||
| cProcTvavArt (Column9) | `tvd.proc_tvav` | `omodificari.vc2:12412` | nu |
|
||||
| cDiscountUnitarArt (Column10) | `tvd.discount_unitar` | `omodificari.vc2:12421` | nu |
|
||||
| **cGestiuneArt (Column11)** | `tvd.nume_gestiune` | `omodificari.vc2:12430` | **DA — gestiune** |
|
||||
| **cValutaArt (Column12)** | `tvd.nume_val` | `omodificari.vc2:12437` | **DA — valuta** |
|
||||
| cExplicatieArt (Column13) | `tvd.explicatie` | `omodificari.vc2:12444` | nu (text liber) |
|
||||
| **cTaxcodeArt (Column14)** | `tvd.taxcode` | `omodificari.vc2:12451` | **DA — cod TVA SAF-T** |
|
||||
| cValoareArt (Column15) | `tvd.valoare` | `omodificari.vc2:12458` | nu (calculat) |
|
||||
| **cExplicatieTvaArt (Column16)** | `Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')` | `omodificari.vc2:12467` | **DA — explicatie TVA / id_jtva_coloana** |
|
||||
|
||||
Cele 5 coloane legate de un nomenclator sunt exact cele acceptate de dispecer —
|
||||
vezi `ArticoleNotaEditor::AreNomenclator` (`ofacturare_editare.prg:978-981`):
|
||||
|
||||
```
|
||||
PROCEDURE AreNomenclator
|
||||
LPARAMETERS tcControl
|
||||
RETURN Inlist(This.NumeCamp(m.tcControl), 'denumire', 'nume_gestiune', 'nume_val', 'taxcode', 'id_jtva_coloana')
|
||||
ENDPROC
|
||||
```
|
||||
|
||||
Observatie importanta pentru cauza de la punctul D: **`cTaxcodeArt` si `cExplicatieTvaArt` au
|
||||
Column.ReadOnly = .F.** (`omodificari.vc2:12379` / linia `Column14.ReadOnly = .F.` la
|
||||
`omodificari.vc2` in blocul 12451-12457, respectiv `Column16.ReadOnly = .F.` in blocul
|
||||
12467-12471), dar **controlul `Text1` din interiorul lor are `ReadOnly = .T.`** — vezi
|
||||
`omodificari.vc2:12759` (cTaxcodeArt.Text1) si `omodificari.vc2:12594` (cExplicatieTvaArt.Text1).
|
||||
Diferenta reala dintre ele nu e ReadOnly-ul (identic), ci **daca `ControlSource` e camp direct
|
||||
sau expresie** — vezi punctul D.
|
||||
|
||||
## B. Ce evenimente exista azi pe fiecare coloana cu nomenclator
|
||||
|
||||
| Coloana | InteractiveChange | DblClick | Buton lateral "Modificare" (`But_modificaR`) |
|
||||
|---|---|---|---|
|
||||
| cDenumireArt | DA — `omodificari.vc2:16710-16714` | **nu exista** | DA (comun, vezi E) |
|
||||
| cGestiuneArt | DA — `omodificari.vc2:16739-16743` | **nu exista** | DA (comun) |
|
||||
| cValutaArt | DA — `omodificari.vc2:16826-16830` | **nu exista** | DA (comun) |
|
||||
| cTaxcodeArt | DA — `omodificari.vc2:16815-16819` | **nu exista** | DA (comun) |
|
||||
| cExplicatieTvaArt | DA — `omodificari.vc2:16728-16732` (exista, dar nu se declanseaza — vezi D) | **nu exista** | DA (comun) |
|
||||
|
||||
Corpul e identic pentru toate cele 5 (exemplu, taxcode):
|
||||
|
||||
```
|
||||
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Text1.InteractiveChange
|
||||
Local loEditor
|
||||
loEditor = Createobject('ArticoleNotaEditor', Thisform)
|
||||
loEditor.ModificaNomenclator(Thisform.pccontrol)
|
||||
ENDPROC
|
||||
```
|
||||
|
||||
Confirmare ca `DblClick` nu exista deloc pe `grdArticoleFactura` sau pe coloanele lui: cautare
|
||||
`InteractiveChange|DblClick` in tot `omodificari.vc2` — singurele `DblClick` din fisier sunt pe
|
||||
alt grid, `grdRequest` (`omodificari.vc2:390` si `omodificari.vc2:408`, vezi F).
|
||||
|
||||
## C. Dispecerul comun — `ArticoleNotaEditor::ModificaNomenclator`
|
||||
|
||||
Definit in `COMUN\programe\ofacturare_editare.prg:1069-1137`, clasa `ArticoleNotaEditor`
|
||||
(`ofacturare_editare.prg:947-1165`).
|
||||
|
||||
Semnatura: `PROCEDURE ModificaNomenclator LPARAMETERS tcControl` — primeste un string de forma
|
||||
`alias.camp` (ex. `tvd.taxcode`, `tvd.id_jtva_coloana`).
|
||||
|
||||
Garda de intrare (`ofacturare_editare.prg:1073-1076`):
|
||||
|
||||
```
|
||||
lcCamp = This.NumeCamp(m.tcControl)
|
||||
IF !This.Editabil() OR Reccount('tvd') = 0 OR !This.AreNomenclator(m.lcCamp) OR Nvl(tvd.id_vanzare_set, 0) <> 0
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
```
|
||||
|
||||
`Do Case` de deschidere a nomenclatorului (`ofacturare_editare.prg:1078-1089`):
|
||||
|
||||
```
|
||||
DO CASE
|
||||
CASE m.lcCamp == 'denumire'
|
||||
loCauta = caut_articol()
|
||||
CASE m.lcCamp == 'nume_gestiune'
|
||||
loCauta = caut_gestiune()
|
||||
CASE m.lcCamp == 'nume_val'
|
||||
loCauta = caut_valuta()
|
||||
CASE m.lcCamp == 'id_jtva_coloana'
|
||||
loCauta = This.CautaExplicatieTva()
|
||||
OTHERWISE
|
||||
loCauta = This.CautaCodTva()
|
||||
ENDCASE
|
||||
```
|
||||
|
||||
Discrimineaza dupa **numele campului** extras din `tcControl` (nu dupa numele coloanei din grid,
|
||||
nu dupa un parametru separat) — `NumeCamp` (`ofacturare_editare.prg:966-974`) taie tot ce e
|
||||
inainte de primul `.`. `taxcode` cade pe ramura `OTHERWISE` (`CautaCodTva`,
|
||||
`ofacturare_editare.prg:1152-1158`).
|
||||
|
||||
Dupa alegere, al doilea `Do Case` (`ofacturare_editare.prg:1096-1132`) scrie valorile alese
|
||||
inapoi in `tvd`, cu ramura dedicata pentru `id_jtva_coloana`
|
||||
(`ofacturare_editare.prg:1124-1129`, recalculeaza `proc_tvav` si cheama
|
||||
`This.oForm.UpdateExplicatieSAFTArt()` + `calculeaza_valori_articol()`).
|
||||
|
||||
## D. De ce "explicatie TVA" nu se declanseaza pe InteractiveChange
|
||||
|
||||
**Nu e o garda care blocheaza si nu e o ramura lipsa in dispecer** — `id_jtva_coloana` e in
|
||||
`AreNomenclator` (`ofacturare_editare.prg:980`) si are ramura proprie in `Do Case`
|
||||
(`ofacturare_editare.prg:1085-1086`). Handlerul `InteractiveChange` exista si e corect
|
||||
(`omodificari.vc2:16728-16732`).
|
||||
|
||||
**Cauza e ca evenimentul `InteractiveChange` pur si simplu nu se produce niciodata din tastare
|
||||
pe aceasta coloana**, pentru ca `ControlSource`-ul ei e o expresie, nu un camp direct:
|
||||
|
||||
```
|
||||
Column16.ControlSource = "Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')"
|
||||
```
|
||||
(`omodificari.vc2:12467`)
|
||||
|
||||
versus `cTaxcodeArt`:
|
||||
|
||||
```
|
||||
Column14.ControlSource = "tvd.taxcode"
|
||||
```
|
||||
(`omodificari.vc2:12451`)
|
||||
|
||||
In VFP, un `Textbox` intr-o coloana de grid cu `ReadOnly = .T.` (asa cum sunt ambele,
|
||||
`omodificari.vc2:12759` si `omodificari.vc2:12594`) nu accepta editare directa de text, dar cand
|
||||
`Column.ControlSource` e un **camp real dintr-un cursor navigabil** ("bound field"), gridul
|
||||
VFP declanseaza cautarea incrementala nativa la tastare (type-ahead / seek pe recordset): tasta
|
||||
apasata muta recordul curent la prima potrivire, ceea ce schimba valoarea afisata legata de acel
|
||||
camp — iar aceasta schimbare de `Value` e cea care declanseaza `InteractiveChange`. Cand
|
||||
`ControlSource` e o **expresie** (ca la `cExplicatieTvaArt`, care afiseaza denumirea din
|
||||
`crsJtvaTemp` via `Seek`, nu campul `tvd.id_jtva_coloana` in sine), gridul nu are un camp
|
||||
indexabil pe care sa faca seek — mecanismul de cautare incrementala nu se activeaza, deci
|
||||
`Value`-ul textboxului nu se schimba niciodata din tastare, deci `InteractiveChange` nu are cum
|
||||
sa se declanseze prin tastare pe aceasta coloana.
|
||||
|
||||
Chiar comentariul din cod confirma asta, la `GotFocus`-ul aceleiasi coloane
|
||||
(`omodificari.vc2:16723`):
|
||||
|
||||
```
|
||||
*!* coloana afiseaza denumirea din crsJtvaTemp, deci ControlSource e expresie - campul editat se da explicit
|
||||
```
|
||||
|
||||
adica autorul stia deja ca `ControlSource` e o expresie si de-aia a scris explicit campul real
|
||||
(`tvd.id_jtva_coloana`) in `Thisform.pccontrol` la `GotFocus`, in loc sa se bazeze pe
|
||||
`This.ControlSource` (cum fac celelalte coloane). Explicatia asta e chiar mecanismul care face ca
|
||||
butonul lateral sa functioneze — vezi E.
|
||||
|
||||
Dublu-click nu se declanseaza pe nicio coloana pentru ca **pur si simplu nu exista niciun handler
|
||||
`DblClick`** pe `grdArticoleFactura` (vezi B) — nu e o problema specifica de "explicatie TVA", e
|
||||
valabil identic si pentru `taxcode`, doar ca la `taxcode` utilizatorul are alternativa tastarii,
|
||||
care functioneaza (camp direct).
|
||||
|
||||
## E. Cum ajunge butonul lateral "Modificare" sa deschida explicatie TVA
|
||||
|
||||
Handler: `PROCEDURE But_modificaR.Click`, `omodificari.vc2:15319-15326`:
|
||||
|
||||
```
|
||||
PROCEDURE But_modificaR.Click
|
||||
Local lcRul, loEditor
|
||||
IF thisform.pgfArticole.ActivePage = 3
|
||||
loEditor = Createobject('ArticoleNotaEditor', Thisform)
|
||||
loEditor.ModificaNomenclator(thisform.pccontrol)
|
||||
RETURN
|
||||
ENDIF
|
||||
...
|
||||
ENDPROC
|
||||
```
|
||||
|
||||
Cheia e ca butonul **nu citeste `ControlSource`-ul coloanei curente** — foloseste direct
|
||||
`Thisform.pccontrol`, o proprietate a formularului scrisa de fiecare `GotFocus` al coloanei
|
||||
active, inainte ca utilizatorul sa apuce sa tasteze ceva. Pentru `cExplicatieTvaArt`, `GotFocus`
|
||||
scrie explicit campul real (`omodificari.vc2:16722-16726`):
|
||||
|
||||
```
|
||||
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cExplicatieTvaArt.Text1.GotFocus
|
||||
*!* coloana afiseaza denumirea din crsJtvaTemp, deci ControlSource e expresie - campul editat se da explicit
|
||||
Thisform.pccontrol = 'tvd.id_jtva_coloana'
|
||||
Thisform.pncolumnorder = This.Parent.ColumnOrder
|
||||
ENDPROC
|
||||
```
|
||||
|
||||
Pentru celelalte 4 coloane, `GotFocus` foloseste `Alltrim(This.ControlSource)` direct (ex.
|
||||
`cTaxcodeArt.Text1.GotFocus`, `omodificari.vc2:16810-16813`), ceea ce functioneaza pentru ele
|
||||
pentru ca acolo `ControlSource` chiar e campul real.
|
||||
|
||||
Deci calea functioneaza pentru ca **`GotFocus` — care se declanseaza la orice intrare in celula,
|
||||
prin click simplu, tab sau navigare cu sagetile, indiferent de tipul de `ControlSource` — a
|
||||
populat deja `Thisform.pccontrol` corect inainte ca butonul sa fie apasat.** Butonul nu depinde
|
||||
deloc de `InteractiveChange`; de-aia merge la explicatie TVA desi tastarea nu merge.
|
||||
|
||||
## F. Cost estimat pentru DblClick pe fiecare coloana cu nomenclator
|
||||
|
||||
**Exista deja un sablon direct aplicabil**, chiar in cadrul modelului de "coloana Text1 in grid cu
|
||||
InteractiveChange care deschide ceva" — `grdRequest`, alt grid din acelasi formular:
|
||||
|
||||
```
|
||||
PROCEDURE grdRequest.cLabelItem.Text1.DblClick
|
||||
Thisform.lEditMode = .T.
|
||||
ENDPROC
|
||||
```
|
||||
(`omodificari.vc2:390-392`)
|
||||
|
||||
```
|
||||
PROCEDURE grdRequest.cValoareDefaultText.Text1.DblClick
|
||||
If !Empty(Nvl(crsRequest.fis_lista, ''))
|
||||
Thisform.Selectfromlookup()
|
||||
Endif
|
||||
ENDPROC
|
||||
```
|
||||
(`omodificari.vc2:408-412`)
|
||||
|
||||
Confirmare pentru VFP: intr-adevar, evenimentul se pune pe **controlul din interiorul coloanei**
|
||||
(`Column.Text1.DblClick`), exact cum arata cele doua exemple de mai sus si exact cum sunt puse
|
||||
deja `InteractiveChange` si `GotFocus` pe `grdArticoleFactura` (`Column.Text1.InteractiveChange`,
|
||||
`Column.Text1.GotFocus`) — nu exista niciun `DblClick` pus pe nivelul `Column` insusi in tot
|
||||
fisierul. Modelul `grdRequest` e potrivit ca forma (unde se pune evenimentul), dar nu ca
|
||||
continut — acolo `DblClick` face lucruri specifice acelui grid (comuta un mod de editare / arata
|
||||
un lookup din alta zona), nu apeleaza un dispecer de nomenclator.
|
||||
|
||||
Sablonul de continut potrivit e chiar `InteractiveChange`-urile deja existente pe
|
||||
`grdArticoleFactura` (`omodificari.vc2:16710-16714`, `16728-16732`, `16739-16743`,
|
||||
`16815-16819`, `16826-16830`) — toate identice, 3 linii:
|
||||
|
||||
```
|
||||
Local loEditor
|
||||
loEditor = Createobject('ArticoleNotaEditor', Thisform)
|
||||
loEditor.ModificaNomenclator(Thisform.pccontrol)
|
||||
```
|
||||
|
||||
Costul e mic si uniform: adaugarea a 5 metode noi `DblClick` (una per coloana cu nomenclator),
|
||||
fiecare cu acelasi corp de 3 linii de mai sus. Pentru ca toate cele 5 `GotFocus` seteaza deja
|
||||
`Thisform.pccontrol` corect — fie din `ControlSource` (denumire, gestiune, valuta, taxcode), fie
|
||||
explicit (explicatie TVA, `omodificari.vc2:16724`) — folosirea lui `Thisform.pccontrol` in
|
||||
`DblClick` functioneaza uniform pe toate cele 5, inclusiv pe explicatie TVA, fara sa depinda de
|
||||
mecanismul de seek care lipseste la coloana cu `ControlSource` expresie. Practic acelasi apel pe
|
||||
care il face deja `But_modificaR.Click` (`omodificari.vc2:15319-15326`, vezi E).
|
||||
|
||||
Nu e nevoie de nicio schimbare in dispecer (`ModificaNomenclator`) sau in `AreNomenclator` — ele
|
||||
deja accepta toate cele 5 campuri.
|
||||
229
docs/cercetare/rec_r6_zecimale_si_aspect_grid.md
Normal file
229
docs/cercetare/rec_r6_zecimale_si_aspect_grid.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# R6: zecimale la Pret si aspect gri al campurilor editabile, grid Articole (frm_modific2024)
|
||||
|
||||
Cercetare, fara modificari de cod. Zona: `COMUN\clase\omodificari.vc2`, clasa `frm_modific2024`
|
||||
(liniile 6375-16838), grid-ul de articole al facturii editate: `pgfArticole.PAGE3.grdArticoleFactura`
|
||||
(nu vechiul `Grid1`/`Column1..44` — acela e alt grid, RecordSource `"tACT"`, folosit pentru notele
|
||||
contabile, nu pentru articolele facturii; grid-ul de articole are 16 coloane numite, ADD OBJECT la
|
||||
`omodificari.vc2:12330`).
|
||||
|
||||
## Partea 1 — zecimalele la pret
|
||||
|
||||
### A. Coloanele monetare din grid si masca lor
|
||||
|
||||
RecordSource al grid-ului = cursorul `tvd` (creat in `COMUN\programe\ofacturare_editare.prg:278`,
|
||||
populat in `IncarcaArticoleFactura`, `ofacturare_editare.prg:294-328`, din view-ul Oracle
|
||||
`VVANZARI_ARTICOLE`).
|
||||
|
||||
| Coloana (Header) | Name | ControlSource | Format/InputMask | Linie |
|
||||
|---|---|---|---|---|
|
||||
| Pret | cPretArt | `tvd.pret` | `get_mask(12,gnPPRET)` | `omodificari.vc2:12391` |
|
||||
| Pret achizitie | cPretAchizitieArt | `tvd.pret_achizitie` | `get_mask(12,gnPPRET)` | `omodificari.vc2:12400` |
|
||||
| Pret cu TVA | cPretCuTvaArt | `tvd.pret_cu_tva` | **fara Format/InputMask** | `omodificari.vc2:12404-12409` |
|
||||
| Discount unitar | cDiscountUnitarArt | `tvd.discount_unitar` | `get_mask(12,gnPPRET)` | `omodificari.vc2:12426` |
|
||||
| Cantitate | cCantitateArt | `tvd.cantitate` | `get_mask(12,gnPCANT)` | `omodificari.vc2:12382` |
|
||||
| Valoare | cValoareArt | `tvd.valoare` | `get_mask(14,gnPa)` | `omodificari.vc2:12463` |
|
||||
| TVA % | cProcTvavArt | `tvd.proc_tvav` | `"9.99"` (fix, 2 zecimale) | `omodificari.vc2:12417` |
|
||||
|
||||
Structura cursorului `tvd` (`ofacturare_editare.prg:278-281`):
|
||||
|
||||
```
|
||||
pret N(14,4) NULL, pret_cu_tva N(14,4) NULL, proc_tvav N(6,4) NULL, discount_unitar N(14,4) NULL,
|
||||
..., pret_achizitie N(14,4) NULL, ..., cantitate N(12,3) NULL (nu e in acest citat, vezi mai jos)
|
||||
```
|
||||
|
||||
Precizia campurilor din cursor e **hardcodata** (4 zecimale la pret/pret_cu_tva/discount_unitar/
|
||||
pret_achizitie, 3 la cantitate — vezi linia completa `ofacturare_editare.prg:278`), independent de
|
||||
`gnPPret`/`gnPCant`. Asta e doar plafonul de stocare; ce se AFISEAZA il decide InputMask-ul de pe
|
||||
coloana (cand exista).
|
||||
|
||||
### B. De unde vin cele 3 zecimale afisate la "Pret"
|
||||
|
||||
**Nu dintr-un bug de masca lipsa.** Coloana "Pret" (`cPretArt`, `tvd.pret`) ARE InputMask legat de
|
||||
o variabila globala: `get_mask(12,gnPPRET)`, `omodificari.vc2:12391`. Daca `gnPPRET` = 3 in mediul
|
||||
curent, InputMask-ul produce exact 3 zecimale — comportamentul e "controlat de o variabila globala",
|
||||
exact ce cere utilizatorul. **Problema e ca variabila legata e cea gresita**: `gnPPRET`, nu
|
||||
`gnPPretV` — vezi punctul D.
|
||||
|
||||
### C. Ce este `gnPPret` si care e sablonul consacrat
|
||||
|
||||
`gnPPret` e o variabila PUBLIC incarcata din firma (parametru `OPTIUNI.PPRET`), documentata deja in
|
||||
cercetarea anterioara `docs\cercetare\rec_fact008_pret_achizitie_zecimale.md:28`:
|
||||
|
||||
> `OPTIUNI.PPRET` (precizia pretului de achizitie) = **3** (in mediul de productie investigat acolo)
|
||||
|
||||
N-am gasit in acest proiect linia exacta unde `gnPPret` e citit din `OPTIUNI` si atribuit (cautare
|
||||
in `COMUN\programe\oinit_optiuni.prg` — gaseste `gnPA`, `gnPC`, `gnPCurs`, `gnPPretV`, `gnZ`, dar
|
||||
nu `gnPPret`; probabil incarcat generic, printr-un mecanism care nu foloseste literal sirul
|
||||
`"gnPPret ="`) — **NESTABILIT exact unde**, dar valoarea si semantica (precizie **achizitie**) sunt
|
||||
confirmate atat de raportul anterior cat si de tiparul de folosire de mai jos.
|
||||
|
||||
Exista o familie de variabile de precizie, cu roluri distincte, confirmate prin comentarii si prin
|
||||
folosire consecventa in cod:
|
||||
|
||||
- `gnPPret` — precizie **pret de achizitie** (cost). Folosire: `ofacturare_stoc.prg:364`
|
||||
`Alltrim(Str(poArticol.pret_achizitie,18,Max(gnPPret,4)))`; masti pe `pret_achizitie` in
|
||||
`ointroduceri.vc2:16390,19638` (`get_mask(12,gnPPret)`, coloana NIR).
|
||||
- `gnPPretV` — precizie **pret de vanzare**, comentata explicit
|
||||
`COMUN\programe\oinit_optiuni.prg:515`: `Store 0 To gnPPretV && nr. de zecimale pret vanzare`.
|
||||
Folosire masiva in `ofacturare_comun.prg` (peste 40 de locuri: `pretftva`, `pretctva`,
|
||||
`pretv_orig`, `discountftva` etc. — toate rotunjite/formatate cu `gnPPretV`) si, esential, la
|
||||
**scrierea efectiva a pretului de vanzare al liniei de factura**:
|
||||
`ofacturare_stoc.prg:367` — `Alltrim(Str(poArticol.Pret,18,Iif(poDate.in_valuta=1,gnPVal,gnPPretV)))`
|
||||
(in functia care insereaza randul in `VANZARI_DETALII`, aceeasi coloana `PRET` pe care o incarca
|
||||
`tvd.pret`).
|
||||
- `gnPCant` — precizie cantitate (`get_mask(12,gnPCANT)`, `omodificari.vc2:12382`).
|
||||
- `gnPc` (`gnPC`) — precizie de calcul/valoare (`get_mask(14,gnPa)`/`gnPc` la coloana Valoare).
|
||||
- `gnPa` (`gnPA`) — precizie de afisare generica (folosit la `cValoareArt`).
|
||||
|
||||
Sablonul consacrat de constructie a mastii: `get_mask(nrCaractere, nrZecimale)`
|
||||
(`COMUN\programe\proceduri_comune.prg:1090-1110`) — construieste un sir `"999 999.999"` cu atatea
|
||||
`9` dupa punct cate zecimale i se dau; **nu exista logica speciala in `get_mask`**, deci diferenta
|
||||
de comportament vine strict din CE variabila i se paseaza la fiecare coloana.
|
||||
|
||||
### D. Modelul corect exista deja, in acelasi fisier de clase — `ofacturare.vc2`
|
||||
|
||||
`COMUN\clase\ofacturare.vc2:3489-3498`, grid de rulaje/stoc, doua coloane invecinate:
|
||||
|
||||
```
|
||||
Column5.ControlSource = "pret", ;
|
||||
Column5.InputMask = (get_mask(14,gnPPret)), ; && cost / achizitie
|
||||
Column5.Name = "cPret", ;
|
||||
...
|
||||
Column6.ControlSource = "pretv", ;
|
||||
Column6.InputMask = (get_mask(14,gnPPretV)), ; && vanzare
|
||||
Column6.Name = "cPretv", ;
|
||||
```
|
||||
|
||||
Acelasi tipar apare si in `COMUN\clase\ocomenzi.vc2:1174`
|
||||
(`This._grdrow2.cPret.InputMask = get_mask(14,gnPPretV)` — pret de vanzare pe comanda) si in
|
||||
`ointroduceri.vc2:10071,16390,19638` (`get_mask(...,gnPPret)` — toate pe campuri de **achizitie**,
|
||||
in formularele de NIR).
|
||||
|
||||
**Regula confirmata in tot codul suitei: campul care reprezinta pretul de ACHIZITIE foloseste
|
||||
`gnPPret`; campul care reprezinta pretul de VANZARE al liniei foloseste `gnPPretV`.** Chiar scrierea
|
||||
in Oracle a coloanei `VANZARI_DETALII.PRET` (aceeasi coloana din care se umple `tvd.pret`) respecta
|
||||
aceasta regula: `ofacturare_stoc.prg:367` foloseste `gnPPretV`, nu `gnPPret`.
|
||||
|
||||
### Concluzie Partea 1 — defectul
|
||||
|
||||
`omodificari.vc2:12391` — `Column6.InputMask = (get_mask(12,gnPPRET))` pentru **`cPretArt`**
|
||||
(`tvd.pret`, coloana "Pret" = pretul de VANZARE al liniei) foloseste variabila gresita din familie:
|
||||
**`gnPPRET`** (precizie achizitie) in loc de **`gnPPretV`** (precizie vanzare, cea corecta pentru
|
||||
acest camp). Coloana vecina, `cPretAchizitieArt` (`omodificari.vc2:12400`, `tvd.pret_achizitie`),
|
||||
foloseste corect `gnPPRET` — e evident ca la scrierea acestei sectiuni de grid s-a copiat masca de
|
||||
la achizitie si pe coloana de vanzare de langa ea.
|
||||
|
||||
Daca in mediul reclamat `OPTIUNI.PPRET` (achizitie) = 3 si `OPTIUNI.PPRETV` (vanzare) e alta
|
||||
valoare (tipic 2), exact asta explica simptomul: coloana "Pret" (vanzare) afiseaza 3 zecimale
|
||||
pentru ca "imprumuta" precizia de achizitie, nu pe cea de vanzare.
|
||||
|
||||
Coloana "Pret cu TVA" (`cPretCuTvaArt`) nu are deloc InputMask (`omodificari.vc2:12404-12409`) — nu
|
||||
e sursa celor "3 zecimale" reclamate la "Pret", dar e un defect inrudit: afiseaza precizia bruta a
|
||||
campului din cursor (4 zecimale, `pret_cu_tva N(14,4)`), necontrolata de nicio variabila globala.
|
||||
|
||||
**Schita de interventie** (fara aplicare): `Column6.InputMask = (get_mask(12,gnPPRET))` ->
|
||||
`Column6.InputMask = (get_mask(12,gnPPretV))` la `omodificari.vc2:12391`, dupa modelul din
|
||||
`ofacturare.vc2:3497` / `ocomenzi.vc2:1174`.
|
||||
|
||||
## Partea 2 — fundalul gri al campurilor editabile
|
||||
|
||||
### E-F. Sursa reala a griului si divergenta Column.ReadOnly vs Text1.ReadOnly
|
||||
|
||||
Confirmat: aproape toate coloanele au `DynamicForeColor` (nu `BackColor`) la nivel de coloana —
|
||||
un singur `IIF` pentru culoarea textului dupa `tvd.sters`/`id_vanzare_set`, identic pe toate cele
|
||||
16 coloane (ex. `omodificari.vc2:12350`). **Niciun `DynamicBackColor`, niciun `Enabled=.F.`** pe
|
||||
coloane. Culoarea de fundal reala vine din proprietatea `BackColor` fixa a controlului `Text1`
|
||||
inclus in fiecare coloana (`ADD OBJECT '...Text1' AS textbox`), si **nu coincide cu
|
||||
`Column.ReadOnly`**:
|
||||
|
||||
| Camp | Column.ReadOnly | Column linie | Text1.ReadOnly | Text1.BackColor | Text1 linii |
|
||||
|---|---|---|---|---|---|
|
||||
| Denumire (cDenumireArt) | `.F.` (editabil) | 12354 | **`.T.`** | **225,225,225 (gri)** | 12527-12536 |
|
||||
| Cod material (cCodmatArt) | `.T.` | 12361 | `.T.` | 225,225,225 | 12506-12515 (consistent) |
|
||||
| Serie (cSerieArt) | `.F.` (editabil) | 12368 | **`.T.`** | **225,225,225 (gri)** | 12734-12743 |
|
||||
| Lot (cLotArt) | `.F.` (editabil) | 12375 | **`.T.`** | **225,225,225 (gri)** | 12631-12640 |
|
||||
| Cantitate (cCantitateArt) | `.F.` | 12384 | `.F.` | 255,255,255 (alb) | 12485-12494 (consistent) |
|
||||
| Pret (cPretArt) | `.F.` | 12393 | `.F.` | 255,255,255 (alb) | 12673-12682 (consistent) |
|
||||
| Pret achizitie (cPretAchizitieArt) | `.F.` | 12402 | `.F.` | 255,255,255 (alb) | 12652-12661 (consistent) |
|
||||
| TVA % (cProcTvavArt) | `.F.` (editabil) | 12419 | **`.T.`** | **225,225,225 (gri)** | 12713-12722 |
|
||||
| Discount unitar (cDiscountUnitarArt) | `.T.` | 12428 | `.T.` | 225,225,225 | 12548-12557 (consistent) |
|
||||
| Gestiune (cGestiuneArt) | `.F.` (editabil) | 12435 | **`.T.`** | **225,225,225 (gri)** | 12610-12619 |
|
||||
| Valuta (cValutaArt) | `.F.` (editabil) | 12442 | **`.T.`** | **225,225,225 (gri)** | 12797-12806 |
|
||||
| Explicatie (cExplicatieArt) | `.F.` (editabil) | 12449 | **`.T.`** | **225,225,225 (gri)** | 12569-12578 |
|
||||
| Taxcode (cTaxcodeArt) | `.F.` (editabil) | 12456 | **`.T.`** | **225,225,225 (gri)** | 12755-12764 |
|
||||
| Valoare (cValoareArt) | `.T.` | 12465 | `.T.` | 225,225,225 | 12776-12785 (consistent) |
|
||||
| Explicatie TVA (cExplicatieTvaArt) | `.F.` (editabil) | 12472 | **`.T.`** | **225,225,225 (gri)** | 12589-12598 |
|
||||
|
||||
**9 din 16 coloane sunt declarate editabile la nivel de `Column.ReadOnly=.F.` dar controlul `Text1`
|
||||
inclus e `ReadOnly=.T.` cu fundal gri (225,225,225).** In VFP, cand o coloana de grid contine un
|
||||
control custom (nu editorul implicit), editabilitatea reala vine din controlul inclus, nu din
|
||||
`Column.ReadOnly` — deci aceste 9 campuri **nu accepta tastare directa in celula**, indiferent ce
|
||||
spune `Column.ReadOnly`. Asta explica de ce aratau gri: griul e corect pentru "nu se editeaza prin
|
||||
tastare directa".
|
||||
|
||||
Pentru unele din ele exista insa un mecanism real de editare, prin dialog, nu prin tastare:
|
||||
`cGestiuneArt.Text1.GotFocus` (`omodificari.vc2:12734`... de fapt liniile de cod sunt la
|
||||
`16734-16744`) seteaza `Thisform.pccontrol` si `InteractiveChange` deschide un editor:
|
||||
|
||||
```
|
||||
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cGestiuneArt.Text1.InteractiveChange (16739)
|
||||
loEditor = Createobject('ArticoleNotaEditor', Thisform)
|
||||
loEditor.ModificaNomenclator(Thisform.pccontrol)
|
||||
```
|
||||
|
||||
Acelasi tipar la `cExplicatieTvaArt` (`16722-16732`). Pentru `cSerieArt`, `cLotArt`, `cExplicatieArt`
|
||||
exista doar un handler `.When` (`16745`, `16716`, `16804`) care blocheaza intrarea in celula cand
|
||||
`Thisform.lArticoleReadOnly` sau `id_vanzare_set<>0`, dar **niciun cod care sa comute
|
||||
`Text1.ReadOnly` la `.F.`** — n-am gasit nicio atribuire dinamica `.Text1.ReadOnly = ...` in tot
|
||||
corpul clasei `frm_modific2024` pentru aceste coloane (cautare exhaustiva pe intervalul
|
||||
6375-16838). **Concluzie verificabila pe cod**: cu configuratia actuala, `cSerieArt`, `cLotArt`,
|
||||
`cExplicatieArt`, `cValutaArt`, `cTaxcodeArt`, `cDenumireArt`, `cProcTvavArt` **nu au niciun
|
||||
mecanism de editare activ in acest grid** — nici tastare (Text1.ReadOnly=.T.), nici editor prin
|
||||
dublu-clic/InteractiveChange (spre deosebire de cGestiuneArt/cExplicatieTvaArt, care au editor).
|
||||
Daca utilizatorul le percepe ca "editabile", fie testeaza cu date/build vechi, fie editarea se
|
||||
intampla prin alta cale neinventariata aici (buton `but_modificaR` — vezi mai jos, NESTABILIT unde
|
||||
e definit click-ul lui).
|
||||
|
||||
`Thisform.but_modificaR.Enabled` se seteaza in `AfterRowColChange`
|
||||
(`omodificari.vc2:16678-16682`) dupa coloana curenta si `!lArticoleReadOnly`, sugerand un buton
|
||||
extern de "Modifica" — dar **n-am gasit definitia obiectului `but_modificaR` sau handler-ul lui de
|
||||
`Click` in `omodificari.vc2`** (cautare `grep -n "but_modificaR"` in tot fisierul: doar cele 7
|
||||
referinte de mai sus, toate `.Enabled = ...` sau `.Visible = ...`, niciun `ADD OBJECT` sau
|
||||
`PROCEDURE but_modificaR...Click`). **NESTABILIT**: butonul e probabil mostenit dintr-o clasa
|
||||
parinte in afara acestui fisier — nu s-a investigat mai departe (ar necesita cautare in
|
||||
`_frm_base.vcx`/alte `.vc2` din `COMUN\clase`, in afara scopului "aceasta clasa").
|
||||
|
||||
### G. Conventia de culoare in restul aplicatiei
|
||||
|
||||
**Exista o conventie, si e vizibila chiar in acelasi fisier de clasa**, pe pagina "Rulaje"
|
||||
(`pgfArticole.PAGE1.grdRulaje`, alt tab al aceluiasi formular):
|
||||
|
||||
- editabil prin tastare directa: `BackColor = 255,255,255` (alb) + `ReadOnly = .F.` — ex.
|
||||
`cPret.Text1`, `omodificari.vc2:9990-9999`.
|
||||
- editabil prin dialog (GotFocus seteaza `pccontrol`, InteractiveChange deschide editor):
|
||||
`BackColor = 160,255,205` (**verde deschis**) + `ReadOnly = .F.` — ex. `cGest.Text1`
|
||||
(`omodificari.vc2:9689-9698`) si `cDenumire.Text1` (`omodificari.vc2:9595-9604`).
|
||||
- needitabil: `BackColor = 225,225,225` (gri) + `ReadOnly = .T.`.
|
||||
|
||||
Pe pagina "Articole" (`grdArticoleFactura`, PAGE3 — grid-ul reclamat), campul `cGestiuneArt` are
|
||||
**exact acelasi mecanism de editare prin dialog** ca `cGest` de pe pagina Rulaje (acelasi tipar
|
||||
GotFocus/InteractiveChange/`ArticoleNotaEditor`), dar culoarea lui e **gri + ReadOnly=.T.**
|
||||
(`omodificari.vc2:12610-12619`), nu verde + ReadOnly=.F. ca omologul lui de pe Rulaje. **Asta e
|
||||
dovada directa a inconsistentei**: acelasi tipar functional (editare prin editor extern) e codat
|
||||
cu doua culori diferite in doua pagini ale aceluiasi formular — verde (corect, semnaleaza "se
|
||||
editeaza altfel") pe Rulaje, gri (gresit, semnaleaza "nu se editeaza") pe Articole.
|
||||
|
||||
## Rezumatul defectelor gasite (fara aplicare)
|
||||
|
||||
1. **Zecimale**: `Column6.InputMask` la `cPretArt` (`omodificari.vc2:12391`) foloseste `gnPPRET`
|
||||
(precizie achizitie) in loc de `gnPPretV` (precizie vanzare) — schita: schimbat argumentul.
|
||||
2. **Aspect gri**: 9 coloane (`cDenumireArt`, `cSerieArt`, `cLotArt`, `cProcTvavArt`,
|
||||
`cGestiuneArt`, `cValutaArt`, `cExplicatieArt`, `cTaxcodeArt`, `cExplicatieTvaArt`) au
|
||||
`Column.ReadOnly=.F.` dar `Text1.ReadOnly=.T.` + `BackColor=225,225,225` — contrazic propria
|
||||
lor declaratie de editabilitate. Dintre ele, doar `cGestiuneArt` si `cExplicatieTvaArt` au
|
||||
mecanism real de editare (editor extern) si ar trebui colorate verde
|
||||
(`160,255,205`, ca modelul de pe Rulaje) + `ReadOnly=.F.` pe Text1; restul (`cDenumireArt`,
|
||||
`cSerieArt`, `cLotArt`, `cProcTvavArt`, `cValutaArt`, `cExplicatieArt`, `cTaxcodeArt`) fie n-au
|
||||
deloc cale de editare activa in cod (raman corect needitabile — atunci `Column.ReadOnly` ar
|
||||
trebui pus `.T.` ca sa nu mai contrazica aspectul), fie editarea lor se face pe o cale
|
||||
neidentificata in aceasta cercetare (butonul `but_modificaR`, NESTABILIT unde e definit).
|
||||
53
docs/cercetare/rec_regresie_cantitate.md
Normal file
53
docs/cercetare/rec_regresie_cantitate.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Regresie dupa modificarea `omodificari.vc2` (validare cantitate + garda S4b)
|
||||
|
||||
Context: s-au schimbat doua puncte in `COMUN\clase\omodificari.vc2` (write-back deja facut si
|
||||
verificat inainte de aceasta rulare):
|
||||
|
||||
1. `:14386` — validarea de cantitate `Nvl(cantitate,0) <= 0` -> `= 0` (negativul e legitim: retur,
|
||||
storno, linii de discount).
|
||||
2. `:14438` — blocul de sincronizare S4b de la salvare a primit garda
|
||||
`AND Used('tvd') AND m.lnLiniiActive > 0`.
|
||||
|
||||
Scopul acestei rulari: confirmarea ca suita de regresie ramane la baseline dupa aceste doua
|
||||
schimbari.
|
||||
|
||||
## Rezultate
|
||||
|
||||
Fiecare suita a fost rulata individual (o singura lansare de `vfp9.exe -A -T` per apel de tool),
|
||||
cu `.FXP`/`.fxp` sterse inainte de fiecare rulare si mtime-ul logului verificat imediat dupa,
|
||||
inainte de a citi vreo cifra.
|
||||
|
||||
| suita | baseline | obtinut | mtime log | exit code | verdict |
|
||||
|---|---|---|---|---|---|
|
||||
| `test_adauga_linie_articol` | 20 PASS / 0 FAIL | 20 PASS / 0 FAIL | 2026-08-13 03:05:52 | 0 | OK, la baseline |
|
||||
| `test_adauga_linie_valuta` | 16 PASS / 0 FAIL | 16 PASS / 0 FAIL | 2026-08-13 03:06:02 | 0 | OK, la baseline |
|
||||
| `test_verdict_act_rul` | 26 PASS / 0 FAIL | 26 PASS / 0 FAIL | 2026-08-13 03:06:12 | 0 | OK, la baseline |
|
||||
| `test_incarca_vanzare_din_nota` | 5 PASS / 0 FAIL (`done`) | 5 PASS / 0 FAIL (`done`) | 2026-08-13 03:06:25 | 0 | OK, la baseline |
|
||||
| `test_efactura_readonly` | 23 PASS / 0 FAIL | 23 PASS / 0 FAIL | 2026-08-13 03:06:35 | 0 | OK, la baseline |
|
||||
| `test_s4b_sincronizare` | 42 PASS / 0 FAIL | 42 PASS / 0 FAIL | 2026-08-13 03:06:44 | 0 | OK, la baseline |
|
||||
| `test_s4b_dialog` | 35 PASS / 0 FAIL | 35 PASS / 0 FAIL | 2026-08-13 03:06:54 | 0 | OK, la baseline |
|
||||
| `test_s7_rotunjire` | 6 PASS / 0 FAIL | 6 PASS / 0 FAIL | 2026-08-13 03:07:06 | 0 | OK, la baseline |
|
||||
|
||||
Deja rulate anterior de sesiunea principala (nu au fost reluate in aceasta misiune):
|
||||
|
||||
| suita | baseline | obtinut |
|
||||
|---|---|---|
|
||||
| `test_s5_validari_articole` | 35/0 | 35/0 (la baseline) |
|
||||
| `test_page3_articole` | 14/2 | 14/2 (la baseline) |
|
||||
|
||||
## Suite care au cerut rerulare
|
||||
|
||||
Niciuna. Toate cele 8 suite rulate in aceasta misiune au pornit curat, au scris logul cu mtime
|
||||
proaspat imediat dupa lansare si au iesit cu `exit=0` la prima incercare — nu a fost nevoie de
|
||||
nicio garda de timp (`timeout`) declansata si nicio rerulare.
|
||||
|
||||
## Procese ramase
|
||||
|
||||
Zero procese `vfp9.exe` ramase dupa terminarea rularilor (verificat cu
|
||||
`Get-Process vfp9 -ErrorAction SilentlyContinue`, rezultat gol).
|
||||
|
||||
## Concluzie
|
||||
|
||||
Toate cele 8 suite din lista primita plus cele 2 deja rulate de sesiunea principala (10 suite in
|
||||
total) raporteaza cifre identice cu baseline-ul. Nu s-a observat nicio regresie in urma celor doua
|
||||
modificari din `omodificari.vc2`.
|
||||
168
docs/cercetare/rec_s8_matrice.md
Normal file
168
docs/cercetare/rec_s8_matrice.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# S8 — matricea pe tipuri de sursa, cele 4 documente
|
||||
|
||||
Test: `COMUN\utile\Teste\editare_factura\test_s8_matrice_surse.prg`, log:
|
||||
`COMUN\utile\Teste\editare_factura\test_s8_matrice_surse_log.txt` (rescris la fiecare rulare -
|
||||
cifrele de mai jos sunt citite din log **imediat dupa fiecare rulare**, inainte de a activa
|
||||
urmatorul document, si verificate independent prin `sqlplus`).
|
||||
|
||||
Matricea completa (4 documente) a fost rulata, cate unul pe rand, activat prin `gaCazuriActive` in
|
||||
`test_s8_matrice_surse.prg`. Codul (`EditeazaDinRoafacturare`/`EditeazaDinRegistruJurnal`/
|
||||
`VerificaDupaEditare`/`CalculeazaTotaluriS4b`) e neschimbat intre rulari - parametrizarea prin
|
||||
`id_vanzare` a functionat neschimbata pe toate cele 4 tipuri.
|
||||
|
||||
## Sinteza cifrelor `REZULTAT` (autoritare, din log)
|
||||
|
||||
| Document | Tip sursa | REZULTAT | FAIL-uri | Cauza FAIL-urilor |
|
||||
|---|---|---|---|---|
|
||||
| 1048 | lista de preturi (tip 1) | **44 PASS / 0 FAIL** | - | - |
|
||||
| 1055 | factura din contract (tip 2) | **44 PASS / 0 FAIL** | - | - |
|
||||
| 1052 | aviz (tip 22) | **26 PASS / 1 FAIL** | 1 | garda `ReferinteDocumenteNota` blocheaza corect intrarea ROAFACTURARE (constatare, nu defect de test - vezi mai jos) |
|
||||
| 1054 | factura din aviz (tip 4) | **42 PASS / 2 FAIL** | 2 | `Reccount(trul)=0` pe ambele intrari - normal pentru tip 4 (vezi mai jos), nu defect |
|
||||
|
||||
Niciun `EROARE` in niciunul din cele 4 loguri.
|
||||
|
||||
## Document 1048 — lista de preturi (tip 1)
|
||||
|
||||
Deja raportat integral in versiunea anterioara a acestui document (ambele intrari au reusit complet,
|
||||
0 anomalii). Stare finala neschimbata de rularile ulterioare pe celelalte documente: `cod=1140918`,
|
||||
`id_fact=8009658` (neschimbat), totaluri `250.01/52.50/302.51` (neschimbate), 1 linie activa.
|
||||
|
||||
## Document 1055 — factura din contract (tip 2)
|
||||
|
||||
Ambele intrari au reusit complet (`COMMIT`), fara nicio anomalie in lantul de scriere.
|
||||
|
||||
| | inainte | dupa ROAFACTURARE | dupa registrul jurnal |
|
||||
|---|---|---|---|
|
||||
| `cod` | 1140911 | 1140919 | **1140920** |
|
||||
| `id_fact` | 8009680 | 8009680 | 8009680 (neschimbat) |
|
||||
| totaluri | 200.00 / 42.00 / 242.00 | neschimbate | neschimbate |
|
||||
| linii active | 2 | 2 | 2 |
|
||||
|
||||
`vact_tot`: cod 1140911 si 1140919 (notele vechi) - toate randurile `STERS=1`; cod 1140920 (nota
|
||||
curenta) - toate randurile `STERS=0`.
|
||||
|
||||
**Constatare (nu defect)**: verdictul S4b e **divergent** pe tot parcursul - `ACT=242`, `RUL=121`
|
||||
(exact jumatate), neschimbat de cele doua editari (`121 -> 121` pe ambele treceri). Verdictul e
|
||||
etichetat explicit "informativ, nu e eroare" de catre `frm_modific2024` insusi (decizia din S4b:
|
||||
formularul arata divergenta, nu o blocheaza). Asertiile testului nu presupun `ACT=RUL` - verifica
|
||||
doar ca fiecare total ramane **concordant fata de inainte de editare**, ceea ce s-a confirmat.
|
||||
|
||||
## Document 1052 — aviz (tip 22)
|
||||
|
||||
**Nu e factura** - constatare confirmata, exact cum a semnalat briefingul.
|
||||
|
||||
### Intrarea ROAFACTURARE (`do_editare_factura`): BLOCATA de garda, corect
|
||||
|
||||
```
|
||||
FAIL ... document fara referinte / netrimis in eFactura [.T.]
|
||||
```
|
||||
|
||||
`ReferinteDocumenteNota(2026, 8, 1140908)` a intors adevarat - verificat direct in Oracle:
|
||||
`ACT.id_factc = 8009677` (id_fact-ul avizului 1052) exista pe cod=1140910, care e nota lui **1054**
|
||||
("factura din aviz", emisa din acest aviz). Garda functioneaza exact cum trebuie: **blocheaza
|
||||
editarea unui document sursa care are deja o factura emisa din el** - nicio scriere nu s-a produs
|
||||
(verificat: `cod` a ramas `1140908` neschimbat pana la intrarea urmatoare). `EsteInEFactura` nu a
|
||||
contribuit (`anaf_efactura` nu are randuri pentru `id_fact=8009677`).
|
||||
|
||||
Aceasta e o `FAIL` de asertie **asteptata si corecta** - testul a presupus (mostenit din modelul
|
||||
S5, gandit pentru facturi) ca documentul e editabil; pentru un aviz cu factura deja emisa din el,
|
||||
nu e. Marcata ca atare, nu ca defect.
|
||||
|
||||
### Intrarea registru jurnal ROACONT (`do_modifica`): A REUSIT COMPLET, fara aceeasi garda
|
||||
|
||||
```
|
||||
PASS ... blocul ScrieArticoleFacturaEditate s-a executat pe aceasta cale (garda satisfacuta) [.T.]
|
||||
PASS ... lantul complet a reusit (COMMIT) [lnSucces=1]
|
||||
```
|
||||
|
||||
**Constatare reala, de raportat lui Marius**: `afisjurcom.do_modifica` (`comun.vc2:2222-2569`) **nu
|
||||
are garda `ReferinteDocumenteNota`/`EsteInEFactura`** in secventa reprodusa (confirmat deja indirect
|
||||
de `test_s5_al_doilea_intrare.prg`, dar niciodata exercitata pana acum pe un document care CHIAR are
|
||||
o referinta reala). Rezultat: editarea prin registrul jurnal a trecut pana la `COMMIT` pe un
|
||||
document (1052) pe care intrarea ROAFACTURARE l-a blocat explicit din acelasi motiv.
|
||||
|
||||
Pe aceasta rulare **fara** consecinta vizibila (editarea nu a modificat nicio linie - doar a
|
||||
realocat `cod`-ul si a refacut nota; documentul 1054, care il refera, a fost verificat neschimbat:
|
||||
`cod=1140910`, `id_fact=8009679`, totaluri `252.07/52.93/305.00`, toate identice cu inainte).
|
||||
Legatura structurala ramane valida pentru ca trece prin `id_fact`/`id_vanzare`, niciodata prin `cod`
|
||||
(S6, deja inchis). Dar daca editarea prin ROACONT ar fi inclus si o modificare de continut pe un
|
||||
document cu referinte reale, nimic nu ar fi oprit-o - asimetria intre cele doua puncte de intrare e
|
||||
reala, nu doar teoretica. **Nu s-a atins `comun.vc2`/`omodificari.vc2` pentru a o corecta** (livrare
|
||||
inchisa) - se raporteaza pentru decizia lui Marius.
|
||||
|
||||
| | inainte | dupa ROAFACTURARE | dupa registrul jurnal |
|
||||
|---|---|---|---|
|
||||
| `cod` | 1140908 | *(blocat, nescris)* | **1140921** |
|
||||
| `id_fact` | 8009677 | - | 8009677 (neschimbat) |
|
||||
| totaluri | 271.17 / 56.94 / 328.11 | - | neschimbate |
|
||||
| linii active | 2 | - | 2 |
|
||||
|
||||
`vact_tot`: cod 1140908 (nota veche) - toate randurile `STERS=1`; cod 1140921 (nota curenta) -
|
||||
toate randurile `STERS=0`. S4b: `ACT=RUL=328.11`, verdict sincronizat, neschimbat de editare.
|
||||
|
||||
## Document 1054 — factura din aviz (tip 4)
|
||||
|
||||
Ambele intrari au reusit complet (`COMMIT`), singurele 2 `FAIL` din aceasta rulare sunt pe
|
||||
`Reccount(trul)>0`.
|
||||
|
||||
**Stabilit inainte de editare, nu ghicit**: interogare pe toate cele 6 documente `tip=4` din baza
|
||||
(`145, 665, 668, 697, 974, 1054`) - **toate** au exact 2 randuri `ACT` si **0** randuri `RUL`,
|
||||
indiferent de `total_cu_tva`. Tiparul e 100% consistent pe populatia completa de documente tip 4,
|
||||
nu doar pe 1054 - **normal pentru tip 4**, nu o particularitate a acestui document. Explicatia
|
||||
structurala plauzibila: miscarea de stoc s-a inregistrat deja la emiterea avizului sursa; factura
|
||||
emisa din aviz nu mai genereaza randuri `RUL` proprii (ar dubla miscarea), doar nota contabila
|
||||
(`ACT`). Cele doua `FAIL` (`Reccount(trul)>0` cerut de asertia generica, `Reccount(trul)=0` gasit)
|
||||
sunt **asteptate si corecte pentru acest tip de document** - verificarea "rulaje refacute" nu e
|
||||
concludenta pentru tip 4 (nu exista rulaje de refacut), nu semnaleaza un defect.
|
||||
|
||||
Restul verificarilor (nota veche/noua, `id_fact`, totaluri denormalizate, S4b ACT concordant,
|
||||
stoc) au trecut integral pe ambele intrari.
|
||||
|
||||
| | inainte | dupa ROAFACTURARE | dupa registrul jurnal |
|
||||
|---|---|---|---|
|
||||
| `cod` | 1140910 | 1140922 | **1140923** |
|
||||
| `id_fact` | 8009679 | 8009679 | 8009679 (neschimbat) |
|
||||
| totaluri | 252.07 / 52.93 / 305.00 | neschimbate | neschimbate |
|
||||
| linii active | 1 | 1 | 1 |
|
||||
|
||||
`vact_tot`: cod 1140910 si 1140922 (notele vechi) - toate randurile `STERS=1`; cod 1140923 (nota
|
||||
curenta) - toate randurile `STERS=0`. S4b: `ACT=305`, `RUL=0`, verdict divergent (informativ),
|
||||
neschimbat de editare (`305->305`, `0->0`).
|
||||
|
||||
## Verdict explicit pe cele 8 verificari cerute de plan (`plan_06_editare_factura.md:286-288`)
|
||||
|
||||
| # | Verificare | Verdict pe matrice |
|
||||
|---|---|---|
|
||||
| 1 | nota veche `STERS=1` | **PASS** pe toate cele 7 scrieri reusite (1048x2, 1055x2, 1052x1 - ROACONT, 1054x2). N/A pe 1052/ROAFACTURARE (blocat inainte de scriere, nu s-a creat nicio nota noua). |
|
||||
| 2 | nota noua corecta (exista, activa, acelasi `id_fact`) | **PASS** pe toate cele 7 scrieri reusite. |
|
||||
| 3 | `id_fact` neschimbat | **PASS** pe toate cele 7 - 8009658, 8009680, 8009677, 8009679 identice inainte/dupa. |
|
||||
| 4 | `VANZARI`/`VANZARI_DETALII` sincronizate | **PASS** pe toate cele 7 - numar de linii active neschimbat, totaluri coerente (`ftva+tva=ctva`). |
|
||||
| 5 | totalurile denormalizate corecte | **PASS** pe toate cele 7 - identice cu inainte de editare (reeditare fara modificari de continut). |
|
||||
| 6 | rulajele refacute | **PASS** pe 1048 (x2), 1055 (x2), 1052 (x1, ROACONT). **FAIL asteptat** pe 1054 (x2) - `RUL=0` e normal pentru tip 4 (confirmat pe toate cele 6 documente tip 4 din baza), verificarea nu e concludenta pentru acest tip. |
|
||||
| 7 | totalurile de control S4b concordante | **PASS** pe toate cele 7 - `Total ACT` si `Total RUL` raman fiecare **neschimbate fata de inainte de editare**, indiferent daca verdictul absolut e sincronizat (1048, 1052) sau divergent (1055, 1054 - divergenta insasi e preexistenta editarii, nu cauzata de ea). |
|
||||
| 8 | verificarile de stoc de la emitere NU s-au declansat | **PASS** pe toate cele 7 - `gcMockUltimMesaj` ramas gol dupa fiecare lant de scriere; structural, `verifica_stoc` (`oscrie_in_fisiere.prg:92`) nu se poate declansa pentru ca ambele puncte de intrare trec `tlModificare=.T.`. |
|
||||
|
||||
**Al treilea caz obligatoriu** (document care nu e factura, deschis din registru jurnal, fara
|
||||
pagina de articole) - deja acoperit separat, per handoff (`docs\handoff_punct6_10082026_pm.md:113`).
|
||||
|
||||
## Constatare de raportat separat (nu de reparat aici)
|
||||
|
||||
> **INCHISA — decizia lui Marius, 12.08.2026: asimetria se accepta, nu se repara.** Nu se atinge nici
|
||||
> `comun.vc2`, nici `omodificari.vc2`. Sectiunea de mai jos ramane ca inventar al constatarii, **nu mai
|
||||
> e o intrebare deschisa** — nu o redeschide. Motivele, in `docs\progres.md`.
|
||||
|
||||
**Asimetria de garda intre punctele de intrare** (sectiunea document 1052 de mai sus): intrarea
|
||||
ROAFACTURARE (`ofacturare_comun.vc2`, `do_editare_factura`) verifica `ReferinteDocumenteNota`/
|
||||
`EsteInEFactura` inainte de a permite editarea; intrarea registru jurnal ROACONT
|
||||
(`comun.vc2`, `afisjurcom.do_modifica`) nu are aceasta garda in secventa reprodusa si a scris pana
|
||||
la capat pe acelasi document pe care ROAFACTURARE l-a blocat. Nicio consecinta vizibila pe aceasta
|
||||
rulare (editare fara modificari de continut, documentul care refera - 1054 - verificat neschimbat),
|
||||
dar protectia lipseste structural pe calea ROACONT. `omodificari.vc2`/`comun.vc2` **nu au fost
|
||||
atinse** (livrari inchise) - decizia (adaugarea gardei si pe ROACONT, sau acceptarea asimetriei) ii
|
||||
revine lui Marius.
|
||||
|
||||
## Ramas de facut
|
||||
|
||||
Toate cele 4 documente din matrice au fost editate de doua ori si verificate. Nimic ramas pe
|
||||
felia S8 in sine. Documentele consumate (coduri realocate, note vechi sterse ireversibil):
|
||||
1048 (-> 1140918), 1052 (-> 1140921), 1054 (-> 1140923), 1055 (-> 1140920).
|
||||
143
docs/cercetare/rec_s8_rulaje_tip4.md
Normal file
143
docs/cercetare/rec_s8_rulaje_tip4.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Cercetare: rulaje (RUL) pe factura emisă din aviz (tip = 4) — `test_s8_matrice_surse.prg:308`
|
||||
|
||||
## Verdict
|
||||
|
||||
**Asserția e greșită pentru tip = 4.** Tabela `RUL` reține exclusiv mișcări pe conturi de stoc
|
||||
(`371` în datele verificate); o factură emisă din aviz nu atinge niciodată contul de stoc — ea doar
|
||||
transformă creanța provizorie (`418`) în creanță fermă (`4111`) și TVA neexigibilă în TVA colectată
|
||||
(`4428`→`4428`) — pentru că marfa a ieșit deja din gestiune la avizul-sursă (tip 22), care e cel ce
|
||||
scrie rândurile de `RUL`. `Reccount(trul) = 0` pe un document de tip 4 e starea corectă, nu un semn
|
||||
că editarea a distrus rulaje.
|
||||
|
||||
---
|
||||
|
||||
## Ce am verificat la sursă (cod + date Oracle)
|
||||
|
||||
### 1. De unde se umple `trul`
|
||||
|
||||
`IncarcaCursoareModificareNota` (`COMUN\programe\ofacturare_editare.prg:35-148`) încarcă `trul`
|
||||
direct din view-ul `vrul_tot`, filtrat pe `an`/`luna`/`cod`, fără nicio sinteză sau completare:
|
||||
|
||||
```
|
||||
COMUN\programe\ofacturare_editare.prg:76
|
||||
lcSql = [select * from vrul_tot where ] + lcConditieSters + [ an = ] + Transform(m.tnAn) + [ and luna = ] + Transform(m.tnLuna) + [ and cod = ] + Alltrim(Str(m.tnCod)) + [ order by id_rul]
|
||||
```
|
||||
|
||||
`Reccount(trul)` e deci exact numărul de rânduri deja existente în `RUL` (server) pentru acel `cod`
|
||||
— nu ceva calculat sau garantat nenul de vreo regulă de business în client.
|
||||
|
||||
### 2. Cine scrie rulajele — și diferența pe tip de document
|
||||
|
||||
Scrierea efectivă e server-side, în Oracle, `PACK_CONTAFIN.SCRIE_IN_RUL` (`COMUN\docs\PACK_CONTAFIN.pck:1896-2019`):
|
||||
face `INSERT INTO RUL (...) SELECT ... FROM RUL_TEMP` — deci **scrie exact ce a fost pus în
|
||||
`RUL_TEMP` de partea VFP**, fără vreo ramură condiționată de tipul documentului. Tot ce contează e
|
||||
dacă `RUL_TEMP` (populat din `trul`, deci din `vrul_tot` deja existent) are rânduri.
|
||||
|
||||
Pe partea VFP, calea de editare a facturii (cea exercitată de test, prin `frm_facturi.do_editare_factura`,
|
||||
`COMUN\clase\ofacturare_comun.vc2:3799-3821`) copiază necondiționat `trul` înapoi în `RUL_TEMP` și
|
||||
apelează `OSCRIE_IN_FISIERE(0,.T.,.T.)` — al treilea parametru (echivalentul lui `llRul` din editorul
|
||||
generic) e **hardcodat `.T.`**, nu depinde de starea inițială:
|
||||
|
||||
```
|
||||
COMUN\clase\ofacturare_comun.vc2:3811-3821
|
||||
If Used('rul_temp')
|
||||
Use In rul_temp
|
||||
Endif
|
||||
Select * From trul Into Cursor RUL_TEMP Readwrite
|
||||
Replace All id_util With gnIdUtil, sters With 0
|
||||
...
|
||||
lnSucces = OSCRIE_IN_FISIERE(0,.T.,.T.)
|
||||
```
|
||||
|
||||
Deci: dacă `trul` a fost gol la încărcare (pasul 1), rămâne gol și la scriere — codul **conservă**
|
||||
starea, nu o corectează și nu o strică. (Contrast: editorul generic de jurnal contabil,
|
||||
`afisjurcom.do_modifica`, `COMUN\clase\comun.vc2:2370-2482`, are un flag explicit `llRul` setat doar
|
||||
dacă `Reccount(lcCursor)>0` la încărcarea inițială din `vrul_tot` — dar calea de factură nu folosește
|
||||
acest flag deloc, e mereu `.T.`, ceea ce confirmă din nou că absența rândurilor din `RUL` nu e
|
||||
tratată ca eroare de nicio ramură a codului.)
|
||||
|
||||
Originea rândurilor din `RUL` e deci strict la **crearea/finalizarea notei**, nu la editare —
|
||||
`PACK_CONTAFIN.finalizeaza_modificare_nota` (apelat și la creare, și la editare) doar re-scrie ce
|
||||
primește; nu am găsit nicio ramură condiționată de `tip`/`ntip` care să decidă "acest tip de
|
||||
document trebuie să aibă rulaje" — decizia e implicită, dată de **ce conturi apar în notă**, nu de
|
||||
tipul documentului.
|
||||
|
||||
### 3. Rolul lui `IN_STOC` în `ActualizeazaVerdictActRul`
|
||||
|
||||
`COMUN\clase\omodificari.vc2:13045-13146`. Verdictul ACT/RUL are trei stări, toate **informative,
|
||||
niciodată eroare**:
|
||||
|
||||
```
|
||||
COMUN\clase\omodificari.vc2:13129-13139
|
||||
DO CASE
|
||||
CASE !m.llVerdictSigur
|
||||
lcVerdict = 'Verdict ACT/RUL: nu se aplica (informativ, date de import)'
|
||||
CASE Abs(This.nTotalActRon - This.nTotalRulRon) <= 0.02
|
||||
lcVerdict = 'Verdict ACT/RUL: sincronizat (informativ)'
|
||||
OTHERWISE
|
||||
lcVerdict = 'Verdict ACT/RUL: divergent (informativ, nu e eroare)'
|
||||
ENDCASE
|
||||
```
|
||||
|
||||
`in_stoc` corectează doar **suma** comparată (exclude valoarea liniilor nestocate din articolele
|
||||
facturii, convertită în RON, din totalul RUL — `tvd` unde `Nvl(in_stoc,1) = 0`, linia 13111), nu
|
||||
decide dacă documentul "trebuie" să aibă rulaje. Nu există în această metodă (verificat integral,
|
||||
liniile 13045-13146, nu doar grep) nicio ramură specifică pentru `RUL = 0` pe tip 4 — cazul cade pur
|
||||
și simplu în "divergent (informativ, nu e eroare)" dacă `nTotalActRon <> 0` și `nTotalRulRon = 0`, ceea
|
||||
ce confirmă că design-ul acceptă explicit acest caz ca non-eroare.
|
||||
|
||||
### 4. Verificare pe date, read-only, Oracle `MARIUSM_AUTO`/`ROA_CENTRAL`
|
||||
|
||||
Conectat cu succes (`sqlplus.exe`, `SET TRANSACTION READ ONLY` ... `ROLLBACK`, fișier `.sql` ASCII,
|
||||
fără pipe). Cod-urile curente (verificate din `VANZARI`, nu presupuse):
|
||||
|
||||
| id_vanzare | tip | cod (curent) | sters | RUL (cnt activ) | ACT (cnt activ) |
|
||||
|---|---|---|---|---|---|
|
||||
| 1048 | 1 | 1140918 | 0 | 1 | 5 |
|
||||
| 1052 | 22 (aviz) | 1140921 | 0 | 4 | 12 |
|
||||
| 1054 | 4 (factură din aviz) | **1140923** | 0 | **0** | 2 |
|
||||
| 1055 | 2 | 1140920 | 0 | 1 | 7 |
|
||||
|
||||
`id_vanzare = 1054` are `cod` curent `1140923` (confirmă realocarea din test:
|
||||
`1140910 -> 1140922 -> 1140923`, citită direct din `VANZARI`, nu presupusă).
|
||||
|
||||
Detaliu decisiv — conturile efective din `ACT`/`RUL` pentru perechea aviz→factură (avizul 1052 e cel
|
||||
mai probabil sursă a facturii 1054, pe baza secvenței de cod-uri și a fluxului tip 22→tip 4):
|
||||
|
||||
```
|
||||
ACT pe avizul 1052 (tip 22): scd/scc includ 607/371, 371/378, 378/371, 4428/371, 371/4428, 418/704, 418/4428
|
||||
RUL pe avizul 1052 (tip 22): 4 rânduri, toate CONT = 371 (cont de stoc)
|
||||
|
||||
ACT pe factura 1054 (tip 4): scd/scc = 4111/418 (305 lei) și 4428/4428 (52.93 lei) -- NICIUN cont 371
|
||||
RUL pe factura 1054 (tip 4): 0 rânduri
|
||||
```
|
||||
|
||||
Avizul e cel care mișcă efectiv contul de stoc (`371`) și de aceea are rânduri în `RUL` (care
|
||||
urmărește exclusiv cantități pe conturi de stoc — vezi și `cant`/`cante` din `trul`, folosite ca
|
||||
atare în `ActualizeazaVerdictActRul:13102`). Factura emisă din acel aviz nu mai atinge `371` deloc —
|
||||
transformă doar creanța provizorie (`418`) în creanță fermă (`4111`) și TVA neexigibilă (`4428`) în
|
||||
TVA colectată (`4428`). N-are, structural, ce rând de stoc să scrie.
|
||||
|
||||
Zero tranzacții deschise la final (`ROLLBACK` executat, `SET TRANSACTION READ ONLY` respectat pe tot
|
||||
scriptul).
|
||||
|
||||
## Ce am dedus (nu verificat direct, dar consistent cu toate dovezile de mai sus)
|
||||
|
||||
- Regula generală: `RUL` se scrie doar pentru documentele/notele care conțin mișcare pe cont de
|
||||
stoc (aviz, NIR, bonuri de consum etc.); facturile "de închidere" (emise din aviz, sau orice
|
||||
document care doar transformă o creanță provizorie într-una fermă) nu vor avea niciodată rânduri
|
||||
în `RUL`, indiferent câte documente de acest fel verifici — nu e o particularitate a lui
|
||||
`id_vanzare = 1054`, ci a tipului de operațiune contabilă.
|
||||
- Aserția de la `test_s8_matrice_surse.prg:308` (`Reccount(trul) > 0` obligatoriu după editare) e
|
||||
probabil corectă ca test general "rulajele nu trebuie distruse de editare" pentru documentele care
|
||||
AU avut rulaje înainte de editare, dar e o presupunere greșită aplicată universal — pentru tip 4
|
||||
(și, prin extensie, orice tip de document care nu atinge cont de stoc) condiția trebuie relaxată
|
||||
la "dacă documentul avea rulaje înainte, tot le are și după" sau pur și simplu exclusă pentru
|
||||
tip = 4.
|
||||
|
||||
## Fișiere atinse
|
||||
|
||||
Niciunul (misiune read-only). Fișiere citite: `COMUN\programe\ofacturare_editare.prg`,
|
||||
`COMUN\clase\omodificari.vc2`, `COMUN\clase\comun.vc2`, `COMUN\clase\ofacturare_comun.vc2`,
|
||||
`COMUN\docs\PACK_CONTAFIN.pck`. Interogări Oracle read-only pe `MARIUSM_AUTO`@`ROA_CENTRAL`, cu
|
||||
`ROLLBACK` la final.
|
||||
87
docs/conventii_mediu_oracle.md
Normal file
87
docs/conventii_mediu_oracle.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Conventii - mediul Oracle de dev/test
|
||||
|
||||
Regula lui Marius, 20.08.2026. Se aplica **de fiecare data**, si sesiunii principale, si
|
||||
subagentilor. Nu e context, e conventie de lucru.
|
||||
|
||||
## 1. Nu se spune "baza"
|
||||
|
||||
Termenul e vag si a produs deja o intrebare care n-ar fi trebuit sa existe: *"ce este baza? care
|
||||
schema?"*. Se scrie intotdeauna **schema + instanta**, explicit:
|
||||
|
||||
> "se ruleaza pe schema `MARIUSM_AUTO` de pe `ROA_CENTRAL`"
|
||||
|
||||
nu "se ruleaza pe baza". Acelasi lucru in rapoarte, in handoff-uri, in propuneri si in mesaje.
|
||||
|
||||
## 2. Pe dev/test se lucreaza NUMAI cu ROA_CENTRAL
|
||||
|
||||
Instanta: **`ROA_CENTRAL`**. Schemele permise, singurele doua:
|
||||
|
||||
| Schema | Ce este |
|
||||
|---|---|
|
||||
| **`MARIUSM_AUTO`** | schema **de firma** - datele si pachetele de business (`VANZARI`, `VANZARI_DETALII`, `JTVA_COLOANE`, `PACK_FACTURARE`...). Aici se lucreaza pe functionalitate. |
|
||||
| **`CONTAFIN_ORACLE`** | schema **comuna tuturor schemelor de firma**: pachete comune, actualizarea bazei de date, utilizatori, drepturi de utilizator, nomenclatorul de firme. Se creeaza **pe fiecare server**. |
|
||||
|
||||
Nimic altceva. Daca o sarcina pare sa ceara altceva, se intreaba intai.
|
||||
|
||||
Modelul de retinut: fiecare firma are schema ei (in dev/test, `MARIUSM_AUTO`), iar
|
||||
`CONTAFIN_ORACLE` sta alaturi, o singura data per server, si deserveste toate schemele de firma.
|
||||
Deci o modificare in `CONTAFIN_ORACLE` e **transversala peste toate firmele de pe acel server** -
|
||||
se trateaza cu grija corespunzatoare, nu ca o schimbare locala.
|
||||
|
||||
## 3. Schema `ACN` nu se foloseste si nu se citeaza
|
||||
|
||||
Nu se interogheaza, nu se ia ca sursa de adevar, nu se pomeneste in rapoarte ca element de
|
||||
comparatie. Daca apare in rezultatul unei interogari pe `ALL_OBJECTS`/`ALL_SOURCE`, se **filtreaza
|
||||
din start**, nu se comenteaza.
|
||||
|
||||
Consecinta practica pentru interogarile de dictionar: se pune **intotdeauna** filtrul de `owner`.
|
||||
Fara el, randurile mai multor scheme se intercaleaza si output-ul devine ilizibil - capcana deja
|
||||
platita o data la citirea unui corp de pachet din `ALL_SOURCE`.
|
||||
|
||||
```sql
|
||||
select text from all_source
|
||||
where owner = 'MARIUSM_AUTO' -- niciodata fara aceasta linie
|
||||
and name = 'PACK_FACTURARE'
|
||||
and type = 'PACKAGE BODY'
|
||||
order by line;
|
||||
```
|
||||
|
||||
## 4. Conectare
|
||||
|
||||
```
|
||||
& 'D:\ROA\instantclient_19_18\sqlplus.exe' -L -S 'MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL' '@fisier.sql'
|
||||
```
|
||||
|
||||
Fisierul `.sql` se scrie in **ASCII**. SQL trimis prin pipe din PowerShell e spart de BOM.
|
||||
|
||||
## 5. Ce inseamna "se ruleaza un script de pachet"
|
||||
|
||||
Scripturile din `D:\ROA\DATABASE\SCRIPTURI_CLAR\` **nu modifica date** - inlocuiesc **cod stocat in
|
||||
Oracle** (`CREATE OR REPLACE PACKAGE` + `PACKAGE BODY`). De retinut:
|
||||
|
||||
- Efectul e **imediat si pentru toti cei conectati**. Nu seamana cu VFP, unde fiecare statie ruleaza
|
||||
propriul `.exe`.
|
||||
- Se recreeaza si **specificatia**, nu doar corpul - asta invalideaza obiectele dependente, care se
|
||||
recompileaza la prima folosire.
|
||||
- **Nu exista "undo".** Revenirea inseamna rularea unei versiuni anterioare a pachetului.
|
||||
- Scripturile sunt scrise **necalificat** (`CREATE OR REPLACE PACKAGE "PACK_FACTURARE"`, fara
|
||||
`CONNECT`, fara prefix de schema). Deci **se aplica schemei cu care esti conectat**, nu au tinta
|
||||
proprie. Schema tinta o decide exclusiv sirul de conectare.
|
||||
- Rularea unui script pe orice schema **se face doar la cererea explicita a lui Marius**. Un agent
|
||||
scrie scriptul; nu il aplica.
|
||||
|
||||
## 6. Stare constatata pe `ROA_CENTRAL`, 20.08.2026
|
||||
|
||||
Utila ca reper; de reconfirmat daca a trecut timp.
|
||||
|
||||
```
|
||||
DB_NAME: ROA server: 4e73d257c791 alias TNS: ROA_CENTRAL
|
||||
```
|
||||
|
||||
- `MARIUSM_AUTO` isi detine **propriile tabele** (`VANZARI_DETALII`, `JTVA_COLOANE` sunt TABLE in
|
||||
schema, nu sinonime catre alta schema). Deci cifrele obtinute conectat ca `MARIUSM_AUTO` sunt ale
|
||||
datelor din `MARIUSM_AUTO`.
|
||||
- `CONTAFIN_ORACLE` are 393 de obiecte si **nu** detine `VANZARI_DETALII`/`JTVA_COLOANE` - coerent
|
||||
cu rolul ei: infrastructura comuna (utilizatori, drepturi, firme, actualizare), nu date de firma.
|
||||
- `MARIUSM_AUTO.PACK_FACTURARE`: PACKAGE si PACKAGE BODY **VALID**, corpul modificat ultima data
|
||||
**20.08.2026 08:43** (aplicarea scriptului `ff_2026_08_20_01`, garda `FACT-025`).
|
||||
109
docs/diagnostic_pagina_articole_ux.md
Normal file
109
docs/diagnostic_pagina_articole_ux.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Diagnostic - pagina "Articole factura" in formularul de modificare
|
||||
|
||||
Sursa: raport Marius, 19.08.2026, cu captura pe formularul MODIFICARE maximizat
|
||||
(`{A97AED0C-...}.png`). Doua probleme distincte, fara legatura intre ele.
|
||||
|
||||
## Problema 1 - continutul paginii 3 nu se intinde la maximizare
|
||||
|
||||
**Simptom.** Pe formularul maximizat, gridul de articole ramane la dimensiunea de design si bara
|
||||
de totaluri (Total linii / Discount / Total net / Total salvat / Total ACT / Total RUL / verdict)
|
||||
apare la mijlocul paginii, cu spatiu gol dedesubt.
|
||||
|
||||
**Geometrie la design** (`COMUN\clase\omodificari.vc2`):
|
||||
|
||||
| Obiect | Linie | Top | Height | Anchor |
|
||||
|---|---|---|---|---|
|
||||
| `pgfArticole` | 8709-8712 | 361 | 164 | **15** |
|
||||
| `PAGE3.grdArticoleFactura` | 12324-12341 | 26 | 81 | **15** |
|
||||
| `PAGE3.cmdSincronizeazaArticole` | 12306-12320 | 0 | 22 | **0** |
|
||||
| `PAGE3.lbl/txt Total linii, Discount, Total net, Total salvat` | 12791-12951 | 110-114 | 17/21 | **absent (0)** |
|
||||
| `PAGE3.lbl/txt Total ACT, Total RUL, verdict` | 12803-12873 | 132-136 | 17/21 | **absent (0)** |
|
||||
|
||||
Formularul are `Height = 530` (`:6868`), deci pagina are inaltime utila ~140px la design. Randul
|
||||
al doilea de totaluri se termina la 153 - **sub marginea paginii**: la dimensiunea de design nu e
|
||||
vizibil deloc. Bara a fost pozitionata presupunand implicit ca pagina va fi mai mare.
|
||||
|
||||
**Cauza.** Doua lucruri, ambele necesare pentru simptom:
|
||||
|
||||
1. Bara de totaluri **nu are `Anchor` deloc** - la crestere ramane la `Top` fix, adica sus, in
|
||||
timp ce pagina creste in jos. Asta e sigur, se vede direct in definitie.
|
||||
2. Gridul are `Anchor = 15`, deci ar fi trebuit sa creasca si sa acopere bara - dar nu creste.
|
||||
Formularul se maximizeaza in `Show()` (`:14874`, `This.WindowState = 2`) **cat timp pagina
|
||||
activa e PAGE1**. VFP reasaza pe `Anchor` doar controalele paginii active in momentul
|
||||
redimensionarii; PAGE3, nefiind activa atunci, ramane la geometria de design si nu se mai
|
||||
corecteaza niciodata, pentru ca formularul nu mai e redimensionat dupa aceea. Acelasi lucru se
|
||||
vede si in captura: PAGE1 (`grdRulaje`, tot `Anchor = 15`) arata corect.
|
||||
|
||||
**Consecinta a aceleiasi cauze - valorile din bara sunt vechi.** In captura, `Total ACT` si
|
||||
`Total RUL` arata amandoua `0.00`, dar verdictul spune "divergent". Cele doua nu pot fi adevarate
|
||||
simultan: `ActualizeazaVerdictActRul` (`:13123-13126`) scrie "divergent" numai cand
|
||||
`Abs(nTotalActRon - nTotalRulRon) > 0.02`. Deci proprietatile formularului au valori reale, iar
|
||||
casutele afiseaza altceva: `ActualizeazaBaraTotaluri` se cheama o singura data, din `Show()`
|
||||
(`:14864`), si se termina cu `This.pgfArticole.PAGE3.Refresh()` (`:13025`) - executat tot cat timp
|
||||
PAGE3 e inactiva. `txtDiscountArt` si `txtTotalSalvatArt`, legate de `tvanz.*`, apar goale, ceea ce
|
||||
duce in aceeasi directie.
|
||||
|
||||
**Ce lipseste, structural.** PAGE3 nu are niciun `Activate`. Nimic nu ruleaza cand utilizatorul
|
||||
intra pe pagina: nici reasezare, nici recalcul, nici refresh. Toate celelalte doua pagini isi fac
|
||||
treaba in `Show()`, cand sunt (PAGE1) sau nu conteaza (PAGE2, doar grid ancorat) active.
|
||||
|
||||
**Reparatie propusa** (nu aplicata):
|
||||
|
||||
1. `PROCEDURE pgfArticole.PAGE3.Activate` nou, care cheama o metoda de asezare a paginii si
|
||||
`Thisform.ActualizeazaBaraTotaluri()`.
|
||||
2. Metoda `aseaza_pagina_articole()` in `frm_modific2024`, care pozitioneaza **explicit**, din cod,
|
||||
nu prin `Anchor`: gridul de la `Top = 26` pana la `inaltime_pagina - 52`, apoi cele doua randuri
|
||||
de totaluri lipite de marginea de jos. Explicit, pentru ca `Anchor` s-a dovedit exact aici
|
||||
nesigur - nu are rost sa reparam bara cu acelasi mecanism care a picat pentru grid.
|
||||
3. Aceeasi metoda se cheama si din `Resize()`, ca redimensionarea manuala a ferestrei sa mearga.
|
||||
|
||||
Atinge un singur fisier, `COMUN\clase\omodificari.vc2`; nicio schimbare de logica de calcul.
|
||||
|
||||
## Problema 2 - dialogul de sincronizare apare la iesirea din editare
|
||||
|
||||
**Nu e o eroare, e comportamentul proiectat** - dar proiectarea nu tine cont de cazul din captura.
|
||||
|
||||
Declansatorul e in `frm_modific2024.inainte_de_do_termin`, `omodificari.vc2:14434-14451`: la
|
||||
fiecare salvare, daca documentul are articole si nu e blocat de eFactura, se recompara rulajele cu
|
||||
articolele si, daca ies divergente, se deschide dialogul. Numaratoarea (`:14444`) socoteste
|
||||
`Modificare`, `Adaugare` si `Semnalare`.
|
||||
|
||||
**De ce apare desi nu s-a modificat nimic.** Verificarea compara *starea documentului*, nu
|
||||
*modificarile utilizatorului*. Un document care era deja desincronizat inainte de deschidere -
|
||||
si captura arata exact asta, verdictul ACT/RUL e "divergent" pe un document neatins - produce
|
||||
divergente la fel ca unul stricat acum. Nu exista nicaieri o comparatie cu starea de la intrare.
|
||||
|
||||
**De ce e neclar ce cere dialogul.** "Cantitate veche / Cantitate noua / Pret vechi / Pret nou" nu
|
||||
inseamna istoric. Inseamna (`ofacturare_editare.prg:696-701`):
|
||||
|
||||
- **vechi** = ce e acum in cursorul-**tinta**, adica ce s-ar salva daca apesi Salveaza fara sa
|
||||
sincronizezi;
|
||||
- **nou** = ce rezulta din cursorul-**sursa**, adica din partea aleasa cu butoanele radio de sus.
|
||||
|
||||
Cu optiunea implicita ("Rulajul e sursa"), "vechi" = articolele facturii, "nou" = valorile
|
||||
calculate din rulaje. Nimic nu se scrie in Oracle din dialog; "Aplica" muta valorile doar in
|
||||
cursoare, iar "Renunta" nu lasa nimic in urma - salvarea continua oricum
|
||||
(`omodificari.vc2:14435-14436`, comentariul explica de ce `llRet` nu se schimba).
|
||||
|
||||
**Ce mai lipseste in dialog**, pe langa declansare: titlul si butoanele sunt cele generice
|
||||
(`frm_termin_renunt`), nu scrie nicaieri *de ce* s-a deschis si ce se intampla daca renunti.
|
||||
|
||||
**Optiuni** - decizie de produs, ceruta lui Marius:
|
||||
|
||||
| # | Varianta | Efect |
|
||||
|---|---|---|
|
||||
| A | Declansare doar cand divergentele s-au **schimbat** fata de deschiderea documentului (semnatura calculata o data, in `Show`) | Documentele deja desincronizate nu mai deranjeaza; ce strici acum se semnaleaza |
|
||||
| B | Fara declansare la salvare; ramane doar butonul manual | Cel mai linistit, dar se pierde plasa de siguranta |
|
||||
| C | Intrebare simpla da/nu inainte de dialog | Pastreaza semnalarea, scoate formularul din drum |
|
||||
| D | Ramane cum e, doar se explica in dialog | Minimul |
|
||||
|
||||
In toate variantele: text explicativ in capul dialogului ("Articolele facturii difera de rulaje.
|
||||
Vechi = ce se salveaza acum, Nou = ce rezulta din sursa aleasa mai sus. Poti renunta, salvarea
|
||||
continua.") si etichete de butoane pe intelesul actiunii.
|
||||
|
||||
## Ce nu s-a verificat
|
||||
|
||||
Nimic nu a fost rulat. Punctul 2 e citit integral din cod si e sigur. La punctul 1, faptul ca bara
|
||||
de totaluri nu are `Anchor` e sigur; explicatia pentru grid (reasezarea sarita pe pagina inactiva)
|
||||
e cea singura compatibila cu captura, dar nu a fost confirmata pe ecran - reparatia propusa nu
|
||||
depinde de ea, pentru ca renunta cu totul la `Anchor` pe PAGE3.
|
||||
@@ -1,7 +1,84 @@
|
||||
# Handoff — #13 formular unificat de facturare + editare prin regenerare
|
||||
|
||||
Sesiune: 11.08.2026, **runda 16** (istoricul de mai jos e cumulativ; blocul rundei 14 e pastrat ca
|
||||
Sesiune: 11.08.2026, **runda 17** (istoricul de mai jos e cumulativ; blocul rundei 14 e pastrat ca
|
||||
atare). Runda 13 predase dupa cinci proiectari si deciziile 44-50.
|
||||
|
||||
> ## RUNDA 17 — „Urmatorul bloc de lucru" S-A GOLIT. Proiectarea lui #13 e INCHEIATA.
|
||||
>
|
||||
> Runda 17 a executat **exact cele doua sarcini** care mai ramasesera (punctele 4 si 5 din lista de
|
||||
> jos) si **nu a deschis niciuna noua**.
|
||||
>
|
||||
> > ### DECIZIA 66 (runda 17) RASTOARNA DECIZIA 64 — citeste asta inainte de orice paragraf despre asezare
|
||||
> > **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri, nu ca a treia sectiune jos.**
|
||||
> > Formularea lui Marius: *„motiv discount vreau sa fie langa discount, nu a treia coloana"*.
|
||||
> > **Randul de jos revine la DOUA sectiuni** (incasare, alte date), fiecare pe jumatate de latime —
|
||||
> > adica exact decizia 57, punctul 2, neatinsa. **Argumentul „~440 px fiecare, strans" cade odata cu
|
||||
> > decizia 64.** Mai jos, in istoricul cumulativ al rundei 16, decizia 64 apare inca in vigoare in
|
||||
> > cateva paragrafe: **sunt istorie, nu stare curenta.** Locurile decisive sunt corectate; daca
|
||||
> > gasesti unul necorectat, **decizia 66 castiga**. Enuntul complet, cu regula de activare adaugata de
|
||||
> > proiectare (campul e activ **doar cand discountul nu e zero**): **in plan**, la „Decizia 66".
|
||||
>
|
||||
> **O singura decizie noua, 66**, deci numerotarea se opreste la **66**.
|
||||
|
||||
> ### AUDIT DE INCHIDERE (18.08.2026) — planul e curatat de marcajele „deschis" ramase in urma
|
||||
> Verificare ceruta de Marius inainte de a inchide sesiunea: **e planul finalizat?** Raspuns: **da ca
|
||||
> proiectare**, dar avea **noua intrebari fantoma** — blocuri „De decis de Marius" si „ramane deschis"
|
||||
> care supravietuisera deciziilor care le inchisesera. Un agent proaspat le-ar fi citit ca sarcini si
|
||||
> ar fi redeschis lucruri transate. **Toate au fost stinse**, fiecare cu decizia care o inchide, in
|
||||
> **8 linii** din plan (verificat prin diff fata de o copie de siguranta: zero modificari colaterale):
|
||||
> - trei blocuri **„De decis de Marius"** (S5c, S8b, S11) → inchise de deciziile **51**, **52**, **53**
|
||||
> si **56**; textul ramane ca **inventar al recomandarilor acceptate**, nu ca intrebari;
|
||||
> - doua afirmatii „ramane deschisa asezarea campului de motiv" → inchise de **decizia 66**;
|
||||
> - „din intrebarea 7 ramane deschisa partea de tipuri 48/49" → inchisa de **decizia 60**.
|
||||
>
|
||||
> **Ce a ramas deschis, si e corect asa — trei puncte, toate de implementare, niciunul blocant:**
|
||||
> 1. **Relistarea unei facturi vechi deja trimise** (decizia 62, rest semnalat): dupa decizia 59 ar
|
||||
> produce N randuri de discount in loc de unul, deci hartie diferita de originalul din eFactura.
|
||||
> Relistarea nu e modificare, deci `EsteInEFactura` n-o acopera. **Nedecis, semnalat lui Marius.**
|
||||
> 2. **`PROC_TVAV` ca parametru** (consecinta 3 a deciziei 54): doar daca se cere reproducere exacta
|
||||
> peste o modificare legala de cota. **De decis separat.**
|
||||
> 3. **De unde se reconstituie `IN_STOC`** (cerinta rundei 17): preconditie de proiectare a lui S8,
|
||||
> trei variante scrise, niciuna aleasa.
|
||||
> **Cod: neatins** — nici un `.vc2` / `.sc2` / `.prg` deschis macar. **Zero Oracle** in aceasta runda,
|
||||
> nici macar `SELECT`. **Niciun commit dat de aceasta sesiune.**
|
||||
>
|
||||
> **Punctul 4 — golul `IN_STOC` din S8 — INCHIS**, prins acolo unde cerea handoff-ul precedent (in nota
|
||||
> de executie a lui S8, nu la S12). Trei editari in plan, toate verificate pe disc dupa ce sesiunea de
|
||||
> la #6 a rescris `docs\` in paralel:
|
||||
> - `plan_13:3520-3546` — caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17";
|
||||
> - `plan_13:3567-3569` — criteriul intra in „gata cand" al lui S8;
|
||||
> - `plan_13` la S10, consecinta 1 — marcata **PRELUAT**, ca sa nu se redescopere.
|
||||
>
|
||||
> **Punctul dur, si e mai greu decat suna cerinta:** valoarea istorica a lui `IN_STOC` **nu e stocata
|
||||
> nicaieri** — nu e coloana pe `VANZARI_DETALII` (verificat pe DB inca de la S10), traieste doar in
|
||||
> temp. Deci „S8 incarca `IN_STOC` din document" **nu e implementabil ca atare azi**. De unde se
|
||||
> reconstituie e o **preconditie de proiectare a lui S8** — trei variante scrise in plan (din urma
|
||||
> lasata in rulaje/gestiune; nomenclatorul curent ca aproximatie **declarata**; coloana noua, deci
|
||||
> migrare DB inainte de EXE). **Nu s-a ales niciuna** si nu se presupune niciuna.
|
||||
>
|
||||
> **Punctul 5 — mockup v9 — LIVRAT.** `docs\mockup_13_formular_unificat.html`, 71 336 octeti (era
|
||||
> 67 075 la v8). Asezarea e acum **varianta D** cu **randul de jos in doua** si motivul discountului in
|
||||
> banda de totaluri (deciziile 57 + 66):
|
||||
> antet strans pe doua randuri fara titluri de grup; panoul „Discount pe document" **disparut**; banda
|
||||
> de totaluri **in interiorul panoului Articole, dupa `.gridbar`**, deci lipita de grid, cu procentul
|
||||
> de discount editabil pe loc, **motivul discountului imediat dupa suma pe care o explica** (decizia
|
||||
> 66) si totalul mare singur la dreapta; sub ea, **doua** sectiuni colapsabile egale
|
||||
> (incasare · alte date), cu **rezumatul continutului pe randul inchis**.
|
||||
> Legenda are acum 5 callout-uri (2 si 4 repurpozate, 5 nou). In text au intrat deciziile
|
||||
> **52, 53, 54, 59, 60, 61, 62, 63+65**. Raport: `docs\cercetare\mockup_v9_modificari.md`.
|
||||
> **Verificat de sesiunea principala, nu preluat din raport:** 0 taguri nechise, 0 inchideri
|
||||
> neasteptate, `<style>` unic si inchis.
|
||||
>
|
||||
> **Mockup-ul E REPUBLICAT** (Marius a cerut-o in aceeasi runda), **pe URL-ul existent, nu pe unul nou**:
|
||||
> **https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142**. Cu asta decizia 33 e
|
||||
> consumata — URL-ul nu mai e in urma, e la v9. **URL-ul e notat acum si in plan**, la S1; pana in runda
|
||||
> 17 nu exista nicaieri in `docs\` si a trebuit scos cu `Artifact action: "list"`.
|
||||
> Inainte de publicare s-a facut **WebFetch pe URL** (obligatoriu, altfel publicarea e refuzata) si s-a
|
||||
> confirmat ca versiunea online era **v8**, deci v9 e superset si nu s-a suprascris nimic.
|
||||
>
|
||||
> **Ce ramane dupa runda 17:** *nimic de proiectat*. Raman **testele (S6, S12)** si **inchiderea
|
||||
> (S13)**, si amandoua cer cod, care nu poate incepe inainte de #6 (decizia 30). Prima sarcina la
|
||||
> pornire: **spargerea planului pe stories** + nota de executie a primeia (decizia 55).
|
||||
Stare: **ETAPA I E PROIECTATA INTEGRAL. ETAPA II E PROIECTATA INTEGRAL — S8 a fost ultima poveste, si
|
||||
e gata. Raman testele (S6, S12), diff/review (S13), si O SINGURA VERIFICARE PE COD, in curs. Cod:
|
||||
neatins.** Runda 14 a livrat **doua rapoarte** (S8 in detaliu, garda pe aviz), a luat **deciziile
|
||||
@@ -134,7 +211,16 @@ antet). Nu mai ramane nicio intrebare deschisa pentru Marius.**
|
||||
> „Capcane de mediu".
|
||||
|
||||
> ### ATENTIE — copia de lucru are modificari necomise care NU sunt ale rundei 14
|
||||
> `git status` arata doar `docs/` netracked, dar **SVN e sursa de adevar aici** si arata mult mai mult:
|
||||
> **Actualizat in runda 17, masurat, nu copiat.** Ce e necomis **din partea lui #13**: exact trei
|
||||
> fisiere, toate in `docs\` — `handoff_13_formular_unificat.md`, `mockup_13_formular_unificat.html`
|
||||
> (v9) si `cercetare\mockup_v9_modificari.md` (netracked). Nimic altceva. Editarile rundei 17 in
|
||||
> `plan_13` si `plan_index` **apar deja comise**, prinse in commit-ul de curatenie al sesiunii de la
|
||||
> #6 — vezi „Capcane de mediu", prima intrare.
|
||||
>
|
||||
> Restul de mai jos e al lui **#6** si/sau zgomot de compilare VFP, si e **inca acolo** (verificat cu
|
||||
> `svn status` in runda 17: `changelog_roafacturare.txt`, `roafacturare.PJX` / `.PJT`, `versiune_db.txt`,
|
||||
> plus in `COMUN\`: `anaf_efactura`, `comun`, `ofacturare_comun`, `omodificari`, doua ferestre de import
|
||||
> si `ofacturare_editare.prg`). Lista originala, pastrata ca atare:
|
||||
> ```
|
||||
> M changelog_roafacturare.txt · M roafacturare.PJX / .PJT · M versiune_db.txt
|
||||
> M COMUN\clase\ofacturare_comun.vcx / .vct · M COMUN\clase\omodificari.vcx / .VCT
|
||||
@@ -409,14 +495,18 @@ sau separat, dupa.
|
||||
## Ce ramane de proiectat
|
||||
|
||||
**Nu mai e „nimic" — runda 16 a adaugat doua povesti noi, ambele cu cercetare in curs, nu inchise:**
|
||||
1. **Golul `IN_STOC` din S8** — deschis de verificarea S10 (consecinta 1). Nu e o poveste noua, e o
|
||||
cerinta in plus pentru S8, de prins in nota lui de executie.
|
||||
**Actualizare runda 17: punctele 1 si 4 de mai jos sunt FACUTE. Lista e goala pe partea de proiectare
|
||||
— raman doar 3 (testele si inchiderea), care cer cod.**
|
||||
|
||||
1. ~~**Golul `IN_STOC` din S8**~~ — **FACUT in runda 17**, in nota de executie a lui S8. A lasat in
|
||||
urma o **alegere de proiectare** (de unde se reconstituie valoarea istorica), nu o sarcina.
|
||||
2. **Canalul care scrie `serie_act` / `numar_act` / `data_act` / `data_scad` / `id_ruta` / `tip_saft` /
|
||||
`efactura`** pe documentul reemis — **nu apar in `scrie_factura2`**. Se inchide in S9, la implementare.
|
||||
3. `S6` / `S12` (teste pe flux real) si `S13` (diff, review, changelog) raman la final.
|
||||
4. **Mockup-ul e la v8 si a ramas cu patru runde in urma.** Nu s-a republicat, conform deciziei 33.
|
||||
Asezarea zonei de jos e insa **decisa** — varianta D, decizia 57 (cu intrebarea de detaliu
|
||||
redeschisa de decizia 61, vezi mai sus).
|
||||
4. ~~**Mockup-ul e la v8**~~ — **FACUT in runda 17: e la v9.1**, cu varianta D, randul de jos in doua
|
||||
si motivul discountului in banda de totaluri (deciziile 57 + 66), plus deciziile
|
||||
52/53/54/59/60/61/62/63+65 in text. **Republicat** pe URL-ul existent (vezi punctul 5 din
|
||||
„Urmatorul bloc de lucru") — decizia 33 e consumata, URL-ul e la zi.
|
||||
5. **Verificarea custodiei (decizia 60, runda 16) — TERMINATA.** Verdict:
|
||||
`scrie_fact_aviz_custodie` nu are legatura cu 48/49 (serveste `ntip = 4`); emiterea 48/49 nu
|
||||
atinge stocul (`cursor_articole_k`, `IN_STOC = 0`); `sterge_factura` n-are ce reversa. **Blocantul
|
||||
@@ -495,10 +585,10 @@ cele patru intrebari ale rundei 14 puse lui Marius direct, **si asezarea zonei d
|
||||
de creare fara un transport explicit, la fel cum era riscul pentru `ID_FACT` inainte de decizia
|
||||
care l-a pastrat. Locul de afisare, ramas deschis aici, se inchide la decizia 65. Enuntul
|
||||
complet: **in plan**, la „Decizia 63" si la S14.
|
||||
64. **Asezarea campului de motiv: randul se imparte in trei.** Inchide ultimul punct ramas deschis
|
||||
din decizia 57, reluat de decizia 61: randul de jos are trei sectiuni pe acelasi rand —
|
||||
incasare, alte date, motivul discountului — ~440 px fiecare la 1366 px, strans; D ramane
|
||||
intr-un etaj. Enuntul complet: **in plan**, la „Decizia 64".
|
||||
64. ~~**Asezarea campului de motiv: randul se imparte in trei.**~~ **RASTURNATA DE DECIZIA 66
|
||||
(runda 17). Nu mai e in vigoare** — se pastreaza doar ca istorie, ca sa se stie ca varianta „a
|
||||
treia sectiune jos" a fost incercata si respinsa dupa ce Marius a vazut-o in mockup. Ce e in
|
||||
vigoare: **motivul sta in banda de totaluri, langa discount; randul de jos are doua sectiuni.**
|
||||
65. **Auditul (decizia 63) se afiseaza in gridul din `frm_facturi`, nu in formularul facturii.**
|
||||
Confirma continutul de la decizia 63 (trei perechi data + utilizator: adaugat, modificat,
|
||||
sters). Inchide ambele puncte ramase de la S14: e pe **antet** (gridul listeaza documente) si
|
||||
@@ -506,6 +596,16 @@ cele patru intrebari ale rundei 14 puse lui Marius direct, **si asezarea zonei d
|
||||
implementarii S14: inventarul gridului din `frm_facturi`**, posibil sa existe deja coloane de
|
||||
audit. Enuntul complet: **in plan**, la „Decizia 65" si la S14.
|
||||
|
||||
### Decizia 66 (Marius, runda 17) — luata, nu de reluat. RASTOARNA DECIZIA 64.
|
||||
|
||||
66. **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri** — *„motiv discount vreau sa fie
|
||||
langa discount, nu a treia coloana"*. **Rastoarna decizia 64**: randul de jos ramane cu **doua**
|
||||
sectiuni (incasare, alte date), fiecare pe jumatate de latime, adica exact decizia 57 punctul 2.
|
||||
Proiectarea a adaugat o regula care nu i-a fost ceruta explicit si care se poate schimba dintr-o
|
||||
linie: **campul e activ doar cand discountul de document nu e zero**. Consecinta de asezare,
|
||||
acceptata: banda de totaluri devine plina si la latimi mici se rupe pe doua randuri. Enuntul
|
||||
complet: **in plan**, la „Decizia 66".
|
||||
|
||||
## Interzis
|
||||
|
||||
- **Nu se atinge `COMUN\clase\ofacturare_comun.vc2` si nici `COMUN\programe\ofacturare_editare.prg`** —
|
||||
@@ -521,11 +621,35 @@ cele patru intrebari ale rundei 14 puse lui Marius direct, **si asezarea zonei d
|
||||
- **Nu se reargumenteaza deciziile 22, 29, 39, 43, 49, 50, 57** — perimetrul lor e inchis.
|
||||
- **Nu se mai propune dialog modal pentru incasare / alte date** si **nu se readuce bara de comenzi
|
||||
jos** — respinse de decizia 57. Nici A / B / C nu se mai propun.
|
||||
- **Nu se mai propune motivul discountului ca sectiune separata jos** — incercat in v9 si **respins de
|
||||
decizia 66**. Sta in banda de totaluri, langa discount.
|
||||
- Fara commit din proprie initiativa: diff-ul se livreaza intai ca fisier in `docs\`.
|
||||
- **Nu se incepe implementarea pe cod** — #13 porneste dupa terminarea lui #6 (decizia 30).
|
||||
|
||||
## Capcane de mediu
|
||||
|
||||
- **NOU (runda 17), si e cea care conteaza pentru oricine lucreaza acum: SESIUNEA DE LA #6 COMITE IN
|
||||
ACEEASI COPIE DE LUCRU, IN PARALEL, INCLUSIV IN `docs\`.** In timpul rundei 17 a dat commit-ul
|
||||
`b5a7108` („cercetarile comune trec in COMUN, reziduul #6 dispare") — o curatenie care a **rescris 39
|
||||
de fisiere din `docs\`**, a mutat 14 cercetari in `COMUN\docs\cercetare\` si a **sters 20 de rapoarte**.
|
||||
Doua consecinte de stiut:
|
||||
1. **A sters si doua fisiere ale lui #13**, incadrate gresit drept reziduu de #6:
|
||||
`mockup_v7_modificari.md` si `mockup_v8_modificari.md`. **Nu sunt pierdute** —
|
||||
`git show d9f5ca4:docs/cercetare/mockup_v8_modificari.md` le scoate. Nu s-au restaurat: erau
|
||||
nereferite si stergerea a fost o curatenie aprobata a altei sesiuni; **daca le vrei inapoi, e o
|
||||
comanda, dar se cere lui Marius intai**.
|
||||
2. **Editarile rundei 17 in plan au fost prinse in acel commit**, nu de mine — `git status` arata
|
||||
`plan_13` si `plan_index` **curate**, desi le-am scris eu. Nu e o dovada ca n-am scris nimic.
|
||||
**Verifica pe continut (`grep`), nu pe `git status`.** Toate trei editarile au supravietuit,
|
||||
confirmat.
|
||||
**Regula practica:** cat timp #6 lucreaza, orice fisier din `docs\` poate fi rescris sau sters sub
|
||||
tine intre doua apeluri. Editarile se **reverifica pe continut dupa** ce le-ai facut, iar fisierele
|
||||
proprii nu se presupun stabile.
|
||||
- **Ruda punctului de mai sus, platita tot in runda 17: un `grep` care nu gaseste nu dovedeste ca
|
||||
lipseste.** Am „constatat" ca decizia 62 lipseste din mockup si eram gata s-o adaug — era acolo,
|
||||
rupta pe doua randuri (`decizia\n62`), iar eu cautasem si forma feminina („trimisa"), nu pe cea din
|
||||
text („trimis"). **Inainte sa declari ceva lipsa dintr-un fisier, cauta termenul cel mai scurt si
|
||||
neflexionat**, si abia apoi forma completa.
|
||||
- **NOU (runda 16): `PACK_CONTAFIN` are DOUA copii pe schema** — `owner = ACN` si
|
||||
`owner = MARIUSM_AUTO`. O interogare pe `all_source` **fara filtru pe `owner`** intoarce cele doua
|
||||
corpuri intercalate, adica **text corupt** care pare cod real. Filtreaza mereu pe owner, sau
|
||||
@@ -595,6 +719,11 @@ trecut chiar prin `oscrie_in_fisiere` — daca cineva „simplifica" tripleta la
|
||||
`sterge_factura`, rulajele raman in picioare si gestiunea se descarca **de doua ori**, fara niciun
|
||||
mesaj. Testul se face pe stocul agregat inainte / dupa, nu pe inspectia codului.
|
||||
|
||||
**Caz adaugat de runda 17, pereche cu cel de mai sus:** un document emis cu un articol caruia i s-a
|
||||
schimbat intre timp `IN_STOC` in nomenclator, deschis in formular, **poarta valoarea de la emitere**,
|
||||
nu pe cea de azi; iar reemiterea lui lasa **stocul agregat neschimbat**. Masurat inainte / dupa, nu
|
||||
prin inspectia codului. E criteriul care demonstreaza ca golul `IN_STOC` din S8 chiar s-a inchis.
|
||||
|
||||
**Nu s-a rulat nimic.** Nu exista inca cod de testat (decizia 30). Testele minime raman in
|
||||
`canal_cont_venit_fara_politica.md` §6, plus cazurile rundei 11 (aviz cu articol fara politica → `SCD`
|
||||
ramane `461` / `418`; `ntip = 46` → `scrie_nota` nu se cheama), probele de paritate ale rundei 12
|
||||
@@ -640,11 +769,19 @@ sursa.
|
||||
> publicat din alta conversatie — asa a fost si in runda 15, si a mers.
|
||||
> **`docs\mockup_13_formular_unificat.html` (v8) nu e atins de regula asta** — Marius a cerut-o
|
||||
> numai pentru mockup-ul asezarii.
|
||||
4. **Golul de la `IN_STOC` in S8** — flag-ul de la S10 nu ajunge daca S8 nu incarca valoarea cu care s-a
|
||||
scris documentul. De prins **acum**, in nota de executie a lui S8, nu la S12.
|
||||
5. **Mockup v9** — v8 a ramas cu patru runde in urma. Nici variantele rundei 14, nici **varianta D a
|
||||
rundei 15 nu sunt integrate in el** — traiesc doar in artifactul de la punctul 3, iar v8 n-a fost
|
||||
atins. Cand se face v9, D e asezarea de pornit.
|
||||
4. ~~**Golul de la `IN_STOC` in S8**~~ — **FACUT in runda 17.** E in plan, in nota de executie a lui S8
|
||||
(caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17"), cu criteriul de test in „gata cand" si cu
|
||||
consecinta 1 de la S10 marcata **PRELUAT**. **Nu se reia.** Ce a ramas in urma lui **nu e o sarcina
|
||||
de executie, e o alegere de facut la proiectarea lui S8**: valoarea istorica a lui `IN_STOC` nu e
|
||||
stocata nicaieri, deci trebuie ales **de unde se reconstituie** (una din cele trei variante scrise
|
||||
in plan). Nu se presupune niciuna.
|
||||
5. ~~**Mockup v9**~~ — **FACUT in runda 17.** `docs\mockup_13_formular_unificat.html` e la **v9.1**, cu
|
||||
varianta D, randul de jos in **doua** sectiuni si motivul discountului in banda de totaluri
|
||||
(decizia 66, care rastoarna decizia 64); raport de modificari:
|
||||
`docs\cercetare\mockup_v9_modificari.md`. **Republicat**, la cererea lui Marius, pe URL-ul existent:
|
||||
**https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142** (notat de acum si in plan,
|
||||
la S1). Ca sa-l modifici: `WebFetch` pe URL intai — fara asta publicarea e refuzata — apoi `Artifact`
|
||||
cu `url` = link-ul de mai sus.
|
||||
6. Dupa aceea **nu mai e proiectare de facut**: #13 asteapta terminarea lui #6 (decizia 30). Prima
|
||||
sarcina la pornirea implementarii e **spargerea planului pe stories** si scrierea notei de executie
|
||||
pentru prima dintre ele (decizia 55).
|
||||
|
||||
@@ -180,6 +180,21 @@ table.doc td.k{white-space:nowrap; font-family:ui-monospace,"Cascadia Mono",Cons
|
||||
|
||||
ul.tight, ol.tight{margin:8px 0 0; padding-left:20px}
|
||||
ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
|
||||
/* ---------- v9: banda de totaluri + randul de trei sectiuni (varianta D, decizia 57/64) ---------- */
|
||||
.totband{display:flex; align-items:center; gap:22px; flex-wrap:wrap; padding:8px 12px; border-top:1px solid var(--line); background:var(--card)}
|
||||
.tb-item{display:flex; align-items:baseline; gap:7px}
|
||||
.tb-item .tb-lbl{font-size:10px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); font-weight:600}
|
||||
.tb-item b{font-variant-numeric:tabular-nums; font-weight:600; font-size:13px}
|
||||
.tb-disc{gap:9px}
|
||||
.tb-disc .inp{width:56px}
|
||||
.tb-grand{margin-left:auto; display:flex; align-items:baseline; gap:10px}
|
||||
.tb-grand .tb-lbl{font-size:11px}
|
||||
.tb-grand b{font-size:17px; font-weight:700; color:var(--accent)}
|
||||
.tb-reason{flex:1 1 200px; min-width:150px}
|
||||
.tb-reason .inp{flex:1 1 auto; min-width:0; font-style:italic}
|
||||
.duo{display:flex; gap:12px; align-items:flex-start}
|
||||
.duo > .panel{flex:1 1 0; min-width:0}
|
||||
</style>
|
||||
|
||||
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
||||
@@ -196,7 +211,7 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<p class="eyebrow">ROAFACTURARE · todos.txt punctul 13 · mockup, versiunea 8 (runda 11)</p>
|
||||
<p class="eyebrow">ROAFACTURARE · todos.txt punctul 13 · mockup, versiunea 9 (runda 17)</p>
|
||||
<h1>Un singur formular de facturare</h1>
|
||||
<p class="lede">
|
||||
Datele documentului si articolele in aceeasi fereastra, fara incarcarea prealabila a tuturor
|
||||
@@ -207,13 +222,17 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
Mockup, nu implementare. Campurile, etichetele si coloanele sunt cele reale, verificate pe cod la
|
||||
09.08.2026 — rapoartele sunt in <code>docs\cercetare\</code>.
|
||||
Plan: <code>docs\plan_13_unificare_formular_facturare.md</code>
|
||||
Asezarea e varianta D (decizia 57, runda 15), cu motivul discountului in banda de totaluri
|
||||
(decizia 66, runda 17).
|
||||
</p>
|
||||
|
||||
<h2><span class="n">1</span>Formularul</h2>
|
||||
<p class="sub">
|
||||
Deschis pe o factura in lei, emisa pe baza de comanda. Antetul e blocat — butonul din bara lui
|
||||
arata creionul. La introducerea unui document nou arata identic, cu antetul deschis si campurile
|
||||
goale.
|
||||
arata creionul. Asezarea e varianta D: antetul strans pe doua randuri, fara panou separat de
|
||||
discount, banda de totaluri lipita de grid pe toata latimea — cu procentul si motivul discountului
|
||||
chiar in ea — si randul de jos cu doua sectiuni colapsabile, incasare si alte date. La introducerea
|
||||
unui document nou arata identic, cu antetul deschis si campurile goale.
|
||||
</p>
|
||||
|
||||
<div class="app">
|
||||
@@ -233,16 +252,14 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pb">
|
||||
<div class="grp">
|
||||
<p class="gl">Documentul</p>
|
||||
<div class="row">
|
||||
<div class="f w-md"><label>Tip document</label><div class="inp ro"><span class="combo">FACTURA</span></div></div>
|
||||
<div class="f w-sm"><label>Serie</label><div class="inp ro">FF</div></div>
|
||||
<div class="f w-sm"><label>Numar</label><div class="inp num ro">1 244</div></div>
|
||||
<div class="f w-sm"><label>Data doc.</label><div class="inp ro">07.08.2026</div></div>
|
||||
<div class="f w-sm"><label>Scadenta</label><div class="inp ro">06.09.2026</div></div>
|
||||
</div>
|
||||
<div class="grp">
|
||||
<p class="gl">Client si sursa</p>
|
||||
<div class="row">
|
||||
<div class="f w-fill"><label>Nume client</label><div class="inp ro">SC EXEMPLU DISTRIBUTIE SRL <span class="find">F4</span></div></div>
|
||||
<div class="f w-md"><label>Cod fiscal</label><div class="inp ro">RO12345678 <span class="find">ANAF</span></div></div>
|
||||
<div class="f w-md"><label>Sold curent</label><div class="inp num ro">14 820,00</div></div>
|
||||
@@ -250,9 +267,6 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
<div class="f w-lg"><label>Gestiune sursa</label><div class="inp ro"><span class="combo">DEPOZIT CENTRAL</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="disclosure">▸ Incasare <span class="hint">alocare/dezalocare — comutator propriu</span></div>
|
||||
<div class="disclosure">▸ Alte date — analitice, delegat si transport, adresa de facturare, text aditional
|
||||
<span class="callout">2</span><span class="hint">nimic completat peste implicit</span></div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
@@ -312,26 +326,40 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
<span class="gb key">Ins — linie noua</span><span class="gb key">Del — sterge</span>
|
||||
<span class="gb key">F4 — cauta</span><span class="gb key">Ctrl+N / Ctrl+D</span>
|
||||
</div>
|
||||
<div class="totband">
|
||||
<div class="tb-item"><span class="tb-lbl">Baza</span><b>4 696,00</b></div>
|
||||
<div class="tb-item"><span class="tb-lbl">Discount articole</span><b>84,00</b></div>
|
||||
<div class="tb-item tb-disc"><span class="tb-lbl">Discount document</span><span class="callout">4</span>
|
||||
<span class="inp num">0,00 %</span><b>0,00</b>
|
||||
<span class="check tiny"><span class="box on">✓</span> evidentiat pe factura</span>
|
||||
</div>
|
||||
<div class="tb-item tb-reason"><span class="tb-lbl">Motiv</span><span class="callout">5</span>
|
||||
<span class="inp ro">motivul discountului…</span>
|
||||
</div>
|
||||
<div class="tb-item"><span class="tb-lbl">TVA</span><b>986,16</b></div>
|
||||
<div class="tb-grand"><span class="tb-lbl">Total factura</span><b>5 682,16 lei</b></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="foot">
|
||||
<div class="duo">
|
||||
<section class="panel">
|
||||
<div class="ph">Discount pe document <span class="callout">4</span></div>
|
||||
<div class="pb">
|
||||
<div class="row">
|
||||
<div class="f w-xs"><label>Procent</label><div class="inp num">0,00</div></div>
|
||||
<div class="f w-md"><label>Suma (lei)</label><div class="inp num ro">0,00</div></div>
|
||||
</div>
|
||||
<div class="row"><span class="check"><span class="box on">✓</span> Se pune in evidenta discount-ul pe articole in notele contabile si pe factura</span></div>
|
||||
<div class="ph">▸ Incasare
|
||||
<span class="right"><span class="stlbl">NUMERAR · 5 570,55 lei</span></span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="panel">
|
||||
<div class="ph">Totaluri</div>
|
||||
<div class="ph">▾ Alte date <span class="callout">2</span></div>
|
||||
<div class="pb">
|
||||
<div class="tot"><span>Total baza</span><b>4 696,00</b></div>
|
||||
<div class="tot"><span>Discount articole</span><b>84,00</b></div>
|
||||
<div class="tot"><span>TVA</span><b>986,16</b></div>
|
||||
<div class="tot grand"><span>Total factura</span><b>5 682,16 lei</b></div>
|
||||
<div class="row">
|
||||
<div class="f w-sm"><label>Venit/Chelt.</label><div class="inp ro">701</div></div>
|
||||
<div class="f w-sm"><label>Sectie</label><div class="inp ro">—</div></div>
|
||||
<div class="f w-sm"><label>Responsabil</label><div class="inp ro">—</div></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="f w-sm"><label>Delegat</label><div class="inp ro">IONESCU V.</div></div>
|
||||
<div class="f w-sm"><label>Masina</label><div class="inp ro">B 123 XYZ</div></div>
|
||||
<div class="f w-fill"><label>Adresa facturare</label><div class="inp ro">ca a clientului</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -346,18 +374,33 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
scadenta. Fara valuta si fara data de curs — factura e in lei si niciun articol nu are pret in
|
||||
valuta (sectiunea 4). <b>Cat anume deschide</b>, si de ce nu chiar tot in prima etapa:
|
||||
sectiunea 2.</p></div></div>
|
||||
<div class="leg"><span class="callout">2</span><div><h4>Restul, pliat — doua comutatoare</h4>
|
||||
<p>Analiticele, delegatul/transportul, adresa de facturare si textul aditional sub un comutator;
|
||||
<b>incasarea</b> sub altul, separat (decizia 41) — e singura cu efecte laterale reale
|
||||
(alocare/dezalocare de numere) si singura blocata pe document deja emis (decizia 25), deci garda
|
||||
de non-alocare la toggle se scrie si se testeaza intr-un singur loc. Pe un document <b>deja
|
||||
emis</b>, analiticele se vad dar nu se editeaza de aici — sectiunea 2.</p></div></div>
|
||||
<div class="leg"><span class="callout">2</span><div><h4>Randul de jos, doua sectiuni</h4>
|
||||
<p>Incasarea si alte date (analitice, delegat/transport, adresa de facturare, text aditional)
|
||||
stau colapsate, una langa alta, nu una sub alta (decizia 57). Sunt independente — se poate tine
|
||||
deschisa doar una — iar randul inchis arata rezumatul continutului, nu doar titlul: cand
|
||||
incasarea e completata arata suma si modalitatea, cand nu e nimic completat rezumatul spune asta.
|
||||
Campurile din interior stau pe orizontala, doua randuri fiecare. <b>A treia sectiune, pentru
|
||||
motivul discountului, a fost incercata si respinsa</b> — motivul sta langa discount, in banda de
|
||||
totaluri (decizia 66 rastoarna decizia 64).
|
||||
<b>Incasarea ramane cea deosebita</b> (decizia 41): e singura cu efecte laterale reale —
|
||||
alocare/dezalocare de numere — deci garda de non-alocare la deschidere se scrie si se testeaza
|
||||
intr-un singur loc, si e singura blocata pe un document deja emis (decizia 25). Tot pe document
|
||||
emis, analiticele se vad dar nu se editeaza de aici — sectiunea 2.</p></div></div>
|
||||
<div class="leg"><span class="callout">3</span><div><h4>Un singur buton de adaugare</h4>
|
||||
<p><i>Adauga articole</i> deschide un meniu cu optiunile potrivite sursei — sectiunea 3.
|
||||
Dispare gridul de sus cu toate articolele din toate politicile.</p></div></div>
|
||||
<div class="leg"><span class="callout">4</span><div><h4>Discount, procent si valoare</h4>
|
||||
<p>Amandoua exista deja azi, dar intr-un dialog separat pe fiecare articol. Aici sunt doua
|
||||
coloane in grid; tastezi in oricare, cealalta se calculeaza — sectiunea 5.</p></div></div>
|
||||
<div class="leg"><span class="callout">4</span><div><h4>Banda de totaluri, lipita de grid</h4>
|
||||
<p>Baza, discount articole, discount document si TVA, desfacute pe un singur rand, cu totalul
|
||||
mare singur la dreapta (decizia 57). Discountul de document se editeaza pe loc, procentul langa
|
||||
suma; bifa „se pune in evidenta…" de pe panoul care disparea s-a mutat aici, discret. Repartizarea
|
||||
pe cote de TVA e automata si proportionala (decizia 59) — nu se cere nimic utilizatorului.</p></div></div>
|
||||
<div class="leg"><span class="callout">5</span><div><h4>Motivul discountului, langa discount</h4>
|
||||
<p>Singura bucata din reparatia discountului de document care cere UI nou (decizia 61): text
|
||||
optional, ajunge in <code>AllowanceChargeReason</code> din eFactura (<code>ReasonCode</code>
|
||||
ramane <code>95</code>). Sta <b>in banda de totaluri, imediat dupa suma pe care o explica</b>
|
||||
(decizia 66) — nu ca sectiune separata jos, cum se stabilise la decizia 64. Ia latimea ramasa
|
||||
intre discount si TVA; <b>e activ doar cand discountul de document nu e zero</b> — un motiv fara
|
||||
discount n-are ce explica.</p></div></div>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">2</span>Modificarea antetului, fara sa treci prin articole</h2>
|
||||
@@ -704,6 +747,13 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
daca e transmis in eFactura si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica
|
||||
totalul.
|
||||
</p>
|
||||
<p class="meta">
|
||||
<span class="pill have">decis</span> <b>Discountul de DOCUMENT (nu cel de linie, de mai sus) se
|
||||
repartizeaza proportional pe cote de TVA, nu pe cota maxima cum face codul de azi (decizia 59).</b>
|
||||
E automat, nu cere nimic de la operator. In banda de totaluri (sectiunea 1) se vede o singura suma;
|
||||
pe factura tiparita, cu cote mixte, pot aparea mai multe randuri „Discount X % Factura", cate unul
|
||||
per cota.
|
||||
</p>
|
||||
|
||||
<h2><span class="n">6</span>De unde se intra in formular</h2>
|
||||
<p class="sub">
|
||||
@@ -794,9 +844,27 @@ ul.tight li, ol.tight li{margin:0 0 8px; color:var(--ink-2); font-size:13.5px}
|
||||
filtreaza <code>STERS = 0</code>, deci trebuie citita inainte de stergere.</li>
|
||||
<li><span class="pill check">de verificat</span>
|
||||
<b>Stergerea si reemiterea in aceeasi tranzactie</b>, fara commit intre ele.</li>
|
||||
<li><span class="pill check">de verificat</span>
|
||||
<b>Pretul nu se re-deriva</b> la reemitere, pe ramurile unde serverul il recalculeaza din
|
||||
documentul sursa.</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b>La reemitere se scriu valorile din formular, nu se reciteste sursa (decizia 54).</b> Intrebarea
|
||||
nu mai e ce garda punem peste re-derivare — e daca re-derivarea chiar se produce; raspunsul lui
|
||||
Marius: nu trebuie sa se produca. Fara avertizare si fara confirmare, a fost respinsa explicit.</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b>Atasamentul PDF al documentului vechi se sterge la reemitere, nu se remigreaza (decizia 53).</b>
|
||||
Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare.</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b>Facturile de marfa in custodie (tipurile 48 si 49) sunt editabile prin #13 (decizia 60).</b>
|
||||
Cu restrictia pastrata: pe ele nu se pot adauga articole gestionabile
|
||||
(<code>IN_STOC <> 0</code>).</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b>Singura restrictie de modificare e ca documentul sa nu fi fost trimis in eFactura (decizia
|
||||
62).</b> Fara nicio conditie de data, fara retroactivitate.</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b>Auditul (data + utilizator pentru adaugare, modificare si stergere) se afiseaza in gridul din
|
||||
<code>frm_facturi</code>, nu in formularul unificat (deciziile 63 si 65).</b> Formularul nu capata
|
||||
niciun control nou din cerinta de audit.</li>
|
||||
<li><span class="pill have">decis</span>
|
||||
<b><code>do_modifica</code> de pe <code>frm_facturi</code> ramane activ pentru multi-selectie
|
||||
(decizia 52).</b> Formularul unificat preia doar cazul cu un singur document.</li>
|
||||
<li><span class="pill check">de verificat</span>
|
||||
<b>Descarcarea de gestiune pe proforma</b>, pe partea Oracle.</li>
|
||||
<li><span class="pill check">de verificat</span>
|
||||
|
||||
@@ -1603,7 +1603,7 @@ Factura" in loc de unul, si nota contabila primeste TVA-ul discountului spart pe
|
||||
|
||||
**Cele doua puncte ramase s-au inchis si ele, tot in runda 16.** Campul text optional de motiv —
|
||||
**decizia 61**: da, se adauga (`AllowanceChargeReason`, `ReasonCode` ramane `95`), cu stocare noua
|
||||
pe `VANZARI` si un control nou in formular; ramane deschisa doar asezarea lui pe rand. Retroactivi-
|
||||
pe `VANZARI` si un control nou in formular. Asezarea lui s-a inchis in runda 17 — **decizia 66**: sta in banda de totaluri, langa discount. Retroactivi-
|
||||
tatea la relistare/retrimitere — **decizia 62**: restrictia de modificare e strict `EsteInEFactura`,
|
||||
fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei
|
||||
facturi vechi deja trimise, vezi decizia 62.
|
||||
@@ -2063,9 +2063,19 @@ deschis. Ce e de retinut aici, pentru executia lui S1:
|
||||
repartizare proportionala automata pe cote, discountul **NU cere cota de la utilizator**, deci **nu
|
||||
e nevoie de o a treia sectiune pentru cota**. Ce mai cere UI e **un camp text optional de motiv**
|
||||
(pentru `AllowanceChargeReason`), mult mai mic decat o sectiune. **Decizia 61 (runda 16) l-a
|
||||
materializat: Marius vrea campul. Decizia 64 (runda 16) a inchis si asezarea: randul se imparte
|
||||
in trei** — incasare, alte date si motivul discountului, pe acelasi rand (varianta grea, cota +
|
||||
explicatie proprii, ramane exclusa, ca mai sus).
|
||||
materializat: Marius vrea campul. Asezarea lui s-a inchis definitiv abia in runda 17 — decizia 64
|
||||
(a treia sectiune jos) a fost **rasturnata de decizia 66: motivul sta in banda de totaluri, langa
|
||||
suma pe care o explica**, iar randul de jos ramane cu **doua** sectiuni, ca la decizia 57. Varianta
|
||||
grea (cota + explicatie proprii) ramane exclusa, ca mai sus.
|
||||
|
||||
**Cele doua pagini online ale lui #13 — nu se confunda:**
|
||||
- **mockup-ul formularului** (fisier pe disc, `docs\mockup_13_formular_unificat.html`, **v9** din runda
|
||||
17, cu asezarea D si motivul discountului in banda de totaluri):
|
||||
**https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142**.
|
||||
Are copie pe disc, si ea e sursa de adevar; artifactul se republica din ea (`WebFetch` pe URL intai,
|
||||
apoi `Artifact` cu `url`). Decizia 33 e consumata — URL-ul e la zi.
|
||||
- **pagina cu variantele de asezare** (A/B/C/D), **fara copie pe disc**, deci artifactul **e** sursa de
|
||||
adevar:
|
||||
|
||||
Pagina cu variantele: **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7**
|
||||
— **nu mai are copie pe disc** (cerinta lui Marius, runda 15: „doar online, ca sa nu mai intretii 2
|
||||
@@ -3008,7 +3018,7 @@ ambele capcane au raspuns cu dovada. Vestea buna: mecanismul de baza chiar exist
|
||||
din `do_copiaza`, `cursor_retur_document` (`GESTIONABIL = B.IN_STOC`), si `scrie_corespondente_vanzari`
|
||||
insasi.
|
||||
|
||||
**De decis de Marius (cinci puncte, niciunul blocant):**
|
||||
**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Cele cinci puncte de mai jos au primit raspuns: punctul 1 (`TIP = 4`) prin **decizia 51**, restul in bloc prin **decizia 56** („da la toate"). Lista ramane ca **inventar al recomandarilor acceptate**, ca sa se stie ce s-a decis si de ce — nu ca intrebari:
|
||||
1. **`TIP = 4`** — liber azi, dar alocarea e **ireversibila in date** odata intrata in productie. De
|
||||
confirmat explicit, nu tacit.
|
||||
2. **Apel pe starea de sesiune a pachetului (`clistaid` / `nid_vanzare`) vs. procedura noua cu
|
||||
@@ -3226,8 +3236,8 @@ cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in poveste
|
||||
**cercetare TERMINATA** (sectiunea K-bis): cota era regula implicita „cota maxima de pe factura",
|
||||
explicatia e azi o constanta hardcodata (`ReasonCode="95"`, text „Discount"). **Partea de
|
||||
repartizare s-a decis — decizia 59, runda 16: proportional pe cote.** Ramane deschis doar campul
|
||||
text optional de motiv.
|
||||
*Alegerea variantei de asezare, listata aici ca a treia, s-a inchis intre timp — decizia 57.*
|
||||
text optional de motiv — **inchis si el: decizia 61** (da, se adauga), cu asezarea fixata de
|
||||
**decizia 66** (in banda de totaluri, langa discount).
|
||||
55. **Metoda de executie e obligatorie, si e scrisa in plan.** La implementare planul **se sparge pe
|
||||
stories**, fiecare story fiind o livrare de sine statatoare; **fiecare pas se testeaza**, nu doar
|
||||
capetele de etapa (S6 / S12); si **fiecare story trece prin code review dupa implementare si dupa
|
||||
@@ -3272,9 +3282,10 @@ cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in poveste
|
||||
deschis (b) de la decizia 56) **s-a terminat** — vezi sectiunea K-bis — si repartizarea s-a decis
|
||||
(**decizia 59, runda 16**: proportional pe cote, fara sa se ceara cota de la utilizator), deci a
|
||||
treia sectiune „grea" (cota + explicatie proprii) nu mai e in discutie. Ramane varianta minimala:
|
||||
**decizia 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv — si
|
||||
**decizia 64 (runda 16) a inchis si asezarea: randul se imparte in trei**, ~440 px fiecare la
|
||||
1366 px, strans. D ramane intr-un etaj.
|
||||
**decizia 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv. Asezarea
|
||||
lui a oscilat o data: decizia 64 il facea a treia sectiune jos, **decizia 66 (runda 17) o
|
||||
rastoarna si il muta in banda de totaluri, langa discount**. **Punctul 2 de mai sus ramane deci
|
||||
exact cum a fost aprobat: doua sectiuni jos, fiecare pe jumatate de latime.** D ramane intr-un etaj.
|
||||
|
||||
58. **Mockup-ul asezarii ramane doar online.** Formularea lui: *„nu vreau artifactul html, doar online,
|
||||
ca sa nu mai intretii 2 variante"*. `docs\mockup_13_variante_asezare_jos.html` **a fost scos din
|
||||
@@ -3380,10 +3391,11 @@ cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in poveste
|
||||
- cere **un control nou in formular** — singura bucata din reparatia discountului (K-bis /
|
||||
decizia 59) care chiar cere UI nou, restul fiind repartizare automata fara interactiune.
|
||||
|
||||
**Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57 — INCHISA acum prin
|
||||
decizia 64 (runda 16): randul de jos se imparte in trei** (~440 px fiecare la 1366 px, strans);
|
||||
varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate in
|
||||
acest sens.
|
||||
**Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57. INCHISA DEFINITIV
|
||||
PRIN DECIZIA 66 (runda 17): campul sta in banda de totaluri, langa discount.** Decizia 64, care il
|
||||
facea a treia sectiune jos, **e rasturnata** — randul de jos ramane cu doua sectiuni, ca la decizia
|
||||
57. Varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate
|
||||
in acest sens.
|
||||
|
||||
62. **Restrictia de modificare e „documentul sa NU fi fost trimis in eFactura" — si de aici
|
||||
retroactivitatea NU mai e o intrebare.** Formularea lui: *„singura restrictie pentru modificare
|
||||
@@ -3421,11 +3433,16 @@ cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in poveste
|
||||
**Poveste noua, proiectata: S14** — verdict complet, recomandare si punctele ramase de decis cu
|
||||
Marius acolo.
|
||||
|
||||
64. **Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.** Formularea lui: *„campul de motiv
|
||||
imparte randul in 3"*. **Inchide** ultimul punct ramas deschis din decizia 57 (varianta D de
|
||||
asezare), reluat de decizia 61: randul de jos al formularului unificat are **trei sectiuni pe
|
||||
acelasi rand** — incasare, alte date si motivul discountului — nu coboara pe rand propriu, nu
|
||||
devine D cu doua etaje. La 1366 px inseamna ~440 px de sectiune, strans, si asumat ca atare.
|
||||
64. ~~**Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.**~~ **RASTURNATA DE DECIZIA 66
|
||||
(runda 17) — NU MAI E IN VIGOARE.** Formularea de atunci: *„campul de motiv imparte randul in 3"*;
|
||||
randul de jos ar fi avut trei sectiuni (incasare, alte date, motivul discountului), ~440 px
|
||||
fiecare la 1366 px.
|
||||
|
||||
**Se pastreaza ca istorie, nu ca regula**, si merita pastrata: varianta a fost **construita in
|
||||
mockup (v9) si respinsa dupa ce Marius a vazut-o** — *„motiv discount vreau sa fie langa discount,
|
||||
nu a treia coloana"*. E argumentul cel mai bun din tot planul pentru **de ce se face mockup
|
||||
inainte de cod**: decizia luata pe descriere s-a intors la prima privire pe forma desenata.
|
||||
Ce e in vigoare: **decizia 66**, mai jos.
|
||||
|
||||
65. **Auditul (decizia 63) se afiseaza in gridul din `frm_facturi`, nu in formularul facturii.**
|
||||
Formularea lui: *„auditul presupun coloanele dataora si utilizator adaugat/modificat/sters, le
|
||||
@@ -3442,6 +3459,33 @@ cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in poveste
|
||||
**inventarul gridului din `frm_facturi`** — ce coloane de audit sunt deja acolo, ce surse au —
|
||||
si abia apoi adauga ce lipseste. Nu se presupune nici ca exista, nici ca nu.
|
||||
|
||||
### Decizia 66 (Marius, runda 17) — luata, nu de reluat. RASTOARNA DECIZIA 64.
|
||||
|
||||
66. **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri — nu ca a treia sectiune jos.**
|
||||
Formularea lui: *„motiv discount vreau sa fie langa discount, nu a treia coloana"*.
|
||||
|
||||
**Anuleaza decizia 64** (randul de jos se imparte in trei). Randul de jos revine la **doua
|
||||
sectiuni colapsabile** — incasare si alte date — adica exact la ce aprobase decizia 57, punctul 2,
|
||||
inainte ca decizia 61 sa redeschida discutia. **Decizia 57 ramane intreaga; nu se mai atinge.**
|
||||
|
||||
**Ce inseamna concret, pentru S1 si S3:**
|
||||
- campul de motiv e un **control in banda de totaluri**, imediat dupa suma discountului de
|
||||
document, inainte de TVA; ia latimea ramasa pana la totalul mare, care sta la dreapta;
|
||||
- **cele doua sectiuni de jos redevin pe jumatate de latime** (~660 px la 1366 px), nu ~440 —
|
||||
argumentul „strans, asumat ca atare" din decizia 64 **cade odata cu ea**;
|
||||
- **regula de activare, adaugata de proiectare, nu ceruta explicit:** campul e activ **numai cand
|
||||
discountul de document nu e zero**. Un motiv fara discount n-are ce explica, si ar ajunge in
|
||||
`AllowanceChargeReason` pe un `AllowanceCharge` inexistent. Daca Marius vrea altfel, e o linie
|
||||
de schimbat.
|
||||
|
||||
**Consecinta de asezare, de stiut la implementare:** banda de totaluri devine plina — baza,
|
||||
discount articole, discount document (procent + suma + bifa „evidentiat"), motiv, TVA, total. La
|
||||
latimi mici **se rupe pe doua randuri** (`flex-wrap`), si asta e acceptat: alternativa ar fi
|
||||
scoaterea bifei „evidentiat" din banda, care n-a fost ceruta.
|
||||
|
||||
Restul deciziei 61 (stocare noua pe `VANZARI`, `AllowanceChargeReason`, `ReasonCode` ramane `95`)
|
||||
**e neatins** — se schimba doar locul controlului in formular.
|
||||
|
||||
#### S6 — Test pe fluxul real, formularul unificat
|
||||
Headless (`COMUN\docs\depanare_testare_vfp.md`) + UI (`COMUN\docs\testare-ui-vfp.md`), cate un caz
|
||||
pe fiecare sursa: lista de preturi, comanda, contract, aviz, retur, transfer, valuta. Comparatie
|
||||
@@ -3514,8 +3558,8 @@ luna inchisa, referentiate, sau care au urmasi (retur, factura din aviz, factura
|
||||
> `ofacturare.vc2:15741-19355`). Deci S8 il tinteste pe el, nu pe `frm_facturare_articole`. Faptul ca
|
||||
> are `do_adauga_tot` / `do_adauga_articol` proprii, cu logica divergenta (fara testul `llGestionabil`,
|
||||
> `ofacturare.vc2:17476`, `:17124`), **nu e un motiv de excludere, e exact driftul din 2017 pe care S2
|
||||
> il inchide** — cele doua se unifica, nu se aleg. Din intrebarea 7 ramane deschisa **numai** partea de
|
||||
> tipuri 48/49.
|
||||
> il inchide** — cele doua se unifica, nu se aleg. Partea de tipuri 48/49 din intrebarea 7 **s-a
|
||||
> inchis prin decizia 60**: sunt editabile. Intrebarea 7 e deci inchisa integral.
|
||||
|
||||
> **CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17 — `IN_STOC` se incarca din document, nu din nomenclator.**
|
||||
> Vine din consecinta 1 a deciziei 54 (vezi S10, „Trei consecinte"), si se prinde **aici**, nu la S12.
|
||||
@@ -3604,7 +3648,7 @@ dar avea un gol nedocumentat, si el schimba forma solutiei.**
|
||||
- **Comparatia se face pe valori rotunjite la precizia de scriere** (`gnPc` / `gnPPretV` / `gnPCant`),
|
||||
nu pe valoarea binara — altfel rotunjirea singura produce diferente.
|
||||
|
||||
**De decis de Marius:**
|
||||
**INCHIS, nu de reintrebat (marcaj pus in runda 17).** Punctul de mai jos are raspuns: **decizia 52** — `do_modifica` ramane activ pentru multi-selectie, iar formularul unificat preia doar cazul cu un singur document. Se pastreaza enuntul, nu intrebarea:
|
||||
1. **Editarea multipla** — `do_modifica` de azi lucreaza pe multi-selectie, capacitate fara echivalent
|
||||
in formularul unificat. Se accepta pierderea ei la retragere, sau `do_modifica` ramane activ separat
|
||||
exact pentru cazul asta?
|
||||
@@ -3976,7 +4020,7 @@ nu-i gasise.**
|
||||
- **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau
|
||||
rescrise automat.
|
||||
|
||||
**De decis de Marius:**
|
||||
**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Punctul 1 e transat de cercetarea garzilor din runda 14 (garda exista si e corecta; S7 o muta in pre-flight read-only) — ramane **limitare declarata**. Punctul 2 e rasturnat de **decizia 53**: atasamentul vechi **nu se remigreaza, se sterge**, deci premisa „factura editata ajunge cu PDF-ul vechi" nu se mai produce. Punctul 3 e inchis in runda 14, cu cerinta ca **S7 sa refuze explicit** cele doua tipuri. Se pastreaza ca inventar:
|
||||
1. **Editarea unui document care are deja retur emis pe el e azi BLOCATA, nu doar riscanta** —
|
||||
`sterge_factura` arunca `ORA-20000`. Garda e corecta (protejeaza lantul), iar **S7 planifica deja
|
||||
sa semnaleze conditia inainte de intrarea in formular**, nu ca eroare Oracle la final. Ce ramane de
|
||||
|
||||
371
docs/progres.md
371
docs/progres.md
@@ -4,8 +4,76 @@
|
||||
incheie. Planurile (`plan_0*.md`) spun *ce* e de facut; acesta spune *unde s-a ajuns*. Istoricul
|
||||
sesiunilor nu se pastreaza aici — doar starea la zi, deciziile, faptele stabilite si datoriile.
|
||||
|
||||
Ultima actualizare: **11.08.2026, seara**. **Punctul #6 e practic terminat: S1-S7 si S9 sunt gata si
|
||||
comise.** Ramane **S8** — si nu ca lipsa de acoperire, ci cu doua esecuri reale.
|
||||
Ultima actualizare: **19.08.2026**.
|
||||
|
||||
> **RUNDA 19.08.2026 — butoanele de linie trec pe butoanele laterale ale grid-ului de articole.**
|
||||
> Cerere Marius, executata si testata; **fara commit**.
|
||||
>
|
||||
> **Eroarea raportata** (`gnButon este redefinit ilegal` la „Adauga articol") avea cauza in doua
|
||||
> `PUBLIC gnButon` din `omodificari.vc2` (`cmdAdaugaArticol.Click` si
|
||||
> `AfiseazaDialogSincronizareArticole`): `gnButon` e declarat **PRIVATE** la nivelul aplicatiei
|
||||
> (`Programe\roafacturare.prg:400`), iar restul codebase-ului doar il atribuie. Ambele declaratii
|
||||
> au fost sterse.
|
||||
>
|
||||
> **Ce s-a schimbat**: butoanele late „Adauga articol" / „Sterge / Restaureaza linie" au disparut de
|
||||
> pe pagina; comenzile stau acum pe coloana de butoane din dreapta grid-ului — **but_nouR** (nou,
|
||||
> clasa `but_nou`, `caction = do_adauga_rul`), `but_copiazaR` (duplica linia), `but_modificaR`
|
||||
> (nomenclatorul coloanei curente: articol, gestiune, valuta, cod TVA) si `but_stergeR` (comuta
|
||||
> `sters`). Toate dispecerizeaza dupa `pgfArticole.ActivePage = 3`. „Sincronizeaza cu rulaje..." a
|
||||
> ramas pe pagina, mutat la stanga (`Anchor = 0`) si evidentiat (fundal galben pal, text bold violet).
|
||||
>
|
||||
> **Logica noua sta in `.prg`, nu in binar** — clasa `ArticoleNotaEditor` din
|
||||
> `COMUN\programe\ofacturare_editare.prg` (`AdaugaLinie`, `ComutaSters`, `DuplicaLinie`,
|
||||
> `ModificaNomenclator`, `AreNomenclator`, `Editabil`); metodele din `.vc2` sunt apeluri de 3-4 linii.
|
||||
> Preferinta lui Marius, scrisa acum si in `COMUN\docs\reguli_lucru.md`, punctul 3.
|
||||
>
|
||||
> **Capcana platita**: `Createobject('X', p).Metoda()` **nu e sintaxa valida in VFP** — da „Syntax
|
||||
> error" la rulare, prins de `test_efactura_readonly`. Se trece prin variabila.
|
||||
>
|
||||
> **Defect latent reparat in trecere**: `AdaugaLinieTvdDinArticol` si `calculeaza_valori_articol` erau
|
||||
> metode de clasa **fara intrare `*m:`** in `*<DefinedPropArrayMethod>` — mergeau, dar prima salvare
|
||||
> din IDE le-ar fi aruncat tacit. Intrarile au fost adaugate.
|
||||
>
|
||||
> **Teste**: `test_butoane_laterale_articole` (suita noua) 33/0, `test_efactura_readonly` 21/0,
|
||||
> `test_s4b_dialog` 35/0, `test_s4b_sincronizare` 42/0, `test_adauga_linie_articol` 20/0,
|
||||
> `test_ui_sterge_linie` 8/0, `test_ui_culoare_contrast` 1/0, `test_ui_efactura_readonly` 13/0,
|
||||
> `test_page3_articole` 14/2 (**cele 2 = artefactul cunoscut de grid nematerializat headless**, la
|
||||
> baseline). Cele patru suite care tinteau butoanele sterse au fost mutate pe butoanele laterale.
|
||||
>
|
||||
> **Stare**: `omodificari.vc2` are **write-back-ul facut** (fidelity-check trecut de trei ori); cens de
|
||||
> octeti >0x7F identic cu baseline-ul (2x`aa`, 2x`e3`, 2x`fe`), zero LF izolati. Patch de review:
|
||||
> `docs\diff_runda_butoane_laterale_articole.patch`. Backup-uri:
|
||||
> `COMUN\clase\omodificari.pre_runda_butoane.bak.vc2`,
|
||||
> `COMUN\programe\ofacturare_editare.prg.pre_runda_butoane.bak`. **Niciun commit, in niciun repo.**
|
||||
> Changelog **neatins deliberat**: 2.11.15 n-a ajuns la utilizatori, iar intrarea existenta descrie
|
||||
> deja functionalitatea finala.
|
||||
>
|
||||
> **Ramas deschis**: `AdaugaLinieTvdDinArticol` si `calculeaza_valori_articol` sunt inca logica in
|
||||
> binar — de mutat in `ArticoleNotaEditor` la o runda viitoare (rup testele care le apeleaza direct,
|
||||
> deci nu s-a facut acum). Pe formularul maximizat, bara de totaluri se suprapune peste randurile 3-4
|
||||
> ale grid-ului de articole (grid ancorat 15, totalurile nu) — **preexistent**, nu s-a atins.
|
||||
|
||||
**Punctul #6 e terminat pe partea care se poate face fara Marius:
|
||||
S1-S9 sunt gata, inclusiv S8.** Ce ramane cere aplicatia pornita, IDE-ul sau o decizie — nu mai e
|
||||
lucru de delegat unui agent.
|
||||
|
||||
> **PREDARE, 13.08.2026.** Sesiunea a inchis trei lucruri si a deschis zero. **S8** — matricea era deja
|
||||
> rulata, „cele doua esecuri" erau o stare pierduta la o curatenie; aserttia prea larga s-a restrans.
|
||||
> **Asimetria de garda** — acceptata, fara atingere de cod. **Validarea de cantitate** — negativul
|
||||
> (retur, storno, discount) nu mai e respins, iar blocul S4b nu mai deschide un modal pe document gol.
|
||||
> Regresia: **10/10 suite headless la baseline**.
|
||||
>
|
||||
> **Nimic nu e intr-o stare periculoasa.** `omodificari.vc2` are write-back-ul facut si **verificat pe
|
||||
> binar** (nu pe mtime); zero procese `vfp9.exe`; pe Oracle numai `SELECT` sub `READ ONLY` cu
|
||||
> `ROLLBACK`; **niciun commit** in niciun repo. Documente de test consumate in acest bloc: **niciunul**
|
||||
> — matricea S8 nu a fost rerulata, deliberat.
|
||||
>
|
||||
> **Prima sarcina a sesiunii urmatoare**: nu incepe cod nou. Citeste punctele 1-6 din lista de mai jos —
|
||||
> toate cer aplicatia, IDE-ul sau o decizie a lui Marius. **Nu redeschide** asimetria de garda si nu
|
||||
> reinvestiga S8: ambele sunt inchise mai jos, cu motivele scrise.
|
||||
>
|
||||
> **Fire in paralel**: alta sesiune lucreaza pe **#13** (`docs\handoff_13_formular_unificat.md`,
|
||||
> `mockup_13_formular_unificat.html`, `plan_13_*.md` — apar modificate in `git status`). **Nu le atinge.**
|
||||
|
||||
**PREDAREA CURENTA e chiar acest fisier.** Handoff-urile intermediare intre sesiuni au fost
|
||||
desfiintate (11.08.2026, decizia lui Marius) impreuna cu diff-urile deja aplicate; ce era durabil in
|
||||
@@ -15,18 +83,24 @@ ele a intrat in antetele fisierelor de test sau in mesajele de commit. Nu mai ca
|
||||
|
||||
1. **Stergerea celor sase documente parazite** (`1051`, `1056`, `1057`, `1058`, `1059`, `1060`) prin
|
||||
aplicatie — ramasa din blocul de creare documente pentru S8.
|
||||
2. **S8 — doua esecuri reale.** Pe „factura din aviz" (tip 4), din **ambele** puncte de intrare,
|
||||
rulajele nu se refac pe nota noua (`Reccount(trul)=0`): `test_s8_matrice_surse.prg` da
|
||||
**42 PASS / 2 FAIL**. Tot acolo, trei tipuri de sursa au fost sarite la ultima rulare (lista de
|
||||
preturi `1048`, aviz `1052`, factura din contract `1055`), iar harnessul
|
||||
`COMUN\utile\Teste\editare_factura\creeaza_documente_s8.prg` **nu a scris inca niciun document** —
|
||||
capcanele de mediu (plaja de serii care chiar prinde, ordinea lui `mock_amessagebox`) sunt scrise
|
||||
in antetul lui.
|
||||
2. ~~S8 — doua esecuri reale.~~ **INFIRMAT pe 12.08.2026. S8 e gata, n-a fost niciodata un esec** —
|
||||
vezi sectiunea „S8 INCHIS" de mai jos. Nu relua investigatia.
|
||||
3. **Rebuild `roafacturare.exe`** din IDE si verificarea pe ecran, pe `1140895` / `1140921` — ambele
|
||||
contin `IV93900901` (`id_articol = 3598545102`), adica exact cazul corectat pe 11.08.
|
||||
4. **Punctele A si B** din `docs\rec_s4b_etapa2.md` — **deja implementate**, asteapta doar
|
||||
confirmarea lui Marius. Nu sunt lucru ramas.
|
||||
4. **Punctul B** din `docs\rec_s4b_etapa2.md` — **CONFIRMAT 12.08.2026**, ramane cum e.
|
||||
**Punctul A s-a transformat** intr-o intrebare mai mare, despre validarea de cantitate — vezi
|
||||
sectiunea de mai jos. **Deschis.**
|
||||
5. **`git push` nefacut** in ambele repo-uri (remote `romfast`) — decizia lui Marius.
|
||||
6. **Necomis din 12-13.08.2026, asteapta review.** In `COMUN`: `clase\omodificari.vc2` — validarea de
|
||||
cantitate (`:14386`) si garda pe blocul S4b (`:14438`), **write-back facut si verificat pe binar**,
|
||||
diff `docs\diff_cantitate_negativa_garda_s4b.patch`; plus aserttia relaxata din
|
||||
`utile\Teste\editare_factura\test_s8_matrice_surse.prg`, diff `docs\diff_s8_aserttie_rulaje.patch`.
|
||||
In `ROAFACTURARE`: `docs\cercetare\rec_s8_matrice.md` (**recuperat** din `fc9c378`, fusese sters din
|
||||
greseala), `rec_s8_rulaje_tip4.md`, `rec_cantitate_negativa_date.md`, `rec_regresie_cantitate.md`.
|
||||
**Backup-uri de revenire**, de sters la curatenie: `omodificari.pre_cantitate.bak.vc2` si
|
||||
`omodificari.pre_garda.bak.vc2`.
|
||||
**Changelog: nefacut** — de decis daca schimbarea de cantitate intra in `2.11.15` (nu e inca in
|
||||
productie, deci `:modificare:`, nu `:eroare:`).
|
||||
|
||||
**Comis pe ramura `punct6-s4-runda3`, 11.08.2026**: `COMUN` `40a112a` (S4b etapa 2 — butonul,
|
||||
dialogul `frm_sincronizare_articole`, `id_articol` de la `I` la `N(20)` in toate cele 6 locuri, si
|
||||
@@ -37,7 +111,8 @@ doua `This.` -> `Thisform.` care faceau butonul „Adauga articol" sa dea eroare
|
||||
**Cifre de test, citite din log, nu din rapoarte**: `test_s4b_sincronizare` **42/0** (include cazul de
|
||||
regresie `id_articol = 3598545102`, dovedit cu control negativ — cu campul `I` iese perechea
|
||||
`Adaugare`+`Semnalare` in loc de `Modificare`), `test_s4b_dialog` **35/0**, `test_s7_rotunjire` **6/0**,
|
||||
`test_s8_matrice_surse` **42 PASS / 2 FAIL**.
|
||||
`test_s8_matrice_surse` pe cele patru felii **44/0, 44/0, 26/1, 42/2** (vezi „S8 INCHIS" — cele 3 FAIL
|
||||
sunt asteptate si explicate, nu defecte).
|
||||
|
||||
**Doua capcane de mediu platite pe 11.08, valabile pentru orice sesiune viitoare:**
|
||||
|
||||
@@ -58,9 +133,182 @@ trans-proiect au trecut in `COMUN\docs\cercetare\` (valuta si curs, TVA/VANZARI,
|
||||
`VANZARI` in suita, integrarile #10/#11/#12, watchdog VFP, proiectarea Oracle a lui S5, view-ul
|
||||
`VVANZARI_ARTICOLE`), iar 20 de rapoarte de executie ale lui #6 au fost sterse.
|
||||
|
||||
**Starea la incheierea sesiunii — verificat, nu presupus**: zero procese `vfp9.exe`, zero tranzactii
|
||||
Oracle deschise (in acest bloc nu s-a atins Oracle), niciun fisier binar editat fara write-back,
|
||||
ambii arbori git curati fata de commituri.
|
||||
**Starea la incheierea sesiunii din 13.08.2026 — verificat, nu presupus**: zero procese `vfp9.exe`;
|
||||
pe Oracle numai `SELECT` sub `SET TRANSACTION READ ONLY` incheiat cu `ROLLBACK`, zero tranzactii
|
||||
deschise; **`omodificari.vc2` e singurul fisier de cod de productie atins, si are write-back-ul facut
|
||||
si dovedit pe binar** (condiitile noi apar in `omodificari.VCT`, cele vechi zero) — deci niciun binar
|
||||
in urma textului; **niciun commit** in niciunul din cele doua repo-uri.
|
||||
|
||||
**De verificat pe ecran de Marius** (netestabil headless): mesajul „are cantitatea 0" pe o linie cu
|
||||
cantitate 0, si salvarea reusita a unei facturi reale cu linie de retur (cantitate negativa).
|
||||
|
||||
## DESCHIS — validarea de cantitate din `inainte_de_do_termin` respinge returul si storno-ul?
|
||||
|
||||
**Ridicata de Marius, 12.08.2026**, ca observatie la punctul A din `rec_s4b_etapa2.md`: *„pot exista
|
||||
articole cu cantitate negativa (retur, storno) sau cu pret 0"*.
|
||||
|
||||
Ce spune codul, verificat la sursa (`frm_modific2024.inainte_de_do_termin`,
|
||||
`COMUN\clase\omodificari.vc2:14331-14599`, blocul de validari `:14378-14430`):
|
||||
|
||||
| Ancora | Verifica | Efect |
|
||||
|---|---|---|
|
||||
| `:14386` | `Nvl(cantitate,0) <= 0` | **BLOCHEAZA** — „are cantitatea 0 sau negativa" |
|
||||
| `:14394` | `Isnull(pret)` | blocheaza doar `NULL`, **nu** si `0` |
|
||||
| `:14402` | `Nvl(id_articol,0) = 0` | blocheaza |
|
||||
| `:14410` | linie noua cu `pret_achizitie = 0` | doar confirmare |
|
||||
|
||||
**Pretul 0 nu e o problema** — `:14394` testeaza `Isnull`, deci `0` trece. (E si ramura moarta
|
||||
cunoscuta: `tvd.pret` vine `NOT NULL` din view.) **Cantitatea negativa insa cade pe `:14386`**, si nu
|
||||
doar dupa „Aplica" din dialogul S4b — pe **calea normala de salvare**, la orice editare a unui
|
||||
document cu articole de vanzari.
|
||||
|
||||
**Consecinta asupra punctului A**: plasa de siguranta propusa initial (reluarea validarilor blocante
|
||||
dupa „Aplica") **nu se mai face**. Din cele trei, `Isnull(pret)` e ramura moarta si `id_articol` vine
|
||||
mereu completat din sursa, deci singura care ar fi putut pica e chiar cea de cantitate — a carei
|
||||
premisa e sub semnul intrebarii. O plasa din doua verificari care nu pot pica ar fi cod fara efect.
|
||||
|
||||
**REZOLVAT — decizia lui Marius, 12.08.2026: se permite negativul, 0 ramane blocat.** Aplicat pe
|
||||
`omodificari.vc2:14386-14387`: conditia `Nvl(cantitate,0) <= 0` a devenit `= 0`, mesajul „are
|
||||
cantitatea 0 sau negativa" a devenit „are cantitatea 0". **O singura linie de logica**, plus mesajul.
|
||||
|
||||
**Datele care au sustinut decizia** (`docs\cercetare\rec_cantitate_negativa_date.md`, Oracle
|
||||
read-only): **65 de linii active cu `cantitate < 0` pe 52 de documente** — grosul pe tipurile `-6`
|
||||
(25 linii / 24 doc) si `41` (4/4), adica **retur-transfer**, dar si pe `tip 3` (comanda) 10 linii pe 4
|
||||
documente, care sunt linii **`DISCOUNT`** cu cantitate negativa, cea mai recenta din 26.03.2026. Deci
|
||||
negativul e si mecanismul de discount pe linie, nu doar retur/storno. `pret = 0`: 19 linii pe 17
|
||||
documente, toate cu articol — **trec deja**, `:14394` testeaza `Isnull(pret)`, nu `= 0`; iar
|
||||
`pret IS NULL` da **0 linii**, ceea ce reconfirma ramura moarta.
|
||||
|
||||
**Write-back FACUT si verificat pe binar**, nu pe mtime: `txt2vcx.ps1` a dat `P1,E0,S1,X0` cu fidelity
|
||||
check `OK`; in `omodificari.VCT` conditia noua si mesajul nou apar o data, iar cele vechi **zero**.
|
||||
Editarea s-a facut **pe octeti** (`perl`, `binmode`), nu cu tool-ul Edit — cens `>0x7F` identic
|
||||
inainte si dupa (`2 aa / 2 e3 / 2 fe`), zero `EF BF BD`, diff exact 2 linii. Backup:
|
||||
`COMUN\clase\omodificari.pre_cantitate.bak.vc2`.
|
||||
|
||||
> ### DESCOPERIT LA REGRESIE — S4B FACE `test_s5_validari_articole` SA ATARNE HEADLESS
|
||||
>
|
||||
> **Nu e cauzat de schimbarea de mai sus** — dovedit din cod, nu presupus. Suita se opreste la cazul
|
||||
> **A5** (`test_s5_validari_articole.prg:186-194`), care face `REPLACE ALL sters WITH 1 IN tvd`: cu
|
||||
> toate liniile sterse, `SCAN FOR Nvl(sters,0) <> 1` are **zero iteratii**, deci verificarea de
|
||||
> cantitate nici nu e atinsa. Lantul real: `lnLiniiActive = 0` -> confirmarea „toate liniile au fost
|
||||
> sterse" -> mock-ul raspunde Da -> `llRet` ramane `.T.` -> intra in blocul S4b (`omodificari.vc2:14438`)
|
||||
> -> cu toate liniile sterse fiecare articol-sursa iese `Adaugare`, deci `lnDivergente > 0` ->
|
||||
> `AfiseazaDialogSincronizareArticole()`, **formular modal** -> headless, atarna. Cazurile A2/A3/A4 trec
|
||||
> pentru ca dau `RETURN .F.` inainte de `:14438`; cazul de control A1 ajunge acolo dar n-are divergente.
|
||||
>
|
||||
> **De ce abia acum**: suita a rulat ultima oara **10.08 22:13**, iar S4b etapa 2 a intrat **11.08
|
||||
> 09:34**, si `rec_s4b_etapa2.md` declara explicit „Netestat: nimic nu a fost rulat".
|
||||
>
|
||||
> **Cifre partiale ale rularii intrerupte** (12.08 21:36): 8 aserttii, **toate PASS**, inclusiv
|
||||
> `cantitate<=0 -> blocheaza` — testul foloseste `REPLACE cantitate WITH 0` (`:130`), deci **schimbarea
|
||||
> de azi ii pastreaza comportamentul**. Plus linia documentara „FAIL asteptat" despre `Isnull(pret)`,
|
||||
> care nu e o aserttie picata. Procesul a fost oprit de garda de timp; **zero procese `vfp9.exe`** dupa.
|
||||
>
|
||||
> **REZOLVAT — decizia lui Marius, 12.08.2026: varianta (a), garda in cod.** Conditia de la
|
||||
> `omodificari.vc2:14438` a primit `AND Used('tvd') AND m.lnLiniiActive > 0`, plus o linie de comentariu
|
||||
> care explica ramura. `Used('tvd')` sta **inaintea** lui `lnLiniiActive` intentionat: VFP
|
||||
> scurtcircuiteaza `AND`, deci variabila (declarata in blocul `IF ... Used('tvd')` de la `:14378`) nu se
|
||||
> evalueaza cand blocul acela n-a rulat.
|
||||
>
|
||||
> **Write-back facut si verificat pe binar**: `P1,E0,S1,X0`, fidelity `OK`; garda apare o data in
|
||||
> `omodificari.VCT`, conditia veche fara garda **zero**. Editare pe octeti, cens `>0x7F` neschimbat
|
||||
> (`2 aa / 2 e3 / 2 fe`), zero `EF BF BD`. Backup: `omodificari.pre_garda.bak.vc2`.
|
||||
>
|
||||
> **Efectul, masurat**: `test_s5_validari_articole` trece iar integral — **35 PASS / 0 FAIL, exit 0**
|
||||
> (log proaspat, 13.08 02:43). Inainte de garda atarna la cazul A5 dupa 8 aserttii.
|
||||
|
||||
> ### DOUA CAPCANE DE MEDIU NOI, platite pe 12-13.08 — de stiut inainte de orice rulare de regresie
|
||||
>
|
||||
> **1. Lansarile consecutive de `vfp9.exe` atarna.** Doua suite rulate una dupa alta in aceeasi bucla
|
||||
> `for` / aceeasi comanda: a doua **atarna la pornire, inainte sa scrie orice in log**. Aceleasi suite,
|
||||
> rulate cate una per comanda, merg. Se ruleaza **o suita per apel**, cu omorarea proceselor ramase
|
||||
> intre ele.
|
||||
>
|
||||
> **2. Logul stat da cifre plauzibile si false.** Fiecare suita isi **rescrie** logul la pornire (ex.
|
||||
> `test_page3_articole.prg:24`, inaintea conectarii Oracle de la `:30`). Daca rularea atarna inainte,
|
||||
> logul ramane cel vechi. Combinat cu capcana 1, doua suite au raportat `20 PASS / 0 FAIL` si
|
||||
> `16 PASS / 0 FAIL` **din 10.08**, la trei zile distanta, cu `exit=124`. **Verifica mereu mtime-ul
|
||||
> logului fata de ceasul curent inainte sa citesti o cifra.**
|
||||
>
|
||||
> **3. Numararea** (reconfirmare a capcanei vechi): `grep -c "^PASS"` **subnumara** — unele linii au
|
||||
> `PASS` la mijloc. Pe `test_page3_articole` a dat `10/0` in loc de `14/2`. Se numara cu
|
||||
> `grep -o "PASS" <log> | wc -l`, iar unde exista linia `REZULTAT` aia e autoritara.
|
||||
|
||||
**REGRESIA E LA BASELINE PE TOATE CELE 10 SUITE HEADLESS**, 13.08.2026 02:43-03:07. Cifrele au fost
|
||||
**recitite de orchestrator direct din loguri**, dupa ce a verificat mtime-ul fiecaruia fata de ceasul
|
||||
curent — nu preluate din raportul agentului:
|
||||
|
||||
| suita | obtinut | baseline |
|
||||
|---|---|---|
|
||||
| `test_s5_validari_articole` | 35 / 0 | 35 / 0 |
|
||||
| `test_page3_articole` | 14 / 2 | 14 / 2 |
|
||||
| `test_adauga_linie_articol` | 20 / 0 | 20 / 0 |
|
||||
| `test_adauga_linie_valuta` | 16 / 0 | 16 / 0 |
|
||||
| `test_verdict_act_rul` | 26 / 0 | 26 / 0 |
|
||||
| `test_incarca_vanzare_din_nota` | 5 / 0 | 5 / 0 |
|
||||
| `test_efactura_readonly` | 23 / 0 | 23 / 0 |
|
||||
| `test_s4b_sincronizare` | 42 / 0 | 42 / 0 |
|
||||
| `test_s4b_dialog` | 35 / 0 | 35 / 0 |
|
||||
| `test_s7_rotunjire` | 6 / 0 | 6 / 0 |
|
||||
|
||||
Singurul log cu linie `EROARE` e `test_page3_articole`: `EROARE 1925 [VERIFICA_EDITARE_GRID:366]
|
||||
Unknown member COLUMN5` — **artefactul headless cunoscut** (sub `-A -T` coloanele de grid nu se
|
||||
materializeaza), care produce chiar cele 2 FAIL din baseline. Nu e regresie.
|
||||
|
||||
**Nerulate deliberat**: suitele `test_ui_*` (cer formular vizibil pe un ecran partajat, iar schimbarile
|
||||
nu ating ce verifica ele) si `test_s8_matrice_surse` (ar consuma documente de test ireversibil).
|
||||
|
||||
**Diff consolidat pentru review**: `docs\diff_cantitate_negativa_garda_s4b.patch` — 4 randuri schimbate
|
||||
in `omodificari.vc2` (2 de logica, 2 de comentariu).
|
||||
|
||||
## S8 INCHIS — 12.08.2026. Matricea era rulata integral; „cele doua esecuri" erau o stare pierduta
|
||||
|
||||
**Matricea S8 a fost rulata pe toate cele patru documente, fiecare de doua ori, din ambele puncte de
|
||||
intrare, inca din 10.08.2026.** Cifrele, din log, felie cu felie: `1048` (lista de preturi) **44/0**,
|
||||
`1055` (factura din contract) **44/0**, `1052` (aviz) **26/1**, `1054` (factura din aviz) **42/2**.
|
||||
Cele 8 verificari cerute de `plan_06_editare_factura.md:286-288` au verdict explicit in raport.
|
||||
|
||||
**Cele 3 FAIL sunt asteptate, niciunul nu e defect:**
|
||||
- `1052` — garda `ReferinteDocumenteNota` **a blocat corect** intrarea ROAFACTURARE: avizul are deja o
|
||||
factura emisa din el (`ACT.id_factc = 8009677` pe nota lui `1054`). Testul presupusese, mostenit din
|
||||
modelul S5, ca orice document e editabil.
|
||||
- `1054`, de doua ori — `Reccount(trul)=0`. Documentul **n-avea rulaje nici inainte** de editare, deci
|
||||
aserttia cerea ca editarea sa *creeze* rulaje care n-au existat.
|
||||
|
||||
**De ce `RUL=0` e corect pe tip 4 — stabilit din cod, nu din tipar de date** (`rec_s8_rulaje_tip4.md`):
|
||||
`RUL` retine exclusiv miscari pe cont de stoc. Avizul (tip 22) scoate marfa din gestiune si scrie
|
||||
rulajele — pe `1052`, 4 randuri, toate pe contul `371`. Factura emisa din aviz nu mai atinge `371`:
|
||||
transforma doar creanta provizorie in creanta ferma si TVA neexigibila in TVA colectata (`ACT` pe
|
||||
`1054`: `4111/418` si `4428/4428`, niciun `371`). N-are, structural, ce rand de stoc sa scrie.
|
||||
Editarea doar **conserva** ce exista — `trul` se incarca din `vrul_tot` (`ofacturare_editare.prg:76`)
|
||||
si se scrie inapoi neconditionat (`ofacturare_comun.vc2:3821`, `OSCRIE_IN_FISIERE(0,.T.,.T.)`).
|
||||
Verdictul ACT/RUL are trei stari, **toate informative** (`omodificari.vc2:13129-13139`); „nu se aplica"
|
||||
e rezervat lui `tip=51`, deci tip 4 cade legitim in „divergent (informativ, nu e eroare)".
|
||||
Cele trei ancore sunt **verificate la sursa de sesiunea principala**, nu preluate din raport.
|
||||
|
||||
**Aserttia a fost restransa** la ce voia de fapt sa apere — ca editarea nu pierde rulaje:
|
||||
`test_s8_matrice_surse.prg:313-314`, `Reccount(trul)` pe nota noua `>=` cat era inainte, cu linia de
|
||||
baza dusa prin `tnNrRulBaseline` de la ambele puncte de intrare. Rulare de compilare cu lista de cazuri
|
||||
goala: **0 PASS / 0 FAIL, exit 0, fara `EROARE`**, zero documente consumate.
|
||||
|
||||
**Rularea completa pe date NU se face — decizia lui Marius, 12.08.2026.** Ar consuma inca 8 generatii
|
||||
de `cod` pe cele patru documente, pentru un rezultat previzibil: relaxarea e stricta (`>= linia de
|
||||
baza` in loc de `> 0`), deci muta `1054` de la 42/2 la 44/0 si lasa `1048`/`1052`/`1055` neschimbate.
|
||||
**Deci logul de pe disc ramane cel al rularii de compilare, nu al matricei** — cifrele matricei se
|
||||
citesc din `rec_s8_matrice.md`, iar felia `1054` asa cum a rulat inainte de relaxare e pastrata in
|
||||
`COMUN\utile\Teste\editare_factura\test_s8_matrice_surse_log.1054.txt`.
|
||||
|
||||
> **Cum s-a pierdut starea, ca sa nu se repete.** Commit-ul `b5a7108` (curatenia din 11.08) a **sters
|
||||
> `docs\cercetare\rec_s8_matrice.md`** — incadrat drept „raport de executie al lui #6, nereferit de
|
||||
> nimic viu", desi e referit din `test_s8_matrice_surse.prg` — **si in acelasi commit** a scris in
|
||||
> `progres.md` starea dedusa din `test_s8_matrice_surse_log.txt`. Dar logul **se rescrie la fiecare
|
||||
> rulare**, deci pastra doar ultima felie (`1054`): de acolo au aparut „cele doua esecuri reale" si
|
||||
> „trei tipuri de sursa sarite". Raportul e **recuperat pe disc** din `fc9c378`. Aceeasi curatenie a
|
||||
> sters si `mockup_v7/v8_modificari.md`, care erau ale lui **#13**, nu ale lui #6.
|
||||
>
|
||||
> **Regula**: un log care se rescrie nu e sursa de stare. Starea sta in raport; daca raportul dispare,
|
||||
> starea dispare cu el. Inainte de a sterge un raport, cauta-i numele in `COMUN\utile\Teste\` si in
|
||||
> `docs\`, nu doar in `plan_*.md`.
|
||||
|
||||
## S8 — VERIFICAT PE VENDING PRODUCTIE, 10.08.2026: nu deblocheaza avizul
|
||||
|
||||
@@ -165,9 +413,12 @@ de `TRY/CATCH` imediat dupa ce driverul apeleaza `.do_termin()` pe `frm_alte_dat
|
||||
Harness-ul si investigatia completa de cod (cu `fisier:linie`) raman pe disc in
|
||||
`docs\cercetare\rec_s8_creare_variante.md` — utile daca se reia candva, dar **scopul e atins altfel**.
|
||||
|
||||
**Ce ramane din S8**: matricea propriu-zisa — fiecare din cele patru documente editat **de doua ori**,
|
||||
o data din ROAFACTURARE si o data din registrul jurnal ROACONT, cu verificarile din
|
||||
`plan_06_editare_factura.md:282-291`.
|
||||
**Matricea propriu-zisa e RULATA** — fiecare din cele patru documente editat de doua ori, o data din
|
||||
ROAFACTURARE si o data din registrul jurnal ROACONT, cu verificarile din
|
||||
`plan_06_editare_factura.md:282-291`. Cifre si verdict: sectiunea „S8 INCHIS" de mai sus; detaliile pe
|
||||
document, in `docs\cercetare\rec_s8_matrice.md`. **Documentele sunt consumate** (coduri realocate,
|
||||
note vechi sterse ireversibil): `1048` -> `1140918`, `1052` -> `1140921`, `1054` -> `1140923`,
|
||||
`1055` -> `1140920`.
|
||||
|
||||
## Firele rundei (10.08.2026, dupa-amiaza)
|
||||
- `s8-creare-exec` — **executia** planului: creeaza avizul, factura din aviz si factura din contract in
|
||||
@@ -297,11 +548,26 @@ Registrul Jurnal, `afisjurcom.do_modifica` (`comun.vc2:2222-2572`), **nu are nic
|
||||
restrictia de luna curenta, preexistenta pentru orice nota. Deci o factura deja trimisa la ANAF se
|
||||
poate edita din ROACONT, dar nu din ROAFACTURARE. Verificat de orchestrator cu `vfp_symbols.ps1`, nu
|
||||
din raport: hitul `ReferinteDocumenteNota` de la `comun.vc2:2620` e in `afisjurcom.do_sterge`
|
||||
(2574-2733), **alta metoda** — deci nu infirma constatarea. Variante: (1) se lasa si se documenteaza
|
||||
(deja documentat); (2) garda eFactura se muta in `PregatesteArticoleFacturaEditare`, deci se aplica
|
||||
doar cand documentul chiar e factura de vanzare, fara sa atinga notele obisnuite; (3) se dubleaza
|
||||
explicit in `do_modifica`. **Nefacut** — `comun.vc2` e livrare inchisa, iar calea din jurnal serveste
|
||||
orice tip de nota. Atinge si S8, care cere fiecare caz rulat din ambele puncte de intrare.
|
||||
(2574-2733), **alta metoda** — deci nu infirma constatarea. Reconfirmat pe 12.08.2026 cu
|
||||
`vfp_symbols.ps1 -Where`: in tot `comun.vc2` functia apare **o singura data**, la `:2620`, deci
|
||||
`do_modifica` (2222-2572) n-o are deloc.
|
||||
|
||||
**INCHISA — decizia lui Marius, 12.08.2026: asimetria se accepta, nu se repara.** Nu se atinge nici
|
||||
`comun.vc2`, nici `omodificari.vc2`. Motivele care sustin decizia:
|
||||
- **jumatatea eFactura nu se putea dubla oricum** — decizia 42 spune explicit ca notele si rulajul se
|
||||
modifica *doar* din ROACONT, deci o garda eFactura pe calea din jurnal ar fi anulat chiar decizia 42;
|
||||
- **absenta lui `ReferinteDocumenteNota` din `do_modifica` e voita**, nu o scapare: garda protejeaza
|
||||
incasarile legate de `id_fact` la **stergerea** notei, unde legatura chiar se rupe; la modificare,
|
||||
legatura structurala trece prin `id_fact`/`id_vanzare`, niciodata prin `cod` (S6, inchis), deci
|
||||
realocarea `cod`-ului nu o strica;
|
||||
- **`comun.vc2` serveste orice tip de nota** din toate cele trei aplicatii — o garda acolo ar fi blocat
|
||||
editarea oricarei note cu referinte de incasari, mult peste cazul semnalat.
|
||||
|
||||
**Ce ramane adevarat si de stiut**: pe calea din registrul jurnal nimic nu opreste editarea unui
|
||||
document-sursa care are deja o factura emisa din el (dovedit pe `1052` in S8, unde intrarea
|
||||
ROAFACTURARE a blocat si cea din ROACONT a scris pana la `COMMIT`). Pe reeditare fara modificari de
|
||||
continut e inofensiv; o modificare reala de continut pe un asemenea document nu e oprita de nimic.
|
||||
**Acceptat ca atare.**
|
||||
|
||||
## #6 / S7 — rotunjirea la reeditare, GATA 10.08.2026: NU E DEFECT
|
||||
|
||||
@@ -1986,3 +2252,62 @@ pareau sa infirme o corectie s-au dovedit, la verificare, altceva decat pareau.
|
||||
textul regenerat din `<staging>\verify\*.vc2`**, nu incerca sa ghicesti ordinea.
|
||||
- **`InputMask` cu literal se ghilimeleaza** (`"9.99"`, nu `9.99`) — altfel VFP il ia numeric.
|
||||
Fidelity-check-ul **nu** semnaleaza asta separat.
|
||||
|
||||
## Runda 6 - 20.08.2026: grid Articole (nomenclatoare, zecimale, aspect), meniu, punctul 6
|
||||
|
||||
Livrat si aplicat, cu write-back facut si verificat prin reconversie independenta (0 linii
|
||||
diferenta pe ambele fisiere):
|
||||
|
||||
- **`COMUN\clase\omodificari.vc2`** — cinci metode `DblClick` noi pe coloanele cu nomenclator
|
||||
(`:16706`, `:16729`, `:16747`, `:16829`, `:16846`); `cPretArt` (`:12391`) si `cPretCuTvaArt`
|
||||
(`:12408`) trec pe `get_mask(12,gnPPretV)`, `cPretAchizitieArt` (`:12400`) ramane pe `gnPPRET`;
|
||||
cele cinci coloane cu nomenclator trec pe verde `160,255,205` (`Text1.ReadOnly` ramane `.T.`);
|
||||
`cSerieArt`/`cLotArt`/`cExplicatieArt` trec pe alb `255,255,255` + `Text1.ReadOnly = .F.`
|
||||
- **`COMUN\clase\ofacturare_comun.vc2:4930`** — optiunea din `xmenu` devine
|
||||
`Editare \<factura (note, rulaje, articole)`
|
||||
|
||||
Ce s-a stabilit, ca sa nu se rediscute:
|
||||
|
||||
- `cExplicatieTvaArt` are `ControlSource` **expresie** (`omodificari.vc2:12467`), nu camp. Intr-un
|
||||
grid VFP tastarea intr-o celula read-only declanseaza cautarea incrementala pe campul legat;
|
||||
fara camp nu are pe ce face seek, deci `InteractiveChange` nu porneste niciodata acolo. `DblClick`
|
||||
se sprijina pe `Thisform.pccontrol` (pus explicit in `GotFocus`, `:16722-16726`) si de aceea
|
||||
acopera uniform toate cele cinci coloane.
|
||||
- `gnPPret` = precizie pret **achizitie**, `gnPPretV` = precizie pret **vanzare**
|
||||
(`oinit_optiuni.prg:515`). Model: `ofacturare.vc2:3490` vs `:3497`. Scrierea in Oracle a lui
|
||||
`VANZARI_DETALII.PRET` foloseste `gnPPretV` (`ofacturare_stoc.prg:367`).
|
||||
- Conventia de culoare (de pe pagina Rulaje a aceluiasi formular): alb = se tasteaza direct,
|
||||
verde `160,255,205` = se editeaza prin dialog, gri `225,225,225` = nu se editeaza.
|
||||
- Inainte de runda 6, serie/lot/explicatie **nu erau editabile pe nicio cale**: garda `.When` fusese
|
||||
relaxata in runda 5, dar `Text1.ReadOnly` ramasese `.T.`, iar `but_modificaR` se activeaza doar pe
|
||||
coloanele cu `GotFocus` (`:16678-16682`).
|
||||
- **NU** schimba `Text1.ReadOnly` pe cele cinci coloane cu nomenclator: calea de tastare merge
|
||||
tocmai fiindca sunt `.T.`.
|
||||
|
||||
### Punctul 6 - explicatie TVA in butonul de modificare din `frm_facturi`
|
||||
|
||||
Decizia finala a lui Marius: **fara recalcul**, lista **filtrata pe cota salvata a liniei**, iar
|
||||
`taxcode` se sincronizeaza dupa explicatia aleasa. Varianta cu recalcul a fost propusa, aleasa
|
||||
initial, apoi **respinsa** — nu se reintroduce.
|
||||
|
||||
- Script scris, **NERULAT** pe schema `MARIUSM_AUTO` (`ROA_CENTRAL`):
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_20_02_COMUN_PACK_FACTURARE.sql`
|
||||
- Proiectarea completa: `docs\propunere_runda6_punct6_plsql.md`
|
||||
- `UPDATE`-ul scrie exact trei coloane: `EXPLICATIE`, `TAXCODE`, `ID_JTVA_COLOANA`. `PROC_TVAV` nu
|
||||
se atinge, totalurile nu se recalculeaza.
|
||||
- Garda de neutralitate e in PL/SQL: `FACT-029` daca cota explicatiei difera de a liniei, plus
|
||||
`FACT-026` (linie inexistenta), `FACT-027` (explicatie inexistenta/fara cota), `FACT-028`
|
||||
(cota liniei NULL, verificata **separat**, inainte de comparatie).
|
||||
- Semantica verificata pe date: `PROC_TVAV` e **multiplicator** (1.19, 1.09...), `COTA_TVA` e
|
||||
**procent** (19, 9...), deci comparatia e `(COTA_TVA+100)/100`. Cele 8 cote folosite azi pe linii
|
||||
active au toate explicatie corespunzatoare — cazul "lista goala" e teoretic.
|
||||
- **Partea VFP nu a pornit.** Ce trebuie scris: sectiunea "Ce ramane de facut in VFP" din propunere.
|
||||
`taxcode` se deriva **in VFP** prin `GetTaxCodeIdPart` si se trimite prin parametrul existent
|
||||
`V_TAXCODE` — verificat ca lantul ei e inregistrat neconditionat in toate trei produsele
|
||||
(`roafacturare.prg:187,189`, `roacont.prg:277,279`, `roagest.prg:239,241`), spre deosebire de
|
||||
`ofacturare_editare.prg`, pe care doar ROAFACTURARE il inregistreaza.
|
||||
- `ff_2026_08_20_01` (garda `FACT-025`, runda 5) e **deja aplicat** pe `MARIUSM_AUTO`; scriptul 02 il
|
||||
contine. **Nu se re-aplica 01** — ar sterge punctul 6.
|
||||
|
||||
Ramase: proba pe ecran a punctelor 1-5, aplicarea scriptului pe `MARIUSM_AUTO`, partea VFP a
|
||||
punctului 6, si intrarea de changelog pentru runda 6 (nu exista inca).
|
||||
|
||||
84
docs/propunere_fact008_precizie_pret.md
Normal file
84
docs/propunere_fact008_precizie_pret.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Propunere: precizia pretului de achizitie la trimiterea liniilor de factura (FACT-008)
|
||||
|
||||
Diagnostic: `docs/cercetare/rec_fact008_pret_achizitie_zecimale.md`.
|
||||
Diff: `docs/diff_fact008_precizie_pret.patch` — **aplicat pe 18.08.2026**, necomis.
|
||||
|
||||
In productia vending optiunea `PPRET` a fost pusa pe 4 si eroarea a disparut, confirmand diagnosticul.
|
||||
Modificarea de cod acopera acelasi lucru independent de configurare.
|
||||
|
||||
## Modificarea propusa
|
||||
|
||||
Un singur tipar, in 8 locuri: serializarea in SQL foloseste precizia declarata a cursorului, nu
|
||||
`gnPPret` / `gnPPretV` / `gnPPretVal` singure.
|
||||
|
||||
```foxpro
|
||||
- Alltrim(Str(poArt.pret_achizitie,18,gnPPret))
|
||||
+ Alltrim(Str(poArt.pret_achizitie,18,Max(gnPPret,4)))
|
||||
```
|
||||
|
||||
Cursoarele sunt deja definite `pret_achizitie N(20, Max(gnPPret,4))`, `pretd N(20,
|
||||
Max(gnPPretVal,4))`, `pretv_orig N(20, Max(gnPPretV,4))` — modificarea doar aliniaza emiterea cu
|
||||
declaratia. Coloanele tinta din Oracle suporta precizia (`STOC.PRET/PRETV/PRETD` au scale 4,
|
||||
`VANZARI_DETALII_TEMP.PRET_ACHIZITIE` scale 6).
|
||||
|
||||
| fisier | linii | camp |
|
||||
|---|---|---|
|
||||
| `COMUN\clase\ofacturare.vc2` (`frm_facturare_articole.do_scrie_articole`) | 14073, 14074, 14087 | `pret_achizitie`, `pretd`, `pretv_orig` |
|
||||
| `COMUN\clase\ofacturare.vc2` (`frm_facturare_articole2.do_scrie_articole`) | 18108, 18109, 18122 | `pret_achizitie`, `pretd`, `pretv_orig` |
|
||||
| `COMUN\programe\ofacturare_stoc.prg` (`adauga_articol_factura_stoc`) | 360, 361 | `pret_achizitie`, `pretd` |
|
||||
|
||||
`pret_achizitie` rezolva eroarea raportata. `pretd` si `pretv_orig` sunt aceeasi eroare, latenta:
|
||||
`descarca_gestiune` le compara si pe ele prin egalitate exacta (`A.PRETD = V_PRETD`,
|
||||
`A.PRETV = V_PRETV_ALES`), iar coloanele din `STOC` au tot scale 4. La vending nu se manifesta
|
||||
(`pretv = 0`, `pretd = 0` peste tot), dar pe o gestiune tinuta la pret de vanzare ar da acelasi
|
||||
FACT-008. Daca se prefera modificarea strict minima, se pot pastra doar cele 3 linii cu
|
||||
`pret_achizitie` — spun explicit ca as include si celelalte 5.
|
||||
|
||||
## Ce NU propun
|
||||
|
||||
- **Rotunjire la importul eFactura** (`ROACONT\COMUN\clase\anaf_efactura.vc2`, liniile 12727,
|
||||
12744, 12786). Ar strica valoarea receptiei: 8.559 x 500 = 4279.50 fata de 4279.28 cat e valoarea
|
||||
reala a facturii. Zecimalele sunt corecte, transportul din facturare e cel gresit. (Ramane
|
||||
discutabila inconsecventa dintre `4` la 12744/12786 si `6` la 12727 — dar e cosmetica, nu
|
||||
cauza.)
|
||||
- **Relaxarea predicatului din `pack_facturare.descarca_gestiune`** (`round(A.PRET, ...)`). Pe un
|
||||
articol cu mai multe loturi in aceeasi gestiune ar putea potrivi lotul gresit si ar descarca alt
|
||||
cost.
|
||||
|
||||
## Deblocare imediata in productie, separat de cod
|
||||
|
||||
`OPTIUNI.PPRET` de la 3 la 4 la vending — facut de Marius pe 18.08.2026, eroarea a disparut. Are
|
||||
efect fara recompilare si a deblocat toate cele 14 articole din stoc care dadeau FACT-008. Cu
|
||||
`PPRET = 4`, `Max(gnPPret,4)` din diff devine oricum echivalent, deci cele doua masuri nu se bat cap
|
||||
in cap. Optiunea ramane pe 4 pana cand versiunea noua e in productie.
|
||||
|
||||
## Aplicare (dupa aprobare)
|
||||
|
||||
1. `.prg` — editare directa, atentie la CRLF.
|
||||
2. `.vc2` — editare byte-safe (fisierul e cp1250 cu diacritice; liniile atinse sunt ASCII) apoi
|
||||
write-back cu `txt2vcx.ps1 -AllowComun`, si verificare pe binar prin reconversie in cache
|
||||
temporar + diff, nu pe mtime.
|
||||
3. `COMUN\` e partajat de toate produsele ROA si versionat separat (`comun.git`) — modificarea
|
||||
atinge si ROAGEST/ROAACNPRO/ROAIMOB. Commit-ul se face din `COMUN\`.
|
||||
4. Changelog: intrarea intra la `2.11.15`, tag `:eroare:` — versiunea nu se bumpeaza, 2.11.15 nu e inca in productie.
|
||||
5. Fara test headless util aici: eroarea apare doar cu un rand real din `STOC` cu pret pe 4
|
||||
zecimale. Verificarea practica e o factura pe articolul 5155 dupa recompilare.
|
||||
|
||||
## Scriptul PPRET = 4 pentru toate firmele: abandonat
|
||||
|
||||
Scriptul a fost scris si apoi sters (`SCRIPTURI_CLAR\2026\08\ff_2026_08_19_01_COMUN_OPTIUNI_PPRET.sql`).
|
||||
Decizia lui Marius, 19.08.2026: nu e nevoie de el.
|
||||
|
||||
Dupa ce intra versiunile cu `Max(gnPPret,4)`, scriptul nu mai repara nimic, iar `PPRET` nu e doar
|
||||
precizie de transport — conduce si rotunjirea pretului unitar la NIR-ul introdus manual
|
||||
(`ointroduceri.vc2`: `Round(valoare/cantitate, gnPPret)`) si mastile de afisare. Pus pe 4 la toate
|
||||
firmele ar fi schimbat rotunjirea receptiilor manuale la toti clientii, ca sa repare ceva ce codul
|
||||
repara deja. `versiune_db.txt` a ramas nebumpat.
|
||||
|
||||
Ramane de acoperit prin cod, nu prin configurare: `ROAGEST\Programe\ofactureaza.prg` (liniile 267,
|
||||
268, 280) inca trunchiaza, iar copiile `COMUN` din celelalte produse sunt in urma si primesc
|
||||
reparatia la urmatorul `svn update` plus recompilare.
|
||||
|
||||
**La vending PPRET ramane pe 4 pana cand versiunea noua e in productie** — acum e singurul lucru care
|
||||
tine facturarea in picioare acolo. Dupa punerea in productie se poate reveni la 3, daca nu se doresc
|
||||
preturi de achizitie pe 4 zecimale la receptiile manuale.
|
||||
286
docs/propunere_runda4_note_sincronizare_tva.md
Normal file
286
docs/propunere_runda4_note_sincronizare_tva.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# 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.
|
||||
294
docs/propunere_runda5_articole_valuta_rate.md
Normal file
294
docs/propunere_runda5_articole_valuta_rate.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# Runda 5 - editare inline pe pagina Articole, totalurile facturii din aviz, liniile de rata
|
||||
|
||||
Cinci semnalari din proba lui Marius, 19.08.2026. Fiecare punct: constatarea, dovada
|
||||
(`fisier:linie` sau interogare pe `MARIUSM_AUTO@ROA_CENTRAL`), propunerea.
|
||||
|
||||
**Stare: nimic aplicat.** Toate punctele de mai jos sunt propuneri. Diff-urile se livreaza dupa
|
||||
aprobare, iar write-back-ul text->binar vine dupa diff.
|
||||
|
||||
Cercetarile din spate: `docs\cercetare\rec_r5_editare_inline_articole.md`,
|
||||
`docs\cercetare\rec_r5_linii_fara_articol_contract.md`,
|
||||
`docs\cercetare\rec_r5_totaluri_factura_din_aviz.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Editare inline pe pagina Articole: serie, lot, explicatie, procent TVA
|
||||
|
||||
Coloanele cerute sunt azi `ReadOnly = .T.` in `grdArticoleFactura`
|
||||
(`COMUN\clase\omodificari.vc2:12368` serie, `:12375` lot, `:12449` explicatie, `:12419`
|
||||
`proc_tvav`).
|
||||
|
||||
Trecerea pe editabil urmeaza tiparul deja folosit pe `cPretArt`: `Text1.When` refuza editarea cand
|
||||
`Thisform.lArticoleReadOnly` sau `Nvl(tvd.id_vanzare_set,0) <> 0`
|
||||
(`omodificari.vc2:16735-16740`), `Text1.Valid` recalculeaza unde e nevoie.
|
||||
|
||||
`serie`, `lot`, `explicatie` nu cer niciun recalcul - se scriu direct in cursor.
|
||||
|
||||
### Defect care trebuie reparat in aceeasi livrare
|
||||
|
||||
`ScrieArticoleFacturaEditate` (`COMUN\programe\ofacturare_editare.prg:505-517`) **nu scrie
|
||||
`serie`, `lot`, `explicatie` in UPDATE-ul liniilor deja salvate** - cele trei campuri apar doar in
|
||||
INSERT-ul liniilor noi (`:535-546`). Fara aceasta corectie, editarea inline ceruta s-ar pierde
|
||||
tacut la salvare pe orice linie existenta. `proc_tvav` e deja in UPDATE, deci pentru el nu e
|
||||
nimic de facut.
|
||||
|
||||
### Procentul de TVA - punctul delicat
|
||||
|
||||
`tvd.proc_tvav` e in forma **1.21**, nu 21 (verificat pe date: randurile 1599-1604 din
|
||||
`VANZARI_DETALII` au toate `1.21`). Se pastreaza forma asta: exista precedent direct pe aceeasi
|
||||
pagina, coloana `cProc_tva` de pe grila de rulaje e deja editabila liber in aceeasi forma.
|
||||
|
||||
Riscul real e altul: pe `tvd`, cota e corelata cu `id_jtva_coloana` (explicatia TVA) si cu
|
||||
`taxcode` (SAF-T). Azi, alegerea explicatiei scrie cota **si** recoreleaza taxcode-ul
|
||||
(`ofacturare_editare.prg:1099-1104` -> `UpdateExplicatieSAFTArt`, `omodificari.vc2:15049-15073`).
|
||||
Daca utilizatorul tasteaza direct cota, corelarea ramane la cota veche: explicatie de 21% langa o
|
||||
cota de 19%, si `taxcode` gresit in raportarea 406. Pe rulaje riscul nu exista - `trul` nu poarta
|
||||
aceeasi corelare.
|
||||
|
||||
**DECIZIE CERUTA - A sau B:**
|
||||
|
||||
| | Ce face | Consecinta |
|
||||
|---|---|---|
|
||||
| **A** (recomandat) | La editarea manuala a cotei, `Valid` goleste `id_jtva_coloana` si `taxcode` pe randul respectiv | Explicatia TVA si Taxcode raman goale - semnal vizibil ca trebuie realeasa explicatia; dialogul se deschide deja filtrat pe noua cota |
|
||||
| **B** | Doar `calculeaza_valori_articol()`, corelarea ramane neatinsa (ca pe rulaje) | Mai simplu, dar lasa tacut un taxcode SAF-T gresit |
|
||||
|
||||
Varianta "recoreleaza automat" a fost respinsa: pe aceeasi cota pot exista mai multe explicatii
|
||||
(JC vs JV, exigibil vs neexigibil - parametrul `tlTipEx` din `caut_explicatie_tva`,
|
||||
`COMUN\programe\ocautare.prg:3174`), iar alegerea automata a uneia ar fi arbitrara exact acolo unde
|
||||
conteaza sa fie corecta.
|
||||
|
||||
**DECIZIE CERUTA - garda pe serie/lot.** Pretul de achizitie e blocat si pe liniile deja salvate
|
||||
(`Nvl(tvd.id_vanzare_det,0) <> 0`, `omodificari.vc2:16721-16726`); cantitatea si pretul nu.
|
||||
Propunerea merge pe garda slaba (serie/lot editabile si pe liniile salvate, blocate doar pe cele
|
||||
venite din set). Daca trasabilitatea lotului pe linii deja livrate conteaza, se aliniaza la garda
|
||||
pretului de achizitie.
|
||||
|
||||
### Nomenclatoarele pe `InteractiveChange`
|
||||
|
||||
Cele cinci coloane cu nomenclator - `cDenumireArt`, `cGestiuneArt`, `cValutaArt`, `cTaxcodeArt`,
|
||||
`cExplicatieTvaArt` - au deja `Text1.GotFocus` care pune `Thisform.pccontrol`
|
||||
(`omodificari.vc2:16705-16720`, `:16755-16765`). Lipsesc doar `ReadOnly = .F.` si
|
||||
`Text1.InteractiveChange`, care cheama `ArticoleNotaEditor.ModificaNomenclator(Thisform.pccontrol)`.
|
||||
|
||||
Sablonul e cel de pe grila de note (`omodificari.vc2:5890-5940`): coloana editabila, `GotFocus`
|
||||
pune `pccontrol`, `InteractiveChange` deschide dialogul. `But_modificaR` ramane functional si
|
||||
sincron - `BeforeRowColChange`/`AfterRowColChange` nu cer nicio modificare.
|
||||
|
||||
Efect lateral cunoscut si acceptat in sablonul existent: primul caracter tastat ajunge in celula
|
||||
inainte sa se deschida dialogul. Daca utilizatorul renunta la dialog, caracterul ramane vizibil
|
||||
pana la urmatorul refresh. Se poate atenua cu un `RefreshGrid()` pe ramura de anulare din
|
||||
`ModificaNomenclator` - **spune daca il vrei in aceasta livrare** sau ramane pe alta runda.
|
||||
|
||||
---
|
||||
|
||||
## 2. Totalurile goale pe factura editata din aviz
|
||||
|
||||
Confirmat pe date, nu dedus.
|
||||
|
||||
`VANZARI` id_vanzare=1054 (SSS 100037, tip=4, `IN_VALUTA=0`, `DISCOUNT=0`) are
|
||||
**`TOTAL_FARA_TVA` / `TOTAL_TVA` / `TOTAL_CU_TVA` = NULL** - de aici coloanele goale din lista.
|
||||
|
||||
Are o singura linie activa, `VANZARI_DETALII.id_vanzare_det=1602`, cu **`ID_VALUTA = 2` (EURO)**.
|
||||
Linia-sursa din aviz (`id_vanzare_det=1599`) are `id_valuta = 3` (RON). Pe linia 1602 difera fata
|
||||
de aviz si `id_gestiune` (1 vs 2), `taxcode` (310344 vs 310350) si `id_jtva_coloana` (35 vs 37) -
|
||||
adica exact nomenclatoarele probate in runda 4, valuta inclusa.
|
||||
|
||||
### Lantul cauzal
|
||||
|
||||
`pack_facturare.recalculeaza_totaluri_vanzari`
|
||||
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`):
|
||||
|
||||
```sql
|
||||
(case when (lnInValuta = 1 or vd.id_valuta <> pack_def.GetIdMonedaNationala())
|
||||
then ROUND(vc.curs * vd.pret / vc.multiplicator, lnPreciziePretV)
|
||||
else vd.pret end) as pret_ron
|
||||
...
|
||||
left join vanzari_cursuri vc on vc.id_vanzare = V_ID_VANZARE and vd.id_valuta = vc.id_valuta
|
||||
```
|
||||
|
||||
EURO (2) <> RON (3) -> intra pe ramura de conversie -> `VANZARI_CURSURI` **n-are niciun rand**
|
||||
pentru id_vanzare=1054 -> `vc.curs` NULL -> `pret_ron` NULL -> `SUM(...)` NULL -> UPDATE-ul final
|
||||
scrie NULL in cele trei totaluri. Selectul a fost rulat separat pentru 1054: intoarce NULL, NULL.
|
||||
|
||||
Nu conteaza care view alimenteaza lista: `FACT_VFACTURI2` citeste direct `VANZARI`, deci vede
|
||||
NULL-ul scris; `FACT_VFACTURI` recalculeaza live, cu acelasi tip de agregare vulnerabila la NULL,
|
||||
deci iese la fel. Alegerea intre ele e un prompt runtime
|
||||
(`Clase\ofundal_facturare.vc2:944-953`).
|
||||
|
||||
### De ce lipseste randul de curs
|
||||
|
||||
La emitere, `pack_facturare.scrie_cursuri` (acelasi fisier, `:14538`) insereaza in
|
||||
`VANZARI_CURSURI` cate un rand pentru fiecare valuta distincta, non-nationala, din liniile
|
||||
documentului. **Calea de editare nu face acest lucru**: `ModificaNomenclator`, ramura `nume_val`
|
||||
(`ofacturare_editare.prg:1096-1098`), scrie `id_valuta` pe linie, iar
|
||||
`ScrieArticoleFacturaEditate` (`:512`) il duce in tabela - fara sa atinga `VANZARI_CURSURI`.
|
||||
Invariantul pe care se bazeaza procedura de totaluri se rupe exact aici.
|
||||
|
||||
`VANZARI_CURSURI` se scrie **o singura data, la emitere**, din tabela de staging
|
||||
`VANZARI_DETALII_TEMP`; nu exista nicio cale care sa-l actualizeze dupa aceea. Deci: linii intr-o
|
||||
valuta straina pe un document in RON sunt legitime si frecvente - **157 de documente** cu
|
||||
`in_valuta = 0` au asemenea linii, iar `VANZARI_CURSURI` are randuri pentru **125 de documente in
|
||||
RON** - dar numai daca alegerea s-a facut **la emitere**. Orice schimbare de valuta facuta ulterior,
|
||||
din formularul de editare, produce garantat o valuta orfana, fara curs.
|
||||
|
||||
Amploarea, masurata: din 235 de randuri `VANZARI` cu `total_cu_tva` NULL in toata baza, **unul
|
||||
singur** se potriveste acestui defect - chiar SSS 100037 / id_vanzare 1054. Restul au cauze vechi,
|
||||
cunoscute (120 fara linii active, 109 cu `discount_evidentiat` NULL). Deci nu e nevoie de nicio
|
||||
reparatie in masa.
|
||||
|
||||
Confirmata si regresia: diff-ul fata de `ofacturare_editare.prg.pre_runda_butoane.bak:501-505`
|
||||
arata ca UPDATE-ul liniilor existente **nu includea `id_valuta`** inainte de runda 4 - alegerea de
|
||||
valuta se pierdea silentios si nu ajungea niciodata in tabela. Simptomul apare exact de la
|
||||
extinderea UPDATE-ului.
|
||||
|
||||
### Propunere - doua straturi
|
||||
|
||||
**(a) Client - repara cauza.** Alegerea valutei pe linia de articol nu poate ramane libera: fara
|
||||
un rand de curs pe document, orice valuta non-nationala e orfana, iar cursul nu se poate inventa
|
||||
la editare (la emitere el vine din staging, adica din pretul negociat, nu din cursul zilei).
|
||||
Garda merge in `ArticoleNotaEditor.ModificaNomenclator`, ramura `nume_val`
|
||||
(`ofacturare_editare.prg:1078`), langa cea care blocheaza deja liniile din seturi (`:1069`).
|
||||
|
||||
Doua forme, **DECIZIE CERUTA**:
|
||||
|
||||
| | Regula | Efect |
|
||||
|---|---|---|
|
||||
| **a1** (recomandat) | se pot alege doar valutele care au deja rand in `VANZARI_CURSURI` pe documentul curent, plus moneda nationala | corectarea unei linii intre valutele deja folosite pe document ramane posibila; valuta orfana devine imposibila |
|
||||
| **a2** | nomenclatorul de valuta e blocat cu totul cand `Nvl(tvanz.in_valuta,0) <> 1` | mai simplu si mai strict, dar interzice si corectiile legitime pe cele 125 de documente in RON care au deja cursuri |
|
||||
|
||||
**Respinsa**: varianta "salvarea completeaza singura randul lipsa din `VANZARI_CURSURI` cu cursul
|
||||
zilei documentului". Cursul de la emitere e cel din oferta, nu cursul BNR al zilei - completarea
|
||||
automata ar scrie un curs plauzibil dar inventat, si ar face totalurile sa arate corect fara sa fie.
|
||||
|
||||
**Respinsa si** varianta de a restrange conditia de conversie din procedura la `lnInValuta = 1`:
|
||||
ar strica exact cele 157 de documente in RON cu linii in valuta, ale caror preturi sunt chiar in
|
||||
valuta si trebuie convertite.
|
||||
|
||||
**(b) DB - plasa de siguranta.** In `recalculeaza_totaluri_vanzari`, UPDATE-ul final nu trebuie sa
|
||||
poata scrie NULL peste totaluri valide:
|
||||
|
||||
```sql
|
||||
total_fara_tva = NVL(lnTotalFaraTVA, total_fara_tva),
|
||||
total_tva = NVL(lnTotalTVA, total_tva),
|
||||
total_cu_tva = NVL(lnTotalCuTVA, total_cu_tva),
|
||||
```
|
||||
|
||||
Pastreaza valoarea veche cand recalculul iese NULL, in loc sa goleasca documentul. E doar plasa
|
||||
de siguranta, nu inlocuieste (a): fara (a), totalurile ar ramane **vechi si gresite** dupa o
|
||||
editare, ceea ce e la fel de rau ca goale, doar mai greu de observat.
|
||||
|
||||
**Reparatia datelor - un singur document.** Randul 1602 are azi EURO fara curs, iar factura 1054
|
||||
are totaluri NULL. Se repara punctual: `id_valuta` inapoi pe RON pe randul 1602, apoi un apel la
|
||||
`recalculeaza_totaluri_vanzari(1054)`. **Nu-l rulez fara sa-mi ceri** - e singura scriere in baza
|
||||
din toata livrarea, si e pe schema ta de dezvoltare.
|
||||
|
||||
---
|
||||
|
||||
## 3. `Field ID_ARTICOL does not accept null values` la editarea facturii din contract
|
||||
|
||||
Liniile de rata dintr-un contract sunt **prin proiectare** linii `VANZARI_DETALII` fara
|
||||
`id_articol` (`COMUN\programe\ofacturare_comun.prg:1792` - `crsfacttemp` declara
|
||||
`id_articol N(20) null` -, `:1836-1840`; documentat si in
|
||||
`docs\plan_13_unificare_formular_facturare.md:2275`). Baza le accepta:
|
||||
`VANZARI_DETALII.ID_ARTICOL` e **nullable**, si sunt deja 20 de linii active cu NULL.
|
||||
|
||||
Eroarea vine din cursorul de agregare, care nu declara campul ca acceptand NULL:
|
||||
|
||||
```
|
||||
ofacturare_editare.prg:640 CREATE CURSOR agg_tvd (id_articol N(20), ...)
|
||||
ofacturare_editare.prg:651 INSERT INTO agg_tvd (...) VALUES (tvd.id_articol, ...)
|
||||
```
|
||||
|
||||
`agg_rul` (`:617`) are aceeasi declaratie, dar nu se poate manifesta: `RUL.ID_ARTICOL` e
|
||||
**NOT NULL** in Oracle si nu exista niciun rand cu NULL.
|
||||
|
||||
### Propunere
|
||||
|
||||
Liniile fara articol se **exclud din comparatia rulaje <-> articole, chiar la sursa**: ambele
|
||||
`SCAN`-uri de agregare din `ConstruiestePropunereSincronizare` (`:620` peste `trul`, `:643` peste
|
||||
`tvd`) primesc in plus conditia pe articol completat. Cheia comparatiei chiar este `id_articol`;
|
||||
o linie fara cheie nu poate avea corespondent in rulaje prin definitie. Filtrand la sursa, NULL-ul
|
||||
nu mai ajunge la `INSERT INTO agg_tvd`, deci eroarea dispare de la radacina. Declaratia `NULL` pe
|
||||
ambele cursoare ramane oricum, ca igiena.
|
||||
|
||||
Cele doua alternative si de ce le-am respins - comportamentul VFP a fost **masurat**, nu presupus
|
||||
(test izolat `vfp9 -A -T` care reproduce `CREATE CURSOR` + `UNION` + `LOCATE FOR`, in scratchpad):
|
||||
|
||||
- **Doar declaratia `NULL`, fara sa umblam la comparatii.** `LOCATE FOR camp = NULL` nu gaseste
|
||||
niciodata nimic, nici cu memvar NULL, nici intre doua cursoare. Randul de rata ar iesi din
|
||||
`DO CASE` cu `llGasitSursa = .F.` **si** `llGasitTinta = .F.`, ar cadea pe `OTHERWISE` cu 0 = 0 si
|
||||
n-ar genera nicio linie in `propunere_sincronizare` - **disparitie tacuta**, fara eroare si fara
|
||||
semnalare. Acelasi rezultat practic ca filtrarea propusa, dar obtinut printr-un efect secundar
|
||||
nedocumentat, care s-ar schimba brusc daca cineva "repara" mai tarziu comparatiile.
|
||||
- **`Nvl(...,-1)` in cele cinci `LOCATE`** (`:626`, `:647`, `:671`, `:687`, plus `:849`/`:887`/`:923`
|
||||
in aplicatoare). `UNION` trateaza NULL = NULL ca acelasi rand la deduplicare, deci **toate**
|
||||
ratele unui document s-ar agrega laolalta sub un singur pseudo-articol si ar iesi ca divergenta
|
||||
la fiecare salvare - dialogul de sincronizare s-ar deschide degeaba pe orice factura din contract.
|
||||
|
||||
---
|
||||
|
||||
## 4. `Linia '' nu are articol asociat` la salvarea facturii din contract
|
||||
|
||||
Garda e la `COMUN\clase\omodificari.vc2:14449`, in `inainte_de_do_termin`:
|
||||
`IF Nvl(id_articol,0) = 0` pe fiecare linie activa din `tvd`. A fost pusa pe premisa ca
|
||||
`id_articol` vine mereu completat din sursa - premisa infirmata de date (cele 20 de linii de mai
|
||||
sus).
|
||||
|
||||
Denumirea goala din mesaj e reala, nu cosmetica: "RATA 2" sta in coloana `EXPLICATIE`, iar
|
||||
`tvd.denumire` vine prin join pe `NOM_ARTICOLE` in `VVANZARI_ARTICOLE`
|
||||
(`ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`), deci iese NULL pe randul fara articol.
|
||||
|
||||
### Propunere
|
||||
|
||||
Garda ramane, dar **doar pe liniile noi, nesalvate** (`id_vanzare_det = 0`): acolo lipsa
|
||||
articolului chiar inseamna ca utilizatorul n-a ales nimic din nomenclator. O linie incarcata din
|
||||
baza cu `id_articol` NULL e stare legitima si trece.
|
||||
|
||||
Regula e crisp si nu se bazeaza pe euristici de tipul "are pret de achizitie, deci e articol
|
||||
real".
|
||||
|
||||
### Defect legat, obligatoriu de reparat odata cu asta
|
||||
|
||||
`ScrieArticoleFacturaEditate` scrie azi `Nvl(id_articol,0)` **si in UPDATE (`:512`), si in INSERT
|
||||
(`:537`)**. `id_articol = 0` nu exista in `NOM_ARTICOLE` (verificat: count = 0), iar coloana are FK
|
||||
(`FK_VANZARE_DET002`) - deci prima salvare reusita a unei facturi cu linie de rata ar cadea pe
|
||||
**`ORA-02291`**. Trece la tiparul deja folosit alaturi pentru `id_gestiune`/`id_valuta`/`taxcode`:
|
||||
`Iif(Isnull(id_articol),[NULL],Alltrim(Str(id_articol)))`.
|
||||
|
||||
Acelasi tratament pentru inca trei campuri, dupa numarul de randuri active care au azi NULL in
|
||||
baza (masurat pe `VANZARI_DETALII`, `sters = 0`, 1124 randuri in total):
|
||||
|
||||
| Camp | Randuri cu NULL azi | De ce conteaza |
|
||||
|---|---|---|
|
||||
| `pret_achizitie` | **401** | o rata n-are cost de achizitie; `0` inseamna "cost efectiv zero", valoare falsa pentru orice calcul de marja facut direct din tabela |
|
||||
| `discount_unitar` | **238** | NULL si 0 sunt echivalente ca sens, dar salvarea ar rescrie tacut 238 de randuri |
|
||||
| `proc_tvav` | **2** | e multiplicator (1.21), nu procent - `0` nu inseamna "fara TVA", ci anuleaza orice calcul care-l inmulteste |
|
||||
|
||||
Restul raman cum sunt: `pret` are coloana NOT NULL si garda proprie la `:14441`, `cantitate` e
|
||||
filtrata de garda de la `:14433` si oricum n-are niciun NULL in baza, `pret_cu_tva` e flag 0/1.
|
||||
|
||||
---
|
||||
|
||||
## 5. Optional, de decis separat
|
||||
|
||||
`VVANZARI_ARTICOLE` ar putea capata acelasi fallback pe care il are view-ul vechi
|
||||
`fact_vfacturi_detalii`: `NVL2(vd.id_articol, na.denumire, vd.explicatie) as denumire`. Ar face ca
|
||||
"RATA 2" sa apara si in coloana Denumire, nu doar in Explicatie. E o modificare de view, deci un
|
||||
script DB separat - **nu o bag in aceasta livrare** fara sa o ceri.
|
||||
|
||||
---
|
||||
|
||||
## Ce nu se atinge
|
||||
|
||||
- Ordinea coloanelor din grid (`cExplicatieTvaArt` ramane ultima) - mutarea ei langa Taxcode cere
|
||||
`ColumnOrder` pe toate coloanele.
|
||||
- Datele din Oracle - nicio reparatie de randuri existente fara cerere explicita.
|
||||
- Dialogul de sincronizare si bara de totaluri - neschimbate fata de runda 4.
|
||||
|
||||
## Fisiere atinse de propunere
|
||||
|
||||
| Fisier | Puncte | Write-back |
|
||||
|---|---|---|
|
||||
| `COMUN\clase\omodificari.vc2` | 1 (grid + evenimente), 4 (garda) | necesar (`txt2vcx.ps1 -AllowComun`) |
|
||||
| `COMUN\programe\ofacturare_editare.prg` | 1 (UPDATE), 2a (garda de valuta), 3 (agregare), 4 (scriere NULL) | n/a |
|
||||
| script DB nou, `PACK_FACTURARE` | 2b (NVL pe UPDATE-ul de totaluri) | script separat, dupa aprobare |
|
||||
437
docs/propunere_runda6_grid_articole_ux.md
Normal file
437
docs/propunere_runda6_grid_articole_ux.md
Normal file
@@ -0,0 +1,437 @@
|
||||
# Propunere runda 6 - grid Articole: nomenclatoare, zecimale, aspect, meniu, buton frm_facturi
|
||||
|
||||
Stare: **diagnostic incheiat, nimic aplicat.** Cele sase semnalari din proba pe ecran din
|
||||
20.08.2026 sunt diagnosticate cu dovada pe cod. Fiecare punct de mai jos are cauza, interventia
|
||||
propusa si ce anume trebuie sa decizi.
|
||||
|
||||
Cercetari de sprijin:
|
||||
`docs\cercetare\rec_r6_nomenclatoare_grid.md`, `docs\cercetare\rec_r6_zecimale_si_aspect_grid.md`,
|
||||
`docs\cercetare\rec_r6_meniu_editare_factura.md`,
|
||||
`docs\cercetare\rec_r6_buton_modificare_frm_facturi.md`.
|
||||
|
||||
---
|
||||
|
||||
## Cum functioneaza de fapt gridul de Articole azi
|
||||
|
||||
Miezul, verificat direct pe cod - fara asta punctele 1, 2 si 4 par contradictorii:
|
||||
|
||||
Cinci coloane au nomenclator (`GotFocus` + `InteractiveChange`), enumerate din
|
||||
`COMUN\clase\omodificari.vc2`:
|
||||
|
||||
| Coloana | ControlSource | GotFocus | InteractiveChange |
|
||||
|---|---|---|---|
|
||||
| `cDenumireArt` (articol) | `tvd.denumire` | :16705 | :16710 |
|
||||
| `cGestiuneArt` | `tvd.gestiune` | :16734 | :16739 |
|
||||
| `cTaxcodeArt` | `tvd.taxcode` | :16810 | :16815 |
|
||||
| `cValutaArt` | `tvd.valuta` | :16821 | :16826 |
|
||||
| `cExplicatieTvaArt` | **expresie** (vezi mai jos) | :16722 | :16728 |
|
||||
|
||||
Toate cinci au `Text1.ReadOnly = .T.`. Asta **nu** e o greseala: mecanismul se sprijina fix pe ea.
|
||||
Intr-un grid VFP, cand celula curenta e read-only si coloana e legata de un **camp real**, tastarea
|
||||
declanseaza cautarea incrementala a gridului, care schimba `Value`, ceea ce declanseaza
|
||||
`InteractiveChange`, care deschide nomenclatorul. Asa merge `taxcode` la tastare.
|
||||
|
||||
`cExplicatieTvaArt` e singura exceptie, si de aici vine punctul 1:
|
||||
|
||||
```
|
||||
omodificari.vc2:12467
|
||||
Column16.ControlSource = "Iif(Seek(Nvl(tvd.id_jtva_coloana,0),'crsJtvaTemp','id_jtva'),crsJtvaTemp.denumire,'')"
|
||||
```
|
||||
|
||||
Coloana afiseaza *denumirea* din `crsJtvaTemp`, deci e legata de o **expresie**, nu de un camp.
|
||||
Nu exista camp pe care sa se faca seek, deci `Value` nu se schimba niciodata la tastare, deci
|
||||
`InteractiveChange` nu porneste. Nu e o garda gresita si nu lipseste nicio ramura din dispecer -
|
||||
e o consecinta structurala a felului in care coloana isi ia textul.
|
||||
|
||||
Butonul lateral **Modificare** ocoleste complet problema fiindca nu citeste `ControlSource`, ci
|
||||
`Thisform.pccontrol`, pe care `GotFocus` il pune explicit:
|
||||
|
||||
```
|
||||
omodificari.vc2:16722-16726
|
||||
PROCEDURE ... cExplicatieTvaArt.Text1.GotFocus
|
||||
*!* coloana afiseaza denumirea din crsJtvaTemp, deci ControlSource e expresie - campul editat se da explicit
|
||||
Thisform.pccontrol = 'tvd.id_jtva_coloana'
|
||||
Thisform.pncolumnorder = This.Parent.ColumnOrder
|
||||
```
|
||||
|
||||
**De aici rezulta fixul care rezolva punctele 1 si 2 dintr-o singura miscare**: `DblClick` se
|
||||
sprijina si el pe `pccontrol`, deci merge uniform pe toate cele cinci coloane, inclusiv pe
|
||||
explicatie TVA, unde tastarea nu are cum sa mearga niciodata.
|
||||
|
||||
---
|
||||
|
||||
## Punctul 1+2 - dublu-click pe toate nomenclatoarele
|
||||
|
||||
**Cauza:** `DblClick` nu exista deloc pe `grdArticoleFactura` azi. Explicatie TVA n-are nici calea
|
||||
de tastare, din motivul de mai sus.
|
||||
|
||||
**Interventia propusa:** cinci metode noi `DblClick`, cate una per coloana cu nomenclator, pe
|
||||
controlul din coloana (`<coloana>.Text1.DblClick`, nu pe coloana), cu corp identic - acelasi corp
|
||||
de trei linii ca `InteractiveChange`-urile existente:
|
||||
|
||||
```foxpro
|
||||
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.<coloana>.Text1.DblClick
|
||||
Local loEditor
|
||||
loEditor = Createobject('ArticoleNotaEditor', Thisform)
|
||||
loEditor.ModificaNomenclator(Thisform.pccontrol)
|
||||
ENDPROC
|
||||
```
|
||||
|
||||
Nimic de schimbat in dispecer. `pccontrol` e deja corect la momentul dublu-click-ului: primul clic
|
||||
da focusul si declanseaza `GotFocus`, al doilea completeaza `DblClick`.
|
||||
|
||||
**Efect:** explicatie TVA capata in sfarsit o cale din grid (dublu-click), iar celelalte patru
|
||||
capata a doua cale, pe langa tastare.
|
||||
|
||||
**De decis:** nimic - e curat si aditiv. Confirma doar ca vrei si pe `cDenumireArt` (articol),
|
||||
nu doar pe cele patru mici.
|
||||
|
||||
---
|
||||
|
||||
## Punctul 3 - trei zecimale la Pret
|
||||
|
||||
Aici formularea ta era aproape exacta, dar cu variabila inversata, si asta e chiar defectul.
|
||||
|
||||
Coloana **este** deja controlata de o variabila globala:
|
||||
|
||||
```
|
||||
omodificari.vc2:12391 Column6.InputMask = (get_mask(12,gnPPRET)) && cPretArt = tvd.pret
|
||||
omodificari.vc2:12400 Column7.InputMask = (get_mask(12,gnPPRET)) && cPretAchizitieArt
|
||||
```
|
||||
|
||||
Ambele coloane primesc `gnPPret`. Dar `gnPPret` e precizia **pretului de achizitie** (=3 in mediul
|
||||
investigat, `OPTIUNI.PPRET`), iar `tvd.pret` e pretul de **vanzare** al liniei, a carui precizie e
|
||||
`gnPPretV` (`oinit_optiuni.prg:515`: `Store 0 To gnPPretV && nr. de zecimale pret vanzare`).
|
||||
|
||||
Regula e consecventa in restul suitei, si se vede pe doua coloane vecine in acelasi fisier:
|
||||
|
||||
```
|
||||
ofacturare.vc2:3490 Column5.InputMask = (get_mask(14,gnPPret)) && "pret" = achizitie
|
||||
ofacturare.vc2:3497 Column6.InputMask = (get_mask(14,gnPPretV)) && "pretv" = vanzare
|
||||
```
|
||||
|
||||
Chiar scrierea in Oracle a coloanei `VANZARI_DETALII.PRET` - aceeasi din care se umple `tvd.pret` -
|
||||
foloseste `gnPPretV`:
|
||||
|
||||
```
|
||||
ofacturare_stoc.prg:367
|
||||
Alltrim(Str(poArticol.Pret,18,Iif(poDate.in_valuta = 1,gnPVal,gnPPretV)))
|
||||
```
|
||||
|
||||
**Cauza:** la scrierea sectiunii de grid s-a copiat masca de la coloana de achizitie pe coloana de
|
||||
vanzare de langa ea.
|
||||
|
||||
**Interventia propusa:** `omodificari.vc2:12391`, `get_mask(12,gnPPRET)` -> `get_mask(12,gnPPretV)`.
|
||||
O singura linie.
|
||||
|
||||
**Doua lucruri conexe, de decis separat:**
|
||||
|
||||
- `cPretCuTvaArt` (`omodificari.vc2:12404-12409`) **n-are deloc** `Format`/`InputMask`, deci afiseaza
|
||||
precizia bruta a cursorului (4 zecimale), necontrolata de nimic. Il aliniem la `gnPPretV` in
|
||||
aceeasi runda, sau il lasam?
|
||||
- Linia din `ofacturare_stoc.prg:367` arata ca pe documentele **in valuta** precizia corecta e
|
||||
`gnPVal`, nu `gnPPretV`. `InputMask` de pe coloana se evalueaza o singura data, la instantierea
|
||||
clasei, cand moneda documentului inca nu e cunoscuta in acel context - deci o masca fidela pe
|
||||
ambele cazuri ar cere setare la runtime, in `Init`, dupa ce se stie `in_valuta`. **Recomand sa
|
||||
NU intram acum**: e o rafinare separata, iar simptomul pe care l-ai vazut se rezolva complet cu
|
||||
schimbarea de o linie. Semnalez doar ca ramane o inexactitate cunoscuta pe facturile in valuta.
|
||||
|
||||
---
|
||||
|
||||
## Punctul 4 - fundalul gri
|
||||
|
||||
Aici concluzia e in doua parti, si a doua e mai serioasa decat pare din enunt.
|
||||
|
||||
Conventia de culoare exista deja in aplicatie, si chiar in acelasi formular, pe pagina **Rulaje**:
|
||||
|
||||
| Aspect | Semnificatie | Exemplu |
|
||||
|---|---|---|
|
||||
| alb `255,255,255` + `ReadOnly=.F.` | se scrie direct, prin tastare | `grdRulaje.cPret.Text1`, :9990 |
|
||||
| verde `160,255,205` + `ReadOnly=.F.` | se editeaza, dar prin dialog/nomenclator | `grdRulaje.cGest.Text1`, :9689; `cDenumire.Text1`, :9595 |
|
||||
| gri `225,225,225` + `ReadOnly=.T.` | nu se editeaza | - |
|
||||
|
||||
### 4a. Cele cinci coloane cu nomenclator sunt colorate gresit
|
||||
|
||||
`cDenumireArt`, `cGestiuneArt`, `cTaxcodeArt`, `cValutaArt`, `cExplicatieTvaArt` au toate
|
||||
`BackColor = 225,225,225` (gri) desi au editor. Omologul direct al lui `cGestiuneArt` de pe pagina
|
||||
Rulaje (`cGest`, acelasi tipar `GotFocus`/`InteractiveChange`/`ArticoleNotaEditor`) e **verde**.
|
||||
Deci acelasi mecanism functional e colorat diferit in doua pagini ale aceluiasi formular.
|
||||
|
||||
**Interventia propusa:** cele cinci trec pe verde `160,255,205`.
|
||||
|
||||
**Recomand sa NU atingem `Text1.ReadOnly`** pe ele, desi pe Rulaje verdele vine cu `ReadOnly=.F.`
|
||||
Motivul: pe Articole, calea de tastare merge **tocmai fiindca** sunt read-only (cautarea
|
||||
incrementala). Ai probat-o azi pe `taxcode` si functioneaza. Daca le facem `.F.`, tastarea ar scrie
|
||||
direct in celula in loc sa declanseze cautarea - alt comportament, netestat, cu risc de a scrie
|
||||
gunoi in `tvd.taxcode`. Verdele singur spune adevarul si nu misca nimic functional.
|
||||
|
||||
### 4b. Serie, lot si explicatie NU sunt editabile azi - griul spune adevarul
|
||||
|
||||
Asta e partea care nu se potriveste cu ce ai scris, si cred ca e o bucata neterminata din runda 5.
|
||||
|
||||
Aceste trei coloane au `Column.ReadOnly = .F.` si o garda `.When` relaxata in runda 5
|
||||
(`cSerieArt` :16804, `cLotArt` :16745, `cExplicatieArt` :16716 - toate `IF lArticoleReadOnly OR
|
||||
id_vanzare_set <> 0 -> RETURN .F.`). Dar controlul din coloana a ramas neatins:
|
||||
|
||||
```
|
||||
omodificari.vc2:12734-12743 cSerieArt.Text1 BackColor = 225,225,225 ReadOnly = .T.
|
||||
omodificari.vc2:12631-12640 cLotArt.Text1 BackColor = 225,225,225 ReadOnly = .T.
|
||||
omodificari.vc2:12569-12578 cExplicatieArt.Text1 BackColor = 225,225,225 ReadOnly = .T.
|
||||
```
|
||||
|
||||
Comparatie de control, pe o coloana pe care sigur o editezi:
|
||||
|
||||
```
|
||||
omodificari.vc2:12485-12494 cCantitateArt.Text1 BackColor = 255,255,255 ReadOnly = .F.
|
||||
```
|
||||
|
||||
Nu au nici cale prin buton: `but_modificaR` se activeaza in `AfterRowColChange` (:16678-16682) doar
|
||||
cand `Thisform.pncolumnorder = nColIndex`, iar `pncolumnorder` se scrie exclusiv in `GotFocus` -
|
||||
care exista doar pe cele cinci coloane cu nomenclator. Pe serie/lot/explicatie butonul ramane
|
||||
dezactivat.
|
||||
|
||||
**Deci: garda a fost relaxata in runda 5, dar campurile au ramas read-only. Nu se pot edita pe
|
||||
nicio cale.** Griul e onest; intentia din runda 5 e cea neimplinita.
|
||||
|
||||
**Interventia propusa:** cele trei trec pe alb `255,255,255` + `Text1.ReadOnly = .F.`, ca
|
||||
`cCantitateArt`. Abia atunci devin editabile prin tastare, cum s-a decis in runda 5.
|
||||
|
||||
**De decis - e o schimbare reala de comportament, nu cosmetica:** confirmi ca serie, lot si
|
||||
explicatie trebuie sa devina scriabile direct in celula pe liniile deja salvate? (Garda `.When`
|
||||
existenta ramane si continua sa blocheze liniile din seturi si facturile intrate in e-Factura.)
|
||||
|
||||
---
|
||||
|
||||
## Punctul 5 - textul din meniu
|
||||
|
||||
Eticheta **nu e in `.mnx`** - cautarea in toate meniurile proiectului n-a gasit-o. E un literal
|
||||
intr-o metoda, deci se poate schimba prin text + write-back, fara interventie manuala in IDE:
|
||||
|
||||
```
|
||||
COMUN\clase\ofacturare_comun.vc2:4930
|
||||
lnOptiune = xmenu('Modificare \<date factura;Editare \<factura (articole, cantitati, preturi)')
|
||||
```
|
||||
|
||||
**Interventia propusa:** a doua optiune devine
|
||||
`Editare \<factura (note, rulaje, articole)`, cu acceleratorul `\<F` pastrat.
|
||||
|
||||
`ToolTipText = "Modificare (CTRL+M)"` de pe butonul generic (`cmd_butoane.vc2:194`) **nu se
|
||||
atinge** - e mostenit de peste 100 de formulare din toata suita; un text despre facturi acolo ar
|
||||
minti pe ecranele de parteneri, personal, stocuri.
|
||||
|
||||
**Doua observatii inainte sa confirmi textul:**
|
||||
|
||||
- Paginile reale ale formularului nu se numesc "Note / Rulaje / Articole":
|
||||
```
|
||||
omodificari.vc2:8725 PAGE1.Caption = "Rulaje materii prime, materiale, marfuri, obiecte inventar (303)"
|
||||
omodificari.vc2:8728 PAGE2.Caption = "Rulaje obiecte inventar in folosinta (8039)"
|
||||
omodificari.vc2:8731 PAGE3.Caption = "Articole factura"
|
||||
```
|
||||
Nu exista pagina "Note" - liniile notei sunt corpul principal al formularului, nepaginat.
|
||||
Formularea ta descrie corect cele trei zone functionale, dar nu citeaza etichetele de pe ecran.
|
||||
E in regula asa, sau vrei alt text?
|
||||
- Pagina Articole e **conditionata** (`omodificari.vc2:14927-14936`): apare doar cand nota se
|
||||
mapeaza pe exact o vanzare (`Reccount('tvanz') = 1`); altfel `PageCount = 2`. Un text care
|
||||
promite mereu "articole" va fi inexact pe notele fara vanzare atasata. Nu e grav - dar sa stii.
|
||||
|
||||
---
|
||||
|
||||
## Punctul 6 - explicatie TVA in butonul de modificare din frm_facturi
|
||||
|
||||
**Premisa ta e corecta si am verificat-o direct pe baza**, nu din raport. Procedura care salveaza:
|
||||
|
||||
```sql
|
||||
PROCEDURE modifica_explicatie_articol(V_ID_VANZARE_DET IN NUMBER,
|
||||
V_EXPLICATIE IN VARCHAR2,
|
||||
V_ID_UTIL IN NUMBER,
|
||||
V_TAXCODE IN NUMBER DEFAULT NULL) is
|
||||
BEGIN
|
||||
UPDATE VANZARI_DETALII
|
||||
SET EXPLICATIE = V_EXPLICATIE, TAXCODE = V_TAXCODE
|
||||
WHERE ID_VANZARE_DET = V_ID_VANZARE_DET;
|
||||
END modifica_explicatie_articol;
|
||||
```
|
||||
|
||||
Doua coloane, fara recalcul, fara atingerea cantitatii/pretului/cotei. Butonul e intr-adevar
|
||||
"campurile care nu modifica valorile". (Detaliu lateral: `V_ID_UTIL` e primit si nefolosit.)
|
||||
|
||||
Traseul azi: `But_modifica2` (`ofacturare_comun.vc2:1436`) -> `do_modifica_explicatie()` (:4642) ->
|
||||
dialogul `frm_modifica_articol_factura` (:5129), cu `explicatie` (editbox) si `taxcode` (combo pe
|
||||
lista plata de coduri SAF-T, fara legatura cu explicatia TVA).
|
||||
|
||||
**Aici e capcana.** Sablonul din `frm_modific2024` **nu se poate copia 1:1**: acolo alegerea
|
||||
explicatiei TVA scrie `id_jtva_coloana` **si** `proc_tvav` (cota noua) **si** recheama
|
||||
`calculeaza_valori_articol()` (`ofacturare_editare.prg:1100-1147`, `omodificari.vc2:15049`). Adica
|
||||
exact ce butonul asta nu face si nu trebuie sa faca.
|
||||
|
||||
**Interventia propusa, ca sa ramana neutru valoric:**
|
||||
|
||||
1. In dialog, combo nou pe explicatie TVA, **filtrat pe cota curenta a liniei** - ca sa nu se poata
|
||||
alege o explicatie cu alta cota. Asta e ce pastreaza promisiunea "nu modifica valorile".
|
||||
2. La alegere, se deriva `taxcode` prin `GetTaxCodeIdPart` (`oproceduri_comune.prg`, functie
|
||||
globala, apelabila din orice formular) si se scrie in combo-ul existent. `proc_tvav` **nu** se
|
||||
atinge.
|
||||
3. Piesele sunt refolosibile ca atare: `caut_explicatie_tva` (`ocautare.prg`) si `GetTaxCodeIdPart`
|
||||
sunt globale. Cod VFP nou estimat: ~30-40 de linii.
|
||||
|
||||
**Blocajul, si de asta punctul 6 nu poate intra in aceeasi livrare cu 1-5:** `id_jtva_coloana` n-are
|
||||
unde sa se salveze. Procedura Oracle primeste doar explicatie si taxcode. Daca scriem doar
|
||||
`taxcode`, explicatia TVA a liniei ramane desincronizata de codul fiscal - adica exact defectul pe
|
||||
care vrem sa-l evitam. Deci e nevoie de **un parametru nou in `pack_facturare.modifica_explicatie_articol`**,
|
||||
schimbare PL/SQL in afara acestui repo, de coordonat separat.
|
||||
|
||||
**De decis:**
|
||||
- Confirmi filtrarea pe cota curenta (varianta neutra)? Alternativa - lista completa de explicatii,
|
||||
cu recalcul - ar transforma butonul in altceva decat e azi si l-ar suprapune peste editarea din
|
||||
`frm_modific2024`. **Recomand filtrarea.**
|
||||
- Vrei sa pornim modificarea PL/SQL, sau punctul 6 asteapta?
|
||||
|
||||
**Bonus, deja rezolvat:** gridul din `frm_facturi` afiseaza deja "Explicatie TVA" / "Explicatie TVA 2"
|
||||
ca si coloane read-only (`ofacturare_comun.vc2:1679-1687`) - nu e nimic de adaugat in grid, doar in
|
||||
dialog.
|
||||
|
||||
---
|
||||
|
||||
## Ce propun sa intre in livrarea imediata
|
||||
|
||||
Punctele **1, 2, 3, 4, 5** - toate in `COMUN\clase\omodificari.vc2` si
|
||||
`COMUN\clase\ofacturare_comun.vc2`, ambele write-back-abile. Fara atingerea bazei de date.
|
||||
|
||||
| # | Fisier | Interventie | Volum |
|
||||
|---|---|---|---|
|
||||
| 1+2 | `omodificari.vc2` | 5 metode `DblClick` noi | ~25 linii |
|
||||
| 3 | `omodificari.vc2:12391` | `gnPPRET` -> `gnPPretV` | 1 linie |
|
||||
| 4a | `omodificari.vc2` | 5 `BackColor` -> verde `160,255,205` | 5 linii |
|
||||
| 4b | `omodificari.vc2` | 3 `BackColor` -> alb + `ReadOnly=.F.` | 6 linii |
|
||||
| 5 | `ofacturare_comun.vc2:4930` | textul optiunii 2 din `xmenu` | 1 linie |
|
||||
|
||||
Punctul **6** ramane separat - depinde de o schimbare PL/SQL.
|
||||
|
||||
## Ce astept de la tine inainte sa deleg aplicarea
|
||||
|
||||
1. **Punctul 4b** - confirmi ca serie/lot/explicatie devin scriabile in celula? (singura schimbare
|
||||
reala de comportament din lot)
|
||||
2. **Punctul 3** - aliniem si `cPretCuTvaArt` la `gnPPretV`, sau il lasam?
|
||||
3. **Punctul 5** - textul exact: `Editare \<factura (note, rulaje, articole)`?
|
||||
4. **Punctul 1+2** - `DblClick` si pe coloana de articol (`cDenumireArt`), sau doar pe celelalte patru?
|
||||
5. **Punctul 6** - pornim schimbarea PL/SQL acum, sau asteapta?
|
||||
|
||||
Nimic nu e aplicat. Niciun fisier de cod n-a fost atins in aceasta runda, nu s-a facut write-back,
|
||||
nu s-a rulat `git_sync.ps1`, nu s-a scris nimic in Oracle (doar un `SELECT` din `all_source` pentru
|
||||
sursa procedurii), nu s-a comis nimic.
|
||||
|
||||
---
|
||||
|
||||
# DECIZIILE LUI MARIUS - 20.08.2026, luate, se aplica
|
||||
|
||||
| Punct | Decizie |
|
||||
|---|---|
|
||||
| 1+2 `DblClick` | **toate cinci** coloanele cu nomenclator, inclusiv `cDenumireArt` |
|
||||
| 3 Pret | `gnPPRET` -> `gnPPretV` la `omodificari.vc2:12391` |
|
||||
| 3 conex `cPretCuTvaArt` | **se aliniaza** la `get_mask(12,gnPPretV)` |
|
||||
| 4a cele 5 cu nomenclator | verde `160,255,205`; `Text1.ReadOnly` ramane `.T.` |
|
||||
| 4b serie / lot / explicatie | **devin scriabile**: alb `255,255,255` + `Text1.ReadOnly = .F.` |
|
||||
| 5 meniu | `Editare \<factura (note, rulaje, articole)`; Caption-urile paginilor **nu** se ating |
|
||||
| 6 explicatie TVA in `frm_facturi` | **lista completa, CU recalcul**; PL/SQL **porneste acum** |
|
||||
|
||||
## Consecinta punctului 6, semnalata si acceptata
|
||||
|
||||
Cu lista completa si recalcul, butonul `But_modifica2` **nu mai e** "campuri fara impact pe
|
||||
valori". Devine o a doua cale de editare care schimba cota si totalurile documentului. Concret,
|
||||
fata de estimarea initiala din propunere, modificarea PL/SQL creste: procedura nu mai primeste doar
|
||||
`id_jtva_coloana`, ci si `proc_tvav`, si trebuie sa antreneze recalcularea valorilor liniei si a
|
||||
totalurilor documentului. Marius a ales aceasta varianta dupa ce consecinta a fost semnalata.
|
||||
|
||||
## Cum se imparte aplicarea
|
||||
|
||||
Un singur scriitor per fisier - regula de baza, ca sa nu se piarda modificari tacut.
|
||||
|
||||
| Agent | Fisier | Puncte |
|
||||
|---|---|---|
|
||||
| `apply-grid` | `COMUN\clase\omodificari.vc2` + write-back | 1+2, 3, 4a, 4b |
|
||||
| `apply-meniu` | `COMUN\clase\ofacturare_comun.vc2` + write-back | 5 |
|
||||
| `design-plsql` | script in `D:\ROA\DATABASE\SCRIPTURI_CLAR\` (doar scris, **nerulat**) | 6 - partea DB + proiectarea partii VFP |
|
||||
|
||||
Partea **VFP** a punctului 6 (combo-ul din `frm_modifica_articol_factura`) atinge tot
|
||||
`ofacturare_comun.vc2`, deci **nu porneste in paralel cu `apply-meniu`** si nici inainte ca
|
||||
semnatura procedurii sa fie fixata. Intra intr-un al doilea val, dupa ce `design-plsql` livreaza
|
||||
contractul si `apply-meniu` termina.
|
||||
|
||||
Commit-ul (git sau svn) **nu** se da - nici in acest val, nici in urmatorul.
|
||||
|
||||
---
|
||||
|
||||
# Constatare pe baza, 20.08.2026 - corectie la handoff-ul rundei 5
|
||||
|
||||
Handoff-ul rundei 5 spune despre `ff_2026_08_20_01_COMUN_PACK_FACTURARE.sql` (garda `FACT-025` pe
|
||||
totaluri) ca e **"nerulat pe schema `MARIUSM_AUTO` (`ROA_CENTRAL`)"**. **Nu mai e adevarat.** Verificat direct in `ALL_SOURCE`,
|
||||
schema `MARIUSM_AUTO`, corpul lui `PACK_FACTURARE`:
|
||||
|
||||
```
|
||||
lnLiniiActive in DB = 3 aparitii
|
||||
FACT-025 in DB = 2 aparitii
|
||||
```
|
||||
|
||||
Deci **scriptul 01 a fost aplicat pe schema `MARIUSM_AUTO` (`ROA_CENTRAL`)** intre timp. Consecinte, toate favorabile:
|
||||
|
||||
- `ff_2026_08_20_02` a fost construit din sursa vie a pachetului, deci **contine deja** modificarile
|
||||
scriptului 01. Nu exista riscul ca aplicarea lui 02 sa dea inapoi runda 5.
|
||||
- Formularea din raportul lui `design-plsql` — *"trebuie aplicat DUPA/impreuna cu 01"* — e
|
||||
depasita: 01 e deja in baza, 02 il include.
|
||||
|
||||
**Capcana ramasa, de retinut:** daca cineva **re-aplica** `ff_2026_08_20_01` **dupa** ce s-a aplicat
|
||||
`02`, sterge modificarile punctului 6. Scriptul 01 e consumat; nu se mai ruleaza.
|
||||
|
||||
Sursa de derivare a cotei, confirmata pe dictionar: `MARIUSM_AUTO.JTVA_COLOANE.COTA_TVA` exista.
|
||||
|
||||
---
|
||||
|
||||
# REVIZUIRE punctul 6 - 20.08.2026, decizie schimbata de Marius
|
||||
|
||||
Textual: **"nu vreau sa schimb cota de tva, maxim explicatia de tva si taxcode, aferente cotei de
|
||||
tva, ca sa nu se modifice totaluri"**.
|
||||
|
||||
Se revine la varianta **filtrata pe cota curenta** - cea recomandata initial in propunere.
|
||||
Varianta "lista completa, CU recalcul", aleasa in prima runda de intrebari, e **ABANDONATA**.
|
||||
|
||||
## Ce ramane
|
||||
|
||||
Butonul `But_modifica2` din `frm_facturi` **redevine neutru valoric**, cum era si cum il descrisese
|
||||
Marius de la inceput. Se scriu trei coloane: `EXPLICATIE`, `TAXCODE`, `ID_JTVA_COLOANA`.
|
||||
`PROC_TVAV` **nu** se atinge, totalurile documentului **nu** se recalculeaza.
|
||||
|
||||
Parametrul nou din PL/SQL, `V_ID_JTVA_COLOANA IN NUMBER DEFAULT NULL`, ramane necesar: fara el,
|
||||
explicatia aleasa n-ar avea unde sa se salveze si ar ramane desincronizata de `taxcode`.
|
||||
|
||||
## Ce cade din discutia de gardi
|
||||
|
||||
Toata sectiunea A5 din `docs\propunere_runda6_punct6_plsql.md` era consecinta recalculului. Fara el:
|
||||
|
||||
- **Garda pe seturi (`FACT-027`) cade.** Motivul ei era agregarea `MAX(proc_tvav)` peste
|
||||
componentele setului; odata ce `proc_tvav` nu mai e atins, componentele isi pot schimba linistit
|
||||
explicatia si taxcode-ul.
|
||||
- **Garda e-Factura cade.** Butonul redevine neutru valoric, iar azi nu are nicio astfel de
|
||||
verificare - a o adauga acum ar bloca ceva ce functioneaza, fara ca decizia s-o ceara.
|
||||
- **Garda pe valuta** oricum nu fusese propusa.
|
||||
|
||||
## Garda care ramane, si de ce e in PL/SQL
|
||||
|
||||
Neutralitatea valorica e chiar cerinta lui Marius, deci se **impune in baza**, nu se lasa doar pe
|
||||
seama filtrarii combo-ului in interfata: daca filtrul din VFP e gresit sau ocolit, baza trebuie sa
|
||||
refuze.
|
||||
|
||||
Daca `V_ID_JTVA_COLOANA` e dat si `JTVA_COLOANE.COTA_TVA` a explicatiei alese **difera** de
|
||||
`PROC_TVAV`-ul liniei, procedura nu scrie nimic si arunca `RAISE_APPLICATION_ERROR` - stilul
|
||||
`FACT-025` din runda 5: esec vizibil cu rollback, niciodata scriere tacuta.
|
||||
|
||||
Raman si gardele ieftine: linie inexistenta sau stearsa, explicatie TVA inexistenta sau fara cota
|
||||
valida.
|
||||
|
||||
## Starea livrabilelor
|
||||
|
||||
`ff_2026_08_20_02_COMUN_PACK_FACTURARE.sql`, in forma scrisa la 14:22, implementeaza varianta
|
||||
**respinsa** (recalcul + `FACT-027`). Se **rescrie peste el**, pornind de la sursa vie din
|
||||
`MARIUSM_AUTO` - nu se lasa doua fisiere cu acelasi scop pe disc, exact capcana din runda 5 cand o
|
||||
varianta respinsa a stat alaturi de cea buna. **Scriptul nu a fost rulat pe schema `MARIUSM_AUTO` (`ROA_CENTRAL`) in nicio forma.**
|
||||
|
||||
Partea VFP: combo-ul din `frm_modifica_articol_factura` trebuie **filtrat pe cota liniei**.
|
||||
320
docs/propunere_runda6_punct6_plsql.md
Normal file
320
docs/propunere_runda6_punct6_plsql.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# Runda 6, punctul 6 — proiectare PL/SQL: explicatie TVA in butonul de modificare din `frm_facturi`
|
||||
|
||||
> **NOTA — decizie schimbata, 20.08.2026.** Prima varianta discutata cu Marius era "lista completa
|
||||
> de explicatii TVA, CU recalcul": alegerea unei explicatii cu alta cota scria si `proc_tvav`, si
|
||||
> recalcula totalurile documentului. **Varianta a fost respinsa de Marius** dupa ce consecinta
|
||||
> (butonul nu mai era neutru valoric) a fost semnalata — cuvintele lui: *"nu vreau sa schimb cota
|
||||
> de tva, maxim explicatia de tva si taxcode, aferente cotei de tva, ca sa nu se modifice
|
||||
> totaluri"*. Documentul de fata reflecta **varianta finala, aprobata**: filtrata pe cota curenta,
|
||||
> **fara** recalcul. Nu reintroduce varianta cu recalcul fara o decizie noua, explicita, a lui
|
||||
> Marius.
|
||||
|
||||
Livrabile: scriptul PL/SQL (Partea B, mai jos) + acest document (Partea A). **Nimic nu s-a rulat
|
||||
pe Oracle** in afara de `SELECT`-uri de investigatie. Niciun fisier VFP n-a fost atins.
|
||||
|
||||
---
|
||||
|
||||
## A1. Cum se face azi acelasi lucru in `frm_modific2024`
|
||||
|
||||
Sablonul complet, verificat pe cod (nu e in domeniul acestei livrari, doar citit, si e citat aici
|
||||
doar ca sa arate *de ce* nu se copiaza 1:1 pentru `frm_facturi` — vezi A2):
|
||||
|
||||
```
|
||||
COMUN\programe\ofacturare_editare.prg:1100-1105 (ArticoleNotaEditor.ModificaNomenclator)
|
||||
CASE m.lcCamp == 'id_jtva_coloana'
|
||||
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()
|
||||
```
|
||||
|
||||
Lantul, in ordine:
|
||||
|
||||
1. **`loCauta`** vine din `This.CautaExplicatieTva()` (`ofacturare_editare.prg:1142-1147`), care
|
||||
cheama `caut_explicatie_tva(id_jtva_curent, , , , lnCotaFiltru)` — **filtrat pe cota liniei
|
||||
curente** (`ocautare.prg:3174-3238`, filtrul la `:3218-3220`). Returneaza un obiect cu
|
||||
`.id_jtva_coloana`, `.denumire`, `.cota_tva`.
|
||||
2. **Scrierea locala** (pe cursorul `tvd`, in memorie, inca nesalvat): `id_jtva_coloana` primeste
|
||||
valoarea aleasa, `proc_tvav` primeste `(cota_tva + 100) / 100` — calculat in VFP.
|
||||
3. **`UpdateExplicatieSAFTArt()`** (`COMUN\clase\omodificari.vc2:15049-15074`) deriva `taxcode` din
|
||||
`id_jtva_coloana` prin functia globala **`GetTaxCodeIdPart(an, luna, data_act, id_jtva, id_part,
|
||||
n50, n100, neexigibil)`** (`COMUN\programe\oproceduri_comune.prg:6059-6125`).
|
||||
4. **`calculeaza_valori_articol()`** recalculeaza valoarea liniei pe cursorul local `tvd`.
|
||||
5. La salvarea intregului formular, `ScrieArticoleFacturaEditate()`
|
||||
(`ofacturare_editare.prg:452-573`) scrie liniile si cheama o singura data, pentru tot
|
||||
documentul, `pack_facturare.recalculeaza_totaluri_vanzari(id_vanzare, discount)`.
|
||||
|
||||
**Important pentru varianta finala**: acest sablon presupune ca `id_jtva_coloana` **poate** aduce
|
||||
o cota diferita — de-asta exista filtrul pe cota in `caut_explicatie_tva` doar ca o *reducere* a
|
||||
listei, nu ca o *garda*. Pentru `frm_facturi`, decizia lui Marius transforma filtrul de cota
|
||||
dintr-o comoditate de UI intr-o **conditie obligatorie**, impusa si in baza (A5).
|
||||
|
||||
---
|
||||
|
||||
## A2. Unde cade validarea — nu mai exista recalcul de pus undeva
|
||||
|
||||
Cu decizia finala, **nu se recalculeaza nimic**: nici valoarea liniei, nici totalurile
|
||||
documentului. `proc_tvav` nu se scrie. Intrebarea "VFP sau PL/SQL" din varianta initiala nu se mai
|
||||
pune pentru un recalcul — dar ramane o intrebare echivalenta pentru **garda de neutralitate**
|
||||
(cota explicatiei alese trebuie sa fie egala cu cota liniei): unde se impune?
|
||||
|
||||
**Raspuns: in PL/SQL**, in `pack_facturare.modifica_explicatie_articol` insusi.
|
||||
|
||||
Argumente:
|
||||
|
||||
1. **Neutralitatea valorica e chiar cerinta lui Marius** — nu un detaliu de implementare. Daca ar
|
||||
fi impusa doar prin filtrarea combo-ului in VFP (cum era propunerea initiala, inainte de
|
||||
recalcul), un bug de UI, un combo needitat corect, sau orice cale ocolitoare ar putea trimite
|
||||
un `id_jtva_coloana` cu alta cota, iar baza ar scrie-o fara sa clipeasca — exact ce Marius nu
|
||||
vrea.
|
||||
2. **`frm_facturi` nu are, si acum nici nu are nevoie de**, un motor de calcul local — cererea nu
|
||||
mai atinge `calculeaza_valori_articol()` sau echivalentul lui. PL/SQL are tot ce ii trebuie
|
||||
pentru garda: `VANZARI_DETALII.PROC_TVAV` (linia) si `JTVA_COLOANE.COTA_TVA` (explicatia).
|
||||
3. **E acelasi stil ca FACT-025** (runda 5): validare in baza, esec zgomotos cu rollback, nu o
|
||||
validare "de bune maniere" doar in client.
|
||||
|
||||
**Ce pierde varianta "doar VFP"** (garda doar prin filtrarea combo-ului, fara verificare in
|
||||
PL/SQL): nimic in flux normal — dar orice cale care ocoleste combo-ul (alt formular viitor, un
|
||||
script, o corectie manuala printr-un alt ecran care cheama aceeasi procedura) ar putea scrie o
|
||||
cota diferita nedetectat. Cum procedura oricum trebuie sa primeasca `id_jtva_coloana` ca sa-l
|
||||
scrie, verificarea in PL/SQL nu costa un apel suplimentar — e in aceeasi tranzactie.
|
||||
|
||||
---
|
||||
|
||||
## A3. Semnatura noua
|
||||
|
||||
```sql
|
||||
PROCEDURE modifica_explicatie_articol(V_ID_VANZARE_DET IN NUMBER,
|
||||
V_EXPLICATIE IN VARCHAR2,
|
||||
V_ID_UTIL IN NUMBER,
|
||||
V_TAXCODE IN NUMBER DEFAULT NULL,
|
||||
V_ID_JTVA_COLOANA IN NUMBER DEFAULT NULL);
|
||||
```
|
||||
|
||||
Neschimbata fata de prima varianta ca lista de parametri — un singur parametru nou,
|
||||
**`V_ID_JTVA_COLOANA`, `DEFAULT NULL`** — dar **semantica e diferita**: nu mai e "valoarea de
|
||||
scris fara conditii", ci "valoarea de scris **doar daca** cota ei coincide cu cota liniei".
|
||||
Procedura **nu** mai scrie `PROC_TVAV` si **nu** mai cheama `recalculeaza_totaluri_vanzari`.
|
||||
|
||||
**Compatibilitate inapoi — verificata, nu presupusa** (neschimbat fata de investigatia initiala):
|
||||
|
||||
- **Apelant unic in tot codul VFP**: `COMUN\clase\ofacturare_comun.vc2:5236`
|
||||
(`inainte_de_do_termin`, dialogul `frm_modifica_articol_factura`). Cautare pe intregul working
|
||||
copy (`.vc2`, `.sc2`, `.prg`) — niciun alt loc nu cheama
|
||||
`pack_facturare.modifica_explicatie_articol`.
|
||||
- **Niciun apelant PL/SQL**: `SELECT` pe `ALL_SOURCE`, text `MODIFICA_EXPLICATIE_ARTICOL`,
|
||||
excluzand `PACK_FACTURARE` insusi — zero randuri, in orice schema din `ROA_CENTRAL`.
|
||||
- Apelul existent trimite exact 3 parametri pozitionali + `?poRec.taxcode` — `V_ID_JTVA_COLOANA`
|
||||
ramane `NULL`, neschimbat.
|
||||
- **Ramura `V_ID_JTVA_COLOANA IS NULL` reproduce byte-cu-byte `UPDATE`-ul vechi** (aceleasi doua
|
||||
coloane, acelasi `WHERE`, fara verificare noua) si face `RETURN` imediat.
|
||||
|
||||
---
|
||||
|
||||
## A4. Cazurile care nu trebuie sa treaca tacut
|
||||
|
||||
Pastrez regula din runda 5: cand nu se poate valida ceva desi exista date pentru validare,
|
||||
procedura **nu scrie nimic** si arunca `RAISE_APPLICATION_ERROR` (rollback). Cod de eroare
|
||||
urmator liber la momentul scrierii: **FACT-025** — confirmat direct pe sursa vie din
|
||||
`MARIUSM_AUTO.PACK_FACTURARE` (comentariul `-- ultima eroare atribuita`), neschimbat fata de
|
||||
runda 5 (runda 5 e deja aplicata, vezi nota din Partea B despre corectia facuta aici). Patru coduri
|
||||
noi, FACT-026..029:
|
||||
|
||||
| Cod | Cand se arunca | De ce nu trece tacut |
|
||||
|---|---|---|
|
||||
| **FACT-026** | `V_ID_JTVA_COLOANA` dat, dar `V_ID_VANZARE_DET` nu (mai) exista sau are `STERS <> 0` | Fara o linie activa gasita nu exista `PROC_TVAV` de comparat — a continua ar insemna fie un `UPDATE` pe 0 randuri, fie o comparatie pe o valoare nedefinita |
|
||||
| **FACT-027** | `V_ID_JTVA_COLOANA` dat, dar nu exista in `JTVA_COLOANE` (sau are `STERS <> 0`) | Fara `cota_tva` validata nu exista cu ce sa se compare cota liniei |
|
||||
| **FACT-028** | Linia gasita (FACT-026 trecut), dar `PROC_TVAV` al ei e `NULL` | Comparatia `NULL <> x` nu e nici adevarata nici falsa in PL/SQL — a lasa `IF`-ul sa treaca tacut pe langa ea ar insemna sa scrii o explicatie fara sa stii daca respecta neutralitatea. Cazul e real: **2 randuri active au `PROC_TVAV IS NULL`** azi (confirmat cu `SELECT COUNT(*) FROM vanzari_detalii WHERE sters=0 AND proc_tvav IS NULL` — acelasi fapt semnalat in runda 5) |
|
||||
| **FACT-029** | Ambele valori cunoscute, dar `ROUND(cota liniei, 4) <> ROUND(cota explicatiei, 4)` | Aceasta e garda de neutralitate ceruta de Marius — orice diferenta de cota inseamna ca alegerea ar schimba taxa liniei, deci si totalurile, daca s-ar scrie |
|
||||
|
||||
**Comparatia de cota** — decizie si motivare: `VANZARI_DETALII.PROC_TVAV` e `NUMBER(10,4)`,
|
||||
`JTVA_COLOANE.COTA_TVA` e `NUMBER(10,0)` (confirmat pe dictionar: `ALL_TAB_COLUMNS`). Cota
|
||||
explicatiei se converteste cu aceeasi formula ca in VFP, `(NVL(cota_tva,0)+100)/100`. Ambele
|
||||
numere provin din fractii cu numitor 100 (19/100, 9/100, 5/100, 0/100) — reprezentabile exact in
|
||||
tipul `NUMBER` (zecimal, nu binar) al Oracle, deci nu exista risc de eroare de rotunjire in
|
||||
practica; `ROUND(..., 4)` pe ambele parti e adaugat ca **toleranta explicita, documentata**, nu ca
|
||||
raspuns la o problema observata, ca o eventuala a cincea zecimala reziduala pe date vechi sa nu
|
||||
produca un refuz fals. `NULL` pe linie **nu** e tratat ca "egal cu orice" si nici ca "diferit de
|
||||
orice" prin comparatie implicita — e verificat explicit (`IF lnProcTvavLinie IS NULL THEN RAISE`),
|
||||
inaintea comparatiei, tocmai ca sa nu se bazeze pe semantica NULL a lui `<>` din PL/SQL (care ar fi
|
||||
lasat garda sa treaca tacut).
|
||||
|
||||
**Ce NU arunca eroare**: `V_ID_JTVA_COLOANA IS NULL` (apelul vechi — niciun comportament nou).
|
||||
|
||||
---
|
||||
|
||||
## A5. Riscul — categorii de documente, verdict pe fiecare
|
||||
|
||||
### a) Facturi intrate in e-Factura
|
||||
|
||||
**Nu exista azi nicio garda** pe acest flux (confirmat: `do_modifica_explicatie`/
|
||||
`inainte_de_do_termin` nu verifica `EsteInEFactura`/`anaf_efactura` deloc). Cu varianta finala
|
||||
(fara recalcul, fara scriere de `proc_tvav`), butonul **ramane neutru valoric** — `EXPLICATIE`,
|
||||
`TAXCODE` si `ID_JTVA_COLOANA` sunt metadate de raportare, nu valori financiare ale liniei.
|
||||
|
||||
**Decizie: nu se adauga garda e-Factura**, nici in VFP, nici in PL/SQL — situatia de azi nu se
|
||||
schimba, iar Marius nu a cerut-o.
|
||||
|
||||
**Observatie separata, semnalata, nu implementata**: `TAXCODE` **este** el insusi o valoare
|
||||
raportata in SAF-T/e-Factura (coloana `taxcode` pe `VANZARI_DETALII`, folosita la generarea
|
||||
declaratiei 406 si potential in fisierul e-Factura). Schimbarea lui pe o factura **deja trimisa**
|
||||
in e-Factura (`ANAF_EFACTURA`) nu modifica totaluri, dar **poate produce o discrepanta reala**
|
||||
intre ce s-a raportat/trimis deja si ce arata acum baza — factura trimisa nu se retrimite automat.
|
||||
Daca acest risc conteaza pentru Marius, e o garda separata, de decis explicit (nu a fost ceruta
|
||||
aici si nu blocheaza livrarea curenta).
|
||||
|
||||
### b) Facturi din seturi (`id_vanzare_set`)
|
||||
|
||||
Riscul din varianta initiala (agregarea `MAX(c.proc_tvav)` pe componentele de set in
|
||||
`recalculeaza_totaluri_vanzari`) **dispare complet**, pentru ca `proc_tvav` nu mai e atins de
|
||||
aceasta procedura. **Confirmat pe cod, nu presupus**: am recitit interogarea de agregare din
|
||||
`recalculeaza_totaluri_vanzari` (`PACK_FACTURARE` body, ~liniile 14804-14979 din sursa vie) —
|
||||
coloanele citite din `VANZARI_DETALII` pentru total sunt `pret`, `proc_tvav`, `cantitate`,
|
||||
`diferenta`, `discount_unitar`, `id_valuta`, `pret_cu_tva`, `pret_achizitie`; **nici
|
||||
`EXPLICATIE`, nici `TAXCODE`, nici `ID_JTVA_COLOANA` nu apar nicaieri in aceasta procedura** (si
|
||||
oricum procedura de fata nu o mai cheama). Nu exista alt loc in `PACK_FACTURARE` unde
|
||||
`recalculeaza_totaluri_vanzari` sau vreo alta procedura de agregare a totalurilor sa citeasca
|
||||
aceste trei coloane.
|
||||
|
||||
**Decizie: fara garda pe `id_vanzare_set`.** Componentele de set isi pot schimba linistit
|
||||
explicatia si taxcode-ul, ca orice alta linie — motivul pentru care runda 5/varianta initiala ar
|
||||
fi blocat asta (efectul lui `proc_tvav` pe agregarea de set) nu se mai aplica.
|
||||
|
||||
### c) Facturi in valuta
|
||||
|
||||
Recalculul (singurul loc unde intra in joc `VANZARI_CURSURI`, scris doar la emitere — runda 5) nu
|
||||
mai e apelat de aceasta procedura. **Fara relevanta pentru varianta finala** — nu exista nimic de
|
||||
garda aici.
|
||||
|
||||
---
|
||||
|
||||
## Partea B — scriptul
|
||||
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_20_02_COMUN_PACK_FACTURARE.sql` —
|
||||
**suprascris** cu varianta finala (nu a ramas pe disc varianta cu recalcul, respinsa; a fost
|
||||
inlocuita integral, nu lasata alaturi).
|
||||
|
||||
Pornit de la **sursa vie**, re-citita din `ALL_SOURCE` chiar pentru aceasta livrare (nu de la
|
||||
scriptul anterior, respins): schema `MARIUSM_AUTO` (`current_schema` al conexiunii read-only
|
||||
folosite). Diff fata de sursa vie, verificat cu `diff` linie-cu-linie:
|
||||
|
||||
- antetul (comentariu descriptiv, fara `*!*`/data/autor, ca la scriptul din runda 5)
|
||||
- comentariul de tracking `FACT-025` -> `FACT-029`
|
||||
- spec: parametrul nou `V_ID_JTVA_COLOANA IN NUMBER DEFAULT NULL` la
|
||||
`modifica_explicatie_articol` (1 bloc, 4 -> 5 linii)
|
||||
- body: acelasi parametru in antetul procedurii, plus corpul nou din A3/A4 — validare, **fara**
|
||||
scriere de `PROC_TVAV`, **fara** apel de recalcul (1 bloc, 9 -> 49 linii); restul pachetului
|
||||
(peste 16000 de randuri) neatins — verificat cu `diff`, nicio alta diferenta
|
||||
- linia `exec pack_migrare.UpdateVersiune('ff_2026_08_20_02_COMUN_PACK_FACTURARE')` + `commit;`
|
||||
(numele fisierului nu s-a schimbat, doar continutul)
|
||||
|
||||
Fisier scris in ASCII (0 octeti > 0x7F, verificat), CRLF pe toate liniile. **Scriptul nu a fost
|
||||
rulat.**
|
||||
|
||||
**Corectie fata de raportul initial**: sectiunea A5(c) din prima versiune a acestui document
|
||||
afirma ca `ff_2026_08_20_01_COMUN_PACK_FACTURARE.sql` (FACT-025, runda 5) trebuie aplicat
|
||||
**inainte** de scriptul curent. Verificat direct pe baza: **`...20_01` e deja aplicat** — sursa
|
||||
vie din `MARIUSM_AUTO.PACK_FACTURARE` contine deja `lnLiniiActive`/`FACT-025`. Deci **ordinea
|
||||
corecta e: se aplica doar scriptul curent** (`...20_02`); re-aplicarea lui `...20_01` dupa el ar
|
||||
suprascrie/sterge continutul de fata. (Fisierul `...20_01` insusi nu mai e prezent in
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\` la momentul acestei livrari — semnalat ca observatie,
|
||||
nu afecteaza corectitudinea scriptului curent, care a fost construit din sursa vie din Oracle, nu
|
||||
din acel fisier.)
|
||||
|
||||
---
|
||||
|
||||
## Ce ramane de facut in VFP
|
||||
|
||||
Pentru agentul care va scrie codul VFP (nu porneste inainte ca semnatura de mai sus sa fie
|
||||
confirmata/aplicata, si nu in paralel cu `apply-meniu` — ambele ating `ofacturare_comun.vc2`):
|
||||
|
||||
1. **Combo nou in `frm_modifica_articol_factura`** (`ofacturare_comun.vc2:5129-5253`), langa
|
||||
`cbo_saft` existent. Populat cu explicatii TVA **filtrate pe cota curenta a liniei** — apel
|
||||
**`caut_explicatie_tva(poRec.id_jtva_coloana, , , , lnCotaFiltru)`**, cu `lnCotaFiltru` calculat
|
||||
la fel ca in `CautaExplicatieTva` (`ofacturare_editare.prg:1142-1147`):
|
||||
`Round((Nvl(poRec.proc_tvav, 0) - 1) * 100, 2)`. **Nu** lista completa — asta era varianta
|
||||
respinsa. Filtrarea in UI e o comoditate (userul nu vede optiuni pe care oricum baza le va
|
||||
refuza), garda reala e in PL/SQL (A2/A4). Sursa datelor: `poRec` are deja `id_jtva_coloana`,
|
||||
`jtva_coloana`, `proc_tvav`, `taxcode` disponibile (schema `crsDetalii`,
|
||||
`oproceduri_facturare.prg:382-387`).
|
||||
2. **La alegere**: deriva `taxcode` in **VFP** (nu in PL/SQL — precizarea lui Marius confirma
|
||||
directia, iar aici e verificat explicit ca se poate), prin
|
||||
`GetTaxCodeIdPart(gnAn, gnLuna, poRec.data_act, ...)`, si trimite rezultatul prin parametrul
|
||||
existent `V_TAXCODE`. **Verificat pe cod, nu presupus, ca `GetTaxCodeIdPart` e apelabila din
|
||||
`frm_facturi`:**
|
||||
- `GetTaxCodeIdPart` (`oproceduri_comune.prg:6059-6125`) nu presupune niciun cursor/formular
|
||||
specific — ia doar parametri simpli si isi gestioneaza singura cursorul `jtva_coloane` (il
|
||||
deschide cu `update_jtva_coloane()` daca nu e deja deschis, si il inchide la iesire daca ea
|
||||
l-a deschis, `:6092-6118`).
|
||||
- Lantul ei de apeluri interne — `update_jtva_coloane` (`updateserver.prg:553`),
|
||||
`VERIFICA_RTVAI`/`GetCodFiscalPartenerById` (ambele in `oproceduri_comune.prg`), `GetTaxCode`
|
||||
(`oproceduri_comune.prg:6130`) — sunt toate in fisiere **inregistrate necondiționat**
|
||||
(`roafacturare.prg:187` `OPROCEDURI_COMUNE.PRG`, `:189` `updateserver.PRG`), spre deosebire de
|
||||
`ofacturare_editare.prg` (`:214`, care e cel condiționat pe produs — de acolo vine restrictia
|
||||
pe `EsteInEFactura`, nu de aici). Deci **taxcode-ul se deriva in VFP, in `frm_facturi`**, exact
|
||||
ca in `frm_modific2024`, fara nicio schimbare de plan fata de propunerea initiala.
|
||||
- `poRec` (din `crsDetalii`) are `data_act`? De verificat la implementare — schema confirmata
|
||||
(`oproceduri_facturare.prg:382-387`) nu listeaza explicit `data_act` pe `crsDetalii`;
|
||||
echivalentul e pe `crsfacturi` (headerul), de adus prin `id_vanzare`. `id_part` vine din
|
||||
`crsfacturi.id_part` (`oproceduri_facturare.prg:341`). `n50`/`n100` = `.F.` (ca in
|
||||
`UpdateExplicatieSAFTArt`, "nu se mai folosesc"); `neexigibil` — de stabilit explicit (sablonul
|
||||
din `omodificari.vc2` il deriva din `tAct.scd`/`scc`, cursor care nu exista in `frm_facturi`;
|
||||
probabil `.F.` e suficient, dar nu presupune, confirma).
|
||||
3. **Lista/combo-ul poate iesi goala — trateaz-o explicit, nu lasa un dropdown gol fara
|
||||
explicatie.** Verificat pe date (`SELECT` direct, read-only): pentru **toate cele 8 cote
|
||||
distincte folosite azi pe linii active** (`0, 5, 9, 11, 19, 20, 21, 24` — 1122 linii in total),
|
||||
**exista cel putin o explicatie TVA activa in `JTVA_COLOANE` cu aceeasi cota** (`id_jtva_coloana
|
||||
> 0 AND NVL(sters,0)=0`) — deci lista **nu iese goala azi pentru nicio cota reala**. Singurele
|
||||
**2 linii** unde interogarea ar iesi goala sunt exact cele cu `PROC_TVAV IS NULL` (acelasi 2
|
||||
randuri de la FACT-028/runda 5) — pentru ele problema nu e "nicio explicatie cu aceasta cota", ci
|
||||
"cota liniei insasi nu se cunoaste", un caz diferit si deja acoperit (PL/SQL va refuza oricum cu
|
||||
FACT-028 daca s-ar incerca salvarea). Practic: **cazul "lista goala pentru o cota reala,
|
||||
cunoscuta" e azi 0/1122 — teoretic posibil doar pentru o cota noua, adaugata pe o linie fara ca
|
||||
nomenclatorul `JTVA_COLOANE` sa aiba inca o explicatie activa cu acea cota.**
|
||||
|
||||
**Recomandare de comportament** (proiectare, nu implementare): cand interogarea de populare
|
||||
(`Reccount()` pe cursorul RowSource) iese cu 0 randuri, combo-ul ramane **dezactivat**
|
||||
(`Enabled = .F.`), cu un text vizibil langa el (label sau tooltip, nu `messagebox` modal — nu
|
||||
trebuie sa blocheze restul dialogului, care ramane editabil pe `explicatie`/`taxcode` ca azi):
|
||||
|
||||
> *Nu exista nicio explicatie TVA activa cu aceeasi cota ca linia facturata (cota: `<cota>` %).*
|
||||
|
||||
`<cota>` = `Round((Nvl(poRec.proc_tvav,0)-1)*100,2)`, acelasi calcul ca la filtrul de populare.
|
||||
Daca `poRec.proc_tvav` e `NULL` (cele 2 linii identificate), afiseaza in loc:
|
||||
|
||||
> *Cota de TVA a acestei linii nu este cunoscuta; explicatia TVA nu poate fi modificata prin
|
||||
> acest buton.*
|
||||
|
||||
4. **Fara garda e-Factura, fara garda de set** in aceasta parte (A5) — butonul ramane disponibil ca
|
||||
azi pe orice linie/document.
|
||||
4. **Apelul de salvare** (`inainte_de_do_termin`, `:5225-5233`): adauga al cincilea parametru
|
||||
pozitional `?poRec.id_jtva_coloana` la textul SQL existent (`NULL`/`.NULL.` cand userul n-a
|
||||
atins combo-ul nou, ca sa ramana pe ramura veche a procedurii).
|
||||
5. **Trateaza eroarea FACT-029** (si FACT-026/027/028) ca orice alt esec `goExecutor.oExecuta` —
|
||||
mesajul Oracle ajunge deja vizibil prin mecanismul existent (`amessagebox` in `oExecuta`), deci
|
||||
nu trebuie cod nou de afisare, doar sa nu presupui ca apelul reuseste mereu.
|
||||
6. **Refresh dupa salvare**: `Thisform.actualizeaza_grid2()` (apelat deja la `gnButon=1`) e
|
||||
suficient acum — headerul (`crsfacturi`, totalurile) **nu se schimba**, deci nu mai e nevoie de
|
||||
`Thisform.do_cauta()` suplimentar (recomandarea din varianta initiala nu se mai aplica).
|
||||
|
||||
**Nu s-a scris cod VFP in aceasta livrare** — punctele de mai sus sunt proiectare, nu
|
||||
implementare.
|
||||
|
||||
---
|
||||
|
||||
## Rezumat surse
|
||||
|
||||
| ce | fisier:linie |
|
||||
|---|---|
|
||||
| sablonul de sincronizare (runda 5, `frm_modific2024`) | `COMUN\programe\ofacturare_editare.prg:1100-1147`, `COMUN\clase\omodificari.vc2:15049-15074` |
|
||||
| `GetTaxCodeIdPart` | `COMUN\programe\oproceduri_comune.prg:6059-6125` |
|
||||
| `caut_explicatie_tva` (filtru de cota la `:3218-3220`) | `COMUN\programe\ocautare.prg:3174-3238` |
|
||||
| `do_modifica_explicatie` / dialog / salvare | `COMUN\clase\ofacturare_comun.vc2:4642-4659`, `:5129-5253`, `:5225-5233` |
|
||||
| `crsDetalii` / `crsfacturi` (schema, populare) | `COMUN\programe\oproceduri_facturare.prg:340-412` |
|
||||
| `EsteInEFactura` (doar ROAFACTURARE) | `COMUN\programe\ofacturare_editare.prg:1-30` |
|
||||
| `recalculeaza_totaluri_vanzari` — nu mai e apelata din aceasta procedura, citata doar pentru A5(c) | `MARIUSM_AUTO.PACK_FACTURARE`, body linia 14784 (sursa vie) |
|
||||
| garda FACT-025 (runda 5, deja aplicata) | `docs\raport_runda5_script_nvl_totaluri.md` |
|
||||
| `modifica_explicatie_articol` — pristina, confirmata pe baza | `MARIUSM_AUTO.PACK_FACTURARE`, spec linia 939, body linia 13271 (sursa vie la momentul acestei livrari) |
|
||||
| `PROC_TVAV`/`COTA_TVA` — precizie confirmata pe dictionar | `ALL_TAB_COLUMNS`: `VANZARI_DETALII.PROC_TVAV` = `NUMBER(10,4)`, `JTVA_COLOANE.COTA_TVA` = `NUMBER(10,0)` |
|
||||
| 2 randuri active cu `PROC_TVAV IS NULL` | `SELECT COUNT(*) FROM vanzari_detalii WHERE sters=0 AND proc_tvav IS NULL` (verificat direct) |
|
||||
| scriptul (suprascris) | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_20_02_COMUN_PACK_FACTURARE.sql` |
|
||||
@@ -92,6 +92,16 @@ reluarea validarilor dupa aplicare — nu am facut-o, ca sa nu extind scopul.
|
||||
**B. `N-A` nu mai declanseaza dialogul la salvare** (corectia 3). E o alegere de produs facuta de
|
||||
mine: altfel dialogul ar aparea la fiecare salvare pe documentele in valuta.
|
||||
|
||||
> **B — CONFIRMAT de Marius, 12.08.2026. Ramane cum e, nu se schimba nimic.** Numaratoarea de la
|
||||
> `omodificari.vc2:14444` sta pe `Inlist(Alltrim(actiune),'Modificare','Adaugare','Semnalare')`.
|
||||
> Pretul acceptat, explicit: pe liniile in valuta si pe cele nestocate o divergenta reala nu mai e
|
||||
> semnalata **la salvare** — se vede in schimb oricand prin butonul manual „Sincronizeaza articole",
|
||||
> care le arata in grid.
|
||||
>
|
||||
> **A ramane deschis** si si-a schimbat forma — vezi `progres.md`, sectiunea despre validarea de
|
||||
> cantitate. Plasa de siguranta pe care o propuneam initial **nu se mai face**: singura validare care
|
||||
> ar fi putut pica dupa „Aplica" e cea de cantitate, iar premisa ei e sub semnul intrebarii.
|
||||
|
||||
## Defect preexistent gasit pe drum — REPARAT (aprobat separat, 11.08.2026)
|
||||
|
||||
`omodificari.vc2:16495`, in `frm_modific2024.pgfArticole.PAGE3.cmdAdaugaArticol.Click`:
|
||||
|
||||
Reference in New Issue
Block a user