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
28 KiB
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 pefrm_date_facturacandpoDate.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) sifrm_facturare_articole2.Init(:19000-19012):Acesta e cel mai apropiat precedent de "arata/ascunde in functie de continut" din codul existent — dar evalueazaofacturare.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) + ")", "") ...crscursuri(cursurile incarcate pentru toate valutele din listele de preturi ale utilizatorului), o singura data, laInit, 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 decrsfactura. Nu se poate copia ca atare pentru S4d; poate fi reutilizat doar ca idiom (verificareReccount(...) > 0care decideRemoveObject/reafisare), mutat pe alt cursor si alt eveniment (punctul 2).poArticol.tip_valuta— proprietate a politicii de pret a articolului (nu a documentului), populata lado_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 coloanacrsfactura.tip_valuta(vezi punctul 2) — acesta e semnalul pe care se construieste conditia reactiva, nupoArticol(obiect temporar, mort dupaRelease).
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:
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 candReccount(...) > 0" — o conditie booleana simpla, reevaluabila oricand fara efecte laterale, pentru caRemoveObject/ re-adaugare (sauVisible=) 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_cursnu se goleste niciodata in tot codul existent (confirmat exhaustiv inzi_curs_validare.md§5-6) — nici laRemoveObject(tip 8/9), nici la ascunderealb_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 laInit), 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):
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:
* 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 !". 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" —lnRaspunsnu se reseteaza pe ramura de eroare (ofacturare.prg:555-564, inElse, niciodata atinsa candlnSucces<0), deci bucla externa reintra automat si recreeaza formularul de antet de la zero (Createobject,:230).Init-ul noii instante seteaza focus neconditionat pect_clb_fdoc(:9788-9793) — asta e "focusul care revine pe tip document" — siLostFocus-ul care urmeaza cheama neconditionatclb_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-350e explicit: portarea antetului (S3) NU rezolva automat #16 — arhitectura unificata (un singurInitpersistent, 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.
- Adauga functia de evaluare a conditiei reactive pe formularul unificat (metoda noua, de ex.
do_actualizeaza_vizibilitate_curs), care calculeazalVizibilconform formulei din punctul 2 si seteazaVisiblepe controlulclb_zi_cursmostenit din prototipulfrm_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 peType(...)<>'U', deci tolereaza si eliminare, nu doarVisible=.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_valuta0/1, cu/fara linietip_valuta=1). - Cheama metoda din pasul 1 la sfarsitul
do_adauga_articolsido_sterge, imediat dupa (sau in)Thisform.do_calculeaza_totaluri()(:17360,:18687in prototip — liniile echivalente in formularul unificat, dupa portarea din S3/S4). Gata cand: adaugarea unei linii cutip_valuta=1pe 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 goleascapoDate.zi_curs. - Verifica initializarea la deschiderea formularului (cazul
in_valuta=1sau document reincarcat cu linii deja existente, ex. la editare factura emisa) —Init-ul unificat trebuie sa apeleze aceeasi metoda o data, dupa cecrsfacturae 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 primulShow(), fara sa fie nevoie de o adaugare/stergere care sa-l declanseze. - 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 folosestezi_curs, vezizi_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. - Readu campul vizibil la eroarea
-20005(punctul 5.4) — modifica ramuragoExecutor.nEroare=20005din 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 fortezeVisible=.T.peclb_zi_cursinainte de a deschidefrm_curs. Gata cand: pe o factura in lei cu campul ascuns, o eroare-20005simulata (curs lipsa de test) face campul vizibil imediat dupa mesaj, inainte sau odata cu deschidereafrm_curs. - Coordoneaza cu decizia 42 (S4 punctul 1): cand acea poveste implementeaza restrangerea
verifica_cursuri_valutela valuta articolului cautat, verifica ca textul afisat prinoPrelucrareEroare()(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-20005numeste doar valuta cautata, nu un agregat. - 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 diffarata 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:
- Factura in lei, fara articole in valuta —
clb_zi_cursnu apare la deschidere; documentul se emite corect (acelasi rezultatVANZARI/ACT/RULca azi, criteriul din S3 §7). - 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). - Stergerea articolului din pasul 2 (singurul cu
tip_valuta=1) — campul dispare din nou; valoarea dinpoDate.zi_cursramane cea introdusa/implicita (verificabil citind proprietatea direct, nu doar vizual). - Readaugarea unui alt articol in valuta, in aceeasi sesiune — campul reapare cu aceeasi data ca la pasul 2, nu resetata.
- Factura in valuta (
in_valuta=1) — campul apare mereu, indiferent de continutul grilei, exact ca azi (fara regresie pe cazul deja functional). - Retur (tip 8/9) — campul absent indiferent de linii; document salvat corect (cf. precedent deja existent).
-20005cu 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.- 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). - 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.Visibledupa apelul metodei (faraShow()), fie cuvfp_ui_harness.ps1, UI vizibil. - Eroarea Oracle
-20005reprodusa 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 pegoExecutorcare simuleazanEroare=20005fara 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
- Incarcarea in masa pe comanda/aviz/contract ramane neconditionata dupa decizia 42 (punctul 5.3).
Pe aceste tipuri,
-20005poate inca numi o valuta nelegata de documentul curent, chiar dacaS4dascunde 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. - Cine seteaza
Visible=.T.la-20005cand antetul ar fi fost recreat (daca #16 nu se rezolva complet in S2/S3 pana la implementarea S4d) — punctul 5.4/6 presupunethisform= acelasi formular persistent; daca arhitectura finala tot recreaza o instanta, logica trebuie mutata inInit, cu un semnal explicit propagat (ex. proprietatepoDate.lCursLipsasau echivalent) ca sa stie noua instanta sa arate campul. Recomandare: de tratat ca parte a pasului 6 dins3_portare_antet.md(integrarea buclei de emitere), nu izolat in S4d. 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:8076trebuie aliniata la aceeasi regula reactiva (azi ramane necondiționata, perzi_curs_validare.md§6). Recomandare: nu se atinge acum; se trateaza cand/daca acele tipuri intra in unificare.- 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).