MVP Mark Polino on Transaction Posting vs Batch Posting

June 30, 2009

If you thought Post to GL and Post through GL were confusing, wait until you have to explain batch posting versus transaction posting and the implications of either to your client. Fortunately, MVP Mark Polino breaks it down in easy terms in his new Weekly Dynamic article on the subject.

Remember that every implementation is different and that the options you select during configuration will impact the way the business conducts its operations, so go on and read Mark’s article to get some insight on how these options work.

Until next post!

Mariano Gomez, MIS, MCP, MVP
Maximum Global Business, LLC


Google it with Bing!

June 27, 2009

You know that I don’t often deviate from everything Dynamics GP, but this is got to be the most hilarious stuff I have seen in the last … who knows how long.

Sorry Microsofties, but even you have to admit this is too dagarn funny.

Until next post!

Mariano Gomez, MVP
Maximum Global Business, LLC

You receive error "Someone is clearing company files and you cannot get into this company" when logging into a company – The Clear Data process

June 25, 2009

This one was actually a very interesting case reported on the Dynamics GP newsgroup, so I figured I needed to get to the bottom of it. The very frustrated user reported seing the following error trying to get into a company in Dynamics GP.

In an attempt to replicate this issue, the most logical place to start was to login into Dynamics GP as my ‘sa’ user and open the Clear Data window and try to do something there.

Since Clear Data is a distructive process, the Dynamics GP Development team figured they had to find a way to prevent users from accessing the system while this process was being executed by, perhaps, the system administrator. This is how the Clear Data Comp reserved user ID was born. When the window is first accessed by the system administrator, Dynamics GP checks for any users currently available in the system. If there are no users, it will display the Clear Data window and create a record for the Clear Data Comp user ID in the system databases User Activity table (DYNAMICS.dbo.ACTIVITY). The following query shows the content of the ACTIVITY table.

If a second user attempts to access the same company, the code will check for the Clear Data Comp user activity record before allowing the user to access the company. If the record is found, the system will issue the error message originally described.

Since this user ID is internal to the application, there are no locks placed or sessions logged in the tempdb..DEX_LOCK or tempdb..DEX_SESSION tables respectively. If you noted the query results, the Clear Data Comp user ID’s SQL session ID is 0.

What happens when Dynamics GP crashes in middle of running Clear Data?

If the system crashes in middle of the Clear Data process or SQL Server becomes unavailable, the ACTIVITY record for the Clear Data Comp user ID will not be properly released. Hence, when users attempt to log back into the company they will receive the same error message.

To correct this problem, the system administrator must log into SQL Server Management Studio and remove the record by running the following query:

-- created by Mariano Gomez, MVP

Hope this helps in your troubleshooting efforts and to understand another one of those ‘old’ Dynamics maintenance utilities.

Until next post!

Mariano Gomez, MIS, MCP, MVP, PMP
Maximum Global Business, LLC

How are Payables transactions work error messages stored?

June 24, 2009

This question came up in the Dynamics GP Partner forum a few days ago, the specifics inquiried by the consultant were as follow:

I was just looking at the PM10000 table in SQL Server Management Studio. I found a column named PMWRKMSG, with a data type of binary(4), which I cannot understand. In addition, I found entries like, 0x00019000, 0x00009000, 0x00000000 as column values. I know that they represent error messages, generated on the Batch Edit List and Posting Journals, but how can I check which message is being generated by just looking at this column? Do I need to convert this entry to some other integer and refer to some other table, which in turn would have a list of all the error messages?


The ‘PM WORK Messages’ field (PM10000.PMWRKMSG column) is a Dexterity multi-select listbox control (so are the ‘PM WORK Messages 2’ and the ‘PM Distribution Messages’ fields) which contains static texts. The only way of storing such controls (with the listbox values checked or unchecked) on SQL Server is by using a binary data type. During the posting process, a number of failed validation rules will trigger any of the 32 static text values — added by the development team — to be checked in the multi-select listbox control.

The static texts (and possible errors you may receive during posting) for the ‘PM WORK Messages’ multi-select listbox control are:

No vendor record exists for this vendor ID.
This vendor is inactive.
This transaction already has been posted.
Duplicate check numbers are not allowed.
Duplicate invoice numbers are not allowed.
No record exists for this credit card.
No unique voucher numbers are available.
This transaction has been posted and fully paid.
Distributions for this transaction contain errors.
Vendor summary records cannot be updated.
This transaction is recurring; it cannot include a payment amount.
This document should not be applied to other documents.
The total applied amount is incorrect.
Tax detail information is incorrect.
Vendor ID is on hold
Withholding Vendor ID is invalid
Applied record is on hold
Batch information is invalid
GL posting date is invalid
Fiscal period for the posting date does not exist
Fiscal period for the posting date is closed
Taxes Incorrectly Distributed
This transaction contains multicurrency error(s).
This transaction contains errors. It won't be posted.
Transaction Analysis information for this transaction is incorrect or missing.
Transaction contains intercompany distributions; mark as an IC transaction.
Intercompany Processing is not registered;cannot post intercompany transactions
The currency must be either the functional currency,
The currency must be the same as the currency assigned to the checkbook
the Euro currency,
an enabled denomination currency,
or the same as the currency assigned to the checkbook

Assuming the values follow a binary storage pattern, 0x00009000, can be converted to its decimal equivalent of 36,864. In turn, this number can be represented as 2^15 + 2^12. If is the case, the error messages displayed on the batch edit list are: “Withholding Vendor ID is invalid” and “The total applied amount is incorrect”. In the case of 0x00019000, this is 2^16 + 2^15 + 2^12, this would result in the messages “Applied Record is on hold”, “Withholding Vendor ID is invalid” and “The total applied amount is incorrect”.

Of course, these errors can only be cleared via the interface, since resetting the value in SQL Server would have no direct effect on the validation rules applied by the posting process. A transaction will not move to the PM Transaction Open table (PM20000) if all the validation rules are not cleared, which in turn would render a value of 0x00000000 in the column.

The ‘PM WORK Messages 2’ multi-select listbox field stores the following static text values:

You cannot post to a tax date within a closed tax period.
You cannot post to a tax period that has not been set up.
The vendor's remit to address bank format is missing or inactive.
The bank format for the vendor and the checkbook must be the same country.
The bank format for the vendor and the checkbook must be different countries.
The bank format assigned to the checkbook is missing.
This check amount exceeds the maximum check amount for the checkbook.
Remit-To address doesn't exist; please enter a different address.

The above errors are mostly used during the validation of tax computations and vendors setup as EFT vendors.

The ‘PM Distribution Messages’ multi-select listbox field stores the following static text values:

The accounts payable distribution(s) does not equal the actual amount.
The purchases distribution(s) does not equal the actual amount.
The discount available distribution(s) does not equal the actual amount.
The trade discount distribution(s) does not equal the actual amount.
The discount taken distribution(s) does not equal the actual amount.
The misc distribution(s) does not equal the actual amount.
The freight distribution(s) does not equal the actual amount.
The tax distribution(s) does not equal the actual amount.
The cash distribution(s) does not equal the actual amount.
The write off distribution(s) does not equal the actual amount.
The other distribution(s) does not equal the actual amount.
The GST distribution(s) does not equal the actual amount.
The withholding distribution(s) does not equal the actual amount.
The debit distributions do not equal the credit distributions.
No account has been specified for one or more distributions.
The Realized Gain distribution(s) does not equal the actual amount.
The Realized Loss distribution(s) does not equal the actual amount.
The Round distribution(s) does not equal the actual amount.

Hope this helps in troubleshooting and understanding how Dynamics GP processes and manages errors when executing a Payables transaction posting validation.

Until next post!

Mariano Gomez, MIS, MVP, MCP
Maximum Global Business, LLC

Fixing Microsoft Dynamics GP and Illegal Characters error messages

June 24, 2009

By now you probably read David Musgrave’s series on Microsoft Dynamics GP and Illegal Characters and have come to realize that the single quote character is perhaps one of the biggest culprits in the errors you are receiving when running reports such as your age trial balances in both receivables and payables. It’s enough for the single quote character to be in your customer ID, vendor ID, or even document numbers to cause some error while running a report or inquiring a transaction.

The following is an actual case reported by a user on the Dynamics GP newsgroup when running the Payables Historical Aged Trial Balance report:

The stored procedure pmHistoricalAgedTrialBalance returned the following
result: DMBS: -127, Microsoft Dynamics GP: 0.

After some research the user realized they had a few document numbers that contained the illegal single quote character, for example INV’23002. Note that this case is specific to the document number.

So, what to do if you are a victim of the single quotes?

1) You will need to first identify the tables possibly affected. In particular, you will need to search all tables in the database that may contain the column storing the data — in the case of the example, DOCNUMBR. For this you can use a script I published before here. You may also use the script provided by David Musgrave on Part 2 of his series, which would allow you to search through all your company databases if experiencing the issue in more than one company.

2) You will then need to identify the specific records affected by a single quote. Once the tables have been identified, you can do the following:


NOTE: The GL20000 and GL30000 tables will store this information in the ORDOCNUM column.

3) Once you identify the document numbers you will need to have a plan to correct them. Your plan should include making sure the new resulting document number is not going to create a duplicate situation with an already existing record.

4) Update the affected records.


Note the use of a double single quote to format the document number string properly and avoid an early string termination.

Since this situation can present itself in other modules, be sure to identify the specific module affected and the tables that may be impacted based on the data column storing the informatin. Hope this helps and complements the articles and information on Developing for Dynamics GP.

Until next post!

Mariano Gomez, MIS
Maximum Global Business, LLC

How does Check Links work?

June 18, 2009

This is a rather complex question. To understand how Check Links works one needs to step back a bit into the history of Microsoft Dynamics GP.

In its origins, Dynamics GP was conceived with the idea of running on multiple operating systems (Windows, Mac OS, Novell Netware), and “database” platforms, namely Ctree and Btrieve. The term database is a misnomer because in reality Ctree and Btrieve at the time were nothing more than Index Sequential Access Method (ISAM)-based file systems, with Btrieve being at best a record manager environment that could run on Netware and Windows. However, ISAM-based systems lacked referential integrity capabilities.

This brings me to Microsoft Dexterity — the Microsoft Dynamics GP development environment. Within a Dexterity application, such as Dynamics GP, developers can define table structures and relationships that the Dexterity Runtime Engine then replicates into physical files depending on the file system platform. This table replication could be controlled via the DEX.INI file, with the FileHandler key.

FileHandler = Ctree

Since ISAM file systems had no ability to support referential integrity, the Dynamics development team had to create their own routines to analyze and maintain data integrity. The routines that were put into place were Shrink, Rebuild, Check Links, and Reconcile. In fact, back in the 80’s and 90’s if you called Great Plains Technical Support, the first thing you were always asked to do was to run a Shrink-Rebuild-Check Links-Reconcile.

The Shrink operation would remove any blank spaces between records, which was very common in ISAM-based platforms. Since ISAM files lacked the ability to compress files, records removed from GP’s physical files would create logical gaps in the file itself, hence space was never released.

NOTE: Btrieve was modularized starting with version 6.15 and became one of two database backends that plugged into a standard software interface called the Micro-Kernel Database Engine. The other product is Scalable SQL, a relational database product that uses Structured Query Language, otherwise known as SQL. After several new versions were released the company was renamed to Pervasive Software and they now sell the product as Pervasive P-SQL. All three platforms were supported by Dynamics at some point in time.

The Rebuild operation would “attempt” to recreate table structures and place default records in them if needed. It was not uncommon, for example, for palette files (the predecessor of today’s menus) to become corrupt and for system administrators to have to run a Rebuild operation to restore the default records in them.

Check Links however, is a different animal, so to speak. Check Links are entire procedures designed to analyze and remove any suspect records based on how tables are related in the application’s dictionary. Hence, at the time it was highly recommended to take full backups of the physical files before running this maintenance utility. Check Links performs validations to make sure every record that is part of an atomic document(for example, a customer record, a purchase order, a sales order, an RM transaction, etc.) is valid within the context of that document. The check links algorithms sweep through the records in every table that is not the main table of a table group, then compare to the records in the main table. If there is any missing corresponding record in the main table, for the records currently being processed, these records in the table being processed are removed. For example, invoice line items cannot subsist without an invoice header record; invoice distributions, cannot subsist without a header record. But Check Links only verifies integrity. Accuracy is carried out by the Reconciliation processes. Hence, after running a Check Links, it is always recommended to run a Reconcile.

With the release of SQL Server, the development team decided that it was more cost efficient (time, effort, and money) to live with the table relationships and the routines to maintain data integrity within the Dynamics dictionary all the while creating the extensions needed to validate certain operations required in the Dynamics code only for SQL. For example, it was not unusual to find source code that was designed to work in one way if running on Ctree/Btrieve versus calling a stored procedure if working on SQL Server — this is also known as selective code. After all, Ctree and Btrieve were still supported up until v7.5 and the code needed to work across all platforms supported. It was a lot easier to enhace parts of the application to support SQL Server rather than rewriting the code from scratch for the SQL platform. Introducing SQL Server also needed to be transparent to the 1000’s of ISVs who had integrating applications and add-ons to GP that wanted to make their code available for SQL Server without major rewrites.

Back to Check Links… SQL Server also allowed the development team to create more efficient routines to analyze data integrity. Portions of Check Links, especially those related to table groups, have been migrated to SQL stored procedures. In fact, the following list of stored procedures execute Check Links operations and are called from the Dexterity Sanscript code based on the selected table group.


So what happens to non-table groups? Certain tables are managed from the Dynamics interface with Sanscript code, certain other tables are not even checked as they may be self contained.

[Edit] David Musgrave also adds that you must “Be aware that Check Links can delete records it feels are damaged or orphaned. I have heard of valid data being removed. This has been known to occur with some ISV products which store data in GP tables, but Check Links does not recognise them.“. The moral of the story: backup, backup, backup all your data!

Related Articles:

Understanding how Microsoft Dynamics GP works with Microsoft SQL Server – David Musgrave @ Developing for Dynamics GP.

Understanding how Microsoft Dynamics GP works with Microsoft SQL Server continued – David Musgrave @ Developing for Dynamics GP.

More Dex.ini Settings! – This Site

Until next post!

Mariano Gomez, MVP
Maximum Global Business, LLC

Retrieving Windows Registry key values with Microsoft Dexterity

June 12, 2009

Every so often you get these development requests that seem to push Dexterity to its limits. One of such requests is being able to read a Windows Registry key value using nothing more than SanScript.

The following example will show how to use the Microsoft Windows Management Instrumentation (WMI) Scripting library, ADVAPI32.DLL to retrieve a Windows Registry key value by examining how to retrieve the default Internet browser software being used.

The code will use the RegOpenKeyA and RegQueryValueExA DLL functions to a) return a handle for the registry path where we can found the key, b) then retrieve the actual key value. In order to access external DLL functions, it is necessary to create Dexterity prototype global procedures for the external DLL functions.


{ prototype procedure RegOpenKeyA@ADVAPI.DLL }
out long return_value; {function returns ERROR_SUCCESS }
in long hKey; {Handle of parent key to open the new key under}
in string lpcstr; {Name of the key under hkey to open }
inout long phkey; {Destination for the resulting Handle }


{ prototype procedure RegQueryValueExA@ADVAPI32.DLL }
out long return_value; {function returns ERROR_SUCCESS }
in long hKey; {handle of the key to query }
in string sName; {Name of value under hkey to query }
in long lReserved; {Reserved, must be null }
inout long lType; {Destination for the value type, or NULL if not}
{required. }
inout string sKeyValue; {Destination for the values contents, or NULL }
{if not required. }
inout long lResultLen; {Size of sKeyValue, updated with the number of }
{bytes returned. }

Now that we have the prototype functions, we can proceed to use the Constant Definition window in Dexterity to define values for each Registry hive.

Registry Hive Constants

Constant Name Constant Value
HKEY_USERS 2147483651

NOTE: These constants are usually known by their hexadecimal values, but Dexterity does treats hexadecimal constants as strings, hence the decimal notation used.

Once the prototype functions have been defined, we can wrap these in an API that isolates the developer from dealing with the innerworks of the calls. We will define to global functions as follow:


{ global function RegKeyExists }
function returns long phkey;

in long hkey;
in string lpcstr;

local long return_value;

extern 'RegOpenKeyA@ADVAPI32.dll'
, return_value
, hkey
, lpcstr
, phkey;
error "Error calling RegOpenKeyA@ADVAPI32.DLL. Could not locate DLL pointer.";
end try;

RegKeyExists accepts the registry hive parameter (hkey) and the registry path and returns a handler if the path is valid (phkey).


{ global function RegGetKeyValue }
function returns string key_value;
in long hKey;

local long return_value, lValueType, lValueLength;
local string sKeyValue;

set lValueLength to 255;

extern 'RegQueryValueExA@ADVAPI32.DLL'
, return_value
, hKey
, ""
, 0
, lValueType
, sKeyValue
, lValueLength;
error "Error calling RegQueryValueExA@ADVAPI32.DLL. Could not locate DLL pointer.";
end try;

if return_value = OKAY then
key_value = sKeyValue;
key_value = "KEY_ERROR";
end if;

RegGetKeyValue in turn will take the handler (returned by RegKeyExists) and attempt to retrieve a value for the Default entry of the path previously given. If the function succeeds, it will return a string with the actual value, else the user will get a KEY_ERROR message.

Making it all work together..

For this project, I have created the following form:

The form contains 4 local variables:

‘(L) RegistryHive’: is a drop-down list with the following string values corresponding to each registry hive, as follows: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, and HKEY_CURRENT_CONFIG.

‘(L) RegistryPath’: is a string of 255 characters in length.

‘(L) RegistryKey’: is a string of 50 characters in length, not used in this project. I will leave this for a future post.

‘(L) RegKeyValue’: is a string of 255 characters in length. Set the property to Editable to False.

Now that you have the strings and drop-down list, drag the ‘OK Button’ push button control to finish — I am working in the Dynamics dictionary, DYNAMICS.DIC.

The actual production form will look like this:

We can now add the following Sanscript code to the OK Button change script:

syGetRegistryKey OK Button K_CHG

local long hKey; { [In] Handle to an open key. }
local long lHive;

local long return_value, nSubkeys, nSubkeyMaxSize,nMaxChars, nValues, lValueName, lValueData, lClassLen;
local reference ft;
local string lClass;

{ check for the last backslash character on the path string }
if substring('(L) RegistryPath', length('(L) RegistryPath'), 1) CH_BACKSLASH then
'(L) RegistryPath' = '(L) RegistryPath' + CH_BACKSLASH;
end if;

{ check the hive selected from the DDL and assign the proper constant }
case '(L) RegistryHive'
in [1] lHive = HKEY_CLASSES_ROOT;
in [2] lHive = HKEY_CURRENT_USER;
in [3] lHive = HKEY_LOCAL_MACHINE;
in [4] lHive = HKEY_USERS;
end case;

{ establish if the key entered is valid within the hive }
hKey = RegKeyExists(lHive, '(L) RegistryPath');

{ RegKeyExists will return 0 if the path does not exist within the hive,
else it will return the handle value for the key

lClass = "";
lClassLen = 0;

if hKey 0 then
{ Get the key value; will append the key to the path to make it whole }
'(L) RegKeyValue' = trim(RegGetKeyValue(hKey));
warning "Invalid registry key entered";
end if;

You can compile and run the code in test mode and use the Developer Assistant form to open the newly created form. For example, let’s check the registry for the default Internet browser running:

I will be taking a shot a two other functions in the Microsoft WMI Scripting library in a futute installment. Hope you enjoy this article and can’t wait to hear your comments.


A big thank you to Jon Eastman for his insight on the ADVAPI32.DLL. He gave me the idea for this post… that’s what it’s all about!

Until next post!

Mariano Gomez, MVP
Maximum Global Business, LLC