mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-11-13 12:23:44 +01:00
fix(transactions): keep a recurrence on its day after a short month
A monthly recurrence on the 31st clamped to Feb 28 and stayed on the 28th for every later month.
This commit is contained in:
1 parent
bdc0603327
commit
6a289327b6
2 files changed
+40
-1
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import calendar
|
||||
import decimal
|
||||
import logging
|
||||
import uuid
|
||||
@@ -1183,7 +1184,21 @@ class RecurringTransaction(TransferTemplate):
|
||||
return relativedelta(years=self.recurrence_interval)
|
||||
|
||||
def get_next_date(self, current_date):
|
||||
return current_date + self.get_recurrence_delta()
|
||||
next_date = current_date + self.get_recurrence_delta()
|
||||
|
||||
# A start on the 29th-31st is clamped in shorter months; return to that day
|
||||
# afterwards instead of keeping the clamped one.
|
||||
if (
|
||||
self.recurrence_type
|
||||
in (self.RecurrenceType.MONTH, self.RecurrenceType.YEAR)
|
||||
and self.start_date.day > current_date.day
|
||||
and current_date.day
|
||||
== calendar.monthrange(current_date.year, current_date.month)[1]
|
||||
):
|
||||
last_day = calendar.monthrange(next_date.year, next_date.month)[1]
|
||||
next_date = next_date.replace(day=min(self.start_date.day, last_day))
|
||||
|
||||
return next_date
|
||||
|
||||
@classmethod
|
||||
def generate_upcoming_transactions(cls):
|
||||
|
||||
@@ -242,6 +242,30 @@ class RecurringTransactionTests(TestCase):
|
||||
self.assertEqual(recurring.recurrence_interval, 1)
|
||||
self.assertEqual(recurring.account.currency.code, "USD")
|
||||
|
||||
def test_month_end_recurrence_returns_to_its_day(self):
|
||||
"""A recurrence on the 31st goes back to it after a short month"""
|
||||
recurring = RecurringTransaction.objects.create(
|
||||
account=self.account,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
amount=Decimal("100.00"),
|
||||
description="Rent",
|
||||
start_date=datetime.date(2026, 1, 31),
|
||||
recurrence_type=RecurringTransaction.RecurrenceType.MONTH,
|
||||
recurrence_interval=1,
|
||||
)
|
||||
|
||||
feb = recurring.get_next_date(datetime.date(2026, 1, 31))
|
||||
march = recurring.get_next_date(feb)
|
||||
april = recurring.get_next_date(march)
|
||||
|
||||
self.assertEqual(feb, datetime.date(2026, 2, 28))
|
||||
self.assertEqual(march, datetime.date(2026, 3, 31))
|
||||
self.assertEqual(april, datetime.date(2026, 4, 30))
|
||||
self.assertEqual(
|
||||
recurring.get_next_date(datetime.date(2026, 2, 1)),
|
||||
datetime.date(2026, 3, 1),
|
||||
)
|
||||
|
||||
def test_generate_upcoming_transactions_keeps_tags_and_entities(self):
|
||||
"""Generation must copy tags/entities even with no current user"""
|
||||
tag = TransactionTag.objects.create(name="Essential")
|
||||
|
||||
Reference in new issue
Block a user