&
|
>
=
OData Filter Builder
Filters & F Factory
Type-safe OData filters using Python operators
Python
F.Amount > 1000OData
Amount gt 1000Filter Translator
Convert OData filters to Python syntax
Try these:
CustomerID eq 'ABC123' and Amount gt 100...
Status eq 'Active' or Priority eq 1...
startswith(CustomerName, 'ABC') and cont...
year(CreatedDate) eq 2024 and month(Crea...
Price mul Quantity gt 500...
CustomerClass in ('WHOLESALE', 'RETAIL')...
Translation will appear here...The F Factory
The F factory builds OData filters using Python operators. Use standard comparison operators (==, !=, >, <) and methods that translate to OData syntax.
from easy_acumatica.odata import F
# Import the F factory
from easy_acumatica.odata import F
# Create simple filters
active_filter = F.Status == "Active"
high_value_filter = F.Amount > 1000
# Combine filters with logical operators
combined_filter = (F.Status == "Active") & (F.Amount > 1000)
# Use the filter with QueryOptions
from easy_acumatica.odata import QueryOptions
options = QueryOptions(filter=combined_filter)
results = client.customers.get_list(options)Common Filter Patterns
Basic Comparisons
Standard comparison operators
# Equality
filter = F.CustomerID == "ABCDE"
# Inequality
filter = F.Status != "Deleted"
# Greater than / Less than
filter = (F.Amount > 1000) & (F.Amount <= 5000)
# Null checks
filter = F.DeletedDate == None
filter = F.Description != None Logical Operations
Logical AND (&), OR (|), NOT (~)
# AND operation
filter = (F.Status == "Active") & (F.Balance > 0)
# OR operation
filter = (F.Priority == "High") | (F.DueDate < datetime.now())
# NOT operation
filter = ~(F.Status == "Deleted")
# Complex combination
filter = (
(F.CustomerClass == "WHOLESALE") &
((F.CreditLimit >= 10000) | (F.PrepaymentRequired == False))
) String Functions
String operations
# String matching
filter = F.CustomerName.startswith("ABC")
filter = F.Email.endswith("@company.com")
filter = F.Description.contains("urgent")
# String transformation
filter = F.CustomerName.tolower() == "acme corp"
filter = F.Code.toupper().startswith("US")
# String length
filter = F.Description.length() > 100
filter = F.Code.trim().length() == 5 Date Functions
Date component extraction
# Date component extraction
filter = F.CreatedDate.year() == 2024
filter = F.DueDate.month() == 12
filter = F.OrderDate.day() <= 15
# Date range filters
filter = (F.Date.year() == 2024) & (F.Date.month() >= 6)
# Time components
filter = F.Timestamp.hour() >= 9
filter = F.Timestamp.minute() == 0 Arithmetic Operations
Arithmetic operations in filters
# Basic arithmetic
filter = (F.Price * F.Quantity) > 1000
filter = (F.Total - F.Discount) >= 500
# Percentage calculations
filter = (F.Discount / F.Total) > 0.1
filter = F.Quantity % 10 == 0
# Complex expressions
filter = ((F.Price * 1.1) + F.Tax) <= F.MaxPrice List Operations (v4)
Check membership in list (OData v4)
# In operator (OData v4 only)
filter = F.Status.in_(["Active", "Pending", "Processing"])
filter = F.CustomerClass.in_(["WHOLESALE", "RETAIL"])
# Multiple values
customer_ids = ["CUST001", "CUST002", "CUST003"]
filter = F.CustomerID.in_(customer_ids)Advanced Features
Best Practices
Do's
Use F factory for all field references
Use parentheses for complex expressions
Use type-safe operators (==, !=, >, etc.)
Use in_() for multiple value checks
Chain string functions for complex logic
Test filters with small datasets first
Use meaningful variable names for filters
Don'ts
Writing raw OData strings
Forgetting parentheses in complex filters
Using Python "and", "or" instead of &, |
Hardcoding field names as strings
Building filters with string concatenation
Ignoring null value handling
Creating overly complex single expressions
Complete Function Reference
All available filter operations and their OData equivalents:
| Python Syntax | OData Output | Description | Version |
|---|---|---|---|
F.Field == value | Field eq value | Equality check | v3+ |
F.Field != value | Field ne value | Inequality check | v3+ |
F.Field > value | Field gt value | Greater than | v3+ |
F.Field >= value | Field ge value | Greater or equal | v3+ |
F.Field < value | Field lt value | Less than | v3+ |
F.Field <= value | Field le value | Less or equal | v3+ |
filter1 & filter2 | filter1 and filter2 | Logical AND | v3+ |
filter1 | filter2 | filter1 or filter2 | Logical OR | v3+ |
~filter | not filter | Logical NOT | v3+ |
F.Field + value | Field add value | Addition | v3+ |
F.Field - value | Field sub value | Subtraction | v3+ |
F.Field * value | Field mul value | Multiplication | v3+ |
F.Field / value | Field div value | Division | v3+ |
F.Field % value | Field mod value | Modulo | v3+ |
F.Field.startswith("val") | startswith(Field, 'val') | String starts with | v3+ |
F.Field.endswith("val") | endswith(Field, 'val') | String ends with | v3+ |
F.Field.contains("val") | contains(Field, 'val') | String contains | v4 |
F.Field.substringof("val") | substringof('val', Field) | String contains (v3) | v3 |
F.Field.length() | length(Field) | String length | v3+ |
F.Field.indexof("val") | indexof(Field, 'val') | Find substring index | v3+ |
F.Field.substring(start) | substring(Field, start) | Extract substring | v3+ |
F.Field.tolower() | tolower(Field) | Convert to lowercase | v3+ |
F.Field.toupper() | toupper(Field) | Convert to uppercase | v3+ |
F.Field.trim() | trim(Field) | Remove whitespace | v3+ |
F.Field.replace("a", "b") | replace(Field, 'a', 'b') | Replace substring | v4 |
F.Date.year() | year(Date) | Extract year | v3+ |
F.Date.month() | month(Date) | Extract month | v3+ |
F.Date.day() | day(Date) | Extract day | v3+ |
F.Time.hour() | hour(Time) | Extract hour | v3+ |
F.Time.minute() | minute(Time) | Extract minute | v3+ |
F.Time.second() | second(Time) | Extract second | v3+ |
F.Field.in_([1,2,3]) | Field in (1,2,3) | Value in list | v4 |
F.cf("Type", "View", "Field") | cf.Type(f='View.Field') | Custom field | v3+ |
Additional Functions
| Function | Example | Description | Version |
|---|---|---|---|
round() | F.Price.round() | Round to nearest integer | v3+ |
floor() | F.Price.floor() | Round down to integer | v3+ |
ceiling() | F.Price.ceiling() | Round up to integer | v3+ |
has() | F.Field.has(Enum.Value) | Check enum flag | v4 |
cast() | F.Field.cast("Edm.String") | Type cast | v4 |
isof() | F.Field.isof("Type") | Type check | v4 |
divby() | F.Total.divby(12) | Decimal division | v4 |
matchesPattern() | F.Email.matchesPattern(".*@.*") | Regex match | v4 |