MoniteScript

Learn and explore all the possibilities of MoniteScript.

Overview

MoniteScript is our unique declarative programming language based on JSON syntax that allows the creation of complex business logic scenarios related to business logic objects.

Through MoniteScript, it is possible to implement customized scenarios, such as:

  • Set a specific approval policy for all payables with more than 300 EUR worth to be approved by a specific entity user.
  • Set a specific approval policy for all payables between 1000 and 5000 GBP worth to be approved by any user with a specific role.

Currently, MoniteScript supports only the payable object. More objects will be added to MoniteScript in the near future.

The sample script below defines an approval policy in which all the payables over 500 worth submitted for approval must be approved by at least two users from a given list:

1{
2 "name": "Sample approval policy",
3 "description": "Approval of two users required for any payables over 500 worth",
4 "trigger": {
5 "all": [
6 "{event_name == 'submitted_for_approval'}",
7 "{invoice.amount >= 50000}"
8 ]
9 },
10 "script": [
11 {
12 "call": "ApprovalRequests.request_approval_by_users",
13 "params": {
14 "user_ids": [
15 "91bff192-1a13-4a13-a4da-a2945ed0537d",
16 "ae6e88a8-c088-428c-ace2-d657bf407805",
17 "c2daca46-c0cb-45a3-a3a2-bfb1e768104c"
18 ],
19 "required_approval_count": 2
20 }
21 }
22 ]
23}

A MoniteScript script consists of two parts:

  • trigger: The conditions for the script execution.
  • script: The main logical statement executed.

Trigger

The trigger defines the conditions that cause the script to be executed by combining expressions with the parameters event_name and invoice (both mandatory). The only value allowed for the event_name parameter is submitted_for_approval. For the invoice parameter you can use the attributes of the invoice object such as invoice.amount, invoice.project_id, and invoice.tags.name.

According to the sample trigger below, all payables submitted for approval above 500 worth will be affected by the script:

1 "trigger": {
2 "all": [
3 "{event_name == 'submitted_for_approval'}",
4 "{invoice.amount >= 50000}"
5 ]
6 },

Check out some examples of triggers designed to assign automated approval policies for payables.

Script

The script is the main logical statement to be executed.

Below is an example of a script function. This example requires approval from two users from a given list:

1"script": [
2 {
3 "call": "ApprovalRequests.request_approval_by_users",
4 "params": {
5 "user_ids": [
6 "91bff192-1a13-4a13-a4da-a2945ed0537d",
7 "ae6e88a8-c088-428c-ace2-d657bf407805",
8 "c2daca46-c0cb-45a3-a3a2-bfb1e768104c"
9 ],
10 "required_approval_count": 2
11 }
12 }
13]

MoniteScript language

The elements that compose the MoniteScript language are described below:

Connectors

A connector represents a business logic object as a class in the MoniteScript language. The connector has a group of methods to deal with a certain piece of functionality in MoniteScript.

The call clause is followed by the connector method that is going to be called. Optionally accepts params whose values are instances of a raw expression. Every parameter from that expression is going to be passed as an argument to the chosen connector method:

1{
2 "call": "ConnectorName.method",
3 "params": <Raw expression>
4}

The connectors can also use the any and all expressions:

1{
2 "any": [
3 {
4 "call": "ConnectorName1.method1",
5 "params": <Raw expression>
6 },
7 {
8 "call": "ConnectorName2.method2",
9 "params": <Raw expression>
10 }
11 ]
12}

The supported connectors are Payables, ApprovalRequests, and Currency.

Payables

The Payables connector has methods that allow you to modify the status of the payable. The params must be declared in raw expression.

The available methods and parameters of the Payables connector are:

  • mark_as_paid(payable_id: uuid, comment: str)
  • approve(payable_id: uuid)
  • reject(payable_id: uuid)
  • add_tags(payable_id: uuid, tags: list[str])

Examples:

1"script": [
2 {
3 "call": "Payables.mark_as_paid",
4 "params": {
5 "payable_id": "{invoice.id}",
6 "comment": "Marked as paid by an automated approval process."
7 }
8 }
9]

ApprovalRequests

The ApprovalRequests connector is used to require approvals from users or roles. It has the following methods:

  • request_approval_by_users(object_id: uuid, user_ids: list[uuid], required_approval_count: int)
  • request_approval_by_roles(object_id: uuid, role_ids: list[uuid], required_approval_count: int)

Examples:

1"script": [
2 {
3 "call": "ApprovalRequests.request_approval_by_users",
4 "params": {
5 "user_ids": ["ae6e88a8-c088-428c-ace2-d657bf407805"],
6 "required_approval_count": 1
7 }
8 }
9]

Currency

The Currency connector is used to convert a payable’s amount to another currency, such as the entity’s preferred currency. This lets you create trigger conditions that compare the amount due of payables in various currencies to the same threshold amount in the base currency. The currency exchange rate used is from the day when the trigger is executed.

Its sole method convert has the following parameters:

convert(amount: int, from_currency: str, to_currency: str)
  • amount - the amount to be converted. You would typically use {invoice.amount} as the value.
  • from_currency - the currency you want to convert from. You would typically use {invoice.currency} as the value.
  • to_currency - the currency you want to convert to, as a three-letter ISO 4217 currency code.

Example:

1"trigger": {
2 "all": [
3 "{event_name == 'submitted_for_approval'}",
4 {
5 "left_operand": {
6 "call": "Currency.convert",
7 "params": {
8 "amount": "{invoice.amount}",
9 "from_currency": "{invoice.currency}",
10 "to_currency": "USD"
11 }
12 },
13 "operator": ">=",
14 "right_operand": 50000
15 }
16 ]
17}

If statement

The if statement consists of three clauses: if, then, and else. An if clause can contain any expression that evaluates to a boolean value, while then and else clauses contain a JSON array of other statements. The else clause is optional.

1{
2 "if": <Boolean expression>,
3 "then": [<Statement or Expression>, ...],
4 "else": [<Statement or Expression>, ...]
5}

Expressions

The expressions used in MoniteScript are:

Primitives

Primitives expressions include all JSON primitive types:

  • null
  • boolean
  • number
  • string

Array

Evaluates to a JSON array of values, each one of which is also an expression. It is used inside any and all expressions.

1[<Expression>, <Expression>, ...]

Any

Returns true if at least one of the values in its array expression is true. For example, {"any": [true, false, false]} evaluates to true.

1{
2 "any": [Expression1, Expression2, ...]
3}

All

Returns true only if all of the values in its array expression are true. For example, {"all": [true, false, false]} evaluates to false.

1{
2 "all": [Expression1, Expression2, ...]
3}

Not

Evaluates to a boolean that is the complement of its inner expression.

1{
2 "not": <Expression>
3}

Binary operation

Compares two operands and evaluates the result of the operation. The following operators are supported:

  • ==
  • !=
  • =
  • < or >
  • <= or >=
1{
2 "left_operand": <Expression>,
3 "operator": "==",
4 "right_operand": <Expression>
5}

Name

Retrieves the values of the variable Event, which has all the information about the event that triggered the execution of a MoniteScript script.

Name expression supports the basic operations of access on the object: square bracket indexing (object_list[0]) and dot access (object.attribute).

1{
2 "name": "Event.invoice.counterparts[0].id"
3}

Connector name

Evaluates to specific methods from the connector classes. It uses the same syntax as the name expression with the only difference being that it evaluates to a non-JSON object.

As a result, connector name expression can only be used within call expressions.

"Payables.get"

Raw

Evaluates to a JSON object. It is only necessary because many other expressions are also JSON objects. The raw expression exists to distinguish expressions from regular JSON objects.

1{
2 "<Any string>": <Expression>,
3 "<Any string>": <Expression>,
4 ...
5}

Evaluated string

The evaluated string expression can evaluate to other expressions in the MoniteScript language. It allows writing simple scripts without delving into the complexities of binary operations and calls.

They are regular JSON strings wrapped in { }, to distinguish them from non-evaluated strings.

The different types of evaluated string are:

Primitive string

Evaluates to a simple string.

Evaluated string:

"{'hello world'}"

Evaluates to:

"hello world"
Name expression string

Evaluates to a name expression.

Evaluated string:

"{Event.invoice.amount}"

Evaluates to:

1{
2 "name": "Event.invoice.amount"
3}
Binary expression string

A one-line syntax that evaluates to a binary operation expression.

Binary expression string:

"{83 > 11}"

Evaluates to false, which is the result of:

1{
2 "left_operand": 83,
3 "operator": ">",
4 "right_operand": 11
5}

However, evaluated strings are recursive, so the following is supported:

Evaluated string:

"{Event.invoice.amount <= 100000}"

Evaluates to:

1{
2 "left_operand": {
3 "name": "Event.invoice.amount"
4 },
5 "operator": "{=",
6 "right_operand": 100000
7}
Call string

A one-line syntax that evaluates to a call expression.

Evaluated string:

"{Payables.pay('payable_uuid')}"

Evaluates to:

1{
2 "call": "Payables.pay",
3 "params": {
4 "payable_id": "payable_uuid"
5 }
6}

Also, this evaluated string:

"{Payables.pay(Event.invoice.id)}"

Evaluates to:

1{
2 "call": "Payables.pay",
3 "params": {
4 "payable_id": {
5 "name": "Event.invoice.id"
6 }
7 }
8}

Chain logic

Chain logic allows you to perform a series of sequential actions on a script, where each action is dependent on the previous one, without needing to write separate lines of code for each action. There are three types of chain logic:

Straight chain

On a straight chain, the first action must be executed before the script continues to the next. In the example below, the first approval must be performed before the second one, so the payable approval can be concluded:

1[
2 {
3 "call": "ApprovalRequests.request_approval_by_users",
4 "params": {
5 "user_ids": [random_user_and_role_uuid],
6 "required_approval_count": 1
7 }
8 },
9 {
10 "call": "ApprovalRequests.request_approval_by_users",
11 "params": {
12 "user_ids": [random_user_and_role_uuid],
13 "required_approval_count": 1
14 }
15 },
16 "{Payables.approve(invoice.id)}"
17]

“And” logic

A chain with an “And” condition refers to a situation where multiple conditions or actions are combined together using the all operator, and all of those conditions must be true or actions must be successfully executed for the overall chain to be considered successful.

In the example below, all approval requests are sent and their approval is required to conclude the payable approval:

1[
2 {
3 "all": [
4 {
5 "call": "ApprovalRequests.request_approval_by_users",
6 "params": {
7 "user_ids": [random_user_and_role_uuid],
8 "required_approval_count": 1
9 }
10 },
11 {
12 "call": "ApprovalRequests.request_approval_by_roles",
13 "params": {
14 "role_ids": [random_user_and_role_uuid],
15 "required_approval_count": 2
16 }
17 }
18 ]
19 },
20 "{Payables.approve(invoice.id)}"
21]

“Or” logic

A chain with an “Or” condition refers to a situation where multiple conditions or actions are combined together using the any operator, and at least one of those conditions must be true or actions must be successfully executed for the overall chain to be considered successful. Once one of the conditions returns true, the next ones will be canceled.

In the example below, only one type of approval is required to conclude the payable approval:

1[
2 {
3 "any": [
4 {
5 "call": "ApprovalRequests.request_approval_by_users",
6 "params": {
7 "user_ids": [random_user_and_role_uuid],
8 "required_approval_count": 1
9 }
10 },
11 {
12 "call": "ApprovalRequests.request_approval_by_roles",
13 "params": {
14 "role_ids": [random_user_and_role_uuid],
15 "required_approval_count": 2
16 }
17 }
18 ]
19 },
20 "{Payables.approve(invoice.id)}"
21]