Verified Commit 0d26a638 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg
Browse files

Add registrations feature

Move members welcome email to emails.py

Remove BecomeAMemberDocument model

Add registrations app with models

Add emails.py and email templates

Model changes:
- Remove email_confirmed boolean from Registration model
- Change default value of Registration model status to 'confirm'
- Make Registration model email unique

Add first model tests for Registration

Write clean() for Registration

Fix PEP8 errors

Add init file to tests directory

Make Membership types constants instead of using the string value everywhere

Change membership lengths to constants in Entry model

Mention upgrade behaviour in registrations README

Use constants in registrations models test

Use constants for Entry status and fix membership_type argument in models test

Convert payment types to constants

Override translation language in registration emails by preferred language

Add services.py and tests

Implemented services.py

Add admin.py, fix docs in services.py and add app to sphinx

Wrote email contents

Make Registration email not unique

Fix call to renewal complete email in services.py

Add mail outbox assert to test_process_payment

Fix email formatting and do not completely block payment permissions

Make welcome mail non-trimmed

Change model admin changeform and add accept/reject buttons for registration and renewal

Add process buttons to payment admin and add permissions for reviewing and processing

Make some changes to the registrations admin (RenewalAdmin now inherits from RegistrationsAdmin) and add some tests

Add confirmation messages to custom admin calls, and redirect back to change form

Improve automatic username generation

Prevent processing payment for member with already active study membership

Rewrite registrations views to classes

Add tests for views

Fix sending confirmation email and related tests

Send confirmation mail after registration creation

Change README to reflect new supposed behaviour

Add frontend views

Update migration file to reflect model changes

Update view tests to reflect url changes

Add tests for forms

Change birthday field to SelectDateWidget

Generate username when accepting registration, not when processing payment

Change README to reflect new rules

Improve renewal admin

Move programme and starting year checks to model

Rename views and form to be member only

Make changes to model

Fix typo's and price for renewal

Fix tests

Move renewal validation to model instead of form

Change rules for processing in August

Update migration

Make renewal member field not readonly for new obj

Fix lecture year in test

Add confirmation to accept/reject buttons

Add localization for registrations

Change until date for new memberships to 01-09

Add processing date to payments

Add updated_at field to Entry model

Creation of renewal determines price

Fix tests

Always start membership renewals in August in September

Add board notifications

Move become a member page to registrations app

Update localisation

Add link to registration renewal on account page

Change file names and add link to documents page
parent 5f5421ca
...@@ -16,6 +16,7 @@ website ...@@ -16,6 +16,7 @@ website
partners partners
photos photos
pizzas pizzas
registrations
thabloid thabloid
thaliawebsite thaliawebsite
utils utils
registrations package
=====================
.. automodule:: registrations
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
registrations\.admin module
---------------------------
.. automodule:: registrations.admin
:members:
:undoc-members:
:show-inheritance:
registrations\.apps module
--------------------------
.. automodule:: registrations.apps
:members:
:undoc-members:
:show-inheritance:
registrations\.emails module
----------------------------
.. automodule:: registrations.emails
:members:
:undoc-members:
:show-inheritance:
registrations\.models module
----------------------------
.. automodule:: registrations.models
:members:
:undoc-members:
:show-inheritance:
registrations\.services module
------------------------------
.. automodule:: registrations.services
:members:
:undoc-members:
:show-inheritance:
registrations\.urls module
--------------------------
.. automodule:: registrations.urls
:members:
:undoc-members:
:show-inheritance:
""" """
This module registers admin pages for the models This module registers admin pages for the models
""" """
import csv
import datetime import datetime
from django.contrib import admin from django.contrib import admin
...@@ -10,7 +11,6 @@ from django.db.models import Q ...@@ -10,7 +11,6 @@ from django.db.models import Q
from django.http import HttpResponse from django.http import HttpResponse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import csv
from . import forms, models from . import forms, models
...@@ -145,8 +145,6 @@ class MemberAdmin(UserAdmin): ...@@ -145,8 +145,6 @@ class MemberAdmin(UserAdmin):
return False return False
admin.site.register(models.BecomeAMemberDocument)
# re-register User admin # re-register User admin
admin.site.unregister(User) admin.site.unregister(User)
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
...@@ -107,3 +107,17 @@ def send_expiration_announcement(dry_run=False): ...@@ -107,3 +107,17 @@ def send_expiration_announcement(dry_run=False):
{'members': members}), {'members': members}),
connection=connection, connection=connection,
) )
def send_welcome_message(user, password, language):
with translation.override(language):
email_body = loader.render_to_string(
'members/email/welcome.txt',
{
'full_name': user.get_full_name(),
'username': user.username,
'password': password
})
user.email_user(
_('Welcome to Study Association Thalia'),
email_body)
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
"username": "testuser", "username": "testuser",
"first_name": "", "first_name": "",
"last_name": "", "last_name": "",
"email": "", "email": "testuser@example.com",
"is_staff": true, "is_staff": true,
"is_active": true, "is_active": true,
"date_joined": "2016-07-07T12:00:21Z", "date_joined": "2016-07-07T12:00:21Z",
...@@ -53,6 +53,24 @@ ...@@ -53,6 +53,24 @@
"user_permissions": [] "user_permissions": []
} }
}, },
{
"model": "auth.user",
"pk": 4,
"fields": {
"password": "",
"last_login": null,
"is_superuser": false,
"username": "testuser3",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": true,
"is_active": true,
"date_joined": "2016-07-07T14:50:26Z",
"groups": [],
"user_permissions": []
}
},
{ {
"model": "members.profile", "model": "members.profile",
"pk": 1, "pk": 1,
...@@ -131,6 +149,32 @@ ...@@ -131,6 +149,32 @@
"bank_account": "" "bank_account": ""
} }
}, },
{
"model": "members.profile",
"pk": 4,
"fields": {
"user": 4,
"programme": null,
"student_number": "",
"address_street": "testuser 3",
"address_street2": "",
"address_postal_code": "6525 TE",
"address_city": "Nijmegen",
"phone_number": "",
"emergency_contact": "",
"emergency_contact_phone_number": "",
"birthday": "2016-07-07",
"show_birthday": true,
"website": "",
"profile_description": "",
"nickname": "",
"display_name_preference": "full",
"language": "nl",
"receive_optin": true,
"direct_debit_authorized": false,
"bank_account": ""
}
},
{ {
"model": "members.membership", "model": "members.membership",
"pk": 1, "pk": 1,
......
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm
from django.template import loader from django.contrib.auth.models import User
from django.utils import translation
from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .models import Profile from .models import Profile
from members import emails
class ProfileForm(forms.ModelForm): class ProfileForm(forms.ModelForm):
...@@ -74,17 +72,7 @@ class UserCreationForm(BaseUserCreationForm): ...@@ -74,17 +72,7 @@ class UserCreationForm(BaseUserCreationForm):
language = str(self.data.get('profile-0-language', 'en')) language = str(self.data.get('profile-0-language', 'en'))
if language not in ('nl', 'en'): if language not in ('nl', 'en'):
language = 'en' language = 'en'
with translation.override(language): emails.send_welcome_message(user, password, language)
email_body = loader.render_to_string(
'members/email/welcome.txt',
{
'full_name': user.get_full_name(),
'username': user.username,
'password': password
})
user.email_user(
ugettext('Welcome to Study Association Thalia'),
email_body)
return user return user
class Meta: class Meta:
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ 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-10-18 21:31+0200\n" "POT-Creation-Date: 2017-11-22 15:49+0100\n"
"PO-Revision-Date: 2017-10-18 21:32+0200\n" "PO-Revision-Date: 2017-10-18 21:32+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"
...@@ -54,11 +54,11 @@ msgstr "Adres" ...@@ -54,11 +54,11 @@ msgstr "Adres"
msgid "Address line 2" msgid "Address line 2"
msgstr "Tweede adresregel" msgstr "Tweede adresregel"
#: admin.py:113 models.py:202 #: admin.py:113 models.py:201
msgid "Postal code" msgid "Postal code"
msgstr "Postcode" msgstr "Postcode"
#: admin.py:113 models.py:208 #: admin.py:113 models.py:207
msgid "City" msgid "City"
msgstr "Woonplaats" msgstr "Woonplaats"
...@@ -66,7 +66,7 @@ msgstr "Woonplaats" ...@@ -66,7 +66,7 @@ msgstr "Woonplaats"
msgid "Download address label for selected users" msgid "Download address label for selected users"
msgstr "Download adreslabels voor geselecteerde gebruikers" msgstr "Download adreslabels voor geselecteerde gebruikers"
#: admin.py:131 models.py:165 #: admin.py:131 models.py:164
msgid "Student number" msgid "Student number"
msgstr "Studentnummer" msgstr "Studentnummer"
...@@ -98,99 +98,99 @@ msgstr "Verlopen lidmaatschap" ...@@ -98,99 +98,99 @@ msgstr "Verlopen lidmaatschap"
msgid "Membership expiration announcement sent" msgid "Membership expiration announcement sent"
msgstr "Meldingen vervallen lidmaatschap verzonden" msgstr "Meldingen vervallen lidmaatschap verzonden"
#: forms.py:33 #: emails.py:122
msgid "Welcome to Study Association Thalia"
msgstr "Welkom bij Studievereniging Thalia"
#: forms.py:31
msgid "Please enter a bank account" msgid "Please enter a bank account"
msgstr "Voer een bankrekening in" msgstr "Voer een bankrekening in"
#: forms.py:53 #: forms.py:51
msgid "Send welcome email" msgid "Send welcome email"
msgstr "Stuur welkomste-mails" msgstr "Stuur welkomste-mails"
#: forms.py:54 #: forms.py:52
msgid "This email will include the generated password" msgid "This email will include the generated password"
msgstr "Deze e-mail zal het gegenereerde wachtwoord bevatten" msgstr "Deze e-mail zal het gegenereerde wachtwoord bevatten"
#: forms.py:86 #: models.py:109
msgid "Welcome to Study Association Thalia"
msgstr "Welkom bij Studievereniging Thalia"
#: models.py:110
msgid "Is this user currently active" msgid "Is this user currently active"
msgstr "Is deze user op dit moment actief" msgstr "Is deze user op dit moment actief"
#: models.py:144 #: models.py:143
msgid "Computing Science" msgid "Computing Science"
msgstr "Informatica" msgstr "Informatica"
#: models.py:145 #: models.py:144
msgid "Information Sciences" msgid "Information Sciences"
msgstr "Informatiekunde" msgstr "Informatiekunde"
#: models.py:159 templates/members/profile.html:40 #: models.py:158 templates/members/profile.html:40
msgid "Study programme" msgid "Study programme"
msgstr "Studie" msgstr "Studie"
#: models.py:169 #: models.py:168
msgid "Enter a valid student- or e/z/u-number." msgid "Enter a valid student- or e/z/u-number."
msgstr "Voer een geldig student- of e/z/u-nummer in." msgstr "Voer een geldig student- of e/z/u-nummer in."
#: models.py:175 #: models.py:174
msgid "Starting year" msgid "Starting year"
msgstr "Startjaar" msgstr "Startjaar"
#: models.py:176 #: models.py:175
msgid "The year this member started studying." msgid "The year this member started studying."
msgstr "Het jaar waarop dit lid begon met studeren." msgstr "Het jaar waarop dit lid begon met studeren."
#: models.py:187 #: models.py:186
msgid "Include the house number" msgid "Include the house number"
msgstr "Inclusief huisnummer" msgstr "Inclusief huisnummer"
#: models.py:189 #: models.py:188
msgid "Street and house number" msgid "Street and house number"
msgstr "Straat en huisnummer" msgstr "Straat en huisnummer"
#: models.py:195 #: models.py:194
msgid "Second address line" msgid "Second address line"
msgstr "Tweede adresregel" msgstr "Tweede adresregel"
#: models.py:214 #: models.py:213
msgid "Phone number" msgid "Phone number"
msgstr "Telefoonnummer" msgstr "Telefoonnummer"
#: models.py:215 #: models.py:214
msgid "Enter a phone number so Thalia may reach you" msgid "Enter a phone number so Thalia may reach you"
msgstr "Voer een telefoonnummer in zodat Thalia je kan bereiken" msgstr "Voer een telefoonnummer in zodat Thalia je kan bereiken"
#: models.py:218 models.py:240 #: models.py:217 models.py:239
msgid "Please enter a valid phone number" msgid "Please enter a valid phone number"
msgstr "Voer svp een geldig telefoonnummer in" msgstr "Voer svp een geldig telefoonnummer in"
#: models.py:228 #: models.py:227
msgid "Emergency contact name" msgid "Emergency contact name"
msgstr "Contact voor noodgevallen" msgstr "Contact voor noodgevallen"
#: models.py:229 #: models.py:228
msgid "Who should we contact in case of emergencies" msgid "Who should we contact in case of emergencies"
msgstr "Wie Thalia moet bereiken in bij noodgevallen" msgstr "Wie Thalia moet bereiken in bij noodgevallen"
#: models.py:236 #: models.py:235
msgid "Emergency contact phone number" msgid "Emergency contact phone number"
msgstr "Telefoonnummer noodcontact" msgstr "Telefoonnummer noodcontact"
#: models.py:237 #: models.py:236
msgid "The phone number for the emergency contact" msgid "The phone number for the emergency contact"
msgstr "Het telefoonummer van de noodcontact" msgstr "Het telefoonummer van de noodcontact"
#: models.py:249 templates/members/profile.html:47 #: models.py:248 templates/members/profile.html:47
msgid "Birthday" msgid "Birthday"
msgstr "Verjaardag" msgstr "Verjaardag"
#: models.py:254 #: models.py:253
msgid "Display birthday" msgid "Display birthday"
msgstr "Laat verjaardag zien" msgstr "Laat verjaardag zien"
#: models.py:256 #: models.py:255
msgid "" msgid ""
"Show your birthday to other members on your profile page and in the birthday " "Show your birthday to other members on your profile page and in the birthday "
"calendar" "calendar"
...@@ -198,107 +198,107 @@ msgstr "" ...@@ -198,107 +198,107 @@ msgstr ""
"Toon je verjaardag aan andere leden op je profielpagina en in de " "Toon je verjaardag aan andere leden op je profielpagina en in de "
"verjaardagskalender" "verjaardagskalender"
#: models.py:263 templates/members/profile.html:43 #: models.py:262 templates/members/profile.html:43
msgid "Website" msgid "Website"
msgstr "Website" msgstr "Website"
#: models.py:264 #: models.py:263
msgid "Website to display on your profile page" msgid "Website to display on your profile page"
msgstr "Website om op je profiel te linken" msgstr "Website om op je profiel te linken"
#: models.py:270 #: models.py:269
msgid "Profile text" msgid "Profile text"
msgstr "Profieltekst" msgstr "Profieltekst"
#: models.py:271 #: models.py:270
msgid "Text to display on your profile" msgid "Text to display on your profile"
msgstr "Tekst om te laten zien op je profielpagina" msgstr "Tekst om te laten zien op je profielpagina"
#: models.py:278 #: models.py:277
msgid "Initials" msgid "Initials"
msgstr "Initialen" msgstr "Initialen"
#: models.py:285 #: models.py:284
msgid "Nickname" msgid "Nickname"
msgstr "Bijnaam" msgstr "Bijnaam"
#: models.py:292 #: models.py:291
msgid "How to display name" msgid "How to display name"
msgstr "Weergave naam" msgstr "Weergave naam"
#: models.py:293 #: models.py:292
msgid "Show full name" msgid "Show full name"
msgstr "Volledige naam" msgstr "Volledige naam"
#: models.py:294 #: models.py:293
msgid "Show only nickname" msgid "Show only nickname"
msgstr "Alleen initialen" msgstr "Alleen initialen"
#: models.py:295 #: models.py:294
msgid "Show only first name" msgid "Show only first name"
msgstr "Alleen voornaam" msgstr "Alleen voornaam"
#: models.py:296 #: models.py:295
msgid "Show initials and last name" msgid "Show initials and last name"
msgstr "Alleen initialen en achternaam" msgstr "Alleen initialen en achternaam"
#: models.py:297 #: models.py:296
msgid "Show name like \"John 'nickname' Doe\"" msgid "Show name like \"John 'nickname' Doe\""
msgstr "Laat zien als \"John 'bijnaam' Doe\"" msgstr "Laat zien als \"John 'bijnaam' Doe\""
#: models.py:298 #: models.py:297
msgid "Show nickname and last name" msgid "Show nickname and last name"
msgstr "Laat bijnaam en achternaam zien" msgstr "Laat bijnaam en achternaam zien"
#: models.py:303 #: models.py:302
msgid "Photo" msgid "Photo"
msgstr "Foto" msgstr "Foto"
#: models.py:311 #: models.py:310
msgid "Which events can this member attend" msgid "Which events can this member attend"
msgstr "Welke evenementen mag dit lid bijwonen" msgstr "Welke evenementen mag dit lid bijwonen"
#: models.py:312 #: models.py:311
msgid "All events" msgid "All events"
msgstr "Alle evenementen" msgstr "Alle evenementen"
#: models.py:313 #: models.py:312
msgid "User may not attend events" msgid "User may not attend events"
msgstr "Gebruiker mag niet naar evenementen" msgstr "Gebruiker mag niet naar evenementen"