Passing Parameter/Filter in Dynamic Query

I wanted to post this in the Expert’s Corner, but alas, I am not allowed to start new Topics there.

First off, I have no idea what I am talking about. So, if this is totally off base and makes the people who know what they are doing laugh, please just let me know. With that out of the way………

So, one of the biggest requests is to be able to pass parameters to a BAQ from a dashboard. I completely understand the need/want for this, but I was poking around and think it could already be done (this is where I begin to talk about things I don’t know about). Here are my thoughts, please let me know what you think.

Here are the pieces I found that I think would be needed.

  • A dashboard with a Tracker View
  • The ability to set BPM Call Context fields based off of selections in the Tracker view
  • The SysRowID of the query that is being executed in the dashboard
  • A BPM that would read the BPM Call Context fields and set the executionParams.ExecutionSetting dataset

So, basically I am thinking you would add field prompts to the tracker view. Then code the values of the prompts to fields in the BPM Call Context (this is where I don’t know if you can even do this or if it is easy or hard). Create a BPM that has a query to check for the SysRowID and if true change the executionParams.ExecutionSetting.

I know this is not super elegant, but wouldn’t it allow the ability to pass parameters without the pop-up and filter the query before pulling back all the data?

Again, I will not be offended if you want to laugh at this because it is just so wrong :grinning:. But if it is so right, then I’m secretly a mad genius and I do know what I am talking about :mage:

Maybe… however I dn’t know that there’s a BPM intercept in the Dashboard excution point… Maybe ExecuteQuery? But by then it would have already prompted you for the params… (Me thinks… I haven’t tested this)

I was looking at the ice.dynamicquery business object and execute method.

image

It looks like there is a BPM intercept in the trace log.

</parameter>
    <parameter name="executionParams" type="QueryExecutionDataSet">
      <QueryExecutionDataSet xmlns="http://www.epicor.com/Ice/300/BO/DynamicQuery/QueryExecution">
        <ExecutionSetting>
          <Name>Where</Name>
          <Value>(PORel_JobSeqType = N'epiRadioButton2') AND (PORel_JobSeqType = N'epiRadioButton1')</Value>
          <SysRowID>00000000-0000-0000-0000-000000000000</SysRowID>
        </ExecutionSetting>
      </QueryExecutionDataSet>
    </parameter>
    <parameter name="CallContext" type="Ice.Bpm.Context.ContextDataSet">
      <ContextDataSet xmlns="http://www.epicor.com/Ice/300/Bpm/Context">
        <BpmData>
          <SysRowID>996d57c4-8811-4fb6-874b-d3aa1e74cfbf</SysRowID>
        </BpmData>
      </ContextDataSet>
    </parameter>

I would test but I am no where near competent enough to write the code in the tracker view.

If you are taking it this far and needing code, then actually you don’t need to be so creative.

If you use the Dynamic Query object directly in a customization then you can pass parameters to it in doing so. Bind the result to a grid and any fields you want to custom controls, with other controls as your parameter entry fields, and it will all work as normal. Obviously there is customization coding involved instead of the automatic results you get from the standard dashboard builder, but then the possibility you’re looking at would be quite code-heavy too.

1 Like

I totally want to run a Dashboard using a BAQ that has parameters without the annoying pop-up! If you find something that works…Post it!

Taking Daryl’s advice I rethought what I was doing and did what I think he was suggesting (although I am not sure).

I don’t have any parameters on my BAQ. I use the standard tracker selection for the fields I don’t need to tinker with. For the fields that I did need to do something different, I added in the fields through customization. Here is a screenshot of what I did.

image

The radio buttons are not Tracker Query Controls, they are just there to drive a result to a hidden text box that is the control. So, you can filter based off of these and not have a parameter on the BAQ.

But you are still filtering the wide open BAQ using this method right? Same a filtering a dashboard? You just did the radio buttons instead of a drop down. It’s a nice solution to get that to work! However, unless I am missing something, doesn’t quite get the parameters into the tracker as needed.

Am I correct in my assumptions?

1 Like

I am certainly no expert, but it is filtering the Dynamic Query. So, I believe that it IS NOT running the BAQ wide open.

Run a trace on a dashboard that has a tracker panel with selections and you will see the area that passes the field x = filter.

True, you are correct in that. And if you don’t have any sub-queries with aggregating data, works very well.

The issue comes in the use case where you are aggregating some data and want to limit what is being aggregated. This is where you need parameters in your BAQ to do this, because otherwise you are only filtering the final table that is presented.

For example, I can’t run a CTE that get a multilevel BOM without a parameter to anchor the parent part number.

If you BAQ doesn’t need a parameter, then filtering works the same as a parameter. But sometimes you need a parameter (like when passing a number to calculate a markup percentage) where you can’t just filter the final table.

3 Likes

Ah, I just ran it 2 different ways with the trace on and got back the same dataset in the trace. I did not know that, thanks for pointing it out.

So, it goes back to my original idea. Can’t you hijack the method and insert a where clause in the actual query text?

Many have tried, none have succeeded. If you do, you will be lauded a hero among paupers!

2 Likes

Well, that is not going to happen. I did not even understand what people were saying until you explained it to me :rofl:

You’re not the first, you won’t be the last,

I like your radio button fix though, and I will probably use that in the future!

I have a couple of “light code” options for you - I am short on time so will post second option separately.

Option 1

Add a Text Box to allow user to enter a filter value. Add a Button that will execute the BAQ and apply the value entered in the TextBox as a filter against one of the Columns in the BAQ. Filter applied is in addition to any criteria defined on the BAQ.

Pros - not much code; Simple Setup; allows BAQ level where clause filtering without pop-up prompt
Cons - Only Supports filter condition against one column; Filter condition is always “Equal”
Pro and Con (depending on need) - Allows standard DBD Refresh to execute without filter applied

My Specific setup - use as example for your use case:
BAQ called “DemandSearch” created with Customer CustID as one of the display columns.
DBD created with DemandSearch BAQ. Tracker Panel added - No Fields set as Prompt
DBD AppBuilt and added to menu

Run DBD from Menu in Customization Mode. Customize:
Add TextBox control - leave it unbound
Add Button control - Set Text to “Refresh”
On Event Wizard - Add “Click” Event for Refresh Button

Add Code via Script Editor:
TextBox reference to Custom TextBox
Dispose Custom TextBox reference
Line of C# added for Click event - OnSearch has three parameters (we will look at the third one in Option 2 sample). First Param is the Column you want to filter. Second is the Value to use as the Filter. This one is applying Filter to the Customer CustID column.


// Code Sample Pulled from Script Editor

public class Script
{
// ** Wizard Insert Location - Do Not Remove ‘Begin/End Wizard Added Module Level Variables’ Comments! **
// Begin Wizard Added Module Level Variables **

// End Wizard Added Module Level Variables **

// Add Custom Module Level Variables Here **

private EpiTextBox epiTB1;

public void InitializeCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
	// Begin Wizard Added Variable Initialization
    this.epiTB1 = (EpiTextBox)csm.GetNativeControlReference("your TextBox EpiGUID here");

	// End Wizard Added Variable Initialization

	// Begin Wizard Added Custom Method Calls

	this.epiButtonC1.Click += new System.EventHandler(this.epiButtonC1_Click);
	// End Wizard Added Custom Method Calls
}

public void DestroyCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
	// Begin Wizard Added Object Disposal

	this.epiButtonC1.Click -= new System.EventHandler(this.epiButtonC1_Click);
	// End Wizard Added Object Disposal

	// Begin Custom Code Disposal
    this.epiTB1 = null;

	// End Custom Code Disposal
}

private void epiButtonC1_Click(object sender, System.EventArgs args)
{
	// ** Place Event Handling Code Here **

    // DemandSerch is the ID of the BAQ I am using. DemandSearch as in On Demand Search...

  V_DemandSearch_1View_Row.OnSearch("Customer_CustID",epiTB1.Value.ToString());

}

}

3 Likes

A couple of “light code” options for you – continued…

Option 2

Add one or more controls – this example uses two Text Boxes – to allow user to enter filter values. Add a Button that will execute the BAQ and through the magic of BAQ Markup, the filter values will be applied to the BAQ as specified in the BAQ Criteria.

BAQ Markup (in my opinion) is relatively unknown and little understood and yet for some UI specific purposes, it is really powerful. BAQ Markup was introduced in 9.05 for InfoZones and has since expanded to BAQ Combos and optionally to programmatically executed BAQs – which is what is being done here. BAQ Markup is a reference to data in the UI and the referenced data will be “substituted” at runtime for the Markup. In this example, the Markup is referencing a specific Epibinding and the Markup will be replaced with the data in the UI for BAQ execution. BAQ Markup (as a concept) is actually pretty well documented in newer versions.

Pros - not much code; Pretty simple setup; allows BAQ level where clause filtering without pop-up prompt and with full control of Criteria condition (Equal, Greater than, Begins, etc.) and also full control of And / Or condition linking

Cons – Requires that All BAQ Markup applied filters have a Filter value entered in the UI; Standard DBD Refresh will return no records (you might want to remove the Toolbar Tool or hi-jack the click event)

My Specific setup - use as example for your use case:
BAQ called “DemandSearch” created against the Customer table with BAQ Markup applied in the Table Criteria against the CustID and City columns (not particularly useful except for as a simple example)

On the “Table Criteria” Sheet of the BAQ Designer, create two criteria lines:

Field: “CustID” Operation: “Begins” Filter value: “specified Constant” for the specified part enter: [Epibinding: CallContextBpmData.Character01]
And
Field: “City” Operation: “Begins” Filter value: “specified Constant” for the specified part enter: [Epibinding: CallContextBpmData.Character02]

An image of my sample BAQ is below.

DBD created with DemandSearch BAQ. Tracker Panel added - No Fields set as Prompt
DBD AppBuilt and added to menu

Run DBD from Menu in Customization Mode. Customize:
Add TextBox control for CustID filter – bind to CallContextBpmData.Character01
Add TextBox control for City filter – bind to CallContextBpmData.Character02

Add Button control - Set Text to “Refresh”
On Event Wizard - Add “Click” Event for Refresh Button

Add Code via Script Editor:
Line of C# added for Click event. As referenced in Option 1, OnSearch has three parameters leave the first two parameters set to an empty string and set the third parameter to true. The third parameter tells the OnSearch method to handle Markup substitution. While this Option uses Markup and the first Option used Criteria Injection, the two are not mutually exclusive and you could use both techniques together.

Not added for this Sample but something you would want to do – Add Row rule to Disable the “Refresh” button until all Markup referenced filters have a value – Bind the Button to something like BpmData.Character10 and enable and disable via that data reference.

Good to know:
If there is no data in the Markup Referenced field, the Markup itself is sent as the Where Clause Criteria – clearly not useful…
You cannot Test Markup in the BAQ Designer - it just executes the BAQ with the Markup as the criteria. Test your BAQ without Markup and when happy, add the Markup criteria.


// Code Sample Pulled from Script Editor

public class Script
{
// ** Wizard Insert Location - Do Not Remove ‘Begin/End Wizard Added Module Level Variables’ Comments! **
// Begin Wizard Added Module Level Variables **

        // End Wizard Added Module Level Variables **

        // Add Custom Module Level Variables Here **

        public void InitializeCustomCode()
        {
                    // ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
                    // Begin Wizard Added Variable Initialization

                    // End Wizard Added Variable Initialization

                    // Begin Wizard Added Custom Method Calls

                    this.epiButtonC1.Click += new System.EventHandler(this.epiButtonC1_Click);
                    // End Wizard Added Custom Method Calls
        }

        public void DestroyCustomCode()
        {
                    // ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
                    // Begin Wizard Added Object Disposal

                    this.epiButtonC1.Click -= new System.EventHandler(this.epiButtonC1_Click);
                    // End Wizard Added Object Disposal

                    // Begin Custom Code Disposal

                    // End Custom Code Disposal
        }

        private void epiButtonC1_Click(object sender, System.EventArgs args)
        {
                    // ** Place Event Handling Code Here **

                    // DemandSerch is the ID of the BAQ I am using. DemandSearch as in On Demand Search...

                    V_DemandSearch_1View_Row.OnSearch("","",true);

        }

}

7 Likes

Just to clarify, the method I was outlining is a lot of code, but it does do exactly what you want. We’ve standardised a lot of it to do what we need and now build most of our dashboards this way because it turns out to be more flexible and maintainable in the long run.

Step 1. Build and deploy a dashboard with a near-empty dummy BAQ, no grid view and one full-screen tracker view with no fields. That gives you a blank canvas.

Step 2. Create a customization for the blank dashboard. Use the standard tools to put an EpiUltraGrid where the grid would have been if you’d built the dashboard normally, and whatever other controls (eg an EpiTextBox) you want to put the parameters into. Add Assembly references for DynamicQuery.

Step 3. In the Script, put some variant of the following code:

		DynamicQueryAdapter yourbaq = new DynamicQueryAdapter(this.oTrans);
                DataTable results;
		yourbaq.BOConnect();
		string baqname = "BAQNAME";
		Ice.BO.QueryExecutionDataSet dsBAQ = yourbaq.GetQueryExecutionParametersByID(baqname);
		dsBAQ.ExecutionParameter[0].ParameterID = "YOURPARAMETERNAME";
		dsBAQ.ExecutionParameter[0].IsEmpty = false;
		dsBAQ.ExecutionParameter[0].ParameterValue = VALUE FROM YOUR CONTROL AS A STRING;
		dsBAQ.AcceptChanges();
		yourbaq.ExecuteByID(baqname, dsBAQ);
		if (yourbaq.QueryResults != null && yourbaq.QueryResults.Tables.Count > 0)
		{
			results = yourbaq.QueryResults.Tables["Results"];
		}
		else
		{
			results = new DataTable();
		}
		EpiDataView edv = (EpiDataView)oTrans.EpiDataViews[baqName];
		if (!(edv != null))
		{
			edv = new EpiDataView();
			oTrans.Add(baqName, edv);
		}
		edv.dataView = results.DefaultView;

Step 4. You can use the new EpiDataView to bind to your grid. If you close and open the screen (assuming you’ve put the above somewhere that runs on opening) then you’ll even find the dataview is available in the normal properties to use. I prefer to bind the grid’s DataSource rather than use EpiBinding myself, because it’s more flexible that way, but it’s simpler to use Epicor’s proper system.

Step 5. Either create a “Refresh” button or hook into the menu RefreshTool and make sure the above code runs when the user wants to make changes.

Updateable BAQs need a bit more code than this but can also be made to work very reliably.

6 Likes

This will be a perfect solution for a use case where I need to calculate a markup percentage. Currently I do it with parameters. I can make a text box that always has a number in it. Nice!

Edit: had to think for a second, that’s only filters. Can I use the same technique to pass in a value to a calculated field?

If you are configuring the Parameter on the Table Criteria section, you can use the Markup feature instead - instead of using the “specified Parameter” option use the “specified Constant” and configure the value as the Markup.

TableCriterria

But can I use it in a calculated field? That’s what I’m wondering.

I have spent the morning testing this using the BAQ Markup and it works fine given the above example. However, I need to do this on a Date field. I have added two EpiDateTimeEditor fields and bound them to CallContextBpmData.Date01 and Date02. My Table Criteria are

image

This returns no results, looking at SQL Profiler it looks like the BAQ Markup inserted the date but did not put quotes around the date.
’ where [SalesDetail].[ShipDate] >= 2018-07-01 12:00:00 AM And [SalesDetail].[ShipDate] <= 2018-07-31 12:00:00 AM ’

Any ideas?