Using the Dexterity Three Trigger Technique Part 2

David Meego - Click for blog homepageThis is a reposting of an article I originally wrote on my Developing for Dynamics GP blog.

In the previous part of this post we discussed the theory of the Three Trigger Technique and the details of a problem that was solved using this technique.

This post will cover the actual solution used and has the scripts used so you can see the techniques in action.

The Solution

Before we can look at the scripts involved we need to create two global system variables.  We need to create the following Fields and then add them as Globals

  1. ‘MBS Trigger Active’ using Data Type of Boolean. This is the global system variable that tells us when we are inside the Script A mentioned previously.
  2. ‘MBS Table Reference’ using Data Type of Reference. This global system variable will be used to allow us to capture a table reference which can then be instantiated in our trigger script.

As we are running  Cross Dictionary code as well, it is best practice to create a Constant for the third party dictionary.

  • MBS_PROD_COPIER with a value of 2992, which is the Product ID for the Copier Series.

Now we are ready to create the code. Below is the Startup script to register the triggers and the three triggers of the technique.

The Code

The Startup script registers the triggers.  It checks that we are not test mode and then confirms that the Copier Series dictionary is installed before registering the triggers.  There are cross dictionary triggers before and after the function Copy_PO of form QPOP_Copy. Then there is the trigger against the function SetNonIVItemID of form POP_POLine in the core Dynamics.dic.

Global Procedure: Startup

{Turn off the warning for literal strings}
pragma(disable warning LiteralStringUsed);

{ Check not in Test mode and Copier Series installed }
if not empty(Launch_GetFileName()) then
  if Launch_GetProdPosition(MBS_PROD_COPIER) >= 1 then

    if Trigger_RegisterFunctionByName(MBS_PROD_COPIER, "Copy_PO of form QPOP_Copy", TRIGGER_BEFORE_ORIGINAL, function MBS_QPOP_Copy_Copy_PO_PRE) <> SY_NOERR then
      warning "Trigger Before Copy_PO() of form QPOP_Copy Registration Failed.";
    end if;

    if Trigger_RegisterFunctionByName(MBS_PROD_COPIER, "Copy_PO of form QPOP_Copy", TRIGGER_AFTER_ORIGINAL, script MBS_QPOP_Copy_Copy_PO_POST) <> SY_NOERR then
      warning "Trigger After Copy_PO() of form QPOP_Copy Registration Failed.";
    end if;

    if Trigger_RegisterFunction(function SetNonIVItemID of form POP_POLine, TRIGGER_AFTER_ORIGINAL, function MBS_POP_POLine_SetNonIVItemID_POST) <> SY_NOERR then
      warning "Trigger After SetNonIVItemID() of form POP_POLine Registration Failed.";
    end if;

  end if;
end if;

{Turn the literal string warning back on}
pragma(enable warning LiteralStringUsed);

The script below is the first trigger in the three trigger technique. It is used to set our global system variable as well as capture a reference to the table QPOP_POLine.

Global Function: MBS_QPOP_Copy_Copy_PO_PRE

function returns boolean stat;

inout anonymous table QPOP_PO; {Contains the header record as the current record}
inout anonymous table QPOP_POLine; {Contains all the line items}
inout table POP_PO;
inout POP_POState POP_POState; { PO object's state composite }
in integer copyMode;
in string vendorid;
in date orderDate;
in date required_date;
in date promise_date;
in date promise_shipdate;
in 'Customer Number' custNumber;
in 'Address Code' addressCode;
in 'Currency ID' currencyId;
in integer siteOption;
in 'Location Code' siteId;
in boolean copy_freight, copy_misc, copyLC, copySM;
inout 'PO Number' q_poid;

{ Inside Copy Code }
'MBS Trigger Active' of globals = true;

{ Capture Table Reference }
assign 'MBS Table Reference' of globals as reference to table QPOP_POLine;

The next script is the second trigger of the three trigger technique. It is used to clear our global system variable and clear the previously captured table reference.

Global Procedure: MBS_QPOP_Copy_Copy_PO_POST

{ Outside Copy Code }
'MBS Trigger Active' of globals = false;

{ Clear Table Reference }
clear 'MBS Table Reference' of globals;

The following script is third and final script of the three trigger technique and actually contains the working code of our “fix”.  The check of ‘MBS Trigger Active’ of globals is important as this prevents our trigger from running when the SetNonIVItemID of form POP_POLine function is called from anywhere else.

The rest of the code is specific to the fix and includes checks to ensure that the script we are triggering against has not returned a status code to signify it has failed, as well as checks to confirm the current document is not part of a Blanket PO.  Assuming that we passed the checks the code sets the Unit of Measure using the value from the ‘U Of M’ field in the table QPOP_POLine, which was re-instantiated from the captured reference.

Global Function: MBS_POP_POLine_SetNonIVItemID_POST

function returns integer Status;

inout POP_POLineState Me; { PO Line State Data }
inout table POP_POLine; { PO Line Table Buffer }
in 'Item Number' sItemID; { Item for the Line Item }
in integer nDecPlacesCurr; { Non IV Currency Decimal Places in DDL format }
in integer nDecPlacesQtys; { Non IV Quantity Decimal Places in DDL format }
in 'Free On Board' nFreeOnBoard; { Free on Board value - for nonIV should come from vendor card }
optional in AddressData PurchaseAddr; { Purchase Address from the PO }
optional in AddressData ShipToAddr; { Ship To Address from the PO }
optional in BlanketPOParentLineValues ParentItem; {parent item info used for child line defaults}

local 'U Of M' l_UOfM;

{ If not inside the Copy code, do not continue }
if not 'MBS Trigger Active' of globals then
  abort script;
end if;

{ Trigger function failed, do not continue }
if Status <> OKAY then
  abort script;
end if;

{ Check for Blanket POs }
if IsNonIV(Me, table POP_POLine) of form POP_POLine then
  if IsParentLine(Me) of form POP_POLine then
    if (not Me:ReconcileInProgress) and (ExistsAsChildForPO('PO Number' of table POP_POLine, true {blanket po}) of form POP_POLine) then
      {can't change blanket parent UofM if child lines exist - UNLESS }
      abort script;
    end if;
  elseif IsChildLine(Me) of form POP_POLine and ('U Of M' of table POP_POLine <> "") then
    {can't change UofM for blanket child line from default value}
    abort script;
  end if;
end if;

{ l_UOfM = 'U Of M' of table QPOP_POLine; }
l_UOfM = 'U Of M' of table('MBS Table Reference' of globals);

{Set the Unit of Measure}
Status = SetUofMID(Me, table POP_POLine, l_UOfM) of form POP_POLine;

Don’t worry about the specifics of the actual fix here, but make sure you understand the following important concepts covered:

  • Three Trigger Technique
  • Cross Dictionary Triggers
  • Capturing and Using References

Please let me know if you find this information useful.


23-Nov-2009: See follow-up article Accessing a Table Buffer when it is not passed as a parameter.

This article was originally posted on the Developing for Dynamics GP Blog and has been reposted on

5 thoughts on “Using the Dexterity Three Trigger Technique Part 2

Please post feedback or comments

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.