Formula Fields – An Advanced Guide to Manual Formula Creation

Prev Next

For most tasks, Copilot handles everything without needing code. But if you want to explore formula coding, this page is for you.

We recommend using Copilot for the best results, as it’s constantly improving with new features.

This guide offers detailed instructions and examples to help you master formula fields. If you are looking for a general guide on using formula fields or Copilot, check out this link.

Basic rules

Code: Rossum formulas use TxScript that is based on Python, in addition including a variety of additional predefined functions and variables. More information here – LINK.

No return statements: The last expression evaluated in the code is automatically used as the output.

Referring to a field on the document: Use the field. prefix to access document fields. Referencing a field name without this prefix is incorrect.

  • Example: field.invoice_id

Conditional Logic: Use Python’s if-else syntax for conditional operations. Ensure the last evaluated expression is the result you want.

Check for Field Values: Rossum provides the custom is_empty(field) function to check if a field contains a value. Use this to avoid errors when a field might be empty – returns True if the field is empty.

Default Values: Use the Rossum-provided default_to(field, default_value) function to set a fallback value when a field is empty or missing. This ensures your formula works as expected.

  • Example: default_to(field.discount_rate, 0) sets the discount rate to 0 if it’s not available.

Value Overwriting: formula fields cannot conditionally overwrite their own value — if no value is returned, the field remains empty. Ensure the formula always produces a result.

Working with Line Items:

  • Line items are rows in a table (e.g., items on an invoice). Each field within a line item is treated independently for each row.

  • Formulas applied to line items work on each row individually, not on the entire set of rows.

    • Example: field.item_quantity * field.item_price calculates the total price per line item.

  • To aggregate data across all line items, use the all_values property. This lets you access all values from a column in the line items as a list, enabling operations like summing or finding maximum values.

Avoid Circular References: A formula field should never reference itself, as this will cause circular references and errors.

Datetime Operations: Perform date and time calculations using Python’s datetime and timedelta modules. Note that timedelta, datetime, date and re (for regular expressions) are pre-imported by default. If you need other datetime functions, you must import them manually.

Messaging Functions: Formula fields let you provide direct feedback to users by displaying info, warning, or error messages.

Examples

This section gives you easy examples to help you get started.

Copying a field value

Goal: Copy the value from one field to another field.

field.total_amount

In this example, you copy the value from the total_amount field to the formula field you’re defining.

String manipulation

Goal: Convert the vendor name to uppercase.

field.vendor_name.upper()

Conditional logic

Goal: Apply a discount based on specific conditions.

20 if field.total_amount > 20000 else 0

In this example, a discount of 20 is applied if the total amount exceeds 20,000; otherwise, no discount is applied.

Datetime operations

Goal: Add 14 days to the issue date.

field.date_issue + timedelta(days=14)

Here, we’re using the timedelta function from the Python datetime module. This function is already imported by default in the formula environment, so you don’t need to import it yourself.

Line item aggregation

Goal: Calculate the total sum of the amount across all line items.

sum(field.item_total_amount.all_values)

In this case, item_total_amount is a value within a line item. Line item values have an all_values property that provides a list of all values in the column across the table.

Conditional discounted price calculation

Goal: Calculate the discounted price based on the total amount.

field.amount_total * 0.8 if field.amount_total > 20000 else field.amount_total

In this example, the discounted price is 80% of the total amount if it exceeds 20,000; otherwise, the discounted price is the same as the total amount.

Messaging functions

Formula fields let you provide direct feedback to users by displaying info, warning, or error messages.

Info messages

Use show_info("message content", field.XYZ) to display field-specific informational messages.

show_info("Invoice is coming from the EU.", field.vendor_country)

Use show_info("message content") to display document level informational messages.

show_info("Invoice is coming from the EU.")

Warning messages

Use show_warning("message content", field.XYZ) to highlight potential issues on a field level.

show_warning("This value seems unusually high.", field.invoice_total)

Use show_warning("message content") to highlight potential issues on a document level.

show_warning("This value seems unusually high.")

Error messages

Use show_error("message content", field.XYZ) to show critical errors on a field level and block document processing.

show_error("Invalid date format detected.", field.date_issue)

Use show_error("message content") to show critical errors on a document level and block document processing.

show_error("Invalid date format detected.")

Additional examples

Use invoice date as a backup invoice ID.

default_to(field.document_id, field.date_issue.strftime('%Y-%m-%d'))

Use invoice date as a backup invoice ID, but not for credit notes.

if field.document_type != 'credit_note': 
default_to(field.document_id, field.date_issue.strftime('%Y-%m-%d'))
else:
field.document_id

If there is no gross amount, use net amount. If there is no net amount, use the sum of line items.

default_to(default_to(field.amount_total, field.amount_total_base), sum(default_to(field.item_amount_total.all_values, 0)))

Remove non-alphanumeric characters from Vendor VAT ID.

substitute(r'[^a-z0-9]', '', field.sender_vat_id, flags=re.IGNORECASE)

Calculate base amount if it’s missing.

if is_set(field.amount_total_base):
field.amount_total_base
else:
field.amount_total - default_to(field.amount_total_tax, 0)

Distribute PO number into line items, but only if there is no PO on that line item.

if is_set(field.item_order_id):
field.item_order_id
else:
field.order_id

Check if sum of line items equals total amount.

line_items_sum = sum(default_to(field.item_amount_total.all_values, 0))
if round(line_items_sum, 2) != round(field.amount_total, 2):
show_warning('Sum of line items does not equal total amount!', field.amount_total)

Check if the invoice date is within 60 days from today and block automation otherwise.

if field.date_issue < date.today - timedelta(days=60):
reason = 'Invoice date is more than 60 days in the past.'
show_warning(reason, field.date_issue)
automation_blocker(reason, field.date_issue)
elif field date_issue > date.today() + timedelta(days=60):
reason = 'Invoice date is more than 60 days in the future.'
show_warning(reason, field.date_issue)
automation_blocker(reason, field.date_issue)

Check line items for negative total amounts.

for row in field.line_items:

if is_set(row.item_amount_total) and row.item_amount_total < 0:

show_error("Negative amount", row.item_amount_total)

Show an info message with the number of line items without item code.

missing_count = 0
for row in field.line_items:
if is_empty(row.item_code):
missing_count+=1
if missing_count > 0:
show_info(f'Number of line items without code: {missing_count}')