Commit 9b5e6fdc authored by Tom van Bussel's avatar Tom van Bussel
Browse files

Merge branch 'feature/organiser-mail-at-cancel' into 'master'

Add email for event's organising party at registration cancellation

See merge request !425
parents a80391f4 f699ff92
...@@ -53,7 +53,7 @@ class EventAdmin(DoNextModelAdmin): ...@@ -53,7 +53,7 @@ class EventAdmin(DoNextModelAdmin):
inlines = (RegistrationInformationFieldInline,) inlines = (RegistrationInformationFieldInline,)
fields = ('title', 'description', 'start', 'end', 'organiser', 'category', fields = ('title', 'description', 'start', 'end', 'organiser', 'category',
'registration_start', 'registration_end', 'cancel_deadline', 'registration_start', 'registration_end', 'cancel_deadline',
'location', 'map_location', 'price', 'fine', 'send_cancel_email', 'location', 'map_location', 'price', 'fine',
'max_participants', 'no_registration_message', 'published') 'max_participants', 'no_registration_message', 'published')
list_display = ('overview_link', 'event_date', 'registration_date', list_display = ('overview_link', 'event_date', 'registration_date',
'num_participants', 'organiser', 'category', 'published', 'num_participants', 'organiser', 'category', 'published',
......
...@@ -18,7 +18,7 @@ def notify_first_waiting(request, event): ...@@ -18,7 +18,7 @@ def notify_first_waiting(request, event):
.order_by('date')[event.max_participants]) .order_by('date')[event.max_participants])
first_waiting_member = first_waiting.member first_waiting_member = first_waiting.member
text_template = get_template('events/email.txt') text_template = get_template('events/member_email.txt')
with translation.override(first_waiting_member.language): with translation.override(first_waiting_member.language):
subject = _("[THALIA] Notification about your " subject = _("[THALIA] Notification about your "
...@@ -36,3 +36,22 @@ def notify_first_waiting(request, event): ...@@ -36,3 +36,22 @@ def notify_first_waiting(request, event):
text_message, text_message,
to=[first_waiting_member.user.email] to=[first_waiting_member.user.email]
).send() ).send()
def notify_organiser(event, registration):
if event.organiser is None or event.organiser.contact_mailinglist is None:
return
text_template = get_template('events/organiser_email.txt')
subject = 'Registration for {} cancelled by member'.format(
event.title)
text_message = text_template.render({
'event': event,
'registration': registration
})
EmailMessage(
subject,
text_message,
to=[event.organiser.contact_mailinglist.name + "@thalia.nu"]
).send()
...@@ -7,8 +7,8 @@ msgid "" ...@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-22 14:54+0200\n" "POT-Creation-Date: 2017-09-23 13:33+0200\n"
"PO-Revision-Date: 2017-09-22 14:54+0200\n" "PO-Revision-Date: 2017-09-23 13:35+0200\n"
"Last-Translator: Sébastiaan Versteeg <se_bastiaan@outlook.com>\n" "Last-Translator: Sébastiaan Versteeg <se_bastiaan@outlook.com>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: nl\n" "Language: nl\n"
...@@ -16,7 +16,7 @@ msgstr "" ...@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.3\n" "X-Generator: Poedit 2.0.4\n"
#: admin.py:86 #: admin.py:86
msgid "Event Date" msgid "Event Date"
...@@ -122,7 +122,7 @@ msgstr "Geen aanmelding vereist" ...@@ -122,7 +122,7 @@ msgstr "Geen aanmelding vereist"
msgid "title" msgid "title"
msgstr "titel" msgstr "titel"
#: models.py:36 models.py:384 #: models.py:36 models.py:398
msgid "description" msgid "description"
msgstr "beschrijving" msgstr "beschrijving"
...@@ -162,16 +162,28 @@ msgstr "einde aanmelden" ...@@ -162,16 +162,28 @@ msgstr "einde aanmelden"
msgid "cancel deadline" msgid "cancel deadline"
msgstr "afmelddeadline" msgstr "afmelddeadline"
#: models.py:82 templates/events/admin/details.html:76 #: models.py:81
msgid "send cancellation notifications"
msgstr "verstuur afmeldnotificaties"
#: models.py:83
msgid ""
"Send an email to the organising party when a member cancels their "
"registration after the deadline."
msgstr ""
"Verzend een e-mail naar de organiserende partij wanneer een lid zich na de "
"afmelddeadline afmeld voor een evenement."
#: models.py:89 templates/events/admin/details.html:76
#: templates/events/event.html:38 #: templates/events/event.html:38
msgid "location" msgid "location"
msgstr "locatie" msgstr "locatie"
#: models.py:87 #: models.py:94
msgid "location for minimap" msgid "location for minimap"
msgstr "locatie voor minimap" msgstr "locatie voor minimap"
#: models.py:89 #: models.py:96
msgid "" msgid ""
"Location of Huygens: Heyendaalseweg 135, Nijmegen. Location of Mercator 1: " "Location of Huygens: Heyendaalseweg 135, Nijmegen. Location of Mercator 1: "
"Toernooiveld 212, Nijmegen. Not shown as text!!" "Toernooiveld 212, Nijmegen. Not shown as text!!"
...@@ -179,138 +191,142 @@ msgstr "" ...@@ -179,138 +191,142 @@ msgstr ""
"Locatie van ’t Huygens: Heyendaalseweg 135, Nijmegen. Locatie van Mercator " "Locatie van ’t Huygens: Heyendaalseweg 135, Nijmegen. Locatie van Mercator "
"1: Toernooiveld 212, Nijmegen. Dit veld wordt niet getoond als tekst!!" "1: Toernooiveld 212, Nijmegen. Dit veld wordt niet getoond als tekst!!"
#: models.py:95 templates/events/admin/details.html:78 #: models.py:102 templates/events/admin/details.html:78
#: templates/events/event.html:43 #: templates/events/event.html:43
msgid "price" msgid "price"
msgstr "prijs" msgstr "prijs"
#: models.py:103 #: models.py:110
msgid "fine" msgid "fine"
msgstr "boete" msgstr "boete"
#: models.py:109 #: models.py:116
msgid "Fine if participant does not show up (at least €5)." msgid "Fine if participant does not show up (at least €5)."
msgstr "Boete als deelnemer niet komt opdagen (minimaal €5)." msgstr "Boete als deelnemer niet komt opdagen (minimaal €5)."
#: models.py:114 #: models.py:121
msgid "maximum number of participants" msgid "maximum number of participants"
msgstr "maximum aantal deelnemers" msgstr "maximum aantal deelnemers"
#: models.py:121 #: models.py:128
msgid "message when there is no registration" msgid "message when there is no registration"
msgstr "bericht dat getoond wordt wanneer aanmelden niet nodig is" msgstr "bericht dat getoond wordt wanneer aanmelden niet nodig is"
#: models.py:125 #: models.py:132
msgid "Default:" msgid "Default:"
msgstr "Standaard:" msgstr "Standaard:"
#: models.py:129 #: models.py:136
msgid "published" msgid "published"
msgstr "gepubliceerd" msgstr "gepubliceerd"
#: models.py:185 #: models.py:192
msgid "Can't have an event travel back in time" msgid "Can't have an event travel back in time"
msgstr "Een evenement kan niet terugreizen in de tijd" msgstr "Een evenement kan niet terugreizen in de tijd"
#: models.py:189 #: models.py:196
msgid "The fine for this event is too low (must be at least €5)." msgid "The fine for this event is too low (must be at least €5)."
msgstr "De boete voor dit evenement is te laag (minimaal €5)." msgstr "De boete voor dit evenement is te laag (minimaal €5)."
#: models.py:196 #: models.py:203
msgid "Doesn't make sense to have this if you require registrations." msgid "Doesn't make sense to have this if you require registrations."
msgstr "Het is niet logisch om dit te hebben als je aanmelden vereist." msgstr "Het is niet logisch om dit te hebben als je aanmelden vereist."
#: models.py:201 #: models.py:208
msgid "If registration is required, you need a start of registration" msgid "If registration is required, you need a start of registration"
msgstr "" msgstr ""
"Als aanmelden vereist is, dan heb je een starttijd voor de aanmeldperiode " "Als aanmelden vereist is, dan heb je een starttijd voor de aanmeldperiode "
"nodig" "nodig"
#: models.py:206 #: models.py:213
msgid "If registration is required, you need an end of registration" msgid "If registration is required, you need an end of registration"
msgstr "" msgstr ""
"Als aanmelden vereist is, dan heb je een eindtijd voor de aanmeldperiode " "Als aanmelden vereist is, dan heb je een eindtijd voor de aanmeldperiode "
"nodig" "nodig"
#: models.py:211 #: models.py:218
msgid "If registration is required, you need a deadline for the cancellation" msgid "If registration is required, you need a deadline for the cancellation"
msgstr "" msgstr ""
"Als aanmelden vereist is, dan heb je een eindtijd voor de aanmeldperiode " "Als aanmelden vereist is, dan heb je een eindtijd voor de aanmeldperiode "
"nodig" "nodig"
#: models.py:216 #: models.py:223
msgid "The cancel deadline should be before the start of the event." msgid "The cancel deadline should be before the start of the event."
msgstr "De afmelddeadline moet voor de start van het evenement liggen." msgstr "De afmelddeadline moet voor de start van het evenement liggen."
#: models.py:220 #: models.py:227
msgid "Registration start should be before registration end" msgid "Registration start should be before registration end"
msgstr "De starttijd voor de aanmeldperiode moet voor de eindtijd liggen" msgstr "De starttijd voor de aanmeldperiode moet voor de eindtijd liggen"
#: models.py:251 #: models.py:236
msgid "This organiser does not have a contact mailinglist."
msgstr "De organisator heeft geen contact mailinglijst."
#: models.py:265
msgid "No payment" msgid "No payment"
msgstr "Niet betaald" msgstr "Niet betaald"
#: models.py:252 #: models.py:266
msgid "Paid with cash" msgid "Paid with cash"
msgstr "Contact betaald" msgstr "Contact betaald"
#: models.py:253 #: models.py:267
msgid "Paid with card" msgid "Paid with card"
msgstr "Pin betaald" msgstr "Pin betaald"
#: models.py:266 templates/events/admin/registrations_table.html:6 #: models.py:280 templates/events/admin/registrations_table.html:6
msgid "name" msgid "name"
msgstr "naam" msgstr "naam"
#: models.py:268 #: models.py:282
msgid "Use this for non-members" msgid "Use this for non-members"
msgstr "Gebruikt dit voor niet-leden" msgstr "Gebruikt dit voor niet-leden"
#: models.py:273 #: models.py:287
msgid "registration date" msgid "registration date"
msgstr "aanmelddatum" msgstr "aanmelddatum"
#: models.py:275 #: models.py:289
msgid "cancellation date" msgid "cancellation date"
msgstr "afmelddatum" msgstr "afmelddatum"
#: models.py:280 templates/events/admin/registrations_table.html:11 #: models.py:294 templates/events/admin/registrations_table.html:11
msgid "present" msgid "present"
msgstr "aanwezig" msgstr "aanwezig"
#: models.py:287 #: models.py:301
msgid "payment" msgid "payment"
msgstr "betaling" msgstr "betaling"
#: models.py:340 models.py:341 #: models.py:354 models.py:355
msgid "Either specify a member or a name" msgid "Either specify a member or a name"
msgstr "Geef een lid of een naam op" msgstr "Geef een lid of een naam op"
#: models.py:364 #: models.py:378
msgid "Checkbox" msgid "Checkbox"
msgstr "Checkbox" msgstr "Checkbox"
#: models.py:365 #: models.py:379
msgid "Text" msgid "Text"
msgstr "Text" msgstr "Text"
#: models.py:366 #: models.py:380
msgid "Integer" msgid "Integer"
msgstr "Integer" msgstr "Integer"
#: models.py:371 #: models.py:385
msgid "field type" msgid "field type"
msgstr "veldtype" msgstr "veldtype"
#: models.py:378 #: models.py:392
msgid "field name" msgid "field name"
msgstr "veldnaam" msgstr "veldnaam"
#: models.py:390 #: models.py:404
msgid "required" msgid "required"
msgstr "verplicht" msgstr "verplicht"
#: models.py:442 #: models.py:456
msgid "last changed" msgid "last changed"
msgstr "laatst aangepast" msgstr "laatst aangepast"
...@@ -328,7 +344,7 @@ msgstr "Je bent al aangemeld." ...@@ -328,7 +344,7 @@ msgstr "Je bent al aangemeld."
msgid "You may not register." msgid "You may not register."
msgstr "Je mag je niet aanmelden." msgstr "Je mag je niet aanmelden."
#: services.py:117 services.py:131 services.py:182 #: services.py:121 services.py:135 services.py:186
msgid "You are not registered for this event." msgid "You are not registered for this event."
msgstr "Je bent niet aangemeld voor dit evenement." msgstr "Je bent niet aangemeld voor dit evenement."
...@@ -486,39 +502,6 @@ msgstr "Aangemeld voor dit evenement" ...@@ -486,39 +502,6 @@ msgstr "Aangemeld voor dit evenement"
msgid "Not registered for this event" msgid "Not registered for this event"
msgstr "Niet aangemeld voor dit evenement" msgstr "Niet aangemeld voor dit evenement"
#: templates/events/email.txt:1
#, python-format
msgid ""
"Hi %(name)s,\n"
"\n"
"You registered for the event '%(event_title)s' on %(registration_date)s and "
"unfortunately you were placed on the waiting list.\n"
"However someone just unregistered and we would like to let you know that "
"you'll be able to attend now!\n"
"\n"
"You can find more information about the event on the website: %(baseurl)s"
"%(event_url)s\n"
"\n"
"We're assuming that you'll be there, but you're still able to unregister "
"until %(cancel_deadline)s.\n"
"\n"
"Best regards,\n"
"Study Association Thalia"
msgstr ""
"Beste %(name)s,\n"
"\n"
"Je hebt je op %(registration_date)s aangemeld voor het evenement "
"'%(event_title)s’. Toen kwam je op de wachtlijst terecht, maar inmiddels is "
"er plaats!\n"
"\n"
"Je kunt het evenement terugvinden op de website: %(baseurl)s%(event_url)s\n"
"\n"
"We gaan ervan uit dat je erbij bent, maar tot %(cancel_deadline)s kan je je "
"nog afmelden.\n"
"\n"
"Met vriendelijke groet,\n"
"Studievereniging Thalia"
#: templates/events/event.html:4 templates/events/event.html:5 #: templates/events/event.html:4 templates/events/event.html:5
#: templates/events/index.html:5 templates/events/index.html:6 #: templates/events/index.html:5 templates/events/index.html:6
#: templates/events/index.html:18 templates/events/registration.html:4 #: templates/events/index.html:18 templates/events/registration.html:4
...@@ -645,6 +628,39 @@ msgstr "toon verjaardagen" ...@@ -645,6 +628,39 @@ msgstr "toon verjaardagen"
msgid "list" msgid "list"
msgstr "lijst" msgstr "lijst"
#: templates/events/member_email.txt:1
#, python-format
msgid ""
"Hi %(name)s,\n"
"\n"
"You registered for the event '%(event_title)s' on %(registration_date)s and "
"unfortunately you were placed on the waiting list.\n"
"However someone just unregistered and we would like to let you know that "
"you'll be able to attend now!\n"
"\n"
"You can find more information about the event on the website: %(baseurl)s"
"%(event_url)s\n"
"\n"
"We're assuming that you'll be there, but you're still able to unregister "
"until %(cancel_deadline)s.\n"
"\n"
"Best regards,\n"
"Study Association Thalia"
msgstr ""
"Beste %(name)s,\n"
"\n"
"Je hebt je op %(registration_date)s aangemeld voor het evenement "
"'%(event_title)s’. Toen kwam je op de wachtlijst terecht, maar inmiddels is "
"er plaats!\n"
"\n"
"Je kunt het evenement terugvinden op de website: %(baseurl)s%(event_url)s\n"
"\n"
"We gaan ervan uit dat je erbij bent, maar tot %(cancel_deadline)s kan je je "
"nog afmelden.\n"
"\n"
"Met vriendelijke groet,\n"
"Studievereniging Thalia"
#: templates/events/registration.html:4 templates/events/registration.html:5 #: templates/events/registration.html:4 templates/events/registration.html:5
msgid "Registration" msgid "Registration"
msgstr "Aanmelding" msgstr "Aanmelding"
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-27 17:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0018_2_registration_payment'),
]
operations = [
migrations.AddField(
model_name='event',
name='send_cancel_email',
field=models.BooleanField(default=False, help_text='Send an email to the organising party when a member cancels their registration after the deadline.', verbose_name='send cancellation notifications'),
),
migrations.AlterField(
model_name='event',
name='send_cancel_email',
field=models.BooleanField(default=True,
help_text='Send an email to the organising party when a member cancels their registration after the deadline.',
verbose_name='send cancellation notifications'),
),
]
...@@ -77,6 +77,13 @@ class Event(models.Model, metaclass=ModelTranslateMeta): ...@@ -77,6 +77,13 @@ class Event(models.Model, metaclass=ModelTranslateMeta):
blank=True blank=True
) )
send_cancel_email = models.BooleanField(
_('send cancellation notifications'),
default=True,
help_text=_("Send an email to the organising party when a member "
"cancels their registration after the deadline."),
)
location = MultilingualField( location = MultilingualField(
models.CharField, models.CharField,
_("location"), _("location"),
...@@ -222,6 +229,13 @@ class Event(models.Model, metaclass=ModelTranslateMeta): ...@@ -222,6 +229,13 @@ class Event(models.Model, metaclass=ModelTranslateMeta):
errors.update({ errors.update({
'registration_start': message, 'registration_start': message,
'registration_end': message}) 'registration_end': message})
if (self.organiser is not None and
self.send_cancel_email and
self.organiser.contact_mailinglist is None):
errors.update(
{'send_cancel_email': _("This organiser does not "
"have a contact mailinglist.")})
if errors: if errors:
raise ValidationError(errors) raise ValidationError(errors)
......
...@@ -107,6 +107,10 @@ def cancel_registration(request, user, event): ...@@ -107,6 +107,10 @@ def cancel_registration(request, user, event):
if registration.queue_position == 0: if registration.queue_position == 0:
emails.notify_first_waiting(request, event) emails.notify_first_waiting(request, event)
if (event.send_cancel_email and
event.after_cancel_deadline):
emails.notify_organiser(event, registration)
# Note that this doesn"t remove the values for the # Note that this doesn"t remove the values for the
# information fields that the user entered upon registering. # information fields that the user entered upon registering.
# But this is regarded as a feature, not a bug. Especially # But this is regarded as a feature, not a bug. Especially
......
{% load baseurl %}Hi,
A member that was registered for the event '{{ event.title }}' that you're organising has cancelled their registration after the deadline.
Name: {{ registration.member.display_name }}
Registration date: {{ registration.date|date:"SHORT_DATETIME_FORMAT" }}
...@@ -6,20 +6,31 @@ from django.utils import timezone ...@@ -6,20 +6,31 @@ from django.utils import timezone
from activemembers.models import Committee from activemembers.models import Committee
from events.models import Event, Registration from events.models import Event, Registration
from mailinglists.models import MailingList
from members.models import Member from members.models import Member
class EventTest(TestCase): class EventTest(TestCase):
"""Tests events""" """Tests events"""
fixtures = ['members.json', 'committees.json'] fixtures = ['members.json']
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
cls.mailinglist = MailingList.objects.create(
name="testmail"
)
cls.committee = Committee.objects.create(
name_nl="commissie",
name_en="committee",
contact_mailinglist=cls.mailinglist
)
cls.event = Event.objects.create( cls.event = Event.objects.create(
title_nl='testevene', title_nl='testevene',
title_en='testevent', title_en='testevent',
organiser=Committee.objects.get(pk=1),