AcumaticaClient
The intelligent client that discovers your entire Acumatica instance
Introduction
The AcumaticaClient
is your gateway to seamless Acumatica integration. It automatically discovers your endpoints, generates models, and creates type-safe services - all at runtime.
# The v0.4.8 way - everything is dynamic!
from easy_acumatica import AcumaticaClient
# Initialize - this discovers your entire instance
client = AcumaticaClient(
base_url="https://your-instance.acumatica.com",
username="your_username",
password="your_password",
tenant="YourTenant",
branch="YourBranch"
)
# Models are generated and ready to use
print(f"Available models: {dir(client.models)}")
# Services are attached dynamically
print(f"Available services: {[attr for attr in dir(client) if attr.endswith('s') and not attr.startswith('_')]}")
# Use them immediately with full type safety!
customer = client.customers.get_by_id("ABCCOMP")
print(f"Customer: {customer.CustomerName}")
Initialization
Initialize your client with credentials. The client will automatically discover and configure everything else.
from easy_acumatica import AcumaticaClient
# Method 1: Direct parameters
client = AcumaticaClient(
base_url="https://your-instance.acumatica.com",
username="api_user",
password="secure_password",
tenant="Company",
branch="MAIN"
)
# Method 2: Environment variables
# Set: ACUMATICA_URL, ACUMATICA_USERNAME, etc.
client = AcumaticaClient()
# Method 3: Mix of both (env vars as defaults)
client = AcumaticaClient(
tenant="Company", # Override specific values
branch="BRANCH01"
)
# Method 4: Using a config object
from easy_acumatica import AcumaticaConfig
config = AcumaticaConfig(
base_url="https://your-instance.acumatica.com",
username="api_user",
password="secure_password",
tenant="Company",
endpoint_name="Default",
endpoint_version="23.200.001"
)
client = AcumaticaClient(config=config)
Parameter | Type | Required | Description |
---|---|---|---|
base_url | str | Optional | Root URL of your Acumatica instance (or use env var) |
username | str | Optional | API username (or use env var) |
password | str | Optional | API password (or use env var) |
tenant | str | Optional | Tenant/Company name (or use env var) |
branch | str | Optional | Branch code (optional) |
locale | str | Optional | Locale like "en-US" (optional) |
verify_ssl | bool | Optional | Verify SSL certificates (default: True) |
persistent_login | bool | Optional | Keep session alive (default: True) |
retry_on_idle_logout | bool | Optional | Auto-retry on timeout (default: True) |
endpoint_name | str | Optional | API endpoint name (default: "Default") |
endpoint_version | str | Optional | Specific API version (optional) |
config | AcumaticaConfig | Optional | Config object (overrides other params) |
rate_limit_calls_per_second | float | Optional | API rate limit (default: 10.0) |
timeout | int | Optional | Request timeout in seconds (default: 60) |
Configuration Options
Configure advanced behaviors with optional parameters.
# Configure advanced behaviors
client = AcumaticaClient(
base_url="https://your-instance.acumatica.com",
username="api_user",
password="secure_password",
tenant="Company",
# Session behavior
persistent_login=True, # Keep session alive (default)
retry_on_idle_logout=True, # Auto-retry on 401 (default)
# Performance tuning
rate_limit_calls_per_second=10.0, # API throttling
timeout=60, # Request timeout in seconds
# SSL and security
verify_ssl=True, # Verify SSL certificates
# API targeting
endpoint_name="Default", # Which endpoint to use
endpoint_version=None, # Auto-detect latest version
# Localization
locale="en-US" # API locale setting
)
Dynamic Discovery
The client automatically discovers your Acumatica instance structure on initialization.
# The client discovers everything on initialization
client = AcumaticaClient(...)
# See what was discovered
print("\nAvailable Services:")
for attr in dir(client):
if attr.endswith('s') and not attr.startswith('_'):
service = getattr(client, attr)
print(f" - client.{attr} -> {type(service).__name__}")
print("\nAvailable Models:")
for model_name in sorted(dir(client.models)):
if not model_name.startswith('_'):
model = getattr(client.models, model_name)
print(f" - {model_name}: {len(model.__dataclass_fields__)} fields")
# Check if custom entities exist
if hasattr(client, 'custom_entity'):
print("\nCustom entity service available!")
# Check for custom fields
customer_fields = client.models.Customer.__dataclass_fields__
custom_fields = [f for f in customer_fields if f.startswith('Usr')]
print(f"\nCustom fields on Customer: {custom_fields}")
Dynamic Models
Models are generated dynamically based on your actual Acumatica entities. They include all standard fields and your custom fields automatically.
# All models follow Python dataclass patterns
# Create a new customer
customer = client.models.Customer(
CustomerID="PYTHN001",
CustomerName="Python Customer",
CustomerClass="DEFAULT",
# Your custom fields are here too!
UsrCustomField="Custom Value" if hasattr(client.models.Customer, 'UsrCustomField') else None
)
# Models support all standard operations
print(customer.CustomerID) # PYTHN001
print(customer.__dict__) # See all fields
# Nested models work seamlessly
order = client.models.SalesOrder(
OrderType="SO",
CustomerID="PYTHN001",
Details=[
client.models.SalesOrderDetail(
InventoryID="WIDGET",
Quantity=5
)
]
)
# Type hints work perfectly in your IDE
# Your editor knows all fields and their types!
Dynamic Services
Services are dynamically attached to the client based on your available endpoints. Each service provides methods that correspond exactly to your API operations.
# Services provide intuitive CRUD operations
# GET - Retrieve by keys
customer = client.customers.get_by_id("PYTHN001")
# GET - With field selection
opts = QueryOptions(filter=F.CustomerID == "PYTHN001", select=["CustomerID","CustomerName","Balance"])
customer = client.customers.get_list(options=opts)
# LIST - Get multiple records
all_customers = client.customers.get_list()
# LIST - With OData parameters
from easy_acumatica import F, QueryOptions
# CREATE - Add new record
new_customer = client.models.Customer(
CustomerID="PYTHN002",
CustomerName="Another Python Customer"
)
created = client.customers.put_entity(new_customer)
# UPDATE - Modify existing
customer.CustomerName = "Updated Name"
updated = client.customers.put_entity(customer)
# DELETE - Remove record
client.customers.delete_by_id("PYTHN002")
Service Method Patterns
Method Pattern | Description |
---|---|
get(keys, select=None, expand=None) | Retrieve a single record by its keys |
list(top=None, skip=None, filter=None, select=None, orderby=None, expand=None) | List records with optional OData parameters |
create(entity) | Create a new record |
update(entity) | Update an existing record |
delete(keys) | Delete a record by its keys |
get_by_id(id) | Get a record by its internal ID |
invoke_action(name, payload) | Execute an action |
attach_file(keys, filename, content) | Attach a file to a record |
get_files(keys) | List files attached to a record |
get_file(keys, file_id) | Download a specific file |
delete_file(keys, file_id) | Delete a file attached to an entity |
OData Query Features
Leverage the power of OData with our intuitive query builder and filter factory.
from easy_acumatica import F
# Simple equality filter
active_filter = F.Status == "Active"
# Comparison operators
high_value = F.Balance > 10000
recent = F.CreatedDate >= "2024-01-01"
# Logical operators
combined = (F.Status == "Active") & (F.Balance > 5000)
either_or = (F.Type == "Business") | (F.Type == "Corporate")
# String functions (lowercase for OData compatibility)
name_starts = F.CustomerName.tolower().startswith("acme")
contains_text = F.Description.contains("important")
name_ends = F.CustomerName.endswith("LLC")
# Date functions
this_year = F.CreatedDate.year() == 2024
this_month = F.OrderDate.month() == 3
# Navigation properties
has_email = F.MainContact.Email != None
contact_city = F.MainContact.Address.City == "New York"
# Complex nested conditions
complex_filter = (
((F.Status == "Active") | (F.Status == "OnHold")) &
(F.Balance > 1000) &
F.CustomerName.tolower().startswith("tech") &
(F.Terms == "30D" | F.Terms == "60D")
)
# Use with list operations
results = client.customers.get_list(filter=complex_filter)
F
factory creates filter expressions that work with both OData v3 and v4. Function names are automatically lowercased for compatibility. Session Management
The client handles all session complexity for you, with intelligent retry logic and automatic cleanup.
# Default behavior - one login, multiple operations
client = AcumaticaClient(
base_url="https://your-instance.acumatica.com",
username="api_user",
password="secure_password",
tenant="Company"
# persistent_login=True is the default
)
# Session established once during __init__
# All operations use the same session
customers = client.customers.get_list()
orders = client.sales_orders.get_list()
invoices = client.invoices.get_list()
# Session automatically cleaned up on exit
Exception Handling
Easy-Acumatica provides specific exception types for different error scenarios.
from easy_acumatica import (
AcumaticaException,
AcumaticaAuthError,
AcumaticaAPIError,
AcumaticaNetworkError,
AcumaticaTimeoutError,
AcumaticaNotFoundError
)
try:
# This might fail in various ways
customer = client.customers.get_by_id("UNKNOWN")
except AcumaticaNotFoundError as e:
# Record doesn't exist
print(f"Customer not found: {e}")
except AcumaticaAuthError as e:
# Authentication failed
print(f"Auth error: {e}")
# Maybe refresh credentials
except AcumaticaTimeoutError as e:
# Request timed out
print(f"Timeout after {e.timeout}s")
# Maybe retry with longer timeout
except AcumaticaAPIError as e:
# API returned an error
print(f"API error {e.status_code}: {e.message}")
print(f"Details: {e.details}")
except AcumaticaNetworkError as e:
# Network-level error
print(f"Network error: {e}")
except AcumaticaException as e:
# Catch-all for any Acumatica-related error
print(f"Acumatica error: {e}")