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

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

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

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

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

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:

  1. Lista de coloane din SELECT (ofacturare_editare.prg:201-208, IncarcaArticoleFactura) - 20 coloane explicite (vd.id_vanzare_det ... nv.nume_val).
  2. CREATE CURSOR tvd din fallback-ul de eroare Oracle, in aceeasi functie (ofacturare_editare.prg:214-216) - acelasi 20 de campuri, aceleasi tipuri, scrise separat.
  3. CREATE CURSOR tvd placeholder din Load() (omodificari.vc2, ~14076, If !Used('tvd') ...)
    • a treia copie, byte-cu-byte aceeasi structura ca (2), intr-un alt fisier.
  4. Coloanele gridului grdArticoleFactura (omodificari.vc2, ADD OBJECT ~12258-12454) - 13 ControlSource/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 .vcx intr-un .prg, n-o elimini. Cost mare: e o schimbare de tipar fara precedent pe acest formular - gridurile surori grdRulaje/grdRulajeObinv de pe PAGE1/PAGE2 (omodificari.vc2:8694, :10517, 63 si 61 de coloane) sunt 100% declarative in .vcx, cu ColumnOrder/DynamicForeColor/ InputMask per coloana - cautabile cu vfp_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 din SELECT). Pentru coloana STEARSA/REDENUMITA e mai rau: azi eroarea e prinsa curat la nivel de SQL (lnSucces < 0, punct de control unic); cu SELECT * 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 un ControlSource inexistent - exact tipul de capcana (dialog nativ VFP) pe care runda asta a trebuit sa-l ocoleasca separat pentru tvd (rec_s4_runda1.md, blocajul #3). Tiparul corect pentru SELECT * 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 - vezi ofacturare_editare.prg:54,69,100) care izoleaza exact coloanele si numele expuse catre VFP. Replicarea corecta a tiparului ar insemna un view nou vvanzari_articole/similar pentru VANZARI_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: azi IncarcaVanzareNota/IncarcaArticoleFactura (ofacturare_editare.prg:174-177, :213-217) returneaza .T. cu cursor gol pe lnSucces < 0, spre deosebire de funcita sora IncarcaCursoareModificareNota din ACELASI FISIER (ofacturare_editare.prg:59-62), care afiseaza AMESSAGEBOX(goExecutor.cEroare,...). Adaugarea aceluiasi AMESSAGEBOX (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 pentru trul/trul_obinv in partea de campuri neprovenite direct din view (vezi Column3-Column15 la grdRulaje, omodificari.vc2:8721-8829, multe cu ControlSource pe 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:161 si :175 (IncarcaVanzareNota), byte-cu-byte identic. Fix simplu: un singur CREATE CURSOR la 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 din IncarcaArticoleFactura (ofacturare_editare.prg:214-216) si placeholder-ul din Load() (omodificari.vc2, ~14076). Identice ca structura. O functie comuna in ofacturare_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 grid grdArticoleFactura sunt calificate cu tvd. (omodificari.vc2, Column1-Column13, ex. "tvd.denumire", "tvd.codmat") - exact regula din COMUN\docs\capcana_grid_controlsource.md pentru formulare cu 2+ grid-uri (formularul are acum trei: grdRulaje, grdRulajeObinv, grdArticoleFactura). De comparat cu gridurile surori grdRulaje/grdRulajeObinv, unde o parte din coloane au ControlSource NECALIFICAT (ex. "dataact", "codmat", "denumire", "pret", "cant" la omodificari.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.md pct. 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 e Go Top In tact (discutata la punctul 1) - nu e cazul "GO pe un Recno() capturat/primit ca parametru" din conventie_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 TABLE pe cursor din goExecutor.oExecute(): nu se foloseste in diff, nu se aplica.
  • Nu am gasit cod mort introdus, nici nume inconsistente - lAreArticoleVanzari/nIdVanzare/ nTipVanzare respecta exact conventia Hungarian deja folosita pe restul clasei (lavertizatexigibilizare, nid_set etc.).
  • 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/Width scrise manual in .vcx) - e identic cu tiparul din PAGE1/PAGE2, cautabil cu vfp_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 fiecare Text1 e corect si suficient pentru o runda "doar afisare" - nu trebuie dus mai departe acum. PageCount comutat intre 2 si 3 in Show() e simplu si testat, nu are nevoie de alta arhitectura.
  • Placeholder-ul CREATE CURSOR tvd in Load() ca sa evite dialogul nativ "Open" - solutia corecta pentru capcana documentata deja in rec_s4_runda1.md; singura problema e ca structura lui e duplicata (punctul 3), nu ca exista.
  • Filtrul compus cod + nract + serie_act + dataact din IncarcaVanzareNota - justificat solid de docs\progres.md (VANZARI.COD nedovedit unic, coliziune verificata pe cod=1139934), corect implementat si testat pe cazul de coliziune. Nu-l simplifica inapoi la cod singur.

Recomandare finala

Inainte de commit, in ordinea asta:

  1. Verifica riscul de la punctul 1 (Go Top In tact) pe un caz real cu INCASARE ca prim rand - e singurul lucru care poate face pagina PAGE3 sa lipseasca gresit pe facturi reale.
  2. Adauga AMESSAGEBOX pe erorile Oracle din IncarcaVanzareNota/IncarcaArticoleFactura (punctul 2, varianta C) - raspunsul corect si ieftin la cerinta de ancorare a lui Marius.
  3. Opional, daca ramane timp: elimina cele doua duplicari de CREATE CURSOR (punctul 3). Restul (structura declarativa a gridului, filtrul compus, placeholder-ul din Load()) e in regula asa cum e si nu merita atins.