sync SVN r18004/r18007: #6 editare factura emisa - pagina de articole

ofacturare_editare.prg (nou): helpere comune - garda eFactura, cursoarele
notei si ale rulajelor, randul de vanzare corespunzator notei si liniile de
articole citite din view-ul VVANZARI_ARTICOLE.

omodificari.vc2 (frm_modific2024): pagina noua de articole ale facturii,
deocamdata doar afisare. Apare numai cand ofacturare_editare.prg e
inregistrat si documentul are rand in VANZARI; in ROACONT si ROAGEST, unde
fisierul nu e incarcat, pagina lipseste si registrul jurnal ramane neatins.
Randul de vanzare al notei se cauta pe toate tripletele distincte (nract,
serie_act, dataact) din nota, pentru ca primul rand poate fi o incasare.

ofacturare_comun.vc2 (frm_facturi): do_editare_factura si butonul aferent,
dupa modelul lui do_sterge, cu garzile de luna inchisa, luna curenta,
document sters, referinte si eFactura.

docs: comentariile se scriu strict necesar, si in cod si in scripturile de
migrare - fara referinte la planuri, stories, decizii sau erori, istoricul
doar in antetul fisierului. Plus regula zero (predarea contextului),
capcanele de la testarea headless si completari pe fluxul text -> binar.

utile/Teste: suita de regresie pentru editarea facturii si harness-ul
watchdog (nu sunt in SVN, unde utile/Teste e ignorat). .gitignore ignora si
capturile PNG si watchdog_out/, ramase din rulari.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
2026-08-08 16:47:24 +03:00
parent c5b0ce3a25
commit 13b4f65723
20 changed files with 2750 additions and 21 deletions

View File

@@ -0,0 +1,72 @@
* test_incarca_articole_view.prg
* Smoke test izolat pentru IncarcaArticoleFactura pe vvanzari_articole (runda 2), inainte de a atinge omodificari.vc2.
* Regresie: id_vanzare=1050 (cod=1140888) -> 4 randuri, id_vanzare=1047 (cod=1140885) -> 2 randuri,
* coincide cu baza din rec_view_articole_vanzare.md. Verifica si coloana noua tvd.id_vanzare.
* Nu atinge Oracle in scriere. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_incarca_articole_view_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
DO verifica WITH 1050, 'cod=1140888', lcLog, 4
DO verifica WITH 1047, 'cod=1140885', lcLog, 2
*!* cursorul gol - structura cu 21 de campuri, inclusiv id_vanzare (nou)
CreeazaCursorTvdGol()
STRTOFILE('--- CreeazaCursorTvdGol ---' + CHR(13) + CHR(10) + ;
' Fcount(tvd) = ' + TRANSFORM(FCOUNT('tvd')) + ' (asteptat 21)' + CHR(13) + CHR(10) + ;
' are id_vanzare: ' + TRANSFORM(TYPE('tvd.id_vanzare') <> 'U') + CHR(13) + CHR(10) + ;
' ' + IIF(FCOUNT('tvd') == 21, 'PASS', 'FAIL') + CHR(13) + CHR(10), lcLog, 1)
IF Used('tvd')
Use In tvd
ENDIF
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE verifica
LPARAMETERS tnIdVanzare, tcEticheta, tcLog, tnLiniiAsteptate
LOCAL lnLinii, lnIdVanzareCol
IncarcaArticoleFactura(tnIdVanzare)
lnLinii = Reccount('tvd')
lnIdVanzareCol = -1
IF lnLinii > 0
SELECT tvd
GO TOP
lnIdVanzareCol = tvd.id_vanzare
ENDIF
STRTOFILE('--- id_vanzare=' + TRANSFORM(tnIdVanzare) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' Reccount(tvd) = ' + TRANSFORM(lnLinii) + ' (asteptat ' + TRANSFORM(tnLiniiAsteptate) + ')' + CHR(13) + CHR(10) + ;
' tvd.id_vanzare pe primul rand = ' + TRANSFORM(lnIdVanzareCol) + ' (asteptat ' + TRANSFORM(tnIdVanzare) + ')' + CHR(13) + CHR(10) + ;
' ' + IIF(lnLinii == tnLiniiAsteptate AND (lnLinii == 0 OR lnIdVanzareCol == tnIdVanzare), 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
IF Used('tvd')
Use In tvd
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,141 @@
* test_incarca_cursoare.prg
* Verifica IncarcaCursoareModificareNota (COMUN\programe\ofacturare_editare.prg) pe date reale:
* cod=1140888 - nota cu rulaje (non-regresie)
* cod=1140885 - nota FARA randuri in rul (productie/manopera, fara miscare de stoc)
* si pozitionarea in actactan din do_editare_factura (ofacturare_comun.vc2), pe cursoare sintetice.
* Nu atinge Oracle in scriere - doar SELECT-uri prin goExecutor. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_incarca_cursoare_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
STRTOFILE('gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + CHR(13) + CHR(10), lcLog, 1)
DO verifica_cod WITH 1140888, 'A (regresie, cu rulaje)', lcLog
DO verifica_cod WITH 1140885, 'B (fara randuri in rul)', lcLog
DO verifica_pozitionare_actactan WITH 9999995, 'a) nota normala, un singur id_fact pe toate randurile', lcLog, 5000, 5000, 25010, 100, .T.
DO verifica_pozitionare_actactan WITH 9999996, 'b) prim rand INCASARE cu id_fact-1, Locate il sare', lcLog, 5000, 5000, 25010, 200, .T.
DO verifica_pozitionare_actactan WITH 9999997, 'c) nota fara randul cu id_fact cerut, cade pe Go Top', lcLog, 9999, 7000, 25011, 50, .F.
DO verifica_pozitionare_actactan WITH 9999998, 'd) lnIdFact=0 (aviz/transfer), direct Go Top', lcLog, 0, 8000, 25012, 10, .F.
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
* reproduce mecanica pozitionarii din do_editare_factura (ofacturare_comun.vc2): cauta in actactan
* randul cu id_fact = lnIdFact (cel citit din crsfacturi/VANZARI.ID_FACT); daca nu-l gaseste (sau
* lnIdFact e 0 - avize, transferuri), cade pe Go Top si preia id_fact de acolo. lnIdSet si
* lnIdFactD se citesc mereu de pe randul gasit (Locate sau Go Top).
PROCEDURE verifica_pozitionare_actactan
LPARAMETERS tnCod, tcEticheta, tcLog, tnIdFactIntrare, tnIdFactAsteptat, tnIdSetAsteptat, tnIdFactDAsteptat, tlGasitAsteptat
LOCAL lnIdFact, lnIdSet, lnIdFactD, llGasitRand, llOk
CREATE CURSOR actactan (id_fact N(9), id_factd N(9), id_set N(9))
DO CASE
CASE tnCod = 9999995
&& a) nota normala: toate randurile cu acelasi id_fact
INSERT INTO actactan VALUES (5000, 100, 25010)
INSERT INTO actactan VALUES (5000, 101, 25010)
CASE tnCod = 9999996
&& b) prim rand INCASARE, id_fact = id_fact-ul facturii - 1
INSERT INTO actactan VALUES (4999, 0, 25010)
INSERT INTO actactan VALUES (5000, 200, 25010)
CASE tnCod = 9999997
&& c) nota nu contine id_fact-ul cerut (document vechi)
INSERT INTO actactan VALUES (7000, 50, 25011)
CASE tnCod = 9999998
&& d) aviz/transfer, fara id_fact la intrare
INSERT INTO actactan VALUES (8000, 10, 25012)
ENDCASE
lnIdFact = tnIdFactIntrare
SELECT actactan
llGasitRand = .F.
IF lnIdFact > 0
LOCATE FOR NVL(id_fact, 0) = lnIdFact
llGasitRand = FOUND()
ENDIF
IF !llGasitRand
GO TOP
lnIdFact = id_fact
ENDIF
lnIdSet = id_set
lnIdFactD = id_factd
USE IN actactan
llOk = (lnIdFact == tnIdFactAsteptat AND lnIdSet == tnIdSetAsteptat AND ;
lnIdFactD == tnIdFactDAsteptat AND llGasitRand == tlGasitAsteptat)
STRTOFILE('--- pozitionare actactan, cod=' + TRANSFORM(tnCod) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' gasit rand = ' + TRANSFORM(llGasitRand) + ' (asteptat ' + TRANSFORM(tlGasitAsteptat) + ')' + CHR(13) + CHR(10) + ;
' id_fact = ' + TRANSFORM(lnIdFact) + ' (asteptat ' + TRANSFORM(tnIdFactAsteptat) + ')' + CHR(13) + CHR(10) + ;
' id_set = ' + TRANSFORM(lnIdSet) + ' (asteptat ' + TRANSFORM(tnIdSetAsteptat) + ')' + CHR(13) + CHR(10) + ;
' id_factd = ' + TRANSFORM(lnIdFactD) + ' (asteptat ' + TRANSFORM(tnIdFactDAsteptat) + ')' + CHR(13) + CHR(10) + ;
' ' + IIF(llOk, 'OK', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
ENDPROC
PROCEDURE verifica_cod
LPARAMETERS tnCod, tcEticheta, tcLog
LOCAL llOk, lcMsg, lnIdSetDistinct
llOk = IncarcaCursoareModificareNota(tnCod, gnAn, gnLuna, .F.)
lnIdSetDistinct = -1
IF Used('actactan') AND Reccount('actactan') > 0
SELECT Distinct id_set FROM actactan INTO CURSOR csr_idset_verif_test
lnIdSetDistinct = Reccount('csr_idset_verif_test')
USE IN csr_idset_verif_test
ENDIF
lcMsg = '--- cod=' + TRANSFORM(tnCod) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' IncarcaCursoareModificareNota => ' + TRANSFORM(llOk) + CHR(13) + CHR(10) + ;
' Reccount(actactan) = ' + TRANSFORM(IIF(Used('actactan'), Reccount('actactan'), -1)) + CHR(13) + CHR(10) + ;
' Reccount(tact) = ' + TRANSFORM(IIF(Used('tact'), Reccount('tact'), -1)) + CHR(13) + CHR(10) + ;
' id_set distincte in actactan = ' + TRANSFORM(lnIdSetDistinct) + CHR(13) + CHR(10) + ;
' Used(trul) = ' + TRANSFORM(Used('trul')) + ' Reccount(trul) = ' + TRANSFORM(IIF(Used('trul'), Reccount('trul'), -1)) + CHR(13) + CHR(10) + ;
' Used(trul_obinv) = ' + TRANSFORM(Used('trul_obinv')) + ' Reccount(trul_obinv) = ' + TRANSFORM(IIF(Used('trul_obinv'), Reccount('trul_obinv'), -1)) + CHR(13) + CHR(10)
STRTOFILE(lcMsg, tcLog, 1)
IF Used('actactan')
USE IN actactan
ENDIF
IF Used('tact')
USE IN tact
ENDIF
IF Used('rul_temp')
USE IN rul_temp
ENDIF
IF Used('trul')
USE IN trul
ENDIF
IF Used('rul_temp_obinv')
USE IN rul_temp_obinv
ENDIF
IF Used('trul_obinv')
USE IN trul_obinv
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,154 @@
* test_incarca_vanzare_din_nota.prg
* Smoke test izolat pentru IncarcaVanzareDinNota (ofacturare_editare.prg), inainte de a atinge
* omodificari.vc2 runda 2. Verifica pe cazurile de regresie ale bugului "Go Top orb pe tact":
* cod=1137874/an=2009/luna=8 -> trebuie sa gaseasca id_vanzare=506 (azi, cu Go Top orb, da 0 randuri)
* cod=1139934/an=2021/luna=12 -> id_vanzare=882 (coliziune VANZARI.COD deja cunoscuta)
* plus regresie pe cazurile vechi: cod=1140888 -> id_vanzare=1050; cod=1125486 -> nu gaseste (tact fara VANZARI)
* plus cazul EOF: tact pozitionat deliberat la EOF (ca dupa CalculeazaTotal() din Show()) inainte de
* apel - dovedeste ca "cod" se ia din cursorul de triplete (SELECT DISTINCT), nu de pe randul curent
* Nu atinge Oracle in scriere - doar SELECT-uri prin goExecutor. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_incarca_vanzare_din_nota_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
DO verifica WITH 1137874, 2009, 8, 'blocant Go Top - prim rand INCASARE', lcLog, .T., 506
DO verifica WITH 1139934, 2021, 12, 'coliziune VANZARI.COD', lcLog, .T., 882
DO verifica WITH 1140888, (gnAn), (gnLuna), 'regresie runda 1 - factura normala', lcLog, .T., 1050
DO verifica WITH 1125486, 2008, 2, 'regresie runda 1 - fara VANZARI', lcLog, .F., 0
DO verifica_eof WITH 1137874, 2009, 8, lcLog, 506
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
* pozitioneaza tact deliberat la EOF (Go Bottom + Skip) inainte de IncarcaVanzareDinNota - dovedeste
* ca "cod" nu se mai citeste de pe randul curent al aliasului (corectia care a inlocuit Evaluate(alias.cod))
PROCEDURE verifica_eof
LPARAMETERS tnCod, tnAn, tnLuna, tcLog, tnIdVanzareAsteptat
LOCAL llOk, llGasit, lnIdVanzare, llEofInainte, llEofDupa
llOk = IncarcaCursoareModificareNota(tnCod, tnAn, tnLuna, .F.)
llGasit = .F.
lnIdVanzare = 0
llEofInainte = .F.
llEofDupa = .F.
IF llOk AND Used('tact')
SELECT tact
Go Bottom In tact
Skip In tact
llEofInainte = Eof('tact')
llGasit = IncarcaVanzareDinNota('tact')
llEofDupa = Eof('tact')
IF llGasit
lnIdVanzare = tvanz.id_vanzare
ENDIF
ENDIF
STRTOFILE('--- EOF pe tact, cod=' + TRANSFORM(tnCod) + ' [pozitionare la EOF inainte de apel] ---' + CHR(13) + CHR(10) + ;
' Eof(tact) inainte de apel = ' + TRANSFORM(llEofInainte) + ' (asteptat .T.)' + CHR(13) + CHR(10) + ;
' gasit = ' + TRANSFORM(llGasit) + ' (asteptat .T.)' + CHR(13) + CHR(10) + ;
' id_vanzare = ' + TRANSFORM(lnIdVanzare) + ' (asteptat ' + TRANSFORM(tnIdVanzareAsteptat) + ')' + CHR(13) + CHR(10) + ;
' Eof(tact) dupa apel (pozitia restaurata) = ' + TRANSFORM(llEofDupa) + ' (asteptat .T.)' + CHR(13) + CHR(10) + ;
' ' + IIF(llEofInainte AND llGasit AND lnIdVanzare == tnIdVanzareAsteptat AND llEofDupa, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
IF Used('rul_temp')
Use In rul_temp
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('rul_temp_obinv')
Use In rul_temp_obinv
ENDIF
IF Used('trul_obinv')
Use In trul_obinv
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
ENDPROC
PROCEDURE verifica
LPARAMETERS tnCod, tnAn, tnLuna, tcEticheta, tcLog, tlAsteptatGasit, tnIdVanzareAsteptat
LOCAL llOk, llGasit, lnIdVanzare, lcMsg, lnAreaOrigineTest, lcAliasOrigineTest
llOk = IncarcaCursoareModificareNota(tnCod, tnAn, tnLuna, .F.)
llGasit = .F.
lnIdVanzare = 0
lnAreaOrigineTest = 0
lcAliasOrigineTest = ''
IF llOk AND Used('tact')
SELECT tact
lnAreaOrigineTest = Select()
lcAliasOrigineTest = Alias()
llGasit = IncarcaVanzareDinNota('tact')
IF llGasit
lnIdVanzare = tvanz.id_vanzare
ENDIF
ENDIF
lcMsg = '--- cod=' + TRANSFORM(tnCod) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' IncarcaCursoareModificareNota => ' + TRANSFORM(llOk) + CHR(13) + CHR(10) + ;
' gasit = ' + TRANSFORM(llGasit) + ' (asteptat ' + TRANSFORM(tlAsteptatGasit) + ')' + CHR(13) + CHR(10) + ;
' id_vanzare = ' + TRANSFORM(lnIdVanzare) + ' (asteptat ' + TRANSFORM(tnIdVanzareAsteptat) + ')' + CHR(13) + CHR(10) + ;
' workarea restaurata la ' + TRANSFORM(lnAreaOrigineTest) + ': ' + TRANSFORM(Select() == lnAreaOrigineTest) + CHR(13) + CHR(10) + ;
' ' + IIF(llGasit == tlAsteptatGasit AND lnIdVanzare == tnIdVanzareAsteptat AND Select() == lnAreaOrigineTest, 'PASS', 'FAIL') + CHR(13) + CHR(10)
STRTOFILE(lcMsg, tcLog, 1)
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
IF Used('rul_temp')
Use In rul_temp
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('rul_temp_obinv')
Use In rul_temp_obinv
ENDIF
IF Used('trul_obinv')
Use In trul_obinv
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,305 @@
* test_page3_articole.prg
* Verifica S4 runda 1+2 (PAGE3, view VVANZARI_ARTICOLE, pozitionare in tact, garda ROACONT/ROAGEST) pe date reale:
* - IncarcaVanzareNota/IncarcaArticoleFactura (ofacturare_editare.prg) direct, pe cod=1140888,
* cod=1140885 (au rand in VANZARI) si cod=1125486 (nota fara rand in VANZARI)
* - cazul de coliziune pe cod: cod=1139934 are 4 randuri VANZARI cu acelasi cod - verifica ca
* filtrul compus (cod+nract+serie_act+data_act) gaseste exact randul corect si NU gaseste
* nimic pentru un nract fara corespondent (evitarea unei potriviri gresite)
* - frm_modific2024 instantiat ca in do_editare_factura (Createobject+Show): PageCount ajunge 3
* pe documente cu rand in VANZARI, ramane 2 pe o nota fara VANZARI
* - regresie runda 2: cod=1137874/an=2009/luna=8 si cod=1139934/an=2021/luna=12, unde primul
* rand din tact e o INCASARE - Go Top orb (runda 1) rata id_vanzare, IncarcaVanzareDinNota il
* gaseste prin toate tripletele distincte (nract,serie_act,dataact) din tact
* - garda ROACONT/ROAGEST: cu ofacturare_editare.prg eliberat din SET PROCEDURE, Show() degradeaza
* curat la PageCount=2, fara eroare
* Nu atinge Oracle in scriere - doar SELECT-uri prin goExecutor. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_page3_articole_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
*!* OBLIGATORIU: test_init_env_auto:139 face "Set Classlib To omodificari Additive", iar SET PATH
*!* e pe D:\ROA\ROACONT\..., deci se incarca copia ROACONT a clasei (alt working copy, fara
*!* modificarile noastre). O eliberam si o reincarcam pe cea DE SUB TEST, cu cale completa -
*!* altfel Createobject valideaza cu totul alt fisier decat cel editat.
RELEASE CLASSLIB omodificari
SET CLASSLIB TO D:\ROA\ROAFACTURARE\COMUN\clase\omodificari.vcx ADDITIVE
*!* 1) IncarcaVanzareNota / IncarcaArticoleFactura direct, pe documente reale
*!* (gnAn)/(gnLuna) in paranteze = pasare PRIN VALOARE: DO ... WITH paseaza prin REFERINTA, iar o
*!* variabila pasata prin referinta devine invizibila sub numele ei propriu in procedura apelata -
*!* de acolo venea dialogul nativ "View Parameter: Enter the value for gnAn" (?gnAn din SQL passthrough)
DO verifica_vanzare_nota WITH 1140888, (gnAn), (gnLuna), 'A (factura, cu articole)', lcLog, .T., 1050, 1, 4
DO verifica_vanzare_nota WITH 1140885, (gnAn), (gnLuna), 'B (aviz/tip -12, cu rand in VANZARI)', lcLog, .T., 1047, -12, -1
DO verifica_vanzare_nota WITH 1125486, 2008, 2, 'C (nota fara rand in VANZARI)', lcLog, .F., 0, 0, -1
*!* 2) cazul de coliziune pe cod=1139934 (4 randuri VANZARI cu acelasi cod) - filtrul compus
DO verifica_coliziune_cod WITH lcLog
*!* 3) frm_modific2024 - PageCount pe factura reala (cod=1140888) si pe nota fara VANZARI (cod=1125486)
DO verifica_pagecount_form WITH 1140888, (gnAn), (gnLuna), 'A (factura reala)', lcLog, 3, .T., 1050
DO verifica_pagecount_form WITH 1125486, 2008, 2, 'C (nota fara VANZARI)', lcLog, 2, .F., 0
*!* 4) regresie runda 2 - blocantul "Go Top orb pe tact": prim rand din tact e o INCASARE, cu
*!* nract/serie_act/dataact diferite de randul facturii - Go Top orb (runda 1) rata id_vanzare
DO verifica_pagecount_form WITH 1137874, 2009, 8, 'D (Go Top orb - blocant runda 2)', lcLog, 3, .T., 506
DO verifica_pagecount_form WITH 1139934, 2021, 12, 'E (coliziune cod + Go Top orb)', lcLog, 3, .T., 882
*!* 5) garda ROACONT/ROAGEST: cu ofacturare_editare.prg eliberat, Show() nu mai gaseste garda si
*!* degradeaza curat la PageCount=2 - ultimul test, elibereaza procedura definitiv pentru restul rularii
DO verifica_garda_procedura_lipsa WITH 1140888, (gnAn), (gnLuna), lcLog
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE verifica_vanzare_nota
LPARAMETERS tnCod, tnAn, tnLuna, tcEticheta, tcLog, tlAsteptatGasit, tnIdVanzareAsteptat, tnTipAsteptat, tnLiniiAsteptate
LOCAL llOk, lcMsg, llGasit, lnIdVanzare, lnTip, lnLinii, llIdOk, llTipOk, llLiniiOk
llOk = IncarcaCursoareModificareNota(tnCod, tnAn, tnLuna, .F.)
llGasit = .F.
lnIdVanzare = 0
lnTip = 0
lnLinii = -1
IF llOk AND Used('tact') AND Reccount('tact') > 0
Go Top In tact
IncarcaVanzareNota(tact.cod, tact.nract, tact.serie_act, tact.dataact)
IF Reccount('tvanz') = 1
llGasit = .T.
lnIdVanzare = tvanz.id_vanzare
lnTip = tvanz.tip
IncarcaArticoleFactura(lnIdVanzare)
lnLinii = Reccount('tvd')
ENDIF
ENDIF
llIdOk = IIF(tlAsteptatGasit, lnIdVanzare == tnIdVanzareAsteptat, .T.)
llTipOk = IIF(tlAsteptatGasit, lnTip == tnTipAsteptat, .T.)
llLiniiOk = IIF(tlAsteptatGasit AND tnLiniiAsteptate >= 0, lnLinii == tnLiniiAsteptate, .T.)
lcMsg = '--- cod=' + TRANSFORM(tnCod) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' IncarcaCursoareModificareNota => ' + TRANSFORM(llOk) + CHR(13) + CHR(10) + ;
' gasit in vanzari = ' + TRANSFORM(llGasit) + ' (asteptat ' + TRANSFORM(tlAsteptatGasit) + ')' + CHR(13) + CHR(10) + ;
' id_vanzare = ' + TRANSFORM(lnIdVanzare) + ' (asteptat ' + TRANSFORM(tnIdVanzareAsteptat) + ')' + CHR(13) + CHR(10) + ;
' tip = ' + TRANSFORM(lnTip) + ' (asteptat ' + TRANSFORM(tnTipAsteptat) + ')' + CHR(13) + CHR(10) + ;
' Reccount(tvd) = ' + TRANSFORM(lnLinii) + ' (asteptat ' + TRANSFORM(tnLiniiAsteptate) + ')' + CHR(13) + CHR(10) + ;
' ' + IIF(llGasit == tlAsteptatGasit AND llIdOk AND llTipOk AND llLiniiOk, 'PASS', 'FAIL') + CHR(13) + CHR(10)
STRTOFILE(lcMsg, tcLog, 1)
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
IF Used('rul_temp')
Use In rul_temp
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('rul_temp_obinv')
Use In rul_temp_obinv
ENDIF
IF Used('trul_obinv')
Use In trul_obinv
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
IF Used('tvd')
Use In tvd
ENDIF
ENDPROC
* cod=1139934 are 4 randuri active in VANZARI cu acelasi cod (verificat pe MARIUSM_AUTO):
* id_vanzare=882 numar_act=375/SSS, 883=24/PF, 884=25/PF, 885=26/PF - filtrul pe cod singur
* ar fi ambiguu; filtrul compus (cod+nract+serie_act+data_act) trebuie sa gaseasca exact
* randul cerut si sa NU gaseasca nimic pentru un nract fara corespondent in VANZARI (ACT are
* randuri cu nract=13 pe acelasi cod, fara VANZARI corespunzator)
PROCEDURE verifica_coliziune_cod
LPARAMETERS tcLog
LOCAL ldDataAct, llOk1, llOk2
ldDataAct = DATE(2021,12,31)
IncarcaVanzareNota(1139934, 375, 'SSS', ldDataAct)
llOk1 = (Reccount('tvanz') = 1 AND tvanz.id_vanzare == 882)
IF Used('tvanz')
Use In tvanz
ENDIF
IncarcaVanzareNota(1139934, 13, '', ldDataAct)
llOk2 = (Reccount('tvanz') = 0)
IF Used('tvanz')
Use In tvanz
ENDIF
STRTOFILE('--- coliziune cod=1139934 (4 randuri VANZARI cu acelasi cod) ---' + CHR(13) + CHR(10) + ;
' nract=375/SSS => gaseste id_vanzare=882: ' + IIF(llOk1, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' nract=13 (fara corespondent) => 0 randuri: ' + IIF(llOk2, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
ENDPROC
* instantiaza frm_modific2024 exact ca in do_editare_factura (ofacturare_comun.vc2:3781-3803):
* IncarcaCursoareModificareNota, rebuild tact cu coloana cu_Tva, Createobject+Show; verifica
* pgfArticole.PageCount si proprietatile de detectie dupa Show()
PROCEDURE verifica_pagecount_form
LPARAMETERS tnCod, tnAn, tnLuna, tcEticheta, tcLog, tnPageCountAsteptat, tlAreArticoleAsteptat, tnIdVanzareAsteptat
LOCAL llOk, lnIdSet, lnPageCount, llAreArticole, lnIdVanzare, loForm, llIdVanzareOk
llOk = IncarcaCursoareModificareNota(tnCod, tnAn, tnLuna, .F.)
lnPageCount = -1
llAreArticole = .F.
lnIdVanzare = 0
IF llOk AND Used('tact') AND Reccount('tact') > 0
Select a.*, Iif(Nvl(id_jtva_coloana,0)=0,0,1) As cu_Tva From tact a Into Cursor tact Readwrite
Go Top In tact
lnIdSet = tact.id_set
IF !Used('crsJtvaTemp')
update_jtva_coloane("", "crsJtvaTemp", 6)
ENDIF
Select tact
loForm = Createobject([frm_modific2024], lnIdSet)
loForm.WindowType = 0
loForm.Show()
lnPageCount = loForm.pgfArticole.PageCount
llAreArticole = loForm.lAreArticoleVanzari
lnIdVanzare = loForm.nIdVanzare
loForm.Release()
ENDIF
llIdVanzareOk = IIF(PCOUNT() >= 9, lnIdVanzare == tnIdVanzareAsteptat, .T.)
STRTOFILE('--- frm_modific2024, cod=' + TRANSFORM(tnCod) + ' [' + tcEticheta + '] ---' + CHR(13) + CHR(10) + ;
' PageCount = ' + TRANSFORM(lnPageCount) + ' (asteptat ' + TRANSFORM(tnPageCountAsteptat) + ')' + CHR(13) + CHR(10) + ;
' lAreArticoleVanzari = ' + TRANSFORM(llAreArticole) + ' (asteptat ' + TRANSFORM(tlAreArticoleAsteptat) + ')' + CHR(13) + CHR(10) + ;
' nIdVanzare = ' + TRANSFORM(lnIdVanzare) + IIF(PCOUNT() >= 9, ' (asteptat ' + TRANSFORM(tnIdVanzareAsteptat) + ')', '') + CHR(13) + CHR(10) + ;
' ' + IIF(lnPageCount == tnPageCountAsteptat AND llAreArticole == tlAreArticoleAsteptat AND llIdVanzareOk, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
IF Used('rul_temp')
Use In rul_temp
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('rul_temp_obinv')
Use In rul_temp_obinv
ENDIF
IF Used('trul_obinv')
Use In trul_obinv
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
IF Used('tvd')
Use In tvd
ENDIF
ENDPROC
* verifica garda ROACONT/ROAGEST: cu ofacturare_editare.prg eliberat din SET PROCEDURE, Show()
* trebuie sa degradeze curat la PageCount=2, fara eroare - ultimul test din suita, procedura
* ramane eliberata pentru restul rularii (QUIT urmeaza imediat)
PROCEDURE verifica_garda_procedura_lipsa
LPARAMETERS tnCod, tnAn, tnLuna, tcLog
LOCAL llOk, lnIdSet, lnPageCount, loForm, lcProcList, lnI, lcFile
llOk = IncarcaCursoareModificareNota(tnCod, tnAn, tnLuna, .F.)
lnPageCount = -1
IF llOk AND Used('tact') AND Reccount('tact') > 0
Select a.*, Iif(Nvl(id_jtva_coloana,0)=0,0,1) As cu_Tva From tact a Into Cursor tact Readwrite
Go Top In tact
lnIdSet = tact.id_set
IF !Used('crsJtvaTemp')
update_jtva_coloane("", "crsJtvaTemp", 6)
ENDIF
*!* simuleaza ROACONT/ROAGEST: reconstruieste lista de proceduri incarcate, fara ofacturare_editare.prg
*!* (VFP nu are un "release" pe un singur fisier din SET PROCEDURE - se inchid toate si se redeschid restul)
lcProcList = Set('Procedure')
SET PROCEDURE TO
FOR lnI = 1 TO GETWORDCOUNT(lcProcList, ',')
lcFile = ALLTRIM(GETWORDNUM(lcProcList, lnI, ','))
IF !EMPTY(lcFile) AND !("OFACTURARE_EDITARE" $ UPPER(lcFile))
SET PROCEDURE TO (lcFile) ADDITIVE
ENDIF
ENDFOR
Select tact
loForm = Createobject([frm_modific2024], lnIdSet)
loForm.WindowType = 0
loForm.Show()
lnPageCount = loForm.pgfArticole.PageCount
loForm.Release()
ENDIF
STRTOFILE('--- garda ofacturare_editare.prg lipsa, cod=' + TRANSFORM(tnCod) + ' ---' + CHR(13) + CHR(10) + ;
' PageCount = ' + TRANSFORM(lnPageCount) + ' (asteptat 2, ca pe ROACONT/ROAGEST)' + CHR(13) + CHR(10) + ;
' ' + IIF(lnPageCount == 2, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
IF Used('rul_temp')
Use In rul_temp
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('rul_temp_obinv')
Use In rul_temp_obinv
ENDIF
IF Used('trul_obinv')
Use In trul_obinv
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,40 @@
* test_repro_izolat_gnan.prg
* Experimentul B (cerut de team-lead): reproduce izolat, FARA nimic din S4 - fara
* IncarcaCursoareModificareNota, fara frm_modific2024, fara PAGE3. Doar init mediu +
* update_jtva_coloane singura. Daca dialogul "View Parameter" apare si aici, blocajul nu are
* nicio legatura cu modificarea S4 - e o capcana preexistenta a harness-ului/mediului.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_repro_izolat_gnan_errhandler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
STRTOFILE(' TYPE(gnAn)=' + TYPE('gnAn') + ' TRANSFORM(gnAn)=' + TRANSFORM(gnAn) + ;
' TYPE(gnLuna)=' + TYPE('gnLuna') + ' TRANSFORM(gnLuna)=' + TRANSFORM(gnLuna) + CHR(13) + CHR(10), lcLog, 1)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\updateserver.prg ADDITIVE
update_jtva_coloane("", "crsJtvaTemp", 6)
STRTOFILE(' dupa update_jtva_coloane: Used(crsJtvaTemp)=' + TRANSFORM(Used('crsJtvaTemp')) + ;
' Reccount=' + IIF(Used('crsJtvaTemp'), TRANSFORM(Reccount('crsJtvaTemp')), '(n/a)') + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE test_repro_izolat_gnan_errhandler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,55 @@
* test_scan_workarea.prg
* Verificare punctuala: IncarcaVanzareDinNota foloseste SCAN...ENDSCAN pe tmp_triplete_pozvanz, iar
* corpul buclei (IncarcaVanzareNota) schimba workarea curenta (creeaza/selecteaza tvanz). SCAN in VFP
* poate sa nu tolereze schimbarea workareai curente in interiorul buclei (SKIP/EOF() implicit la
* ENDSCAN se aplica pe workarea CURENTA, nu neaparat pe cea de la inceputul SCAN). Testul forteaza
* explicit cazul cu 2+ triplete unde primul NU se potriveste, ca sa verifice daca a doua iteratie
* chiar se executa (nu doar "a mers din intamplare" pe cod=1137874 unde ordinea DISTINCT ar fi putut
* pune din start tripletul corect primul).
* Nu atinge Oracle in scriere. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_scan_workarea_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
*!* construieste un cursor "tact" sintetic cu 2 triplete, ordonate explicit ca primul (INCASARE,
*!* nract=99999 fara corespondent in VANZARI) sa iasa PRIMUL din SELECT DISTINCT (fara ORDER BY,
*!* dar inserat primul si numeric mai mic), iar al doilea sa fie tripletul real (cod=1137874,
*!* nract=5, serie=FFFFF, data=27.08.2009) care CHIAR exista in VANZARI (id_vanzare=506)
CREATE CURSOR tact (cod I, nract I, serie_act C(20), dataact D, sters I)
INSERT INTO tact (cod, nract, serie_act, dataact, sters) VALUES (1137874, 1, '', {}, 0)
INSERT INTO tact (cod, nract, serie_act, dataact, sters) VALUES (1137874, 5, 'FFFFF', DATE(2009,8,27), 0)
LOCAL llGasit, lnIdVanzare
llGasit = IncarcaVanzareDinNota('tact')
lnIdVanzare = IIF(llGasit, tvanz.id_vanzare, -1)
STRTOFILE('--- SCAN cu workarea schimbata in corp: 2 triplete, primul fara corespondent ---' + CHR(13) + CHR(10) + ;
' gasit = ' + TRANSFORM(llGasit) + ' (asteptat .T.)' + CHR(13) + CHR(10) + ;
' id_vanzare = ' + TRANSFORM(lnIdVanzare) + ' (asteptat 506)' + CHR(13) + CHR(10) + ;
' ' + IIF(llGasit AND lnIdVanzare == 506, 'PASS', 'FAIL') + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,27 @@
* test_set_procedure.prg
* Verifica strict formatul intors de SET("PROCEDURE") in VFP9, dupa mai multe
* SET PROCEDURE TO ... ADDITIVE succesive, ca sa se stabileasca garda din omodificari.vc2 Show().
* Nu atinge Oracle, nu deschide formulare. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_set_procedure_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
STRTOFILE('dupa 1 SET PROCEDURE: [' + SET('PROCEDURE') + ']' + CHR(13) + CHR(10), lcLog, 1)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\oproceduri_facturare.prg ADDITIVE
STRTOFILE('dupa 2 SET PROCEDURE (alt fisier ADDITIVE): [' + SET('PROCEDURE') + ']' + CHR(13) + CHR(10), lcLog, 1)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_stoc.prg ADDITIVE
STRTOFILE('dupa 3 SET PROCEDURE (al treilea fisier ADDITIVE): [' + SET('PROCEDURE') + ']' + CHR(13) + CHR(10), lcLog, 1)
LOCAL llGasit
llGasit = "OFACTURARE_EDITARE" $ UPPER(SET('PROCEDURE'))
STRTOFILE('contine OFACTURARE_EDITARE dupa 3 seturi: ' + TRANSFORM(llGasit) + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT

View File

@@ -0,0 +1,523 @@
* test_writeback_buton1.prg
* Test REAL de write-back pentru ramura buton=1 a lui frm_facturi.do_editare_factura
* (COMUN\clase\ofacturare_comun.vc2:3805-3840) - APROBAT explicit de Marius pe 08.08.2026 sa
* scrie in schema de dev MARIUSM_AUTO@ROA_CENTRAL (nu doar sa citeasca).
*
* Document consumat: cod=1140886, id_vanzare=1048 (factura tip 1, 07-AUG-26, total_cu_tva=302.51,
* 1 linie in VANZARI_DETALII, 5 randuri in ACT, id_set=25010, id_fact=8009658, 1 rand in RUL).
* NU foloseste cod=1140888/1140885 (baze de regresie ale test_incarca_cursoare.prg).
*
* Harness, nu apel direct al metodei: nu instantiaza frm_facturi si NU mai instantiaza
* frm_modific2024 deloc (incercarea initiala s-a blocat la Createobject/Show pe un dialog nativ
* Windows "Open" - cursor/tabela lipsa in acest mediu minimal, capcana j din testare-ui-vfp.md -
* fara legatura cu calea de scriere care se testeaza aici). In loc, harnessul:
* 1) reproduce exact starea de cursoare pe care do_editare_factura o lasa inainte de
* Createobject('frm_modific2024',...) (IncarcaCursoareModificareNota, functia reala din
* ofacturare_editare.prg);
* 2) seteaza buton=1 direct (SARE peste inainte_de_do_termin - vezi ATENTIE mai jos);
* 3) ruleaza LITERAL codul ramurii buton=1 (copiat din ofacturare_comun.vc2:3805-3840).
* Thisform.do_deschide_tranzactie/do_inchide_tranzactie (mostenite nemodificate din _frm_base,
* fara dependinta de vreo clasa de formular anume) sunt reproduse local, INLINE, dupa codul din
* _frm_base.vc2:252-302 - ocolesc si instantierea unui _frmbase generic, ca sa nu riste acelasi
* fel de blocaj vazut la frm_modific2024.
*
* ATENTIE - ce NU acopera acest test: validarea din inainte_de_do_termin
* (omodificari.vc2:13357-13549 - verificare_note_contabile, echilibru 4426-4428,
* VerificaAvertizareExigibilizareTVA) si comportamentul real al formularului la butonul
* "Terminat". Testul demonstreaza ca LANTUL DE SCRIERE merge, nu ca utilizatorul ajunge la el
* prin UI - cele doua nu trebuie confundate la interpretarea rezultatului.
*
* Mediu: test_init_env_auto.prg (bootstrap ROACONT, conexiune reala MARIUSM_AUTO) - acelasi mediu
* folosit deja cu succes de test_incarca_cursoare.prg si test_page3_articole.prg. oscrie_in_fisiere.prg
* e incarcat explicit din COMUN-ul ROAFACTURARE (nu cel auto-rezolvat din calea ROACONT) ca sa
* ruleze exact fisierul pe care il ruleaza aplicatia.
*
* Ruleaza 2 treceri pe acelasi document (cod-ul se schimba dupa fiecare salvare, se recitesc din
* VANZARI la fiecare trecere):
* TEST 1 - salvare "goala" (fara nicio modificare in tact) - proba ca lantul chiar scrie.
* TEST 2 - modifica explicatia unui rand ACT inainte de salvare - proba ca modificarea ajunge.
*
* Nu atinge .PJX/.PJT/.exe, nu modifica cod aplicatie. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_writeback_buton1_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog
gcErrLog = lcLog
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
*-- mock amessagebox INCARCAT PRIMUL (inaintea lantului din test_init_env_auto, care incarca
*-- amessagebox real) - la nume duplicat de procedura, castiga fisierul cautat PRIMUL (testare-ui-vfp.md, capcana g)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\utile\Teste\mock_amessagebox.prg ADDITIVE
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
*-- verificare ca mock-ul chiar umbreste amessebox real (altfel un dialog real ar bloca headless)
LOCAL lnVerifMock
lnVerifMock = amessagebox('verificare mock', 4+32, 'test')
STRTOFILE('verificare mock amessagebox = ' + TRANSFORM(lnVerifMock) + ;
IIF(lnVerifMock = 6, ' (OK, mock activ)', ' (ATENTIE: real amessagebox activ, risc de blocaj)') + CHR(13) + CHR(10), lcLog, 1)
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\oscrie_in_fisiere.prg ADDITIVE
IF TYPE('gnPC') <> 'N'
PUBLIC gnPC
gnPC = 2
ENDIF
*-- capteaza textul ultimului amessagebox mocked (mock_amessagebox.prg, liniile 13-14/23-26) -
*-- necesar ca sa vedem CE mesaj de eroare arunca oscrie_in_fisiere.prg pe caile de esec
PUBLIC gcMockUltimMesaj, gnMockUltimTip
gcMockUltimMesaj = ''
gnMockUltimTip = 0
STRTOFILE('gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + ' gcS=' + gcS + CHR(13) + CHR(10), lcLog, 1)
DO test_editare_writeback WITH 1048, .F., 'TEST 1: salvare fara nicio modificare', lcLog
DO test_editare_writeback WITH 1048, .T., 'TEST 2: modificare explicatie pe un rand ACT', lcLog
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
*-- executa SELECT-ul agregat prin goExecutor si intoarce lnSucces; NU citi campurile cursorului
*-- decat dupa ce ai verificat lnSucces > 0 (IIF() evalueaza ambele ramuri, ar da eroare pe alias lipsa)
PROCEDURE ExecAgg
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
ENDPROC
*-- logheaza si RESETEAZA gcMockUltimMesaj/goExecutor.cEroare - reseteaza ca sa nu se confunde
*-- mesajul de la un pas cu mesajul (sau lipsa lui) de la pasul urmator
PROCEDURE LogMockSiEroare
LPARAMETERS tcEticheta, tcLog
STRTOFILE(' [' + tcEticheta + '] gcMockUltimMesaj=[' + ALLTRIM(gcMockUltimMesaj) + '] ' + ;
'goExecutor.cEroare=[' + ALLTRIM(TRANSFORM(NVL(goExecutor.cEroare, ''))) + ']' + CHR(13) + CHR(10), tcLog, 1)
gcMockUltimMesaj = ''
ENDPROC
*-- reproduce _frm_base.vc2:252-265 (do_deschide_tranzactie) fara sa instantieze niciun formular
PROCEDURE MyDeschideTranzactie
LOCAL llReturn, lnSuccesLocal
IF TYPE('goExecutor') = 'O'
goExecutor.oExecuta([select * from dual])
ENDIF
lnSuccesLocal = SQLSetprop(gnHandle, "Transactions", 2)
llReturn = IIF(lnSuccesLocal < 0, .F., .T.)
RETURN llReturn
ENDPROC
*-- reproduce _frm_base.vc2:279-302 (do_inchide_tranzactie) fara sa instantieze niciun formular
PROCEDURE MyInchideTranzactie
LPARAMETERS tnTip
LOCAL llReturn, lnSuccesLocal
IF tnTip = 1
lnSuccesLocal = SQLCOMMIT(gnHandle)
ELSE
lnSuccesLocal = SQLROLLBACK(gnHandle)
ENDIF
IF lnSuccesLocal < 0
llReturn = .F.
ELSE
lnSuccesLocal = SQLSetprop(gnHandle, "Transactions", 1)
llReturn = IIF(lnSuccesLocal < 0, .F., .T.)
ENDIF
RETURN llReturn
ENDPROC
PROCEDURE test_editare_writeback
LPARAMETERS tnIdVanzare, tlModificaLinie, tcEticheta, tcLog
*-- pnAn/pnLuna/lnCod/lnIdFact PRIVATE (nu LOCAL), identic cu declararea reala
*-- (ofacturare_comun.vc2:3742) - finalizeaza_modificare_nota le foloseste ca bind-variabile
*-- ?pnLuna/?pnAn in interiorul lui goExecutor.oExecuta, care le cere vizibile PRIVATE pe stiva
PRIVATE pnAn, pnLuna, lnCod, lnIdFact
LOCAL lnSters, llEProforma
LOCAL lnIdSet, lnIdFactD, lnSucces, llGasitRand
LOCAL lcSql, lnQ
LOCAL lnActBeforeCnt, lnActBeforeSuma, lnRulBeforeCnt, lnRulObinvBeforeCnt
LOCAL lnVdCnt, lnVdSumaPret, lnVdSumaPretCuTva
LOCAL lcExplicatieVeche, lcExplicatieNoua
LOCAL lnVzCodBefore, lnVzStersBefore, lnVzTotalFaraTvaBefore, lnVzTotalTvaBefore, lnVzTotalCuTvaBefore, lnVzDiscountBefore
STRTOFILE(CHR(13) + CHR(10) + '========== ' + tcEticheta + ' (id_vanzare=' + TRANSFORM(tnIdVanzare) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
*-- 1) starea curenta din VANZARI (cod-ul se poate schimba fata de trecerea anterioara)
lcSql = [select cod, sters, id_fact, eproforma, total_fara_tva, total_tva, total_cu_tva, discount, ] + ;
[extract(year from data_act) as anact, extract(month from data_act) as lunaact from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecAgg(lcSql, 'v_vanzare_curenta')
IF lnQ <= 0 OR RECCOUNT('v_vanzare_curenta') <> 1
STRTOFILE('FAIL: VANZARI id_vanzare=' + TRANSFORM(tnIdVanzare) + ' - lnQ=' + TRANSFORM(lnQ) + ;
' reccount=' + IIF(lnQ > 0, TRANSFORM(RECCOUNT('v_vanzare_curenta')), '?') + ;
' cEroare=[' + ALLTRIM(TRANSFORM(NVL(goExecutor.cEroare, ''))) + ']' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
SELECT v_vanzare_curenta
lnCod = cod
lnSters = sters
lnIdFact = id_fact
llEProforma = (eproforma = 1)
pnAn = anact
pnLuna = lunaact
lnVzCodBefore = cod
lnVzStersBefore = sters
lnVzTotalFaraTvaBefore = total_fara_tva
lnVzTotalTvaBefore = total_tva
lnVzTotalCuTvaBefore = total_cu_tva
lnVzDiscountBefore = discount
USE IN v_vanzare_curenta
STRTOFILE(' VANZARI inainte: cod=' + TRANSFORM(lnCod) + ' sters=' + TRANSFORM(lnSters) + ' id_fact=' + TRANSFORM(lnIdFact) + ;
' an/luna=' + TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna) + ' total_cu_tva=' + TRANSFORM(lnVzTotalCuTvaBefore) + CHR(13) + CHR(10), tcLog, 1)
*-- 2) garzi (replica do_editare_factura, fara UI)
IF lnSters = 1
STRTOFILE(' SKIP: document deja sters' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
IF llEProforma
STRTOFILE(' SKIP: proforma' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
IF (pnAn*12) + pnLuna <> (gnAn*12) + gnLuna
STRTOFILE(' SKIP: nu e luna curenta (document ' + TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna) + ', curenta ' + TRANSFORM(gnAn) + '/' + TRANSFORM(gnLuna) + ')' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
IF EsteInEFactura(lnIdFact)
STRTOFILE(' SKIP: trimis in eFactura' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
*-- 3) instantaneu INAINTE: ACT / RUL / RUL_OBINV (pe cod-ul curent) + VANZARI_DETALII
lcSql = [select count(*) as cnt, sum(suma) as totsuma from vact_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCod)
lnQ = ExecAgg(lcSql, 'v_act_before')
IF lnQ > 0
lnActBeforeCnt = v_act_before.cnt
lnActBeforeSuma = NVL(v_act_before.totsuma, 0)
USE IN v_act_before
ELSE
lnActBeforeCnt = -1
lnActBeforeSuma = -1
ENDIF
lcSql = [select count(*) as cnt from vrul_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCod)
lnQ = ExecAgg(lcSql, 'v_rul_before')
IF lnQ > 0
lnRulBeforeCnt = v_rul_before.cnt
USE IN v_rul_before
ELSE
lnRulBeforeCnt = -1
ENDIF
lcSql = [select count(*) as cnt from vrul_obinv_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCod)
lnQ = ExecAgg(lcSql, 'v_rulobinv_before')
IF lnQ > 0
lnRulObinvBeforeCnt = v_rulobinv_before.cnt
USE IN v_rulobinv_before
ELSE
lnRulObinvBeforeCnt = -1
ENDIF
lcSql = [select count(*) as cnt, sum(cantitate) as sumcant, sum(pret) as sumpret, sum(pret_cu_tva) as sumpretcutva ] + ;
[from vanzari_detalii where sters = 0 and id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecAgg(lcSql, 'v_vd_before')
IF lnQ > 0
lnVdCnt = v_vd_before.cnt
lnVdSumaPret = NVL(v_vd_before.sumpret, 0)
lnVdSumaPretCuTva = NVL(v_vd_before.sumpretcutva, 0)
USE IN v_vd_before
ELSE
lnVdCnt = -1
lnVdSumaPret = -1
lnVdSumaPretCuTva = -1
ENDIF
STRTOFILE(' INAINTE: ACT cnt=' + TRANSFORM(lnActBeforeCnt) + ' suma=' + TRANSFORM(lnActBeforeSuma) + ;
' RUL cnt=' + TRANSFORM(lnRulBeforeCnt) + ' RUL_OBINV cnt=' + TRANSFORM(lnRulObinvBeforeCnt) + ;
' VANZARI_DETALII cnt=' + TRANSFORM(lnVdCnt) + ' sum(pret)=' + TRANSFORM(lnVdSumaPret) + ' sum(pret_cu_tva)=' + TRANSFORM(lnVdSumaPretCuTva) + CHR(13) + CHR(10), tcLog, 1)
*-- 4) IncarcaCursoareModificareNota (functia reala, ofacturare_editare.prg)
IF !IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.)
STRTOFILE(' FAIL: IncarcaCursoareModificareNota a esuat (eroare Oracle)' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
IF RECCOUNT('actactan') = 0
STRTOFILE(' FAIL: nu exista nota contabila (Reccount(actactan)=0)' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
*-- 5) pozitionare id_fact/id_set/id_factd - copiat identic din do_editare_factura
*-- (ofacturare_comun.vc2:3785-3797; garda id_set scoasa - decizia 24, cod curent nu o mai are)
SELECT actactan
llGasitRand = .F.
IF lnIdFact > 0
LOCATE FOR NVL(id_fact, 0) = lnIdFact
llGasitRand = FOUND()
ENDIF
IF !llGasitRand
GO TOP
lnIdFact = id_fact
ENDIF
lnIdSet = id_set
lnIdFactD = NVL(id_factd, 0)
STRTOFILE(' pozitionare actactan: gasit=' + TRANSFORM(llGasitRand) + ' id_fact=' + TRANSFORM(lnIdFact) + ;
' id_set=' + TRANSFORM(lnIdSet) + ' id_factd=' + TRANSFORM(lnIdFactD) + CHR(13) + CHR(10), tcLog, 1)
SELECT a.*, IIF(NVL(id_jtva_coloana, 0) = 0, 0, 1) AS cu_Tva FROM tact a INTO CURSOR tact READWRITE
*-- 6) TEST 2: modifica un camp simplu si verificabil (explicatia) INAINTE de salvare
lcExplicatieVeche = ''
lcExplicatieNoua = ''
IF tlModificaLinie
SELECT tact
LOCATE FOR !EMPTY(ALLTRIM(NVL(explicatia, '')))
IF FOUND()
lcExplicatieVeche = ALLTRIM(explicatia)
lcExplicatieNoua = lcExplicatieVeche + ' (test writeback)'
REPLACE explicatia WITH lcExplicatieNoua
STRTOFILE(' modific tact.explicatia: [' + lcExplicatieVeche + '] -> [' + lcExplicatieNoua + ']' + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' ATENTIE: nu am gasit niciun rand cu explicatia completata, testul nu modifica nimic real' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ENDIF
*-- 7) frm_modific2024 OCOLIT COMPLET (a blocat Createobject/Show pe dialog nativ "Open" -
*-- vezi antetul fisierului). buton=1 fortat direct - inainte_de_do_termin NU se executa.
buton = 1
STRTOFILE(' frm_modific2024 ocolit: buton=1 fortat direct, inainte_de_do_termin SARIT' + CHR(13) + CHR(10), tcLog, 1)
*-- 8) ramura buton=1 din do_editare_factura (ofacturare_comun.vc2:3805-3840), copiata identic;
*-- Thisform.do_deschide_tranzactie/do_inchide_tranzactie sunt reproduse INLINE mai jos
*-- (MyDeschideTranzactie/MyInchideTranzactie, dupa _frm_base.vc2:252-302) - fara sa instantieze
*-- niciun formular, ca sa nu riste acelasi blocaj vazut la frm_modific2024
lnSucces = 0
TRY
STRTOFILE(' [chk] inainte de MyDeschideTranzactie()' + CHR(13) + CHR(10), tcLog, 1)
IF MyDeschideTranzactie()
STRTOFILE(' [chk] MyDeschideTranzactie() => .T., inainte de OSCRIE_IN_FISIERE(2,...)' + CHR(13) + CHR(10), tcLog, 1)
SELECT actactan
lnSucces = OSCRIE_IN_FISIERE(2, .T., .T.)
STRTOFILE(' [chk] OSCRIE_IN_FISIERE(2,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
LogMockSiEroare('dupa OSCRIE_IN_FISIERE(2,...)', tcLog)
IF lnSucces > 0
IF USED('actactan')
USE IN actactan
ENDIF
SELECT tact
REPLACE id_jtva_coloana WITH NULL, proc_tva WITH 0 FOR cu_Tva = 0
SELECT * FROM tact INTO CURSOR actactan READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
IF USED('rul_temp')
USE IN rul_temp
ENDIF
SELECT * FROM trul INTO CURSOR RUL_TEMP READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
IF USED('rul_temp_obinv')
USE IN rul_temp_obinv
ENDIF
SELECT * FROM trul_obinv INTO CURSOR RUL_TEMP_OBINV READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
STRTOFILE(' [chk] cursoare rebuild OK, inainte de OSCRIE_IN_FISIERE(0,...)' + CHR(13) + CHR(10), tcLog, 1)
lnSucces = OSCRIE_IN_FISIERE(0, .T., .T.)
STRTOFILE(' [chk] OSCRIE_IN_FISIERE(0,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
LogMockSiEroare('dupa OSCRIE_IN_FISIERE(0,...)', tcLog)
ENDIF
IF lnSucces > 0
STRTOFILE(' [chk] inainte de pack_contafin.finalizeaza_modificare_nota' + CHR(13) + CHR(10), tcLog, 1)
lcSql = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,Null,] + ALLTRIM(STR(lnIdSet)) + [,] + ;
ALLTRIM(STR(lnCod)) + [,] + ALLTRIM(STR(lnIdFact)) + [,] + ALLTRIM(STR(lnIdFactD)) + [,?gnIdUtil); end;]
STRTOFILE(' [chk] SQL finalizeaza_modificare_nota = [' + lcSql + ']' + CHR(13) + CHR(10), tcLog, 1)
lnSucces = IIF(goExecutor.oExecuta(lcSql), 1, -1)
STRTOFILE(' [chk] finalizeaza_modificare_nota => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
LogMockSiEroare('dupa finalizeaza_modificare_nota', tcLog)
ENDIF
STRTOFILE(' lnSucces dupa lant scriere = ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
STRTOFILE(' [chk] inainte de MyInchideTranzactie' + CHR(13) + CHR(10), tcLog, 1)
IF MyInchideTranzactie(IIF(lnSucces < 0, 2, 1))
STRTOFILE(' tranzactie inchisa cu ' + IIF(lnSucces < 0, 'ROLLBACK', 'COMMIT') + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' FAIL la do_inchide_tranzactie' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ELSE
STRTOFILE(' FAIL: do_deschide_tranzactie a esuat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
CATCH TO loExc
STRTOFILE(' EXCEPTIE in lantul de scriere: ' + TRANSFORM(loExc.ErrorNo) + ' [' + loExc.Message + '] ' + ;
loExc.Procedure + ':' + TRANSFORM(loExc.LineNo) + ' -> ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
lnSucces = -99
MyInchideTranzactie(2)
ENDTRY
IF USED('actactan')
USE IN actactan
ENDIF
IF USED('tact')
USE IN tact
ENDIF
IF USED('rul_temp')
USE IN rul_temp
ENDIF
IF USED('trul')
USE IN trul
ENDIF
IF USED('rul_temp_obinv')
USE IN rul_temp_obinv
ENDIF
IF USED('trul_obinv')
USE IN trul_obinv
ENDIF
IF lnSucces <= 0
STRTOFILE(' TEST FAIL - scrierea nu s-a produs (lnSucces=' + TRANSFORM(lnSucces) + '), fara verificari after' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
*-- 9) instantaneu DUPA + verificarile 1-5 cerute
LOCAL lnCodNou, lnVzStersAfter, llV3ok
lcSql = [select cod, sters, id_fact, total_fara_tva, total_tva, total_cu_tva, discount from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecAgg(lcSql, 'v_vanzare_dupa')
IF lnQ <= 0 OR RECCOUNT('v_vanzare_dupa') <> 1
STRTOFILE(' FAIL: VANZARI dupa - nu am gasit exact 1 rand' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
SELECT v_vanzare_dupa
lnCodNou = cod
lnVzStersAfter = sters
llV3ok = (lnCodNou <> lnVzCodBefore) AND (lnVzStersAfter = 0) AND (id_fact == lnIdFact) AND ;
(ABS(total_fara_tva - lnVzTotalFaraTvaBefore) < 0.01) AND (ABS(total_tva - lnVzTotalTvaBefore) < 0.01) AND ;
(ABS(total_cu_tva - lnVzTotalCuTvaBefore) < 0.01) AND (ABS(discount - lnVzDiscountBefore) < 0.01)
STRTOFILE(' [3] VANZARI dupa: cod=' + TRANSFORM(lnCodNou) + ' (era ' + TRANSFORM(lnVzCodBefore) + ') sters=' + TRANSFORM(lnVzStersAfter) + ;
' id_fact=' + TRANSFORM(id_fact) + ' total_cu_tva=' + TRANSFORM(total_cu_tva) + ' -> ' + IIF(llV3ok, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
USE IN v_vanzare_dupa
*-- [1] ACT: randurile vechi STERS=1 pe cod vechi; randuri noi pe cod nou, acelasi id_fact, aceeasi suma (daca nemodificat)
LOCAL lnActVechiCnt, lnActVechiSumSters, lnActNouCnt, lnActNouSuma, llV1ok
lcSql = [select count(*) as cnt, sum(sters) as sumsters from vact_tot where an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnVzCodBefore)
lnQ = ExecAgg(lcSql, 'v_act_vechi_dupa')
IF lnQ > 0
lnActVechiCnt = v_act_vechi_dupa.cnt
lnActVechiSumSters = NVL(v_act_vechi_dupa.sumsters, 0)
USE IN v_act_vechi_dupa
ELSE
lnActVechiCnt = -1
lnActVechiSumSters = -1
ENDIF
lcSql = [select count(*) as cnt, sum(suma) as totsuma from vact_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCodNou)
lnQ = ExecAgg(lcSql, 'v_act_nou_dupa')
IF lnQ > 0
lnActNouCnt = v_act_nou_dupa.cnt
lnActNouSuma = NVL(v_act_nou_dupa.totsuma, 0)
USE IN v_act_nou_dupa
ELSE
lnActNouCnt = -1
lnActNouSuma = -1
ENDIF
llV1ok = (lnActVechiCnt = lnActBeforeCnt) AND (lnActVechiSumSters = lnActVechiCnt) AND (lnActNouCnt = lnActBeforeCnt) AND ;
(IIF(tlModificaLinie, .T., ABS(lnActNouSuma - lnActBeforeSuma) < 0.01))
STRTOFILE(' [1] ACT: vechi(cod=' + TRANSFORM(lnVzCodBefore) + ') cnt=' + TRANSFORM(lnActVechiCnt) + ' toate sters=' + TRANSFORM(lnActVechiSumSters = lnActVechiCnt) + ;
'; nou(cod=' + TRANSFORM(lnCodNou) + ') cnt=' + TRANSFORM(lnActNouCnt) + ' suma=' + TRANSFORM(lnActNouSuma) + ;
' (era ' + TRANSFORM(lnActBeforeSuma) + ') -> ' + IIF(llV1ok, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
*-- [2] RUL: acelasi tipar (daca documentul are randuri RUL)
LOCAL lnRulVechiCnt, lnRulVechiSumSters, lnRulNouCnt, llV2ok
lcSql = [select count(*) as cnt, sum(sters) as sumsters from vrul_tot where an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnVzCodBefore)
lnQ = ExecAgg(lcSql, 'v_rul_vechi_dupa')
IF lnQ > 0
lnRulVechiCnt = v_rul_vechi_dupa.cnt
lnRulVechiSumSters = NVL(v_rul_vechi_dupa.sumsters, 0)
USE IN v_rul_vechi_dupa
ELSE
lnRulVechiCnt = -1
lnRulVechiSumSters = -1
ENDIF
lcSql = [select count(*) as cnt from vrul_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCodNou)
lnQ = ExecAgg(lcSql, 'v_rul_nou_dupa')
IF lnQ > 0
lnRulNouCnt = v_rul_nou_dupa.cnt
USE IN v_rul_nou_dupa
ELSE
lnRulNouCnt = -1
ENDIF
llV2ok = (lnRulVechiCnt = lnRulBeforeCnt) AND (lnRulVechiSumSters = lnRulVechiCnt) AND (lnRulNouCnt = lnRulBeforeCnt)
STRTOFILE(' [2] RUL: vechi cnt=' + TRANSFORM(lnRulVechiCnt) + ' toate sters=' + TRANSFORM(lnRulVechiSumSters = lnRulVechiCnt) + ;
'; nou cnt=' + TRANSFORM(lnRulNouCnt) + ' (era ' + TRANSFORM(lnRulBeforeCnt) + ') -> ' + IIF(llV2ok, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
*-- [4] VANZARI_DETALII: neatins de aceasta cale
LOCAL lnVdCntDupa, lnVdSumaPretDupa, lnVdSumaPretCuTvaDupa, llV4ok
lcSql = [select count(*) as cnt, sum(cantitate) as sumcant, sum(pret) as sumpret, sum(pret_cu_tva) as sumpretcutva ] + ;
[from vanzari_detalii where sters = 0 and id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecAgg(lcSql, 'v_vd_dupa')
IF lnQ > 0
lnVdCntDupa = v_vd_dupa.cnt
lnVdSumaPretDupa = NVL(v_vd_dupa.sumpret, 0)
lnVdSumaPretCuTvaDupa = NVL(v_vd_dupa.sumpretcutva, 0)
USE IN v_vd_dupa
ELSE
lnVdCntDupa = -1
lnVdSumaPretDupa = -1
lnVdSumaPretCuTvaDupa = -1
ENDIF
llV4ok = (lnVdCntDupa = lnVdCnt) AND (ABS(lnVdSumaPretDupa - lnVdSumaPret) < 0.01) AND (ABS(lnVdSumaPretCuTvaDupa - lnVdSumaPretCuTva) < 0.01)
STRTOFILE(' [4] VANZARI_DETALII: cnt=' + TRANSFORM(lnVdCntDupa) + ' (era ' + TRANSFORM(lnVdCnt) + ') sum(pret)=' + TRANSFORM(lnVdSumaPretDupa) + ;
' (era ' + TRANSFORM(lnVdSumaPret) + ') -> ' + IIF(llV4ok, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
*-- [5] lnSucces>0 pe tot lantul + tranzactia inchisa cu commit (deja logat mai sus, sintetizat aici)
STRTOFILE(' [5] lnSucces final = ' + TRANSFORM(lnSucces) + ' -> ' + IIF(lnSucces > 0, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
*-- verificare suplimentara TEST 2: explicatia modificata ajunge efectiv in ACT pe randul nou
IF tlModificaLinie AND !EMPTY(lcExplicatieNoua)
LOCAL lnExplicatieCnt
lcSql = [select count(*) as cnt from vact_tot where sters = 0 and an = ] + TRANSFORM(pnAn) + ;
[ and luna = ] + TRANSFORM(pnLuna) + [ and cod = ] + TRANSFORM(lnCodNou) + ;
[ and explicatia = '] + STRTRAN(lcExplicatieNoua, "'", "''") + [']
lnQ = ExecAgg(lcSql, 'v_act_explicatie')
IF lnQ > 0
lnExplicatieCnt = v_act_explicatie.cnt
USE IN v_act_explicatie
ELSE
lnExplicatieCnt = -1
ENDIF
STRTOFILE(' [2b] explicatia modificata ajunge in ACT: cnt=' + TRANSFORM(lnExplicatieCnt) + ' -> ' + IIF(lnExplicatieCnt = 1, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)
ENDIF
STRTOFILE(' REZUMAT ' + tcEticheta + ': cod ' + TRANSFORM(lnVzCodBefore) + ' -> ' + TRANSFORM(lnCodNou) + CHR(13) + CHR(10), tcLog, 1)
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC

View File

@@ -0,0 +1,14 @@
* watchdog_selftest.prg - caz banal pentru validarea watchdog_vfp.ps1: afiseaza un MESSAGEBOX
* nativ (OK/Cancel) si asteapta. Foloseste doar pentru testarea utilitarului, nu face parte
* din nicio suita de regresie.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_selftest_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), lcLog)
= MESSAGEBOX('Mesaj de test watchdog_vfp.ps1', 1, 'Watchdog selftest')
STRTOFILE('done' + CHR(13) + CHR(10), lcLog, 1)
QUIT

View File

@@ -0,0 +1,390 @@
# watchdog_vfp.ps1 - lanseaza un test VFP headless si detecteaza dialoguri native blocante
# (mesaje Windows, "View Parameter", erori modale) care nu apar in logul scris de script si
# nu sunt prinse de ON ERROR / mock-uri - cazul clasic de proces "agatat" cu CPU~0.
#
# Ce face: sterge .fxp-ul vechi, lanseaza `vfp9.exe -A -T "<script>"`, apoi polleaza la interval
# scurt ferestrele TOP-LEVEL ale procesului (EnumWindows + GetWindowThreadProcessId). Fereastra
# principala VFP se identifica prin Process.MainWindowHandle (.NET) - NU dupa numele clasei:
# VFP inregistreaza clase diferite prefixate "vfp9..." atat pentru shell-ul principal cat si
# pentru dialogurile proprii (ex. "View Parameter"), deci clasificarea pe clasa e nesigura.
# Orice fereastra NOUA, diferita de MainWindowHandle, e tratata ca dialog blocant.
#
# Pentru fiecare dialog detectat: captura PNG (PrintWindow, fallback CopyFromScreen daca iese
# neagra) + dump text (titlu, clasa, si textul/clasa fiecarui control copil via WM_GETTEXT - se
# citesc controale native Windows chiar daca apartin unui proces strain pe alta arhitectura).
#
# INTERZIS: watchdog-ul NU are voie sa foloseasca input real de tastatura/mouse (keybd_event,
# SetForegroundWindow, SendInput, mouse_event) - masina e PARTAJATA cu utilizatorul, iar un
# ESCAPE/click injectat la nivel de sistem ajunge in orice fereastra are focus in acel moment,
# nu neaparat in dialogul tintit (verificat: a ajuns in IDE-ul VFP al utilizatorului aflat in
# lucru in paralel, intrerupandu-l). -AutoDismiss foloseste STRICT mesaje Windows tintite pe
# handle-ul dialogului: buton copil cu text Cancel/Anulare/Renunta -> BM_CLICK; altfel
# WM_COMMAND IDCANCEL -> ESCAPE ca mesaj (WM_KEYDOWN/WM_KEYUP, nu input real) -> WM_CLOSE, in
# ordine, primul care inchide fereastra castiga. Dialogurile proprii VFP owner-drawn (ex.
# "View Parameter" - butoanele sunt desenate, nu HWND-uri reale) pot sa NU raspunda la niciunul -
# atunci dismiss-ul ESUEAZA CINSTIT (ramane deschis pana la timeout), nu se cade pe input real.
# Captura (PNG+dump text) e intotdeauna de incredere, indiferent de rezultatul dismiss-ului.
# Continua polling-ul dupa fiecare dismiss reusit, ca sa prinda si eventuale dialoguri urmatoare
# (pana la -MaxDialogs). Fara -AutoDismiss: captureaza primul dialog gasit, apoi opreste
# polling-ul (nu-l atinge).
#
# Procesul e omorat mereu la iesire (finally) - nu ramane niciodata viu.
#
# Utilizare:
# powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "D:\...\test_x.prg" -AutoDismiss
# powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "D:\...\test_x.prg" -TimeoutSec 60 -OutDir "D:\...\out"
#
# Parametri:
# -Script cale completa la .prg de rulat (obligatoriu)
# -Vfp cale la vfp9.exe (implicit instalarea standard x86)
# -TimeoutSec timeout total de polling (implicit 90)
# -OutDir folder pentru PNG-uri + log text (implicit "<folderul scriptului>\watchdog_out")
# -AutoDismiss inchide automat dialogurile gasite (Cancel/WM_CLOSE) si continua polling-ul
# -MaxDialogs cate dialoguri succesive sa trateze cu -AutoDismiss (implicit 10)
#
# Iesire: rezumat pe stdout (numar dialoguri, textul fiecaruia, caile PNG, exit code-ul
# procesului) + acelasi continut scris in "<Script>_watchdog.log" din -OutDir.
param(
[Parameter(Mandatory=$true)][string]$Script,
[string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe',
[int]$TimeoutSec = 90,
[string]$OutDir,
[switch]$AutoDismiss,
[int]$MaxDialogs = 10
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path $Script)) { throw "Nu gasesc scriptul: $Script" }
$Script = (Resolve-Path $Script).Path
$ScriptDir = [System.IO.Path]::GetDirectoryName($Script)
$ScriptBase = [System.IO.Path]::GetFileNameWithoutExtension($Script)
if (-not $OutDir) { $OutDir = Join-Path $ScriptDir 'watchdog_out' }
if (-not (Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null }
$LogPath = Join-Path $OutDir ($ScriptBase + '_watchdog.log')
Add-Type -AssemblyName System.Drawing
Add-Type @"
using System;
using System.Text;
using System.Runtime.InteropServices;
public class WdWin32 {
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
public static extern IntPtr SendMessageText(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left, Top, Right, Bottom; }
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, uint nFlags);
}
"@
$WM_GETTEXT = 0x000D
$WM_CLOSE = 0x0010
$BM_CLICK = 0x00F5
function Get-TopWindowsForPid([int]$ProcId) {
$result = New-Object System.Collections.Generic.List[IntPtr]
$cb = {
param($hWnd, $lParam)
$owner = 0
[void][WdWin32]::GetWindowThreadProcessId($hWnd, [ref]$owner)
if ($owner -eq $ProcId -and [WdWin32]::IsWindowVisible($hWnd)) { $result.Add($hWnd) }
return $true
}
[WdWin32]::EnumWindows($cb, [IntPtr]::Zero) | Out-Null
return $result
}
function Get-ChildWindowsOf([IntPtr]$hWndParent) {
$result = New-Object System.Collections.Generic.List[IntPtr]
$cb = {
param($hWnd, $lParam)
$result.Add($hWnd)
return $true
}
[WdWin32]::EnumChildWindows($hWndParent, $cb, [IntPtr]::Zero) | Out-Null
return $result
}
# WM_GETTEXT (nu GetWindowText) - citeste text de la controale ale unui proces strain, indiferent
# de arhitectura (32/64 bit); GetWindowText poate esua tacut pe controale custom VFP.
function Get-WmText([IntPtr]$hWnd, [int]$MaxLen = 1024) {
$sb = New-Object System.Text.StringBuilder $MaxLen
[WdWin32]::SendMessageText($hWnd, $WM_GETTEXT, $MaxLen, $sb) | Out-Null
return $sb.ToString()
}
function Get-WinClassName([IntPtr]$hWnd) {
$sb = New-Object System.Text.StringBuilder 256
[WdWin32]::GetClassName($hWnd, $sb, $sb.Capacity) | Out-Null
return $sb.ToString()
}
function Test-BitmapAllBlack([System.Drawing.Bitmap]$bmp) {
$w = $bmp.Width; $h = $bmp.Height
if ($w -le 0 -or $h -le 0) { return $true }
for ($i = 0; $i -lt 9; $i++) {
$x = [Math]::Min($w - 1, [int]($w * (($i % 3) + 0.5) / 3))
$y = [Math]::Min($h - 1, [int]($h * ([Math]::Floor($i / 3) + 0.5) / 3))
$px = $bmp.GetPixel($x, $y)
if ($px.R -ne 0 -or $px.G -ne 0 -or $px.B -ne 0) { return $false }
}
return $true
}
# PrintWindow direct pe handle; fallback CopyFromScreen daca iese neagra (fereastra themed/GDI+
# care nu se randeaza corect prin PrintWindow). Foloseste doar pentru dialoguri surprinse
# on-screen - nu se muta off-screen aici (spre deosebire de vfp_ui_harness.ps1), fiindca scopul
# e sa surprindem exact dialogul care blocheaza, o singura data.
function Save-DialogScreenshot([IntPtr]$hWnd, [string]$Path) {
$rect = New-Object WdWin32+RECT
[WdWin32]::GetWindowRect($hWnd, [ref]$rect) | Out-Null
$w = $rect.Right - $rect.Left
$h = $rect.Bottom - $rect.Top
if ($w -le 0 -or $h -le 0) { return $false }
$bmp = New-Object System.Drawing.Bitmap $w, $h
$g = [System.Drawing.Graphics]::FromImage($bmp)
$hdc = $g.GetHdc()
$ok = [WdWin32]::PrintWindow($hWnd, $hdc, 2)
$g.ReleaseHdc($hdc)
$g.Dispose()
if ((-not $ok) -or (Test-BitmapAllBlack $bmp)) {
$bmp.Dispose()
$bmp = New-Object System.Drawing.Bitmap $w, $h
$g2 = [System.Drawing.Graphics]::FromImage($bmp)
try { $g2.CopyFromScreen($rect.Left, $rect.Top, 0, 0, (New-Object System.Drawing.Size $w, $h)) } catch {}
$g2.Dispose()
}
$bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
$bmp.Dispose()
return $true
}
function Get-DialogDump([IntPtr]$hWnd, [System.Collections.Generic.List[IntPtr]]$children) {
$title = Get-WmText $hWnd
$class = Get-WinClassName $hWnd
$lines = New-Object System.Collections.Generic.List[string]
$lines.Add("dialog hwnd=$hWnd class='$class' title='$title'")
foreach ($c in $children) {
$ct = (Get-WmText $c 512).Trim()
$cc = Get-WinClassName $c
if ($ct -or $cc) { $lines.Add(" child hwnd=$c class='$cc' text='$ct'") }
}
# unary comma obligatoriu: fara ea, un List[string] cu UN SINGUR element (dialog fara copii,
# ex. "View Parameter") e "descompus" de PowerShell la scalar string simplu la return, iar
# indexarea ulterioara $dumpLines[0] citeste primul CARACTER, nu primul rand - prins la testul
# pe test_page3_articole.prg (rezumatul arata titlul "d" in loc de linia intreaga)
return , $lines.ToArray()
}
function Find-DismissButton([System.Collections.Generic.List[IntPtr]]$children) {
foreach ($c in $children) {
if ((Get-WinClassName $c) -ne 'Button') { continue }
$txt = ((Get-WmText $c 256) -replace '&', '').Trim()
if ($txt -ieq 'Cancel' -or $txt -ieq 'Anulare' -or $txt -ieq 'Renunta' -or $txt -ieq 'Renunță') { return $c }
}
return [IntPtr]::Zero
}
$WM_COMMAND = 0x0111
$WM_KEYDOWN = 0x0100
$WM_KEYUP = 0x0101
$VK_ESCAPE = 0x1B
$IDCANCEL = 2
# INTERZIS: input real de tastatura/mouse (keybd_event, SetForegroundWindow, SendInput,
# mouse_event). Masina e PARTAJATA cu utilizatorul - un ESCAPE injectat la nivel de sistem
# ajunge in orice fereastra are focus in acel moment, INDIFERENT carui proces ii apartine
# (verificat: a ajuns in IDE-ul VFP al utilizatorului, aflat in lucru in paralel, intrerupandu-l
# cu o eroare + "Execution was canceled by the user"). Foloseste STRICT mesaje Windows tintite pe
# HANDLE-ul dialogului (PostMessage/SendMessage: WM_COMMAND IDCANCEL, WM_KEYDOWN/WM_KEYUP
# VK_ESCAPE, WM_CLOSE, BM_CLICK) - astea nu ating focusul/inputul global, raman izolate la
# fereastra tintita. Dialogurile native VFP proprii (ex. "View Parameter") NU au controale copil
# reale (owner-drawn) si pot sa NU raspunda la niciunul dintre mesajele de mai jos - in acel caz
# dismiss-ul ESUEAZA CINSTIT (dialogul ramane deschis, procesul e omorat la timeout de finally),
# nu se cade pe input real. Captura (PNG+dump text) ramane oricum de incredere, indiferent de
# rezultatul dismiss-ului.
function Dismiss-Dialog([IntPtr]$hWnd, [System.Collections.Generic.List[IntPtr]]$children) {
$btn = Find-DismissButton $children
if ($btn -ne [IntPtr]::Zero) {
[WdWin32]::SendMessage($btn, $BM_CLICK, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null
Start-Sleep -Milliseconds 300
if (-not [WdWin32]::IsWindow($hWnd)) { return 'click Cancel/Anulare' }
return 'NEREUSIT: BM_CLICK pe Cancel/Anulare nu a inchis dialogul'
}
[WdWin32]::SendMessage($hWnd, $WM_COMMAND, [IntPtr]$IDCANCEL, [IntPtr]::Zero) | Out-Null
Start-Sleep -Milliseconds 300
if (-not [WdWin32]::IsWindow($hWnd)) { return 'WM_COMMAND IDCANCEL' }
[WdWin32]::SendMessage($hWnd, $WM_KEYDOWN, [IntPtr]$VK_ESCAPE, [IntPtr]::Zero) | Out-Null
[WdWin32]::SendMessage($hWnd, $WM_KEYUP, [IntPtr]$VK_ESCAPE, [IntPtr]::Zero) | Out-Null
Start-Sleep -Milliseconds 300
if (-not [WdWin32]::IsWindow($hWnd)) { return 'tasta ESCAPE trimisa cu SendMessage (mesaj, nu input real)' }
[WdWin32]::SendMessage($hWnd, $WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null
Start-Sleep -Milliseconds 300
if (-not [WdWin32]::IsWindow($hWnd)) { return 'WM_CLOSE' }
$motiv = if ($children.Count -eq 0) { 'fara controale copil (owner-drawn) si niciun mesaj tintit nu a functionat' } else { 'niciun mesaj tintit (WM_COMMAND/ESCAPE mesaj/WM_CLOSE) nu a functionat' }
return "NEREUSIT: dialogul a ramas deschis ($motiv) - fara input real, per regula masinii partajate"
}
# omoara doar vfp9 lansate pe ACEST script (linia de comanda contine calea lui), nu instante straine
function Stop-VfpForScript([string]$ScriptPath) {
Get-CimInstance Win32_Process -Filter "Name='vfp9.exe'" -ErrorAction SilentlyContinue | ForEach-Object {
if ($_.CommandLine -and $_.CommandLine.ToLower().Contains($ScriptPath.ToLower())) {
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
}
}
}
# --- pregatire ---
$Fxp = [System.IO.Path]::ChangeExtension($Script, 'fxp')
if (Test-Path $Fxp) { Remove-Item $Fxp -Force }
Stop-VfpForScript $Script
Start-Sleep -Milliseconds 300
$log = New-Object System.Collections.Generic.List[string]
function Log([string]$s) { Write-Host $s; $log.Add($s) }
Log "watchdog_vfp: lansez $Vfp -A -T `"$Script`""
$proc = Start-Process -FilePath $Vfp -ArgumentList @('-A', '-T', "`"$Script`"") -PassThru
$dialogs = New-Object System.Collections.Generic.List[object]
$exitCode = $null
try {
# Fereastra principala = Process.MainWindowHandle (.NET), latch-uita o singura data. NU se
# poate distinge dupa numele clasei: VFP inregistreaza clase diferite prefixate "vfp9..." atat
# pentru shell-ul principal ("vfp99400000") CAT SI pentru dialoguri native proprii precum
# "View Parameter" ("vfp994000002") - verificat empiric, clasificarea pe clasa a lasat
# "View Parameter" nedetectat. MainWindowHandle e acelasi mecanism folosit deja cu succes in
# vfp_ui_harness.ps1 (Wait-MainWindowHandle) pentru shell-ul VFP.
$mainHwnd = [IntPtr]::Zero
$mwDeadline = (Get-Date).AddSeconds(20)
while ($mainHwnd -eq [IntPtr]::Zero -and (Get-Date) -lt $mwDeadline) {
$proc.Refresh()
if ($proc.HasExited) { break }
if ($proc.MainWindowHandle -ne [IntPtr]::Zero) { $mainHwnd = $proc.MainWindowHandle }
else { Start-Sleep -Milliseconds 200 }
}
if ($mainHwnd -ne [IntPtr]::Zero) {
Log ("fereastra principala VFP: hwnd={0} class='{1}' title='{2}'" -f $mainHwnd, (Get-WinClassName $mainHwnd), (Get-WmText $mainHwnd))
} else {
Log "ATENTIE: MainWindowHandle nu a aparut in 20s - orice fereastra gasita va fi tratata ca dialog."
}
$knownWindows = New-Object System.Collections.Generic.HashSet[IntPtr]
if ($mainHwnd -ne [IntPtr]::Zero) { [void]$knownWindows.Add($mainHwnd) }
$deadline = (Get-Date).AddSeconds($TimeoutSec)
$stop = $false
while (-not $stop -and (Get-Date) -lt $deadline) {
$proc.Refresh()
if ($proc.HasExited) { Log "proces terminat singur (exit code $($proc.ExitCode))"; break }
# daca MainWindowHandle inca nu era prins, incearca din nou (fereastra principala poate
# aparea mai tarziu decat asteptarea initiala de 20s in cazuri lente)
if ($mainHwnd -eq [IntPtr]::Zero -and $proc.MainWindowHandle -ne [IntPtr]::Zero) {
$mainHwnd = $proc.MainWindowHandle
[void]$knownWindows.Add($mainHwnd)
Log ("fereastra principala VFP (intarziata): hwnd={0} class='{1}' title='{2}'" -f $mainHwnd, (Get-WinClassName $mainHwnd), (Get-WmText $mainHwnd))
}
$windows = Get-TopWindowsForPid $proc.Id
foreach ($w in $windows) {
if ($knownWindows.Contains($w)) { continue }
[void]$knownWindows.Add($w)
if ($w -eq $mainHwnd) { continue }
$children = Get-ChildWindowsOf $w
$dumpLines = Get-DialogDump $w $children
$idx = $dialogs.Count
$png = Join-Path $OutDir ("{0}_dialog{1}.png" -f $ScriptBase, $idx)
Save-DialogScreenshot $w $png | Out-Null
Log "=== DIALOG $idx detectat ==="
foreach ($l in $dumpLines) { Log $l }
Log " screenshot: $png"
$dismissMethod = $null
if ($AutoDismiss) {
$dismissMethod = Dismiss-Dialog $w $children
Log " dismiss: $dismissMethod"
}
$dialogs.Add([pscustomobject]@{
Index = $idx
Title = ($dumpLines[0])
Png = $png
Dismiss = $dismissMethod
DumpText = ($dumpLines -join "`r`n")
})
if ($dialogs.Count -ge $MaxDialogs) { Log "MaxDialogs ($MaxDialogs) atins."; $stop = $true; break }
if (-not $AutoDismiss) { Log "AutoDismiss oprit - opresc polling-ul dupa primul dialog."; $stop = $true; break }
}
if (-not $stop) { Start-Sleep -Milliseconds 700 }
}
if (-not $proc.HasExited -and (Get-Date) -ge $deadline) {
Log "TIMEOUT (${TimeoutSec}s) - procesul e inca viu, il opresc."
}
$proc.Refresh()
if ($proc.HasExited) { $exitCode = $proc.ExitCode }
}
finally {
if ($proc -and -not $proc.HasExited) {
try { $proc.Kill() } catch {}
}
Stop-VfpForScript $Script
}
# --- rezumat ---
Log ""
Log "=== REZUMAT ==="
Log ("dialoguri detectate: {0}" -f $dialogs.Count)
foreach ($d in $dialogs) {
Log (" [{0}] {1}" -f $d.Index, $d.Title)
Log (" png: {0}" -f $d.Png)
if ($d.Dismiss) { Log (" dismiss: {0}" -f $d.Dismiss) }
}
Log ("exit code proces: {0}" -f ($(if ($null -ne $exitCode) { $exitCode } else { '(omorat de watchdog, nu s-a terminat singur)' })))
Log ("log complet: {0}" -f $LogPath)
[System.IO.File]::WriteAllLines($LogPath, $log)