Filters - Nioron07/Easy-Acumatica GitHub Wiki
This guide explains how to use the F object from easy-acumatica to build flexible, type-safe OData-v3 queries in a Pythonic way. This new method uses operator overloading to replace manual string manipulation.
1. Importing the Helper
To get started, import the F
factory object.
from easy_acumatica.models.filter_builder import F
F
Object: Your Gateway to Filtering
2. The The F
object is a factory for creating filter expressions. You can refer to any field in your Acumatica entity by simply accessing it as an attribute on F
.
F.FieldName
creates an expression for a standard field.F.MainContact.Email
creates an expression for a field on a linked entity.
These F
objects can then be combined using standard Python operators to build your filter.
3. Building Filters with Operators
You can use familiar Python operators to create your filter logic. The library automatically translates them to the correct OData syntax.
3.1. Comparison Operators
Python Operator | OData Equivalent | Example |
---|---|---|
== |
eq | F.Status == 'Active' |
!= |
ne | F.Type != 'Credit Memo' |
> |
gt | F.Amount > 100 |
>= |
ge | F.Amount >= 100 |
< |
lt | F.Amount < 500 |
<= |
le | F.Amount <= 500 |
3.2. Logical Operators
Because Python's and, or, and not keywords cannot be overloaded, we use the bitwise operators &
, |
, and ~
for logical operations.
Python Operator | OData Equivalent | Example |
---|---|---|
& |
and | (F.Status == 'Open') & (F.Amount > 1000) |
| |
or | (F.Name == 'Matt') | (F.Age >= 30) |
~ |
not | ~(F.Country == 'USA') |
Important: Always wrap individual clauses in parentheses () when combining them with logical operators to ensure correct precedence.
3.3. Arithmetic Operators
Standard arithmetic operators +
, -
, *
, /
, and %
are also supported.
# OData: (Price mul 1.1) gt 50
filter_expr = (F.Price * 1.1) > 50
# OData: (QtyOnHand sub QtyOnOrders) gt 0
inventory_check = (F.QtyOnHand - F.QtyOnOrders) > 0
4. Using OData Functions
For OData functions like tolower, substringof, or round, you can chain methods directly onto your F
object expressions.
4.1. String Functions
- contains(value):
F.CustomerName.contains('Acme')
->substringof('Acme', CustomerName)
- startswith(value):
F.SKU.startswith('PROD-')
->startswith(SKU, 'PROD-')
- endswith(value):
F.FileName.endswith('.pdf')
->endswith(FileName, '.pdf')
- tolower():
F.Email.tolower() == '[email protected]'
- toupper():
F.Country.toupper() == 'USA'
- length():
F.Description.length() > 10
- trim():
F.Comment.trim() != ''
- indexof(substring):
F.Description.indexof('Error') > -1
- replace(find, replace_with):
F.Description.replace(' ', '\_')
- concat(string):
F.FirstName.concat(' ').concat(F.LastName)
- substring(pos, length?):
F.AccountNumber.substring(0, 4\) == 'ACCT'
4.2. Date and Math Functions
- .day(), .month(), .year(), .hour(), etc.:
F.DueDate.month() == 12
- .round(), .floor(), .ceiling():
F.Freight.round() > 10
5. Putting It All Together: Complex Examples
The true power of this new syntax is how cleanly these pieces can be combined.
Example 1: Nested Logic
Find all open invoices over $500, or any invoice for a specific customer.
filter1 = (F.Type == 'Invoice') & (F.Status == 'Open') & (F.Amount > 500)
filter2 = F.CustomerID == 'C00123'
final_filter = filter1 | filter2
# This creates a filter like:
# ((((Type eq 'Invoice') and (Status eq 'Open')) and (Amount gt 500)) or (CustomerID eq 'C00123'))
Example 2: Chained Functions and Logic
Find all active customers whose email address is in the 'example.com' domain.
active_customers = F.Status == 'Active'
email_check = F.MainContact.Email.tolower().endswith('@example.com')
final_filter = active_customers & email_check
# ((Status eq 'Active') and endswith(tolower(MainContact/Email),'@example.com'))
Example 3: Filtering on Custom Fields
You can easily filter on custom fields using the special F.cf() helper method.
# Find records where the custom string field UsrRepairType is 'Battery'
cf_filter = F.cf("String", "ItemSettings", "UsrRepairType") == 'Battery'
# Use it in a larger query
full_query = (F.IsActive == True) & cf_filter
# ((IsActive eq true) and (cf.String(f='ItemSettings.UsrRepairType') eq 'Battery'))
This updated approach provides a much more intuitive and less error-prone way to construct powerful OData queries.