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.