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

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

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

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

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

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

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

2
.gitignore vendored
View File

@@ -133,3 +133,5 @@ bash.exe.stackdump
utile/Teste/**/screenshots*/
utile/Teste/**/uisync*/
utile/Teste/**/*_log.txt
utile/Teste/**/watchdog_out/
utile/Teste/**/*_dialog*.png

View File

@@ -150,7 +150,7 @@ DEFINE CLASS frm_borderou_facturi AS _frmbase OF "_frm_base.vcx"
ADD OBJECT 'Cmd_executa1' AS cmd_executa WITH ;
Anchor = 6, ;
Caption = "Marcheaz<61> \<trimis", ;
Caption = "Marcheaz<61> \<trimis", ;
ForeColor = 0,128,0, ;
Height = 27, ;
Left = 140, ;
@@ -1334,8 +1334,10 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
*< OBJECTDATA: ObjPath="But_attach1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="But_listare1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="But_verifica1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="But_editare1" UniqueID="" Timestamp="" />
*<DefinedPropArrayMethod>
*m: do_editare_factura
*m: do_listare_cc
*m: do_listare_centr
*m: do_listare_document
@@ -1350,7 +1352,7 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
*<PropValue>
BorderStyle = 1
cbuton2 = but_listare1;but_listare2;But_listare3
cbuton3 = but_modifica1;but_modifica2
cbuton3 = but_modifica1;but_modifica2;But_editare1
cfiltru_initial =
cgridsortlist = grid_facturi,grid_detalii
DoCreate = .T.
@@ -1410,6 +1412,17 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
Visible = .F.
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'But_editare1' AS but_modifica WITH ;
Anchor = 12, ;
caction = do_editare_factura, ;
Caption = "", ;
Left = 650, ;
Name = "But_editare1", ;
TabIndex = 29, ;
ToolTipText = "Editare factura (articole, cantitati, preturi)", ;
Top = 341
*< END OBJECT: ClassLib="cmd_butoane.vcx" BaseClass="commandbutton" />
ADD OBJECT 'But_listare1' AS but_listare WITH ;
Anchor = 8, ;
Left = 698, ;
@@ -3471,7 +3484,7 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
Anchor = 12, ;
AutoSize = .T., ;
BackStyle = 0, ;
Caption = "Total <20>n valuta", ;
Caption = "Total <20>n valuta", ;
FontSize = 10, ;
Height = 18, ;
Left = 311, ;
@@ -3711,6 +3724,150 @@ DEFINE CLASS frm_facturi AS _frmbase OF "_frm_base.vcx"
This.do_cauta()
ENDPROC
PROCEDURE do_editare_factura
If !This.lactiv3
Return
Endif
If Reccount('crsfacturi') = 0
amessagebox("Nu exista inregistrari pentru editare!",0+48,"Atentie")
Return
Endif
If glLunaInchisa
Return
Endif
Local lnSters, llEProforma, lnIdSet, lnIdFactD, lnSucces, Omodif, llGasitRand
Private pnAn, pnLuna, lnCod, lnIdFact, lnIdVanzare
Store 0 To pnAn, pnLuna, lnCod, lnIdFact, lnIdSet, lnIdFactD, lnIdVanzare
Select crsfacturi
lnIdVanzare = id_vanzare
lnCod = cod
pnAn = Year(data_act)
pnLuna = Month(data_act)
lnSters = sters
lnIdFact = id_fact
llEProforma = (eproforma = 1)
If lnSters = 1
amessagebox("Acest document este deja sters!",0+48,"Atentie")
Return
Endif
If llEProforma
amessagebox("Proformele nu pot fi editate pe aceasta cale!",0+48,"Atentie")
Return
Endif
If (pnAn * 12) + pnLuna <> (gnAn * 12) + gnLuna
amessagebox('Nu puteti edita decat inregistrari din luna curenta!',0,'Atentie!')
Return
Endif
*** verificare restrictie editare documente cu referinte (incasari/plati)
If ReferinteDocumenteNota(pnAn, pnLuna, lnCod) && odocumente.prg
amessagebox("Documentul are referinte (incasari/plati). Nu poate fi editat.", 0+48,"Editare")
Return
Endif
*** verificare restrictie editare facturi trimise in eFactura
If EsteInEFactura(lnIdFact) && ofacturare_editare.prg
amessagebox("Documentul a fost trimis in eFactura. Nu poate fi editat.", 0+48,"Editare")
Return
Endif
If IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.) && ofacturare_editare.prg
If Reccount('actactan') = 0
amessagebox("Nu exista nota contabila pentru aceasta factura.",0+48,"Atentie")
Else
Select actactan
llGasitRand = .F.
If lnIdFact > 0
Locate For Nvl(id_fact, 0) = lnIdFact
llGasitRand = Found()
Endif
If !llGasitRand
*** rezerva: nota nu contine id_fact-ul din vanzari (avize, transferuri, documente vechi)
Go Top
lnIdFact = id_fact
Endif
lnIdSet = id_set
lnIdFactD = id_factd
Select a.*, Iif(Nvl(id_jtva_coloana,0)=0,0,1) As cu_Tva From tact a Into Cursor tact Readwrite
Select tact
Omodif = Createobject([frm_modific2024], lnIdSet)
Omodif.Show()
If buton = 1
If Thisform.do_deschide_tranzactie()
Select actactan
lnSucces = OSCRIE_IN_FISIERE(2,.T.,.T.)
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.)
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)
Endif
If Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1))
If lnSucces > 0
Thisform.do_cauta()
Endif
Endif
Endif
Endif
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
Endif
Select crsfacturi
Locate For id_vanzare = lnIdVanzare
If !Found()
Go Top
Endif
ENDPROC
PROCEDURE do_listare
*!* 26.11.2019
*!* marius.mutu
@@ -4988,7 +5145,7 @@ DEFINE CLASS frm_modifica_articol_factura AS frm_termin_renunt OF "_frm_child.vc
_shape2.Top = 0
_shape2.Width = 64
_shape2.ZOrderSet = 2
Lb_titlu_alb_b121.Caption = "Modific<69> explica<63>ie articol"
Lb_titlu_alb_b121.Caption = "Modific<69> explica<63>ie articol"
Lb_titlu_alb_b121.Name = "Lb_titlu_alb_b121"
Lb_titlu_alb_b121.TabIndex = 5
Lb_titlu_alb_b121.ZOrderSet = 3
@@ -5046,7 +5203,7 @@ DEFINE CLASS frm_modifica_articol_factura AS frm_termin_renunt OF "_frm_child.vc
_edbase1.Name = "_edbase1", ;
_edbase1.Top = 23, ;
_edbase1.Width = 325, ;
_lbbase1.Caption = "Explica<63>ie", ;
_lbbase1.Caption = "Explica<63>ie", ;
_lbbase1.Name = "_lbbase1"
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
@@ -5280,7 +5437,7 @@ DEFINE CLASS frm_modifica_factura AS frm_termin_renunt OF "_frm_child.vcx"
Text_simplu1.Name = "Text_simplu1", ;
Text_simplu1.Top = 3, ;
Text_simplu1.Width = 177, ;
Lb_simplu1.Caption = "Data <20>i ora expedierii", ;
Lb_simplu1.Caption = "Data <20>i ora expedierii", ;
Lb_simplu1.Name = "Lb_simplu1"
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
@@ -5330,7 +5487,7 @@ DEFINE CLASS frm_modifica_factura AS frm_termin_renunt OF "_frm_child.vcx"
Top = 128, ;
ZOrderSet = 9, ;
img_cautare.Name = "img_cautare", ;
clb_tx_cautare.Lb_simplu1.Caption = "Ma<4D>ina", ;
clb_tx_cautare.Lb_simplu1.Caption = "Ma<4D>ina", ;
clb_tx_cautare.Lb_simplu1.Name = "Lb_simplu1", ;
clb_tx_cautare.Name = "clb_tx_cautare", ;
clb_tx_cautare.Text_simplu1.Left = 133, ;
@@ -6820,7 +6977,7 @@ DEFINE CLASS frm_proforme AS _frmbase OF "_frm_base.vcx"
Anchor = 12, ;
AutoSize = .T., ;
BackStyle = 0, ;
Caption = "Total <20>n valuta", ;
Caption = "Total <20>n valuta", ;
FontSize = 10, ;
Height = 18, ;
Left = 311, ;
@@ -6852,7 +7009,7 @@ DEFINE CLASS frm_proforme AS _frmbase OF "_frm_base.vcx"
OPTION1.Top = 5, ;
OPTION1.Value = 1, ;
OPTION2.AutoSize = .T., ;
OPTION2.Caption = "<22>n valuta", ;
OPTION2.Caption = "<22>n valuta", ;
OPTION2.FontCharSet = 238, ;
OPTION2.FontSize = 10, ;
OPTION2.Left = 55, ;
@@ -6860,7 +7017,7 @@ DEFINE CLASS frm_proforme AS _frmbase OF "_frm_base.vcx"
OPTION2.Top = 5, ;
Option3.AutoSize = .T., ;
Option3.BackStyle = 0, ;
Option3.Caption = "<22>n RON", ;
Option3.Caption = "<22>n RON", ;
Option3.FontCharSet = 238, ;
Option3.FontSize = 10, ;
Option3.Height = 18, ;
@@ -6891,7 +7048,7 @@ DEFINE CLASS frm_proforme AS _frmbase OF "_frm_base.vcx"
OPTION1.Top = 5, ;
OPTION1.Value = 0, ;
OPTION2.AutoSize = .T., ;
OPTION2.Caption = "<22>terse", ;
OPTION2.Caption = "<22>terse", ;
OPTION2.FontCharSet = 238, ;
OPTION2.FontSize = 10, ;
OPTION2.ForeColor = 255,0,0, ;
@@ -6900,7 +7057,7 @@ DEFINE CLASS frm_proforme AS _frmbase OF "_frm_base.vcx"
OPTION2.Top = 5, ;
Option3.AutoSize = .T., ;
Option3.BackStyle = 0, ;
Option3.Caption = "Ne<4E>terse", ;
Option3.Caption = "Ne<4E>terse", ;
Option3.FontCharSet = 238, ;
Option3.FontSize = 10, ;
Option3.Height = 18, ;

View File

@@ -6628,6 +6628,33 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
*< OBJECTDATA: ObjPath="pgfArticole.PAGE2.grdRulajeObinv.cvaloarecTVA.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE2.grdRulajeObinv.cvaloarevTVA.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE2.grdRulajeObinv.cvaloarevTVA.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cDenumireArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cDenumireArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cCodmatArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cCodmatArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cLotArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cLotArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Header1" UniqueID="" Timestamp="" />
*< 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.cPretCuTvaArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cDiscountUnitarArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cDiscountUnitarArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cGestiuneArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cGestiuneArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cValutaArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cValutaArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cExplicatieArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cExplicatieArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Header1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE2.grdRulajeObinv.cvaloarevcTVA.cvaloarevcTVA" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="pgfArticole.PAGE2.grdRulajeObinv.cvaloarevcTVA.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="Grid1" UniqueID="" Timestamp="" />
@@ -6774,18 +6801,21 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
*p: ccontrol
*p: cpaymentcode
*p: fel_compensare
*p: larearticolevanzari
*p: lavertizatexigibilizare
*p: leditare
*p: lmodificare
*p: lnotanoua
*p: lverificare
*p: lvizualizare
*p: nidvanzare
*p: nid_set
*p: nnnirvechi
*p: noldsuma && Valoarea la intrarea in campul "Suma"
*p: nprimadata
*p: nsuma && Total suma inregistrari
*p: ntaxcode
*p: ntipvanzare
*p: obackupxml
*p: oldvalue
*p: oset
@@ -6805,6 +6835,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
DoCreate = .T.
FontCharSet = 238
Height = 508
larearticolevanzari = .F.
lavertizatexigibilizare = .F.
leditare = .F.
lmodificare = .F.
@@ -6812,12 +6843,14 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
lverificare = .F.
lvizualizare = .F.
Name = "frm_modific2024"
nidvanzare = 0
nid_set = 0
nnnirvechi = 0
noldsuma = 0
nprimadata = 0
nsuma = 0
ntaxcode = 0
ntipvanzare = 0
obackupxml = NULL
oldvalue =
oset = NULL
@@ -8630,6 +8663,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
Height = 142, ;
Left = 5, ;
Name = "pgfArticole", ;
PageCount = 3, ;
Top = 361, ;
Width = 764, ;
ZOrderSet = 0, ;
@@ -8638,7 +8672,10 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
PAGE1.Name = "PAGE1", ;
PAGE2.Caption = "Rulaje obiecte inventar in folosinta (8039)", ;
PAGE2.ForeColor = 0,128,64, ;
PAGE2.Name = "PAGE2"
PAGE2.Name = "PAGE2", ;
PAGE3.Caption = "Articole factura", ;
PAGE3.ForeColor = 128,0,128, ;
PAGE3.Name = "PAGE3"
*< END OBJECT: ClassLib="_baza.vcx" BaseClass="pageframe" />
ADD OBJECT 'pgfArticole.PAGE1._grdfooter1' AS _grdfooter WITH ;
@@ -12217,6 +12254,374 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura' AS _grdrow WITH ;
Anchor = 15, ;
ColumnCount = 13, ;
DeleteMark = .F., ;
FontSize = 10, ;
GridLineColor = 128,128,128, ;
HeaderHeight = 35, ;
Height = 107, ;
Left = 0, ;
Name = "grdArticoleFactura", ;
ReadOnly = .T., ;
RecordSource = "tvd", ;
RowHeight = 19, ;
TabIndex = 4, ;
Tag = "RESIZABLE", ;
Top = 0, ;
Width = 759, ;
ZOrderSet = 1, ;
Column1.ControlSource = "tvd.denumire", ;
Column1.FontName = "Arial", ;
Column1.FontSize = 10, ;
Column1.Name = "cDenumireArt", ;
Column1.Width = 170, ;
Column2.ControlSource = "tvd.codmat", ;
Column2.FontName = "Arial", ;
Column2.FontSize = 10, ;
Column2.Name = "cCodmatArt", ;
Column2.Width = 90, ;
Column3.ControlSource = "tvd.serie", ;
Column3.FontName = "Arial", ;
Column3.FontSize = 10, ;
Column3.Name = "cSerieArt", ;
Column3.Width = 70, ;
Column4.ControlSource = "tvd.lot", ;
Column4.FontName = "Arial", ;
Column4.FontSize = 10, ;
Column4.Name = "cLotArt", ;
Column4.Width = 60, ;
Column5.ControlSource = "tvd.cantitate", ;
Column5.FontName = "Arial", ;
Column5.FontSize = 10, ;
Column5.Format = "R", ;
Column5.InputMask = (get_mask(12,gnPCANT)), ;
Column5.Name = "cCantitateArt", ;
Column5.Width = 70, ;
Column6.ControlSource = "tvd.pret", ;
Column6.FontName = "Arial", ;
Column6.FontSize = 10, ;
Column6.Format = "R", ;
Column6.InputMask = (get_mask(12,gnPPRET)), ;
Column6.Name = "cPretArt", ;
Column6.Width = 70, ;
Column7.ControlSource = "tvd.pret_cu_tva", ;
Column7.FontName = "Arial", ;
Column7.FontSize = 10, ;
Column7.Format = "R", ;
Column7.InputMask = (get_mask(12,gnPPRET)), ;
Column7.Name = "cPretCuTvaArt", ;
Column7.Width = 80, ;
Column8.ControlSource = "tvd.proc_tvav", ;
Column8.FontName = "Arial", ;
Column8.FontSize = 10, ;
Column8.Format = "R", ;
Column8.InputMask = "9.99", ;
Column8.Name = "cProcTvavArt", ;
Column8.Width = 55, ;
Column9.ControlSource = "tvd.discount_unitar", ;
Column9.FontName = "Arial", ;
Column9.FontSize = 10, ;
Column9.Format = "R", ;
Column9.InputMask = (get_mask(12,gnPPRET)), ;
Column9.Name = "cDiscountUnitarArt", ;
Column9.Width = 90, ;
Column10.ControlSource = "tvd.nume_gestiune", ;
Column10.FontName = "Arial", ;
Column10.FontSize = 10, ;
Column10.Name = "cGestiuneArt", ;
Column10.Width = 90, ;
Column11.ControlSource = "tvd.nume_val", ;
Column11.FontName = "Arial", ;
Column11.FontSize = 10, ;
Column11.Name = "cValutaArt", ;
Column11.Width = 60, ;
Column12.ControlSource = "tvd.explicatie", ;
Column12.FontName = "Arial", ;
Column12.FontSize = 10, ;
Column12.Name = "cExplicatieArt", ;
Column12.Width = 150, ;
Column13.ControlSource = "tvd.taxcode", ;
Column13.FontName = "Arial", ;
Column13.FontSize = 10, ;
Column13.Name = "cTaxcodeArt", ;
Column13.Width = 60
*< END OBJECT: ClassLib="_grd_base.vcx" BaseClass="grid" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Cantitate", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cCantitateArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cCodmatArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Cod material", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cCodmatArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cDenumireArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Articol", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cDenumireArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cDiscountUnitarArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Discount unitar", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cDiscountUnitarArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cExplicatieArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Explicatie", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cExplicatieArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cGestiuneArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Gestiune", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cGestiuneArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cLotArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Lot", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cLotArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Pret", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Pret cu TVA", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cPretCuTvaArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "TVA %", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cProcTvavArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Serie", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cSerieArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Taxcode", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cTaxcodeArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cValutaArt.Header1' AS header WITH ;
Alignment = 2, ;
Caption = "Valuta", ;
FontName = "Arial", ;
FontSize = 10, ;
Name = "Header1", ;
WordWrap = .T.
*< END OBJECT: BaseClass="header" />
ADD OBJECT 'pgfArticole.PAGE3.grdArticoleFactura.cValutaArt.Text1' AS textbox WITH ;
BackColor = 225,225,225, ;
BorderStyle = 0, ;
FontName = "Arial", ;
FontSize = 10, ;
ForeColor = 0,0,0, ;
Margin = 0, ;
Name = "Text1", ;
ReadOnly = .T., ;
SelectedBackColor = 100,185,255
*< END OBJECT: BaseClass="textbox" />
ADD OBJECT 'txtSuma' AS _textbox WITH ;
Alignment = 3, ;
Anchor = 8, ;
@@ -13667,6 +14072,16 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
If !Used('saft_mecanisme_plati')
update_saft_mecanisme_plati()
ENDIF
*!* cursorul tvd, gol - din functia comuna daca ofacturare_editare.prg e incarcat
If !Used('tvd')
IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure"))
CreeazaCursorTvdGol()
ELSE
*!* structura se repeta: in ROACONT/ROAGEST functia comuna nu e incarcata
CREATE CURSOR tvd (id_vanzare I, id_vanzare_det I, id_articol I, cantitate N(12,3), pret N(14,4), pret_cu_tva N(14,4), proc_tvav N(6,4), discount_unitar N(14,4), id_gestiune I, cont C(20), id_valuta I, id_jtva_coloana I, serie C(20), explicatie C(60), taxcode I, lot C(20), sters I, denumire C(100), codmat C(30), nume_gestiune C(60), nume_val C(20))
ENDIF
ENDIF
ENDPROC
PROCEDURE Resize
@@ -13754,6 +14169,27 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx"
This.Grid1.HeaderHeight = 37
this.pgfArticole.page1.grdRulaje.HeaderHeight = 37
this.pgfArticole.page2.grdRulajeObinv.HeaderHeight = 37
* pagina de articole factura (VANZARI/VANZARI_DETALII) - apare doar cand ofacturare_editare.prg
* e incarcat (doar ROAFACTURARE - ROACONT/ROAGEST nu-l inregistreaza) si documentul are rand in vanzari
This.lAreArticoleVanzari = .F.
This.nIdVanzare = 0
This.nTipVanzare = 0
IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Reccount('tact') > 0
IncarcaVanzareDinNota('tact')
IF Reccount('tvanz') = 1
This.lAreArticoleVanzari = .T.
This.nIdVanzare = tvanz.id_vanzare
This.nTipVanzare = tvanz.tip
ENDIF
ENDIF
IF This.lAreArticoleVanzari
IncarcaArticoleFactura(This.nIdVanzare)
This.pgfArticole.PageCount = 3
ELSE
This.pgfArticole.PageCount = 2
ENDIF
This.grid1.column42.visible = m.gl406 && saft taxa
This.grid1.column43.visible = m.gl406 && saft plata

View File

@@ -76,6 +76,17 @@ fals `roundtrip text1 != text2`. Ruleaza atunci cu lista rebazata:
- Metoda noua de clasa cere `*m: nume` in `*<DefinedPropArrayMethod>`; fara ea, prima salvare
din IDE o arunca tacut, iar refresh-ul urmator absoarbe pierderea in cache (si in .bak-uri).
Override-urile de metode de baza (Init, Show, hook-uri) nu au nevoie de `*m:`.
- **Proprietate noua de clasa cere identic `*p: nume` in `*<DefinedPropArrayMethod>`** — nu doar
valoarea in `*<PropValue>`. Fara `*p:`, valoarea se scrie in text, **trece fidelity-check-ul**
(fidelity compara doar text-sursa cu text-regenerat) si ajunge in binar ca text, dar VFP nu o
materializeaza pe obiect: `PEMSTATUS(o,'nume',5)` da `.F.` si orice acces cade cu eroarea 1734
"Property ... is not found". Dovedit in ambele sensuri pe o clasa de proba izolata (cu `*p:` ->
`.T.`, fara -> `.F.`).
- **FoxBin2Prg sorteaza alfabetic cu `_` DUPA literele obisnuite**, nu in ordine ASCII brut (unde
`'_' (0x5F) < 'v' (0x76)` pe litere mici). O intrare `*p:`/`*m:` scrisa de mana in ordine "ASCII
corecta" pica fidelity-check-ul; ordinea canonica se citeste din textul regenerat in
`<staging>\verify\*.vc2` (vezi punctul de mai jos despre pozitia metodelor noi — se aplica identic
proprietatilor).
- FoxBin2Prg NU pastreaza pozitia in text a unei metode noi: o regenereaza la pozitia
alfabetica din `*<DefinedPropArrayMethod>`. O metoda scrisa in alta parte a fisierului
face fidelity-check-ul sa pice. Sursa de adevar pentru relocare e textul regenerat din

View File

@@ -4,6 +4,45 @@ Regula (valabila in toate proiectele ROA/VFP): un singur subagent care duce o mi
(iteratii de teste, investigatii, mai multe sarcini inlantuite) acumuleaza context urias —
calitate degradata, cost mare. Nu se lucreaza asa.
## REGULA ZERO: predarea contextului e OBLIGATORIE, nu optionala
Se aplica **identic sesiunii principale si oricarui subagent**. Nu e o recomandare de eficienta,
e o conditie de corectitudine: un context supraincarcat pierde tacut decizii deja luate si reia
investigatii deja platite.
**Declansatoare — la oricare dintre ele, predarea porneste imediat:**
1. contextul propriu a trecut de **~50% din fereastra** (orchestrator) sau de **~200-250k tokens**
(subagent);
2. harness-ul anunta compactare automata (`PreCompact`) — **prea tarziu ca sa mai amani, dar nu
prea tarziu ca sa scrii fisierul**; scrie handoff-ul inainte sa raspunzi orice altceva;
3. utilizatorul semnaleaza ca s-a atins limita;
4. s-a terminat un bloc de lucru — indiferent cat context a mai ramas.
**Procedura, in ordine, fara exceptii:**
1. **Opreste lucrul.** Nu incepe o sarcina noua, nu porni o rulare noua. Daca esti in mijlocul unei
editari, du-o pana la o stare **consistenta pe disc** (text si binare sincronizate) si atat.
2. **Scrie handoff-ul pe disc** (`docs\handoff_<subiect>.md`). Nu in mesaj, nu in raport — **pe
disc**. Un mesaj catre orchestrator se pierde; fisierul nu.
3. **Confirma in doua randuri** ce ai lasat in urma si daca ceva e intr-o stare periculoasa
(fisier editat fara write-back, tranzactie deschisa, proces ramas viu, date de test consumate).
4. **Opreste-te.** Blocul urmator il ia un agent proaspat. "Mai am putin" nu e motiv de amanare —
exact acolo se pierd lucrurile.
**Obligatia e a fiecaruia, nu doar a orchestratorului.** Un subagent care simte ca se apropie de
prag **anunta singur** si cere predarea; nu asteapta sa fie oprit. Un orchestrator care se apropie
de prag isi scrie propriul handoff si preda orchestrarea unei sesiuni noi — regula nu are portita
pentru "eu doar coordonez".
**Ce intra obligatoriu in handoff**: inventarul livrabilelor cu `fisier:linie`; ce e terminat si ce
nu; **daca write-back-ul e facut** pentru fiecare fisier atins; comanda exacta de rulare a testelor
si unde scriu logurile; ce s-a stabilit deja (ca sa nu se reia); ce e interzis; starea datelor de
test consumate; capcanele de mediu platite. Fara analize noi — doar stare.
Doua lectii platite scump, amandoua din aceeasi cauza — un agent tinut peste prag (`s5-nivel2`
533k, `s2d-modifica-gestiuni` 434k). Predarea e aproape gratuita: raportul e oricum deja scris.
## Cum se lucreaza
- Sesiunea principala (sau un agent primar) e ORCHESTRATOR: imparte planul pe sarcini
@@ -58,7 +97,9 @@ diagnostic, fixuri aplicate direct in loc de delegate.
care intoarce da/nu, nu continutul din care ai deduce singur.
- **Nu aplica fixuri direct** decat sub ~5 linii si doar daca ai deja contextul in sesiune.
- **Prag si pe orchestrator**: la ~50% din fereastra, scrie handoff-ul si reia orchestrarea
dintr-o sesiune noua. Handoff-ul e deja pe disc - exact pentru asta exista.
dintr-o sesiune noua. Handoff-ul e deja pe disc - exact pentru asta exista. Vezi **Regula zero**
de la inceputul fisierului: obligatia e aceeasi ca pentru subagenti, fara portita pentru
"eu doar coordonez".
## Model de lucru: backlog de story-uri mici, context mic per story

View File

@@ -12,13 +12,20 @@
handoff-uri/planuri de runda, `*.pre_runda*.bak`, `.bak`-urile din cache-ul text si artefactele
de test (`screenshots*`, `uisync*`, `.fxp`, `.err`, loguri). Sterge doar fisiere neurmarite de
git — cele tracked sunt sarite.
2. Comentarii in cod: minime, strict functionale, descriu comportamentul CURENT (ca si cum ar fi
scris asa de la inceput) — o linie de regula, 2-3 doar pentru metode cu contract nebanal
(parametri, cursor asteptat/lasat deschis, pozitionare la iesire). Istoricul modificarilor
sta DOAR in ANTETUL fisierului (niciodata inline) — la fel in `.prg` si in PL/SQL Oracle.
INTERZIS in comentariu: nume de agent ("claude"), referinte la propuneri/decizii/etape
(`docs/propuneri_*.md`, "M2", "T3", "dec.6", "runda27", "E7", "D-H2"), istoricul modificarii
("inlocuieste...", "nu mai depinde de...", "mutat din..."), date si autori pe cod nou.
2. Comentarii in cod SI in scripturile de migrare `.sql`: minime, strict functionale, descriu
comportamentul CURENT (ca si cum ar fi scris asa de la inceput). **Doar daca e cazul** — un
comentariu se scrie acolo unde explica o **ramura** sau un **filtru** care nu se citeste singur,
ca sa se inteleaga codul; unde codul e limpede, nu se comenteaza. O linie de regula, 2-3 doar
pentru metode cu contract nebanal (parametri, cursor asteptat/lasat deschis, pozitionare la
iesire), 4-5 in antetul unui script de migrare (ce e obiectul si ce contract are). Istoricul
modificarilor sta DOAR in ANTETUL fisierului (niciodata inline) — la fel in `.prg`, in `.sql`
si in PL/SQL Oracle.
INTERZIS in comentariu: nume de agent ("claude"), referinte la planuri, stories, propuneri,
decizii, etape sau erori (`docs/plan_*.md`, `docs/propuneri_*.md`, "#6", "S4", "M2", "T3",
"dec.6", "runda27", "E7", "D-H2"), trimiteri la rapoartele din `docs/` ("vezi raportul,
sectiunea ..."), justificarea alegerii facute (de ce numele acesta, de ce s-a respins varianta
cealalta), istoricul modificarii ("inlocuieste...", "nu mai depinde de...", "mutat din..."),
date si autori pe cod nou.
Comentarii vechi in stil jurnal nu se rescriu din oficiu — doar cand blocul e oricum atins
sau la cerere explicita.
Antet: o singura intrare CUMULATIVA per functionalitate (`*!* DD.MM.YYYY` / `*!* autor` /
@@ -46,6 +53,20 @@
se umple contextul. Misiuni lungi: subagenti proaspeti per sarcina (max ~200-250k
tokens/subagent), nu unul singur cu context acumulat; handoff compact pe disc:
`orchestrare-subagenti.md`.
**Verificarea a ce se intoarce** — pentru executie delegarea merge, pentru concluzii nu:
- **citeste contractul functiei apelate, nu-l presupune.** Un raport care descrie corect
structura codului poate gresi sensul: ex. `goExecutor.oExecute` intoarce succes/insucces
(`CT_SUCCES`/`CT_INSUCCES`), NU numarul de randuri — o garda `If lnSucces > 0` citita ca
"are randuri" a produs un bug inexistent, raportat ca blocant. Deschide functia.
- **fix ingust, nu supra-constructie.** Cand premisa se corecteaza, corectia se restrange:
mecanismele noi introduse "ca sa fie sigur" diverg de la codul-sablon si blocheaza
refolosirea lui in alta parte.
- **"zero cazuri in date" nu dovedeste nimic** pe scheme cu date de test; raspunsul vine din
cod. Si invers: o potrivire de cifre pe 1-2 documente nu e o regula.
- **cere raportul sa separe "testat" de "analizat static"**, si ce n-a putut fi acoperit. Un
agent care nu e intrebat explicit va prezenta amandoua la fel.
- **spune-i ca poate infirma briefingul.** Altfel implementeaza corectia ceruta chiar cand a
descoperit ca nu e nevoie de ea.
7. Conventii obligatorii, de citit cand atingi zona respectiva:
- editezi fisiere cu octeti cp1252 - `.vc2`/`.sc2`, dar si `.prg` (ex. `programe\ocautare.prg`,
unde `s`-urile cu virgula din `masina`/`si contul`/`transa` sunt octetul `0xBA`) ->

View File

@@ -70,6 +70,10 @@ Marius ce nu e aplicat. Exportul sursei de referinta: `oracle_export.md`.
- **Idempotent**: rulat de doua ori nu mai schimba nimic (`merge`, `where <coloana> is null`).
- **Un pachet sta singur in scriptul lui**, fara alt DDL sau DML alaturi: modificarile de tabele si
curateniile de date merg in scripturi separate, cu numar propriu.
- **Comentarii strict necesare**, ca in cod: antet de 4-5 randuri (data + autor, ce e obiectul, ce
contract are), fara referinte la planuri, stories, propuneri, decizii sau erori, fara trimiteri la
rapoartele din `docs/`, fara justificarea alegerilor si fara istoricul modificarii. Regula completa
si lista de interdictii: `reguli_lucru.md`, punctul 2.
## Compatibilitate cu serverele clientilor

View File

@@ -175,3 +175,40 @@ p. **Nu numi variabilele ca functii built-in VFP.** VFP e insensibil la litere m
**tacut false** si par sa spuna ca obiectul nu a fost gasit — masuratoarea e stricata, nu
rezultatul. Simptom de recunoscut: mesajul de eroare contine numele **cu majuscule** al unei
functii VFP, nu numele variabilei asa cum ai scris-o. Prefixeaza distinctiv (`loBifa`, `loCtrl`).
q. **`DO ... WITH` paseaza PRIN REFERINTA, iar variabila devine invizibila sub numele ei propriu in
procedura apelata.** Consecinta urata: orice `?variabila` din SQL passthrough de pe lantul
apelurilor respective nu se mai poate lega, si VFP deschide dialogul nativ **"View Parameter —
Enter the value for `<var>`"**, care blocheaza testul headless la infinit (fara log, fara
eroare). Dovedit pe date: o variabila globala era valida in programul principal la fiecare
checkpoint, dar `TYPE()` intorcea `'U'` (nedefinita) exact la intrarea in procedurile apelate cu
`DO proc WITH gnAn, gnLuna` — si valida in cele apelate fara `DO ... WITH` — corelatie exacta cu
**sintaxa apelului**, nu cu ce face procedura. Remediu: paranteze pe fiecare argument forteaza
pasarea prin valoare — `DO proc WITH (gnAn), (gnLuna)`.
r. **O suita care testeaza o clasa din `COMUN` poate incarca implicit copia ALTUI PRODUS**, daca
harnessul de mediu comun (ex. `test_init_env_auto.prg`) face `SET DEFAULT TO`/`SET PATH` pe
working copy-ul altui produs (ex. `D:\ROA\ROACONT\...`) inainte de `SET CLASSLIB TO <clasa>
ADDITIVE`: `Createobject` gaseste prima clasa cu acel nume pe `PATH`, care poate fi
`D:\ROA\ROACONT\COMUN\clase\<clasa>.vcx` in loc de fisierul editat sub test — silentios, fara
eroare. Simptom: testul trece curat, dar valideaza cod vechi/alt working copy; orice concluzie
trasa asa e nula. Remediu, dupa initializarea mediului comun: `RELEASE CLASSLIB <nume>` +
`SET CLASSLIB TO <cale completa a fisierului sub test> ADDITIVE` (`SET CLASSLIB ... ADDITIVE`
**singur**, fara `RELEASE` intai, da eroarea 24 "Alias name is already in use" si pastreaza
tacut copia veche). Verificare obligatorie dupa instantiere: `loForm.ClassLibrary` trebuie sa
arate calea completa asteptata.
s. **Dialoguri native care nu apar in log si nu sunt prinse de `ON ERROR`/mock-uri** (ex. "View
Parameter", "Open" pe fisier lipsa): nu se depaneaza orbeste, se ruleaza sub
`COMUN\utile\Teste\watchdog_vfp.ps1 -Script <test.prg> -AutoDismiss`. Lanseaza `vfp9.exe -A -T`,
polleaza ferestrele top-level ale procesului (identificate prin `Process.MainWindowHandle`, nu
dupa numele clasei — VFP foloseste clase `vfp9...` si pentru shell-ul principal si pentru
dialogurile proprii), captureaza PNG + textul fiecarui control copil (`WM_GETTEXT`) la orice
fereastra noua, si omoara procesul la iesire (`finally`, niciodata ramas viu). **Interzis strict:
input real** (`keybd_event`/`SetForegroundWindow`/`SendInput`/`mouse_event`) — masina e
partajata, un input injectat la nivel de sistem ajunge in orice fereastra are focus in acel
moment, nu neaparat in dialogul tintit (patit: un ESCAPE injectat a intrerupt sesiunea IDE a
utilizatorului, aflat in lucru in paralel). `-AutoDismiss` foloseste STRICT mesaje Windows
tintite pe handle (`BM_CLICK` pe buton Cancel/Anulare, altfel `WM_COMMAND IDCANCEL` ->
ESCAPE ca mesaj `WM_KEYDOWN`/`WM_KEYUP` -> `WM_CLOSE`); pe dialogurile proprii VFP owner-drawn
(butoane desenate, nu HWND-uri reale) dismiss-ul poate esua cinstit — captura PNG + dump text
raman oricum de incredere, indiferent de rezultatul dismiss-ului. Sterge intotdeauna `.fxp`-ul
vechi inainte de FIECARE rulare (watchdog-ul o face singur la lansare) — altfel VFP ruleaza
tacut codul compilat vechi si rulari "identice" dau rezultate diferite, fara nicio urma in log.

View File

@@ -46,3 +46,6 @@ INTERESUL MEU ESTE SA SIMPLIFIC INTERFATA, SA FIE MAI EFICIENTA, SA NU FIE 2 FOR
17. ROAFACTURARE - daca o nota contabile de vanzare nu are configurat tip de venit/cheltuiala, si nici articolele din lista de preturi, si nici nu se completeaza tip venit/cheltuiala la facturare, la finalizarea facturii apare o eroare oracle ORA ca id_venchelt nu are voie sa fie null. eroarea nu este user friendly si nu se poate recupera usor, pentru ca trebuie sa se dea renunt la toata factura si sa se reia tot porcesul, cu completarea tip venit cheltuiala. si nici nu stiu de ce este obligatoriu tip venit/cheltuiala.
18. ROAFACTURARE - EMITERE FACTURA DIN PROFORMA
19. ROAFACTURARE - EDITARE PROFORMA

View File

@@ -0,0 +1,296 @@
*** ofacturare_editare
*!* 08.08.2026
*!* marius.mutu
*!* helpere comune pentru editarea unei facturi emise: garda eFactura, cursoarele notei si ale rulajelor,
*!* randul de vanzare corespunzator notei si liniile de articole pentru pagina de articole factura
*!* parametru: id_fact
*!* verifica daca factura cu id_fact dat a fost deja trimisa in eFactura (anaf_efactura)
FUNCTION EsteInEFactura
LPARAMETERS tnIdFact
LOCAL lcSql, lnEFactura, llSucces
lnEFactura = 0
lcSql = [select count(*) as lneFactura from anaf_efactura where id_fact = ] + Alltrim(Str(Nvl(m.tnIdFact,0)))
llSucces = goExecutor.oSelecteaza2Value(m.lcSql, @lnEFactura)
RETURN (Nvl(m.lnEFactura,0) > 0)
ENDFUNC && EsteInEFactura
*!* parametri: cod, an, luna, arata si randurile sterse (implicit .F.)
*!* incarca cursoarele notei (tact) si ale rulajelor (trul, trul_obinv), cu recalculul de
*!* valoare/valtva/valoarev/valtvav pe randurile goale; raman deschise READWRITE pe seama apelantului
*!* pe eroare Oracle intoarce .F. si inchide ce a deschis pana atunci
FUNCTION IncarcaCursoareModificareNota
LPARAMETERS tnCod, tnAn, tnLuna, tlAratasterse
LOCAL lcConditieSters, lcSql, lcCursor, 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
lcConditieSters = IIF(m.tlAratasterse, [ ], [ STERS = 0 AND ])
lcSql = [select * from vact_tot where ] + lcConditieSters + [ an = ] + Transform(m.tnAn) + [ and luna = ] + Transform(m.tnLuna) + [ and cod = ] + Alltrim(Str(m.tnCod)) + [ order by id_act]
lcCursor = [v_act]
lnSucces = goExecutor.oExecute(lcSql,lcCursor)
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
RETURN .F.
ENDIF
SELECT * FROM v_act INTO CURSOR actactan
SELECT .F. AS ales, T.*, CAST(0 AS N(2)) AS tipnota FROM actactan T INTO CURSOR tact READWRITE
USE IN v_act
IF lnSucces > 0
lcSql = [select * from vrul_tot where ] + lcConditieSters + [ an = ] + Transform(m.tnAn) + [ and luna = ] + Transform(m.tnLuna) + [ and cod = ] + Alltrim(Str(m.tnCod)) + [ order by id_rul]
lcCursor = [v_rul]
lnSucces = goExecutor.oExecute(lcSql,lcCursor)
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
IF Used('actactan')
Use In actactan
ENDIF
IF Used('tact')
Use In tact
ENDIF
RETURN .F.
ENDIF
IF lnSucces > 0
UPDATE v_rul SET valoare = ROUND(IIF(cant<>0, cant, cante)*pret,m.gnPC) WHERE EMPTY(NVL(valoare,0))
UPDATE v_rul SET valtva = ROUND(IIF(cant<>0, cant, cante)*pret * (IIF(!EMPTY(NVL(proc_tva,0)), proc_tva, 1)-1), m.gnPC) WHERE EMPTY(NVL(valtva,0))
UPDATE v_rul SET valoarev = ROUND(IIF(cant<>0, cant, cante)*pretv,m.gnPC) WHERE EMPTY(NVL(valoarev,0))
UPDATE v_rul SET valtvav = ROUND(IIF(cant<>0, cant, cante)*pretv * (IIF(!EMPTY(NVL(proc_tvav,0)), proc_tvav, 1)-1), m.gnPC) WHERE EMPTY(NVL(valtvav,0))
SELECT *, ;
valoare + valtva AS valoarectva, ;
valoarev + valtvav AS valoarevctva ;
FROM v_rul ;
INTO CURSOR RUL_TEMP READWRITE
SELECT * FROM RUL_TEMP INTO CURSOR trul READWRITE
USE IN v_rul
lcSql = [select * from vrul_obinv_tot where ] + lcConditieSters + [ an = ] + Transform(m.tnAn) + [ and luna = ] + Transform(m.tnLuna) + [ and cod = ] + Alltrim(Str(m.tnCod)) + [ order by id_rul_obinv]
lcCursor = [v_rul_obinv]
lnSucces = goExecutor.oExecute(lcSql,lcCursor)
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
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
RETURN .F.
ENDIF
ENDIF
IF lnSucces > 0
UPDATE v_rul_obinv SET valoare = ROUND(IIF(cant<>0, cant, cante)*pret,m.gnPC) WHERE EMPTY(NVL(valoare,0))
UPDATE v_rul_obinv SET valtva = ROUND(IIF(cant<>0, cant, cante)*pret * (IIF(!EMPTY(NVL(proc_tva,0)), proc_tva, 1)-1), m.gnPC) WHERE EMPTY(NVL(valtva,0))
UPDATE v_rul_obinv SET valoarev = ROUND(IIF(cant<>0, cant, cante)*pretv,m.gnPC) WHERE EMPTY(NVL(valoarev,0))
UPDATE v_rul_obinv SET valtvav = ROUND(IIF(cant<>0, cant, cante)*pretv * (IIF(!EMPTY(NVL(proc_tvav,0)), proc_tvav, 1)-1), m.gnPC) WHERE EMPTY(NVL(valtvav,0))
SELECT *, ;
valoare + valtva AS valoarectva, ;
valoarev + valtvav AS valoarevctva ;
FROM v_rul_obinv ;
INTO CURSOR RUL_TEMP_OBINV READWRITE
SELECT * FROM RUL_TEMP_OBINV INTO CURSOR trul_obinv READWRITE
USE IN v_rul_obinv
ENDIF
ENDIF
RETURN .T.
ENDFUNC && IncarcaCursoareModificareNota
*!* creeaza cursorul tvanz gol (structura VANZARI redusa) - fallback la eroare Oracle sau cod/data lipsa
FUNCTION CreeazaCursorTvanzGol
IF Used('tvanz')
Use In tvanz
ENDIF
CREATE CURSOR tvanz (id_vanzare I, tip I, discount N(12,2), total_fara_tva N(14,2), total_tva N(14,2), total_cu_tva N(14,2))
ENDFUNC && CreeazaCursorTvanzGol
*!* parametri: cod, nract, serie_act, dataact (din cursorul notei, ex. tact)
*!* incarca in tvanz randul din VANZARI al notei; cod singur nu e unic, de aceea filtrul cere tot setul
*!* tvanz ramane deschis READWRITE, cu 0 randuri daca nu s-a gasit sau la eroare Oracle
FUNCTION IncarcaVanzareNota
LPARAMETERS tnCod, tnNract, tcSerieAct, tdDataAct
LOCAL lcSql, lnSucces, lcSerieAct
IF Used('v_vanz')
Use In v_vanz
ENDIF
IF Empty(m.tnCod) OR Empty(m.tdDataAct)
CreeazaCursorTvanzGol()
RETURN .T.
ENDIF
lcSerieAct = Alltrim(Nvl(m.tcSerieAct,""))
lcSql = [select id_vanzare, tip, discount, total_fara_tva, total_tva, total_cu_tva from vanzari where cod = ] + Transform(m.tnCod) + ;
[ and Nvl(numar_act,-1) = ] + Transform(Nvl(m.tnNract,-1)) + ;
[ and Nvl(serie_act,'') = '] + m.lcSerieAct + [' and trunc(data_act) = to_date('] + Dtoc(m.tdDataAct,1) + [','YYYYMMDD') and sters = 0]
lnSucces = goExecutor.oExecute(lcSql,[v_vanz])
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
CreeazaCursorTvanzGol()
RETURN .T.
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
SELECT * FROM v_vanz INTO CURSOR tvanz READWRITE
USE IN v_vanz
RETURN .T.
ENDFUNC && IncarcaVanzareNota
*!* parametru: alias cu randurile notei (tact), cu campurile cod/nract/serie_act/dataact/sters
*!* incearca toate combinatiile distincte (cod, nract, serie_act, dataact) din alias (sters=0) pana
*!* gaseste un rand in VANZARI; lasa workarea si pozitia curenta neschimbate, .F. cu tvanz gol daca nu gaseste
FUNCTION IncarcaVanzareDinNota
LPARAMETERS tcAliasAct
LOCAL lnAreaOrigine, lnRecnoOrigine, llGasit
lnAreaOrigine = Select()
llGasit = .F.
IF Empty(m.tcAliasAct) OR !Used(m.tcAliasAct) OR Reccount(m.tcAliasAct) = 0
CreeazaCursorTvanzGol()
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN .F.
ENDIF
lnRecnoOrigine = Recno(m.tcAliasAct)
IF Used('tmp_triplete_pozvanz')
Use In tmp_triplete_pozvanz
ENDIF
*!* cod se ia tot din cursor, nu de pe randul curent - acesta poate fi la EOF dupa CalculeazaTotal
SELECT DISTINCT cod, nract, serie_act, dataact FROM (m.tcAliasAct) WHERE sters = 0 INTO CURSOR tmp_triplete_pozvanz READWRITE
SELECT tmp_triplete_pozvanz
SCAN
IncarcaVanzareNota(cod, nract, serie_act, dataact)
IF Reccount('tvanz') = 1
llGasit = .T.
EXIT
ENDIF
ENDSCAN
IF Used('tmp_triplete_pozvanz')
Use In tmp_triplete_pozvanz
ENDIF
IF !m.llGasit
CreeazaCursorTvanzGol()
ENDIF
IF Used(m.tcAliasAct)
IF m.lnRecnoOrigine > 0 AND m.lnRecnoOrigine <= Reccount(m.tcAliasAct)
Go (m.lnRecnoOrigine) In (m.tcAliasAct)
ELSE
*!* pozitia initiala era EOF (Recno() peste Reccount()) - se restaureaza tot la EOF
Go Bottom In (m.tcAliasAct)
Skip In (m.tcAliasAct)
ENDIF
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.llGasit
ENDFUNC && IncarcaVanzareDinNota
*!* creeaza cursorul tvd gol, cu structura view-ului VVANZARI_ARTICOLE
FUNCTION CreeazaCursorTvdGol
IF Used('tvd')
Use In tvd
ENDIF
CREATE CURSOR tvd (id_vanzare I, id_vanzare_det I, id_articol I, cantitate N(12,3), pret N(14,4), pret_cu_tva N(14,4), proc_tvav N(6,4), ;
discount_unitar N(14,4), id_gestiune I, cont C(20), id_valuta I, id_jtva_coloana I, serie C(20), explicatie C(60), ;
taxcode I, lot C(20), sters I, denumire C(100), codmat C(30), nume_gestiune C(60), nume_val C(20))
ENDFUNC && CreeazaCursorTvdGol
*!* parametru: id_vanzare
*!* incarca in tvd liniile active (sters=0) ale vanzarii, din VVANZARI_ARTICOLE
*!* tvd ramane deschis READWRITE, cu 0 randuri la eroare Oracle
FUNCTION IncarcaArticoleFactura
LPARAMETERS tnIdVanzare
LOCAL lcSql, lnSucces
IF Used('v_vd')
Use In v_vd
ENDIF
lcSql = [select * from vvanzari_articole where id_vanzare = ] + Transform(Nvl(m.tnIdVanzare,0)) + [ and sters = 0 order by id_vanzare_det]
lnSucces = goExecutor.oExecute(lcSql,[v_vd])
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
CreeazaCursorTvdGol()
RETURN .T.
ENDIF
IF Used('tvd')
Use In tvd
ENDIF
SELECT * FROM v_vd INTO CURSOR tvd READWRITE
USE IN v_vd
RETURN .T.
ENDFUNC && IncarcaArticoleFactura

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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