Button script to change invoice date for a whole batch... error 84

SOLVED

A customer has asked for an easy way to change the invoice date for an entire batch (without using VI).  What I am trying is a button script run from SO Invoice Entry, opening a new object, grabbing the ModuleDate, scrolling through the batch and setting the invoice date one at a time.  That part works, but at the end of the script I get an error 84 (crashing the UI).

Is what I am attempting feasible?  Can I not open a new instance of SO_Invoice_Bus from within an SO Invoice Entry button script?

oInvoiceEntry = oSession.GetObject("SO_Invoice_Bus")
Set oInvoiceEntry = oSession.AsObject(oInvoiceEntry)
oInvoiceEntry.SetIndex("KBATCH")
oInvoiceEntry.SetBrowseFilter(sBatchNo) 
retVal = oInvoiceEntry.MoveFirst()
do until oInvoiceEntry.EoF 
	sLoopInvoiceNo = ""
	retVal = oInvoiceEntry.GetValue("InvoiceNo$", sLoopInvoiceNo)
	if sLoopInvoiceNo <> sInvoiceNo then ' skip over the currently open invoice.
		retVal = oInvoiceEntry.SetValue("InvoiceDate$", sModuleDate)
		retVal = oInvoiceEntry.Write()
	end if
	retVal = oInvoiceEntry.MoveNext()
loop ' oInvoiceEntry
oInvoiceEntry.SetIndex("KPRIMARY")
Set oInvoiceEntry = nothing

Trying to invoke Accept before the loop doesn't change anything.

retVal = oScript.InvokeButton("BT_Accept") 
retVal = oUIObj.HandleScriptUI() 

I also tried with and without forcing close the current invoice (before the loop), with the same error.  (If I do this, the SetValue on the current invoice works, and all the other invoices in the batch are updated, but it still crashes the screen... which is not something I could enable on a customer system).

retVal = oBusObj.Write()
retVal = oBusObj.Clear()

  • +1
    verified answer

    Is there a reason you are getting a new object handle instead of using the current oBusObj?

    You are only going through the currently select batch, correct?

    I think i have used the invokebutton approach to do what you want while checking the return value first for success before doing anything else, since this is a button script you could also just call oUIObj.BT_Accept() and check its return value before proceeding with the rest of the script. You could also check the oBusObj.RecordChanged property and if 0, just call oUIObj.BT_Cancel() then proceed to loop.

  • +1
    verified answer

    The way you have it is more conducive to when you're not sitting in the Invc Data Entry UI. In this case SelectBatch will work out better than SetIndex and SetBrowseFilter to restrict invoices to the current batch. Also for scripting better to SetBrowseIndex instead of SetIndex (which might be causing your Error 84). Something like this:

    'Get BatchNo from oUIObj
    If oSession.UI <> 0 Then Set oUI = oSession.AsObject(oSession.UI)
    
    If oBusObj.EditState Then
    	sMsg =  "Clear the invoice on the screen first." & vbCrLf & "Then click this button again."
    	retMsg = oUI.MessageBox("", sMsg)
    Else
    
    	'DropObject in case
    	If IsObject(oInvoiceEntry) = True Then oSession.DropObject "SO_Invoice_bus"
    	oInvoiceEntry = oSession.GetObject("SO_Invoice_bus")
    	Set oInvoiceEntry = oSession.AsObject(oInvoiceEntry)
    
    	retVal = oUIObj.GetControlProperty("ML_BATCHNO", "Value", sBatchNo)
    	retVal = oInvoiceEntry.SelectBatch(sBatchNo)
    	r = oUI.ProgressBar("init","Change Invoice Date","Change Invoice Datefor each invoice in batch.",0,"force")
    
    	retVal = oInvoiceEntry.MoveFirst()
    	sInvoiceNo = oInvoiceEntry.GetKey() 'This is BatchNo + InvoiceNo
    	
    	sMsg = "In Batch " & sBatchNo & " sInvoiceNo after MoveFirst = " & sInvoiceNo
    	r=oScript.DebugPrint(sMsg)
    
    	Do Until cBool(oInvoiceEntry.EOF)
    
    		retVal = oInvoiceEntry.GetValue("InvoiceNo$", sInvoiceNo) 
    		retVal = oInvoiceEntry.GetValue("InvoiceType$", sInvoiceType)
    		
    		If sInvoiceType = "IN" Then
    			retVal = oInvoiceEntry.SetKey(sInvoiceNo)
    			retVal = oInvoiceEntry.SetValue("InvoiceDate$",oSession.ModuleDate)
    			rWrite = oInvoiceEntry.Write()
    			If rWrite = 0 Then
    				r=oScript.DebugPrint("Write on " & sInvoiceNo & " failed with: " & oInvoiceEntry.LastErrorMsg)
    			Else
    				r=oScript.DebugPrint("Write on " & sInvoiceNo & " succeeded.")
    				nCounter = nCounter + 1
    				r = oUI.ProgressBar("update")
    			End If
    		End If
    	End If
    
    		retMove = oInvoiceEntry.MoveNext()
    
    	Loop
    
    	'Clean-Up: Clear, DropObject, Close ProgressBar, Completion MessageBox
    	retClear = oInvoiceEntry.Clear()
    	
    	If IsObject(oInvoiceEntry) = True Then
    		oSession.DropObject "SO_Invoice_bus" 'Allows the BT_BROWSE buttons to work
    	End If
    	
    	r = oUI.ProgressBar("close")
    

  • 0 in reply to David Speck

    The reason is that I couldn't get the invoice to close properly... oUIObj.InvokeButton("BT_Accept") gave me an error (Object does not support this property...), but oUIObj.BT_Accept does work... (although I still have to check the return value).

    Thanks!

  • 0 in reply to Alnoor_C

    Thank you Alnoor!  It seems like the SetBrowseFilter was causing the error 84.  Changing to retVal = oInvoiceEntry.SelectBatch(sBatchNo) and the error is gone.

  • 0 in reply to Alnoor_C

    Oh, and about the oBusObj.EditState check at the top of your code... how do you allow a custom office button to work when there is no open transaction?  I've never been able to do that.  (Buttons are greyed out with no open record).

  • 0 in reply to Kevin M

    Three approaches.

    1. A ui post load script on dMain that removes your custom button from the MAIN, BUTTON, and one other nomads group that i can't remember off the top of my head. I think i have examples of this floating around on here somewhere. Also, certain panels may also need this same code on the table's post read event so it makes sure the button is removed from the groups the first time a record is read so when the record is cleared, the custom button remains enabled. I can't remember which task and panel i encountered that required this because of the timing of the ui post load firing and the panel being processed by nomads.
    2. DFDM the custom library and find the record for your button and modify the field that contains the nomads groups and empty it out iirc.
    3. rely on the user to be on a record and check the oBusObj.RecordChanged property, if 1, then display a custom message box with yes/no buttons asking if they want to save changes before processing your script or change the message as you see fit. Depending on their response, call oUIObj.BT_Accept to save the record, oUIObj.BT_Cancel to clear the record then process your script however you see fit based on their response.

    There is another potential approach that uses the DDE (i think it is this one iirc) option instead of script but you have to point it to a text file containing ProvideX code instead of VBScript. For some reason, buttons set up with the DDE option are not disabled when a record is not selected.

  • +1 in reply to Kevin M
    verified answer

    Kevin - Without benefit of Nomads, this is how you make BT_LINK_X buttons on DMAIN behave the same as other controls in the KEYFIELD group, like the Next Invoice No. 

  • 0 in reply to Alnoor_C

    Excellent... thanks again!

  • 0 in reply to David Speck

    Thanks David... #2 (with the "how to" from Alnoor) seems to work for me.  :-)

  • 0 in reply to Kevin M

    i prefer #1 if possible because it will survive upgrades and future modifications to the library, any time you touch the button in custom office, you'll have to repeat the DFDM step but it is a nice immediate fix.