Showing posts with label Ax D365. Show all posts
Showing posts with label Ax D365. Show all posts

Saturday, April 19, 2025

Error at "using Microsoft.IdentityModel.Clients.ActiveDirectory" in D365FO update 10.0.43

 If you used Azure Active Directory Authentication Library (ADAL) in your customization, and get a compilation error at "using Microsoft.IdentityModel.Clients.ActiveDirectory" statement in 10.0.43 update, this is because ADAL is deprecated and MS have removed the reference from the AOT. 

You will need to migrate your code to Microsoft Authentication Library (MSAL).

In order to do that, you will need to add the Microsoft.Identity.Client.dll library to your bin-folder (can be found in other subfolders under K:\AosService\PackagesLocalDirectory), and add a corresponding reference to a VS project. The reference will then be created as an XML-file. Both the DLL and the XML files must be added to source control via "Add Items to Folder...", like this:

K:\AosService\PackagesLocalDirectory\<package name>\bin\Microsoft.Identity.Client.dll

K:\AosService\PackagesLocalDirectory\<package name>\<model name>\AxReference\Microsoft.Identity.Client.xml

Then, you will need to change the code.

Before:

using Microsoft.IdentityModel.Clients.ActiveDirectory;


            ClientCredential clientCrendential = new ClientCredential(clientId, clientSecret);
            AuthenticationContext authContext = new AuthenticationContext(authority);
            AuthenticationResult authResult = authContext.AcquireToken(resource, clientCrendential);
            accessToken = authResult.AccessToken;


After change, it will be like this:

using Microsoft.Identity.@Client;


            ConfidentialClientApplicationBuilder clientApplicationbuilder = ConfidentialClientApplicationBuilder::Create(clientId);

            IConfidentialClientApplication app = clientApplicationbuilder.WithClientSecret(clientSecret).WithAuthority(authority).Build();

            System.Collections.Generic.List<System.String> scopes = new System.Collections.Generic.List<System.String>();

            scopes.Add(resource + '/.default');

            System.Collections.IEnumerable enumerable = scopes;

            AcquireTokenForClientParameterBuilder parameterBuilder = app.AcquireTokenForClient(enumerable);

            var task = parameterBuilder.ExecuteAsync();

            task.Wait();

            AuthenticationResult authResult = task.Result;

            accessToken = authResult.AccessToken;



Here is the code to test the above written code and generate token using X++ job:

public static void main(Args _args)

{

    str clientId            = 'xxxxxxxxxxxxxxxxxxxx'; 
    str clientSecret     = 'xxxxxxxxxxxxxxxxxxxx'; 
    str resource          =  '<EnvironmentId>';
    str tenant             = 'xxxxxxxxxxxxxxxxxxxx';

    //Before

    //Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext context;
    //Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential credential;
    //Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result;
    //str accessToken;
    //context = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.microsoftonline.com/"+ tenant +"/oauth2/token");
    //credential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
    //result = context.AcquireToken(resource, credential);
    //accessToken = result.AccessToken;
    //info(accessToken);

    //After

    ConfidentialClientApplicationBuilder clientApplicationbuilder = ConfidentialClientApplicationBuilder::Create(clientId);
    IConfidentialClientApplication app = clientApplicationbuilder.WithClientSecret(clientSecret).WithAuthority("https://login.microsoftonline.com/"+ tenant +"/oauth2/token").Build();
    str accessToken;
    System.Collections.Generic.List<System.String> scopes = new System.Collections.Generic.List<System.String>();
    scopes.Add(resource + '/.default');
    System.Collections.IEnumerable enumerable = scopes;
    AcquireTokenForClientParameterBuilder parameterBuilder = app.AcquireTokenForClient(enumerable);
    var task = parameterBuilder.ExecuteAsync();
    task.Wait();
    AuthenticationResult authResult = task.Result;
    accessToken = authResult.AccessToken;
    info(accessToken);

}


Note: where resource is enviornment url in both cases.

Sunday, April 2, 2023

The Statistics '_dta_dams_stat_2126801566_113_114_101_126' is dependent on column 'XXXXX'

Database synchronization is a crucial aspect of any application that relies on a database for storing and retrieving data. However, there can be times when the synchronization process encounters issues, causing data to be inconsistent or lost altogether. In this article, we'll explore a common cause of database synchronization issue and how to fixed it.

Error:
Sometimes during database synchronization we faced this kind of error:





Solution:
In order to fix this error, just run the below query in SQL server. After that run DB sync again. It'll run successfully.


Enjoy :)

Sunday, July 24, 2022

Calculate MarkupTrans Amount in D365FO using X++

 static void calculateMarkupTrans(Args _args)
{
    SalesTable              salesTable = SalesTable::find('000854');
    SalesLine               salesLine;
    AmountCur               markupAmount;
    MarkupTrans             markupTrans;
    CurrencyExchangeHelper  curCurrencyExchangeHelper;
     
    while select salesLine
        where salesLine.SalesId == salesTable.SalesId
    {
        while select markupTrans
            where markupTrans.TransTableId  == salesLine.TableId
                && markupTrans.TransRecId    == salesLine.RecId
        {
            markupAmount = Markup::calcTrans(markupTrans, salesLine.SalesQty, salesLine.LineAmount);
            if (markupTrans.CurrencyCode != salesTable.CurrencyCode)
            {
                // To automatically conver amount Markup::calcMarkupAmount can be used
                curCurrencyExchangeHelper = CurrencyExchangeHelper::newExchangeDate(Ledger::primaryLedger(CompanyInfo::findDataArea(markupTrans.company()).RecId), systemDateGet());
                markupAmount = curCurrencyExchangeHelper.calculateAccountingToTransaction(salesTable.CurrencyCode, markupAmount, true);
            }
        }
    }
}

Report name in report contract cannot be null or empty.

 I ran into this issue while running the invoice journal report.



After spending sometime, I'm able to find the solution. To fix this issue just run  the following code in the runnable class:

class RunnableClass
{
    /// <summary>
    /// Run the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        PrintMgmtReportFormat                     printMgmtReportFormat;
        
        ttsbegin;
        select forupdate printMgmtReportFormat
            where printMgmtReportFormat.Name == 'ReportName.Report';
        printMgmtReportFormat.System = NoYes::Yes;
        printMgmtReportFormat.doUpdate();
        ttscommit;
    }
}

Saturday, May 21, 2022

Using Extension Chain of Command (COC) and EventHandlers in D365FO

 1. DataSource Extension class:

[ExtensionOf(formDataSourceStr(InventQualityOrderTable, InventQualityOrderLine)) ]

final class AxInventQualityOrderTableFormDataSource_Extension

{

    public void initValue()

    {

        FormDataSource formDS = this;

        InventQualityOrderLine  qualityOrderLine = formDS.cursor();

        

        //OR you can directly do this like

        InventQualityOrderLine qualityOrderLine = this.cursor();

        next initValue();

        qualityOrderLine.VariableId     = 'Test';

    }

}


2. FormDataSourceField Extension Class

[ExtensionOf(formDataFieldStr(InventQualityOrderTable, InventQualityOrderLine, TestId)) ]

final class AxInventQualityOrderTableFormDataSourceField_Extension

{

    public void modified()

    {

        FormDataObject      formDataObject = any2Object(this) as FormDataObject;    

        FormDataSource      formDS = formDataObject.datasource();

        InventQualityOrderLine  qualityOrderLine = formDS.cursor();

        //OR you can merge above two lines into single line like this;

        InventQualityOrderLine qualityOrderLine = formDataObject.datasource().cursor();

        next modified();

        // do your logic here

        qualityOrderLine.VariableId  = 'Assign value';

    }

}


3. Form Button COC Class Extension

[ExtensionOf(formControlStr(LedgerJournalTransCustPaym, ButtonSettlement))]

final class AxLedgerJournalTransCustPaymFormButton_Extension

{

    public void clicked()

    {  

        FormButtonControl       btnCtrl = any2Object(this) as FormButtonControl;

        FormDataSource          ds = btnCtrl.formRun().dataSource(tableStr(LedgerJournalTrans));

        LedgerJournalTrans      ledgerJournalTrans = ds.cursor();

        

        //do your logic here

        info(strFmt("%1", ledgerJournalTrans.Voucher));

        next clicked();

        //Here 

    }

}


4. Form Datafield OnModified EventHandler

[FormDataFieldEventHandler(formDataFieldStr(InventQualityOrderTable, InventQualityOrderLine, IncludeResults), FormDataFieldEventType::Modified)]

public static void IncludeResults_OnModified(FormDataObject sender, FormDataFieldEventArgs e)

{

    InventQualityOrderLine      qualityOrderLine = sender.datasource().cursor();

    info(strFmt("%1", qualityOrderLine.TestId));

}


5. Form control modified eventHandler

[FormControlEventHandler(formControlStr(InventQualityOrderTableCreate, InventoryDimensions_InventSiteId), FormControlEventType::Modified)]

public static void InventoryDimensions_InventSiteId_OnModified(FormControl sender, FormControlEventArgs e)

{

    FormRun                     formRun = sender.formRun();

    InventQualityOrderTable     inventQualityOrderTable   = formRun.dataSource(tableStr(InventQualityOrderTable)).cursor();

    InventDim                   inventDim                 = formRun.dataSource(tableStr(InventDim)).cursor();

    info(StrFmt("%1", inventDim.InventSiteId));


}


6. Post Event Handler 

[PostHandlerFor(tableStr(PurchTable), tableMethodStr(PurchTable, initValue))]

public static void PurchTable_Post_initValue(XppPrePostArgs args)

{

    PurchTable purchTable = args.getThis() as PurchTable;

    purchTable.test_PrintNote=NoYes::Yes;

}


7. Form DataSource OnActivated EventHandler

[FormDataSourceEventHandler(formDataSourceStr(CustTable, CustTable), FormDataSourceEventType::Activated)]

public static void CustTable_OnActivated(FormDataSource sender, FormDataSourceEventArgs e)

{

     CustTable           custTable     = sender.cursor();

     FormDataSource      custTable_ds  = sender.formRun().dataSource("CustTable");

     FormRun             element       = sender.formRun();

     FormControl         myNewButton   = element.design(0).controlName("MyNewButton");

     myNewButton.enabled(false);

}


8. Table OnInserted EventHandler

[DataEventHandler(tableStr(CustInvoiceTrans), DataEventType::Inserted)]

public static void CustInvoiceTrans_onInserted(Common sender, DataEventArgs e)

{

    CustInvoiceTrans custInvoiceTrans = sender as CustInvoiceTrans;

    SalesTable salesTable = SalesTable::find(custInvoiceTrans.SalesId);

    if(salesTable.SalesStatus == SalesStatus::Invoiced )

    {  

     //code

    }

}


9. Pre EventHandler

[PreHandlerFor(tableStr(PurchLineHistory), tableMethodStr(PurchLineHistory, update))]

public static void PurchLineHistory_Pre_update(XppPrePostArgs args)

{

    PurchLine _purchline;

    PurchLineHistory _purchLineHistory= args.getThis();

    _purchline=PurchLine::find(_purchLineHistory.PurchId,_purchLineHistory.LineNumber );

    _purchLineHistory.PriceDiscTableRefRecId =_purchline.PriceDiscTableRefRecId;

}

10. Form init Method COC

/// <summary>
/// LedgerJournalTransDaily Form Extension
/// </summary>
[ExtensionOf(formStr(LedgerJournalTransDaily))]
public final class LedgerJournalTransDaily_Extension
{
    /// <summary>
    /// Performs the basic initialization of the form.
    /// </summary>
    public void init()
    {
        FormRun formRun = this as FormRun;

        next init();
        
        //write your logic here
    }
}

SSRS report error message: The number of defined parameters is not equal to the number of cell definitions in the parameter panel.

Sometimes when we make any changes to the SSRS dataset and deploy the report. We face this kind of issue which is very common:


There are multiple solutions in order to solve this issue. Some are mentioned below:

Solution 1:
a) Create new design (Shouldn't duplicate it from existing design).
b) Copy all objects from old design and paste it in new design.
c) Save, Rebuild and deploy the report. It will work.

Solution 2:
a) Go to xml file of that report (Path : K:\AosService\PackagesLocalDirectory\ModelName\ModelName\AxReport)
b) Take a backup of that report for a safe side (optional)
c) Open that xml file in notepad (Hint: open notepad as administrator)
d) Remove "ReportParametersLayout" tag completely. See figure 2.1
e) Replace (in case of copy) or save the xml file and redeploy the report. It will work.

Figure 2.1:

Remove all the highlighted text coming inside ReportParametersLaout tag.








Happy Daxing :)

Sunday, February 6, 2022

SysOperation Framework in D365FO

The SysOperation Framework, initially called Business Operation Framework, seem to be the new substitute of the RunBase framework. As such, it allows you to perform operations that require parameters from the user, it allows you to set the operations to be executed in batch, or in a new asynchronous way, or in a synchronous manner. The great thing is that it simplifies the pack / unpack of variables that was pretty nasty in the RunBase framework, taking advantage of the Attributes feature, introduced with AX 2012.

SysOperation framework allows application logic to be written in a way that supports running operations interactively or via the Microsoft Dynamics AX batch server. It implements the MVC (Model–View–Controller) design pattern, with the isolation of Parameters (Model), Dialog (View) and Service (Controller), for the code that’s executed.

The key objects of the framework are defined below:

  • DataContract
  • UIBuilder
  • Controller
  • Service


DataContract:

The data contract is the model class in which we define which attributes we need for our operation, commonly set as parameters by the user in a dialog. It's nothing more than a model class with a few attributes in it. We can define a SysOperation Data Contract class simply by adding the DataContractAttribute attribute to its declaraion. 

Additionally, if we want a set of methods to be available to us, we can also extend the SysOperationDataContractBase base class. With this class, we can define how our basic dialog will look like to the user. 

We can define labels, groups, sizes and types of the parameters.


UIBuilder:

The UI builder class is actually an optional class for the SysOperation framework, which kind of acts as the view part of the pattern. You should only use it if you want to add some extra behavior to the dialog that AX constructs dynamically for you. If you are perfectly happy with the dialog AX shows you when you run your operation, you shouldn't worry about this class. To create a UI Builder, you should extend the SysOperationAutomaticUIBuilder class. It will provide you a set of methods to work with the dialog's design, but we usually add extra behavior or customize a lookup inside the postBuild method.


Controller:

The controller class has greater responsibility than the others. As the name suggests, it is the class that orchestrates the whole operation. The controller class also holds information about the operation, such as if it should show a progress form, if it should show the dialog, and its execution mode - asynchronous or not. To create a controller class you should extend the SysOperationServiceController, which will give you all of the methods you need.


Service:

There are some who put the business logic on controller classes so that they also perform the operation itself. I'm particularly not a big fan of that, it's too much responsibility for a single class! The programmers who created the SysOperation framework probably think the same, and they have made a way to separate the operation. You can create a service class! The only thing you have to do is extend the SysOperationServiceBase class and you're good to go. This class is the one that should contain all the business logic. When constructing your controller, you will indicate which class holds the operation that the controller will trigger, I'll demonstrate it later.

So this was a brief explanation of SysOperation framework . For more information, you can also download the official Microsoft whitepaper here.

Now, Let's have a look on sysoperation framework with the help of an example:

DataContract Class:

UI Builder:

  


Controller:


Service:


In the last, create a new action menu item that will call the controller class. So in the properties of action menu item  set the 'Object type' to be class and the 'object' to be name of the controller class. In my example, the controller class is named "DemoController".

Here is a screenshot of dialog box from the above UI Builder class defined by single and multiselect parameters.






































It's Done ☺

Friday, July 2, 2021

Access D365 FO Local VM Environment URL from your machine

Today, we will see how we can access D365FO Local VM URL from your machine.

First Find the host file of your machine from the following path: C:\Windows\System32\drivers\etc

Your host file looks like:


Lets assume your local Environment IP address is 182.10.10.17

Once you find the host file, open the file and replace the local host IP address with the local environment IP address. After that, add a line having IP address with URL of local Environment at the bottom.

Your host file looks like:


Now, open internet explorer in your machine and enter the above URL. It worked!


Note: If there is any VPN required to access your local environment then make sure your VPN is connected.


:) 


Wednesday, June 30, 2021

Error importing database: Could not read schema model header information from package. The model version '3.6' is not supported

Error:

Error importing database: Could not read schema model header information from package. The model version '3.6' is not supported.




In order to fix this issue, you need to download and install the latest Data Tier Application Framework(18.2).

(Here is the link: https://www.microsoft.com/en-us/download/confirmation.aspx?id=58207)


After installed the latest Framework, open command prompt as administrator and write the below command:

C:\Program Files\Microsoft SQL Server\150\DAC\bin>SqlPackage.exe /Action:import /Sourcefile:"C:\temp\UATDataBasebackup.bacpac" /tsn:localhost /tdn:AxDB_New /p:CommandTimeout="0"


It worked.


Friday, January 22, 2021

The required assembly 'Microsoft.Dynamics.Framework.Tools.ApplicationExplorer.dll' does not exists.

I was applying the latest Platform update to one of our internal server when I got this error.

Error:

"The required assembly 'Microsoft.Dynamics.Framework.Tools.ApplicationExplorer.dll' does not exists."




Solution:

To solve this error, just copied the dll file to the location where it popped up and that's it. Now, run the step again and the issue is resolved. 
(Hint: you can search this dll file in that drive where VS is installed).


Wednesday, November 11, 2020

D365 FO :- Unable to connect to the remote server at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy,

 I got following exception while running report in D365 FO.


Unable to connect to the remote server at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext) at Microsoft.WindowsAzure.Storage.Table.CloudTable.Exists(Boolean primaryOnly, TableRequestOptions requestOptions, OperationContext operationContext) at Microsoft.WindowsAzure.Storage.Table.CloudTable.CreateIfNotExists(TableRequestOptions requestOptions, OperationContext operationContext) at Microsoft.DynamicsOnline.Infrastructure.Components.TableAccessor.TableStorageAccessor.PerformOperation(CloudStorageAccount storageAccount, String tableName, Func`1 operation) at Microsoft.DynamicsOnline.Infrastructure.Components.TableAccessor.TableStorageAccessor.AddRecord[T](........................



After searching, I found that this exception occurs when the Store Emulator is not running. Therefore, you have to start it. Perform the following step to start Store Emulator.

  1. Open Command Prompt as administrator.
  2. Now go to "C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator> AzureStorageEmulator.exe start.




Saturday, March 28, 2020

The option 4 for querystore query_capture_mode is not supported



It is caused due to a setup mismatch between AzureSQL and SQL server. To solve this issue you have to run this SQL statement
ALTER DATABASE [QueryStoreDB] SET QUERY_STORE (QUERY_CAPTURE_MODE = ALL); in the Azure SQL (UAT) environment before you export the database.
Other solution is: 
The solution is to rename the bacpac file to zip, extract the content and locate the "model.xml" file. Copy the "model.xml" file to a new location where you can locate it easily. Rename the zip file back to bacpac. Edit the "model.xml" file:
From:
<Property Name="QueryStoreCaptureMode" Value="4" />
To:
<Property Name="QueryStoreCaptureMode" Value="2" />
Save the "model.xml" file.
Now you need to use the "/ModelFilePath" parameter of SqlPackage.exe, and point it to your updated "model.xml" file. This will instruct the SqlPackage.exe to read the updated "model.xml" file and ignore the one from within the bacpac file.
After adding /ModelFilePath parameter of SqlPackage.exe, the command will look like this: 
C:\>"Program Files (x86)\Microsoft SQL Server\140\DAC\bin\SqlPackage.exe" /Action:Import /SourceFile:"bacpacfile" /mfp:"modelfilepath" /tsn:servername /tdn:dbname /p:CommandTimeout=1200

Wednesday, March 25, 2020

Copy Data of Custom fields from PurchLine to PurchaseOrderVersions


  1. Add fields by using table extensions to the PurchLine and PurchLineHistory tables.
  2. Create a new table map containing the fields that must be copied. 
  3. Implement the new table map on the two new table extensions.
  4. Create a new class to implement the PurchLineIVersioningFieldSet interface and implement the following required methods.
    • copyVersion method - Copies data between two records of the new table map type.
    • fieldSetTableMapId method - Returns the ID of the new table map.
    • isChangeConfirmationRequired method - Returns true or false based on whether the change to the newly added field values requires a confirmation to be created.
  5. Now run build + sync.

Monday, March 23, 2020

Run db sync using cmd in ax d365

Here is the command to run db sync using cmd in ax d365. Open command prompt as administrator and write the below code.

K:\> AosService\PackagesLocalDirectory\Bin\Microsoft.Dynamics.AX.Deployment.Setup.exe  -bindir "K:AosService\PackagesLocalDirectory"  -metadatadir K:AosService\PackagesLocalDirectory -sqluser username  -sqlserver  serverName  -sqldatabase databasename -setupmode sync -syncmode fullall -isazuresql false -sqlpwd pswd