sync SVN r18077
This commit is contained in:
426
docs/cercetare/s3c_sursa_ca_parametru.md
Normal file
426
docs/cercetare/s3c_sursa_ca_parametru.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# 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.
|
||||
|
||||
```foxpro
|
||||
* 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.
|
||||
Reference in New Issue
Block a user