mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-04-24 01:28:42 +02:00
changes
This commit is contained in:
@@ -42,6 +42,7 @@ class TransactionRuleForm(forms.ModelForm):
|
|||||||
Column(Switch("on_create")),
|
Column(Switch("on_create")),
|
||||||
Column(Switch("on_delete")),
|
Column(Switch("on_delete")),
|
||||||
),
|
),
|
||||||
|
"order",
|
||||||
Switch("sequenced"),
|
Switch("sequenced"),
|
||||||
"description",
|
"description",
|
||||||
"trigger",
|
"trigger",
|
||||||
|
|||||||
18
app/apps/rules/migrations/0018_transactionrule_order.py
Normal file
18
app/apps/rules/migrations/0018_transactionrule_order.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-09-02 14:52
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rules', '0017_updateorcreatetransactionruleaction_search_mute_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transactionrule',
|
||||||
|
name='order',
|
||||||
|
field=models.PositiveIntegerField(default=0, verbose_name='Order'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -17,6 +17,7 @@ class TransactionRule(SharedObject):
|
|||||||
verbose_name=_("Sequenced"),
|
verbose_name=_("Sequenced"),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
order = models.PositiveIntegerField(default=0, verbose_name=_("Order"))
|
||||||
|
|
||||||
objects = SharedObjectManager()
|
objects = SharedObjectManager()
|
||||||
all_objects = models.Manager() # Unfiltered manager
|
all_objects = models.Manager() # Unfiltered manager
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from decimal import Decimal
|
|||||||
from itertools import chain
|
from itertools import chain
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from random import randint, random
|
from random import randint, random
|
||||||
from textwrap import indent
|
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from cachalot.api import cachalot_disabled
|
from cachalot.api import cachalot_disabled
|
||||||
@@ -38,6 +37,10 @@ class DryRunResults:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.results = []
|
self.results = []
|
||||||
|
|
||||||
|
def header(self, header: str, action):
|
||||||
|
result = {"type": "header", "header_type": header, "action": action}
|
||||||
|
self.results.append(result)
|
||||||
|
|
||||||
def triggering_transaction(self, instance):
|
def triggering_transaction(self, instance):
|
||||||
result = {
|
result = {
|
||||||
"type": "triggering_transaction",
|
"type": "triggering_transaction",
|
||||||
@@ -82,18 +85,12 @@ class DryRunResults:
|
|||||||
}
|
}
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
def error(
|
def error(self, error, level: Literal["error", "warning", "info"] = "error"):
|
||||||
self,
|
|
||||||
action: Literal["update_or_create_transaction", "edit_transaction"],
|
|
||||||
error,
|
|
||||||
action_obj,
|
|
||||||
):
|
|
||||||
result = {
|
result = {
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"action": action,
|
|
||||||
"action_obj": action_obj,
|
|
||||||
"error": error,
|
"error": error,
|
||||||
"traceback": traceback.format_exc(),
|
"traceback": traceback.format_exc(),
|
||||||
|
"level": level,
|
||||||
}
|
}
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
@@ -126,129 +123,145 @@ def check_for_transaction_rules(
|
|||||||
if k.startswith(prefix):
|
if k.startswith(prefix):
|
||||||
del simple.names[k]
|
del simple.names[k]
|
||||||
|
|
||||||
def _get_names(instance: Transaction | dict, prefix: str = ""):
|
def _get_names(transaction: Transaction | dict, prefix: str = ""):
|
||||||
if isinstance(instance, Transaction):
|
if isinstance(transaction, Transaction):
|
||||||
return {
|
return {
|
||||||
f"{prefix}id": instance.id,
|
"is_on_create": True if signal == "transaction_created" else False,
|
||||||
f"{prefix}account_name": instance.account.name if instance.id else None,
|
"is_on_delete": True if signal == "transaction_deleted" else False,
|
||||||
f"{prefix}account_id": instance.account.id if instance.id else None,
|
"is_on_update": True if signal == "transaction_updated" else False,
|
||||||
|
f"{prefix}id": transaction.id,
|
||||||
|
f"{prefix}account_name": (
|
||||||
|
transaction.account.name if transaction.id else None
|
||||||
|
),
|
||||||
|
f"{prefix}account_id": (
|
||||||
|
transaction.account.id if transaction.id else None
|
||||||
|
),
|
||||||
f"{prefix}account_group_name": (
|
f"{prefix}account_group_name": (
|
||||||
instance.account.group.name
|
transaction.account.group.name
|
||||||
if instance.id and instance.account.group
|
if transaction.id and transaction.account.group
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
f"{prefix}account_group_id": (
|
f"{prefix}account_group_id": (
|
||||||
instance.account.group.id
|
transaction.account.group.id
|
||||||
if instance.id and instance.account.group
|
if transaction.id and transaction.account.group
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
f"{prefix}is_asset_account": (
|
f"{prefix}is_asset_account": (
|
||||||
instance.account.is_asset if instance.id else None
|
transaction.account.is_asset if transaction.id else None
|
||||||
),
|
),
|
||||||
f"{prefix}is_archived_account": (
|
f"{prefix}is_archived_account": (
|
||||||
instance.account.is_archived if instance.id else None
|
transaction.account.is_archived if transaction.id else None
|
||||||
),
|
),
|
||||||
f"{prefix}category_name": (
|
f"{prefix}category_name": (
|
||||||
instance.category.name if instance.category else None
|
transaction.category.name if transaction.category else None
|
||||||
),
|
),
|
||||||
f"{prefix}category_id": (
|
f"{prefix}category_id": (
|
||||||
instance.category.id if instance.category else None
|
transaction.category.id if transaction.category else None
|
||||||
),
|
),
|
||||||
f"{prefix}tag_names": (
|
f"{prefix}tag_names": (
|
||||||
[x.name for x in instance.tags.all()] if instance.id else []
|
[x.name for x in transaction.tags.all()] if transaction.id else []
|
||||||
),
|
),
|
||||||
f"{prefix}tag_ids": (
|
f"{prefix}tag_ids": (
|
||||||
[x.id for x in instance.tags.all()] if instance.id else []
|
[x.id for x in transaction.tags.all()] if transaction.id else []
|
||||||
),
|
),
|
||||||
f"{prefix}entities_names": (
|
f"{prefix}entities_names": (
|
||||||
[x.name for x in instance.entities.all()] if instance.id else []
|
[x.name for x in transaction.entities.all()]
|
||||||
|
if transaction.id
|
||||||
|
else []
|
||||||
),
|
),
|
||||||
f"{prefix}entities_ids": (
|
f"{prefix}entities_ids": (
|
||||||
[x.id for x in instance.entities.all()] if instance.id else []
|
[x.id for x in transaction.entities.all()] if transaction.id else []
|
||||||
),
|
),
|
||||||
f"{prefix}is_expense": instance.type == Transaction.Type.EXPENSE,
|
f"{prefix}is_expense": transaction.type == Transaction.Type.EXPENSE,
|
||||||
f"{prefix}is_income": instance.type == Transaction.Type.INCOME,
|
f"{prefix}is_income": transaction.type == Transaction.Type.INCOME,
|
||||||
f"{prefix}is_paid": instance.is_paid,
|
f"{prefix}is_paid": transaction.is_paid,
|
||||||
f"{prefix}description": instance.description,
|
f"{prefix}description": transaction.description,
|
||||||
f"{prefix}amount": instance.amount or 0,
|
f"{prefix}amount": transaction.amount or 0,
|
||||||
f"{prefix}notes": instance.notes,
|
f"{prefix}notes": transaction.notes,
|
||||||
f"{prefix}date": instance.date,
|
f"{prefix}date": transaction.date,
|
||||||
f"{prefix}reference_date": instance.reference_date,
|
f"{prefix}reference_date": transaction.reference_date,
|
||||||
f"{prefix}internal_note": instance.internal_note,
|
f"{prefix}internal_note": transaction.internal_note,
|
||||||
f"{prefix}internal_id": instance.internal_id,
|
f"{prefix}internal_id": transaction.internal_id,
|
||||||
f"{prefix}is_deleted": instance.deleted,
|
f"{prefix}is_deleted": transaction.deleted,
|
||||||
f"{prefix}is_muted": instance.mute,
|
f"{prefix}is_muted": transaction.mute,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
f"{prefix}id": instance.get("id"),
|
"is_on_create": True if signal == "transaction_created" else False,
|
||||||
f"{prefix}account_name": instance.get("account", (None, None))[1],
|
"is_on_delete": True if signal == "transaction_deleted" else False,
|
||||||
f"{prefix}account_id": instance.get("account", (None, None))[0],
|
"is_on_update": True if signal == "transaction_updated" else False,
|
||||||
f"{prefix}account_group_name": instance.get(
|
f"{prefix}id": transaction.get("id"),
|
||||||
|
f"{prefix}account_name": transaction.get("account", (None, None))[1],
|
||||||
|
f"{prefix}account_id": transaction.get("account", (None, None))[0],
|
||||||
|
f"{prefix}account_group_name": transaction.get(
|
||||||
"account_group", (None, None)
|
"account_group", (None, None)
|
||||||
)[1],
|
)[1],
|
||||||
f"{prefix}account_group_id": instance.get(
|
f"{prefix}account_group_id": transaction.get(
|
||||||
"account_group", (None, None)
|
"account_group", (None, None)
|
||||||
)[0],
|
)[0],
|
||||||
f"{prefix}is_asset_account": instance.get("is_asset"),
|
f"{prefix}is_asset_account": transaction.get("is_asset"),
|
||||||
f"{prefix}is_archived_account": instance.get("is_archived"),
|
f"{prefix}is_archived_account": transaction.get("is_archived"),
|
||||||
f"{prefix}category_name": instance.get("category", (None, None))[1],
|
f"{prefix}category_name": transaction.get("category", (None, None))[1],
|
||||||
f"{prefix}category_id": instance.get("category", (None, None))[0],
|
f"{prefix}category_id": transaction.get("category", (None, None))[0],
|
||||||
f"{prefix}tag_names": [x[1] for x in instance.get("tags", [])],
|
f"{prefix}tag_names": [x[1] for x in transaction.get("tags", [])],
|
||||||
f"{prefix}tag_ids": [x[0] for x in instance.get("tags", [])],
|
f"{prefix}tag_ids": [x[0] for x in transaction.get("tags", [])],
|
||||||
f"{prefix}entities_names": [x[1] for x in instance.get("entities", [])],
|
f"{prefix}entities_names": [
|
||||||
f"{prefix}entities_ids": [x[0] for x in instance.get("entities", [])],
|
x[1] for x in transaction.get("entities", [])
|
||||||
f"{prefix}is_expense": instance.get("type") == Transaction.Type.EXPENSE,
|
],
|
||||||
f"{prefix}is_income": instance.get("type") == Transaction.Type.INCOME,
|
f"{prefix}entities_ids": [
|
||||||
f"{prefix}is_paid": instance.get("is_paid"),
|
x[0] for x in transaction.get("entities", [])
|
||||||
f"{prefix}description": instance.get("description", ""),
|
],
|
||||||
f"{prefix}amount": Decimal(instance.get("amount")),
|
f"{prefix}is_expense": transaction.get("type")
|
||||||
f"{prefix}notes": instance.get("notes", ""),
|
== Transaction.Type.EXPENSE,
|
||||||
f"{prefix}date": datetime.fromisoformat(instance.get("date")),
|
f"{prefix}is_income": transaction.get("type")
|
||||||
|
== Transaction.Type.INCOME,
|
||||||
|
f"{prefix}is_paid": transaction.get("is_paid"),
|
||||||
|
f"{prefix}description": transaction.get("description", ""),
|
||||||
|
f"{prefix}amount": Decimal(transaction.get("amount")),
|
||||||
|
f"{prefix}notes": transaction.get("notes", ""),
|
||||||
|
f"{prefix}date": datetime.fromisoformat(transaction.get("date")),
|
||||||
f"{prefix}reference_date": datetime.fromisoformat(
|
f"{prefix}reference_date": datetime.fromisoformat(
|
||||||
instance.get("reference_date")
|
transaction.get("reference_date")
|
||||||
),
|
),
|
||||||
f"{prefix}internal_note": instance.get("internal_note", ""),
|
f"{prefix}internal_note": transaction.get("internal_note", ""),
|
||||||
f"{prefix}internal_id": instance.get("internal_id", ""),
|
f"{prefix}internal_id": transaction.get("internal_id", ""),
|
||||||
f"{prefix}is_deleted": instance.get("deleted", True),
|
f"{prefix}is_deleted": transaction.get("deleted", True),
|
||||||
f"{prefix}is_muted": instance.get("mute", False),
|
f"{prefix}is_muted": transaction.get("mute", False),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _process_update_or_create_transaction_action(processed_action):
|
def _process_update_or_create_transaction_action(processed_action):
|
||||||
"""Helper to process a single linked transaction action"""
|
"""Helper to process a single linked transaction action"""
|
||||||
|
|
||||||
|
dry_run_results.header("update_or_create_transaction", action=processed_action)
|
||||||
|
|
||||||
# Build search query using the helper method
|
# Build search query using the helper method
|
||||||
search_query = processed_action.build_search_query(simple)
|
search_query = processed_action.build_search_query(simple)
|
||||||
_log(f" ├─ Searching transactions using: {search_query}")
|
_log(f"Searching transactions using: {search_query}")
|
||||||
|
|
||||||
starting_instance = None
|
starting_instance = None
|
||||||
|
|
||||||
# Find latest matching transaction or create new
|
# Find latest matching transaction or create new
|
||||||
if search_query:
|
if search_query:
|
||||||
transactions = Transaction.objects.filter(search_query).order_by(
|
searched_transactions = Transaction.objects.filter(search_query).order_by(
|
||||||
"-date", "-id"
|
"-date", "-id"
|
||||||
)
|
)
|
||||||
if transactions.exists():
|
if searched_transactions.exists():
|
||||||
transaction = transactions.first()
|
transaction = searched_transactions.first()
|
||||||
existing = True
|
existing = True
|
||||||
starting_instance = deepcopy(transaction)
|
starting_instance = deepcopy(transaction)
|
||||||
_log(" ├─ Found at least one matching transaction, using latest:")
|
_log("Found at least one matching transaction, using latest:")
|
||||||
_log(
|
_log("{}".format(pformat(model_to_dict(transaction))))
|
||||||
" ├─ {}".format(
|
|
||||||
indent(pformat(model_to_dict(transaction)), " ")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
transaction = Transaction()
|
transaction = Transaction()
|
||||||
existing = False
|
existing = False
|
||||||
_log(
|
_log(
|
||||||
" ├─ No matching transaction found, creating a new transaction",
|
"No matching transaction found, creating a new transaction",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
transaction = Transaction()
|
transaction = Transaction()
|
||||||
existing = False
|
existing = False
|
||||||
_log(
|
_log(
|
||||||
" ├─ No matching transaction found, creating a new transaction",
|
"No matching transaction found, creating a new transaction",
|
||||||
)
|
)
|
||||||
|
|
||||||
simple.names.update(_get_names(transaction, prefix="my_"))
|
simple.names.update(_get_names(transaction, prefix="my_"))
|
||||||
@@ -256,9 +269,10 @@ def check_for_transaction_rules(
|
|||||||
if processed_action.filter:
|
if processed_action.filter:
|
||||||
value = simple.eval(processed_action.filter)
|
value = simple.eval(processed_action.filter)
|
||||||
if not value:
|
if not value:
|
||||||
_log(
|
dry_run_results.error(
|
||||||
" ├─ Filter did not match. Execution of this action has been stopped."
|
error="Filter did not match. Execution of this action has stopped.",
|
||||||
)
|
)
|
||||||
|
_log("Filter did not match. Execution of this action has stopped.")
|
||||||
return # Short-circuit execution if filter evaluates to false
|
return # Short-circuit execution if filter evaluates to false
|
||||||
|
|
||||||
# Set fields if provided
|
# Set fields if provided
|
||||||
@@ -310,34 +324,24 @@ def check_for_transaction_rules(
|
|||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
if not transaction.id:
|
if not transaction.id:
|
||||||
_log(" ├─ Transaction would be created as:")
|
_log("Transaction would be created as:")
|
||||||
else:
|
else:
|
||||||
_log(" ├─ Trasanction would be updated as:")
|
_log("Trasanction would be updated as:")
|
||||||
|
|
||||||
_log(
|
_log(
|
||||||
" ├─ {}".format(
|
"{}".format(
|
||||||
indent(
|
pformat(model_to_dict(transaction, exclude=["tags", "entities"])),
|
||||||
pformat(
|
|
||||||
model_to_dict(transaction, exclude=["tags", "entities"])
|
|
||||||
),
|
|
||||||
" ",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not transaction.id:
|
if not transaction.id:
|
||||||
_log(" ├─ Transaction will be created as:")
|
_log("Transaction will be created as:")
|
||||||
else:
|
else:
|
||||||
_log(" ├─ Trasanction will be updated as:")
|
_log("Trasanction will be updated as:")
|
||||||
|
|
||||||
_log(
|
_log(
|
||||||
" ├─ {}".format(
|
"{}".format(
|
||||||
indent(
|
pformat(model_to_dict(transaction, exclude=["tags", "entities"])),
|
||||||
pformat(
|
|
||||||
model_to_dict(transaction, exclude=["tags", "entities"])
|
|
||||||
),
|
|
||||||
" ",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
transaction.save()
|
transaction.save()
|
||||||
@@ -347,9 +351,9 @@ def check_for_transaction_rules(
|
|||||||
if processed_action.set_tags:
|
if processed_action.set_tags:
|
||||||
tags = simple.eval(processed_action.set_tags)
|
tags = simple.eval(processed_action.set_tags)
|
||||||
if dry_run:
|
if dry_run:
|
||||||
_log(f" ├─ And tags would be set as: {tags}")
|
_log(f" And tags would be set as: {tags}")
|
||||||
else:
|
else:
|
||||||
_log(f" ├─ And tags will be set as: {tags}")
|
_log(f" And tags will be set as: {tags}")
|
||||||
transaction.tags.clear()
|
transaction.tags.clear()
|
||||||
if isinstance(tags, (list, tuple)):
|
if isinstance(tags, (list, tuple)):
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@@ -367,9 +371,9 @@ def check_for_transaction_rules(
|
|||||||
if processed_action.set_entities:
|
if processed_action.set_entities:
|
||||||
entities = simple.eval(processed_action.set_entities)
|
entities = simple.eval(processed_action.set_entities)
|
||||||
if dry_run:
|
if dry_run:
|
||||||
_log(f" ├─ And entities would be set as: {entities}")
|
_log(f" And entities would be set as: {entities}")
|
||||||
else:
|
else:
|
||||||
_log(f" ├─ And entities will be set as: {entities}")
|
_log(f" And entities will be set as: {entities}")
|
||||||
transaction.entities.clear()
|
transaction.entities.clear()
|
||||||
if isinstance(entities, (list, tuple)):
|
if isinstance(entities, (list, tuple)):
|
||||||
for entity in entities:
|
for entity in entities:
|
||||||
@@ -391,8 +395,6 @@ def check_for_transaction_rules(
|
|||||||
TransactionEntity.objects.get(name=entities)
|
TransactionEntity.objects.get(name=entities)
|
||||||
)
|
)
|
||||||
|
|
||||||
transaction.full_clean()
|
|
||||||
|
|
||||||
dry_run_results.update_or_create_transaction(
|
dry_run_results.update_or_create_transaction(
|
||||||
start_instance=starting_instance,
|
start_instance=starting_instance,
|
||||||
end_instance=deepcopy(transaction),
|
end_instance=deepcopy(transaction),
|
||||||
@@ -403,68 +405,52 @@ def check_for_transaction_rules(
|
|||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _process_edit_transaction_action(instance, processed_action) -> Transaction:
|
# transaction.full_clean()
|
||||||
|
|
||||||
|
def _process_edit_transaction_action(transaction, processed_action) -> Transaction:
|
||||||
|
dry_run_results.header("edit_transaction", action=processed_action)
|
||||||
|
|
||||||
field = processed_action.field
|
field = processed_action.field
|
||||||
original_value = getattr(instance, field)
|
original_value = getattr(transaction, field)
|
||||||
new_value = simple.eval(processed_action.value)
|
new_value = simple.eval(processed_action.value)
|
||||||
|
|
||||||
tags = []
|
tags = []
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
_log(
|
_log(
|
||||||
f" ├─ Changing field '{field}' from '{original_value}' to '{new_value}'",
|
f"Changing field '{field}' from '{original_value}' to '{new_value}'",
|
||||||
)
|
)
|
||||||
|
|
||||||
form_data = {}
|
if field == TransactionRuleAction.Field.account:
|
||||||
|
|
||||||
if field in [
|
|
||||||
TransactionRuleAction.Field.type,
|
|
||||||
TransactionRuleAction.Field.is_paid,
|
|
||||||
TransactionRuleAction.Field.date,
|
|
||||||
TransactionRuleAction.Field.reference_date,
|
|
||||||
TransactionRuleAction.Field.amount,
|
|
||||||
TransactionRuleAction.Field.description,
|
|
||||||
TransactionRuleAction.Field.notes,
|
|
||||||
TransactionRuleAction.Field.mute,
|
|
||||||
TransactionRuleAction.Field.internal_note,
|
|
||||||
TransactionRuleAction.Field.internal_id,
|
|
||||||
]:
|
|
||||||
setattr(
|
|
||||||
instance,
|
|
||||||
field,
|
|
||||||
new_value,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif field == TransactionRuleAction.Field.account:
|
|
||||||
if isinstance(new_value, int):
|
if isinstance(new_value, int):
|
||||||
account = Account.objects.get(id=new_value)
|
account = Account.objects.get(id=new_value)
|
||||||
instance.account = account
|
transaction.account = account
|
||||||
elif isinstance(new_value, str):
|
elif isinstance(new_value, str):
|
||||||
account = Account.objects.filter(name=new_value).first()
|
account = Account.objects.filter(name=new_value).first()
|
||||||
instance.account = account
|
transaction.account = account
|
||||||
|
|
||||||
elif field == TransactionRuleAction.Field.category:
|
elif field == TransactionRuleAction.Field.category:
|
||||||
if isinstance(new_value, int):
|
if isinstance(new_value, int):
|
||||||
category = TransactionCategory.objects.get(id=new_value)
|
category = TransactionCategory.objects.get(id=new_value)
|
||||||
instance.category = category
|
transaction.category = category
|
||||||
elif isinstance(new_value, str):
|
elif isinstance(new_value, str):
|
||||||
category = TransactionCategory.objects.get(name=new_value)
|
category = TransactionCategory.objects.get(name=new_value)
|
||||||
instance.category = category
|
transaction.category = category
|
||||||
|
|
||||||
elif field == TransactionRuleAction.Field.tags:
|
elif field == TransactionRuleAction.Field.tags:
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.tags.clear()
|
transaction.tags.clear()
|
||||||
if isinstance(new_value, list):
|
if isinstance(new_value, list):
|
||||||
for tag_value in new_value:
|
for tag_value in new_value:
|
||||||
if isinstance(tag_value, int):
|
if isinstance(tag_value, int):
|
||||||
tag = TransactionTag.objects.get(id=tag_value)
|
tag = TransactionTag.objects.get(id=tag_value)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.tags.add(tag)
|
transaction.tags.add(tag)
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
elif isinstance(tag_value, str):
|
elif isinstance(tag_value, str):
|
||||||
tag = TransactionTag.objects.get(name=tag_value)
|
tag = TransactionTag.objects.get(name=tag_value)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.tags.add(tag)
|
transaction.tags.add(tag)
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
|
|
||||||
elif isinstance(new_value, (int, str)):
|
elif isinstance(new_value, (int, str)):
|
||||||
@@ -474,23 +460,23 @@ def check_for_transaction_rules(
|
|||||||
tag = TransactionTag.objects.get(name=new_value)
|
tag = TransactionTag.objects.get(name=new_value)
|
||||||
|
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.tags.add(tag)
|
transaction.tags.add(tag)
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
|
|
||||||
elif field == TransactionRuleAction.Field.entities:
|
elif field == TransactionRuleAction.Field.entities:
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.entities.clear()
|
transaction.entities.clear()
|
||||||
if isinstance(new_value, list):
|
if isinstance(new_value, list):
|
||||||
for entity_value in new_value:
|
for entity_value in new_value:
|
||||||
if isinstance(entity_value, int):
|
if isinstance(entity_value, int):
|
||||||
entity = TransactionEntity.objects.get(id=entity_value)
|
entity = TransactionEntity.objects.get(id=entity_value)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.entities.add(entity)
|
transaction.entities.add(entity)
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
elif isinstance(entity_value, str):
|
elif isinstance(entity_value, str):
|
||||||
entity = TransactionEntity.objects.get(name=entity_value)
|
entity = TransactionEntity.objects.get(name=entity_value)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.entities.add(entity)
|
transaction.entities.add(entity)
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
|
|
||||||
elif isinstance(new_value, (int, str)):
|
elif isinstance(new_value, (int, str)):
|
||||||
@@ -499,13 +485,18 @@ def check_for_transaction_rules(
|
|||||||
else:
|
else:
|
||||||
entity = TransactionEntity.objects.get(name=new_value)
|
entity = TransactionEntity.objects.get(name=new_value)
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
instance.entities.add(entity)
|
transaction.entities.add(entity)
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
|
|
||||||
instance.full_clean()
|
else:
|
||||||
|
setattr(
|
||||||
|
transaction,
|
||||||
|
field,
|
||||||
|
new_value,
|
||||||
|
)
|
||||||
|
|
||||||
dry_run_results.edit_transaction(
|
dry_run_results.edit_transaction(
|
||||||
instance=deepcopy(instance),
|
instance=deepcopy(transaction),
|
||||||
action=processed_action,
|
action=processed_action,
|
||||||
old_value=original_value,
|
old_value=original_value,
|
||||||
new_value=new_value,
|
new_value=new_value,
|
||||||
@@ -514,7 +505,9 @@ def check_for_transaction_rules(
|
|||||||
entities=entities,
|
entities=entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
return instance
|
transaction.full_clean()
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
|
||||||
user = get_user_model().objects.get(id=user_id)
|
user = get_user_model().objects.get(id=user_id)
|
||||||
write_current_user(user)
|
write_current_user(user)
|
||||||
@@ -522,7 +515,7 @@ def check_for_transaction_rules(
|
|||||||
dry_run_results = DryRunResults()
|
dry_run_results = DryRunResults()
|
||||||
|
|
||||||
if dry_run and not rule_id:
|
if dry_run and not rule_id:
|
||||||
raise Exception("-> Cannot dry run without a rule id")
|
raise Exception("Cannot dry run without a rule id")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with cachalot_disabled():
|
with cachalot_disabled():
|
||||||
@@ -553,8 +546,8 @@ def check_for_transaction_rules(
|
|||||||
"transactions": transactions.TransactionsGetter,
|
"transactions": transactions.TransactionsGetter,
|
||||||
}
|
}
|
||||||
|
|
||||||
_log("-> Starting rule execution...")
|
_log("Starting rule execution...")
|
||||||
_log("-> Available functions: {}".format(functions.keys()))
|
_log("Available functions: {}".format(functions.keys()))
|
||||||
|
|
||||||
names = _get_names(instance)
|
names = _get_names(instance)
|
||||||
|
|
||||||
@@ -564,45 +557,55 @@ def check_for_transaction_rules(
|
|||||||
simple.names.update(_get_names(old_data, "old_"))
|
simple.names.update(_get_names(old_data, "old_"))
|
||||||
|
|
||||||
# Select rules based on the signal type
|
# Select rules based on the signal type
|
||||||
if signal == "transaction_created":
|
if dry_run and rule_id:
|
||||||
|
rules = TransactionRule.objects.filter(id=rule_id)
|
||||||
|
elif signal == "transaction_created":
|
||||||
rules = TransactionRule.objects.filter(
|
rules = TransactionRule.objects.filter(
|
||||||
active=True, on_create=True
|
active=True, on_create=True
|
||||||
).order_by("id")
|
).order_by("order", "id")
|
||||||
elif signal == "transaction_updated":
|
elif signal == "transaction_updated":
|
||||||
rules = TransactionRule.objects.filter(
|
rules = TransactionRule.objects.filter(
|
||||||
active=True, on_update=True
|
active=True, on_update=True
|
||||||
).order_by("id")
|
).order_by("order", "id")
|
||||||
elif signal == "transaction_deleted":
|
elif signal == "transaction_deleted":
|
||||||
rules = TransactionRule.objects.filter(
|
rules = TransactionRule.objects.filter(
|
||||||
active=True, on_delete=True
|
active=True, on_delete=True
|
||||||
).order_by("id")
|
).order_by("order", "id")
|
||||||
else:
|
else:
|
||||||
rules = TransactionRule.objects.filter(active=True).order_by("id")
|
rules = TransactionRule.objects.filter(active=True).order_by(
|
||||||
|
"order", "id"
|
||||||
|
)
|
||||||
|
|
||||||
if dry_run and rule_id:
|
_log("Testing {} rule(s)...".format(len(rules)))
|
||||||
rules = rules.filter(id=rule_id)
|
|
||||||
|
|
||||||
_log("-> Testing {} rule(s)...".format(len(rules)))
|
|
||||||
|
|
||||||
# Process the rules as before
|
# Process the rules as before
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
_log("Testing rule: {}".format(rule.name))
|
_log("Testing rule: {}".format(rule.name))
|
||||||
if simple.eval(rule.trigger):
|
if simple.eval(rule.trigger):
|
||||||
_log("├─ Initial trigger matched!")
|
_log("Initial trigger matched!")
|
||||||
# For deleted transactions, we want to limit what actions can be performed
|
# For deleted transactions, we want to limit what actions can be performed
|
||||||
if signal == "transaction_deleted":
|
if signal == "transaction_deleted":
|
||||||
_log(
|
_log(
|
||||||
"├─ Event is of type 'delete'. Only processing Update or Create actions..."
|
"Event is of type 'delete'. Only processing Update or Create actions..."
|
||||||
)
|
)
|
||||||
# Process only create/update actions, not edit actions
|
# Process only create/update actions, not edit actions
|
||||||
for action in rule.update_or_create_transaction_actions.all():
|
for action in rule.update_or_create_transaction_actions.all():
|
||||||
try:
|
try:
|
||||||
|
_log(
|
||||||
|
"Processing action with id {} and order {}...".format(
|
||||||
|
action.id, action.order
|
||||||
|
)
|
||||||
|
)
|
||||||
_process_update_or_create_transaction_action(
|
_process_update_or_create_transaction_action(
|
||||||
processed_action=action,
|
processed_action=action,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
dry_run_results.error(
|
||||||
|
"Error raised: '{}'. Check the logs tab for more "
|
||||||
|
"information".format(e)
|
||||||
|
)
|
||||||
_log(
|
_log(
|
||||||
f"├─ Error processing update or create transaction action {action.id} on deletion",
|
f"Error processing update or create transaction action {action.id} on deletion",
|
||||||
level="error",
|
level="error",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -619,7 +622,7 @@ def check_for_transaction_rules(
|
|||||||
|
|
||||||
if has_custom_order:
|
if has_custom_order:
|
||||||
_log(
|
_log(
|
||||||
"├─ One or more actions have a custom order, actions will be processed ordered by "
|
"One or more actions have a custom order, actions will be processed ordered by "
|
||||||
"order and creation date..."
|
"order and creation date..."
|
||||||
)
|
)
|
||||||
# Combine and sort actions by order
|
# Combine and sort actions by order
|
||||||
@@ -631,8 +634,13 @@ def check_for_transaction_rules(
|
|||||||
for action in all_actions:
|
for action in all_actions:
|
||||||
try:
|
try:
|
||||||
if isinstance(action, TransactionRuleAction):
|
if isinstance(action, TransactionRuleAction):
|
||||||
|
_log(
|
||||||
|
"Processing 'edit_transaction' action with id {} and order {}...".format(
|
||||||
|
action.id, action.order
|
||||||
|
)
|
||||||
|
)
|
||||||
instance = _process_edit_transaction_action(
|
instance = _process_edit_transaction_action(
|
||||||
instance=instance,
|
transaction=instance,
|
||||||
processed_action=action,
|
processed_action=action,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -640,13 +648,22 @@ def check_for_transaction_rules(
|
|||||||
# Update names for next actions
|
# Update names for next actions
|
||||||
simple.names.update(_get_names(instance))
|
simple.names.update(_get_names(instance))
|
||||||
else:
|
else:
|
||||||
|
_log(
|
||||||
|
"Processing 'update_or_create_transaction' action with id {} and order {}...".format(
|
||||||
|
action.id, action.order
|
||||||
|
)
|
||||||
|
)
|
||||||
_process_update_or_create_transaction_action(
|
_process_update_or_create_transaction_action(
|
||||||
processed_action=action,
|
processed_action=action,
|
||||||
)
|
)
|
||||||
_clear_names("my_")
|
_clear_names("my_")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
dry_run_results.error(
|
||||||
|
"Error raised: '{}'. Check the logs tab for more "
|
||||||
|
"information".format(e)
|
||||||
|
)
|
||||||
_log(
|
_log(
|
||||||
f"├─ Error processing action {action.id}",
|
f"Error processing action {action.id}",
|
||||||
level="error",
|
level="error",
|
||||||
)
|
)
|
||||||
# Save at the end
|
# Save at the end
|
||||||
@@ -654,22 +671,31 @@ def check_for_transaction_rules(
|
|||||||
instance.save()
|
instance.save()
|
||||||
else:
|
else:
|
||||||
_log(
|
_log(
|
||||||
"├─ No actions have a custom order, actions will be processed ordered by creation "
|
"No actions have a custom order, actions will be processed ordered by creation "
|
||||||
"date, with Edit actions running first, then Update or Create actions..."
|
"date, with Edit actions running first, then Update or Create actions..."
|
||||||
)
|
)
|
||||||
# Original behavior
|
# Original behavior
|
||||||
for action in edit_actions:
|
for action in edit_actions:
|
||||||
|
_log(
|
||||||
|
"Processing 'edit_transaction' action with id {}...".format(
|
||||||
|
action.id
|
||||||
|
)
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
instance = _process_edit_transaction_action(
|
instance = _process_edit_transaction_action(
|
||||||
instance=instance,
|
transaction=instance,
|
||||||
processed_action=action,
|
processed_action=action,
|
||||||
)
|
)
|
||||||
if rule.sequenced:
|
if rule.sequenced:
|
||||||
# Update names for next actions
|
# Update names for next actions
|
||||||
simple.names.update(_get_names(instance))
|
simple.names.update(_get_names(instance))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
dry_run_results.error(
|
||||||
|
"Error raised: '{}'. Check the logs tab for more "
|
||||||
|
"information".format(e)
|
||||||
|
)
|
||||||
_log(
|
_log(
|
||||||
f"├─ Error processing edit transaction action {action.id}",
|
f"Error processing edit transaction action {action.id}",
|
||||||
level="error",
|
level="error",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -680,18 +706,30 @@ def check_for_transaction_rules(
|
|||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
for action in update_or_create_actions:
|
for action in update_or_create_actions:
|
||||||
|
_log(
|
||||||
|
"Processing 'update_or_create_transaction' action with id {}...".format(
|
||||||
|
action.id
|
||||||
|
)
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
_process_update_or_create_transaction_action(
|
_process_update_or_create_transaction_action(
|
||||||
processed_action=action,
|
processed_action=action,
|
||||||
)
|
)
|
||||||
_clear_names("my_")
|
_clear_names("my_")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
dry_run_results.error(
|
||||||
|
"Error raised: '{}'. Check the logs tab for more "
|
||||||
|
"information".format(e)
|
||||||
|
)
|
||||||
_log(
|
_log(
|
||||||
f"├─ Error processing update or create transaction action {action.id}",
|
f"Error processing update or create transaction action {action.id}",
|
||||||
level="error",
|
level="error",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_log("├─ Initial trigger didn't match, this rule will be skipped")
|
dry_run_results.error(
|
||||||
|
error="Initial trigger didn't match, this rule will be skipped",
|
||||||
|
)
|
||||||
|
_log("Initial trigger didn't match, this rule will be skipped")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_log(
|
_log(
|
||||||
"** Error while executing 'check_for_transaction_rules' task",
|
"** Error while executing 'check_for_transaction_rules' task",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def rules_index(request):
|
|||||||
@disabled_on_demo
|
@disabled_on_demo
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def rules_list(request):
|
def rules_list(request):
|
||||||
transaction_rules = TransactionRule.objects.all().order_by("id")
|
transaction_rules = TransactionRule.objects.all().order_by("order", "id")
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"rules/fragments/list.html",
|
"rules/fragments/list.html",
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ class Transaction(OwnedObject):
|
|||||||
db_table = "transactions"
|
db_table = "transactions"
|
||||||
default_manager_name = "objects"
|
default_manager_name = "objects"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def clean_fields(self, *args, **kwargs):
|
||||||
self.amount = truncate_decimal(
|
self.amount = truncate_decimal(
|
||||||
value=self.amount, decimal_places=self.account.currency.decimal_places
|
value=self.amount, decimal_places=self.account.currency.decimal_places
|
||||||
)
|
)
|
||||||
@@ -390,6 +390,11 @@ class Transaction(OwnedObject):
|
|||||||
elif not self.reference_date and self.date:
|
elif not self.reference_date and self.date:
|
||||||
self.reference_date = self.date.replace(day=1)
|
self.reference_date = self.date.replace(day=1)
|
||||||
|
|
||||||
|
super().clean_fields(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
# This is not recommended as it will run twice on some cases like form and API saves.
|
||||||
|
# We only do this here because we forgot to independently call it on multiple places.
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@@ -447,7 +452,11 @@ class Transaction(OwnedObject):
|
|||||||
type_display = self.get_type_display()
|
type_display = self.get_type_display()
|
||||||
frmt_date = date(self.date, "SHORT_DATE_FORMAT")
|
frmt_date = date(self.date, "SHORT_DATE_FORMAT")
|
||||||
account = self.account
|
account = self.account
|
||||||
tags = ", ".join([x.name for x in self.tags.all()]) or _("No tags")
|
tags = (
|
||||||
|
", ".join([x.name for x in self.tags.all()])
|
||||||
|
if self.id
|
||||||
|
else None or _("No tags")
|
||||||
|
)
|
||||||
category = self.category or _("No category")
|
category = self.category or _("No category")
|
||||||
amount = localize_number(drop_trailing_zeros(self.amount))
|
amount = localize_number(drop_trailing_zeros(self.amount))
|
||||||
description = self.description or _("No description")
|
description = self.description or _("No description")
|
||||||
|
|||||||
@@ -177,7 +177,6 @@ def transaction_edit(request, transaction_id, **kwargs):
|
|||||||
transaction = get_object_or_404(Transaction, id=transaction_id)
|
transaction = get_object_or_404(Transaction, id=transaction_id)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
print(request.POST, request.POST.items())
|
|
||||||
form = TransactionForm(request.POST, instance=transaction)
|
form = TransactionForm(request.POST, instance=transaction)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
|
|||||||
@@ -61,19 +61,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tw:text-gray-400 tw:text-sm">
|
<div class="tw:text-gray-400 tw:text-sm">
|
||||||
{# Entities #}
|
{# Entities #}
|
||||||
{% with transaction.entities.all as entities %}
|
{% comment %} First, check for the highest priority: a valid 'overriden_entities' list. {% endcomment %}
|
||||||
{% if entities and not overriden_entities %}
|
{% if overriden_entities %}
|
||||||
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
||||||
<div class="col-auto pe-1"><i class="fa-solid fa-user-group fa-fw me-1 fa-xs"></i></div>
|
<div class="col-auto pe-1"><i class="fa-solid fa-user-group fa-fw me-1 fa-xs"></i></div>
|
||||||
<div class="col ps-0">{{ entities|join:", " }}</div>
|
<div class="col ps-0">{{ overriden_entities|join:", " }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% elif overriden_entities %}
|
|
||||||
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
{% comment %} If no override, fall back to transaction entities, but ONLY if the transaction has an ID. {% endcomment %}
|
||||||
<div class="col-auto pe-1"><i class="fa-solid fa-user-group fa-fw me-1 fa-xs"></i></div>
|
{% elif transaction.id and transaction.entities.all %}
|
||||||
<div class="col ps-0">{{ overriden_entities|join:", " }}</div>
|
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
||||||
</div>
|
<div class="col-auto pe-1"><i class="fa-solid fa-user-group fa-fw me-1 fa-xs"></i></div>
|
||||||
{% endif %}
|
<div class="col ps-0">{{ transaction.entities.all|join:", " }}</div>
|
||||||
{% endwith %}
|
</div>
|
||||||
|
{% endif %}
|
||||||
{# Notes#}
|
{# Notes#}
|
||||||
{% if transaction.notes %}
|
{% if transaction.notes %}
|
||||||
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
||||||
@@ -89,19 +90,20 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# Tags#}
|
{# Tags#}
|
||||||
{% with transaction.tags.all as tags %}
|
{% comment %} First, check for the highest priority: a valid 'overriden_tags' list. {% endcomment %}
|
||||||
{% if tags and not overriden_tags %}
|
{% if overriden_tags %}
|
||||||
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
||||||
<div class="col-auto pe-1"><i class="fa-solid fa-hashtag fa-fw me-1 fa-xs"></i></div>
|
<div class="col-auto pe-1"><i class="fa-solid fa-hashtag fa-fw me-1 fa-xs"></i></div>
|
||||||
<div class="col ps-0">{{ tags|join:", " }}</div>
|
<div class="col ps-0">{{ overriden_tags|join:", " }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% elif overriden_tags %}
|
|
||||||
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
{% comment %} If no override, fall back to transaction tags, but ONLY if the transaction has an ID. {% endcomment %}
|
||||||
<div class="col-auto pe-1"><i class="fa-solid fa-hashtag fa-fw me-1 fa-xs"></i></div>
|
{% elif transaction.id and transaction.tags.all %}
|
||||||
<div class="col ps-0">{{ overriden_tags|join:", " }}</div>
|
<div class="row mb-2 mb-lg-1 tw:text-gray-400">
|
||||||
</div>
|
<div class="col-auto pe-1"><i class="fa-solid fa-hashtag fa-fw me-1 fa-xs"></i></div>
|
||||||
{% endif %}
|
<div class="col ps-0">{{ transaction.tags.all|join:", " }}</div>
|
||||||
{% endwith %}
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="col-auto"></th>
|
<th scope="col" class="col-auto"></th>
|
||||||
<th scope="col" class="col-auto"></th>
|
<th scope="col" class="col-auto"></th>
|
||||||
|
<th scope="col" class="col-auto">{% translate 'Order' %}</th>
|
||||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -80,6 +81,9 @@
|
|||||||
<i class="fa-solid fa-toggle-off tw:text-red-400"></i>{% endif %}
|
<i class="fa-solid fa-toggle-off tw:text-red-400"></i>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="col text-center">
|
||||||
|
<div>{{ rule.order }}</div>
|
||||||
|
</td>
|
||||||
<td class="col">
|
<td class="col">
|
||||||
<div>{{ rule.name }}</div>
|
<div>{{ rule.name }}</div>
|
||||||
<div class="tw:text-gray-400">{{ rule.description }}</div>
|
<div class="tw:text-gray-400">{{ rule.description }}</div>
|
||||||
|
|||||||
@@ -22,6 +22,21 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
{% for result in results %}
|
{% for result in results %}
|
||||||
|
|
||||||
|
{% if result.type == 'header' %}
|
||||||
|
<div class="my-3">
|
||||||
|
<h6 class="text-center mb-3">
|
||||||
|
<span class="badge text-bg-secondary">
|
||||||
|
{% if result.header_type == "edit_transaction" %}
|
||||||
|
{% translate 'Edit transaction' %}
|
||||||
|
{% elif result.header_type == "update_or_create_transaction" %}
|
||||||
|
{% translate 'Update or create transaction' %}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if result.type == 'triggering_transaction' %}
|
{% if result.type == 'triggering_transaction' %}
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<h6 class="text-center mb-3"><span class="badge text-bg-secondary">{% translate 'Start' %}</span></h6>
|
<h6 class="text-center mb-3"><span class="badge text-bg-secondary">{% translate 'Start' %}</span></h6>
|
||||||
@@ -31,37 +46,43 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if result.type == 'edit_transaction' %}
|
{% if result.type == 'edit_transaction' %}
|
||||||
<div class="mt-4">
|
<div>
|
||||||
<h6 class="text-center mb-3"><span class="badge text-bg-secondary">{% translate 'Edit transaction' %}</span>
|
|
||||||
</h6>
|
|
||||||
<div>
|
<div>
|
||||||
{% translate 'Set' %} <span
|
{% translate 'Set' %} <span
|
||||||
class="badge text-bg-secondary">{{ result.field }}</span> {% translate 'to' %}
|
class="badge text-bg-secondary">{{ result.field }}</span> {% translate 'to' %}
|
||||||
<span class="badge text-bg-secondary">{{ result.new_value }}</span>
|
<span class="badge text-bg-secondary">{{ result.new_value }}</span>
|
||||||
</div>
|
</div>
|
||||||
<c-transaction.item :transaction="result.transaction" :dummy="True"
|
<c-transaction.item :transaction="result.transaction" :dummy="True"
|
||||||
:disable-selection="True" :overriden_tags="result.tags" :overriden_entities="result.entities"></c-transaction.item>
|
:disable-selection="True" :overriden_tags="result.tags"
|
||||||
|
:overriden_entities="result.entities"></c-transaction.item>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if result.type == 'update_or_create_transaction' %}
|
{% if result.type == 'update_or_create_transaction' %}
|
||||||
<div class="mt-4">
|
<div>
|
||||||
<h6 class="text-center mb-3"><span class="badge text-bg-secondary">{% translate 'Update or create transaction' %}</span>
|
|
||||||
</h6>
|
|
||||||
<div class="alert alert-info" role="alert">
|
<div class="alert alert-info" role="alert">
|
||||||
{% translate 'Search' %}: {{ result.query }}
|
{% translate 'Search' %}: {{ result.query }}
|
||||||
</div>
|
</div>
|
||||||
{% if result.start_transaction %}
|
{% if result.start_transaction %}
|
||||||
<c-transaction.item :transaction="result.start_transaction" :dummy="True"
|
<c-transaction.item :transaction="result.start_transaction" :dummy="True"
|
||||||
:disable-selection="True"></c-transaction.item>
|
:disable-selection="True"></c-transaction.item>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
{% translate 'No transaction found, a new one will be created' %}
|
{% translate 'No transaction found, a new one will be created' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="text-center h3"><i class="fa-solid fa-arrow-down"></i></div>
|
<div class="text-center h3 my-2"><i class="fa-solid fa-arrow-down"></i></div>
|
||||||
<c-transaction.item :transaction="result.start_transaction" :dummy="True"
|
<c-transaction.item :transaction="result.end_transaction" :dummy="True"
|
||||||
:disable-selection="True" :overriden_tags="result.tags" :overriden_entities="result.entities"></c-transaction.item>
|
:disable-selection="True" :overriden_tags="result.tags"
|
||||||
|
:overriden_entities="result.entities"></c-transaction.item>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if result.type == 'error' %}
|
||||||
|
<div>
|
||||||
|
<div class="alert alert-{% if result.level == 'error' %}danger{% elif result.level == 'warning' %}warning{% else %}info{% endif %}" role="alert">
|
||||||
|
{{ result.error }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user