Friday, 17 July 2015

Dynamics AX 2012 Workflow receives "Failed to find workflow" error

We have run across a few situations in Dynamics AX 2012 where the workflow batch job fails with the error "Failed to find workflow" message.  When the customer received this error it prevented the remaining workflow messages in the queue from being processed.  In order to process all of the items that were queued up we needed to complete the following steps:

1.  Identify the record(s) that are causing the batch to fail.
Select a.RECID from SYSWORKFLOWMESSAGETABLE a Where a.ROOTCORRELATIONID notin(Select b.ROOTCORRELATIONID from SYSWORKFLOWTABLE b)and a.MESSAGELIFECYCLESTATE = 1
2.  Dequeue the SYSWORKFLOWMESSAGETABLE records that do not have a corresponding SYSWORKFLOWMESSAGETABLE record.
Update SYSWORKFLOWMESSAGETABLE Set MESSAGELIFECYCLESTATE = 2 where RECID =<RECID returned from statement above>
The procedures above will dequeue the message causing the error and allow the batch to complete.

Note:  As always, ensure you do a proper backup of your existing SYSWORKFLOWMESSAGETABLE prior to attempting this fix.

Forms not opening in local client.

In support we have seen a number of cases described along the lines of “Forms opens on remote desktop client but not on local AX client.”
Or “Dialog boxes fail to open.“ Historically we have found that the forms that fail to open are not out of the box forms. Instead they are USR or ISV layer objects.

What we have found is that the “Slide open combo boxes” under Performance options needs to be checked.


On server operating systems this is not turned on by default.

You can find this setting by right clicking on Computer -> Properties -> Advanced system settings -> Performance -> Settings.

You receive the error: “The transactions on voucher xxxxxxx do not balance as per xx/xx/xxxx. (Company currency: -x.xx - secondary currency: x.xx)” when attempting to post an invoice proposal in Project management and accounting.

One possible cause of this issue is the duplication of number sequences.  In Project management and accounting, there are separate number sequence setups available for On-Account Invoice, Invoice, On-Account Credit Note and Credit Note document types.  If the number sequences are not uniquely defined, in the posting process, when the records are fetched from the projProposalJour\ProjInvoiceJour it is possible that posting a credit would fetch the corresponding invoice with the same number sequence and vice versa.  This would then result in a situation where the merger of the documents results in an out of balance transaction, and then you would receive the error: "The transactions on voucher xxxxxxx do not balance as per xx/xx/xxxx. (Company currency: -x.xx - secondary currency: x.xx)".
To resolve this issue, you have a few options:
  1. Uniquely define the number sequences with a character segment specific to each document type.
  2. Use the same number sequence for the document types.
  3. You could also resolve the error by bumping up the next number to something that would not be a duplicate.  However, this would be a temporary solution as you would likely run into the error in the future on other documents.

How to enable the setup for user specific font Settings in MS Dynamics AX 2012

Found the following way on how to customize user depended fonts for reports and forms in AX 2012 client:
(1) Open a new developer workspace
(2) Navigate in the AOT to forms, SysUserSetup, designs, design(sys), [tab:tab](sys)], right click [TabPage.Fonts] (sys) and select properties
(3) Set the property visible by default No to Yes
(4) Save and compiling you the form change.
(5) In the AX 2012 client you can now see menu file (Note: blue button top left), tools, options the fonts and set the setting there user specifically

How to force complete CIL recreation in MS Dynamics AX 2012

“Microsoft provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability or fitness for a particular purpose. This mail message assumes that you are familiar with the programming language that is being demonstrated and the tools that are used to create and debug procedures.”
Prerequisite: Make sure that a full X++ compile was run without errors before

1) Stop all relevant AOS server(s)

2) On your relevant AOS server(s) navigate to the following folder (default) using Windows Explorer:
C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\XppIL

3) Make sure you create a safe copy of the XPPIL folder content to another new local folder on the AOS computer (example: C:\XPPIL_SAVE)

4) Now delete all folders and files inside the folder “C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\XppIL” but keep the folder “XPPIL” itself.

5) Start all AOS server(s)

6) Run a full CIL creation from AOT. This will create a fresh rebuild of all files/ folders inside the XPPIL folder

Happy DAXing
.........

Importing a General Journal using Data Import/Export Framework AX 2012

Here is a tutorial on how to configure Dynamics AX 2012 to import a general journal from a CSV file. The steps below are using Contoso demo data.
  1. Create a new folder on the root of the C: Drive and name it “DIEF”:
  2. Navigate to Data import export framework | Setup | Data import export framework parameters. Click “Browse” next to “Shared working directory” and select the “DIEF” folder we created. Once selected, click “Validate”.
  3. Close the “Data import export framework parameters” form.
  4. Go to Data import export framework | Setup | Source data formats. Enter “GLJOURNAL” for the “Source name” and “Type” = “File. In the parameters on the right side, enter “File format” = “Delimited”, “First row header” = TRUE, “Row delimiter” = “{CR}{LF}”, “Column delimiter” = “Comma {,}”, “Text Qualifier” = “*”, and “Role separator” = “;”. Click “Application”, and then select “CostCenter”, “Department”, and “ExpensePurpose”. Enter “CostCenter-Department-ExpensePurpose” for the “Dimension format” value.

  5. Go to Data import export framework | Setup | Target entities. Click “New” and enter “Entity type” = “Entity”, “Entity” = “Custom”, “Entity name” = “GLJOURNAL”, “Staging table” = “DMFLedgerJournalEntity”, “Entity class” = “DMFLedgerBalanceEntityClass”, and “Target entity” = “DMFLedgerJournalTransEntity”. Close the “Target entites” form.
  6. Go to Data import export framework | Common | Processing group. Type “GLJOURNAL” for the “Group name”, Ctrl+S to save, and click “Entities”.
  7. On the “Select entities for processing group” form, enter “GLJOURNAL” for both the “Entity name” and the “Source data format”. Click “Generate source file”.
  8. On the “Wizard” form, click “Next”. For the “Display data” fields, select the following and put them in the following sequence: JournalName, JournalNum, LineNum, CurrencyCode, TransDate, Voucher, AccountType, LedgerDimension, AmountCurDebit, AmountCurCredit, OffsetAccountType, OffsetLedgerDimension. Click “Generate sample file”.
  9. A .txt file should open, and save it to the root of the C: drive.
  10. Click “Finish” on the “Wizard” form. Close the “Select entities for processing group” form. Close the “Processing group” form.
  11. Go to General ledger | Setup | General ledger parameters. Click “Number sequences”. Right-click “Gene_10” next to “Journal batch number” and click “View details”.
  12. Click “Edit”, change the “_010” to “JN”, and click “Move up”. Click the “General” fast tab and note the “Next” value, in my case “JN000421”.
  13. Close the “Number sequences” form and the “General ledger parameters” form.
  14. Navigate to General ledger | Setup | Journals | Journal names. Select “GenJrn” and right-click the “Acco_18” next to “Voucher series”, and then click “View details”.
  15. On the “Number sequences” form, click “Edit”. In the “Segments” fast tab, click “Add”, select “Constant” for the “Segment”, and type “VN” for “Value”. Move this new segment to the top by clicking “Move up”. Note the next number in the series. In my case, “VN00000038”.
  16. Close all forms.
  17. Open Excel. Click File | Open. Navigate to the C: drive and select the “GLJOURNAL.txt” file (You may need to change the drop menu to “All Files (*.*)”)
  18. On the “Text Import Wizard” form, click “Delimited” and click “Next”. Check the box for “Comma” and click “Finish”.
  19. In line 2, enter the following values for each header:
    1. JournalName = GenJrn
    2. JournalNum = JN000421 (Value from step 12)
    3. LineNum = 1
    4. CurrencyCode = USD
    5. TransDate = 8/19/2013
    6. Voucher = VN00000038 (Value from step 16)
    7. AccountType = Ledger
    8. LedgerDimension = 110180-OU_1-OU_3566-Training
    9. AmountCurDebit = 10
    10. OffsetAccountType = Ledger
    11. OffsetLedgerDimension = 110101-OU_1-OU_3566-Training
  20. Click File | Save As. Click “CSV (Comma delimited)” for the “Save as type” drop-menu.
  21. Close Excel.
  22. Go to Data import export framework | Common | Processing group. Select the line for “GLJOURNAL” and click “Entities”.
  23. Click the folder icon next to “Sample file path”, and select the .csv file from step 20. Click “Generate source mapping”. Close the infolog.
  24. Close the “Select entities for processing group” form.
  25. On the “Processing group” form, select the line for “GLJOURNAL” and click “Get staging data”.
  26. A form for “Create a job ID for the staging data job” should open and populate with a “Job ID”. Click “OK”.
  27. On the “Staging data execution” form, click “Preview”. Verify the columns are correct, and then click “Run”.
  28. Close the infolog.
  29. On the “Processing group” form, click “Copy data to target”. Select the “Job ID” created earlier, and click “OK”. On the “Target data execution” form, click “Run”, then click “OK”.
  30. Close the Infolog.
  31. Go to General ledger | Journals | General journal. Locate the imported journal, and click “Lines”.
  32. Notice the values imported properly, and click Post | Post. The journal posts successfully.
Happy DAXing
............

Dynamics AX Table Caching: Basic Rules

This post provides some basic general guidance to get you started on setting table caching for custom tables, bearing in mind there will be exceptions. This should generally be defined at design time to avoid costly round trips to the database. As explained by Bertrand Caillet from our PFE (Premier Field Engineering) team:
“This is one of the most fundamental feature of the product today. The three tiers architecture of Dynamics AX allows you to define caching on AOS and client. Not using caching properly is the first root cause for performance.”
http://blogs.msdn.com/b/axinthefield/archive/2014/02/18/top-10-issues-discovered-in-the-dynamics-ax-code-review.aspx
Essentially there are two types of table caching as explained on msdn:
Set based caching (AX 2012)
Single record caching (AX 2012)
In AX 2012, table caching is more advanced than in previous versions, including support for joins, unique indexes (as opposed to primary indexes only), cross company queries, etc. (under certain constraints as explained in the above links). So for AX 2009, please see the following links (which generally cover previous versions too):
Set Based Caching (AX 2009)
Single Record Caching (AX 2009)
Cache settings for a table can be found in the following location in the application:
AOT > Data Dictionary > Tables > [TableName] > Properties > CacheLookup
Changes like this should be made by a developer in accordance with best practice guidance.
You can use the script at the bottom of this post to check cache lookup settings for all tables using the “Performance Analyser 1.20 for Microsoft Dynamics” (DynamicsPerf) tool (partly based on the analysis scripts that come with this tool).
Set the appropriate table group depending on how the table is used; see the following article for further details for AX 2012 (for previous versions it is basically the same but with fewer table groups):
 
Table and table group reference [AX 2012]
http://technet.microsoft.com/en-us/library/gg731855.aspx
Following that, you can generally set table caching according to the table below, again bearing in mind there can be exceptions. Please refer back to the above links for an explanation of each cache lookup type.
Table GroupCache Lookup
Miscellaneous* See notes below
 Parameter EntireTable
 Group Found
 Main Found
 Transaction NotInTTS
 WorksheetHeader NotInTTS
 WorksheetLine NotInTTS
 Framework N/A
 Reference Found
 Worksheet NotInTTS
 TransactionHeader NotInTTS
 TransactionLine NotInTTS
 * All newly created tables default to a table group of Miscellaneous. Ideally don’t use this table group for custom tables.
Finally, bear in mind that in AX 2012, the cache limit is configurable for every table group in the server performance settings:
System Administration > Setup > System > Server Configuration > Performance optimisation tab
Entire table cache size determines in kilobytes how much data is cached in memory before spilling to disk. The defaults are 32KB for AX 2012 RTM and 96KB for AX 2012 R2/R3.
The record cache limits define (per table group) the number of records stored in the server side cache and the client record cache factor defines based on that the number records stored in client cache, e.g. server side cache of 2000 (default) and client record cache factor of 20 (default) means 100 records are stored in client cache. Each AOS server can have its own cache settings. The basic rule here it is to keep the defaults unless performance testing proves it addresses a specific issue.

Happy DAXing
........

Thursday, 16 July 2015

Customizing the system-generated query of the Invoice journal form

When creating a Sales order in Dynamics AX 2012, you can define both a "Customer account" and an "Invoice account". The Customer account identifies the customer for which the Sales order is being created, and the Invoice account references the account number of the customer to invoice, in case it is different.


If you open the Invoice journal form from the Customer list page, you see only the transactions that were created for the customer selected in the grid, and not the transactions that were invoiced to this customer:
1- Go to Accounts receivable > common > Customers > All customers
2- In the ribbon, click the Invoice tab
3- Click the Invoice journal button in the Journals button group




If you want to modify this behavior, you will need to modify the query that is executed when the CustInvoiceJournalform is opened.
This is a good example of customization that would require accessing and modifying the system-generated query of a form, like explained here: https://msdn.microsoft.com/en-us/library/aa659696.aspx.
Setting the AutoQuery property on a form data source to Yes - as in the case here - causes the system to automatically generate the query that retrieves data to display in the form.
In this scenario, modifying the system-generated query to display a different set of data can be done in the init method of the data source of the form (in bold below):
Form: CustInvoiceJournal
Data source: CustInvoiceJour
Method: init
public void init()
{
   QueryBuildDataSource   queryDataSourceLink;
   CustInvoiceJour              custInvoiceJourLoc;
   SalesTable                      salesTableLoc; 
   TAMDeduction           tamDeduction;
   CustTable              custTable;
   super();
   if (element.args().dataset() == tableNum(CustTable) && element.args().record().(fieldNum(CustTable, AccountNum)))
   {   
      custTable = element.args().record();
      this.query().dataSourceTable(tablenum(CustInvoiceJour)).clearDynalinks();    
      this.query().dataSourceTable(tableNum(CustInvoiceJour)).addRange(fieldNum(CustInvoiceJour, RecId)).value(strFmt('((OrderAccount == "%1") || (InvoiceAccount == "%1"))', custTable.AccountNum));
   }
(…)

I hope this is helpful!

Happy DAXing
..........

X++ code to Count Records in Query

Following code illustrates how we can use SysQuery::countTotal() method to get the number of records in Query.

static void Query_cntRecords(Args _args)
{
    Query                query = new Query();
    QueryRun             queryRun;
    QueryBuildDataSource qbd;
    ;
   
    qbd = query.addDataSource(tablenum(CustTable));
    queryRun = new QueryRun(query);
   
    info(strfmt("Total Records in Query %1",SysQuery::countTotal(queryRun)));
   
}


No of ways to count the records

From data source
datasource.numberOfRowsLoaded()
From select queryselect count(RecId) from tableA;

Fetch number of records in a FormDataSource (e.g. of a Grid)

SysQuery::getTotal works fine, but the trick is how to handle temporary data sources where getTotal does not work:
if(!formDataSource.curser().isTmp())
{
    recordCount = SysQuery::getTotal(formDataSource.queryRun());
}
else
{
    while(!formDataSource.allRowsLoaded())
    {
        formDataSource.getNext();
    }
    recordCount = formDataSource.numberOfRowsLoaded();
}
Now recordCount contains the number of Records in the FormDataSource irrespective of the Tmp status of the data source. Of course the whole tmp-data source has been loaded in the process, which might be an issue in some cases.

Happy DAXing
.............

AOT Synchronization Errors (Troubleshooting )

Illegal data conversion from original field VendTable.ciscompanyregnum to vendtable.usecashdisc: unable to convert data types to anything but character field type (0 to 4)

During the Synchronization process in Dynamics AX 2012 there can be times when you experience errors which relate directly to SQL server statement that need to be handled and resolved. This blog has a number of tips you can use to help troubleshoot some of the more common errors.  You can get Synchronization errors in the course of performing multiple tasks; Initialization checklist, full version upgrade, applying rollups, service packs or cumulative updates, moving model files or code, and in applying License key or configuration keys changes.
It is important to understand why the synchronization was triggered and if the right approach is even to continue with the synchronization or if you triggered the synchronization in error, such as missing pre-processing upgrade tasks or doing things in the wrong order.
The right approach to use in each case may be different and may not necessarily be the steps described below, but at least they may help you in investigating and understanding the underlying problem. The errors in the infolog may not contain the whole error message so get the exact details from the application event logs of the AOS server you are connected to.  Normally the synchronize error is SQL statement that cannot be executed at the database and cannot be resolved without some action taken either in the database or in the AOT.
The error I will focus on is the following:
Illegal data conversion from original field VendTable.ciscompanyregnum to vendtable.usecashdisc: unable to convert data types to anything but character field type (0 to 4)
If you look at the issue at its surface the problem is that it can’t convert data types, but you need to understand this in the context of what the user is trying to accomplish.  In this case they wanted to remove the SYP layer code which was from CU3, before upgrading to the Feature Pack and then upgrading to CU3.  In the RTM code both columns exist, but it didn’t make sense that it would need to do a data conversion on these two fields.
To investigate this further we needed to understand why it was trying to do this seemingly incorrect conversion, but to accomplish this you need to know how the AOS determines what needs to be synchronized.  The AOS looks at the AOT to see what is currently in the code and compares it to a Dynamics AX table called SQLDictionary which holds information about the state of objects that also exist in SQL server.
To get the information about the VendTable in the error we created the following SQL statement:
select * from sqldictionary where TABLEID in (select TABLEID from sqldictionary where name = 'VendTable')
This will give you the fieldids which of each object, which represent the objects ids when you look at the fields in the AOT properties.
When we checked the object id in his AOT environment in the developer workspace the id didn’t match the value we found in the SQLDictionary table


To resolve the synchronization error we changed fieldid in the SQLDictionary to match what showed in the AOT.
                     UPDATE SQLDICTIONARY
                     SET FIELDID = '60039'
                     WHERE SQLDICTIONARY.RECID = 5638807121;
In the course of working through the rest of the errors we found a different type of issue.  This error was as follows:
Alter table DBO.CUSTINVOICELINESCRIPTBUFFER
ADD CONSTRAINT I_100412RECID Cannot add CONSTRAINT I_100412RECID because it already exists in the database
This would normally be a simple case of finding the Constraint or Index in SQL management studio on the CUSTINVOICELINESCRIPTBUFFER table and deleting it, so the synchronize could add it back, but when we checked that constraint or index didn’t exist on the CUSTINVOICELINESCRIPTBUFFER table, so we had to use a different approach.
Since we knew we could trust the SQL error message we checked the SQL server system tables to help identify the problem with the following statement.
select * from sysobjects where id in (select parent_obj from sysobjects where name = 'I_100412RECID')
 This will give you the name of the SQL table where that Constraint or Index is already in use; in this case it was on an USR layer object that was removed from the environment, but where the underlying table still existed in SQL.  In this case we just dropped the custom table as it was no longer in the AOT.
If you are getting synchronization errors that do no make sense or that you think you should have already resolved there may be old records left in the SQLsyncinfo table or have old or incorrect object ID references.  If you suspect this make a database backup, shutdown the AOS, in SQL run truncate table SQLSYNCINFO, start the AOS and run a new Synchronize from the AOT.
Hopefully this will give you an understanding of some troubleshooting steps that you can use but you need to be careful about changing things in the SQL server database and the SQLDictionary table in a production system.  Although we were able to resolve the problems using the steps above the correct method may have been to do a full X++ compile, full CIL generation, and database synchronization after removing the model files and before attempting the feature pack upgrade.
Always make sure that you are testing these steps against a copy of the production database in a test environment and that you have a good SQL backup.  You want to sure that even in the test environment you can restore to a backup in case you take the wrong approach and so that you can perfect your steps before making any changes against the production database

Happy DAXing
............