#GPPT Best Practices for Developing with GP Power Tools

David Meego - Click for blog homepageWe are seeing more and more sites realizing the potential of using GP Power ToolsDeveloper Tools module as their platform for custom development for customizations, tweaking and fixing behavior of code within Microsoft Dynamics GP.

Many customers are using GP Power Tools (GPPT) to replace their unsupported Visual Basic for Applications (VBA) customizations, which is great as GPPT is the best solution for replacing VBA, Dexterity and Visual Studio Tools addins. It offers many benefits over other development tools.

A while ago, I wrote an article to help developers and consultants “Move on” from the VBA Mindset. This is helping change the way people think about developing code against Microsoft Dynamics GP, but I am still seeing code written using GP Power Tools which is not following “Best Practices”.

In this article, I will gather all the “Best Practices” together so developers have this as a reference, and together we can improve the quality and stability of customization projects.

This is a living document that I will continue to update over time. Please revisit to see what’s new.

GP Power Tools Build

  • Always stay on the most recent released build for your version of Microsoft Dynamics GP.
  • The latest build information is available on the GP Power Tools Portal: https://winthropdc.com/GPPT
  • Developers will also want the Administrator Tools module registered to be able to use the Resource Information, Resource Finder and Dynamic Product Selection windows.

Visual Studio Integration Toolkit Build

  • Visual Studio Integration Toolkit adds Application Level Menus and Custom Form functionality.
  • Always stay on the most recent released build for your version of Microsoft Dynamics GP.
  • The latest build information is available on the Visual Studio Integration Toolkit Portal: https://winthropdc.com/VSIT

Development Languages

  • GP Power Tools uses Dexterity Triggers and sanScript (Dexterity’s scripting language) as its base level development environment.
  • Understanding Dexterity and sanScript will dramatically improve your development experience and efficiency. Knowing the basic commands for looping and conditional statements as well as important concepts such as table buffers (unsaved table records in memory), table ranges and using temp control numbers are important. Look at this How to get started with Dexterity article and the Dexterity Links page.
  • It is recommended to complete the Dexterity Fundamentals training course either as self-study or even better attend a five-day bootcamp class (usually run by David Musgrave in the week before the Community Summit conference). The materials can be downloaded from the Training Links page.
  • While it is recommended to use Dexterity table commands to read and write single records to and from tables defined in the product dictionaries, GP Power Tools can also use SQL Server Transact-SQL language for reading and writing data to and from the system and company databases. Using SQL can be much more efficient when working with data sets or multiple tables joined together. Knowledge of Transact-SQL and creating SQL query statements will be invaluable.
  • While sanScript does allow you to call .Net libraries with its .Net Interop functionality, you can also call .Net scripts using either Visual C# or Visual Basic .Net to perform functions not normally available or supported by Dexterity. Knowledge of C# or VB.Net and the .Net Framework 4.X can also be very helpful.

Development Approach

Projects

  • Use Project Setup as your starting point for your customizations.
  • Make the project you are currently working on the “Current Project”. This will make it open the window straight to that project.
  • Add all the components of each customization into the project.
  • Populate the Open Form field with the details of the form you can test your project from. Clicking the hyperlink can start the triggers and open the window for testing.
  • Opening other forms or reports can be achieved by selecting the resource in the project that points to it and clicking the Open Form or Report button.
  • Add any modified forms or reports to the project using Add >> Customization Maintenance.
  • Export projects after each set of changes to keep a backup.
  • Make sure the option to Export linked custom resources package is checked.
  • Use the Update Triggers/Scripts button to turn on Minimize Logs. This will stop GPPT reporting every thing it is doing and only log when there is a problem.
  • Turn Minimize Logs off if you need to see more of what is happening when trying to debug an issue. Then review the GPPTools*.log file for the user and company. Remember to turn Minimize Logs on again once the issue is resolved.
  • Use the new Options Menu >> Find in Scripts tool added to Build 30 for searching scripts.

Resource Information and Resource Finder

  • Open the Resource Information window (from Administrator Tools module) and set it to Forms, Window and Fields mode with the “Show currently selected Window and Field information” checked.
  • Now when moving around the application you will always know exactly which field on which window on which form you are on.
  • Use the Copy button to place the fully qualified name of a field into the clipboard.
  • Use the Copy button to populate the Resource tab of the Trigger Setup window.
  • Use the Resource Finder window to identify the physical table(s) where data is located.

Note: It is recommended to close the Resource Information and Resource Finder windows when capturing logs to avoid additional scripts being logged as you move around the application.

Logging

  • Use GPPT to Log scripts to identify the best places to register triggers. For example: Save Button is not good location to capture a save event as there are 7 ways a record can be saved and the Save Button is only one of those. But all of them will call a single Save Record script or equivalent. This can be seen in the Script Log.
  • When reading the Script Log, scripts are indented to show the call hierarchy.
  • In the Script Log, if the logged script says “on form XYZ”, it is a focus event script. The suffix _PRE, _CHG, _POST will identify the focus event.
  • In the Script Log, if the logged script has brackets/parentheses “()” it is a function, otherwise it is a procedure.
  • In the Script Log, if the logged script says “of form XYZ”, it is a form level procedure or function.
  • In the Script Log, if the line has B: at the beginning of the line, that script is running as a background process.
  • In the Script Log, lines starting with MBS_ are usually internal code from GP Power Tools itself. This shows its heritage as the MBS Support Debugging Tool before the MBS was dropped from the name. These lines and their called lines can be skipped past.
  • When windows are opened there will be lots of GPPT scripts for handling security and window positioning recorded in the logs. Just skip past them.

Triggers

  • Create triggers in the best locations as identified by logging scripts. This might not be the final location as testing is needed to confirm the trigger fires when expected.
  • Additional Triggers might be required to capture other related events. For example: You might need a trigger on Display Existing Record and on Item Number to pick up when an Item is entered or changed.
  • Always trigger against the original non-modified version of a resource when it is available. You only ever need to trigger on alternate or modified forms when the resource you want to reference is not part of the original form.
  • You can have a trigger script that runs on a different dictionary or against a modified version even when the trigger itself is on the original form. This allows referencing of a field not on the original form.
  • Do not have the trigger script run on the modified and/or alternate window unless it is required. If you are only referencing fields on the original form, execute the script against that original form.
  • If executing code against a modified or alternate version of a window, enable the Check Form Security option so that the script will ONLY execute when that version of the window is used. You can also use a Helper Function to make the check if you want some code to run regardless.
  • Triggers will run in the order they are registered. So, if you need to control the order of triggers firing, rename the Trigger IDs accordingly. Triggers are registered in alphabetical order based on the Trigger ID.
  • Set OUT_Condition to true if you want to use actions on the Action Tab. This can be used to display warning dialogs, prevent original scripts from running (reject script) or prevent data showing in scrolling windows (reject record).
  • When displaying Dialogs, you can use Messages to allow the wording on the dialog to be reused in multiple locations or to be translated for multi-lingual systems.
  • Preventing original scripts from running will ONLY work when the trigger is a focus event trigger running BEFORE the original script. It does not work after the original script or for Procedures and Functions.
  • On the Options Tab you can start triggers temporarily disabled and have them disable themselves after execution. This feature can be combined with other triggers and Helper Functions to ensure a trigger only executes when needed. It can be used with table read triggers to capture a reference to a table and store it as a Memory Parameter using a Helper Function.
  • Do not use Dexterity system dialog commands (ask, warning, error, debug) in a trigger script when the trigger is for a modal dialog focus event. This will cause an “inception” loop that will crash GP. You can use the dialog on the action tab with OUT_Condition set to true.
  • When triggering on the Fill event of a scrolling window, use the Focus Event selection rather than Focus Event with Table. It will still need the linked table defined but will copy the table fields to the window before running the script instead of attempting to capture and pass a table reference. It is easier to use and more reliable as capturing a reference can often fail until after the scrolling window has been populated the first time.
  • If a trigger fails to fire as desired, you might be able to achieve the desired effect by triggering off another script that is called during the process. Logging scripts can identify potential locations for alternative triggers. Note: Please report to support if a trigger fails to work so the cause can be researched.

Scripts

  • Dexterity sanScript is used for Trigger scripts and Runtime Execute scripts. It is case sensitive, and each line must be terminated with a semi colon (;).
  • Comments can be started with a left brace ({) and closed with a right brace (}) character. Comments can be nested.
  • Comments can also be started with /* and ended with */. Comments to the end of the line can be started with //.
  • For Dexterity Script editors in GPPT, there is a help icon in the bottom right corner which opens the Dexterity Help File. Click on the Index tab and search for commands you need.
  • It is highly recommended to attend Dexterity Training (usually run in the week before Community Summit Conferences).
  • Use GPPT Helper Functions to achieve functionality not possible with standard commands.
  • Use the Insert Button on the Script editor to insert standard constructs into your code, such as loops, conditional statements and table commands. For SQL scripts it can insert standard constructs for the most common Transact-SQL commands.
  • Use the Names button to insert fully qualified resource names into your code.
  • Try changing the Scripts Menu >> Names Button uses Clipboard option to place the result from the Resource Explorer windows into the clipboard rather than inserting into the code. You can then paste it multiple times exactly where you need it.
  • Use the Copy Button on the Resource Information window to copy resource names.
  • Always use if isopen(form XYZ) then to check a form is open before attempting to reference resources on that form. This will avoid illegal address errors.
  • Error messages when compiling against modified dictionary can give deceptive messages.  If you can comment out references to the Modified resources, you might be able to turn off the modified checkbox and get a better error message.
  • Commenting out sections of code can help narrow down where a compilation error is occurring.
  • Press Ctrl-S or select Script Menu >> Save and Continue to check syntax and save if no errors without clearing the window.
  • Use warning statements when debugging scripts, you can confirm that a particular point in the script and also display variables to see if they have the expected values.  Use the str() command to convert non-string datatypes to string. Wrap the entire string in text() if you want to display a text style warning statement from which you can use Ctrl-C to copy the displayed information. For example: warning text(Variable);
  • Use constants when possible, especially system constants. For example: Use ASKBUTTON1, ASKBUTTON2 and ASKBUTTON3 with the ask() function and use OKAY, MISSING, EOF, DUPLICATE, RECORDCHANGED, LOCKED with the err() function.
  • Use constants for minimum and maximum numeric values MINTINY, MAXTINY, MININT, MAXINT MINLONG, and MAXLONG.

Scripts – Helper Functions

  • Don’t use Helper Functions when the standard Dexterity sanScript commands can do what you want faster and simpler.
  • Only use Helper Functions when they are required. When you need an action that cannot be performed with native Dexterity code.
  • Using Helper Functions for setting and getting fields is only needed if you need to swap to a different dictionary or between original and modified context. Otherwise just use Dexterity commands such as: Field of window Window of form Form = Value;
  • Using Helper Functions for reading or writing table values is only required if the table is in a different dictionary.

Scripts – Table Access

  • Use Dexterity table commands (see Insert Button on script editors) to read or write single records for tables that have table definitions in a Dexterity dictionary. This is the fastest method.
  • Using Dexterity table commands is much faster than use SQL commands, especially for custom RW (Report Writer) functions. Using SQL commands or scripts in RW functions can slow down report printing.
  • Use Helper Functions to read and write to tables in a different dictionary, if reading and writing single fields.
  • If needing to read or write multiple fields in a table in a different dictionary, use a Runtime Execute script to change dictionary context. Then load and execute it using Helper Functions.
  • If working with SQL only tables (no Dexterity Table Definition) or need to leverage multiple joins or complex set based logic, then use SQL to access the tables.
  • To run SQL statements in GPPT it is recommended to use SQL Execute scripts rather than embedding the SQL code into a Dexterity or .Net script.

Runtime Execute Scripts

  • Use Runtime Execute Scripts when you need to call the same code from multiple triggers. This is the equivalent of call a sub procedure.
  • Use Runtime Execute Scripts when you need to execute code in a different dictionary or swap between original and modified context.
  • Use the Memory Parameter Helper Functions to pass values between the parent/calling and child/called scripts.
  • You can also use Parameter Lists to pass values between multiple scripts. Parameter Lists values are automatically inserted into the executed code where the placeholders are.
  • Parameter Lists can be loaded, and their values populated programmatically using Helper Functions.
  • Having Triggers call Runtime Execute scripts allows the Runtime Execute scripts to be edited without having to stop and restart triggers when testing.
  • When using SQL Queries from Runtime Execute Scripts, use the SQL Parse Data Helper Functions to retrieve the data returned. The data is stored as a text field, and it is best to retrieve the data in order of columns and rows. If you jump around with the order of the columns, the system will need to start counting tab characters from the beginning of the line each time. If you jump around with the order of the roes the system will need to start counting return characters from the beginning of the data set each time. If you retrieve the data in sequence, the system can use the last position and work forward to the next position and is significantly faster.

SQL Execute Scripts

  • It is better to write SQL Queries using SQL Execute Scripts rather than embedding the SQL commands in a Dexterity or .Net Script.
  • This allows the script to be tested independently and then loaded and executed when needed.
  • Using a Parameter List to populate values in the SQL query helps with testing but also ensures that there is no risk of SQL Injection and that the beginning and ending values or ranges are handled correctly based on the SQL Sort order in use.
  • When loading and executing SQL Scripts, Parameter Lists can be loaded, and their values populated programmatically using Helper Functions.
  • Make sure that SQL Queries do not return NULL values. use isnull() or coalesce() to ensure no NULL values are returned. This is because Dexterity understands empty values but not NULL values.

.Net Execute Scripts

  • While Parameter Lists work with .Net Execute it is recommended to use the Memory Parameter Helper Functions instead.
  • This is because if the script is changed in any way, it will be recompiled into a new memory assembly before execution. This slows execution and increases memory usage as memory assemblies are not released from memory until the application is closed.
  • It is possible to call .Net Libraries from Dexterity scripts (Triggers and Runtime Execute Scripts). However, for full compatibility you can use C# or VB.Net with .Net Execute Scripts.
  • Only use .Net Execute scripts when Dexterity or GPPT Helper Functions cannot achieve what is needed.

Parameter Lists

  • You can also use Parameter Lists to pass values between multiple scripts. Parameter Lists values are automatically inserted into the executed code where the placeholders are.
  • Parameter Lists can be loaded, and their values populated programmatically using Helper Functions.
  • Parameter Lists can be linked with custom lookups based on a SQL Query or wrapping an existing Lookup form.
  • These custom lookups can also be called outside of Parameter Lists if your custom code needs a lookup.
  • You can use Parameter Lists to store additional values that are not displayed to users even if the Parameter List dialog is displayed.
  • Parameter Lists can be used across multiple scripts and multiple languages and the values will be automatically inserted where the placeholders are in the scripts.

Messages

  • Messages can be used to store dialog text so it can be reused in multiple locations.
  • Messages also support multi-lingual installations and can automatically translate dialogs.
  • You can use Helper Functions to retrieve the Message text to display in your code.
  • You can use %1, %2, %3 …. placeholders in your messages and the substitute Dexterity command to replace the placeholders.

Avoid Using Modifier

  • If you don’t need to create a modified window, then don’t add the complexity of needing to maintain a modified window and its related security settings.
  • You can perform many customizations without using modifier and just using code instead.
  • Resizing windows to make space can use Window_SetBaseSize(). Resizing fields can use resize field command.
  • Moving Fields can use move field command. If the prompt is linked to the Field, it will be moved with the field.
  • You can hide and show, lock and unlock, and disable and enable fields with code.
  • Changing Field Properties can use the entire series of Field_Set*() commands. Checking properties can use the Field_Get*() commands. For example: use Field_SetBooleanProperty() to set the Required property with a WIN PRE trigger.

Modifier Limitations

  • Modifier does not allow some field properties to be set, such as Hyperspace (needed for Lookup buttons and Clear or Delete buttons), instead you can use Field_SetBooleanProperty() to set the property with a WIN PRE trigger.
  • Some field types cannot be added using Modifier, such as ListViews and TreeViews. This is because VBA and VSTools did not support working with these field types. However, GP Power Tools can work with these field types. To work around, create the local field as ListBox instead, then export the package file and edit with Notepad and re-import it. Once the control type has been changed you can drag the desired local field onto the window.

Snippets

  • Snippets are new to Build 30 of GPPT. Use them to store reusable code that you can insert into your scripts as desired.
  • Snippets can belong to a project or be left without a project to make them generic.

Dynamic Product Selection

  • Use Dynamic Product Selection (from Administrator Tools module) to temporarily set security to open a modified version of a window while developing.
  • It can be limited to specific users and set to automatically open the modified window (with only one choice) or ask when opening the window (with more than one choice).

Application Level Menus

  • Use GP Power Tools Triggers with Visual Studio Integration Toolkit to add Application Level menus.

Dialogs

  • To display system dialogs, you can use warningerror, and ask() commands. All three of these commands can be passed a text variable instead for a string to display more information. You can use the text() function to convert a string to a text datatype.
  • The ask() command can have one, two or three buttons displayed on it.
  • There is also a getstring() command that can be used get a string value entered.
  • There is also a Dialog Custom Form available in Visual Studio Integration Toolkit (see below).

Custom Forms

  • New to Build 30 of GP Power Tools combined with Build 18 of Visual Studio Integration Toolkit is the ability to create new Dexterity base custom forms. These include blank forms, setup forms, maintenance forms (with and without a scrolling window) and dialog forms.
  • Maintenance forms come with a matching inquiry form and report.
  • Use Runtime Execute special script purposes to create EventRegisterForm and EventHandlerForm scripts.
  • Use Triggers for events additional events not covered by the EventHandlerForm script.

Troubleshooting

  • Whenever GP Power Tools code does not behave as expected, check the GPPTools_*.log file for the user and company. If there was an issue running the code, it will be logged to this file.
  • You can use warning statements with numbers or actual data to confirm which section of code has been executed. Please remember to remove these debugging warnings before deploying code.
  • Be careful adding warning statements in loops or scrolling windows as it will require multiple clicks to dismiss them. Do not use warnings for debugging in Modal Dialog triggers, using logging (below) instead.
  • You can use the MBS_Auto_Log Helper Function to write debugging information into the GPPTools_*.log file instead of using warnings.
  • If you are having problems compiling code, especially with scripts running on a Modified dictionary which do not always report the correct error message, use the braces { } to comment out code until it compiles. Then slowly add code back until you identify where the error is.
  • Note the use of the default form to and default window to commands can simplify coding by not requiring fields to be fully qualified. However, use of any tables that are not already assigned to the original form are likely to fail to work correctly. No error messages will be shown, the code will just not work as expected. This is why these commands were removed from the script templates.

Samples

More Information

Please also see the following articles:

Hope you find this information helpful.

David

06-Dec-2023: Added sections of Avoid Using Modifier, Modifier Limitations and System Dialogs.
18-Jan-2024: Added information on Troubleshooting and Scrolling windows.
30-Jan-2024: Added information on using SQL Parse Data Helper Functions.
28-Feb-2024: Added sections on Development Languages and Development Approach.

This article was originally posted on http://www.winthropdc.com/blog.

One thought on “#GPPT Best Practices for Developing with GP Power Tools

Please post feedback or comments

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