Repository: carpentries/amy

Issue:
Event's Admin fee and Attendance can't be negative numbers
We should enforce that the "Admin fee" and "Attendance" are non-negative.



Discussion:

Resolving patch:
diff --git a/workshops/migrations/0030_auto_20150720_0311.py b/workshops/migrations/0030_auto_20150720_0311.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0030_auto_20150720_0311.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0029_auto_20150720_0258'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='event',
+            name='attendance',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+    ]
diff --git a/workshops/migrations/0031_auto_20150720_0344.py b/workshops/migrations/0031_auto_20150720_0344.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0031_auto_20150720_0344.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.core.validators
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0030_auto_20150720_0311'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='event',
+            name='admin_fee',
+            field=models.DecimalField(validators=[django.core.validators.MinValueValidator(0)], null=True, decimal_places=2, blank=True, max_digits=6),
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -5,6 +5,7 @@
     AbstractBaseUser, BaseUserManager, PermissionsMixin)
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.urlresolvers import reverse
+from django.core.validators import MinValueValidator
 from django.db import models
 from django.db.models import Q
 
@@ -297,8 +298,8 @@ class Event(models.Model):
     url        = models.CharField(max_length=STR_LONG, unique=True, null=True, blank=True,
                                   help_text='Setting this and startdate "publishes" the event.')
     reg_key    = models.CharField(max_length=STR_REG_KEY, null=True, blank=True, verbose_name="Eventbrite key")
-    attendance = models.IntegerField(null=True, blank=True)
-    admin_fee  = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
+    attendance = models.PositiveIntegerField(null=True, blank=True)
+    admin_fee  = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True, validators=[MinValueValidator(0)])
     invoiced   = models.NullBooleanField(default=False, blank=True)
     notes      = models.TextField(default="", blank=True)
 


Test patch:
diff --git a/workshops/test/test_event.py b/workshops/test/test_event.py
--- a/workshops/test/test_event.py
+++ b/workshops/test/test_event.py
@@ -361,6 +361,67 @@ def test_start_date_gte_end_date(self):
         response = self.client.post(reverse('event_add'), data)
         assert response.status_code == 302
 
+    def test_negative_admin_fee_attendance(self):
+        """Ensure we disallow negative admin fee or attendance.
+
+        This is a regression test for
+        https://github.com/swcarpentry/amy/issues/435."""
+        data = {
+            'site': self.test_site.id,
+            'tags': [self.test_tag.id],
+            'slug': 'test-event',
+            'admin_fee': -1200,
+        }
+        S = "Ensure this value is greater than or equal to 0"
+        response = self.client.post(reverse('event_add'), data)
+        assert response.status_code == 200
+        content = response.content.decode('utf-8')
+        assert content.count(S) == 1
+
+        data = {
+            'site': self.test_site.id,
+            'tags': [self.test_tag.id],
+            'slug': 'test-event',
+            'admin_fee': -1200,
+            'attendance': -36,
+        }
+        response = self.client.post(reverse('event_add'), data)
+        assert response.status_code == 200
+        content = response.content.decode('utf-8')
+        assert content.count(S) == 2
+
+        data = {
+            'site': self.test_site.id,
+            'tags': [self.test_tag.id],
+            'slug': 'test-event',
+            'attendance': -36,
+        }
+        S = "Ensure this value is greater than or equal to 0"
+        response = self.client.post(reverse('event_add'), data)
+        assert response.status_code == 200
+        content = response.content.decode('utf-8')
+        assert content.count(S) == 1
+
+        data = {
+            'site': self.test_site.id,
+            'tags': [self.test_tag.id],
+            'slug': 'test-event',
+            'admin_fee': 0,
+            'attendance': 0,
+        }
+        response = self.client.post(reverse('event_add'), data)
+        assert response.status_code == 302
+
+        data = {
+            'site': self.test_site.id,
+            'tags': [self.test_tag.id],
+            'slug': 'test-event2',
+            'admin_fee': 1200,
+            'attendance': 36,
+        }
+        response = self.client.post(reverse('event_add'), data)
+        assert response.status_code == 302
+
 
 class TestEventNotes(TestBase):
     """Make sure notes once written are saved forever!"""