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) facegoComanda = ''inainte deDO facturare_comenzi;ofundal_facturare.vc2:886-897(Page2.Cw2, ruta generica de contract) nu face echivalentul pentrugoContract— cheama directDO facturare_contracte WITH 'FACTURA LEI'/'INVOICE'/'FACTURA VALUTA'. - Dar
facturare_contracte(oproceduri_facturare.prg:119-136) nu citeste si nu scriegoContractdeloc — primeste un string de tip ('FACTURA LEI'etc.) si cheama directfactureaza(2)/factureaza(6)/factureaza(52). Precompletarea din contract se intampla exclusiv inoDateFactura.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 dincrscontractelegat depoDate.listaid(ofacturare.prg:283-291,433-440) — un canal complet diferit, care nu trece pringoContractdeloc (confirmat citind codul, sectiunea 3). - Deci, in ROAFACTURARE azi, nu exista nicio secventa executabila in care
goContractsa fie citit cu o valoare veche. Pentru ca asta sa se intample ar trebui ca (a) ceva sa fi scrisgoContractmai devreme in aceeasi sesiune ROAFACTURARE — nu exista niciun asemenea cod azi — si (b) urmatorul apel sa fie cutnTipin(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
goContractfara 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
goContractchiar 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 deAfterRowColChange,: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,gnIdSetca surse de precompletare — nu exista.goDatenu apare niciunde inofacturare.prg/ofacturare_comun.prg(cautare directa, zero rezultate).gnIdSetnu exista ca atare — parametrul se numestetnIdSet/lnIdSet, calculat local infactureaza(lnIdSet = 25000 + tnTip - 1 + gnScadereStoc * 10,ofacturare.prg:123) dintnTipsi un optiune de configurare (gnScadereStoc), nu un canal de sursa.- Singurele doua verificari
Type('go...')dinoDateFactura.Initsunt exactgoContract(:261) sigoComanda(:301) — cautareType\('gope totofacturare.prg+ofacturare_comun.prg: zero alte rezultate in afara comutatorului de dezvoltatorgnFacturareNou(irelevant aici, documentat in S2). poDate.listaidpentru 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-unScan/Locatepeste 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 inoDateFactura.Init/logica de copiere valorile vin dintoDateAnterior(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. poDateinsusi nu e un canal de scurgere intre apeluri — se creeaza cuCreateobjectla fiecare intrare in bucla luifactureaza(ofacturare.prg:184, doarIf Type('poDate') <> 'O', ceea ce e adevarat prima data si ramane fals doar in interiorul aceluiasi apel, la reintrarile buclaDo 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, implicitNULL(nepasat de niciun apelant existent). Poarta fie un obiect cu forma luigoComanda(candtnTip = 3), fie un obiect cu forma luigoContract(candtnTip IN (2, 6, 52)) — exact aceeasi dualitate pe care codul de azi o rezolva deja prin ramificare petnTipinoDateFactura.Init(:261vs: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 precedentulV_TAXCODE/V_LOTcitat 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 doartnTipraman 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:toFacturainseamna „copiaza factura asta” (un document deja emis),toSursainseamna „precompleteaza din documentul asta” (o comanda sau un contract inca nefacturat) — semantic distincte, sicopiere_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)dinfactureaza(ofacturare.prg:184) — devineCreateobject("oDateFactura", lnIdSet, tnTip, toSursa). Nu exista alt loc din cod care instantiazaoDateFactura(cautareCreateobject("oDateFactura"pe totCOMUN: 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 dinROAFACTURARE, pentru ca al doilea ei scriitor (ocomenzi.vc2:2191-2207,AfterRowColChange) nu are nicio legatura cufactureaza— exista doar pentrubut_factura1.Visible. Criteriul realist: linia de citire inoDateFactura.Init(:301) poate deveni neconditionata de fallback abia cand se confirma, pringit_sync.ps1+ Grep, ca niciun apel lafactureaza(3, ...)mai lasatoSursanepasat — adica se verifica apelantii luifactureaza, nu scriitorii globalei (care raman, pentru alt scop). - Pentru
goContract: acelasi lucru, dar cu o observatie in plus — global-ul insusi (Public goContractinroacontracte.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 catrefactureaza(2/6/52, ...), in toate produsele, pasesctoSursaexplicit — verificabil cu aceeasi comandaSelect-Stringca in S2, aplicata pefactureaza(2/factureaza(6/factureaza(52in loc defactureaza2. - 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:
oDateFactura.Init(ofacturare_comun.prg) — adaugatoSursa, muta logica de citire pe modelul din sectiunea 5. Se poate face si testa izolat: fara niciun apelant care sa pasezetoSursainca, comportamentul ramane identic cu azi (fallback pe global, mereu).factureaza(ofacturare.prg) — adaugatoSursa, il paseaza laCreateobject("oDateFactura", lnIdSet, tnTip, toSursa). Inca niciun apelant nu paseazatoSursa— comportament neschimbat.oproceduri_facturare.prg—facturare_contracte/facturare_comenziprimesctoSursasi il relaeaza. Inca niciun apelant real nu il paseaza — comportament neschimbat.- Abia acum, apelantii reali:
ocomenzi.vc2:do_facturatrimiteloComandaexplicit;ferestre_contracte.vc2:but_factura.ClicktrimitegoContractexplicit (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. - 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). - 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 BARin 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
toSursaare prioritate e sa populezi manualgoComanda/goContractcu 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 -Tnu 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 suprascriegoComandadupa 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 exempluAndin loc deOr), fallback-ul pe global s-ar rupe silentios pentru toti apelantii care inca nu au fost convertiti, fara nicio eroare vizibila (oDateFacturaar porni pur si simplu fara precompletare). Se testeaza explicit pe cel putin un apel neconvertit (oricefactureaza(N)cu un singur argument, tip 1) dupa modificare. - Al doilea cel mai probabil: parametrul nou nepasat trebuie verificat
Type('toSursa') = 'O', nuType('toSursa') <> 'U'— un parametru VFP nepasat nu e'U'(asta e pentru variabile nedeclarate), ci'L'cu valoarea.F.implicita a luiLPARAMETERS. O garda gresita (<> 'U') ar trece mereu adevarat si ar incerca sa citeasca.id_partde 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 comportamentulType()pe un parametru nepasat intr-un.prgde 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.prgin 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 cugoComanda = ''redundant dupa conversia luido_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:
- 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. - 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. - Se curata acum redundanta
goComanda = ''de laCw3(sectiunea „Riscuri”), sau se lasa pentru o poveste ulterioara de curatenie? Nu afecteaza corectitudinea — decizie de stil, nu de comportament. - 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_facturacugoComandapopulat manual cu alta comanda decat cea trimisa printoSursaproduce 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
tnTipproduce acelasi document ca inainte de modificare (fallback pe global, sau fara precompletare acolo unde nu exista global de citit). - ROACONTRACTE: facturarea din
but_factura.Clickcu contractul trimis explicit printoSursaproduce acelasi rezultat ca azi (precompletare identica), verificat manual pe build separat.
- Structural:
Ce nu s-a putut stabili si de ce
- Continutul exact al obiectului
loComanda/goContractscaturat nu a fost comparat camp cu camp intre ce citeste azioDateFactura.Initsi ce ar contine unSCATTERfacut in afara contextuluicrsComenzi/cContractecurent — 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 aleofacturare.prg(ROARETAIL etc.) nu au fost verificate pentrugoComanda/goContractla 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.