Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents
minLevel1
maxLevel1

Calculation Logic Architecture

...

This flow shows how different logics are executed from the Quote perspective:

...

Prerequisites

Review execution flows diagrams of how and when the quote logics are executed:

Syntax Check

This dry run is used to discover inputs in the line item. Most of other API calls are mocked. Be aware that the syntax check dry run is executed only once when a new line item is added.

...

  • Configurator Logic

    • The logic is executed after clicking the Open button of Configurator.

      Image RemovedImage Added

      After clicking the Save button of a popup Configurator, a new configurator value is saved to the Quote object. (Quote object is what you get by calling quoteProcessor.getQuoteView().) But you cannot work with the values until the quote is recalculated. Why? All the logic knows is 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; to learn more see how to recalculate a quote automatically.

  • Template Logic

    • The logic is executed after clicking the download PDF button.

    • From the template logic the whole quote object is accessible through api.getCurrentItem().

...

Step 1: Create the Configurator button in the header logic.

Code Block
languagegroovy
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 input matrix in this example.

Code Block
languagegroovy
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:

Code Block
languagegroovy
if(quoteProcessor.isPrePhase()) {
    def quote = quoteProcessor.getQuoteView()
    def quoteFeatures = quote?.inputs?.find { it.name == "Configurator" }?.value
}

...

Let’s suppose you have the following header logic stringUserEntry field which you want to pass to the configurator:

Code Block
languagegroovy
if (quoteProcessor.isPrePhase()) {
    quoteProcessor.addOrUpdateInput("ROOT",
            [
                    "name"    : "ValueToBePassed",
                    "label"   : "ValueToBePassed",
                    "type"    : "STRINGUSERENTRY",
                    "required": false
            ]
    )
}

...

The issue here is that the configurator and header logic do not share a global space. So you cannot simply use api.global. Instead, you need to pass this value to the configurator value property.

Code Block
languagegroovy
if (quoteProcessor.isPrePhase()) {

    def passedValue = quoteProcessor.getQuoteView().inputs?.find { it.name == "ValueToBePassed" }?.value
    quoteProcessor.addOrUpdateInput(
            "ROOT",
            [
                    "name" : "Configurator",
                    "label": "Configurator",
                    "url"  : "ConfiguratorLogic",
                    "type" : "CONFIGURATOR",
                    "value": passedValue
            ]
    )
}

...

{QuoteFeatures=[{Name=firstLineName, firstLineDescr, selected=false}], PassedValue=asdfasfd}

Code snippet:

Code Block
languagegroovy
if (quoteProcessor.isPrePhase()) {
    def quote = quoteProcessor.getQuoteView()

    def passedValue = quote.inputs?.find { it.name == "ValueToBePassed" }?.value
    def configuratorValueBackup = quote.inputs?.find { it.name == "Configurator" }?.value ?: [:]
    configuratorValueBackup.put("PassedValue", passedValue)

    quoteProcessor.addOrUpdateInput(
            "ROOT",
            [
                    "name" : "Configurator",
                    "label": "Configurator",
                    "url"  : "ConfiguratorLogic",
                    "type" : "CONFIGURATOR",
                    "value": configuratorValueBackup
            ]
    )
}

...

To be able to read a value inside the configuration logic which was passed from the header logic, you need to use a HIDDEN input. The code illustrates this better:

Code Block
languagegroovy
def confEntryPassedValue = api.createConfiguratorEntry(InputType.HIDDEN, "PassedValue")
def passedValue = confEntryPassedValue.getFirstInput()?.getValue()

...

Quote line logic:

Code Block
languagegroovy
def value = api.global.variableTest

api.logInfo("LineItemLogic value:" + value);

...

If you use publishing templates for quotes, you have to pass data to the preprocessing (publishing) logic. To do that, simply call api.currentItem from the preprocessing logic to get a full quote view.

Code Block
languagegroovy
def quote = api.currentItem().

...

To do this, you need to combine the following use cases:

Use Case 1: Quote Header Logic Configurator → Quote Header Logic.

and then

Use Case 3: Quote Header Logic → Line Item logic

If priceEntityAfterSave is not set to true, the Recalculate button has to be used after saving values in the configurator.

...

To read values from a line item logic, use the steps from Use Case 4: Quote Line Item Logic → Quote Header Logic.

Then in the post-phase in the header logic, pass those values through the value property in the configurator. For details see Use Case 2: Quote Header Logic → Quote Header Logic Configurator.

Thanks to the post-phase you can pass these values within one recalculation transaction.

Final code:

Code Block
languagegroovy
if (quoteProcessor.isPostPhase()) {
    def quote = quoteProcessor.getQuoteView()

    def lineItems = quote?.lineItems
    def sum = 0
    lineItems.each { lineItem ->
        def outputItems = lineItem.outputs
        def value = outputItems.find { it.resultName == "LineItemValue" }?.result
        sum = sum + value
    }

    def passedSumValue = sum
    def mergedValue = quote.inputs?.find { it.name == "Configurator" }?.value ?: [:]
    mergedValue.put("PassedValue", passedSumValue)

    quoteProcessor.addOrUpdateInput(
            "ROOT",
            [
                    "name" : "Configurator",
                    "label": "Configurator",
                    "url"  : "ConfiguratorLogic",
                    "type" : "CONFIGURATOR",
                    "value": mergedValue
            ]
    )
}

...

Why? Let’s see the example of passing values from a line item logic to a line item configurator.

Code Block
languagegroovy
api.configurator("Configurator A","ConfiguratorFormula")
def confA = api.getParameter("Configurator A")
if(confA != null && confA.getValue() == null) {
     confA.setValue(["ConfiguratorType":"A"])
}

...

But this is does not work because:

  1. Line item configurator “test” is created during the syntax check.

  2. During the syntax check, api.input returns only mock data, not real data.

  3. During the syntax check (and only during the syntax check), api.getParameter("test") returns a context parameter of the "test" configurator. During recalculation (when the syntax check is not running), api.getParameter("test") returns null.

  4. ConfA?.setValue in the syntax check:

    1. Has a context parameter of the configurator.

    2. Does not have a value loaded from api.input.

  5. ConfA?.setValue when the quote is recalculated:

    1. Does not have a context parameter of the configurator.

    2. 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 the value is known during the syntax check.

  • You cannot dynamically set loaded data from the header configurator because in the syntax check api.input does not return an expected value and during recalculation, api.getParameters("test") does not return the context parameter of the configurator.

...

Configurator at the header level, defined in the header logic. From this configurator, you load data to the line item configurator.

Code Block
languagegroovy
if (quoteProcessor.isPrePhase()) {
    quoteProcessor.addOrUpdateInput(
            "ROOT",
            [
                    "name" : "Configurator",
                    "label": "Configurator",
                    "url"  : "ConfiguratorLogic",
                    "type" : "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 lineItems you need to look inside the configurator value field and collect all needed values.

This needs to be done in the quote header logic.

Code Block
languagegroovy
    def quote = quoteProcessor.getQuoteView()
    def lineItems = quote?.lineItems
    
    lineItems.each { lineItem ->
        def inputItems = lineItem.inputs
        def value = inputItems.find { it.name == "ConfiguratorName" }?.value
        sum = sum + value?.configuratorFieldValue? : 0
    }

...

It is possible to add default values to the line item configurator from the line item logic using the following code:

Code Block
languagegroovy
api.configurator("Configurator A","ConfiguratorFormula")
def confA = api.getParameter("Configurator A")
if(confA != null && confA.getValue() == null) {
     confA.setValue(["ConfiguratorType":"A"])
}

...