Functions and subroutines help define logic in one place, and allow you to use that logic in many other places throughout your rules.By placing your logic into a function or subroutine, you can keep your code "DRY" by following the best practice of "Don't Repeat Yourself". As in other programming languages, Snap functions and subroutines can both accept input parameters, and functions can return an output value.
Function Example 1
The function is defined in the definition statement in the Start block. From there it can be used in the code below it.

Function Example 2
Certain functions -- such as those who make database queries, web service calls, or refer to safe functions – cannot be called using the same "call function" block as in Example 1. Instead, they must be called using this similar-looking "call function... returns" block. If you do not see your function in the drop-down list provided by the call function block shown in Example 1, try finding it in this block instead shown in Example 2.

Subroutine Example 3
Subroutines are the same as functions, except they do not return a value. Instead, they perform actions. In this example, it processes a car object such that the car's price attribute is rounded to a whole number.

Communication Between Your Code and a Subroutine / Function
The Snap language supports both primitive types (expressed like strings or numbers) and complex types, usually expressed as objects.
-
Primitive types are passed by value.
In example 1, the function gets a separate, independent copy of the basic data values of 1 and 2. These values, along with any other variables declared in the function, disappear at the end of the function's life.
-
Objects are passed by reference.
In example 3, however, "car" passed into the subroutine is an object, since it was created from a database query. We see the car object has a price property, and may have many other properties as well. Objects are not passed by value, like primitive strings or numbers. Objects are passed by reference, meaning the entire original object is passed into the function or subroutine, not an independent copy of it. Because of this, changes made to that object by the function/subroutine remain, even after the function ends.
When writing a function or subroutine in Snap, consider which location for your library would be best.
| Benefit | Safe Function |
Global Rule |
Global Function |
|---|---|---|---|
| Logic available to entire environment |
|
||
| Logic available through the REST API |
|
||
| Logic hidden |
|
||
| Logic available to only this configurator/scene |
|
|
|
| Calculates faster, without network lag |
|
|
|
| Can write directly to UI elements |
|
|
|
| Can read directly from UI elements |
|
Storing your Subroutine / Function
Epicor CPQ has different places where you can store reusable logic. Which one is best? While every application is different, consider these guidelines:
| What does the function do? | From where is it called? | The best location for this function |
|---|---|---|
| Snap code that can run locally | From only one place, in only one configurator. |
Define your function in that same rule type, so it's easy to maintain. For example, a function to help a value rule work can be defined at the top of that value rule, or in any preceding value rule. |
| Snap code that can run locally | From many places in only one configurator. |
Define your function in a "global" rule type, so it's visible throughout the configurator. |
| Snap code that can run locally | From many configurators. |
For faster user experience: For easier administrator experience: |
|
Snap code that can only be performed on a server, such as
|
Anywhere | Define it as a safe function. Remember, safe functions can be called from anywhere. So if your configurator needs information from a database, simply query the database from a safe function, and then call that safe function from your configurator. Here's a walkthrough example. |
Keep your application secure.
Remember the Snap rules you write for your configurator or scene are executed in one of two places: either server-side within the cloud, or client-side within the user's browser. Consider the best location to balance needs for security and speed.
-
Server Side
Server-side code is executed on the server, and the results sent to the client. The Snap code itself is never sent to the user's device for execution. Server-side code is best for any proprietary calculations or patented, sensitive lookups. However, network latency does make this code slower.
Examples: pricing rules, workflow rules, safe functions, global functions. -
Client Side
Most other code is processed within the web browser on the user's device. Local to the user with no network latency, client-side code is fast and responsive. However, it is not as secure: some users could expose the logic or comments within the rule, as they can with any client-side javascript. Local rules are the best place for code which runs often, needs to be fast, and does not contain sensitive calculations or comments.
Examples: configurator rules, scene rules, global rules, etc.
You can easily mix and match your code, placing sensitive functions on the server and other code locally. Learn more about where Snap rules are executed.