How to Handle Passing Values within Quote's Header Logics, Configurators and Line Item Logic
Written by: Jachym Krasek, Jiri Skorvanek
This article summarizes possible solutions for different use cases of passing on values within Quote’s different calculation logics.
We tried to describe possible solutions, some obstacles or wrong ways how to do it. Please feel free to comment the solutions.
Calculation Logic Architecture Prerequisities
How different logics are executed from the Quote perspective:
Syntax Check
This dry run is used to discover inputs in the line items. Most of other API calls are mocked. Be aware that syntax check dry run is executed only once in case of adding a new line item.
Other Logics Executable from Quote
There are two other logics which can be executed from header logics:
Configurator Logic
This logic is executed after clicking the Open Button of a configurator:
After clicking the Save button in the configurator pop-up, a new configurator value is saved to the Quote object. (The quote object is what you get by calling
quoteProcessor.getQuoteView()
). But you cannot work with the values until the Quote is recalculated. This is because the logic knows only the old Quote object since all other calculation logics finished before the configurator logic was executed. You must click the Recalculate button or set automatic recalculation (for details see how to recalculate a quote automatically).
Template Logic
This logic is executed after clicking the Download PDF button.
From the template logic the whole quote object is accessible through
api.getCurrentItem()
.
Summary of Passing Values between Different Types of Logics
Use case | From | To | Solution |
---|---|---|---|
1 | Quote Header Logic Configurator | Quote Header Logic | Read the configurator value field. |
2 | Quote Header Logic | Quote Header Logic Configurator | Pass through the configurator value field, read using a HIDDEN field inside the configurator. |
3 | Quote Header Logic | Line Item Logic | Hidden input (pre-phase). |
4 | Line Item Logic | Header Logic | Read from |
5 | Quote Header Logic | Publishing Template Logic | Hidden input or |
6 | Quote Header Configurator | Line Item Logic | Hidden input. |
7 | Line Item Logic | Quote Header Logic Configurator | Read from |
8 | Quote Header Logic Configurator | Line Item Logic Configurator | Not possible, please see details below, including workaround. |
9 | Line Item Logic Configurator | Quote Header Configurator | Read from |
10 | Line Item Logic | Line Item Logic Configurator | Not possible, please see details below, including workaround. |
Use Case 1: Quote Header Logic Configurator → Quote Header Logic
Step 1: Create the Configurator button in the header logic.
if (quoteProcessor.isPrePhase()) {
quoteProcessor.addOrUpdateInput(
"ROOT",
[
"name" : "Configurator",
"label": "Configurator",
"url" : "ConfiguratorLogic",
"type" : "CONFIGURATOR"
]
)
}
Step 2: Define a generic logic (name it e.g. ConfiguratorLogic). It contains an input matrix in this example.
def ce = api.createConfiguratorEntry()
def p = ce.createParameter(InputType.INPUTMATRIX, "QuoteFeatures")
def cols = ["Name", "Description"]
def types = ["Text", "Text"]
p.addParameterConfigEntry("columns", cols)
p.addParameterConfigEntry("columnType", types)
return ce
Step 3: In the header logic, access all the values from the configurator using the following code snippet:
if(quoteProcessor.isPrePhase()) {
def quote = quoteProcessor.getQuoteView()
def quoteFeatures = quote?.inputs?.find { it.name == "Configurator" }?.value
}
Where Configurator
is a name of the configurator element (created in Step 1). What you will get is the following HashMap:
{QuoteFeatures=[{Name=firstLineName, firstLineDescr, selected=false}]
Use Case 2: Quote Header Logic → Quote Header Logic Configurator
Step 1: Create a header input field.
Suppose we have the following header logic stringUserEntry field which we want to pass to the configurator:
Step 2: Pass the header value to the configurator.
The problem is that the configurator and header logic do not share a global space. So we cannot simply use api.global
. Instead of that we need to pass this value to the configurator "value" property.
Step 3: Merge values.
Problem with the solution from Step 2 is that the logic always overrides the configurator value after recalculation. The configurator value field is used for storing the configurator object. It looks like this (QuoteFeature is inputMatrix object):
{QuoteFeatures=[{Name=firstLineName, firstLineDescr, selected=false}]
Therefore we need to read configurator values first and then create a value map where we put backed up configurator’s values together with values we need to pass to the configurator. The goal is this:
{QuoteFeatures=[{Name=firstLineName, firstLineDescr, selected=false}], PassedValue=asdfasfd}
Code snippet:
Step 4: Read the passed value in the configurator logic.
This is the final step. To be able to read a value inside the configuration logic which has been passed from the header logic we need to use HIDDEN input. The code bellow illustrates this:
The QuoteFeatures
object is automatically mapped to the input matrix, so the passed values are displayed in the input matrix and we can use PassedValue for any purpose (e.g. conditional field display).
Use Case 3: Quote Header Logic → Line Item Logic
Solution for this use case is easy: api.global.
Header logic:
Quote line logic:
Instead of api.retainGlobal = true
you can set the option api.retainGlobal defaults to TRUE in Administration > Configuration > General Settings to true.
Use Case 4: Quote Line Item Logic → Quote Header Logic
This can be useful if you want to, for example, calculate a summary on the header level or display a summary chart in the custom Quote header.
This is very easy, as it involves just reading the quote structure. This needs to be done in the Quote post-phase (= after line item calculation).
In the header logic, you can read the values from line items this way:
Use Case 5: Quote Header Logic → Publishing (Preprocessing) Logic
If you use publishing templates for quotes, you have to pass data to the preprocessing (publishing) logic. Simply call api.currentItem
from the preprocessing logic to get a full quote view.
Note: Data will be available in the preprocessing logic only after the quote was saved.
Use Case 6: Quote Header Configurator → Line Item Logic
From the configurator logic you do not have a direct access to the quote object. This means you have to pass values from the header logic to the line item logic.
To do this, you need to combine the following use cases: Use case 1: Quote Header Logic Configurator → Quote Header Logic and Use case 3: Quote Header Logic -> Line Item logic.
Without priceEntityAfterSave set to true the Recalculate button has to be clicked after saving values in the configurator.
Use Case 7: Line Item Logic → Quote Header Configurator Logic
To read values from a line item logic, we can use a logic from Use case 4: Quote Line Item Logic → Quote Header Logic.
Then at the post-phase in the header logic we pass those values through the “value” property in the configurator. Refer to Use case 2: Quote Header Logic → Quote Header Logic Configurator.
Thanks to the post-phase we can pass these values within one recalculation transaction.
Final code:
Now when you click the configurator button, you will have values from line items available in the configurator.
Use Case 8: Quote Header Logic Configurator → Line Item Logic Configurator
This can be useful if you want to, for example, set generic parameters in the quote header configurator and then have a pre-filled configurator on each line item, to edit the values for each line item separately.
It is possible to pass data from the quote header logic configurator to the line item logic, but it is not possible to pass data from the quote header logic configurator to the configurator defined in the line item logic.
To explain this, let’s see the example of passing values from the line item logic to the line item configurator.
This works fine as you use static (hard-coded) values. Data are not loaded dynamically. If you want to load data dynamically from the quote header configurator, this is the code in the line item logic:
But this does not work because:
Line item configurator “test” is created during syntax check.
During syntax check,
api.input
returns only mock data, not real data.During syntax check (and only during syntax check),
api.getParameter("test")
returns a context parameter of the "test" configurator. During recalculation (when syntax check is not running),api.getParameter("test")
returns null.ConfA?.setValue in syntax check:
has a context parameter of the configurator;
does not have a value loaded from
api.input
.
ConfA?.setValue when quote is recalculated:
does not have a context parameter of the configurator;
has a value loaded from
api.input
, but it cannot be set as confA is null.
As a result:
You can set a static value via
confA.setValue()
as a value which is known at syntax check.You cannot set dynamically loaded data from the header configurator because in the syntax check
api.input
does not return the expected value and during recalculation,api.getParameters("test")
does not return the context parameter of the configurator.
In this case, quote recalculation will not help as during recalculation, there is no syntax check. So let’s see a workaround.
Workaround: Create Line Item Configurator on Quote Header
Create a configurator for line items from the header logic and pass it to the line items via the addOrUpdate
function.
Example:
Configurator on the header level, defined in the header logic. From this configurator, we load data to our line item configurator.
Line item configurator defined in the same header logic:
In this case, the line item configurator will always have the same values as the quote header configurator. Any change in the line item configurator will be overwritten on quote recalculation. If you want to keep changes done on the item level, you have to merge them with the header value before you set them via addOrUpdateInput
.
Use Case 9: Quote Line Item Configurator → Quote Header Configurator
The principles are same as in Use Case 7: Line Item Logic -> Quote Header Configurator Logic.
The only change is when you iterate over line items you need to look inside the configurator value field and collect the needed values.
This needs to be done in the quote header logic.
Use Case 10: Line Item Logic → Line Item Logic Configurator
It is possible to add default values to the line item configurator from the line item logic using the following code:
So when you need pass values only once as an initialization step, it can be done using the code above. If you would like to pass different values each time on recalculate, you must change the approach.
The problem here is the function api.getParameter("Configurator A")
. It returns the configurator object only in case when the configurator was not opened before and has no value in the value property. The same applies to e.g. stringUserEntry: while it is empty you can get the userEntry object by calling api.getParameter
. After you fill it in, no object is returned to you.
Workaround / Solution
To dynamically fill in configurators in the line item calculation logic, you have to update it using the header logic. Basically follow the instructions in Use Case 4: Quote Line Item Logic → Quote Header Logic and update configurator values while iterating.
Possible Design Troubles
“Add new line item” Architecture Awareness
Some of the use cases above are implemented with the header logic updating line items.
Example:
Header logic configurator has three string inputs. You need to see these input values at the line item level to work with them. You can do this in the quote header pre-phase logic. You will iterate over lineItems and passing them some HIDDEN fields.
So far so good.
What happens when you add a new item? Only the item logic syntax check and item logic is executed. When the quote header pre-phase did the iteration over all items, this new line was not there yet.
Conclusion:
It means that you will get these passed values on a new line item only after clicking the Recalculate button.
Found an issue in documentation? Write to us.