Files
roafacturare/docs/plan_06_s4_proiectare.md
Marius Mutu d9f5ca4226 docs: planurile, proiectarile si rapoartele de lucru intra in versionare
Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git,
nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de
cercetare pe care se sprijina modificarile din cod. O stergere acolo era
definitiva.

Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au
fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele
fisierelor de test la care se refereau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
2026-08-11 22:17:17 +03:00

625 lines
44 KiB
Markdown

# Proiectare S4/S4b — pagina de articole factura in frm_modific2024
Cercetare + proiectare pentru `plan_06_editare_factura.md`, story S4 (pagina noua de articole) si
S4b (helpere/totaluri/verificari). NU e implementare — propunere supusa aprobarii lui Marius.
Stare de plecare (verificata, nu presupusa): S1-S3 sunt deja **implementate si testate**
(`docs\progres.md`, sectiunea "#6, runda 1"). Actiunea `frm_facturi.do_editare_factura`
(`COMUN\clase\ofacturare_comun.vc2:3727-3869`) si `afisjurcom.do_modifica`
(`COMUN\clase\comun.vc2:2222-2563`) sunt cele doua puncte de intrare reale, ambele deschid deja
`frm_modific2024` pe cursoarele `tact`/`trul`/`trul_obinv`. Tot ce descrie documentul de fata se
adauga **peste** acest cod existent, fara sa-l modifice decat unde e explicit spus (D, E).
## Deciziile lui Marius pe intrebarile din F (08.08.2026) — au prioritate fata de recomandarile din text
- **F.1 — respinsa recomandarea "strict facturi".** Pagina se aplica pe **orice rand din `VANZARI`**,
deci **si pe avize**. Tot ce spune A.3 despre restrangerea la lista de `TIP` de facturi se
reciteste in cheia asta.
- **F.2 — premisa din C.1 e gresita.** Contul nu e `4111` peste tot: **avizele folosesc `418`**. In
plus, nota contine **randuri de discount** si poate contine **note adaugate manual de utilizator**.
Deci filtrul fix `SCD='4111'` nu e o regula, ci o potrivire pe un singur document. Regula corecta
se stabileste in `docs\cercetare\rec_suma_act.md` (cercetare in curs), si **C.1 se rescrie dupa
ea**. Pana atunci, nu implementa indicatorul pe formula din C.1.
- **F.3 — raspuns de la Marius**: in `RUL` pot fi **si linii cu diferente de pret**, cand pretul de
vanzare din factura difera de cel din stoc — **doar pentru marfa tinuta la pret de vanzare**.
Deci **suma bruta din `RUL` nu e comparabila prin constructie** cu totalul documentului (pe
`cod=1140888`: 10 randuri `RUL` pentru 4 linii, 4476.28 fata de 1924.59). O bara de totaluri care
le compara direct ar semnala "desincronizat" permanent pe orice document cu marfa la pret de
vanzare. Subsetul comparabil se stabileste in `docs\cercetare\rec_suma_act.md`.
- **F.4 — varianta A** (bara de totaluri sub grid, permanent vizibila).
- **F.5 — alegere globala** a directiei de sincronizare, pe document, nu per linie.
- Separat, din decizia 18: **liniile din seturi se trateaza ca orice alta linie**.
- **Transfer si custodie** (23, 25, 30, 41, 27, 42, 47): pagina **apare**, dar **fara bara de
totaluri** — nu bara goala cu mesaj, ci fara ea. Pe aceste tipuri nu exista suma comparabila
(transferurile merg pe cont de stoc, custodia nu genereaza randuri `ACT` per articol).
- **Tipul 51 (ROAACNPRO) foloseste `4111`** — deci divergenta gasita in cercetare are alta cauza
decat contul; prima suspiciune e filtrarea pe `cod` fara `an`+`luna`.
- **Comparatia stricta e imposibila prin constructie**: `ACT` nu marcheaza originea randului, deci
un rand adaugat manual nu se distinge de unul generat. Bara de totaluri **arata cifrele si
diferenta, fara verdict automat de eroare**.
- **Garda pe `id_set` se scoate de tot** (decizia 24) — premisa ei a picat.
- **Documente mixte** (decizia 25): suma `RUL` se corecteaza cu valoarea liniilor nestocate din
`VANZARI_DETALII`, marcata in bara ca ajustata.
- **`RUL` are formula comparabila** (nu mai e doar informativ):
`SUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ <> 3)`. **C.1 se rescrie** dupa
`docs\cercetare\rec_suma_act.md`, care are si propunerile concrete de corectie la final.
Ipoteza lui Marius de verificat, cu efect asupra codului deja scris: `id_set + 5` la discount ar fi
un **marcaj tranzitoriu** consumat inainte de `ACT`, nu o valoare persistata — caz in care garda pe
`id_set` din `do_editare_factura` (runda 3) e construita pe o premisa falsa.
## A. Ce se poate face concret pe `frm_modific2024`
### A.1 Structura curenta a `pgfArticole` si a celor doua pagini de rulaje
Clasa `frm_modific2024` incepe la `COMUN\clase\omodificari.vc2:6375`. Pageframe-ul:
- **Nu are `PageCount` propriu setat** in `ADD OBJECT 'pgfArticole'...` (`:8627-8641`) — mosteneste
`PageCount = 2` din clasa de baza `_pageframe` (`COMUN\clase\_baza.vc2:496`). Cele doua pagini sunt
configurate doar prin proprietatile `PAGE1.Caption/ForeColor/Name` si `PAGE2.Caption/ForeColor/Name`
in acelasi bloc `ADD OBJECT`.
- `PAGE1` contine `_grdfooter1` (`:8644-8655`, footer cu sume pe coloane, `csumcolumns=...`) si
`grdRulaje` (`:8657-...`, `ColumnCount=63`, `RecordSource="trul"`, `ReadOnly=.F.`).
- `PAGE2` are aceeasi structura pe `trul_obinv`/`grdRulajeObinv` (confirmat prin indexul de metode:
`pgfArticole.PAGE2.grdRulajeObinv.*`, `omodificari.vc2:15159-15424`), nu am recitit blocul `ADD
OBJECT` complet (nu era necesar — tiparul e identic cu PAGE1, doar alt cursor).
- **`Init`** (`:13551-13660`): primeste `Lparameters tnIdSet, tlNotaNoua, toBackupXML, toSet,
tlVizualizare`; seteaza `This.nid_set`, `lEditare`/`lVizualizare`/`lModificare`/`lVerificare`. Zona
`:13610-13643` ascunde `but_nou1`/`but_sterge1` si face `grid1` readonly pe seturi speciale
(id_set 25000-25099 sau o lista fixa) — logica veche, fara legatura cu facturile de vanzare (acele
id_set-uri sunt pentru note "cu scadere", nu pentru pagina noua).
- **`Activate`** (`:12238-12243`): la prima activare cheama `resize_grid1()`.
- `resize_grid1()` (`:13680-13688`) si `afiseaza_rulaje()` (`:12245-12281`) folosesc deja
`this.pgfArticole.Visible` ca switch — **dar e un toggle manual, comandat de utilizator** (butonul
`but_afiseaza_rulaje`, colapseaza/expandeaza TOT pageframe-ul ca sa faca loc pentru `grid1`), nu o
decizie "documentul e de tip X". Nu e mecanismul de folosit pentru afisarea conditionata a PAGE3.
- **`Show`** (`:13719-13775`) e unde se leaga efectiv footerele de sume la grid-uri:
`this.pgfArticole.page1._grdfooter1.attachtogrid(...)` +`.calcTotal()`, identic pentru PAGE2
(`:13748-13752`) si unde se ascunde automat tot pageframe-ul cand nu exista deloc rulaje
(`:13764-13770`, `If Reccount('trul')=0 And Reccount('trul_obinv')=0 Then This.afiseaza_rulaje()`).
### A.2 Mecanica adaugarii PAGE3
`PageCount` nu e o proprietate care se citeste o singura data — poate fi modificata la runtime, si
exact acest tipar exista deja in codebase, la un alt pageframe: `comun.vc2:10521-10531`
(`frm_...Init`) face `THISFORM.pgcod.PageCount = n+1` + `.Pages(n+1).Caption = 'Gestiuni'` cand
conditia e adevarata, altfel `PageCount = n` (pagina ramane ascunsa, pentru ca nu exista in
colectia activa de pagini). **Acesta e mecanismul recomandat pentru PAGE3**, nu `.Visible` pe
pagina (VFP pageframe `Page` are `Visible`, dar codebase-ul nu-l foloseste nicaieri pentru
show/hide conditionat — a fost cautat explicit, 0 rezultate `Page.*Visible` in `COMUN\clase`).
Propunere concreta:
- In definitia clasei, `ADD OBJECT 'pgfArticole'` capata `PageCount = 3` **explicit** (nu mai
ramane pe default-ul 2) + `PAGE3.Caption = "Articole factura"`, `PAGE3.Name = "PAGE3"`, plus
grid-ul nou `pgfArticole.PAGE3.grdArticoleFactura` (dupa modelul `grdRulaje`) si un
`_grdfooter` propriu (`pgfArticole.PAGE3._grdfooter1`), la fel ca PAGE1/PAGE2.
- La runtime, in `Show` (langa blocul `:13764-13770`, dupa acelasi principiu): daca documentul
curent **nu** e o factura de vanzare (vezi A.3), `This.pgfArticole.PageCount = 2` — PAGE3 dispare
din colectia activa de pagini, comportament identic cu azi pentru orice alt tip de nota. Cand
**este** factura, `PageCount` ramane `3` (valoarea din clasa) si se leaga footerul nou:
`this.pgfArticole.page3._grdfooter1.attachtogrid(...)` + `.calcTotal()`, exact ca la PAGE1/PAGE2.
- Consecinta directa: pentru **orice document care nu e factura** (99.9% din utilizarile din
ROAGEST/ROACONT), `PageCount` scade inapoi la 2 la fiecare deschidere — clasa arata **byte-cu-byte
ca azi**, nicio schimbare vizuala sau de comportament. Asta e conditia ceruta explicit in plan
(S4: "pagina se afiseaza doar cand documentul curent are rand in vanzari... altfel formularul
arata exact ca azi") si raspunde direct la riscul cel mai mare listat in plan.
### A.3 Cum decide formularul ca documentul curent are randuri in `VANZARI`
**Comparatia celor trei variante**, asa cum a cerut misiunea. Concluzia (varianta 3) nu se schimba
fata de versiunea anterioara a acestei sectiuni — se schimba doar **filtrul** aplicat dupa ea, cf.
`[DECIS — decizia 19 din progres.md]` mai jos:
1. **Test pe `id_set`** — respins. Intervalele facturilor si ale avizelor din
`COMUN\docs\tipuri_documente_facturare.md` se suprapun aproape complet (facturi:
25000-25009, 25042-25048, 25051, 50100 + variantele "cu scadere" +10; avize: 25020-25029,
25040-25041, 25046 + variantele +10 — ambele in acelasi interval brut 25000-25099). Un test de
forma `Between(tnIdSet,25000,25099)` (deja folosit in `Init`, `:13610-13612`, dar pentru alt
scop) ar prinde si avizele, nu doar facturile. Documentul insusi semnaleaza o coliziune
nerezolvata pe `25051` (punctul 3 din capcanele acelui fisier) — nu e o baza solida pentru o
decizie care controleaza afisarea/ascunderea unei pagini cu bani.
2. **Flag pasat de apelant** — respins ca mecanism principal. `afisjurcom.do_modifica`
(ROACONT/ROAGEST, registrul jurnal) e cod generic pentru **orice** tip de nota; azi nu stie si
nu are motiv sa stie ca documentul curent are randuri in `VANZARI` (asta a fost motivul pentru
care garda eFactura si cea de referinte au trebuit extrase in `COMUN\programe\` la S1, nu
lasate in `frm_facturi`). A cere unui al doilea apelant sa afle si sa transmita acest flag ar
duplica exact interogarea pe care oricum trebuie sa o facem undeva, cu riscul ca un al treilea
apelant viitor sa uite s-o transmita corect.
3. **`SELECT` in `vanzari` dupa `cod`** — recomandat. `tact` (cursorul pe care se leaga deja
formularul, incarcat de `IncarcaCursoareModificareNota`,
`COMUN\programe\ofacturare_editare.prg:27-140`) contine coloana `cod` din `vact_tot` (filtrul
`WHERE ... cod = tnCod` de la `:53` confirma coloana). `tact.cod` se poate citi **oricare ar fi
workarea curenta** (referinta calificata pe alias), deci **nu conteaza care apelant a deschis
formularul** — informatia necesara e deja in cursorul pe care oricum formularul il primeste
prin contract. Un singur `SELECT tip, id_vanzare FROM vanzari WHERE cod = <tact.cod>` (rulat o
singura data, la deschidere) da simultan raspunsul la "are rand in `vanzari`?" si, daca da,
`tip`-ul exact — necesar in Runda 4 pentru tratamentul special al transferului/custodiei
(decizia 22, mai jos).
**Recomandare**: detectia se face **in interiorul `frm_modific2024`** (nu in apelanti), intr-o
metoda noua apelata din `Init` sau din `Show` (inaintea blocului de la A.2), care citeste
`tact.cod`, interogheaza `vanzari` o singura data, si populeaza **trei** proprietati noi pe
formular: `This.lAreArticoleVanzari`, `This.nIdVanzare` si `This.nTipVanzare` (tip-ul documentului
din `vanzari`). **Asta inseamna ca PAGE3 apare automat din ambele puncte de intrare fara nicio
modificare in `do_editare_factura` sau `afisjurcom.do_modifica`** — exact arhitectura ceruta de
plan ("extinderea clasei comune, nu cod apelant nou").
Numele `lAreArticoleVanzari` (nu `lEsteFacturaVanzare`, cum se numea in versiunea anterioara a
acestei sectiuni) e ales deliberat: sub decizia 19 de mai jos flagul devine adevarat si pe avize,
transferuri si custodie, nu doar pe facturi — numele vechi ar fi mintit. `nTipVanzare` se retine
separat, tot din acelasi `SELECT`, pentru ca Runda 4 (decizia 22: transfer/custodie afiseaza
pagina, dar fara bara de totaluri) are nevoie sa stie tipul exact, nu doar "are/nu are randuri".
**`[DECIS — decizia 19 din progres.md]`** Filtrul de mai sus **nu** se restrange la lista de `TIP`
de factura — pagina apare pe **orice rand din `VANZARI`**, deci si pe avize, transfer intre
subunitati, transfer pe lucrare si custodie. Varianta "strict facturi" (recomandarea acestei
sectiuni intr-o versiune anterioara, cand decizia inca nu fusese luata) a fost **respinsa explicit
de Marius**. Consecinte pentru restul lucrarii:
- Garda eFactura/S1 ramane specifica facturilor si **nu se extinde** — pe avize/transfer/custodie
pur si simplu nu se aplica azi, pentru ca acele tipuri de document nu trec pe acolo.
- Scrierea (E, S5) si bara de totaluri (C.1, C.3) raman gatate separat, dupa `nTipVanzare`:
transfer/custodie (decizia 22) primesc pagina, dar fara bara.
- `frm_modific2024` deschis din `afisjurcom.do_modifica` (registrul jurnal ROACONT/ROAGEST) capata
aceeasi pagina pe orice document cu rand in `VANZARI`, nu doar pe facturi — de retinut la testarea
riscului D, care azi verifica doar cazul "document care nu e deloc in `VANZARI`".
### A.4 Lantul `inainte_de_do_termin` — unde intra validarile noi
`omodificari.vc2:13357-13549`. Ce face azi, pe scurt:
- `:13360-13367` reseteaza filtrul pe `tact`, completeaza `id_set` gol pe `tact`/`trul`/`trul_obinv`.
- `:13369-13386` verificare generica de completare cont/analitic/partener
(`verificare_note_contabile('tact',...)`, `oOperatii_comune`), sarita pentru seturile speciale
(`id_set` 99998/90024).
- `:13389-13402` (doar `gnAn >= 2013`): avertisment pe combinatia de conturi `4426-4428`/
`4428-4427` fara sa fi folosit optiunea dedicata, apoi `This.VerificaAvertizareExigibilizareTVA()`
(`:13909-14083`, verificare separata, neatinsa de aceasta propunere).
- `RETURN m.llRet` — daca oricare pas a esuat, formularul nu inchide (butonul Termina ramane
blocat pana la corectare).
**Punctul de agatare pentru validarile noi ale lui S4b**: chiar inainte de `RETURN m.llRet`
(`:13404`), un bloc nou gatat de `This.lAreArticoleVanzari` (A.3) — de exemplu, garda "nu lasa
utilizatorul sa iasa cu `buton=1` daca a marcat sincronizarea ca necesara dar n-a confirmat-o
explicit" (detaliu in C). **Nu inlocuieste nimic din ce exista azi** — se adauga dupa validarile
generice, cu acelasi tipar (`If m.llRet Then ... Endif`).
## B. Cursorul de articole
### B.1 Sursa si momentul incarcarii
Confirmat in `docs\cercetare\rec_cale_vanzari_detalii.md`: scrierea la editare merge **direct** in
`VANZARI_DETALII` (fara `VANZARI_DETALII_TEMP`, care e GTT populata doar la emitere). Simetric,
**citirea** pentru formular trebuie sa vina direct din `VANZARI_DETALII`, nu din vreo tabela temp.
Propunere: o functie noua in `COMUN\programe\ofacturare_editare.prg` (alaturi de
`IncarcaCursoareModificareNota`, acelasi stil de cod — `goExecutor.oExecute`, gestiune de eroare
simetrica), de exemplu:
```foxpro
FUNCTION IncarcaArticoleFactura
LPARAMETERS tnIdVanzare
* incarca header-ul (1 rand, cursor tvanz) si liniile active (cursor tvd) pentru factura data
* tvd/tvanz raman deschise READWRITE - apelantul (frm_modific2024) le foloseste si le inchide
ENDFUNC
```
Apelata **din interiorul `frm_modific2024`** (Init/Show, dupa ce A.3 a stabilit
`This.lAreArticoleVanzari = .T.` si `This.nIdVanzare`), nu din apelanti — acelasi motiv ca la A.3:
zero cod nou in `do_editare_factura`/`afisjurcom.do_modifica` pentru partea de citire.
`tvanz` (1 rand, header): `id_vanzare, cod, discount` (singurul camp editabil din antet, decizia
17 din `progres.md`) + campurile needitabile utile ca referinta vizuala (`total_fara_tva,
total_tva, total_cu_tva` — valorile **vechi**, denormalizate, afisate readonly langa totalurile
live din S4b, nu suprascrise decat de S5 la salvare).
`tvd` (liniile), coloane din `VANZARI_DETALII` (lista completa in
`rec_cale_vanzari_detalii.md`, sectiunea 2.2) — subsetul relevant editarii:
`id_vanzare_det, id_articol, cantitate, pret, pret_cu_tva, proc_tvav, discount_unitar,
id_gestiune, cont, id_valuta, id_jtva_coloana, serie, explicatie, taxcode, lot, sters`.
Filtrul de incarcare: `WHERE id_vanzare = :tnIdVanzare AND sters = 0` (liniile deja sterse nu se
mai arata — simetric cu `tact`, care si el filtreaza `sters=0` la incarcare).
### B.2 Marcaje de stare, fara concept nou fata de restul clasei
Clasa foloseste deja doua idiomuri simple pentru starea liniilor din `tact`, ambele reutilizabile
ca atare pentru `tvd`, fara sa inventam un al treilea:
- **Sters = flag pe randul existent**, nu stergere fizica din cursor — exact cum `tact`/`trul`
reprezinta deja `STERS` ca o coloana obisnuita (rescrisa la scriere, nu `DELETE`d din cursor).
`tvd.STERS` (coloana deja prezenta in `VANZARI_DETALII`) se flipuieste in cursor la actiunea
"sterge linie"; la scriere (D), un rand cu `STERS=1` care avea `STERS=0` la incarcare devine un
`UPDATE ... SET STERS=1`.
- **Adaugat = `id_vanzare_det = 0`** — acelasi sentinel folosit deja in `do_adauga`
(`omodificari.vc2:12685`, `loadd.id_act = 0` pentru randurile noi din `tact`, inainte de
`Append Blank`/`Gather`). La scriere, orice rand din `tvd` cu `id_vanzare_det = 0` e un `INSERT`
(PK-ul real vine automat din `SEQ_VANZARI_DETALII`, confirmat in
`rec_cale_vanzari_detalii.md` sectiunea 1.3/3.2 — nu trebuie generat in VFP).
- **Modificat** — singurul marcaj cu adevarat nou necesar, pentru ca "a fost atins" nu se poate
deduce din `id_vanzare_det`/`STERS`. Propunere: o coloana logica `_modificat` (prefix `_`,
convenabil pentru un camp de lucru care nu exista in tabela reala — verifica totusi ca VFP nu
interpreteaza gresit numele; alternativ `lModificat`), setata `.T.` din handler-ele `Valid`/
`InteractiveChange` ale coloanelor editabile din grid — acelasi tipar folosit deja de clasa pe
`trul` (`pgfArticole.PAGE1.grdRulaje.cCant.Text1.Valid`, `omodificari.vc2:14906-14911`, si
restul handler-elor `Valid`/`When`/`InteractiveChange` din acelasi grid). La scriere, un rand cu
`id_vanzare_det > 0` si `_modificat = .T.` e un `UPDATE`; fara flag, randul nu se atinge (evita
`UPDATE`-uri inutile pe linii doar rasfoite).
Acest model evita complet o alternativa mai grea (snapshot + diff intre cursorul original si cel
curent) care ar fi introdus un concept nou, fara sa aduca vreun beneficiu fata de flag-urile deja
folosite in clasa pentru `tact`.
## C. Helperele si verificarile
### C.1 Ce inseamna "suma comparabila" — regula pe tip de document, verificata pe date reale
Corectie fata de versiunea anterioara a acestei sectiuni (premisa ei — filtru fix `SCD='4111'` — a
fost respinsa explicit de Marius, F.2 mai jos). Cercetarea completa, cu toate interogarile pe cele
trei scheme si sursele exacte pe cod, e in `docs\cercetare\rec_suma_act.md`; ce urmeaza e concluzia
ei. Doua completari ulterioare, verificate separat pe `MARIUSM_AUTO` (08.08.2026), sunt in
`docs\cercetare\rec_cele_41_facturi.md`: derivarea `an`/`luna` si cauza reala a divergentei pe
`cod=1138989`. Nu se reimplementeaza nicio formula fiscala in VFP — recomandarea ramane cea de
dinainte: "suma din `VANZARI_DETALII`" se ia direct din `calculeaza_total_fara_tva_fact`/
`calculeaza_total_tva_fact` (Oracle, aceleasi functii care vor rula la salvarea din S5), niciodata
recalculata in VFP.
**Nu exista un filtru fix de cont pentru "suma din ACT".** Contul de debit al liniei depinde de
**tipul documentului** (`PACK_FACTURARE.pck`, `contabilizeaza_articol` decide contul la
`:7390-7415`):
| Grup de `TIP` | Cont debit (linie) | Comparabil cu `TOTAL_CU_TVA`? |
|---|---|---|
| Facturi (1,2,3,5,7,8,9,10,43,44,45,46,48,49,51,52 fara rata) | `4111` (empiric stabil pe 3 scheme) | DA |
| Factura din aviz (tip 4) | `4111`, discount direct pe el cu semn negativ | DA |
| Vanzare pe rate/contract (2,6,52 cu `id_rata<>0`) | din `NOTE_CONTABILE`, legat de `CONTRACTE` | DA |
| Avize catre clienti debitori (28, 29) | `461` (hardcodat) | DA |
| Restul avizelor (21,22,24,26) | `418` (hardcodat) | DA |
| Transfer intre subunitati (23,25,30,41) | cont de STOC, nu de client | NU |
| Transfer pe lucrare (27) | cont de STOC | NU |
| Custodie (42,47) | — (`descarca_gestiune` nu scrie rand `ACT` per articol) | NU |
| ROAACNPRO (51) | `4111`, confirmat stabil de Marius | cont OK, dar comparatie nesigura — vezi mai jos |
**Filtrul pe `ACT` cere `cod` + `an` + `luna`, niciodata `cod` singur.** Dovada: `cod=1140632` are
un rand de achizitie straina (OCR furnizor) in `an=2026,luna=1` si nota de vanzare reala in
`an=2026,luna=2`, cu **acelasi** `cod` reutilizat intre module. `IncarcaCursoareModificareNota(
tnCod, tnAn, tnLuna, ...)` (`COMUN\programe\ofacturare_editare.prg:27-140`) filtreaza deja corect —
orice interogare noua din S4b trebuie sa foloseasca acelasi tipar de filtru complet, nu doar `cod`.
**`an`/`luna` nu se deriva din `VANZARI.DATA_ACT`** — trebuie luate din contextul notei deja
incarcate, niciodata recalculate din antet. Pe 703 documente `VANZARI` (`MARIUSM_AUTO`,
`docs\cercetare\rec_cele_41_facturi.md`): 542 au nota in luna din `DATA_ACT`, **78 (11%) au nota
intr-o alta luna**, 83 n-au deloc randuri `ACT` pe `cod`. Exemplu: `cod=1138989` are
`VANZARI.DATA_ACT = 01-JAN-19`, dar cele 12 randuri `ACT` sunt in `an=2019, luna=3`
(`dataact=31-MAR-19`) — un filtru `cod + an(data_act) + luna(data_act)` ar intoarce zero randuri.
Pe datele de test cazurile sunt concentrate pe tip=51 (ROAACNPRO, `DATA_ACT` sablon `01-JAN-19`),
deci nu e dovedit tipar general de productie — dar consecinta de implementare e reala: `an`/`luna`
se iau din cursorul `tact`/`actactan` deja incarcat de `IncarcaCursoareModificareNota` (apelata cu
`an`/`luna` explicite), niciodata recalculate din `VANZARI.DATA_ACT`. Pentru cele 78 de documente cu
luna divergenta, `do_editare_factura` raspunde azi "Nu exista nota contabila pentru aceasta
factura" si refuza editarea — comportament sigur, dar de consemnat ca limitare cunoscuta.
**Discountul de document intra NET (debit minus credit), nu ca `SUM(SCD=cont)` simplu.**
`scrie_discount` (`PACK_FACTURARE.pck:12859-13057`) scrie discountul pe sensul OPUS liniei de
vanzare: pe facturi normale (`tip<=20` sau in `(44,45,46,43,48,49,51,52)`) discountul e
`SCD='667'`/`SCC='4111'`, adica **pe credit** fata de contul de client — un `SUM(SUMA) WHERE
SCD='4111'` simplu il ignora complet. Suma corecta e **soldul net**, aceeasi formula pe care
aplicatia insasi o foloseste la auto-verificarea de la emitere (`verifica_total_document`,
`PACK_FACTURARE.pck:16073-16145`):
```sql
SUM(CASE WHEN SCD = :cont THEN SUMA
WHEN SCC = :cont AND SCD NOT IN ('5311','5314','5121','5125','5126') THEN -SUMA
ELSE 0 END)
```
(pe factura din aviz, tip=4, discountul e deja direct pe `4111` cu semn negativ, deci formula neta
da acelasi rezultat ca un `SUM` simplu pe acel tip — nu strica nimic sa se aplice uniform pe toate
tipurile comparabile din tabel.)
**`RUL` are acum o formula comparabila** (nu mai ramane doar informativ):
```
SUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ <> 3)
```
Randurile "in plus" observate initial (10 randuri `RUL` pentru 4 linii, suma bruta 4476.28 in loc
de 1924.59 pe `cod=1140888`) vin din **perechile de diferenta de pret**, generate de
`PACK_FACTURARE.descarca_gestiune` pentru marfa/produse tinute la pret de vanzare
(`V_CONT IN ('371','357') AND V_TIP_GESTIUNE=6`, `:9361-9540`; produse/ambalaje similar,
`:9641-9818`) cand pretul de vanzare inregistrat in stoc difera de cel facturat efectiv
(`V_PRETV_ORIG <> V_PRETV`). Fiecare diferenta scrie o pereche marcata `ID_TIP_RULAJ=3` (`:7745`):
un rand cu `CANTE>0` la pretul VECHI din stoc (de exclus din suma) si unul cu `CANT>0` la pretul
REAL facturat (de inclus). Formula de mai sus, aplicata pe `cod=1140888`, da exact `1924.59` =
`TOTAL_CU_TVA`.
**Documente mixte (decizia 25): liniile nestocate nu au deloc rand `RUL`.**
`descarca_gestiune` sare complet peste articolele cu `NOM_ARTICOLE.IN_STOC=0` (`:7783-7789`) — nu
scrie nimic in `RUL` pentru ele (confirmat pe productie, `cod=1397106`: linia de "SERVICII
TRANSPORT" lipseste integral din `RUL`). Pe orice document cu linii stocate SI nestocate, suma
`RUL` de mai sus **subestimeaza sistematic** documentul cu exact valoarea liniilor nestocate.
Corectie: suma `RUL` se aduna cu suma liniilor `IN_STOC=0` din `VANZARI_DETALII`, iar bara de
totaluri marcheaza explicit ca cifra e **ajustata** (nu doar `RUL` brut).
**Tipuri fara suma comparabila — nu se afiseaza bara, nu se da verdict** (decizia 22): transfer
intre subunitati (23, 25, 30, 41) si transfer pe lucrare (27) merg pe cont de **stoc**, nu de
client; custodia (42, 47) nu scrie **niciun** rand `ACT` per articol. Pe niciunul din aceste tipuri
nu exista ce compara — tratament explicit ("pagina apare, bara nu"), nu o eroare de raportat. Pe
tip=50 (marcat "in lucru" in pachet), acelasi tratament, prin analogie.
**ROAACNPRO (tip 51): cauza divergentei pe `cod=1138989` gasita — nota e DUBLATA, nu e cazul
deciziei 9.** Cele 12 randuri `ACT` (toate in aceeasi `an`/`luna`, deci nu e capcana de filtrare de
mai sus; toate pe `4111`, zero `411`) sunt **doua blocuri de cate 6**, iar al doilea e **exact 2x**
primul, rand cu rand (`docs\cercetare\rec_cele_41_facturi.md`): blocul A insumeaza exact
`TOTAL_CU_TVA = 13895.45` (0.01 lei rotunjire), blocul B e dublul lui, iar suma totala peste toate
cele 12 randuri e `41686.35` = 3x totalul — exact raportul semnalat initial. **Regula pentru suma
din `ACT` se inchide si pe acest document**: nu e o exceptie a regulii, e o nota postata de doua
ori — o anomalie reala de date pe care un indicator corect trebuie s-o semnaleze, nu s-o ascunda.
**Nu e printre cele 41 de facturi de la decizia 9** (#8/S9) — acelea sunt toate `STERS=1`, iar
`cod=1138989` are `STERS=0` si o linie activa; presupunerea anterioara ca ar fi "aceeasi familie"
nu se sustine. Ramane deschis, separat: cele 12 randuri `ACT` explica antetul `VANZARI`, dar linia
unica din `VANZARI_DETALII` (`2468.21`) nu reconciliaza cu niciuna din cele doua cifre — asta chiar
ramane neexplicat, si e un argument in plus pentru caracterul informativ (nu verdict automat) al
comparatiei `ACT` vs `VANZARI_DETALII` de mai jos.
**Comparatia ramane informativa, niciodata verdict automat de eroare.** `ACT` **nu are nicio
coloana care sa marcheze originea randului** (`omodificari.vc2:12653-12701`, `do_adauga`) — un rand
adaugat manual de utilizator (posibil, decizia F.1: pagina apare pe orice document din `VANZARI`)
e indistinctibil, dupa salvare, de unul generat automat la emitere. Indicatorul din S4b ramane deci
**informativ pe subset bine definit**, niciodata sursa unica de adevar pentru un verdict de eroare.
**Gradul de incredere**: regula de mai sus (cont pe tip + filtru `cod+an+luna` + sold net) e
verificata pe **360 de documente, 12 tipuri de document, 3 scheme** (`MARIUSM_AUTO` date de test,
`ROMFAST@ROA_ROMFAST` client real, `VENDING` productie) — **~97.5% potrivire exacta**
(`docs\cercetare\rec_suma_act.md`, sectiunea "Concluzie C"). `cod=1138989` (ROAACNPRO) nu mai e un
caz neexplicat — cauza e gasita (nota dublata, de mai sus) — dar un indicator automat tot ar
semnala divergenta pe el, corect, pentru ca e o anomalie reala de date. Restul cazurilor **raman
deschise, nu ascunse**: transfer/custodie (necomparabile prin design, de mai sus), 5 documente
`ROMFAST` fara nicio nota `ACT` scrisa, si un document `VENDING` in valuta (`cod=1165566`, tip=9)
cu o diferenta de 5454.12 lei neexplorata — niciunul din aceste cazuri nu infirma regula, dar
niciunul nu trebuie prezentat ca "inchis" fara nuanta de mai sus.
### C.2 Ce inseamna "live" vs "la ultima salvare"
Cele trei surse nu pot fi comparate coerent "in timp real" cat timp utilizatorul tasteaza intr-o
celula din grid, pentru ca `ACT`/`RUL`/`VANZARI_DETALII` reala raman la valoarea de dinaintea
editarii curente pana la `Salveaza`. Propunere clara, ca sa nu induca fals sentiment de precizie:
- **Subtotal pe linie, in grid** (cantitate x pret, cu/fara TVA dupa flag) — calcul simplu, live,
in VFP, la fiecare `InteractiveChange`/`Valid` (acelasi tipar ca `calculeaza_valori_rul`,
`omodificari.vc2:12519-12588`, aplicat pe `trul`). Nu implica formula fiscala complexa (fara
discount de document, fara rotunjiri de agregare), deci riscul de divergenta e neglijabil si
local, vizibil imediat de utilizator.
- **Cele trei totaluri de control (RUL / VANZARI_DETALII / ACT)** raman "la ultima stare
persistata" — se recalculeaza (interogare Oracle) la deschiderea paginii si dupa fiecare
`Salveaza` reusit, **nu** la fiecare tasta. Rolul lor real (asa cum reiese din problema descrisa
de Marius) e sa arate ca un document editat anterior a ramas nesincronizat, nu sa dea un preview
in timp real al editarii curente — editarea curenta oricum nu poate fi "corecta" pana nu trece
prin acelasi calcul Oracle care va rula la salvare.
### C.3 Indicatorul de stare a sincronizarii
Un label vizibil pe PAGE3 (langa footerul de totaluri), 3 stari: **verde/OK** (toate sumele
aplicabile coincid, in limita rotunjirii de 2 zecimale — pe tipurile fara suma comparabila,
transfer/custodie, bara nici nu apare, cf. C.1), **galben/atentie** (`VANZARI_DETALII` si `ACT`
coincid, dar cifra din `RUL` e ajustata pentru linii nestocate sau documentul e de un tip cu
comparatie nesigura, ex. ROAACNPRO — cf. C.1), **rosu/desincronizat** (`VANZARI_DETALII` si `ACT`
NU coincid — semnalul real ca ceva nu s-a propagat corect, singurul caz in care indicatorul trebuie
sa opreasca vizual atentia utilizatorului). Recalculat la deschidere si dupa fiecare salvare
reusita (C.2).
### C.4 Actiunea explicita de sincronizare
Cerinta lui Marius: directia (rulaj->articol sau articol->rulaj) o alege utilizatorul, niciodata
implicit. Propunere de flux (schita UI in F, intrebarea de UX):
1. Buton "Verifica sincronizarea" (sau automat la deschidere, doar afisare) — ruleaza C.1/C.3,
populeaza un cursor de diferente `tvd_diff` (id_articol, cantitate_rul, cantitate_vd,
pret_rul, pret_vd, ...) doar pentru randurile unde difera.
2. Daca exista diferente, buton "Propune sincronizare" deschide un dialog/grid cu liniile afectate
si valorile vechi/noi, **pe ambele directii posibile** (utilizatorul alege per-sesiune care
parte e sursa — RUL sau VANZARI_DETALII —, nu per-linie individual, ca sa evite o combinatie
inconsistenta).
3. Confirmarea aplica modificarile **doar in cursorul in memorie** (`tvd` sau `trul`, dupa
directie) — nu scrie nimic in Oracle pana la `Termina`/`Salveaza`. Consistent cu principiul
"nimic nu se aplica silentios si nimic nu se declanseaza automat la `do_termin`" din plan.
4. Refuzul propunerii nu modifica nimic — utilizatorul poate corecta manual, linie cu linie, in
oricare din cele doua griduri.
### C.5 Linii adaugate/sterse fara corespondent in RUL
O linie noua in `tvd` (id_vanzare_det=0) nu are, prin definitie, niciun rand `RUL` corespunzator —
nu exista "vechi" de comparat. Propunere: astfel de linii sunt automat excluse din comparatia
C.1/C.3 (nu pot fi "desincronizate", pentru ca n-au fost niciodata sincronizate) si marcate separat
in UI ("linie noua, fara rulaj — se creeaza la salvare" / "linie stearsa"). Simetric pentru liniile
sterse (`STERS=1` in `tvd`): nu mai intra in suma "curenta" din C.2, dar raman vizibile (tacuate/
strikethrough) pana la salvare, ca utilizatorul sa vada ce a marcat pentru stergere inainte sa
confirme.
### C.6 Ce NU se poate verifica automat
- Corelatia RUL <-> VANZARI_DETALII **linie-cu-linie** (nu agregat) — ramane nesigura: formula din
C.1 e verificata ca sumă pe tot documentul, nu mapeaza un rand `RUL` anume pe o linie anume din
`VANZARI_DETALII`. Suma agregata (C.1) are acum formula verificata; ce nu se poate face e
legatura 1-la-1 intre randuri.
- Corectitudinea contabila a notei dupa editare (echilibrul debit=credit, alegerea corecta a
conturilor) — ramane acoperita de validarile generice deja existente in
`inainte_de_do_termin` (A.4), care nu se ating.
- Impactul asupra eFactura/SAFT dupa editare — in afara scopului #6 (garda S1 blocheaza deja
editarea facturilor trimise in eFactura).
## D. Riscul asupra registrului jurnal
`omodificari.vc2` e in COMUN si serveste si `afisjurcom`/registrul jurnal ROACONT/ROAGEST. Ce
poate regresa, concret, si cum se limiteaza:
1. **`PageCount` schimbat global pe clasa** — daca noul `PageCount=3` din definitia clasei nu e
readus la 2 la runtime pentru non-facturi (A.2), PAGE3 ar aparea (goala sau cu date gresite) pe
orice nota din ROACONT/ROAGEST. Mitigare: testul din A.2 ruleaza necontional in `Show`, inaintea
oricarei afisari, si defaultul din clasa e explicit "1 pas de siguranta" (daca testul crapa/nu
ruleaza, `PageCount` ramane 3 din clasa — deci testul TREBUIE sa aiba un `Catch`/`else` care
forteaza `PageCount=2`, nu invers). **De verificat explicit la implementare**: comportamentul pe
eroare al noii interogari Oracle (A.3) trebuie sa fie "ascunde PAGE3", nu "las-o vizibila".
2. **Interogarea noua din A.3 (`SELECT ... FROM vanzari WHERE cod=...`) ruleaza la FIECARE
deschidere a formularului**, inclusiv pentru note care n-au nicio legatura cu facturarea — cost
suplimentar mic (un SELECT indexat pe `cod`), dar **trebuie sa fie garantat sa nu blocheze
deschiderea** pe eroare de retea/Oracle. Mitigare: acelasi tipar defensiv ca
`IncarcaCursoareModificareNota` (`ofacturare_editare.prg:58-61`) — pe eroare, se comporta ca
"nu e factura" (ascunde PAGE3), nu propaga eroarea in sus si nu blocheaza formularul.
3. **Cursoarele noi (`tvd`/`tvanz`) nu trebuie sa interfereze cu `tact`/`trul`/`trul_obinv`** —
nume de alias distincte, verificate ca nu exista deja in cod (`tvd`/`tvanz` cautate, 0
rezultate azi). Grid-ul nou trebuie sa aiba `ControlSource` calificat complet pe fiecare coloana
(`COMUN\docs\capcana_grid_controlsource.md`) — capcana confirmata activa exact in acest scenariu
(3+ griduri pe acelasi formular: `grid1`/`grdRulaje`/`grdRulajeObinv`/noul grid).
4. **Scrierea (nu doar afisarea)**: partea cea mai sensibila. Scrierea noua in `VANZARI_DETALII`
(E, S5) trebuie sa fie **strict gatata de `This.lAreArticoleVanzari`**, apelata din apelanti
(`do_editare_factura`/`afisjurcom.do_modifica`) DOAR dupa ce pasul existent
`finalizeaza_modificare_nota` a reusit deja — deci pe orice nota fara rand in `VANZARI`, pasul
nou nu se executa niciodata (flag-ul e `.F.`), cod identic cu azi.
5. **`gridextra1.setup()`** (`:13729`) — salveaza/restaureaza preferinte per-grid, cheie
`SYS(1272, grid)`. Grid-ul nou (`pgfArticole.PAGE3.grdArticoleFactura`) e complet nou, deci nu
are preferinte salvate de niciun utilizator — capcana din
`capcana_grid_preferinte_utilizator.md` (coloana noua intr-un grid EXISTENT ajunge la coada) nu
se aplica la infiintare, doar daca se adauga o coloana ulterior. **Neverificat**: daca
`gridextra1` inregistreaza automat orice grid nou de pe formular sau necesita inregistrare
explicita — de confirmat direct in `gridextras.vc2` la implementare, nu presupus aici.
**Cum se testeaza D**: un caz de test obligatoriu (deja in S8 din plan) e "document care nu e
factura, deschis din registrul jurnal — pagina de articole nu apare si comportamentul e identic cu
azi". Recomand completarea lui cu: (a) o rulare explicita cu Oracle temporar indisponibil pe
interogarea din A.3 (verifica gracious degradation, punctul 2 de mai sus), (b) o comparatie
byte-cu-byte a XML-ului salvat de `salveazaxml` (`:13690-13717`) inainte/dupa modificare, pe un
document non-factura, ca sa confirme ca noul cod n-a atins deloc acel drum.
## E. Impartirea in runde de implementare
Ordonat pe risc, cu rezultat testabil la finalul fiecarei runde. S1-S3 (deja facute) raman runda 0.
**Runda 1 — PAGE3 doar afisare, fara scriere** (depinde doar de VFP + Oracle read-only, nu de S5)
- A.2 (PageCount/Caption/grid nou) + A.3 (detectie factura, in `frm_modific2024`) + B (incarcare
`tvanz`/`tvd`, read-only).
- Grid needitabil (`ReadOnly=.T.` pe toate coloanele), fara buton de salvare separat pe pagina.
- *Gata cand*: PAGE3 apare pe orice document cu rand in `VANZARI` (facturi, avize, transfer,
custodie — decizia 19), din ambele puncte de intrare, arata liniile corecte; pe orice document
fara rand in `VANZARI` dispare complet (testul de risc D).
- **Nu depinde de S5.** Se poate livra si testa independent.
**Runda 2 — editare in memorie, fara scriere in Oracle**
- Grid editabil (B.2, marcaje `_modificat`/`sters`/`id_vanzare_det=0`), dialogul per-linie
(reutilizarea `frm_articol_factura`, vezi nota tehnica de mai jos), adaugare/stergere linie in
cursor, discount de antet editabil in `tvanz`.
- Subtotalul live pe linie (C.2, calcul simplu VFP).
- *Gata cand*: utilizatorul poate adauga/sterge/modifica linii in grid, vede subtotaluri live;
`Renunta` lasa totul neschimbat; `Termina` **nu** scrie inca nimic in `VANZARI_DETALII` (doar
nota contabila, ca azi).
- **Nu depinde de S5** — poate rula complet pe VFP, testabil headless fara risc de scriere Oracle.
**Runda 3 — S5 (Oracle) + scrierea reala**
- Procedura `recalculeaza_totaluri_vanzari` (Oracle, `rec_s5_oracle_vanzari.md` sectiunea B) +
procedurile de UPDATE/INSERT/soft-DELETE pe `VANZARI_DETALII` (`rec_cale_vanzari_detalii.md`
sectiunea 4, Varianta B).
- Scrierea efectiva din VFP: apel nou, simetric in ambii apelanti, gatat de
`Omodif.lAreArticoleVanzari`, dupa `finalizeaza_modificare_nota` reusit (D.4).
- *Gata cand*: dupa `Termina`, `VANZARI_DETALII`/`VANZARI` reflecta editarea; S7 (rotunjire la
reeditare) verificat pe acest flux.
- **Depinde de Runda 2** (cursorul editat trebuie sa existe) si de S5/S6 din planul general.
**Runda 4 — S4b complet (helpere/verificari)**
- C.1-C.6: totalurile de control (interogare directa a functiilor Oracle existente, nu formula
VFP), indicatorul de stare, actiunea explicita de sincronizare.
- *Gata cand*: criteriile din plan_06 S4b (indicator vizibil, propunere enumerata, refuzul nu
modifica nimic).
- **Depinde de Runda 3** — comparatia cu ACT/VANZARI_DETALII n-are sens pana nu exista scriere
reala de comparat.
**Nota tehnica pentru Runda 2**: dialogul per-linie recomandat e **reutilizarea**
`frm_articol_factura` (`COMUN\clase\ofacturare.vc2:2315-...`, deschis azi din
`frm_facturare_articole.do_modifica`, `ofacturare.vc2:13746-13844`, model documentat si in
`plan_06_editare_factura.md` "Ce preda #7 catre S6"). Dialogul citeste `PRIVATE poArticol` (setat
de apelant inainte de `Createobject`) si `Lparameters tnCantitate, tlAscunde` — **fara plafon**
(decizia 15), `tnCantitate` se transmite cu o valoare santinela mare (ex. `999999999`), nu se
recalculeaza din stoc. `poArticol` asteptat de dialog are un set bogat de proprietati calculate
(`valftva`, `vval*`, etc. — vezi lista completa la `ofacturare.vc2:13835-13840`), diferite de
coloanele brute din `VANZARI_DETALII`; adaptorul nou trebuie sa populeze campurile de baza
(`pret_achizitie, cantitate, id_articol, cont, id_gestiune, proc_tvav, pretftva/pretctva dupa
flag, discount_unitar, id_valuta, ...`) din `tvd`, apoi sa cheme aceeasi functie VFP
`calculeaza_totaluri(poArticol)` (apelata deja la `:13875` pentru randuri noi) ca sa deriveze restul
— **nu se reinventeaza formula**, se refoloseste exact ca la compunere. Ramura `do_alege_stoc`
(redeschiderea dialogului de gestiuni, folosita azi doar la compunere) **nu se foloseste in #6** —
decizia "fara verificare de stoc" (15) inseamna ca toate liniile, gestionabile sau nu, trec prin
acelasi dialog simplu.
## F. Intrebari pentru Marius
1. **[DECIS — decizia 19 din progres.md, vezi A.3]** Nu strict facturi: pagina apare pe **orice
rand din `VANZARI`**, deci si pe avize, transfer si custodie. Varianta "strict facturi"
recomandata initial in A.3 a fost respinsa explicit de Marius.
2. **[REZOLVAT — vezi C.1]** Contul nu e fix `4111`: regula e pe tip de document (facturi `4111`,
avize `418`, avize catre clienti debitori `461`), verificata pe 360 de documente / 12 tipuri /
3 scheme (`docs\cercetare\rec_suma_act.md`). Raman deschise, fara sa infirme regula: 5 documente
`ROMFAST` fara nicio nota `ACT` si un document `VENDING` in valuta (`cod=1165566`) cu diferenta
neexplorata.
3. **[REZOLVAT — vezi C.1]** Regula gasita: perechile `cant`/`cante` marcate `ID_TIP_RULAJ=3` sunt
randuri de diferenta de pret (`PACK_FACTURARE.descarca_gestiune`), de exclus randul cu pretul
vechi din stoc. Formula `SUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ<>3)`
reproduce exact totalul pe documente cu toate liniile stocate; pe documente mixte se corecteaza
cu liniile nestocate (C.1).
4. **UX-ul concret al zonei de totaluri si al indicatorului** — trei variante, cu recomandare:
**Varianta A (recomandata) — bara de totaluri sub grid-ul PAGE3, indicator ca punct colorat +
text:**
```
+-----------------------------------------------------------------------+
| [Articole factura] Discount document: [____] % |
| +---------------------------------------------------------------+ |
| | Articol | Cant | Pret | Cu TVA | ... | |
| | ... | |
| +---------------------------------------------------------------+ |
| Total ACT (contabil): 1924.59 lei |
| Total VANZARI_DETALII: 1924.59 lei |
| Total RUL (formula C.1): 1924.59 lei [*] Sincronizat |
| [Verifica sincronizare] [Propune] |
+-----------------------------------------------------------------------+
```
Exemplu `cod=1140888` (C.1): dupa formula corecta, cele trei sume coincid — nu mai e un caz care
sa ilustreze o divergenta. Pentru un document mixt (linii stocate + nestocate, decizia 25),
randul RUL se marcheaza explicit ca ajustat, de exemplu:
```
| Total RUL (ajustat, +linii nestocate): 1170.00 lei |
```
Simplu, aliniat cu `_grdfooter1` deja existent pe PAGE1/PAGE2 (acelasi loc, acelasi stil vizual).
**Varianta B — indicator langa caption-ul paginii** (`PAGE3.Caption = "Articole factura ⚠"` sau
cu iconita), totalurile doar la cerere (buton "Arata totaluri de control" care deschide un
dialog separat). Mai compact, dar ascunde informatia pana la un click — risc sa nu fie observat.
**Varianta C — culoare de fond pe intreaga pagina** (rosu deschis) cand desincronizat, fara
text explicit pana la deschiderea dialogului de sincronizare. Cel mai putin verbose, dar
ambiguu (utilizatorul nu stie CE e desincronizat fara sa deschida dialogul).
Recomand **A** — respecta convenția UX (`conventie_ux_formulare.md`: informatia de control
trebuie vizibila, nu ascunsa dupa un click) si reutilizeaza tiparul deja vizual familiar din
PAGE1/PAGE2 (footer de sume sub grid).
5. **Dialogul de propunere de sincronizare (C.4)** — schita:
```
+---------------------------------------------------------+
| Propunere sincronizare (sursa: Articole factura -> Rulaj)|
| +-------------------------------------------------------+|
| | Articol | Rulaj (vechi) | Articol (nou) | ||
| | Piesa X | cant=2 pret=100 | cant=3 pret=110 | ||
| | Piesa Y | -- (fara rulaj) | cant=1 pret=50 | ||
| +-------------------------------------------------------+|
| [Alege directia: Articol->Rulaj | Rulaj->Articol]|
| [Aplica in memorie] [Renunta] |
+---------------------------------------------------------+
```
De confirmat daca alegerea directiei e un singur radio-button global (recomandat, C.4 punctul 2)
sau daca Marius vrea control per-linie (mai flexibil, mult mai complex de implementat si de
explicat utilizatorului — nerecomandat pentru complexitatea/beneficiul).
## Ce nu am putut verifica
- Daca `gridextra1.setup()` inregistreaza automat grid-uri noi de pe formular (D.5).
- Comportamentul `_grdfooter`/`attachtogrid` pe un grid gol (0 randuri) la prima deschidere a
PAGE3 — nu a fost testat, doar citit codul PAGE1/PAGE2 ca precedent.
(Cele trei puncte legate de formula `ACT`/`RUL` care erau listate aici — divergenta 903.53 vs
1924.59, regula perechilor `cant`/`cante`, stabilitatea contului `4111` — s-au rezolvat prin
cercetarea din `docs\cercetare\rec_suma_act.md` si sunt acum in C.1 / F.2 / F.3.)