Configurator Forms (Conceptual Guide)

You’re negotiating a Quote for a product, which requires the Sales Person to “configure” the product - that is to enter several parameters about the product, which influence the final Price. But the selection of the parameters depends on the previous choices, so it’s never the same.

What Is a Configurator

Configurator is an interactive form with multiple inputs, where values selected in one input field can influence visibility of other input fields or values available in those input fields.

  • The form can dynamically change after each change done by user. Anytime the user enters a value or selects a value, the Configurator logic is executed and can modify the list of input fields and their values.

  • The form content and dynamic behavior is defined in a separate Configurator logic. It is of Generic/Default logic type.

Buttons Behavior

OK – Closes the form and remembers the values of the input fields.The values entered by the user will be used once the user Recalculates the Quote (or refreshes the Dashboard).

Cancel – Closes the form and forgets all the values modified by the user.

Clean – Sets all inputs to empty value.

Reset – Restores all the inputs to its default value (if defined in the form).

Schema

Form – Interactive form, called Configurator.Each form is defined in its own logic.

Section – Each form can have more Sections. Each section groups together more Input fields, usually somehow related.

Input field – Any kind of input field available in Pricefx - number, dropdown, hidden, etc.Note: "Hidden" input can be useful for passing additional information calculated by the Configurator logic back to the Main logic.

Figure 1. Example of schema of the Configurator with 2 sections and 3 input fields.
Figure 2. Example of schema side by side with screenshot of real _Configurator_ form in _Unity_ UI.

Flow of Usage

  1. User adds a new product/line to a quote.

  2. The quote line logic is executed in Input Generation mode.

    1. It inserts a reference to the Configurator i.e., a placeholder saying: here I want to show the Configurator.

    2. The system collects the definition of the input fields, including the Configurator reference.

  3. The UI renders the line item input fields.

    1. The reference to Configurator is rendered as a Button. (It’s also possible to use "Inline" configurator, which renders in another way.)

  4. The user clicks the button (which should open the Configurator).

    1. The system runs the Configurator logic to create all the form’s sections and input fields.

    2. The UI renders the Form on the screen.

  5. The user selects a value in the form’s input field. This triggers a refresh of the whole Configurator form, i.e.:

    1. The system will execute the Configurator logic.

      1. It can read the values selected by the user.

      2. It (again) creates all the form’s sections and input fields.

    2. The UI refreshes the form’s inputs on the screen.

By default, the Configurator form will refresh every time when some input value is changed by the user, i.e., the Configurator logic will be executed again and the content of the form will be refreshed.

But it’s possible to mark some input fields as "noRefresh" to prevent the refresh, when the value of the field changes.

Configuration

Working with Configurator form requires code in two logics:

  • Configurator (form) logic - A separate logic (of Generic/Default type) which defines the form’s:

    • Sections - Each Section is returned as a result of one visible logic Element. Also, it is possible to define an Array or sections in one Element.

    • Input fields

    • Behavior / interactivity - How the Form reacts to the changes of values in the input fields. I.e., the code which dynamically (based on user inputs and selections) changes the list of input fields or their values.

  • "Main" logic (i.e., Quote, Dashboard, …​)

    • Inserts a reference to the Configurator. The reference is usually rendered as a button.

      • The Configurator can be also _Inlined, i.e., the UI will reserve a block space on the screen (instead of rendering a button), and the Configurator form will render in that area.

    • Reads the values (entered/selected by the user) from the Form. Because the form has many input fields, the values are returned as a Map.

    • Uses the collected values for calculation.

The Configurator logic does not use Input Generation execution mode to collect the inputs and sections. It always executes in a regular execution, when the Form needs to refresh its sections and inputs.

"Hello World" Sample

Configurator logic - build the form with one section and one input

// define a Section def section = api.createConfiguratorEntry() // add one dropdown input field section.createParameter(InputType.OPTION, "Meat") .setValueOptions("Beef", "Lamb", "Pork") .setRequired(true) //return the Section definition. The logic Element containing this code must have DisplayMode set to Everywhere return section

Main logic (e.g., Quote, Dashboard, etc) - insert a reference to Configurator

if (api.isInputGenerationExecution()) { //insert the reference to the Configurator api.inputBuilderFactory() .createConfiguratorInputBuilder("<configurator_input_name>", "<configurator_logic_name>",false) .setLabel("Configure the Product") //❶ .getInput() //❷ }

❶ Set the label of the button, which opens the Configurator.
❷ This creates the Button which opens the Configurator form.The button works technically an input field, so the values entered by user in the Configurator form can be later read in the main logic the same way as any other input value.

Main logic - reading a value returned by Configurator

input["<configurator_input_name>"]?.Meat //❶

❶ The values of configurator form are available all together in one Map.This Map is provided as input field value, where the name of the input field is the one provided during call of api.configurator().In this case, we’re reading only the value of Meat input field from the Configurator form.

Interactive Form Sample

Many times you need some of the form’s input fields to appear depending on a value selected by the user in another input field. That’s why anytime, when the user makes some selection (or enters manually a value) in some input fields, the Configurator logic is executed, and the whole Form is rebuilt.

In the following sample the form will show only one dropdown input at the beginning. Once the user selects "Beef", the form will refresh (the Configurator logic will run again) and this time the logic will build two inputs. So now the user will see two inputs.

Configurator logic - element "Meat"

// define a Section def section = api.createConfiguratorEntry() // add one dropdown input field section.createParameter(InputType.OPTION, "Meat") .setValueOptions("Beef", "Lamb", "Pork") .setRequired(true) //return the Section definition. The logic Element containing this code must have DisplayMode set to Everywhere return section

Configurator logic - element "Weight"

if (input.Meat == "Beef") { /* when the value of input field "Meat" will not be "Beef", we will not build the "Weight" section neither the input field, so it will simply not appear on the screen at all. */ def section = api.createConfiguratorEntry() section.createParameter(InputType.USERENTRY, "Weight") return section }

Error Messaging in the Form

Each Section can have a Message, which you can use for various purposes but it’s handy when you need to show an error message to the user while they use the Form. The Message can accept a piece of HTML, so you can style it as you need.

Configurator logic - use Message to display error to user

def section = api.createConfiguratorEntry() def weightInput = section.createParameter(InputType.USERENTRY, "Weight") if (weightInput.getValue() < 3) { section.setMessage("<div style='color:red;'>error message: Weight must be at least 3kg</div>") } return section

Testing of the Configurator Form

The Configurator logic is, in principle, a lot different from other pricing logics:

  • It does not use the Input Generation mode to collect the input fields - instead it builds the form’s sections and the inputs differently.

  • The content of the form is refreshed each time when the user makes a change of any input field in the form. I.e., the system triggers recalculation of the Configurator’s logic, which will produce a brand new list of sections with inputs.

All that makes the testing somewhat different from a normal calculation logic, like Price List line or Quote line.

You have several options to test:

Directly in Pricefx UI (with both Configurator and Main logics deployed)

You will deploy both - Main and Configurator - logics to the partition and test as if you would be using it as a regular user.

In Studio via the Main logic (with the Configurator logic deployed)

You will deploy the Configurator logic to the partition, and then in open the Main logic in Studio, and test the Configurator in the Parameters tab (remember to click Generate parameters first).

In Studio, using additional code to simulate the inputs from user

You need to add an additional element (with code used only in DebugMode) to the Configurator logic, which will create the input fields definitions to simulate the inputs of the Form. Then you can test the Configurator logic directly from Studio without even deploying it.

Other Useful Code Samples

Add reference to Configurator to Header/Root folder

if (quoteProcessor.isPrePhase()) { def configuratorDefinition = api.inputBuilderFactory() createConfiguratorInputBuilder( "<configurator_input_name>", "<configurator_logic_name>", false ) .setLabel("<configurator_input_label>") .buildMap() quoteProcessor.addOrUpdateInput("ROOT", configuratorDefinition) }

Passing initial values from the Main logic to Configurator logic

if (api.isInputGenerationExecution()) { api.inputBuilderFactory() .createConfiguratorInputBuilder("<configurator_input_name>", "<configurator_logic_name>", false) .setLabel("Configure the Product") // set initial values of the inputs in Configurator form .setValue(["<cfg_input_name>" : "<cfg_input_value>"]) .getInput() }

References

Documentation

Groovy API

Knowledge Base

Found an issue in documentation? Write to us.