D365 and Power Platform Talks
Blog Archive
Monday, June 24, 2024
D365 F&O Build Pipeline failed after upgrade to 10.0.40
Sunday, March 26, 2023
Dual-write application configuration failed
Hello D365 Community
Point to remember - We can set up Power Platform Integration only during the deployment of the cloud-hosted environment. So, Power Platform Integration can't be set up after the developer environment is created.
For example, here is a screenshot of the Dev environment in LCS, for which I had set "Configure Power Platform" to No during the env creation. And because of this, the Dual-Write button is missing:
Ref: Enable Power Platform Integration - Finance & Operations | Dynamics 365 | Microsoft Learn
Now, let's move to main subject. I created a new Cloud hosted env with "Configure Power Platform" set to Yes in the deployment wizard. This went well and as expected I see "Setup Dual-Write application".
Now that env is created, I performed following actions in the env:
- Link it to DevOps and got latest code from Dev branch.
- Restore DB backup from our UAT/Sandbox environment.
- Compile the whole Application.
- DB sync
Then in LCS, I clicked the button "Setup Dual-Write application" and after few minutes it failed with this message.
- In the FO application, manually ran the "Refresh Entity List" (Location: Data Management workspace -> Framework parameters -> Entity List)
- The batch job failed with the error "Change Tracking is not enabled on the database: SQL Server Change Tracking"
Sunday, January 29, 2023
Search for an object in F&O VS Projects Folder
Sometimes F&O Developers may want to find the Projects in which a particular element was referred.
For example, a MenuItem/Table/Data Entity element that was not checked into the Azure DevOps source control and the original developer is not reachable immediately.
Then this tip may come in handy...
In this case, I am trying to find the Projects where the custom view "ST5CustDeliveryAddressView" is referred.
My Projects root folder:
Search for the custom view "ST5CustDeliveryAddressView", but no results.
No luck even after selecting the "File contents" in Windows
Here is the solution:
Extension for F&O Visual Studio Project file is "rnrproj":
In Windows Explorer, navigate to F&O VS Projects root folder -> Search -> Advanced Options -> Change Indexed Locations
1. Click Modify
2. add F&O VS Project folder to the existing list of Indexed Locations.
You could open "Indexing Options" tool from the Windows search bar too.
3. Click the Advanced button -> Advanced options dialog -> tab File Types ->
4. In the text box for "Add new extension to list", key-in rnrproj and click Add
6. Click OK -> Close "Indexing Options" tool
7. Wait for a min, so windows can index the Projects directory
8. Now search for the object/element and you will see the Projects that refer to the element.
Tuesday, January 10, 2023
Using Power Platform Data Integrator to integrate F&O Delivery Address Location with CE Account
In this article, we will see how to Integrate D365 F&O Customer Delivery Address Location with D365 CE Account via Power Platform's Data Integrator. This is going to be a one-way integration from F&O to CE, but you can expand the solution to be bi-directional.
Here is the recording of this Demo:
Low-Code options within the MS ecosystem for the Integration layer:
- Dual-Write
- Power Platform Data Integrator (approach used in this Demo)
- Data Events through Virtual Entity and Power Automate
In F&O Location is Global data and in CE Account is Business Unit specific, this case seems to be not a fit for the Dual-Write framework.
On trying to save the table map in Dual-Write, this is the error message I received:
Project validation failed. [DIPV1002] ST5CustDeliveryAddressEntity or AX is a cross-company entity that doesn't have primary company field set and accounts of CRM is a company-specific entity with primaryCompanyField set to msdyn_company.cdm_companycode. Please make sure the entities are both cross-company or company-specific entities.
Looks like it is a pre-requisite in Dula-Write, that either both the entities need to be Global or Legal entity specific.
So, I moved on to Power Platform's Data Integrator approach.
Power Platform's Data Integrator approach:
Environments Set-up:
- I have setup Contoso demo data in F&O
- I have setup CE Dataverse to be blank data.
- Create USMF BU in Dataverse manually (or go through Dual-Write setup which would create this for you).
- Manually Created a new Customer "San-1" in F&O
- Manually Created a new account "San-1" in CE
Security in Dataverse:
In Power Platform, make sure in each Business Unit the default Team has System Admin role. Integration layer uses the default Team of the BU in context.
At the minimum, do this for root BU and USMF BU
F&O Custom Data Entity:
In F&O, I created a custom Data Entity ST5CustDeliveryAddressEntity to expose Customer Delivery Addresses:
Data sources used:
- LogisticsPostalAddress
- LogisticsLocation
- DirPartyLocation
- CustTable
At the minimum, we would expose the following fields:
- LocationId (from LogisticsLocation)
- Description (from LogisticsLocation)
- AccountNum (from CustTable)
Range: On DirPartyLocation.IsRoleDelivery set to true
Verify the data exposed by OData API, it should render only Customer Delivery Address Locations:
https://<FO instance name>.axcloud.dynamics.com/data/ST5CustDeliveryAddresses
Now Add new Delivery Address with name "Chicago Del-3" on the Customer San-1
Verify that Delivery Address with name "Chicago Del-3" shows up in the OData API output:
https://<FO instance name>.axcloud.dynamics.com/data/ST5CustDeliveryAddresses?$filter=AccountNum%20eq%20%27San-1%27
Data Integrator Project in Power Platform Admin Portal:
In Power Platform Admin Portal -> Data Integration:
- Create a new Connection Set, to establish a connection between F&O instance and CE instance
- Here we would select the Organization to be USMF
- Create a new Project (name could be FO to CE OOB)
- Associate the Connection set created earlier
- Select the template to be "Fin and Ops to Sales"
- Select Organization to be USMF
- Click the Project created -> Add Task ->
- Task Name: FO Delivery Address to CE Account
- F&O Table: ST5CustDeliveryAddressEntity
- CE Table: Accounts
- Org: USMF
- Click the Task to add field mappings as per below screenshot
- Click Filter icon on Source, add following filter: ((ACCOUNTNO eq "San-1"))
- Save the Task and Run the Project manually
- Go to Execution History Tab, the run instance would be in status running.
- After couple of mins the status would change to Completed
Now go CE All Accounts page, we would see the new Account with name "Chicago Del-3"
Hope this blog comes in handy for the community.
In the later series, I may try to come up with a similar demo using Data Events (via Virtual Entity) approach.
References:
Data Integrator: Integrate data into Microsoft Dataverse - Power Platform | Microsoft Learn
Thursday, August 25, 2016
Catch exact error message from AX AIF service
when interacting with AX AIF services to catch the exact Error message this is what you would do in your C# Catch block
catch(FaultException<AifFault> ex)
{
InfologMessage[] infoLogList = ex.Detail.InfologMessageList;
foreach (var info in infoLogList)
{
Console.WriteLine(info.Message);
}
proxy.Abort();
Console.ReadLine();
}
Wednesday, July 27, 2016
Using Customer AIF Service in AX 2012
- Read a Customer
- Create a Customer
- Update a Customer
- Delete a Customer
- Find Customers
- FindKeys for Customers
using CustService = ConsumeAXServices.CustomerServiceRef;
Here is the C# method to Read a Customer:
public CustService.AxdCustomer CustomerRead(ref CustService.EntityKey[] entityKeyList, string accNum = "US-003" , Boolean halt = true)
{
CustService.CustomerServiceClient proxy = null;
CustService.AxdCustomer custDoc = null;
CustService.CallContext callContext = null;
try
{
custDoc = new CustService.AxdCustomer();
CustService.EntityKey[] custKeyList = new CustService.EntityKey[1];
CustService.EntityKey custKey = new CustService.EntityKey();
custKey.KeyData = new CustService.KeyField[1];
CustService.KeyField custAccountNum = new CustService.KeyField();
custAccountNum.Field = "AccountNum";
custAccountNum.Value = accNum;
custKey.KeyData[0] = custAccountNum;
custKeyList[0] = custKey;
callContext = new CustService.CallContext() { Company = "usmf" };
proxy = new CustService.CustomerServiceClient();
custDoc = proxy.read(callContext, custKeyList);
Console.WriteLine("Cust Num - " + custDoc.CustTable[0].AccountNum);
Console.WriteLine("Cust CreditStatus - " + custDoc.CustTable[0].CreditRating);
if (halt)
Console.ReadLine();
entityKeyList = custKeyList;
}
catch (Exception ex)
{
Console.WriteLine("Error - " + ex.Message);
proxy.Abort();
Console.ReadLine();
}
finally
{
if (proxy != null)
{
proxy.Close();
proxy = null;
}
}
return custDoc;
}
Here is the C# method to Create a Customer:
public void CreateCustomer(string accNum = "SanCus003", string custName = "Santosh Cust 003")
{
CustService.CustomerServiceClient proxy = null;
CustService.AxdCustomer custDoc;
CustService.CallContext callContext = null;
CustService.AxdEntity_CustTable custTable = null;
CustService.AxdEntity_DirParty_DirOrganization dirPartyOrg = null;
CustService.AxdEntity_OrganizationName orgName = null;
try
{
custDoc = new CustService.AxdCustomer();
custTable = new CustService.AxdEntity_CustTable() { AccountNum = accNum, CustGroup = "20" };
dirPartyOrg = new CustService.AxdEntity_DirParty_DirOrganization() { NumberOfEmployees = 11, NumberOfEmployeesSpecified = true };
orgName = new CustService.AxdEntity_OrganizationName { Name = custName };
dirPartyOrg.OrganizationName = new CustService.AxdEntity_OrganizationName[] { orgName };
custDoc.CustTable = new CustService.AxdEntity_CustTable[] { custTable};
custTable.DirParty = new CustService.AxdEntity_DirParty_DirPartyTable[] { dirPartyOrg };
callContext = new CustService.CallContext() { Company = "usmf" };
proxy = new CustService.CustomerServiceClient();
var entityKeyList = proxy.create(callContext, custDoc);
var keyField = entityKeyList.FirstOrDefault().KeyData.FirstOrDefault();
Console.WriteLine("Key Field - " + keyField.Field + "Key Value - " + keyField.Value);
Console.ReadLine();
}
// removed Catch and finally block to save the space
Here is the C# method to Update a Customer:
Here is the C# method to Delete a Customer (customer shouldn't have any transactions assoicated):
Here is the C# method to Find Customers based on a Criteria
public void FindCustomers(string custGrp = "10"){
CustService.CustomerServiceClient proxy = null;
CustService.CallContext callContext = null;
CustService.QueryCriteria qc = null;
CustService.AxdCustomer custDoc = null;
CustService.AxdEntity_CustTable[] custTableList = null;
try
{
CustService.CriteriaElement criteria = new CustService.CriteriaElement()
{ DataSourceName = "CustTable", FieldName = "CustGroup", Value1 = custGrp };
CustService.CriteriaElement[] criteriaList = new CustService.CriteriaElement[] { criteria };
qc = new CustService.QueryCriteria();
qc.CriteriaElement = criteriaList;
callContext = new CustService.CallContext() { Company = "usmf" };
proxy = new CustService.CustomerServiceClient();
custDoc = proxy.find(callContext, qc);
custTableList = custDoc.CustTable;
foreach (var custTable in custTableList)
{
Console.WriteLine("Customer - {0} , Group - {1}" , custTable.Name, custTable.CustGroup);
}
Console.ReadLine();
}
// removed Catch and finally block to save the space
Here is the C# method to FindKeys of Customers based on a Criteria:
public void FindKeysCustomer(string custGrp = "10"){
CustService.CustomerServiceClient proxy = null;
CustService.CallContext callContext = null;
CustService.QueryCriteria qc = null;
CustService.EntityKey[] custKeyList = null;
try
{
CustService.CriteriaElement criteria = new CustService.CriteriaElement() { DataSourceName = "CustTable", FieldName = "CustGroup", Value1 = custGrp };
CustService.CriteriaElement[] criteriaList = new CustService.CriteriaElement[] { criteria };
qc = new CustService.QueryCriteria();
qc.CriteriaElement = criteriaList;
callContext = new CustService.CallContext() { Company = "usmf" };
proxy = new CustService.CustomerServiceClient();
custKeyList = proxy.findKeys(callContext, qc);
foreach (var custKey in custKeyList)
{
CustService.KeyField keyField = custKey.KeyData.FirstOrDefault();
Console.WriteLine("Key Field - {0} , Key Value - {1}", keyField.Field, keyField.Value);
}
Console.ReadLine();
}
Saturday, July 9, 2016
AX 7 Tips
syntax: https://<aosUrl>/?mi=SysClassRunner&cls=<RunnableClassName>
Ex:
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?mi=SysClassRunner&cls=WipRunClass1
an FYI...Runnable is just a regular class with Main method (main method is the starting point)
Tip2: AX dlls needed in a C# Project:
Tip3: Not able to break into X++ code or C# code, then here is your solution:
You may need to manually load the debug Info files(.dll, .pdb, .netModule) through the VS -> Debug -> Modules window -> Search for C# namespace or X++ class name) -> Load Symbols
Tip4: you can manually attach the VS debugger to worker process : VS -> Debug -> Process -> W3Wp.exe . In the case of AX 2012 you would attach the VS debugger to "AX32Serv.exe" process to debug anything that always runs in CLR, Example: Services.