Integrating Customer / Sales Order data with Dynamics 365 Supply Chain Management
In this article we will cover how to integrate / exchange Customers and Sales Order data in Sales and marketing module of D365 SCM, D365 Commerce and D365 F&O (Finance & Operations). Going forward in this article we will refer to all above applications collectively as D365 SCM as it applies to all these application versions.
D365 SCM offer many options to integrate / connect and exchange data with 3rd party applications. In this article we will cover CRUD (Create, Read, Update & Delete) operations using OData protocol and talk about Data Entities involved in creation of customers and sales order. In case you want to understand functionality of Customer management and Sales Order management in D365 SCM please refer to Configure and manage Sales Orders and Customers in D365 SCM.
Pre-requisites
This article assumes you already have a D365 SCM trial license and tier-1 development sandbox available.
- Acquire D365 SCM Trial License: If you are new to ISV development with D365 SCM or learning D365 SCM development. You can sign up for trial license to kick off your development journey with D365 SCM. Once you signed up for trial account you can spin off a cloud VM to host your D365 SCM sandbox following the article here. Kindly note the environment (VM) hosted on Azure will be charged to partner’s azure subscription use auto shutdown scripts to control consumption based on need.
- Have familiarity with OData data exchange concepts and familiar with nomenclature of API URIs and supported operations. The following article can serve for quick refresher on OData Capabilities of D365 Finance & Operations APIs.
- Please refer to Register a web application with AAD & this article to register an application in Azure Active Directory to allow authentication and token to exchange data between 3rd party application and D365 SCM application.
- For testing and mimicking 3rd party applications for integration. We will be using Postman with D365 SCM
Postman Variables & Parameters:
Let’s start with understanding some over the variables defined in Postman calls and their purpose.
client_id, client_Secret: It is the ID you get when registering your application in AAD during pre-requisite step 2.
tenant_id: The tenant ID of AAD on which the Application is registered as part of pre-requite step 2.
resource: The base URL of the instance without the trailing '/' e.g., https://YourInstance.sandbox.operations.dynamics.com
TokenAAD and resourceForToken: I am using to get token for authentication. TokenAAD represent https://login.microsoftonline.com/[tenant_id] and ResourceForToken is same as resource i.e., https://YourInstance.sandbox.operations.dynamics.com.
tokenSalt: It’s a token of authentication please refer to query OData by using Postman to get token or my example below (Token request & response sections) in response see value of access_token copy its value without “”.
CustID_Salt, CustName_Salt, SalesID_Salt: These are variable placeholders for Customer ID and name and Sales Order ID we will create in D365 SCM and will pass as variables in our OData requests for creation of Customers / Sales Order requests.
Token Request:
Please find below Post request to get oauth token as body we are sending grant_type, client_id, client_secret and D365 base url (as resource).
Token Response:
In response see value of access_token copy its value without “” this the bearer token value for subsequent calls.
Read Customer Request:
We will call get operation on Customers to get Customer data. I had put a filter to get just customer whose CustomerAccount = 1001 and DataAreaID=USRT. If we remove filter, it will bring all customers and if we add cross-company=true we can get across legal entities data. Please note that authorization is taking the Bearer Token from environment variables which we set based on access token Response we received in above example.
OData Call Sequence for Create Request:
For create call we are using POST function on Customers entity and passing data fields important are CustomerAccountNumber in our case its set to manual numbersequence so we have to pass the value. Also notice it is creating Customer in a specific legal entity USRT. In response you get the full Customer data as per data entity fields.
Update / Delete Requests:
In case we must update certain fields, we can use Patch operation. In the above case let us use the same newly created customer and update its name, in below example notice operation is changed from POST to PATCH and filter is applied in the request for conditional update. The request will be as follows and if we read the Customer data afterwards, we will get the updated information in the request.
In case we must delete newly created Customer we can use DELETE operation along with filter criteria as follows. Kindly note deleting will only be successful if no transactions exist on the Customer. In case it is released and if some Sales Order / trade agreements / reference transactions already created in the D365 SCM. It will not allow us to delete the Customer.
Read Sales Order & Sales Line:
For Sales Order in D365 SCM this is being managed via two data entities, SalesOrderHeaders and SalesOrderLines. In order to create a Sales Order we will first use Header entity and based on its Sales Order ID can create / read Sales lines. Let see how to read Sales Order & line data first.
Create Sales Order & Sales Line:
We can use POST operation on SalesOrderHeaders entity followed by taking the newly created Sales Order number to create lines in SalesOrderLines data entity.
Sample Sales Order Header Request:
{ "dataAreaId": "usrt", "FiscalDocumentOperationTypeId": "", "OrderTakerPersonnelNumber": "000073", "RequestedReceiptDate": "2015-01-16T12:00:00Z", "EInvoiceDimensionAccountCode": "", "IsOwnEntryCertificateIssued": "No", "CampaignId": "", "Email": "", "DefaultShippingSiteId": "EAST", "TransportationBrokerId": "", "TransportationModeId": "", "DeliveryAddressDescription": "", "CFPSCode": "", "IsSalesProcessingStopped": "No", "TMACustomerGroupId": "", "NumberSequenceGroupId": "", "SalesOrderOriginCode": "", "CustomerPaymentFinancialInterestCode": "", "TransportationTemplateId": "", "IsOneTimeCustomer": "No", "SalesOrderPoolId": "03", "DeliveryAddressCountryRegionId": "", "DeliveryAddressLatitude": 0, "TotalDiscountCustomerGroupCode": "", "DeliveryAddressCity": "", "SalesRebateCustomerGroupId": "", "ThirdPartySalesDigitalPlatformCNPJ": "", "SalesOrderPromisingMethod": "None", "ShippingCarrierId": "", "TotalDiscountPercentage": 0, "DeliveryAddressDistrictName": "", "DeliveryAddressCountyId": "", "ConfirmedReceiptDate": "2015-01-16T12:00:00Z", "DeliveryAddressZipCode": "", "FiscalOperationPresenceType": "DoesNotApply", "QuotationNumber": "", "IsConsolidatedInvoiceTarget": "No", "LanguageId": "en-us", "DeliveryAddressDunsNumber": "", "MultilineDiscountCustomerGroupCode": "", "CustomerPaymentMethodSpecificationName": "", "ServiceFiscalInformationCode": "", "CommissionCustomerGroupId": "", "DeliveryAddressName": "{{CustName_Salt}}", "PaymentTermsBaseDate": "1900-01-01T12:00:00Z", "DeliveryAddressStreetNumber": "", "CreditNoteReasonCode": "", "ChargeCustomerGroupId": "", "TaxExemptNumber": "", "IsDeliveryAddressPrivate": "No", "CustomersOrderReference": "", "ExportReason": "", "OrderResponsiblePersonnelNumber": "", "DeliveryAddressCountryRegionISOCode": "", "CashDiscountCode": "", "PaymentScheduleName": "", "IntrastatTransactionCode": "", "URL": "", "CurrencyCode": "USD", "InvoiceType": "Invoice", "ArePricesIncludingSalesTax": "No", "InvoiceCustomerAccountNumber": "{{CustID_Salt}}", "DeliveryAddressLocationId": "", "CustomerTransactionSettlementType": "None", "CommissionSalesRepresentativeGroupId": "", "WillAutomaticInventoryReservationConsiderBatchAttributes": "No", "IntrastatStatisticsProcedureCode": "", "IsEInvoiceDimensionAccountCodeSpecifiedPerLine": "No", "DeliveryAddressStreet": "", "DeliveryModeCode": "", "IsExportSale": "No", "ConfirmedShippingDate": "2015-01-16T12:00:00Z", "FixedDueDate": "1900-01-01T12:00:00Z", "SalesTaxGroupCode": "WA", "IsDeliveryAddressOrderSpecific": "No", "CustomerRequisitionNumber": "", "FiscalDocumentTypeId": "", "IsFinalUser": "No", "ShippingCarrierServiceGroupId": "", "ContactPersonId": "", "EUSalesListCode": "IncludeNot", "ThirdPartySalesDigitalPlatform": "No", "PaymentTermsName": "", "CustomerPostingProfileId": "GEN", "DeliveryTermsCode": "", "ShippingCarrierServiceId": "", "DefaultLedgerDimensionDisplayValue": "-015-033", "DeliveryAddressTimeZone": null, "SalesOrderName": "{{CustName_Salt}}", "DefaultShippingWarehouseId": "ANNAPOL", "IsEntryCertificateRequired": "No", "DeliveryAddressStateId": "", "DeliveryBuildingCompliment": "", "IntrastatTransportModeCode": "", "InvoicePaymentAttachmentType": "None", "DeliveryAddressPostBox": "", "InvoiceAddressTimeZone": null, "DirectDebitMandateId": "", "LineDiscountCustomerGroupCode": "", "IntrastatPortId": "", "OrderingCustomerAccountNumber": "{{CustID_Salt}}", "CustomerPaymentFineCode": "", "PriceCustomerGroupCode": "", "DeliveryReasonCode": "", "IsServiceDeliveryAddressBased": "No", "InventoryReservationMethod": "Automatic", "RequestedShippingDate": "2015-01-16T12:00:00Z", "TransportationRoutePlanId": "", "CustomerPaymentMethodName": "", "SalesUnitId": "", "FixedExchangeRate": 0 }
Sales Line Request & Response
Once we have created the Sales header now, we can create corresponding Sales lines. In the example below we are creating two Sales lines.
- The first Sales line is for product master, item number 9x002 with Color Red, Size 32 and Style Regular.
- The second Sales line is for product, item number px005.
We can create Sales line 1 by 1, however we are using POST operation in batch multiple lines in same call. Notice in below request the use is changed to batch operation as well the content type is changed from application/json to multipart/mixed;boundary=batch_xxxx-xxxx-xxxx (where xxxx can be any unique identifier)
Sample Sales Line Request:
--batch_d365-1111-2222 Content-Type: multipart/mixed; boundary=changeset_d365-2222-3333 --changeset_d365-2222-3333 Content-Type: application/http Content-Transfer-Encoding: binary POST SalesOrderLines HTTP/1.1 Content-ID: 1 Accept: application/json;q=0.9, */*;q=0.1 OData-Version: 4.0 Content-Type: application/json OData-MaxVersion: 4.0 { "RequestedReceiptDate": "2015-01-16T12:00:00Z", "RetailCalculatedTotalDiscountPercentage": 0, "EInvoiceDimensionAccountCode": "", "FormattedDelveryAddress": "", "ShippingWarehouseLocationId": "", "ProjectCategoryId": "", "ItemNumber": "9x002", "DeliveryAddressDescription": "", "MultilineDiscountPercentage": 0, "ShippingWarehouseId": "ANNAPOL", "GiftCardBuyerName": "", "GiftCardNumber": "", "CustomsDocumentNumber": "", "LineDiscountAmount": 0, "ProductStyleId": "Regular", "ProductConfigurationId": "", "DeliverySalesTaxGroupCode": "", "MultilineDiscountAmount": 0, "ItemBatchNumber": "", "DeliveryAddressCountryRegionId": "", "DeliveryAddressLatitude": 0, "IsPriceLocked": "No", "DeliveryAddressCity": "", "SalesProductCategoryName": "", "SalesOrderPromisingMethod": "None", "ProjectId": "", "ProjectLinePropertyId": "", "SalesRebateProductGroupId": "", "DeliveryAddressDistrictName": "", "DeliveryAddressCountyId": "", "SalesUnitSymbol": "Ea", "ItemWithholdingTaxCodeGroupCode": "", "ConfirmedReceiptDate": "2015-01-16T12:00:00Z", "ProductSizeId": "32", "FixedPriceCharges": 0, "DeliveryAddressZipCode": "", "DeliveryAddressDunsNumber": "", "DeliveryTermsId": "", "ServiceFiscalInformationCode": "", "RetailCalculatedTotalDiscountAmount": 0, "DeliveryAddressName": "Default Retail Customer", "PackingUnitSymbol": "", "OrderedSalesQuantity": 1, "BOMId": "", "DeliveryAddressStreetNumber": "", "CreditNoteReasonCode": "", "DeliveryAddressStreetInKana": "", "ShippingSiteId": "EAST", "NGPCode": 0, "RetailCalculatedLineDiscountAmount": 0, "IsDeliveryAddressPrivate": "No", "LineCreationSequenceNumber": 1, "RetailCalculatedTenderDiscountPercentage": 0, "MainAccountIdDisplayValue": "", "OrderedInventoryStatusId": "", "GiftCardRecipientEmail": "", "DeliveryAddressCountryRegionISOCode": "", "ItemSerialNumber": "", "RetailCalculatedManualLineDiscountPercentage": 0, "CalculateLineAmount": "Yes", "RetailCalculatedPeriodicDiscountAmount": 0, "CurrencyCode": "USD", "DeliveryAddressLocationId": "", "SalesTaxItemGroupCode": "RP", "RouteId": "", "EInvoicePropertyNumber": "", "ProductVersionId": "", "CommissionSalesRepresentativeGroupId": "", "CustomsDocumentName": "", "WillAutomaticInventoryReservationConsiderBatchAttributes": "No", "OverrideSalesTax": "No", "LineDescription": "Signature BMX Gloves", "DeliveryAddressStreet": "", "DeliveryModeCode": "", "ConfirmedShippingDate": "2015-01-16T12:00:00Z", "RetailCalculatedLineDiscountPercentage": 0, "SalesTaxGroupCode": "MD", "IsDeliveryAddressOrderSpecific": "No", "CustomerRequisitionNumber": "", "SalesOrderNumber": "{{SalesID_Salt}}", "PlanningPriority": 0, "FiscalDocumentTypeId": "", "DeliveryCFOPCode": "", "LineDiscountPercentage": 0, "SalesPrice": 14.99, "GiftCardGiftMessage": "", "SalesOrderLineCreationMethod": "SalesOrder", "FulfillmentStatus": "Unknown", "OrderedCatchWeightQuantity": 0, "RetailCalculatedPeriodicDiscountPercentage": 0, "OrderLineReference": "", "AllowedUnderdeliveryPercentage": 0, "DeliveryAddressCityInKana": "", "AllowedOverdeliveryPercentage": 0, "DeliveryAddressLongitude": 0, "DefaultLedgerDimensionDisplayValue": "006-015-033", "DeliveryAddressTimeZone": null, "ProductColorId": "Red", "GiftCardRecipientName": "", "CFOPCode": "", "DeliveryAddressStateId": "", "DeliverySalesTaxItemGroupCode": "", "DeliveryBuildingCompliment": "", "DeliveryAddressPostBox": "", "CustomsDocumentDate": "1900-01-01T12:00:00Z", "CustomersLineNumber": 0, "GiftCardType": "Email", "LineAmount": 15.89, "SalesPriceQuantity": 1, "WillRebateCalculationExcludeLine": "No", "RetailCalculatedTenderDiscountAmount": 0, "SuframaDiscountPercentage": 0, "InventoryReservationMethod": "Automatic", "RequestedShippingDate": "2015-01-16T12:00:00Z", "RetailCalculatedManualLineDiscountAmount": 0, "WithholdingTaxGroupCode": "", "FulfillmentStoreId": "", "SkipCreateAutoCharges": "Yes", "ExternalItemNumber": "", "IsLineStopped": "No", "GiftCardBuyerEmail": "", "RevRecRevenueScheduleId": "", "RevRecSalesDeliverNow": 0, "RevRecContractEndDate": "1900-01-01T12:00:00Z", "RevRecContractStartDate": "1900-01-01T12:00:00Z", "SubBillRevenueSplit": "No" } --changeset_d365-2222-3333-- --batch_d365-1111-2222 Content-Type: multipart/mixed; boundary=changeset_d365-3333-4444 --changeset_d365-3333-4444 Content-Type: application/http Content-Transfer-Encoding: binary POST SalesOrderLines HTTP/1.1 Content-ID: 2 Accept: application/json;q=0.9, */*;q=0.1 OData-Version: 4.0 Content-Type: application/json OData-MaxVersion: 4.0 { "RequestedReceiptDate": "2015-01-16T12:00:00Z", "RetailCalculatedTotalDiscountPercentage": 0, "EInvoiceDimensionAccountCode": "", "FormattedDelveryAddress": "", "ShippingWarehouseLocationId": "", "ProjectCategoryId": "", "ItemNumber": "PX005", "DeliveryAddressDescription": "", "MultilineDiscountPercentage": 0, "ShippingWarehouseId": "ANNAPOL", "GiftCardBuyerName": "", "GiftCardNumber": "", "CustomsDocumentNumber": "", "LineDiscountAmount": 0, "ProductStyleId": "", "ProductConfigurationId": "", "DeliverySalesTaxGroupCode": "", "MultilineDiscountAmount": 0, "ItemBatchNumber": "", "DeliveryAddressCountryRegionId": "", "DeliveryAddressLatitude": 0, "IsPriceLocked": "No", "DeliveryAddressCity": "", "SalesProductCategoryName": "", "SalesOrderPromisingMethod": "None", "ProjectId": "", "ProjectLinePropertyId": "", "SalesRebateProductGroupId": "", "DeliveryAddressDistrictName": "", "DeliveryAddressCountyId": "", "SalesUnitSymbol": "Ea", "ItemWithholdingTaxCodeGroupCode": "", "ConfirmedReceiptDate": "2015-01-16T12:00:00Z", "ProductSizeId": "", "FixedPriceCharges": 0, "DeliveryAddressZipCode": "", "DeliveryAddressDunsNumber": "", "DeliveryTermsId": "", "ServiceFiscalInformationCode": "", "RetailCalculatedTotalDiscountAmount": 0, "DeliveryAddressName": "Default Retail Customer", "PackingUnitSymbol": "", "OrderedSalesQuantity": 1, "BOMId": "", "DeliveryAddressStreetNumber": "", "CreditNoteReasonCode": "", "DeliveryAddressStreetInKana": "", "ShippingSiteId": "EAST", "NGPCode": 0, "RetailCalculatedLineDiscountAmount": 0, "IsDeliveryAddressPrivate": "No", "LineCreationSequenceNumber": 1, "RetailCalculatedTenderDiscountPercentage": 0, "MainAccountIdDisplayValue": "", "OrderedInventoryStatusId": "", "GiftCardRecipientEmail": "", "DeliveryAddressCountryRegionISOCode": "", "ItemSerialNumber": "", "RetailCalculatedManualLineDiscountPercentage": 0, "CalculateLineAmount": "Yes", "RetailCalculatedPeriodicDiscountAmount": 0, "CurrencyCode": "USD", "DeliveryAddressLocationId": "", "SalesTaxItemGroupCode": "RP", "RouteId": "", "EInvoicePropertyNumber": "", "ProductVersionId": "", "CommissionSalesRepresentativeGroupId": "", "CustomsDocumentName": "", "WillAutomaticInventoryReservationConsiderBatchAttributes": "No", "OverrideSalesTax": "No", "LineDescription": "Signature BMX Gloves", "DeliveryAddressStreet": "", "DeliveryModeCode": "", "ConfirmedShippingDate": "2015-01-16T12:00:00Z", "RetailCalculatedLineDiscountPercentage": 0, "SalesTaxGroupCode": "MD", "IsDeliveryAddressOrderSpecific": "No", "CustomerRequisitionNumber": "", "SalesOrderNumber": "{{SalesID_Salt}}", "PlanningPriority": 0, "FiscalDocumentTypeId": "", "DeliveryCFOPCode": "", "LineDiscountPercentage": 0, "SalesPrice": 14.99, "GiftCardGiftMessage": "", "SalesOrderLineCreationMethod": "SalesOrder", "FulfillmentStatus": "Unknown", "OrderedCatchWeightQuantity": 0, "RetailCalculatedPeriodicDiscountPercentage": 0, "OrderLineReference": "", "AllowedUnderdeliveryPercentage": 0, "DeliveryAddressCityInKana": "", "AllowedOverdeliveryPercentage": 0, "DeliveryAddressLongitude": 0, "DefaultLedgerDimensionDisplayValue": "006-015-033", "DeliveryAddressTimeZone": null, "ProductColorId": "", "GiftCardRecipientName": "", "CFOPCode": "", "DeliveryAddressStateId": "", "DeliverySalesTaxItemGroupCode": "", "DeliveryBuildingCompliment": "", "DeliveryAddressPostBox": "", "CustomsDocumentDate": "1900-01-01T12:00:00Z", "CustomersLineNumber": 0, "GiftCardType": "Email", "LineAmount": 15.89, "SalesPriceQuantity": 1, "WillRebateCalculationExcludeLine": "No", "RetailCalculatedTenderDiscountAmount": 0, "SuframaDiscountPercentage": 0, "InventoryReservationMethod": "Automatic", "RequestedShippingDate": "2015-01-16T12:00:00Z", "RetailCalculatedManualLineDiscountAmount": 0, "WithholdingTaxGroupCode": "", "FulfillmentStoreId": "", "SkipCreateAutoCharges": "Yes", "ExternalItemNumber": "", "IsLineStopped": "No", "GiftCardBuyerEmail": "", "RevRecRevenueScheduleId": "", "RevRecSalesDeliverNow": 0, "RevRecContractEndDate": "1900-01-01T12:00:00Z", "RevRecContractStartDate": "1900-01-01T12:00:00Z", "SubBillRevenueSplit": "No" } --changeset_d365-3333-4444--
Conclusion:
In this post we have seen how to create Customer and then use it in Sales Order in D365 SCM. We have also seen how to update (Patch) and Delete using OData operations. The request formats / fields can be used from any 3rd party application to communicate and exchange data with Sales & marketing module of D365 SCM.
Full Blog Series Links:
- Integrating Products / Product Master data with Dynamics 365 Supply Chain Management
- Integrating Vendors / Purchase Order data with Dynamics 365 Supply Chain Management
- Integrating Customer / Sales Order data with Dynamics 365 Supply Chain Management
- Generate CUD Data Events in D365 Finance & Operations and Capture in 3rd Party Applications
*This post is locked for comments