dodil/erp-brick-ar
DODIL ERP brick: accounts receivable
src/brick_ar/domain/dates.py
"""Dates: terms, periods, and the rule that no dated row takes effect today or earlier."""
import calendar
from datetime import date, timedelta
from brick_ar.domain.errors import RuleBroken
def end_of_month(d: date) -> date:
return d.replace(day=calendar.monthrange(d.year, d.month)[1])
def add_months(d: date, n: int) -> date:
"""d + n months, the day clamped to the month's end (31 Jan + 1 month = 28/29 Feb)."""
m = d.month - 1 + n
y, m = d.year + m // 12, m % 12 + 1
return date(y, m, min(d.day, calendar.monthrange(y, m)[1]))
def due_date(issue: date, days: int, basis: str) -> date:
base = end_of_month(issue) if basis == "end_of_month" else issue
return base + timedelta(days=int(days))
def first_of_next_month(d: date) -> date:
return end_of_month(d) + timedelta(days=1)
def check_dated(what: str, valid_from: date, today: date, first: bool) -> None:
"""A dated row (a billing profile, a control, an account-map row) starts AFTER today; the first row of its key may
start today. Nothing starts earlier: an issued document never changes and a past day is never rewritten."""
if valid_from > today or (first and valid_from == today):
return
if first:
raise RuleBroken(f"{what} cannot start on {valid_from}, which is before today ({today}): the first one may start today")
raise RuleBroken(f"{what} starts tomorrow at the earliest: {valid_from} is today ({today}) or earlier, and a day "
"that has passed is never rewritten — date it from tomorrow")