Quantcast
Channel: Dynamics NAVAX
Viewing all 219 articles
Browse latest View live

Table casting in AX 2012 and D365O

$
0
0

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:

image

image


Azure AD Authentication for Windows Applications

$
0
0

In this post I will describe how you can get the login dialog to pop up when you are developing a windows desktop application.

Below is an example of the dialog appearing.

image

Below is a sample code how to acquire the token.

string authorityUri = "https://login.windows.net/common/oauth2/authorize";
Uri redirectURI = new Uri("https://login.live.com/oauth20_desktop.srf");

AuthenticationContext authenticationContext = new AuthenticationContext(authorityUri);

AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync(
ClientConfig.Default.ActiveDirectoryResource,
ClientConfig.Default.ActiveDirectoryClientAppId,
redirectURI,
new PlatformParameters(PromptBehavior.RefreshSession)).Result;

Add the redirect URI for https://login.live.com/oauth20_desktop.srf

image


Reference:https://docs.microsoft.com/en-us/rest/api/datacatalog/authenticate-a-client-app

Technical dive into the Mobile Platform in Update 10

$
0
0

With the recent Platform update 10 they introduced server side coding and made some enhancements to the mobile platform. This brings a lot of flexibility and capabilities. Below I will focus on just the Expense Management Mobile workspace.

For one, the Mobile workspaces are now embedded in the AOT as a resource file. Notice how it says “In metadata”. This indicates that it has loaded from the AOT. I think this is a good move. Its all about ALM. This is also great to keep the server side and client side in sync.

image

The nice thing is that the Expense Mobile solution is in its own model. Makes it easy for us to track and learn from.

The main entry is the workspace resource file xml. Notice the guid, this is important for doing some server side coding.

image

Now lets take a look at the workspace class. You will notice the same guid is used for the class attribute.

image

in one of my prior posts, I mentioned that the currency field wasn’t being defaulted when entering a new expense. Now with server side coding you can achieve some of these things.

Below is a default config contract class. It is triggered from the above class. You can add as many contract classes as you like. See line 20 and 21 in the above screenshot.

image

If you look at the js file embedded in the workspace xml. You will notice that it loads the config contract onto the client side. Allowing it to work client side and not requiring a connection to the server.

image

The other things you will notice is that there are a number of forms created just for the mobile solution. Below are screenshots of the whole model. Its not that big but there has been substantial work done to get to this point.

Below are some screenshot for my own reference. I know I will refer to to it in the future.

image

image

image

image

Refer to Mobile Platform landing page for more information.

https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/mobile-apps/platform/mobile-platform-home-page

First look at Azure Functions

$
0
0

Azure Functions allows you to write code that can be triggered by an event or timer. You don’t need a machine to run it on. All the computing runs on the Azure cloud ie sevrverless computing.

Below is a quick walk through. Keep in mind Microsoft has great documentation on their site. Just google it.

In this post I will put a simple walkthrough. Save you from the information overload.


Make sure you are using Visual Studio 2017.

Create a project of type “Azure Function”

image

Give it a minute to resolve the NuGet package. You will see a warning icon and watch it disappear by it self.

image

Once its done. Right click and Add item.

Select the type of function. The easiest to do is the Http trigger.

image

This will create a class with some sample code.

image

When you run it, it will run a local instance.

image

Navigate to the URL and watch it run.

image

There are many applications in the Dynamics space. Hopefully next time I can cover some Dynamics scenarios.

Chain of Command–next() gotcha

$
0
0

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("Inner - After next");
}
info("Outer - After next");
}
}


The compiler will not error or give you a warning. However, at run time you will get a misleading error.

In my case I got this. “Error executing code: Wrong argument type for function.”

image

Having a look at Event viewer (Folder: AX-XppRuntime), it gave me a little more info. At least I know the class that was calling it. I searched for any customization.

image

I found an extension class PurchReqCreate_Extension. I was able to eye ball and figure out the problem. Taking next() outside the “if” condition.

Rewrote the same:

public void init()
{
  if (this.args() && this.args().record())
  {
    //
do something before
  }

  next init();

  if (this.args() && this.args().record())
  {
    //
do something after
  }

}

Analyse SQL performance issues using SQL Query Store for D365FO

$
0
0

Someone at work showed me this. With the production environment the Query Store is enabled. So when you bring it back to a sandbox environment. All that information comes with it.

If you connect to the database via SQL management studio. Then navigate to the Query Store. There is a lot of great in depth information you could get. I have a customer that has gone live for 3 months and I wanted to see if there were any improvements we could do. Such as missing indexes we could add to our product.

image

I won’t dive in too deep as there are some great resources online on how to utilise Query Store.

I should mention a lot of this information shows up summerised on LCS. So that should be your starting point but I am really glad this is available.

Show deleted items in Source Control Explorer

$
0
0

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

Run a class in background–Asynchronies mode

$
0
0

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.


Workflow hierarchy limit checker for D365FO

$
0
0

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.

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

$
0
0

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.

Resolve ledger dimension through X++ [D365FO]

$
0
0

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.

Resolve default dimension through X++ [D365FO]

$
0
0

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 Budget dimension through X++ [D365FO]

$
0
0

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()));
}

Approve Workflow via email using template placeholders #Dyn365FO

$
0
0

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

Add Spending/Approval limit to Purchase Order Workflow #MSDyn365FO

$
0
0

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


Add a new starting worker for workflow hierarchy approval #MSDyn365FO

$
0
0

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

Build a Question and Answer Chatbot #Dyn365FO

$
0
0

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

Build and deploy your chatbot #Dyn365FO

$
0
0

This post is a continuation from the last blog post. We will explore what it takes to build and deploy your bot.

In the Azure Portal, create a new “Web App Bot” by searching for “bot”. Select Web App Bot and follow the wizard.

image

Enter the required details. Make sure to change the pricing tier to the free one, if you are experimenting. It is a good idea to select your template here.

image

There are various templates available. Question and Answer one is a good template for fast set up of your QnA maker. If you are intending to use LUIS, select the Language understanding template. It will create LUIS project and connect it up for you.

image


image

In the App Settings you will all the application ids, secret keys etc to hook it all up.

image

If you used the LUIS template, you will see the LUIS application created here.

https://www.luis.ai

image

If you used the QnA template, then you will be required to enter the QnA appkey and password.

You can download the source code from the Build blade.

image

Top 5 tips for developing a Chatbot

$
0
0

This is different from my usual blog posts but I have been sitting on this for some time. I have been working with Chatbots for about 1 year now. Been on and off various engagements. So, I wrote something to make it a a bit clearer for those wanting to develop it and those wanting to know about it.

Chatbots are very trendy these days and thought I would write up some personal learnings I have made. Chatbots have existed for many years but the technology has been democratised like many things in Azure. You can literally build a bot for free of charge (of course you have to invest a bit of your time). A user would generally communicate through a chat window and with the addition of cognitive services, you get a really powerful tool.

Below are my top tips for when building your first chat bot.

1. A chat bot must have a purpose

There are a few forms of chat bots out there.

a) General Chatbots – Alexa, Google, Cortana, Siri, Watson. This is the big problem these major companies mentioned are trying to solve. Some are very similar and some are very unique in the way they are tackling the problem. A while back Alexa and Cortana have signed an agreement. This may seem odd at first but it benefits bot companies. Rather than competing with each other, their partnership offers them a greater reach.

This is not the area for every company. It requires huge investment.

b) Domain Specific and/or Task Specific – Some examples are Alarm (Wake me up at 7am tomorrow), Weather (What’s the weather today in Perth), Health (Natural health advice), Retail (Product recommender), Company Policies (QnA)

These are some examples but the application is endless. With the toolkits available you can build a chat bot that has deep knowledge about a specific domain or a specific task. If you can focus on solving a problem for the domain then the application has purpose.

2. Know your audience

Know your audience before you develop your chat bot. If you are developing a chat bot for business users, make sure to communicate to them at the same level. Don’t try to tell jokes or deviate from your purpose. Some would call this a personality but most business users don’t expect a personality when they are performing a task that is supposed to be simple.

However, if you are developing for a consumer market, you could add personality to your chat bot. Making the users experience less serious and a bit of fun. You could tell a random joke if the user asks. You could personalise the message and try to up sell.

3. Keep it up to date

A chat bot should be well trained and kept up to date to provide the user with the best possible answer. A stale chat bot will die a lonely death and be forgotten. Remember, users don’t uninstall a chatbot. They merely stop using the chat bot and loses its relevence.

4. Be helpful

If your chat bot does not know how to answer a question. Don’t say you don’t know and stop there. Offer suggestions or some kind of summary of what the chat bot can do. For example, a user may enter a topic that is unrelated. The chat bot can respond with “I don’t understand” but you can improve this response by being helpful. Tell the user what you can do. “I don’t understand but I can help with booking your leave”. We could provide buttons or dialogs for the user to enter one of these conversations.

5. Design your flow

This sounds obvious but many forget. Before you begin any development, even a prototype, design your conversation dialog. You will see how many ways you could achieve the same thing via a conversation.

Think of a chat bot for booking leave. You could provide a wizard type dialog. Asking various questions such as; What date, how many days, what type of leave etc. Alternatively you could just build language understanding to interpret a single sentence.

User: “Book me annual leave starting next Monday for 5 days”.

From this one line we could interpret the following:

a) Type of leave is Annual leave

b) Date – starts next Monday

c) Duration – 5 days


A bonus tip is to watch this youtube video. I learnt so much and made me reflect a number of things that I have done.

https://youtu.be/aHI8qfofH4c?t=1032

Take away quote that really resonated with me “Solve one thing and solve it well.

Import document handling (attachment) files #MSDyn365FO

$
0
0

Out of the box you have limited data entities for migrating attachments. If you search what is already in the AOT, you will see a few various examples. I suggest you look at the LedgerJournalAttachmentsEntity as it is the simplest and cleans to copy from. I wont go into detail but I will give a quick run down of what it looks like.

  1. Use the DocuRefEntity as your main datasource. It does most of the work for you.
  2. Set your table you want to import for as the child datasource
  3. Add the Key

image

You will need to add the postLoad method. There is minor code to update the virtual field FileContents.

image


Below is an export I did for the general journal attachments. The import zip structure should be the same way.

It will create the usual artifacts such as the excel, manifest and package header xml files. You will see a Resources folder under that.

image

If you drill down to the resources you will see the attachments. This is an export and it used the document GUID for uniqueness. The other thing is the extension isn’t added to the file name. When you are importing you are ok to leave the extension name.

image

The Excel file looks like this.

image

Hope it helps.

Viewing all 219 articles
Browse latest View live