sync SVN r18077
This commit is contained in:
478
docs/cercetare/s3_portare_antet.md
Normal file
478
docs/cercetare/s3_portare_antet.md
Normal file
@@ -0,0 +1,478 @@
|
||||
# Cercetare — proiectare S3: portarea logicii de antet in formularul unificat
|
||||
|
||||
Investigatie READ-ONLY pentru povestea **S3** din `docs\plan_13_unificare_formular_facturare.md:1428-1439`.
|
||||
Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit. Constrangerile din briefing —
|
||||
`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` doar citite (perimetrul
|
||||
#6), `pack_facturare` si `pack_facturare_comun`-ul intern neatinse, analiticele read-only — respectate.
|
||||
|
||||
## Verdict (5-10 randuri)
|
||||
|
||||
S3 e **mai mare decat inventarul din plan sugereaza pe numar de metode** (29 `do_cauta_*` reale, nu
|
||||
30 — vezi punctul 1), dar **mult mai mare decat sugereaza pe complexitate reala**, pentru doua motive
|
||||
descoperite aici, nu presupuse: (a) `Init`, `inainte_de_do_termin` si — partial — `do_cauta_fdoc` sunt
|
||||
**omonime cu semantica total diferita pe toate cele patru formulare-sursa** (antet vs. articole vs.
|
||||
alte-date), deci portarea "o singura data" cere de fapt patru fuziuni de metoda, nu una singura cu
|
||||
ramificare; (b) bug-ul **#16 e real, reprodus pe cod pana la linia exacta**, si **nu e un bug de focus**
|
||||
— e o consecinta a faptului ca bucla de reincercare din `factureaza`/`factureaza2`
|
||||
(`ofacturare.prg:174-571`) trateaza **orice esec SQL** (nu doar cursul lipsa) ca pe un "DA, continui cu
|
||||
alt document", redeschizand din senin formularul de antet de la zero. Unificarea **nu-l reproduce
|
||||
automat** — poate chiar sa-l elimine ca efect secundar, daca antetul nu se mai reconstruieste de la
|
||||
Init dupa un esec Oracle in pasul urmator. Obstacolul principal pentru implementare nu e portarea
|
||||
codului de validare (majoritatea e `Do Case`/`amessagebox` mecanic), ci **reconcilierea a patru cicluri
|
||||
de viata `Init`/`inainte_de_do_termin` independente intr-unul singur**, cu ordinea de dependente de la
|
||||
punctul 5.
|
||||
|
||||
---
|
||||
|
||||
## 1. Inventarul metodelor de portat — numere corectate
|
||||
|
||||
Sursa: `vfp_symbols.ps1 -Class frm_date_factura` / `-Class frm_date_aviz`, confirmat pe fisierul real
|
||||
`COMUN\clase\ofacturare.vc2` (nu `.bak`).
|
||||
|
||||
**`frm_date_factura`** (`ofacturare.vc2:8482-9869`) — **16** metode `do_cauta_*`, nu 17:
|
||||
|
||||
| Metoda | Linii | Ce face | Echivalent in `frm_facturare_articole2` |
|
||||
|---|---|---|---|
|
||||
| `do_cauta_altele` | `8940-8961` (21) | cautare pe campul cu eticheta dinamica ("Altele"/contract/comanda/etc.) | nu |
|
||||
| `do_cauta_avize` | `8963-8999` (36) | cautare aviz-sursa (tip=4) | nu |
|
||||
| `do_cauta_client` | `9001-9060` (59) | cautare client, populeaza sold/cod fiscal | nu |
|
||||
| `do_cauta_comanda` | `9062-9090` (28) | cautare comanda-sursa | nu |
|
||||
| `do_cauta_contract` | `9092-9153` (61) | cautare contract-sursa | nu |
|
||||
| `do_cauta_factura` | `9155-9171` (16) | cautare factura-sursa (credit note, tip 7) | nu |
|
||||
| `do_cauta_facturi` | `9173-9212` (39) | cautare multi-factura (retur, tip 8/9) | nu |
|
||||
| `do_cauta_fdoc` | `9214-9225` (11) | cautare generica fel-document, `caut_fdoc()` — **identica byte-cu-byte cu varianta din `frm_date_aviz`**, vezi punctul 4 | nu |
|
||||
| `do_cauta_gestiune_init` | `9227-9242` (15) | cautare gestiune sursa | nu |
|
||||
| `do_cauta_locatii` | `9244-9253` (9) | cautare locatie (tip 45) | nu |
|
||||
| `do_cauta_lucrare` | `9255-9279` (24) | cautare lucrare | nu |
|
||||
| `do_cauta_responsabil` | `9281-9301` (20) | cautare responsabil | nu |
|
||||
| `do_cauta_sectie` | `9303-9342` (39) | cautare sectie | nu |
|
||||
| `do_cauta_valuta` | `9344-9359` (15) | cautare valuta, muta focus pe `clb_zi_curs` daca exista, altfel pe serie (`:9351-9356`, deja conditionat `Type(...)<>'U'`) | nu |
|
||||
| `do_cauta_venchelt` | `9361-9391` (30) | cautare venit/cheltuiala | nu |
|
||||
| `do_cauta_venit` | `9393-9394` (1) | stub/alias, o linie — de verificat ce mai apeleaza | nu |
|
||||
| `do_schimba_tipdoc` | `9396-9438` (42) | vezi punctul 6 — schimba `poDate.nIdTipDoc` si reinitializeaza serie+numar | **nu** — apelat orfan din prototip, vezi punctul 3 |
|
||||
| `do_verifica` | `9440-9453` (13) | verificare ANAF pe cod fiscal client | nu (dar exista pe `frm_facturi`, aceeasi semantica) |
|
||||
| `inainte_de_do_termin` | `9455-9561` (106) | validare completa antet inainte de trecerea la articole | **omonim cu semantica diferita** pe toate 4 formulare, vezi mai jos |
|
||||
| `Init` | `9563-9796` (234) | vezi punctul 5 | **omonim cu semantica diferita**, vezi mai jos |
|
||||
| `Clb_dataact...LostFocus` | `9798-9813` (15) | sincronizeaza `zi_curs=dataact` daca `clb_zi_curs` exista | nu |
|
||||
| `Clb_dataireg...LostFocus` | `9815-9831` (16) | idem, pe `dataireg` | nu |
|
||||
| `Clb_nract...LostFocus` | `9833-9835` (2) | — | nu |
|
||||
| `Clb_serie_act...LostFocus` | `9837-9844` (7) | — | nu |
|
||||
| `Ct_clb_fdoc._combobox1.Init` | `9846-9855` (9) | populeaza combo-ul FACTURA/PROFORMA/BON FISCAL | nu |
|
||||
| `Ct_clb_fdoc._combobox1.LostFocus` | `9857-9859` (2) | `thisform.do_schimba_tipdoc()` — vezi punctul 6 | nu (frm_facturare_articole2 are un apel similar, dar **orfan**, vezi punctul 3) |
|
||||
| `Ed_tx_simplu1._EDBASE1.KeyPress` | `9861-9867` (6) | — | nu |
|
||||
|
||||
**`frm_date_aviz`** (`ofacturare.vc2:6566-7618`) — **13** metode `do_cauta_*`, numarul din plan e corect:
|
||||
|
||||
| Metoda | Linii | Observatie |
|
||||
|---|---|---|
|
||||
| `do_cauta_altele` | `6882-6894` (12) | mai scurta decat pe factura (21) — set de tipuri mai mic |
|
||||
| `do_cauta_avize` | `6896-6930` (34) | apropiata ca marime de factura (36) |
|
||||
| `do_cauta_client` | `6932-7009` (77) | **mai lunga** decat pe factura (59) — trateaza eticheta variabila "Retur de la"/"Gestiune sursa" pe transfer |
|
||||
| `do_cauta_comanda` | `7011-7034` (23) | |
|
||||
| `do_cauta_contract` | `7036-7091` (55) | |
|
||||
| `do_cauta_fdoc` | `7093-7104` (11) | **identica cu factura**, vezi punctul 4 |
|
||||
| `do_cauta_gestiune_dest` | `7106-7131` (25) | **doar pe aviz** — gestiune destinatie la transfer subunitati |
|
||||
| `do_cauta_gestiune_init` | `7133-7144` (11) | mai scurta decat pe factura (15) |
|
||||
| `do_cauta_lucrare` | `7146-7165` (19) | |
|
||||
| `do_cauta_politica` | `7167-7182` (15) | **doar pe aviz** — `Ct_clb_politici_preturi` |
|
||||
| `do_cauta_responsabil` | `7184-7204` (20) | aceeasi lungime ca factura |
|
||||
| `do_cauta_sectie` | `7206-7240` (34) | |
|
||||
| `do_cauta_venchelt` | `7242-7275` (33) | |
|
||||
| `inainte_de_do_termin` | `7277-7352` (75) | **omonim**, vezi mai jos — mai scurta decat factura (106): fara validare data-scadenta, fara validare valuta, fara validare client (!) |
|
||||
| `Init` | `7354-7600` (247) | **omonim**, nu citit integral aici (doar `9563-9650`+`9733-9796` echivalentul pe factura) |
|
||||
| `Clb_dataact...LostFocus` | `7602-7605` | |
|
||||
| `Clb_dataireg...LostFocus` | `7607-7612` | |
|
||||
| `Clb_nract...LostFocus` | `7614-7616` | |
|
||||
|
||||
**`frm_date_aviz` nu are `do_schimba_tipdoc`** — confirmat, container-ul `Ct_clb_fdoc` de pe aviz e
|
||||
o cautare simpla, fara combo si fara evenimentul `LostFocus` care declanseaza schimbarea de tip.
|
||||
|
||||
**Total real: 16 + 13 = 29 metode `do_cauta_*`**, plus `do_schimba_tipdoc` (doar factura), `do_verifica`
|
||||
(doar factura), `inainte_de_do_termin` si `Init` (omonime pe ambele) — planul le numara global corect
|
||||
ca "17+13 ... plus restul", dar cifra "17" pe factura e gresita cu o unitate: **16**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Metodele omonime — capcana centrala a portarii
|
||||
|
||||
**Nivelul care conteaza cel mai mult, necunoscut inca in plan**: `Init` si `inainte_de_do_termin` sunt
|
||||
**acelasi nume de metoda pe toate cele patru formulare-sursa implicate in unificare**
|
||||
(`frm_date_factura`, `frm_date_aviz`, `frm_facturare_articole`/`frm_facturare_articole2`,
|
||||
`frm_alte_date`), fiecare cu corp complet diferit (antet vs. compunere articole vs. date suplimentare).
|
||||
O singura clasa unificata nu poate avea patru metode `Init`. **Portarea "o singura data" a S3 inseamna
|
||||
concret: patru corpuri de `Init` si patru corpuri de `inainte_de_do_termin` trebuie topite intr-un
|
||||
singur `Init` si un singur `inainte_de_do_termin` (sau redenumite si inlantuite explicit)** — asta e
|
||||
efortul real, nu doar mutarea codului de validare camp-cu-camp.
|
||||
|
||||
**`inainte_de_do_termin`, factura vs. aviz — diferenta reala, cu citat** (comparate direct, ambele corpuri
|
||||
citite integral, `ofacturare.vc2:9455-9561` si `:7277-7352`):
|
||||
|
||||
- **Factura valideaza `id_client` obligatoriu; aviz nu valideaza deloc acest camp**:
|
||||
```
|
||||
ofacturare.vc2:9510-9513 (doar pe factura)
|
||||
Case Empty(poDate.id_client) Or Isnull(poDate.id_client)
|
||||
amessagebox("Nu ati ales clientul!",48,"Atentie")
|
||||
This.ct_clb_nume_client.SetFocus()
|
||||
```
|
||||
Motiv plauzibil (neconfirmat mai departe): avizele de transfer intre subunitati (tip 23/25/30/41) nu
|
||||
au neaparat un "client" — au o gestiune destinatie, validata separat.
|
||||
- **Factura valideaza scadenta si valuta; aviz nu are deloc aceste `Case`-uri** — `Empty(poDate.datascad)`,
|
||||
`poDate.datascad<poDate.dataact`, `poDate.in_valuta=1 And Empty(zi_curs)`, `poDate.in_valuta=1 And
|
||||
Empty(id_valuta)` (`:9471-9493`) — aviz nu are control de valuta pe formular (S1, randul "Valuta").
|
||||
- **Aviz valideaza politica de preturi; factura nu**:
|
||||
```
|
||||
ofacturare.vc2:7334-7337 (doar pe aviz)
|
||||
Case Empty(Nvl(poDate.id_pol,0)) And InList(poDate.tip,23,41)
|
||||
amessagebox("Nu ati ales politica de preturi!",48,"Atentie")
|
||||
```
|
||||
- **Setul de tipuri si mesaje pentru "sursa neselectata" (`listaid`) e complet diferit**: factura
|
||||
`!Inlist(poDate.tip,1,5,7,8,9,10,48,49)` cu mesaje contract/comanda/aviz/locatie (`:9528-9541`); aviz
|
||||
`Inlist(poDate.tip,21,23,25,26,28,41,42,47)` cu mesaje comanda/gestiune-destinatie/gestiune-retur/contract
|
||||
(`:7318-7333`) — seturile de `tip` nu se suprapun deloc (factura = tipuri <21 sau in {45,48,49,51,52};
|
||||
aviz = tipuri >=21 in afara de 27/30). **Discriminatorul natural pentru ramificare exista deja**:
|
||||
`poDate.nIdTipDoc` (5=FACTURA, 6=AVIZ — calculat de apelant la `ofacturare.prg:192-196`) sau simpla
|
||||
apartenenta a lui `poDate.tip` la unul din cele doua seturi disjuncte, **nu** un flag nou.
|
||||
- **Contul folosit la verificarea de facturi duplicate difera**: `facturi_duplicate('4111', 0, ...)`
|
||||
(factura, `:9545`, cont clienti) vs. `facturi_duplicate('418', 0, ...)` (aviz, `:7341`, alt cont) —
|
||||
un literal hardcodat care trebuie sa devina el insusi parte din ramificare, nu doar mesajul.
|
||||
|
||||
**Alte omonime cunoscute, cu semantica diferita, relevante pentru zona din jurul lui S3** (nu introduse
|
||||
aici — reconfirmate, vezi si `docs\handoff_13_formular_unificat.md:192-194`):
|
||||
- `do_calculeaza_discount` — **la nivel de document** pe `frm_facturare_articole` (`:13405-13424`) si
|
||||
`frm_facturare_articole2` (`:17670-17686`, verificat aici: identic ca semantica, scrie
|
||||
`Thisform.ndiscfactron`/`ndiscfactval`); **la nivel de linie** pe `frm_articol_factura` (`:1874-1976`,
|
||||
scrie `poArticol.discount_unitar*`). Nu intra direct in S3 (nu e printre metodele antetului), dar
|
||||
orice cod nou care apeleaza `do_calculeaza_discount` prin nume pe formularul unificat trebuie sa
|
||||
stie care semantica o vrea.
|
||||
- `do_verifica` — pe `frm_date_factura` (`:9440-9453`, verificare ANAF) si pe `frm_facturi`
|
||||
(verificare ANAF, aceeasi semantica) — omonim inofensiv, aceeasi actiune.
|
||||
- `do_cauta_gestiune_init` / `do_cauta_responsabil` / `do_cauta_sectie` / `do_cauta_venchelt` /
|
||||
`do_cauta_altele` / `do_cauta_comanda` / `do_cauta_contract` / `do_cauta_avize` / `do_cauta_lucrare` —
|
||||
**acelasi nume pe factura si aviz, lungimi diferite** (vezi tabelele de la punctul 1) — nu omonime
|
||||
periculoase (aceeasi intentie: cautare pe camp), dar **nu identice** — portarea trebuie sa ia corpul
|
||||
fiecareia separat, nu sa presupuna ca sunt duplicate de sters.
|
||||
- **Singura pereche confirmata identica byte-cu-byte**: `do_cauta_fdoc` (vezi punctul 4) — aceasta
|
||||
chiar poate fi portata o singura data, fara ramificare.
|
||||
|
||||
---
|
||||
|
||||
## 3. Ce e deja in prototip vs. ce lipseste
|
||||
|
||||
`frm_facturare_articole2` (`ofacturare.vc2:15741-19355`) are controalele de antet montate direct pe
|
||||
formular (confirmat in `inventar_controale_formulare.md`), dar **zero logica**:
|
||||
|
||||
| Metoda/mecanism | Exista in `frm_facturare_articole2`? | Detaliu |
|
||||
|---|---|---|
|
||||
| Toate cele 29 `do_cauta_*` | **NU** | niciunul in lista de metode proprii a clasei (confirmat cu `vfp_symbols.ps1 -Class`) |
|
||||
| `do_schimba_tipdoc` | **NU, dar e APELAT** — cod orfan | `clb_fdoc.cboFdoc.Valid` (`:19255-19257`) contine `thisform.do_schimba_tipdoc()`, dar clasa **nu are** aceasta metoda in lista proprie. Daca userul ar schimba azi combo-ul `clb_fdoc` pe acest formular mort, ar cadea cu eroare de metoda inexistenta. Confirma independent constatarea din plan ("controalele sunt acolo, logica nu") — de fapt e mai rau: e cod care ar crapa daca s-ar activa calea, nu doar cod lipsa |
|
||||
| `inainte_de_do_termin` | **DA, dar cu alta semantica** | `:18809-18986` (178 linii) — valideaza ARTICOLE (stoc, discount, TVA), nu antetul; e omonimul de care vorbeste punctul 2, nu un candidat de reutilizare pentru validarea de antet |
|
||||
| `Init` | **DA, dar cu alta semantica** | `:18988-19080` (92 linii, citit integral aici) — confirmat: doar grid/curs-label/total-mode/coloane in/afara valuta; **niciun** `poDate.xxx -> control.Value`. Nu populeaza antetul deloc |
|
||||
| Serie/numar (`Clb_serie_act1`/`Clb_nract`) | Controale prezente, dar fara wiring de populare in `Init` | `Clb_serie_act1.TEXT_SIMPLU1.LostFocus` (`:19263-19270`) exista ca metoda proprie — nu verificat aici daca reproduce `genereazanumar` |
|
||||
|
||||
**Concluzie punctul 3**: prototipul e o **coaja vizuala**, nu un schelet de 50% functional. Ce trebuie
|
||||
scris pentru S3 e efectiv tot codul de comportament — controalele economisesc timpul de aranjare in
|
||||
`.scx`/`.vcx`, nu timpul de portare a logicii.
|
||||
|
||||
---
|
||||
|
||||
## 4. Ramificarea factura / aviz in interior
|
||||
|
||||
**Discriminatorul de ramificare recomandat: `poDate.nIdTipDoc`** (5=FACTURA, 6=AVIZ), deja calculat de
|
||||
apelant inainte de `Createobject` (`ofacturare.prg:187-196`) si deja disponibil pe `poDate` in
|
||||
momentul in care formularul unificat ar porni `Init`. Alternativ, acelasi rezultat se obtine testand
|
||||
direct apartenenta lui `poDate.tip` la unul din cele doua seturi disjuncte folosite azi in
|
||||
`inainte_de_do_termin` (vezi punctul 2) — cele doua seturi nu se suprapun niciodata, deci nu exista
|
||||
ambiguitate. **Nu e nevoie de o proprietate noua** gen `lEsteAviz` — `nIdTipDoc` face deja treaba, si
|
||||
e deja pe obiectul `poDate` care trece prin tot lantul de apeluri.
|
||||
|
||||
Ce trebuie sa ramifice concret, cu dovada:
|
||||
1. **`inainte_de_do_termin`** — Cases specifice fiecarei parti (vezi citatele de la punctul 2), plus
|
||||
constanta de cont (`'4111'` vs `'418'`) la `facturi_duplicate`.
|
||||
2. **`Init`** — seturile `Do Case poDate.tip` care decid ce se elimina (`RemoveObject`) difera pe
|
||||
fiecare parte (S1 documenteaza deja fiecare camp cu conditia lui de vizibilitate; portarea trebuie
|
||||
sa pastreze fiecare conditie identic, nu sa le generalizeze pe ghicite).
|
||||
3. **`do_schimba_tipdoc`** — **exista doar pe factura**; pe aviz nu exista mecanismul de schimbare a
|
||||
tipului de document dupa deschiderea formularului (containerul `ct_clb_fdoc` de pe aviz e cautare
|
||||
simpla, fara `_combobox1`). Ramificarea aici nu e "cod diferit pe aceeasi metoda" ci "metoda
|
||||
prezenta doar pe o ramura" — formularul unificat trebuie sa decida daca aviz capata acest
|
||||
comportament nou (schimbare tip dupa deschidere) sau ramane fara el, ca azi.
|
||||
4. **`do_cauta_fdoc`** — **nu ramifica**, e identic (punctul 1) — poate fi portat o singura data, fara
|
||||
`If nIdTipDoc=...`.
|
||||
5. **`do_cauta_client`** — aviz e cu 18 linii mai lung (77 vs 59) pentru eticheta variabila
|
||||
"Retur de la"/"Gestiune sursa" pe transfer (S1, randul "Client") — de citit integral la
|
||||
implementare pentru ramificarea exacta, nu verificat linie-cu-linie aici.
|
||||
|
||||
---
|
||||
|
||||
## 5. `Init`-urile — ce face fiecare, in ce ordine, ce depinde de ce
|
||||
|
||||
**Secventa de azi, pe drumul normal (factura din lista de preturi, fara eroare Oracle):**
|
||||
|
||||
1. **Apelantul** (`ofacturare.prg:factureaza`, inainte de orice `Createobject`): construieste
|
||||
`poDate = Createobject("oDateFactura", ...)` si `poGeneratorNumere = Createobject("oGeneratorNumere")`
|
||||
(`:184-185`), seteaza `poDate.nIdTipDoc` (`:187-196`), cheama
|
||||
`poDate.completeaza_setari_document(...)` daca e copiere (`:200-206`), apoi
|
||||
`poGeneratorNumere.ResetNumere()` si `poDate.rezultat_serii = poGeneratorNumere.creeaza_cursor_serii(...)`
|
||||
(`:208-211`) — **toate acestea ruleaza inainte ca vreun formular sa existe**. Orice `Init` de mai jos
|
||||
presupune `poDate`/`poGeneratorNumere` deja populate.
|
||||
2. **`frm_date_factura.Init`** (`:9563-9796`, 234 linii, citit integral aici) — `DoDefault()`, apoi
|
||||
masoara inaltimile containerelor de antet intr-un array (`laPozitii`), decide pe `Do Case
|
||||
gnScadereStoc/poDate.tip` ce containere elimina (`RemoveObject`) si reface layout-ul pe verticala,
|
||||
initializeaza serie+numar (`.clb_serie_act.do_initializeaza(poDate.rezultat_serii)`, `:9737`), si
|
||||
**la final** seteaza focus: pe `ct_clb_valuta` daca documentul e in valuta si campul valutei apare
|
||||
deasupra tipului si nu are inca valuta aleasa, **altfel pe `ct_clb_fdoc`** (`:9788-9793`, citat
|
||||
integral la punctul 6). Depinde de: `poDate` (tip, in_valuta, ...), `poGeneratorNumere`
|
||||
(`creeaza_cursor_serii` deja rulat de apelant), variabile globale de firma (`gnScadereStoc`).
|
||||
3. **`frm_facturare_articole2.Init`** (`:18988-19080`, 92 linii, citit integral aici) — confirmat:
|
||||
**nu atinge niciun camp de antet**. Configureaza doar gridul (`nNrInregVizibile`), eticheta
|
||||
cursurilor valutare, modul total (`gnModTotFact`), si elimina coloanele RON sau valuta din grid
|
||||
dupa `poDate.in_valuta`. Depinde de: `poDate.zi_curs`/`in_valuta`/`tip`, `crscursuri` (populat de
|
||||
apelant intre pasii 2 si 3, nu de acest `Init`).
|
||||
4. **`frm_alte_date.Init`** (`ferestre_cere_date.vc2:3105-3207`, 103 linii, citit integral aici) —
|
||||
`DoDefault()`, citeste `poDate.dataora_exp`; **daca nu e proforma**: cauta ultimul delegat/masina
|
||||
folosite pentru client (apel Oracle `cauta_date_ultima_factura[_tip]`, `:3119-3136`), populeaza
|
||||
combo-ul de casa dintr-un cursor Oracle nou (`v_nom_casa`, `:3156-3173`), seteaza `opt_incasat`
|
||||
implicit daca `poDate.incasat<>0`; **daca e proforma**: elimina complet grupul delegat/masina/
|
||||
incasare (`RemoveObject` in cascada, `:3192-3201`) si redimensioneaza formularul la zona de text
|
||||
aditional. **Risc gasit, neurmarit mai departe**: pe eroare la interogarea combo-ului de casa,
|
||||
`Init` cheama `poGeneratorNumere.dezaloca_numar(5)` si `Return` (`:3159-3161`) — acelasi tipar de
|
||||
"dezalocare numar pe eroare SQL neasteptata" ca la bug-ul #16 (punctul 6), dar intr-un `Init`, nu
|
||||
intr-o bucla — **nu s-a verificat daca are aceeasi consecinta de recreare completa a formularului**;
|
||||
semnalat, nu investigat suplimentar aici din motive de buget de context.
|
||||
|
||||
**Ordinea de dependente, explicit**: `poDate`+`poGeneratorNumere` (apelant) -> `frm_date_factura`/
|
||||
`frm_date_aviz.Init` (foloseste serie/numar deja create) -> **Oracle: cursorul de articole**
|
||||
(`ofacturare.prg:266-308`, aici poate pica bug #16) -> `frm_facturare_articole2.Init` (foloseste
|
||||
`crscursuri` populat intre pasi, nu de propriul `Init`) -> `frm_alte_date.Init` (face **inca** un apel
|
||||
Oracle nou, cu propriul risc de dezalocare). **Pentru formularul unificat, aceasta secventa de patru
|
||||
`Init`-uri trebuie sa devina un singur `Init` care ruleaza fazat** (o singura data la deschidere) —
|
||||
sau ramane o discutie deschisa daca vreo faza (Oracle-lookup-ul din `frm_alte_date.Init`) ramane
|
||||
intarziata pana la deschiderea sectiunii pliate, ca sa nu incarce round-trip-uri Oracle inutile cand
|
||||
utilizatorul nu ajunge niciodata la acea sectiune.
|
||||
|
||||
---
|
||||
|
||||
## 6. Bug-ul #16 — mecanismul exact, verificat pe cod
|
||||
|
||||
**Text original** (`COMUN\docs\todos.txt:45`): *"la revenire din formularul de curs valutar, focusul
|
||||
revine inainte de numar document, cred ca pe TIP DOCUMENT, si la iesire din serie se regenereaza numar
|
||||
act, ceea ce este periculos daca utilizatorul l-a schimbat".*
|
||||
|
||||
**Verdict: NU e un bug de focus. E o bucla de reincercare care trateaza orice esec Oracle ca pe un "DA,
|
||||
mai fac un document" si reconstruieste formularul de antet de la zero — focusul si regenerarea
|
||||
numarului sunt doar simptomele vizibile ale acestei reconstructii.**
|
||||
|
||||
Lantul complet, verificat linie cu linie:
|
||||
|
||||
1. Utilizatorul completeaza antetul, apasa Termina — `inainte_de_do_termin` trece, formularul de antet
|
||||
se inchide (`pnButon=1`).
|
||||
2. Apelantul (`factureaza`, `ofacturare.prg:266-308`) construieste cursorul de articole din Oracle
|
||||
(`cursor_preturi`/etc., functie de `poDate.tip`). **Daca cererea esueaza** (`lnSucces<0`,
|
||||
`:313`) — inclusiv, dar nu numai, cu eroarea Oracle `-20005` "Nu este setat cursul..." (curs
|
||||
valutar lipsa pentru o valuta din listele de preturi ale utilizatorului, verificata neconditionat
|
||||
in `pack_facturare.verifica_cursuri_valute`, documentat deja in `zi_curs_validare.md`):
|
||||
```
|
||||
ofacturare.prg:313-322
|
||||
If lnSucces < 0
|
||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
||||
If goExecutor.nEroare = 20005
|
||||
vizualizeaza_curs(poDate.zi_curs) && deschide "formularul de curs valutar" (frm_curs, modal)
|
||||
ENDIF
|
||||
poGeneratorNumere.dezaloca_numar(poDate.nIdTipDoc) && numarul alocat se elibereaza
|
||||
Else
|
||||
... [singurul loc unde se cere "Doriti sa continuati?" si se seteaza lnRaspuns]
|
||||
Endif
|
||||
```
|
||||
`vizualizeaza_curs` (`oproceduri_curs.prg:8-42`) e confirmat: deschide `Createobject("frm_curs",
|
||||
tdDataCurs).Show(1)` — chiar formularul din reclamatie.
|
||||
3. **Punctul central, verificat prin numararea `If`/`Else`/`Endif` din fisier**: prompt-ul "Doriti sa
|
||||
continuati cu operatii de acest fel?" care seteaza variabila de bucla `lnRaspuns` **exista doar in
|
||||
ramura `Else` a lui `If lnSucces<0`** (`ofacturare.prg:555-564`, in interiorul aceluiasi bloc
|
||||
`If/Else/Endif` care se inchide abia la `:568`). **Pe ramura de eroare (`lnSucces<0`), `lnRaspuns`
|
||||
nu e niciodata atins.** Isi pastreaza valoarea din intrarea in aceasta iteratie a buclei — pe primul
|
||||
document, `6` (initializat la `:174`, inainte de `Do While lnRaspuns = 6` de la `:175`).
|
||||
4. Bucla externa (`Do While lnRaspuns = 6 ... Enddo`, `:175-571`) **reintra automat**, fara nicio
|
||||
intrebare catre utilizator, pentru ca `lnRaspuns` e inca `6`. In aceasta noua iteratie:
|
||||
`poGeneratorNumere.ResetNumere()` si `poDate.rezultat_serii = poGeneratorNumere.creeaza_cursor_serii(...)`
|
||||
ruleaza din nou (`:208-211`), apoi se creeaza **o instanta noua** de `frm_date_factura`/`frm_date_aviz`
|
||||
(`Createobject`, `:230`) si se arata modal (`:235`) — `poDate` insusi **nu** e recreat (ramane
|
||||
acelasi obiect, cu `nract`/`serie_act` deja setate de utilizator), dar **formularul da**, deci
|
||||
`Init` ruleaza de la capat.
|
||||
5. `frm_date_factura.Init` (`:9563-9796`) reface layout-ul si, **la final, seteaza focus necondiționat
|
||||
pe `ct_clb_fdoc`** (tip document) in cazul normal:
|
||||
```
|
||||
ofacturare.vc2:9788-9793
|
||||
DO CASE
|
||||
CASE poDate.in_valuta = 1 and this.ct_clb_valuta.Top < this.ct_clb_fdoc.Top AND EMPTY(NVL(poDate.nume_valuta,''))
|
||||
this.ct_clb_valuta.SetFocus()
|
||||
OTHERWISE
|
||||
this.ct_clb_fdoc.SetFocus()
|
||||
ENDCASE
|
||||
```
|
||||
**Aceasta e "focusul care revine pe TIP DOCUMENT"** din reclamatie — nu un bug izolat de focus, ci
|
||||
comportamentul normal de `Init` al unui formular nou, aparut unde utilizatorul nu se astepta la un
|
||||
formular nou.
|
||||
6. Cand focusul paraseste `ct_clb_fdoc` (Tab, click in alta parte — orice), se declanseaza
|
||||
`Ct_clb_fdoc._combobox1.LostFocus -> thisform.do_schimba_tipdoc()` (`:9857-9859`). Prima linie a
|
||||
acestei metode, **inainte de orice verificare "tipul chiar s-a schimbat?"**:
|
||||
```
|
||||
ofacturare.vc2:9417 (in do_schimba_tipdoc, INAINTE de guard-ul de la :9419-9421)
|
||||
This.clb_serie_act._cbbase1.LostFocus()
|
||||
```
|
||||
— apeleaza **neconditionat** handler-ul de LostFocus al combo-ului de serie, indiferent daca tipul
|
||||
documentului s-a schimbat sau nu.
|
||||
7. Acel handler (`serii_numere.vc2:138-140`, clasa `clb_serie_act`) cheama `genereazanumar` (`:114-136`):
|
||||
```
|
||||
serii_numere.vc2:122-127
|
||||
If poGeneratorNumere.verifica_serie(This.nid_tipdoc) Or &lcValoare. = 0
|
||||
lcValoare = lcValoare+[=]+ALLTRIM(Str(poGeneratorNumere.aloca_numar(This.nid_tipdoc,Null),20,0))
|
||||
&lcValoare
|
||||
This.Parent.Refresh()
|
||||
Endif
|
||||
```
|
||||
— **aloca un numar nou** si il scrie peste `poDate.nract` prin macro, **necondiționat de ce numar
|
||||
avea utilizatorul inainte**. **Asta e "iesirea din serie regenereaza numarul actului"** din
|
||||
reclamatie — confirmat exact, pana la linia care face scrierea.
|
||||
|
||||
**Verdict pe intrebarea planului ("se rezolva #16 in S3, sau se reproduce bug-ul?"): se poate rezolva
|
||||
in S3, cu un cost mic, dar nu e o consecinta automata a unificarii — trebuie tratat explicit.**
|
||||
Cauza reala nu e in `frm_date_factura`/`do_schimba_tipdoc`/`clb_serie_act` (cod care se comporta
|
||||
"corect" fata de contractul lui local), ci in bucla de reincercare din apelant
|
||||
(`ofacturare.prg:174-571`), care **nu distinge "utilizatorul a confirmat ca vrea alt document" de
|
||||
"cererea Oracle a esuat"**. Doua directii posibile, ambele in afara perimetrului strict al portarii de
|
||||
antet, dar declansate direct de ea:
|
||||
- **(a) minimal**: in formularul unificat, dupa un esec Oracle la pasul de construire a cursorului de
|
||||
articole (echivalentul liniei `:313`), **nu se distruge formularul de antet** — antetul e deja o
|
||||
singura sectiune persistenta a aceluiasi formular, nu un obiect separat recreat de apelant; simpla
|
||||
arhitectura unificata (un `Init` per sesiune de emitere, nu per formular) elimina pasul 4 de mai sus,
|
||||
deci elimina intreg lantul 5-7. **Acesta e motivul pentru care unificarea are sansa reala sa rezolve
|
||||
#16 ca efect secundar** — dar numai daca implementarea nu recreeaza formularul intreg la reincercare
|
||||
dupa eroare Oracle (ceea ce ar reproduce bug-ul identic, doar mutat).
|
||||
- **(b) daca (a) nu e suficient** (de ex. daca dupa fix la curs tot trebuie relansata interogarea de
|
||||
articole): `lnRaspuns` (sau echivalentul lui in noua arhitectura) trebuie resetat explicit pe orice
|
||||
cale care iese din ramura de eroare, ca sa nu mai fie confundat cu "utilizatorul a spus DA".
|
||||
|
||||
**Coordonarea cu #6 (decizia 30 si constrangerea din briefing) — verificat direct pe diff-uri, nu doar
|
||||
pe proza handoff-ului**: `docs\diff_s4_valuta_dialog*.patch` (trei fisiere) ating exclusiv
|
||||
`COMUN\clase\omodificari.vc2`, `COMUN\programe\ofacturare_editare.prg` si un fisier de test —
|
||||
**niciunul nu atinge `ofacturare.vc2` (unde traiesc `frm_date_factura`, `do_schimba_tipdoc`,
|
||||
`clb_serie_act`) sau `ofacturare.prg` (unde traieste bucla `factureaza`)**. Confirmat si de continutul
|
||||
lui handoff intermediar (sters): intreaga lui cercetare e despre `frm_articol_factura`
|
||||
(dialogul de adaugare articol pe linie, alt fisier/clasa) si `frm_modific2024.cmdAdaugaArticol.Click`
|
||||
— un mecanism de valuta **pe linie de articol**, fara nicio legatura cu antetul sau cu bucla de
|
||||
emitere. **#6 nu a atins deloc lantul lui #16. Bug-ul e in intregime valabil si neschimbat.**
|
||||
|
||||
---
|
||||
|
||||
## 7. Ordinea de lucru propusa pentru S3
|
||||
|
||||
Pasi care lasa suita functionala dupa fiecare (calea veche ramane in productie pe tot parcursul —
|
||||
niciun pas nu sterge `frm_date_factura`/`frm_date_aviz`/`frm_alte_date` originale):
|
||||
|
||||
1. **Fuzioneaza `Init`-urile de antet** (`frm_date_factura` + `frm_date_aviz`) intr-o metoda unica pe
|
||||
formularul unificat, ramificata pe `poDate.nIdTipDoc` (punctul 4). Verificare: formularul unificat
|
||||
se deschide pe fiecare din cele ~15 tipuri principale de `poDate.tip` folosite azi in cele doua
|
||||
`Init`-uri, si arata exact aceleasi campuri vizibile/eliminate ca varianta veche — comparatie
|
||||
camp-cu-camp fata de tabelul din `docs\S1_inventar_campuri_formular_unificat.md`.
|
||||
2. **Porteaza cele 29 `do_cauta_*`** (fara ramificare unde sunt identice — `do_cauta_fdoc` — cu
|
||||
ramificare pe `nIdTipDoc` unde difera). Verificare: fiecare cautare deschide acelasi dialog, scrie
|
||||
aceleasi proprietati pe `poDate`, muta focusul identic cu azi.
|
||||
3. **Fuzioneaza `inainte_de_do_termin`**, cu ramificarea documentata la punctul 2. Verificare: aceleasi
|
||||
mesaje de eroare, in acelasi ordine, pentru aceleasi campuri goale, pe fiecare tip.
|
||||
4. **Porteaza `do_schimba_tipdoc`** — decide explicit daca ramane doar-factura sau se extinde si pe
|
||||
aviz (punctul 4, pct. 3). **Aici se rezolva sau nu #16** (punctul 6) — de facut ca parte a acestui
|
||||
pas, nu separat, pentru ca schimbarea de arhitectura care il rezolva (un singur `Init` persistent)
|
||||
e chiar cea care se construieste la pasul 1.
|
||||
5. **Porteaza `but_modifica`/blocarea antetului** (decizia 9, deja proiectata in plan sectiunea I) —
|
||||
depinde de pasii 1-4 fiind stabili, pentru ca reutilizeaza aceleasi controale.
|
||||
6. **Integreaza bucla de emitere** (`factureaza`/`factureaza2`) cu noul formular unificat, tratand
|
||||
explicit esecul Oracle de la cursorul de articole (punctul 6, directia a/b).
|
||||
|
||||
**Criteriul de "gata", rescris verificabil.** Azi planul spune "un document se emite integral din
|
||||
formularul unificat, pe tip 1, cu acelasi rezultat in `vanzari`/`act`/`rul` ca pe calea veche" — fara
|
||||
sa spuna cum se compara. Propunere concreta:
|
||||
- Se emite **acelasi document** (acelasi client, articole, cantitati, preturi) o data pe calea veche
|
||||
(`frm_date_factura` -> `frm_facturare_articole` -> `frm_alte_date`) si o data pe formularul unificat,
|
||||
pe date de test identice, in aceeasi zi contabila.
|
||||
- Se compara, randuri-cu-randuri, rezultatul in `VANZARI` (toate coloanele, nu doar sumele), `ACT`,
|
||||
`RUL`, `DOCUMENTE`, `JV2007` (nota contabila) — cel mai simplu cu doua interogari identice filtrate
|
||||
pe cei doi `ID_FACT`/`ID_VANZARE` rezultati, exportate si diff-uite text-cu-text (unealta: acelasi
|
||||
export SQL folosit deja pentru `PACK_FACTURARE` in `docs\ff_*.sql`, adaptat pe `SELECT * FROM VANZARI
|
||||
WHERE ID_FACT=...`).
|
||||
- Diferentele **asteptate** (marcate ca OK, nu ca esec): `ID_VANZARE`/`ID_FACT`/timestamp-uri de
|
||||
creare — orice altceva trebuie sa fie identic.
|
||||
- Se repeta pentru **cel putin un tip de factura in valuta** (S3 atinge direct campurile de valuta) si
|
||||
**un tip de aviz** (ramificarea de la punctul 4), nu doar tip 1.
|
||||
|
||||
---
|
||||
|
||||
## 8. Ce nu se poate testa headless din S3
|
||||
|
||||
Capcana cunoscuta (`COMUN\docs\depanare_testare_vfp.md`, memorie de proiect): sub harness `-A -T`,
|
||||
coloanele de grid nu se materializeaza (`ColumnCount=0`, `RecordSource` raman artefacte necitite) —
|
||||
relevant direct pentru `frm_facturare_articole2` (grid-ul de compunere), dar S3 propriu-zis nu atinge
|
||||
gridul.
|
||||
|
||||
Specific pentru S3 (antet):
|
||||
- **Toate cele 29 `do_cauta_*`** deschid un dialog modal de cautare (`caut_ora.vcx`/similare) —
|
||||
interactiunea reala de selectare dintr-o lista si `Show(1)` modal nu se poate simula headless; se
|
||||
pot testa doar efectele **dupa** ce `poCauta`/rezultatul e construit manual (tiparul deja folosit in
|
||||
`test_pret_cu_tva_dialog.prg`/`test_adauga_linie_valuta.prg` mentionat in
|
||||
handoff intermediar (sters), punctul 7) — apel direct al metodei cu un obiect
|
||||
simulat, fara `Show()`.
|
||||
- **`Init`-ul complet** (redimensionare, `RemoveObject` in cascada, repozitionare containere) e
|
||||
verificabil pe proprietati (`.Visible`, `.Height`, existenta obiectului dupa `Type(...)`) fara UI
|
||||
vizibil, pentru ca `Createobject` ruleaza `Init` fara `Show()` — tiparul e deja validat in codebase.
|
||||
- **Focusul si secventa reala de `LostFocus`** (exact ce a produs bug #16) **nu se poate reproduce
|
||||
headless** — necesita fie `vfp_ui_harness.ps1` cu UI vizibil si input simulat pe masina, fie testare
|
||||
manuala de Marius; simularea unui `SetFocus()`/`LostFocus()` apelat direct din cod ocoleste tocmai
|
||||
secventa evenimentelor native care a cauzat bugul.
|
||||
- **Eroarea Oracle -20005 si redeschiderea `vizualizeaza_curs`** — reproductibila headless doar daca
|
||||
se poate forta controlat lipsa unui curs pentru o data de test (manipulare de date, nu de UI) — nu
|
||||
s-a verificat aici daca exista deja o retetare de date de test pentru asta.
|
||||
|
||||
---
|
||||
|
||||
## Ce nu s-a putut stabili si de ce
|
||||
|
||||
- **Corpul complet al `frm_date_aviz.Init`** (`:7354-7600`, 247 linii) — citit doar partial in sesiuni
|
||||
anterioare (pana la `~7533`, conform `inventar_controale_formulare.md:166-167`); nu re-citit integral
|
||||
aici din motive de buget de context. Structura generala (Do Case pe tip, RemoveObject, focus final)
|
||||
e foarte probabil simetrica cu `frm_date_factura.Init`, dar nu verificata linie-cu-linie.
|
||||
- **`do_cauta_venit`** (`frm_date_factura`, `:9393-9394`, o singura linie) — nu s-a citit continutul;
|
||||
posibil alias/stub mort, de verificat la implementare.
|
||||
- **Riscul semnalat la punctul 5** (`frm_alte_date.Init:3159`, `dezaloca_numar` pe eroare la
|
||||
interogarea combo-ului de casa) — **doar semnalat, neurmarit**: nu s-a verificat daca apelantul care
|
||||
cheama `frm_alte_date` are aceeasi bucla "retry silentios" ca `factureaza`, sau daca eroarea aici
|
||||
chiar opreste fluxul curat (`Return` explicit la `:3161`, spre deosebire de bug #16 unde nu exista
|
||||
`Return` echivalent). Merita o cercetare separata, de marimea celei de la punctul 6, inainte de
|
||||
implementare.
|
||||
- **`Clb_serie_act1.TEXT_SIMPLU1.LostFocus`** din `frm_facturare_articole2` (`:19263-19270`) — nu
|
||||
citit; nu s-a verificat daca reproduce corect `genereazanumar` sau e alt cod orfan ca cel de la
|
||||
punctul 3.
|
||||
- **Ramificarea exacta linie-cu-linie pentru restul celor 27 de perechi `do_cauta_*`** (dincolo de
|
||||
`do_cauta_client` si `do_cauta_fdoc`, comparate direct aici) — s-au comparat doar lungimile (indiciu
|
||||
de diferenta), nu continutul; de citit la implementare, nu presupus din lungime.
|
||||
- **`frm_modifica_factura`** — clasa reala traieste in `ofacturare_comun.vc2` (perimetrul #6, doar
|
||||
citire permisa), dar `vfp_symbols.ps1` a indexat-o din `.pre_s4butoane.bak.vc2` (linii duplicate,
|
||||
nesigure) — nu s-au recitit liniile reale, pentru ca `frm_modifica_factura` **nu intra in portarea
|
||||
S3** (e inlocuita de `but_modifica`, deja proiectat separat in plan, sectiunea I).
|
||||
|
||||
## Bug-uri semnalate, nereparate
|
||||
|
||||
1. **Cod orfan in `frm_facturare_articole2`**: `clb_fdoc.cboFdoc.Valid` (`ofacturare.vc2:19256`) cheama
|
||||
`thisform.do_schimba_tipdoc()`, metoda **inexistenta** pe aceasta clasa — ar arunca eroare VFP daca
|
||||
userul ar interactiona cu acest combo pe formularul mort. Fara consecinte azi (formularul nu e
|
||||
accesibil in productie, cf. plan sectiunea A), dar de curatat sau completat cand prototipul devine
|
||||
baza formularului unificat.
|
||||
2. **Bug #16, confirmat si localizat complet** (punctul 6) — in `ofacturare.prg:555-564` (si simetric
|
||||
in `factureaza2`, liniile ~1061 dupa numerotarea echivalenta, nu verificate linie-cu-linie aici):
|
||||
`lnRaspuns` nu se reseteaza pe ramura de eroare Oracle a buclei de emitere, cauzand recrearea
|
||||
completa si nesolicitata a formularului de antet. In afara perimetrului `ofacturare_comun.vc2`/
|
||||
`ofacturare_editare.prg` (deci nu ciocneste cu #6), dar in `ofacturare.vc2`/`ofacturare.prg`, cod
|
||||
comun suitei (afecteaza si ROACONT/ROAGEST/etc. daca folosesc acelasi `factureaza`/`factureaza2`
|
||||
din `COMUN` — neverificat aici daca alte produse il apeleaza, dar fisierul e in `COMUN\programe`,
|
||||
deci probabil da).
|
||||
3. **Risc structural similar, neconfirmat**: `frm_alte_date.Init:3159` — acelasi tipar
|
||||
"`dezaloca_numar` pe eroare SQL neasteptata", posibil fara aceeasi consecinta de reincercare
|
||||
silentioasa, dar nu verificat (vezi "Ce nu s-a putut stabili").
|
||||
Reference in New Issue
Block a user