from __future__ import annotations
from datetime import date
from onegov.core.orm import Base
from onegov.core.orm.mixins import content_property
from onegov.core.orm.mixins import dict_property
from onegov.core.orm.mixins import ContentMixin
from onegov.core.orm.mixins import TimestampMixin
from onegov.pas.i18n import _
from onegov.search import ORMSearchable
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import Mapped
from uuid import uuid4
from uuid import UUID
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from sqlalchemy.sql import ColumnElement
[docs]
class SettlementRun(Base, ContentMixin, TimestampMixin, ORMSearchable):
""" Abrechnungslauf """
[docs]
__tablename__ = 'pas_settlements'
[docs]
fts_type_title = _('Settlement runs')
[docs]
fts_title_property = 'name'
[docs]
fts_properties = {'name': {'type': 'text', 'weight': 'A'}}
@property
[docs]
def title(self) -> str:
return self.name
#: Internal ID
[docs]
id: Mapped[UUID] = mapped_column(
primary_key=True,
default=uuid4
)
#: the name
#: The start date
#: The end date
#: Whether this settlement run is closed
[docs]
closed: Mapped[bool] = mapped_column(default=False)
#: The description
[docs]
description: dict_property[str | None] = content_property()
@hybrid_property
[docs]
def active(self) -> bool:
return not self.closed
@active.inplace.setter
def _active_setter(self, value: bool) -> None:
self.closed = not value
@active.inplace.expression
@classmethod
[docs]
def _active_expression(cls) -> ColumnElement[bool]:
return ~cls.closed
@classmethod
[docs]
def get_run_number_for_year(cls, input_date: date) -> int:
"""
Computes the run number for a given date within a year.
As per customer requirement: No rule mandates 4 payments yearly,
but wages must be reported by the 10th.
Run breakdown:
- Q1: January to March
- Q2: April to June
- Q3: July to September
- Q4: October to November
- Q5: December
Why 5 runs?
The decision to split the fourth quarter ensures that settlement
runs are confined to a single calendar year. This avoids situations
where a settlement overlaps into two different years, which could
cause issues due to differing cost-of-living adjustments (COLA)
applicable to each year. Handling settlements across two fiscal years
would increase complexity.
Thus, we have 5 yearly runs.
"""
month = input_date.month
if 1 <= month <= 3: # January to March
return 1
elif 4 <= month <= 6: # April to June
return 2
elif 7 <= month <= 9: # July to September
return 3
elif 10 <= month <= 11: # October to November
return 4
elif month == 12: # December
return 5
else:
raise ValueError(
f'Invalid month: {month}. Date must be within a valid year.'
)
[docs]
def __repr__(self) -> str:
return f'<SettlementRun {self.name} {self.start} {self.end} >'