Skip to main content

Posts

Showing posts with the label Xpp

My Experience moving FinOps/X++ code to GitHub

I recently saw a question on LinkedIn, asking if we can share our experience with moving to Git version control. https://www.linkedin.com/posts/tommyskaue_developer-git-xppdevs-activity-7017198598765342721-0i8c Here is a response to it. I will share my experience of moving to GitHub version control. We have chosen to move our X++ code to Git and in particular GitHub about 6 months ago. Reasons for the move Below are some of the reasons we have chosen to move from TFVC on Azure DevOps to Git on GitHub: Every single source code/project we have is on Git except for our X++ code. I have been asked way too many times as to why we are on TFVC. I have explained many times but I can't help shake the feeling that others think X++ is some old language. In other words, better alignment. There has been considerable effort to move to GitHub as our approved version control from many other version control systems. This is to make our code more accessible to all teams, have policies in place, leve...

Loop through AOT Display Menu Items #MSDyn365FO

I have been experimenting with getting Metadata information from the AOT from FinOps. There is some discussion on this forum post which helped me. https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/316468/how-to-get-aot-objects I thought I would try to clean it up a bit to make it a bit more readable for future reference. The X++ code below will loop through Display menu items in the AOT and print some info. Menu Item Name Menu Item Label Model Name public static void main (Args _args) { System.Type axMenuItemTypeDisplay = new Microsoft.Dynamics.AX.Metadata.MetaModel.AxMenuItemDisplay ().GetType (); System.Collections.Specialized.StringEnumerator menuItemDisplayNames = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::MenuItemDisplayNames (); while (menuItemDisplayNames.moveNext ()) { str menuItemName = menuItemDisplayNames.get_current (); //Get Model Name for the display menu item ...

Gotcha with Extending Retail Channel transaction table

I will start by pointing you to a good article Andreas Hofmann from Microsoft has written. It steps you through what you need to extend a transactional table in the Retail Channel and bring that back to HQ via the P Job. https://dynamicsnotes.com/extending-a-transactional-retail-channel-table-with-additional-data-hq-x-table-extension-cdx-extension-table-crt-data-service/ Now to summerise the issue I faced recently (being a retail rookie). Following the blog post I created a custom Int64 field on the RetailTransactionSalesTrans. However, when I ran the P job it failed with this error. “Failed to convert parameter value from a String to a Int64” I did some investigation by trying to find out what it is actually doing. Essentially the job will do an outer join to your custom extension table. Even though your custom field is 0 by default. You won’t be creating an extension record for every single transaction record. The p job will do an outer join between RetailTransactionSalesTrans to you...

Print a Sales Invoice via X++ in #MSDyn365FO

Often you want to print a report via X++. One of the more common reports is the sales invoice. Below is some code you could use to download a pdf copy. I am just picking the first invoice and printing to pdf. Next few posts will be dependent on this. I will try to build up the scenario. public static void printSalesInvoice() { CustInvoiceJour custInvoiceJour; select firstonly custInvoiceJour where custInvoiceJour.SalesId != ''; if (custInvoiceJour) { str ext = SRSPrintDestinationSettings::findFileNameType(SRSReportFileFormat::PDF, SRSImageFileFormat::BMP); PrintMgmtReportFormatName printMgmtReportFormatName = PrintMgmtDocType::construct(PrintMgmtDocumentType::SalesOrderInvoice).getDefaultReportFormat(); SalesInvoiceContract salesInvoiceContract = new SalesInvoiceContract(); salesInvoiceContract.parmRecordId(custI...

Chain of Command–next() gotcha

UPDATE: 31/10/2017 Microsoft reached out and I have to correct my example code. It seems the compiler does not catch it when it is 2 nested conditions. See below code. Be careful when using Chain of Command not to place the next() call in a condition. Here is an example. Create a class extension for a form. Call the method init() but place it in an “if” condition. Something like this. [ExtensionOf(formStr(PurchReqCreate))] final class PurchReqCreate_EAM_Extension { public void init() { info("Outer - Before next"); if (this.args() && this.args().record() && this.args().record().TableId == -100) //intentionally made it -100 to not execute this code { //doing something here info("Inner - Before next"); if (this.args().record().TableId == -100) { info("Inside second if condition"); } next init(); info("Inne...

Table casting in AX 2012 and D365O

D365O is a lot more sensitive about casting or should I say AX2012 was too relaxed about it. This post is to show a specific scenario that works differently in the new version of the product. We had a bit of code that worked fine in AX2012 but stopped working in D365O. After debugging we found that when you cast a table it took the tableId of the cast. Below is an example of AX2012 vs D365O: UPDATE – Thanks for Joris for pointing this out. Same code running in CIL in AX2012. The piece of code in question was running client side. Otherwise, I would have noticed this in AX2012 too.

Complex grouping of records for processing [AX 2012]

In this post I will discuss complex grouping of records for processing in a batch.There are many ways to achieve this but most of the time I see developers using temp tables. I feel that is not necessarily the most best way. So, in this post I will show how you can do the same using a map and a RecordSortedList. Avoiding multiple loops through records and also avoiding temp tables. Scenario Assume a scenario where you have to loop through unsorted records. Group them by the multiple fields including things like financial dimension value and balance. Then create a journal for each group. Options This could be achieved in a number of ways. But we want the best performing and extendable solution. You could do a group by selection query but that won’t work as you have to get the balance and dimensions (which are calculations). You could use a temp table to insert all the transaction. What if the key changes a few months later. Could be costly to develop the changes. You could use ...

Resolve the ledger dimension in the a different account structure [AX 2012]

Recently I had a look at an error that occurred on a custom journal posting routine. It was somewhat similar the journal allocations by looking at existing transaction to make adjustment. What I didn’t know is the account structure has changed. An error popped up: Account structure X for the combination x-x-x-x-x, is not valid for the ledger X.   When ever using ledger dimensions don’t just stamp the LedgerDimensionRecId. Use this method to resolve the RecId in the current account structure for the current company. toLedgerDimension = DimensionDefaultingService::serviceCreateLedgerDimension(fromLedgerdimension); I had to do a quick unit test to prove this. Below is a job that converts. Code: static void resolveLedgerDimension( Args _args) { //My expected values are //Dimension display value = '1.1.4.2.03--' //Account structure and dimension From RecId - Brazil - 52565566264 //Account structure and dimension Resolved RecId - Brazil SI - 2256553...

Multithreading with Business Operation Framework [AX 2012]

In this post I will discuss the basics of multi threading with business operations framework and I will provide a sample you can work with. Multithreading can give you a huge performance boost if you could spin each process separately. The biggest hurdle with writing a process to work via multi threading can be a little confusing and time consuming to develop. So, what I have done is create a generic xpo project which I have used many times as a starting point. Below is a screenshot of what the net result is of my sample project. The xpo contains the following project. Download xpo The \Classes\Processor\process method is the main entry method. If it is run with out the batch it will execute each record separately. Otherwise, if it is run in batch it will bundle the records based on a default bundle size (I have set it to 10). I then use a list to store the 10 customer recIds. The reason I bundle the records is because when you multithread, you may not want to process each r...

Open source Exchange rate provider on Codeplex [AX 2012]

Way back I had created an exchange rate provider using Yahoo! Finance services. So, today I thought I would share this code that has been sitting with me for a long time. I have created a Codeplex project to start the sharing. I want to also encourage others to start sharing if they have developed their own exchange rate provider using other services. I am more than happy to add anyone who wants to contribute as a developer or I can upload it for them (and mention their name on the main page). I think we can all benefit from these being freely available or if Microsoft can include it in their product eventually as standards. Visit: Exchange rate provider on Codeplex Download the Yahoo! Finance exchange provider class xpo, import it into your environment, compile + CIL compile, restart your client and start using it. Once you have done the right thing. It should be available for configuring. Then, you should be able to run it. More information to get you started: To learn on ho...

Refresh caller form [AX 2012]

There are a few ways to refresh a caller form. You may notice in AX2012 sometimes calling research(true) causes the record to jump to the starting position. The right way is to reread the references data sources before calling the research(true). Below is some sample code I use. Code: /// <summary> /// Refresh the caller form by calling the research(true) which does not work. /// research(True) alone will jump the cursor to the begining (either first or last) record. /// </summary> /// <remarks> /// Taken from form InventTableInventoryDimensionGroups.closeOk() /// </remarks> public void refreshCaller() { Common common; FormDataSource callingFormDataSource; common = element.args().record(); if (common) { callingFormDataSource = common.dataSource(); if (callingFormDataSource) { // Referenced datasources must be reread before calling research as data has been modified beh...

Event handler for a clean world [AX 2012]

Eventing has been covered in many blogs and Microsoft has documented it pretty well. I thought I should touch on it a little bit today to bring it back in our conversation. Below is an example of eventing that used. Say you wanted to extend the PurchLine table modifiedField method. In the old days you would modify the method directly. Making it dirty. If you had another ISV making a modification, they would do the same. Meaning, someone has to merge code. Have a look at the example below. PMF stands for Process Manufacturing which used to be separate from the foundation model and similarly BR stands for Brazil which is a country specific module. Both are done using event triggers. See the property they are called Post the method. Pointing to a class. The end result is”No code merging” at all. The only reason I would modify the method directly is if you needed to put your code in the middle of a long process. Long story short. Use eventing for a cleaner environment. Write your co...

Import Project Activity Breakdown (Hierarchy)

I hope this is one of many import blog posts. No promises but I may look at the same written using the Data Import Export Framework to show a comparison. To continue with the subject… I wrote a job to import project work breakdown structure tree. The csv file is in this format. Final outcome is like so. The job code is below: static void ImportActvities( Args _args) { AsciiIO asciiIO; Filename filename; NoYesId skipFirstLine; Container line; Dialog dialog; DialogField dialogFileName, dialogSkipFirstLine; int numProcessedRecords=0; smmActivities smmActivities; ProjTable projTable; ProjActivity projActivity; Hierarchy hierarchy; HierarchyLinkTable hierarchyLinkTable; HierarchyTreeTable rootHierarchyTreeTable, parentHierarchyTreeTable; ProjId projId; smmActivityNumber ac...

Embed Google maps in a form

I had to prove a concept the other day to embed google maps in a form. A big of googling and some ideas that had to mash up. Create a form with an ActiveX control “Microsoft Web Browser”. On the init method after super call. Add this line. ctrlactiveX.Navigate(' https://maps.google.com.au/maps?q=1 St Georges Terrace, PERTH WA 6000+(This is a random place)&output=embed' ); Final result looks like this. Reference: Thanks to this blog. http://microsoft-dynamics-ax-erp.blogspot.com.au/2012/01/integrate-google-map-in-dynamics-ax.html Minor change I had to make was to figure out the URL parameters. By passing the output=embed, you get a pretty good result.

Delete Private AOT projects in AX 2012

This is a common problem since the old days. People leave projects in their private project and no one can get to it to clean it up. In pre AX2012, it was a matter of selecting the utilElements and calling the AOTDelete method. I won’t go through it here. In AX2012, it is all maintained in the model store. Let see it in pictures. Below are the projects you want to delete. You can see them in the SysModelElement table. It is in the system tables section. I wrote a job to loop through and delete them. I had to Code: static void deleteAllProjectObjects3( Args _args) { /* msdn.microsoft.com/en-us/library/bb190038(v=ax.10).aspx */ SysModelElement sysModelElement; ProjectNode privatePn; ProjectNode pn; utilElementName shortName; int len; str preFix; // Navigate to the Private projects folder. privatePn = infolog.projectRootNode().AOTfindChild( "Private" ); ...

Renumber line number when its part of a key

In this post, I will show how to renumber the line numbers in a table with out getting the duplicate record is not allowed. A bit of background first, say you have a table with line number field. It gets out of sequence and now you want to renumber it. You can’t simply write a method to loop through and renumber it. The system won’t allow you do it. One solution is to number then to something unique (ie. negative numbers). Then do a second loop and number them correctly. Another solution is below. I store the RecId and LineNum in a temp table. Then I do a bulk update using update_recordset. Note: I am using a fake table below called WorkOrder and WorkOrderLine. static void renumberLineNum ( JournalId _journalId ) { WorkOrder workOrder ; WorkOrderLine workOrderLine ; TmpLineRenumbering tmpLineRenumbering ; real lineNum ;    while select RecId, LineNum from workOrder order by workOrde...