&
|
>
=
OData Filter Builder
Filters & F Factory
Build type-safe OData filters with Python-like syntax
Python
F.Amount > 1000
OData
Amount gt 1000
Filter Translator
Convert OData syntax to Python F factory 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 is your gateway to building type-safe, readable OData filters using familiar Python syntax. Instead of writing error-prone OData strings, you can use Python operators and methods that get automatically translated to proper OData syntax.
from easy_acumatica import F
# Import the F factory
from easy_acumatica 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 import QueryOptions
options = QueryOptions(filter=combined_filter)
results = client.customers.get_list(options)
Common Filter Patterns
Basic Comparisons
Use standard Python operators for comparisons
# 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
Combine filters with &, |, and ~
# 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
Built-in string manipulation functions
# 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
Extract date components for filtering
# 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
Perform calculations 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 if value is in a list
# 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 |