feat(internal): trigger rules on bulk actions

This commit is contained in:
Herculino Trotta
2025-02-14 00:34:51 -03:00
parent 7eceacfe68
commit f3bcef534e
3 changed files with 59 additions and 14 deletions

View File

@@ -1,11 +1,12 @@
from django.dispatch import Signal, receiver from django.dispatch import receiver
from apps.transactions.models import Transaction from apps.transactions.models import (
Transaction,
transaction_created,
transaction_updated,
)
from apps.rules.tasks import check_for_transaction_rules from apps.rules.tasks import check_for_transaction_rules
transaction_created = Signal()
transaction_updated = Signal()
@receiver(transaction_created) @receiver(transaction_created)
@receiver(transaction_updated) @receiver(transaction_updated)

View File

@@ -1,22 +1,66 @@
import logging import logging
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Q from django.db.models import Q
from django.dispatch import Signal
from django.template.defaultfilters import date
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings
from apps.common.fields.month_year import MonthYearModelField from apps.common.fields.month_year import MonthYearModelField
from apps.common.functions.decimals import truncate_decimal from apps.common.functions.decimals import truncate_decimal
from apps.common.templatetags.decimal import localize_number, drop_trailing_zeros
from apps.currencies.utils.convert import convert from apps.currencies.utils.convert import convert
from apps.transactions.validators import validate_decimal_places, validate_non_negative from apps.transactions.validators import validate_decimal_places, validate_non_negative
logger = logging.getLogger() logger = logging.getLogger()
transaction_created = Signal()
transaction_updated = Signal()
class SoftDeleteQuerySet(models.QuerySet): class SoftDeleteQuerySet(models.QuerySet):
@staticmethod
def _emit_signals(instances, created=False):
"""Helper to emit signals for multiple instances"""
for instance in instances:
if created:
transaction_created.send(sender=instance)
else:
transaction_updated.send(sender=instance)
def bulk_create(self, objs, emit_signal=True, **kwargs):
instances = super().bulk_create(objs, **kwargs)
if emit_signal:
self._emit_signals(instances, created=True)
return instances
def bulk_update(self, objs, fields, emit_signal=True, **kwargs):
result = super().bulk_update(objs, fields, **kwargs)
if emit_signal:
self._emit_signals(objs, created=False)
return result
def update(self, emit_signal=True, **kwargs):
# Get instances before update
instances = list(self)
result = super().update(**kwargs)
if emit_signal:
# Refresh instances to get new values
refreshed = self.model.objects.filter(pk__in=[obj.pk for obj in instances])
self._emit_signals(refreshed, created=False)
return result
def delete(self): def delete(self):
if not settings.ENABLE_SOFT_DELETE: if not settings.ENABLE_SOFT_DELETE:
# If soft deletion is disabled, perform a normal delete # If soft deletion is disabled, perform a normal delete
@@ -274,7 +318,13 @@ class Transaction(models.Model):
def __str__(self): def __str__(self):
type_display = self.get_type_display() type_display = self.get_type_display()
return f"{self.description} - {type_display} - {self.account} - {self.date}" frmt_date = date(self.date, "SHORT_DATE_FORMAT")
account = self.account
tags = ", ".join([x.name for x in self.tags.all()]) or _("No Tags")
category = self.category or _("No Category")
amount = localize_number(drop_trailing_zeros(self.amount))
description = self.description or _("No Description")
return f"[{frmt_date}][{type_display}][{account}] {description}{category}{tags}{amount}"
class InstallmentPlan(models.Model): class InstallmentPlan(models.Model):

View File

@@ -18,9 +18,6 @@ def bulk_pay_transactions(request):
count = transactions.count() count = transactions.count()
transactions.update(is_paid=True) transactions.update(is_paid=True)
for transaction in transactions:
transaction_updated.send(sender=transaction)
messages.success( messages.success(
request, request,
ngettext_lazy( ngettext_lazy(
@@ -45,9 +42,6 @@ def bulk_unpay_transactions(request):
count = transactions.count() count = transactions.count()
transactions.update(is_paid=False) transactions.update(is_paid=False)
for transaction in transactions:
transaction_updated.send(sender=transaction)
messages.success( messages.success(
request, request,
ngettext_lazy( ngettext_lazy(
@@ -94,7 +88,7 @@ def bulk_undelete_transactions(request):
selected_transactions = request.GET.getlist("transactions", []) selected_transactions = request.GET.getlist("transactions", [])
transactions = Transaction.deleted_objects.filter(id__in=selected_transactions) transactions = Transaction.deleted_objects.filter(id__in=selected_transactions)
count = transactions.count() count = transactions.count()
transactions.update(deleted=False, deleted_at=None) transactions.update(deleted=False, deleted_at=None, emit_signal=False)
messages.success( messages.success(
request, request,