Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mvj 531 seasonal rent dates validation #752

Merged
merged 5 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions leasing/models/rent.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,9 +660,8 @@ def get_custom_due_dates_as_daymonths(self):
if self.due_dates_type != DueDatesType.CUSTOM:
return set()

return [
dd.as_daymonth() for dd in self.due_dates.all().order_by("month", "day")
]
due_dates: QuerySet[RentDueDate] = self.due_dates
return [dd.as_daymonth() for dd in due_dates.all().order_by("month", "day")]

def get_due_dates_as_daymonths(self):
due_dates = []
Expand Down
9 changes: 9 additions & 0 deletions leasing/serializers/rent.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ManagementSubventionFormOfManagement,
TemporarySubvention,
)
from leasing.serializers.utils import validate_seasonal_day_for_month
from users.serializers import UserSerializer

from ..models import (
Expand Down Expand Up @@ -53,6 +54,10 @@ class Meta:
model = RentDueDate
fields = ("id", "day", "month")

def validate(self, data):
validate_seasonal_day_for_month(data.get("day"), data.get("month"))
return data


class FixedInitialYearRentSerializer(
FieldPermissionsSerializerMixin, serializers.ModelSerializer
Expand Down Expand Up @@ -542,6 +547,10 @@ def validate(self, data):
_("Due dates type must be custom if seasonal dates are set")
)

start_day, start_month, end_day, end_month = seasonal_values
validate_seasonal_day_for_month(start_day, start_month)
validate_seasonal_day_for_month(end_day, end_month)

return data


Expand Down
43 changes: 43 additions & 0 deletions leasing/serializers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import OneToOneRel
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

Expand Down Expand Up @@ -305,3 +306,45 @@ def get_file_url(self, obj):

def get_file_filename(self, obj):
return os.path.basename(obj.file.name)


def validate_seasonal_day_for_month(day: int | None, month: int | None):
if day is None and month is None:
return

if day is None and month is not None:
raise ValidationError({"day": _("Both day and month must be provided")})

if day is not None and month is None:
raise ValidationError({"month": _("Both day and month must be provided")})

if not isinstance(day, int):
raise ValidationError({"day": _("Day must be an integer")})

if not isinstance(month, int):
raise ValidationError({"month": _("Month must be an integer")})

max_days_in_month = {
1: 31, # January
# Since this a generic date and not a calendar date with year, accept only 28 days for February
2: 28, # February (non-leap year)
3: 31, # March
4: 30, # April
5: 31, # May
6: 30, # June
7: 31, # July
8: 31, # August
9: 30, # September
10: 31, # October
11: 30, # November
12: 31, # December
}

if month < 1 or month > 12:
raise ValidationError(
{"month": _(f"Invalid month: {month}. Month must be between 1 and 12.")}
)

max_day = max_days_in_month.get(month)
if day < 1 or day > max_day:
raise ValidationError({"day": _(f"Invalid day: {day} for month: {month}")})
66 changes: 66 additions & 0 deletions leasing/tests/serializers/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest
from rest_framework.exceptions import ValidationError

from leasing.serializers.utils import validate_seasonal_day_for_month


def test_validate_seasonal_day_for_month():
# Test valid cases
try:
validate_seasonal_day_for_month(1, 1)
validate_seasonal_day_for_month(28, 2)
validate_seasonal_day_for_month(30, 4)
validate_seasonal_day_for_month(31, 12)
validate_seasonal_day_for_month(None, None) # No values to validate
except ValidationError:
pytest.fail("validate_seasonal_day_for_month() raised ValidationError!")

# Test month not within bounds
day, month = 1, 0
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert (
exc.value.detail.get("month")
== f"Invalid month: {month}. Month must be between 1 and 12."
)
day, month = 1, 13
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert (
exc.value.detail.get("month")
== f"Invalid month: {month}. Month must be between 1 and 12."
)

# Test day not within bounds
day, month = 0, 1
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("day") == f"Invalid day: {day} for month: {month}"

# Does not take leap years into account, as it is not a calendar day (with year)
day, month = 29, 2
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("day") == f"Invalid day: {day} for month: {month}"

# Test day and month provided
day, month = 1, None
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("month") == "Both day and month must be provided"

day, month = None, 1
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("day") == "Both day and month must be provided"

# Test invalid types
day, month = "2", 1
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("day") == "Day must be an integer"

day, month = 2, "1"
with pytest.raises(ValidationError) as exc:
validate_seasonal_day_for_month(day, month)
assert exc.value.detail.get("month") == "Month must be an integer"
19 changes: 18 additions & 1 deletion locale/fi/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: MVJ 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-12 10:50+0300\n"
"POT-Creation-Date: 2024-10-18 13:53+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: \n"
"Language: fi\n"
Expand Down Expand Up @@ -3466,6 +3466,23 @@ msgstr "Vuokrausperiaatetta tunnisteella {} ei löydy"
msgid "Can't edit locked basis of rent item"
msgstr "Lukittua vuokrausperiaatetta ei voi muokata"

msgid "Both day and month must be provided"
msgstr "Vaaditaan molemmat: päivä ja kuukausi"

msgid "Day must be an integer"
msgstr "Päivän pitää olla kokonaisluku"

msgid "Month must be an integer"
msgstr "Kuukauden pitää olla kokonaisluku"

#, python-brace-format
msgid "Invalid month: {month}. Month must be between 1 and 12."
msgstr "Virheellinen kuukausi: {month}. Kuukauden pitää olla 1-12."

#, python-brace-format
msgid "Invalid day: {day} for month: {month}"
msgstr "Vireellinen päivä: {day} kuukaudelle: {month}"

msgid "Enter a valid business id."
msgstr "Anna kelvollinen Y-tunnus."

Expand Down
19 changes: 18 additions & 1 deletion locale/sv/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-12 10:50+0300\n"
"POT-Creation-Date: 2024-10-18 13:53+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -3468,6 +3468,23 @@ msgstr "Arrenderingsprincipen med identifieraren {} hittas inte"
msgid "Can't edit locked basis of rent item"
msgstr "Låsta arrenderingsprinciper kan inte redigeras"

msgid "Both day and month must be provided"
msgstr ""

msgid "Day must be an integer"
msgstr ""

msgid "Month must be an integer"
msgstr ""

#, python-brace-format
msgid "Invalid month: {month}. Month must be between 1 and 12."
msgstr ""

#, python-brace-format
msgid "Invalid day: {day} for month: {month}"
msgstr ""

msgid "Enter a valid business id."
msgstr "Ange ett giltigt FO-nummer."

Expand Down