How to Open a Form with Filtered Data Using a Button in X++ – A Step-by-Step Guide
Ujjawal Tiwari
"Skilled Professional in Microsoft Dynamics 365 F&O Development, Tech Consulting, Full Stack Dev, Data Analysis, DevOps. Proficient in Azure, Power BI, Power Apps, Digital Marketing, GitHub, PHP, C# and X++"
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:
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:
领英推荐
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:
Tips for Effective Implementation:
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.
Microsoft Dynamics 365 F&O Technical Consultant | X++ Programming
2 个月Very informative
Microsoft Dynamics 365 F&O Technical Consultant | X++ Programming
2 个月Useful tips