Files
roafacturare/docs/propunere_s4b_sincronizare.md
Marius Mutu b5a7108f34 docs: cercetarile comune trec in COMUN, reziduul #6 dispare, progres.md la zi
Curatenie ceruta dupa inchiderea lui #6. Folderul cercetare\ NU se putea sterge
in bloc: plan_13 se sprijina pe el cu 85 de trimiteri, deci e baza de dovezi a
planului aflat in lucru. Impartirea:

- 14 cercetari trans-proiect trec in COMUN\docs\cercetare\ - valuta si curs,
  TVA/VANZARI, consumatorii VANZARI din toata suita, integrarile #10/#11/#12,
  watchdog VFP, proiectarea Oracle a lui S5, view-ul VVANZARI_ARTICOLE. Nu sunt
  ale ROAFACTURARE, iar #10/#11/#12 se reiau chiar din ele.
- 20 de rapoarte de executie ale lui #6, nereferite de nimic viu, sterse.
- 91 raman, neatinse.

Trimiterile catre handoff-urile si diff-urile intermediare deja desfiintate au
fost curatate peste tot (39 de fisiere): 50 catre handoff-uri, 37 catre diff-uri
aplicate, plus caile celor mutate in COMUN. Zero trimiteri rupte ramase.

progres.md preia rolul de predare: ce ramane din #6 (cele sase documente
parazite, cele doua esecuri reale din S8 pe factura din aviz), cifrele de test
citite din log, si cele doua capcane de mediu platite - .FXP vechi executat in
locul .prg-ului, si GETFONT() care atarna un formular instantiat fara goApp.

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

214 lines
14 KiB
Markdown

# Propunere S4b — actiunea explicita de sincronizare RUL <-> articole factura
Document de **proiectare**, nu implementare. Niciun `.vc2`/`.prg` nu a fost modificat. Cerinta
sursa: `docs\plan_06_editare_factura.md:208-231`. Context: handoff intermediar (sters)
(sectiunea „S4b — ce e facut si ce lipseste"), handoff intermediar (sters).
## Corectie fata de predarea anterioara — nu e nevoie de cursor de instantaneu
`docs\cercetare\rec_s5_cale_scriere_vfp.md` 2.3 propunea un cursor `tvd_initial`, luat imediat
dupa `IncarcaArticoleFactura`, ca solutie la blocajul „`tvd` nu pastreaza valorile de dinainte de
editare". Verificat pe cod si pe plan: **acea propunere rezolva o alta problema** — istoricul
editarilor utilizatorului in aceeasi sesiune (cat a schimbat el insusi o cantitate) — nu ce cere
literal S4b.
Planul spune explicit: *„preturile si cantitatile din **rulaje** si cele din **articolele
facturii** se pot desincroniza la editare"* (`plan_06_editare_factura.md:209-210`) — deci
comparatia ceruta e intre `trul` (rulaje) si `tvd` (articole), **doua reprezentari independente
ale aceluiasi document**, editabile separat in acelasi formular (PAGE1 vs PAGE3). Nu e nevoie de
niciun instantaneu: „vechi" = valoarea curenta din cursorul-**tinta** (ce s-ar salva daca
utilizatorul apasa Salveaza acum, fara sincronizare), „nou" = valoarea calculata din cursorul-
**sursa**. Ambele cursoare sunt deja rezidente si populate cand utilizatorul ajunge pe PAGE3 —
comparatia se face live, nu din istoric.
Confirmare suplimentara ca `ACT`/`tact` nu poate fi parte a acestei comparatii (desi indicatorul
existent `ActualizeazaVerdictActRul` compara ACT cu RUL): `tact` e un jurnal de postari
debit/credit (`scd`/`scc`/`suma`), fara `cantitate`/`pret`/`id_articol` per linie de marfa —
structural nu poate fi sursa sau tinta a unei sincronizari „cantitate/pret". Sincronizarea din
S4b ramane **exclusiv RUL <-> articole**, distincta de indicatorul de verdict deja livrat.
## 1. Unde traieste actiunea
**Buton nou** `cmdSincronizeazaArticole`, in randul de butoane din PAGE3, langa cele existente:
```
COMUN\clase\omodificari.vc2:12291-12305 ADD OBJECT 'pgfArticole.PAGE3.cmdAdaugaArticol' ... Left=175 Width=170
COMUN\clase\omodificari.vc2:12307-12321 ADD OBJECT 'pgfArticole.PAGE3.cmdStergeArticol' ... Left=0 Width=170
```
Randul e la `Top=0`, latimea paginii e 764 (`omodificari.vc2:8702`); `cmdAdaugaArticol` se termina
la 345 — spatiul liber pana la 764 (419px) e suficient pentru un al treilea buton. Propunere:
`Left=350, Top=0, Width=250, Height=22`, caption „Sincronizeaza cu rulaje...". `lblArticoleReadOnly`
(`:12776-12787`, Left=360 Top=4) ocupa aceeasi zona vizual, dar e vizibila **doar** cand
`lArticoleReadOnly=.T.` — exact starea in care butonul trebuie oricum dezactivat, deci overlap-ul
nu se vede niciodata simultan (acelasi tipar folosit deja pentru celelalte doua butoane).
Toggle vizibilitate/enabled: se extinde blocul deja existent din `Show()`:
```
omodificari.vc2:14811-14814
This.pgfArticole.PAGE3.cmdAdaugaArticol.Enabled = !This.lArticoleReadOnly
This.pgfArticole.PAGE3.cmdStergeArticol.Enabled = !This.lArticoleReadOnly
This.pgfArticole.PAGE3.txtDiscountArt.ReadOnly = This.lArticoleReadOnly
This.pgfArticole.PAGE3.lblArticoleReadOnly.Visible = This.lArticoleReadOnly
```
cu o linie `This.pgfArticole.PAGE3.cmdSincronizeazaArticole.Enabled = !This.lArticoleReadOnly`.
**Gardare gratuita pentru ROACONT/ROAGEST**: butonul exista doar in PAGE3, care la randul ei
exista doar cand `This.lAreArticoleVanzari` (`:14792-14818`, gatat deja pe
`"OFACTURARE_EDITARE" $ Upper(Set("Procedure"))`). Nu e nevoie de nicio garda noua — butonul
mosteneste gating-ul deja livrat in S3/S4.
## 2. Cum se calculeaza „vechi -> nou"
**Fara FK la nivel de rand** intre `trul` si `VANZARI_DETALII`/`tvd` — verificat direct pe schema
Oracle (`DESCRIBE VRUL_TOT` prin `sqlplus`, 09.08.2026): view-ul sursa al lui `trul`
(`ofacturare_editare.prg:33-100`, in special `:74`) are 218 coloane, **niciuna** de tip
`id_vanzare_det`/`id_rulaj_vanzare`. Singura cheie comuna e **`ID_ARTICOL`**, prezenta in ambele:
`VRUL_TOT.ID_ARTICOL` (confirmat pe schema) si `tvd.id_articol`
(`ofacturare_editare.prg:274`, `CreeazaCursorArticoleGol`). Matching-ul e deci obligatoriu **la
nivel de articol** (agregat), nu la nivel de linie individuala — nu exista alta optiune.
**Agregarea RUL per articol** refoloseste formula validata pe date reale, nu una noua:
`docs\cercetare\rec_suma_act.md`, sectiunea E.3, verificata exact pe doua documente (unul de test,
unul de productie, E.4/E.5):
```
valoare_articol_rul = SUM(cant * pretvtva) + SUM(CASE WHEN id_tip_rulaj <> 3 THEN cante * pretvtva ELSE 0 END)
cantitate_articol_rul = SUM(cant) + SUM(CASE WHEN id_tip_rulaj <> 3 THEN cante ELSE 0 END)
pret_propus = valoare_articol_rul / cantitate_articol_rul -- pret mediu ponderat, daca sunt mai multe randuri RUL pe acelasi articol
```
GROUP BY `id_articol`, filtrat `sters = 0` (trul incarca deja doar randuri active,
`ofacturare_editare.prg:57` cu `tlAratasterse=.F.` din apelantii de editare).
**Articolele nestocate** (`in_stoc=0`, `rec_suma_act.md` E.5/E.6) nu au niciodata randuri in RUL —
excluse automat din comparatie, afisate cu actiune „N/A — articol nestocat, fara corespondent in
rulaje" (aceeasi coloana `in_stoc` deja incarcata in `tvd`, `ofacturare_editare.prg:301`, folosita
azi doar pentru corectia verdictului RUL).
**Liniile adaugate/sterse** (cerinta explicita din plan) intra natural in acelasi matching pe
`id_articol`, ca diferenta de multimi:
- articol activ in sursa, absent (sau `sters=1`) in tinta -> actiune **Adaugare** in tinta;
- articol activ in tinta, absent (sau agregat 0) in sursa -> actiune **Semnalare** (vezi risc,
punctul 6 — NU stergere automata);
- articol in ambele, cantitate/pret diferite -> actiune **Modificare**;
- articol in ambele, identic -> nu apare in enumerare (fara zgomot).
## 3. Cum alege utilizatorul directia
Dialog modal nou, `frm_sincronizare_articole`, in `COMUN\clase\omodificari.vc2` (langa
`frm_modific2024`, ca sa poata referi direct cursoarele `tvd`/`trul` din acelasi data session
implicit — sectiunea 2.5 din `rec_s5_cale_scriere_vfp.md`). Deschis din
`cmdSincronizeazaArticole.Click`, modal (`WindowType=1`, acelasi tipar ca `frm_modific2024` insusi
si ca `frm_modifica_articol_factura`).
Continut:
- doi radio-butoni (optiongroup): **„Rulajul e sursa (actualizeaza articolele facturii)"**
(implicit, recomandat — vezi punctul 5) / **„Articolele sunt sursa (actualizeaza rulajul)"**;
schimbarea selectiei recalculeaza si reafiseaza enumerarea imediat (fara buton „recalculeaza"
separat);
- grid needitabil pe cursorul `propunere_sincronizare`, coloane: **Articol** (denumire + codmat),
**Cantitate veche**, **Cantitate noua**, **Pret vechi**, **Pret nou**, **Actiune**
(Modificare/Adaugare/Semnalare/N-A), cu `DynamicForeColor` gri pe randurile N-A (acelasi tipar ca
`tvd.sters` in `grdArticoleFactura`, `omodificari.vc2:12344`);
- „Aplica" — activ doar daca exista minim o linie cu actiune Modificare sau Adaugare; „Renunta".
Dialogul **citeste** `tvd`/`trul` doar pentru afisare — nu le modifica pana la „Aplica". Refuzul
(„Renunta" sau inchiderea ferestrei) nu are nimic de desfacut, pentru ca nimic n-a fost atins:
satisface direct cerinta din plan („refuzul propunerii lasa datele nemodificate").
## 4. Ce scrie efectiv sincronizarea, si prin ce cale
**Nimic in Oracle, niciodata.** `AplicaSincronizareArticole` (nou, `ofacturare_editare.prg`, langa
restul helperelor pure — nu un nou punct de scriere Oracle) scrie **doar** in cursoarele VFP
in-memory `tvd`/`trul`, la apasarea „Aplica":
- **Directia „rulajul e sursa"**: pentru fiecare articol cu actiune Modificare —
`REPLACE tvd.cantitate, tvd.pret, tvd.pret_cu_tva WITH ..., lmodificat WITH .T.`, apoi
recalculul lui `valoare` cu **aceeasi formula** deja folosita in `calculeaza_valori_articol`
(`omodificari.vc2:13397-13409`) — refolosita, nu reimplementata — si apel la
`Thisform.ActualizeazaBaraTotaluri()` ca dupa orice editare manuala. Pentru Adaugare, se refoloseste
tiparul din `AdaugaLinieTvdDinArticol` (`omodificari.vc2:13130+`), populat cu valorile din RUL.
- **Directia „articolele sunt sursa"**: pentru fiecare articol cu match unic activ in `trul` —
`REPLACE trul.cant, trul.pretvtva WITH ...` pe randul respectiv, fara sa atinga
`id_tip_rulaj`/conturi/`scd`/`scc`. Cand exista mai multe randuri RUL active pe acelasi articol
(perechi de diferenta de pret, `id_tip_rulaj=3`, sau randurile „duplicat" nelamurite din
`rec_suma_act.md` E.4), linia se marcheaza **N-A — mai multe randuri RUL, verificati manual** —
nu se alege automat care rand sa fie atins.
Scrierea **reala** in Oracle ramane exact calea deja livrata in S5, neschimbata:
`do_termin` -> `OSCRIE_IN_FISIERE` pentru `trul`/`tact` (neatins de aceasta lucrare) +
`ScrieArticoleFacturaEditate` pentru `tvd` (`ofacturare_editare.prg:469-556`, comisa in S5).
Sincronizarea doar **pregateste** cursoarele exact cum ar face utilizatorul manual din grid —
niciun helper Oracle nou.
## 5. Alternative, cu recomandare
**A. Granularitatea matching-ului: pe linie vs pe articol.**
Pe linie — respins, nu exista nicio cheie (verificat pe schema, punctul 2). **Recomandat: pe
articol** (`id_articol`), singura cheie comuna confirmata.
**B. O singura directie vs alegere intre doua.**
Planul cere explicit alegerea utilizatorului (`plan_06_editare_factura.md:226-227`). **Recomandat:
ambele directii**, dar cu directia „articole -> RUL" **restransa la actualizarea cantitate/pret pe
randuri RUL deja existente**, niciodata la crearea de randuri noi de contabilitate (motivat la
punctul 6 — un rand RUL nou cere `cont`/`scd`/`scc` pe care sincronizarea nu le poate deduce
sigur din articol). Directia „RUL -> articole" nu are aceasta limitare, pentru ca `tvd`/
`VANZARI_DETALII` nu poarta conturi contabile — de aici si recomandarea ei ca implicit in dialog.
**C. Dialog modal separat vs panou inline pe PAGE3.**
PAGE3 n-are loc: layout-ul complet (`omodificari.vc2:12780-12956`) foloseste toata latimea de 764px
pe cele doua randuri de totaluri, iar randul de butoane are doar 419px liberi — insuficient pentru
un grid de propuneri cu 6 coloane. **Recomandat: dialog modal separat**, ca `frm_modifica_articol_
factura` (acelasi tipar deja folosit in acest formular).
**D. Stergerea automata de linii cand sursa nu mai are articolul.**
Riscant — vezi punctul 6. **Recomandat: fara stergere automata, in nicio directie** — doar
semnalare in enumerare; stergerea efectiva ramane pe butonul deja existent `cmdStergeArticol`,
actionat manual dupa ce utilizatorul a vazut divergenta.
## 6. Ce poate strica
- **Cel mai mare risc concret**: randurile RUL „duplicat" cu `id_tip_rulaj=0` gasite langa perechi
`id_tip_rulaj=3` (`rec_suma_act.md` E.4, intrebarea 3 nerezolvata catre Marius) — pe un document
cu asemenea randuri, agregarea per articol ar putea propune o cantitate/pret gresit(a). Atenuare:
eticheta explicita pe dialog („propunere calculata, verificati inainte de aplicare"), *niciodata*
aplicare automata sau implicita; cand exista >1 rand activ pe acelasi articol, linia se marcheaza
N-A in loc sa se aleaga arbitrar (punctul 4).
- **Registrul jurnal ROACONT/ROAGEST**: butonul si dialogul exista doar in interiorul gardei deja
existente pe PAGE3 (`lAreArticoleVanzari`) — cod complet inert acolo, fara garda noua de adaugat
(punctul 1). Risc zero suplimentar fata de ce e deja livrat in S3-S5.
- **`omodificari.vc2` e partajat de toata suita** — orice atingere in afara blocului gatat pe
`lAreArticoleVanzari`/PAGE3 (de ex. o modificare gresita in `Load()`/`Show()` inainte de gate)
ar afecta toate notele contabile editate din ROACONT/ROAGEST, nu doar facturile. Implementarea
trebuie sa ramana strict in interiorul blocului PAGE3 existent, fara sa atinga `grid1`/`tact`
(grid-ul comun de note, folosit de toata suita).
- **Directia „articole -> RUL" scrie in randuri de contabilitate** (`trul`) fara sa recalculeze
conturile — acceptabil DOAR daca ramane strict la actualizarea cantitate/pret pe randuri deja
existente (punctul 5.B), exact echivalent cu o corectie manuala facuta azi direct in grid-ul de
rulaje (PAGE1) — nu introduce o cale de scriere noua sau mai permisiva decat ce exista deja.
## 7. Estimare de efort
| Bucata | Zile |
|---|---|
| Agregare RUL per articol (formula E.3) + agregare `tvd` per articol, logica pura testabila headless | 0.5-1 |
| `ConstruiestePropunereSincronizare` (matching pe `id_articol`, ambele directii, adaugat/modificat/semnalare) | 1 |
| `AplicaSincronizareArticole` (mutatii in memorie pe `tvd`/`trul`, reutilizare `calculeaza_valori_articol`/`AdaugaLinieTvdDinArticol`) | 1 |
| `frm_sincronizare_articole` (radio directie, grid needitabil, Aplica/Renunta) | 1-1.5 |
| Buton + toggle pe PAGE3 (`txt2vcx.ps1` pe `omodificari.vc2`) | 0.5 |
| Teste headless (logica pura de agregare/matching, pe cursoare construite in test, model S5) | 0.5 |
| Teste UI vizibile (dialogul modal nu e testabil `-A -T`, la fel ca `frm_articol_factura`) | 0.5-1 |
| **Total** | **~5-6.5 zile** |
Confirma caracterizarea din plan: „cea mai mare ramasa, nu blocheaza pe nimeni"
(handoff intermediar (sters)).
## Ce ramane deschis, de decis cu Marius inainte de implementare
1. Directia implicita in dialog — recomandare: „rulajul e sursa" preselectat (punctul 5.B).
2. Randurile RUL „duplicat" nelamurite (`rec_suma_act.md` E.4/F.3) — daca raman nerezolvate, orice
document cu ele va produce linii N-A in loc de propunere, ceea ce e sigur dar posibil frustrant
pentru utilizator pe acele documente specifice.
3. Daca se doreste si optiunea „aplica pe toate liniile deodata" vs selectie linie cu linie in
grid — propunerea de mai sus e „aplica tot ce nu e N-A", cea mai simpla; o selectie fina ar
adauga complexitate UI fara sa fie ceruta explicit de plan.