mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-01-14 21:23:29 +01:00
444 lines
14 KiB
Python
444 lines
14 KiB
Python
from django.db import models
|
|
from django.db.models import Q
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from apps.common.models import SharedObject, SharedObjectManager
|
|
|
|
|
|
class TransactionRule(SharedObject):
|
|
active = models.BooleanField(default=True)
|
|
on_update = models.BooleanField(default=False)
|
|
on_create = models.BooleanField(default=True)
|
|
on_delete = models.BooleanField(default=False)
|
|
name = models.CharField(max_length=100, verbose_name=_("Name"))
|
|
description = models.TextField(blank=True, null=True, verbose_name=_("Description"))
|
|
trigger = models.TextField(verbose_name=_("Trigger"))
|
|
sequenced = models.BooleanField(
|
|
verbose_name=_("Sequenced"),
|
|
default=False,
|
|
)
|
|
order = models.PositiveIntegerField(default=0, verbose_name=_("Order"))
|
|
|
|
objects = SharedObjectManager()
|
|
all_objects = models.Manager() # Unfiltered manager
|
|
|
|
class Meta:
|
|
verbose_name = _("Transaction rule")
|
|
verbose_name_plural = _("Transaction rules")
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class TransactionRuleAction(models.Model):
|
|
class Field(models.TextChoices):
|
|
account = "account", _("Account")
|
|
type = "type", _("Type")
|
|
is_paid = "is_paid", _("Paid")
|
|
date = "date", _("Date")
|
|
reference_date = "reference_date", _("Reference Date")
|
|
mute = "mute", _("Mute")
|
|
amount = "amount", _("Amount")
|
|
description = "description", _("Description")
|
|
notes = "notes", _("Notes")
|
|
category = "category", _("Category")
|
|
tags = "tags", _("Tags")
|
|
entities = "entities", _("Entities")
|
|
internal_note = "internal_nome", _("Internal Note")
|
|
internal_id = "internal_id", _("Internal ID")
|
|
|
|
rule = models.ForeignKey(
|
|
TransactionRule,
|
|
on_delete=models.CASCADE,
|
|
related_name="transaction_actions",
|
|
verbose_name=_("Rule"),
|
|
)
|
|
field = models.CharField(
|
|
max_length=50,
|
|
choices=Field,
|
|
verbose_name=_("Field"),
|
|
)
|
|
value = models.TextField(verbose_name=_("Value"))
|
|
order = models.PositiveIntegerField(default=0, verbose_name=_("Order"))
|
|
|
|
def __str__(self):
|
|
return f"{self.rule} - {self.field} - {self.value}"
|
|
|
|
class Meta:
|
|
verbose_name = _("Edit transaction action")
|
|
verbose_name_plural = _("Edit transaction actions")
|
|
unique_together = (("rule", "field"),)
|
|
ordering = ["order"]
|
|
|
|
@property
|
|
def action_type(self):
|
|
return "edit_transaction"
|
|
|
|
|
|
class UpdateOrCreateTransactionRuleAction(models.Model):
|
|
"""
|
|
Will attempt to find and update latest matching transaction, or create new if none found.
|
|
"""
|
|
|
|
class SearchOperator(models.TextChoices):
|
|
EXACT = "exact", _("is exactly")
|
|
CONTAINS = "contains", _("contains")
|
|
STARTSWITH = "startswith", _("starts with")
|
|
ENDSWITH = "endswith", _("ends with")
|
|
EQ = "eq", _("equals")
|
|
GT = "gt", _("greater than")
|
|
LT = "lt", _("less than")
|
|
GTE = "gte", _("greater than or equal")
|
|
LTE = "lte", _("less than or equal")
|
|
|
|
rule = models.ForeignKey(
|
|
TransactionRule,
|
|
on_delete=models.CASCADE,
|
|
related_name="update_or_create_transaction_actions",
|
|
verbose_name=_("Rule"),
|
|
)
|
|
|
|
filter = models.TextField(
|
|
verbose_name=_("Filter"),
|
|
blank=True,
|
|
help_text=_(
|
|
"Generic expression to enable or disable execution. Should evaluate to True or False"
|
|
),
|
|
)
|
|
|
|
# Search fields with operators
|
|
search_account = models.TextField(
|
|
verbose_name="Search Account",
|
|
blank=True,
|
|
)
|
|
search_account_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Account Operator",
|
|
)
|
|
|
|
search_type = models.TextField(
|
|
verbose_name="Search Type",
|
|
blank=True,
|
|
)
|
|
search_type_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Type Operator",
|
|
)
|
|
|
|
search_is_paid = models.TextField(
|
|
verbose_name="Search Is Paid",
|
|
blank=True,
|
|
)
|
|
search_is_paid_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Is Paid Operator",
|
|
)
|
|
|
|
search_date = models.TextField(
|
|
verbose_name="Search Date",
|
|
blank=True,
|
|
help_text="Expression to match transaction date",
|
|
)
|
|
search_date_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Date Operator",
|
|
)
|
|
|
|
search_reference_date = models.TextField(
|
|
verbose_name="Search Reference Date",
|
|
blank=True,
|
|
)
|
|
search_reference_date_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Reference Date Operator",
|
|
)
|
|
|
|
search_amount = models.TextField(
|
|
verbose_name="Search Amount",
|
|
blank=True,
|
|
)
|
|
search_amount_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Amount Operator",
|
|
)
|
|
|
|
search_description = models.TextField(
|
|
verbose_name="Search Description",
|
|
blank=True,
|
|
)
|
|
search_description_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.CONTAINS,
|
|
verbose_name="Description Operator",
|
|
)
|
|
|
|
search_notes = models.TextField(
|
|
verbose_name="Search Notes",
|
|
blank=True,
|
|
)
|
|
search_notes_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.CONTAINS,
|
|
verbose_name="Notes Operator",
|
|
)
|
|
|
|
search_category = models.TextField(
|
|
verbose_name="Search Category",
|
|
blank=True,
|
|
)
|
|
search_category_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Category Operator",
|
|
)
|
|
|
|
search_tags = models.TextField(
|
|
verbose_name="Search Tags",
|
|
blank=True,
|
|
)
|
|
search_tags_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.CONTAINS,
|
|
verbose_name="Tags Operator",
|
|
)
|
|
|
|
search_entities = models.TextField(
|
|
verbose_name="Search Entities",
|
|
blank=True,
|
|
)
|
|
search_entities_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.CONTAINS,
|
|
verbose_name="Entities Operator",
|
|
)
|
|
|
|
search_internal_note = models.TextField(
|
|
verbose_name="Search Internal Note",
|
|
blank=True,
|
|
)
|
|
search_internal_note_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Internal Note Operator",
|
|
)
|
|
|
|
search_internal_id = models.TextField(
|
|
verbose_name="Search Internal ID",
|
|
blank=True,
|
|
)
|
|
search_internal_id_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Internal ID Operator",
|
|
)
|
|
|
|
search_mute = models.TextField(
|
|
verbose_name="Search Mute",
|
|
blank=True,
|
|
)
|
|
search_mute_operator = models.CharField(
|
|
max_length=10,
|
|
choices=SearchOperator.choices,
|
|
default=SearchOperator.EXACT,
|
|
verbose_name="Mute Operator",
|
|
)
|
|
|
|
# Set fields
|
|
set_account = models.TextField(
|
|
verbose_name=_("Account"),
|
|
blank=True,
|
|
)
|
|
set_type = models.TextField(
|
|
verbose_name=_("Type"),
|
|
blank=True,
|
|
)
|
|
set_is_paid = models.TextField(
|
|
verbose_name=_("Paid"),
|
|
blank=True,
|
|
)
|
|
set_date = models.TextField(
|
|
verbose_name=_("Date"),
|
|
blank=True,
|
|
)
|
|
set_reference_date = models.TextField(
|
|
verbose_name=_("Reference Date"),
|
|
blank=True,
|
|
)
|
|
set_amount = models.TextField(
|
|
verbose_name=_("Amount"),
|
|
blank=True,
|
|
)
|
|
set_description = models.TextField(
|
|
verbose_name=_("Description"),
|
|
blank=True,
|
|
)
|
|
set_notes = models.TextField(
|
|
verbose_name=_("Notes"),
|
|
blank=True,
|
|
)
|
|
set_internal_note = models.TextField(
|
|
verbose_name=_("Internal Note"),
|
|
blank=True,
|
|
)
|
|
set_internal_id = models.TextField(
|
|
verbose_name=_("Internal ID"),
|
|
blank=True,
|
|
)
|
|
set_entities = models.TextField(
|
|
verbose_name=_("Entities"),
|
|
blank=True,
|
|
)
|
|
set_category = models.TextField(
|
|
verbose_name=_("Category"),
|
|
blank=True,
|
|
)
|
|
set_tags = models.TextField(
|
|
verbose_name=_("Tags"),
|
|
blank=True,
|
|
)
|
|
set_mute = models.TextField(
|
|
verbose_name=_("Mute"),
|
|
blank=True,
|
|
)
|
|
|
|
order = models.PositiveIntegerField(default=0, verbose_name=_("Order"))
|
|
|
|
class Meta:
|
|
verbose_name = _("Update or create transaction action")
|
|
verbose_name_plural = _("Update or create transaction actions")
|
|
ordering = ["order"]
|
|
|
|
@property
|
|
def action_type(self):
|
|
return "update_or_create_transaction"
|
|
|
|
def __str__(self):
|
|
return f"Update or create transaction action for {self.rule}"
|
|
|
|
def build_search_query(self, simple):
|
|
"""Builds Q objects based on search fields and their operators"""
|
|
search_query = Q()
|
|
|
|
def add_to_query(field_name, value, operator):
|
|
lookup = f"{field_name}__{operator}"
|
|
return Q(**{lookup: value})
|
|
|
|
if self.search_account:
|
|
value = simple.eval(self.search_account)
|
|
if isinstance(value, int):
|
|
search_query &= add_to_query(
|
|
"account_id", value, self.search_account_operator
|
|
)
|
|
else:
|
|
search_query &= add_to_query(
|
|
"account__name", value, self.search_account_operator
|
|
)
|
|
|
|
if self.search_type:
|
|
value = simple.eval(self.search_type)
|
|
search_query &= add_to_query("type", value, self.search_type_operator)
|
|
|
|
if self.search_is_paid:
|
|
value = simple.eval(self.search_is_paid)
|
|
search_query &= add_to_query("is_paid", value, self.search_is_paid_operator)
|
|
|
|
if self.search_mute:
|
|
value = simple.eval(self.search_mute)
|
|
search_query &= add_to_query("mute", value, self.search_mute_operator)
|
|
|
|
if self.search_date:
|
|
value = simple.eval(self.search_date)
|
|
search_query &= add_to_query("date", value, self.search_date_operator)
|
|
|
|
if self.search_reference_date:
|
|
value = simple.eval(self.search_reference_date)
|
|
search_query &= add_to_query(
|
|
"reference_date", value, self.search_reference_date_operator
|
|
)
|
|
|
|
if self.search_amount:
|
|
value = simple.eval(self.search_amount)
|
|
search_query &= add_to_query("amount", value, self.search_amount_operator)
|
|
|
|
if self.search_description:
|
|
value = simple.eval(self.search_description)
|
|
search_query &= add_to_query(
|
|
"description", value, self.search_description_operator
|
|
)
|
|
|
|
if self.search_notes:
|
|
value = simple.eval(self.search_notes)
|
|
search_query &= add_to_query("notes", value, self.search_notes_operator)
|
|
|
|
if self.search_internal_note:
|
|
value = simple.eval(self.search_internal_note)
|
|
search_query &= add_to_query(
|
|
"internal_note", value, self.search_internal_note_operator
|
|
)
|
|
|
|
if self.search_internal_id:
|
|
value = simple.eval(self.search_internal_id)
|
|
search_query &= add_to_query(
|
|
"internal_id", value, self.search_internal_id_operator
|
|
)
|
|
|
|
if self.search_category:
|
|
value = simple.eval(self.search_category)
|
|
if isinstance(value, int):
|
|
search_query &= add_to_query(
|
|
"category_id", value, self.search_category_operator
|
|
)
|
|
else:
|
|
search_query &= add_to_query(
|
|
"category__name", value, self.search_category_operator
|
|
)
|
|
|
|
if self.search_tags:
|
|
tags_value = simple.eval(self.search_tags)
|
|
if isinstance(tags_value, (list, tuple)):
|
|
for tag in tags_value:
|
|
if isinstance(tag, int):
|
|
search_query &= Q(tags__id=tag)
|
|
else:
|
|
search_query &= Q(tags__name__iexact=tag)
|
|
elif isinstance(tags_value, (int, str)):
|
|
if isinstance(tags_value, int):
|
|
search_query &= Q(tags__id=tags_value)
|
|
else:
|
|
search_query &= Q(tags__name__iexact=tags_value)
|
|
|
|
if self.search_entities:
|
|
entities_value = simple.eval(self.search_entities)
|
|
if isinstance(entities_value, (list, tuple)):
|
|
for entity in entities_value:
|
|
if isinstance(entity, int):
|
|
search_query &= Q(entities__id=entity)
|
|
else:
|
|
search_query &= Q(entities__name__iexact=entity)
|
|
elif isinstance(entities_value, (int, str)):
|
|
if isinstance(entities_value, int):
|
|
search_query &= Q(entities__id=entities_value)
|
|
else:
|
|
search_query &= Q(entities__name__iexact=entities_value)
|
|
|
|
return search_query
|