Logic Inputs
While implementing the solution for customer, many times we need to ask the end user for certain values and then use them for calculation. It’s needed when creating new Price List, negotiating Quotes or setting up filters on Dashboards.
This handbook describes how to define/create the input fields on Line Items or Dashboard, and how to then read the values entered by end user and use it.
This handbook does not cover inputs for Headers.
Concept
What’s an Input Field
An Input Field allows user to enter or select a value on the screen, which could be further used for a decision or in calculation.
Data Model
On the backend, the input definition is represented as a ContextParameter object and persisted in JSON format. Also the value selected/entered by the user is inside of this object.
JSON definition of drop-down input field, how it’s stored in database
{
"name" : "DeliveryType",
"label" : "Delivery Type",
"type" : "OPTION",
"required" : true,
"parameterConfig": {
"labels": {
"Express" : "Express Delivery",
"Standard": "Standard Delivery"
}
},
"valueOptions" : [
"Express",
"Standard"
],
"value" : "Standard" //❶
}
❶ This is the actual value selected by the user.
Principle
Flow of Input Fields Creation
The flow of creation and usage of input fields:
The user gets to a page, where the system needs to show to the user the input fields. For that, it needs the input definitions.
The input field definition could already exist (e.g., on Quote Line Item loaded from database).
Or it must be created (e.g., at the time the user creates a new Price List).
The input fields are rendered/presented to the user, and the user will enter/select the values, and then ask the system for recalculation of the result (e.g., the user presses buttons like "Recalculate", "Refresh", etc).
One Logic, Two Execution Modes
In the flow you can see, that there are two calls to the logic
first time to collect the definitions of the input field and
second time to calculate the results.
There are not two separate logics for that, but it’s all together in one logic. So there must be some way, how the logic can distinguish
if it was executed, because it should create the input fields definition,
or if it was executed, because we need it to calculate new results.
To achieve that, the logic is executed in two different modes:
Input generation mode - For creation of the input field definitions.
Regular (normal, standard) execution mode (i.e., NON-input generation mode) - For standard calculation of results using the values provided by the user.
Input Generation Mode
It is a highly specific execution mode, used only to create and collect the input field definitions.
Formerly, it was called Syntax Check mode, because in the past we used Formula language which was interpreted, and there was a need to do a check of syntax of the code.
Some things are NOT working the same way as in regular execution, e.g., result values of Elements return mock values, and some API calls (e.g., api.stream(), api.currentItem()
) return null, instead of real values.
Inputs Defined by System
Some input fields are defined by the system on its own, and you do not need to define them in the code.
Those are:
Inputs fields for built-in fields (e.g., on Quote Header, when defining a Pricelist, etc.) - these inputs are always there.
Inputs defined by configuration in Administration (e.g., Customer picker on Quote root folder, or Customers picker on Contract root folder) - there are checkboxes in the Administration sections, so you can choose, if you want the system to create them or not.
Implementation
Definition of Input
There are two ways how to define the input fields:
Input Builder
Former set of various API functions (will be depriciated)
Using Input Builders
Input Builders is a new API introduced in version Vesper. The main goal was to unify the way, how inputs are built:
on line items
on headers
in Configurator
The Input Builders internally construct a ContextParameter object. The building of input fields on all the three places is done the same way, only with one small difference in the last function call
in the example below, the call of function
.getInput()
to create Quote Header input we use
.buildMap()
function
Creates a definition of drop-down input field using Input Builder
def deliveryTypes = [
"Express" : "Express Delivery",
"Standard": "Standard Delivery"
]
api.inputBuilderFactory()
.createOptionEntry("DeliveryType")
.setLabel("Delivery Type")
.setRequired(true)
.setOptions(deliveryTypes.keySet() as List)
.setLabels(deliveryTypes)
.getInput() //❶
❶ The function getInput()
builds the input field, but this one makes sense only on Line Items, Dashboards, etc, but not on Headers.On Headers you will use different function.
Using Former API for Inputs
This API is widely used on projects, so you should understand how it works.
Creates a definition of drop-down input field using input API functions. This sample will work only in some situations, e.g. on line items
def deliveryTypes = [
"Express" : "Express Delivery",
"Standard": "Standard Delivery"
]
api.option(
"DeliveryType",
deliveryTypes.keySet() as List,
deliveryTypes
)
api.getParameter("DeliveryType") //❶
.setLabel("Delivery Type")
.setRequired(true)
❶ You need to get a reference to the input field definition and then then modify its properties.
Guard the Creation of Input Definition in Input Generation Block
In the Concept section you learned, that the same logic is used for two purposes.
So when you define the part of code, which creates the input field definition, it’s good practise to perform it only, when the logic is executed in input generation mode - i.e., when api.isInputGenerationExecution()
returns true.
Reading of the Input Values
Reading of input field values is done in Logic during its regular execution. You can use several approaches:
Use binding variable input:
input.DeliveryType
orinput["DeliveryType"]
Call of API function (depreciated) api.input("DeliveryType")
or older approach, by reading value returned by the same function, which is building the definition (e.g., def value = api.option(…)
) during the regular execution.
Best Practices for Handling of Input Fields
Since the logic is executed at different occasions in two different modes, it is a good idea to stick to some good practices how to structure the code in the logic.
General recommendations are:
Place all the Groovy code for creation of the input field definitions to the elements at the beginning of the logic - ideally dedicate those elements only to creation of inputs and nothing else.
Add element "AbortOnInputGeneration", which will block/disallow execution of the "calculation" elements during input generation execution.
This is important, because the subsequent calculation elements can perform some heavy calculations, which do not make any sense at the moment, when the system needs ONLY to collect the input field definitions.
Most logics are executed in input generation mode during deploy (e.g., even Workflow logic) and their execution code usually does not expect to be executed in input generation mode.
All further elements perform only reading of the input values and calculations, but do not create any new definitions.
Element | Groovy script |
---|---|
DefineInputs | |
AbortOnInputGeneration | |
InvoicePrice | |
TotalInvoicePrice |
Reading Inherited Input Field Values
In Documents with hierarchical structure of information (e.g., Quote, Contract, Rebate, Claim) the user can enter values into inputs not only on Line Item level, but also on ROOT folder or sub-folders.
The most common scenario is that you have a "Customer" input field on Quote ROOT folder (i.e., on Header) and you need to read the value in logic, which calculates the Line Item.
But then testing of such line item logic in Studio is complicated, the input field is defined somewhere else, not in the logic, which is reading the value.
Testing of Inputs
To test logic in Studio with input fields you need to be able to do all three things:
Run the logic in Input Generation mode to ensure that you build all the input fields correctly.
Be able to enter the values as user.
Run the logic in regular execution and verify that the logic can read the values and properly use them, plus also properly handle the situations when the user forgot to provide the values.
Generate Input Fields
In Studio, to run the logic in Input Generation mode
Navigate to "Inputs" tab of the Logic Editor
and then click on the Generate button.
The following will happen:
Studio sends the logic to the backend.
The backend will:
Compile the logic to classes.
Execute the compiled logic in input generation mode.
Collect the input field definitions created by the logic.
Return the definitions back to Studio.
Studio will display the input fields and let you enter/select values.
Enter the Values
You’re entering two kinds of values:
Context values.
Values of the input field created by the logic.
Run in Regular Execution
To execute the logic in Studio in regular execution, you click on Test logic button.
Studio will take all the context input values and the values of the generated inputs and send it (together with the logic) to the backend for execution.
Pricefx backend will compile and execute the logic, with the Context set to values from the context input fields and with the other input values available in the
input
variable.The logic will calculate the results based on the input field values the same way as if it executed normally in Pricefx.
The Element’s results will be returned back to Studio.
Studio will display the results of the Elements, traces, logs.
Special Use Cases
Testing of Logics with Inherited Inputs
This describes a typical scenario when testing line item logic of documents like Quote, Contract, Rebate or Claim. These documents can have input fields on Header (aka ROOT folder), on sub-folders and also on Line Items.
When testing a Line Item logic, which needs value from input field defined on any of its parent folders (incl. the ROOT folder - i.e., header) the problem is, that the "line item" logic does not create such input field, so the button Generate will not help.
When you need to test the line item logic in Studio, then one easy method how to deal with this problem, is to define the input field also on the line item, but only in the case when it’s being tested (from within Studio).
You can detect that a logic is being tested in Studion by reading value of method api.isDebugMode()
. Whenever Studio sends the logic to the backend for execution (either for input generation or regular mode), it will also set the DebugMode to true.
Sample of the solution of reading value of header in the "line item" logic. Code of element "CustomerId".
❶ The input field definition will be created only when executed in input generation mode, but only when run from within Studio.
In the subsequent elements, when you will read value of input.Customer
, then when executed from Pricefx UI, it will provide value inherited from parent folders, and when executed from within Studio, it will return value of input field defined in the line-item logic.
Related Features
Header Logics - The inputs are done principally in completely different way (they do not use input generation mode at all), but if you use Input Builders, then their creation will become quite similar to the one described here.
Configurator - The inputs are done also differently (no input generation, but also different from Headers), but again, if you use InputBuilders, their creation will become quite similar to the one described here.
References
Groovy API
Documentation
Knowledge Base
Found an issue in documentation? Write to us.