Wednesday, December 9, 2009

VST - Working with the Action Button in SOP

Last year in May, I worked on a number of VBA SOP customization upgrades from v9 to v10. As a result of the experiences I blogged about the one issue that I ran into in my article Upgrading SOP Entry VBA Customizations from Dynamics GP 9.0 to Dynamics GP 10.0. The main issue was the sudden switch from the individual push button controls to the new Action Button, which is a Dexterity Button Drop List control. Of course, this made the SOP Entry window interface a lot cleaner for v10, but brought its own challenges for developers.

SOP Entry window, GP 9.0


SOP Entry window, GP 10.0


While it is relatively feasible to work with the Action Button from VST, a few things need to be taken into account:

1) Unlike push button controls, button drop list controls do not expose Click Before Original and Click After Original events in Visual Studio. This is because the button drop list control has the menu items selection action to deal with.

2) As a result of the above, the button drop list change event will execute prior to the change event registered in VST, hence, there is a slight chance that certain actions like a clear window or restart form may prevent you from retrieving the option selected by the user in a timely manner.

3) Unfortunately, the only way to access the numeric value associated with an item occupying a specified position in the button drop list is by using passthrough sanScript with the Dynamics Continuum Integration Library. The Value property in VST will return the actual ordinal value of the item selected.

The following code illustrates one method, with a pitfall -- the Transfer action clears out the window before a value can be gathered for the user selection. If you have found a better method to access the Action Button from VST, I would like to hear about your solution.

GPAddIn.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Microsoft.Dexterity.Bridge;
using DynamicsAssembly = Microsoft.Dexterity.Applications;
using Microsoft.Dexterity.Applications.DynamicsDictionary;

namespace SOPActionButton
{
public class GPAddIn : IDexterityAddIn
{
// IDexterityAddIn interface

// setup all Action Button constants
const int ACTION_POST = 1;
const int ACTION_TRANSFER = 2;
const int ACTION_PURCHASE = 3;
const int ACTION_CONFIRMPICK = 4;
const int ACTION_CONFIRMPACK = 5;
const int ACTION_CONFIRMSHIP = 6;
const int ACTION_COPY = 7;
const int ACTION_DELETE = 8;
const int ACTION_VOID = 9;

SopEntryForm sopEntry;

public void Initialize()
{
sopEntry = DynamicsAssembly.Dynamics.Forms.SopEntry;
sopEntry.SopEntry.ActionButton.Change += new EventHandler(ActionButton_Change);

}

void ActionButton_Change(object sender, EventArgs e)
{
// define variables to use with Continuum Library
int compilerErrorCode;
string compilerErrorMsg;
string compilerCmd;

if ((sopEntry.SopEntry.SopNumber != "") && (sopEntry.SopEntry.ActionButton.Value != 0))
{

// create an instance of the Dynamics Application and clear the DDL
Dynamics.Application compilerApp = (Dynamics.Application)Activator.CreateInstance(Type.GetTypeFromProgID("Dynamics.Application"));

// fill the DDL with company name
compilerCmd = "'(L) LATAM LineItemSequence_Return' of window SOP_Entry of form SOP_Entry = itemdata('Action Button' of window SOP_Entry of form SOP_Entry, 'Action Button' of window SOP_Entry of form SOP_Entry);";

compilerApp.CurrentProductID = 0; //Dynamics
compilerErrorCode = compilerApp.ExecuteSanscript(compilerCmd, out compilerErrorMsg);

if (compilerErrorCode != 0)
{
MessageBox.Show(compilerErrorMsg);
}

MessageBox.Show(sopEntry.SopEntry.LocalLatamLineItemSequenceReturn.Value.ToString());
}
}
}
}

The above code requires adding a reference to the Dynamics Continuum Integration Library COM library. As a result, it is necessary to alias out the Microsoft.Dexterity.Applications namespace.

Until next post!

MG.-
Mariano Gomez, MVP
Maximum Global Business, LLC
http://www.maximumglobalbusiness.com/

5 comments:

Anonymous said...

This actually failed spectacularly for my solution.

I was trying to trap the SaveRecord only when Actions > Purchase was not chosen. SaveRecord always ran BEFORE ActionButton.Change - so I couldn't verify the action during SaveRecord.

A better event to run this code in is either ValidateBeforeOriginal. This allows you to get the Action chosen, before the fields have been cleared.

Anonymous said...

Hi,

First of all, I would like to thank you for taking the time and writing this and other blog posts about Dynamics GP, it makes it much easier for us, beginners, to understand it.

Second, regarding this post, I was supposed to put an event which would fire when a user clicks on Actions > Post. However, using the ActionButton_Change left me with a clear form. When I put a MessageBox, it actually always returned a "0" on Post click, when an Invoice was not saved, and proper numbers when other Actions options were clicked (in example, Transfer returned "2", etc), so it was not usable.

I managed to resolve this using the ActionButton_ValidateBeforeOriginal event, as someone in the comment above posted. It returned the Post number - "1", as needed, and without even using the Continuum Library what so ever. Since I am a .NET developer, I feel more comfortable using the VS Tools, so this approach helped me a lot.

Thanks again for the posts!

Anonymous said...

wish this was done in vb.net...but this is great help anyway.

Anonymous said...

Hi Mariano,
"The following code illustrates one method, with a pitfall -- the Transfer action clears out the window before a value can be gathered for the user selection"

So is there any other way to validate records before the Order is transferred...the method in the comments fails to return a result on tranfer.

Mariano Gomez said...

You could probably retrieve the records directly from the database.

MG.-