Fix #9 - Registrations: Add support for non members #22

Merged
heinzel merged 2 commits from heinzel into master 2020-10-15 17:50:37 +02:00
16 changed files with 315 additions and 29 deletions
Showing only changes of commit 63026e429b - Show all commits

View File

@@ -131,11 +131,22 @@
<div id="collapseRegistrations" class="panel-collapse collapse"
role="tabpanel" aria-labelledby="headingRegistrations">
<div class="panel-body">
{% for registration in registrations %}
{% for registration in registrations_all %}
<form action="" method="post" class="form-inline">
{% csrf_token %}
<input type="hidden" name="registration" value="{{ registration.id }}">
{% if has_permission_update_participants %}
{% if registration.answered %}
<button disabled="disabled"
class="btn btn-link no-padding" title="Anmeldung wurde bereits bearbeitet">
<span class="text-muted">{% bootstrap_icon 'plus-sign' %}</span>
</button>
&nbsp;
<button disabled="disabled"
class="btn btn-link no-padding" title="Anmeldung wurde bereits bearbeitet">
<span class="text-muted">{% bootstrap_icon 'minus-sign' %}</span>
</button>
&nbsp;
{% elif has_permission_update_participants %}
<button type="submit" name="action" value="accept_registration"
class="btn btn-link no-padding" title="zur Teilnehmerliste hinzufügen">
<span class="text-success">{% bootstrap_icon 'plus-sign' %}</span>
@@ -148,7 +159,7 @@
&nbsp;
{% endif %}
{% if registration.answered %}
<span class="text-muted">
<s class="text-muted">
{% endif %}
{{ registration.get_full_name }}
(<a href="mailto:{{ registration.email_address }}">{{ registration.email_address }}</a>,
@@ -162,12 +173,13 @@
<span title="{{ registration.get_info }}">
{% bootstrap_icon 'info-sign' %}
</span>
&nbsp;
{% if registration.answered %}
</span>
</s>
{% endif %}
</form>
{% empty %}
{% trans 'Keine unbearbeiteten Anmeldungen vorhanden' %}
{% trans 'Keine Anmeldungen vorhanden' %}
{% endfor %}
</div>
</div>
@@ -189,7 +201,12 @@
<small>
(<a href="mailto:{{ participant.email_address }}">{{ participant.email_address }}</a>, {{ participant.phone_number }})
</small>
&nbsp; {{ participant.dav_number }}
&nbsp;
{% if participant.dav_member %}
{{ participant.dav_number|default:'Fehler! heinzel Bescheid geben!' }}
{% else %}
{% trans 'Nicht Mitglied' %}
{% endif %}
<div class="pull-right">
<form action="" method="post" class="form-inline">
{% csrf_token %}

View File

@@ -29,9 +29,12 @@
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="col-sm-3">
{% bootstrap_field form.dav_number %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.dav_member %}
</div>
<div class="col-sm-6">
{% bootstrap_field form.emergency_contact %}
</div>

View File

@@ -55,7 +55,11 @@
{{ participant.personal_names }}
</td>
<td>
{{ participant.dav_number }}
{% if participant.dav_member %}
{{ participant.dav_number|default:'Fehler! heinzel Bescheid geben!' }}
{% else %}
{% trans 'Nicht Mitglied' %}
{% endif %}
</td>
<td data-order="{{ participant.paid }} {{ event.charge|floatformat:'-2' }}"
data-search="{% if participant.paid %}{% trans 'Grün' %}{% else %}{% trans 'Rot' %}{% endif %} {{ event.charge|floatformat:'-2' }}">

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-10-15 15:38
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dav_events', '0033_auto_20200925_1543'),
]
operations = [
migrations.AddField(
model_name='participant',
name='dav_member',
field=models.BooleanField(default=True, help_text='In Ausnahmefällen nehmen wir auch Nichtmitglieder mit.', verbose_name='DAV Mitglied'),
),
migrations.AlterField(
model_name='participant',
name='dav_number',
field=models.CharField(blank=True, max_length=62, validators=[django.core.validators.RegexValidator('^([0-9]{1,10}/[0-9]{2,10}/)?[0-9]{1,10}(\\*[0-9]{1,10})?(\\*[0-9]{4}\\*[0-9]{4})?([* ][0-9]{8})?$', 'Ungültiges Format.')], verbose_name='DAV Mitgliedsnummer'),
),
]

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
@@ -32,8 +33,11 @@ class Participant(models.Model):
email_address = models.EmailField(verbose_name=_('E-Mail-Adresse'))
phone_number = models.CharField(max_length=254,
verbose_name=_('Telefonnummer'))
dav_member = models.BooleanField(default=True,
verbose_name=_('DAV Mitglied'),
help_text=_('In Ausnahmefällen nehmen wir auch Nichtmitglieder mit.'))
dav_number = models.CharField(max_length=62,
validators=[DAVNumberValidator],
blank=True, validators=[DAVNumberValidator],
verbose_name=_('DAV Mitgliedsnummer'))
emergency_contact = models.TextField(blank=True,
verbose_name=_('Notfall-Kontakt'),
@@ -69,25 +73,38 @@ class Participant(models.Model):
text = """{fullname}
{address}, {postal_code} {city}
DAV Mitglied: {dav_info}
Notfallkontakt:
{emergency_contact}
Anmerkung:
{note}
"""
if not self.dav_member:
dav_info = _('Nein')
else:
dav_info = self.dav_number
return text.format(
fullname=self.get_full_name(),
address=self.address,
postal_code=self.postal_code,
city=self.city,
dav_info=dav_info,
emergency_contact=self.emergency_contact,
note=self.note,
)
def clean(self):
if self.dav_member and not self.dav_number:
raise ValidationError({'dav_number': _('Bei DAV Mitgliedern brauchen wir die Mitgliedsnummer.')})
def save(self, **kwargs):
if not self.purge_at and self.event:
self.purge_at = self.__class__.calc_purge_at(self.event)
self.full_clean()
super(Participant, self).save(**kwargs)
@staticmethod

View File

@@ -29,9 +29,12 @@
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="col-sm-3">
{% bootstrap_field form.dav_number %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.dav_member %}
</div>
<div class="col-sm-6">
{% bootstrap_field form.emergency_contact %}
</div>

View File

@@ -230,9 +230,9 @@ class EventRegistrationsView(EventPermissionMixin, generic.DetailView):
registrations_support = hasattr(event, 'registrations')
context['registrations_support'] = registrations_support
if registrations_support:
registrations_unanswered = event.registrations.filter(answered=False)
registrations_all = event.registrations.all()
context['registrations_unanswered'] = registrations_unanswered
registrations_pending = registrations_all.filter(answered=False)
context['registrations_pending'] = registrations_pending
context['registrations_all'] = registrations_all
context['registrations'] = registrations_all

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import logging
from django import forms
from django.utils.translation import ugettext
from django.utils.translation import ugettext, ugettext_lazy as _
from .models import Registration
@@ -9,10 +9,17 @@ logger = logging.getLogger(__name__)
class RegistrationForm(forms.ModelForm):
not_dav_member = forms.BooleanField(required=False,
label=_('Ich bin noch kein DAV Mitglied.'),
help_text=_('Wenn du noch kein DAV Mitglied bist,'
' oder deine Aufnahme noch in Arbeit ist,'
' kreuze dieses Feld hier an.'))
class Meta:
model = Registration
exclude = ['event', 'created_at', 'privacy_policy', 'purge_at', 'answered']
widgets = {
'dav_member': forms.HiddenInput(),
'emergency_contact': forms.Textarea(attrs={'rows': 4}),
'experience': forms.Textarea(attrs={'rows': 5}),
'note': forms.Textarea(attrs={'rows': 5}),
@@ -41,3 +48,13 @@ class RegistrationForm(forms.ModelForm):
code='privacy_policy_not_accepted',
)
return val
def clean(self):
super(RegistrationForm, self).clean()
dav_member = self.cleaned_data.get('dav_member')
dav_number = self.cleaned_data.get('dav_number')
if dav_member and not dav_number:
error_msg = ugettext(u'Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.')
self.add_error('not_dav_member', error_msg)
raise forms.ValidationError(error_msg, code='dav_number_missing')
return self.cleaned_data

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-10-15 15:38
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dav_registration', '0004_auto_20190605_1400'),
]
operations = [
migrations.AddField(
model_name='registration',
name='dav_member',
field=models.BooleanField(default=True, verbose_name='DAV Mitglied'),
),
migrations.AlterField(
model_name='registration',
name='dav_number',
field=models.CharField(blank=True, help_text='Deine Mitgliedsnummer findest du unter dem Strichcode auf deinem DAV Ausweis.<br /> Beispiel: <tt>131/00/012345</tt> (der Teil bis zum ersten * genügt)', max_length=62, validators=[django.core.validators.RegexValidator('^([0-9]{1,10}/[0-9]{2,10}/)?[0-9]{1,10}(\\*[0-9]{1,10})?(\\*[0-9]{4}\\*[0-9]{4})?([* ][0-9]{8})?$', 'Ungültiges Format.')], verbose_name='DAV Mitgliedsnummer'),
),
]

View File

@@ -2,6 +2,7 @@
from __future__ import unicode_literals
import datetime
import logging
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils import timezone
@@ -39,8 +40,10 @@ class Registration(models.Model):
phone_number = models.CharField(max_length=254,
verbose_name=_('Telefonnummer'),
help_text=_('Idealerweise eine Mobilfunk-Nummer'))
dav_member = models.BooleanField(default=True,
verbose_name=_('DAV Mitglied'))
dav_number = models.CharField(max_length=62,
validators=[DAVNumberValidator],
blank=True, validators=[DAVNumberValidator],
verbose_name=_('DAV Mitgliedsnummer'),
help_text='%s<br /> %s %s' % (
_('Deine Mitgliedsnummer findest du unter dem Strichcode'
@@ -109,7 +112,7 @@ class Registration(models.Model):
text = """{fullname}
{address}, {postal_code} {city}
DAV Mitgliedsnummer: {dav_number}
DAV Mitglied: {dav_info}
Erfahrung:
{experience}
@@ -117,16 +120,25 @@ Erfahrung:
Anmerkung:
{note}
"""
if not self.dav_member:
dav_info = _('Nein')
else:
dav_info = self.dav_number
return text.format(
fullname=self.get_full_name(),
address=self.address,
postal_code=self.postal_code,
city=self.city,
dav_number=self.dav_number,
dav_info=dav_info,
experience=self.experience,
note=self.note,
)
def clean(self):
if self.dav_member and not self.dav_number:
raise ValidationError({'dav_number': _('Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.')})
def save(self, **kwargs):
creating = False
if not self.id:
@@ -135,6 +147,7 @@ Anmerkung:
if not self.purge_at and self.event:
self.purge_at = self.__class__.calc_purge_at(self.event)
self.full_clean()
super(Registration, self).save(**kwargs)
if creating:

View File

@@ -22,7 +22,7 @@ Personendaten
{{ registration.postal_code }} {{ registration.city }}
Telefon: {{ registration.phone_number }}
E-Mail: {{ registration.email_address }}
DAV Mitgliedsnummer: {{ registration.dav_number }}
{% if registration.dav_member %}DAV Mitgliedsnummer: {{ registration.dav_number }}{% else %}DAV Mitglied: Nein{% endif %}
Notfall-Kontakt
---------------

View File

@@ -11,7 +11,7 @@ Teilnehmer*in:
{{ registration.address }}, {{ registration.postal_code }} {{ registration.city }}
{{ registration.phone_number }}
{{ registration.email_address }}
{{ registration.dav_number }}
{% if registration.dav_member %}{{ registration.dav_number }}{% else %}Nicht DAV Mitglied{% endif %}
Notfall-Kontakt:
{% if registration.emergency_contact %}{{ registration.emergency_contact }}{% else %}-{% endif %}

View File

@@ -4,6 +4,40 @@
{% block head-title %}{% block form-title %}{% trans 'Anmeldung' %} - {{ event.number }}{% endblock form-title %} - {{ block.super }}{% endblock head-title %}
{% block head-additional %}
<script type="text/javascript">
function init_not_dav_member_handler() {
var e_orig = $("#id_dav_member");
initial_str = e_orig.val();
initial_bool = (initial_str == 'True')
var e_inverted = $("#id_not_dav_member");
if(e_inverted != null) {
e_inverted.prop("checked", !initial_bool);
e_inverted.change(function(){ not_dav_member_handler(); });
}
}
function not_dav_member_handler() {
var e = $("#id_not_dav_member");
if(e != null)
checked = e.prop("checked");
else
checked = true;
$("#id_dav_number").prop("disabled", checked);
$("#id_dav_member").val(!checked);
if(checked) {
$("#id_dav_number").val("");
}
}
$(document).ready(function(){
init_not_dav_member_handler();
});
</script>
{% endblock head-additional %}
{% block page-container-fluid %}
<h3 class="top-most">{% trans 'Anmeldung' %}</h3>
<form>
@@ -56,9 +90,12 @@
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="col-sm-3">
{% bootstrap_field form.dav_number %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.not_dav_member %}
</div>
<div class="col-sm-6">
{% bootstrap_field form.emergency_contact %}
</div>

View File

@@ -32,11 +32,11 @@ Vorgang: {registration_hexstr} (wird nur gebraucht, wenn irgendwas schief geht)
Personendaten
-------------
{participant_full_name}
Telefon:
Here
1 Karlsruhe
Telefon: 12
E-Mail: {participant_email}
DAV Mitgliedsnummer:
DAV Mitgliedsnummer: 0
Notfall-Kontakt
---------------
@@ -66,10 +66,10 @@ Vorgang: {registration_hexstr}
Teilnehmer*in:
{participant_full_name}
,
Here, 1 Karlsruhe
12
{participant_email}
0
Notfall-Kontakt:
-
@@ -107,7 +107,12 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
'dav_number': '0',
}
registration = self.create_registration(registration_data)
@@ -150,6 +155,7 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
'postal_code': '76131',
'city': 'Karlsruhe',
'phone_number': '+49 721 1234567890 AB (Büro)',
'dav_member': False,
'dav_number': '131/00/007*12345',
'emergency_contact': 'Call 911!',
'experience': 'Yes, we can!',
@@ -167,7 +173,7 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
search += '{} {}\n'.format(registration_data['postal_code'], registration_data['city'])
search += 'Telefon: {}\n'.format(registration_data['phone_number'])
search += 'E-Mail: {}\n'.format(registration_data['email_address'])
search += 'DAV Mitgliedsnummer: {}\n'.format(registration_data['dav_number'])
search += 'DAV Mitglied: Nein\n'
self.assertIn(search, mail.body)
search = '\n'
@@ -204,7 +210,12 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
'dav_number': '0',
}
registration = self.create_registration(registration_data)
@@ -247,6 +258,7 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
'postal_code': '76131',
'city': 'Karlsruhe',
'phone_number': '+49 721 1234567890 AB (Büro)',
'dav_member': False,
'dav_number': '131/00/007*12345',
'emergency_contact': 'Call 911!',
'experience': 'Yes, we can!',
@@ -265,7 +277,7 @@ class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
search += '\n'
search += registration_data['email_address']
search += '\n'
search += registration_data['dav_number']
search += 'Nicht DAV Mitglied'
search += '\n'
self.assertIn(search, mail.body)

View File

@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import apps
from django.core.exceptions import ValidationError
from django.test import TestCase
from dav_events.tests.generic import EventMixin
from .generic import RegistrationMixin
class RegistrationTestCase(EventMixin, RegistrationMixin, TestCase):
def setUp(self):
super(RegistrationTestCase, self).setUp()
app_config = apps.get_app_config('dav_events')
app_config.settings.enable_email_on_status_update = False
self.event = self.create_event_by_model()
self.submit_event(self.event)
self.accept_event(self.event)
self.confirm_publication_event(self.event)
def test_create_members(self):
event = self.event
registration_data = {
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
}
dav_numbers = ['0', '12345', '131/00/12345']
for n in dav_numbers:
d = registration_data
d['dav_number'] = n
self.create_registration(d)
def test_create_members_without_number(self):
event = self.event
registration_data = {
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
}
with self.assertRaisesMessage(ValidationError,
'Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.'):
self.create_registration(registration_data)
registration_data['dav_number'] = ''
with self.assertRaisesMessage(ValidationError,
'Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.'):
self.create_registration(registration_data)
def test_create_members_with_invalid_numbers(self):
event = self.event
registration_data = {
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
}
dav_numbers = ['Nein', '-', '13100123456789']
for n in dav_numbers:
d = registration_data
d['dav_number'] = n
with self.assertRaises(ValidationError) as context:
self.create_registration(d)
self.assertEqual(context.exception.messages, ['Ungültiges Format.'])
def test_create_non_member(self):
event = self.event
registration_data = {
'event': event,
'personal_names': 'Participant',
'family_names': 'One',
'address': 'Here',
'postal_code': '1',
'city': 'Karlsruhe',
'phone_number': '12',
'email_address': 'participant@localhost',
'dav_member': False,
}
self.create_registration(registration_data)

View File

@@ -29,6 +29,16 @@ class UtilsTestCase(RegistrationMixin, EventMixin, TestCase):
'trainer_familyname': 'One',
'trainer_email': 'trainer@localhost',
}
registration_data = {
'personal_names': 'Participant',
'family_names': 'P.',
'address': 'Am Fächerbad 2',
'postal_code': '76131',
'city': 'Karlsruhe',
'phone_number': '555 5555',
'email_address': 'participant@localhost',
'dav_number': '1',
}
first_day = today - (one_day * 367)
while first_day < today:
@@ -41,7 +51,9 @@ class UtilsTestCase(RegistrationMixin, EventMixin, TestCase):
self.accept_event(event)
for i in range(0, registrations_per_event):
self.create_registration({'event': event})
d = registration_data
d['event'] = event
self.create_registration(d)
purge_registrations()