Dynamics 365 finance for operation
Integration development
Thanks anh An.Bui
Nghia Song - Microsoft Dynamics 365 Technical Consultant
Nghia Song
Tel - WhatsApp: +84967324794
Email: songnghia.uit@gmail.com
Contents
I. Prepare (Configuration) for 3rd Project to integrate with D365 FO: 3
II. Prepare (Odata Client Schema) or 3rd Project to integrate with D365 FO 10
III. Sample OdataConsoleApplication 14
IV. Sample customize SOAP Service to check Item On-hand 15
V. Sample Bring your own database (BYOD) 30
D365 FO:
Topic | Register application in Azure |
|
Step | Screenshot | Description |
1 |
| Registration App ID in Azure Go to: https://portal.azure.com/ Login with your email account |
2 |
| Select your subscription |
3 |
| Find Azure Active Directory |
4 |
|
|
5 |
| Input name of your application |
6 |
| Remember Application (Client) Id and Directory (Tenant) Id |
7 |
| Click hyperlink Redirect URL |
8 |
| Input your Dynamics 365FO URL + "/data" |
9 |
| Create client secret key |
10 |
| Copy Client secret key |
11 |
| Set API permission |
12 |
| Select Delegate permission |
13 |
| Select permission |
Topic | Clone and Test with Dynamics D365 FO Integration sample code from Microsoft github |
|
14 |
| |
15 |
| Open solution ServiceSamples.sln |
16 |
| Change client configuration with your app id and your D365 FO
Project: AuthenticationUtility Class: ClientConfigration.cs |
17 |
| Update ActiveDirectoryClientAppSecret (get from Azure) |
18 |
| Update your D365 FO URL |
19 |
| Update your tenant name after : https://login.windows.net/……… |
20 |
| Update Application (Client) Id from Azure |
Prepare (Odata Client Schema) or 3rd Project to integrate with D365 FO
Topic | Generate Odata client schema |
|
Step | Screenshot | Description |
1 |
| Download at: https://github.com/microsoft/Dynamics-AX-Integration
Project: ODataUtility File: ODataClient.tt
Update variable MetadataDocumentUri with your D365 FO, ending with: /data/$metadata
Example: MetadataDocumentUri = "https://rd0111796afae4e7df37devaos.cloudax.dynamics.com/data/$metadata" |
2 |
| Ctrl-S to save file Run Custom tool to update latest schema Data entity from your D365 FO |
3 |
| Build and get error
Severity Code Description Project File Line Suppression State Error CS8103 Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals. ODataUtility D:\Dynamics-AX-Integration-master\ServiceSamples\ODataUtility\CSC 1 Active
|
Topic | Fix error: Combined length of user strings used by the program exceeds allowed limit |
|
4 |
| Mở file ODataClient.cs của Project OdataUtility bằng Notepad++ Kiếm tá»›i dòng code: private const string Edmx = @"<……>" Cắt hết tất cả ná»™i dung XML ra 1 file riêng |
5 |
| Paste ra file notepad++, và lưu thà nh edmx.xml, replace 2 dấu nháy kép thà nh 1 dấu nháy kép, lưu file lại ngay folder của project OdataUtility và ODataConsoleApplication |
6 |
| Lưu file edmx.xml lại file và o project ODataUtility: |
7 |
| Lưu file edmx.xml lại file và o project ODataConsoleApplication, path: D:\Dynamics-AX-Integration-master\ServiceSamples\ODataConsoleApplication\bin\Debug |
8 |
| Gán tên file edmx.xml và o biến string Edmx: |
9 |
|
Modify OdataClient.cs //Create new method CreateXmlReader without Parameters [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "7.5.1")] private static global::System.Xml.XmlReader CreateXmlReader() { return global::System.Xml.XmlReader.Create(new global::System.IO.StreamReader(Edmx)); } |
10 |
| Update LoadModelFromString to call the new overload of CreateXmlReader() so it loads the Edmx from the XML file |
11 |
| Rebuild OdataUtility project |
Sample OdataConsoleApplication
Topic | Review sample OdataConsoleApplication |
|
Step | Screenshot | Description |
1 |
| Find project: ODataConsoleApplication |
2 |
| Set Startup project |
3 |
| Start and Debug ODATAConsoleApplication sample |
Sample customize SOAP Service to check Item On-hand
Topic | Prepare service in AOT |
|
Step | Screenshot | Description |
1 |
[DataContractAttribute] public class VtvItemOnhandContract { ItemId _itemId; InventLocationId _inventLocationId; WMSLocationId _wmsLocationId; Qty _qty; UnitOfMeasureSymbol _inventUnitId;
[DataMemberAttribute] public ItemId ItemId(ItemId itemId = _itemId) { _itemId = itemId; return _itemId; }
[DataMemberAttribute] public InventLocationId WarehouseId(InventLocationId inventLocationId = _inventLocationId) { _inventLocationId = inventLocationId; return _inventLocationId; }
[DataMemberAttribute] public WMSLocationId LocationId(WMSLocationId wmsLocationId = _wmsLocationId) { _wmsLocationId = wmsLocationId; return _wmsLocationId; }
[DataMemberAttribute] public Qty OnHandQty(Qty qty = _qty) { _qty = qty; return _qty; }
[DataMemberAttribute] public UnitOfMeasureSymbol InventUnitId(UnitOfMeasureSymbol inventUnitId = _inventUnitId) { _inventUnitId = inventUnitId; return _inventUnitId; }
} | Create new data contract: VtvItemOnhandContract
|
2 | public class VtvInventoryCustomServicesClass { [ AifCollectionTypeAttribute('return', Types::Class, classStr(VtvItemOnhandContract)), AifCollectionTypeAttribute('itemOnhandContractRequestList',Types::Class,classStr(VtvItemOnhandContract)) ] public List getItemOnhand(List itemOnhandContractRequestList) { ListEnumerator listEnum = itemOnhandContractRequestList.getEnumerator(); List resultList = new List(Types::Class); VtvItemOnhandContract itemOnhandContract; VtvItemOnhandContract itemOnhandContractReturn;
Qty qty; InventDim inventDim; InventDimParm inventDimParm; InventOnhand inventOnhand; ItemId itemId; InventLocationId inventLocationId; WMSLocationId wmsLocationId; UnitOfMeasureSymbol inventUnitId;
; while (listEnum.moveNext()) { itemOnhandContract = listEnum.current();
itemId = itemOnhandContract.ItemId(); inventLocationId = itemOnhandContract.WarehouseId(); wmsLocationId = itemOnhandContract.LocationId();
if(itemId && inventLocationId) { inventDim.clear(); inventDim.InventLocationId = inventLocationId; inventDim.InventSiteId = InventLocation::find(inventLocationId).InventSiteId; inventDim.wMSLocationId = wmsLocationId;
inventDimParm.clear(); inventDimParm.initFromInventDim(inventDim);
inventOnhand = InventOnhand::newParameters(itemId, inventDim, inventDimParm); qty = inventOnhand.availPhysical(); inventUnitId = InventTable::find(itemId).inventUnitId();
itemOnhandContractReturn = new VtvItemOnhandContract(); itemOnhandContractReturn.ItemId(itemId); itemOnhandContractReturn.LocationId(wmsLocationId); itemOnhandContractReturn.WarehouseId(inventLocationId); itemOnhandContractReturn.InventUnitId(inventUnitId); itemOnhandContractReturn.OnHandQty(qty);
resultList.addEnd(itemOnhandContractReturn); } } return resultList;
}
} | Create new service class: VtvInventoryCustomServicesClass
|
3 |
| Add new service: VtvInventoryCustomServices |
4 |
| Set Properties for Service: Class: VtvInventoryCustomServicesClass Description: VtvInventoryCustomServices External name: VtvInventoryCustomServices
Namespace: http://schemas.microsoft.com/dynamics/2011/01/services
|
5 |
| New Operation for Service |
6 |
| Set Properties for new operation |
7 |
| Add new service group: VtvInventoryCustomServicesGroup |
8 |
| Set properties for new service group: Auto deploy: YES |
9 |
| Add new service into service group |
10 |
| Set properties for service in service group |
11 |
| Full build your model |
12 |
| Restart service: World Wide Web Publishing Service |
13 |
| Testing open SOAP WSDL in Browser at Local server Example link: https://rd0111796afae4e7df37devaos.cloudax.dynamics.com/soap/services/VtvInventoryCustomServicesGroup?wsdl
If result look like screenshot, your service is okie at local server |
14 |
| Testing open SOAP WSDL in Browser at another computer internet Example link: https://rd0111796afae4e7df37devaos.cloudax.dynamics.com/soap/services/VtvInventoryCustomServicesGroup?wsdl
If result look like screenshot, your service is okie at internet |
Topic | Use C# Client connect to SOAP Service to query Item Onhand |
|
| Note: Need to set connection configuration in sample of Microsoft first ! |
|
15 |
| Use sample from Microsoft, download at: https://github.com/microsoft/Dynamics-AX-Integration
Project: SoapUtility |
16 |
| Add new service references
|
17 |
| Example Link: https://rd0111796afae4e7df37devaos.cloudax.dynamics.com/soap/services/VtvInventoryCustomServicesGroup?wsdl
Set name of Namespace, ex: InventoryCustomService |
18 |
| Rebuild project SoapUtility |
19 |
| Find project: SoapConsoleApplication |
20 |
| Set as Startup Project for SoapConsoleApplication |
21 | using AuthenticationUtility; using SoapUtility.UserSessionServiceReference; using System; using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; using SoapUtility.InventoryCustomServices;
namespace SoapConsoleApplication { class Program { [STAThread] static void Main(string[] args) { string ServiceGroupName = "";
//InventoryOnhandService #region InventoryOnhandService //Connection ServiceGroupName = "VtvInventoryCustomServicesGroup"; var aosUriString = ClientConfiguration.Default.UriString;
var oauthHeader = OAuthHelper.GetAuthenticationHeader(true); var serviceUriString = SoapUtility.SoapHelper.GetSoapServiceUriString(ServiceGroupName, aosUriString);
var endpointAddress = new System.ServiceModel.EndpointAddress(serviceUriString); var binding = SoapUtility.SoapHelper.GetBinding();
var client = new SoapUtility.InventoryCustomServices.VtvInventoryCustomServicesClient(binding, endpointAddress); var channel = client.InnerChannel;
//Call context SoapUtility.InventoryCustomServices.CallContext callContext = new SoapUtility.InventoryCustomServices.CallContext(); callContext.Company = "USMF";
//Request parameters List<SoapUtility.InventoryCustomServices.VtvItemOnhandContract> itemOnhandRequestList = new List<SoapUtility.InventoryCustomServices.VtvItemOnhandContract>(); //List of requests SoapUtility.InventoryCustomServices.VtvItemOnhandContract itemOnhandContract;
itemOnhandContract = new SoapUtility.InventoryCustomServices.VtvItemOnhandContract(); itemOnhandContract.ItemId = "A0001"; itemOnhandContract.WarehouseId = "24"; itemOnhandRequestList.Add(itemOnhandContract);
itemOnhandContract = new SoapUtility.InventoryCustomServices.VtvItemOnhandContract(); itemOnhandContract.ItemId = "A0002"; itemOnhandContract.WarehouseId = "24"; itemOnhandRequestList.Add(itemOnhandContract);
using (OperationContextScope operationContextScope = new OperationContextScope(channel)) { HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers[OAuthHelper.OAuthHeader] = oauthHeader; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
SoapUtility.InventoryCustomServices.VtvItemOnhandContract[] itemOnhandArrayRequest = itemOnhandRequestList.ToArray(); SoapUtility.InventoryCustomServices.getItemOnhandResponse itemOnhandResponse = ((SoapUtility.InventoryCustomServices.VtvInventoryCustomServices)channel).getItemOnhand( new SoapUtility.InventoryCustomServices.getItemOnhand(callContext, itemOnhandArrayRequest));
SoapUtility.InventoryCustomServices.VtvItemOnhandContract[] itemOnhandArrayResponse = itemOnhandResponse.result;
foreach(VtvItemOnhandContract itemOnhand in itemOnhandArrayResponse) { Console.WriteLine(); Console.WriteLine("ItemId: {0}, Inventory UnitId: {1}, Warehouse: {2}, Location: {3}, Available On-hand: {4}", itemOnhand.ItemId, itemOnhand.InventUnitId, itemOnhand.WarehouseId, itemOnhand.LocationId, itemOnhand.OnHandQty);
} } Console.ReadLine();
#endregion } } }
| Write some code as below.
Note important code: var oauthHeader = OAuthHelper.GetAuthenticationHeader(true);
Must to set True in method: GetAuthenticationHeader(true) |
22 |
|
|
Sample Bring your own database (BYOD)
Topic | Create new SQL Azure Database |
|
Step | Screenshot | Description |
1 |
| Go to SQL Database in Azure |
2 |
| Create new SQL Database Note: Location: Southest Asia Copy note: Server admin login, Password to use later |
3 |
| Change Configure database to Basic 2GB Storage to save money :D Click Review + Create |
4 |
| Click Create |
Topic | Configure Firewall of SQL Azure database to allow Dynamics D365 FO to connect to SQL |
|
5 |
| Go to SQL database |
6 |
| Click to SQL database |
7 |
| Overview -> Set server firewall |
8 |
How to find IP of VM D365 FO ?
| Create new Rule to access to IP address of VM D365 FO |
Topic | Configure bring your own database in D365 |
|
9 |
| Go to System Administration -> Data Management
Click Configure entity export to database |
10 |
How to find connection string of SQL Azure database:
| Create new one Connection to your SQL Azure Database Note: -ConnectionString: Copy from SQL Azure Portal, remember to enter your password to "Password=xxx" |
11 |
| Validate connection and get message Tests completed successfully |
Topic | Publish data entity ready to export |
|
12 |
| In "Configure entity export to database" Click Publish |
13 |
| Example: find entity: Customers V3 Change tracking -> Enable entire entity
|
14 |
| Publish entity Customers V3 |
Topic | Create Export Job to export Customers V3 to SQL Azure database |
|
15 |
| Go to Export in Data management |
16 |
| Create new one Export job Data entity: Customers V3 Target data format: <Your own database> |
17 |
| Click Export |
18 |
| Export job is running |
19 |
| Manage export job
Click Execution details |
20 |
| Export job run succeed |
Topic | Check data in SQL Azure database |
|
21 |
| Use SQL Server Management Studio Connect to SQL Azure database with user and pass |
22 |
| Sign in with email account |
23 |
| Input Email account |
24 |
| Click OK |
25 |
| You can see tables which were publised in D365 FO |
26 | [Optional]
| If you get this error , you need to set firewall rule for new IP address access to SQL Azure database |
27 |
| Configure firewall of SQL Azure database to allow new IP Address |
No comments:
Post a Comment