Header Ads Widget

Developing a Custom Service to Create a Sales Order in Dynamics 365 Finance and Operations Using X++

Introduction

Creating a custom service in Dynamics 365 Finance and Operations (D365FO) enables external applications to interact with the system and invoke business logic for various tasks. In this tutorial, we will guide you through the process of creating a custom service to create a sales order in D365FO using X++.


Step 1: Define the Sales Order Header Data Contract Class

The first step is to define the structure of the incoming data we will receive from external systems. This is done by creating a Data Contract Class for the Sales Order header, which will hold key information such as the customer ID, sales order ID, and a list of sales order lines.

x++
[DataContractAttribute] class d365fo_SalesOrderHeaderDTO { SalesId externalOrderId; CustAccount customerAccount; List salesLines; [DataMemberAttribute("ExternalOrderId")] public SalesId setExternalOrderId(SalesId _externalOrderId = externalOrderId) { externalOrderId = _externalOrderId; return externalOrderId; } [DataMemberAttribute("CustomerAccount")] public CustAccount setCustomerAccount(CustAccount _customerAccount = customerAccount) { customerAccount = _customerAccount; return customerAccount; } [DataMemberAttribute("SalesLines"), AifCollectionType('salesLines', Types::Class, classStr(d365fo_SalesOrderLineDTO))] public List setSalesLines(List _salesLines = salesLines) { salesLines = _salesLines; return salesLines; } }

Step 2: Define the Sales Order Line Data Contract Class

Now, we will create a data contract class for the sales order lines, which will include details such as item ID, quantity, sales price, and discounts for each line item.

x++
[DataContractAttribute] class d365fo_SalesOrderLineDTO { ItemId productId; SalesQty orderQty; SalesPrice unitPrice; SalesLineDisc lineDiscount; SalesLinePercent lineDiscountPercent; [DataMemberAttribute("ProductId")] public ItemId setProductId(ItemId _productId = productId) { productId = _productId; return productId; } [DataMemberAttribute("OrderQty")] public SalesQty setOrderQty(SalesQty _orderQty = orderQty) { orderQty = _orderQty; return orderQty; } [DataMemberAttribute("UnitPrice")] public SalesPrice setUnitPrice(SalesPrice _unitPrice = unitPrice) { unitPrice = _unitPrice; return unitPrice; } [DataMemberAttribute("LineDiscount")] public SalesLineDisc setLineDiscount(SalesLineDisc _lineDiscount = lineDiscount) { lineDiscount = _lineDiscount; return lineDiscount; } [DataMemberAttribute("LineDiscountPercent")] public SalesLinePercent setLineDiscountPercent(SalesLinePercent _lineDiscountPercent = lineDiscountPercent) { lineDiscountPercent = _lineDiscountPercent; return lineDiscountPercent; } }

Step 3: Create the Response Data Contract Class

This class will be used to send the response back to the external application. The response includes the newly created sales order ID and any error messages or success indicators.

x++
[DataContractAttribute] class d365fo_SalesOrderResponseDTO { SalesId createdSalesOrderId; ErrorMsg errorMessage; boolean isSuccess; [DataMemberAttribute("CreatedSalesOrderId")] public SalesId setCreatedSalesOrderId(SalesId _createdSalesOrderId = createdSalesOrderId) { createdSalesOrderId = _createdSalesOrderId; return createdSalesOrderId; } [DataMemberAttribute("ErrorMessage")] public ErrorMsg setErrorMessage(ErrorMsg _errorMessage = errorMessage) { errorMessage = _errorMessage; return errorMessage; } [DataMemberAttribute("IsSuccess")] public boolean setIsSuccess(boolean _isSuccess = isSuccess) { isSuccess = _isSuccess; return isSuccess; } }

Step 4: Implement the Service Class

The service class holds the core business logic to create the sales order. It will take in the Sales Order Header DTO, validate the data, and proceed to create both the sales order header and its corresponding lines.

x++
class d365fo_SalesOrderService { ErrorMsg operationMessage; boolean isProcessed = false; SalesId generatedSalesOrderId; [ SysEntryPoint(true), AifCollectionTypeAttribute('return', Types::Class, classStr(d365fo_SalesOrderResponseDTO)) ] public d365fo_SalesOrderResponseDTO createSalesOrder(d365fo_SalesOrderHeaderDTO _salesOrderHeaderDTO) { SalesTable salesOrderHeader; d365fo_SalesOrderResponseDTO salesOrderResponse = new d365fo_SalesOrderResponseDTO(); try { ttsBegin; if (_salesOrderHeaderDTO == null) { operationMessage = "Sales order data cannot be null."; throw Exception::Error; } if (_salesOrderHeaderDTO.setCustomerAccount() == '') { operationMessage = "Customer account is missing."; throw Exception::Error; } if (_salesOrderHeaderDTO.setSalesLines().empty()) { operationMessage = "No sales order lines found."; throw Exception::Error; } salesOrderHeader = this.createSalesOrderHeader(_salesOrderHeaderDTO); if (salesOrderHeader.RecId != 0) { this.createSalesOrderLines(salesOrderHeader.SalesId, _salesOrderHeaderDTO.setSalesLines()); isProcessed = true; generatedSalesOrderId = salesOrderHeader.SalesId; operationMessage = strFmt("Sales order %1 created successfully.", generatedSalesOrderId); } else { operationMessage = "Failed to create sales order header."; throw Exception::Error; } ttsCommit; } catch (Exception::Error) { if (operationMessage == '') { operationMessage = strFmt("Error occurred while processing the order %1.", _salesOrderHeaderDTO.setExternalOrderId()); } } salesOrderResponse.setCreatedSalesOrderId(generatedSalesOrderId); salesOrderResponse.setErrorMessage(operationMessage); salesOrderResponse.setIsSuccess(isProcessed); return salesOrderResponse; } private SalesTable createSalesOrderHeader(d365fo_SalesOrderHeaderDTO _salesOrderHeaderDTO) { SalesTable salesTable; NumberSeq numSequence; SalesId newSalesOrderId; if (CustTable::find(_salesOrderHeaderDTO.setCustomerAccount()).AccountNum) { salesTable.CustAccount = _salesOrderHeaderDTO.setCustomerAccount(); } else { operationMessage = strFmt("Customer account %1 not found.", _salesOrderHeaderDTO.setCustomerAccount()); throw Exception::Error; } salesTable.initFromCustTable(); newSalesOrderId = NumberSeq::newGetNum(SalesParameters::numRefSalesId()).num(); salesTable.SalesId = newSalesOrderId; salesTable.initValue(); if (!salesTable.validateWrite()) { operationMessage = "Sales order validation failed."; throw Exception::Error; } salesTable.insert(); return salesTable; } private void createSalesOrderLines(SalesId _salesId, List _salesLinesList) { SalesLine salesLine; ListEnumerator lineEnumerator; d365fo_SalesOrderLineDTO orderLineDTO; lineEnumerator = _salesLinesList.getEnumerator(); while (lineEnumerator.moveNext()) { orderLineDTO = lineEnumerator.current(); if (InventTable::find(orderLineDTO.setProductId()).RecId != 0) { salesLine.clear(); salesLine.SalesId = _salesId; salesLine.ItemId = orderLineDTO.setProductId(); salesLine.SalesQty = orderLineDTO.setOrderQty(); salesLine.createLine(true, true, true, true, false, false, false); salesLine.SalesPrice = orderLineDTO.setUnitPrice(); salesLine.LineDisc = orderLineDTO.setLineDiscount(); salesLine.LinePercent = orderLineDTO.setLineDiscountPercent(); salesLine.update(); } else { operationMessage = strFmt("Item %1 not found.", orderLineDTO.setProductId()); throw Exception::Error; } } } }

Step 5: Expose the Service

In Visual Studio, expose this service by creating a Service Node, associating it with the appropriate class, and defining the service operations.

  1. Right-click the project and select Add > New Item.

  2. Select Service and name it d365fo_SalesOrderService.

  3. Set the class to d365fo_SalesOrderService.






Step 6: Add Service Operations





Once the service is added, right-click the Service Operations node under the service node in AOT, and add a new operation for the createSalesOrder method.


Step 7: Create Service Group and Deploy



  1. Right-click Service Groups and select New Service Group.

  2. Name the service group d365fo_SalesOrderServiceGroup and add your service to this group.


Step 8: Test the Service

You can use tools like Postman to send a request to this service and verify its functionality.


Conclusion

With these steps, you can seamlessly create a custom service in D365FO for sales order creation, facilitating integration with external systems. The process outlined here ensures error handling and validation, giving you full control over how sales orders are created within the system.

Happy coding!

Post a Comment

0 Comments