Documentation

Invoice Scripts

Table of Contents

Overview

Invoice Scripts are powerful tools that allow you to customize how invoice items are generated when a reservation is added to an invoice. By attaching an Invoice Script to a resource, you can dynamically modify invoice items, add additional items, or even remove items entirely based on custom business logic.

When Invoice Scripts Run

Invoice Scripts are triggered when:

  • A reservation for a resource with an attached Invoice Script is added to an invoice
  • The system automatically processes reservations for invoicing

Context Variables

Invoice Scripts have access to the following global variables:

  • invoice: Information about the invoice being generated
  • invoice_items: The default invoice items that would be created without scripting
  • reservation: Contains all details about the reservation being invoiced
  • parent_reservation: Information about the parent reservation when the current reservation is part of a multi-resource reservation

Output Format

An Invoice Script must return a dictionary with the following structure:

invoice (Optional)

A dictionary containing modifications to make to the invoice. This can include:

Field Type Description
bill_to_user_id string 32-character alphanumeric ID of the user to bill
bill_to_name string Name to display on the invoice
bill_to_email string Email address for billing
bill_to_account_number string Account number for the billed party
bill_to_address string Billing address
memo string Memo to display on the invoice
payment_instructions string Instructions for payment
purchase_order_number string PO number reference
invoice_address string Address of the invoicing entity
hidden_note string Internal note (not visible to customer)
due_date string Due date in ISO format (YYYY-MM-DD)
invoice_date string Invoice date in ISO format (YYYY-MM-DD)
deposit_amount float Deposit amount applied to the invoice
status string Status of the invoice
status_detail string Additional status details

invoice_items (Optional)

A list of invoice items to include. Each item is a dictionary with:

Field Type Description
date string Date for the item in ISO format (YYYY-MM-DD)
item string Short name of the item
sku string Stock Keeping Unit identifier
description string Detailed description of the item
units float/integer Quantity of units
unit_rate float/integer Price per unit
units_name string Name of the unit (e.g., "hours", "sessions")
tax_rate integer Tax rate to apply stored as 10000 * rate (e.g. 13.4% stored as 134000)
tax_code string Tax code for the item
tax_note string Additional tax information

Note

Tax rates are stored as 10000 * rate where rate is the percentage rate of a tax rate. For example, a tax rate of 13.4% is stored as 10000 * 13.4 = 134000.

Return Behaviour

  • If you return {} (empty dictionary): The default invoice item will be used
  • If you return {'invoice_items': []} (empty list): No invoice items will be created (removes default item)
  • If you don't include invoice_items in the return: The default invoice item will be used
  • If you include invoice_items with items: Only the items you specify will be used (replaces default)

Examples

Example 1: Modifying the Default Invoice Item

# This script modifies the description and adds a tax rate to the default invoice item

# Get information from the reservation
resource_name = util.dicts.get(d=reservation, path='reservable.name')
hours_reserved = util.dicts.get(d=reservation, path='duration') / 3600

# Create a more detailed description
detailed_description = "Reservation of " + str(resource_name) + " for " + str(hours_reserved) + " hours"

# Get the default invoice item (assuming it's the first item in the list)
modified_item = invoice_items[0]

# Update fields in the default item
modified_item = modified_item + {'description': detailed_description}
modified_item = modified_item + {'tax_rate': 70000}  # 7% tax rate
modified_item = modified_item + {'tax_code': 'EQUIPMENT-7'}

# Return the modified item
return {
    'invoice_items': [modified_item]
}

Example 2: Adding Multiple Invoice Items

# This script splits a reservation into multiple invoice items 
# (e.g., base fee plus hourly charges)

# Extract required information
duration_hours = util.dicts.get(d=reservation, path='duration') / 3600
resource_name = util.dicts.get(d=reservation, path='reservable.name')
base_rate = 50.00
hourly_rate = 25.00

# Create list for our invoice items
items = []

# Add base fee item
base_item = {
    'item': 'Base Fee',
    'description': "Setup fee for " + str(resource_name),
    'units': 1,
    'unit_rate': base_rate,
    'units_name': 'fee',
    'tax_rate': 70000
}
items = items + [base_item]

# Add hourly charges item
hourly_item = {
    'item': 'Usage Fee',
    'description': "Usage of " + str(resource_name) + " (" + str(duration_hours) + " hours)",
    'units': duration_hours,
    'unit_rate': hourly_rate,
    'units_name': 'hours',
    'tax_rate': 70000
}
items = items + [hourly_item]

# Return both items
return {
    'invoice_items': items
}

Example 3: Conditional Invoicing Based on User and Multi-Resource Reservations

# This script applies different billing rules based on the user type

# Get user information
user_id = util.dicts.get(d=reservation, path='reserved_for.user_id')

# Define our internal user IDs (for example purposes)
internal_user_ids = [
    'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
    'q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'
]

# Check if user is internal
if user_id in internal_user_ids:
    # For internal users, don't create any invoice items
    return {
        'invoice_items': []
    }
else:
    # For external users, apply a special rate
    modified_items = []
    for invoice_item in invoice_items:
      modified_item = invoice_item
      modified_item = modified_item + {"item": "External Usage"}
      modified_item = modified_item + {"unit_rate": 75.00}
      modified_item = modified_item + {"description": "External user rate - " + modified_item['description']}
    modified_items = modified_items + [modified_item]
    return {
        'invoice_items': modified_items
    }

Example 4: Modifying Invoice Properties

# This script modifies both invoice items and invoice properties

# Current date plus 30 days for due date
today = datetime.now()
due_date = datetime.add(dt=today, days=90)

# Get default item and modify (using the first item in the invoice_items list)
modified_item = invoice_items[0]
modified_item = modified_item + {"description": "Premium service fee"}

# Create our return object with invoice modifications
return {
    'invoice': {
        'payment_instructions': "Payment due within 90 days. Please include invoice number.",
        'memo': "Thank you for your business!",
        'due_date': due_date,
    },
    'invoice_items': [modified_item]
}

Example 5: Dynamic Pricing Based on Reservation Quantity

# This script adjusts pricing based on the quantity reserved

# Get reservation details
quantity = util.dicts.get(d=reservation, path='units')
resource_name = util.dicts.get(d=reservation, path='reservable.name')

# Base pricing
base_rate = 100.00

# Apply volume discounts
if quantity >= 10:
    discounted_rate = base_rate * 0.8  # 20% discount
    discount_text = "20% volume discount applied"
if quantity >= 5 and quantity < 10:
    discounted_rate = base_rate * 0.9  # 10% discount
    discount_text = "10% volume discount applied"
if quantity < 5:
    discounted_rate = base_rate
    discount_text = "Standard rate"

# Create our invoice item
item = {
    'item': resource_name,
    'description': str(resource_name) + " x " + str(quantity) + " - " + str(discount_text),
    'units': quantity,
    'unit_rate': discounted_rate,
    'units_name': 'units',
    'sku': "RES-" + str(resource_name)
}

# Return the item
return {
    'invoice_items': [item]
}

Best Practices

  1. Testing: Always test your invoice scripts thoroughly with different scenarios before using them in production.

  2. Default Values: When modifying the default invoice item, start by making a copy to avoid losing important default values.

  3. Consistency: Maintain consistent pricing and billing logic across related resources.

  4. Documentation: Keep documentation of your custom billing rules for reference by your team.