Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2cc6b78
Move Name and Description fields higher in resource form
japauliina Dec 18, 2024
999675e
Add missing migration
japauliina Dec 18, 2024
f4b2863
Remove trailing whitespace
japauliina Dec 18, 2024
4c2d02e
Send reservation reminders
japauliina Dec 19, 2024
c4d2634
Merge pull request #188 from andersinno/TTVA-220-resource-form-order-…
japauliina Jan 7, 2025
14bc077
Merge pull request #189 from andersinno/TTVA-219-reminder-emails
japauliina Jan 7, 2025
5138f8c
Hide inactive purposes from respa admin resource form
japauliina Jan 8, 2025
674af76
Hide inactive terms of use from respa admin resource form
japauliina Jan 8, 2025
ff55bf4
Hide inactive resource types from respa admin resource form
japauliina Jan 8, 2025
4999a5f
Hide inactive reservation metadata sets from respa admin resource form
japauliina Jan 8, 2025
c148703
Hide inactive equipments from respa admin resource form
japauliina Jan 8, 2025
6652746
Merge pull request #190 from andersinno/TTVA-218-hide-resource-fields
japauliina Jan 14, 2025
7012940
Rename people_capacity to people_capacity_lower
japauliina Jan 9, 2025
48e83dc
Add people_capacity_upper field to Resource model
japauliina Jan 9, 2025
951091a
Take both people capacity fields into account in filtering
japauliina Jan 9, 2025
687f3be
Merge pull request #191 from andersinno/TTVA-217-people-capacity-range
japauliina Jan 14, 2025
c7b0a52
Fix tax class in order creation
japauliina Jan 17, 2025
c8d8179
Merge pull request #192 from andersinno/TTVA-224-fix-tax-per
japauliina Jan 20, 2025
5ac1557
Hide people_capacity_upper from the Respa admin
japauliina Feb 18, 2025
7732bf1
Merge pull request #193 from andersinno/TTVA-217-hide-upper-capacity
japauliina Feb 18, 2025
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
2 changes: 1 addition & 1 deletion .sanitizerconfig
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ strategy:
name_fi: null
name_sv: null
need_manual_confirmation: null
people_capacity: null
people_capacity_lower: null
public: null
reservable: null
reservable_days_in_advance: null
Expand Down
21 changes: 21 additions & 0 deletions locale/fi/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,9 @@ msgstr "Tunnistautuminen"
msgid "People capacity"
msgstr "Henkilömäärä"

msgid "People capacity upper limit"
msgstr "Maksimihenkilömäärä"

msgid "Area (m2)"
msgstr "Pinta-ala (m2)"

Expand Down Expand Up @@ -2202,3 +2205,21 @@ msgstr "Takaisin"
#, python-brace-format
msgid "Missing information required by SAP: {}"
msgstr "SAP:n vaatimia tietoja puuttuu: {}"

msgid "Reservation reminder"
msgstr "Muistutus varauksesta"

msgid "Inactive purposes are not shown in the Respa admin."
msgstr "Jos käyttötarkoitus ei ole aktiivinen, sitä ei näytetä Respan adminissa."

msgid "Inactive terms are not shown in the Respa admin."
msgstr "Jos ehto ei ole aktiivinen, sitä ei näytetä Respan adminissa."

msgid "Inactive resource types are not shown in the Respa admin."
msgstr "Jos resurssityyppi ei ole aktiivinen, sitä ei näytetä Respan adminissa."

msgid "Inactive metadata sets are not shown in the resource form in Respa admin."
msgstr "Jos metatietojoukko ei ole aktiivinen, sitä ei näytetä resurssin lomakkeessa Respan adminissa."

msgid "Inactive equipment is not shown in the Respa admin."
msgstr "Jos varuste ei ole aktiivinen, sitä ei näytetä Respan adminissa."
21 changes: 21 additions & 0 deletions locale/sv/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,9 @@ msgstr "Autentisering"
msgid "People capacity"
msgstr "Kapacitet"

msgid "People capacity upper limit"
msgstr "Maximal kapacitet"

msgid "Area (m2)"
msgstr "Yta (m2)"

Expand Down Expand Up @@ -1789,3 +1792,21 @@ msgid ""
msgstr ""
"anger om användaren är en General Administrator med särskilda behörigheter "
"för många objekt inom Respa"

msgid "Reservation reminder"
msgstr "Bokningspåminnelse"

msgid "Inactive purposes are not shown in the Respa admin."
msgstr "Inaktiva syften visas inte i Respa administratören."

msgid "Inactive terms are not shown in the Respa admin."
msgstr "Inaktiva villkor visas inte i Respa administratören."

msgid "Inactive resource types are not shown in the Respa admin."
msgstr "Inaktiva resurstyper visas inte i Respa administratören."

msgid "Inactive metadata sets are not shown in the resource form in Respa admin."
msgstr "Inaktiva metadatauppsättningar visas inte i resursformuläret i Respa administratören."

msgid "Inactive equipment is not shown in the Respa admin."
msgstr "Inaktiva utrustningar visas inte i Respa administratören."
Empty file.
Empty file.
35 changes: 35 additions & 0 deletions notifications/management/commands/send_reservation_reminders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging
from datetime import timedelta

from django.core.management import BaseCommand
from django.utils import timezone

from resources.models import Reservation

logger = logging.getLogger(__name__)


def send_reservation_reminder(reservation):
reservation.send_reservation_mail(notification_type="reservation_reminder")
reservation.reminder_sent = True
reservation.save()


class Command(BaseCommand):
help = "Send reservation reminders"

def handle(self, *args, **options):
"""
Send reservation reminders for reservations that start in the next 24 hours.
"""

reservations = Reservation.objects.filter(
begin__gte=timezone.now(),
begin__lte=timezone.now() + timedelta(days=1),
reminder_sent=False,
)

logger.info(f"Sending reminders for {reservations.count()} reservations")

for reservation in reservations:
send_reservation_reminder(reservation)
71 changes: 71 additions & 0 deletions notifications/migrations/0012_add_reservation_reminder_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Generated by Django 2.2.11 on 2024-12-19 08:17

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("notifications", "0011_create_invoice_requested_notifications"),
]

operations = [
migrations.AlterField(
model_name="notificationtemplate",
name="type",
field=models.CharField(
choices=[
("reservation_requested", "Reservation requested"),
(
"reservation_requested_official",
"Reservation requested official",
),
("reservation_requested", "Reservation invoice requested"),
(
"reservation_requested_official",
"Reservation invoice requested official",
),
("reservation_cancelled", "Reservation cancelled"),
(
"reservation_cancelled_official",
"Reservation cancelled official",
),
("reservation_confirmed", "Reservation confirmed"),
(
"reservation_confirmed_official",
"Reservation confirmed official",
),
("reservation_created", "Reservation created"),
("reservation_changed", "Reservation changed"),
("reservation_denied", "Reservation denied"),
("reservation_denied_official", "Reservation denied official"),
(
"reservation_created_with_access_code",
"Reservation created with access code",
),
(
"reservation_access_code_created",
"Access code was created for a reservation",
),
("paid_reservation_approved", "Paid reservation approved"),
(
"paid_reservation_approved_official",
"Paid reservation approved official",
),
("catering_order_created", "Catering order created"),
("catering_order_modified", "Catering order modified"),
("catering_order_deleted", "Catering order deleted"),
("reservation_comment_created", "Reservation comment created"),
(
"catering_order_comment_created",
"Catering order comment created",
),
("reservation_reminder", "Reservation reminder"),
],
db_index=True,
max_length=100,
unique=True,
verbose_name="Type",
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Generated by Django 2.2.11 on 2024-12-19 08:35

from django.db import migrations
from django.template import Context, Template

BODY_TEMPLATES = {
"en": """
Hello,

{{ message }}

{% verbatim %}
Resource: {{ resource }}
Unit: {{ unit }}
Starts: {{ begin }}
Ends: {{ end }}
{% endverbatim %}

This is an automated message, please don't reply to this.

Best regards,
Varaamo
""",
"fi": """
Hei,

{{ message }}

{% verbatim %}
Tila: {{ resource }}
Toimipiste: {{ unit }}
Alkaa: {{ begin }}
Päättyy: {{ end }}
{% endverbatim %}

Tämä on automaattinen viesti, joten ethän vastaa tähän.

Ystävällisin terveisin,
Varaamo
""",
}


def forwards(apps, schema_editor):
NotificationTemplate = apps.get_model("notifications", "NotificationTemplate")
NotificationTemplateTranslation = apps.get_model(
"notifications",
"NotificationTemplateTranslation",
)

notification, created = NotificationTemplate.objects.get_or_create(
type="reservation_reminder"
)
if created:
translations = []
for language, subject, message in [
(
"en",
"Upcoming reservation",
"You have a reservation coming up soon:",
),
(
"fi",
"Muistathan lähestyvän varauksesi",
"Sinulla on pian varaus:",
),
]:
body = Template(BODY_TEMPLATES[language]).render(
Context(
{
"message": message,
}
)
)
translations.append(
NotificationTemplateTranslation(
master_id=notification.id,
language_code=language,
subject=subject,
body=body,
)
)
NotificationTemplateTranslation.objects.bulk_create(translations)


class Migration(migrations.Migration):

dependencies = [
("notifications", "0012_add_reservation_reminder_type"),
]

operations = [migrations.RunPython(forwards, migrations.RunPython.noop)]
3 changes: 3 additions & 0 deletions notifications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class NotificationType:
RESERVATION_COMMENT_CREATED = "reservation_comment_created"
CATERING_ORDER_COMMENT_CREATED = "catering_order_comment_created"

RESERVATION_REMINDER = "reservation_reminder"


class NotificationTemplateException(Exception):
pass
Expand Down Expand Up @@ -107,6 +109,7 @@ class NotificationTemplate(TranslatableModel):
NotificationType.CATERING_ORDER_COMMENT_CREATED,
_("Catering order comment created"),
),
(NotificationType.RESERVATION_REMINDER, _("Reservation reminder")),
)

type = models.CharField(
Expand Down
69 changes: 69 additions & 0 deletions notifications/tests/test_send_reservation_reminders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from datetime import timedelta
from unittest import mock

import pytest
from django.core.management import call_command
from django.utils import timezone

from resources.models import Reservation, Resource, ResourceType, Unit


@pytest.fixture
def reservation():
resource_type = ResourceType.objects.get_or_create(main_type="space", name="space")[0]
unit = Unit.objects.create(name="Test unit")
resource = Resource.objects.create(
name="Test resource",
type=resource_type,
unit=unit,
)
reservation = Reservation.objects.create(
resource=resource,
begin=timezone.now() + timedelta(hours=23),
end=timezone.now() + timedelta(hours=24),
reminder_sent=False,
)
return reservation


@pytest.mark.django_db
def test_send_reservation_reminder(reservation):
"""
Test that the reminder is sent and the reminder_sent field is updated.
"""
with mock.patch(
"resources.models.Reservation.send_reservation_mail"
) as mock_send_mail:
call_command("send_reservation_reminders")
reservation.refresh_from_db()
assert reservation.reminder_sent is True
mock_send_mail.assert_called_once_with(notification_type="reservation_reminder")


@pytest.mark.django_db
def test_no_reminder_if_already_sent(reservation):
"""
Test that the reminder is not sent if it has already been sent.
"""
reservation.reminder_sent = True
reservation.save()
with mock.patch(
"resources.models.Reservation.send_reservation_mail"
) as mock_send_mail:
call_command("send_reservation_reminders")
mock_send_mail.assert_not_called()


@pytest.mark.django_db
def test_no_reminder_for_reservation_not_within_24_hours(reservation):
"""
Test that the reminder is not sent if the reservation is not in the next 24 hours.
"""
reservation.begin = timezone.now() + timedelta(days=2)
reservation.end = timezone.now() + timedelta(days=2, hours=1)
reservation.save()
with mock.patch(
"notifications.management.commands.send_reservation_reminders.send_reservation_reminder"
) as mock_send_reminder:
call_command("send_reservation_reminders")
mock_send_reminder.assert_not_called()
4 changes: 2 additions & 2 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ paths:
in: query
description: Order queryset by given resource fields, accepted values are
`resource_name_fi`, `resource_name_en`, `resource_name_sv`, `unit_name_fi`,
`unit_name_en`, `unit_name_sv`, `type`, `people_capacity`. Prefix parameter
`unit_name_en`, `unit_name_sv`, `type`, `people_capacity_lower`. Prefix parameter
value with `-` to get reverse ordering.
schema:
type: string
Expand Down Expand Up @@ -1043,7 +1043,7 @@ components:
authentication:
type: string
description: The type of authentication required to reserve the resource
people_capacity:
people_capacity_lower:
type: number
description: The maximum number of people for the resource
area:
Expand Down
2 changes: 1 addition & 1 deletion payments/api/reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ def create(self, validated_data):

order_line_data["total_price"] = total_price
order_line_data["unit_price"] = Decimal(order_line_price_info["amount"])
order_line_data["tax_percentage"] = order_line_price_info["tax_percentage"]

price_source = order_line_price_info["price_source"]

order_line_data["tax_percentage"] = price_source.tax_percentage
order_line_data["price_period"] = price_source.price_period
order_line_data["price_type"] = price_source.price_type

Expand Down
Loading