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

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 related to that OrderID.

Step 1: Set Up the Button Logic on Form 1

The first part of this solution involves creating a button on Form 1 (the sales order form). When the button is clicked, it will pass the selected OrderID to Form 2, filtering the data based on that ID.

Code for Button Logic on Form 1:

class InvoiceDetailsButton

{

public void clicked()

{

super();

SalesOrder salesOrder;

MenuFunction menuFunction;

InvoiceDetails invoiceDetails;

Args args = new Args();

FormRun formRun = this.formRun();

int64 orderID;

List filteredInvoices = new List(Types::Record); // List to store filtered invoice records

// Validate that the form has a valid data source

if (formRun && formRun.dataSource(formDataSourceStr(SalesOrderForm, SalesOrder)))

{

salesOrder = formRun.dataSource(formDataSourceStr(SalesOrderForm, SalesOrder)).cursor();

if (salesOrder)

{

orderID = salesOrder.OrderID; // Retrieve the selected OrderID

}

else

{

warning("No order selected in SalesOrder.");

return;

}

}

else

{

warning("SalesOrder data source not found on the form.");

return;

}

// Query to filter data from InvoiceDetails

Query query = new Query();

QueryBuildDataSource qbds;

QueryBuildRange qbr;

qbds = query.addDataSource(tableNum(InvoiceDetails));

qbr = qbds.addRange(fieldNum(InvoiceDetails, OrderID)); // Add a range filter for OrderID

qbr.value(queryValue(orderID)); // Use the OrderID as the filter value

QueryRun queryRun = new QueryRun(query);

while (queryRun.next())

{

invoiceDetails = queryRun.get(tableNum(InvoiceDetails));

filteredInvoices.addEnd(invoiceDetails); // Add record to the list

}

if (filteredInvoices.elements() == 0)

{

warning("No matching invoice details found for the selected order.");

}

else

{

// Pass all filtered data to the target form

args.parmObject(filteredInvoices); // Pass filtered invoices to Form 2

args.caller(formRun); // Pass the original form context

menuFunction = new MenuFunction(menuItemDisplayStr(InvoiceDetailsForm), MenuItemType::Display);

menuFunction.run(args); // Launch Form 2 with filtered invoices

}

}

}        

Explanation of the Code for Form 1:


  1. SalesOrder salesOrder: Represents the selected sales order from the SalesOrder data source.
  2. orderID: The OrderID of the selected sales order is extracted, which will be used to filter the invoice records.
  3. Query: We create a query to filter data from the InvoiceDetails table based on the OrderID.
  4. filteredInvoices: The filtered invoice records are stored in a list and passed to Form 2.
  5. args.parmObject(filteredInvoices): This method passes the filtered invoices to Form 2.
  6. menuFunction.run(args): This function launches Form 2, passing the filtered data along with it.



Step 2: Handle the Data in Form 2

Now, let's set up Form 2 (InvoiceDetailsForm) to receive the filtered data and display it accordingly.

Objective: When Form 2 is opened, it should receive the filtered list of invoice details (based on OrderID), and apply the necessary filter to the data source.

Code for Form 2 (InvoiceDetailsForm):

public class InvoiceDetailsFormRun extends FormRun

{

public void init()

{

super();

List filteredInvoices;

int64 orderID = 0;

// Check if the calling form has passed filtered data (InvoiceDetails)

if (this.args().parmObject())

{

filteredInvoices = this.args().parmObject() as List;

if (filteredInvoices)

{

ListEnumerator enumerator = filteredInvoices.getEnumerator();

// Iterate through the filtered list and extract the OrderID

while (enumerator.moveNext())

{

InvoiceDetails invoiceDetails = enumerator.current() as InvoiceDetails;

if (invoiceDetails)

{

orderID = invoiceDetails.OrderID; // Get the OrderID from each record

}

}

}

else

{

warning("No records found in the filtered list.");

}

}

else

{

warning("No data received from the caller form.");

}

// Apply filter to the form's data source (InvoiceDetails)

if (orderID)

{

FormDataSource formDatasource = this.dataSource();

if (formDatasource)

{

QueryBuildDataSource qds = formDatasource.queryBuildDataSource();

qds.addRange(fieldNum(InvoiceDetails, OrderID)).value(SysQuery::value(orderID)); // Filter by OrderID

}

else

{

warning("DataSource not found.");

}

}

else

{

warning("No filter value (OrderID) found.");

}

}

}

        

Step 3: Testing the Solution

Once the button logic is set up on Form 1 and the filtering mechanism is implemented on Form 2, you can proceed to test the solution:


  1. Form 1: Select a sales order from the list, and click the button.
  2. Form 2: After clicking the button, Form 2 should open and display only the invoice records related to the selected OrderID.


Tips for Effective Implementation:


  • Error Handling: Always ensure proper error handling is in place to handle scenarios where data is not passed correctly or records are not found.
  • Optimizing Queries: If the dataset is large, consider optimizing your queries to improve performance, using indices and limits where applicable.
  • User Feedback: Use informative messages (e.g., warning()) to notify users if no records are found, or if there are issues with the data passed.



Conclusion

In this blog, we’ve demonstrated how to dynamically open and filter forms in Dynamics 365 FO using X++. We used a practical example where a user selects a sales order on Form 1, and upon clicking a button, Form 2 opens with filtered invoice details related to the selected OrderID.

By following this pattern, you can pass filtered data between forms seamlessly, improving the user experience and making the data more relevant and accessible.

Muhammad Uzair

Microsoft Dynamics 365 F&O Technical Consultant | X++ Programming

2 个月

Very informative

Muhammad Uzair

Microsoft Dynamics 365 F&O Technical Consultant | X++ Programming

2 个月

Useful tips

要查看或添加评论,请登录

Ujjawal Tiwari的更多文章

社区洞察

其他会员也浏览了