Compare commits
4 Commits
15e13cc333
...
df9698375a
| Author | SHA1 | Date | |
|---|---|---|---|
| df9698375a | |||
| ca465641e8 | |||
| 22e97a9225 | |||
| 95bb2b5b74 |
@@ -5228,7 +5228,7 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
|
||||
|
||||
PROCEDURE citesteraspunsuri
|
||||
Local llSucces, lnOptiune, lnZile, llFortareRaspunsuri, llTest
|
||||
lnOptiune = xmenu('Ultima zi;Ultimele 2 zile;Ultimele 3 zile;Ultimele 7 zile;Ultimele 15 zile;Ultimele 30 zile;Ultimele 60 zile')
|
||||
lnOptiune = xmenu('Ultima zi;Ultimele 2 zile;Ultimele 3 zile;Ultimele 7 zile;Ultimele 15 zile;Ultimele 30 zile;Ultimele 60 zile;Import arhiva zip de pe disc...')
|
||||
IF EMPTY(m.lnOptiune)
|
||||
RETURN
|
||||
ENDIF
|
||||
@@ -5238,7 +5238,11 @@ DEFINE CLASS anaf_efactura AS _frmbase OF "_frm_base.vcx"
|
||||
llTest = This.lTest
|
||||
|
||||
*!* llSucces = Thisform.oAnaf.RaspunsuriFacturi(m.lnZile, m.llFortareRaspunsuri)
|
||||
llSucces = efactura_client(m.lnZile, m.llFortareRaspunsuri, m.llTest)
|
||||
IF m.lnOptiune = 8
|
||||
llSucces = Thisform.oAnaf.AnafeFacturaServer.ImportZipLocal()
|
||||
ELSE
|
||||
llSucces = efactura_client(m.lnZile, m.llFortareRaspunsuri, m.llTest)
|
||||
ENDIF
|
||||
|
||||
Thisform.LockScreen = .T.
|
||||
save_grid_tag(Thisform.pgfeFactura.page2.grdFacturiPrimite)
|
||||
|
||||
@@ -385,18 +385,12 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cTva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoare.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoare.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareTva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareTva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cPretv.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cPretv.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cTvav.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cTvav.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cPretvtva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cPretvtva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareV.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareV.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareVTva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareVTva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cProc_tva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cProc_tva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cUtil.Header1" UniqueID="" Timestamp="" />
|
||||
@@ -478,6 +472,12 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cAles._checkbox1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cIdFact.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cIdFact.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareTva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareTva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareV.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareV.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareVTva.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.grdrulaje.cValoareVTva.Text1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.Shape1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.Text2" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page1.Label1" UniqueID="" Timestamp="" />
|
||||
@@ -527,6 +527,9 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label2" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtValoare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtFurnizor" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_sectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_lucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_responsabil" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_furnizor" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label6" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtSerieAct" UniqueID="" Timestamp="" />
|
||||
@@ -534,14 +537,11 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label7" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label8" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtReponsabil" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label9" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label10" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtSectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtLucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label3" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.txtSectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.Label9" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_responsabil" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_lucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page2.but_modifica_sectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.grdbon" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.grdbon.cNrcrt.Header1" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.grdbon.cNrcrt.Text1" UniqueID="" Timestamp="" />
|
||||
@@ -572,8 +572,8 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.txtLucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.txtSectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.but_modifica_responsabil" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.but_modifica_lucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.but_modifica_sectie" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.but_modifica_lucrare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.txtValoare" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.Label8" UniqueID="" Timestamp="" />
|
||||
*< OBJECTDATA: ObjPath="pgframe.Page3.Text4" UniqueID="" Timestamp="" />
|
||||
@@ -960,7 +960,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column19.InputMask = (get_mask(12,gnPPRET)), ;
|
||||
Column19.Name = "cTva", ;
|
||||
Column19.ReadOnly = .T., ;
|
||||
Column19.Width = 75, ;
|
||||
Column19.Width = 117, ;
|
||||
Column20.ColumnOrder = 27, ;
|
||||
Column20.ControlSource = "round((cant+cante)*pret,gnPc)", ;
|
||||
Column20.FontName = "Arial", ;
|
||||
@@ -968,7 +968,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column20.InputMask = (get_mask(12,gnPa)), ;
|
||||
Column20.Name = "cValoare", ;
|
||||
Column20.ReadOnly = .T., ;
|
||||
Column20.Width = 93, ;
|
||||
Column20.Width = 119, ;
|
||||
Column21.ColumnOrder = 30, ;
|
||||
Column21.ControlSource = "pretv", ;
|
||||
Column21.FontName = "Arial", ;
|
||||
@@ -976,6 +976,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column21.InputMask = (get_mask(14,gnPPretV)), ;
|
||||
Column21.Name = "cPretv", ;
|
||||
Column21.ReadOnly = .T., ;
|
||||
Column21.Width = 101, ;
|
||||
Column22.ColumnOrder = 31, ;
|
||||
Column22.ControlSource = "tvav", ;
|
||||
Column22.FontName = "Arial", ;
|
||||
@@ -983,7 +984,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column22.InputMask = (get_mask(14,gnPPretV)), ;
|
||||
Column22.Name = "cTvav", ;
|
||||
Column22.ReadOnly = .T., ;
|
||||
Column22.Width = 109, ;
|
||||
Column22.Width = 113, ;
|
||||
Column23.ColumnOrder = 32, ;
|
||||
Column23.ControlSource = "pretv+tvav", ;
|
||||
Column23.FontName = "Arial", ;
|
||||
@@ -991,7 +992,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column23.InputMask = (get_mask(14,gnPPretV)), ;
|
||||
Column23.Name = "cPretvtva", ;
|
||||
Column23.ReadOnly = .T., ;
|
||||
Column23.Width = 111, ;
|
||||
Column23.Width = 123, ;
|
||||
Column24.ColumnOrder = 35, ;
|
||||
Column24.ControlSource = "proc_tvav", ;
|
||||
Column24.FontName = "Arial", ;
|
||||
@@ -1228,7 +1229,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column64.InputMask = (get_mask(12,gnPa)), ;
|
||||
Column64.Name = "cValoareTva", ;
|
||||
Column64.ReadOnly = .T., ;
|
||||
Column64.Width = 93, ;
|
||||
Column64.Width = 147, ;
|
||||
Column65.ColumnOrder = 33, ;
|
||||
Column65.ControlSource = "round((cant+cante)*pretv,gnPc)", ;
|
||||
Column65.FontName = "Arial", ;
|
||||
@@ -1236,7 +1237,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column65.InputMask = (get_mask(12,gnPa)), ;
|
||||
Column65.Name = "cValoareV", ;
|
||||
Column65.ReadOnly = .T., ;
|
||||
Column65.Width = 93, ;
|
||||
Column65.Width = 108, ;
|
||||
Column66.ColumnOrder = 34, ;
|
||||
Column66.ControlSource = "round((cant+cante)*(pretv+tvav),gnPc)", ;
|
||||
Column66.FontName = "Arial", ;
|
||||
@@ -1244,7 +1245,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
Column66.InputMask = (get_mask(12,gnPa)), ;
|
||||
Column66.Name = "cValoareVTva", ;
|
||||
Column66.ReadOnly = .T., ;
|
||||
Column66.Width = 100
|
||||
Column66.Width = 143
|
||||
*< END OBJECT: ClassLib="_grd_base.vcx" BaseClass="grid" />
|
||||
|
||||
ADD OBJECT 'pgframe.Page1.grdrulaje.cAcont.Header1' AS header WITH ;
|
||||
@@ -2265,7 +2266,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
|
||||
ADD OBJECT 'pgframe.Page1.grdrulaje.cTva.Header1' AS header WITH ;
|
||||
Alignment = 2, ;
|
||||
Caption = "TVA achizitie", ;
|
||||
Caption = "TVA unitar achizitie", ;
|
||||
FontSize = 10, ;
|
||||
Name = "Header1"
|
||||
*< END OBJECT: BaseClass="header" />
|
||||
@@ -2283,7 +2284,7 @@ DEFINE CLASS frm_rulaje AS _frmbase OF "_frm_base.vcx"
|
||||
|
||||
ADD OBJECT 'pgframe.Page1.grdrulaje.cTvav.Header1' AS header WITH ;
|
||||
Alignment = 2, ;
|
||||
Caption = "TVA vanzare", ;
|
||||
Caption = "TVA unitar vanzare", ;
|
||||
FontSize = 10, ;
|
||||
Name = "Header1"
|
||||
*< END OBJECT: BaseClass="header" />
|
||||
|
||||
@@ -1,174 +1,126 @@
|
||||
# Depanare si testare VFP fara IDE (headless)
|
||||
|
||||
Metoda folosita la depanarea erorii "Property CNRCRT is not found" (ROACONT 2.11.61,
|
||||
15.07.2026) - reutilizabila pentru orice bug runtime VFP. Complementara cu
|
||||
`flux-editare-vfp-text.md` (editare .vcx/.scx pe text) si `cautare_vcx_vct.md` (cautare).
|
||||
|
||||
## 0. Reguli de lucru
|
||||
|
||||
Vezi `reguli_lucru.md` (diff ca fisier + aprobare inainte de write-back, comentarii
|
||||
max o linie, modificari minime).
|
||||
|
||||
## Fisiere de testare (foloseste-le la orice depanare/testare viitoare)
|
||||
|
||||
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
|
||||
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO).
|
||||
Echivalentul headless al `COMUN\programe\test_init_env.prg` (care ramane varianta
|
||||
interactiva, cu INPUTBOX si meniu de lansare formulare).
|
||||
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - exemplu complet de harness:
|
||||
init mediu -> replicare flux (cursoarele din modificanote) -> instantiere forma reala ->
|
||||
executie pas cu pas cu logare -> verdict OK/BUG. Copiaza-i structura pentru bug-uri noi.
|
||||
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness instantiere import_nota
|
||||
(ROAGEST) fara Oracle: cursori dummy + goExecutor dummy; parametrul LPARAMETERS
|
||||
incarca o copie de clasa din scratchpad in loc de cea reala (bisectie A/B).
|
||||
- `COMUN\utile\Teste\teste_roacont.prg` si `COMUN\utile\Teste\test.prg` - teste ad hoc istorice.
|
||||
- Mediu de test standard: server `CENTRAL`, schema `MARIUSM_AUTO`, parola schemei
|
||||
`ROMFASTSOFT`; utilizator de aplicatie "MARIUS M" / parola "123" (unde e nevoie de login).
|
||||
|
||||
## 1. Citirea log-ului aplicatiei (log.txt in radacina proiectului)
|
||||
|
||||
`ErrorHandler` (roacont.prg) scrie pentru fiecare eroare: `nErrror`, `cMethod`,
|
||||
`line` (0 daca exe-ul e compilat fara debug info) si stack-ul complet.
|
||||
|
||||
Semantica ON ERROR (esentiala la interpretare):
|
||||
- Dupa ce handlerul face RETURN, executia CONTINUA de la linia urmatoare din metoda
|
||||
care a crapat. O singura cauza produce deci CASCADE de erori.
|
||||
- Tipar clasic: eroare pe linia `WITH <expr>` => urmatoarele linii `.prop = ...` dau
|
||||
1940 "Expression is not valid outside of WITH/ENDWITH", apoi `ENDWITH` da
|
||||
1939 "WITH/ENDWITH mismatch". PRIMA eroare din secventa e cea reala; restul e zgomot.
|
||||
- Erorile din interiorul TRY/CATCH nu ajung in log (inghitite). Un CATCH gol poate
|
||||
ascunde ani de zile un defect latent, care "explodeaza" cand codul din jur se repara
|
||||
(exact cazul typo-ului oMinHeaderHeight din cusfonthandler).
|
||||
- In stack, liniile "ON..." sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
|
||||
|
||||
## 2. Rulare headless a scripturilor VFP
|
||||
|
||||
Sablonul unui script de diagnostic (vezi `utile\Teste\test_repro_cnrcrt_footer.prg`):
|
||||
- logare in fisier cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
|
||||
- `ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza
|
||||
si continua - mimeaza comportamentul ErrorHandler din aplicatie;
|
||||
- `QUIT` obligatoriu la final (altfel ramane vfp9.exe agatat);
|
||||
- lansare din PowerShell cu timeout:
|
||||
`$p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru; $p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() }`
|
||||
- `-A` ignora config, `-T` fara ecran de start; parametrul dupa .prg ajunge in LPARAMETERS.
|
||||
- LINENO() din handler e relativ la inceputul metodei (utile pentru localizare in .vc2:
|
||||
linia din metoda = linia raportata + linia PROCEDURE din .vc2).
|
||||
|
||||
Precautii: nu rula cu VFP IDE sau exe-ul aplicatiei deschis (lock pe binare);
|
||||
sterge .fxp-ul vechi de langa .prg daca ai editat scriptul intre rulari.
|
||||
|
||||
## 3. Mediu complet cu Oracle, fara interactiune
|
||||
|
||||
`utile\Teste\test_init_env_auto.prg` = varianta neinteractiva a
|
||||
`programe\test_init_env.prg` (fara INPUTBOX/xmenu): primeste host/schema/parola ca
|
||||
parametri, seteaza PATH/CLASSLIB/PROCEDURE ca roacont.prg, instantiaza
|
||||
goConn/goExecutor/goApp/goLog, incarca goFirma/goCalendar/optiuni si pachetele de
|
||||
sesiune Oracle. Dupa apel poti instantia forme reale (`CREATEOBJECT('frm_xxx', ...)`)
|
||||
si apela metodele lor punctual, cu cursoarele pregatite ca in fluxul real.
|
||||
|
||||
Pentru forme care se deschid modal (Show(1) blocheaza), nu apela Show: replica in
|
||||
script doar liniile relevante din Show (attach, calcule) si logheaza starea intre ele.
|
||||
|
||||
## 4. Bisectia versiunilor de clase (regresii)
|
||||
|
||||
Cand un bug apare "de azi", compara comportamentul cu clasa veche fara a atinge SVN:
|
||||
1. `git show <rev_vechi>:clase/fisier.vcx > temp\fisier.vcx` (+ .vct identic);
|
||||
2. convertit la text pentru diff: `vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`;
|
||||
3. in scriptul de test, `SET CLASSLIB TO temp\fisier.vcx ADDITIVE` (varianta veche)
|
||||
vs. cea din COMUN (varianta noua) - A/B pe acelasi flux;
|
||||
4. cand difera rezultatul, cauza e in diff-ul acelei clase (sau intr-un defect latent
|
||||
pe care noul cod il activeaza - vezi capcanele de mai jos).
|
||||
|
||||
Varianta pe backup-uri de runda (fara git): copiaza `*.vc2.pre_rundaN.bak` din cache
|
||||
intr-un folder temp ca `ointroduceri.vc2`, regenereaza binarul direct cu
|
||||
`FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune `..\include\foxpro.h` langa,
|
||||
altfel .ERR), apoi in harness incarca DOAR copia (nu ADDITIVE peste cea reala -
|
||||
rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ).
|
||||
Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource ->
|
||||
camp simplu, sterge linii de proprietate din mijlocul blocului) - NU sterge
|
||||
obiecte/coloane din ADD OBJECT (strica structura: "Cannot add this object to a Grid").
|
||||
|
||||
## 5. Capcane VFP descoperite (de verificat la bug-uri asemanatoare)
|
||||
|
||||
- `SELECT ... INTO CURSOR` NU mosteneste indecsii cursorului sursa. Orice
|
||||
`Seek(..., 'cursor', 'tag')` pe copie crapa. `update_jtva_coloane` creeaza
|
||||
`INDEX ON id_jtva_coloana TAG id_jtva`; daca ii copiezi rezultatul, recreeaza indexul.
|
||||
- Un grid isi pierde TOATE coloanele (ColumnCount devine 0, fara nicio eroare in log)
|
||||
daca un ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse
|
||||
programatic (ex. FontName/FontSize per coloana in cusfonthandler.ApplyFontToGrid,
|
||||
la Init-ul formei prin _frmbase.SetFont). Simptome tipice in aval:
|
||||
`_grdfooter.attachtogrid` copiaza 0 coloane, apoi `calctotal` da
|
||||
"Property <cColWithLabel> is not found" la `Getpem(This, This.cColWithLabel)`.
|
||||
- ControlSource-uri cu campuri inexistente in RecordSource sunt tolerate la afisare,
|
||||
dar orice expresie care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie
|
||||
nedefinita) declanseaza comportamentul de mai sus.
|
||||
- Fix-ul unui typo intr-o clasa partajata poate activa defecte latente in aval
|
||||
(codul care nu rulase niciodata incepe sa ruleze). La regresii dupa fix-uri
|
||||
"inofensive" in COMUN, cauta defectul latent, nu doar diff-ul.
|
||||
- **Coloana de grid cu ControlSource EXPRESIE (nu camp) cere `Bound = .F.`**.
|
||||
Fara el: eroare 9 "Data type mismatch" la CREATEOBJECT-ul formei (faza de
|
||||
constructie, raportata la linia apelanta, Details gol). Caz: import_nota.GridArt
|
||||
Column7/8/9 cu `round(...)` (ROAGEST 16.07.2026); model corect: Grid1.cTaxCode
|
||||
(`Bound = .F.` + expresie, functional de ani).
|
||||
|
||||
## 6. Capcane la SCRIEREA scriptului de test (nu ale bug-ului investigat)
|
||||
|
||||
Descoperite depanand harness-ul pentru "Data type mismatch" la `CREATEOBJECT('IMPORT_nota',...)`
|
||||
(ROAGEST, 16.07.2026) - de verificat oricand un script de test nou pare "agatat".
|
||||
|
||||
- **`DEFINE CLASS ... ENDDEFINE` NU poate sta la mijlocul programului principal.**
|
||||
Daca il pui intre alte instructiuni de top-level (ex. clasa dummy pentru un
|
||||
`goExecutor` de test, definita devreme "ca sa fie la indemana"), VFP compileaza
|
||||
gresit: TOATE liniile de DUPA `ENDDEFINE` dau "Statement is not in a procedure"
|
||||
(vezi `<script>.ERR` generat la compilare). Simptom in productie: `vfp9.exe`
|
||||
pare agatat la nesfarsit - CPU ~0, nicio fereastra de dialog vizibila (verificat
|
||||
si prin enumerare Win32 a ferestrelor procesului), pentru ca de fapt a rulat
|
||||
doar liniile de dinaintea `DEFINE CLASS` si s-a oprit silentios dupa eroarea de
|
||||
compilare, fara sa ajunga la `QUIT`. Fix: muta orice `DEFINE CLASS` auxiliar
|
||||
(dummy executor, helpere) la FINALUL fisierului, dupa `QUIT`, langa celelalte
|
||||
`PROCEDURE` - e locul standard pentru cod suport intr-un script de test.
|
||||
- **Diagnostic rapid compilare vs blocaj real**: cand un script "nu se termina",
|
||||
verifica INTAI daca a aparut `<script>.ERR` langa `.prg` (log de compilare,
|
||||
vezi si `roagest.ERR`/`log.txt` in radacina proiectului) - inseamna ca s-a
|
||||
oprit la compilare, nu ca ruleaza/asteapta ceva. Daca nu exista `.ERR`, verifica
|
||||
CPU-ul procesului (`Get-Process | select CPU`): ~0 dupa cateva secunde inseamna
|
||||
ca asteapta un dialog modal (foloseste enumerarea Win32 de ferestre de mai jos
|
||||
ca sa confirmi), nu ca proceseaza ceva lent.
|
||||
- **Enumerare ferestre Win32 pentru un proces "agatat"** (utila cand nu esti sigur
|
||||
daca exista un dialog modal ascuns): `Add-Type` cu `EnumWindows`/`GetWindowText`/
|
||||
`GetWindowThreadProcessId` din `user32.dll`, filtrat pe PID-ul lui `vfp9.exe`.
|
||||
Daca vezi doar fereastra principala + "Command", nu e dialog - cauta alta cauza
|
||||
(vezi punctul de mai sus).
|
||||
- **`STRTOFILE` inainte de `SET SAFETY OFF` = dialog modal "Overwrite?"** daca
|
||||
fisierul de log exista deja (sub `-A`, SAFETY porneste ON). Pune `SET SAFETY OFF`
|
||||
PRIMA linie sau sterge log-ul inainte de fiecare rulare.
|
||||
- **`-A -T` sunt obligatorii impreuna** la lansare headless (`-A` ignora config.fpw,
|
||||
`-T` sare peste splash screen-ul de start) - fara `-T` procesul ramane agatat pe
|
||||
ecranul de intampinare, identic ca simptom cu o eroare de compilare (CPU ~0,
|
||||
fara log). Confirma mereu care e cauza inainte de a schimba scriptul.
|
||||
- **`goExecutor`/alte dependinte "dummy"**: `CREATEOBJECT('Custom')` + `AddProperty()`
|
||||
poate adauga doar PROPRIETATI, nu METODE. Daca clasa testata apeleaza
|
||||
`goExecutor.oExecute(...)`/`oExecuta(...)`, dummy-ul trebuie sa fie o clasa
|
||||
proprie (`DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute`/
|
||||
`oExecuta` reale), nu un `Custom` cu proprietati adaugate.
|
||||
- **Erorile de instantiere (`CREATEOBJECT`) raportate la linia CREATEOBJECT, nu la
|
||||
`.Init`**: cand `ON ERROR`/`CATCH` arata `Procedure` = scriptul apelant (nu
|
||||
`NUMECLASA.INIT`), inseamna ca eroarea vine din evaluarea proprietatilor/
|
||||
obiectelor din DEFINITIA clasei (`ADD OBJECT ... WITH RowSource = ...`,
|
||||
`ControlSource` de coloana grid evaluat ca expresie, etc.) - controale native
|
||||
VFP (ex. combobox cu `RowSourceType=6` Fields) isi rezolva bind-ul la
|
||||
CONSTRUCTIE, inainte sa apuce sa ruleze `PROCEDURE Init` a formei. Cand
|
||||
reproduci un astfel de bug cu un mediu minimal, o "Alias is not found" pe un
|
||||
cursor pe care nu l-ai deschis tu confirma mecanismul (faza de instantiere),
|
||||
chiar daca nu e mesajul EXACT din bug-ul original.
|
||||
|
||||
## 7. Studiu de caz: "Property CNRCRT is not found" (15.07.2026)
|
||||
|
||||
Lant complet: `frm_import_extrase_banca.modificanote` copia `crsJTVATemp` fara index
|
||||
(defect latent) -> coloana `cExplicatieTVA` din `frm_modific2024.grdRulaje` face
|
||||
`Seek(trul.id_jtva_coloana,'crsJtvaTemp','id_jtva')` in ControlSource -> fix-ul
|
||||
typo-ului `oMinHeaderHeight`->`nMinHeaderHeight` in `cusfonthandler` (r17869) a facut
|
||||
ca ApplyFontToGrid sa atinga coloanele la Init -> grid-ul a ramas cu 0 coloane ->
|
||||
footer-ul a crapat in calctotal. Fix: `INDEX ON id_jtva_coloana TAG id_jtva` dupa
|
||||
SELECT INTO in modificanote. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`
|
||||
(mod FARAINDEX reproduce bug-ul, mod CUINDEX valideaza fix-ul).
|
||||
# Depanare si testare VFP fara IDE (headless)
|
||||
|
||||
Metoda de depanare a bug-urilor runtime VFP, valabila pentru toate proiectele ROA.
|
||||
Complementara cu `flux-editare-vfp-text.md` (editare .vcx/.scx pe text), `cautare_vcx_vct.md`
|
||||
(cautare in binare) si `testare-ui-vfp.md` (teste cu formular vizibil + screenshots).
|
||||
Reguli de livrare: `reguli_lucru.md`.
|
||||
|
||||
## 1. Fisiere de mediu si exemple
|
||||
|
||||
- `COMUN\utile\Teste\test_init_env_auto.prg` - initializare completa de mediu FARA
|
||||
interactiune (parametri: host, schema, parola; implicit CENTRAL / MARIUSM_AUTO, parola
|
||||
schemei `ROMFASTSOFT`; user aplicatie "MARIUS M" / "123"). Seteaza PATH/CLASSLIB/PROCEDURE
|
||||
ca programul principal si instantiaza goConn/goExecutor/goApp/goLog/goFirma/goCalendar +
|
||||
pachetele de sesiune Oracle. Dupa apel poti face `CREATEOBJECT('frm_xxx')` si apela metodele
|
||||
punctual. Varianta interactiva (INPUTBOX + meniu): `COMUN\programe\test_init_env.prg`.
|
||||
- `COMUN\utile\Teste\test_repro_cnrcrt_footer.prg` - harness model: init mediu -> replicare
|
||||
flux -> instantiere forma reala -> executie pas cu pas cu logare -> verdict OK/BUG.
|
||||
- `COMUN\utile\Teste\test_repro_import_nota.prg` - harness fara Oracle (cursori + goExecutor
|
||||
dummy) cu parametru care incarca o copie de clasa din scratchpad, pentru bisectie A/B.
|
||||
|
||||
## 2. Rulare headless
|
||||
|
||||
`vfp9.exe -c<config.fpw> -A -T "<script.prg>" <param>`, lansat din PowerShell cu timeout:
|
||||
```
|
||||
$p = Start-Process 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' -ArgumentList '-A','-T','"D:\...\script.prg" PARAM' -PassThru
|
||||
$p.WaitForExit(120000); if (!$p.HasExited) { $p.Kill() }
|
||||
```
|
||||
- `-A` ignora config.fpw implicit, `-T` sare peste splash; **ambele obligatorii** - fara `-T`
|
||||
procesul ramane pe ecranul de intampinare (simptom identic cu o eroare de compilare).
|
||||
Un `-c<cale>` explicit (lipit de flag) e preluat chiar si impreuna cu `-A`.
|
||||
- **SAFETY**: sub `-A` porneste ON, deci primul `STRTOFILE` peste un log existent (sau
|
||||
`COMPILE` peste un `.err`/`.fxp` existent) scoate dialogul modal "overwrite?" si procesul
|
||||
pare agatat. Pune `SET SAFETY OFF` ca PRIMA linie din script si/sau `SAFETY=OFF` +
|
||||
`RESOURCE=OFF` intr-un config.fpw propriu pasat cu `-c`. Verificare: logheaza
|
||||
`SET("SAFETY")`/`SET("RESOURCE")` la start.
|
||||
- In script: logare cu STRTOFILE (nu WAIT WINDOW / debugger - nu exista consola);
|
||||
`ON ERROR DO <handler> WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()` care logheaza si
|
||||
continua (mimeaza ErrorHandler); `QUIT` obligatoriu la final.
|
||||
- `LINENO()` din handler e relativ la inceputul metodei: linia in `.vc2` = linia raportata +
|
||||
linia `PROCEDURE`.
|
||||
- Nu rula cu IDE-ul sau exe-ul aplicatiei deschis (lock pe binare); sterge `.fxp`-ul vechi
|
||||
daca ai editat scriptul intre rulari.
|
||||
- Forme modale: `Show(1)` blocheaza. Replica in script doar liniile relevante din Show, sau
|
||||
seteaza `WindowType = 0`.
|
||||
|
||||
## 3. Citirea log-ului aplicatiei (log.txt din radacina proiectului)
|
||||
|
||||
`ErrorHandler` scrie `nErrror`, `cMethod`, `line` (0 daca exe-ul e fara debug info) si stack-ul.
|
||||
Semantica ON ERROR, esentiala la interpretare:
|
||||
- Dupa RETURN din handler executia CONTINUA de la linia urmatoare => o singura cauza produce
|
||||
CASCADE de erori. Tipar clasic: eroare pe `WITH <expr>` => liniile `.prop=` dau 1940
|
||||
"Expression is not valid outside of WITH/ENDWITH", `ENDWITH` da 1939. **Prima eroare din
|
||||
secventa e cea reala**, restul e zgomot.
|
||||
- Erorile din TRY/CATCH nu ajung in log. Un CATCH gol poate ascunde ani un defect latent care
|
||||
"explodeaza" cand codul din jur se repara.
|
||||
- Liniile "ON..." din stack sunt dispatch-uri (ON ERROR / READ EVENTS), nu metode reale.
|
||||
|
||||
## 4. Verificare de sintaxa prin compilare headless (dupa orice editare de `.prg`)
|
||||
|
||||
Cel mai ieftin test dupa o modificare: `COMPILE` headless, fara aplicatie si fara Oracle.
|
||||
- Compileaza o COPIE a fisierului in scratchpad, nu sursa din working copy (`COMPILE` scrie
|
||||
`.fxp` langa `.prg`, care ar aparea ca modificare in SVN). VFP nu rezolva simboluri externe
|
||||
la compilare, deci copia izolata e suficienta pentru sintaxa.
|
||||
- Sterge `.err`-ul vechi INAINTE de `COMPILE`; verdictul = existenta lui `<nume>.err` dupa
|
||||
(exista => logheaza continutul; nu exista => compilare curata).
|
||||
- Nu inlocuieste testul functional: prinde sintaxa, nu apeluri catre metode inexistente.
|
||||
|
||||
## 5. Bisectia versiunilor de clase (regresii)
|
||||
|
||||
Cand un bug apare "de azi", compara cu clasa veche fara a atinge SVN:
|
||||
1. `git show <rev>:clase\fisier.vcx > temp\fisier.vcx` (+ `.vct`); pentru diff lizibil,
|
||||
`vcx2txt.ps1 -Source temp\fisier.vcx -CacheRoot temp\txt`.
|
||||
2. In harness incarca DOAR copia (`SET CLASSLIB TO temp\fisier.vcx`), **nu ADDITIVE peste cea
|
||||
reala** - rezolvarea intre doua classlib-uri cu aceeasi clasa nu e garantata => fals negativ.
|
||||
3. Fara git, din backup-urile de runda: copiaza `*.vc2.pre_rundaN.bak` intr-un temp si
|
||||
regenereaza binarul cu `FoxBin2Prg.EXE "<temp>\fisier.vc2" "" "" "" 1 0 1` (pune
|
||||
`..\include\foxpro.h` langa, altfel `.ERR`).
|
||||
4. Bisectie in interiorul unei runde: neutralizeaza valori IN LOC (ControlSource -> camp
|
||||
simplu, sterge linii de proprietate) - NU sterge obiecte/coloane din `ADD OBJECT` (strica
|
||||
structura: "Cannot add this object to a Grid").
|
||||
5. Cand diferenta apare dupa un fix "inofensiv" in COMUN, cauza poate fi un defect latent
|
||||
activat de fix (cod care nu rulase niciodata incepe sa ruleze), nu diff-ul in sine.
|
||||
|
||||
## 6. Capcane VFP (de verificat la bug-uri asemanatoare)
|
||||
|
||||
- **`SELECT ... INTO CURSOR` NU mosteneste indecsii sursei.** Orice `Seek(...,'cursor','tag')`
|
||||
pe copie crapa; recreeaza indexul dupa copiere. Acelasi lucru pentru cursorii creati intr-un
|
||||
test: trebuie sa aiba EXACT indecsii pe care ii creeaza fluxul real inainte de instantiere,
|
||||
altfel ordinea/logica difera si testul da rezultat fals.
|
||||
- **Un grid isi pierde TOATE coloanele** (`ColumnCount` devine 0, fara eroare in log) daca un
|
||||
ControlSource crapa la re-evaluare in timp ce coloanele sunt atinse programatic (ex. font per
|
||||
coloana la Init). Simptome in aval: `attachtogrid` copiaza 0 coloane, apoi `calctotal` da
|
||||
"Property <cColWithLabel> is not found". Similar, un cursor legat la grid recreat de Init
|
||||
lasa grid-ul fara coloane (eroare 1925).
|
||||
- **ControlSource-uri cu campuri inexistente sunt tolerate la afisare**, dar orice expresie
|
||||
care ARUNCA eroare la evaluare (Seek pe tag lipsa, functie nedefinita) declanseaza cazul
|
||||
de mai sus. Repro/regresie: `utile\Teste\test_repro_cnrcrt_footer.prg`.
|
||||
- **Coloana de grid cu ControlSource EXPRESIE (nu camp) cere `Bound = .F.`** Fara el: eroare 9
|
||||
"Data type mismatch" la `CREATEOBJECT`-ul formei (faza de constructie, raportata la linia
|
||||
apelanta, Details gol).
|
||||
- **Erorile de instantiere sunt raportate la linia `CREATEOBJECT`, nu la `.Init`**: daca
|
||||
`Procedure` = scriptul apelant, eroarea vine din evaluarea definitiei clasei (`ADD OBJECT ...
|
||||
WITH RowSource`, ControlSource-expresie), pentru ca bind-ul controalelor native se rezolva la
|
||||
CONSTRUCTIE, inainte de `Init`.
|
||||
- **Literal string > 255 caractere = eroare de COMPILARE, nu de runtime** (masurat 24.07.2026:
|
||||
255 trece, 256 pica). Mesaj: `Unrecognized command verb` daca linia incepe cu un apel de
|
||||
metoda, `Command contains unrecognized phrase/keyword` la o atribuire. Insidios: restul
|
||||
programului ruleaza, doar linia aceea lipseste din `.fxp` si da eroare 16 cand se ajunge la
|
||||
ea. De aceea SQL-ul lung se scrie cu `TEXT TO <var> [TEXTMERGE] NOSHOW ... ENDTEXT` sau
|
||||
concatenat. Verificare: `awk 'length($0) > 260 {print NR": "length($0)}' fisier.prg`.
|
||||
|
||||
## 7. Capcane la SCRIEREA scriptului de test
|
||||
|
||||
- **`DEFINE CLASS ... ENDDEFINE` nu poate sta la mijlocul programului principal.** Toate liniile
|
||||
de DUPA `ENDDEFINE` dau "Statement is not in a procedure" (vezi `<script>.ERR`); scriptul
|
||||
ruleaza doar pana acolo si se opreste silentios, fara sa ajunga la `QUIT` - simptom: vfp9.exe
|
||||
pare agatat la nesfarsit. Pune orice `DEFINE CLASS` auxiliar la FINALUL fisierului.
|
||||
- **Dummy-uri cu metode**: `CREATEOBJECT('Custom')` + `AddProperty()` adauga doar PROPRIETATI.
|
||||
Daca clasa testata apeleaza `goExecutor.oExecute(...)`, dummy-ul trebuie sa fie
|
||||
`DEFINE CLASS dummyexecutor AS Custom` cu `PROCEDURE oExecute` reala.
|
||||
- **Diagnostic "script agatat"**, in ordine: (1) exista `<script>.ERR` langa `.prg`? => s-a
|
||||
oprit la compilare; (2) CPU-ul procesului ~0 dupa cateva secunde (`Get-Process | select CPU`)
|
||||
=> asteapta un dialog modal, nu proceseaza; confirma enumerand ferestrele procesului cu
|
||||
`EnumWindows`/`GetWindowText`/`GetWindowThreadProcessId` (`Add-Type`, user32) filtrat pe PID -
|
||||
daca vezi doar fereastra principala + "Command", nu e dialog, cauta alta cauza (tipic `-T`
|
||||
lipsa). Confirma mereu cauza inainte de a schimba scriptul.
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
Changelog (`changelog_<aplicatie>.txt`): text minimal, pe limba utilizatorului,
|
||||
nu tehnic - o fraza scurta per intrare `:nou:`/`:modificare:`/`:eroare:`.
|
||||
3. Modificari minime: doar ce s-a cerut; fara refactorizari sau curatenie din oficiu.
|
||||
4. Testare headless (fara IDE): `vfp9.exe -A -T "<script.prg>" <param>` din PowerShell,
|
||||
logare cu STRTOFILE, `ON ERROR` care logheaza si continua, QUIT la final.
|
||||
Init mediu neinteractiv: `utile\Teste\test_init_env_auto.prg` (CENTRAL / MARIUSM_AUTO;
|
||||
user aplicatie "MARIUS M"/"123"). Exemplu harness complet: `utile\Teste\test_repro_cnrcrt_footer.prg`.
|
||||
Metoda de depanare (log.txt, cascade ON ERROR, bisectie clase): `depanare_testare_vfp.md`.
|
||||
4. Testare headless (fara IDE): `vfp9.exe -A -T "<script.prg>" <param>` din PowerShell.
|
||||
Mediu, sabloane, capcane si metoda de depanare: `depanare_testare_vfp.md`.
|
||||
5. Editare .vcx/.scx pe text (cache, cp1252 byte-safe, fidelity-check): `flux-editare-vfp-text.md`.
|
||||
Cautare in binare: `cautare_vcx_vct.md`.
|
||||
6. Misiuni lungi (teste, investigatii, sarcini inlantuite): NU un singur subagent cu context
|
||||
|
||||
@@ -1,132 +1,95 @@
|
||||
# Testare UI VFP headless cu harness + screenshots
|
||||
|
||||
Harness reutilizabil pentru teste care afiseaza un formular REAL (fara Oracle/UI complet),
|
||||
executa actiuni si valideaza vizual prin capturi de ecran. Valabil pentru toate proiectele
|
||||
VFP din suita ROA; exemplele sunt pe ROAGEST. Alternativa la
|
||||
`testare-vfp-mcp.md` (care ruleaza programul principal intreg + windows-mcp): aici instantiem o
|
||||
singura clasa cu dependinte dummy, deci pornire rapida si izolata.
|
||||
Harness reutilizabil pentru teste care afiseaza un formular REAL (cu dependinte dummy, fara
|
||||
Oracle), executa actiuni si valideaza vizual prin capturi. Valabil pentru toate proiectele VFP
|
||||
din suita ROA. Alternativa la `testare-vfp-mcp.md` (care porneste programul principal intreg);
|
||||
capcanele generale de rulare headless (`-A -T`, SAFETY, script agatat) sunt in
|
||||
`depanare_testare_vfp.md`.
|
||||
|
||||
Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, parametrizat),
|
||||
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri VFP de handshake),
|
||||
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele de
|
||||
teste stau grupate pe subiect in subfoldere (ex. `achizitie_import\`). Exemplu complet:
|
||||
`achizitie_import\test_import_nota_ui.prg` + `.ps1` (wrapper).
|
||||
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri de handshake),
|
||||
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele stau in
|
||||
subfoldere pe subiect. Exemplu complet: `achizitie_import\test_import_nota_ui.prg` + `.ps1`.
|
||||
|
||||
**REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al
|
||||
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia (ex. "Adauga repere",
|
||||
do_adauga_factura prin dialogul real), NU introducerea directa a randurilor in cursoare/tabele
|
||||
(ex. Append in rul_temp/introdc). Popularea directa sare peste validari/filtre/efecte laterale
|
||||
si da PASS fals (caz real: articol cont 212 "adaugat" direct in rul_temp trecea testul, dar prin
|
||||
fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
|
||||
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia, NU introducerea
|
||||
directa a randurilor in cursoare/tabele. Popularea directa sare peste validari/filtre/efecte
|
||||
laterale si da PASS fals (caz real: un articol "adaugat" direct in cursor trecea testul, dar
|
||||
prin fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
|
||||
dialoguri modale), nu pe pasii fluxului testat.
|
||||
|
||||
## Test UI nou in 5 pasi
|
||||
|
||||
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET TALK OFF` (obligatoriu, vezi cap. c);
|
||||
`SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
|
||||
`SET PROCEDURE TO ...\ui_harness.prg ADDITIVE` si (daca ai dialoguri)
|
||||
`...\mock_amessagebox.prg ADDITIVE`.
|
||||
2. Creeaza dummy-urile minime (`goExecutor`, `goApp`, etc.) + cursoarele pe care le cere
|
||||
Init-ul clasei (RecordSource-urile grilelor).
|
||||
3. Instantiaza clasa, `WindowType=0`, `Show()`, `WindowState=2`.
|
||||
4. Pe fiecare pas: fa actiunea, verifica efectul in date (loghezi PASS/FAIL cu un helper
|
||||
local), apoi `DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la
|
||||
orchestrator sau auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
|
||||
5. Ruleaza: `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
|
||||
PNG-urile ies in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
|
||||
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET SAFETY OFF` + `SET TALK OFF` ca PRIMA
|
||||
linie (vezi capcana a); `SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
|
||||
`ui_harness.prg` si (daca ai dialoguri) `mock_amessagebox.prg`, cu `ADDITIVE`.
|
||||
2. Creeaza dummy-urile minime (`goExecutor` cu metode reale, `goApp` pt. `ReadIni`, `poAct`
|
||||
pt. curs valutar) + cursoarele cerute de Init (RecordSource-urile grilelor).
|
||||
3. Instantiaza clasa, `WindowType=0` (modeless - `Show()` pe modal nu returneaza), `Show()`,
|
||||
`WindowState=2`.
|
||||
4. Pe fiecare pas: fa actiunea, verifica efectul in date (PASS/FAIL cu helper local), apoi
|
||||
`DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la orchestrator sau
|
||||
auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
|
||||
5. `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
|
||||
PNG-uri in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
|
||||
|
||||
## Capcane / deblocari (toate confirmate in sesiune)
|
||||
|
||||
a. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
|
||||
finalul pasilor. Ruleaza-l in background (run_in_background) + asteapta `done.txt`, sau
|
||||
timeout mare (300-600s). NU porni VFP separat de orchestrator - si-l lanseaza singur.
|
||||
b. **Asteptarea ready_0 prea scurta** omoara VFP exact inainte sa scrie semaforul. Cu `.fxp`
|
||||
cald START apare in ~2s, la rece mult mai mult (zeci de classlib-uri). Foloseste
|
||||
`-ReadyTimeoutSec` generos (implicit 180s). Retry NUMAI la "start ratat", nu la "start lent".
|
||||
c. **`SET SAFETY OFF` + `SET TALK OFF` ca PRIMUL lucru, inainte de primul `STRTOFILE`.**
|
||||
Cu SAFETY ON, prima scriere a log-ului (daca fisierul exista deja) scoate dialogul modal
|
||||
"...already exists, overwrite it?" care BLOCHEAZA rularea headless (VFP pare "instanta moarta"
|
||||
- proces viu, blocat pe dialog). Cu TALK ON, `SUM`/`CALCULATE` (ex. din `recalculeaza`) isi
|
||||
echo-eaza rezultatele pe desktop, ca text parazit peste formular in screenshots. Harness-ul
|
||||
are `HarnessInit`/`SET SAFETY OFF` defensiv in procedurile de scriere, dar testul trebuie
|
||||
sa faca `SET SAFETY OFF` oricum INAINTE de scrierea lui proprie de "START".
|
||||
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log ramane gol).
|
||||
Lanseaza `.fxp`-ul PRECOMPILAT (`vfp9 -A test.fxp`) - se executa mereu.
|
||||
e. **Precompilarea (`COMPILE`+`QUIT`) atarna** dupa COMPILE; rulata in ACEEASI sesiune
|
||||
powershell care lanseaza apoi testul, otraveste lansarile (nu executa). Ruleaza precompilarea
|
||||
intr-un proces copil izolat (`Start-Process -Wait _precompile.ps1`).
|
||||
f. **Detectia "a pornit" pe mtime-ul log-ului > momentul lansarii**, nu pe "log ne-gol": un log
|
||||
stale tinut deschis de un `vfp9` zombi pacaleste verificarea. Omoara zombii inainte de fiecare
|
||||
lansare.
|
||||
g. **Formulare modale** (`WindowType`) - `Show()` nu returneaza si blocheaza testul. Seteaza
|
||||
`goObj.WindowType = 0` (modeless) pentru afisare non-modala automatizata.
|
||||
h. **Dummy minimali**: `goExecutor` (oExecute/oExecuta creeaza cursorul cerut), `goApp`
|
||||
(`ReadIni` pt. font din settings.ini), `poAct`/`oact` (curs valutar). Init-ul recreeaza
|
||||
cursoarele goale din Oracle daca nu-s pre-populate - pre-populeaza-le INAINTE de instantiere.
|
||||
i. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded cu spatii nu gaseste nimic** (`==` e
|
||||
exact) - foloseste `ALLTRIM(camp) == 'literal'`.
|
||||
j. **Messagebox-uri de confirmare blocheaza testul headless**. Mock: `FUNCTION amessagebox`
|
||||
care returneaza direct valoarea butonului (6=Da). Incarca-l cu `SET PROCEDURE ... ADDITIVE`
|
||||
**PRIMUL** (inaintea `oproceduri_comune.prg`): la nume duplicat de procedura, VFP foloseste
|
||||
fisierul cautat PRIMUL in lista (verificat empiric: incarcat ultimul PIERDE, primul CASTIGA).
|
||||
Mock-ul umbreste `amessagebox` si din apelurile din metodele de clasa (ex. `do_sterge_articol`).
|
||||
k. **Cursor bound la o grila, recreat de Init -> grila pierde coloanele (eroare 1925).** Ex.:
|
||||
`import_nota.Init` recreeaza `rul_temp` din Oracle daca e gol la instantiere; GridArt ramane
|
||||
fara coloanele denumite. Fix: pune 1 rand placeholder in cursor INAINTE de instantiere (ca Init
|
||||
sa nu-l recreeze), apoi **`ZAP`** dupa `Show()` (NU `DELETE ALL`: acesta lasa un rand fizic
|
||||
sters si pointerul la EOF, iar cod gen `do_calculeaza_diferente` face `GO recno_EOF` -> eroare 5).
|
||||
l. **CREATEOBJECT/ADDPROPERTY se comporta gresit apelate DINTR-O PROCEDURA in acest runtime headless**
|
||||
(obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din programul PRINCIPAL
|
||||
merg. Construieste obiectele (ex. `toDlg` pt. `do_adauga_factura`) INLINE in main, nu intr-un
|
||||
helper. (Cauza exacta neclara; reproductibil.)
|
||||
m. **`gl406=.T.` (SAFT) activeaza cod care are nevoie de mock-uri suplimentare**, altfel crapa sau
|
||||
taxcode ramane mereu NULL: `GetTaxCode`/`GetTaxCodeIdPart` (oproceduri_comune.prg) apeleaza
|
||||
`update_jtva_coloane()` care ruleaza `goExecutor.oExecute("select * from vjtva_coloane ...", <cursor>)`
|
||||
- mock-ul de `oExecute` trebuie sa intercepteze `'VJTVA_COLOANE' $ UPPER(tcSql)` si sa returneze
|
||||
un cursor cu structura reala a view-ului (`id_jtva_coloana, denumire, jc, jv, coloana_jc,
|
||||
coloana_jv, cota_tva, id_tva, taxcode, taxcode_neexigibil, taxcode_n50, taxcode_n50_tvai,
|
||||
taxcode_n100, taxcode_regularizare, taxcode_tvai, afisat, ordine`) - codul apelant face singur
|
||||
`INDEX ON id_jtva_coloana TAG id_jtva` dupa, nu trebuie indexat in mock. Cand `id_partc`/`id_partd`
|
||||
pe rand nu e gol, `GetTaxCodeIdPart` cauta si codul fiscal al partenerului prin
|
||||
`goExecutor.oSelect2Value(sql, @toValue)` (`GetCodFiscalPartenerById`) - metoda lipseste din
|
||||
dummyexecutor-ul de baza, trebuie adaugata (returneaza cod fiscal gol -> `VERIFICA_RTVAI` sare
|
||||
peste apelul Oracle real de validare CIF).
|
||||
n. **Explicatia TVA de pe randul T ("in tandem" cu baza) NU se scrie prin cod separat** - e pur si
|
||||
simplu o A DOUA intrare in `jtva_coloane2`/`vjtva_coloane` (coloana `id_tva` de pe randul bazei
|
||||
punctand spre `id_jtva_coloana` al randului TVA); `aplica_explicatie_tva` (ointroduceri.prg
|
||||
~9807) citeste acea a doua intrare si o scrie pe rand T. Populeaza mock-ul cu ambele randuri
|
||||
(baza + TVA), nu doar cu unul si nu concatena manual "TVA " + denumirea bazei in test.
|
||||
o. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` (captura ecranului intreg) fura
|
||||
focus si se corupe daca utilizatorul lucreaza in paralel - NU se mai foloseste. Capturile se fac
|
||||
cu `PrintWindow` (user32, P/Invoke) pe HANDLE-ul ferestrei vfp9 (`Process.MainWindowHandle`),
|
||||
flag `2` = `PW_RENDERFULLCONTENT` (fallback flag `0` daca iese goala); merge cu fereastra
|
||||
acoperita/in fundal, dar NU minimizata. Fereastra e mutata OFF-SCREEN (`SetWindowPos` cu
|
||||
`HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce apare handle-ul si RE-IMPINSA la fiecare
|
||||
pas (VFP isi reactiveaza singur fereastra la `Show()`/dialoguri modale - o singura impingere la
|
||||
lansare nu e suficienta). Toate consolele powershell copil (precompilare) sunt `-WindowStyle
|
||||
Hidden` (sigur pentru consola; NU si pentru GUI VFP, care trebuie doar off-screen, nu ascuns -
|
||||
PrintWindow pe fereastra minimizata/ascunsa poate reda gol). Implementat in
|
||||
a. **`SET SAFETY OFF` + `SET TALK OFF` inaintea primului `STRTOFILE`.** Cu SAFETY ON, scrierea
|
||||
log-ului existent scoate un dialog modal care blocheaza rularea (proces viu, "instanta
|
||||
moarta"). Cu TALK ON, `SUM`/`CALCULATE` isi echo-eaza rezultatele peste formular in
|
||||
screenshots.
|
||||
b. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
|
||||
final. Ruleaza-l in background + asteapta `done.txt`, sau timeout 300-600s. NU porni VFP
|
||||
separat de orchestrator - si-l lanseaza singur.
|
||||
c. **Asteptarea `ready_0`**: cu `.fxp` cald START apare in ~2s, la rece mult mai mult (zeci de
|
||||
classlib-uri). `-ReadyTimeoutSec` generos (implicit 180s); retry NUMAI la "start ratat",
|
||||
nu la "start lent". Detectia "a pornit" se face pe mtime-ul log-ului > momentul lansarii
|
||||
(un log stale tinut de un vfp9 zombi pacaleste verificarea "log ne-gol") - omoara zombii
|
||||
inainte de fiecare lansare.
|
||||
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log gol). Lanseaza
|
||||
`.fxp`-ul PRECOMPILAT. Precompilarea (`COMPILE`+`QUIT`) atarna dupa COMPILE si, rulata in
|
||||
ACEEASI sesiune powershell, otraveste lansarile ulterioare - ruleaza-o intr-un proces copil
|
||||
izolat (`Start-Process -Wait _precompile.ps1`).
|
||||
e. **Suitele NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 | Stop-Process
|
||||
-Force` (omoara toate instantele), deci un test lansat in paralel il ucide pe cel in curs.
|
||||
f. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` fura focus si se corupe daca
|
||||
utilizatorul lucreaza in paralel - nu se mai foloseste. Capturile se fac cu `PrintWindow`
|
||||
(user32, P/Invoke) pe `Process.MainWindowHandle`, flag `2` = `PW_RENDERFULLCONTENT`
|
||||
(fallback `0` daca iese goala); merge cu fereastra acoperita, dar NU minimizata. Fereastra
|
||||
se muta OFF-SCREEN (`SetWindowPos`, `HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce
|
||||
apare handle-ul si SE RE-IMPINGE la fiecare pas (VFP se reactiveaza singur la
|
||||
`Show()`/dialoguri). Consolele powershell copil sunt `-WindowStyle Hidden` (sigur pentru
|
||||
consola, NU pentru GUI VFP - ascunsa, `PrintWindow` poate reda gol). Implementat in
|
||||
`vfp_ui_harness.ps1` (Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si
|
||||
`_precompile.ps1` (acelasi Push-Offscreen pe fereastra tranzitorie de compilare).
|
||||
p. **Cursorul `introdc` FARA indexul `ord_doc` schimba documentul "principal" la sincronizari
|
||||
repetate** (sincronizeaza() apelat de mai multe ori, ex. la adaugarea a 2+ articole succesiv
|
||||
prin fluxul real): identificarea documentului principal (`Go Top` + `Locate For
|
||||
Inlist(tip_rand,'B','S') And !Empty(doc_key)`) e stabila in productie doar pentru ca
|
||||
`achizitie_import` (COMUN\programe\ointroduceri.prg:1616-1618) creeaza, INAINTE de
|
||||
`CREATEOBJECT('import_nota')`, indexul `ord_doc` (`Str(Nvl(nr_doc,0),8) +
|
||||
Icase(tip_rand='B','1', tip_rand='S','2', tip_rand='T','3', tip_rand='D','4', '5') +
|
||||
Padr(Nvl(scd,''),10)`) + `SET ORDER TO ord_doc`, care ordoneaza dupa `nr_doc` (S-urile
|
||||
principalei mostenesc nr_doc-ul ei) - documentul cu `nr_doc` minim ramane mereu acelasi. Un
|
||||
test care creeaza `introdc` fara acest index ramane pe ordinea FIZICA (recno): dupa ce
|
||||
principala e sparta (randul B sters, randurile S adaugate la coada), urmatorul apel de
|
||||
sincronizeaza() gaseste gresit ca "principal" documentul cu cel mai mic recno ramas si il
|
||||
sparge si pe el - cascada se repeta la fiecare apel ulterior. Fix: dupa popularea `introdc`,
|
||||
INAINTE de `CREATEOBJECT('import_nota', ...)`, adauga exact acelasi `INDEX ON ... TAG ord_doc`
|
||||
+ `SET ORDER TO ord_doc` ca in `achizitie_import`.
|
||||
q. **Suitele de teste NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 |
|
||||
Stop-Process -Force` (omoara TOATE instantele vfp9 de pe masina), deci un test lansat in
|
||||
paralel ucide testul aflat in curs si corupe rezultatele. Ruleaza suitele strict secvential.
|
||||
r. **Fara SELECT-SQL pe cursorul legat de grid**: SELECT ... FROM introdc INTO CURSOR intr-un
|
||||
test, cat timp introdc e RecordSource-ul unui grid viu, poate omori procesul vfp9 silentios
|
||||
(fara eroare catchabila, fara semafor). Foloseste xBase nativ (COUNT FOR ... TO,
|
||||
CALCULATE, LOCATE) care nu comuta zona de lucru a grid-ului.
|
||||
`_precompile.ps1`.
|
||||
g. **Mock de messagebox**: `FUNCTION amessagebox` care returneaza direct valoarea butonului
|
||||
(6=Da), incarcat cu `SET PROCEDURE ... ADDITIVE` **PRIMUL**, inaintea fisierelor aplicatiei:
|
||||
la nume duplicat de procedura VFP foloseste fisierul cautat PRIMUL (verificat empiric).
|
||||
Astfel umbreste `amessagebox` si in apelurile din metodele de clasa.
|
||||
h. **Cursorii ceruti de Init**: pre-populeaza-i INAINTE de instantiere (Init-ul recreeaza din
|
||||
Oracle cursoarele goale, iar recrearea lasa grid-ul fara coloane, eroare 1925). Pune 1 rand
|
||||
placeholder inainte, apoi **`ZAP`** dupa `Show()` — NU `DELETE ALL`, care lasa un rand fizic
|
||||
sters si pointerul la EOF (cod care face `GO <recno>` da eroare 5, vezi
|
||||
`conventie_go_recno.md`).
|
||||
i. **Cursorii trebuie sa aiba EXACT indecsii creati de fluxul real** inainte de instantiere
|
||||
(fluxul real ii creeaza in metoda apelanta, ex. un `INDEX ON ... TAG ord_doc` +
|
||||
`SET ORDER TO` inainte de `CREATEOBJECT`). Fara ei cursorul ramane pe ordinea FIZICA, iar
|
||||
logica de tip `GO TOP` + `LOCATE FOR ...` alege alt rand decat in productie - rezultat fals,
|
||||
uneori in cascada la apeluri repetate. Verifica in codul apelant ce indecsi se creeaza si
|
||||
replica-i identic.
|
||||
j. **Mock-urile de date trebuie sa reproduca structura si semantica REALA a sursei** (view
|
||||
Oracle: toate coloanele; relatii intre randuri: toate randurile implicate, nu unul singur cu
|
||||
valori "compuse" manual in test). Un mock incomplet lasa campuri mereu NULL sau ascunde
|
||||
pasul care se testeaza. Cand un flag global activeaza cod suplimentar (ex. `gl406`), acel
|
||||
cod cere mock-uri in plus - verifica ce apeleaza si adauga metodele lipsa din dummy.
|
||||
k. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded nu gaseste nimic** (`==` e exact) -
|
||||
foloseste `ALLTRIM(camp) == 'literal'`.
|
||||
l. **`CREATEOBJECT`/`ADDPROPERTY` se comporta gresit apelate DINTR-O PROCEDURA in acest runtime
|
||||
headless** (obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din
|
||||
programul PRINCIPAL merg. Construieste obiectele-parametru INLINE in main, nu intr-un helper.
|
||||
(Cauza neclara; reproductibil.)
|
||||
m. **Fara `SELECT-SQL` pe cursorul legat de grid**: `SELECT ... FROM <cursor> INTO CURSOR` cat
|
||||
timp cursorul e RecordSource-ul unui grid viu poate omori procesul vfp9 silentios (fara
|
||||
eroare catchabila, fara semafor). Foloseste xBase nativ (`COUNT FOR ... TO`, `CALCULATE`,
|
||||
`LOCATE`), care nu comuta zona de lucru a grid-ului.
|
||||
|
||||
@@ -1135,6 +1135,109 @@ ENDTEXT
|
||||
|
||||
RETURN m.llSucces
|
||||
ENDPROC && ProcesareRaspunsuri
|
||||
*********************************
|
||||
*!* 24.07.2026 marius.mutu - import manual arhiva zip eFactura descarcata din SPV
|
||||
*********************************
|
||||
PROCEDURE ImportZipLocal
|
||||
LPARAMETERS tcZipFile
|
||||
* tcZipFile: (optional) arhiva zip descarcata din SPV. Daca lipseste, se alege cu Getfile
|
||||
|
||||
Local laFiles[1], lcCodFiscalClient, lcCodFiscalFurnizor, lcDir, lcErrorMessage, lcFilePath
|
||||
Local lcId, lcSemnatura, lcTip, lcXmlFile, lcZipFile, llSucces, lnFile, lnFiles, loFactura
|
||||
|
||||
lcZipFile = IIF(TYPE('tcZipFile') = 'C' AND !EMPTY(NVL(m.tcZipFile, '')), ALLTRIM(m.tcZipFile), GETFILE('zip', 'Arhiva zip eFactura', 'Import'))
|
||||
IF EMPTY(m.lcZipFile) OR !FILE(m.lcZipFile)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
|
||||
lcDir = ADDBS(SYS(2023)) + SYS(2015) + '\'
|
||||
lcErrorMessage = ''
|
||||
llSucces = This.UnzipFile(m.lcZipFile, @lcDir, @lcErrorMessage)
|
||||
|
||||
IF !m.llSucces
|
||||
AMESSAGEBOX('Eroare la dezarhivarea ' + m.lcZipFile + CHR(13) + CHR(10) + m.lcErrorMessage, 0+48, _SCREEN.Caption)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
|
||||
* xml-ul facturii este fisierul xml care nu este semnatura MFinante
|
||||
lcXmlFile = ''
|
||||
lnFiles = ADIR(laFiles, ADDBS(m.lcDir) + '*.xml')
|
||||
FOR lnFile = 1 TO m.lnFiles
|
||||
IF !('SEMNATURA' $ UPPER(laFiles[m.lnFile, 1]))
|
||||
lcXmlFile = ADDBS(m.lcDir) + ALLTRIM(laFiles[m.lnFile, 1])
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDFOR
|
||||
|
||||
IF EMPTY(m.lcXmlFile)
|
||||
AMESSAGEBOX('Arhiva nu contine fisierul xml al facturii!', 0+48, _SCREEN.Caption)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
|
||||
lcId = ALLTRIM(JUSTSTEM(m.lcXmlFile))
|
||||
loFactura = ParseEFactura(m.lcXmlFile)
|
||||
|
||||
IF TYPE('loFactura') <> 'O' OR loFactura.Eroare
|
||||
AMESSAGEBOX('Eroare la citirea facturii din ' + m.lcXmlFile, 0+48, _SCREEN.Caption)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
|
||||
lcCodFiscalFurnizor = UPPER(ALLTRIM(GetNrFromString(NVL(loFactura.CodFiscalFurnizor, ''))))
|
||||
lcCodFiscalClient = UPPER(ALLTRIM(GetNrFromString(NVL(loFactura.CodFiscalClient, ''))))
|
||||
|
||||
DO CASE
|
||||
CASE !EMPTY(m.lcCodFiscalClient) AND m.lcCodFiscalClient == ALLTRIM(This.cCodFiscal)
|
||||
lcTip = 'FACTURA PRIMITA'
|
||||
CASE !EMPTY(m.lcCodFiscalFurnizor) AND m.lcCodFiscalFurnizor == ALLTRIM(This.cCodFiscal)
|
||||
lcTip = 'FACTURA TRIMISA'
|
||||
OTHERWISE
|
||||
AMESSAGEBOX('Factura nu apartine firmei curente (cod fiscal ' + ALLTRIM(This.cCodFiscal) + ')!' + CHR(13) + CHR(10) + ;
|
||||
'Emitent: ' + m.lcCodFiscalFurnizor + ' Beneficiar: ' + m.lcCodFiscalClient, 0+48, _SCREEN.Caption)
|
||||
RETURN .F.
|
||||
ENDCASE
|
||||
|
||||
* Salvez arhiva in directorul local de raspunsuri, ca la descarcarea din SPV
|
||||
lcFilePath = GetPdfPath('EFACTURA', IIF(m.lcTip == 'FACTURA PRIMITA', 'PRIMITE', 'TRIMISE')) + m.lcId + '_' + m.lcId + '.zip'
|
||||
IF !FILE(m.lcFilePath)
|
||||
This.SaveFile(m.lcZipFile, .F., JUSTPATH(m.lcFilePath), JUSTFNAME(m.lcFilePath))
|
||||
ENDIF
|
||||
|
||||
* Cursor temporar cu structura din InitDB, cu factura importata
|
||||
llSucces = goExecutor.oExecuta("select a.*, cast(NULL as varchar2(1000)) as factura_detalii, 0 as modificat from anaf_efactura a where 1 = 0", "canaf_efactura_temp")
|
||||
|
||||
IF !m.llSucces
|
||||
AMESSAGEBOX('Eroare la citirea structurii anaf_efactura!', 0+48, _SCREEN.Caption)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
|
||||
INSERT INTO canaf_efactura_temp (id_incarcare, id_descarcare, data_raspuns, mesaj_raspuns, tip_mesaj_raspuns, ;
|
||||
cod_fiscal_emitent, cod_fiscal_beneficiar, factura_emisa, test, descarcat, filepath, detalii, modificat) ;
|
||||
VALUES (m.lcId, m.lcId, DATETIME(), 'Import manual ' + JUSTFNAME(m.lcZipFile), m.lcTip, ;
|
||||
m.lcCodFiscalFurnizor, m.lcCodFiscalClient, IIF(m.lcTip == 'FACTURA PRIMITA', 0, 1), IIF(This.lTest, 1, 0), ;
|
||||
0, m.lcFilePath, FILETOSTR(m.lcXmlFile), 1)
|
||||
|
||||
TABLEUPDATE(1, .F., 'canaf_efactura_temp')
|
||||
|
||||
ADDPROPERTY(loFactura, 'cIdIncarcare', m.lcId)
|
||||
This.cUpdateFactura(loFactura, 'canaf_efactura_temp')
|
||||
|
||||
llSucces = This.UpdateDb('canaf_efactura_temp')
|
||||
|
||||
USE IN (SELECT('canaf_efactura_temp'))
|
||||
|
||||
lcSemnatura = ADDBS(m.lcDir) + 'semnatura_' + m.lcId + '.xml'
|
||||
DELETE FILE (m.lcXmlFile)
|
||||
IF FILE(m.lcSemnatura)
|
||||
DELETE FILE (m.lcSemnatura)
|
||||
ENDIF
|
||||
|
||||
IF m.llSucces
|
||||
AMESSAGEBOX('Factura ' + ALLTRIM(loFactura.NumarAct) + ' din ' + DTOC(loFactura.DataAct) + ' a fost importata in borderoul eFactura!', 0+64, _SCREEN.Caption)
|
||||
ENDIF
|
||||
|
||||
RETURN m.llSucces
|
||||
ENDPROC && ImportZipLocal
|
||||
|
||||
|
||||
*********************************
|
||||
* RASPUNSURI FACTURI
|
||||
|
||||
237
utile/Teste/efactura_import/test_import_zip_efactura.prg
Normal file
237
utile/Teste/efactura_import/test_import_zip_efactura.prg
Normal file
@@ -0,0 +1,237 @@
|
||||
* test_import_zip_efactura.prg
|
||||
* Test UI pentru importul manual al unei arhive zip eFactura in borderou
|
||||
* (COMUN\clase\anaf_efactura.vcx > clasa anaf_efactura + AnafeFacturaServer.ImportZipLocal).
|
||||
* Mediu: CENTRAL / MARIUSM_AUTO, perioada ianuarie 2026 (data facturii din xml).
|
||||
*
|
||||
* Ce acopera:
|
||||
* T1 - garda pe cod fiscal: arhiva nu apartine firmei de test => import respins, 0 randuri
|
||||
* T2 - import complet cu codul fiscal al beneficiarului din xml => anaf_efactura + detalii
|
||||
* T3 - factura apare in grila "Facturi primite" a borderoului real (captura de ecran)
|
||||
* GETFILE (alegerea fisierului) NU poate fi automatizat headless - functie nativa VFP,
|
||||
* nu poate fi umbrita de un mock - de aceea arhiva se transmite ca parametru.
|
||||
*
|
||||
* Rulare: powershell -ExecutionPolicy Bypass -File test_import_zip_efactura.ps1
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
|
||||
PUBLIC gcUILog, gcSyncDir, gnMockRaspuns, gcTestDir, gcZipFile, gcIdInc, gcCifBenef
|
||||
PUBLIC poFacturiEmise, poFacturiPrimite, poFacturiTrimise, poFacturiDetalii
|
||||
PUBLIC loAnaf, loForm, gnPass, gnFail
|
||||
|
||||
gcTestDir = 'D:\ROA\ROACONT\COMUN\utile\Teste\efactura_import\'
|
||||
gcUILog = gcTestDir + 'test_import_zip_efactura_log.txt'
|
||||
gcSyncDir = gcTestDir + 'uisync_importzip\'
|
||||
gcZipFile = 'D:\ROA\ROACONT\5967557693.zip'
|
||||
gcIdInc = '5967557693'
|
||||
gcCifBenef = '29923675'
|
||||
gnMockRaspuns = 6
|
||||
gnPass = 0
|
||||
gnFail = 0
|
||||
|
||||
STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
|
||||
|
||||
ON ERROR DO TErr WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
|
||||
ON SHUTDOWN QUIT
|
||||
|
||||
* Mock-urile se incarca INAINTE de test_init_env_auto: la nume duplicat de procedura
|
||||
* castiga fisierul incarcat PRIMUL (vezi docs\testare-ui-vfp.md, punctul j)
|
||||
SET PROCEDURE TO ('D:\ROA\ROACONT\COMUN\utile\Teste\mock_amessagebox.prg') ADDITIVE
|
||||
SET PROCEDURE TO ('D:\ROA\ROACONT\COMUN\utile\Teste\ui_harness.prg') ADDITIVE
|
||||
|
||||
DO ('D:\ROA\ROACONT\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT', 2026, 1
|
||||
|
||||
DO HarnessLog WITH 'INIT gnHandle=' + TRANSFORM(gnHandle) + ' gcS=' + TRANSFORM(gcS) + ' perioada=' + TRANSFORM(gnLuna) + '/' + TRANSFORM(gnAn)
|
||||
DO HarnessLog WITH 'FIRMA=' + TRANSFORM(gcFirma) + ' cod fiscal=' + TRANSFORM(goFirma.codfiscalfro)
|
||||
|
||||
* Curatenie: sterg importul unei rulari anterioare, ca testul sa fie repetabil
|
||||
goExecutor.oExecuta("delete from anaf_efactura_detalii where id_efactura in (select id from anaf_efactura where id_incarcare = '" + gcIdInc + "')")
|
||||
goExecutor.oExecuta("delete from anaf_efactura where id_incarcare = '" + gcIdInc + "'")
|
||||
DO HarnessLog WITH 'CURATENIE randuri anterioare OK'
|
||||
|
||||
loAnaf = CREATEOBJECT('ANAFeFactura')
|
||||
DO HarnessLog WITH 'ANAFeFactura creat; cod fiscal server=' + TRANSFORM(loAnaf.AnafeFacturaServer.cCodFiscal) + ' lTest=' + TRANSFORM(loAnaf.AnafeFacturaServer.lTest)
|
||||
|
||||
* ---------- cursoarele borderoului, copie exacta din ANAFeFactura.vizeFactura (anaf_efactura.prg:2867-2942) ----------
|
||||
* FACTURI PRIMITE
|
||||
lcData1 = '01' + PADL(INT(m.gnLuna),2,'0') + ALLTRIM(STR(m.gnAn))
|
||||
Text To lcSchema Noshow
|
||||
ales N(1), id N(20), id_fact N(20), data_act D, data_scad D, numar_act C(30), xfurnizor C(200), xclient C(200), id_incarcare C(36), id_descarcare c(36) null, tip_mesaj_raspuns C(50) null,mesaj_raspuns C(250) null, data_raspuns D null, trimis N(1) null, data_trimis T null, cod_fiscal_emitent C(30) null, cod_fiscal_beneficiar C(30) null, detalii M null, total_fara_tva N(16,4), total_tva N(16,4), total_tva_ron N(16,4), total_cu_tva N(16,4), discount_fara_tva N(16,4), taxe_fara_tva N(16,4), valoare_fara_tva N(16,4), total_de_plata N(16,4), nume_valuta C(5), test N(1) null, jtotctva N(16,4), descriere M null, detalii_plata M null, diferenta N(16,4), procesat N(1), tip_persoana N(1) null, descarcat N(1) null, filepath c(250) null, procesat_server N(1) null, creditnote N(1)
|
||||
Endtext
|
||||
Text To lcSelect Noshow
|
||||
SELECT 0 as ales, id, id_fact, data_act, data_scad, numar_act, xfurnizor, xclient, id_incarcare, id_descarcare, tip_mesaj_raspuns, mesaj_raspuns, data_raspuns, NVL(trimis,0) as trimis, data_trimis, cod_fiscal_emitent, cod_fiscal_beneficiar, '' as detalii, total_fara_tva, total_tva, total_tva_ron, total_cu_tva, discount_fara_tva, taxe_fara_tva, valoare_fara_tva, total_de_plata, nume_valuta, test, jtotctva, descriere, detalii_plata, decode(creditnote,0,1,-1) * NVL(total_cu_tva, 0.00)-NVL(jtotctva, 0.00) as diferenta, procesat, tip_persoana, descarcat, filepath, procesat_server, creditnote FROM anaf_vefactura_primit
|
||||
Endtext
|
||||
lcOrder = [data_act,numar_act,data_raspuns]
|
||||
lcgroup = []
|
||||
* Aduc inregistrarile cand se activeaza prima data pagina cu facturi primite
|
||||
lcFiltru = [1=2] && [(extract(year from data_act) = ?gnAn and extract(month from data_act) = ?gnLuna) or (data_act is null and data_raspuns >= to_date('] + m.lcData1 + [','ddmmyyyy'))]
|
||||
lcFiltruOriginal = []
|
||||
llModParam = .T.
|
||||
llAfiseaza = .F.
|
||||
|
||||
gencursor('poFacturiPrimite', 'crsFacturiPrimite', lcSelect, lcFiltru, lcSchema, lcOrder, llAfiseaza, lcgroup, llModParam, lcFiltruOriginal)
|
||||
poFacturiPrimite.ca_baza1.afisare()
|
||||
Select crsFacturiPrimite
|
||||
Go Top
|
||||
|
||||
* FACTURI TRIMISE
|
||||
Text To lcSchema Noshow
|
||||
ales N(1), id N(20), id_fact N(20), data_act D, data_scad D, numar_act C(30), xfurnizor C(200), xclient C(200), id_incarcare C(36), id_descarcare c(36) null, mesaj_trimis C(250) null, tip_mesaj_raspuns C(50) null,mesaj_raspuns C(250) null, data_raspuns D null, trimis N(1) null, data_trimis T null, cod_fiscal_emitent C(30) null, cod_fiscal_beneficiar C(30) null, detalii M null, total_fara_tva N(16,4), total_tva N(16,4), total_tva_ron N(16,4), total_cu_tva N(16,4), discount_fara_tva N(16,4), taxe_fara_tva N(16,4), valoare_fara_tva N(16,4), total_de_plata N(16,4), nume_valuta C(5), test N(1) null, jtotctva N(16,4), descriere M null, detalii_plata M null, diferenta N(16,4), procesat N(1), tip_persoana N(1) null, descarcat N(1) null, filepath c(250) null, procesat_server N(1) null, creditnote N(1)
|
||||
Endtext
|
||||
Text To lcSelect Noshow
|
||||
SELECT 0 as ales, id, id_fact, data_act, data_scad, numar_act, xfurnizor, xclient, id_incarcare, id_descarcare, mesaj_trimis, tip_mesaj_raspuns, mesaj_raspuns, data_raspuns, NVL(trimis,0) as trimis, data_trimis, cod_fiscal_emitent, cod_fiscal_beneficiar, '' as detalii, total_fara_tva, total_tva, total_tva_ron, total_cu_tva, discount_fara_tva, taxe_fara_tva, valoare_fara_tva, total_de_plata, nume_valuta, test, jtotctva, descriere, detalii_plata, decode(creditnote,0,1,-1) * NVL(total_cu_tva, 0.00)-NVL(jtotctva, 0.00) as diferenta, procesat, tip_persoana, descarcat, filepath, procesat_server, creditnote FROM anaf_vefactura_trimis
|
||||
Endtext
|
||||
lcOrder = [data_act,numar_act,data_trimis,data_raspuns]
|
||||
lcgroup = []
|
||||
* Aduc inregistrarile cand se activeaza prima data pagina cu facturi trimise
|
||||
lcFiltru = [1=2] && [(extract(year from data_act) = ?gnAn and extract(month from data_act) = ?gnLuna) or (data_act is null and data_raspuns >= to_date('] + m.lcData1 + [','ddmmyyyy'))]
|
||||
lcFiltruOriginal = []
|
||||
llModParam = .T.
|
||||
llAfiseaza = .F.
|
||||
|
||||
gencursor('poFacturiTrimise', 'crsFacturiTrimise', lcSelect, lcFiltru, lcSchema, lcOrder, llAfiseaza, lcgroup, llModParam, lcFiltruOriginal)
|
||||
poFacturiTrimise.ca_baza1.afisare()
|
||||
Select crsFacturiTrimise
|
||||
Go Top
|
||||
|
||||
* FACTURI EMISE
|
||||
Text To lcSchema Noshow
|
||||
ales N(1), id N(20), id_fact N(20), id_vanzare N(10), data_act D, numar_act N(14), in_valuta N(1) null, curs N(10,4) null, valval N(16,2), tvaval N(16,2),totval N(16,2), total_fara_tva N(16,2), total_tva N(16,2), total_cu_tva N(16,2), nume_valuta C(5) null, client C(100), id_client N(10), cod_fiscal C(30), tip_persoana N(1) null, trimis N(1) null, data_trimis T null, stare C(100) null, id_incarcare C(36) null, mesaj_trimis C(250) null, id_descarcare c(36) null, tip_mesaj_raspuns C(50) null,mesaj_raspuns C(250) null, data_raspuns D null, cod_fiscal_emitent C(30) null, cod_fiscal_beneficiar C(30) null, detalii M null, xfurnizor C(200), xclient C(200), xdata_act D, xnumar_act C(30), xtotal_fara_tva N(16,4), xtotal_tva N(16,4), xtotal_tva_ron N(16,4), xtotal_cu_tva N(16,4), xdiscount_fara_tva N(16,4), xtaxe_fara_tva N(16,4), xvaloare_fara_tva N(16,4), xtotal_de_plata N(16,4), xnume_valuta C(5), test N(1) null, id_util N(10), utilizator C(100), jtotctva N(16,4), diferenta N(16,4), procesat N(1), procesat_server N(1), creditnote N(1)
|
||||
Endtext
|
||||
Text To lcSelect Noshow
|
||||
SELECT 0 as ales, id, id_fact, id_vanzare, data_act, numar_act, in_valuta, curs, valval, tvaval, totval, total_fara_tva, total_tva,total_cu_tva, nume_valuta, client, id_client, cod_fiscal, NVL(tip_persoana,1) as tip_persoana, NVL(trimis,0) as trimis, data_trimis, stare, id_incarcare, mesaj_trimis, id_descarcare, tip_mesaj_raspuns, mesaj_raspuns, data_raspuns, cod_fiscal_emitent, cod_fiscal_beneficiar, '' as detalii, xfurnizor, xclient, xdata_act, xnumar_act, xtotal_fara_tva, xtotal_tva, xtotal_tva_ron, xtotal_cu_tva, xdiscount_fara_tva, xtaxe_fara_tva, xvaloare_fara_tva, xtotal_de_plata, xnume_valuta, test, id_util, utilizator, jtotctva, decode(creditnote,0,1,-1) * ROUND(NVL(xtotal_cu_tva , 0.00) * decode(in_valuta,1,curs,1), 2)-NVL(jtotctva, 0.00) as diferenta, procesat, procesat_server,creditnote FROM anaf_vefactura_emis
|
||||
Endtext
|
||||
lcOrder = [data_act,numar_act]
|
||||
lcgroup = []
|
||||
lcFiltru = [(extract(year from data_act)=?gnAn and extract(month from data_act)=?gnLuna) OR (trimis = 1 and tip_mesaj_raspuns is null and extract(year from data_act) >=2023)]
|
||||
lcFiltruOriginal = []
|
||||
llModParam = .T.
|
||||
llAfiseaza = .F.
|
||||
|
||||
* Afisez toate facturile emise, ca sa le pot vizualiza pe cele netrimise sau trimise cu starea lor (trimis, eroare etc.)
|
||||
gencursor('poFacturiEmise', 'crsFacturiEmise', lcSelect, lcFiltru, lcSchema, lcOrder, llAfiseaza, lcgroup, llModParam, lcFiltruOriginal)
|
||||
poFacturiEmise.ca_baza1.afisare()
|
||||
|
||||
|
||||
* DETALII FACTURI PRIMITE/TRIMISE/EMISE
|
||||
Text To lcSchema Noshow
|
||||
id N(20), id_efactura N(20), nr N(5), articol V(250), descriere V(250), detalii M, cantitate N(18,6), um V(20), pret N(20,6), proctva N(7,2), valoarefaratva N(20,6), discountfaratva N(20,6)
|
||||
Endtext
|
||||
Text To lcSelect Noshow
|
||||
select id, id_efactura, nr, articol, descriere, detalii, cantitate, um, pret, proctva, valoarefaratva, discountfaratva from anaf_vefactura_detalii
|
||||
Endtext
|
||||
lcOrder = [nr]
|
||||
lcgroup = []
|
||||
lcFiltru = [1=2] && IIF(!EMPTY(NVL(crsFacturiPrimite.id,0)), [id_efactura=] + ALLTRIM(STR(crsFacturiPrimite.id)), [1=2])
|
||||
lcFiltruOriginal = []
|
||||
llModParam = .T.
|
||||
llAfiseaza = .F.
|
||||
|
||||
gencursor('poFacturiDetalii', 'crsDetaliiFacturi', lcSelect, lcFiltru, lcSchema, lcOrder, llAfiseaza, lcgroup, llModParam, lcFiltruOriginal)
|
||||
poFacturiDetalii.ca_baza1.afisare()
|
||||
|
||||
* ---------- Formularul REAL al borderoului, non-modal (Show(1) ar bloca testul) ----------
|
||||
Select crsFacturiEmise
|
||||
loForm = CREATEOBJECT('anaf_efactura', loAnaf)
|
||||
loForm.WindowType = 0
|
||||
loForm.Show()
|
||||
loForm.WindowState = 2
|
||||
DO HarnessLog WITH 'BORDEROU deschis: [' + TRANSFORM(loForm.Caption) + ']'
|
||||
DO HarnessStep WITH 0, 'borderou_deschis'
|
||||
|
||||
* ---------- T1: garda pe cod fiscal (firma de test nu e nici emitent, nici beneficiar) ----------
|
||||
llRezultat = loForm.oAnaf.AnafeFacturaServer.ImportZipLocal(gcZipFile)
|
||||
lnRanduri = 0
|
||||
goExecutor.oSelecteaza2Value("select count(*) from anaf_efactura where id_incarcare = '" + gcIdInc + "'", @lnRanduri)
|
||||
DO TVerdict WITH !llRezultat AND NVL(lnRanduri, 0) = 0, 'T1 garda cod fiscal', 'import respins=' + TRANSFORM(!llRezultat) + ' randuri in anaf_efactura=' + TRANSFORM(lnRanduri)
|
||||
DO HarnessStep WITH 1, 'garda_cod_fiscal'
|
||||
|
||||
* ---------- T2: import complet, cu codul fiscal al beneficiarului din xml ----------
|
||||
loForm.oAnaf.AnafeFacturaServer.cCodFiscal = gcCifBenef
|
||||
llRezultat = loForm.oAnaf.AnafeFacturaServer.ImportZipLocal(gcZipFile)
|
||||
DO TVerdict WITH llRezultat, 'T2 import', 'ImportZipLocal a returnat ' + TRANSFORM(llRezultat)
|
||||
|
||||
* SQL in TEXT/ENDTEXT: un literal string mai lung de 255 caractere nu compileaza
|
||||
TEXT TO lcSqlVerif NOSHOW
|
||||
select id, xnumar_act, xdata_act, xdata_scad, xtotal_fara_tva, xtotal_tva, xtotal_cu_tva, xnume_valuta,
|
||||
xfurnizor, xclient, tip_mesaj_raspuns, factura_emisa, cod_fiscal_emitent, cod_fiscal_beneficiar,
|
||||
descarcat, procesat, creditnote, filepath,
|
||||
nvl(length(detalii),0) as len_detalii, nvl(length(detalii_zip),0) as len_zip
|
||||
from anaf_efactura
|
||||
where id_incarcare = ?gcIdInc
|
||||
ENDTEXT
|
||||
goExecutor.oExecuta(lcSqlVerif, 'cVerif')
|
||||
|
||||
IF USED('cVerif') AND RECCOUNT('cVerif') = 1
|
||||
SELECT cVerif
|
||||
GO TOP
|
||||
DO HarnessLog WITH 'ANAF_EFACTURA id=' + TRANSFORM(cVerif.id) + ' numar=[' + TRANSFORM(cVerif.xnumar_act) + '] data=' + TRANSFORM(cVerif.xdata_act) + ' scadenta=' + TRANSFORM(cVerif.xdata_scad)
|
||||
DO HarnessLog WITH ' furnizor=[' + TRANSFORM(cVerif.xfurnizor) + '] client=[' + TRANSFORM(cVerif.xclient) + ']'
|
||||
DO HarnessLog WITH ' fara tva=' + TRANSFORM(cVerif.xtotal_fara_tva) + ' tva=' + TRANSFORM(cVerif.xtotal_tva) + ' cu tva=' + TRANSFORM(cVerif.xtotal_cu_tva) + ' valuta=' + TRANSFORM(cVerif.xnume_valuta)
|
||||
DO HarnessLog WITH ' tip=[' + TRANSFORM(cVerif.tip_mesaj_raspuns) + '] emisa=' + TRANSFORM(cVerif.factura_emisa) + ' cif emitent=' + TRANSFORM(cVerif.cod_fiscal_emitent) + ' cif benef=' + TRANSFORM(cVerif.cod_fiscal_beneficiar)
|
||||
DO HarnessLog WITH ' descarcat=' + TRANSFORM(cVerif.descarcat) + ' procesat=' + TRANSFORM(cVerif.procesat) + ' creditnote=' + TRANSFORM(cVerif.creditnote) + ' len(detalii)=' + TRANSFORM(cVerif.len_detalii) + ' len(zip)=' + TRANSFORM(cVerif.len_zip)
|
||||
DO HarnessLog WITH ' filepath=[' + TRANSFORM(cVerif.filepath) + '] exista pe disc=' + TRANSFORM(FILE(ALLTRIM(NVL(cVerif.filepath, ''))))
|
||||
|
||||
DO TVerdict WITH ALLTRIM(NVL(cVerif.xnumar_act, '')) == 'SRC/0000809', 'T2.1 numar factura', TRANSFORM(cVerif.xnumar_act)
|
||||
DO TVerdict WITH cVerif.xdata_act = {^2026-01-28}, 'T2.2 data factura', TRANSFORM(cVerif.xdata_act)
|
||||
DO TVerdict WITH ROUND(NVL(cVerif.xtotal_cu_tva, 0), 2) = -5166.63, 'T2.3 total cu tva', TRANSFORM(cVerif.xtotal_cu_tva)
|
||||
DO TVerdict WITH ROUND(NVL(cVerif.xtotal_fara_tva, 0), 2) = -4269.94, 'T2.4 total fara tva', TRANSFORM(cVerif.xtotal_fara_tva)
|
||||
DO TVerdict WITH ALLTRIM(UPPER(NVL(cVerif.tip_mesaj_raspuns, ''))) == 'FACTURA PRIMITA' AND NVL(cVerif.factura_emisa, -1) = 0, 'T2.5 tip factura primita', TRANSFORM(cVerif.tip_mesaj_raspuns)
|
||||
DO TVerdict WITH ALLTRIM(NVL(cVerif.cod_fiscal_emitent, '')) == '17043227' AND ALLTRIM(NVL(cVerif.cod_fiscal_beneficiar, '')) == gcCifBenef, 'T2.6 coduri fiscale', TRANSFORM(cVerif.cod_fiscal_emitent) + '/' + TRANSFORM(cVerif.cod_fiscal_beneficiar)
|
||||
DO TVerdict WITH NVL(cVerif.len_detalii, 0) > 4000, 'T2.7 xml salvat in detalii', TRANSFORM(cVerif.len_detalii) + ' caractere'
|
||||
DO TVerdict WITH FILE(ALLTRIM(NVL(cVerif.filepath, ''))), 'T2.8 arhiva copiata local', TRANSFORM(cVerif.filepath)
|
||||
|
||||
lnDetalii = 0
|
||||
goExecutor.oSelecteaza2Value("select count(*) from anaf_efactura_detalii where id_efactura = " + ALLTRIM(STR(cVerif.id)), @lnDetalii)
|
||||
DO TVerdict WITH NVL(lnDetalii, 0) > 0, 'T2.9 linii in anaf_efactura_detalii', TRANSFORM(lnDetalii) + ' linii'
|
||||
|
||||
goExecutor.oExecuta("select nr, articol, cantitate, um, pret, proctva, valoarefaratva from anaf_efactura_detalii where id_efactura = " + ALLTRIM(STR(cVerif.id)) + " order by nr", 'cLinii')
|
||||
IF USED('cLinii')
|
||||
SELECT cLinii
|
||||
SCAN
|
||||
DO HarnessLog WITH ' linia ' + TRANSFORM(cLinii.nr) + ': [' + TRANSFORM(cLinii.articol) + '] cant=' + TRANSFORM(cLinii.cantitate) + ' ' + TRANSFORM(cLinii.um) + ' pret=' + TRANSFORM(cLinii.pret) + ' tva=' + TRANSFORM(cLinii.proctva) + ' valoare=' + TRANSFORM(cLinii.valoarefaratva)
|
||||
ENDSCAN
|
||||
USE IN (SELECT('cLinii'))
|
||||
ENDIF
|
||||
ELSE
|
||||
DO TVerdict WITH .F., 'T2.1-T2.9 verificare in baza de date', 'nu am gasit exact 1 rand in anaf_efactura'
|
||||
ENDIF
|
||||
|
||||
USE IN (SELECT('cVerif'))
|
||||
DO HarnessStep WITH 2, 'dupa_import'
|
||||
|
||||
* ---------- T3: factura in grila reala "Facturi primite" din borderou ----------
|
||||
* ATENTIE: pagina "Facturi primite in SPV" e obiectul Page2, dar are PageOrder = 3,
|
||||
* iar ActivePage merge pe PageOrder - deci activarea ei se face cu 3, nu cu 2.
|
||||
loForm.pgfeFactura.ActivePage = 3
|
||||
loForm.Refresh()
|
||||
lnGasit = 0
|
||||
IF USED('crsFacturiPrimite')
|
||||
SELECT crsFacturiPrimite
|
||||
COUNT FOR ALLTRIM(NVL(numar_act, '')) == 'SRC/0000809' TO lnGasit
|
||||
DO HarnessLog WITH 'crsFacturiPrimite: ' + TRANSFORM(RECCOUNT('crsFacturiPrimite')) + ' randuri, din care factura importata: ' + TRANSFORM(lnGasit)
|
||||
ENDIF
|
||||
DO TVerdict WITH lnGasit = 1, 'T3 factura vizibila in grila Facturi primite', TRANSFORM(lnGasit) + ' randuri gasite'
|
||||
DO HarnessStep WITH 3, 'grila_facturi_primite'
|
||||
|
||||
DO HarnessLog WITH 'REZULTAT FINAL: PASS=' + TRANSFORM(gnPass) + ' FAIL=' + TRANSFORM(gnFail)
|
||||
DO HarnessDone WITH IIF(gnFail = 0, 'done OK', 'done CU ESECURI')
|
||||
|
||||
QUIT
|
||||
|
||||
***********************
|
||||
PROCEDURE TVerdict
|
||||
LPARAMETERS tlOk, tcNume, tcDetalii
|
||||
IF tlOk
|
||||
gnPass = gnPass + 1
|
||||
ELSE
|
||||
gnFail = gnFail + 1
|
||||
ENDIF
|
||||
DO HarnessLog WITH IIF(tlOk, 'PASS ', 'FAIL ') + tcNume + ' | ' + TRANSFORM(tcDetalii)
|
||||
ENDPROC
|
||||
|
||||
***********************
|
||||
PROCEDURE TErr
|
||||
LPARAMETERS tnError, tcMessage, tcProgram, tnLine
|
||||
DO HarnessLog WITH 'EROARE ' + TRANSFORM(tnError) + ' [' + TRANSFORM(tcMessage) + '] in ' + TRANSFORM(tcProgram) + ' linia ' + TRANSFORM(tnLine)
|
||||
ENDPROC
|
||||
12
utile/Teste/efactura_import/test_import_zip_efactura.ps1
Normal file
12
utile/Teste/efactura_import/test_import_zip_efactura.ps1
Normal file
@@ -0,0 +1,12 @@
|
||||
# test_import_zip_efactura.ps1 - wrapper peste harness-ul generic pentru testul de import
|
||||
# arhiva zip eFactura in borderou (clasa anaf_efactura + AnafeFacturaServer.ImportZipLocal).
|
||||
# Rulare: powershell -ExecutionPolicy Bypass -File test_import_zip_efactura.ps1
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Teste = $PSScriptRoot
|
||||
& (Join-Path (Split-Path $Teste -Parent) 'vfp_ui_harness.ps1') `
|
||||
-TestPrg (Join-Path $Teste 'test_import_zip_efactura.prg') `
|
||||
-Steps @('borderou_deschis','garda_cod_fiscal','dupa_import','grila_facturi_primite') `
|
||||
-ShotsDir (Join-Path $Teste 'screenshots_importzip') `
|
||||
-SyncDir (Join-Path $Teste 'uisync_importzip') `
|
||||
-ReadyTimeoutSec 300 `
|
||||
-StepTimeoutSec 180
|
||||
@@ -253,11 +253,16 @@ IF m.llSucces
|
||||
gnIdSucursala = null
|
||||
gcFirma = ALLTRIM(firma)
|
||||
SCATTER NAME goFirma
|
||||
*!* 24.07.2026 marius.mutu - ca in start_firma (ostartfirma.prg): codfiscalfro nu e coloana in v_firme
|
||||
IF TYPE('goFirma.codfiscalfro') = 'U'
|
||||
ADDPROPERTY(goFirma, 'codfiscalfro', ALLTRIM(STRTRAN(ALLTRIM(NVL(goFirma.cod_fiscal, '')), 'RO', '')))
|
||||
ENDIF
|
||||
ELSE
|
||||
goFirma = CREATEOBJECT('custom')
|
||||
goFirma.addproperty('cod_fiscal', '')
|
||||
goFirma.addproperty('schema', m.gcS)
|
||||
goFirma.addproperty('firma', m.gcS)
|
||||
goFirma.addproperty('codfiscalfro', '')
|
||||
ENDIF
|
||||
USE IN (SELECT('cFirma'))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user