Ok, So I added Variables To BAQs.
I’m going to do this one a little different, so first let me tell you a story.
No, I’m not full of myself. This isn’t the end all, be all project of the year. I’ve
decided to expand my writeups for a few reasons. One of these reasons is we are in
mixed company here on the forum, with different skill levels and interests. Another
is I’m trying to improve my communications skills in general. I’m also trying to
create the type of documentation that I like to read, and I feel lacks to some extent.
I’m never gonna be a legendary coder, that time has passed, but I can try to share
what I’ve learned with others, and maybe we can all learn something on the way.
The last thing is, I want people to look at the process, and see the power of what
Epicor has offered us here with being able to customize, and see how that can be
leveraged to add new features or work around issues.
Anyway, on to the story…
I was perusing the forum at work, looking for things I could help with. OK, Fine → I was
looking for a way to get out of work that wouldn’t make me feel so guilty.
Anyway, I came across this thread:
It peaked my interest, and I participated. I had also pondered these same questions
before, and I had a need, and a desire for this feature. I hacked at the BAQ Designer
for a few days when I had time, trying to find a more elegant workaround, and I didn’t
come up with anything better than we had come up with in the thread.
So I started thinking, looking at the BAQ Designer, and the data structure of how a BAQ
is actually defined, and started running some tests. I asked myself a few questions, some
of those were:
* "What is a variable?"
* "In this context, is it the same as a parameter?"
* "Oh wait, can I add those dynamically?"
So I did a bunch of tracing and decided it was feasable. And then in some really ugly
test code, I made it work. WooHoo!
So then I asked myself a few more questions. I said to myself:
* "Ok smart ass, so what, you can add parameters.. but how do you define them?"
* "Where should you define them?"
* "Can we use the built in tools?"
I had a good look at the BAQ Designer again and the DataSet and had a few ideas.
“Define Parameters” was out. I couldn’t take it over with code, or with BPMs, it’s
not customizable at all, and some other issues.
Then a thought hit me. Subqueries not referenced, are in the DataSet, but not included
in the query… Hmmm, that will also give me an “Editor” to define variables.
(The Calculated Field Editor)
So I decided on my path. I had rules.
I decided to add Variables or should I say “Dynamic Parameters”, to queries, and this
is how I would do it:
* Store the variables in a subquery named "Variables" inside the BAQ itself.
* The "Variables" subquery is not referenced at all in the BAQ. It just exists.
* The "Variables" would be defined as "Calculated" Fields in the query.
* The "Values" of these queries would be stored in the "Formula" field. (This became more flexible later)
* This would allow me to define the "Data Types" of the variables as well!
So now I had a plan, using built in tools, to add a neat and useful feature.
Now we just needed some backing code to make this work.
The place to do this code was the DynamicQuery BO. So I worked up version one with my rules in mind.
First I set up a test query, with variables defined in Calculated Fields, and referenced in the regular top query.
(Variables.Calculated_FieldName would be referenced as a parameter in the rest of the BAQ
as “@FieldName”)
Of course it didn’t work. Errors everywhere.
Then I started writing a pre-processing directive on DynamicQuery.GetList.
First check, does this DataSet have a subquery in it called “Variables”?
- Nope, move along.
- Yes?!, Ok, let the magic happen lol:
Ok, we have a Subquery named variables, now I looped through each field defined, and added the correct data to the datasets to make it work.
Time for truth.
I hit “Test” on the BAQ Analyze tab, and BAM! My test query returned my variable values. It was a little messy,
but it worked!
Of course, the “Analyze” button did not. So I copied the code over unchanged to the “Analyze” method.
Syntax OK
“Sweet.”
“Hmmm, this is going to have to be reused, should I move it to a function, or a UBAQ?”
“Yes, Yes I should.”
(For those of you who do not have functions, don’t worry, if you want this, it can be done without it.)
The rest of the story is where I clean it up, debug it, add add a few features. That information is below.
Ok, Now that story time is over, I’ll try to explain this as clearly as possible. Even if you do not need
a feature like this, the knowledge may be vaulable. I know it is to me. I have two other projects I’m now
working on from the knowledge I’ve gained from this one.
How BAQs Work - The Abbreviated and Highly Simplified Version
A BAQ is just a big DataSet, with Subqueries, Fields, Field Attributes, Links, Parameters, Control
Fields, Etc, that are read from a custom Epicor BO, that creates SQL for the backend. This allows a level of control over how the queries are formed, as well as a loose abstraction away from the underlying database.
Basically, The Dynamic Query Business Object reads the DataSet, and constructs a query to be run.
How can we use this?
We have virtually no access to the underlying database, other than some simple stored procedures and parameters we can pass on down through Execution Settings or Calculated Fields. For Better Or Worse, Opinion wise.
We do however have full access to the Dynamic Query DataSet and Execution Parameters. These can be used along with the BO methods to add features or manipulate behavior along the way. I don’t like everything in Epicor by far, but I LOVE THIS.
Technical & Implementation Details
Variables (Dynamic Parameters) are defined by simply adding a SubQuery To A BAQ
with the name “Variables”
Then, you have a few options to add variables.
Method 1:
* Simply Add a Calculated Field, and use the label caption, as the value.
* Example → Field: Calculated_VariableTest, Label Caption: “Hello World”
* Note: I would just set the “Formula” field here to “null”, no quotes
* You would use this as @VariableTest everywhere else in your BAQ.
* This has a limit of 50 Characters.
* DataTypes are defined in the Calculated Field Editor.
Method 2:
* As a convenience feature, you can add Tables to your “Variables” SubQuery
* These are added just to “steal” the names of these fields
* Example → Field: Part_Partnum, Label Caption: “I reused this field name lol”
* You would use this as @Partnum everywhere else in your BAQ.
* This has a limit of 50 Characters.
* DataTypes is the same as the DataType of the field you referenced.
Then, you have the Expanded methods. These are built off of Method 1.
Method 1 A:
* Simply Add a Calculated Field, set the label caption variable to “null” (literally, the text “null”, no quotes)
* Example → Field: Calculated_VariableTest, Label Caption: “null” (no quotes)
* You would define your variable value in the “Formula” Field in the Calculated Field Editor
* You would use this as @VariableTest everywhere else in your BAQ.
* This does not have the 50 character limitation.
* DataTypes are defined in the Calculated Field Editor.
Method 1 B:
* Simply Add a Calculated Field, set the label caption variable to “null” (literally, the text “null”, no quotes)
* Then set the “Formula” field to “null”, no quotes
* Example → Field: Calculated_VariableTest, Label Caption: “null” (no quotes), Formula Field: “null” (no quotes)
* You would define your variable value in the “Description” Field in the Calculated Field Editor
* Note: Due to a bug in the BAQ Designer, sometimes you will need to exit out of the Calculated Field Editor
* and save and put your data in the Description field to get it to stick. It’s annoying.
* You would use this as @VariableTest everywhere else in your BAQ.
* This does not have the 50 character limitation.
* DataTypes are defined in the Calculated Field Editor.
How to Use
In your BAQ, you would use these variables, just like you would use parameters in your BAQ.
Even in links, criteria, etc.
Note: In links and criteria, you would choose “Expression”, instead of “Parameter”
Just pop your parameter in the expression box, as the designer doesn’t know about dynamic parameters.
And that’s pretty much it for usage.
How it’s put together:
The main meat of this is run via an Epicor Function called:
- Function: BAQDynamicParameters.AddDynamicParameters
- This function takes the information provided in the “Variables” SubQuery,
- and adds Parameters to the BAQ in flight, with the values pre-filled.
- No user interaction is necessary, it just acts as a variable.
This is called from a few method directives with some shim code to call the function.
Method Directives Pre
-
AddDynamicParameters_Generic
- Where Used:
- Ice.BO.DynamicQuery.GetList
- Ice.BO.DynamicQuery.Analyze, Ice.BO.DynamicQuery.Execute
-
AddDynamicParameters_ExecuteByID
- Where Used:
- Ice.BO.DynamicQuery.ExecuteByID
There is also two helper functions for “Status” information.
- Function: BAQDynamicParameters.DynamicParametersStatus-Analyze
- Function: BAQDynamicParameters.DynamicParametersStatus
These are called from these method directives:
Method Directives Post
-
DynamicParameters_Analyze
- Where Used:
- Ice.BO.DynamicQuery.Analyze
-
DynamicParameters_Generic
- Where Used:
- Ice.BO.DynamicQuery.Execute
- Ice.BO.DynamicQuery.GetList
Here are some screenshots:
And that’s pretty much it.
I will be packaging up the code after some final checks, and post the code, the functions,
the method directives, and a solution file today or tommorrow for your enjoyment.