#6 editare factura emisa: S5 - scrierea sumelor editate in Oracle
La salvarea unei facturi deja emise, modificarile facute liniilor de articole se scriu acum real in VANZARI_DETALII, in aceeasi tranzactie cu restul salvarii, si totalurile documentului se recalculeaza din liniile efectiv salvate. - ofacturare_editare.prg: helper nou ScrieArticoleFacturaEditate. Idiomul e "marcheaza tot sters, invie ce ramane", nu delta, pentru ca actualizeaza_vanzari face UPDATE ... SET STERS = 0 pe tot documentul, fara garda; o scriere delta ar lasa liniile sterse sa reinvie la fiecare salvare. Cursor gol = no-op, ca o eroare la incarcare sa nu goleasca factura. - comun.vc2, ofacturare_comun.vc2: agatarea apelului dupa finalizeaza_modificare_nota si inainte de inchiderea tranzactiei, in ambele puncte de intrare. - omodificari.vc2: coloana Pret achizitie in grid, editabila doar pe liniile noi; liniile de set devin needitabile, cu marcaj; cinci validari la salvare. - teste: cinci suite noi (validari, grid UI, scriere reala, discount/valuta, rollback, al doilea punct de intrare) si asteptari actualizate in test_page3_articole. - docs: oracle_export.md - exportul all_source cere linesize 32767, altfel rupe liniile lungi prin mijlocul identificatorilor; scripturi-migrare-db.md - UpdateVersiune adauga singur extensia .sql. Cere scripturile ff_2026_08_09_01 (PACK_FACTURARE) si ff_2026_08_09_02 (VVANZARI_ARTICOLE). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
588
utile/Teste/editare_factura/test_s5_validari_articole.prg
Normal file
588
utile/Teste/editare_factura/test_s5_validari_articole.prg
Normal file
@@ -0,0 +1,588 @@
|
||||
* test_s5_validari_articole.prg
|
||||
* Acopera S5 (scrierea sumelor editate in Oracle - decizii 38-41, docs\handoff_s5.md), functionalitate
|
||||
* NETESTATA la data scrierii acestui fisier (niciun test nu atingea inca pret_achizitie/id_vanzare_set
|
||||
* sau helperul de scriere). Doua sectiuni independente:
|
||||
*
|
||||
* A) Cele 5 validari noi din frm_modific2024.inainte_de_do_termin (omodificari.vc2:14328-14366),
|
||||
* apelate DIRECT pe o instanta reala (nu prin UI): cantitate<=0, pret NULL, id_articol nul/0
|
||||
* blocheaza (RETURN .F.); zero linii active cu document in VANZARI si linie noua cu pret_achizitie
|
||||
* 0/NULL cer CONFIRMARE (amessagebox 4+32), nu blocare - ambele ramuri (Da continua, Nu opreste)
|
||||
* sunt verificate, plus un caz de control (cursor valid, nicio validare nu se declanseaza).
|
||||
* Documentul e real (FACTURA_ARTICOLE, descoperit prin proprietate - descopera_caz_test.prg),
|
||||
* tvd e mutat manual intre stari valide/invalide si restaurat dupa fiecare sub-caz.
|
||||
*
|
||||
* B) Garda de no-op si SQL-ul generat de ScrieArticoleFacturaEditate (ofacturare_editare.prg) -
|
||||
* complet headless, PE CURSOARE CONSTRUITE IN TEST (crstvdtest/crstvanztest, structura reala din
|
||||
* CreeazaCursorArticoleGol), FARA Oracle: goExecutor e inlocuit cu un mock (dummyexecutor) care
|
||||
* doar inregistreaza textul comenzilor intr-un cursor si intoarce o valoare configurabila -
|
||||
* goExecutor real (conectat la MARIUSM_AUTO) ramane neatins tot timpul sectiunii B.
|
||||
* Garda de no-op: tnIdVanzare<=0, alias articole neinchis, alias vanzare fara exact 1 rand,
|
||||
* Reccount(alias articole)=0 (cazul critic - un esec de incarcare Oracle NU trebuie sa goleasca
|
||||
* factura, vezi handoff_s5.md "Cale de pierdere de date, inchisa") - toate patru trebuie sa intoarca
|
||||
* .T. FARA sa apeleze goExecutor.oExecuta vreodata.
|
||||
* Clasificarea liniilor (pastrata/noua/noua-si-stearsa/stearsa/componenta de set) e verificata prin
|
||||
* SQL-ul efectiv generat de helper pe un cursor cu cate un rand din fiecare categorie.
|
||||
* Oprirea la primul esec Oracle e verificata explicit (mock configurat sa esueze la un apel anume).
|
||||
*
|
||||
* Nu scrie in Oracle (mock pe goExecutor in sectiunea B; sectiunea A nu apeleaza deloc scriere -
|
||||
* inainte_de_do_termin e doar validare). Nu da COMMIT. QUIT la final.
|
||||
|
||||
SET SAFETY OFF
|
||||
SET TALK OFF
|
||||
|
||||
LOCAL lcLog
|
||||
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s5_validari_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()
|
||||
|
||||
*!* mock amessagebox INCARCAT PRIMUL (testare-ui-vfp.md, capcana g - verificat pe teren in
|
||||
*!* test_writeback_buton1.prg, acelasi folder): la nume duplicat de procedura VFP foloseste
|
||||
*!* fisierul cautat PRIMUL, deci trebuie deschis inaintea lantului din test_init_env_auto
|
||||
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
|
||||
|
||||
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
|
||||
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\descopera_caz_test.prg ADDITIVE
|
||||
|
||||
*!* test_init_env_auto incarca clasa omodificari din working copy-ul ROACONT (alt SET PATH) -
|
||||
*!* o eliberam si o reincarcam pe cea DE SUB TEST, cu cale completa
|
||||
RELEASE CLASSLIB omodificari
|
||||
SET CLASSLIB TO D:\ROA\ROAFACTURARE\COMUN\clase\omodificari.vcx ADDITIVE
|
||||
|
||||
PUBLIC gnPass, gnFail
|
||||
gnPass = 0
|
||||
gnFail = 0
|
||||
|
||||
PUBLIC gnMockRaspuns, gcMockUltimMesaj, gnMockUltimTip
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
gnMockUltimTip = 0
|
||||
|
||||
LOCAL lnVerifMock
|
||||
lnVerifMock = amessagebox('verificare mock', 4+32, 'test')
|
||||
DO asserteaza WITH 'mock amessagebox activ (umbreste amessagebox real)', lnVerifMock == 6, TRANSFORM(lnVerifMock)
|
||||
|
||||
|
||||
*============================================================================================
|
||||
* SECTIUNEA A - validarile din inainte_de_do_termin, pe o instanta reala frm_modific2024
|
||||
*============================================================================================
|
||||
|
||||
LOCAL llCazA
|
||||
llCazA = DescoperaCazTest('FACTURA_ARTICOLE', 'crscaz_a')
|
||||
IF !llCazA
|
||||
STRTOFILE('EROARE: cazul FACTURA_ARTICOLE nu a fost gasit' + CHR(13) + CHR(10), lcLog, 1)
|
||||
STRTOFILE('REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
|
||||
QUIT
|
||||
ENDIF
|
||||
STRTOFILE('caz A: cod=' + TRANSFORM(crscaz_a.cod) + ' an=' + TRANSFORM(crscaz_a.an) + ' luna=' + TRANSFORM(crscaz_a.luna) + ;
|
||||
' id_vanzare=' + TRANSFORM(crscaz_a.id_vanzare) + CHR(13) + CHR(10), lcLog, 1)
|
||||
|
||||
LOCAL llOk, lnIdSet, loForm
|
||||
|
||||
llOk = IncarcaCursoareModificareNota(crscaz_a.cod, crscaz_a.an, crscaz_a.luna, .F.)
|
||||
IF !llOk OR !Used('tact') OR Reccount('tact') = 0
|
||||
STRTOFILE('EROARE: IncarcaCursoareModificareNota a esuat pe caz A' + CHR(13) + CHR(10), lcLog, 1)
|
||||
STRTOFILE('REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
|
||||
QUIT
|
||||
ENDIF
|
||||
|
||||
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()
|
||||
|
||||
DO asserteaza WITH 'formularul are articole (lAreArticoleVanzari)', loForm.lAreArticoleVanzari, TRANSFORM(loForm.lAreArticoleVanzari)
|
||||
DO asserteaza WITH 'conditie initiala: tvd are cel putin o linie activa', Used('tvd') AND Reccount('tvd') > 0, TRANSFORM(Reccount('tvd'))
|
||||
|
||||
IF !Used('tvd') OR Reccount('tvd') = 0
|
||||
STRTOFILE('EROARE: tvd fara linii, sectiunea A sarita' + CHR(13) + CHR(10), lcLog, 1)
|
||||
ELSE
|
||||
*!* --- A1: caz de control - cursorul incarcat din Oracle e valid, nicio validare nu trebuie sa se declanseze ---
|
||||
LOCAL llRet1
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
llRet1 = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'caz de control: document real valid -> inainte_de_do_termin() = .T., nicio validare S5 declansata', ;
|
||||
llRet1 == .T. AND !('cantitatea' $ Lower(gcMockUltimMesaj)) AND !('pret' $ Lower(gcMockUltimMesaj)) AND !('articol' $ Lower(gcMockUltimMesaj)), ;
|
||||
'llRet=' + TRANSFORM(llRet1) + ' ultimMesaj=[' + gcMockUltimMesaj + ']'
|
||||
|
||||
*!* --- A2: cantitate <= 0 pe o linie activa -> blocheaza ---
|
||||
LOCAL lnOrigCant, llRet2
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
lnOrigCant = tvd.cantitate
|
||||
REPLACE cantitate WITH 0 IN tvd
|
||||
gcMockUltimMesaj = ''
|
||||
llRet2 = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'cantitate<=0 pe linie activa -> blocheaza (RETURN .F.)', llRet2 == .F., TRANSFORM(llRet2)
|
||||
DO asserteaza WITH 'cantitate<=0 -> mesajul mentioneaza cantitatea', 'cantitatea' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
REPLACE cantitate WITH lnOrigCant IN tvd
|
||||
|
||||
*!* --- A3: pret NULL pe o linie activa -> ar trebui sa blocheze, DAR verificat empiric ca
|
||||
*!* tvd.pret (populat de IncarcaArticoleFactura din VVANZARI_ARTICOLE, singura cale reala cand
|
||||
*!* ofacturare_editare.prg e incarcat) e structural NOT NULL - REPLACE ... WITH .NULL. arunca
|
||||
*!* eroarea VFP 1581 "Field PRET does not accept null values", nu doar pe liniile persistate
|
||||
*!* (VANZARI_DETALII.PRET e NOT NULL in Oracle - coloana provine din view), ci si pe o linie
|
||||
*!* noua adaugata prin APPEND BLANK pe ACELASI cursor (restrictia e la nivel de coloana, nu de
|
||||
*!* rand). Concluzie: validarea Isnull(pret) din inainte_de_do_termin (omodificari.vc2:14340)
|
||||
*!* e cod NEATINGIBIL pe fluxul real - nu poate fi provocata nici pe linii existente, nici pe
|
||||
*!* linii noi construite prin fluxul aplicatiei. Se raporteaza, nu se repara (fara UI, cod
|
||||
*!* de productie interzis pentru acest agent) - marcat FAIL asteptat, ca sa nu se numere ca regresie.
|
||||
LOCAL lnOrigPret, llRet3, llPretNullPosibil
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
lnOrigPret = tvd.pret
|
||||
llPretNullPosibil = .T.
|
||||
TRY
|
||||
REPLACE pret WITH .NULL. IN tvd
|
||||
CATCH
|
||||
llPretNullPosibil = .F.
|
||||
ENDTRY
|
||||
IF llPretNullPosibil
|
||||
gcMockUltimMesaj = ''
|
||||
llRet3 = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'pret NULL pe linie activa -> blocheaza (RETURN .F.)', llRet3 == .F., TRANSFORM(llRet3)
|
||||
DO asserteaza WITH 'pret NULL -> mesajul mentioneaza pretul', 'pret' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
REPLACE pret WITH lnOrigPret IN tvd
|
||||
ELSE
|
||||
STRTOFILE('FAIL asteptat pret NULL pe linie activa: tvd.pret e structural NOT NULL (eroare VFP 1581 la REPLACE) - ' + ;
|
||||
'validarea Isnull(pret) din inainte_de_do_termin e neatingibila pe fluxul real, de raportat echipei' + CHR(13) + CHR(10), gcErrLog, 1)
|
||||
ENDIF
|
||||
|
||||
*!* --- A4: id_articol nul sau 0 pe o linie activa -> blocheaza ---
|
||||
LOCAL lnOrigIdArt, llRet4
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
lnOrigIdArt = tvd.id_articol
|
||||
REPLACE id_articol WITH 0 IN tvd
|
||||
gcMockUltimMesaj = ''
|
||||
llRet4 = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'id_articol=0 pe linie activa -> blocheaza (RETURN .F.)', llRet4 == .F., TRANSFORM(llRet4)
|
||||
DO asserteaza WITH 'id_articol=0 -> mesajul mentioneaza articolul', 'articol' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
SELECT tvd
|
||||
GO TOP
|
||||
REPLACE id_articol WITH lnOrigIdArt IN tvd
|
||||
|
||||
*!* --- A5: zero linii active, document cu rand in tvanz -> CONFIRMARE, nu blocare (ambele ramuri) ---
|
||||
LOCAL llRet5da, llRet5nu
|
||||
SELECT tvd
|
||||
REPLACE ALL sters WITH 1 IN tvd
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
llRet5da = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'zero linii active + Da la confirmare -> continua (.T.)', llRet5da == .T., TRANSFORM(llRet5da)
|
||||
DO asserteaza WITH 'zero linii active -> mesajul de confirmare mentioneaza liniile sterse', 'linii' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
|
||||
gnMockRaspuns = 7
|
||||
gcMockUltimMesaj = ''
|
||||
llRet5nu = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'zero linii active + Nu la confirmare -> opreste (.F.)', llRet5nu == .F., TRANSFORM(llRet5nu)
|
||||
|
||||
gnMockRaspuns = 6
|
||||
SELECT tvd
|
||||
REPLACE ALL sters WITH 0 IN tvd
|
||||
|
||||
*!* --- A6: linie NOUA (id_vanzare_det=0) cu pret_achizitie 0/NULL -> CONFIRMARE, nu blocare (ambele ramuri) ---
|
||||
*!* linia noua e altfel valida (cantitate/pret/id_articol completate) ca sa nu declanseze A2/A3/A4 mai devreme
|
||||
LOCAL llRet6da, llRet6nu
|
||||
SELECT tvd
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare WITH loForm.nIdVanzare, id_vanzare_det WITH 0, id_articol WITH 999999, ;
|
||||
cantitate WITH 1, pret WITH 10, pret_cu_tva WITH 0, proc_tvav WITH 1.19, sters WITH 0, ;
|
||||
denumire WITH 'LINIE TEST S5', pret_achizitie WITH 0 IN tvd
|
||||
|
||||
gnMockRaspuns = 6
|
||||
gcMockUltimMesaj = ''
|
||||
llRet6da = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'linie noua cu pret_achizitie=0 + Da la confirmare -> continua (.T.)', llRet6da == .T., TRANSFORM(llRet6da)
|
||||
DO asserteaza WITH 'linie noua cu pret_achizitie=0 -> mesajul mentioneaza pretul de achizitie', 'achizitie' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
|
||||
gnMockRaspuns = 7
|
||||
gcMockUltimMesaj = ''
|
||||
llRet6nu = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'linie noua cu pret_achizitie=0 + Nu la confirmare -> opreste (.F.)', llRet6nu == .F., TRANSFORM(llRet6nu)
|
||||
|
||||
*!* linia de test nu trebuie sa ramana dupa pret_achizitie NULL (nu doar 0) - verificare suplimentara, apoi curatare
|
||||
gnMockRaspuns = 6
|
||||
SELECT tvd
|
||||
GO BOTTOM
|
||||
REPLACE pret_achizitie WITH .NULL. IN tvd
|
||||
gcMockUltimMesaj = ''
|
||||
LOCAL llRet6null
|
||||
llRet6null = loForm.inainte_de_do_termin()
|
||||
DO asserteaza WITH 'linie noua cu pret_achizitie NULL (nu doar 0) -> aceeasi confirmare', 'achizitie' $ Lower(gcMockUltimMesaj), gcMockUltimMesaj
|
||||
|
||||
SELECT tvd
|
||||
GO BOTTOM
|
||||
DELETE IN tvd
|
||||
ENDIF
|
||||
|
||||
loForm.Release()
|
||||
|
||||
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
|
||||
|
||||
|
||||
*============================================================================================
|
||||
* SECTIUNEA B - ScrieArticoleFacturaEditate: garda de no-op + SQL generat, FARA Oracle (mock pe goExecutor)
|
||||
*============================================================================================
|
||||
|
||||
LOCAL loExecutorReal
|
||||
loExecutorReal = goExecutor
|
||||
|
||||
*!* obiectul mock se construieste O SINGURA DATA, INLINE in programul principal (nu intr-un helper -
|
||||
*!* CREATEOBJECT apelat dintr-o PROCEDURA se comporta gresit in acest runtime headless, capcana l,
|
||||
*!* testare-ui-vfp.md) - ResetMockSql de mai jos doar goleste cursorul si reseteaza contorul pe
|
||||
*!* obiectul deja existent, fara sa mai construiasca altul
|
||||
goExecutor = Createobject('dummyexecutor')
|
||||
|
||||
*!* --- B0: garda de no-op, patru cazuri - niciunul nu trebuie sa apeleze goExecutor.oExecuta ---
|
||||
DO ResetMockSql
|
||||
|
||||
*!* tnIdVanzare <= 0
|
||||
CreeazaCursorArticoleGol('crstvdtest')
|
||||
SELECT crstvdtest
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 100, id_articol WITH 1, cantitate WITH 1, pret WITH 1, sters WITH 0 IN crstvdtest
|
||||
IF Used('crstvanztest')
|
||||
Use In crstvanztest
|
||||
ENDIF
|
||||
CREATE CURSOR crstvanztest (id_vanzare I, discount N(18,4) NULL)
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
|
||||
LOCAL llRetB1, llRetB1b
|
||||
llRetB1 = ScrieArticoleFacturaEditate(0, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op: tnIdVanzare=0 -> .T. fara sa scrie', llRetB1 == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB1) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
llRetB1b = ScrieArticoleFacturaEditate(-5, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op: tnIdVanzare negativ -> .T. fara sa scrie', llRetB1b == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB1b) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
*!* alias articole neinchis (nume inexistent)
|
||||
LOCAL llRetB2
|
||||
llRetB2 = ScrieArticoleFacturaEditate(12345, 'crstvd_nu_exista', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op: alias articole neinchis -> .T. fara sa scrie', llRetB2 == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB2) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
*!* alias vanzare fara exact 1 rand: 0 randuri
|
||||
LOCAL llRetB3zero, llRetB3doi
|
||||
SELECT crstvanztest
|
||||
ZAP
|
||||
llRetB3zero = ScrieArticoleFacturaEditate(12345, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op: alias vanzare cu 0 randuri -> .T. fara sa scrie', llRetB3zero == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB3zero) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
*!* alias vanzare fara exact 1 rand: 2 randuri
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
llRetB3doi = ScrieArticoleFacturaEditate(12345, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op: alias vanzare cu 2 randuri -> .T. fara sa scrie', llRetB3doi == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB3doi) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
*!* Reccount(alias articole)=0 - cazul critic: un esec de incarcare Oracle nu trebuie sa goleasca factura
|
||||
LOCAL llRetB4
|
||||
SELECT crstvanztest
|
||||
ZAP
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
SELECT crstvdtest
|
||||
ZAP
|
||||
DO asserteaza WITH 'conditie initiala B4: crstvdtest deschis, 0 randuri', Used('crstvdtest') AND Reccount('crstvdtest') == 0, TRANSFORM(Reccount('crstvdtest'))
|
||||
llRetB4 = ScrieArticoleFacturaEditate(12345, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'no-op CRITIC: Reccount(alias articole)=0 -> .T. fara sa scrie (nu goleste factura la esec de incarcare)', ;
|
||||
llRetB4 == .T. AND Reccount('crsMockSql') == 0, 'ret=' + TRANSFORM(llRetB4) + ' comenzi=' + TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
|
||||
*!* --- B1: scriere normala - un rand din fiecare categorie, verifica SQL-ul si clasificarea ---
|
||||
DO ResetMockSql
|
||||
|
||||
IF Used('crstvdtest')
|
||||
Use In crstvdtest
|
||||
ENDIF
|
||||
CreeazaCursorArticoleGol('crstvdtest')
|
||||
SELECT crstvdtest
|
||||
|
||||
*!* rand 1: pastrata, modificata (lmodificat ignorat de helper - decizia 38 invie TOATE liniile pastrate)
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 555, id_articol WITH 111, cantitate WITH 3, pret WITH 12.5, pret_cu_tva WITH 0, sters WITH 0, lmodificat WITH .T., denumire WITH 'PASTRATA MODIFICATA' IN crstvdtest
|
||||
*!* rand 2: pastrata, nemodificata - trebuie sa primeasca UPDATE la fel ca randul 1 (decizia 38)
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 556, id_articol WITH 112, cantitate WITH 1, pret WITH 5, pret_cu_tva WITH 0, sters WITH 0, lmodificat WITH .F., denumire WITH 'PASTRATA NEMODIFICATA' IN crstvdtest
|
||||
*!* rand 3: stearsa (linie persistata, sters=1) - ramane sters prin pasul 1 (marcheaza tot), fara UPDATE propriu
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 557, id_articol WITH 113, cantitate WITH 1, pret WITH 1, sters WITH 1, denumire WITH 'STEARSA' IN crstvdtest
|
||||
*!* rand 4: linie noua (id_vanzare_det=0) - INSERT; explicatie cu apostrof, ca sa verifice OracleSpecialCharacters
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 0, id_articol WITH 222, cantitate WITH 2, pret WITH 20, pret_cu_tva WITH 0, sters WITH 0, ;
|
||||
pret_achizitie WITH 15.5, explicatie WITH "test 'x'", denumire WITH 'NOUA' IN crstvdtest
|
||||
*!* rand 5: noua-si-stearsa (id_vanzare_det=0, sters=1) - se ignora, fara SQL propriu
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 0, id_articol WITH 223, cantitate WITH 1, pret WITH 1, sters WITH 1, denumire WITH 'NOUA STEARSA' IN crstvdtest
|
||||
*!* rand 6: componenta de set, pastrata (id_vanzare_set<>0, id_vanzare_det>0) - scrisa la fel ca o linie pastrata normala
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 558, id_articol WITH 114, id_vanzare_set WITH 999, cantitate WITH 1, pret WITH 7, sters WITH 0, denumire WITH 'COMPONENTA SET' IN crstvdtest
|
||||
|
||||
IF Used('crstvanztest')
|
||||
Use In crstvanztest
|
||||
ENDIF
|
||||
CREATE CURSOR crstvanztest (id_vanzare I, discount N(18,4) NULL)
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
|
||||
LOCAL llRetB5
|
||||
llRetB5 = ScrieArticoleFacturaEditate(12345, 'crstvdtest', 'crstvanztest')
|
||||
DO asserteaza WITH 'scriere normala: intoarce .T.', llRetB5 == .T., TRANSFORM(llRetB5)
|
||||
DO asserteaza WITH 'total comenzi = 6 (1 marcheaza-tot + 3 UPDATE pastrate + 1 INSERT noua + 1 recalcul)', ;
|
||||
Reccount('crsMockSql') == 6, TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
LOCAL lnCntMarcheazaTot, lnCntUpdate, lnCntInsert, lnCntRecalc
|
||||
SELECT crsMockSql
|
||||
lnCntMarcheazaTot = 0
|
||||
lnCntUpdate = 0
|
||||
lnCntInsert = 0
|
||||
lnCntRecalc = 0
|
||||
SCAN
|
||||
IF 'update vanzari_detalii set sters = 1,' $ Lower(sql) AND 'and sters = 0' $ Lower(sql) AND 'where id_vanzare = 12345' $ Lower(sql)
|
||||
lnCntMarcheazaTot = lnCntMarcheazaTot + 1
|
||||
ENDIF
|
||||
IF 'update vanzari_detalii set sters = 0, cantitate' $ Lower(sql)
|
||||
lnCntUpdate = lnCntUpdate + 1
|
||||
ENDIF
|
||||
IF 'insert into vanzari_detalii' $ Lower(sql)
|
||||
lnCntInsert = lnCntInsert + 1
|
||||
ENDIF
|
||||
IF 'recalculeaza_totaluri_vanzari' $ Lower(sql)
|
||||
lnCntRecalc = lnCntRecalc + 1
|
||||
ENDIF
|
||||
ENDSCAN
|
||||
|
||||
DO asserteaza WITH 'clasificare: exact 1 comanda "marcheaza tot sters" (pasul 1, decizia 38)', lnCntMarcheazaTot == 1, TRANSFORM(lnCntMarcheazaTot)
|
||||
DO asserteaza WITH 'clasificare: exact 3 UPDATE (liniile pastrate 555/556/558, inclusiv nemodificata si componenta de set)', lnCntUpdate == 3, TRANSFORM(lnCntUpdate)
|
||||
DO asserteaza WITH 'clasificare: exact 1 INSERT (linia noua 4, id_vanzare_det=0/sters<>1)', lnCntInsert == 1, TRANSFORM(lnCntInsert)
|
||||
DO asserteaza WITH 'clasificare: exact 1 recalcul de totaluri (pasul final)', lnCntRecalc == 1, TRANSFORM(lnCntRecalc)
|
||||
|
||||
LOCAL llU555, llU556, llU558, llU557Absent, llU0Absent
|
||||
SELECT crsMockSql
|
||||
llU555 = .F.
|
||||
llU556 = .F.
|
||||
llU558 = .F.
|
||||
llU557Absent = .T.
|
||||
llU0Absent = .T.
|
||||
SCAN FOR 'update vanzari_detalii set sters = 0, cantitate' $ Lower(sql)
|
||||
IF 'where id_vanzare_det = 555 and' $ Lower(sql)
|
||||
llU555 = .T.
|
||||
ENDIF
|
||||
IF 'where id_vanzare_det = 556 and' $ Lower(sql)
|
||||
llU556 = .T.
|
||||
ENDIF
|
||||
IF 'where id_vanzare_det = 558 and' $ Lower(sql)
|
||||
llU558 = .T.
|
||||
ENDIF
|
||||
IF 'where id_vanzare_det = 557 and' $ Lower(sql)
|
||||
llU557Absent = .F.
|
||||
ENDIF
|
||||
IF 'where id_vanzare_det = 0 and' $ Lower(sql)
|
||||
llU0Absent = .F.
|
||||
ENDIF
|
||||
ENDSCAN
|
||||
DO asserteaza WITH 'clasificare: UPDATE-ul contine exact liniile pastrate asteptate (555, 556, 558) si niciuna in plus (557 stearsa, 0 = noua, absente)', ;
|
||||
llU555 AND llU556 AND llU558 AND llU557Absent AND llU0Absent, ;
|
||||
'555=' + TRANSFORM(llU555) + ' 556=' + TRANSFORM(llU556) + ' 558=' + TRANSFORM(llU558) + ' 557absent=' + TRANSFORM(llU557Absent) + ' 0absent=' + TRANSFORM(llU0Absent)
|
||||
|
||||
LOCAL llUpdateAncorat
|
||||
SELECT crsMockSql
|
||||
llUpdateAncorat = .T.
|
||||
SCAN FOR 'update vanzari_detalii set sters = 0, cantitate' $ Lower(sql)
|
||||
IF !('and id_vanzare = 12345' $ Lower(sql))
|
||||
llUpdateAncorat = .F.
|
||||
ENDIF
|
||||
ENDSCAN
|
||||
DO asserteaza WITH 'fiecare UPDATE de linie pastrata e ancorat pe document (and id_vanzare = 12345)', llUpdateAncorat, TRANSFORM(llUpdateAncorat)
|
||||
|
||||
LOCAL lcSqlInsert, llInsertOk
|
||||
lcSqlInsert = ''
|
||||
SELECT crsMockSql
|
||||
SCAN FOR 'insert into vanzari_detalii' $ Lower(sql)
|
||||
lcSqlInsert = sql
|
||||
ENDSCAN
|
||||
llInsertOk = ('15.5000' $ lcSqlInsert) AND ("test ''x''" $ lcSqlInsert) AND ('12345' $ lcSqlInsert) AND ('222' $ lcSqlInsert)
|
||||
DO asserteaza WITH 'INSERT: pret_achizitie citit din cursor (15.5000), explicatie escapata (apostrof dublat), id_vanzare/id_articol corecte', llInsertOk, lcSqlInsert
|
||||
|
||||
LOCAL lcSqlRecalc
|
||||
lcSqlRecalc = ''
|
||||
SELECT crsMockSql
|
||||
SCAN FOR 'recalculeaza_totaluri_vanzari' $ Lower(sql)
|
||||
lcSqlRecalc = sql
|
||||
ENDSCAN
|
||||
*!* NULL e literal in sursa helperului ([NULL], majuscul) - comparatia se face pe textul lowercase in ambele parti
|
||||
DO asserteaza WITH 'recalcul: discount NULL in cursor -> parametru NULL (pastreaza discountul curent din baza, decizia 41)', ;
|
||||
'recalculeaza_totaluri_vanzari(12345,null)' $ Lower(Strtran(lcSqlRecalc, ' ', '')), lcSqlRecalc
|
||||
|
||||
*!* --- B2: recalcul cu discount NEnul in cursor - parametrul trebuie sa fie valoarea, nu NULL ---
|
||||
DO ResetMockSql
|
||||
SELECT crstvanztest
|
||||
ZAP
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, 5)
|
||||
LOCAL llRetB6, lcSqlRecalc2
|
||||
llRetB6 = ScrieArticoleFacturaEditate(12345, 'crstvdtest', 'crstvanztest')
|
||||
lcSqlRecalc2 = ''
|
||||
SELECT crsMockSql
|
||||
SCAN FOR 'recalculeaza_totaluri_vanzari' $ Lower(sql)
|
||||
lcSqlRecalc2 = sql
|
||||
ENDSCAN
|
||||
DO asserteaza WITH 'recalcul: discount=5 in cursor -> parametrul e 5.0000, nu NULL', ;
|
||||
llRetB6 == .T. AND ('recalculeaza_totaluri_vanzari(12345,5.0000)' $ Lower(Strtran(lcSqlRecalc2, ' ', ''))), lcSqlRecalc2
|
||||
|
||||
|
||||
*!* --- B3: oprire la primul esec Oracle - mock configurat sa esueze la a doua comanda ---
|
||||
DO ResetMockSql
|
||||
goExecutor.nFailAtCall = 2
|
||||
|
||||
IF Used('crstvdtest2')
|
||||
Use In crstvdtest2
|
||||
ENDIF
|
||||
CreeazaCursorArticoleGol('crstvdtest2')
|
||||
SELECT crstvdtest2
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 601, id_articol WITH 1, cantitate WITH 1, pret WITH 1, sters WITH 0 IN crstvdtest2
|
||||
APPEND BLANK
|
||||
REPLACE id_vanzare_det WITH 602, id_articol WITH 2, cantitate WITH 1, pret WITH 1, sters WITH 0 IN crstvdtest2
|
||||
|
||||
SELECT crstvanztest
|
||||
ZAP
|
||||
INSERT INTO crstvanztest (id_vanzare, discount) VALUES (12345, NULL)
|
||||
|
||||
LOCAL llRetB7
|
||||
llRetB7 = ScrieArticoleFacturaEditate(12345, 'crstvdtest2', 'crstvanztest')
|
||||
DO asserteaza WITH 'esec Oracle la a doua comanda -> functia intoarce .F.', llRetB7 == .F., TRANSFORM(llRetB7)
|
||||
DO asserteaza WITH 'esec Oracle: se opreste la primul esec, nu mai incearca a treia comanda (2 comenzi total, nu 3+)', ;
|
||||
Reccount('crsMockSql') == 2, TRANSFORM(Reccount('crsMockSql'))
|
||||
|
||||
goExecutor.nFailAtCall = 0
|
||||
|
||||
STRTOFILE('REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
|
||||
|
||||
goExecutor = loExecutorReal
|
||||
|
||||
IF Used('crstvdtest')
|
||||
Use In crstvdtest
|
||||
ENDIF
|
||||
IF Used('crstvdtest2')
|
||||
Use In crstvdtest2
|
||||
ENDIF
|
||||
IF Used('crstvanztest')
|
||||
Use In crstvanztest
|
||||
ENDIF
|
||||
IF Used('crsMockSql')
|
||||
Use In crsMockSql
|
||||
ENDIF
|
||||
|
||||
QUIT
|
||||
|
||||
|
||||
*!* goleste jurnalul de comenzi si contorul de apeluri PE OBIECTUL DEJA EXISTENT (nu recreeaza
|
||||
*!* goExecutor - vezi comentariul de la constructia lui in main, capcana l). crsMockSql ramane
|
||||
*!* MEREU deschis (recreat gol, nu doar inchis) - RECCOUNT() pe un alias niciodata deschis arunca
|
||||
*!* eroarea 13 "Alias is not found" in loc sa intoarca 0 (verificat empiric, nu asa cum presupunea
|
||||
*!* prima varianta a acestui fisier)
|
||||
PROCEDURE ResetMockSql
|
||||
IF Used('crsMockSql')
|
||||
Use In crsMockSql
|
||||
ENDIF
|
||||
CREATE CURSOR crsMockSql (nr I, sql M)
|
||||
goExecutor.nApeluri = 0
|
||||
goExecutor.nFailAtCall = 0
|
||||
ENDPROC
|
||||
|
||||
|
||||
PROCEDURE asserteaza
|
||||
LPARAMETERS tcNume, tlConditie, tcValoare
|
||||
|
||||
IF m.tlConditie
|
||||
gnPass = gnPass + 1
|
||||
STRTOFILE('PASS ' + m.tcNume + CHR(13) + CHR(10), gcErrLog, 1)
|
||||
ELSE
|
||||
gnFail = gnFail + 1
|
||||
STRTOFILE('FAIL ' + m.tcNume + ' (valoare: ' + m.tcValoare + ')' + CHR(13) + CHR(10), gcErrLog, 1)
|
||||
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
|
||||
|
||||
|
||||
*!* mock de goExecutor pentru sectiunea B - NU atinge Oracle: doar inregistreaza textul fiecarei
|
||||
*!* comenzi intr-un cursor (crsMockSql) si intoarce .T., sau .F. o singura data la apelul cu numarul
|
||||
*!* nFailAtCall (0 = nu esueaza niciodata) - ca sa verifice oprirea la primul esec fara sa opreasca
|
||||
*!* toate apelurile ulterioare din suita. DEFINE CLASS la finalul fisierului (capcana depanare_testare_vfp.md #7)
|
||||
DEFINE CLASS dummyexecutor AS Custom
|
||||
cEroare = ''
|
||||
nFailAtCall = 0
|
||||
nApeluri = 0
|
||||
|
||||
PROCEDURE oExecuta
|
||||
LPARAMETERS tcSql, tcCursor
|
||||
LOCAL lnUrmator
|
||||
|
||||
This.nApeluri = This.nApeluri + 1
|
||||
IF !Used('crsMockSql')
|
||||
CREATE CURSOR crsMockSql (nr I, sql M)
|
||||
ENDIF
|
||||
SELECT crsMockSql
|
||||
lnUrmator = Reccount('crsMockSql') + 1
|
||||
INSERT INTO crsMockSql (nr, sql) VALUES (m.lnUrmator, m.tcSql)
|
||||
|
||||
IF !Empty(m.tcCursor)
|
||||
IF Used(m.tcCursor)
|
||||
Use In (m.tcCursor)
|
||||
ENDIF
|
||||
CREATE CURSOR (m.tcCursor) (id_temp N(1))
|
||||
ENDIF
|
||||
|
||||
IF This.nFailAtCall > 0 AND This.nApeluri == This.nFailAtCall
|
||||
This.cEroare = 'esec simulat de test la apelul ' + Transform(This.nApeluri)
|
||||
RETURN .F.
|
||||
ENDIF
|
||||
RETURN .T.
|
||||
ENDPROC
|
||||
ENDDEFINE
|
||||
Reference in New Issue
Block a user