Files
comun/utile/Teste/editare_factura/test_s5_validari_articole.prg
Marius Mutu 1c42ae0af4 #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
2026-08-10 01:37:12 +03:00

589 lines
25 KiB
Plaintext

* 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