Repository: dotkom/onlineweb4

Issue:
Attaching a "payment" to a non-existing event fails with ambiguous result
By attaching a payment to a non-existing attendance event triggers an exception and returns the user to the root page without any warnings or errors.

Because of the GenericForeignKey approach, I guess multiple of these cases are possible.



Discussion:

Resolving patch:
diff --git a/apps/payment/migrations/0021_auto_20180312_1713.py b/apps/payment/migrations/0021_auto_20180312_1713.py
new file mode 100644
--- /dev/null
+++ b/apps/payment/migrations/0021_auto_20180312_1713.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.11 on 2018-03-12 16:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('payment', '0020_auto_20180221_1916'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='paymentprice',
+            name='description',
+            field=models.CharField(default='No description', max_length=128),
+            preserve_default=False,
+        ),
+    ]
diff --git a/apps/payment/models.py b/apps/payment/models.py
--- a/apps/payment/models.py
+++ b/apps/payment/models.py
@@ -1,10 +1,12 @@
 # -*- coding: utf-8 -*-
 
+import logging
 import uuid
 
 from django.conf import settings
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ValidationError
 from django.core.mail import send_mail
 from django.db import models
 from django.template.loader import render_to_string
@@ -99,6 +101,10 @@ def create_payment_delay(self, user, deadline):
     def description(self):
         if self._is_type(AttendanceEvent):
             return self.content_object.event.title
+        logging.getLogger(__name__).error(
+            "Trying to get description for payment #{}, but it's not AttendanceEvent.".format(self.pk)
+        )
+        return 'No description'
 
     def get_receipt_description(self):
         receipt_description = ""
@@ -198,6 +204,19 @@ def _is_type(self, model_type):
     def __str__(self):
         return self.description()
 
+    def clean_generic_relation(self):
+        if not self._is_type(AttendanceEvent):
+            raise ValidationError({
+                'content_type': _('Denne typen objekter støttes ikke av betalingssystemet for tiden.'),
+            })
+        else:
+            if not self.content_object or not self.content_object.event.is_attendance_event():
+                raise ValidationError(_('Dette arrangementet har ikke påmelding.'))
+
+    def clean(self):
+        super().clean()
+        self.clean_generic_relation()
+
     class Meta(object):
         unique_together = ('content_type', 'object_id')
 
@@ -210,12 +229,10 @@ class PaymentPrice(models.Model):
     """Payment object"""
     price = models.IntegerField(_("pris"))
     """Price in NOK"""
-    description = models.CharField(max_length=128, null=True, blank=True)
+    description = models.CharField(max_length=128)
     """Description of price"""
 
     def __str__(self):
-        if not self.description:
-            return str(self.price) + "kr"
         return self.description + " (" + str(self.price) + "kr)"
 
     class Meta(object):


Test patch: