AX 2012, D365F&O, Developer

How to create a custom service in D365 F&O

Using customs services is the most flexible and customized way to integrate with D365F&O. Custom services are created with X++ code and can be used for both data-based, and operation-based integrations.

In this post I will show you how to create a custom service in D365 f&o using x++ language. In order to create a custom service in D365, there are several objects we need to have.

Create the ‘Request’ object.

We need to create a class that will be our ‘Request‘ object. The information we send out will be mapped to the variables in our ‘Request‘ class. Then we’ll use this class to run our operation. Request class needs to have a variable for each piece of information you are sending into the system. Below I will attach an example of a ‘Request’ class.

[DataContractAttribute]
class AddSalesOrderHoldRequestContract
{
    SalesId salesId;
    MCRHoldCode holdCode;

    /// <summary>
    /// Sales id of the sales order that we should apply the hold code
    /// </summary>
    /// <param name = "_salesId">SalesId</param>
    /// <returns>SalesId</returns>
    [DataMemberAttribute]
    public SalesId parmSalesId(SalesId _salesId = salesId)
    {
        salesId = _salesId;

        return salesId;
    }

    /// <summary>
    /// The hold code to apply to the sales order hold
    /// </summary>
    /// <param name = "_holdCode">MCRHoldCode</param>
    /// <returns>MCRHoldCode</returns>
    [DataMemberAttribute]
    public MCRHoldCode parmHoldCode(MCRHoldCode _holdCode = holdCode)
    {
        holdCode = _holdCode;

        return holdCode;
    }

}

Create the ‘Response’ object.

We need to create a class that is our ‘Response‘ object. In our code we are going to set the variables in this class to contain the information we want to be returned to the calling program. D365 will then convert these values into JSON structured data.

The Response class needs to have a variable for each piece of information we would like to send back to the calling program. Below I will attach an example of ‘Response’ class.

[DataContractAttribute]
class AddSalesOrderHoldResponseContract
{
    boolean result;
    List errors;

    /// <summary>
    /// Result of the request
    /// </summary>
    /// <param name = "_result">boolean</param>
    /// <returns>boolean</returns>
    [DataMemberAttribute]
    public boolean parmResult(boolean _result = result)
    {
        result = _result;

        return result;
    }

    /// <summary>
    /// List of error messages that occured during the inbound request
    /// </summary>
    /// <param name = "_errors">List of error messages</param>
    /// <returns>List of error messages</returns>
    [DataMemberAttribute]
    public List parmErrors(List _errors = errors)
    {
        errors = _errors;

        return errors;
    }

}

Create the Service class.

The Service class is the most interesting class when you create a custom service in D365. This class is responsible for fetching values from the Request object, running some process, then writing values on the Response object. In this class we will write code for :

  • Reading the information in our request object that are sent from the calling program.
  • Run the custom logic.
  • Populate the response project with data that we want to send back to the calling program.

Below I will attach an example of Service class.

class AddSalesOrderHoldService
{
    public AddSalesOrderHoldResponseContract addHold(AddSalesOrderHoldRequestContract contract)
    {
        boolean result = true;
        List errors = new List(Types::String);
        //Read the information from the request object
        SalesId salesId = contract.parmSalesId();
        MCRHoldCode holdCode = contract.parmHoldCode();

        
        //Run the custom logic
        try
        {
            SalesTable salesTable;

            select firstonly salesId, SalesStatus from salesTable
                where salesTable.SalesId == salesId;

            if(salesTable.RecId != 0)
            {
                //verify the sales order status
                if(salesTable.SalesStatus == SalesStatus::Backorder || salesTable.SalesStatus == SalesStatus::None)
                {
                
                }
                else
                {
                //cannot apply hold code to sales order in current status
                    throw error(strFmt("@label:SalesStatusError", salesTable.SalesId, salesTable.SalesStatus));
                }
            }
            else
            {
                //sales order dont exist
                throw error(strFmt("@label:SalesOrderError", salesId))
            }

            //verify that the hold code exists
            MCRHoldCodeTable holdCodeTable;

            select firstonly holdCodeTable
                where holdCodeTable.MCRHoldCode == holdCode;

            if(holdCodeTable.RecId != 0)
            {
                //declare new MCRHoldCodeTrans record (apply hold code)
                MCRHoldCodeTrans holdCodeTrans;

                ttsbegin;

                holdCodeTrans.InventRefId = salesTable.SalesId;
                holdCodeTrans.MCRHoldCode = holdCodeTable.MCRHoldCode;

                if(holdCodeTrans.validateWrite())
                {
                    holdCodeTrans.insert();
                }
                    
                ttscommit;
            }
            else
            {
                //hold code dont exist
                throw error(strFmt("@label:HoldCodeError, holdCode"));
            }
        }
        catch
        {
            result = false;

            //store errors
            SysInfologEnumerator enumerator;
            SysInfologMessageStruct msgStruct;

            enumerator = SysInfologEnumerator::newData(infolog.cut());

            while(enumerator.moveNext())
            {
                msgStruct = new SysInfologMessageStruct(enumerator.currentMessage());

                errors.addEnd(msgStruct.message());
            }
            
            //clear infolog
            infolog.clear();
        }

        //create and populate the response object
        AddSalesOrderHoldResponseContract response = new AddSalesOrderHoldResponseContract();

        response.parmErrors(errors);
        response.parmResult(result);

        return response;
    }

}

Create the service object.

We need also to create the service object, that will point to the service class that we have created before. The Service object, exposes our Service class and allows it to be called by an another system with the proper authentication. In visual studio we can create a service object and add those properties.

ClassService class name
DescriptionA description of the service function.
External nameName of the service

Now, right click one service node and select ‘New service operation’. In the properties of the Service operation you have to specify the method name that you have used to run the custom logic on your service class. In our example :

methodaddHold
NameaddHld

Your service will look like this:

Create service group.

Now, we have to create ‘The Service Group’ to add our service. Usually we use service groups to organize similar operations together.

First you new to create a ‘Service group’ object and set Auto deploy properties to Yes. After you have created the service group with right click on the service group node you can add a new service. In the properties you can specify your service name. In our example:

NameAddSalesOrderHoldService
ServiceAddSalesOrderHoldService

Your service group will look like this:

After you have finished with building your service you need to call it from Postman. On the next blog we will explain how to do it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s