Dynamic Services
Automatically generated service methods for every endpoint in your Acumatica instance
Overview
When you initialize the AcumaticaClient, it automatically discovers all endpoints from your instance's OpenAPI schema and generates corresponding service objects. Each service provides methods for CRUD operations, custom actions, and file handling. For executing multiple operations concurrently, see Batch Operations.
from easy_acumatica import AcumaticaClient
# Initialize client
client = AcumaticaClient()
# Services are automatically generated and attached
customers = client.customers.get_list()
invoices = client.invoices.get_list()
sales_orders = client.sales_orders.get_list()Accessing Services
Services are accessed as attributes on the client. The attribute name is the entity name converted to snake_case:
# Entity names are converted to snake_case
client.customers # Customer entity
client.sales_orders # SalesOrder entity
client.bills # Bill entity
client.journal_transactions # JournalTransaction entity
# Check if a service exists
if hasattr(client, 'customers'):
print("Customer service available")Naming Convention
Entity names from the OpenAPI schema are automatically converted from PascalCase to snake_case:
Customer→client.customerSalesOrder→client.sales_orderJournalTransaction→client.journal_transactionInquiries→client.inquiries(reserved for Generic Inquiries)
Extended Generic Inquiries published as custom endpoints follow a special naming pattern. For example, "PE All Items (GI908032)" becomes client.pe_all_items.
Common Methods
All services provide a consistent set of methods for interacting with entities:
get_list()
Retrieve multiple records from an endpoint:
from easy_acumatica.odata import QueryOptions, F
# Get all records
all_customers = client.customers.get_list()
# Get with query options
options = QueryOptions(
filter=F.Status == "Active",
select=["CustomerID", "CustomerName", "Email"],
orderby=["CustomerName"],
top=100
)
active_customers = client.customers.get_list(options=options)get_by_id()
Retrieve a single record by its ID:
# Get by primary key ID
customer = client.customers.get_by_id("ABCCOMP")
# With expand to include related data
options = QueryOptions(expand=["Contacts", "Addresses"])
customer = client.customers.get_by_id("ABCCOMP", options=options)get_by_keys()
Retrieve a record by its key field values:
# Single key field
customer = client.customers.get_by_keys(CustomerID="ABCCOMP")
# Multiple key fields (e.g., for entities with composite keys)
journal_entry = client.journal_transactions.get_by_keys(
BatchNbr="00001",
Module="GL"
)
# With query options
options = QueryOptions(expand=["Details"])
invoice = client.invoices.get_by_keys(
RefNbr="INV001234",
options=options
)put_entity()
Create or update a record:
# Create a new customer
new_customer = client.models.Customer(
CustomerID="NEWCUST",
CustomerName="New Customer Inc.",
Email="contact@newcustomer.com"
)
created = client.customers.put_entity(new_customer)
# Update existing (partial update supported)
update = client.models.Customer(
CustomerID="ABCCOMP",
Email="newemail@company.com"
)
updated = client.customers.put_entity(update)delete_by_id()
Delete a record by ID:
# Delete by ID
client.customers.delete_by_id("OLDCUST")delete_by_keys()
Delete a record by key field values:
# Delete by key fields
client.customers.delete_by_keys(CustomerID="OLDCUST")
# Multiple keys
client.invoices.delete_by_keys(RefNbr="INV001234")Query Options
Use QueryOptions to filter, sort, expand, and paginate results. See the OData Queries page for detailed information.
from easy_acumatica.odata import QueryOptions, F
options = QueryOptions(
filter=(F.Status == "Active") & (F.CreditLimit > 10000),
select=["CustomerID", "CustomerName", "CreditLimit"],
expand=["Contacts", "PrimaryContact"],
orderby=["CustomerName asc"],
top=50,
skip=0
)
customers = client.customers.get_list(options=options)Custom Actions
Acumatica entities can have custom actions (like "Release", "Post", etc.). These are automatically discovered and available as invoke_action_* methods:
# Check if action exists
if hasattr(client.invoices, 'invoke_action_release'):
# Get the invoice
invoice = client.invoices.get_by_id("INV001234")
# Execute the release action
client.invoices.invoke_action_release(invoice)
print(f"Released invoice {invoice.RefNbr}")
# Actions with parameters
if hasattr(client.sales_orders, 'invoke_action_create_shipment'):
order = client.sales_orders.get_by_id("SO001234")
client.sales_orders.invoke_action_create_shipment(order)File Operations
Upload and retrieve files attached to entities:
put_file()
Upload a file to an entity:
# Upload a file to an entity
with open('invoice.pdf', 'rb') as f:
file_data = f.read()
client.invoices.put_file(
entity_id="INV001234",
filename="invoice.pdf",
data=file_data,
comment="Original invoice document"
)get_files()
Retrieve files attached to an entity:
# Get list of files attached to an entity
files = client.invoices.get_files("INV001234")
for file_info in files:
print(f"File: {file_info['name']}, Size: {file_info['size']}")Generic Inquiries
Generic Inquiries (GI) are exposed through the client.Inquiries service. Each inquiry gets its own method based on the inquiry name:
from easy_acumatica.odata import QueryOptions
# List available inquiry methods
inquiries = [m for m in dir(client.Inquiries) if not m.startswith('_')]
print(inquiries)
# Call an inquiry
results = client.Inquiries.account_summary(
options=QueryOptions(
filter="AccountCD eq '10000'",
top=50
)
)Custom Endpoints (Extended Generic Inquiries)
Extended Generic Inquiries that have been published as custom endpoints appear as their own services. These use a special query_custom_endpoint() method:
from easy_acumatica.odata import QueryOptions
# Extended GI endpoints appear as services with descriptive names
# Example: "PE All Items (GI908032)" becomes client.pe_all_items
# Query with default options (expand=none)
results = client.pe_all_items.query_custom_endpoint()
# Query with expansion and pagination
options = QueryOptions(
expand=['PEALLPRODSDetails'], # Expand detail fields
top=100,
orderby=['InventoryID']
)
results = client.pe_all_items.query_custom_endpoint(options=options)
# Note: OData filtering may be unstable for custom endpoints
# Filtering is better done in Python after retrieving results
# The results structure depends on your GI configuration
for item in results:
print(f"{item['InventoryID']}: {item['Description']}") Extended GIs require a PUT request with an empty body. The query_custom_endpoint method handles this automatically. Note that OData filtering may be unstable for custom endpoints - consider filtering results in Python instead.
Service Discovery & Introspection
Services provide introspection methods to discover available operations and inspect method signatures:
# List all available services
services = client.list_services()
print(f"Found {len(services)} services")
print("Services:", services[:10])
# Get detailed service information
service_info = client.get_service_info('Customer')
print(f"Customer service methods: {service_info['methods']}")
print(f"Customer service actions: {service_info.get('actions', [])}")
# List methods for a specific service
methods = [m for m in dir(client.customers) if not m.startswith('_')]
print("Customer service methods:", methods)
# Check for custom actions
actions = [m for m in dir(client.invoices)
if m.startswith('invoke_action_')]
print("Invoice actions:", actions)Method Signatures
Use the get_signature() method to inspect the signature of any service method. This is useful for understanding parameter types and return values:
# Get the signature of a specific method
sig = client.sales_order.get_signature('get_list')
print(sig)
# Output: "sales_order.get_list(options: QueryOptions = None, api_version: str = None) -> list"
# Get signature for get_by_id
sig = client.customers.get_signature('get_by_id')
print(sig)
# Output: "customers.get_by_id(entity_id: str, options: QueryOptions = None, api_version: str = None) -> dict"
# Get signature for put_entity
sig = client.invoices.get_signature('put_entity')
print(sig)
# Output: "invoices.put_entity(data: Any, options: QueryOptions = None, api_version: str = None) -> dict"
# Get signature for file operations
sig = client.account_group.get_signature('get_files')
print(sig)
# Output: "account_group.get_files(entity_id: str, api_version: str = None) -> list"
# List all available methods if method not found
try:
sig = client.customers.get_signature('nonexistent_method')
except ValueError as e:
print(f"Error: {e}")
# Prints: "Method 'nonexistent_method' not found. Available methods: get_list, get_by_id, ..."
# Useful for debugging and understanding service capabilities
for method_name in ['get_list', 'get_by_id', 'put_entity', 'delete_by_id']:
sig = client.customers.get_signature(method_name)
print(f"{method_name}: {sig}")
# Get signatures for custom actions
if hasattr(client.sales_orders, 'invoke_action_release'):
sig = client.sales_orders.get_signature('invoke_action_release')
print(f"Release action: {sig}")Method signatures are stored during service generation and provide full type information including parameter names, types, default values, and return types.
Schema Information
Services provide a get_ad_hoc_schema() method to retrieve Acumatica's native $adHocSchema information for the endpoint. This returns the raw schema data from Acumatica:
# Get the Acumatica native schema for a service
schema = client.sales_order.get_ad_hoc_schema()
print(schema)
# Returns the $adHocSchema response from Acumatica
# With specific API version
schema = client.customers.get_ad_hoc_schema(api_version="23.200.001")
# The schema contains Acumatica's field definitions, types, and metadata
# This is the raw schema from Acumatica, not the Python model schema
if schema:
print(f"Schema fields: {schema.keys()}")
# Explore the native Acumatica schema structure The get_ad_hoc_schema() method calls Acumatica's $adHocSchema endpoint and returns the native schema structure. This is different from the model's get_schema() classmethod which returns simplified Python type information.
Working with Models
Services work with dynamically generated models accessed via client.models. See the Model Factory page for details.
# Create entity using model
customer = client.models.Customer(
CustomerID="CUST001",
CustomerName="Acme Corp"
)
# Models provide type hints and validation
invoice = client.models.Invoice(
CustomerID="CUST001",
Date="2024-03-15",
Description="March Services",
Details=[
client.models.InvoiceDetail(
InventoryID="SERVICE01",
Quantity=1,
UnitPrice=1000.00
)
]
)
# Send to Acumatica
created_invoice = client.invoices.put_entity(invoice)Error Handling
Service methods raise specific exceptions for different error conditions:
from easy_acumatica.exceptions import (
AcumaticaNotFoundError,
AcumaticaAuthError,
AcumaticaAPIError
)
try:
customer = client.customers.get_by_id("NOTEXIST")
except AcumaticaNotFoundError:
print("Customer not found")
except AcumaticaAuthError:
print("Authentication failed")
except AcumaticaAPIError as e:
print(f"API error: {e.status_code} - {e.message}")