From 4f6903e8e4865cc8fd75f0286bae64129ba1ebf4 Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Wed, 6 Aug 2025 13:13:59 -0300 Subject: [PATCH] feat(transactions:recurring): Allow to set how many future instances of a recurring transaction to create in advance --- app/apps/transactions/forms.py | 4 +++- .../0048_recurringtransaction_keep_at_most.py | 19 +++++++++++++++++ app/apps/transactions/models.py | 21 +++++++++++++++---- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 app/apps/transactions/migrations/0048_recurringtransaction_keep_at_most.py diff --git a/app/apps/transactions/forms.py b/app/apps/transactions/forms.py index 30c289f..e7b2116 100644 --- a/app/apps/transactions/forms.py +++ b/app/apps/transactions/forms.py @@ -1,5 +1,5 @@ from crispy_bootstrap5.bootstrap5 import Switch, BS5Accordion -from crispy_forms.bootstrap import FormActions, AccordionGroup +from crispy_forms.bootstrap import FormActions, AccordionGroup, AppendedText from crispy_forms.helper import FormHelper from crispy_forms.layout import ( Layout, @@ -963,6 +963,7 @@ class RecurringTransactionForm(forms.ModelForm): "notes", "add_notes_to_transaction", "entities", + "keep_at_most", ] widgets = { "reference_date": AirMonthYearPickerInput(), @@ -1042,6 +1043,7 @@ class RecurringTransactionForm(forms.ModelForm): Column("end_date", css_class="form-group col-md-4 mb-0"), css_class="form-row", ), + AppendedText("keep_at_most", _("future transactions")), ) self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput() diff --git a/app/apps/transactions/migrations/0048_recurringtransaction_keep_at_most.py b/app/apps/transactions/migrations/0048_recurringtransaction_keep_at_most.py new file mode 100644 index 0000000..ce60a4e --- /dev/null +++ b/app/apps/transactions/migrations/0048_recurringtransaction_keep_at_most.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.4 on 2025-08-06 14:51 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('transactions', '0047_alter_transactioncategory_owner_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='recurringtransaction', + name='keep_at_most', + field=models.PositiveIntegerField(default=6, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Keep at most'), + ), + ] diff --git a/app/apps/transactions/models.py b/app/apps/transactions/models.py index 927ca72..0e31531 100644 --- a/app/apps/transactions/models.py +++ b/app/apps/transactions/models.py @@ -722,6 +722,9 @@ class RecurringTransaction(models.Model): recurrence_interval = models.PositiveIntegerField( verbose_name=_("Recurrence Interval"), ) + keep_at_most = models.PositiveIntegerField( + verbose_name=_("Keep at most"), default=6, validators=[MinValueValidator(1)] + ) last_generated_date = models.DateField( verbose_name=_("Last Generated Date"), null=True, blank=True @@ -759,8 +762,10 @@ class RecurringTransaction(models.Model): current_date = self.start_date reference_date = self.reference_date end_date = min( - self.end_date or timezone.now().date() + (self.get_recurrence_delta() * 5), - timezone.now().date() + (self.get_recurrence_delta() * 5), + self.end_date + or timezone.now().date() + + (self.get_recurrence_delta() * self.keep_at_most), + timezone.now().date() + (self.get_recurrence_delta() * self.keep_at_most), ) while current_date <= end_date: @@ -837,8 +842,16 @@ class RecurringTransaction(models.Model): current_date = start_date end_date = min( recurring_transaction.end_date - or today + (recurring_transaction.get_recurrence_delta() * 6), - today + (recurring_transaction.get_recurrence_delta() * 6), + or today + + ( + recurring_transaction.get_recurrence_delta() + * recurring_transaction.keep_at_most + ), + today + + ( + recurring_transaction.get_recurrence_delta() + * recurring_transaction.keep_at_most + ), ) logger.info(f"End date: {end_date}")