Commit 75501213 authored by Luko van der Maas's avatar Luko van der Maas
Browse files

Merge branch 'tc/event-payments-connection' into 'master'

Events payments refactor

See merge request !1112
parents 23b79eb4 fc5079f0
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
from django.contrib import admin from django.contrib import admin
from django.core.exceptions import DisallowedRedirect, PermissionDenied from django.core.exceptions import DisallowedRedirect, PermissionDenied
from django.db.models import Max, Min from django.db.models import Max, Min
from django.forms import Field
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.template.defaultfilters import date as _date from django.template.defaultfilters import date as _date
from django.urls import reverse, path from django.urls import reverse, path
...@@ -15,6 +16,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -15,6 +16,7 @@ from django.utils.translation import ugettext_lazy as _
from activemembers.models import MemberGroup from activemembers.models import MemberGroup
from events import services from events import services
from members.models import Member from members.models import Member
from payments.widgets import PaymentWidget
from pizzas.models import PizzaEvent from pizzas.models import PizzaEvent
from utils.snippets import datetime_to_lectureyear from utils.snippets import datetime_to_lectureyear
from utils.translation import TranslatedModelAdmin from utils.translation import TranslatedModelAdmin
...@@ -301,6 +303,10 @@ class RegistrationAdmin(DoNextModelAdmin): ...@@ -301,6 +303,10 @@ class RegistrationAdmin(DoNextModelAdmin):
field.widget.can_add_related = False field.widget.can_add_related = False
field.widget.can_change_related = False field.widget.can_change_related = False
field.widget.can_delete_related = False field.widget.can_delete_related = False
elif db_field.name == 'payment':
return Field(widget=PaymentWidget,
initial=field.initial,
required=False)
return field return field
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):
......
...@@ -7,6 +7,7 @@ from html import unescape ...@@ -7,6 +7,7 @@ from html import unescape
from rest_framework import serializers from rest_framework import serializers
from rest_framework.fields import empty from rest_framework.fields import empty
from payments.models import Payment
from thaliawebsite.api.services import create_image_thumbnail_dict from thaliawebsite.api.services import create_image_thumbnail_dict
from events import services from events import services
from events.exceptions import RegistrationError from events.exceptions import RegistrationError
...@@ -225,6 +226,14 @@ class RegistrationListSerializer(serializers.ModelSerializer): ...@@ -225,6 +226,14 @@ class RegistrationListSerializer(serializers.ModelSerializer):
size_large='800x800') size_large='800x800')
class PaymentTypeField(serializers.ChoiceField):
def get_attribute(self, instance):
if not instance.payment:
return Payment.NONE
return super().get_attribute(instance)
class RegistrationAdminListSerializer(RegistrationListSerializer): class RegistrationAdminListSerializer(RegistrationListSerializer):
"""Custom registration admin list serializer""" """Custom registration admin list serializer"""
class Meta: class Meta:
...@@ -238,6 +247,8 @@ class RegistrationAdminListSerializer(RegistrationListSerializer): ...@@ -238,6 +247,8 @@ class RegistrationAdminListSerializer(RegistrationListSerializer):
is_late_cancellation = serializers.SerializerMethodField( is_late_cancellation = serializers.SerializerMethodField(
'_is_late_cancellation') '_is_late_cancellation')
queue_position = serializers.SerializerMethodField('_queue_position') queue_position = serializers.SerializerMethodField('_queue_position')
payment = PaymentTypeField(source='payment.type',
choices=Payment.PAYMENT_TYPE)
def _is_late_cancellation(self, instance): def _is_late_cancellation(self, instance):
return instance.is_late_cancellation() return instance.is_late_cancellation()
...@@ -270,6 +281,8 @@ class RegistrationSerializer(serializers.ModelSerializer): ...@@ -270,6 +281,8 @@ class RegistrationSerializer(serializers.ModelSerializer):
photo = serializers.SerializerMethodField('_photo') photo = serializers.SerializerMethodField('_photo')
avatar = serializers.SerializerMethodField('_avatar') avatar = serializers.SerializerMethodField('_avatar')
member = serializers.SerializerMethodField('_member') member = serializers.SerializerMethodField('_member')
payment = PaymentTypeField(source='payment.type',
choices=Payment.PAYMENT_TYPE)
registered_on = serializers.DateTimeField(source='date', read_only=True) registered_on = serializers.DateTimeField(source='date', read_only=True)
is_cancelled = serializers.SerializerMethodField('_is_cancelled') is_cancelled = serializers.SerializerMethodField('_is_cancelled')
is_late_cancellation = serializers.SerializerMethodField( is_late_cancellation = serializers.SerializerMethodField(
......
# Generated by Django 2.1.3 on 2018-11-26 19:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('payments', '0002_auto_20181127_1819'),
('events', '0032_event_documents'),
]
operations = [
migrations.RenameField(
model_name='registration',
old_name='payment',
new_name='payment_str',
),
migrations.AddField(
model_name='registration',
name='payment',
field=models.OneToOneField(null=True, blank=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='events_registration',
to='payments.Payment'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-07-21 19:18
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('events', '0033_registration_payment_obj'),
]
def forwards_func(apps, schema_editor):
Registration = apps.get_model('events', 'registration')
Payment = apps.get_model('payments', 'payment')
db_alias = schema_editor.connection.alias
for reg in Registration.objects.using(db_alias).all():
if reg.event.price > 0 and reg.payment_str != 'no_payment':
reg.payment = Payment.objects.create(
created_at=reg.date,
type=reg.payment_str,
amount=reg.event.price,
paid_by=reg.member,
processing_date=reg.event.start,
notes=f'Event registration {reg.event}. '
f'Registration: {reg}'
)
reg.save()
def reverse_func(apps, schema_editor):
Registration = apps.get_model('events', 'registration')
db_alias = schema_editor.connection.alias
for reg in Registration.objects.using(db_alias).all():
if reg.payment:
reg.payment.delete()
operations = [
migrations.RunPython(forwards_func, reverse_func),
]
# Generated by Django 2.1.3 on 2018-11-30 14:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('events', '0034_registration_payment_obj'),
]
operations = [
migrations.RemoveField(
model_name='registration',
name='payment_str',
),
]
...@@ -12,6 +12,7 @@ from django.utils.text import format_lazy ...@@ -12,6 +12,7 @@ from django.utils.text import format_lazy
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
...@@ -424,14 +425,9 @@ def registration_member_choices_limit(): ...@@ -424,14 +425,9 @@ 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_CARD = 'card_payment' PAYMENT_NONE = Payment.NONE
PAYMENT_CASH = 'cash_payment' PAYMENT_CARD = Payment.CARD
PAYMENT_NONE = 'no_payment' PAYMENT_CASH = Payment.CASH
PAYMENT_TYPES = (
(PAYMENT_NONE, _('No payment')),
(PAYMENT_CASH, _('Paid with cash')),
(PAYMENT_CARD, _('Paid with card')))
event = models.ForeignKey(Event, models.CASCADE) event = models.ForeignKey(Event, models.CASCADE)
...@@ -461,11 +457,12 @@ class Registration(models.Model): ...@@ -461,11 +457,12 @@ class Registration(models.Model):
default=False, default=False,
) )
payment = models.CharField( payment = models.OneToOneField(
choices=PAYMENT_TYPES, 'payments.Payment',
default='no_payment', related_name='events_registration',
verbose_name=_('payment'), on_delete=models.PROTECT,
max_length=20, blank=True,
null=True,
) )
@property @property
...@@ -514,8 +511,7 @@ class Registration(models.Model): ...@@ -514,8 +511,7 @@ class Registration(models.Model):
).count() < self.event.max_participants)) ).count() < self.event.max_participants))
def is_paid(self): def is_paid(self):
return self.payment in [Registration.PAYMENT_CARD, return self.payment and self.payment.processed
Registration.PAYMENT_CASH]
def would_cancel_after_deadline(self): def would_cancel_after_deadline(self):
now = timezone.now() now = timezone.now()
......
...@@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _, get_language ...@@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _, get_language
from events import emails from events import emails
from events.exceptions import RegistrationError from events.exceptions import RegistrationError
from events.models import Registration, RegistrationInformationField from events.models import Registration, RegistrationInformationField
from payments.models import Payment
def is_user_registered(member, event): def is_user_registered(member, event):
...@@ -241,7 +242,26 @@ def update_registration_by_organiser(registration, member, data): ...@@ -241,7 +242,26 @@ def update_registration_by_organiser(registration, member, data):
_("You are not allowed to update this registration.")) _("You are not allowed to update this registration."))
if 'payment' in data: if 'payment' in data:
registration.payment = data['payment'] if (data['payment']['type'] == Payment.NONE
and registration.payment is not None):
p = registration.payment
registration.payment = None
registration.save()
p.delete()
elif (data['payment']['type'] != Payment.NONE
and registration.payment is not None):
registration.payment.type = data['payment']['type']
registration.payment.save()
elif (data['payment']['type'] != Payment.NONE
and registration.payment is None):
registration.payment = Payment.objects.create(
amount=registration.event.price,
paid_by=registration.member,
notes=(f'Event registration {registration.event.title_en}. '
f'Registration date: {registration.date}'),
processed_by=member,
type=data['payment']['type']
)
if 'present' in data: if 'present' in data:
registration.present = data['present'] registration.present = data['present']
......
...@@ -50,10 +50,12 @@ ...@@ -50,10 +50,12 @@
<td>{{ field.value }}</td> <td>{{ field.value }}</td>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<td><input type="checkbox" {{ registration.present|yesno:'checked="checked",' }} class="present-check" /></td> <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 registration.payment == registration.PAYMENT_NONE %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_NONE }}" class="payment-radio" /></td>
<td><input type="radio" name="payment-{{ registration.id }}" {% if registration.payment == registration.PAYMENT_CASH %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_CASH }}" class="payment-radio" /></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 == registration.PAYMENT_CARD %}checked="checked"{% endif %} data-value="{{ registration.PAYMENT_CARD }}" 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 %}
......
...@@ -316,7 +316,7 @@ class RegistrationApiTest(TestCase): ...@@ -316,7 +316,7 @@ class RegistrationApiTest(TestCase):
self.assertEqual(field2.get_value_for(registration), None) self.assertEqual(field2.get_value_for(registration), None)
self.assertEqual(field3.get_value_for(registration), None) self.assertEqual(field3.get_value_for(registration), None)
response = self.client.put( response = self.client.patch(
'/api/v1/registrations/{}/'.format(registration.pk), { '/api/v1/registrations/{}/'.format(registration.pk), {
'fields[info_field_1]': True, 'fields[info_field_1]': True,
'fields[info_field_2]': 1337, 'fields[info_field_2]': 1337,
......
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