MobileForce Expressions: Language and Usage in Low Code Application Development

The Visual software development constructs in MobileForce Low-code/No-code Platform empower the citizen developer to visually create and connect with drag and drop, application components to easily create powerful enterprise applications. However, there are often situations when someone with development skills (for example, a business analyst) that go beyond the citizen developer, would like to rapidly construct custom forms or UI elements using expressions to combine existing constructs in a powerful yet fairly composable manner. It is precisely for this purpose that MobileForce Expressions are created as a a powerful enhancement to the MobileForce Low-code/No-code platform.

MobileForce expressions extend the automation capabilities of the MobileForce Platform in ways that otherwise could only be achieved by an enterprise software developer, but without the learning and implementation overhead of a software development environment. The common expression language defined here is the common foundation, used to create powerful automation features in several different MobileForce products such as CPQ, Forms, and potentially even Field Service.

Simply put, MobileForce expressions enhance forms with dynamic functionality that enable dynamic (i.e., at run-time) changes to the value of form fields and sections, and in general any form elements. Furthermore, MobileForce expressions also support dynamic modification to the visibility and editability of form elements. MobileForce expressions are easy to write using plain text fields within various elements of MobileForce Applications such as CPQ, and potentially also Field Service. Within MobileForce CPQ, expressions are specifically used to enhance Rules, Triggers, Macros, Email Templates etc.

When used in CPQ rules, MobileForce expressions specify a trigger condition for a configuration, pricing, or approval rule. When the trigger condition evaluates to true, the action for that rule is executed. When used in macro variables, one can specify a MobileForce expression instead of a variable. Please note that MobileForce expressions are much more likely to be used in forms and CPQ rules, and rather lightly used in Macros.

MobileForce expressions do not exist on their own. Instead, they are treated as attributes of other components such as fields, form sections, or CPQ rules.

In forms, MobileForce expressions are used for:

In Quotes, MobileForce expressions are also used for:

MobileForce expressions are almost invariably computed on the client side: the dynamic evaluation of expressions enables the Form elements within which MobileForce expressions are used to be highly responsive. Form expressions are evaluated locally in the form element to which they are tied, and make no changes to the surrounding context outside the form element. Hence their evaluation does not result in any side effects. MobileForce expressions include not only traditional values, identifiers, and variables combined using operators, but also include some pre-defined functions that enhance expressions. It must be noted that there are a select few pre-defined functions, specifically the LOOKUPVALUE() functions, which are actually evaluated on the server side, typically as a result of invocation of database operations, but other than these functions, MobileForce expressions are invariably computed on the client side. Please note also that Lookup functions are not supported in CPQ rules or macro variables.

MobileForce Expression Language (MFEL)

Often, expressions in MobileForce are used in forms, CPQ rules, triggers, macros, email templates, etc. All of these uses build upon MFEL in unique ways. In order to define the MobileForce Expression Language, we start with primitives:

1. Primitive Values

The following primitive values are used in both Forms Formula and Expressions as well as CPQ Formula, Rules, Expressions, Functions and Variables.

The only supported primitive values are strings, numbers, and JSON key/value objects. Numbers will be represented internally as double precision numbers. Strings will be automatically converted to numbers when necessary and vice versa. There is no boolean type. A value is considered to be false if it is the empty string, zero or the string “0”. Otherwise, it is considered to be true.

A JSON key/value object is a string that follows the JSON object format. The JSON object must have a “key” and a “value” field. An example JSON key/value object is {"key": 1, "value": "One"}. The “key” field contains the actual or stored value while the “value” field contains the user-displayable string. JSON key/value objects are often used for picklist inputs. For example, a “user” picklist input may have its database ID stored in the “key” field and the user’s name in the “value” field.

A JSON key/value value in any arithmetic or conditional expression will be automatically converted to its key. For example ‘{“key”: 1, “value”: “One”}’ + 3 will be evluated to 4.

2. Form Formula

formula              =  "=" expression

To allow the dynamic updating of certain fields based on the data provided by other fields, expressions can be used in some of the input attributes. These expressions will always start with a ‘=’ character and are inspired-by/use a subset of MS Excel formula syntax. Only string and numeric scalar data types are supported together with a limited set of allowed functions. The input fields of formula can be referenced by their names. A formula can only reference input fields that have been specified previously the current input.

Formulae are allowed in the “value” attribute of inputs of type “readonly” and “hidden”. Formulae can also occur in the “hidden” and “disabled” attributes for inputs.

Formulae can be used in FormSection attributes, specifically the hidden and lock attributes. Formulae can also be used in a Row Element or Col Element which represent a row or a column of fields in a section. Specifically, the hidden attributes of these elements can include a formula. Formula can also be used in the Input Attributes of an Input Element, specifically within the readonly type field, the hidden and value fields, disabled attribute, the invalidmessage attribute, the listitemfilter, the readonly and the validate attributes,

Now we can define Form expressions as follows:

2.1. Form Expressions

expression           =  "(" *WSP expression *WSP ")"
expression           =/ prefix-operator *WSP expression
expression           =/ expression *WSP infix-operator *WSP expression
expression           =/ expression *WSP postfix-operator
expression           =/ constant
expression           =/ variable
expression           =/ function

prefix-operator      =  "-" / "!"

postfix-operator     =  "%"  ; Percent operator.  Divides value by 100

infix-operator       =  arithmetic-operator
infix-operator       =/ concatenate-operator
infix-operator       =/ comparison-operator
infix-operator       =/ logical-operator

arithmetic-operator  =  "^" / "*" / "/" / "+" / "-"

concatenate-operator =  "&"

comparison-operator  =  "=" / "==" / "<>" / "!=" / "<" / "<=" / ">" / ">=" / "===" / "!=="

logical-operator     =  "&&" / "||"

constant             =  numeric-constant / string-constant

numeric-constant     =  1*DIGIT ["." 1*DIGIT]
numeric-constant     =/ "." 1*DIGIT

string-constant      =  DQUOTE *CHAR DQUOTE  ; Double quote chars are escaped via '""'
string-constant      =/ "'" *CHAR "'"  ; Single quote chars are escaped via "''"

variable             =  name
variable             =/ variable *WSP "[" *WSP expression *WSP "]"
variable             =/ variable *WSP "." *WSP name

function             =  name *WSP "(" *WSP expression *(*WSP "," *WSP expression ) *WSP ")"

name                 =  (ALPHA / "_") *( ALPHA / DIGIT / "_")

Note: You can now use ‘${expr}’ macros in any string literal in form expressions. So rather than typing “id = ‘” & id & “‘”, you can now use “id = ‘${id}’” instead.

2.2. Form Built-in Functions

The following built-in functions are supported in MobileForce expressions:

JOIN(separator, array[, nonempty]): Returns a string that is a concatenation of the given array values separated by the given separator string. For example, JOIN(',', ['A', 'B', 'C']) will return ‘A,B,C’. If the second argument is not an array, this function will return it unchanged. By default, empty elements will be shown between delimiters; unless you set third optional parameter to 1, in which case empty elements won’t show.

For example, THERE_EXISTS(line_items, x, x.cpq_code = ‘prod-code-x’ && x.a > 20)

Note: A condition such as x.cpq_code = ‘prod-code-x’ needs to be added to filter by the product code because the THERE_EXISTS function would generate an error if it runs on a product that doesn’t have field ‘a’.

Array Manipulation Expressions FILTER(), MAP(), and REDUCE() operations are now available in form expressions to support array manipulation.

SUM(FILTER(line_items, x, x.cpq_code = ‘widget’), ‘cpq_net_total_price’)

The above is a more readable equivalece to the following expression.

REDUCE(MAP(FILTER(line_items, x, x.cpq_code = ‘widget’), x, x.cpq_net_total_price), x, y, x + y, 0)

Set-product-line-items and set-quantity rule actions require you to specify the product they apply to. They use the product to determine the correct line item.

In addition to forms, MobileForce expressions are also used in CPQ as follows:

3. CPQ Formula, Rules, Expressions, Functions and Variables

Products and Quotes may have one or more rules that are used in Configuration, Pricing and Approvals. A rule specifies an automated action that should be performed on a product or quote when some event occurs on that product or quote.

3.1. CPQ Rule Formula and Expressions Syntax

CPQ Rule expressions can be composed using a combination of built-in system variables, user-specified custom variables, built-in functions and built-in operators (logical and arithmetic). Rule expressions are modeled after the familiar Microsoft Excel formula syntax. More exactly, only string and numeric scalar data types are supported and the set of allowed functions are listed below. The variables in the formula can be referenced by their names. A formula can only reference variables that are either built-in system variables listed below or input field names that are specified in Quote UI Layout or a Product UI Layout.

The grammar for formula is as follows. This grammar is written in Augmented Backus-Naur Form.

formula              =  "=" expression

expression           =  "(" *WSP expression *WSP ")"
expression           =/ prefix-operator *WSP expression
expression           =/ expression *WSP infix-operator *WSP expression
expression           =/ expression *WSP postfix-operator
expression           =/ constant
expression           =/ variable
expression           =/ function

prefix-operator      =  "-" / "!"

postfix-operator     =  "%"  ; Percent operator.  Divides value by 100

infix-operator       =  arithmetic-operator
infix-operator       =/ concatenate-operator
infix-operator       =/ comparison-operator
infix-operator       =/ logical-operator

arithmetic-operator  =  "^" / "*" / "/" / "+" / "-"

concatenate-operator =  "&"

comparison-operator  =  "=" / "==" / "<>" / "!=" / "<" / "<=" / ">" / ">=" / "===" / "!=="

logical-operator     =  "&&" / "||"

constant             =  numeric-constant / string-constant

numeric-constant     =  1*DIGIT ["." 1*DIGIT]
numeric-constant     =/ "." 1*DIGIT

string-constant      =  DQUOTE *CHAR DQUOTE  ; Double quote chars are escaped via '""'
string-constant      =/ "'" *CHAR "'"  ; Single quote chars are escaped via "''"

variable             =  name
variable             =/ variable *WSP "[" *WSP expression *WSP "]"
variable             =/ variable *WSP "." *WSP name

function             =  name *WSP "(" *WSP expression *(*WSP "," *WSP expression ) *WSP ")"

name                 =  (ALPHA / "_") *( ALPHA / DIGIT / "_")

Note: You can now use ‘${expr}’ macros in any string literal in form expressions. So rather than typing “id = ‘” & id & “‘”, you can now use “id = ‘${id}’” instead.

3.2. Built-in CPQ Functions

The following standard Microsoft Excel-like functions are supported:

In addition, the following CPQ-specific functions are supported:

3.3. Built-in CPQ Formula Variables

One can access any attribute, group, or CPQ-computed value using the formula and naming syntax described in the previous section. In the CPQ system, all the variables and values in the entire Quote is stored as a JSON object with nested fields. The top level JSON object will contain data for the entire quote. Each attribute or product group (line items array) defined for the quote will be a field in this JSON object. So, a standard JSON notation can be used to refer to any variable contained in the quote using nested level references for hierarchy. In general, for non-primitive system or input variables, say, arrays, a JSON-object notation can be used to refer to variables inside them. For example: line_items[1].cpq_quantity will refer to the value of the Quantity field of the 2nd line item added in the Quote. By convention, the names of all CPQ-specific inputs will be prefixed with or contain ‘cpq_’. Users are free to define their own inputs (variables) in Product UI Layouts or Quote UI Layouts with names that do not have ‘cpq_’ in them.

Additionally, the following special variables are defined

3.3.1. Quote Level System Variables

The quote object will have the following CPQ specific fields:

3.3.2. Product Group (Line Items in a Quote) System Variables

A product or quote can have one or more product groups, each of which can contain one or more products. The data for a product group named ‘{PG}’ will be stored in fields starting with ‘{PG}’. CPQ specific fields will start with ‘{PG}_cpq_’. For example, the ID field for a product group named ‘line_items’ will be stored in ‘line_items_cpq_id’. These fields are as follows.

Each configured product is stored as a JSON object. Each attribute or product group defined for the product will be a field in this JSON object.

The CPQ specific fields for a product will be stored in the following fields:

Additionally, the following special variables are defined

3.3.4. OpenTBS Variables in Document Templates

The same JSON-style references to variables can be used in the Microsoft Word or other supported Office documents, as placeholders for their respective values in Quote and Contract Document Templates. The CPQ system will process and substitute these variables with their respective values at run-time during the production of a finalized document. Refer to the Quote and Contract Documents section above for more details.

3.4 Expression usage in CPQ

MobileForce Expressions can be used in the following context within CPQ

4. Lookup Functions

Finally, we define Lookup functions, which are actually evaluated on the server side (and not the client side). Due to this distinction, lookup functions are extremely powerful, and if used incorrectly, say by the novice developer, could cause serious unstability and even crash MobileForce Applications. Thus, lookup functions should be used with caution, and only by expert developers.

4.1 Lookup Table

LOOKUP(table [,filter [, column]]) – Lookup up a value from a different datatable table. Can be used only in external picklist fields. Not really used anymore. Only works for form expressions in datatable forms for form apps.

4.2 Lookup Value

LOOKUPVALUE(table [,filter [, column]]) – Lookup up a value from a different datatable table. Can be used in any field, except for some external picklist fields. Only works for form expressions in datatable forms for form apps. By far, this is the most commonly used of the LOOKUP* functions.

4.3 Lookup Key/Value

LOOKUPKEYVALUE(table [,filter [, column]]) – Lookup up a value from a different datatable table. Meant solely for setting values of external picklist fields. Only works for form expressions in datatable forms for form apps.

4.4 DTLookupValue : DataTable

DTLOOKUPVALUE(param-prefix, table [, filter [, column]]) - Lookup up a value from a different datatable table, using a datatable adapter different from the current datatable screen. Can be used in datatable forms, form apps, form-config forms, and CPQ quotes.

5 Examples of MobileForce Expressions

In CPQ Product Rules Condition :

HAS_PROD('7782') && ! HAS_PROD('7779') && (opportunity_type == 'New Business')

(PROD_QTY('7783') > 1 && PROD_QTY('7783') < 50)
	
HAS_PROD('7789') && (! HAS_PROD('7779') || ! HAS_PROD('7792'))
 
 As of Q2, 2022: comparisons also include identity comparison operators, which does comparisons **without** conversions.  
 So “1” == 1 is true but “1" === 1 is false.

(HAS_CAT('TimeClock: Accessories (NR)') || HAS_CAT('TimeClock: Parts (NR)') || HAS_CAT('TimeClock: Sage (NR)') || 
HAS_CAT('TimeClocks: NXG G2+ Ethernet Clock (NR)') || HAS_CAT('TimeClocks: NXG G2+CN - Cellular Biometric Clock (NR)') || 
HAS_CAT('TimeClocks: NXG G2+EFN - Ethernet Biometric Clock (NR)') || 
HAS_CAT('TimeClocks: NXG G2+WFN - WiFi Biometric Clock (NR)') || 
HAS_CAT('TimeClocks: NXG G2+WN - WiFi Clock (NR)') || HAS_CAT('TimeClocks: NXG G7 Ethernet Biometric Clock (NR)') || 
HAS_CAT('TimeClocks: NXG G7 Ethernet Clock (NR)') || HAS_CAT('TimeClocks: NXG LE (Ethernet) (NR)') || 
HAS_CAT('TimeClocks: NXG LE (Wifi) (NR)') || HAS_CAT('TimeClocks: Touch Clock (NR)') || 
HAS_CAT('TimeClocks: Velocity 800 (NR)') || HAS_CAT('TimeClocks: Velocity 850 (NR)') || 
HAS_CAT('TimeClocks: Virtual Clock (NR)') || HAS_CAT('TimeForce (NR)')) && ((opportunity_type == 'New Business') && HAS_PROD('7796'))

You can now use ‘${expr}’ macros in any string literal in form expressions.  So rather than typing “id = ‘” & id & “‘”, you can now use “id = ‘${id}’” instead.

In Quote UI Layout Form access-control hide-condition

In Quote: 

!(HAS_PROD('3709') || HAS_PROD('3712') || HAS_PROD('3706') || HAS_PROD('3714') || HAS_PROD('5409') || HAS_PROD('5410') || HAS_PROD('5408') || HAS_PROD('5407')  || HAS_PROD('5413') || HAS_PROD('5414') || HAS_PROD('5412') || HAS_PROD('5411'))

In Generated Docs: 

cpq_approval_needed || cpq_approval_in_progress || !contact || is_primary == 'no'

In Layouts: making fields READ-ONLY for specific roles: Note the use of single quote (') to demarcate roles since roles can have blank spaces.

ACLMATCH(‘role1’)
!ACLMATCH(‘role2')
ACLMATCH(‘role1 | role2’)
ACLMATCH(‘role1 & role2’)
In Quote UI Layouts: Access control to make fields visible or hidden or conditional based on roles that can be combined using MobileForce expressions, i.e., using and(&) as well as or( ).

In the case of conditional visibility , a condition can further be specified, which is based on expressions that are created using variables that are previously defined in the same CPQ form element.

 As an example, we can control the visibility of a section based on the ACL that expresses that the product 3709 or 3712 have NOT been selected.

!(HAS_PROD('3709') || HAS_PROD('3712'))

Example: the quote_summary section is made visible only for roles 'admin' or 'sales-admin' by writing the Visibility ACL as:

admin | sales-admin

Example: the discount field is made visible only if the total


In CPQ Pricing Rules Condition

benefits_pricing == 'minimum'
is_network_opportunity == 'yes'

In Approval Rules and Approvers

term_years == '4' || term_years == '5'

ARRAY_MAX(line_items, 'cpq_user_discount') > 40 && (!HAS_CAT('Advanced Service (NR)')) && (!HAS_CAT('Advanced Service'))

SVP Approval Group (Individual Product >40% Discount))

5.1 Using quotes around expressions

The following is an incorrect use of DTLOOKUPVALUE

DTLOOKUPVALUE(‘easyclm-‘,’MFCPQProduct’,’code=@test_product_code’,’id’))

The problem is that the value of the ‘code=@test_product_code’ filter is not surrounded by quotes. Hence if it is the empty string, the filter expands to ‘code=‘, which is not a legal SQL where expression

To fix this error, the correct expression syntax is:

DTLOOKUPVALUE(‘easyclm-‘,’MFCPQProduct’,’code=’’@test_product_code’’’,’id’).

There are two ways you can put a quote in a string.

Strings can be surrounded by either single or double quotes. So you can just use the other kind of quote. For example “id=‘@id’”. One can escape out a quote by entering it twice. Two single quotes in a row or double quotes in a row will be printed out a single quote character. For example ‘id=‘’@id’’’

Here is another example of DTLOOKUPVALUE with correct quotes around expressions:

DTLOOKUPVALUE(‘easyclm-‘,’MFCPQPriceBookItem’,’productId=’’@test_product_id’’’,’listPrice’))