Commit ebd86892 authored by Luko van der Maas's avatar Luko van der Maas

Merge branch '838-wire-transfer-events' into 'master'

Wire transfer payments for events

Closes #838

See merge request !1323
parents 5ac4a497 d4a4dbbf
...@@ -19,6 +19,7 @@ from events import services ...@@ -19,6 +19,7 @@ from events import services
from events.decorators import organiser_only from events.decorators import organiser_only
from events.exceptions import RegistrationError from events.exceptions import RegistrationError
from events.forms import FieldsForm, EventMessageForm from events.forms import FieldsForm, EventMessageForm
from payments.models import Payment
from pushnotifications.models import Message, Category from pushnotifications.models import Message, Category
from .models import Event, Registration from .models import Event, Registration
...@@ -34,6 +35,15 @@ class EventAdminDetails(DetailView, PermissionRequiredMixin): ...@@ -34,6 +35,15 @@ class EventAdminDetails(DetailView, PermissionRequiredMixin):
context_object_name = 'event' context_object_name = 'event'
permission_required = 'events.change_event' permission_required = 'events.change_event'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'payment': Payment,
})
return context
@method_decorator(staff_member_required, name='dispatch') @method_decorator(staff_member_required, name='dispatch')
@method_decorator(organiser_only, name='dispatch') @method_decorator(organiser_only, name='dispatch')
......
...@@ -7,12 +7,11 @@ from django.db.models import Q ...@@ -7,12 +7,11 @@ from django.db.models import Q
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.translation import ugettext_lazy as _
from django.utils.text import format_lazy from django.utils.text import format_lazy
from django.utils.translation import ugettext_lazy as _
from tinymce.models import HTMLField from tinymce.models import HTMLField
from members.models import Member from members.models import Member
from payments.models import Payment
from pushnotifications.models import ScheduledMessage, Category from pushnotifications.models import ScheduledMessage, Category
from utils.translation import ModelTranslateMeta, MultilingualField from utils.translation import ModelTranslateMeta, MultilingualField
...@@ -433,10 +432,6 @@ def registration_member_choices_limit(): ...@@ -433,10 +432,6 @@ def registration_member_choices_limit():
class Registration(models.Model): class Registration(models.Model):
"""Describes a registration for an Event""" """Describes a registration for an Event"""
PAYMENT_NONE = Payment.NONE
PAYMENT_CARD = Payment.CARD
PAYMENT_CASH = Payment.CASH
event = models.ForeignKey(Event, models.CASCADE) event = models.ForeignKey(Event, models.CASCADE)
member = models.ForeignKey( member = models.ForeignKey(
......
@import "../../thaliawebsite/static/css/variables";
.dashboard { .dashboard {
#content { #content {
width: auto; width: auto;
...@@ -6,22 +8,62 @@ ...@@ -6,22 +8,62 @@
} }
} }
.module table td a { .module {
display: inline-block; table {
vertical-align: middle; td {
vertical-align: middle;
&.text-center {
text-align: center;
}
a {
display: inline-block;
vertical-align: middle;
&.member-phone, &.member-email {
width: 16px;
height: 16px;
@media(max-width: 767px) {
display: block;
width: 20px;
height: 20px;
}
}
&.member-phone {
background: url('../images/phone-square.svg') no-repeat;
padding-right: 2px;
&.member-phone, &.member-email { @media(max-width: 767px) {
width: 16px; padding-right: 0;
height: 16px; margin-bottom: 2px;
}
}
&.member-email {
background: url('../images/envelope-square.svg') no-repeat;
}
}
}
} }
&.member-phone { select[name=payment] {
background: url('../images/phone-square.svg') no-repeat; background: $danger;
padding-right: 2px; color: white;
&.paid {
background: $success;
}
@media(max-width: 767px) {
height: 40px;
}
} }
&.member-email { .button {
background: url('../images/envelope-square.svg') no-repeat; display: inline-block;
margin: 2px;
} }
} }
} }
......
...@@ -13,18 +13,40 @@ django.jQuery(function () { ...@@ -13,18 +13,40 @@ django.jQuery(function () {
}); });
}); });
$(".payment-radio").change(function () { var payment_previous;
var radiobutton = $(this); $('select[name=payment]').on('focus', function () {
var url = radiobutton.parent().parent().data("url"); payment_previous = this.value;
var value = radiobutton.data("value"); }).change(function() {
if (radiobutton.prop('checked')) { var select = $(this);
patch(url, { payment: value }, function(result) { var url = $(this).parents('tr').data("url");
radiobutton.prop('checked', value === result.payment); var none = $(this).data('none');
$("table").trigger("update");
}, function() { if (payment_previous === $(this).val()) {
radiobutton.prop('checked', !checked); return;
}); }
if ($(this).val() === none) {
$(this).removeClass('paid');
} else {
$(this).addClass('paid');
} }
patch(url, { payment: $(this).val() }, function(data) {
if (data.payment === none) {
select.removeClass('paid');
} else {
select.addClass('paid');
}
select.val(data.payment);
$('table').trigger('update');
}, function(xhr) {
var data = $.parseJSON(xhr.responseText);
if (data.message !== undefined) {
alert(data.message);
} else if (data.payment !== undefined) {
alert(data.payment.join('\n'));
}
});
}); });
$.tablesorter.addParser({ $.tablesorter.addParser({
...@@ -39,14 +61,18 @@ django.jQuery(function () { ...@@ -39,14 +61,18 @@ django.jQuery(function () {
}); });
$.tablesorter.addParser({ $.tablesorter.addParser({
id: "radio", id: 'payment',
is: function(s) { is: function(s) {
return false; return false;
}, },
format: function(s, t, node) { format: function(s, t, node) {
return $(node).children("input[type=radio]").is(':checked') ? 1 : 0; var val = $(node).find('select').val();
if (val === 'no_payment') {
return '';
}
return $(node).find('select').val();
}, },
type: "numeric" type: 'text'
}); });
$.tablesorter.addParser({ $.tablesorter.addParser({
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
<thead> <thead>
<tr> <tr>
<th scope="col">{% trans "name"|capfirst %} <span class="toggle"></span></th> <th scope="col">{% trans "name"|capfirst %} <span class="toggle"></span></th>
<th scope="col"></th>
<th scope="col" class="sorter-payment">{% trans 'payment'|capfirst %} <span class="toggle"></span></th>
<th scope="col" class="sorter-checkbox">{% trans "present"|capfirst %} <span class="toggle"></span></th>
<th scope="col" class="sorter-date">{% trans "registration date"|capfirst %} <span class="toggle"></span></th> <th scope="col" class="sorter-date">{% trans "registration date"|capfirst %} <span class="toggle"></span></th>
{% if cancellations %} {% if cancellations %}
<th scope="col" class="sorter-date">{% trans "cancellation date"|capfirst %} <span class="toggle"></span></th> <th scope="col" class="sorter-date">{% trans "cancellation date"|capfirst %} <span class="toggle"></span></th>
...@@ -11,10 +14,6 @@ ...@@ -11,10 +14,6 @@
{% for field in fields %} {% for field in fields %}
<th scope="col">{{ field.name }} <span class="toggle"></span></th> <th scope="col">{{ field.name }} <span class="toggle"></span></th>
{% endfor %} {% endfor %}
<th class="sorter-checkbox">{% trans "present"|capfirst %} <span class="toggle"></span></th>
<th class="sorter-radio">{% trans "not paid"|capfirst %} <span class="toggle"></span></th>
<th class="sorter-radio">{% trans "paid cash"|capfirst %} <span class="toggle"></span></th>
<th class="sorter-radio">{% trans "paid card"|capfirst %} <span class="toggle"></span></th>
{% if registrations|length > 0 and registrations.0.date_cancelled is not None %} {% if registrations|length > 0 and registrations.0.date_cancelled is not None %}
<th>{% trans "late"|capfirst %}</th> <th>{% trans "late"|capfirst %}</th>
{% endif %} {% endif %}
...@@ -31,12 +30,30 @@ ...@@ -31,12 +30,30 @@
{% if registration.member %} {% if registration.member %}
<td> <td>
<a href="{{ registration.member.get_absolute_url }}">{{ registration.member.get_full_name }}</a> <a href="{{ registration.member.get_absolute_url }}">{{ registration.member.get_full_name }}</a>
</td>
<td>
<a href="tel:{{ registration.member.profile.phone_number }}" title="{{ registration.member.profile.phone_number }}" class="member-phone"></a> <a href="tel:{{ registration.member.profile.phone_number }}" title="{{ registration.member.profile.phone_number }}" class="member-phone"></a>
<a href="mailto:{{ registration.member.email }}" title="{{ registration.member.email }}" class="member-email"></a> <a href="mailto:{{ registration.member.email }}" title="{{ registration.member.email }}" class="member-email"></a>
</td> </td>
{% else %} {% else %}
<td>{{ registration.name }}</td> <td>{{ registration.name }}</td>
<td></td>
{% endif %} {% endif %}
<td>
<select name="payment"
data-none="{{ payment.NONE }}"
{% if registration.payment and registration.payment.type != payment.NONE %}class="paid"{% endif %}>
<option value="{{ payment.NONE }}"
{% if not registration.payment or registration.payment.type == payment.NONE %}selected{% endif %}>{% trans 'None' %}</option>
<option value="{{ payment.CARD }}"
{% if registration.payment and registration.payment.type == payment.CARD %}selected{% endif %}>{% trans 'Card' %}</option>
<option value="{{ payment.CASH }}"
{% if registration.payment and registration.payment.type == payment.CASH %}selected{% endif %}>{% trans 'Cash' %}</option>
<option value="{{ payment.WIRE }}"
{% if registration.payment and registration.payment.type == payment.WIRE %}selected{% endif %}>{% trans 'Wire' %}</option>
</select>
</td>
<td class="text-center"><input type="checkbox" {{ registration.present|yesno:'checked="checked",' }} data-id="{{ registration.id }}" class="present-check" /></td>
<td data-sortval="{{ registration.date|date:'c' }}">{{ registration.date }}</td> <td data-sortval="{{ registration.date|date:'c' }}">{{ registration.date }}</td>
{% if cancellations %} {% if cancellations %}
<td data-sortval="{{ registration.date_cancelled|date:'c' }}">{{ registration.date_cancelled }}</td> <td data-sortval="{{ registration.date_cancelled|date:'c' }}">{{ registration.date_cancelled }}</td>
...@@ -50,12 +67,6 @@ ...@@ -50,12 +67,6 @@
<td>{{ field.value }}</td> <td>{{ field.value }}</td>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<td><input type="checkbox" {{ registration.present|yesno:'checked="checked",' }} data-id="{{ registration.id }}" class="present-check" /></td>
<td><input type="radio" name="payment-{{ registration.id }}" {% if not registration.payment %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_NONE }}" data-id="{{ registration.id }}" class="payment-radio" /></td>
<td><input type="radio" name="payment-{{ registration.id }}" {% if registration.payment and registration.payment.type == registration.PAYMENT_CASH %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_CASH }}" data-id="{{ registration.id }}" class="payment-radio" /></td>
<td><input type="radio" name="payment-{{ registration.id }}" {% if registration.payment and registration.payment.type == registration.PAYMENT_CARD %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_CARD }}" data-id="{{ registration.id }}" class="payment-radio" /></td>
{% if registration.date_cancelled is not None %} {% if registration.date_cancelled is not None %}
<td>{{ registration.is_late_cancellation|yesno }}</td> <td>{{ registration.is_late_cancellation|yesno }}</td>
{% endif %} {% endif %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment