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:
@@ -1,13 +1,23 @@
|
|||||||
<!--
|
<!--
|
||||||
10/08/2026
|
19/08/2026
|
||||||
ROAFACTURARE - 2.11.15
|
ROAFACTURARE - 2.11.15
|
||||||
|
|
||||||
:nou:
|
:nou:
|
||||||
Factura. La editarea unei facturi deja emise, modificarile facute articolelor (cantitate, pret, discount, adaugare sau stergere de linie) se scriu acum si in baza de date, la salvare - anterior ramaneau doar in ecranul de editare. S-a adaugat coloana "Pret achizitie" in grid, editabila pentru articolele nou adaugate pe factura. Liniile care apartin unui set de articole nu se mai pot edita individual - se marcheaza distinct, iar totalul se calculeaza tot din capul setului.
|
In formularul editare note se pot edita si articolele din factura de vanzare.
|
||||||
|
|
||||||
Daca factura a fost trimisa in eFactura, articolele raman vizibile la editare, dar nu se mai pot modifica - nici cantitatea, pretul sau discountul, nici adaugarea sau stergerea de linii. Se afiseaza un marcaj care explica motivul.
|
Pe linia de articol se poate alege explicatia TVA. Lista propune doar explicatiile cu cota TVA a liniei, iar codul de taxa SAF-T se completeaza automat dupa explicatia aleasa.
|
||||||
|
|
||||||
Tot la editare, butonul nou "Sincronizeaza cu rulaje..." compara cantitatile si preturile din rulaje cu cele de pe articolele facturii si le arata, linie cu linie, acolo unde difera. Se alege directia - rulajul completeaza articolele facturii sau invers - si se aplica dintr-o data tot ce se poate alinia. Liniile care nu se pot compara sigur (document in valuta, mai multe rulaje pe acelasi articol, articol nestocat) sunt marcate si lasate neatinse, la fel si articolele ramase fara corespondent, care doar se semnaleaza. Aceeasi comparatie apare si la salvare, daca au ramas diferente, cu posibilitatea de a salva mai departe fara sincronizare.
|
:modificare:
|
||||||
|
Totalul notelor din bara de jos se actualizeaza imediat dupa modificarea sumei de pe randul de nota, nu doar la editarea articolelor.
|
||||||
|
|
||||||
|
Fereastra de sincronizare articole - rulaje: coloanele arata acum ce valoare se inlocuieste si de unde se preia cea noua, in loc de "vechi" si "nou" explicate separat.
|
||||||
|
|
||||||
|
:eroare:
|
||||||
|
S-a corectat o eroare la descarcarea articolelor importate din eFactura, care aveau pret de achizitie cu 4 zecimale.
|
||||||
|
|
||||||
|
La alegerea codului de TVA pe linia de articol, lista arata "Memo" in loc de denumirea codului.
|
||||||
|
|
||||||
|
Modificarile facute pe liniile deja salvate ale facturii - cod de TVA, gestiune, valuta, articol, pret de achizitie, discount - nu se salvau.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
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
|
# 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.
|
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
|
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:
|
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
|
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".
|
> „Capcane de mediu".
|
||||||
|
|
||||||
> ### ATENTIE — copia de lucru are modificari necomise care NU sunt ale rundei 14
|
> ### 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 changelog_roafacturare.txt · M roafacturare.PJX / .PJT · M versiune_db.txt
|
||||||
> M COMUN\clase\ofacturare_comun.vcx / .vct · M COMUN\clase\omodificari.vcx / .VCT
|
> M COMUN\clase\ofacturare_comun.vcx / .vct · M COMUN\clase\omodificari.vcx / .VCT
|
||||||
@@ -409,14 +495,18 @@ sau separat, dupa.
|
|||||||
## Ce ramane de proiectat
|
## Ce ramane de proiectat
|
||||||
|
|
||||||
**Nu mai e „nimic" — runda 16 a adaugat doua povesti noi, ambele cu cercetare in curs, nu inchise:**
|
**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
|
**Actualizare runda 17: punctele 1 si 4 de mai jos sunt FACUTE. Lista e goala pe partea de proiectare
|
||||||
cerinta in plus pentru S8, de prins in nota lui de executie.
|
— 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` /
|
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.
|
`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.
|
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.
|
4. ~~**Mockup-ul e la v8**~~ — **FACUT in runda 17: e la v9.1**, cu varianta D, randul de jos in doua
|
||||||
Asezarea zonei de jos e insa **decisa** — varianta D, decizia 57 (cu intrebarea de detaliu
|
si motivul discountului in banda de totaluri (deciziile 57 + 66), plus deciziile
|
||||||
redeschisa de decizia 61, vezi mai sus).
|
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:
|
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
|
`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
|
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
|
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
|
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.
|
complet: **in plan**, la „Decizia 63" si la S14.
|
||||||
64. **Asezarea campului de motiv: randul se imparte in trei.** Inchide ultimul punct ramas deschis
|
64. ~~**Asezarea campului de motiv: randul se imparte in trei.**~~ **RASTURNATA DE DECIZIA 66
|
||||||
din decizia 57, reluat de decizia 61: randul de jos are trei sectiuni pe acelasi rand —
|
(runda 17). Nu mai e in vigoare** — se pastreaza doar ca istorie, ca sa se stie ca varianta „a
|
||||||
incasare, alte date, motivul discountului — ~440 px fiecare la 1366 px, strans; D ramane
|
treia sectiune jos" a fost incercata si respinsa dupa ce Marius a vazut-o in mockup. Ce e in
|
||||||
intr-un etaj. Enuntul complet: **in plan**, la „Decizia 64".
|
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.**
|
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,
|
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
|
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
|
implementarii S14: inventarul gridului din `frm_facturi`**, posibil sa existe deja coloane de
|
||||||
audit. Enuntul complet: **in plan**, la „Decizia 65" si la S14.
|
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
|
## Interzis
|
||||||
|
|
||||||
- **Nu se atinge `COMUN\clase\ofacturare_comun.vc2` si nici `COMUN\programe\ofacturare_editare.prg`** —
|
- **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 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
|
- **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.
|
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\`.
|
- 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).
|
- **Nu se incepe implementarea pe cod** — #13 porneste dupa terminarea lui #6 (decizia 30).
|
||||||
|
|
||||||
## Capcane de mediu
|
## 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
|
- **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
|
`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
|
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
|
`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.
|
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
|
**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`
|
`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
|
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.
|
> 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
|
> **`docs\mockup_13_formular_unificat.html` (v8) nu e atins de regula asta** — Marius a cerut-o
|
||||||
> numai pentru mockup-ul asezarii.
|
> 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
|
4. ~~**Golul de la `IN_STOC` in S8**~~ — **FACUT in runda 17.** E in plan, in nota de executie a lui S8
|
||||||
scris documentul. De prins **acum**, in nota de executie a lui S8, nu la S12.
|
(caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17"), cu criteriul de test in „gata cand" si cu
|
||||||
5. **Mockup v9** — v8 a ramas cu patru runde in urma. Nici variantele rundei 14, nici **varianta D a
|
consecinta 1 de la S10 marcata **PRELUAT**. **Nu se reia.** Ce a ramas in urma lui **nu e o sarcina
|
||||||
rundei 15 nu sunt integrate in el** — traiesc doar in artifactul de la punctul 3, iar v8 n-a fost
|
de executie, e o alegere de facut la proiectarea lui S8**: valoarea istorica a lui `IN_STOC` nu e
|
||||||
atins. Cand se face v9, D e asezarea de pornit.
|
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
|
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
|
sarcina la pornirea implementarii e **spargerea planului pe stories** si scrierea notei de executie
|
||||||
pentru prima dintre ele (decizia 55).
|
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, 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}
|
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>
|
</style>
|
||||||
|
|
||||||
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
<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">
|
<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>
|
<h1>Un singur formular de facturare</h1>
|
||||||
<p class="lede">
|
<p class="lede">
|
||||||
Datele documentului si articolele in aceeasi fereastra, fara incarcarea prealabila a tuturor
|
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
|
Mockup, nu implementare. Campurile, etichetele si coloanele sunt cele reale, verificate pe cod la
|
||||||
09.08.2026 — rapoartele sunt in <code>docs\cercetare\</code>.
|
09.08.2026 — rapoartele sunt in <code>docs\cercetare\</code>.
|
||||||
Plan: <code>docs\plan_13_unificare_formular_facturare.md</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>
|
</p>
|
||||||
|
|
||||||
<h2><span class="n">1</span>Formularul</h2>
|
<h2><span class="n">1</span>Formularul</h2>
|
||||||
<p class="sub">
|
<p class="sub">
|
||||||
Deschis pe o factura in lei, emisa pe baza de comanda. Antetul e blocat — butonul din bara lui
|
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
|
arata creionul. Asezarea e varianta D: antetul strans pe doua randuri, fara panou separat de
|
||||||
goale.
|
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>
|
</p>
|
||||||
|
|
||||||
<div class="app">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pb">
|
<div class="pb">
|
||||||
<div class="grp">
|
<div class="row">
|
||||||
<p class="gl">Documentul</p>
|
|
||||||
<div class="f w-md"><label>Tip document</label><div class="inp ro"><span class="combo">FACTURA</span></div></div>
|
<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>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>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>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 class="f w-sm"><label>Scadenta</label><div class="inp ro">06.09.2026</div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grp">
|
<div class="row">
|
||||||
<p class="gl">Client si sursa</p>
|
|
||||||
<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-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>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>
|
<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 class="f w-lg"><label>Gestiune sursa</label><div class="inp ro"><span class="combo">DEPOZIT CENTRAL</span></div></div>
|
||||||
</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>
|
||||||
|
|
||||||
<section class="panel">
|
<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">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>
|
<span class="gb key">F4 — cauta</span><span class="gb key">Ctrl+N / Ctrl+D</span>
|
||||||
</div>
|
</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>
|
</section>
|
||||||
|
|
||||||
<div class="foot">
|
<div class="duo">
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<div class="ph">Discount pe document <span class="callout">4</span></div>
|
<div class="ph">▸ Incasare
|
||||||
<div class="pb">
|
<span class="right"><span class="stlbl">NUMERAR · 5 570,55 lei</span></span>
|
||||||
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<div class="ph">Totaluri</div>
|
<div class="ph">▾ Alte date <span class="callout">2</span></div>
|
||||||
<div class="pb">
|
<div class="pb">
|
||||||
<div class="tot"><span>Total baza</span><b>4 696,00</b></div>
|
<div class="row">
|
||||||
<div class="tot"><span>Discount articole</span><b>84,00</b></div>
|
<div class="f w-sm"><label>Venit/Chelt.</label><div class="inp ro">701</div></div>
|
||||||
<div class="tot"><span>TVA</span><b>986,16</b></div>
|
<div class="f w-sm"><label>Sectie</label><div class="inp ro">—</div></div>
|
||||||
<div class="tot grand"><span>Total factura</span><b>5 682,16 lei</b></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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</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
|
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:
|
valuta (sectiunea 4). <b>Cat anume deschide</b>, si de ce nu chiar tot in prima etapa:
|
||||||
sectiunea 2.</p></div></div>
|
sectiunea 2.</p></div></div>
|
||||||
<div class="leg"><span class="callout">2</span><div><h4>Restul, pliat — doua comutatoare</h4>
|
<div class="leg"><span class="callout">2</span><div><h4>Randul de jos, doua sectiuni</h4>
|
||||||
<p>Analiticele, delegatul/transportul, adresa de facturare si textul aditional sub un comutator;
|
<p>Incasarea si alte date (analitice, delegat/transport, adresa de facturare, text aditional)
|
||||||
<b>incasarea</b> sub altul, separat (decizia 41) — e singura cu efecte laterale reale
|
stau colapsate, una langa alta, nu una sub alta (decizia 57). Sunt independente — se poate tine
|
||||||
(alocare/dezalocare de numere) si singura blocata pe document deja emis (decizia 25), deci garda
|
deschisa doar una — iar randul inchis arata rezumatul continutului, nu doar titlul: cand
|
||||||
de non-alocare la toggle se scrie si se testeaza intr-un singur loc. Pe un document <b>deja
|
incasarea e completata arata suma si modalitatea, cand nu e nimic completat rezumatul spune asta.
|
||||||
emis</b>, analiticele se vad dar nu se editeaza de aici — sectiunea 2.</p></div></div>
|
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>
|
<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.
|
<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>
|
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>
|
<div class="leg"><span class="callout">4</span><div><h4>Banda de totaluri, lipita de grid</h4>
|
||||||
<p>Amandoua exista deja azi, dar intr-un dialog separat pe fiecare articol. Aici sunt doua
|
<p>Baza, discount articole, discount document si TVA, desfacute pe un singur rand, cu totalul
|
||||||
coloane in grid; tastezi in oricare, cealalta se calculeaza — sectiunea 5.</p></div></div>
|
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>
|
</div>
|
||||||
|
|
||||||
<h2><span class="n">2</span>Modificarea antetului, fara sa treci prin articole</h2>
|
<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
|
daca e transmis in eFactura si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica
|
||||||
totalul.
|
totalul.
|
||||||
</p>
|
</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>
|
<h2><span class="n">6</span>De unde se intra in formular</h2>
|
||||||
<p class="sub">
|
<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>
|
filtreaza <code>STERS = 0</code>, deci trebuie citita inainte de stergere.</li>
|
||||||
<li><span class="pill check">de verificat</span>
|
<li><span class="pill check">de verificat</span>
|
||||||
<b>Stergerea si reemiterea in aceeasi tranzactie</b>, fara commit intre ele.</li>
|
<b>Stergerea si reemiterea in aceeasi tranzactie</b>, fara commit intre ele.</li>
|
||||||
<li><span class="pill check">de verificat</span>
|
<li><span class="pill have">decis</span>
|
||||||
<b>Pretul nu se re-deriva</b> la reemitere, pe ramurile unde serverul il recalculeaza din
|
<b>La reemitere se scriu valorile din formular, nu se reciteste sursa (decizia 54).</b> Intrebarea
|
||||||
documentul sursa.</li>
|
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>
|
<li><span class="pill check">de verificat</span>
|
||||||
<b>Descarcarea de gestiune pe proforma</b>, pe partea Oracle.</li>
|
<b>Descarcarea de gestiune pe proforma</b>, pe partea Oracle.</li>
|
||||||
<li><span class="pill check">de verificat</span>
|
<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 —
|
**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
|
**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`,
|
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
|
fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei
|
||||||
facturi vechi deja trimise, vezi decizia 62.
|
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
|
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**
|
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
|
(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
|
materializat: Marius vrea campul. Asezarea lui s-a inchis definitiv abia in runda 17 — decizia 64
|
||||||
in trei** — incasare, alte date si motivul discountului, pe acelasi rand (varianta grea, cota +
|
(a treia sectiune jos) a fost **rasturnata de decizia 66: motivul sta in banda de totaluri, langa
|
||||||
explicatie proprii, ramane exclusa, ca mai sus).
|
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**
|
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
|
— **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`
|
din `do_copiaza`, `cursor_retur_document` (`GESTIONABIL = B.IN_STOC`), si `scrie_corespondente_vanzari`
|
||||||
insasi.
|
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
|
1. **`TIP = 4`** — liber azi, dar alocarea e **ireversibila in date** odata intrata in productie. De
|
||||||
confirmat explicit, nu tacit.
|
confirmat explicit, nu tacit.
|
||||||
2. **Apel pe starea de sesiune a pachetului (`clistaid` / `nid_vanzare`) vs. procedura noua cu
|
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",
|
**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
|
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
|
repartizare s-a decis — decizia 59, runda 16: proportional pe cote.** Ramane deschis doar campul
|
||||||
text optional de motiv.
|
text optional de motiv — **inchis si el: decizia 61** (da, se adauga), cu asezarea fixata de
|
||||||
*Alegerea variantei de asezare, listata aici ca a treia, s-a inchis intre timp — decizia 57.*
|
**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
|
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
|
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
|
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
|
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
|
(**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:
|
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 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv. Asezarea
|
||||||
**decizia 64 (runda 16) a inchis si asezarea: randul se imparte in trei**, ~440 px fiecare la
|
lui a oscilat o data: decizia 64 il facea a treia sectiune jos, **decizia 66 (runda 17) o
|
||||||
1366 px, strans. D ramane intr-un etaj.
|
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,
|
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
|
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 /
|
- 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.
|
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
|
**Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57. INCHISA DEFINITIV
|
||||||
decizia 64 (runda 16): randul de jos se imparte in trei** (~440 px fiecare la 1366 px, strans);
|
PRIN DECIZIA 66 (runda 17): campul sta in banda de totaluri, langa discount.** Decizia 64, care il
|
||||||
varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate in
|
facea a treia sectiune jos, **e rasturnata** — randul de jos ramane cu doua sectiuni, ca la decizia
|
||||||
acest sens.
|
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
|
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
|
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
|
**Poveste noua, proiectata: S14** — verdict complet, recomandare si punctele ramase de decis cu
|
||||||
Marius acolo.
|
Marius acolo.
|
||||||
|
|
||||||
64. **Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.** Formularea lui: *„campul de motiv
|
64. ~~**Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.**~~ **RASTURNATA DE DECIZIA 66
|
||||||
imparte randul in 3"*. **Inchide** ultimul punct ramas deschis din decizia 57 (varianta D de
|
(runda 17) — NU MAI E IN VIGOARE.** Formularea de atunci: *„campul de motiv imparte randul in 3"*;
|
||||||
asezare), reluat de decizia 61: randul de jos al formularului unificat are **trei sectiuni pe
|
randul de jos ar fi avut trei sectiuni (incasare, alte date, motivul discountului), ~440 px
|
||||||
acelasi rand** — incasare, alte date si motivul discountului — nu coboara pe rand propriu, nu
|
fiecare la 1366 px.
|
||||||
devine D cu doua etaje. La 1366 px inseamna ~440 px de sectiune, strans, si asumat ca atare.
|
|
||||||
|
**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.**
|
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
|
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 —
|
**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.
|
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
|
#### S6 — Test pe fluxul real, formularul unificat
|
||||||
Headless (`COMUN\docs\depanare_testare_vfp.md`) + UI (`COMUN\docs\testare-ui-vfp.md`), cate un caz
|
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
|
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
|
> `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`,
|
> 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
|
> `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
|
> il inchide** — cele doua se unifica, nu se aleg. Partea de tipuri 48/49 din intrebarea 7 **s-a
|
||||||
> tipuri 48/49.
|
> 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.**
|
> **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.
|
> 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`),
|
- **Comparatia se face pe valori rotunjite la precizia de scriere** (`gnPc` / `gnPPretV` / `gnPCant`),
|
||||||
nu pe valoarea binara — altfel rotunjirea singura produce diferente.
|
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
|
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
|
in formularul unificat. Se accepta pierderea ei la retragere, sau `do_modifica` ramane activ separat
|
||||||
exact pentru cazul asta?
|
exact pentru cazul asta?
|
||||||
@@ -3976,7 +4020,7 @@ nu-i gasise.**
|
|||||||
- **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau
|
- **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau
|
||||||
rescrise automat.
|
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** —
|
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
|
`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
|
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
|
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.
|
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
|
Ultima actualizare: **19.08.2026**.
|
||||||
comise.** Ramane **S8** — si nu ca lipsa de acoperire, ci cu doua esecuri reale.
|
|
||||||
|
> **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
|
**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
|
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
|
1. **Stergerea celor sase documente parazite** (`1051`, `1056`, `1057`, `1058`, `1059`, `1060`) prin
|
||||||
aplicatie — ramasa din blocul de creare documente pentru S8.
|
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,
|
2. ~~S8 — doua esecuri reale.~~ **INFIRMAT pe 12.08.2026. S8 e gata, n-a fost niciodata un esec** —
|
||||||
rulajele nu se refac pe nota noua (`Reccount(trul)=0`): `test_s8_matrice_surse.prg` da
|
vezi sectiunea „S8 INCHIS" de mai jos. Nu relua investigatia.
|
||||||
**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.
|
|
||||||
3. **Rebuild `roafacturare.exe`** din IDE si verificarea pe ecran, pe `1140895` / `1140921` — ambele
|
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.
|
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
|
4. **Punctul B** din `docs\rec_s4b_etapa2.md` — **CONFIRMAT 12.08.2026**, ramane cum e.
|
||||||
confirmarea lui Marius. Nu sunt lucru ramas.
|
**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.
|
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,
|
**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
|
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
|
**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
|
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**,
|
`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:**
|
**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
|
`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.
|
`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
|
**Starea la incheierea sesiunii din 13.08.2026 — verificat, nu presupus**: zero procese `vfp9.exe`;
|
||||||
Oracle deschise (in acest bloc nu s-a atins Oracle), niciun fisier binar editat fara write-back,
|
pe Oracle numai `SELECT` sub `SET TRANSACTION READ ONLY` incheiat cu `ROLLBACK`, zero tranzactii
|
||||||
ambii arbori git curati fata de commituri.
|
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
|
## 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
|
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**.
|
`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**,
|
**Matricea propriu-zisa e RULATA** — fiecare din cele patru documente editat de doua ori, o data din
|
||||||
o data din ROAFACTURARE si o data din registrul jurnal ROACONT, cu verificarile din
|
ROAFACTURARE si o data din registrul jurnal ROACONT, cu verificarile din
|
||||||
`plan_06_editare_factura.md:282-291`.
|
`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)
|
## Firele rundei (10.08.2026, dupa-amiaza)
|
||||||
- `s8-creare-exec` — **executia** planului: creeaza avizul, factura din aviz si factura din contract in
|
- `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
|
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
|
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`
|
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
|
(2574-2733), **alta metoda** — deci nu infirma constatarea. Reconfirmat pe 12.08.2026 cu
|
||||||
(deja documentat); (2) garda eFactura se muta in `PregatesteArticoleFacturaEditare`, deci se aplica
|
`vfp_symbols.ps1 -Where`: in tot `comun.vc2` functia apare **o singura data**, la `:2620`, deci
|
||||||
doar cand documentul chiar e factura de vanzare, fara sa atinga notele obisnuite; (3) se dubleaza
|
`do_modifica` (2222-2572) n-o are deloc.
|
||||||
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.
|
**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
|
## #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.
|
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.
|
- **`InputMask` cu literal se ghilimeleaza** (`"9.99"`, nu `9.99`) — altfel VFP il ia numeric.
|
||||||
Fidelity-check-ul **nu** semnaleaza asta separat.
|
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
|
**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.
|
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)
|
## Defect preexistent gasit pe drum — REPARAT (aprobat separat, 11.08.2026)
|
||||||
|
|
||||||
`omodificari.vc2:16495`, in `frm_modific2024.pgfArticole.PAGE3.cmdAdaugaArticol.Click`:
|
`omodificari.vc2:16495`, in `frm_modific2024.pgfArticole.PAGE3.cmdAdaugaArticol.Click`:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ _LegalTrademark = ""
|
|||||||
_ProductName = "ROA - Facturare"
|
_ProductName = "ROA - Facturare"
|
||||||
_MajorVer = "2"
|
_MajorVer = "2"
|
||||||
_MinorVer = "11"
|
_MinorVer = "11"
|
||||||
_Revision = "14"
|
_Revision = "15"
|
||||||
_LanguageID = "Romana"
|
_LanguageID = "Romana"
|
||||||
_AutoIncrement = "0"
|
_AutoIncrement = "0"
|
||||||
*</DevInfo>
|
*</DevInfo>
|
||||||
@@ -101,7 +101,7 @@ WITH loProject.FILES
|
|||||||
.ADD('comun\clase\ofacturare_rapoarte.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\ofacturare_rapoarte.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\ofundal.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\ofundal.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\ointroduceri.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\ointroduceri.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\omodificari.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="0" User="" />
|
.ADD('comun\clase\omodificari.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\onom_curs.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\onom_curs.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\onomenclatoare.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\onomenclatoare.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\clase\onomenclatoare2.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\clase\onomenclatoare2.vcx') && *< FileMetadata: Type="V" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
@@ -296,6 +296,7 @@ WITH loProject.FILES
|
|||||||
.ADD('comun\programe\oexport.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\oexport.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\programe\ofacturare.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\ofacturare.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\programe\ofacturare_comun.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\ofacturare_comun.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
|
.ADD('comun\programe\ofacturare_editare.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\programe\ofacturare_stoc.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\ofacturare_stoc.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\programe\oheader.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\oheader.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
.ADD('comun\programe\oinainte_de.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
.ADD('comun\programe\oinainte_de.prg') && *< FileMetadata: Type="P" Cpid="1252" Timestamp="0" ID="0" ObjRev="544" User="" />
|
||||||
|
|||||||
Reference in New Issue
Block a user