#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:
2026-08-10 01:37:12 +03:00
parent b9eba29ebe
commit 1c42ae0af4
13 changed files with 3408 additions and 60 deletions

View File

@@ -2488,6 +2488,9 @@ DEFINE CLASS afisjurcom AS _frmbase OF "_frm_base.vcx"
ALLTRIM(Str(lnCod)) + [,] + Alltrim(Str(lnIdFact)) + [,] + Alltrim(Str(lnIdfactd)) + [,?gnIdUtil); end;]
lnSucces = Iif(goExecutor.oExecuta(lcSql),1,-1)
Endif
If lnSucces > 0 And "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) And Used('tvanz') And Reccount('tvanz') = 1
lnSucces = Iif(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1)
Endif
*!* IF lnSucces > 0 AND llGestInvantar && nota de inventariere : update dataOra_validat cu dataora modificarii notei contabile
*!* lcSql = [update ] + gcs + [.gest_inventar set dataora_validat = pack_contafin.GET_DATAORA() where dataora_validat = ?pdDataOra and an = ?pnAn and luna = ?pnLuna]
*!* lnSucces = goExecutor.oExecute(lcSql)

View File

@@ -3825,6 +3825,9 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
Alltrim(Str(lnCod)) + [,] + Alltrim(Str(lnIdFact)) + [,] + Alltrim(Str(lnIdFactD)) + [,?gnIdUtil); end;]
lnSucces = Iif(goExecutor.oExecuta(lcSql),1,-1)
Endif
If lnSucces > 0 And "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) And Used('tvanz') And Reccount('tvanz') = 1
lnSucces = Iif(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1)
Endif
If Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1))
If lnSucces > 0
Thisform.do_cauta()

View File

@@ -6641,6 +6641,8 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretAchizitieArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretAchizitieArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt._checkbox1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Header1" UniqueID="" Timestamp="" />
@@ -12317,7 +12319,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura' AS _grdrow WITH ;
Anchor = 15, ;
ColumnCount = 14, ;
ColumnCount = 15, ;
DeleteMark = .F., ;
FontSize = 10, ;
GridLineColor = 128,128,128, ;
@@ -12336,35 +12338,35 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
Width = 759, ;
ZOrderSet = 1, ;
Column1.ControlSource = "tvd.denumire", ;
Column1.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column1.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column1.FontName = "Arial", ;
Column1.FontSize = 10, ;
Column1.Name = "cDenumireArt", ;
Column1.ReadOnly = .T., ;
Column1.Width = 170, ;
Column2.ControlSource = "tvd.codmat", ;
Column2.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column2.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column2.FontName = "Arial", ;
Column2.FontSize = 10, ;
Column2.Name = "cCodmatArt", ;
Column2.ReadOnly = .T., ;
Column2.Width = 90, ;
Column3.ControlSource = "tvd.serie", ;
Column3.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column3.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column3.FontName = "Arial", ;
Column3.FontSize = 10, ;
Column3.Name = "cSerieArt", ;
Column3.ReadOnly = .T., ;
Column3.Width = 70, ;
Column4.ControlSource = "tvd.lot", ;
Column4.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column4.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column4.FontName = "Arial", ;
Column4.FontSize = 10, ;
Column4.Name = "cLotArt", ;
Column4.ReadOnly = .T., ;
Column4.Width = 60, ;
Column5.ControlSource = "tvd.cantitate", ;
Column5.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column5.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column5.FontName = "Arial", ;
Column5.FontSize = 10, ;
Column5.Format = "R", ;
@@ -12373,7 +12375,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
Column5.ReadOnly = .F., ;
Column5.Width = 70, ;
Column6.ControlSource = "tvd.pret", ;
Column6.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column6.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column6.FontName = "Arial", ;
Column6.FontSize = 10, ;
Column6.Format = "R", ;
@@ -12381,69 +12383,78 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
Column6.Name = "cPretArt", ;
Column6.ReadOnly = .F., ;
Column6.Width = 70, ;
Column7.ControlSource = "tvd.pret_cu_tva", ;
Column7.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column7.ControlSource = "tvd.pret_achizitie", ;
Column7.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column7.FontName = "Arial", ;
Column7.FontSize = 10, ;
Column7.Name = "cPretCuTvaArt", ;
Column7.Format = "R", ;
Column7.InputMask = (get_mask(12,gnPPRET)), ;
Column7.Name = "cPretAchizitieArt", ;
Column7.ReadOnly = .F., ;
Column7.Sparse = .F., ;
Column7.Width = 80, ;
Column8.ControlSource = "tvd.proc_tvav", ;
Column8.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column7.Width = 90, ;
Column8.ControlSource = "tvd.pret_cu_tva", ;
Column8.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column8.FontName = "Arial", ;
Column8.FontSize = 10, ;
Column8.Format = "R", ;
Column8.InputMask = "9.99", ;
Column8.Name = "cProcTvavArt", ;
Column8.ReadOnly = .T., ;
Column8.Width = 55, ;
Column9.ControlSource = "tvd.discount_unitar", ;
Column9.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column8.Name = "cPretCuTvaArt", ;
Column8.ReadOnly = .F., ;
Column8.Sparse = .F., ;
Column8.Width = 80, ;
Column9.ControlSource = "tvd.proc_tvav", ;
Column9.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column9.FontName = "Arial", ;
Column9.FontSize = 10, ;
Column9.Format = "R", ;
Column9.InputMask = (get_mask(12,gnPPRET)), ;
Column9.Name = "cDiscountUnitarArt", ;
Column9.InputMask = "9.99", ;
Column9.Name = "cProcTvavArt", ;
Column9.ReadOnly = .T., ;
Column9.Width = 90, ;
Column10.ControlSource = "tvd.nume_gestiune", ;
Column10.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column9.Width = 55, ;
Column10.ControlSource = "tvd.discount_unitar", ;
Column10.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column10.FontName = "Arial", ;
Column10.FontSize = 10, ;
Column10.Name = "cGestiuneArt", ;
Column10.Format = "R", ;
Column10.InputMask = (get_mask(12,gnPPRET)), ;
Column10.Name = "cDiscountUnitarArt", ;
Column10.ReadOnly = .T., ;
Column10.Width = 90, ;
Column11.ControlSource = "tvd.nume_val", ;
Column11.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column11.ControlSource = "tvd.nume_gestiune", ;
Column11.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column11.FontName = "Arial", ;
Column11.FontSize = 10, ;
Column11.Name = "cValutaArt", ;
Column11.Name = "cGestiuneArt", ;
Column11.ReadOnly = .T., ;
Column11.Width = 60, ;
Column12.ControlSource = "tvd.explicatie", ;
Column12.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column11.Width = 90, ;
Column12.ControlSource = "tvd.nume_val", ;
Column12.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column12.FontName = "Arial", ;
Column12.FontSize = 10, ;
Column12.Name = "cExplicatieArt", ;
Column12.Name = "cValutaArt", ;
Column12.ReadOnly = .T., ;
Column12.Width = 150, ;
Column13.ControlSource = "tvd.taxcode", ;
Column13.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column12.Width = 60, ;
Column13.ControlSource = "tvd.explicatie", ;
Column13.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column13.FontName = "Arial", ;
Column13.FontSize = 10, ;
Column13.Name = "cTaxcodeArt", ;
Column13.Name = "cExplicatieArt", ;
Column13.ReadOnly = .T., ;
Column13.Width = 60, ;
Column14.ControlSource = "tvd.valoare", ;
Column14.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),RGB(0,0,0))", ;
Column13.Width = 150, ;
Column14.ControlSource = "tvd.taxcode", ;
Column14.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column14.FontName = "Arial", ;
Column14.FontSize = 10, ;
Column14.Format = "R", ;
Column14.InputMask = (get_mask(14,gnPa)), ;
Column14.Name = "cValoareArt", ;
Column14.Name = "cTaxcodeArt", ;
Column14.ReadOnly = .T., ;
Column14.Width = 90
Column14.Width = 60, ;
Column15.ControlSource = "tvd.valoare", ;
Column15.DynamicForeColor = "IIF(tvd.sters=1,RGB(150,150,150),IIF(NVL(tvd.id_vanzare_set,0)<>0,RGB(0,70,153),RGB(0,0,0)))", ;
Column15.FontName = "Arial", ;
Column15.FontSize = 10, ;
Column15.Format = "R", ;
Column15.InputMask = (get_mask(14,gnPa)), ;
Column15.Name = "cValoareArt", ;
Column15.ReadOnly = .T., ;
Column15.Width = 90
*< END OBJECT: ClassLib="_grd_base.vcx" BaseClass="grid" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Header1' AS header WITH ;
@@ -12593,6 +12604,27 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretAchizitieArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Pret achizitie", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretAchizitieArt.Text1' AS textbox WITH ;
BackColor = 255,255,255, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .F., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Pret", ;
@@ -13116,7 +13148,8 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
id_jtva_coloana WITH toArticol.id_jtva_coloana, serie WITH toArticol.serie, ;
explicatie WITH toArticol.explicatie, taxcode WITH toArticol.taxcode, lot WITH toArticol.lot, ;
sters WITH 0, denumire WITH toArticol.denumire, codmat WITH toArticol.codmat, ;
nume_gestiune WITH '', nume_val WITH Iif(Used('tvanz') And Reccount('tvanz') > 0, Nvl(tvanz.nume_val,''), toArticol.nume_val), lmodificat WITH .T.
nume_gestiune WITH '', nume_val WITH Iif(Used('tvanz') And Reccount('tvanz') > 0, Nvl(tvanz.nume_val,''), toArticol.nume_val), ;
pret_achizitie WITH Nvl(toArticol.pret_achizitie,0), lmodificat WITH .T.
Thisform.calculeaza_valori_articol()
ENDPROC
@@ -14293,6 +14326,64 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
Endif
ENDIF
*!* modificare v 2.4.4 ^
IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Used('tvd')
LOCAL lnAreaTvd, lnRecnoTvd, lnLiniiActive
lnAreaTvd = Select()
lnRecnoTvd = Recno('tvd')
lnLiniiActive = 0
SELECT tvd
SCAN FOR Nvl(sters,0) <> 1
lnLiniiActive = m.lnLiniiActive + 1
IF Nvl(cantitate,0) <= 0
AMESSAGEBOX("Linia '" + Alltrim(Nvl(denumire,'')) + "' are cantitatea 0 sau negativa.",0+16,"Eroare")
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
RETURN .F.
ENDIF
IF Isnull(pret)
AMESSAGEBOX("Linia '" + Alltrim(Nvl(denumire,'')) + "' nu are pret completat.",0+16,"Eroare")
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
RETURN .F.
ENDIF
IF Nvl(id_articol,0) = 0
AMESSAGEBOX("Linia '" + Alltrim(Nvl(denumire,'')) + "' nu are articol asociat.",0+16,"Eroare")
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
RETURN .F.
ENDIF
IF Nvl(id_vanzare_det,0) = 0 AND Nvl(pret_achizitie,0) = 0
IF amessagebox("Linia '" + Alltrim(Nvl(denumire,'')) + "' are pretul de achizitie 0." + CRLF + ;
"Doriti sa continuati?",4+32,"Pret de achizitie") <> 6
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
RETURN .F.
ENDIF
ENDIF
ENDSCAN
IF m.lnLiniiActive = 0 AND Used('tvanz') AND Reccount('tvanz') = 1
IF amessagebox("Toate liniile facturii au fost sterse." + CRLF + ;
"Doriti sa continuati?",4+32,"Factura fara linii") <> 6
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
RETURN .F.
ENDIF
ENDIF
IF Between(m.lnRecnoTvd,1,Reccount('tvd'))
GO (m.lnRecnoTvd) IN tvd
ENDIF
SELECT (m.lnAreaTvd)
ENDIF
RETURN m.llRet
@@ -14568,7 +14659,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
IF Used('tvd')
Use In tvd
ENDIF
CREATE CURSOR tvd (id_vanzare I NULL, id_vanzare_det I NULL, id_articol I NULL, cantitate N(12,3) NULL, pret N(14,4) NULL, pret_cu_tva N(14,4) NULL, proc_tvav N(6,4) NULL, discount_unitar N(14,4) NULL, id_gestiune I NULL, cont C(20) NULL, id_valuta I NULL, id_jtva_coloana I NULL, serie C(20) NULL, explicatie C(60) NULL, taxcode I NULL, lot C(20) NULL, sters I NULL, denumire C(100) NULL, codmat C(30) NULL, nume_gestiune C(60) NULL, nume_val C(20) NULL, lmodificat L, valoare N(14,2), in_stoc I NULL)
CREATE CURSOR tvd (id_vanzare I NULL, id_vanzare_det I NULL, id_articol I NULL, cantitate N(12,3) NULL, pret N(14,4) NULL, pret_cu_tva N(14,4) NULL, proc_tvav N(6,4) NULL, discount_unitar N(14,4) NULL, id_gestiune I NULL, cont C(20) NULL, id_valuta I NULL, id_jtva_coloana I NULL, serie C(20) NULL, explicatie C(60) NULL, taxcode I NULL, lot C(20) NULL, sters I NULL, id_vanzare_set I NULL, pret_achizitie N(14,4) NULL, denumire C(100) NULL, codmat C(30) NULL, nume_gestiune C(60) NULL, nume_val C(20) NULL, lmodificat L, valoare N(14,2), in_stoc I NULL)
ENDIF
*!* cursorul tvanz - placeholder gol, ca bara de totaluri (ControlSource pe tvanz.discount) sa se lege la construire
@@ -16433,6 +16524,16 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
ENDPROC
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Text1.When
IF Nvl(tvd.id_vanzare_set,0) <> 0
RETURN .F.
ENDIF
Thisform.oldvalue = This.Value
ENDPROC
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cPretAchizitieArt.Text1.When
IF Nvl(tvd.id_vanzare_set,0) <> 0 OR Nvl(tvd.id_vanzare_det,0) <> 0
RETURN .F.
ENDIF
Thisform.oldvalue = This.Value
ENDPROC
@@ -16444,6 +16545,9 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
ENDPROC
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Text1.When
IF Nvl(tvd.id_vanzare_set,0) <> 0
RETURN .F.
ENDIF
Thisform.oldvalue = This.Value
ENDPROC
@@ -16457,6 +16561,10 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
This.Parent.Parent.Refresh()
ENDPROC
PROCEDURE pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt._checkbox1.When
RETURN Nvl(tvd.id_vanzare_set,0) = 0
ENDPROC
PROCEDURE pgfArticole.PAGE3.txtDiscountArt.Valid
Thisform.ActualizeazaBaraTotaluri()
ENDPROC

View File

@@ -27,9 +27,22 @@ Nu trimite SQL prin pipe din PowerShell (`'select...' | sqlplus`) — BOM-ul UTF
## Comanda de export (re-generare .pck)
> **`linesize` prea mic corupe tăcut sursa.** SQL\*Plus face **wrap** la coloana `linesize`, deci o
> linie de cod mai lungă e ruptă în două — inclusiv **prin mijlocul unui identificator**. Fișierul
> pare întreg, `diff` față de el nu arată nimic, dar recompilarea lui dă erori la linii care n-au
> nicio legătură cu ce ai modificat (`ORA-00920`, `PLS-*`). `PACK_FACTURARE` are linii de peste
> **1000** de caractere, iar un export la `linesize 400` i-a rupt 8. Folosește `linesize 32767` și
> verifică după export că nicio linie nu atinge exact valoarea `linesize`.
>
> **Exportul e bun pentru CITIT, nu ca bază pentru un script de migrare.** Pentru un script de
> pachet, pleacă de la ultimul script aplicat din `SCRIPTURI_CLAR` (fișier real, care n-a trecut
> niciodată printr-un spool) și confirmă că e la zi comparându-l cu exportul **ignorând spațiul
> alb** — așa wrap-ul nu mai contează:
> `tr -d ' \t\r\n' < fisier | md5sum` pe ambele.
```sql
-- export_pack.sql
set pagesize 0 linesize 400 trimspool on feedback off heading off termout off
set pagesize 0 linesize 32767 long 2000000 longchunksize 32767 trimspool on feedback off heading off termout off
spool D:\ROA\ROAGEST\COMUN\docs\PACK_CONTAFIN.pck
select text from all_source where owner='MARIUSM_AUTO' and name='PACK_CONTAFIN' and type='PACKAGE' order by line;
select '/' from dual;

View File

@@ -22,7 +22,11 @@ Reguli confirmate de Marius (24.07.2026):
arata spre comentariu. Pus la sfarsit de fraza, `.` in loc de `;`. Comentariile de **dinaintea**
instructiunii nu sunt afectate.
- **Fiecare script se incheie cu `exec pack_migrare.UpdateVersiune('<nume_script>');` urmat de
`commit;`.** DDL-ul comite implicit, dar `UpdateVersiune` e DML: fara `commit` explicit, un script
`commit;`.** Numele se da **fara extensia `.sql`** — o adauga `UpdateVersiune`; trecuta si in
argument, ajunge in `VERSIUNE` ca `..._.sql.sql` si scriptul apare ca neaplicat la comparatia de
mai jos. Toate scriptele din `SCRIPTURI_CLAR` o omit, iar in `VERSIUNE` nu exista niciun rand cu
dubla extensie.
DDL-ul comite implicit, dar `UpdateVersiune` e DML: fara `commit` explicit, un script
se poate aplica fara ca versiunea sa se inregistreze, iar `VERSIUNE` ajunge sa minta despre ce s-a
aplicat.
- **Rularea manuala se face conectat pe schema tinta** (`CONTAFIN_ORACLE` pentru `co_`, schema firmei

View File

@@ -4,10 +4,11 @@
*!* marius.mutu
*!* helpere comune pentru editarea unei facturi emise: garda eFactura, cursoarele notei si ale rulajelor,
*!* randul de vanzare corespunzator notei (inclusiv valuta documentului: in_valuta/id_valuta/nume_val),
*!* liniile de articole pentru pagina de articole factura (inclusiv in_stoc, pentru corectia verdictului
*!* RUL pe liniile nestocate), pregatirea cursorului de articole inainte de Createobject(frm_modific2024)
*!* si construirea obiectului poArticol pentru adaugarea unei linii noi prin dialogul frm_articol_factura,
*!* populat cu valuta documentului cand acesta e in valuta
*!* liniile de articole pentru pagina de articole factura (inclusiv in_stoc si id_vanzare_set/pret_achizitie,
*!* pentru corectia verdictului RUL pe liniile nestocate si pentru liniile din seturi), pregatirea
*!* cursorului de articole inainte de Createobject(frm_modific2024), construirea obiectului poArticol
*!* pentru adaugarea unei linii noi prin dialogul frm_articol_factura (populat cu valuta documentului
*!* cand acesta e in valuta) si scrierea in VANZARI_DETALII a liniilor editate/adaugate/sterse
*!* parametru: id_fact
@@ -271,7 +272,7 @@ FUNCTION CreeazaCursorArticoleGol
ENDIF
CREATE CURSOR (m.lcAlias) (id_vanzare I NULL, id_vanzare_det I NULL, id_articol I NULL, cantitate N(12,3) NULL, pret N(14,4) NULL, pret_cu_tva N(14,4) NULL, proc_tvav N(6,4) NULL, ;
discount_unitar N(14,4) NULL, id_gestiune I NULL, cont C(20) NULL, id_valuta I NULL, id_jtva_coloana I NULL, serie C(20) NULL, explicatie C(60) NULL, ;
taxcode I NULL, lot C(20) NULL, sters I NULL, denumire C(100) NULL, codmat C(30) NULL, nume_gestiune C(60) NULL, nume_val C(20) NULL, ;
taxcode I NULL, lot C(20) NULL, sters I NULL, id_vanzare_set I NULL, pret_achizitie N(14,4) NULL, denumire C(100) NULL, codmat C(30) NULL, nume_gestiune C(60) NULL, nume_val C(20) NULL, ;
lmodificat L, valoare N(14,2), in_stoc I NULL)
ENDFUNC && CreeazaCursorArticoleGol
@@ -455,3 +456,100 @@ FUNCTION CreeazaPoArticolNouTvd
RETURN loArticol
ENDFUNC && CreeazaPoArticolNouTvd
*!* parametri: id_vanzare (din tvanz.id_vanzare), alias articole (implicit tvd), alias vanzare (implicit tvanz)
*!* scrie in VANZARI_DETALII liniile editate ale notei, ca diferenta fata de starea persistata: marcheaza
*!* sters tot documentul, invie/actualizeaza liniile pastrate din alias, insereaza liniile noi, apoi
*!* recalculeaza totalurile documentului (cu discountul din alias vanzare, ca sa nu poata diverge de
*!* liniile scrise); ruleaza in tranzactia manuala deja deschisa de apelant, dupa finalizeaza_modificare_nota
*!* - nu da COMMIT/ROLLBACK, nu inchide cursoare, salveaza si restaureaza workarea/pozitia curenta
*!* retur logic: .T. daca a mers tot sau nu era nimic de facut, .F. la primul esec Oracle (se opreste acolo)
FUNCTION ScrieArticoleFacturaEditate
LPARAMETERS tnIdVanzare, tcAliasArticole, tcAliasVanzare
LOCAL lcAliasArt, lcAliasVanz, lnAreaOrigine, lnRecnoArt, llSucces, lcSql, lnDiscount, llDiscountNul
lcAliasArt = IIF(Type('tcAliasArticole') = 'C' AND !Empty(m.tcAliasArticole), m.tcAliasArticole, 'tvd')
lcAliasVanz = IIF(Type('tcAliasVanzare') = 'C' AND !Empty(m.tcAliasVanzare), m.tcAliasVanzare, 'tvanz')
*!* cursor gol de articole nu inseamna "document fara linii" - poate fi si un esec de incarcare (IncarcaArticoleFactura intoarce .T. cu cursor gol la eroare Oracle); fara ancora, ar goli tacut factura
IF Nvl(m.tnIdVanzare,0) <= 0 OR !Used(m.lcAliasArt) OR !Used(m.lcAliasVanz) OR Reccount(m.lcAliasVanz) <> 1 OR Reccount(m.lcAliasArt) = 0
RETURN .T.
ENDIF
lnAreaOrigine = Select()
llSucces = .T.
SELECT (m.lcAliasVanz)
GO TOP
llDiscountNul = Isnull(discount)
lnDiscount = Nvl(discount,0)
*!* marcheaza sters tot documentul - perechea reset-ului STERS=0 din actualizeaza_vanzari
lcSql = [update vanzari_detalii set sters = 1, id_utils = ] + Alltrim(Str(gnIdUtil)) + [, dataoras = sysdate ] + ;
[where id_vanzare = ] + Alltrim(Str(m.tnIdVanzare)) + [ and sters = 0]
llSucces = goExecutor.oExecuta(m.lcSql)
*!* invie si actualizeaza liniile pastrate - toate cele active din alias, nu doar lmodificat, pasul de mai sus le-a marcat pe toate
IF m.llSucces
SELECT (m.lcAliasArt)
lnRecnoArt = Recno()
SCAN FOR id_vanzare_det > 0 AND Nvl(sters,0) <> 1
lcSql = [update vanzari_detalii set sters = 0, cantitate = ] + Alltrim(Str(Nvl(cantitate,0),18,3)) + ;
[, pret = ] + Alltrim(Str(Nvl(pret,0),18,4)) + [, pret_cu_tva = ] + Alltrim(Str(Nvl(pret_cu_tva,0))) + ;
[, id_utils = ] + Alltrim(Str(gnIdUtil)) + [, dataoras = sysdate ] + ;
[where id_vanzare_det = ] + Alltrim(Str(id_vanzare_det)) + [ and id_vanzare = ] + Alltrim(Str(m.tnIdVanzare))
llSucces = goExecutor.oExecuta(m.lcSql)
IF !m.llSucces
EXIT
ENDIF
ENDSCAN
TRY
GO m.lnRecnoArt IN (m.lcAliasArt)
CATCH
GO TOP IN (m.lcAliasArt)
ENDTRY
ENDIF
*!* insereaza liniile noi - fara id_vanzare_det, il genereaza trigger-ul TRG_VANZARI_DET_BEFOINS
IF m.llSucces
SELECT (m.lcAliasArt)
lnRecnoArt = Recno()
SCAN FOR id_vanzare_det = 0 AND Nvl(sters,0) <> 1
lcSql = [insert into vanzari_detalii (id_vanzare, id_articol, cantitate, pret, pret_cu_tva, proc_tvav, discount_unitar, ] + ;
[id_gestiune, cont, id_valuta, id_jtva_coloana, serie, explicatie, taxcode, lot, pret_achizitie, id_utils, dataoras) values (] + ;
Alltrim(Str(m.tnIdVanzare)) + [,] + Alltrim(Str(Nvl(id_articol,0))) + [,] + Alltrim(Str(Nvl(cantitate,0),18,3)) + [,] + ;
Alltrim(Str(Nvl(pret,0),18,4)) + [,] + Alltrim(Str(Nvl(pret_cu_tva,0))) + [,] + Alltrim(Str(Nvl(proc_tvav,0),18,4)) + [,] + ;
Alltrim(Str(Nvl(discount_unitar,0),18,4)) + [,] + Iif(Isnull(id_gestiune),[NULL],Alltrim(Str(id_gestiune))) + [,] + ;
Iif(Empty(Nvl(cont,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(cont,''))) + [']) + [,] + ;
Iif(Isnull(id_valuta),[NULL],Alltrim(Str(id_valuta))) + [,] + Iif(Isnull(id_jtva_coloana),[NULL],Alltrim(Str(id_jtva_coloana))) + [,] + ;
Iif(Empty(Nvl(serie,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(serie,''))) + [']) + [,] + ;
Iif(Empty(Nvl(explicatie,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(explicatie,''))) + [']) + [,] + ;
Iif(Isnull(taxcode),[NULL],Alltrim(Str(taxcode))) + [,] + ;
Iif(Empty(Nvl(lot,'')),[NULL],['] + OracleSpecialCharacters(Alltrim(Nvl(lot,''))) + [']) + [,] + ;
Alltrim(Str(Nvl(pret_achizitie,0),18,4)) + [,] + Alltrim(Str(gnIdUtil)) + [, sysdate)]
llSucces = goExecutor.oExecuta(m.lcSql)
IF !m.llSucces
EXIT
ENDIF
ENDSCAN
TRY
GO m.lnRecnoArt IN (m.lcAliasArt)
CATCH
GO TOP IN (m.lcAliasArt)
ENDTRY
ENDIF
*!* recalculul totalurilor, cu discountul documentului dat ca parametru - .NULL. pastreaza discountul curent
IF m.llSucces
lcSql = [begin pack_facturare.recalculeaza_totaluri_vanzari(] + Alltrim(Str(m.tnIdVanzare)) + [,] + ;
Iif(m.llDiscountNul,[NULL],Alltrim(Str(m.lnDiscount,18,4))) + [); end;]
llSucces = goExecutor.oExecuta(m.lcSql)
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.llSucces
ENDFUNC && ScrieArticoleFacturaEditate

View File

@@ -355,11 +355,15 @@ PROCEDURE verifica_editare_grid
loForm.Show()
loGrid = loForm.pgfArticole.Page3.grdArticoleFactura
llStructuraOk = (loGrid.ColumnCount == 14) AND ;
(Type('tvd.lmodificat') == 'L') AND (Type('tvd.valoare') == 'N')
*!* S5: coloana noua Column7=pret_achizitie (dupa Column6=pret), Column8..15 = restul, deplasate cu 1;
*!* pret_achizitie e editabila la nivel de coloana (gating-ul e mutat in When, per rand - decizia S5.C)
llStructuraOk = (loGrid.ColumnCount == 15) AND ;
(Type('tvd.lmodificat') == 'L') AND (Type('tvd.valoare') == 'N') AND ;
(Type('tvd.pret_achizitie') == 'N') AND (Type('tvd.id_vanzare_set') == 'N')
llReadOnlyOk = (loGrid.Column5.ReadOnly == .F.) AND (loGrid.Column6.ReadOnly == .F.) AND ;
(loGrid.Column7.ReadOnly == .F.) AND (Pemstatus(loGrid.Column7, '_checkbox1', 5)) AND ;
(loGrid.Column1.ReadOnly == .T.) AND (loGrid.Column14.ReadOnly == .T.)
(loGrid.Column7.ReadOnly == .F.) AND (loGrid.Column8.ReadOnly == .F.) AND ;
(Pemstatus(loGrid.Column8, '_checkbox1', 5)) AND ;
(loGrid.Column1.ReadOnly == .T.) AND (loGrid.Column15.ReadOnly == .T.)
IF Reccount('tvd') > 0
Go Top In tvd
@@ -379,8 +383,8 @@ PROCEDURE verifica_editare_grid
ENDIF
STRTOFILE('--- S4 runda 3A, editare grid, cod=' + TRANSFORM(tnCod) + ' ---' + CHR(13) + CHR(10) + ;
' structura grid/cursor (ColumnCount=14, lmodificat L, valoare N) = ' + IIF(llStructuraOk, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' ReadOnly (cantitate/pret/pret_cu_tva editabile, checkbox pe pret_cu_tva, restul readonly) = ' + IIF(llReadOnlyOk, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' structura grid/cursor (ColumnCount=15, lmodificat L, valoare N, pret_achizitie N, id_vanzare_set N) = ' + IIF(llStructuraOk, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' ReadOnly (cantitate/pret/pret_achizitie editabile, checkbox pe pret_cu_tva, restul readonly) = ' + IIF(llReadOnlyOk, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' stare initiala (lmodificat=.F., valoare calculata corect) = ' + IIF(llInitOk, 'PASS', 'FAIL') + CHR(13) + CHR(10) + ;
' dupa editare cantitate (lmodificat=.T., valoare recalculata) = ' + IIF(llDupaEditareOk, 'PASS', 'FAIL') + CHR(13) + CHR(10), tcLog, 1)

View File

@@ -0,0 +1,415 @@
* test_s5_al_doilea_intrare.prg
* Inchide golul "al doilea punct de intrare" din docs\livrare_s5.md, sectiunea 5: agatarea din
* comun.vc2:2491-2493 (afisjurcom.do_modifica, "Registrul jurnal" ROACONT/ROAGEST, textual identica
* cu cea din ofacturare_comun.vc2:3828-3830, deja testata cu scriere reala in test_s5_scriere_reala.prg)
* nu trecuse inca prin nicio scriere reala.
*
* Diferenta functionala fata de do_editare_factura (frm_facturi), singura care conteaza pentru acest
* test: afisjurcom.do_modifica NU apeleaza IncarcaVanzareDinNota direct - apeleaza
* PregatesteArticoleFacturaEditare('tact') (comun.vc2:2436) INAINTE de Createobject(frm_modific2024),
* care populeaza tvanz (si crsArticoleFactura) pe alta cale de cod. Testul reproduce EXACT secventa
* comun.vc2:2222-2569 (do_modifica), mai putin partea de UI (grid-ul actjur, frm_modific2024 - nu
* garda "Used('tvanz') And Reccount('tvanz')=1" testata prin PregatesteArticoleFacturaEditare reala,
* nici SCAN pe articole, care ramane la verificarea vizuala a lui Marius, per briefing), pe aceeasi
* nota folosita deja de test_s5_scriere_reala.prg/test_s5_discount_valuta.prg (id_vanzare=1049) -
* SINGURA din documentele de test cunoscute care are simultan rand real in VANZARI si nota in luna
* curenta (cerinta lui afisjurcom.do_modifica, linia 2265-2268).
*
* Fara nicio modificare de articole (ca in blocul C din test_s5_discount_valuta.prg): dovedeste ca
* PE ACEASTA CALE lnSucces ajunge > 0 dupa finalizeaza_modificare_nota, garda noua se satisface real
* (nu doar static) si ScrieArticoleFacturaEditate se executa cu tvanz.id_vanzare corect. Tranzactia
* se inchide cu COMMIT (Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1)), comun.vc2:2541) - lantul
* reuseste, deci NU ROLLBACK (spre deosebire de celelalte suite S5 cu esec provocat deliberat).
*
* ATENTIE - GASIT SI REPARAT IN TIMPUL SCRIERII ACESTUI TEST (nu ramane in fisierul final): o prima
* varianta apela ScrieArticoleFacturaEditate FARA sa incarce intai 'tvd' (frm_modific2024, sarit aici
* deliberat, e cel care il populeaza in fluxul real, din crsArticoleFactura). Garda externa
* (Used('tvanz') And Reccount('tvanz')=1) s-a satisfacut, dar garda INTERNA de no-op a helperului
* (!Used('tvd')) a facut din apel un no-op TACUT (intoarce tot .T.) - fara sa corecteze reset-ul
* STERS=0 pe care actualizeaza_vanzari il face pe TOT documentul (acelasi mecanism documentat in
* rec_s5_scriere_reala.md), iar tranzactia a COMMIS reset-ul necorectat: o linie definitiv stearsa
* in testele anterioare (det=1582, id_vanzare=1049) a fost reinviata real in Oracle. Reparat imediat
* (UPDATE vanzari_detalii SET STERS=1 WHERE id_vanzare_det=1582, COMMIT, verificat prin sqlplus) -
* varianta finala incarca 'tvd' cu IncarcaArticoleFactura chiar dupa PregatesteArticoleFacturaEditare
* (ca sa reproduca ce ar face Load()-ul lui frm_modific2024), deci helperul chiar corecteaza reset-ul,
* la fel ca in fluxul real. CONSUMA UN COD (finalizeaza_modificare_nota il realoca la orice salvare,
* chiar fara modificari de articole - acelasi efect ca blocul C din test_s5_discount_valuta.prg).
*
* Harness: do_deschide/inchide_tranzactie reproduse inline dupa _frm_base.vc2:252-302 (identic cu
* celelalte suite S5 cu scriere reala). Nu atinge cod de aplicatie, nu instantiaza afisjurcom sau
* frm_modific2024 (evita riscul de UI blocanta headless). QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s5_al_doilea_intrare_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gcTestLog, gnPass, gnFail
gcErrLog = lcLog
gcTestLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
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\programe\oscrie_in_fisiere.prg ADDITIVE
PUBLIC gnIdVanzareTest
gnIdVanzareTest = 1049
DO test_al_doilea_intrare WITH gnIdVanzareTest, lcLog
STRTOFILE(CHR(13) + CHR(10) + 'REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ENDIF
ENDPROC
*-- executa SELECT-ul prin goExecutor (cai care CITESC - fara risc) si intoarce codul de succes
PROCEDURE ExecSel
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
ENDPROC
*-- reproduce _frm_base.vc2:252-265 (do_deschide_tranzactie / Thisform.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 / Thisform.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_al_doilea_intrare
LPARAMETERS tnIdVanzare, tcLog
*-- pnAn/pnLuna/lnCod/lnIdFact/lnIdfactd/lnIdSet/pdDataOra PRIVATE - finalizeaza_modificare_nota
*-- foloseste ?pnLuna/?pnAn/?pdDataOra ca bind-variabile in josul stivei (comun.vc2:2487-2488)
PRIVATE pnAn, pnLuna, lnCod, lnIdFact, lnIdfactd, lnIdSet, pdDataOra
LOCAL lcSql, lnQ, lnSters, llEProforma, llSters, llGestInvantar, llRul, lnSucces
LOCAL lnCodInainte, lnCodDupa, lnDiscInainte, lnFtvaInainte, lnTvaInainte, lnCtvaInainte
LOCAL lnDiscDupa, lnFtvaDupa, lnTvaDupa, lnCtvaDupa, lnNrLiniiInainte, lnNrLiniiDupa
LOCAL llBlocExecutat
llBlocExecutat = .F.
STRTOFILE(CHR(13) + CHR(10) + '========== al doilea punct de intrare (afisjurcom.do_modifica, comun.vc2:2222-2569), ' + ;
'id_vanzare=' + TRANSFORM(tnIdVanzare) + ' ==========' + CHR(13) + CHR(10), tcLog, 1)
*-- starea documentului INAINTE, citita direct din VANZARI (echivalentul "cod-ul se realoca la fiecare salvare")
lcSql = [select cod, nvl(sters,0) as sters, discount, total_fara_tva, total_tva, total_cu_tva, ] + ;
[extract(year from data_act) as anact, extract(month from data_act) as lunaact ] + ;
[from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz0')
IF lnQ <= 0 OR RECCOUNT('v_vz0') <> 1
DO asserteaza WITH '0: VANZARI citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT v_vz0
lnCod = cod
lnCodInainte = cod
lnDiscInainte = NVL(discount, 0)
lnFtvaInainte = total_fara_tva
lnTvaInainte = total_tva
lnCtvaInainte = total_cu_tva
USE IN v_vz0
lcSql = [select count(*) as nr from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ and sters = 0]
lnQ = ExecSel(lcSql, 'v_cnt0')
lnNrLiniiInainte = IIF(lnQ > 0, v_cnt0.nr, -1)
USE IN v_cnt0
STRTOFILE(' VANZARI inainte: cod=' + TRANSFORM(lnCodInainte) + ' discount=' + TRANSFORM(lnDiscInainte) + ;
' ftva=' + TRANSFORM(lnFtvaInainte) + ' tva=' + TRANSFORM(lnTvaInainte) + ' ctva=' + TRANSFORM(lnCtvaInainte) + ;
' linii active=' + TRANSFORM(lnNrLiniiInainte) + CHR(13) + CHR(10), tcLog, 1)
*-- 1) "Select actjur" (comun.vc2:2253-2263) - un rand din vact_tot, exact ca gridul registrului jurnal
lcSql = [select cod, an, luna, id_set, sters, id_fact, id_factd, dataora from vact_tot ] + ;
[where cod = ] + TRANSFORM(lnCod) + [ and rownum = 1]
lnQ = ExecSel(lcSql, 'actjur')
IF lnQ <= 0 OR RECCOUNT('actjur') <> 1
DO asserteaza WITH '1: actjur (randul din registrul jurnal) gasit pentru cod=' + TRANSFORM(lnCod), .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT actjur
pnAn = an
pnLuna = luna
lnIdSet = id_set
lnSters = sters
lnIdFact = id_fact
lnIdfactd = id_factd
pdDataOra = NULL
STRTOFILE(' actjur: cod=' + TRANSFORM(lnCod) + ' an/luna=' + TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna) + ;
' id_set=' + TRANSFORM(lnIdSet) + ' sters=' + TRANSFORM(lnSters) + ' id_fact=' + TRANSFORM(lnIdFact) + CHR(13) + CHR(10), tcLog, 1)
*-- 2) garzile din do_modifica (comun.vc2:2265-2290), fara UI
DO asserteaza WITH '2: nota din luna curenta (garda comun.vc2:2265)', (pnAn * 12) + pnLuna = (gnAn * 12) + gnLuna, ;
TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna) + ' vs curent ' + TRANSFORM(gnAn) + '/' + TRANSFORM(gnLuna)
IF (pnAn * 12) + pnLuna <> (gnAn * 12) + gnLuna
RETURN
ENDIF
DO asserteaza WITH '3: id_set NU e in intervalul ROAPRODUCTIE (30000-30009, comun.vc2:2278)', !BETWEEN(lnIdSet, 30000, 30009), TRANSFORM(lnIdSet)
IF BETWEEN(lnIdSet, 30000, 30009)
RETURN
ENDIF
llSters = (lnSters = 1)
DO asserteaza WITH '4: nota nu e deja stearsa (llSters=.F.)', !llSters, TRANSFORM(lnSters)
llGestInvantar = (lnIdSet = 90101)
DO asserteaza WITH '5: nu e nota de inventariere (id_set<>90101, ramura pdDataOra=actjur.dataora nu se activeaza)', !llGestInvantar, TRANSFORM(lnIdSet)
IF llGestInvantar
pdDataOra = actjur.dataora
ENDIF
*-- 3) cursoarele notei (actactan/tact/trul/trul_obinv) - identic cu comun.vc2:2313-2427,
*-- deja extras ca helper si folosit de test_s5_scriere_reala.prg pe aceeasi nota
IF !IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.)
DO asserteaza WITH '6: IncarcaCursoareModificareNota', .F., 'a esuat'
RETURN
ENDIF
DO asserteaza WITH '6: nota are randuri in actactan/tact', RECCOUNT('actactan') > 0, TRANSFORM(RECCOUNT('actactan'))
IF RECCOUNT('actactan') = 0
RETURN
ENDIF
*-- 4) cu_Tva pe tact - a doua reasignare, comun.vc2:2433 (identica cu ofacturare_comun.vc2/test_s5_scriere_reala.prg)
SELECT a.*, IIF(NVL(id_jtva_coloana, 0) = 0, 0, 1) AS cu_Tva FROM tact a INTO CURSOR tact READWRITE
*-- 5) PregatesteArticoleFacturaEditare('tact') - comun.vc2:2436, DIFERENTA functionala fata de
*-- ofacturare_comun.vc2/do_editare_factura (care apeleaza IncarcaVanzareDinNota direct); aici
*-- populeaza tvanz SI crsArticoleFactura, calea specifica acestui punct de intrare
IF USED('tvanz')
USE IN tvanz
ENDIF
IF USED('crsArticoleFactura')
USE IN crsArticoleFactura
ENDIF
LOCAL llGasitTvanz
llGasitTvanz = PregatesteArticoleFacturaEditare('tact')
DO asserteaza WITH '7: PregatesteArticoleFacturaEditare gaseste documentul in VANZARI (calea specifica acestui punct de intrare)', ;
llGasitTvanz, TRANSFORM(llGasitTvanz)
DO asserteaza WITH '8: garda noua satisfacuta REAL (nu static): Used(tvanz) And Reccount(tvanz)=1', ;
USED('tvanz') AND RECCOUNT('tvanz') = 1, 'Used=' + TRANSFORM(USED('tvanz')) + ' Reccount=' + IIF(USED('tvanz'), TRANSFORM(RECCOUNT('tvanz')), '-')
DO asserteaza WITH '9: tvanz.id_vanzare corect (populat de PregatesteArticoleFacturaEditare, nu presupus)', ;
USED('tvanz') AND RECCOUNT('tvanz') = 1 AND tvanz.id_vanzare = tnIdVanzare, ;
IIF(USED('tvanz') AND RECCOUNT('tvanz') = 1, TRANSFORM(tvanz.id_vanzare), 'tvanz gol')
DO asserteaza WITH '10: crsArticoleFactura precarcat (acelasi apel)', USED('crsArticoleFactura'), TRANSFORM(USED('crsArticoleFactura'))
*-- frm_modific2024 SARIT (nu se instantiaza - risc de UI blocanta headless, verificare vizuala
*-- ramane la Marius, per briefing); buton=1 fortat direct, ca in celelalte suite S5. In fluxul
*-- real, Load()-ul formularului populeaza 'tvd' din crsArticoleFactura - reprodus aici explicit,
*-- FARA nicio modificare, ca ScrieArticoleFacturaEditate sa nu fie no-op (vezi ATENTIE din antet)
IF USED('tvd')
USE IN tvd
ENDIF
IncarcaArticoleFactura(tnIdVanzare, 'tvd')
DO asserteaza WITH '10b: tvd incarcat cu liniile active reale (ca Load()-ul lui frm_modific2024)', ;
USED('tvd') AND RECCOUNT('tvd') = lnNrLiniiInainte, 'Reccount=' + IIF(USED('tvd'), TRANSFORM(RECCOUNT('tvd')), '-') + ' asteptat=' + TRANSFORM(lnNrLiniiInainte)
*-- 6) ramura buton=1 (comun.vc2:2447-2493), copiata identic; llRul=.T. neconditionat (:2449,
*-- "este posibil sa modific si rul, rul_obinv" - asa cum scrie codul, nu dupa Reccount(trul))
llRul = .T.
lnSucces = 0
TRY
IF MyDeschideTranzactie()
SELECT actactan
IF !llSters
lnSucces = OSCRIE_IN_FISIERE(2, .T., llRul)
ELSE
lnSucces = 1
ENDIF
STRTOFILE(' OSCRIE_IN_FISIERE(2,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
IF lnSucces > 0
IF USED('actactan')
USE IN actactan
ENDIF
SELECT tact
IF gnAn >= IIF(TYPE('gnAnFormNou') = 'U', 2007, gnAnFormNou)
REPLACE id_jtva_coloana WITH NULL, proc_tva WITH 0 FOR cu_Tva = 0
ENDIF
SELECT * FROM tact INTO CURSOR actactan READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
IF llRul
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
ENDIF
lnSucces = OSCRIE_IN_FISIERE(0, .T., llRul)
STRTOFILE(' OSCRIE_IN_FISIERE(0,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0
lcSql = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,?pdDataOra,] + ALLTRIM(STR(lnIdSet)) + [,] + ;
ALLTRIM(STR(lnCod)) + [,] + ALLTRIM(STR(lnIdFact)) + [,] + ALLTRIM(STR(lnIdfactd)) + [,?gnIdUtil); end;]
lnSucces = IIF(goExecutor.oExecuta(lcSql), 1, -1)
STRTOFILE(' finalizeaza_modificare_nota => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
DO asserteaza WITH '11: lantul pana la finalizeaza_modificare_nota a reusit (lnSucces>0)', lnSucces > 0, 'lnSucces=' + TRANSFORM(lnSucces)
*-- blocul nou, comun.vc2:2491-2493, identic textual cu ofacturare_comun.vc2:3828-3830
IF lnSucces > 0 AND "OFACTURARE_EDITARE" $ UPPER(SET("Procedure")) AND USED('tvanz') AND RECCOUNT('tvanz') = 1
llBlocExecutat = .T.
lnSucces = IIF(ScrieArticoleFacturaEditate(tvanz.id_vanzare, 'tvd', 'tvanz'), 1, -1)
STRTOFILE(' ScrieArticoleFacturaEditate => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' ATENTIE: blocul ScrieArticoleFacturaEditate NU s-a executat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
*-- comun.vc2:2541 - Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,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 inchiderea tranzactiei' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ELSE
STRTOFILE(' FAIL: deschiderea tranzactiei 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
DO asserteaza WITH '12: blocul ScrieArticoleFacturaEditate S-A EXECUTAT pe aceasta cale (garda satisfacuta)', ;
llBlocExecutat, TRANSFORM(llBlocExecutat)
DO asserteaza WITH '13: lantul complet (inclusiv ScrieArticoleFacturaEditate prin acest punct de intrare) a reusit', ;
lnSucces > 0, 'lnSucces=' + TRANSFORM(lnSucces)
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('crsArticoleFactura')
USE IN crsArticoleFactura
ENDIF
IF USED('tvd')
USE IN tvd
ENDIF
IF USED('tvanz')
USE IN tvanz
ENDIF
IF USED('actjur')
USE IN actjur
ENDIF
*-- verificare de fond: lantul a COMIS (lnSucces>0) - COD-ul se realoca (ca in blocul C din
*-- test_s5_discount_valuta.prg), dar continutul liniilor si totalurile raman identice, pentru ca
*-- 'tvd' a fost incarcat fara nicio modificare (vezi ATENTIE din antet)
lcSql = [select cod, nvl(discount,0) as discount, total_fara_tva, total_tva, total_cu_tva from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz1')
IF lnQ > 0 AND RECCOUNT('v_vz1') = 1
lnCodDupa = v_vz1.cod
lnDiscDupa = v_vz1.discount
lnFtvaDupa = v_vz1.total_fara_tva
lnTvaDupa = v_vz1.total_tva
lnCtvaDupa = v_vz1.total_cu_tva
ENDIF
USE IN v_vz1
lcSql = [select count(*) as nr from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ and sters = 0]
lnQ = ExecSel(lcSql, 'v_cnt1')
lnNrLiniiDupa = IIF(lnQ > 0, v_cnt1.nr, -1)
USE IN v_cnt1
STRTOFILE(' DUPA COMMIT: cod=' + TRANSFORM(lnCodDupa) + ' (era ' + TRANSFORM(lnCodInainte) + ') discount=' + TRANSFORM(lnDiscDupa) + ;
' ftva=' + TRANSFORM(lnFtvaDupa) + ' tva=' + TRANSFORM(lnTvaDupa) + ' ctva=' + TRANSFORM(lnCtvaDupa) + ;
' linii active=' + TRANSFORM(lnNrLiniiDupa) + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH '14: COD s-a realocat la salvare (ca la orice trecere prin finalizeaza_modificare_nota, chiar fara modificari)', ;
lnCodDupa <> lnCodInainte, TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(lnCodDupa)
DO asserteaza WITH '15: discount/totalurile raman neschimbate (nicio modificare in tvd)', ;
ABS(lnDiscDupa - lnDiscInainte) < 0.0001 AND ABS(lnFtvaDupa - lnFtvaInainte) < 0.0001 ;
AND ABS(lnTvaDupa - lnTvaInainte) < 0.0001 AND ABS(lnCtvaDupa - lnCtvaInainte) < 0.0001, ;
'disc=' + TRANSFORM(lnDiscDupa) + ' ftva=' + TRANSFORM(lnFtvaDupa) + ' tva=' + TRANSFORM(lnTvaDupa) + ' ctva=' + TRANSFORM(lnCtvaDupa)
DO asserteaza WITH '16: numarul de linii active ramane neschimbat (idempotent, ca la reeditare fara modificari)', ;
lnNrLiniiDupa = lnNrLiniiInainte, 'dupa=' + TRANSFORM(lnNrLiniiDupa) + ' inainte=' + TRANSFORM(lnNrLiniiInainte)
*-- verificarea CRITICA: linia definitiv stearsa in testele anterioare (det=1582) trebuie sa
*-- RAMANA stearsa - exact reset-ul pe care ScrieArticoleFacturaEditate trebuie sa-l corecteze
LOCAL lnStersLiniaVecheStearsa
lcSql = [select nvl(sters,0) as sters from vanzari_detalii where id_vanzare_det = 1582]
lnQ = ExecSel(lcSql, 'v_1582')
lnStersLiniaVecheStearsa = IIF(lnQ > 0 AND RECCOUNT('v_1582') = 1, v_1582.sters, -1)
USE IN v_1582
DO asserteaza WITH '17: linia definitiv stearsa in testele anterioare (det=1582) a ramas stearsa dupa aceasta salvare', ;
lnStersLiniaVecheStearsa = 1, 'sters=' + TRANSFORM(lnStersLiniaVecheStearsa)
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,816 @@
* test_s5_discount_valuta.prg
* Inchide cele doua goluri declarate de test_s5_scriere_reala: parametrul de discount al procedurii
* pack_facturare.recalculeaza_totaluri_vanzari (decizia 41) si documentele in valuta.
*
* Contractul verificat: recalculeaza_totaluri_vanzari(V_ID_VANZARE, V_DISCOUNT DEFAULT NULL), cu
* NULL = "pastreaza valoarea curenta". Cazul periculos e al doilea: daca NULL ar zeroa tacit
* discountul, orice salvare ulterioara a documentului l-ar sterge.
*
* BLOCUL A - contractul pe un document in RON (id_vanzare = 1049, in_valuta = 0). Patru apeluri
* succesive in aceeasi tranzactie, cu citirea starii necomise intre ele:
* NULL pe discount 0 -> recalculul reproduce exact starea persistata (idempotent)
* valoare nenula -> ajunge in VANZARI.DISCOUNT si scade totalurile
* NULL peste ea -> VERDICTUL: pastreaza sau zeroeaza
* 0 -> readuce documentul la starea de plecare
* Tranzactia se inchide cu ROLLBACK, deci blocul NU consuma date.
*
* BLOCUL B - acelasi contract pe un document REAL in valuta (id_vanzare = 1037, in_valuta = 1,
* id_valuta = 2, curs = 5.2688, cu rand in VANZARI_CURSURI). Aici se exercita ramura
* decode(in_valuta, 1, ROUND(curs * discount / multiplicator, PPRETV), discount) din procedura -
* discountul e exprimat in valuta si se converteste la RON prin cursul documentului. Se verifica
* si perechea valval/tvaval/totval. Documentul e din 05.2026 (arhiva), deci tranzactia se inchide
* obligatoriu cu ROLLBACK: nimic nu se modifica in el.
*
* BLOCUL C - lantul real de salvare pe un document cu discount NENUL (id_vanzare = 1049). Singurul
* bloc care scrie: pune discountul prin procedura (COMMIT), ruleaza lantul complet de editare fara
* nicio modificare de linii (COMMIT) si verifica ca discountul supravietuieste salvarii - adica
* tvanz.discount (citit din VANZARI de IncarcaVanzareNota) ajunge inapoi in procedura prin
* ScrieArticoleFacturaEditate. La final restaureaza discount = 0 si totalurile initiale (COMMIT).
* CONSUMA UN COD: VANZARI.COD se realoca la salvare.
*
* Harness identic cu test_s5_scriere_reala.prg: frm_modific2024 ocolit, buton = 1 fortat,
* do_deschide/inchide_tranzactie reproduse inline dupa _frm_base.vc2:252-302. Nu atinge cod de
* aplicatie, nu modifica PACK_FACTURARE. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s5_discount_valuta_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gcTestLog, gnPass, gnFail
gcErrLog = lcLog
gcTestLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
*-- mock amessagebox incarcat PRIMUL, inaintea lantului real (la nume duplicat castiga primul gasit)
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
LOCAL lnVerifMock
lnVerifMock = amessagebox('verificare mock', 4+32, 'test')
STRTOFILE('verificare mock amessagebox = ' + TRANSFORM(lnVerifMock) + ;
IIF(lnVerifMock = 6, ' (OK, mock activ)', ' (ATENTIE: amessagebox real, 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
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\odocumente.prg ADDITIVE
IF TYPE('gnPC') <> 'N'
PUBLIC gnPC
gnPC = 2
ENDIF
PUBLIC gcMockUltimMesaj, gnMockUltimTip
gcMockUltimMesaj = ''
gnMockUltimTip = 0
*-- documentele de test si valorile de discount folosite
PUBLIC gnIdRon, gnIdValuta, gnDiscRon, gnDiscValuta, gnPPretV
gnIdRon = 1049
gnIdValuta = 1037
gnDiscRon = 12.5
gnDiscValuta = 10
gnPPretV = 2
*-- capul documentului citit de CitesteCap
PUBLIC gnCapCod, gnCapSters, gnCapInValuta, gnCapDisc, gnCapDiscNul, gnCapDiscTva
PUBLIC gnCapVA, gnCapFtva, gnCapTva, gnCapCtva, gnCapValval, gnCapTvaval, gnCapTotval
PUBLIC gnCapIdValuta, gnCapCurs, gnCapMult
STRTOFILE('gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + ' gnIdUtil=' + TRANSFORM(gnIdUtil) + CHR(13) + CHR(10), lcLog, 1)
*-- precizia de pret in valuta, din optiunile firmei - intra in formula de conversie a discountului
IF ExecSel([select to_number(pack_sesiune.getOptiuneFirma('PPRETV')) as ppretv from dual], 'v_opt') > 0
gnPPretV = NVL(v_opt.ppretv, 2)
USE IN v_opt
ENDIF
STRTOFILE('PPRETV = ' + TRANSFORM(gnPPretV) + CHR(13) + CHR(10), lcLog, 1)
DO bloc_a_discount_ron WITH lcLog
DO bloc_b_valuta WITH lcLog
DO bloc_c_lant_real WITH lcLog
STRTOFILE(CHR(13) + CHR(10) + 'REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ENDIF
ENDPROC
*-- executa SELECT-ul prin goExecutor si intoarce codul de succes; cursorul vechi se inchide intai
PROCEDURE ExecSel
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
ENDPROC
*-- citeste capul documentului in variabilele gnCap*; citirea merge pe aceeasi conexiune, deci vede
*-- si starea necomisa din interiorul tranzactiei. Intoarce .F. daca randul nu exista.
PROCEDURE CitesteCap
LPARAMETERS tnId
LOCAL lcSql, lnQ
lcSql = [select cod, nvl(sters,0) as sters, nvl(in_valuta,0) as in_valuta, ] + ;
[nvl(discount,0) as discount, nvl2(discount,0,1) as disc_nul, nvl(discount_tva,0) as discount_tva, ] + ;
[nvl(valoare_achizitie,0) as valoare_achizitie, nvl(total_fara_tva,0) as total_fara_tva, ] + ;
[nvl(total_tva,0) as total_tva, nvl(total_cu_tva,0) as total_cu_tva, ] + ;
[nvl(valval,0) as valval, nvl(tvaval,0) as tvaval, nvl(totval,0) as totval, ] + ;
[nvl(id_valuta,0) as id_valuta, nvl(curs,0) as curs, nvl(multiplicator,0) as multiplicator ] + ;
[from vanzari where id_vanzare = ] + TRANSFORM(tnId)
lnQ = ExecSel(lcSql, 'v_cap')
IF lnQ <= 0 OR RECCOUNT('v_cap') <> 1
RETURN .F.
ENDIF
SELECT v_cap
gnCapCod = cod
gnCapSters = sters
gnCapInValuta = in_valuta
gnCapDisc = discount
gnCapDiscNul = disc_nul
gnCapDiscTva = discount_tva
gnCapVA = valoare_achizitie
gnCapFtva = total_fara_tva
gnCapTva = total_tva
gnCapCtva = total_cu_tva
gnCapValval = valval
gnCapTvaval = tvaval
gnCapTotval = totval
gnCapIdValuta = id_valuta
gnCapCurs = curs
gnCapMult = multiplicator
USE IN v_cap
RETURN .T.
ENDPROC
PROCEDURE LogCap
LPARAMETERS tcEticheta, tcLog
STRTOFILE(' [' + tcEticheta + '] cod=' + TRANSFORM(gnCapCod) + ' in_valuta=' + TRANSFORM(gnCapInValuta) + ;
' discount=' + TRANSFORM(gnCapDisc) + IIF(gnCapDiscNul = 1, ' (NULL in baza)', '') + ;
' discount_tva=' + TRANSFORM(gnCapDiscTva) + ' val_ach=' + TRANSFORM(gnCapVA) + CHR(13) + CHR(10) + ;
' ftva=' + TRANSFORM(gnCapFtva) + ' tva=' + TRANSFORM(gnCapTva) + ' ctva=' + TRANSFORM(gnCapCtva) + ;
' | valval=' + TRANSFORM(gnCapValval) + ' tvaval=' + TRANSFORM(gnCapTvaval) + ' totval=' + TRANSFORM(gnCapTotval) + ;
' | id_valuta=' + TRANSFORM(gnCapIdValuta) + ' curs=' + TRANSFORM(gnCapCurs) + ' mult=' + TRANSFORM(gnCapMult) + ;
CHR(13) + CHR(10), tcLog, 1)
ENDPROC
*-- apeleaza procedura de recalcul; tcDiscount e literalul SQL ('NULL' sau o valoare)
PROCEDURE ApelRecalc
LPARAMETERS tnId, tcDiscount, tcLog
LOCAL lcSql, llOk
lcSql = [begin pack_facturare.recalculeaza_totaluri_vanzari(] + ALLTRIM(STR(tnId)) + [,] + tcDiscount + [); end;]
llOk = goExecutor.oExecuta(lcSql)
STRTOFILE(' APEL ' + lcSql + ' => ' + IIF(llOk, 'OK', 'ESEC [' + ALLTRIM(TRANSFORM(NVL(goExecutor.cEroare, ''))) + ']') + CHR(13) + CHR(10), tcLog, 1)
RETURN llOk
ENDPROC
*-- cota de TVA maxima de pe liniile active - intra in formula discountului de TVA din procedura
PROCEDURE CotaMaxima
LPARAMETERS tnId
LOCAL lcSql, lnQ, lnRez
lnRez = 0
lcSql = [select nvl(max(proc_tvav),0) as tvavmax from vanzari_detalii where id_vanzare = ] + ;
TRANSFORM(tnId) + [ and sters = 0]
lnQ = ExecSel(lcSql, 'v_tvav')
IF lnQ > 0 AND RECCOUNT('v_tvav') = 1
lnRez = v_tvav.tvavmax
ENDIF
IF USED('v_tvav')
USE IN v_tvav
ENDIF
RETURN lnRez
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
*=====================================================================================
*-- BLOCUL A: contractul parametrului de discount, pe document in RON, cu ROLLBACK la final
*=====================================================================================
PROCEDURE bloc_a_discount_ron
LPARAMETERS tcLog
LOCAL lnBaseDisc, lnBaseDiscTva, lnBaseVA, lnBaseFtva, lnBaseTva, lnBaseCtva
LOCAL lnP1Disc, lnP1Ftva, lnP1Tva, lnP1Ctva, lnP1DiscTva, lnP1VA
LOCAL lnTvavMax, lnAstFtva, lnAstDiscTva, llOk, lcEt
lcEt = 'A'
STRTOFILE(CHR(13) + CHR(10) + '========== BLOCUL A - contractul de discount pe document in RON (id_vanzare=' + ;
TRANSFORM(gnIdRon) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
IF !CitesteCap(gnIdRon)
DO asserteaza WITH lcEt + ': documentul in RON exista', .F., 'id_vanzare=' + TRANSFORM(gnIdRon)
RETURN
ENDIF
DO LogCap WITH 'STARE INITIALA', tcLog
IF gnCapInValuta <> 0
DO asserteaza WITH lcEt + ': documentul ales e in RON (in_valuta=0)', .F., TRANSFORM(gnCapInValuta)
RETURN
ENDIF
lnBaseDisc = gnCapDisc
lnBaseDiscTva = gnCapDiscTva
lnBaseVA = gnCapVA
lnBaseFtva = gnCapFtva
lnBaseTva = gnCapTva
lnBaseCtva = gnCapCtva
lnTvavMax = CotaMaxima(gnIdRon)
STRTOFILE(' cota TVA maxima pe liniile active = ' + TRANSFORM(lnTvavMax) + CHR(13) + CHR(10), tcLog, 1)
IF !MyDeschideTranzactie()
DO asserteaza WITH lcEt + ': tranzactia s-a deschis', .F., 'SQLSetprop a esuat'
RETURN
ENDIF
TRY
*-- PASUL 1: NULL peste discountul curent (0) - recalculul trebuie sa reproduca exact starea
llOk = ApelRecalc(gnIdRon, 'NULL', tcLog)
DO asserteaza WITH lcEt + '1: apelul cu NULL a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF llOk AND CitesteCap(gnIdRon)
DO LogCap WITH 'dupa recalc(NULL) pe discount 0', tcLog
DO asserteaza WITH lcEt + '1: recalculul reproduce exact totalurile persistate (idempotent)', ;
ABS(gnCapFtva - lnBaseFtva) < 0.005 AND ABS(gnCapTva - lnBaseTva) < 0.005 ;
AND ABS(gnCapCtva - lnBaseCtva) < 0.005 AND ABS(gnCapVA - lnBaseVA) < 0.005, ;
'ftva ' + TRANSFORM(lnBaseFtva) + '->' + TRANSFORM(gnCapFtva) + ' tva ' + TRANSFORM(lnBaseTva) + ;
'->' + TRANSFORM(gnCapTva) + ' ctva ' + TRANSFORM(lnBaseCtva) + '->' + TRANSFORM(gnCapCtva)
DO asserteaza WITH lcEt + '1: NULL nu schimba discountul (0 ramane 0)', ;
ABS(gnCapDisc - lnBaseDisc) < 0.0001, TRANSFORM(lnBaseDisc) + ' -> ' + TRANSFORM(gnCapDisc)
ENDIF
*-- PASUL 2: valoare nenula - trebuie sa ajunga in VANZARI.DISCOUNT si sa scada totalurile.
*-- pe document in RON procedura ia discountul ca atare (decode(in_valuta,1,...,discount))
lnAstFtva = lnBaseFtva - gnDiscRon
lnAstDiscTva = ROUND(gnDiscRon * (lnTvavMax - 1), gnPPretV)
llOk = ApelRecalc(gnIdRon, ALLTRIM(STR(gnDiscRon, 18, 4)), tcLog)
DO asserteaza WITH lcEt + '2: apelul cu valoare nenula a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF !llOk OR !CitesteCap(gnIdRon)
MyInchideTranzactie(2)
RETURN
ENDIF
DO LogCap WITH 'dupa recalc(' + TRANSFORM(gnDiscRon) + ')', tcLog
DO asserteaza WITH lcEt + '2: valoarea nenula ajunge in VANZARI.DISCOUNT', ;
ABS(gnCapDisc - gnDiscRon) < 0.0001, 'discount=' + TRANSFORM(gnCapDisc) + ' (cerut ' + TRANSFORM(gnDiscRon) + ')'
DO asserteaza WITH lcEt + '2: total_fara_tva scade exact cu discountul', ;
ABS(gnCapFtva - lnAstFtva) < 0.005, 'ftva=' + TRANSFORM(gnCapFtva) + ' asteptat ' + TRANSFORM(lnAstFtva)
DO asserteaza WITH lcEt + '2: DISCOUNT_TVA = discount * (cota - 1), rotunjit', ;
ABS(gnCapDiscTva - lnAstDiscTva) < 0.011, 'discount_tva=' + TRANSFORM(gnCapDiscTva) + ' asteptat ' + TRANSFORM(lnAstDiscTva)
DO asserteaza WITH lcEt + '2: total_tva scade cu TVA-ul discountului', ;
ABS(gnCapTva - (lnBaseTva - lnAstDiscTva)) < 0.011, ;
'tva=' + TRANSFORM(gnCapTva) + ' asteptat ' + TRANSFORM(lnBaseTva - lnAstDiscTva)
DO asserteaza WITH lcEt + '2: totalurile raman coerente (fara_tva + tva = cu_tva)', ;
ABS((gnCapFtva + gnCapTva) - gnCapCtva) < 0.01, ;
TRANSFORM(gnCapFtva) + ' + ' + TRANSFORM(gnCapTva) + ' = ' + TRANSFORM(gnCapCtva)
DO asserteaza WITH lcEt + '2: VALOARE_ACHIZITIE nu depinde de discount', ;
ABS(gnCapVA - lnBaseVA) < 0.005, TRANSFORM(lnBaseVA) + ' -> ' + TRANSFORM(gnCapVA)
lnP1Disc = gnCapDisc
lnP1Ftva = gnCapFtva
lnP1Tva = gnCapTva
lnP1Ctva = gnCapCtva
lnP1DiscTva = gnCapDiscTva
lnP1VA = gnCapVA
*-- PASUL 3: VERDICTUL. NULL peste un discount nenul - pastreaza sau zeroeaza?
llOk = ApelRecalc(gnIdRon, 'NULL', tcLog)
DO asserteaza WITH lcEt + '3: apelul cu NULL peste discount nenul a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF !llOk OR !CitesteCap(gnIdRon)
MyInchideTranzactie(2)
RETURN
ENDIF
DO LogCap WITH 'VERDICT - dupa recalc(NULL) peste discount ' + TRANSFORM(lnP1Disc), tcLog
DO asserteaza WITH lcEt + '3: VERDICT - NULL PASTREAZA discountul curent, nu-l zeroeaza', ;
ABS(gnCapDisc - lnP1Disc) < 0.0001, ;
'discount inainte=' + TRANSFORM(lnP1Disc) + ' dupa NULL=' + TRANSFORM(gnCapDisc)
DO asserteaza WITH lcEt + '3: NULL lasa toate totalurile neschimbate', ;
ABS(gnCapFtva - lnP1Ftva) < 0.0001 AND ABS(gnCapTva - lnP1Tva) < 0.0001 ;
AND ABS(gnCapCtva - lnP1Ctva) < 0.0001 AND ABS(gnCapDiscTva - lnP1DiscTva) < 0.0001 ;
AND ABS(gnCapVA - lnP1VA) < 0.0001, ;
'ftva=' + TRANSFORM(gnCapFtva) + ' tva=' + TRANSFORM(gnCapTva) + ' ctva=' + TRANSFORM(gnCapCtva)
*-- PASUL 4: 0 explicit - readuce documentul la starea de plecare
llOk = ApelRecalc(gnIdRon, '0', tcLog)
DO asserteaza WITH lcEt + '4: apelul cu 0 a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF llOk AND CitesteCap(gnIdRon)
DO LogCap WITH 'dupa recalc(0)', tcLog
DO asserteaza WITH lcEt + '4: 0 explicit zeroeaza discountul (spre deosebire de NULL)', ;
ABS(gnCapDisc - 0) < 0.0001, 'discount=' + TRANSFORM(gnCapDisc)
DO asserteaza WITH lcEt + '4: totalurile revin exact la starea de plecare', ;
ABS(gnCapFtva - lnBaseFtva) < 0.005 AND ABS(gnCapTva - lnBaseTva) < 0.005 ;
AND ABS(gnCapCtva - lnBaseCtva) < 0.005, ;
'ftva=' + TRANSFORM(gnCapFtva) + ' tva=' + TRANSFORM(gnCapTva) + ' ctva=' + TRANSFORM(gnCapCtva)
ENDIF
CATCH TO loExc
STRTOFILE(' EXCEPTIE in blocul A: ' + TRANSFORM(loExc.ErrorNo) + ' [' + loExc.Message + '] ' + ;
loExc.Procedure + ':' + TRANSFORM(loExc.LineNo) + CHR(13) + CHR(10), tcLog, 1)
ENDTRY
*-- blocul A nu consuma date: tot ce s-a scris mai sus se anuleaza
IF MyInchideTranzactie(2)
STRTOFILE(' tranzactie inchisa cu ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' FAIL la ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF CitesteCap(gnIdRon)
DO LogCap WITH 'DUPA ROLLBACK', tcLog
DO asserteaza WITH lcEt + '5: ROLLBACK-ul a lasat documentul exact cum era (blocul nu consuma date)', ;
ABS(gnCapDisc - lnBaseDisc) < 0.0001 AND ABS(gnCapFtva - lnBaseFtva) < 0.0001 ;
AND ABS(gnCapTva - lnBaseTva) < 0.0001 AND ABS(gnCapCtva - lnBaseCtva) < 0.0001 ;
AND ABS(gnCapDiscTva - lnBaseDiscTva) < 0.0001 AND ABS(gnCapVA - lnBaseVA) < 0.0001, ;
'discount=' + TRANSFORM(gnCapDisc) + ' ftva=' + TRANSFORM(gnCapFtva) + ' tva=' + TRANSFORM(gnCapTva)
ENDIF
ENDPROC
*=====================================================================================
*-- BLOCUL B: acelasi contract pe un document REAL in valuta, cu ROLLBACK la final
*=====================================================================================
PROCEDURE bloc_b_valuta
LPARAMETERS tcLog
LOCAL lnBaseDisc, lnBaseDiscTva, lnBaseVA, lnBaseFtva, lnBaseTva, lnBaseCtva
LOCAL lnBaseValval, lnBaseTvaval, lnBaseTotval, lnBaseIdVal, lnBaseCurs, lnBaseMult
LOCAL lnP1Disc, lnP1Ftva, lnP1Tva, lnP1Valval, lnP1Tvaval
LOCAL lnTvavMax, lnDiscRon, lnAstDiscTvaVal, llOk, lcEt, lnNrCursuri
lcEt = 'B'
STRTOFILE(CHR(13) + CHR(10) + '========== BLOCUL B - document REAL in valuta (id_vanzare=' + ;
TRANSFORM(gnIdValuta) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
IF !CitesteCap(gnIdValuta)
DO asserteaza WITH lcEt + ': documentul in valuta exista', .F., 'id_vanzare=' + TRANSFORM(gnIdValuta)
RETURN
ENDIF
DO LogCap WITH 'STARE INITIALA', tcLog
DO asserteaza WITH lcEt + '0: documentul ales e chiar in valuta (in_valuta=1, cu id_valuta si curs)', ;
gnCapInValuta = 1 AND gnCapIdValuta > 0 AND gnCapCurs > 0 AND gnCapMult > 0, ;
'in_valuta=' + TRANSFORM(gnCapInValuta) + ' id_valuta=' + TRANSFORM(gnCapIdValuta) + ;
' curs=' + TRANSFORM(gnCapCurs) + ' mult=' + TRANSFORM(gnCapMult)
IF gnCapInValuta <> 1 OR gnCapCurs <= 0
RETURN
ENDIF
*-- fara rand in VANZARI_CURSURI, join-ul din procedura da NULL si agregatele se golesc (riscul R5)
lnNrCursuri = 0
IF ExecSel([select count(*) as nr from vanzari_cursuri where id_vanzare = ] + TRANSFORM(gnIdValuta), 'v_crs') > 0
lnNrCursuri = v_crs.nr
USE IN v_crs
ENDIF
DO asserteaza WITH lcEt + '0: documentul are rand in VANZARI_CURSURI (altfel recalculul da NULL)', ;
lnNrCursuri > 0, 'randuri=' + TRANSFORM(lnNrCursuri)
lnBaseDisc = gnCapDisc
lnBaseDiscTva = gnCapDiscTva
lnBaseVA = gnCapVA
lnBaseFtva = gnCapFtva
lnBaseTva = gnCapTva
lnBaseCtva = gnCapCtva
lnBaseValval = gnCapValval
lnBaseTvaval = gnCapTvaval
lnBaseTotval = gnCapTotval
lnBaseIdVal = gnCapIdValuta
lnBaseCurs = gnCapCurs
lnBaseMult = gnCapMult
lnTvavMax = CotaMaxima(gnIdValuta)
STRTOFILE(' cota TVA maxima pe liniile active = ' + TRANSFORM(lnTvavMax) + CHR(13) + CHR(10), tcLog, 1)
IF !MyDeschideTranzactie()
DO asserteaza WITH lcEt + ': tranzactia s-a deschis', .F., 'SQLSetprop a esuat'
RETURN
ENDIF
TRY
*-- PASUL 1: recalcul cu NULL pe un document in valuta emis normal - trebuie sa reproduca
*-- exact totalurile persistate, si in RON si in valuta (proba ca agregarea si conversia
*-- prin curs sunt corecte pe un document real, nu doar pe unul simulat in VFP)
llOk = ApelRecalc(gnIdValuta, 'NULL', tcLog)
DO asserteaza WITH lcEt + '1: apelul cu NULL a mers pe documentul in valuta', llOk, 'llOk=' + TRANSFORM(llOk)
IF !llOk OR !CitesteCap(gnIdValuta)
MyInchideTranzactie(2)
RETURN
ENDIF
DO LogCap WITH 'dupa recalc(NULL)', tcLog
DO asserteaza WITH lcEt + '1: totalurile in RON sunt reproduse exact pe documentul in valuta', ;
ABS(gnCapFtva - lnBaseFtva) < 0.005 AND ABS(gnCapTva - lnBaseTva) < 0.005 ;
AND ABS(gnCapCtva - lnBaseCtva) < 0.005, ;
'ftva ' + TRANSFORM(lnBaseFtva) + '->' + TRANSFORM(gnCapFtva) + ' tva ' + TRANSFORM(lnBaseTva) + ;
'->' + TRANSFORM(gnCapTva) + ' ctva ' + TRANSFORM(lnBaseCtva) + '->' + TRANSFORM(gnCapCtva)
DO asserteaza WITH lcEt + '1: totalurile in valuta (valval/tvaval/totval) sunt reproduse exact', ;
ABS(gnCapValval - lnBaseValval) < 0.005 AND ABS(gnCapTvaval - lnBaseTvaval) < 0.005 ;
AND ABS(gnCapTotval - lnBaseTotval) < 0.005, ;
'valval ' + TRANSFORM(lnBaseValval) + '->' + TRANSFORM(gnCapValval) + ' tvaval ' + ;
TRANSFORM(lnBaseTvaval) + '->' + TRANSFORM(gnCapTvaval) + ' totval ' + TRANSFORM(lnBaseTotval) + ;
'->' + TRANSFORM(gnCapTotval)
DO asserteaza WITH lcEt + '1: valuta documentului (id_valuta/curs/multiplicator) e pastrata', ;
gnCapIdValuta = lnBaseIdVal AND ABS(gnCapCurs - lnBaseCurs) < 0.00001 AND ABS(gnCapMult - lnBaseMult) < 0.00001, ;
'id_valuta=' + TRANSFORM(gnCapIdValuta) + ' curs=' + TRANSFORM(gnCapCurs) + ' mult=' + TRANSFORM(gnCapMult)
DO asserteaza WITH lcEt + '1: NULL nu schimba discountul documentului in valuta', ;
ABS(gnCapDisc - lnBaseDisc) < 0.0001, TRANSFORM(lnBaseDisc) + ' -> ' + TRANSFORM(gnCapDisc)
*-- PASUL 2: discount nenul pe document in valuta. Discountul e exprimat in valuta:
*-- in RON se scade ROUND(curs * discount / multiplicator, PPRETV), in valuta se scade brut.
lnDiscRon = ROUND(lnBaseCurs * gnDiscValuta / lnBaseMult, gnPPretV)
lnAstDiscTvaVal = ROUND(gnDiscValuta * (lnTvavMax - 1), gnPPretV)
STRTOFILE(' discount ' + TRANSFORM(gnDiscValuta) + ' in valuta => ' + TRANSFORM(lnDiscRon) + ' RON (curs ' + ;
TRANSFORM(lnBaseCurs) + ' / mult ' + TRANSFORM(lnBaseMult) + ')' + CHR(13) + CHR(10), tcLog, 1)
llOk = ApelRecalc(gnIdValuta, ALLTRIM(STR(gnDiscValuta, 18, 4)), tcLog)
DO asserteaza WITH lcEt + '2: apelul cu valoare nenula a mers pe documentul in valuta', llOk, 'llOk=' + TRANSFORM(llOk)
IF !llOk OR !CitesteCap(gnIdValuta)
MyInchideTranzactie(2)
RETURN
ENDIF
DO LogCap WITH 'dupa recalc(' + TRANSFORM(gnDiscValuta) + ')', tcLog
DO asserteaza WITH lcEt + '2: valoarea nenula ajunge in VANZARI.DISCOUNT (document in valuta)', ;
ABS(gnCapDisc - gnDiscValuta) < 0.0001, 'discount=' + TRANSFORM(gnCapDisc) + ' (cerut ' + TRANSFORM(gnDiscValuta) + ')'
DO asserteaza WITH lcEt + '2: total_fara_tva scade cu discountul CONVERTIT prin cursul documentului', ;
ABS(gnCapFtva - (lnBaseFtva - lnDiscRon)) < 0.011, ;
'ftva=' + TRANSFORM(gnCapFtva) + ' asteptat ' + TRANSFORM(lnBaseFtva - lnDiscRon)
DO asserteaza WITH lcEt + '2: valval scade cu discountul BRUT, in valuta', ;
ABS(gnCapValval - (lnBaseValval - gnDiscValuta)) < 0.011, ;
'valval=' + TRANSFORM(gnCapValval) + ' asteptat ' + TRANSFORM(lnBaseValval - gnDiscValuta)
DO asserteaza WITH lcEt + '2: DISCOUNT_TVA pastreaza TVA-ul discountului in valuta', ;
ABS(gnCapDiscTva - lnAstDiscTvaVal) < 0.011, ;
'discount_tva=' + TRANSFORM(gnCapDiscTva) + ' asteptat ' + TRANSFORM(lnAstDiscTvaVal)
DO asserteaza WITH lcEt + '2: tvaval scade cu TVA-ul discountului in valuta', ;
ABS(gnCapTvaval - (lnBaseTvaval - lnAstDiscTvaVal)) < 0.011, ;
'tvaval=' + TRANSFORM(gnCapTvaval) + ' asteptat ' + TRANSFORM(lnBaseTvaval - lnAstDiscTvaVal)
DO asserteaza WITH lcEt + '2: totalurile in RON raman coerente (fara_tva + tva = cu_tva)', ;
ABS((gnCapFtva + gnCapTva) - gnCapCtva) < 0.01, ;
TRANSFORM(gnCapFtva) + ' + ' + TRANSFORM(gnCapTva) + ' = ' + TRANSFORM(gnCapCtva)
DO asserteaza WITH lcEt + '2: totalurile in valuta raman coerente (valval + tvaval = totval)', ;
ABS((gnCapValval + gnCapTvaval) - gnCapTotval) < 0.01, ;
TRANSFORM(gnCapValval) + ' + ' + TRANSFORM(gnCapTvaval) + ' = ' + TRANSFORM(gnCapTotval)
lnP1Disc = gnCapDisc
lnP1Ftva = gnCapFtva
lnP1Tva = gnCapTva
lnP1Valval = gnCapValval
lnP1Tvaval = gnCapTvaval
*-- PASUL 3: verdictul pe NULL, repetat pe calea de valuta
llOk = ApelRecalc(gnIdValuta, 'NULL', tcLog)
DO asserteaza WITH lcEt + '3: apelul cu NULL peste discount nenul a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF llOk AND CitesteCap(gnIdValuta)
DO LogCap WITH 'VERDICT valuta - dupa recalc(NULL) peste discount ' + TRANSFORM(lnP1Disc), tcLog
DO asserteaza WITH lcEt + '3: VERDICT - NULL pastreaza discountul si pe documentul in valuta', ;
ABS(gnCapDisc - lnP1Disc) < 0.0001, ;
'discount inainte=' + TRANSFORM(lnP1Disc) + ' dupa NULL=' + TRANSFORM(gnCapDisc)
DO asserteaza WITH lcEt + '3: NULL lasa neschimbate si totalurile in RON, si cele in valuta', ;
ABS(gnCapFtva - lnP1Ftva) < 0.0001 AND ABS(gnCapTva - lnP1Tva) < 0.0001 ;
AND ABS(gnCapValval - lnP1Valval) < 0.0001 AND ABS(gnCapTvaval - lnP1Tvaval) < 0.0001, ;
'ftva=' + TRANSFORM(gnCapFtva) + ' valval=' + TRANSFORM(gnCapValval)
ENDIF
CATCH TO loExc
STRTOFILE(' EXCEPTIE in blocul B: ' + TRANSFORM(loExc.ErrorNo) + ' [' + loExc.Message + '] ' + ;
loExc.Procedure + ':' + TRANSFORM(loExc.LineNo) + CHR(13) + CHR(10), tcLog, 1)
ENDTRY
*-- documentul e din arhiva (2026-05): ROLLBACK obligatoriu, nu se modifica nimic in el
IF MyInchideTranzactie(2)
STRTOFILE(' tranzactie inchisa cu ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' FAIL la ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF CitesteCap(gnIdValuta)
DO LogCap WITH 'DUPA ROLLBACK', tcLog
DO asserteaza WITH lcEt + '4: ROLLBACK-ul a lasat documentul din arhiva exact cum era', ;
ABS(gnCapDisc - lnBaseDisc) < 0.0001 AND ABS(gnCapFtva - lnBaseFtva) < 0.0001 ;
AND ABS(gnCapTva - lnBaseTva) < 0.0001 AND ABS(gnCapCtva - lnBaseCtva) < 0.0001 ;
AND ABS(gnCapValval - lnBaseValval) < 0.0001 AND ABS(gnCapTvaval - lnBaseTvaval) < 0.0001 ;
AND ABS(gnCapTotval - lnBaseTotval) < 0.0001 AND ABS(gnCapVA - lnBaseVA) < 0.0001, ;
'discount=' + TRANSFORM(gnCapDisc) + ' ftva=' + TRANSFORM(gnCapFtva) + ' valval=' + TRANSFORM(gnCapValval)
ENDIF
ENDPROC
*=====================================================================================
*-- BLOCUL C: lantul real de salvare pe un document cu discount NENUL. Singurul care comite.
*=====================================================================================
PROCEDURE bloc_c_lant_real
LPARAMETERS tcLog
*-- pnAn/pnLuna/lnCod/lnIdFact PRIVATE (nu LOCAL), ca in ofacturare_comun.vc2:3730 -
*-- finalizeaza_modificare_nota le foloseste ca bind-variabile ?pnLuna/?pnAn in josul stivei
PRIVATE pnAn, pnLuna, lnCod, lnIdFact
LOCAL lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva, lnBaseVA, lnBaseDiscTva
LOCAL lnCodInainte, lnSters, llEProforma, lnIdSet, lnIdFactD, lnSucces, llGasitRand
LOCAL lcSql, lnQ, lcEt, llOk, lnDiscTvaAst, lnTvavMax, lnNrLinii
lcEt = 'C'
STRTOFILE(CHR(13) + CHR(10) + '========== BLOCUL C - lantul real de salvare pe document cu discount nenul (id_vanzare=' + ;
TRANSFORM(gnIdRon) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
IF !CitesteCap(gnIdRon)
DO asserteaza WITH lcEt + ': documentul exista', .F., 'id_vanzare=' + TRANSFORM(gnIdRon)
RETURN
ENDIF
DO LogCap WITH 'STARE INITIALA', tcLog
lnBaseDisc = gnCapDisc
lnBaseDiscTva = gnCapDiscTva
lnBaseVA = gnCapVA
lnBaseFtva = gnCapFtva
lnBaseTva = gnCapTva
lnBaseCtva = gnCapCtva
lnTvavMax = CotaMaxima(gnIdRon)
lnDiscTvaAst = ROUND(gnDiscRon * (lnTvavMax - 1), gnPPretV)
*-- 1) pregatirea: documentul primeste un discount nenul, comis, prin chiar procedura testata
IF !MyDeschideTranzactie()
DO asserteaza WITH lcEt + '0: tranzactia de pregatire s-a deschis', .F., 'SQLSetprop a esuat'
RETURN
ENDIF
llOk = ApelRecalc(gnIdRon, ALLTRIM(STR(gnDiscRon, 18, 4)), tcLog)
IF !llOk
MyInchideTranzactie(2)
DO asserteaza WITH lcEt + '0: pregatirea documentului cu discount nenul', .F., 'apelul a esuat'
RETURN
ENDIF
MyInchideTranzactie(1)
IF !CitesteCap(gnIdRon)
RETURN
ENDIF
DO LogCap WITH 'PREGATIT, comis - discount nenul pe document', tcLog
DO asserteaza WITH lcEt + '0: documentul are acum discount nenul, comis', ;
ABS(gnCapDisc - gnDiscRon) < 0.0001, 'discount=' + TRANSFORM(gnCapDisc)
*-- 2) lantul real de editare, fara nicio modificare de linii
lcSql = [select cod, sters, id_fact, eproforma, ] + ;
[extract(year from data_act) as anact, extract(month from data_act) as lunaact ] + ;
[from vanzari where id_vanzare = ] + TRANSFORM(gnIdRon)
lnQ = ExecSel(lcSql, 'v_vz')
IF lnQ <= 0 OR RECCOUNT('v_vz') <> 1
DO asserteaza WITH lcEt + '1: VANZARI citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT v_vz
lnCod = cod
lnCodInainte = cod
lnSters = sters
lnIdFact = id_fact
llEProforma = (NVL(eproforma, 0) = 1)
pnAn = anact
pnLuna = lunaact
USE IN v_vz
IF lnSters = 1 OR llEProforma OR (pnAn * 12) + pnLuna <> (gnAn * 12) + gnLuna
DO asserteaza WITH lcEt + '1: document editabil, din luna curenta', .F., ;
'sters=' + TRANSFORM(lnSters) + ' an/luna=' + TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna)
DO restaureaza_discount WITH tcLog, lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva
RETURN
ENDIF
IF ReferinteDocumenteNota(pnAn, pnLuna, lnCod) OR EsteInEFactura(lnIdFact)
DO asserteaza WITH lcEt + '1: document fara referinte / netrimis in eFactura', .F., 'blocat de garda'
DO restaureaza_discount WITH tcLog, lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva
RETURN
ENDIF
IF !IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.) OR RECCOUNT('actactan') = 0
DO asserteaza WITH lcEt + '1: IncarcaCursoareModificareNota', .F., 'a esuat sau nota e goala'
DO restaureaza_discount WITH tcLog, lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva
RETURN
ENDIF
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)
SELECT a.*, IIF(NVL(id_jtva_coloana, 0) = 0, 0, 1) AS cu_Tva FROM tact a INTO CURSOR tact READWRITE
IF !IncarcaVanzareDinNota('tact')
DO asserteaza WITH lcEt + '1: IncarcaVanzareDinNota', .F., 'nu a gasit randul din VANZARI'
DO restaureaza_discount WITH tcLog, lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva
RETURN
ENDIF
IncarcaArticoleFactura(gnIdRon, 'tvd')
lnNrLinii = RECCOUNT('tvd')
*-- proba cheie a caii VFP: discountul documentului ajunge in cursorul pe care se sprijina helperul
DO asserteaza WITH lcEt + '2: tvanz.discount aduce discountul nenul din VANZARI in cursorul de lucru', ;
!ISNULL(tvanz.discount) AND ABS(NVL(tvanz.discount, 0) - gnDiscRon) < 0.0001, ;
'tvanz.discount=' + TRANSFORM(NVL(tvanz.discount, 0)) + ' (in VANZARI ' + TRANSFORM(gnDiscRon) + ')'
STRTOFILE(' tvd incarcat cu ' + TRANSFORM(lnNrLinii) + ' linii active; tvanz.discount=' + ;
TRANSFORM(NVL(tvanz.discount, 0)) + CHR(13) + CHR(10), tcLog, 1)
*-- frm_modific2024 ocolit, buton=1 fortat (inainte_de_do_termin NU se executa)
buton = 1
lnSucces = 0
TRY
IF MyDeschideTranzactie()
SELECT actactan
lnSucces = OSCRIE_IN_FISIERE(2, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(2,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
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
lnSucces = OSCRIE_IN_FISIERE(0, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(0,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0
lcSql = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,Null,] + ALLTRIM(STR(lnIdSet)) + [,] + ;
ALLTRIM(STR(lnCod)) + [,] + ALLTRIM(STR(lnIdFact)) + [,] + ALLTRIM(STR(lnIdFactD)) + [,?gnIdUtil); end;]
lnSucces = IIF(goExecutor.oExecuta(lcSql), 1, -1)
STRTOFILE(' finalizeaza_modificare_nota => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
*-- starea necomisa a discountului dupa realinierea COD-ului: actualizeaza_vanzari nu-l atinge
IF lnSucces > 0 AND CitesteCap(gnIdRon)
DO LogCap WITH 'IN TRANZACTIE, dupa finalizeaza_modificare_nota', tcLog
DO asserteaza WITH lcEt + '3: finalizeaza_modificare_nota nu atinge DISCOUNT', ;
ABS(gnCapDisc - gnDiscRon) < 0.0001, 'discount=' + TRANSFORM(gnCapDisc)
ENDIF
IF lnSucces > 0 AND "OFACTURARE_EDITARE" $ UPPER(SET("Procedure")) AND USED('tvanz') AND RECCOUNT('tvanz') = 1
lnSucces = IIF(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1)
STRTOFILE(' ScrieArticoleFacturaEditate => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' ATENTIE: blocul ScrieArticoleFacturaEditate NU s-a executat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
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 inchiderea tranzactiei' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ELSE
STRTOFILE(' FAIL: deschiderea tranzactiei 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
DO asserteaza WITH lcEt + '4: lantul de salvare a mers pana la capat (lnSucces>0, COMMIT)', ;
lnSucces > 0, 'lnSucces=' + TRANSFORM(lnSucces)
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('tvd')
USE IN tvd
ENDIF
IF USED('tvanz')
USE IN tvanz
ENDIF
*-- 3) verificarile de dupa commit: discountul a supravietuit salvarii complete
IF CitesteCap(gnIdRon)
DO LogCap WITH 'DUPA COMMIT', tcLog
DO asserteaza WITH lcEt + '5: discountul nenul supravietuieste salvarii complete a documentului', ;
ABS(gnCapDisc - gnDiscRon) < 0.0001, ;
'discount=' + TRANSFORM(gnCapDisc) + ' (era ' + TRANSFORM(gnDiscRon) + ')'
DO asserteaza WITH lcEt + '5: VANZARI.COD realocat, documentul ramane activ', ;
gnCapCod <> lnCodInainte AND gnCapSters = 0, TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(gnCapCod)
DO asserteaza WITH lcEt + '5: totalurile de dupa salvare tin cont de discount', ;
ABS(gnCapFtva - (lnBaseFtva - gnDiscRon)) < 0.011 AND ABS(gnCapDiscTva - lnDiscTvaAst) < 0.011, ;
'ftva=' + TRANSFORM(gnCapFtva) + ' asteptat ' + TRANSFORM(lnBaseFtva - gnDiscRon) + ;
' discount_tva=' + TRANSFORM(gnCapDiscTva)
DO asserteaza WITH lcEt + '5: totalurile raman coerente dupa salvare', ;
ABS((gnCapFtva + gnCapTva) - gnCapCtva) < 0.01, ;
TRANSFORM(gnCapFtva) + ' + ' + TRANSFORM(gnCapTva) + ' = ' + TRANSFORM(gnCapCtva)
ENDIF
*-- 4) restaurarea: documentul se intoarce la discount 0 si la totalurile de plecare
DO restaureaza_discount WITH tcLog, lnBaseDisc, lnBaseFtva, lnBaseTva, lnBaseCtva
ENDPROC
*-- readuce documentul de lucru la discountul si totalurile de plecare, comis
PROCEDURE restaureaza_discount
LPARAMETERS tcLog, tnBaseDisc, tnBaseFtva, tnBaseTva, tnBaseCtva
LOCAL llOk
IF !MyDeschideTranzactie()
DO asserteaza WITH 'C6: tranzactia de restaurare s-a deschis', .F., 'SQLSetprop a esuat'
RETURN
ENDIF
llOk = ApelRecalc(gnIdRon, ALLTRIM(STR(tnBaseDisc, 18, 4)), tcLog)
IF llOk
MyInchideTranzactie(1)
ELSE
MyInchideTranzactie(2)
ENDIF
DO asserteaza WITH 'C6: restaurarea discountului initial a mers', llOk, 'llOk=' + TRANSFORM(llOk)
IF CitesteCap(gnIdRon)
DO LogCap WITH 'DUPA RESTAURARE', tcLog
DO asserteaza WITH 'C6: documentul e readus la discountul si totalurile de plecare', ;
ABS(gnCapDisc - tnBaseDisc) < 0.0001 AND ABS(gnCapFtva - tnBaseFtva) < 0.005 ;
AND ABS(gnCapTva - tnBaseTva) < 0.005 AND ABS(gnCapCtva - tnBaseCtva) < 0.005, ;
'discount=' + TRANSFORM(gnCapDisc) + ' ftva=' + TRANSFORM(gnCapFtva) + ;
' tva=' + TRANSFORM(gnCapTva) + ' ctva=' + TRANSFORM(gnCapCtva)
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,470 @@
* test_s5_rollback_real.prg
* Inchide golul "calea de ROLLBACK la esec partial" din docs\livrare_s5.md, sectiunea 5: pana acum
* niciun test nu provocase deliberat un esec in mijlocul ScrieArticoleFacturaEditate (ofacturare_editare.prg)
* pe date reale - comportamentul de rollback era verificat doar static, prin simetria codului.
*
* PARTEA M (mock, fara Oracle) - completeaza test_s5_validari_articole.prg/B3 (care opreste esecul
* la a DOUA comanda, adica in mijlocul buclei UPDATE a liniilor pastrate): aici esecul e la PRIMA
* comanda (chiar update-ul "marcheaza tot sters", primul pas al helperului) - dovedeste ca daca
* pasul 1 esueaza, functia se opreste imediat, FARA nicio comanda de UPDATE/INSERT/recalcul.
*
* PARTEA R (real, Oracle) - NU apeleaza ScrieArticoleFacturaEditate direct (vezi ATENTIE mai jos),
* ci reproduce EXACT aceeasi secventa SQL pe care helperul ar genera-o pentru un caz cu esec la
* INSERT (id_articol inexistent, FK_VANZARE_DET002), prin SQLEXEC brut, in tranzactia manuala
* deja folosita de test_s5_scriere_reala.prg. Dovedeste, pe id_vanzare = 1049:
* 1) pasii 1-2 ai helperului (marcheaza tot sters, invie+actualizeaza liniile pastrate) chiar
* produc starea asteptata NECOMISA, verificata pe aceeasi conexiune;
* 2) o linie noua VALIDA se INSEREaza cu succes, tot necomis - proba de executie PARTIALA reala;
* 3) o linie noua cu id_articol inexistent arunca eroarea Oracle reala (ORA-02291) - exact
* punctul unde helperul s-ar opri (EXIT din SCAN al pasului 3, recalculeaza_totaluri_vanzari
* NU s-ar mai apela);
* 4) tranzactia se inchide cu ROLLBACK (tiparul apelantilor: lnSucces=Iif(...,1,-1), apoi
* MyInchideTranzactie(Iif(lnSucces<0,2,1)));
* 5) verificat INDEPENDENT prin sqlplus, dupa terminarea procesului VFP: VANZARI_DETALII pentru
* 1049 are EXACT acelasi continut (id_vanzare_det/sters/cantitate/pret/pret_achizitie) ca
* inainte de test - nimic din pasii 1-3 nu a supravietuit.
*
* ATENTIE - GASIT IN TIMPUL ACESTUI TEST, NU REPARAT (cod de productie interzis pentru acest agent):
* apelul goExecutor.oExecuta(<sql invalid>) - EXACT calea Oracle folosita de ScrieArticoleFacturaEditate
* pentru toate comenzile ei - AGATA procesul VFP headless la o eroare Oracle REALA (nu simulata),
* sub tranzactie manuala (SQLSetprop Transactions=2), chiar daca Oracle a raspuns deja cu eroarea
* (sesiunea Oracle arata INACTIVE/"SQL*Net message from client", 0 sesiuni blocante, nicio fereastra
* de dialog Windows - EnumWindows arata doar fereastra principala). Reprodus de doua ori izolat, cu
* un script minimal (~15 linii, test_repro_hang_fk.prg, sters dupa investigatie), inclusiv fara nicio
* legatura cu ScrieArticoleFacturaEditate - deci NU e un bug al helperului, ci al executorului comun
* (COMUN\programe\oproceduri_comune.prg, clasa oexecutor, PROCEDURE oExecute). SQLEXEC brut (fara
* goExecutor) pe ACEEASI comanda intoarce eroarea instant (lnRes=-1, AERROR populat) - vezi
* test_repro_hang_fk_raw.prg (sters dupa investigatie). Suspiciune, neconfirmata: ramurile de eroare
* din oExecute apeleaza necondiotionat goLog.Log() cand Type('goLog')='O' - posibil sa incerce o
* comanda Oracle proprie pe aceeasi conexiune, aflata deja sub tranzactie manuala. Consecinta pentru
* livrare: comportamentul REAL al aplicatiei (nu doar al helperului izolat) la un esec Oracle propriu-zis
* in acest lant de scriere NU a putut fi verificat capat-la-capat prin apelul functiei de productie -
* doar prin reproducerea manuala a secventei ei SQL. De raportat separat echipei, nu de reparat aici.
*
* Harness identic cu test_s5_scriere_reala.prg: do_deschide/inchide_tranzactie reproduse inline dupa
* _frm_base.vc2:252-302. Nu atinge cod de aplicatie. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s5_rollback_real_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gcTestLog, gnPass, gnFail
gcErrLog = lcLog
gcTestLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
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
PUBLIC gnIdVanzareTest
gnIdVanzareTest = 1049
DO parte_m_mock WITH lcLog
DO parte_r_real WITH lcLog, gnIdVanzareTest
STRTOFILE(CHR(13) + CHR(10) + 'REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ENDIF
ENDPROC
*-- executa SELECT-ul prin goExecutor (cai care CITESC - fara risc, vezi ATENTIE din antet) si
*-- intoarce codul de succes; cursorul vechi se inchide intai
PROCEDURE ExecSel
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
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
*=====================================================================================
*-- PARTEA M: mock pe goExecutor - esec chiar la PRIMA comanda (pasul 1, "marcheaza tot sters")
*=====================================================================================
PROCEDURE parte_m_mock
LPARAMETERS tcLog
STRTOFILE(CHR(13) + CHR(10) + '========== PARTEA M - esec mock la prima comanda (pasul 1) ==========' + CHR(13) + CHR(10), tcLog, 1)
LOCAL loExecutorReal
loExecutorReal = goExecutor
*-- obiectul mock se construieste O SINGURA DATA, inline in program - vezi capcana l, testare-ui-vfp.md
goExecutor = CREATEOBJECT('dummyexecutorrollback')
goExecutor.nFailAtCall = 1
IF USED('crstvdrb')
USE IN crstvdrb
ENDIF
CreeazaCursorArticoleGol('crstvdrb')
SELECT crstvdrb
APPEND BLANK
REPLACE id_vanzare_det WITH 701, id_articol WITH 1, cantitate WITH 1, pret WITH 1, sters WITH 0 IN crstvdrb
IF USED('crstvanzrb')
USE IN crstvanzrb
ENDIF
CREATE CURSOR crstvanzrb (id_vanzare I, discount N(18,4) NULL)
INSERT INTO crstvanzrb (id_vanzare, discount) VALUES (99999, NULL)
LOCAL llRet
llRet = ScrieArticoleFacturaEditate(99999, 'crstvdrb', 'crstvanzrb')
DO asserteaza WITH 'M1: esec la prima comanda (pasul 1) -> functia intoarce .F. imediat', llRet == .F., TRANSFORM(llRet)
DO asserteaza WITH 'M2: esec la prima comanda -> NICIO alta comanda incercata (1 comanda total, nu update/insert/recalcul)', ;
goExecutor.nApeluri == 1, 'nApeluri=' + TRANSFORM(goExecutor.nApeluri)
goExecutor = loExecutorReal
IF USED('crstvdrb')
USE IN crstvdrb
ENDIF
IF USED('crstvanzrb')
USE IN crstvanzrb
ENDIF
IF USED('crsMockSqlRb')
USE IN crsMockSqlRb
ENDIF
ENDPROC
*=====================================================================================
*-- PARTEA R: aceeasi secventa SQL pe care ScrieArticoleFacturaEditate ar genera-o pentru un caz cu
*-- esec la INSERT, prin SQLEXEC brut (vezi ATENTIE din antetul fisierului - goExecutor.oExecuta pe
*-- o comanda care esueaza real agata procesul, deci NU e folosit aici pentru comenzile de scriere)
*=====================================================================================
PROCEDURE parte_r_real
LPARAMETERS tcLog, tnIdVanzare
LOCAL lcSql, lnQ, lnRes, lnArtLipsa, lnArtValid
LOCAL lnDet1, lnDet2, lnDet3, lnCant1Inainte, lnPret1
LOCAL lnNrLiniiInainte, lcSnapshotInainte, lcSnapshotDupa
LOCAL lnCodInainte, lnCodDupa, lnNrLiniiInTranz, lnStersDet1InTranz
LOCAL lnSuccesFinal, lnNrLiniiDupa
STRTOFILE(CHR(13) + CHR(10) + '========== PARTEA R - esec Oracle real (FK id_articol), rollback (id_vanzare=' + ;
TRANSFORM(tnIdVanzare) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
*-- id_articol garantat inexistent - verificat inainte de scriere, nu presupus
lnArtLipsa = 999999999
lnQ = ExecSel([select count(*) as nr from nom_articole where id_articol = ] + TRANSFORM(lnArtLipsa), 'v_chk')
DO asserteaza WITH 'R0: id_articol folosit pentru esec chiar NU exista in NOM_ARTICOLE', ;
lnQ > 0 AND v_chk.nr = 0, 'nr=' + IIF(lnQ > 0, TRANSFORM(v_chk.nr), 'eroare citire')
IF USED('v_chk')
USE IN v_chk
ENDIF
*-- starea INAINTE, citita direct din Oracle
lcSql = [select cod from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz0')
IF lnQ <= 0 OR RECCOUNT('v_vz0') <> 1
DO asserteaza WITH 'R0: VANZARI citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
lnCodInainte = v_vz0.cod
USE IN v_vz0
lcSql = [select id_vanzare_det, id_articol, nvl(sters,0) as sters, nvl(cantitate,0) as cantitate, ] + ;
[nvl(pret,0) as pret, nvl(pret_achizitie,0) as pret_achizitie ] + ;
[from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ order by id_vanzare_det]
lnQ = ExecSel(lcSql, 'v_lin0')
IF lnQ <= 0
DO asserteaza WITH 'R0: VANZARI_DETALII citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
lnNrLiniiInainte = RECCOUNT('v_lin0')
lcSnapshotInainte = ''
lnDet1 = 0
lnDet2 = 0
lnDet3 = 0
SELECT v_lin0
SCAN
IF sters = 0
DO CASE
CASE lnDet1 = 0
lnDet1 = id_vanzare_det
lnCant1Inainte = cantitate
lnPret1 = pret
CASE lnDet2 = 0
lnDet2 = id_vanzare_det
CASE lnDet3 = 0
lnDet3 = id_vanzare_det
ENDCASE
ENDIF
lcSnapshotInainte = lcSnapshotInainte + TRANSFORM(id_vanzare_det) + ':' + TRANSFORM(sters) + ':' + ;
TRANSFORM(cantitate) + ':' + TRANSFORM(pret) + ':' + TRANSFORM(pret_achizitie) + '|'
ENDSCAN
GO TOP IN v_lin0
lnArtValid = v_lin0.id_articol
USE IN v_lin0
STRTOFILE(' INAINTE: cod=' + TRANSFORM(lnCodInainte) + ' linii=' + TRANSFORM(lnNrLiniiInainte) + ;
' pastrate=' + TRANSFORM(lnDet1) + '/' + TRANSFORM(lnDet2) + '/' + TRANSFORM(lnDet3) + ;
' snapshot=[' + lcSnapshotInainte + ']' + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH 'R0: documentul are cel putin 3 linii active (pastrate)', lnDet1 > 0 AND lnDet2 > 0 AND lnDet3 > 0, ;
TRANSFORM(lnDet1) + '/' + TRANSFORM(lnDet2) + '/' + TRANSFORM(lnDet3)
IF lnDet1 = 0 OR lnDet2 = 0 OR lnDet3 = 0
RETURN
ENDIF
*-- lantul de scriere, tiparul exact al apelantilor: pornire tranzactie -> comenzi -> esec -> ROLLBACK
lnSuccesFinal = 1
TRY
IF !MyDeschideTranzactie()
DO asserteaza WITH 'R1: tranzactia s-a deschis', .F., 'SQLSetprop a esuat'
RETURN
ENDIF
*-- PASUL 1 al helperului: marcheaza tot sters (ofacturare_editare.prg:489-490)
lcSql = [update vanzari_detalii set sters = 1, id_utils = ] + TRANSFORM(gnIdUtil) + [, dataoras = sysdate ] + ;
[where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ and sters = 0]
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS1 marcheaza tot sters => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
lnSuccesFinal = -1
ENDIF
*-- PASUL 2 al helperului: invie+actualizeaza liniile pastrate (:498-501) - cele 3 identificate mai sus,
*-- prima cu cantitatea dublata (ca sa se poata verifica direct efectul UPDATE-ului)
IF lnSuccesFinal > 0
lcSql = [update vanzari_detalii set sters = 0, cantitate = ] + TRANSFORM(lnCant1Inainte * 2) + ;
[, pret = ] + TRANSFORM(lnPret1) + [, pret_cu_tva = 0, id_utils = ] + TRANSFORM(gnIdUtil) + ;
[, dataoras = sysdate where id_vanzare_det = ] + TRANSFORM(lnDet1) + [ and id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS2 update linie pastrata det=' + TRANSFORM(lnDet1) + ' => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
lnSuccesFinal = -1
ENDIF
ENDIF
IF lnSuccesFinal > 0
lcSql = [update vanzari_detalii set sters = 0, id_utils = ] + TRANSFORM(gnIdUtil) + [, dataoras = sysdate ] + ;
[where id_vanzare_det = ] + TRANSFORM(lnDet2) + [ and id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS2 update linie pastrata det=' + TRANSFORM(lnDet2) + ' => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
lnSuccesFinal = -1
ENDIF
ENDIF
IF lnSuccesFinal > 0
lcSql = [update vanzari_detalii set sters = 0, id_utils = ] + TRANSFORM(gnIdUtil) + [, dataoras = sysdate ] + ;
[where id_vanzare_det = ] + TRANSFORM(lnDet3) + [ and id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS2 update linie pastrata det=' + TRANSFORM(lnDet3) + ' => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
lnSuccesFinal = -1
ENDIF
ENDIF
*-- proba intermediara: starea NECOMISA dupa pasii 1-2, pe aceeasi conexiune
IF lnSuccesFinal > 0
lcSql = [select count(*) as nr from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ and sters = 0]
lnQ = ExecSel(lcSql, 'v_activ')
lnNrLiniiInTranz = IIF(lnQ > 0, v_activ.nr, -1)
USE IN v_activ
lcSql = [select nvl(sters,0) as sters, cantitate from vanzari_detalii where id_vanzare_det = ] + TRANSFORM(lnDet1)
lnQ = ExecSel(lcSql, 'v_det1')
lnStersDet1InTranz = IIF(lnQ > 0, v_det1.sters, -1)
STRTOFILE(' IN TRANZACTIE dupa pasii 1-2: linii active=' + TRANSFORM(lnNrLiniiInTranz) + ' det=' + TRANSFORM(lnDet1) + ;
' sters=' + TRANSFORM(lnStersDet1InTranz) + ' cantitate=' + IIF(lnQ > 0, TRANSFORM(v_det1.cantitate), '?') + CHR(13) + CHR(10), tcLog, 1)
USE IN v_det1
DO asserteaza WITH 'R2: dupa pasii 1-2, cele 3 linii pastrate sunt active (sters=0), NECOMIS', ;
lnNrLiniiInTranz = 3, 'linii active in tranzactie=' + TRANSFORM(lnNrLiniiInTranz)
DO asserteaza WITH 'R3: linia pastrata are cantitatea actualizata deja, NECOMIS (proba de executie partiala)', ;
lnStersDet1InTranz = 0, 'sters=' + TRANSFORM(lnStersDet1InTranz)
ENDIF
*-- PASUL 3 al helperului: INSERT linii noi (:519-530) - prima VALIDA, trebuie sa reuseasca
IF lnSuccesFinal > 0
lcSql = [insert into vanzari_detalii (id_vanzare, id_articol, cantitate, pret, pret_cu_tva, proc_tvav, ] + ;
[id_gestiune, pret_achizitie, id_utils, dataoras) values (] + TRANSFORM(tnIdVanzare) + [,] + TRANSFORM(lnArtValid) + ;
[,1,33.33,0,1.21,-1000,44.44,] + TRANSFORM(gnIdUtil) + [,sysdate)]
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS3 insert linie noua VALIDA (id_articol=' + TRANSFORM(lnArtValid) + ') => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
lnSuccesFinal = -1
ENDIF
ENDIF
*-- proba intermediara: linia noua e deja acolo, NECOMIS
IF lnSuccesFinal > 0
lcSql = [select count(*) as nr from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_cnt')
lnNrLiniiInTranz = IIF(lnQ > 0, v_cnt.nr, -1)
USE IN v_cnt
STRTOFILE(' IN TRANZACTIE dupa insertul valid: total linii=' + TRANSFORM(lnNrLiniiInTranz) + ' (erau ' + TRANSFORM(lnNrLiniiInainte) + ')' + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH 'R4: linia noua VALIDA a fost inserata deja, NECOMIS (proba de executie partiala)', ;
lnNrLiniiInTranz = lnNrLiniiInainte + 1, 'linii=' + TRANSFORM(lnNrLiniiInTranz)
ENDIF
*-- PASUL 3, a doua linie: id_articol inexistent - trebuie sa ARUNCE ORA-02291 si sa opreasca aici
*-- (exact ce ar face SCAN-ul helperului: EXIT la primul esec, fara recalculeaza_totaluri_vanzari)
IF lnSuccesFinal > 0
lcSql = [insert into vanzari_detalii (id_vanzare, id_articol, cantitate, pret, pret_cu_tva, proc_tvav, ] + ;
[id_gestiune, pret_achizitie, id_utils, dataoras) values (] + TRANSFORM(tnIdVanzare) + [,] + TRANSFORM(lnArtLipsa) + ;
[,1,1,0,1.21,-1000,1,] + TRANSFORM(gnIdUtil) + [,sysdate)]
lnRes = SQLEXEC(gnHandle, lcSql)
STRTOFILE(' PAS3 insert linie noua INVALIDA (id_articol=' + TRANSFORM(lnArtLipsa) + ') => ' + TRANSFORM(lnRes) + CHR(13) + CHR(10), tcLog, 1)
IF lnRes < 0
LOCAL laErrRb(1)
AERROR(laErrRb)
STRTOFILE(' eroare Oracle: ' + TRANSFORM(laErrRb(2)) + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH 'R5: insertul invalid arunca eroarea asteptata (FK_VANZARE_DET002 / ORA-02291)', ;
'FK_VANZARE_DET002' $ TRANSFORM(laErrRb(2)) OR 'ORA-02291' $ TRANSFORM(laErrRb(2)), TRANSFORM(laErrRb(2))
lnSuccesFinal = -1
ELSE
DO asserteaza WITH 'R5: insertul invalid a esuat asa cum era asteptat', .F., 'lnRes=' + TRANSFORM(lnRes) + ' (nu a esuat!)'
ENDIF
ENDIF
*-- pasul 4 al helperului (recalculeaza_totaluri_vanzari) NU se apeleaza aici - exact ca in helper,
*-- care s-ar opri la EXIT-ul din SCAN inainte sa ajunga la el
*-- tiparul apelantilor: lnSucces=Iif(...,1,-1) -> MyInchideTranzactie(Iif(lnSucces<0,2,1))
IF MyInchideTranzactie(IIF(lnSuccesFinal < 0, 2, 1))
STRTOFILE(' tranzactie inchisa cu ' + IIF(lnSuccesFinal < 0, 'ROLLBACK', 'COMMIT') + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' FAIL la inchiderea tranzactiei' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
CATCH TO loExc
STRTOFILE(' EXCEPTIE: ' + TRANSFORM(loExc.ErrorNo) + ' [' + loExc.Message + '] ' + ;
loExc.Procedure + ':' + TRANSFORM(loExc.LineNo) + ' -> ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
lnSuccesFinal = -99
MyInchideTranzactie(2)
ENDTRY
DO asserteaza WITH 'R6: secventa s-a oprit la insertul invalid (lnSuccesFinal<0)', lnSuccesFinal < 0, 'lnSuccesFinal=' + TRANSFORM(lnSuccesFinal)
DO asserteaza WITH 'R7: tranzactia s-a inchis cu ROLLBACK (nu COMMIT)', lnSuccesFinal < 0, 'lnSuccesFinal=' + TRANSFORM(lnSuccesFinal)
*-- verificarea de FOND: dupa rollback, documentul e EXACT cum era - pe aceeasi conexiune (prima
*-- linie de aparare); verificarea INDEPENDENTA, prin sqlplus dupa QUIT, e cea care conteaza
lcSql = [select cod from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz1')
lnCodDupa = IIF(lnQ > 0, v_vz1.cod, -1)
USE IN v_vz1
DO asserteaza WITH 'R8: dupa ROLLBACK, VANZARI.COD e neschimbat', lnCodDupa = lnCodInainte, TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(lnCodDupa)
lcSql = [select count(*) as nr from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_cnt1')
lnNrLiniiDupa = IIF(lnQ > 0, v_cnt1.nr, -1)
USE IN v_cnt1
DO asserteaza WITH 'R9: dupa ROLLBACK, numarul de linii ale documentului a revenit la valoarea initiala', ;
lnNrLiniiDupa = lnNrLiniiInainte, 'dupa=' + TRANSFORM(lnNrLiniiDupa) + ' inainte=' + TRANSFORM(lnNrLiniiInainte)
lcSql = [select id_vanzare_det, nvl(sters,0) as sters, nvl(cantitate,0) as cantitate, ] + ;
[nvl(pret,0) as pret, nvl(pret_achizitie,0) as pret_achizitie ] + ;
[from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ order by id_vanzare_det]
lnQ = ExecSel(lcSql, 'v_lin1')
lcSnapshotDupa = ''
IF lnQ > 0
SELECT v_lin1
SCAN
lcSnapshotDupa = lcSnapshotDupa + TRANSFORM(id_vanzare_det) + ':' + TRANSFORM(sters) + ':' + ;
TRANSFORM(cantitate) + ':' + TRANSFORM(pret) + ':' + TRANSFORM(pret_achizitie) + '|'
ENDSCAN
ENDIF
IF USED('v_lin1')
USE IN v_lin1
ENDIF
STRTOFILE(' DUPA ROLLBACK: snapshot=[' + lcSnapshotDupa + ']' + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH 'R10: dupa ROLLBACK, snapshot-ul liniilor (det:sters:cant:pret:pret_ach) e IDENTIC cu cel de dinaintea testului', ;
lcSnapshotDupa == lcSnapshotInainte, 'dupa=[' + lcSnapshotDupa + ']'
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 PARTEA M - esueaza la apelul cu numarul nFailAtCall (implicit 1 = chiar
*-- prima comanda); jurnalizeaza in crsMockSqlRb, separat de crsMockSql (folosit de alta suita)
DEFINE CLASS dummyexecutorrollback AS Custom
cEroare = ''
nFailAtCall = 1
nApeluri = 0
PROCEDURE oExecuta
LPARAMETERS tcSql, tcCursor
LOCAL lnUrmator
This.nApeluri = This.nApeluri + 1
IF !USED('crsMockSqlRb')
CREATE CURSOR crsMockSqlRb (nr I, sql M)
ENDIF
SELECT crsMockSqlRb
lnUrmator = RECCOUNT('crsMockSqlRb') + 1
INSERT INTO crsMockSqlRb (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

View File

@@ -0,0 +1,624 @@
* test_s5_scriere_reala.prg
* Test REAL de scriere in VANZARI_DETALII pentru lantul de editare a unei facturi emise -
* APROBAT explicit de Marius (09.08.2026) sa scrie in schema de dev MARIUSM_AUTO@ROA_CENTRAL.
*
* Ce dovedeste, si nu poate fi dovedit headless: pack_facturare.actualizeaza_vanzari (apelata din
* pack_contafin.finalizeaza_modificare_nota) face UPDATE VANZARI_DETALII SET STERS = 0 pe TOT
* documentul, deci INVIE liniile sterse la o editare anterioara. Idiomul "marcheaza tot sters,
* invie ce ramane" din ScrieArticoleFacturaEditate (ofacturare_editare.prg), apelat DUPA
* finalizeaza_modificare_nota si in aceeasi tranzactie, trebuie sa corecteze ambele consecinte.
*
* Ordinea testata (o singura tranzactie manuala), copiata din ofacturare_comun.vc2:3799-3837:
* do_deschide_tranzactie -> OSCRIE_IN_FISIERE(2) -> OSCRIE_IN_FISIERE(0) ->
* finalizeaza_modificare_nota -> ScrieArticoleFacturaEditate -> do_inchide_tranzactie
*
* Doua treceri pe acelasi document:
* TRECEREA 1 - o linie stearsa, o linie cu cantitatea schimbata, o linie noua adaugata.
* TRECEREA 2 - reeditare fara nicio modificare; singura care dovedeste ca linia stearsa la
* trecerea 1 RAMANE stearsa (consecinta 2 a resetului).
*
* In interiorul tranzactiei, dupa finalizeaza_modificare_nota si inainte de scrierea liniilor,
* se citeste starea necomisa a lui VANZARI_DETALII (aceeasi conexiune) - proba directa ca resetul
* STERS = 0 chiar ruleaza inainte, si ca scrierea liniilor il corecteaza dupa.
*
* Harness, nu UI: frm_modific2024 e ocolit (se blocheaza headless pe un dialog nativ - capcana j din
* testare-ui-vfp.md), buton=1 e fortat direct, deci inainte_de_do_termin NU se executa.
* do_deschide_tranzactie/do_inchide_tranzactie sunt reproduse inline dupa _frm_base.vc2:252-302.
* OSCRIE_IN_FISIERE, finalizeaza_modificare_nota si ScrieArticoleFacturaEditate ruleaza reale.
*
* CONSUMA DATE DE TEST: VANZARI.COD se realoca la fiecare salvare, iar liniile sterse raman sterse.
* Documentul e id_vanzare = 1049 (nu 1050, cel ales de descopera_caz_test pentru FACTURA_ARTICOLE).
* Nu modifica cod de aplicatie, nu atinge .PJX/.exe. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s5_scriere_reala_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gcTestLog, gnPass, gnFail
gcErrLog = lcLog
gcTestLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
*-- mock amessagebox incarcat PRIMUL, inaintea lantului real (la nume duplicat castiga primul gasit)
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
LOCAL lnVerifMock
lnVerifMock = amessagebox('verificare mock', 4+32, 'test')
STRTOFILE('verificare mock amessagebox = ' + TRANSFORM(lnVerifMock) + ;
IIF(lnVerifMock = 6, ' (OK, mock activ)', ' (ATENTIE: amessagebox real, 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
*-- garda de referinte din do_editare_factura sta in odocumente.prg, care nu e in mediul de test
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\odocumente.prg ADDITIVE
IF TYPE('gnPC') <> 'N'
PUBLIC gnPC
gnPC = 2
ENDIF
PUBLIC gcMockUltimMesaj, gnMockUltimTip
gcMockUltimMesaj = ''
gnMockUltimTip = 0
STRTOFILE('gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + ' gnIdUtil=' + TRANSFORM(gnIdUtil) + CHR(13) + CHR(10), lcLog, 1)
*-- valorile alese pentru linia noua; pret_achizitie distinctiv, ca sa se poata urmari peste treceri
PUBLIC gnPretAchNou, gnCantNoua, gnPretNou
gnPretAchNou = 77.77
gnCantNoua = 3
gnPretNou = 50
PUBLIC gnIdVanzareTest
gnIdVanzareTest = 1049
*-- liniile urmarite peste ambele treceri: la trecerea 2 documentul se reincarca din Oracle, iar
*-- linia stearsa nu mai e in tvd - identitatea ei trebuie pastrata din trecerea 1
PUBLIC gnDetModificat, gnDetSters, gnDetNou
gnDetModificat = 0
gnDetSters = 0
gnDetNou = 0
*-- starea de plecare, inainte de orice scriere
DO LogStareLinii WITH 'STARE INITIALA', gnIdVanzareTest, lcLog
DO ruleaza_trecere WITH gnIdVanzareTest, 'TRECEREA 1 - sterge o linie, schimba o cantitate, adauga o linie', .T., lcLog
DO ruleaza_trecere WITH gnIdVanzareTest, 'TRECEREA 2 - reeditare fara nicio modificare', .F., lcLog
STRTOFILE(CHR(13) + CHR(10) + 'REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ENDIF
ENDPROC
*-- executa SELECT-ul prin goExecutor si intoarce codul de succes; cursorul vechi se inchide intai
PROCEDURE ExecSel
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
ENDPROC
*-- logheaza starea tuturor liniilor documentului (inclusiv cele sterse); folosit si INTRE pasii
*-- tranzactiei manuale - citirea merge pe aceeasi conexiune, deci vede si ce nu e inca comis
PROCEDURE LogStareLinii
LPARAMETERS tcEticheta, tnIdVanzare, tcLog
LOCAL lcSql, lnQ, lcRand
lcSql = [select id_vanzare_det, nvl(sters,0) as sters, nvl(cantitate,0) as cantitate, ] + ;
[nvl(pret,0) as pret, nvl(pret_achizitie,0) as pret_achizitie ] + ;
[from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ order by id_vanzare_det]
lnQ = ExecSel(lcSql, 'v_stare')
IF lnQ <= 0
STRTOFILE(' [' + tcEticheta + '] EROARE citire linii: ' + ALLTRIM(TRANSFORM(NVL(goExecutor.cEroare, ''))) + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
lcRand = ' [' + tcEticheta + '] linii:' + CHR(13) + CHR(10)
SELECT v_stare
SCAN
lcRand = lcRand + ' det=' + TRANSFORM(id_vanzare_det) + ' sters=' + TRANSFORM(sters) + ;
' cant=' + TRANSFORM(cantitate) + ' pret=' + TRANSFORM(pret) + ' pret_ach=' + TRANSFORM(pret_achizitie) + CHR(13) + CHR(10)
ENDSCAN
STRTOFILE(lcRand, tcLog, 1)
USE IN v_stare
ENDPROC
*-- citeste STERS de pe o linie anume, direct din Oracle (si necomis, in tranzactie); -1 = eroare/lipsa
PROCEDURE CitesteSters
LPARAMETERS tnIdVanzareDet
LOCAL lcSql, lnQ, lnRez
lnRez = -1
lcSql = [select nvl(sters,0) as sters from vanzari_detalii where id_vanzare_det = ] + TRANSFORM(tnIdVanzareDet)
lnQ = ExecSel(lcSql, 'v_unsters')
IF lnQ > 0 AND RECCOUNT('v_unsters') = 1
lnRez = v_unsters.sters
ENDIF
IF USED('v_unsters')
USE IN v_unsters
ENDIF
RETURN lnRez
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 ruleaza_trecere
LPARAMETERS tnIdVanzare, tcEticheta, tlAplicaModificari, tcLog
*-- pnAn/pnLuna/lnCod/lnIdFact PRIVATE (nu LOCAL), ca in ofacturare_comun.vc2:3730 -
*-- finalizeaza_modificare_nota le foloseste ca bind-variabile ?pnLuna/?pnAn in josul stivei
PRIVATE pnAn, pnLuna, lnCod, lnIdFact
LOCAL lnSters, llEProforma, lnIdSet, lnIdFactD, lnSucces, llGasitRand
LOCAL lcSql, lnQ, lnI
LOCAL lnDetModificat, lnDetSters, lnDetPastrat, lnCantVecheMod, lnPretAchMod
LOCAL lnMaxDetInainte, lnNrActiveInainte
LOCAL lnCodInainte, lnTotFtvaInainte, lnTotTvaInainte, lnTotCtvaInainte
LOCAL lnStersProbaDupaFinalizeaza, lnStersProbaDupaScriere
LOCAL lnCodDupa, lnStersDupa, lnTotFtvaDupa, lnTotTvaDupa, lnTotCtvaDupa
LOCAL lnDetNou, lnPretAchNouDupa, lnCantModDupa, lnPretAchModDupa, lnStersModDupa
LOCAL lnNrActiveDupa, lnBrutAsteptat, lnStersNouUrmarit, lnPretAchNouUrmarit
STRTOFILE(CHR(13) + CHR(10) + '========== ' + tcEticheta + ' (id_vanzare=' + TRANSFORM(tnIdVanzare) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
*-- 1) starea documentului din VANZARI (cod-ul se realoca la fiecare salvare)
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 = ExecSel(lcSql, 'v_vz_inainte')
IF lnQ <= 0 OR RECCOUNT('v_vz_inainte') <> 1
DO asserteaza WITH tcEticheta + ': VANZARI citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT v_vz_inainte
lnCod = cod
lnSters = sters
lnIdFact = id_fact
llEProforma = (NVL(eproforma, 0) = 1)
pnAn = anact
pnLuna = lunaact
lnCodInainte = cod
lnTotFtvaInainte = total_fara_tva
lnTotTvaInainte = total_tva
lnTotCtvaInainte = total_cu_tva
USE IN v_vz_inainte
STRTOFILE(' VANZARI inainte: cod=' + TRANSFORM(lnCod) + ' sters=' + TRANSFORM(lnSters) + ' id_fact=' + TRANSFORM(lnIdFact) + ;
' an/luna=' + TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna) + ' total_ftva=' + TRANSFORM(lnTotFtvaInainte) + ;
' total_tva=' + TRANSFORM(lnTotTvaInainte) + ' total_ctva=' + TRANSFORM(lnTotCtvaInainte) + CHR(13) + CHR(10), tcLog, 1)
*-- 2) garzile din do_editare_factura (ofacturare_comun.vc2:3742-3767), fara UI
IF lnSters = 1 OR llEProforma
DO asserteaza WITH tcEticheta + ': document editabil (nesters, neproforma)', .F., 'sters=' + TRANSFORM(lnSters)
RETURN
ENDIF
IF (pnAn*12) + pnLuna <> (gnAn*12) + gnLuna
DO asserteaza WITH tcEticheta + ': document din luna curenta', .F., TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna)
RETURN
ENDIF
IF ReferinteDocumenteNota(pnAn, pnLuna, lnCod) OR EsteInEFactura(lnIdFact)
DO asserteaza WITH tcEticheta + ': document fara referinte / netrimis in eFactura', .F., 'blocat de garda'
RETURN
ENDIF
*-- 3) instantaneul liniilor inainte de editare
lcSql = [select count(*) as nr, max(id_vanzare_det) as maxdet from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_max')
lnMaxDetInainte = IIF(lnQ > 0, NVL(v_max.maxdet, 0), 0)
IF USED('v_max')
USE IN v_max
ENDIF
*-- 4) cursoarele notei, exact ca do_editare_factura
IF !IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.)
DO asserteaza WITH tcEticheta + ': IncarcaCursoareModificareNota', .F., 'a esuat'
RETURN
ENDIF
IF RECCOUNT('actactan') = 0
DO asserteaza WITH tcEticheta + ': exista nota contabila', .F., 'Reccount(actactan)=0'
RETURN
ENDIF
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)
SELECT a.*, IIF(NVL(id_jtva_coloana, 0) = 0, 0, 1) AS cu_Tva FROM tact a INTO CURSOR tact READWRITE
*-- 5) tvanz + tvd, cursoarele pe care le lasa calea reala inainte de Createobject(frm_modific2024)
IF !IncarcaVanzareDinNota('tact')
DO asserteaza WITH tcEticheta + ': IncarcaVanzareDinNota', .F., 'nu a gasit randul din VANZARI'
RETURN
ENDIF
IncarcaArticoleFactura(tnIdVanzare, 'tvd')
lnNrActiveInainte = RECCOUNT('tvd')
STRTOFILE(' tvd incarcat cu ' + TRANSFORM(lnNrActiveInainte) + ' linii active; tvanz.id_vanzare=' + TRANSFORM(tvanz.id_vanzare) + ;
' discount=' + TRANSFORM(NVL(tvanz.discount, 0)) + CHR(13) + CHR(10), tcLog, 1)
IF lnNrActiveInainte < 3
DO asserteaza WITH tcEticheta + ': documentul are cel putin 3 linii active', .F., TRANSFORM(lnNrActiveInainte)
RETURN
ENDIF
*-- 6) editarile utilizatorului, aplicate pe tvd exact cum le lasa pagina de articole:
*-- randul 1 primeste cantitate dubla, randul 2 e marcat sters (sters=1, ramane in cursor),
*-- randul 3 ramane neatins, plus o linie noua cu id_vanzare_det = 0
lnDetPastrat = 0
lnCantVecheMod = 0
lnPretAchMod = 0
IF tlAplicaModificari
SELECT tvd
GO TOP
gnDetModificat = id_vanzare_det
lnCantVecheMod = NVL(cantitate, 0)
lnPretAchMod = NVL(pret_achizitie, 0)
SKIP
gnDetSters = id_vanzare_det
SKIP
lnDetPastrat = id_vanzare_det
ELSE
*-- linia stearsa la trecerea 1 nu mai e in tvd; identitatile vin din trecerea 1
SELECT tvd
LOCATE FOR id_vanzare_det = gnDetModificat
IF !FOUND()
DO asserteaza WITH tcEticheta + ': linia editata la trecerea 1 e reincarcata', .F., 'det=' + TRANSFORM(gnDetModificat)
RETURN
ENDIF
lnCantVecheMod = NVL(cantitate, 0)
lnPretAchMod = NVL(pret_achizitie, 0)
ENDIF
lnDetModificat = gnDetModificat
lnDetSters = gnDetSters
IF tlAplicaModificari
SELECT tvd
GO TOP
REPLACE cantitate WITH lnCantVecheMod * 2, lmodificat WITH .T.
SKIP
REPLACE sters WITH 1, lmodificat WITH .T.
*-- linia noua: coloanele care conteaza pentru INSERT se iau de pe prima linie, ca sa fie
*-- coerenta cu documentul (cota TVA, jurnal, valuta, cont); gestiunea e cea a adaugarii
*-- din dialog (gnScadereStoc = 0 -> id_gestiune = -1000)
LOCAL lnArtNou, lnPretCuTvaNou, lnProcTvavNou, lnIdValNou, lnJtvaNou, lnTaxcodeNou, lcContNou
SELECT tvd
GO TOP
lnArtNou = id_articol
lnPretCuTvaNou = NVL(pret_cu_tva, 0)
lnProcTvavNou = NVL(proc_tvav, 1)
lnIdValNou = id_valuta
lnJtvaNou = id_jtva_coloana
lnTaxcodeNou = taxcode
lcContNou = NVL(cont, '')
APPEND BLANK IN tvd
REPLACE id_vanzare WITH tnIdVanzare, id_vanzare_det WITH 0, id_articol WITH lnArtNou, ;
cantitate WITH gnCantNoua, pret WITH gnPretNou, pret_cu_tva WITH lnPretCuTvaNou, ;
proc_tvav WITH lnProcTvavNou, discount_unitar WITH 0, id_gestiune WITH -1000, ;
cont WITH lcContNou, id_valuta WITH lnIdValNou, id_jtva_coloana WITH lnJtvaNou, ;
taxcode WITH lnTaxcodeNou, sters WITH 0, pret_achizitie WITH gnPretAchNou, ;
lmodificat WITH .T. IN tvd
STRTOFILE(' editari in tvd: det=' + TRANSFORM(lnDetModificat) + ' cantitate ' + TRANSFORM(lnCantVecheMod) + ;
' -> ' + TRANSFORM(lnCantVecheMod * 2) + ' (pret_achizitie ramane ' + TRANSFORM(lnPretAchMod) + ')' + CHR(13) + CHR(10) + ;
' det=' + TRANSFORM(lnDetSters) + ' marcata stearsa (tvd.sters=1)' + CHR(13) + CHR(10) + ;
' det=' + TRANSFORM(lnDetPastrat) + ' neatinsa' + CHR(13) + CHR(10) + ;
' linie noua: id_articol=' + TRANSFORM(lnArtNou) + ' cantitate=' + TRANSFORM(gnCantNoua) + ;
' pret=' + TRANSFORM(gnPretNou) + ' pret_achizitie=' + TRANSFORM(gnPretAchNou) + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' tvd nemodificat: det ' + TRANSFORM(lnDetModificat) + ' / ' + TRANSFORM(lnDetSters) + ' / ' + ;
TRANSFORM(lnDetPastrat) + ' (linia stearsa la trecerea 1 nici nu mai e incarcata)' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
*-- 7) frm_modific2024 ocolit, buton=1 fortat (inainte_de_do_termin NU se executa)
buton = 1
*-- 8) ramura buton=1 din do_editare_factura (ofacturare_comun.vc2:3799-3837), copiata identic;
*-- do_deschide/inchide_tranzactie sunt reproduse inline (MyDeschide/MyInchideTranzactie)
lnSucces = 0
lnStersProbaDupaFinalizeaza = -1
lnStersProbaDupaScriere = -1
TRY
IF MyDeschideTranzactie()
SELECT actactan
lnSucces = OSCRIE_IN_FISIERE(2, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(2,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
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
lnSucces = OSCRIE_IN_FISIERE(0, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(0,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0
lcSql = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,Null,] + ALLTRIM(STR(lnIdSet)) + [,] + ;
ALLTRIM(STR(lnCod)) + [,] + ALLTRIM(STR(lnIdFact)) + [,] + ALLTRIM(STR(lnIdFactD)) + [,?gnIdUtil); end;]
lnSucces = IIF(goExecutor.oExecuta(lcSql), 1, -1)
STRTOFILE(' finalizeaza_modificare_nota => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
*-- PROBA DE ORDONARE: starea necomisa, dupa finalizeaza_modificare_nota si INAINTE de
*-- scrierea liniilor - aici se vede resetul STERS = 0 din actualizeaza_vanzari
IF lnSucces > 0
DO LogStareLinii WITH 'IN TRANZACTIE, dupa finalizeaza_modificare_nota', tnIdVanzare, tcLog
IF lnDetSters > 0
lnStersProbaDupaFinalizeaza = CitesteSters(lnDetSters)
ENDIF
ENDIF
*-- blocul nou, identic cu ofacturare_comun.vc2:3828-3830 si comun.vc2:2491
IF lnSucces > 0 AND "OFACTURARE_EDITARE" $ UPPER(SET("Procedure")) AND USED('tvanz') AND RECCOUNT('tvanz') = 1
lnSucces = IIF(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1)
STRTOFILE(' ScrieArticoleFacturaEditate => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' ATENTIE: blocul ScrieArticoleFacturaEditate NU s-a executat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0
DO LogStareLinii WITH 'IN TRANZACTIE, dupa ScrieArticoleFacturaEditate', tnIdVanzare, tcLog
IF lnDetSters > 0
lnStersProbaDupaScriere = CitesteSters(lnDetSters)
ENDIF
ENDIF
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 inchiderea tranzactiei' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ELSE
STRTOFILE(' FAIL: deschiderea tranzactiei 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
DO asserteaza WITH tcEticheta + ': lantul de scriere a mers pana la capat (lnSucces>0, COMMIT)', ;
lnSucces > 0, 'lnSucces=' + TRANSFORM(lnSucces)
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(' scrierea nu s-a produs - fara verificari dupa' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
*-- 9) verificarile de dupa commit
DO LogStareLinii WITH 'DUPA COMMIT', tnIdVanzare, tcLog
*-- proba de ordonare, singura care nu poate fi obtinuta din afara tranzactiei
DO asserteaza WITH tcEticheta + ': resetul STERS=0 din actualizeaza_vanzari a rulat inainte de scrierea liniilor', ;
lnStersProbaDupaFinalizeaza = 0, 'sters pe det=' + TRANSFORM(lnDetSters) + ' dupa finalizeaza = ' + TRANSFORM(lnStersProbaDupaFinalizeaza)
*-- starea liniilor dupa commit
lcSql = [select id_vanzare_det, nvl(sters,0) as sters, nvl(cantitate,0) as cantitate, ] + ;
[nvl(pret,0) as pret, nvl(pret_achizitie,0) as pret_achizitie ] + ;
[from vanzari_detalii where id_vanzare = ] + TRANSFORM(tnIdVanzare) + [ order by id_vanzare_det]
lnQ = ExecSel(lcSql, 'v_dupa')
IF lnQ <= 0
DO asserteaza WITH tcEticheta + ': citirea liniilor dupa commit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
lnStersModDupa = -1
lnCantModDupa = -1
lnPretAchModDupa = -1
lnDetNou = 0
lnPretAchNouDupa = -1
lnStersNouUrmarit = -1
lnPretAchNouUrmarit = -1
lnNrActiveDupa = 0
lnBrutAsteptat = 0
SELECT v_dupa
SCAN
IF sters = 0
lnNrActiveDupa = lnNrActiveDupa + 1
lnBrutAsteptat = lnBrutAsteptat + ROUND(cantitate * pret, 2)
ENDIF
IF id_vanzare_det = lnDetModificat
lnStersModDupa = sters
lnCantModDupa = cantitate
lnPretAchModDupa = pret_achizitie
ENDIF
IF id_vanzare_det > lnMaxDetInainte
lnDetNou = id_vanzare_det
lnPretAchNouDupa = pret_achizitie
ENDIF
*-- linia adaugata la trecerea 1, urmarita si la trecerea 2 (acolo e deja linie existenta)
IF gnDetNou > 0 AND id_vanzare_det = gnDetNou
lnStersNouUrmarit = sters
lnPretAchNouUrmarit = pret_achizitie
ENDIF
ENDSCAN
USE IN v_dupa
*-- linia marcata stearsa: STERS=1 dupa ce s-a intors finalizeaza_modificare_nota
LOCAL lnStersDetSters
lnStersDetSters = CitesteSters(lnDetSters)
DO asserteaza WITH tcEticheta + ': linia marcata stearsa are STERS=1 in VANZARI_DETALII', ;
lnStersDetSters = 1, 'det=' + TRANSFORM(lnDetSters) + ' sters=' + TRANSFORM(lnStersDetSters)
*-- si dovada ca scrierea liniilor e cea care a corectat resetul
DO asserteaza WITH tcEticheta + ': ScrieArticoleFacturaEditate a re-marcat linia stearsa in aceeasi tranzactie', ;
lnStersProbaDupaScriere = 1, 'sters pe det=' + TRANSFORM(lnDetSters) + ' dupa scriere = ' + TRANSFORM(lnStersProbaDupaScriere)
*-- liniile pastrate raman active, cu cantitatea editata
DO asserteaza WITH tcEticheta + ': linia pastrata e activa cu cantitatea editata', ;
lnStersModDupa = 0 AND ABS(lnCantModDupa - IIF(tlAplicaModificari, lnCantVecheMod * 2, lnCantVecheMod)) < 0.001, ;
'det=' + TRANSFORM(lnDetModificat) + ' sters=' + TRANSFORM(lnStersModDupa) + ' cant=' + TRANSFORM(lnCantModDupa)
*-- PRET_ACHIZITIE pe linia existenta ramane neatins (omis din SET-ul de UPDATE)
DO asserteaza WITH tcEticheta + ': PRET_ACHIZITIE pe linia existenta editata a ramas neatins', ;
ABS(lnPretAchModDupa - lnPretAchMod) < 0.0001, ;
'det=' + TRANSFORM(lnDetModificat) + ' inainte=' + TRANSFORM(lnPretAchMod) + ' dupa=' + TRANSFORM(lnPretAchModDupa)
IF tlAplicaModificari
*-- linia noua a intrat prin INSERT, cu ID_VANZARE_DET alocat de trigger
DO asserteaza WITH tcEticheta + ': linia noua a fost inserata cu ID_VANZARE_DET alocat de trigger', ;
lnDetNou > lnMaxDetInainte, 'det nou=' + TRANSFORM(lnDetNou) + ' (max inainte=' + TRANSFORM(lnMaxDetInainte) + ')'
DO asserteaza WITH tcEticheta + ': linia noua are PRET_ACHIZITIE exact valoarea tastata', ;
ABS(lnPretAchNouDupa - gnPretAchNou) < 0.0001, 'pret_achizitie=' + TRANSFORM(lnPretAchNouDupa) + ' (tastat ' + TRANSFORM(gnPretAchNou) + ')'
DO asserteaza WITH tcEticheta + ': documentul are 3 linii active (una stearsa, una noua)', ;
lnNrActiveDupa = 3, 'active=' + TRANSFORM(lnNrActiveDupa)
gnDetNou = lnDetNou
ELSE
DO asserteaza WITH tcEticheta + ': nicio linie noua nu a fost inserata', ;
lnDetNou = 0, 'det peste maximul dinainte=' + TRANSFORM(lnDetNou)
DO asserteaza WITH tcEticheta + ': numarul de linii active nu s-a schimbat', ;
lnNrActiveDupa = lnNrActiveInainte, 'active=' + TRANSFORM(lnNrActiveDupa) + ' (erau ' + TRANSFORM(lnNrActiveInainte) + ')'
DO asserteaza WITH tcEticheta + ': linia adaugata la trecerea 1 e activa si la reeditare', ;
lnStersNouUrmarit = 0, 'det=' + TRANSFORM(gnDetNou) + ' sters=' + TRANSFORM(lnStersNouUrmarit)
DO asserteaza WITH tcEticheta + ': PRET_ACHIZITIE al liniei adaugate la trecerea 1 a ramas neatins', ;
ABS(lnPretAchNouUrmarit - gnPretAchNou) < 0.0001, ;
'det=' + TRANSFORM(gnDetNou) + ' pret_achizitie=' + TRANSFORM(lnPretAchNouUrmarit) + ' (tastat ' + TRANSFORM(gnPretAchNou) + ')'
ENDIF
*-- totalurile din VANZARI dupa recalculeaza_totaluri_vanzari
lcSql = [select cod, nvl(sters,0) as sters, total_fara_tva, total_tva, total_cu_tva from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz_dupa')
IF lnQ <= 0 OR RECCOUNT('v_vz_dupa') <> 1
DO asserteaza WITH tcEticheta + ': VANZARI citit dupa commit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT v_vz_dupa
lnCodDupa = cod
lnStersDupa = sters
lnTotFtvaDupa = total_fara_tva
lnTotTvaDupa = total_tva
lnTotCtvaDupa = total_cu_tva
USE IN v_vz_dupa
STRTOFILE(' VANZARI dupa: cod=' + TRANSFORM(lnCodDupa) + ' (era ' + TRANSFORM(lnCodInainte) + ') sters=' + TRANSFORM(lnStersDupa) + ;
' total_ftva=' + TRANSFORM(lnTotFtvaDupa) + ' total_tva=' + TRANSFORM(lnTotTvaDupa) + ;
' total_ctva=' + TRANSFORM(lnTotCtvaDupa) + ' (brut din linii active=' + TRANSFORM(lnBrutAsteptat) + ')' + CHR(13) + CHR(10), tcLog, 1)
DO asserteaza WITH tcEticheta + ': VANZARI.COD realocat, documentul ramane activ', ;
lnCodDupa <> lnCodInainte AND lnStersDupa = 0, TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(lnCodDupa)
DO asserteaza WITH tcEticheta + ': totalurile din VANZARI sunt coerente (fara_tva + tva = cu_tva)', ;
ABS((lnTotFtvaDupa + lnTotTvaDupa) - lnTotCtvaDupa) < 0.01, ;
TRANSFORM(lnTotFtvaDupa) + ' + ' + TRANSFORM(lnTotTvaDupa) + ' = ' + TRANSFORM(lnTotCtvaDupa)
*-- toate liniile documentului au preturile cu TVA (pret_cu_tva=1), deci totalul brut al
*-- documentului e suma cantitate*pret pe liniile active - relatie verificata si inainte de editare
DO asserteaza WITH tcEticheta + ': total_cu_tva corespunde liniilor active dupa recalcul', ;
ABS(lnTotCtvaDupa - lnBrutAsteptat) < 0.01, ;
'total_cu_tva=' + TRANSFORM(lnTotCtvaDupa) + ' suma linii active=' + TRANSFORM(lnBrutAsteptat)
IF USED('tvd')
USE IN tvd
ENDIF
IF USED('tvanz')
USE IN tvanz
ENDIF
STRTOFILE(' REZUMAT ' + tcEticheta + ': cod ' + TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(lnCodDupa) + 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,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

View File

@@ -0,0 +1,202 @@
* test_ui_s5_grid_pret_achizitie.prg - verificare UI (formular vizibil, nu headless - coloanele
* gridului NU se materializeaza sub -A -T, memoria recurenta) a S5.B/C (docs\cercetare\rec_s5_grid_articole.md):
* grdArticoleFactura are 15 coloane (era 14), coloana noua cPretAchizitieArt, si editabilitatea
* PER RAND (nu per coloana - ReadOnly ramane .F. la nivel de coloana, gating-ul e in When):
* - pe o linie din SET (tvd.id_vanzare_set<>0): cantitate/pret/pret_achizitie/pret_cu_tva NU
* primesc focus (When()=.F.) si linia are marcaj vizual (DynamicForeColor albastru RGB(0,70,153));
* - pret_achizitie primeste focus DOAR pe o linie noua (id_vanzare_det=0, id_vanzare_set=0), NU
* pe una existenta;
* - marcajul de linie stearsa (gri RGB(150,150,150)) ramane neregresat.
*
* Document real: cod=1139934/an=2021/luna=12 (id_vanzare=882) - acelasi folosit deja de
* test_ui_sterge_linie.prg/test_ui_grid_articole.prg (are rulaje, altfel Show() ascunde tot
* pgfArticole indiferent de tvd). Documentul NU are linii de set in date reale (in toata schema
* exista doar 4 randuri in VANZARI_SETURI - insuficient ca sa gaseasca un caz real prin proprietate,
* si oricum absenta/prezenta pe 1-2 documente nu ar fi dovada). CAZUL DE SET E SINTETIC: un rand
* real din tvd e marcat manual cu id_vanzare_set - tipar deja folosit in test_adauga_linie_valuta.prg
* (tvanz suprascris manual pentru cazul de valuta). Verificarile de editabilitate se fac prin apel
* DIRECT al metodei .When() a controlului (echivalentul programatic al "celula primeste focus"),
* nu prin navigare cu input real (masina partajata - fara SendInput/click real).
* Nu scrie in Oracle. QUIT la final.
SET SAFETY OFF
SET TALK OFF
PUBLIC gcUILog, gcSyncDir
gcSyncDir = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\uisync_s5grid\'
gcUILog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_ui_s5_grid_pret_achizitie_log.txt'
STRTOFILE('start' + CHR(13) + CHR(10), gcUILog)
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
PUBLIC gnPass, gnFail
gnPass = 0
gnFail = 0
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\utile\Teste\ui_harness.prg ADDITIVE
DO HarnessInit
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), gcUILog, 1)
DO HarnessDone WITH 'error'
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
*!* test_init_env_auto incarca intai copia omodificari din ROACONT - o eliberam si o reincarcam pe cea de sub test
RELEASE CLASSLIB omodificari
SET CLASSLIB TO D:\ROA\ROAFACTURARE\COMUN\clase\omodificari.vcx ADDITIVE
LOCAL llOk, lnIdSet, loForm, loGrid
LOCAL lnReccountInitial, lnIdArtRow1, lnIdArtRow2
llOk = IncarcaCursoareModificareNota(1139934, 2021, 12, .F.)
IF !llOk OR !Used('tact') OR Reccount('tact') = 0
STRTOFILE('EROARE: IncarcaCursoareModificareNota a esuat pe cod=1139934' + CHR(13) + CHR(10), gcUILog, 1)
DO HarnessDone WITH 'error'
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
IncarcaArticoleFactura(882, 'crsArticoleFactura')
Select tact
loForm = Createobject([frm_modific2024], lnIdSet)
loForm.WindowType = 0
loForm.Show()
loForm.pgfArticole.ActivePage = 3
DOEVENTS FORCE
loGrid = loForm.pgfArticole.Page3.grdArticoleFactura
DO asserteaza WITH 'gridul are 15 coloane (S5: +1 fata de S4, pret_achizitie)', loGrid.ColumnCount == 15, TRANSFORM(loGrid.ColumnCount)
DO asserteaza WITH 'coloana cPretAchizitieArt exista', Type('loGrid.cPretAchizitieArt.ControlSource') == 'C', Type('loGrid.cPretAchizitieArt.ControlSource')
DO asserteaza WITH 'coloana cPretAchizitieArt legata pe tvd.pret_achizitie', Alltrim(loGrid.cPretAchizitieArt.ControlSource) == 'tvd.pret_achizitie', loGrid.cPretAchizitieArt.ControlSource
DO asserteaza WITH 'eticheta coloanei = "Pret achizitie"', Alltrim(loGrid.cPretAchizitieArt.Header1.Caption) == 'Pret achizitie', loGrid.cPretAchizitieArt.Header1.Caption
lnReccountInitial = Reccount('tvd')
DO asserteaza WITH 'documentul are cel putin 2 linii reale (necesar pentru cazurile de mai jos)', lnReccountInitial >= 2, TRANSFORM(lnReccountInitial)
IF lnReccountInitial >= 2
*!* --- linie existenta, NEdinSet: pret_achizitie NU primeste focus (id_vanzare_det<>0) ---
SELECT tvd
GO TOP
lnIdArtRow1 = tvd.id_vanzare_det
DO asserteaza WITH 'linie existenta (id_vanzare_det<>0): pret_achizitie NU primeste focus', ;
loGrid.cPretAchizitieArt.Text1.When() == .F., 'id_vanzare_det=' + TRANSFORM(lnIdArtRow1)
*!* --- CAZ SINTETIC: a doua linie marcata manual ca si componenta de set (id_vanzare_set<>0) ---
SELECT tvd
SKIP
lnIdArtRow2 = tvd.id_vanzare_det
REPLACE id_vanzare_set WITH 777, sters WITH 0 IN tvd
DO asserteaza WITH 'CAZ SINTETIC linie de set: cantitate NU primeste focus', loGrid.cCantitateArt.Text1.When() == .F., ''
DO asserteaza WITH 'CAZ SINTETIC linie de set: pret NU primeste focus', loGrid.cPretArt.Text1.When() == .F., ''
DO asserteaza WITH 'CAZ SINTETIC linie de set: pret_achizitie NU primeste focus', loGrid.cPretAchizitieArt.Text1.When() == .F., ''
DO asserteaza WITH 'CAZ SINTETIC linie de set: checkbox pret_cu_tva NU primeste focus', loGrid.cPretCuTvaArt._checkbox1.When() == .F., ''
DO asserteaza WITH 'CAZ SINTETIC linie de set: marcaj vizual albastru RGB(0,70,153)', ;
Evaluate(loGrid.cDenumireArt.DynamicForeColor) == Rgb(0,70,153), Transform(Evaluate(loGrid.cDenumireArt.DynamicForeColor))
*!* restaurare linie 2 la starea normala (nu de set), ca sa nu contamineze verificarile urmatoare
SELECT tvd
REPLACE id_vanzare_set WITH 0 IN tvd
*!* --- marcaj de linie stearsa (gri) - regresie fata de S4, folosind linia 1 ---
SELECT tvd
GO TOP
REPLACE sters WITH 1 IN tvd
DO asserteaza WITH 'linie stearsa: marcaj vizual gri RGB(150,150,150) neregresat', ;
Evaluate(loGrid.cDenumireArt.DynamicForeColor) == Rgb(150,150,150), Transform(Evaluate(loGrid.cDenumireArt.DynamicForeColor))
REPLACE sters WITH 0 IN tvd
*!* --- linie NOUA (id_vanzare_det=0, id_vanzare_set=0): pret_achizitie PRIMESTE focus ---
SELECT tvd
APPEND BLANK
REPLACE id_vanzare WITH loForm.nIdVanzare, id_vanzare_det WITH 0, id_vanzare_set WITH 0, id_articol WITH 999999, ;
cantitate WITH 1, pret WITH 10, sters WITH 0, denumire WITH 'LINIE NOUA TEST S5 UI' IN tvd
DO asserteaza WITH 'linie noua (id_vanzare_det=0): pret_achizitie PRIMESTE focus', loGrid.cPretAchizitieArt.Text1.When() == .T., ''
DO asserteaza WITH 'linie noua: cantitate/pret raman editabile ca inainte (id_vanzare_set=0)', ;
loGrid.cCantitateArt.Text1.When() == .T. AND loGrid.cPretArt.Text1.When() == .T., ''
*!* linia de test se sterge din cursor (nu ramane in captura finala)
SELECT tvd
GO BOTTOM
DELETE IN tvd
*!* pozitionare pentru captura: linia 1 grizata ramane vizibila la mijlocul verificarii, se
*!* repozitioneaza pe linia 2 (revenita la normal) ca fundalul de selectie sa nu acopere culoarea
SELECT tvd
GO TOP
REPLACE sters WITH 1 IN tvd
SKIP
loGrid.SetFocus()
DOEVENTS FORCE
ENDIF
STRTOFILE('REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), gcUILog, 1)
DO HarnessStep WITH 0, 'pagina3 articole S5 - 15 coloane, linie 1 grizata (sters=1), pret_achizitie vizibila', 25
loForm.Release()
IF Used('crsArticoleFactura')
Use In crsArticoleFactura
ENDIF
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
DO HarnessDone WITH 'done'
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + CHR(13) + CHR(10), gcUILog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' (valoare: ' + m.tcValoare + ')' + CHR(13) + CHR(10), gcUILog, 1)
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcUILog, 1)
ENDPROC