June 12, 2023

“Application specific parameters are not set correctly for the configuration” in electronic reporting - D365 FO

The issue is due to specific parameters are not configured properly as per legal entity.

This problem usually occurs when the specific parameters of a format version are “In Progress” state, while they should be in a “completed” state.

Go to “Workspace” > “Electronic reporting” and click on “Reporting configurations.”

Select your ER Format and click on Configurations > Setup.

Select your last active completed version and look at the state value.

If it’ “In progress” modify it in Completed” state

After modified Save it by pressing Save button.

June 10, 2023

Difference - Refresh, Reread, Research and Execute query

 Refresh

  • refresh () will not reread the record from the database. It basically just refreshes the screen with whatever is stored in the form cache.
Reread
  • reread () will only re-read the CURRENT record from the DB so you should not use it to refresh the form data if you have added/removed records.  It’s often used if you change some values in the current record in some code, and commit them to the database using .update() on the table, instead of through the form data source.  In this case, .reread () will make those changes appear on the form.
ReSearch
  • research() will rerun the existing form query against the data source, therefore updating the list with new/removed records as well as updating existing ones.  This will honor any existing filters and sorting on the form.
ExecuteQuery
  • executeQuery () is another useful one.  It should be used if you have modified the query in your code and need to refresh the form.  It’s like research () except it takes query changes into account.

June 09, 2023

Track user sign-in In D365FO

 Many organizations are required to maintain an audit trail of users who have used the system. This requirement can be in place for compliance reasons, or to enable trackbacks in the event of incorrect use.

In Microsoft Dynamics AX 2012, the Audit log form recorded which users accessed the Microsoft Dynamics AX environment. In Microsoft Dynamics 365 finance and operations apps, this information is captured in telemetry. IT administrators can download this information by using Microsoft Dynamics Lifecycle Services (LCS) and then move it to offline storage to maintain the audit trail of users who have signed in.

To generate an audit log of users who have used the system, follow these steps.

  1. Sign in to LCS and open the project that is associated with your implementation.
  2. Navigate to the production environment and open the Environment details page.
  3. On the Monitoring tab, select the Environment monitoring link to open the monitoring dashboard.
  4. On the Activity tab, select View raw logs.
  5. In the Query field, select User Login Events. You see a time duration that has a start date that is set to End date - 7 days.
  6. Set the end date, and then select Search. The search results that are returned include all users who signed in to the system during the seven days before the selected end date.
  7. The search results show the AADUserID value and the sign-in start and end times of the user's session. To map the AADUserID value to the user's username and email address, use the Users page (System administration > Users).
  8. To export the records and keep them for a longer period, select Export grid.

May 30, 2023

Get the List of fields and methods available on table through x++ - D365 FO

 We might get some scenarios to get the list of objects or a list of elements available in a particular object in X++. Here is a sample piece of code to retrieve the details about the table object.

Whenever we try to get the metadata of an object (Tables, Table fields, table methods) we should keep in mind to use the below namespace in our code

using Microsoft.Dynamics.AX.Metadata.MetaModel;

To get list of tables

using Microsoft.Dynamics.AX.Metadata.MetaModel;
public class TableNamesList
{
    public static void getTableNames()
    {
       var tables = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetAllTables();
        var tablesEnumr = tables.GetEnumerator();
        while (tablesEnumr.MoveNext())
        {
            info(strfmt("%1", axTable.Name));
        }
    }
}


To get list of Fields from a table

public static void getFieldNames()
    {
        AxTable table =     Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTable(tableId2Name(tablenum(SalesTable)));
       var fields = table.Fields;
        var fieldEnumerator = fields.GetEnumerator();
        while (fieldEnumerator.MoveNext())
        {
            info(strfmt("%1", field.Name));
        }
    }


To get list of methods available on a table

public static void getMethodNames()
    {
        AxTable table = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTable(tableId2Name(tablenum(SalesTable)));
        var methods = table.Methods;
        var methodEnumerator = methods.GetEnumerator();
        while (methodEnumerator.MoveNext())
        {
            AxMethod method = methodEnumerator.Current;
            if(method.IsDisplay)
            {
                info(strfmt("Display method : %1", method.Name));
            }
            else
            {
                info(strfmt("Normal method : %1", method.Name));
            }
        }
    }

Similar to the above code, we can use the metadata .dll in X++ to get all the objects and its related details available in AOT 

May 21, 2023

Changing Company Id in D365 FO

Changing the company id in D365 FO is not directly possible and also not a recommended approach. But there still is a way through which we can change the Id in rare cases. Below are the list of technical steps to be followed for changing the company id (DataAreaId).


Note: Please take the database back up before any changes to revert back in case of any issues during the process.

There are three scenarios of tables where the company Id is present.  They all need to be updated in order to bring the new Id all over the application.

1. Company Specific tables where there is a DataAreaId filed available.
  • Use the below stored procedure command in SQL to update the field in all tables with the values required.
  • Use the below SQL query to update the table "DataArea"
  • Get the list of tables using the below query

        EXEC sp_MSforeachtable 'update ? set DataAreaID = "XNEW" where ?.DataAreaID = "XOLD"

2. DataArea Table
        UPDATE DataArea SET ID = 'XNEW' WHERE DataArea.ID = 'XOLD'

3. Global tables which do not have a default framework maintained "DataAreaId" field but has a physical ordinary field called "DataArea".

  • Use the below SQL query to get the list of table having the field as DataArea

        SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
            WHERE TABLE_TYPE = 'BASE TABLE' 
            AND TABLE_NAME NOT IN (SELECT DISTINCT TABLE_NAME    
            FROM INFORMATION_SCHEMA.COLUMNS 
            WHERE COLUMN_NAME = 'DataArea');
  • Update the list of tables with the new companyId value in the fields.
        UPDATE TableName SET DataArea = 'XNEW' WHERE DataArea.ID = 'XOLD'

May 04, 2023

Direct delivery Purchase Order creation from Sales order through X++ - D365 FO

 In Some scenarios, we might have to create a direct delivery Purchase order related to the available sales orders. The below code will help us in doing it.

public void creaeDirectDeliveryPO()

{    

    TmpPurchLinePrice                  tmpPurchLinePrice;

    PurchCreateFromSalesOrder    purchCreateFromSalesOrder;

    PurchAutoCreate                       purchAutoCreate;

    VendTable                            vendTable;

    SalesLine                                   salesLinePOCreation;

    SalesTable                                 salesTablePOCreation;

    AccountNum                 vendAccountNum;

    LineNum                                   lineNumber;

    boolean                                      createDirectDelivery;

       

    vendAccountNum = "XYZ"; // Use the vendor account for which the PO should be created

    //Loop through the sales order lines for which the direct delivery Po lines to be created

    while select salesLinePOCreation

        join salesTablePOCreation

        where salesTablePOCreation.SalesId == "ABC" // Sales order number

        && salesLinePOCreation.SalesId == salesTablePOCreation.SalesId

    {

        if(!salesLinePOCreation.referencedPurchLine().RecId && vendAccountNum)

        {

            tmpPurchLinePrice.SalesId                      = salesLinePOCreation.SalesId;

            tmpPurchLinePrice.LineNum                  = lineNumber + 1;

            tmpPurchLinePrice.SalesLineRefRecId   = salesLinePOCreation.RecId;

            tmpPurchLinePrice.AccountNum            = vendAccountNum;

            tmpPurchLinePrice.ItemId                       = salesLinePOCreation.ItemId;

            tmpPurchLinePrice.InventDimId             = salesLinePOCreation.InventDimId;

            tmpPurchLinePrice.PurchQty                  = salesLinePOCreation.SalesQty;

            tmpPurchLinePrice.QtyOrdered              = salesLinePOCreation.SalesQty;

            tmpPurchLinePrice.PurchUnit                = salesLinePOCreation.SalesUnit;

            tmpPurchLinePrice.LineAmount            = salesLinePOCreation.LineAmount;

            tmpPurchLinePrice.Price                        = salesLinePOCreation.PriceUnit;

            tmpPurchLinePrice.CurrencyCode         = salesLinePOCreation.CurrencyCode;


            if(tmpPurchLinePrice.validateWrite())

            {

                tmpPurchLinePrice.insert();

                createDirectDelivery = true;

                lineNumber++;

            }

        }

    }


    if (createDirectDelivery)

    {

        ttsbegin;

        purchCreateFromSalesOrder   = PurchCreateFromSalesOrder::construct();

    

        purchCreateFromSalesOrder.parmCallerRecord(salesTablePOCreation);

        purchCreateFromSalesOrder.parmSalesTable(salesTablePOCreation);

        purchCreateFromSalesOrder.tradeLineDlvType(TradeLineDlvType::DropShip);

        purchCreateFromSalesOrder.parmTransferAddress(true);

        purchCreateFromSalesOrder.mcrDropShipment(true);


        purchAutoCreate = PurchAutoCreate::construct(tmpPurchLinePrice, purchCreateFromSalesOrder);

        purchAutoCreate.create();

        ttscommit;


        Info(strfmt("Direct delivery PO created is  %1", purchAutoCreate.purchId()));

    }

}

May 01, 2023

Use temporary Blob storage for accessing files during runtime.

 We can learn about how to store a file in the temporary blob storage, so that the file can be used during runtime process for our scenarios. I have a scenario, where I need to get the company image and use it in my process.

In order to do this, we can get the image file from DB and upload it to the temporary Blob storage. Further we can get the URL for the uploaded file and that can be used to send or download the image as required in our process.

Below code sample help us for the scenario.

Image                             logoImage;
Binary                             binary;
System.IO.Stream        imageStream;
str                                   downloadUrl;
       
logoImage = new Image();
logoImage.setData(CompanyImage::findByRecord(CompanyInfo::find()).Image);
 
binary = Binary::constructFromContainer(logoImage.getData());
 
imageStream = binary.getMemoryStream();
 
if (imageStream)
{
    str fileName        = 'Your file name'; //Fixed name or GUID can be created for dynamic name
    str contentType  = 'image/png'; // Change according to your file type
    str fileExtension = enum2Str(ImageType::PNG); // Change according to your file type
 
    FileUploadTemporaryStorageStrategy fileUploadStrategy = new FileUploadTemporaryStorageStrategy();
    FileUploadTemporaryStorageResult fileUploadResult = fileUploadStrategy.uploadFile(imageStream, fileName + fileExtension, contentType, fileExtension);
 
    if (fileUploadResult == null || !fileUploadResult.getUploadStatus())
    {
        warning("@ApplicationPlatform:FileUploadFailed");
    }
    else
    {
        downloadUrl = fileUploadResult.getDownloadUrl();
        if (downloadUrl == "")
        {
            throw Exception::Error;
        }
    }
}

The downloadUrl hold the link for the temporarily stored file path. We can use this in our next process as required.