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
430 lines
28 KiB
Markdown
430 lines
28 KiB
Markdown
# Proiectare S4d — Data cursului valutar, numai cand are sens (reactiv)
|
|
|
|
Poveste: `docs\plan_13_unificare_formular_facturare.md`, `#### S4d` (decizia 15, proiectata in
|
|
sectiunea M). Cercetare preliminara deja facuta si citata ca sursa de adevar:
|
|
`docs\cercetare\zi_curs_validare.md`, `COMUN\docs\cercetare\valuta_si_curs.md`,
|
|
`docs\cercetare\rec_dec42_proiectare.md`/`rec_d42_efactura.md` (decizia 42),
|
|
`docs\cercetare\s3_portare_antet.md` (bug #16), `docs\cercetare\s4_cautare_articole_server.md`/
|
|
`s4_puncte_deschise.md` (decizia 42, mecanismul ei exact). Read-only: nicio editare de cod, niciun
|
|
`git_sync.ps1`/`txt2vcx.ps1`, nicio scriere pe Oracle.
|
|
|
|
---
|
|
|
|
## Verdict (rezumat)
|
|
|
|
Regula reactiva se poate implementa fara sa strice nimic din ce e deja demonstrat sigur: implicitul
|
|
`poDate.zi_curs` ramane neconditionat (nu se schimba), iar cheia reactivitatii e un camp deja prezent
|
|
in cursorul de grid — `crsfactura.tip_valuta` — verificabil cu exact acelasi tipar SQL folosit deja in
|
|
cod (`ofacturare.prg:1656`, `:1730`: `Select Distinct ... From crsfactura Where tip_valuta = 1`).
|
|
Formularul-prototip `frm_facturare_articole2` are deja, azi, un `Clb_zi_curs` propriu, editabil, legat
|
|
la `poDate.zi_curs` (`ofacturare.vc2:16285-16305`) — nu trebuie inventat un control nou, ci reevaluata
|
|
vizibilitatea celui care exista deja. Mesajul Oracle `-20005` **contine deja** data si numele valutei
|
|
lipsa (`STRINGAGG` peste toate valutele fara curs) — decizia 42 nu schimba *continutul* mesajului, ii
|
|
schimba **domeniul**: il restrange de la "toate valutele din listele de preturi ale utilizatorului" la
|
|
"valuta articolului cautat", dar **doar pe varianta filtrata a cautarii** (S4 punctul 1); pe caile cu
|
|
document sursa (comanda/aviz/contract) incarcarea in masa ramane neconditionata. Plan sectiunea M
|
|
(`:2635-2638`) cere explicit ca la aceasta eroare campul sa **revina vizibil** — asta intra ca pas de
|
|
implementare, nu ca optiune. Bug-ul #16 **nu e in perimetrul S4d** — e deja cercetat si legat de S2
|
|
(bucla de reincercare din `ofacturare.prg`), cu verdict separat in `s3_portare_antet.md`; S4d doar
|
|
**confirma** ca noua arhitectura (antet persistent, nu recreat) ii inlatura mecanismul, daca S2/S3 nu
|
|
recreeaza formularul la eroare.
|
|
|
|
---
|
|
|
|
## 1. Inventarul controalelor de valuta si curs (`fisier:linie`)
|
|
|
|
Patru definitii `Clb_zi_curs` in tot `ofacturare.vc2` (control compus `clb_tx_data`, camp text legat
|
|
la `poDate.zi_curs`), confirmate exhaustiv (`grep "ADD OBJECT 'Clb_zi_curs'"`):
|
|
|
|
| Formular | Definitie | Eliminat azi? | Validare la Termina | Sincronizare cu data |
|
|
|---|---|---|---|---|
|
|
| `frm_date_aviz` | `ofacturare.vc2:6727-6740` | Niciodata | Nu exista (fara `inainte_de_do_termin` care sa o ceara) | `Clb_dataact...LostFocus` (`:7603-7604`), `Clb_dataireg...LostFocus` (`:7610-7611`) — `Thisform.clb_zi_curs.Refresh()`, neconditionat |
|
|
| `frm_date_aviz_lucrare` | `:7787-7801` | Niciodata | **Neconditionata**: `:8076` `Case Empty(Nvl(poDate.zi_curs,{}))` -> `:8078 This.clb_zi_curs.SetFocus()`, `plReturn=.F.` | `:8186-8187`, `:8193-8194`, neconditionat |
|
|
| `frm_date_factura` | `:8701-8714` | **Doar** tip 8/9 (retur), `Init` `:9717-9722` (`RemoveObject`) | Conditionata: `:9484` `Case poDate.in_valuta=1 And Empty(...) And Type('thisform.clb_zi_curs.visible')<>'U'` -> `:9488 SetFocus` | `:9805-9808`, `:9824-9827`, ambele cu garda `Type(...)<>'U'` |
|
|
| `frm_facturare_articole2` (prototip) | `:16285-16305`, camp text legat la `poDate.zi_curs` (`TEXT_SIMPLU1.ControlSource`) | **Niciodata** — `Init` (`:18988-19080`) nu contine niciun `RemoveObject('clb_zi_curs')` | Nu exista (formularul de articole nu are `inainte_de_do_termin` propriu de tip antet) | `Clb_dataact.TEXT_SIMPLU1.LostFocus` (`:19219-19222`) — acelasi tipar cu garda `Type(...)<>'U'` |
|
|
|
|
Alte controale de valuta/curs, relevante ca sa nu fie confundate cu `clb_zi_curs`:
|
|
|
|
- **`Ct_clb_valuta`** (selector de valuta document) — eliminat pe `frm_date_factura` cand
|
|
`poDate.in_valuta=0` (`:9725-9728`), **neconditionat de tip**. Confirma ca "ascunde cand nu are sens"
|
|
e deja un tipar folosit in aceeasi metoda, la doar 3 linii distanta de blocul retur — dovada directa
|
|
ca autorul stia sa faca exact acest lucru, doar nu l-a aplicat si campului de curs.
|
|
- **`lb_cursuri` + `grd_cursuri`** (eticheta si grid read-only "Curs valutar (data)") —
|
|
`frm_facturare_articole.Init` (`:15092-15100`) si `frm_facturare_articole2.Init` (`:19000-19012`):
|
|
```
|
|
ofacturare.vc2:19000-19012
|
|
If !Used('crscursuri') Or Reccount('crscursuri') = 0
|
|
Thisform.RemoveObject('lb_cursuri')
|
|
Thisform.RemoveObject('grd_cursuri')
|
|
Else
|
|
If !Empty(Nvl(poDate.zi_curs, {}))
|
|
Thisform.lb_cursuri.Caption = "Curs valutar " + Iif(!Inlist(poDate.tip, 8, 9), "(" + Dtoc(poDate.zi_curs) + ")", "")
|
|
...
|
|
```
|
|
Acesta e **cel mai apropiat precedent de "arata/ascunde in functie de continut"** din codul existent
|
|
— dar evalueaza `crscursuri` (cursurile incarcate pentru toate valutele din listele de preturi ale
|
|
utilizatorului), **o singura data, la `Init`**, inainte ca userul sa fi adaugat vreo linie pe grid.
|
|
**Nu e reactiv** la adaugare/stergere de articole — e evaluat o singura data pe un cursor diferit de
|
|
`crsfactura`. Nu se poate copia ca atare pentru S4d; poate fi reutilizat doar ca **idiom** (verificare
|
|
`Reccount(...) > 0` care decide `RemoveObject`/reafisare), mutat pe alt cursor si alt eveniment
|
|
(punctul 2).
|
|
- **`poArticol.tip_valuta`** — proprietate a politicii de pret a articolului (nu a documentului),
|
|
populata la `do_initializeaza_articol`/cautare, citita masiv in calculul de pret/discount
|
|
(`ofacturare.vc2:1857-2288`, `frm_articol_factura`). Cand articolul e adaugat pe grid, valoarea
|
|
ajunge in coloana `crsfactura.tip_valuta` (vezi punctul 2) — acesta e semnalul pe care se construieste
|
|
conditia reactiva, nu `poArticol` (obiect temporar, mort dupa `Release`).
|
|
|
|
**Cine citeste `poDate.zi_curs` dupa formular** (relevant ca sa nu se rupa nimic la ascundere) — deja
|
|
documentat exhaustiv in `zi_curs_validare.md` §4 si `valuta_si_curs.md` §4: cursoarele de articole
|
|
(`cursor_preturi`/`cursor_articole_k`/`cursor_gestiune`/`cursor_lucrare`, `ofacturare.prg:266-308`),
|
|
eticheta `lb_cursuri`, si validarile de mai sus. Nimic din acest tabel se schimba prin S4d.
|
|
|
|
---
|
|
|
|
## 2. Conditia reactiva, exact
|
|
|
|
**Camp folosit:** `crsfactura.tip_valuta` (N(1)), definit la creearea cursorului de grid,
|
|
`creeaza_facturacrs` (`COMUN\programe\ofacturare_comun.prg:1776`), populat la fiecare linie adaugata
|
|
prin `do_adauga_articol` (coloana e in lista de `Insert`/`Scatter`, `ofacturare.vc2:12950`,
|
|
`:17258` pentru `frm_facturare_articole2`) direct din `poArticol.tip_valuta`.
|
|
|
|
**Verificare, cu exact acelasi tipar deja folosit in productie** (nu inventat):
|
|
|
|
```
|
|
ofacturare.prg:1656, :1730 [listeaza_ofacturare]
|
|
Select Distinct nume_val, Curs, multiplicator From crsfactura Where tip_valuta = 1
|
|
```
|
|
|
|
Pentru S4d, verificarea de vizibilitate se reduce la:
|
|
|
|
```foxpro
|
|
lVizibil = !Inlist(poDate.tip, 8, 9) And ;
|
|
(poDate.in_valuta = 1 Or (Used('crsfactura') And Reccount('crsfactura', 1) > 0) )
|
|
```
|
|
|
|
unde `Reccount('crsfactura', 1)` inseamna "exista macar un rand cu `tip_valuta=1`" — in practica se
|
|
scrie ca `Calculate Cnt() To lnLinii For tip_valuta=1` sau `Select Count(*) From crsfactura Where
|
|
tip_valuta=1 Into Array laCnt`, ca sa nu depinda de pozitia recordului curent din grid.
|
|
|
|
**Cand se reevalueaza — pe evenimentul deja existent de recalcul, nu pe un timer nou.** Atat
|
|
`do_adauga_articol` (`:17124-17393`) cat si `do_sterge` (`:18619-18704`) se termina cu apelul
|
|
**`Thisform.do_calculeaza_totaluri()`** (`:17360`, `:18687`) — acesta e deja punctul unic prin care
|
|
formularul "stie" ca s-a schimbat compozitia liniilor (totaluri, discount pe articole etc). E locul
|
|
natural unde se adauga si reevaluarea `clb_zi_curs`: dupa fiecare adaugare de linie **si** dupa fiecare
|
|
stergere, `do_calculeaza_totaluri` (sau un apel adaugat imediat dupa el in cele doua metode) reface
|
|
`lVizibil` de mai sus si seteaza `Thisform.clb_zi_curs.Visible = lVizibil`.
|
|
|
|
**Modificarea unei linii existente** (schimbare cantitate/pret pe o linie deja adaugata) nu schimba
|
|
`tip_valuta` — acesta e o proprietate a politicii de pret, fixata la adaugare, nu editabila pe grid.
|
|
Deci nu exista eveniment suplimentar de "editare linie" care sa afecteze conditia — doar adaugare si
|
|
stergere pot muta numarul de linii cu `tip_valuta=1` de la 0 la >0 sau invers. (Verificat: nu exista
|
|
`do_modifica` separat pe `frm_facturare_articole2` in indexul de simboluri — editarea unei linii merge
|
|
prin acelasi `do_adauga_articol`/dialog, care oricum recheama `do_calculeaza_totaluri`.)
|
|
|
|
**De ce nu `poDate.in_valuta`-style (o singura evaluare la Init):** documentul poate incepe fara nicio
|
|
linie in valuta si poate primi una la mijlocul sesiunii de facturare — exact cazul pe care unificarea
|
|
il face posibil de rezolvat (azi antetul se inchide inainte sa existe `crsfactura`). Evaluarea trebuie
|
|
sa fie pe eveniment, nu pe `Init`.
|
|
|
|
---
|
|
|
|
## 3. Trecerea inapoi — ultimul articol in valuta e sters
|
|
|
|
**Recomandare: ascunde din nou (simetric), dar NU goli `poDate.zi_curs`.**
|
|
|
|
Argumente pe tiparul deja folosit, nu pe teorie:
|
|
|
|
- `lb_cursuri`/`grd_cursuri` (punctul 1) folosesc deja idiomul "vizibil doar cand `Reccount(...) > 0`"
|
|
— o conditie booleana simpla, reevaluabila oricand fara efecte laterale, pentru ca `RemoveObject`/
|
|
re-adaugare (sau `Visible=`) nu ating proprietatea de date din spate (`poDate.zi_curs`). Simetria
|
|
(ascunde la 0, arata la >0) e deja tratata ca stare normala pentru acel control, nu ca o exceptie de
|
|
construit.
|
|
- `poDate.zi_curs` **nu se goleste niciodata** in tot codul existent (confirmat exhaustiv in
|
|
`zi_curs_validare.md` §5-6) — nici la `RemoveObject` (tip 8/9), nici la ascunderea `lb_cursuri`. Deci
|
|
ascunderea campului de curs cand ultima linie in valuta dispare **nu pierde valoarea**: daca userul
|
|
adauga din nou o linie in valuta in aceeasi sesiune, campul reapare cu **aceeasi data** pe care o avea
|
|
inainte de ascundere (fie cea introdusa manual, fie implicitul de la `Init`), nu resetata la azi.
|
|
- Alternativa ("ramane vizibil o data aratat") ar introduce un tip nou de stare per-sesiune
|
|
(`lFostVizibilCandva`) fara niciun precedent in cod si fara beneficiu clar — userul care sterge
|
|
singura linie in valuta de pe o factura in lei nu mai are, de fapt, niciun motiv sa vada/editeze
|
|
cursul; a lasa campul vizibil ar fi exact inconsistenta pe care decizia 15 vrea sa o elimine.
|
|
|
|
**Exceptie de la simetrie, ceruta de plan (sectiunea M, `:2635-2638`):** cand vine eroarea Oracle
|
|
`-20005` cu campul ascuns, campul **trebuie adus inapoi vizibil**, indiferent de `Reccount`-ul curent —
|
|
vezi punctul 5. Acesta e singurul caz in care regula reactiva simetrica se suspenda explicit.
|
|
|
|
---
|
|
|
|
## 4. Interactiunea cu `in_valuta` la nivel de document
|
|
|
|
`poDate.in_valuta` e stabilit o singura data, in `Init`, exclusiv din `tnTip`/`tnIdSet`
|
|
(`ofacturare_comun.prg:248-250`, confirmat exhaustiv in `valuta_si_curs.md` §1) — **nu exista cale de
|
|
cod care sa-l schimbe dupa Init**, iar controlul de alegere a valutei (`Ct_clb_valuta`) e chiar
|
|
eliminat din formular cand `in_valuta=0` (`ofacturare.vc2:9725-9728`), deci operatorul nu are de unde
|
|
sa-l aleaga manual. Prin urmare **intrebarea "ce se intampla daca operatorul schimba valuta documentului
|
|
dupa ce a adaugat linii" nu se poate pune in arhitectura actuala** — nu exista control care sa permita
|
|
acea schimbare. Documentul e in valuta sau nu inca de la alegerea tipului (`tnTip`), inainte ca vreo
|
|
linie sa existe.
|
|
|
|
Pentru S4d, consecinta e simpla: `poDate.in_valuta=1` e o conditie **statica** pe toata durata sesiunii
|
|
de facturare — cand e adevarata, `clb_zi_curs` ramane vizibil necontenit (ca azi), indiferent de ce se
|
|
intampla pe grid; conditia reactiva descrisa la punctul 2 conteaza **doar** pentru ramura
|
|
`in_valuta=0`. Nu exista tranzitie `in_valuta 0->1` sau `1->0` de tratat.
|
|
|
|
---
|
|
|
|
## 5. Mesajul de eroare `-20005`
|
|
|
|
### 5.1 Unde se ridica azi
|
|
|
|
`pack_facturare.verifica_cursuri_valute` (Oracle, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16247-16274`):
|
|
|
|
```sql
|
|
16268 IF V_NUME_VALUTE IS NOT NULL THEN
|
|
16269 RAISE_APPLICATION_ERROR(-20005,
|
|
16270 'Nu este setat cursul din data de ' ||
|
|
16271 to_char(V_DATA_CURS, 'DD/MM/YYYY') ||
|
|
16272 ' pentru ' || V_NUME_VALUTE || '!');
|
|
16273 END IF;
|
|
```
|
|
|
|
`V_NUME_VALUTE` e un `STRINGAGG` (`:16252-16266`) peste **toate** valutele din
|
|
`FACT_VPRETURI_UTILIZATOR` ale utilizatorului curent care nu au curs valabil la `V_DATA_CURS` — exclude
|
|
doar moneda nationala. **Mesajul contine deja data si numele valutei/valutelor** — nu e un cod generic.
|
|
Apelata neconditionat din `cursor_preturi` (`:2153`), si delegat din `cursor_contract` (jumatatea
|
|
`crsarticole`, `s4_cautare_articole_server.md:83-85`).
|
|
|
|
### 5.2 Traseul pana la operator, azi
|
|
|
|
```
|
|
ofacturare.prg:313-317 (identic ofacturare.prg:828-832, factureaza2)
|
|
If lnSucces < 0
|
|
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
|
If goExecutor.nEroare = 20005
|
|
vizualizeaza_curs(poDate.zi_curs)
|
|
ENDIF
|
|
```
|
|
|
|
`goExecutor.oPrelucrareEroare()` (`COMUN\programe\oproceduri_comune.prg:626-639`) extrage **textul
|
|
brut** dintre `ORA-20xxx:` si urmatorul `ORA` din mesajul Oracle — pentru erori in intervalul
|
|
20000-20999 (cazul de aici), operatorul vede **exact** textul PL/SQL de mai sus, netrunchiat, nemodificat.
|
|
Deci raspunsul la "mesajul spune care valuta si ce zi lipsesc" e: **da, deja o face**, azi, inainte de
|
|
orice modificare S4d.
|
|
|
|
### 5.3 Ce schimba decizia 42
|
|
|
|
Decizia 42 (plan `:512-516`) **nu schimba textul** mesajului — schimba **domeniul de valute verificate**,
|
|
si **doar pe varianta filtrata a cautarii** (S4 punctul 1, `cursor_preturi` chemat per-articol-cautat,
|
|
nu la incarcarea in masa). Azi (fara decizia 42), pe orice apel al lui `cursor_preturi`,
|
|
`V_NUME_VALUTE` poate include valute complet nelegate de articolul pe care userul tocmai il cauta —
|
|
de exemplu userul cauta un articol in RON, dar mesajul ii spune ca lipseste cursul pentru EUR, pentru
|
|
ca EUR apare undeva in politicile lui de pret. Dupa decizia 42, pe cautarea filtrata, verificarea se
|
|
restrange la valuta randului adus — deci mesajul (acelasi format text) va numi, natural, **doar
|
|
valuta relevanta pentru cautarea curenta**, nu un agregat strain.
|
|
|
|
**Rezerva importanta, de citit impreuna cu S4d:** decizia 42 se aplica explicit "pe varianta filtrata
|
|
a cursoarelor" — adica pe calea noua de cautare per-articol introdusa de S4 punctul 1, folosita azi
|
|
doar pe **ramura de lista de preturi** (`s4_cautare_articole_server.md:68-69`: "S4 se aplica curat doar
|
|
pe ramurile de lista de preturi"). Pe **comanda / aviz / contract**, incarcarea in masa a articolelor
|
|
(bookkeeping obligatoriu, `crsarticole` citit si scris de `do_adauga_tot`/`do_sterge`/`do_scrie_factura`)
|
|
**ramane neschimbata**, deci `verifica_cursuri_valute` tot ruleaza neconditionat (toate valutele din
|
|
politicile utilizatorului), la incarcarea initiala a grilei — nu doar pe articolul cautat. **Pe aceste
|
|
tipuri, mesajul poate inca numi o valuta care n-are legatura cu documentul curent**, indiferent de
|
|
decizia 42 — asta ramane o diferenta reala fata de "campul e ascuns pentru ca documentul nu are nevoie
|
|
de curs", si intra la riscuri (punctul 10).
|
|
|
|
### 5.4 Ce se schimba prin S4d — nu textul, ci vizibilitatea campului dupa eroare
|
|
|
|
Cerinta explicita din plan, sectiunea M (`:2635-2638`):
|
|
|
|
> Ascunderea datei de curs poate lasa un document fara curs corectabil [...] utilizatorul nu mai are
|
|
> unde sa corecteze data daca i-am ascuns campul. **Regula din M trebuie sa aduca inapoi campul in
|
|
> exact acest caz.**
|
|
|
|
Implementare propusa, la punctul de interceptare deja existent:
|
|
|
|
```foxpro
|
|
* ofacturare.prg:313-317 (si simetric la :828-832)
|
|
If lnSucces < 0
|
|
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
|
If goExecutor.nEroare = 20005
|
|
If Type('thisform.clb_zi_curs.visible')<>'U' And !thisform.clb_zi_curs.Visible
|
|
thisform.clb_zi_curs.Visible = .T. && aduce campul inapoi, indiferent de conditia reactiva
|
|
Endif
|
|
vizualizeaza_curs(poDate.zi_curs)
|
|
ENDIF
|
|
```
|
|
|
|
Nota: `thisform` aici nu e formularul de antet (deja inchis la acest punct in arhitectura veche) — e
|
|
motivul pentru care acest fix e legat de rezultatul S3/S2 asupra bug-ului #16 (punctul 6): daca
|
|
antetul unificat ramane deschis/persistent (nu recreat), `thisform.clb_zi_curs` exista si poate fi
|
|
readus vizibil direct; daca arhitectura tot recreaza un formular nou dupa eroare, readucerea vizibila
|
|
trebuie facuta in `Init`-ul noii instante, verificand acelasi semnal (`goExecutor.nEroare=20005` din
|
|
iteratia anterioara, sau un flag explicit propagat).
|
|
|
|
**Textul mesajului propus de pastrat neschimbat** (deja corect): *"Nu este setat cursul din data de
|
|
DD/MM/YYYY pentru <valuta>!"*. Nu se propune inlocuirea lui — se propune doar sa nu mai fie surprinzator
|
|
faptul ca operatorul nu are unde sa corecteze, prin readucerea campului.
|
|
|
|
---
|
|
|
|
## 6. Verificarea daca #16 dispare
|
|
|
|
**Ce e #16:** `COMUN\docs\todos.txt:45` — "la revenire din formularul de curs valutar, focusul revine
|
|
[...] pe TIP DOCUMENT, si la iesire din serie se regenereaza numar act". Citat si in plan
|
|
`:1926-1927`, `:2665-2668`.
|
|
|
|
**Deja cercetat, cu verdict**, in `docs\cercetare\s3_portare_antet.md` §6 (runda 9), **inainte** de
|
|
aceasta sesiune — S4d nu redeschide cercetarea, o **confirma si o leaga** de domeniul propriu (eroarea
|
|
`-20005`, singurul declansator relevant pentru S4d):
|
|
|
|
- **#16 nu e un bug de focus.** E o bucla de reincercare (`ofacturare.prg:174-571`, `Do While
|
|
lnRaspuns=6`) care trateaza **orice** esec Oracle la incarcarea cursorului de articole ca pe un "DA,
|
|
utilizatorul vrea alt document" — `lnRaspuns` nu se reseteaza pe ramura de eroare
|
|
(`ofacturare.prg:555-564`, in `Else`, niciodata atinsa cand `lnSucces<0`), deci bucla externa reintra
|
|
automat si **recreeaza formularul de antet de la zero** (`Createobject`, `:230`). `Init`-ul noii
|
|
instante seteaza focus neconditionat pe `ct_clb_fdoc` (`:9788-9793`) — asta e "focusul care revine pe
|
|
tip document" — si `LostFocus`-ul care urmeaza cheama neconditionat `clb_serie_act`, care aloca un
|
|
numar nou (`serii_numere.vc2:122-127`) — asta e "regenerarea numarului".
|
|
- **Cauza reala e in `ofacturare.prg` (bucla de emitere, cod comun suitei), nu in formularul de antet.**
|
|
`s3_portare_antet.md:337-350` e explicit: portarea antetului (S3) NU rezolva automat #16 — arhitectura
|
|
unificata (un singur `Init` persistent, antetul nu mai e un obiect separat distrus/recreat de apelant)
|
|
**are sansa reala** sa-l elimine ca efect secundar, **dar numai daca implementarea nu recreaza
|
|
formularul intreg la reincercare dupa o eroare Oracle** — ceea ce reproduce bugul identic, doar mutat.
|
|
Verdictul e legat explicit de **S2**, nu de S3/S4d: bucla traieste in procedura de emitere, nu in
|
|
formular.
|
|
|
|
**Raspunsul specific S4d, cerut de plan** ("Verifica in acelasi timp daca #16 dispare — vezi M"):
|
|
**da, S4d confirma exact scenariul care declanseaza #16** — eroarea `-20005` de la
|
|
`verifica_cursuri_valute` e unul dintre cazurile `lnSucces<0` care intra pe ramura problematica a
|
|
buclei (`vizualizeaza_curs(poDate.zi_curs)` la `:315`, chiar linia care deschide `frm_curs`, exact
|
|
recuperarea citata in reclamatia originala). Deci daca S2/S3 rezolva #16 (antet persistent, fara
|
|
recreare), **S4d beneficiaza direct** — dupa `-20005`, userul revine in acelasi antet unificat
|
|
(nu unul nou), campul `clb_zi_curs` readus vizibil (punctul 5.4) ramane exact acolo unde a fost adus
|
|
inapoi, fara sarituri de focus si fara renumerotare. Daca S2/S3 NU rezolva #16 (recreare inca prezenta),
|
|
S4d **nu il agraveaza si nu il repara** — comportamentul ramane identic cu azi pe acest punct, dar
|
|
readucerea campului vizibil (5.4) trebuie facuta in `Init`-ul noii instante, nu presupusa mostenita.
|
|
|
|
**Concluzie:** #16 **nu e in perimetrul de implementare al S4d** (e S2, per plan `:1944`), dar S4d
|
|
**depinde de rezultatul lui** pentru UX-ul complet al recuperarii de eroare (5.4) — de marcat explicit
|
|
ca dependenta la implementare, nu de reinvestigat.
|
|
|
|
---
|
|
|
|
## 7. Pasi de implementare, ordonati
|
|
|
|
Fiecare pas lasa suita functionala; calea veche (`frm_date_factura`/`frm_facturare_articole2` cum sunt
|
|
azi) nu se sterge in etapa I.
|
|
|
|
1. **Adauga functia de evaluare a conditiei reactive** pe formularul unificat (metoda noua, de ex.
|
|
`do_actualizeaza_vizibilitate_curs`), care calculeaza `lVizibil` conform formulei din punctul 2 si
|
|
seteaza `Visible` pe controlul `clb_zi_curs` mostenit din prototipul `frm_facturare_articole2`
|
|
(`ofacturare.vc2:16285-16305`) — **nu un control nou**, cel existent, ale carui evenimente de
|
|
sincronizare (`Clb_dataact...LostFocus`, `:19219-19222`) raman neschimbate (deja garda pe
|
|
`Type(...)<>'U'`, deci tolereaza si eliminare, nu doar `Visible=.F.`).
|
|
*Gata cand:* metoda exista, se poate apela manual (fara UI), si intoarce `.T.`/`.F.` corect pe cele
|
|
patru combinatii din punctul 8 (tip retur / nu, `in_valuta` 0/1, cu/fara linie `tip_valuta=1`).
|
|
2. **Cheama metoda din pasul 1 la sfarsitul `do_adauga_articol` si `do_sterge`**, imediat dupa (sau in)
|
|
`Thisform.do_calculeaza_totaluri()` (`:17360`, `:18687` in prototip — liniile echivalente in
|
|
formularul unificat, dupa portarea din S3/S4).
|
|
*Gata cand:* adaugarea unei linii cu `tip_valuta=1` pe o factura in lei fara alte linii de acest fel
|
|
face campul vizibil imediat, fara Refresh manual; stergerea ultimei asemenea linii il ascunde din nou
|
|
(simetric, punctul 3), fara sa goleasca `poDate.zi_curs`.
|
|
3. **Verifica initializarea la deschiderea formularului** (cazul `in_valuta=1` sau document reincarcat
|
|
cu linii deja existente, ex. la editare factura emisa) — `Init`-ul unificat trebuie sa apeleze aceeasi
|
|
metoda o data, dupa ce `crsfactura` e populat, nu doar sa se bazeze pe evenimentele de adaugare/
|
|
stergere (care nu ruleaza la incarcarea initiala a unui document existent).
|
|
*Gata cand:* deschiderea unei facturi existente (in lei, cu linii in valuta deja salvate) arata
|
|
campul corect de la primul `Show()`, fara sa fie nevoie de o adaugare/stergere care sa-l declanseze.
|
|
4. **Elimina campul neconditionat pe retur (tip 8/9)**, ca azi — verifica ca formula din pasul 1 include
|
|
deja `!Inlist(poDate.tip,8,9)` inaintea oricarei alte conditii, ca sa nu-l readuca vizibil din greseala
|
|
printr-o linie in valuta pe un retur (desi returul nu foloseste `zi_curs`, vezi `zi_curs_validare.md`
|
|
§4c/§6 — comportamentul trebuie sa ramana identic azi, nu doar "fara efect").
|
|
*Gata cand:* pe orice tip 8/9, campul e absent indiferent de continutul grilei.
|
|
5. **Readu campul vizibil la eroarea `-20005`** (punctul 5.4) — modifica ramura `goExecutor.nEroare=20005`
|
|
din bucla de emitere (`ofacturare.prg:313-317`/`:828-832`, sau echivalentul ei in noua arhitectura
|
|
integrata cu formularul unificat, cf. S3 pasul 6) ca sa forteze `Visible=.T.` pe `clb_zi_curs` inainte
|
|
de a deschide `frm_curs`.
|
|
*Gata cand:* pe o factura in lei cu campul ascuns, o eroare `-20005` simulata (curs lipsa de test)
|
|
face campul vizibil imediat dupa mesaj, inainte sau odata cu deschiderea `frm_curs`.
|
|
6. **Coordoneaza cu decizia 42 (S4 punctul 1)**: cand acea poveste implementeaza restrangerea
|
|
`verifica_cursuri_valute` la valuta articolului cautat, verifica ca textul afisat prin
|
|
`oPrelucrareEroare()` (punctul 5.1-5.2, neschimbat) tot numeste corect valuta si data — nu necesita
|
|
modificare pe partea VFP, doar confirmare ca noul parametru Oracle e transmis corect din calea de
|
|
cautare filtrata.
|
|
*Gata cand:* pe cautarea filtrata (dupa decizia 42), mesajul `-20005` numeste doar valuta cautata, nu
|
|
un agregat.
|
|
7. **Regresie pe formularul vechi**: confirma ca niciuna din modificarile 1-6 nu atinge
|
|
`frm_date_factura`/`frm_facturare_articole` (calea veche, ramasa in productie) — toate modificarile
|
|
se fac pe clasele formularului unificat / prototip, nu pe cele originale.
|
|
*Gata cand:* `svn diff`/`git diff` arata modificari doar in clasele noii cai.
|
|
|
|
---
|
|
|
|
## 8. Cum se verifica
|
|
|
|
Pe probele din plan (`:2243-2246`), plus cazurile suplimentare care rezulta din punctele 2-6:
|
|
|
|
1. **Factura in lei, fara articole in valuta** — `clb_zi_curs` nu apare la deschidere; documentul se
|
|
emite corect (acelasi rezultat `VANZARI`/`ACT`/`RUL` ca azi, criteriul din S3 §7).
|
|
2. **Aceeasi factura, dupa adaugarea unui articol cu pret in valuta** — campul apare imediat, cu data
|
|
implicita deja completata (data documentului, de la `Init`/`Reset`, nemodificata).
|
|
3. **Stergerea articolului din pasul 2** (singurul cu `tip_valuta=1`) — campul dispare din nou; valoarea
|
|
din `poDate.zi_curs` ramane cea introdusa/implicita (verificabil citind proprietatea direct, nu doar
|
|
vizual).
|
|
4. **Readaugarea unui alt articol in valuta, in aceeasi sesiune** — campul reapare cu **aceeasi** data
|
|
ca la pasul 2, nu resetata.
|
|
5. **Factura in valuta (`in_valuta=1`)** — campul apare mereu, indiferent de continutul grilei, exact ca
|
|
azi (fara regresie pe cazul deja functional).
|
|
6. **Retur (tip 8/9)** — campul absent indiferent de linii; document salvat corect (cf. precedent deja
|
|
existent).
|
|
7. **`-20005` cu campul ascuns** (factura in lei, curs de test lipsa pentru o valuta din politicile
|
|
utilizatorului) — mesajul numeste valuta si data lipsa (deja azi); campul devine vizibil dupa eroare.
|
|
8. **Editare factura emisa** (cf. #6, formular deja in lucru separat) — deschiderea unei facturi in lei
|
|
cu linii in valuta deja salvate arata campul corect de la primul `Show()` (pasul 3 de implementare).
|
|
9. **Dupa decizia 42** — cautarea filtrata a unui articol cu curs lipsa arata mesaj cu o singura valuta
|
|
(a articolului cautat), nu un agregat.
|
|
|
|
---
|
|
|
|
## 9. Ce nu se poate testa headless
|
|
|
|
- **Focusul si secventa reala `SetFocus`/`LostFocus`/`Visible=`** pe controale UI reale — capcana deja
|
|
cunoscuta (`grid-coloane-nu-se-materializeaza-headless`, memorie de proiect): sub harness `-A -T`,
|
|
proprietatile de grid/vizibilitate nu se materializeaza identic cu UI vizibil. Verificarea
|
|
vizibilitatii reactive (punctele 2-3) trebuie facuta fie prin verificare directa a proprietatii
|
|
`.Visible` dupa apelul metodei (fara `Show()`), fie cu `vfp_ui_harness.ps1`, UI vizibil.
|
|
- **Eroarea Oracle `-20005` reprodusa real** — necesita fie date de test cu un curs lipsa garantat pe o
|
|
fereastra de date controlata (manipulare de date, nu de UI), fie mock pe `goExecutor` care simuleaza
|
|
`nEroare=20005` fara conexiune reala — niciuna verificata ca exista deja in suita de teste.
|
|
- **Deschiderea modala a `frm_curs`** (`vizualizeaza_curs`) — `Show(1)` modal, aceeasi limitare generala
|
|
a testarii headless pe formulare modale.
|
|
- **Bug #16 propriu-zis** (secventa reala de focus dupa recreare de formular) — deja marcat netestabil
|
|
headless in `s3_portare_antet.md` §8; S4d nu adauga o cale noua de testare, doar depinde de acelasi
|
|
rezultat.
|
|
|
|
---
|
|
|
|
## 10. Riscuri si de decis de Marius
|
|
|
|
1. **Incarcarea in masa pe comanda/aviz/contract ramane neconditionata dupa decizia 42** (punctul 5.3).
|
|
Pe aceste tipuri, `-20005` poate inca numi o valuta nelegata de documentul curent, chiar daca `S4d`
|
|
ascunde corect campul pe baza continutului grilei. **Recomandare:** de discutat daca extinderea
|
|
restrangerii decizia-42-style merita si pe incarcarea in masa (poveste separata, posibil in S4 sau
|
|
intr-o continuare a deciziei 42), sau se accepta ca diferenta cunoscuta, documentata aici.
|
|
2. **Cine seteaza `Visible=.T.` la `-20005` cand antetul ar fi fost recreat** (daca #16 nu se rezolva
|
|
complet in S2/S3 pana la implementarea S4d) — punctul 5.4/6 presupune `thisform` = acelasi formular
|
|
persistent; daca arhitectura finala tot recreaza o instanta, logica trebuie mutata in `Init`, cu un
|
|
semnal explicit propagat (ex. proprietate `poDate.lCursLipsa` sau echivalent) ca sa stie noua instanta
|
|
sa arate campul. **Recomandare:** de tratat ca parte a pasului 6 din `s3_portare_antet.md` (integrarea
|
|
buclei de emitere), nu izolat in S4d.
|
|
3. **`frm_date_aviz`/`frm_date_aviz_lucrare` (tipurile 27, 30) nu intra in perimetrul imediat** — daca
|
|
formularul unificat le preia mai tarziu, validarea neconditionata de la `:8076` trebuie aliniata la
|
|
aceeasi regula reactiva (azi ramane necondiționata, per `zi_curs_validare.md` §6). **Recomandare:** nu
|
|
se atinge acum; se trateaza cand/daca acele tipuri intra in unificare.
|
|
4. **Simetria "ascunde la 0 linii" (punctul 3) e o recomandare, nu o certitudine ceruta explicit de
|
|
plan** — planul cere doar "apare cand...", nu specifica explicit comportamentul la disparitia ultimei
|
|
linii. **Recomandare:** simetria propusa aici (argumentata pe tiparul `lb_cursuri`), dar de confirmat
|
|
cu Marius inainte de implementare, pentru ca schimba usor experienta (campul "clipeste" la
|
|
adaugare/stergere repetata a aceleiasi linii).
|