Tuesday, May 22, 2018

'PostLoad' method of Data entity in Dynamics 365 for finance and operations

I have seen lot of developers using 'PostLoad' method to implement most of their business logic related to integrations.

But, if you are writing any logic that is specific to the export process.
For ex: Setting any flags after export, or initiating any process after the export is done and so on.

We have to keep in mind that entities are not just used for Asynchronous but also for Synchronous scenarios.
The PostLoad() method will be executed every time the data is loaded.
For ex: 
1) When that particular data entity is opened through 'Open in excel'/Excel       Addin feature.
2) Whenever there is an get request against that entity
 - A simple 'Get' request from browser or any other tools.
   (Through the Odata endpoint -https://[Base URL]/data/'YourEntity') 
 - Use of 'Odata feed' in Excel or any other tools.

So we have to ensure that, there will not be any such logic written, which would be executed unnecessarily.

Thursday, May 3, 2018

Custom service to create Sales order with lines in Dynamics 365 and testing it

As you might know, 'composite entities' are not supported in Odata and are meant only to be used in Data management.

Which means we cannot use a single service or single odata endpoint to create a SO and also that there can be only asynchronous way to leverage it.

What if we have to create a synchronous service to create Sales order in D365?
Yes, there are standard entities for Sales order header and sales order line, which can be used separately.

But in that case, it will be the responsibility of the caller to:
-Call the header service first. 
-Check if header is inserted correctly 
-Call line service.

Even with this approach there are certain limitations:
-What if any one of the sales line fails, and is not created in D365?
It has to be rolled back. Then what happens to the Header which was already created in the previous call?
A new delete request must be sent to delete the header.

If the caller of the service will not be able to do this complete roll back , during any error or if they are not fine making 3 separate calls to create SO.

We can go for custom service in such cases, as it gives these benefits:
-Roll back capability, as we can write code to handle this.
-Give a single endpoint, to the caller to consume, which means through a single endpoint the complete sales order along with lines can be created.

What are all needed for this?
Firstly creating a contract class.
One header contract class and an line contract class.
Header contract class must also accept the list of line contract object class to support 1:N scenario.

Below is an example for a simple contract classes:
Header contract class, that has two methods:
-Customer account number for which sales order has to be created.
-A method that accepts the list of sales line contract class objects.


Line contract class:
-A method for 'Item number'
-A method of the 'Qty'

Then a service class that has all the logic to accept this contract classes and create a SO in D365.

class CreateSOService extends SysOperationServiceBase
{
    public str createSO(SalesOrderHeaderContract  _contract)
    {
        str                                 result;
        SalesTable                    salesTable;
        SalesLine                      salesLine;
        SalesId                          salesId;
        NumberSeq                   numberSeq;
        ListEnumerator              salesLineListEnumerator  = _contract.parmSalesOrderLinesList().getEnumerator();
        SalesOrderLineContract   salesOrderLineContract;
            
        try
        {           
            ttsbegin;
            numberSeq = NumberSeq::newGetNumFromId(SalesParameters::numRefSalesId().NumberSequenceId);
            salesId = numberSeq.num();
            salesTable.SalesId = salesId;
            salesTable.initValue();
            salesTable.CustAccount = _contract.parmCustAccount();
            salesTable.initFromCustTable();
            salesTable.LanguageId = "en-us";

            if(salesTable.validateWrite())
            {
                salesTable.insert();
            } 
            else
            {            
                throw Exception::Error;
            } 

            while(salesLineListEnumerator.moveNext())
            {
                salesOrderLineContract = salesLineListEnumerator.current();
                if(salesOrderLineContract)
                {
                    salesLine.clear();
                    salesLine.initFromSalesTable(salesTable);
                    salesLine.ItemId = salesOrderLineContract.parmItemNumber();
                    salesLine.QtyOrdered = salesOrderLineContract.parmOrderedSalesQty();
                    salesLine.SalesQty = salesOrderLineContract.parmOrderedSalesQty();;
                    
                    if(salesLine.validateWrite())
                    {
                        salesLine.createLine(true, true, true, true, true, true);
                    }
                    else
                    {
                        throw Exception::Error;
                    }
                }
            }
            ttscommit;
            if(SalesTable::exist(salesId))
            {
                result = strfmt("Success: Sales order '%1' is successfully created", salesId);
            }
        }
        catch(Exception::CLRError)
        {
            result =  enum2Str(Exception::CLRError);
        }
        catch(Exception::Error)
        {         
            result = "Insertion failed: Data validation error";
        }
        return result;
    }

}

Now that the required objects are ready.
It can be attached to the service node and then to service group.
Post the build, the service is ready to be consumed, deployed as per below url:
https:[Your organization's root URL]/api/services/service_group_name/service_group_service_name/operation_name

Now, if it has to be tested in any of the tools, apart from writing code and consuming it in any visual studio console applications.
We can use 'Postman' here.

A post request can be created in postman to call the service:

The input can be passed, in the body section, as shown below screenshot.


Upon clicking 'Send', a Sales order along with lines will be created in D365.
The response will be shown on the postman lower body section, as below:


It is the response, which is provided in the service class code, mentioned above.
You can verify if the SO is created in D365.

Testing Odata actions in Dynamics 365

With new Odata actions, we can now expose any custom business logic from D365, without having to create an custom service.
Apart from using the data entities for data integrations, we can also create and expose the methods too, as Odata actions.They can be used for the process integrations.

For example, using a SalesTable entity to create an Odata action to confirm or invoice the sales order.

Here is the simple example on testing the Odata actions in D365, using a tool called 'Postman'.

For the basic setup and for authentication the below url can be followed:


In this example, I have created a new static method for my custom entity.
The only new thing is, it has been decorated with [SysODataActionAttribute]



This method will invoice the sales order id that has been passed as the argument.
[Your organization's root URL]/data/[Your data entity]/Microsoft.Dynamics.DataEntities.['Method name']

For passing the parameter for the method, we can use the body section:


On clicking the 'Send' button for the POST request, the called method will be executed.

We can go back to D365FO to check if this succeed.
Below is the screenshot, where the mentioned sales order has been invoiced.



So, with this way we can create and expose the custom methods as Odata actions.
The example is for static method, but you can also try with the instance method for the data entity.



Quick & Easy way to create XSD from Dynamics 365 for finance and operations

If you are looking for an quick & easy way to create XSD (schema) from Dynamics 365 for finance and operations. Below is the way, ...