Tuesday, 30 January 2018

Resolve Budget dimension through X++ [D365FO]

This one is to resolve budget dimensions. Be careful here to use the right class. Budget plan and Budget register use a different contract class.

   
   public static void getBudgetLedgerDimension()  
   {      
     //use BudgetPlanningContract for Budget plan  
     //use BudgetAccountContract for Budget register  
     BudgetAccountContract budgetAccountContract = new BudgetAccountContract();  
     budgetAccountContract.parmValues(new List(Types::Class));  
     budgetAccountContract.parmAccountStructure('Manufacturing P&L');  
   
     DimensionAttributeValueContract attributeValueContract;  
       
     //Main account      
     attributeValueContract = DimensionAttributeValueContract::construct('MainAccount', '110180');  
     budgetAccountContract.parmValues().addEnd(attributeValueContract);  
          
     //Dimension 1 - repeat this for all other dimensions  
     attributeValueContract = DimensionAttributeValueContract::construct('Department', '022');  
     budgetAccountContract.parmValues().addEnd(attributeValueContract);  
     
     //resolve the dimension  
     BudgetDimensionCombinationServiceProvider budgetDimensionCombinationServiceProvider = BudgetDimensionCombinationServiceProvider::newForBudgetAccountContract(budgetAccountContract);  
     DimensionStorageResult dimensionStorageResult = budgetDimensionCombinationServiceProvider.resolve();  
     if (dimensionStorageResult.parmInvalidValue())  
     {  
       error("Invalid dimension");  
     }  
       
     info(strFmt("Budget Ledger RecId: %1", dimensionStorageResult.parmSavedRecId()));  
   }  

Resolve default dimension through X++ [D365FO]

This one is resolving the Default dimension

   
   public static void getDefaultDimension()  
   {  
     DimensionNameValueListContract dimensionNameValueListContract = new DimensionNameValueListContract();  
     dimensionNameValueListContract.parmValues(new List(Types::Class));  
       
     DimensionAttributeValueContract dimensionAttributeValueContract;  
   
     //Dimension 1 - repeat this for all other dimensions  
     dimensionAttributeValueContract = DimensionAttributeValueContract::construct('Department', '022');  
     dimensionNameValueListContract.parmValues().addEnd(dimensionAttributeValueContract);  
       
     //resolve the dimension  
     DimensionNameValueListServiceProvider dimensionNameValueListServiceProvider = DimensionNameValueListServiceProvider::newForDimensionNameValueListContract(dimensionNameValueListContract);  
     DimensionStorageResult dimensionStorageResult = dimensionNameValueListServiceProvider.resolve();  
       
     if (dimensionStorageResult.parmInvalidValue())  
     {  
       error("Invalid dimension");  
     }  
   
     info(strFmt("Default dimension RecId: %1", dimensionStorageResult.parmSavedRecId()));  
   }  

Resolve ledger dimension through X++ [D365FO]

A bit of code to show how to resolve ledger dimensions. There are various codes out there but I thought I would write it in an easy way to understand. It is hard code but I did that for illustration purposes.

   
   public static void getLedgerDimension()  
   {  
     DimensionAttribute     dimensionAttribute;  
     DimensionAttributeValue   dimensionAttributeValue;  
     DimensionSetSegmentName   dimensionSet;  
     DimensionStorage      dimStorage;  
   
     LedgerAccountContract ledgerAccountContract = new LedgerAccountContract();  
     ledgerAccountContract.parmValues(new List(Types::Class));  
     ledgerAccountContract.parmAccountStructure('Manufacturing B/S');  
      
     DimensionAttributeValueContract dimensionAttributeValueContract;  
   
     //Main account  
     ledgerAccountContract.parmMainAccount('110180');  
       
     //Dimension 1 - repeat this for all other dimensions  
     dimensionAttributeValueContract = DimensionAttributeValueContract::construct('Department', '022');  
     ledgerAccountContract.parmValues().addEnd(dimensionAttributeValueContract);      
       
     //resolve the dimension  
     LedgerDimensionCombinationServiceProvider dimensionServiceProvider = LedgerDimensionCombinationServiceProvider::newForLedgerAccountContract(ledgerAccountContract);  
     DimensionStorageResult dimensionStorageResult = dimensionServiceProvider.resolve();  
       
     if (dimensionStorageResult.parmInvalidValue())  
     {  
       error("Invalid dimension");  
     }  
   
     info(strFmt("Ledger dimension RecId: %1", dimensionStorageResult.parmSavedRecId()));  
   }  


There are a few other ways that do the same. Another example is using this method.

LedgerAccountDimensionResolver::newResolver

Use find reference to get some examples. However I prefer this method above.

Saturday, 13 January 2018

Integration - Create a lead in CRM via a web service in 10 minutes

CRM (Microsoft Dynamics Online – not AX CRM) has an SDK which you could use to integrate to. It can been overwhelming sometimes. So, I decided to use Flow to do the communication for me (HTTP Request > Dynamics). Took me 10 minutes from start to finish.

I didn’t have to learn the CRM SDK or figure out how to do authentication etc. I wanted to send a simple json message like this.

{
     "Email": "munib@fakeemail.com",
     "FirstName": "Munib",
     "LastName": "Ahmed",
     "Topic": "Health"
}

Go to Flow and create a new HTTP request. Click on “Use sample payload to generate schema” and enter the above json message. It will generate a schema as per below screenshot. Take note of the HTTP POST URL that has been generated. We will use that later to send the message to.

image

In the Actions select Dynamics > “Create a new record”. Select Leads as the entity name. Map the fields and you are done.

image

Now we just need to send a message to Flow using the URL. It doesn’t matter what tool you use to send a message. Below I used AX2012 (just because I had this job from a previous blog post I did).

image

Go to Flow Run history to see what has happened. Notice in the output – it looks like the message we sent.

image

We see the same in the creation. The mapping has worked.

image

Final result in CRM is the lead created. All my mapped fields look fine.

image

A word of caution. This is not a production ready solution. As you should consider security and your end to end architecture.

I did this as a proof of concept and wanted to show the power of Flow. Allowing me to integrate to another system that I didn’t have much knowledge of.

Tuesday, 26 December 2017

Workflow hierarchy limit checker for D365FO

A couple of years back I wrote a post on a workflow hierarchy limit checker job. It was a simple job that took a starting worker and traversed up the hierarchy. It printed the userid/worker/position/manager/spending limit/approval limit in an info log.

Today I rewrote it slightly to use a dialog for D365FO. Since it is not possible to change the code that easily in D365. This currently only works for Expenses. Its not pretty at the moment but it works.

image

I intentionally left the error in there. This will print the same error as what the workflow engine would. Hopefully this would give you a clue on where it stops.

image

Hopefully I can come back and extend it to the other document types such as purchase requisition and purchase orders.

Code is available on GitHub. I gave it a generic name like “Workflow plus”. I hope to add a few other nice commonly used workflow enhancements to it.

Another common enhancement I can think of is - Purchase order spending and approval limit; currently you don’t have this on the document. It is not possible to create a stop condition based on approval/spending limits

You can run the job via the url. Later hopefully I can add a menu item and security to it.

Saturday, 16 December 2017

Run a class in background–Asynchronies mode

This is not much of a new thing but a reminder. I feel it should be used more often in D365fFO. In prior versions we showed a progress bar and gave the user a nice feeling that something was processing. In D365fFO we need better a way. If you look at the Data Management, it has a workspace for this. Process runs in async and workspace shows us the status.

Code wise, it is pretty simple. Below is an example from standard code. I got it using “Find reference” feature in Visual Studio.

image

Any info or error messages will appear in the message centre. This is great as the user can continue doing their job without waiting.

Why am I writing about this?

I had a project recently where the a process was taking minutes to run. Based on the amount of data and what it was doing, I found it acceptable that it ran that long. When the user was running it, they perceive it as slow and they are waiting on the screen to refresh. I used runAsync and what do you know – happy customer.

Monday, 4 December 2017

Show deleted items in Source Control Explorer

Common query I have seen a few times. Thought to share a simple tip.

There are times when items are deleted or moved to a different model. In that case you may need to see the deleted items to roll it back or see what was deleted. In visual studio, go to the Options and tick the flag to “Show deleted items in the Source Control Explorer”.

image

Alternatively click on this button. Thanks to Joris for pointing it out.

image