> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tesouro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TesouroScript

> Learn and explore all the possibilities of TesouroScript.

## Overview

TesouroScript is our unique declarative programming language based on JSON syntax that allows the creation of complex business logic scenarios related to [business logic objects](/finops/support/glossary#business-logic-object).

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

* Set a specific [approval policy](/finops/guides/accounts-payable/approvals/policies/index) for all [payables](/finops/support/glossary#payable) with more than \$300 worth to be approved by a specific [organization user](/finops/guides/organizations/users).
* Set a specific approval policy for all payables between \$1000 and \$5000 worth to be approved by any user with a specific [role](/finops/guides/organizations/users#create-role).

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:

```json expandable lines theme={null}
{
  "name": "Sample approval policy",
  "description": "Approval of two users required for any payables over 500 worth",
  "trigger": {
    "all": ["{event_name == 'submitted_for_approval'}", "{invoice.amount >= 50000}"]
  },
  "script": [
    {
      "call": "ApprovalRequests.request_approval_by_users",
      "params": {
        "user_ids": [
          "91bff192-1a13-4a13-a4da-a2945ed0537d",
          "ae6e88a8-c088-428c-ace2-d657bf407805",
          "c2daca46-c0cb-45a3-a3a2-bfb1e768104c"
        ],
        "required_approval_count": 2
      }
    }
  ]
}
```

A TesouroScript 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](#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`:

```json lines theme={null}
  "trigger": {
    "all": [
      "{event_name == 'submitted_for_approval'}",
      "{invoice.amount >= 50000}"
    ]
  },
```

<Tip>
  Check out some [examples of triggers](/finops/guides/accounts-payable/approvals/policies/trigger-examples)
  designed to assign automated approval policies for payables.
</Tip>

## 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:

```json lines theme={null}
"script": [
  {
    "call": "ApprovalRequests.request_approval_by_users",
    "params": {
      "user_ids": [
        "91bff192-1a13-4a13-a4da-a2945ed0537d",
        "ae6e88a8-c088-428c-ace2-d657bf407805",
        "c2daca46-c0cb-45a3-a3a2-bfb1e768104c"
      ],
      "required_approval_count": 2
    }
  }
]
```

## TesouroScript language

The elements that compose the TesouroScript language are described below:

### Connectors

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

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](#raw). Every parameter from that expression is going to be passed as an argument to the chosen connector method:

```json lines theme={null}
{
  "call": "ConnectorName.method",
  "params": <Raw expression>
}
```

The connectors can also use the [`any`](#any) and [`all`](#all) expressions:

<Tabs>
  <Tab title="any">
    ```json lines theme={null}
    {
      "run_concurrently": true,
      "any": [
        {
          "call": "ConnectorName1.method1",
          "params": <Raw expression>
        },
        {
          "call": "ConnectorName2.method2",
          "params": <Raw expression>
        }
      ]
    }
    ```
  </Tab>

  <Tab title="all">
    ```json lines theme={null}
    {
      "run_concurrently": false,
      "all": [
        {
          "call": "ConnectorName1.method1",
          "params": <Raw expression>
        },
        {
          "call": "ConnectorName2.method2",
          "params": <Raw expression>
        }
      ]
    }
    ```
  </Tab>
</Tabs>

The optional field `run_concurrently` determines, in case of multiple clauses, if the clauses will be executed at the same time, if set to `true`, or sequentially, if set to `false`. Its default value is `true`,

#### Supported connectors

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](#raw).

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:

<Tabs>
  <Tab title="mark_as_paid">
    ```json lines theme={null}
    "script": [
      {
        "call": "Payables.mark_as_paid",
        "params": {
           "payable_id": "{invoice.id}",
           "comment": "Marked as paid by an automated approval process."
        }
      }
    ]
    ```
  </Tab>

  <Tab title="approve">
    ```json lines theme={null}
    "script": [
      {
        "call": "Payables.approve",
        "params": {
           "payable_id": "{invoice.id}"
        }
      }
    ]
    ```
  </Tab>

  <Tab title="reject">
    ```json lines theme={null}
    "script": [
      {
        "call": "Payables.reject",
        "params": {
           "payable_id": "{invoice.id}"
        }
      }
    ]
    ```
  </Tab>

  <Tab title="add_tags">
    ```json lines theme={null}
    "script": [
      {
        "call": "Payables.add_tags",
        "params": {
          "tags": [
            "Marketing",
            "Website"
          ],
          "payable_id": "{invoice.id}"
        }
      }
    ]
    ```
  </Tab>
</Tabs>

#### `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:

<Tabs>
  <Tab title="request_approval_by_users">
    ```json lines theme={null}
    "script": [
      {
        "call": "ApprovalRequests.request_approval_by_users",
        "params": {
           "user_ids": ["ae6e88a8-c088-428c-ace2-d657bf407805"],
           "required_approval_count": 1
        }
      }
    ]
    ```
  </Tab>

  <Tab title="request_approval_by_roles">
    ```json lines theme={null}
    "script": [
      {
        "call": "ApprovalRequests.request_approval_by_roles",
        "params": {
           "object_id": "{invoice.id}",
           "role_ids": ["ae6e88a8-c088-428c-ace2-d657bf407805"],
           "required_approval_count": 1
        }
      }
    ]
    ```
  </Tab>
</Tabs>

#### `Currency.convert`

The `Currency.convert` method is used to convert a payable's amount to another currency, such as the organization'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.

<Tabs>
  <Tab title="Full verbose form">
    <div className="no-line-numbers">
      ```json lines theme={null}
      {
        "call": "Currency.convert",
        "params": {
          "amount": "{invoice.amount}",
          "from_currency": "{invoice.currency}",
          "to_currency": "USD"
        }
      }
      ```
    </div>
  </Tab>

  <Tab title="Shorthand form">
    ```lines theme={null}
    "{Currency.convert(invoice.amount, invoice.currency, 'USD')}"
    ```
  </Tab>
</Tabs>

The parameters are:

* `amount` - the amount to be converted. You would typically use the `{invoice.amount}` expression as the value.
* `from_currency` - the currency you want to convert from. You would typically use the `{invoice.currency}` expression as the value.
* `to_currency` - the currency you want to convert to, as a three-letter ISO 4217 currency code.

The `Currency.convert` method does not update the amount value in-place. Instead, it returns the converted amount which can be used inside other expresions. If you always need to convert the amounts to a specific currency, replace all instances of `{invoice.amount}` in your approval policy with the `Currency.convert` calls.

Because of this, `Currency.convert` is never used standalone but usually used as a [comparison operand](#binary-operation), for example:

```json expandable {6,7,8,9,10,11} lines theme={null}
"trigger": {
  "all": [
    "{event_name == 'submitted_for_approval'}",
    {
      "left_operand": {
        "call": "Currency.convert",
        "params": {
          "amount": "{invoice.amount}",
          "from_currency": "{invoice.currency}",
          "to_currency": "USD"
        }
      },
      "operator": ">=",
      "right_operand": 50000
    }
  ]
}
```

When used in the `script` part, the comparison would typically be wrapped into an [`if`](#if-statement) statement, for example:

```json expandable {3,5,6,7,8,9,10} lines theme={null}
"script": [
  {
    "if": {
      "left_operand": {
        "call": "Currency.convert",
        "params": {
          "amount": "{invoice.amount}",
          "from_currency": "{invoice.currency}",
          "to_currency": "USD"
        }
      },
      "operator": "<",
      "right_operand": 50000
    },
    "then": [ ... ],
    "else": [ ... ]
  }
]
```

#### `CreditNote`

The `CreditNote` connector has methods that allow you to modify the status of the credit note. The `params` must be declared in [raw expression](#raw).

The available methods and parameters of the `CreditNote` connector are:

* `approve(credit_note_id: uuid)`
* `reject(credit_note_id: uuid)`
* `add_tags(credit_note_id: uuid, tags: list[str])`

Examples:

<Tabs>
  <Tab title="approve">
    ```json lines theme={null}
    "script": [
      {
        "call": "CreditNote.approve",
        "params": {
           "credit_note_id": "{credit_note.id}"
        }
      }
    ]
    ```
  </Tab>

  <Tab title="reject">
    ```json lines theme={null}
    "script": [
      {
        "call": "CreditNote.reject",
        "params": {
           "credit_note_id": "{credit_note.id}"
        }
      }
    ]
    ```
  </Tab>

  <Tab title="add_tags">
    ```json lines theme={null}
    "script": [
      {
        "call": "CreditNote.add_tags",
        "params": {
          "tags": [
            "Marketing",
            "Website"
          ],
          "credit_note_id": "{credit_note.id}"
        }
      }
    ]
    ```
  </Tab>
</Tabs>

### 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.

```json lines theme={null}
{
    "if": <Boolean expression>,
    "then": [<Statement or Expression>, ...],
    "else": [<Statement or Expression>, ...]
}
```

### Expressions

The expressions used in TesouroScript 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](#any) and [all](#all) expressions.

```json lines theme={null}
[<Expression>, <Expression>, ...]
```

#### Any

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

```json lines theme={null}
{
  "any": [Expression1, Expression2, ...]
}
```

#### All

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

```json lines theme={null}
{
  "all": [Expression1, Expression2, ...]
}
```

#### Not

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

```json lines theme={null}
{
  "not": <Expression>
}
```

#### Binary operation

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

* `==`
* `!=`
* `=`
* `<` or `>`
* `<=` or `>=`

```json lines theme={null}
{
  "left_operand": <Expression>,
  "operator": "==",
  "right_operand": <Expression>
}
```

#### Name

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

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

```json lines theme={null}
{
  "name": "Event.invoice.counterparts[0].id"
}
```

#### Connector name

Evaluates to specific methods from the [connector classes](#connectors). It uses the same syntax as the [name expression](#name) 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](#connectors).

```lines theme={null}
"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.

```json lines theme={null}
{
    "<Any string>": <Expression>,
    "<Any string>": <Expression>,
    ...
}
```

#### Evaluated string

The evaluated string expression can evaluate to other expressions in the TesouroScript 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:

```lines theme={null}
"{'hello world'}"
```

Evaluates to:

```lines theme={null}
"hello world"
```

##### Name expression string

Evaluates to a [name expression](#name).

Evaluated string:

```lines theme={null}
"{Event.invoice.amount}"
```

Evaluates to:

```json lines theme={null}
{
  "name": "Event.invoice.amount"
}
```

##### Binary expression string

A one-line syntax that evaluates to a [binary operation expression](#binary-operation).

Binary expression string:

```lines theme={null}
"{83 > 11}"
```

Evaluates to `false`, which is the result of:

```json lines theme={null}
{
  "left_operand": 83,
  "operator": ">",
  "right_operand": 11
}
```

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

Evaluated string:

```lines theme={null}
"{Event.invoice.amount <= 100000}"
```

Evaluates to:

```json lines theme={null}
{
  "left_operand": {
    "name": "Event.invoice.amount"
  },
  "operator": "<=",
  "right_operand": 100000
}
```

##### Call string

A one-line syntax that evaluates to a [call expression](#connectors).

Evaluated string:

```lines theme={null}
"{Payables.pay('payable_uuid')}"
```

Evaluates to:

```json lines theme={null}
{
  "call": "Payables.pay",
  "params": {
    "payable_id": "payable_uuid"
  }
}
```

Also, this evaluated string:

```lines theme={null}
"{Payables.pay(Event.invoice.id)}"
```

Evaluates to:

```json lines theme={null}
{
  "call": "Payables.pay",
  "params": {
    "payable_id": {
      "name": "Event.invoice.id"
    }
  }
}
```

### 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:

```json expandable lines theme={null}
[
  {
      "call": "ApprovalRequests.request_approval_by_users",
      "params": {
          "user_ids": [random_user_and_role_uuid],
          "required_approval_count": 1
      }
  },
  {
      "call": "ApprovalRequests.request_approval_by_users",
      "params": {
          "user_ids": [random_user_and_role_uuid],
          "required_approval_count": 1
      }
  },
  "{Payables.approve(invoice.id)}"
]
```

#### "And" logic

A chain with an "And" condition refers to a situation where multiple conditions or actions are combined together using the [`all`](#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:

```json expandable lines theme={null}
[
  {
      "all": [
          {
              "call": "ApprovalRequests.request_approval_by_users",
              "params": {
                  "user_ids": [random_user_and_role_uuid],
                  "required_approval_count": 1
              }
          },
          {
              "call": "ApprovalRequests.request_approval_by_roles",
              "params": {
                  "role_ids": [random_user_and_role_uuid],
                  "required_approval_count": 2
              }
          }
      ]
  },
  "{Payables.approve(invoice.id)}"
]
```

#### "Or" logic

A chain with an "Or" condition refers to a situation where multiple conditions or actions are combined together using the [`any`](#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:

```json expandable lines theme={null}
[
  {
      "any": [
          {
              "call": "ApprovalRequests.request_approval_by_users",
              "params": {
                  "user_ids": [random_user_and_role_uuid],
                  "required_approval_count": 1
              }
          },
          {
              "call": "ApprovalRequests.request_approval_by_roles",
              "params": {
                  "role_ids": [random_user_and_role_uuid],
                  "required_approval_count": 2
              }
          }
      ]
  },
  "{Payables.approve(invoice.id)}"
]
```
