Skip to content

Python in Heptora

This manual describes how to develop business automations using Python in the Heptora platform. Unlike the Heptora Language, here you can use the full power of Python along with the specific functions of the heptora library.

The Python approach in Heptora is to offer flexibility and power. You can use:

  • ✅ Native Python libraries
  • ✅ Control structures (if, for, while)
  • ✅ The entire language ecosystem
  • ✅ Exception handling (try/except)
  • ✅ Object-oriented programming
  • ✅ Any package from PyPI

The heptora library provides high-level functions to interact with applications, browsers, and the platform itself.

AspectHeptora LanguagePython in Heptora
ComplexitySimple processesComplex automations
Flow controlNot allowedFully allowed
LibrariesOnly Heptora functionsEntire Python ecosystem
Learning curveLowMedium-High (requires Python)
SecurityVery high (restricted)High (more flexible)
Ideal forBusiness usersDevelopers

  • Python 3.8 or higher installed on the local robot
  • The heptora library is automatically installed with the robot
# Always at the beginning of your script
import heptora
# Import specific modules if you prefer
from heptora import log, browser, excel, data

Below is the complete API of the heptora library for Python.


Functions to record information and results.

heptora.log.info(message: str) -> None

Description: Publishes an informative log message to the platform.

Parameters:

  • message (str): Informative message to log

Examples:

heptora.log.info("Data loading process started.")
heptora.log.info(f"Processing client: {client_name}")
# With variables
total = len(invoices)
heptora.log.info(f"Total invoices to process: {total}")

heptora.log.result(messageType: str, message: str) -> None

Description: Records a final process result.

Parameters:

  • messageType (str): Result type: "ok", "ko", or "error"
  • message (str): Result description

Examples:

# Success
heptora.log.result("ok", "50 invoices processed successfully.")
# Controlled error
heptora.log.result("ko", f"Invoice {number} doesn't meet validations.")
# Unexpected error
heptora.log.result("error", "Could not connect to database server.")

Usage with conditionals:

if validation_successful:
heptora.log.result("ok", f"Invoice {number} processed correctly")
else:
heptora.log.result("ko", f"Invoice {number}: {error_reason}")

heptora.log.force_ko(message: str) -> None

Description: Records a controlled KO and stops processing the current record.

Parameters:

  • message (str): Description of the controlled error

Examples:

# Data validation
if not invoice_number:
heptora.log.force_ko("Invoice number cannot be empty")
# Business rule verification
if status == "Paid":
heptora.log.force_ko("Invoice is already paid, no processing required")
# Format validation
if not validate_nif(client_nif):
heptora.log.force_ko(f"NIF {client_nif} has incorrect format")

Functions to manage file attachments in results.

heptora.report.add_attachment(file_path: str) -> None

Description: Attaches a file to the current task. It will be sent in the summary email.

Parameters:

  • file_path (str): Path of the file to attach

Examples:

# Attach a generated report
heptora.report.add_attachment("reports/sales_report.pdf")
# With absolute paths
import os
full_path = os.path.join(os.getcwd(), "output", "result.xlsx")
heptora.report.add_attachment(full_path)
# Verify existence before attaching
if os.path.exists("error_report.csv"):
heptora.report.add_attachment("error_report.csv")
heptora.log.info("Error report attached")

Functions for web automation with Playwright.

heptora.browser.connect() -> Page

Description: Connects to a browser session managed by Heptora and returns a Playwright Page object.

Returns: Playwright Page object to interact with the browser

Prerequisite: The browser must be previously opened from a Heptora code block with heptora.browser.open().

Basic example:

# Connect to browser
page = heptora.browser.connect()
# Navigate to a URL
page.goto("https://www.google.com")
# Interact with elements
page.locator("#APjFqb").fill("Automation with Heptora")
page.locator("input[name='btnK']").first.click()
# Wait and get text
page.wait_for_selector(".g")
results = page.locator(".g").all_text_contents()
heptora.log.info(f"Found {len(results)} results")

Advanced example - Login:

page = heptora.browser.connect()
page.goto("https://portal.company.com/login")
# Wait for form to load
page.wait_for_selector("#username")
# Enter credentials
username = heptora.data.get("portal_username")
password = heptora.data.get("portal_password")
page.locator("#username").fill(username)
page.locator("#password").fill(password)
page.locator("button[type='submit']").click()
# Verify successful login
try:
page.wait_for_selector(".dashboard", timeout=5000)
heptora.log.info("Login successful")
except:
heptora.log.force_ko("Could not log in")

Example - Data scraping:

page = heptora.browser.connect()
page.goto("https://example.com/data-table")
# Wait for table
page.wait_for_selector("table.data")
# Extract data from table
rows = page.locator("table.data tbody tr").all()
extracted_data = []
for row in rows:
columns = row.locator("td").all_text_contents()
extracted_data.append({
"code": columns[0],
"name": columns[1],
"price": columns[2]
})
heptora.log.info(f"Extracted {len(extracted_data)} records")
heptora.data.set("web_data", extracted_data)

heptora.browser.go_to(url: str) -> None

Description: Simplified way to navigate to a URL, which includes a small automatic pause.

Parameters:

  • url (str): Complete URL to navigate to

Example:

# Simple navigation
heptora.browser.go_to("https://portal.heptora.com")
# In a loop
urls_to_visit = [
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3"
]
for url in urls_to_visit:
heptora.browser.go_to(url)
# Process the page...
heptora.log.info(f"Processed: {url}")

heptora.browser.solve_captcha() -> None

Description: Automatically solves the captcha on the current browser page.

Example:

page = heptora.browser.connect()
page.goto("https://site-with-captcha.com/login")
# Try to solve captcha if present
try:
heptora.browser.solve_captcha()
heptora.time.pause(3) # Wait for resolution
heptora.log.info("Captcha solved")
except Exception as e:
heptora.log.info("No captcha found or already solved")

Functions to work with Excel files.

heptora.excel.load(
file_path: str,
sheet_name: str,
columns: List[str],
has_header: bool
) -> List[Dict]

Description: Reads data from an Excel file.

Parameters:

  • file_path (str): Path to the Excel file
  • sheet_name (str): Sheet name
  • columns (List[str]): List of column names to extract
  • has_header (bool): Whether the first row contains headers

Returns: List of dictionaries, each representing a row

Basic example:

# Define columns to read
columns = ["Product_ID", "Name", "Stock", "Price"]
# Load data
products = heptora.excel.load(
file_path="inventory.xlsx",
sheet_name="Warehouse",
columns=columns,
has_header=True
)
# Process the data
for product in products:
heptora.log.info(
f"Product {product['Product_ID']}: "
f"{product['Name']} - Stock: {product['Stock']}"
)

Example with processing:

# Load invoices
invoices = heptora.excel.load(
"pending_invoices.xlsx",
"January",
["InvoiceNumber", "Client", "Amount", "Status"],
True
)
# Filter only pending ones
pending_invoices = [
f for f in invoices
if f["Status"] == "Pending"
]
heptora.log.info(
f"Of {len(invoices)} invoices, "
f"{len(pending_invoices)} are pending"
)
# Save for later processing
heptora.data.set("invoices_to_process", pending_invoices)

Example with validations:

clients = heptora.excel.load("clients.xlsx", "Sheet1", ["Name", "Email", "NIF"], True)
valid_clients = []
errors = []
for i, client in enumerate(clients, start=2): # start=2 for header
# Validate required fields
if not client.get("Email") or not client.get("NIF"):
errors.append(f"Row {i}: Missing required fields")
continue
# Validate email format
if "@" not in client["Email"]:
errors.append(f"Row {i}: Invalid email")
continue
valid_clients.append(client)
heptora.log.info(f"Valid clients: {len(valid_clients)}")
heptora.log.info(f"Errors found: {len(errors)}")
for error in errors:
heptora.log.info(error)

heptora.excel.write(
values: List[Dict],
file_path: str,
sheet_name: str
) -> None

Description: Writes data to an Excel file.

Parameters:

  • values (List[Dict]): List of dictionaries with the data
  • file_path (str): Path where to save the file
  • sheet_name (str): Sheet name

Basic example:

new_data = [
{"Name": "John", "City": "Madrid", "Age": 30},
{"Name": "Laura", "City": "Barcelona", "Age": 28}
]
heptora.excel.write(
new_data,
"new_clients.xlsx",
"Clients"
)
heptora.log.info("Excel file created successfully")

Example - Generate processing report:

# Process invoices and generate report
invoices = heptora.data.get("invoices_to_process")
results = []
for invoice in invoices:
try:
# Process invoice...
result = process_invoice(invoice)
results.append({
"InvoiceNumber": invoice["InvoiceNumber"],
"Client": invoice["Client"],
"Status": "Processed",
"Result": "OK",
"Observations": ""
})
except Exception as e:
results.append({
"InvoiceNumber": invoice["InvoiceNumber"],
"Client": invoice["Client"],
"Status": "Error",
"Result": "KO",
"Observations": str(e)
})
# Save report
from datetime import datetime
date = datetime.now().strftime("%Y%m%d_%H%M%S")
report_file = f"invoice_report_{date}.xlsx"
heptora.excel.write(results, report_file, "Results")
heptora.report.add_attachment(report_file)
heptora.log.info(f"Report generated: {report_file}")

Example - Consolidate multiple sources:

# Read from multiple files
sales_january = heptora.excel.load("sales_january.xlsx", "Sales", ["Product", "Quantity"], True)
sales_february = heptora.excel.load("sales_february.xlsx", "Sales", ["Product", "Quantity"], True)
# Consolidate data
all_sales = sales_january + sales_february
# Group by product
from collections import defaultdict
sales_by_product = defaultdict(int)
for sale in all_sales:
product = sale["Product"]
quantity = int(sale["Quantity"])
sales_by_product[product] += quantity
# Prepare for writing
consolidated = [
{"Product": product, "Total_Sold": total}
for product, total in sales_by_product.items()
]
# Write consolidated
heptora.excel.write(consolidated, "consolidated_sales.xlsx", "Summary")

Functions for desktop application automation.

heptora.windows.open_app(path: str) -> None

Description: Opens a Windows application.

Example:

# Common applications
heptora.windows.open_app("calc.exe")
heptora.windows.open_app("notepad.exe")
heptora.windows.open_app("excel.exe")
# With full path
heptora.windows.open_app("C:\\Program Files\\MyApp\\app.exe")
# Wait for it to open
heptora.time.pause(2)

heptora.windows.type(text: str) -> None

Description: Simulates typing text.

Example:

heptora.windows.type("Order confirmation number 12345.")
# With variables
order_number = "ORD-2024-001"
heptora.windows.type(f"Order: {order_number}")

heptora.windows.press(*keys: str) -> None

Description: Simulates pressing keys.

Examples:

# Single key
heptora.windows.press("enter")
heptora.windows.press("escape")
# Combinations
heptora.windows.press("control", "s") # Ctrl+S
heptora.windows.press("alt", "f4") # Alt+F4
heptora.windows.press("control", "shift", "escape") # Ctrl+Shift+Esc

heptora.windows.click_by_vision(base64Image: str) -> None

Description: Searches for an image on screen and clicks on it.

Example:

button_image = "data:image/png;base64,iVBORw0KG..."
heptora.windows.click_by_vision(button_image)
heptora.time.pause(1)

heptora.windows.alert(message: str) -> None

Description: Shows a dialog box.

Example:

heptora.windows.alert("Process has finished. Review the results.")

heptora.beta.windows.press_macro(
requiredKey1: str,
requiredKey2: str,
*optionalKeys: str
) -> None

Description: Simulates complex keyboard shortcuts.

Example:

# Win+R (Run)
heptora.beta.windows.press_macro("win", "r")

Functions for timing control.

heptora.time.pause(seconds: float = None) -> None

Description: Pauses execution for a specified time.

Parameters:

  • seconds (float, optional): Seconds to pause

Examples:

# 5-second pause
heptora.time.pause(5)
# Fractional pause
heptora.time.pause(1.5)
# Process default pause
heptora.time.pause()

Functions to pass data between blocks.

heptora.data.set(var_name: str, value: Any) -> None

Description: Saves a variable for later use.

Parameters:

  • var_name (str): Variable name
  • value (Any): Value to save (any serializable type)

Examples:

# Save different data types
heptora.data.set("client_name", "Ana García")
heptora.data.set("total_invoices", 42)
heptora.data.set("average_price", 125.50)
# Save complex structures
user_data = {
"id": 101,
"name": "Carlos",
"roles": ["admin", "user"]
}
heptora.data.set("currentUser", user_data)
# Save lists
processed_products = ["PROD001", "PROD002", "PROD003"]
heptora.data.set("products_ok", processed_products)

heptora.data.get(var_name: str) -> Any

Description: Retrieves a previously saved variable.

Parameters:

  • var_name (str): Variable name

Returns: The saved value, or None if it doesn’t exist

Examples:

# Retrieve simple data
name = heptora.data.get("client_name")
total = heptora.data.get("total_invoices")
# Retrieve and use complex structures
user = heptora.data.get("currentUser")
if user and user["id"] == 101:
heptora.log.info(f"Processing user: {user['name']}")
# With default value
invoices = heptora.data.get("invoices") or []
for invoice in invoices:
# Process...
pass

Use language features for cleaner and more robust code.

Loops and comprehensions:

# Load and filter data
invoices = heptora.excel.load("invoices.xlsx", "Sheet1", ["Number", "Amount", "Status"], True)
# Filter using list comprehension
pending = [f for f in invoices if f["Status"] == "Pending"]
over_1000 = [f for f in pending if float(f["Amount"]) > 1000]
heptora.log.info(f"Pending invoices over 1000€: {len(over_1000)}")
# Process with for
for invoice in over_1000:
number = invoice["Number"]
amount = invoice["Amount"]
heptora.log.info(f"Processing invoice {number} for {amount}€")
# Processing...

Complex conditionals:

def validate_invoice(invoice):
"""Validates an invoice according to business rules."""
# Validate required fields
if not invoice.get("Number"):
return False, "Missing invoice number"
if not invoice.get("Client"):
return False, "Missing client"
# Validate amounts
try:
amount = float(invoice.get("Amount", 0))
if amount <= 0:
return False, "Amount must be greater than 0"
except ValueError:
return False, "Invalid amount"
# Validate status
valid_statuses = ["Pending", "Paid", "Cancelled"]
if invoice.get("Status") not in valid_statuses:
return False, f"Invalid status. Must be: {', '.join(valid_statuses)}"
return True, "OK"
# Use the function
invoices = heptora.data.get("invoices")
for invoice in invoices:
is_valid, message = validate_invoice(invoice)
if not is_valid:
heptora.log.force_ko(f"Invoice {invoice['Number']}: {message}")
continue
# Process valid invoice...

Use try/except to manage errors in a controlled way.

Basic pattern:

invoices = heptora.data.get("invoices")
for invoice in invoices:
number = invoice["Number"]
try:
# Attempt to process
heptora.log.info(f"Processing invoice {number}")
# Code that may fail
result = process_in_system(invoice)
heptora.log.result("ok", f"Invoice {number} processed correctly")
except ValueError as e:
# Data error (controlled)
heptora.log.result("ko", f"Invoice {number}: Data error - {str(e)}")
except ConnectionError as e:
# Connectivity error (unexpected)
heptora.log.result("error", f"Invoice {number}: Connection error - {str(e)}")
except Exception as e:
# Any other error
heptora.log.result("error", f"Invoice {number}: Unexpected error - {str(e)}")

Advanced pattern with retries:

def process_with_retries(invoice, max_attempts=3):
"""Processes an invoice with retries in case of error."""
for attempt in range(1, max_attempts + 1):
try:
heptora.log.info(f"Attempt {attempt}/{max_attempts} for invoice {invoice['Number']}")
# Process
result = send_to_portal(invoice)
heptora.log.result("ok", f"Invoice {invoice['Number']} processed")
return True
except ConnectionError:
if attempt < max_attempts:
heptora.log.info(f"Connection error. Retrying in 5 seconds...")
heptora.time.pause(5)
else:
heptora.log.result("error", f"Invoice {invoice['Number']}: Error after {max_attempts} attempts")
return False
except Exception as e:
# Unrecoverable error
heptora.log.result("error", f"Unrecoverable error: {str(e)}")
return False
return False
# Use
invoices = heptora.data.get("invoices")
for invoice in invoices:
process_with_retries(invoice)

For long scripts, define functions to separate logic.

def load_excel_data(file_path):
"""Loads and validates data from Excel file."""
heptora.log.info(f"Loading data from: {file_path}")
columns = ["Number", "Client", "Amount", "Status"]
data = heptora.excel.load(file_path, "Invoices", columns, True)
heptora.log.info(f"Loaded {len(data)} invoices")
return data
def validate_data(invoices):
"""Validates that invoices meet requirements."""
valid = []
invalid = []
for invoice in invoices:
if invoice.get("Status") == "Pending" and float(invoice.get("Amount", 0)) > 0:
valid.append(invoice)
else:
invalid.append(invoice)
heptora.log.info(f"Valid: {len(valid)}, Invalid: {len(invalid)}")
return valid, invalid
def process_invoices(invoices):
"""Processes invoices in the system."""
successful = 0
failed = 0
for invoice in invoices:
try:
# Processing logic...
successful += 1
heptora.log.result("ok", f"Invoice {invoice['Number']}: OK")
except Exception as e:
failed += 1
heptora.log.result("error", f"Invoice {invoice['Number']}: {str(e)}")
return successful, failed
def generate_report(successful, failed):
"""Generates a results report."""
summary = {
"Total_Processed": successful,
"Total_Failed": failed,
"Success_Rate": f"{(successful / (successful + failed) * 100):.2f}%"
}
heptora.excel.write([summary], "summary_report.xlsx", "Summary")
heptora.report.add_attachment("summary_report.xlsx")
# Main script flow
def main():
# 1. Load data
invoices = load_excel_data("pending_invoices.xlsx")
# 2. Validate
valid, invalid = validate_data(invoices)
# 3. Process
successful, failed = process_invoices(valid)
# 4. Report
generate_report(successful, failed)
heptora.log.info("Process completed")
# Execute
main()

Python’s f-strings are the cleanest way to create messages.

# ✅ Recommended: f-strings
name = "Carlos"
age = 30
heptora.log.info(f"Processing user: {name} (age: {age})")
# ✅ With expressions
invoices = [1, 2, 3, 4, 5]
heptora.log.info(f"Total invoices: {len(invoices)}")
heptora.log.info(f"Sum of amounts: {sum(amounts):.2f}€")
# ❌ Avoid: concatenation with +
heptora.log.info("Processing user: " + name + " (age: " + str(age) + ")")
# ❌ Avoid: old format()
heptora.log.info("Processing user: {} (age: {})".format(name, age))

Never include passwords or keys in code.

❌ DON’T do this:

# NEVER do this!
user = "admin@company.com"
password = "MySecretPassword123"
api_key = "sk-1234567890abcdef"

✅ DO this:

# Use Heptora's secrets manager
user = heptora.data.get("portal_user")
password = heptora.data.get("portal_password")
api_key = heptora.data.get("api_key")
# Verify they exist
if not user or not password:
heptora.log.force_ko("Missing credentials. Configure them in Secrets.")
# Use securely
page = heptora.browser.connect()
page.goto("https://portal.com/login")
page.locator("#username").fill(user)
page.locator("#password").fill(password)

Document your code to facilitate maintenance.

"""
Process: Automatic Invoicing
Description: Processes pending invoices and sends them to FACe portal
Author: RPA Team
Date: 2024-01-15
"""
import heptora
from datetime import datetime
# ==============================================================================
# CONFIGURATION
# ==============================================================================
INVOICES_FILE = "invoices_january.xlsx"
AMOUNT_THRESHOLD = 1000.0 # Only invoices over 1000€
# ==============================================================================
# HELPER FUNCTIONS
# ==============================================================================
def load_configuration():
"""
Loads necessary configuration and secrets.
Returns:
dict: Process configuration
"""
return {
"user": heptora.data.get("face_user"),
"password": heptora.data.get("face_password"),
"portal_url": "https://face.gob.es"
}
def process_invoice(invoice, config):
"""
Processes an individual invoice.
Args:
invoice (dict): Invoice data
config (dict): Process configuration
Returns:
bool: True if processed correctly
Raises:
ValueError: If invoice is not valid
ConnectionError: If there's a connection error
"""
# Validate invoice
if not invoice.get("Number"):
raise ValueError("Missing invoice number")
# Connect and process
page = heptora.browser.connect()
page.goto(config["portal_url"])
# ... processing logic ...
return True
# ==============================================================================
# MAIN FLOW
# ==============================================================================
def main():
"""Main process function."""
heptora.log.info("=== START: Invoice Processing ===")
# 1. Load configuration
config = load_configuration()
# 2. Load invoices
invoices = heptora.excel.load(
INVOICES_FILE,
"Invoices",
["Number", "Client", "Amount"],
True
)
# 3. Filter by amount
filtered_invoices = [
f for f in invoices
if float(f["Amount"]) >= AMOUNT_THRESHOLD
]
heptora.log.info(f"Invoices to process: {len(filtered_invoices)}")
# 4. Process each invoice
for invoice in filtered_invoices:
try:
process_invoice(invoice, config)
heptora.log.result("ok", f"Invoice {invoice['Number']}: OK")
except Exception as e:
heptora.log.result("error", f"Invoice {invoice['Number']}: {str(e)}")
heptora.log.info("=== END: Process completed ===")
# Execute process
if __name__ == "__main__":
main()

Complete Example: Advanced Invoice Process

Section titled “Complete Example: Advanced Invoice Process”

This example shows a complete process with all best practices.

"""
Advanced Process: Invoice Management with FACe Portal
"""
import heptora
from datetime import datetime
from typing import List, Dict, Tuple
# ==============================================================================
# CONFIGURATION
# ==============================================================================
CONFIG = {
"input_file": "pending_invoices.xlsx",
"input_sheet": "Invoices",
"columns": ["InvoiceNumber", "CIF", "Amount", "Date"],
"portal_url": "https://face.gob.es",
"page_timeout": 10000 # 10 seconds
}
# ==============================================================================
# LOADING AND VALIDATION FUNCTIONS
# ==============================================================================
def load_invoices() -> List[Dict]:
"""Loads invoices from Excel."""
heptora.log.info("Loading invoices...")
invoices = heptora.excel.load(
CONFIG["input_file"],
CONFIG["input_sheet"],
CONFIG["columns"],
has_header=True
)
heptora.log.info(f"Loaded {len(invoices)} invoices")
return invoices
def validate_invoice(invoice: Dict) -> Tuple[bool, str]:
"""
Validates an invoice.
Returns:
Tuple[bool, str]: (is_valid, error_message)
"""
# Validate number
if not invoice.get("InvoiceNumber"):
return False, "Missing invoice number"
# Validate CIF
cif = invoice.get("CIF", "")
if len(cif) != 9:
return False, f"Invalid CIF: {cif}"
# Validate amount
try:
amount = float(invoice.get("Amount", 0))
if amount <= 0:
return False, "Amount must be greater than 0"
except (ValueError, TypeError):
return False, "Invalid amount"
return True, ""
def filter_valid_invoices(invoices: List[Dict]) -> Tuple[List[Dict], List[Dict]]:
"""Separates valid from invalid invoices."""
valid = []
invalid = []
for invoice in invoices:
is_valid, error = validate_invoice(invoice)
if is_valid:
valid.append(invoice)
else:
invoice["_error"] = error
invalid.append(invoice)
heptora.log.info(
f"Invoice {invoice.get('InvoiceNumber', 'NO_NUM')}: {error}"
)
heptora.log.info(f"Valid: {len(valid)}, Invalid: {len(invalid)}")
return valid, invalid
# ==============================================================================
# WEB PROCESSING FUNCTIONS
# ==============================================================================
def login_portal(page, user: str, password: str) -> bool:
"""Performs login to the portal."""
try:
heptora.log.info("Logging into portal...")
page.goto(CONFIG["portal_url"] + "/login")
page.wait_for_selector("#username", timeout=CONFIG["page_timeout"])
page.locator("#username").fill(user)
page.locator("#password").fill(password)
page.locator("button[type='submit']").click()
# Verify successful login
page.wait_for_selector(".dashboard", timeout=CONFIG["page_timeout"])
heptora.log.info("Login successful")
return True
except Exception as e:
heptora.log.result("error", f"Login error: {str(e)}")
return False
def process_invoice_in_portal(page, invoice: Dict) -> bool:
"""Processes an invoice in the web portal."""
number = invoice["InvoiceNumber"]
try:
heptora.log.info(f"Processing invoice {number}...")
# Navigate to form
page.goto(CONFIG["portal_url"] + "/new-invoice")
page.wait_for_selector("#invoice-form", timeout=CONFIG["page_timeout"])
# Fill form
page.locator("#number").fill(str(invoice["InvoiceNumber"]))
page.locator("#cif").fill(str(invoice["CIF"]))
page.locator("#amount").fill(str(invoice["Amount"]))
page.locator("#date").fill(str(invoice["Date"]))
# Submit
page.locator("button#submit").click()
# Wait for confirmation
page.wait_for_selector(".success-message", timeout=CONFIG["page_timeout"])
heptora.log.result("ok", f"Invoice {number} processed correctly")
return True
except Exception as e:
heptora.log.result("error", f"Invoice {number}: {str(e)}")
return False
# ==============================================================================
# REPORTING FUNCTIONS
# ==============================================================================
def generate_invalid_report(invalid: List[Dict]):
"""Generates report of invalid invoices."""
if not invalid:
return
report = [
{
"InvoiceNumber": f.get("InvoiceNumber", "NO_NUM"),
"CIF": f.get("CIF", ""),
"Amount": f.get("Amount", ""),
"Error": f.get("_error", "")
}
for f in invalid
]
file = f"invalid_invoices_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
heptora.excel.write(report, file, "Invalid")
heptora.report.add_attachment(file)
heptora.log.info(f"Invalid report generated: {file}")
def generate_summary_report(total: int, successful: int, failed: int, invalid: int):
"""Generates process summary report."""
success_rate = (successful / total * 100) if total > 0 else 0
summary = [{
"Total_Invoices": total,
"Processed_OK": successful,
"Processed_Error": failed,
"Invalid": invalid,
"Success_Rate": f"{success_rate:.2f}%",
"Process_Date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}]
file = f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
heptora.excel.write(summary, file, "Summary")
heptora.report.add_attachment(file)
heptora.log.info(f"Summary generated: {file}")
# ==============================================================================
# MAIN FLOW
# ==============================================================================
def main():
"""Main process function."""
heptora.log.info("=" * 60)
heptora.log.info("START: FACe Invoicing Process")
heptora.log.info("=" * 60)
# 1. LOAD DATA
invoices = load_invoices()
total_invoices = len(invoices)
# 2. VALIDATE DATA
valid, invalid = filter_valid_invoices(invoices)
if not valid:
heptora.log.force_ko("No valid invoices to process")
return
# 3. CONNECT TO PORTAL
page = heptora.browser.connect()
# Get credentials
user = heptora.data.get("face_user")
password = heptora.data.get("face_password")
if not user or not password:
heptora.log.force_ko("Missing credentials. Configure them in Secrets.")
return
# Login
if not login_portal(page, user, password):
heptora.log.force_ko("Could not log into portal")
return
# 4. PROCESS VALID INVOICES
successful = 0
failed = 0
for i, invoice in enumerate(valid, 1):
heptora.log.info(f"Processing {i}/{len(valid)}...")
if process_invoice_in_portal(page, invoice):
successful += 1
else:
failed += 1
# Pause between invoices
if i < len(valid):
heptora.time.pause(2)
# 5. GENERATE REPORTS
generate_invalid_report(invalid)
generate_summary_report(total_invoices, successful, failed, len(invalid))
# 6. FINAL LOG
heptora.log.info("=" * 60)
heptora.log.info(f"Total invoices: {total_invoices}")
heptora.log.info(f" - Processed OK: {successful}")
heptora.log.info(f" - Processed with error: {failed}")
heptora.log.info(f" - Invalid: {len(invalid)}")
heptora.log.info("=" * 60)
heptora.log.info("END: Process completed")
# ==============================================================================
# ENTRY POINT
# ==============================================================================
if __name__ == "__main__":
try:
main()
except Exception as e:
heptora.log.result("error", f"Fatal process error: {str(e)}")
raise

Heptora Language (restrictive, secure):

// Cannot use if, for, foreach
List<Dictionary<string, object>> invoices =
(List<Dictionary<string, object>>)heptora.data.get("invoices");
Dictionary<string, object> invoice = invoices[0];
string number = (string)invoice["InvoiceNumber"];
heptora.log.info("Processing: " + number);
heptora.log.result("ok", "Invoice processed");

Python (flexible, powerful):

# You can use all Python
invoices = heptora.data.get("invoices")
for invoice in invoices:
number = invoice["InvoiceNumber"]
# Use if/else
if invoice["Amount"] > 1000:
try:
# Complex logic with try/except
process_invoice(invoice)
heptora.log.result("ok", f"Invoice {number} processed")
except Exception as e:
heptora.log.result("error", f"Error: {str(e)}")
else:
heptora.log.info(f"Invoice {number} skipped (low amount)")

You can use any Python library installed on the robot.

# Date manipulation
from datetime import datetime, timedelta
current_date = datetime.now()
thirty_days_ago = current_date - timedelta(days=30)
heptora.log.info(f"Current date: {current_date.strftime('%Y-%m-%d')}")
# Regular expressions
import re
def validate_email(email):
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
return re.match(pattern, email) is not None
# Requests for APIs (if installed)
import requests
response = requests.get("https://api.example.com/data")
if response.status_code == 200:
data = response.json()
heptora.data.set("api_data", data)

If this guide didn’t solve your problem or you found an error in the documentation:

  • Technical support: help@heptora.com
  • Clearly describe the problem you encountered
  • Include screenshots if possible
  • Indicate which documentation steps you followed

Our support team will help you resolve any issue.