Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

Show Page Sections

Using Macro-Based BRC

The macro-based BRCs (Macros) are usable in multiple circumstances. They can be used to:

  • Do complex calculations
  • Query on Business Data Tables or external database tables lookup using SQL statements
  • Java-based function calls
  • Dynamically determining the output CPE (which was unknown beforehand)

This section focuses on some specific issues concerning macros.

Note: For more details on macros, see the Macro Language Guide

Understanding macros

Macros are written using the CPQ Macro Language, which can be compared to a script language. CPQ Macro Language is an interpreted language, meaning that macros are not compiled (once they are written) but interpreted by the CPQ Engine.

The CPQ Macro Language itself is Java-based. This has as a consequence that it can easily be extended with custom functions that fulfill one or another role in the context of a Configuration Process. More information about extending the CPQ Macro Language will be given in a subsequent paragraph.

In the context of CPQ, the CPQ Macro Language is used to express BRC, meaning that macros are a way to express constraints. This is extremely important, as macros are in essence procedural. As these macros are procedural, you can theoretically do a lot of things with them, but you will have to keep in mind that they are triggered by a constraint engine, and that “a lot of things” can have a negative impact on performances.

Understanding the impact on performances

As the CPQ Macro Language is a scripting language, a lot of things can be done. However you should keep in mind that the execution of a macro is not controlled by you, but by the CPQ Engine.

remember: , the macro execution is controlled by the CPQ constraint engine. Therefore, a macro can execute or re-execute (like any other BRC by the way) when:
  • The existence rule for an input alias issues “TRUE” (by the execution of another constraint)
  • The domain for an input alias is calculated (by the execution of another constraint)
  • The domain for an input alias is filtered (by the execution of another constraint)
  • The form property corresponding to the input alias becomes answered (by the execution of another constraint, by the user, by an event, …)
  • The answer to the form property corresponding to the input alias is changed (by the execution of another constraint, by the user, by an event, …)
  • The answer to the form property corresponding to the input alias becomes invalid (by the execution of another constraint)
  • The answer to the form property corresponding to the input alias is reset (by the user, by an event, …)
  • The domain for an input alias is reset (by the execution of another constraint)
  • The existence rule for an input alias issues “FALSE” (by the execution of another constraint)

As the macro will frequently re-execute during its lifetime, and as you control the content of a macro, you should watch out when you use:

  • SQL connections: on top of license issues (DBMS-related licenses), you can have performance issues because an SQL connection “takes time”. To optimize these connections, make sure that they have been defined in a macro-language data source in the cameleon-ds.xml file.
  • SQL statements: poorly written SQL statements can obviously have a negative impact on performances (for instance when using multiple joins).
  • Business Data Tables: SQL statements on Business Data Tables can slow down when the tables are not optimized. The use of indexes is recommended in order to avoid table scans.
  • External databases: external databases can slow down the system because of multiple issues.
    • Connection times, distant data coming across the network, not-optimized data…everything comes into play here. Moreover, from a functional point of view, external database cannot be a part of the versioning mechanism.
  • Complex calculations: watch out for (infinite) loops, and for recursive processes. These can slow down the process.
  • Traces: macros enable the use of trace files. Tracing will write to physical files, and can degrade performances.
  • File management: macros can create, read and write physical files. Physical file manipulation can degrade performances.
  • OS calls: operating system calls (e.g. to executables) should be avoided whenever possible. These calls can degrade performances or can give concurrency problems (problems related to pieces of code which are not meant to be executed by multiple users simultaneously).

The basic idea of all of these remarks is the following:

Warning: Although the CPQ Engine executes Macro-based BRC, the performance of these BRC is driven by their quality. PROS cannot be held responsible for poorly performing macros.

Using data tables

In some cases, it can be necessary to execute SQL statements in order to retrieve data from tables. When using SQL Statements, please respect the following recommendations:

Tip:

  • Prefer the usage of Business Data Tables over externally managed tables.
  • Use indexes in order to optimize the queries on the Business Data Tables.
  • Do not query physical tables in the CPQ database with some exceptions for the Business Data Table when you need it absolutely.

Moreover, it is important to remember that Business Data Tables are versioned, so you cannot immediately access them. In order to access a Business Data Table using an SQL statement, the following syntax has to be used:

BRC DictionaryListDiscounts()

/* Look up the physical name of the BDT */

LOCAL bdtTableName “”

LET bdtTableName confML.getBdtTableName("bdtName")

/* Construct the SQL statement */

LOCAL lQuery “”

LET lQuery “SELECT COL1 FROM “ + bdtTableName + “ WHERE …”

/* Execute the SQL statement */

SQL_QUERY lQuery IN “outputTable”

/* Format the resulting array by mapping the aliases and by explicitly

defining the number of columns (the number of rows is calculated by the query

itself) */

LET outputTable[0,0] “aOutput”

LET outputTable[“NC”] 1

/* Return the table to the constraint engine */

DISPLAY “outputTable”

END_DEFINE

Tip:

  • Use the getBdtTableName(“bdtName”) function to retrieve a Business Data Table in the current workspace.
  • Use the getBdtTableName("bdtWorkspace","bdtName") function to retrieve a Business Data Table in a different workspace.

Dynamical BRC

In some cases, it may be difficult or impossible to know which output CPE will have to be issued by a BRC. In this particular case, a “dynamical BRC” may provide a solution.

Definition: A dynamical BRC is a BRC for which at least one output alias is undefined at design time.

A dynamical BRC will use a macro in order to determine, at run-time, the correct CPE to output. We will refer to these CPE as dynamical CPEs.

A dynamical BRC will be interpreted differently than a “normal” BRC. In a general case, a BRC can have N+M aliases, N “input” aliases and M “output” aliases. Whereas a Matrix-based BRC would create a constraint with “N+M” columns, a Macro-based BRC can create a constraint with a number of columns varying between “M” and “N+M”.

Let’s suppose the following example, which is designed using a regular (as in: not dynamic) Macro-based BRC. The aliases are defined as follows:

NAME CPE MUSTEXIST MUSTBEANSWERED
aEngine CPE.currentForm.FP/fpEngine.value TRUE TRUE
aPack CPE.currentForm.FP/fpPack.value TRUE FALSE
aWheel CPE.currentForm.FP/fpWheel.value TRUE FALSE

The macro itself can be written as follows:

DEFINE regularMacro()

LET outputTable[0,0] “aPack”

LET outputTable[0,1] “aWheel”

IF aEngine = “DIESEL”

/* content for DIESEL */

ELSE_IF aEngine = “GASOLINE”

/* valid combination 1 */

LET outputTable[1,0] “STANDARD”

LET outputTable[1,1] “15 INCH”

/* valid combination 2 */

LET outputTable[2,0] “STANDARD”

LET outputTable[2,1] “16 INCH”

/* valid combination 3 */

LET outputTable[3,0] “COMFORT”

LET outputTable[3,1] “17 INCH”

/* valid combination 4 */

LET outputTable[4,0] “SPORT”

LET outputTable[4,1] “18 INCH”

/* formatting output table */

LET outputTable[“NR”] 4

LET outputTable[“NC”] 2

END_IF

DISPLAY “outputTable”

END_DEFINE

In this example, “aEngine” is clearly an “input” alias (it needs to be answered before determining the values for the pack and the wheel). The resulting constraint for a “Gasoline” engine can be represented as follows:

APACK AWHEEL
STANDARD 15 INCH
APACK AWHEEL
STANDARD 16 INCH
COMFORT 17 INCH
SPORT 18 INCH

The CPQ Engine will read this constraint “by line”, meaning that each “line” is a valid combination. If the user would choose a “Sport” pack, the only possible wheel size would be “18 inch”.

Tip: A regular BRC will issue valid lines meaning that it will issue valid combinations of values between the output aliases.

Suppose now that this BRC is a dynamical BRC. The alias table would not know the “output” aliases and can be represented as follows:

NAME CPE MUSTEXIST MUSTBEANSWERED
aEngine CPE.currentForm.FP/fpEngine.value TRUE TRUE

The macro would have to determine itself which CPE to output:

DEFINE dynamicMacro()

LET outputTable[0,0] “CPE.currentForm.FP/fpPack.value”

LET outputTable[0,1] “CPE.currentForm.FP/fpWheel.value”

IF aEngine = “DIESEL”

/* content for DIESEL */

ELSE_IF aEngine = “GASOLINE”

/* first column */

LET outputTable[1,0] “STANDARD”

LET outputTable[2,0] “COMFORT”

LET outputTable[3,0] “SPORT”

LET outputTable[“NR”,0] 3

/* second column */

LET outputTable[1,1] “15 INCH”

LET outputTable[2,1] “16 INCH”

LET outputTable[3,1] “17 INCH”

LET outputTable[4,1] “18 INCH”

LET outputTable[“NR”,1] 4

/* formatting output table */

LET outputTable[“NR”] 4

LET outputTable[“NC”] 2

END_IF

DISPLAY “outputTable”

END_DEFINE

The resulting matrix would look as follows for the “Gasoline” answer:

CPE.CURRENTFORM.FP/FPPACK.VALUE CPE.CURRENTFORM.FP/FPWHEEL.VALUE
STANDARD 15 INCH
STANDARD 16 INCH
COMFORT 17 INCH
SPORT 18 INCH

In this case, you can clearly see that the header of the resulting constraint has been dynamically constructed by the macro. Also, the number of results in each column is not necessarily the same.

Most importantly, this constraint is not applied in the same way. Indeed, whereas the “normal BRC” would issue “valid combinations between all output aliases”, the “dynamical BRC” only issues “possible combinations per output alias”.

Tip: A dynamical BRC will issue individual constraints meaning that it will issue valid values for each alias independently of the other aliases.

In this example, if the user would choose a “Sport” pack, he would still be able to choose between 4 wheel sizes: “16 inch”, “17 inch” or “18 inch”.

Warning: When a dynamical BRC issues N dynamical output aliases, the CPQ Engine will apply N unitary constraints which correspond to each of the N columns.

Managing your macros

It is well known that any coding can quickly become spaghetti. In order to avoid drowning in macro code, do not hesitate to use child macros. Here’s an example of a parent macro that calls a child macro:

DEFINE parentMacro()

/* Do something */

/* Initialize variables to be passed as parameters to the child macro */

LOCAL lInput1 “something”

LOCAL lInput2 “something else”

/* Initialize variable to receive the result from the child macro */

LOCAL lChildOutput “”

LET lChildOutput childMacro(lInput1, lInput2)

/* Do something else and display end-result */

END_DEFINE

Here’s the child macro that issues a result:

DEFINE childMacro(parameter1, parameter2)

/* Do something with parameter1 and parameter2 */

/* Send back result to parent macro */

LET childMacro “the result”

END_DEFINE

Tip: For maintenance reasons, and to enhance reusability, do not hesitate to create child macros for certain pieces of code.

Java code inclusion

As mentioned before, it is possible to extend the standard functions available in the macro language by custom Java code inclusion. In order to that, the ml.javalib parameter in the cameleon.properties file will have reference the necessary class names. For instance:

ml.javalib = java.lang.Integer

Moreover, you will need to deploy the ear or jar file in the jboss target in which the Configurator has been installed.

Warning: Writing Macro-based BRC with Java code inclusion is a task that requires IT skills and cannot be attributed to a regular business user.

Example of a Macro-based BRC with Java code inclusion:

DEFINE javaCodeInclusion()

/* Initialization of a numeric variable in macro language */

LET counter 1

/* Creation of the associated Java Integer object */

LET intCounter Integer(STR counter)

/* Initialization of a string variable in macro language */

LET zipCode “32600”

/* Usage of the associated Java Integer object static method “parseInt” */

LET intZipCode Integer.parseInt(zipCode)

/* rest of the macro */

END_DEFINE

Warning: Warning: all objects that were created in a session are unreferenced with the destruction of the session. Thus they will be collected and destroy by the Java Garbage Collector if they do not have other CPQ-external references.