Skip to main content

🚀 How to Dynamically Copy Matching Fields Between Tables in X++ using DictTable

 In Microsoft Dynamics AX (X++), there are many scenarios where you need to duplicate data from one table to another—especially during custom import, duplication, or versioning processes. Traditionally, this is done by manually assigning each field: targetTable.Field1 = sourceTable.Field1; targetTable.Field2 = sourceTable.Field2; ... But what if the tables have many fields ? Or maybe you’re dealing with multiple similar table pairs? That’s where the powerful DictTable class comes in. Let’s walk through how to use it to copy matching fields dynamically between two tables. 💡 Use Case: Copy Customer Templates Let’s assume you have these tables: CustTemplateHeader – stores predefined customer templates. CustTemplateHeaderHistory – a historical copy of templates for versioning. You want to copy records from CustTemplateHeader to CustTemplateHeaderHistory , but only for fields that exist in both tables. ✅ The Solution Using DictTable SalesHeaderTemplate   ...

Customizing Date Lookup to Use the Persian Calendar in D365FO (X++)

Introduction

Dynamics 365 Finance and Operations (D365FO) traditionally uses the Gregorian calendar for date selections. If your organization requires the Persian calendar, you can customize the default date picker form, SysDateLookUp, to accommodate this need. This blog provides a detailed guide on how to achieve this, including the creation of necessary classes and methods for date conversion.

Step-by-Step Guide

1. Duplicate the SysDateLookUp Form

To begin, you need to create a copy of the existing SysDateLookUp form, which will be customized to use the Persian calendar.

  • Action: In the Application Object Tree (AOT), locate the SysDateLookUp form, right-click it, and select "Duplicate."
  • Name the new form: PersianDateLookup

2. Configure the Persian Calendar

  1. Open the Form:

    • Navigate to the AOT, find the duplicated PersianDateLookup form, and open it.
  2. Modify the Init Method:

    • Open the init method of the PersianDateLookup form and set the calendar type to Persian.
    • Use a utility or method to handle Persian calendar settings.
public void init()
{
    super();
    
    // Initialize systemDate and selectedValue
    initialDate = DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());
    selectedValue = initialDate;

    // Set the calendar control to display the initial date
    element.setControlDate(initialDate);
}

3. Convert Selected Dates to Persian

  1. Modify the CloseSelect Method:

    • Open the CloseSelect method in the PersianDateLookup form.
    • Implement logic to convert the Gregorian date returned by the picker to a Persian date.


public void closeSelect(str _selectedValue)
{
    FormRun fr;
    FormControl fc;
    str persianDateString;
    ;
    
    // Convert the selectedValue to Persian date string
    persianDateString = Global::gregorianToPersianString(str2Date(_selectedValue, #str2DateSequence));
    
    // Get the selected control from the parent form
    fr = element.args().caller();
    
    if (fr)
    {
        fc = fr.selectedControl();
        
        if (fc && fc is FormStringControl)
        {
            FormStringControl stringControl = fc as FormStringControl;
            stringControl.text(persianDateString);
        }
    }
    
    super(_selectedValue);
}

Update Date Control Handling:

  • Implement methods to initialize and handle date control changes.



public void setControlDate(date _date)
{
    FormDateControl dateControl = element.design().controlName('CalendarControl') as FormDateControl;
    if (dateControl)
    {
        dateControl.dateValue(_date);
    }
}



public void selectedDateChanged()
{
    FormDateControl dateControl = element.design().controlName('CalendarControl') as FormDateControl;
    if (dateControl)
    {
        selectedValue = dateControl.dateValue();
        element.closeSelect(date2Str(selectedValue, 123, DateDay::Digits2, DateSeparator::Slash, DateMonth::Digits2, DateSeparator::Slash, DateYear::Digits4));
    }
}


4. Create and Configure New String EDT

  1. Create a New String EDT:

    • Navigate to the EDT (Extended Data Type) node in AOT and create a new String EDT named PersianDate.

4. Create and Configure New String EDT

  1. Create a New String EDT:

    • Navigate to the EDT (Extended Data Type) node in AOT and create a new String EDT named PersianDate.

    EDT Name: PersianDate
  2. Set Lookup Reference:

    • Configure the lookup reference of the PersianDate EDT to point to the PersianDateLookup form.
  3. Adjust Properties:

    • Set the properties of the PersianDate EDT to ensure it visually mimics the standard datetime picker style.

5. Update Table Fields and Form Controls

  1. Replace Existing EDTs:

    • Replace any existing DateTime EDT references in your tables with the new PersianDate EDT.
  2. Update Form Controls:

    • On forms where dates are displayed or selected, update the controls to use the PersianDate EDT. This ensures that all date-related interactions utilize the Persian calendar.

Additional Methods for Date Conversion

To support Persian calendar functionalities, you can add the following methods to the Global_Extension class:

[ExtensionOf(classStr(Global))]
internal static final class Global_Extension
{
    public static str gregorianToPersianString(date gregorianDate)
    {
        System.Globalization.PersianCalendar persianCalendar = new System.Globalization.PersianCalendar();
        int year = DateTimeUtil::year(gregorianDate);
        int month = DateTimeUtil::month(gregorianDate);
        int day = DateTimeUtil::day(gregorianDate);
        int persianYear = persianCalendar.GetYear(gregorianDate);
        int persianMonth = persianCalendar.GetMonth(gregorianDate);
        int persianDay = persianCalendar.GetDayOfMonth(gregorianDate);
        return strFmt('%1/%2/%3', persianYear, persianMonth, persianDay);
    }

    public static date persianToGregorian(date persianDate)
    {
        System.Globalization.PersianCalendar persianCalendar = new System.Globalization.PersianCalendar();
        int year = DateTimeUtil::year(persianDate);
        int month = DateTimeUtil::month(persianDate);
        int day = DateTimeUtil::day(persianDate);
        System.DateTime gregorianDate = persianCalendar.ToDateTime(year, month, day, 0, 0, 0, 0);
        return DateTimeUtil::date(gregorianDate);
    }
}

Conclusion

Customizing the SysDateLookUp form to use the Persian calendar involves several steps: duplicating and modifying the form, creating and configuring a new String EDT, and updating your system to use Persian dates. Additionally, adding methods for date conversion in the Global_Extension class ensures seamless handling of dates between Gregorian and Persian calendars. By following these steps, you can provide a tailored date handling experience in D365FO that meets your organization's needs.


Comments

Popular posts from this blog

How to Refresh a Form or Data Source in D365FO Using X++

  Introduction In Microsoft Dynamics 365 Finance & Operations (D365FO), refreshing the form after an action (like inserting, updating, or deleting a record) is essential for keeping the UI updated with the latest data. In this blog, we’ll explore two ways to refresh the form in X++: ✅ Refreshing the entire form using taskRefresh ✅ Refreshing a specific data source using research Let's dive into the best practices for implementing these refresh methods! 🔄 Refreshing the Entire Form If you need to refresh the whole form , use the taskRefresh method. This method is useful when multiple data sources are involved, and you want to reload everything. 📌 X++ Code for Full Form Refresh public void refreshForm() {     // Get the current form instance     FormRun formRun = this.formRun();     // Check if formRun is valid before refreshing     if (formRun)     {         info("Refreshing the form...");     ...

How to Open a Form with Filtered Data Using a Button in X++ – A Step-by-Step Guide

 In Dynamics 365 for Finance and Operations (D365FO), a common requirement is to open a form dynamically from another form and pass filtered data based on a specific condition. This functionality can enhance user experience by allowing them to interact with multiple forms seamlessly, while keeping the data relevant and focused. In this blog, we’ll explore how to implement such a solution using X++, where a user clicks a button on Form 1 (such as a list of sales orders), and based on a selected record, Form 2 (such as invoice details) opens with only the relevant filtered data. Scenario Overview Let’s assume the following scenario: Form 1 : Displays a list of sales orders, and each order has an OrderID , CustomerID , and OrderAmount . Form 2 : Displays details of invoices (from the InvoiceDetails table) that are linked to the selected OrderID from Form 1 . The goal is to click a button on Form 1 , pass the OrderID to Form 2 , and display only the relevant invoice records relate...

Sorting Data in X++ (D365FO) Grids Using Form Data Source Events

  Introduction : In Dynamics 365 Finance and Operations, form grids often display data retrieved from a table or query. However, the default sorting applied may not always align with business requirements. Customizing sorting behavior at runtime ensures that the grid data is presented in a meaningful order for users. This blog post demonstrates how to use the Initialized event of a form data source to apply custom sorting to a grid. We will sort the grid rows based on a specific field ( DisplayOrder ) in ascending order. Understanding the Code : Here’s the complete code snippet: --------------------------------------------------------------------------------------------------------------- [FormDataSourceEventHandler(formDataSourceStr(MyFormDataSource,  MyTable), FormDataSourceEventType::Initialized)] public static void MyFormDataSource_OnInitialized(FormDataSource sender, FormDataSourceEventArgs e) {     // It will clear if any other sorting is applied on the grid ...