test(invoices): document month-end anchor and cover more recurring frequencies

This commit is contained in:
moneebullah25 2026-10-11 00:18:32 -04:00
parent f60a23c137
commit 1497894a38
2 changed files with 24 additions and 1 deletions

View file

@ -1264,7 +1264,11 @@ class RecurringInvoice(AssignableMixin, BaseModel):
return self._add_months(current, 1)
def _add_months(self, current, months):
"""Add months, restoring the start_date day after a month-end clamp."""
"""Add months, restoring the start_date day after a month-end clamp.
start_date.day is the billing anchor: a next_generation_date on the
last day of a short month is treated as clamped and moves back to it.
"""
import calendar
from dateutil.relativedelta import relativedelta

View file

@ -4830,6 +4830,25 @@ class TestRecurringInvoiceModel:
recurring_invoice.next_generation_date = datetime.date(2026, 2, 15)
assert recurring_invoice.calculate_next_date() == datetime.date(2026, 3, 15)
def test_calculate_next_date_month_end_anchors(self, recurring_invoice):
d = datetime.date
cases = [
("MONTHLY", d(2026, 1, 30), d(2026, 2, 28), d(2026, 3, 30)),
("MONTHLY", d(2026, 1, 31), d(2026, 12, 31), d(2027, 1, 31)),
("QUARTERLY", d(2026, 1, 31), d(2026, 4, 30), d(2026, 7, 31)),
("SEMI_ANNUALLY", d(2026, 8, 31), d(2026, 2, 28), d(2026, 8, 31)),
("MONTHLY", d(2026, 1, 31), d(2026, 2, 28), d(2026, 3, 31)),
]
for frequency, start, current, expected in cases:
recurring_invoice.frequency = frequency
recurring_invoice.start_date = start
recurring_invoice.next_generation_date = current
assert recurring_invoice.calculate_next_date() == expected, (
frequency,
start,
current,
)
def test_calculate_next_date_custom(self, recurring_invoice):
recurring_invoice.frequency = "CUSTOM"
recurring_invoice.custom_days = 10