sync SVN r18077
This commit is contained in:
429
docs/cercetare/s4d_zi_curs_reactiv.md
Normal file
429
docs/cercetare/s4d_zi_curs_reactiv.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user