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
14 KiB
Review S4 runda 1 (PAGE3 "Articole factura") - ancorare coloane + calitate cod
Review de cod, fara nicio modificare aplicata. Obiect: diff aplicat (sters)
(COMUN\clase\omodificari.vc2 clasa frm_modific2024, COMUN\programe\ofacturare_editare.prg).
Context citit: docs\cercetare\rec_s4_runda1.md, docs\progres.md (sectiunea "#6, S4 runda 1" si
decizia/nota despre pozitionarea in actactan), COMUN\docs\reguli_lucru.md,
COMUN\docs\capcana_grid_controlsource.md.
1. Riscul cel mai important - NU e despre coloane, e o pozitionare oarba in tact (corectitudine)
omodificari.vc2:14171, in Show():
IF Reccount('tact') > 0
Go Top In tact
IncarcaVanzareNota(tact.cod, tact.nract, tact.serie_act, tact.dataact)
...
tact poate avea MAI MULTE randuri pentru aceeasi nota (o factura + o incasare, etc.), ordonate
dupa id_act (ofacturare_editare.prg:54, order by id_act). Exact acest tipar - "Go Top orb pe
actactan/tact" - e documentat ca riscant in docs\cercetare\rec_pozitionare_actactan.md (§2):
pe 39 de facturi in schema de dev, primul rand dupa id_act e INCASARE/INCASARE NUMERAR,
cu id_fact = facturii minus 1, nu al facturii insesi. Acolo era vorba de id_fact; aici codul
nou citeste tact.nract/tact.serie_act/tact.dataact de pe randul gasit de Go Top - daca
INCASAREA are propriile ei nract/serie_act/dataact (referinta la chitanta, nu la factura),
filtrul compus din IncarcaVanzareNota (cod + nract + serie_act + dataact) cauta valorile
GRESITE in VANZARI -> 0 randuri gasite -> lAreArticoleVanzari = .F. silentios, desi factura
chiar are vanzare asociata. Nu inseamna eroare vizibila, ci pagina PAGE3 lipsind pe documente care
ar trebui sa o aiba.
Nu e o certitudine (n-am verificat direct daca nract/serie_act/dataact difera intre randul
INCASARE si randul facturii pe cele 39 de documente), dar riscul e concret si documentat de proiect
insusi pentru exact acelasi tipar de pozitionare, pe alt camp din acelasi cursor. Testele existente
(test_page3_articole.prg:70 si :167) repeta acelasi Go Top In tact, deci nu-l acopera -
"zero cazuri in date" nu e dovada ca nu exista (COMUN\docs\reguli_lucru.md pct. 6).
Recomandare: inainte de a inchide runda, verifica direct pe unul din cele ~39 de documente din
rec_pozitionare_actactan.md (sau cauta altele cu INCASARE ca prim rand si rand de vanzare in
VANZARI) daca Go Top da alt nract/serie_act/dataact decat cel corect. Daca da, nu exista
azi in cod un anchor gata de refolosit la momentul Show() (ales e flag de UI, populat de
utilizator din grid, gol la deschidere - nu ajuta aici); ar trebui gasit un criteriu de pozitionare
mai bun, posibil analog cu ce se recomanda in rec_pozitionare_actactan.md §5 pentru id_fact.
Efort: mic ca sa verifici (o interogare + 1-2 randuri de test), nedeterminat ca sa remediezi -
depinde ce arata verificarea. Prioritate maxima inainte de commit, e mai important decat
subiectul de ancorare de coloane cerut initial.
2. Cerinta principala: ancorarea codului de structura tabelelor
Ce e hardcodat azi, si ce se rupe
Structura e prezenta in 3-4 locuri separate, toate manuale:
- Lista de coloane din
SELECT(ofacturare_editare.prg:201-208,IncarcaArticoleFactura) - 20 coloane explicite (vd.id_vanzare_det ... nv.nume_val). CREATE CURSOR tvddin fallback-ul de eroare Oracle, in aceeasi functie (ofacturare_editare.prg:214-216) - acelasi 20 de campuri, aceleasi tipuri, scrise separat.CREATE CURSOR tvdplaceholder dinLoad()(omodificari.vc2, ~14076,If !Used('tvd') ...)- a treia copie, byte-cu-byte aceeasi structura ca (2), intr-un alt fisier.
- Coloanele gridului
grdArticoleFactura(omodificari.vc2, ADD OBJECT ~12258-12454) - 13ControlSource/Header.Caption/Width/InputMask, cate un bloc pe coloana.
Coloana ADAUGATA in VANZARI_DETALII (sau in nom_articole/nom_gestiuni/nom_valute): NU
rupe nimic. SELECT-ul explicit o ignora, cursorul tvd nu o capata, gridul (13 coloane fixe) nu o
cere. Zero impact pana cineva decide s-o afiseze - caz in care tot trebuie atinse (1) si (4) oricum,
indiferent de strategia de ancorare aleasa.
Coloana STEARSA sau REDENUMITA dintre cele 20 folosite: SELECT-ul explicit din (1) pica pe
Oracle -> lnSucces < 0 -> se intra pe fallback-ul (2), cursorul tvd gol, IncarcaArticoleFactura
returneaza .T. fara niciun mesaj vizibil (vezi punctul 3 mai jos). Practic: pagina PAGE3 arata
goala, silentios, fara semnal ca ceva s-a stricat structural. Acesta e cazul real de reparat cand se
schimba schema - nu adaugarea de coloane.
Cat de des se intampla la ROA: dupa docs\progres.md (decizia 2, sursa DDL e schema de
dezvoltare MARIUSM_AUTO, aplicata prin scripturi de migrare versionate) - stergerea/redenumirea de
coloane pe tabele active ca VANZARI_DETALII nu pare o practica frecventa (schimbarile de schema
documentate in sesiune sunt adaugari de coloane/view-uri, nu redenumiri). Deci riscul real e rar, dar
cand se intampla azi e silentios, nu zgomotos - asta conteaza mai mult decat frecventa.
Variante de ancorare, cu ce pierde fiecare
-
A. Grid construit dinamic din
AFIELDS()+ dictionar de etichete. Elimina nevoia sa atingi (4) cand se schimba coloanele afisate implicit, dar tot trebuie sa intretii un dictionar {camp -> caption/width/format} undeva - muti hardcodarea din.vcxintr-un.prg, n-o elimini. Cost mare: e o schimbare de tipar fara precedent pe acest formular - gridurile surorigrdRulaje/grdRulajeObinvde pe PAGE1/PAGE2 (omodificari.vc2:8694,:10517, 63 si 61 de coloane) sunt 100% declarative in.vcx, cuColumnOrder/DynamicForeColor/InputMaskper coloana - cautabile cuvfp_symbols.ps1/grep. Un grid dinamic ar fi unicat in tot formularul (si, dupa cat am vazut, in restul clasei) - o datorie de intretinut de unul singur, nu un castig, exact contrariul principiului "consistenta cu codul din jur" din brief. Nu recomand. -
B.
SELECT *in loc de lista explicita de coloane. Pentru coloana ADAUGATA nu aduce niciun beneficiu fata de azi (gridul tot leaga doar 13 coloane numite, indiferent cate vin dinSELECT). Pentru coloana STEARSA/REDENUMITA e mai rau: azi eroarea e prinsa curat la nivel de SQL (lnSucces < 0, punct de control unic); cuSELECT *pe un join direct pe 4 tabele, interogarea SQL reuseste oricum (nu refera explicit campul lipsa), iar eroarea apare abia la binding-ul gridului pe unControlSourceinexistent - exact tipul de capcana (dialog nativ VFP) pe care runda asta a trebuit sa-l ocoleasca separat pentrutvd(rec_s4_runda1.md, blocajul #3). Tiparul corect pentruSELECT *folosit deja de gridurile surori (trul,trul_obinv,tact) nu e pe join brut, ci pe un view Oracle dedicat (vrul_tot,vact_tot,vrul_obinv_tot- veziofacturare_editare.prg:54,69,100) care izoleaza exact coloanele si numele expuse catre VFP. Replicarea corecta a tiparului ar insemna un view nouvvanzari_articole/similar pentruVANZARI_DETALII- fezabil, dar e o migrare de schema Oracle (scripturi-migrare-db.md), nu o editare VFP; cost si coordonare mai mari decat editarea.vc2. Merita luat in calcul DACA schema chiar incepe sa se miste des pe zona asta, nu acum pentru o runda "doar afisare". -
C. Coloane declarate (ca azi), plus garda care semnaleaza divergenta la rulare. Nu schimba nimic structural - pastreaza controlul total pe ordine/latime/format, consistent 1:1 cu
grdRulaje/grdRulajeObinv. Cere doar sa nu mai fie inghitita silentios eroarea Oracle: aziIncarcaVanzareNota/IncarcaArticoleFactura(ofacturare_editare.prg:174-177,:213-217) returneaza.T.cu cursor gol pelnSucces < 0, spre deosebire de funcita soraIncarcaCursoareModificareNotadin ACELASI FISIER (ofacturare_editare.prg:59-62), care afiseazaAMESSAGEBOX(goExecutor.cEroare,...). Adaugarea aceluiasiAMESSAGEBOX(sau macar un log) pe cele doua functii noi transforma o coloana stearsa/redenumita dintr-un gol tacut intr-un semnal vizibil- fara sa schimbe deloc modul in care se intretine gridul. Cost: cateva linii, minim.
-
D. Lasat asa cum e. Argument real: e runda 1, "doar afisare" (
rec_s4_runda1.md), iar tiparul (SQL explicit + grid declarat) e identic cu ce exista deja de ani pe acelasi formular pentrutrul/trul_obinvin partea de campuri neprovenite direct din view (vezi Column3-Column15 lagrdRulaje,omodificari.vc2:8721-8829, multe cuControlSourcepe nume simplu de camp). Nu e o liabilitate noua introdusa de diff, e consistenta cu practica existenta. Singurul gol real fata de sora ei e lipsa mesajului de eroare (punctul C), nu structura declarativa insasi.
Recomandare
C, nu A sau B: adauga AMESSAGEBOX (dupa modelul IncarcaCursoareModificareNota) pe cele doua
lnSucces < 0 din IncarcaVanzareNota/IncarcaArticoleFactura. E schimbarea cu cel mai bun raport
cost/beneficiu - cateva linii, zero impact pe tipar, transforma exact riscul real (coloana
stearsa/redenumita) dintr-un gol silentios intr-un semnal vizibil. Grid dinamic (A) sau SELECT *
pe join brut (B) NU merita azi - ambele fie muta hardcodarea in alta parte fara sa reduca
intretinerea, fie inrautatesc raspunsul la exact riscul pe care vor sa-l elimine. Daca la un moment
dat VANZARI_DETALII incepe sa-si schimbe structura des, varianta corecta e B cu view Oracle
dedicat (ca la trul/tact), nu grid dinamic.
3. Duplicare de cod - structura cursorului tvd/tvanz scrisa manual de mai multe ori
CREATE CURSOR tvanz (...)(6 campuri) apare de doua ori in aceeasi functie,ofacturare_editare.prg:161si:175(IncarcaVanzareNota), byte-cu-byte identic. Fix simplu: un singurCREATE CURSORla inceputul functiei / dupa cele doua conditii de iesire timpurie, in loc de doua copii separate la 14 linii distanta. Efort: mic, cateva minute.CREATE CURSOR tvd (...)(20 campuri) apare in doua fisiere diferite: fallback-ul dinIncarcaArticoleFactura(ofacturare_editare.prg:214-216) si placeholder-ul dinLoad()(omodificari.vc2, ~14076). Identice ca structura. O functie comuna inofacturare_editare.prg(ex.CreeazaCursorTvdGol) apelata din ambele locuri ar elimina a treia copie manuala si ar garanta ca raman sincronizate cand se adauga/scoate un camp. Efort: mic-mediu (o functie noua + doua puncte de apel, testat deja indirect de suita existenta).
4. Alte observatii de calitate
- Pozitiv: toate
ControlSource-urile noului gridgrdArticoleFacturasunt calificate cutvd.(omodificari.vc2, Column1-Column13, ex."tvd.denumire","tvd.codmat") - exact regula dinCOMUN\docs\capcana_grid_controlsource.mdpentru formulare cu 2+ grid-uri (formularul are acum trei:grdRulaje,grdRulajeObinv,grdArticoleFactura). De comparat cu gridurile surorigrdRulaje/grdRulajeObinv, unde o parte din coloane auControlSourceNECALIFICAT (ex."dataact","codmat","denumire","pret","cant"laomodificari.vc2:8728-8785) - expuse in teorie la exact capcana descrisa in document daca alt cursor ajunge sa fie workarea curenta. E o expunere preexistenta, nu introdusa de acest diff, si gridul respectiv nu pare sa fi avut probleme raportate - semnalez doar ca informatie, nu ca ceva de reparat acum. - Comentariu usor peste norma: header-ul
IncarcaVanzareNota(ofacturare_editare.prg:144-147) are 4 linii; regula permite 2-3 pentru contract nebanal (reguli_lucru.mdpct. 2). Continutul e util (parametri, capcana cod-neunic, cursor lasat deschis) - as comprima usor, nu as sterge informatie. Nu blocant. GO/Recno(): singura pozitionare noua eGo Top In tact(discutata la punctul 1) - nu e cazul "GO pe un Recno() capturat/primit ca parametru" dinconventie_go_recno.md, deci acea conventie specifica nu se aplica direct, dar tot e o pozitionare pe un cursor cu mai multe randuri posibile, deci riscul de fond e inrudit.ALTER TABLEpe cursor dingoExecutor.oExecute(): nu se foloseste in diff, nu se aplica.- Nu am gasit cod mort introdus, nici nume inconsistente -
lAreArticoleVanzari/nIdVanzare/nTipVanzarerespecta exact conventia Hungarian deja folosita pe restul clasei (lavertizatexigibilizare,nid_setetc.). - Nimic de refolosit ratat: n-am gasit o functie comuna existenta pentru "gaseste randul din
VANZARI pentru o nota" sau "incarca liniile unei vanzari" inainte de acest diff - functiile noi
chiar completeaza un gol, nu dubleaza ceva ce exista deja (conform si cu
rec_s4_runda1.md).
Ce NU merita schimbat
- Tiparul declarativ al gridului (
ColumnN.ControlSource/Header.Caption/Widthscrise manual in.vcx) - e identic cu tiparul din PAGE1/PAGE2, cautabil cuvfp_symbols.ps1, si schimbarea lui ar fi o inconsistenta noua, nu o simplificare reala (vezi Variantele A/B mai sus).ReadOnly = .T.pe grid si pe fiecareText1e corect si suficient pentru o runda "doar afisare" - nu trebuie dus mai departe acum.PageCountcomutat intre 2 si 3 inShow()e simplu si testat, nu are nevoie de alta arhitectura. - Placeholder-ul
CREATE CURSOR tvdinLoad()ca sa evite dialogul nativ "Open" - solutia corecta pentru capcana documentata deja inrec_s4_runda1.md; singura problema e ca structura lui e duplicata (punctul 3), nu ca exista. - Filtrul compus
cod + nract + serie_act + dataactdinIncarcaVanzareNota- justificat solid dedocs\progres.md(VANZARI.CODnedovedit unic, coliziune verificata pecod=1139934), corect implementat si testat pe cazul de coliziune. Nu-l simplifica inapoi lacodsingur.
Recomandare finala
Inainte de commit, in ordinea asta:
- Verifica riscul de la punctul 1 (
Go Top In tact) pe un caz real cuINCASAREca prim rand - e singurul lucru care poate face pagina PAGE3 sa lipseasca gresit pe facturi reale. - Adauga
AMESSAGEBOXpe erorile Oracle dinIncarcaVanzareNota/IncarcaArticoleFactura(punctul 2, varianta C) - raspunsul corect si ieftin la cerinta de ancorare a lui Marius. - Opional, daca ramane timp: elimina cele doua duplicari de
CREATE CURSOR(punctul 3). Restul (structura declarativa a gridului, filtrul compus, placeholder-ul dinLoad()) e in regula asa cum e si nu merita atins.