Rules
RuleSet
A RuleSet is a collection of Conditions and Actions that will evaluate for one or more transactions.
The Conditions are list of logical comparisons that will compare fields from the
transaction to stored values. If all conditions from a Transaction are met (or any, if the Rule has an or
operation), then the actions will be applied.
The actions are a list of changes that will be applied to the transaction.
Full example ruleset: "If all of these conditions match 'notes' contains 'foo' then set 'notes' to 'bar'"
To create that rule set, you can do:
RuleSet(rules=[
Rule(
# all of these conditions match
operation="and",
# 'notes' contains 'foo'
conditions=[Condition(field="notes", op=ConditionType.CONTAINS, value="foo")],
# set 'notes' to 'bar'
actions=[Action(field="notes", value="bar")],
)
])
Parameters:
Methods:
-
__str__–Returns a readable string representation of the ruleset.
-
__iter__–Returns an iterator over the rules in the ruleset.
-
_run– -
run–Runs the rules of the Ruleset for every transaction on the list.
-
add–Adds a new rule to the ruleset.
Attributes:
__str__
__iter__
_run
_run(
transaction: Transactions | Sequence[Transactions],
stage: Literal["pre", "post", None],
)
Source code in actual/rules.py
run
run(
transaction: Transactions | Sequence[Transactions],
stage: Literal["all", "pre", "post", None] = "all",
)
Runs the rules of the Ruleset for every transaction on the list.
If stage is set to 'all' (default), all rules are run in the order pre -> None -> post.
You can provide a value to run only a certain stage of rules.
If necessary, you can also individually select the rules you want to run by initializing a new ruleset from
the original one, or select individual rules from, by using the list of rules RuleSet.rules.
Source code in actual/rules.py
Rule
Bases: BaseModel
Contains an individual rule, with multiple conditions and multiple actions. Can only be used
on a single transaction at a time. You can evaluate if the rule would activate without doing any changes on the
transaction by calling the evaluate method.
Note that the frontend refers to the operation as either 'all' or 'any', but stores them as 'and' and 'or' respectively on the database. If you provide the frontend values, they will be converted on the background automatically.
Parameters:
-
(conditionslist[Condition]) –List of conditions that need to be met (one or all) in order for the actions to be applied.
-
(operationLiteral['and', 'or', 'any', 'all'], default:'and') –Operation to apply for the rule evaluation. If 'all' or 'any' need to be evaluated.
-
(actionslist[Action]) –List of actions to apply to the transaction.
-
(stageLiteral['pre', 'post', None], default:None) –Stage in which the rule will be evaluated (default None)
Methods:
-
correct_operation–If the user provides the same
alloranythat the frontend provides, we fix it silently toandand -
__str__–Returns a readable string representation of the rule.
-
set_split_amount–Run the rules from setting split amounts.
-
evaluate–Evaluates the rule on the transaction, without applying any action.
-
run–Runs the rule on the transaction, calling evaluate, and if the return is
Truethen running each of
Attributes:
-
conditions(list[Condition]) – -
operation(Literal['and', 'or', 'any', 'all']) – -
actions(list[Action]) – -
stage(Literal['pre', 'post', None]) –
conditions
class-attribute
instance-attribute
conditions: list[Condition] = Field(
...,
description="List of conditions that need to be met (one or all) in order for the actions to be applied.",
)
operation
class-attribute
instance-attribute
operation: Literal["and", "or", "any", "all"] = Field(
"and",
description="Operation to apply for the rule evaluation. If 'all' or 'any' need to be evaluated.",
)
actions
class-attribute
instance-attribute
stage
class-attribute
instance-attribute
stage: Literal["pre", "post", None] = Field(
None,
description="Stage in which the rule will be evaluated (default None)",
)
correct_operation
If the user provides the same all or any that the frontend provides, we fix it silently to and and
or respectively.
Source code in actual/rules.py
__str__
Returns a readable string representation of the rule.
Source code in actual/rules.py
set_split_amount
set_split_amount(
transaction: Transactions,
) -> list[Transactions]
Run the rules from setting split amounts.
Source code in actual/rules.py
evaluate
evaluate(transaction: Transactions) -> bool
Evaluates the rule on the transaction, without applying any action.
run
run(transaction: Transactions) -> bool
Runs the rule on the transaction, calling evaluate, and if the return is True then running each of
the actions.
Source code in actual/rules.py
Condition
Bases: BaseModel
A condition does a single comparison check for a transaction. The op indicates the action type, usually being
set to IS or CONTAINS, and the comparison is applied to a field with certain value. If the transaction
value matches the condition, the run method returns True, otherwise it returns False. The individual condition
cannot change the value of the transaction, as only the Action can.
Important: Actual shows the amount on frontend as decimal but handles it internally as cents. Make sure that, if
you provide the amount rule manually, you either provide number of cents, as an integers, or a float that get
automatically converted to cents. As an example, 50 will be interpreted as 50 cents, but 50.0 will be
interpreted as 50 of the currency (or 5000 cents).
The 'field' can be one of the following ('type' will be set automatically):
imported_description:typemust bestringandvalueany stringacct:typemust beidandvaluea valid uuidcategory:typemust beidandvaluea valid uuiddate:typemust bedateandvaluea string in the date format'2024-04-11'description:typemust beidandvaluea valid uuid (means payee_id)notes:typemust be 'string' andvalueany stringamount:typemust benumberand format in centsamount_inflow:typemust benumberand format in cents, will set"options":{"inflow":true}amount_outflow:typemust benumberand format in cents, will set"options":{"outflow":true}
Parameters:
-
(fieldLiteral['imported_description', 'acct', 'category', 'date', 'description', 'notes', 'amount', 'amount_inflow', 'amount_outflow']) – -
(opConditionType) – -
(valueint | str | list[str] | Schedule | BetweenValue | date | None) – -
(typeValueType, default:<ValueType.ID: 'id'>) – -
(optionsdict | None, default:None) –
Methods:
-
__str__– -
serialize_model–Returns valid dict for database insertion.
-
get_value– -
convert_value– -
check_operation_type– -
run–
Attributes:
-
field(Literal['imported_description', 'acct', 'category', 'date', 'description', 'notes', 'amount', 'amount_inflow', 'amount_outflow']) – -
op(ConditionType) – -
value(Annotated[int | str | list[str] | Schedule | BetweenValue | date | None, BeforeValidator(_coerce_value)]) – -
type(ValueType) – -
options(dict | None) –
field
instance-attribute
field: Literal[
"imported_description",
"acct",
"category",
"date",
"description",
"notes",
"amount",
"amount_inflow",
"amount_outflow",
]
value
instance-attribute
value: Annotated[
int
| str
| list[str]
| Schedule
| BetweenValue
| date
| None,
BeforeValidator(_coerce_value),
]
__str__
__str__() -> str
get_value
convert_value
Source code in actual/rules.py
check_operation_type
Source code in actual/rules.py
run
run(transaction: Transactions) -> bool
Source code in actual/rules.py
Action
Bases: BaseModel
An Action does a single column change for a transaction. The op indicates the
ActionType, usually being to SET a field with certain value.
For the op LINKED_SCHEDULE, the operation will link the transaction to a certain schedule id that generated it.
This is often done automatically when you create schedules, as they are also rules internally.
For the op SET_SPLIT_AMOUNT, the transaction will be split into multiple different splits depending on the
method defined by the user, being it on fixed-amount, fixed-percent or remainder. The method will be stored
under options with the format {"method": "remainder", "splitIndex": 1}, indicating both the method of splitting
but also which index that split will take.
The field can be one of the following (type will be set automatically based on the database):
category:typemust beidandvaluea valid uuiddescription:typemust beidvaluea valid uuid (additional"options":{"splitIndex":0})notes:typemust bestringandvalueany stringcleared:typemust bebooleanandvalueis a literal True/False (additional"options":{"splitIndex":0})acct:typemust beidandvaluean uuiddate:typemust bedateandvaluea string in the date format"2024-04-11"amount:typemust benumberand format in cents
Parameters:
-
(fieldLiteral['category', 'description', 'notes', 'cleared', 'acct', 'date', 'amount'] | None, default:None) – -
(opActionType, default:<ActionType.SET: 'set'>) –Action type to apply (default changes a column).
-
(valuestr | bool | int | None) – -
(typeValueType, default:<ValueType.ID: 'id'>) – -
(optionsdict[str, Union[str, int]] | None, default:None) –
Methods:
-
__str__– -
serialize_model–Returns valid dict for database insertion.
-
convert_value– -
check_operation_type– -
get_split_index–Extract splitIndex from options as an int.
-
run–Runs the action on the transaction, regardless of the condition. For the condition based rule, see
Attributes:
-
field(Literal['category', 'description', 'notes', 'cleared', 'acct', 'date', 'amount'] | None) – -
op(ActionType) – -
value(Annotated[str | bool | int | None, BeforeValidator(_coerce_value)]) – -
type(ValueType) – -
options(dict[str, str | int] | None) –
field
class-attribute
instance-attribute
field: (
Literal[
"category",
"description",
"notes",
"cleared",
"acct",
"date",
"amount",
]
| None
) = None
op
class-attribute
instance-attribute
op: ActionType = Field(
SET,
description="Action type to apply (default changes a column).",
)
value
instance-attribute
__str__
__str__() -> str
Source code in actual/rules.py
convert_value
classmethod
check_operation_type
Source code in actual/rules.py
get_split_index
get_split_index() -> int
run
run(transaction: Transactions) -> None
Runs the action on the transaction, regardless of the condition. For the condition based rule, see Rule.run.
Source code in actual/rules.py
ValueType
Bases: Enum
Methods:
-
is_valid–Returns if a conditional operation for a certain type is valid. For example, if the value is of type string,
-
validate– -
from_field–
Attributes:
is_valid
is_valid(operation: ConditionType) -> bool
Returns if a conditional operation for a certain type is valid. For example, if the value is of type string,
then RuleValueType.STRING.is_valid(ConditionType.GT) will return false, because there is no logical
greater than defined for strings.
Source code in actual/rules.py
validate
Source code in actual/rules.py
from_field
classmethod
Source code in actual/rules.py
ActionType
Attributes:
ConditionType
Attributes:
-
IS– -
IS_APPROX– -
GT– -
GTE– -
LT– -
LTE– -
CONTAINS– -
ONE_OF– -
IS_NOT– -
DOES_NOT_CONTAIN– -
NOT_ONE_OF– -
IS_BETWEEN– -
MATCHES– -
HAS_TAGS– -
ON_BUDGET– -
OFF_BUDGET–