diff --git a/clase/comun.vc2 b/clase/comun.vc2 index cb393d3..51b85c6 100644 --- a/clase/comun.vc2 +++ b/clase/comun.vc2 @@ -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) diff --git a/clase/ofacturare_comun.vc2 b/clase/ofacturare_comun.vc2 index ddb8dff..0d92456 100644 --- a/clase/ofacturare_comun.vc2 +++ b/clase/ofacturare_comun.vc2 @@ -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() diff --git a/clase/omodificari.vc2 b/clase/omodificari.vc2 index c4dfdc8..eb77cc7 100644 --- a/clase/omodificari.vc2 +++ b/clase/omodificari.vc2 @@ -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 diff --git a/docs/oracle_export.md b/docs/oracle_export.md index e80a00c..93451fa 100644 --- a/docs/oracle_export.md +++ b/docs/oracle_export.md @@ -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; diff --git a/docs/scripturi-migrare-db.md b/docs/scripturi-migrare-db.md index c7ee029..906abdb 100644 --- a/docs/scripturi-migrare-db.md +++ b/docs/scripturi-migrare-db.md @@ -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('');` 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 diff --git a/programe/ofacturare_editare.prg b/programe/ofacturare_editare.prg index c054a63..07eb408 100644 --- a/programe/ofacturare_editare.prg +++ b/programe/ofacturare_editare.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_page3_articole.prg b/utile/Teste/editare_factura/test_page3_articole.prg index ee2a6cc..88c24c7 100644 --- a/utile/Teste/editare_factura/test_page3_articole.prg +++ b/utile/Teste/editare_factura/test_page3_articole.prg @@ -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) diff --git a/utile/Teste/editare_factura/test_s5_al_doilea_intrare.prg b/utile/Teste/editare_factura/test_s5_al_doilea_intrare.prg new file mode 100644 index 0000000..86636e7 --- /dev/null +++ b/utile/Teste/editare_factura/test_s5_al_doilea_intrare.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_s5_discount_valuta.prg b/utile/Teste/editare_factura/test_s5_discount_valuta.prg new file mode 100644 index 0000000..1e54b59 --- /dev/null +++ b/utile/Teste/editare_factura/test_s5_discount_valuta.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_s5_rollback_real.prg b/utile/Teste/editare_factura/test_s5_rollback_real.prg new file mode 100644 index 0000000..9de11a5 --- /dev/null +++ b/utile/Teste/editare_factura/test_s5_rollback_real.prg @@ -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() - 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 diff --git a/utile/Teste/editare_factura/test_s5_scriere_reala.prg b/utile/Teste/editare_factura/test_s5_scriere_reala.prg new file mode 100644 index 0000000..65bfa77 --- /dev/null +++ b/utile/Teste/editare_factura/test_s5_scriere_reala.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_s5_validari_articole.prg b/utile/Teste/editare_factura/test_s5_validari_articole.prg new file mode 100644 index 0000000..e02bf69 --- /dev/null +++ b/utile/Teste/editare_factura/test_s5_validari_articole.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_ui_s5_grid_pret_achizitie.prg b/utile/Teste/editare_factura/test_ui_s5_grid_pret_achizitie.prg new file mode 100644 index 0000000..479a5e9 --- /dev/null +++ b/utile/Teste/editare_factura/test_ui_s5_grid_pret_achizitie.prg @@ -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