Files
roafacturare/docs/cercetare/s3c_sursa_ca_parametru.md
2026-09-09 22:19:22 +03:00

32 KiB

S3c — Proiectare: sursa ca parametru, nu ca global

Cercetare read-only pentru povestea S3c din docs\plan_13_unificare_formular_facturare.md (#### S3c, linia 1855; decizia 12, linia 473; L.3, linia 1733; „Canalul de precompletare”, liniile 465-474). Zero modificari de cod, zero write-back, zero git_sync.ps1, zero commit — in niciun produs din D:\ROA. Depinde de S2 (docs\cercetare\s2_factureaza_unificare.md), a carui proiectare a lasat explicit semnatura lui factureaza neschimbata „pentru ca e treaba lui S3c” (sectiunea 5 a acelui raport).

Status: complet.


Verdict

Se poate face, e o interventie mica pe cod, dar textul planului supraestimeaza cat de „globala" e problema azi. goContract nu e niciodata scris in ROAFACTURARE — nici in codul specific produsului, nici in COMUN-ul lui — deci ramura de precompletare din contract (ofacturare_comun.prg:261-297) e cod mort in ROAFACTURARE azi, nu doar teoretic riscant. Asimetria de resetare semnalata la L.3 (ofundal_facturare.vc2:886-902) exista textual, dar nu produce niciun efect observabil azi, pentru ca nu exista niciun scriitor al lui goContract in acest produs care sa lase ceva de resetat. Ea devine un risc real abia daca cineva adauga in viitor, in ROAFACTURARE, un cod care scrie goContract — situatie in care lipsa resetarii ar deveni activa dintr-o data, tacut. goComanda, in schimb, chiar e viu in ROAFACTURARE: are doi scriitori (ocomenzi.vc2:1583 la clic pe „Factureaza”, si ocomenzi.vc2:2199, ca efect colateral al navigarii in grid), iar reset-ul explicit de la ofundal_facturare.vc2:900 e singura plasa de siguranta reala azi.

Suprafata de regresie e mica si masurata exact: doi scriitori de convertit (ocomenzi.vc2:1580-1596 in familia ROAFACTURARE, ferestre_contracte.vc2:1538-1549+:1598-1606 in ROACONTRACTE), trei fisiere comune de atins o singura data fiecare (ofacturare.prg, oproceduri_facturare.prg, ofacturare_comun.prg), si zero schimbari la celelalte ~31 puncte de intrare din inventarul S2 — toate cheama factureaza(N) cu un singur parametru, deci un al treilea parametru opozitional cu implicit .F./NULL nu le atinge.

Corectie de scop fata de formularea planului: „pana se convertesc toti apelantii" nu poate insemna, pentru goContract, „pana dispare global-ul" — goContract e bufferul de editare al intregului ecran de contracte din ROACONTRACTE (peste 100 de ControlSource legate de el, sectiunea 1), populat continuu de navigarea in grid, independent de facturare. Nu va disparea niciodata din ROACONTRACTE la aceasta poveste sau la vreuna viitoare rezonabila — S3c schimba doar canalul prin care valoarea ajunge la oDateFactura.Init, nu existenta globalei in ROACONTRACTE. Criteriul de „gata” de mai jos (sectiunea 8) e rescris sa reflecte asta.


1. Inventarul scrierilor si citirilor goComanda / goContract in toata suita

Cautare in D:\ROA\ROAFACTURARE, D:\ROA\ROACONT, D:\ROA\ROAGEST, D:\ROA\ROAAUTO, D:\ROA\ROAACNPRO, D:\ROA\ROAIMOB, D:\ROA\ROACONTRACTE — radacina fiecarui produs si COMUN\-ul lui explicit (capcana „Grep nu vede COMUN”). D:\ROA\COMUNROA nu contine niciuna din cele doua variabile (cautare separata, zero rezultate) — confirma ca sursa nu e livrata prin biblioteca partajata, ci prin fisierele COMUN duplicate per produs.

goComanda

Fisier Linie Rol Produse
Programe\roafacturare.prg :473-474 Declarare: PRIVATE goComanda / goComanda = null, la pornirea aplicatiei doar ROAFACTURARE (fiecare produs are propriul roafacturare.prg/echivalent, nu verificat identic — irelevant, doar initializeaza la null)
COMUN\clase\ocomenzi.vc2:1580-1596 (ct_comenzi.do_factura) :1583 SCRIE: SELECT crsComenzi / SCATTER NAME goComanda MEMO, apoi cheama facturare_comenzi ROAFACTURARE, ROACONT, ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB (fisier identic pe MD5, sectiunea 6)
COMUN\clase\ocomenzi.vc2:2191-2207 (_grdrow1.AfterRowColChange) :2199 SCRIE ca efect colateral: la fiecare schimbare de rand in grid-ul de comenzi, SCATTER NAME goComanda MEMO, folosit doar pentru but_factura1.Visible = (goComanda.facturat = 0) (:2202) — nu are nicio legatura cu intentia de facturare, dar suprascrie global-ul de fiecare data cand utilizatorul navigheaza in grid idem, acelasi fisier identic
COMUN\programe\ofacturare_comun.prg:301-328 (oDateFactura.Init) :301 CITESTE: If tnTip = 3 And Type('goComanda') = 'O' -> .id_client, .nume_client, .cod_fiscal, .listaid, .descriere, .id_sectie, plus un SELECT sectie FROM nom_sectii pe goComanda.id_sectie identic pe 6/7 produse (sectiunea 6; ROAIMOB diverge cu o linie, dar nu pe acest bloc)
Clase\ofundal_facturare.vc2:899-902 (Page2.Cw3.do_actiune) :900 RESETEAZA explicit: goComanda = '' inainte de DO facturare_comenzi doar ROAFACTURARE (fisierul e specific produsului, nu COMUN)

Niciun alt fisier, in niciun produs verificat, nu scrie sau citeste goComanda.

goContract

Fisier Linie Rol Produse
ROACONTRACTE\Programe\roacontracte.prg:559-560 :559-560 Declarare: Public poCtr, goContract / Store '' To poCtr, goContract, la pornirea aplicatiei doar ROACONTRACTE
ROACONTRACTE\Clase\ferestre_contracte.vc2 ~200 aparitii (liniile 977-11005, tabel complet in sectiunea de cautare) SCRIE si CITESTE ca buffer de editare al intregului formular de contracte: Scatter Name goContract Memo Blank la creare (:1026, :1186), SCATTER NAME goContract MEMO la fiecare schimbare de rand in grid (grid_contracte.AfterRowColChange, :1598-1606), peste 20 ControlSource = "goContract.<camp>" pe controale (combo-uri, textbox-uri), Gather Name goContract Memo la salvare (:1053, :2406-2472 etc.) doar ROACONTRACTE
ROACONTRACTE\Clase\ferestre_contracte.vc2:1538-1549 (but_factura.Click) — CITESTE implicit (nu re-scrie): cheama facturare_contracte fara sa re-populeze goContract — se bazeaza pe scrierea facuta deja de AfterRowColChange la selectia randului curent doar ROACONTRACTE
ROACONTRACTE\Clase\outlook2003bar.vc2, ofundal_roaclienti.vc2 multiple CITESC goContract.id_ctr/.id_part pentru navigare in bara laterala si ecrane de parteneri doar ROACONTRACTE
ROACONTRACTE\Programe\roacontracte.prg, oparteneri_contracte.prg, oproceduri_roacontracte.prg multiple CITESC/SCRIU goContract in fluxuri proprii ROACONTRACTE (incasari, plati, rate, garantii) — complet independente de facturare doar ROACONTRACTE
COMUN\programe\ofacturare_comun.prg:261-297 (oDateFactura.Init) :261 CITESTE: If INLIST(m.tnTip, 2, 6, 52) And Type('goContract') <> 'U' -> .id_client, .nume_client, .cod_fiscal, .listaid, .descriere, .id_sectie, .sectie, .id_responsabil, .responsabil, .id_valuta, .nume_valuta, plus interogare fact_vcontracte pentru scadenta identic pe 6/7 produse (ROAIMOB diverge cu exact aceasta linie lipsa — sectiunea 6)
COMUN\clase\ferestre_atasamente.vc2, COMUN\programe\oproceduri_atasamente.prg 5 aparitii CITESC goContract.id_ctr, dar numai in ramura Case Upper(Alltrim(gcNumeProgram)) = "ROACONTRACTE" — cod prezent in fiecare copie COMUN (deci si in ROAFACTURARE), dar mort acolo pentru ca gcNumeProgram nu e niciodata "ROACONTRACTE" in afara procesului ROACONTRACTE identic pe toate produsele verificate, dar activ doar in ROACONTRACTE

Confirmare directa: goContract nu e scris niciunde in ROAFACTURARE — nici in Programe\, Clase\, Ferestre\, Meniuri\, nici in COMUN\ (cautare pe PUBLIC goContract/Public goContract in tot arborele: zero rezultate, tabelul complet cu toate declaratiile PUBLIC go* gasite e in sectiunea 2). Singura mentiune a lui goContract in ROAFACTURARE e citirea garda de Type() din ofacturare_comun.prg:261, care ramane mereu falsa (Type('goContract') = 'U') intr-o sesiune ROAFACTURARE.

2. Verdict pe L.3 — asimetria de resetare

Textul din cod e adevarat, dar efectul practic e altul decat sugereaza formularea „de verificat” din plan.

  • Asimetria exista, literal: ofundal_facturare.vc2:899-902 (Page2.Cw3, ruta genericaa de comanda) face goComanda = '' inainte de DO facturare_comenzi; ofundal_facturare.vc2:886-897 (Page2.Cw2, ruta generica de contract) nu face echivalentul pentru goContract — cheama direct DO facturare_contracte WITH 'FACTURA LEI'/'INVOICE'/'FACTURA VALUTA'.
  • Dar facturare_contracte (oproceduri_facturare.prg:119-136) nu citeste si nu scrie goContract deloc — primeste un string de tip ('FACTURA LEI' etc.) si cheama direct factureaza(2)/factureaza(6)/factureaza(52). Precompletarea din contract se intampla exclusiv in oDateFactura.Init, la citirea globalei — nu exista niciun pas intermediar care ar putea fi „resetat”. In ROAFACTURARE, calea genericaa (Cw2) las utilizatorul sa aleaga contractul in formular, printr-un combo populat din crscontracte legat de poDate.listaid (ofacturare.prg:283-291,433-440) — un canal complet diferit, care nu trece prin goContract deloc (confirmat citind codul, sectiunea 3).
  • Deci, in ROAFACTURARE azi, nu exista nicio secventa executabila in care goContract sa fie citit cu o valoare veche. Pentru ca asta sa se intample ar trebui ca (a) ceva sa fi scris goContract mai devreme in aceeasi sesiune ROAFACTURARE — nu exista niciun asemenea cod azi — si (b) urmatorul apel sa fie cu tnTip in (2, 6, 52). Fara (a), (b) singur nu ajunge nicaieri.
  • Verdictul pe L.3: asimetria de resetare e reala ca defect de simetrie in cod (o ruta isi curata globala inainte de apel, cealalta nu), dar inert azi in ROAFACTURARE — nu exista niciun document gresit care se poate emite azi din cauza ei, pentru ca variabila pe care ar trebui sa o resetezi nu e niciodata populata in acest produs. Devine un risc real doar daca un cod viitor (in ROAFACTURARE) incepe sa scrie goContract fara sa adauge simetric si resetul — exact genul de capcana pe care „parametru explicit, fara global implicit” o elimina structural, nu prin inca o linie de reset de tinut minte.
  • In ROACONTRACTE, unde goContract chiar e viu, nu exista o ruta „generica fara precompletare” analoaga lui Cw2 — but_factura.Click (:1538-1549) e singurul punct de intrare si presupune intotdeauna un contract selectat in grid (populat de AfterRowColChange, :1598-1606, la fiecare schimbare de rand). Riscul teoretic acolo nu e „global ramas din alta sesiune de facturare”, ci „utilizatorul apasa Factureaza fara sa fi selectat explicit un rand dupa un refresh programatic” — un caz marginal, netratat de L.3 si nelegat de asimetria semnalata in plan.

3. Doar doua globale, sau mai sunt?

Doar doua. Verificat prin citirea completa a oDateFactura.Init (ofacturare_comun.prg:223-332) si a rutarii cursoarelor de articole din ofacturare.prg:260-308:

  • goDate, gnIdSet ca surse de precompletare — nu exista. goDate nu apare niciunde in ofacturare.prg/ofacturare_comun.prg (cautare directa, zero rezultate). gnIdSet nu exista ca atare — parametrul se numeste tnIdSet/lnIdSet, calculat local in factureaza (lnIdSet = 25000 + tnTip - 1 + gnScadereStoc * 10, ofacturare.prg:123) din tnTip si un optiune de configurare (gnScadereStoc), nu un canal de sursa.
  • Singurele doua verificari Type('go...') din oDateFactura.Init sunt exact goContract (:261) si goComanda (:301) — cautare Type\('go pe tot ofacturare.prg + ofacturare_comun.prg: zero alte rezultate in afara comutatorului de dezvoltator gnFacturareNou (irelevant aici, documentat in S2).
  • poDate.listaid pentru avize (tip 4, 21, 28, 42, 47) NU trece printr-un global — se scrie direct din cursorul de selectie al formularului: poDate.listaid = Iif(Inlist(poDate.tip, 3, 21, 28, 42, 47), Alltrim(Str(id_comanda)), []) (ofacturare_comun.vc2:4255, si varianta similara la :4062, :7271) — populat dintr-un Scan/Locate peste cursorul cu avizele bifate de utilizator in acelasi apel, nu dintr-o variabila globala persistenta intre apeluri. Avizele nu intra in S3c — nu au canal implicit de eliminat.
  • Copierea (toFactura) e deja parametru, nu global — copiere_factura(toFactura) (oproceduri_facturare.prg:150-153) -> factureaza(toFactura.Tip, toFactura), si in oDateFactura.Init/logica de copiere valorile vin din toDateAnterior (parametrul), de exemplu .listaid = toDateAnterior.id_vanzare (ofacturare_comun.vc2:387) — e exact precedentul pe care S3c il extinde la comanda/contract, nu un al treilea canal implicit de adaugat la lista.
  • poDate insusi nu e un canal de scurgere intre apeluri — se creeaza cu Createobject la fiecare intrare in bucla lui factureaza (ofacturare.prg:184, doar If Type('poDate') <> 'O', ceea ce e adevarat prima data si ramane fals doar in interiorul aceluiasi apel, la reintrarile bucla Do While lnRaspuns = 6) — deci nu poate purta stare intre doua clicuri distincte pe „Factureaza”.

Concluzie: goComanda si goContract sunt singurele doua canale implicite relevante pentru decizia 12. Nu mai exista o a treia variabila de convertit odata cu ele.

4. Semnatura propusa

factureaza (COMUN\programe\ofacturare.prg:81-82), azi Lparameters tnTip, toFactura:

Lparameters tnTip, toFactura, toSursa
  • toSursa — obiect, implicit NULL (nepasat de niciun apelant existent). Poarta fie un obiect cu forma lui goComanda (cand tnTip = 3), fie un obiect cu forma lui goContract (cand tnTip IN (2, 6, 52)) — exact aceeasi dualitate pe care codul de azi o rezolva deja prin ramificare pe tnTip in oDateFactura.Init (:261 vs :301), deci nu introduce un tip nou de decizie, doar muta sursa valorii.
  • Pozitia: al treilea parametru, dupa toFactura, niciodata inaintea lui — la fel ca precedentul V_TAXCODE/V_LOT citat de tine: apelurile VFP sunt pozitionale, iar singurul mod sa nu rupi apelantii existenti e sa adaugi la coada, cu implicit. Toate cele ~31 de apeluri din inventarul S2 care pasesc doar tnTip raman neschimbate — VFP completeaza automat parametrii nepasati la coada cu .F. (verificat comportamental: Type() pe un parametru nepasat intoarce 'L' cu valoarea .F., nu 'U' — de tratat explicit in garda, vezi sectiunea 5).
  • De ce nu inlocuieste toFactura: toFactura inseamna „copiaza factura asta” (un document deja emis), toSursa inseamna „precompleteaza din documentul asta” (o comanda sau un contract inca nefacturat) — semantic distincte, si copiere_factura (oproceduri_facturare.prg:150-153) ar putea teoretic avea nevoie de amandoua simultan in viitor (copiere + realocare pe alt contract) — motiv suplimentar sa nu le contopesti intr-un singur parametru.

Constructorul oDateFactura.Init (ofacturare_comun.prg:223-224), azi Lparameters tnIdSet, tnTip:

Lparameters tnIdSet, tnTip, toSursa
  • Aceeasi regula de pozitionare: la coada. Singurul apelant azi e Createobject("oDateFactura", lnIdSet, tnTip) din factureaza (ofacturare.prg:184) — devine Createobject("oDateFactura", lnIdSet, tnTip, toSursa). Nu exista alt loc din cod care instantiaza oDateFactura (cautare Createobject("oDateFactura" pe tot COMUN: un singur rezultat).

5. Mecanismul de sursa de rezerva

Regula: la fiecare din cele doua ramuri, se prefera toSursa daca a fost pasat ca obiect; daca nu, se cade pe globala, exact ca azi. Nu se schimba forma datelor citite (aceleasi campuri), doar sursa lor.

* ofacturare_comun.prg, oDateFactura.Init — inlocuieste liniile 261 si 301

Local loSursa
loSursa = Iif(Type('toSursa') = 'O', toSursa, Null)

If INLIST(m.tnTip, 2, 6, 52) And (Type('loSursa') = 'O' Or Type('goContract') <> 'U')
    If Type('loSursa') <> 'O'
        loSursa = goContract
    Endif
    .id_client = loSursa.id_part
    .nume_client = loSursa.denumire
    * ... restul neschimbat, doar goContract -> loSursa
Endif

loSursa = Iif(Type('toSursa') = 'O', toSursa, Null)   && re-evaluat, nu se refoloseste variabila de mai sus intre ramuri

If tnTip = 3 And (Type('loSursa') = 'O' Or Type('goComanda') = 'O')
    If Type('loSursa') <> 'O'
        loSursa = goComanda
    Endif
    .id_client = loSursa.id_part
    * ... restul neschimbat, doar goComanda -> loSursa
Endif

De ce doua evaluari separate ale lui loSursa (nu una singura la inceput): toSursa poarta o singura forma per apel (fie comanda, fie contract, niciodata amandoua — tnTip decide exclusiv care), dar garda trebuie sa verifice tnTip inainte sa presupuna forma, la fel ca azi. O variabila locala unica evita sa scrii toSursa de doua ori in tot blocul, fara sa schimbe logica.

Cum se stie cand se poate scoate globala — criteriu verificabil, nu „cand se convertesc toti”:

  • Pentru goComanda: nu se poate scoate niciodata complet din ROAFACTURARE, pentru ca al doilea ei scriitor (ocomenzi.vc2:2191-2207, AfterRowColChange) nu are nicio legatura cu factureaza — exista doar pentru but_factura1.Visible. Criteriul realist: linia de citire in oDateFactura.Init (:301) poate deveni neconditionata de fallback abia cand se confirma, prin git_sync.ps1 + Grep, ca niciun apel la factureaza(3, ...) mai lasa toSursa nepasat — adica se verifica apelantii lui factureaza, nu scriitorii globalei (care raman, pentru alt scop).
  • Pentru goContract: acelasi lucru, dar cu o observatie in plus — global-ul insusi (Public goContract in roacontracte.prg:559-560) nu va disparea niciodata cat timp exista formularul de contracte din ROACONTRACTE, care il foloseste ca buffer de editare, nu doar ca transport spre facturare. Criteriul de scos fallback-ul: cand toate apelurile catre factureaza(2/6/52, ...), in toate produsele, pasesc toSursa explicit — verificabil cu aceeasi comanda Select-String ca in S2, aplicata pe factureaza(2 / factureaza(6 / factureaza(52 in loc de factureaza2.
  • Pana atunci, fallback-ul ramane — el nu costa nimic in productie (o verificare Type() in plus), si e singura plasa de siguranta pentru cele ~31 puncte de intrare care nu au fost, nu vor fi, si nu trebuie sa fie convertite (nu au sursa de precompletat).

6. Cele N copii ale fisierelor atinse — identice sau divergente?

MD5 pe fiecare fisier, in cele 7 produse care au copie (ROAFACTURARE, ROACONT, ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB, ROACONTRACTE):

Fisier Rezultat
COMUN\programe\ofacturare.prg (2662 linii) Identic pe toate cele 7 — un singur MD5
COMUN\programe\oproceduri_facturare.prg (2393 linii) Identic pe toate cele 7 — un singur MD5
COMUN\clase\ocomenzi.vc2 (8301 linii) Identic pe cele 6 care il au (nu verificat separat in ROACONTRACTE, care nu are comenzi)
COMUN\programe\ofacturare_comun.prg (2224 linii) Identic pe 6 din 7 — ROAIMOB diverge cu exact o linie lipsa: :264, .cod_fiscal = ALLTRIM(NVL(goContract.cod_fiscal, '')), absenta din copia ROAIMOB (2223 linii). Restul fisierului, inclusiv blocul goComanda/goContract de la :261-328, e identic caracter cu caracter.

Spre deosebire de ofacturare.vc2 (7 copii, 4 variante MD5 distincte, citat ca precedent de comparat), fisierele atinse de S3c sunt aproape perfect sincronizate — un singur punct de drift, minor si izolat. Concluzie pentru propagare: modificarea din ofacturare.prg si oproceduri_facturare.prg se poate copia caracter cu caracter in toate cele 7 produse fara nicio adaptare. Modificarea din ofacturare_comun.prg se poate copia identic in 6 produse, dar in ROAIMOB trebuie aplicata pe baza divergentei existente (linia cod_fiscal lipseste deja acolo — o copiere oarba a diff-ului ar putea sa nu se aplice curat sau sa reintroduca acea linie fara sa fie intentionat; de verificat manual la sincronizare, nu doar copiat).

ferestre_contracte.vc2 (ROACONTRACTE) e specific produsului, nu are copii de sincronizat.

7. Suprafata de regresie masurata

Fisiere care chiar se modifica (5, plus 1 verificare de sincronizare):

# Fisier Ce se schimba Cate copii de propagat
1 COMUN\programe\ofacturare.prg:81-82 semnatura factureaza, +toSursa 7 (identice, copiere directa)
2 COMUN\programe\ofacturare_comun.prg:223-224, 261-328 semnatura Init, +toSursa; ramurile de citire cu fallback 7 (6 identice + ROAIMOB cu drift de reconciliat)
3 COMUN\programe\oproceduri_facturare.prg:119-141 facturare_contracte(tcTip, toSursa), facturare_comenzi(toSursa), relay catre factureaza(N, NULL, toSursa) 7 (identice, copiere directa)
4 COMUN\clase\ocomenzi.vc2:1580-1596 (do_factura) SCATTER NAME loComanda MEMO (local, nu global) + DO facturare_comenzi WITH loComanda IN oproceduri_facturare.prg 6 (identice, copiere directa)
5 ROACONTRACTE\Clase\ferestre_contracte.vc2:1538-1549 (but_factura.Click) trimite goContract (sau o copie locala scatter-uita din randul curent) explicit ca al doilea parametru la DO facturare_contracte WITH lcTip, goContract IN ... 1 (specific ROACONTRACTE)

Apelanti care NU se modifica (confirmat, nu presupus): toate cele ~31 de puncte de intrare din inventarul S2 (Meniuri\politica.mn2, Meniuri\contracte.mn2, Meniuri\aviz_*.mn2, ofundal_facturare.vc2:883-905) cheama factureaza(N) cu un singur argument sau facturare_contracte/facturare_comenzi fara sursa — al treilea parametru ramane implicit NULL, fallback-ul pe global preia (pentru cele care oricum nu aveau sursa reala, fallback-ul da acelasi rezultat ca azi: nimic de precompletat). Inclusiv ofundal_facturare.vc2:886-902 (Page2.Cw2/Cw3) raman neschimbate — Cw3 isi pastreaza goComanda = '' (devine redundant, dar inofensiv, odata ce do_factura trimite explicit; se poate curata separat, nu la aceasta poveste), Cw2 ramane cum e.

Total: 5 fisiere reale de editat, in 4 continuturi distincte de modificare (fisierele #1-#3 au acelasi continut in toate copiile lor), propagate in pana la 7 produse — mult sub suprafata unei modificari „de suita” tipice, pentru ca ofacturare.prg/oproceduri_facturare.prg sunt deja perfect sincronizate (sectiunea 6).

8. Ordinea de executie, ca suita sa nu fie stricata niciun moment

Spre deosebire de S2 (unde factureaza2 nu avea alt apelant decat el insusi, deci risc de stricare aproape nul), aici exista o fereastra in care semnaturile trebuie sa fie compatibile intre fisiere diferite (ofacturare.prg cheama oDateFactura::Init, oproceduri_facturare.prg cheama factureaza) — ordinea trebuie sa garanteze ca niciun punct intermediar nu are un apelant cu semnatura veche impotriva unui apelat cu semnatura noua incompatibila. Pentru ca toti parametrii noi sunt optionali, la coada, riscul e de fapt mic — o semnatura veche care cheama o rutina noua functioneaza (parametrul lipsa devine implicit), problema ar aparea doar invers (rutina veche chemata cu un parametru in plus, care s-ar ignora silentios in VFP — tot fara eroare, dar fara efect). Deci ordinea de mai jos e despre corectitudine, nu despre a evita crash-uri:

  1. oDateFactura.Init (ofacturare_comun.prg) — adauga toSursa, muta logica de citire pe modelul din sectiunea 5. Se poate face si testa izolat: fara niciun apelant care sa paseze toSursa inca, comportamentul ramane identic cu azi (fallback pe global, mereu).
  2. factureaza (ofacturare.prg) — adauga toSursa, il paseaza la Createobject("oDateFactura", lnIdSet, tnTip, toSursa). Inca niciun apelant nu paseaza toSursa — comportament neschimbat.
  3. oproceduri_facturare.prg — facturare_contracte/facturare_comenzi primesc toSursa si il relaeaza. Inca niciun apelant real nu il paseaza — comportament neschimbat.
  4. Abia acum, apelantii reali: ocomenzi.vc2:do_factura trimite loComanda explicit; ferestre_contracte.vc2:but_factura.Click trimite goContract explicit (in ROACONTRACTE). Din acest punct, comportamentul chiar se schimba (sursa vine din parametru, nu din citirea globalei), dar rezultatul trebuie sa fie identic — parametrul poarta exact aceleasi campuri pe care globala le avea.
  5. Sincronizare in celelalte 6 produse: pasii 1-3 se copiaza caracter cu caracter (identice, sectiunea 6), cu atentia speciala la ROAIMOB (drift de o linie). Pasul 4 pe partea de comenzi se copiaza si el (fisierul e identic in toate). Nu exista pas 4 de propagat pe partea de contract in afara de ROACONTRACTE (nu exista alt produs cu formular de contracte care sa scrie goContract).
  6. Un singur produs se poate testa complet integrat de la sine: ROAFACTURARE (pasii 1-4, partea de comanda). ROACONTRACTE cere o rulare separata (build propriu, sincronizat manual) pentru pasul 5 pe partea de contract — acelasi tip de limitare semnalata deja in S2 pentru factureaza2.

9. Ce nu se poate testa headless

  • Toate cele ~31+2 puncte de intrare sunt declansate din UI (clic pe buton, ON SELECTION BAR in meniu) — niciunul nu are un test headless existent, la fel ca in S2.
  • Testarea „parametrul castiga peste global” cere manipulare de sesiune: singurul mod sa verifici ca toSursa are prioritate e sa populezi manual goComanda/goContract cu o valoare diferita de cea din parametru si sa confirmi ca documentul rezultat foloseste parametrul — asta cere fie cod de test care seteaza global-ul inainte de apel (posibil headless, dar artificial fata de fluxul real), fie o sesiune interactiva VFP cu comenzi tastate manual in fereastra de comenzi.
  • Efectul colateral al AfterRowColChange (ocomenzi.vc2:2199) nu se poate simula headless — harnessul -A -T nu declanseaza fiabil evenimente de grid (confirmat de precedentul din memorie „Coloanele de grid nu se materializeaza headless”), deci nu se poate verifica automat ca navigarea in grid tot suprascrie goComanda dupa modificare (comportament care ramane neschimbat, dar trebuie confirmat vizual, nu presupus).
  • ROACONTRACTE nu poate fi testat din arborele de lucru ROAFACTURARE — cere build si rulare separata, cu propriul executabil si propria sincronizare a ofacturare.prg/ofacturare_comun.prg /oproceduri_facturare.prg (nu sunt acelasi fisier fizic, sunt copii).
  • Rezultatul final (facturare emisa cu antetul corect precompletat din comanda/contract) se verifica doar prin continutul lui crsfactura/VANZARI/formularul de antet, vizual sau prin interogare Oracle read-only dupa emitere — nu exista assert automat de facut.

10. Riscuri si ce ramane de decis de Marius

Riscuri:

  • Cel mai probabil sa se strice: garda Type('loSursa') = 'O' Or Type('goContract') <> 'U' din sectiunea 5 — daca se scrie gresit (de exemplu And in loc de Or), fallback-ul pe global s-ar rupe silentios pentru toti apelantii care inca nu au fost convertiti, fara nicio eroare vizibila (oDateFactura ar porni pur si simplu fara precompletare). Se testeaza explicit pe cel putin un apel neconvertit (orice factureaza(N) cu un singur argument, tip 1) dupa modificare.
  • Al doilea cel mai probabil: parametrul nou nepasat trebuie verificat Type('toSursa') = 'O', nu Type('toSursa') <> 'U' — un parametru VFP nepasat nu e 'U' (asta e pentru variabile nedeclarate), ci 'L' cu valoarea .F. implicita a lui LPARAMETERS. O garda gresita (<> 'U') ar trece mereu adevarat si ar incerca sa citeasca .id_part de pe .F., eroare de rulare imediata la primul apel neconvertit. De verificat cu un test minimal inainte de a atinge fisierele reale — nu presupune, verifica comportamentul Type() pe un parametru nepasat intr-un .prg de proba.
  • ROACONTRACTE, singurul apelant real din alt produs — acelasi risc semnalat in S2: netestabil din acest arbore de lucru, cere confirmare manuala separata.
  • Drift-ul de o linie din ofacturare_comun.prg in ROAIMOB — o copiere mecanica a diff-ului ar putea esua silentios sau reintroduce linia lipsa fara sa fie intentia — de aplicat manual acolo, nu prin copy-paste orb.
  • Cw3.do_actiune (ofundal_facturare.vc2:900) ramane cu goComanda = '' redundant dupa conversia lui do_factura — nu e o problema (ramane inofensiv), dar merita mentionat explicit ca „lasat asa, intentionat” in codul livrat, ca sa nu para o omisiune la revizuirea diff-ului.

Ce ramane de decis de Marius:

  1. Forma lui toSursa: ramane duck-typing pe obiect scatter (ca azi), sau se formalizeaza o clasa cu doua forme (oSursaComanda/oSursaContract)? Recomandare: ramane duck-typing — nu schimba nimic functional, adauga doar o clasa noua de intretinut pentru un beneficiu marginal la aceasta poveste.
  2. Se face si conversia partii de comanda si cea de contract in acelasi commit, sau separat? Ambele ating exact aceleasi trei fisiere comune (ofacturare.prg, ofacturare_comun.prg, oproceduri_facturare.prg) — separarea nu reduce suprafata de regresie pe fisierele comune, doar amana testarea reala pe ROACONTRACTE. Recomandare: un singur commit, cu testare manuala pe ambele produse inainte de a-l considera gata.
  3. Se curata acum redundanta goComanda = '' de la Cw3 (sectiunea „Riscuri”), sau se lasa pentru o poveste ulterioara de curatenie? Nu afecteaza corectitudinea — decizie de stil, nu de comportament.
  4. Criteriul de „gata” rescris (inlocuieste formularea din plan, linia 1861-1862):
    • Structural: Select-String -Path ofacturare.prg,ofacturare_comun.prg,oproceduri_facturare.prg -Pattern 'toSursa' gaseste parametrul in toate cele trei fisiere, in toate cele 7 produse care au copii, cu semnatura identica.
    • Comportamental, cale convertita: facturarea pornita din ocomenzi.vc2:do_factura cu goComanda populat manual cu alta comanda decat cea trimisa prin toSursa produce documentul corespunzator parametrului, nu globalei — testat manual, cu global-ul deliberat „murdar” dintr-o navigare anterioara in grid.
    • Comportamental, cale neconvertita: oricare din cele ~31 de apeluri care trimit doar tnTip produce acelasi document ca inainte de modificare (fallback pe global, sau fara precompletare acolo unde nu exista global de citit).
    • ROACONTRACTE: facturarea din but_factura.Click cu contractul trimis explicit prin toSursa produce acelasi rezultat ca azi (precompletare identica), verificat manual pe build separat.

Ce nu s-a putut stabili si de ce

  • Continutul exact al obiectului loComanda/goContract scaturat nu a fost comparat camp cu camp intre ce citeste azi oDateFactura.Init si ce ar contine un SCATTER facut in afara contextului crsComenzi/cContracte curent — presupunerea (rezonabila, dar neverificata pe date) e ca structura cursorului nu se schimba intre cele doua puncte de scatter.
  • Daca exista alte produse din suita (dincolo de cele 7 verificate) care au propriile copii ale ofacturare.prg/ofacturare_comun.prg/oproceduri_facturare.prg — cautarea s-a limitat la produsele numite explicit in sarcina; alte ~30 de produse mentionate in S2 ca avand copii ale ofacturare.prg (ROARETAIL etc.) nu au fost verificate pentru goComanda/goContract la aceasta poveste.
  • Testarea reala pe ROACONTRACTE nu a fost efectuata (read-only, fara rulare de cod) — doar confirmata structura codului sursa.

Bug-uri semnalate, nereparate

Niciunul nou — cercetarea a confirmat un defect de simetrie deja semnalat in plan (L.3), l-a verificat pe cod pana la verdictul „inert azi in ROAFACTURARE” (sectiunea 2), si nu a gasit alte probleme in fisierele atinse.