Tuesday, 17 April 2018

Build a Question and Answer Chatbot #Dyn365FO

I am going to do a series of posts on Chatbots. I have been working on (and off) it for about a year now. I will start slowly and build up on the concepts.

The easiest to do is probably Microsofts QnA Maker. QnA Maker can help your create an FAQ type chatbot that has language understanding. You just provide it with a link to an FAQ site or file, it will index all the content. The user then uses natural language to ask their questions.

Lets get into it.

1. Navigate to https://qnamaker.ai and sign in with your Microsoft account

Accept the terms and conditions.

image

2. Create a new service.

Give it a name and a source for the FAQ. This can be a URL or a flat file. Then click on Create button.

image

3. This will create a question and answer pair.

image

4. Lets test it out before we publish it.

a) Enter your text

b) Best answer it could pick up

c) Improve it by adding alternative questions. For a simple greeting intent, you could say it in many ways. Eg. Hello, Hi, Hey etc..

image

5. Now that you are happy and Save your work by clicking the green “Save and retain”. Then click on Publish.

This will take you to a review page. You can download your diff file to get an understanding how many new QnA pairs you have added.

image

6. Once you clicked publish you will get a review the keys that have been generated for you. Make sure you click on Learn how, it has many different ways of communicating to it.

image

Friday, 13 April 2018

Add a new starting worker for workflow hierarchy approval #MSDyn365FO

This is very simple but not too obvious. There are times when you want to start the hierarchy approval with a specific worker and then have it traverse up the hierarchy.

See screenshot below screenshot of some of the options. You may want to add something different to what is already available.

image

The way it works is, it uses certain EDTs on the table.

So, all you need to do is add a field that extends one of these EDTs. Or just subscribe to the delegate to add another EDT.

Below is some code that could give inspiration.

image

image

Sunday, 1 April 2018

Add Spending/Approval limit to Purchase Order Workflow #MSDyn365FO

I feel this is very limiting when you try to use purchase order workflow via hierarchy assignment. The set up is there but you really can’t use it to the end. When you try to set up a stop condition, you can’t really use it.

Vote for the idea for Microsoft to develop this.

https://experience.dynamics.com/ideas/idea/?ideaid=6b55a93d-7235-e811-bbd3-0003ff68aa57

Below is a screenshot of how I have added it.image

To add these fields or any dynamic (calculated) field is very simple. Just a parm method to the workflow document class.

For this example, I just copied the parm method and made it point to the PurchTable.

Copy from PurchReqDocument to PurchTableDocument

Here is a class I put together to show you how I added the spending limit.

https://github.com/munib00/WorkflowPlus/blob/master/AxClass_PurchTableDocument_WP_Extension.xpp

Wednesday, 14 March 2018

Approve Workflow via email using template placeholders #Dyn365FO

Dynamics 365 for Finance and Operations has placeholders which can be inserted into the instructions. Normally you would want this to show up in the email that is sent. One of the most useful ones is the URL link to the exact record that you are approving.

In the workflow configurations use the placeholder and build up your message. Towards the end it has workflow specific ones. The URL token is %Workflow.Link to web%.

image

For the technical people the token is replaced in this class WorkflowDocumentField.

image

This is what I inserted into my email template.

<BODY>
subject: %subject%
<BR>
message: %message%
<BR>
company: %company%
<BR>
for: %for%
<BR>
</BODY>


Should look like this.

image

The final result looks like this.

image

If you debug these are the place holders that are put together.

image

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.