Repository: svthalia/concrexit

Issue:
Membership 'upgrade' to 'until graduation' is always 30 euros
### Describe the bug
Membership 'upgrade' to 'until graduation' is always 30 euros.

### How to reproduce
Steps to reproduce the behaviour:
1. Make sure you have a membership for the current year
2. Open the renewal page: http://localhost:8000/user/membership/
3. Select the member renewal with 'until graduation'
4. Submit
5. Open the admin and note that the contribution is 30 euros, it should give a discount since you already paid for this year

### Expected behaviour
The amount should include the discount and be 22,50.

### Screenshots
<img width="411" alt="Screenshot 2021-06-20 at 19 05 57" src="https://user-images.githubusercontent.com/1799914/122682589-b1cd8f00-d1fa-11eb-9088-82d71117bbd8.png">
<img width="538" alt="Screenshot 2021-06-20 at 19 06 03" src="https://user-images.githubusercontent.com/1799914/122682592-b2662580-d1fa-11eb-8bb6-e0bd2a768b15.png">




Discussion:

Resolving patch:
diff --git a/website/registrations/models.py b/website/registrations/models.py
--- a/website/registrations/models.py
+++ b/website/registrations/models.py
@@ -4,7 +4,7 @@
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core import validators
-from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.core.validators import MinValueValidator, RegexValidator
 from django.db import models
 from django.template.defaultfilters import floatformat
@@ -102,6 +102,23 @@ class Entry(models.Model):
         "members.Membership", on_delete=models.SET_NULL, blank=True, null=True,
     )
 
+    @property
+    def membership_upgrade_discount_applies(self):
+        if isinstance(self, Renewal):
+            member = self.member
+        else:
+            try:
+                member = self.renewal.member
+            except ObjectDoesNotExist:
+                return False
+
+        return (
+            self.length == Entry.MEMBERSHIP_STUDY
+            and member.current_membership is not None
+            and member.current_membership.until is not None
+            and member.current_membership.type == Membership.MEMBER
+        )
+
     def save(
         self, force_insert=False, force_update=False, using=None, update_fields=None
     ):
@@ -111,7 +128,13 @@ def save(
         if self.membership_type == Membership.BENEFACTOR:
             self.length = self.MEMBERSHIP_YEAR
         else:
-            self.contribution = settings.MEMBERSHIP_PRICES[self.length]
+            if self.membership_upgrade_discount_applies:
+                self.contribution = (
+                    settings.MEMBERSHIP_PRICES["study"]
+                    - settings.MEMBERSHIP_PRICES["year"]
+                )
+            else:
+                self.contribution = settings.MEMBERSHIP_PRICES[self.length]
 
         super().save(force_insert, force_update, using, update_fields)
 
diff --git a/website/registrations/services.py b/website/registrations/services.py
--- a/website/registrations/services.py
+++ b/website/registrations/services.py
@@ -153,15 +153,12 @@ def accept_entries(user_id: int, queryset: QuerySet) -> int:
                 entry.registration.username = _generate_username(entry.registration)
                 entry.registration.save()
 
-            entry.save()
-
             if entry.registration.direct_debit:
                 member = _create_member_from_registration(entry.registration)
                 membership = _create_membership_from_entry(entry.registration, member)
                 entry.membership = membership
                 entry.status = Entry.STATUS_COMPLETED
                 entry.payment = create_payment(entry, member, Payment.TPAY)
-                entry.save()
             else:
                 emails.send_registration_accepted_message(entry.registration)
 


Test patch:
diff --git a/website/registrations/tests/test_models.py b/website/registrations/tests/test_models.py
--- a/website/registrations/tests/test_models.py
+++ b/website/registrations/tests/test_models.py
@@ -409,6 +409,20 @@ def test_has_active_membership(self):
                 },
             )
 
+    def test_discount_membership_upgrade(self):
+        membership = self.member.current_membership
+        membership.until = timezone.now().date() + timezone.timedelta(days=3)
+        membership.save()
+
+        self.renewal.save()
+        self.renewal.refresh_from_db()
+
+        self.assertEqual(
+            self.renewal.contribution,
+            settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_STUDY]
+            - settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_YEAR],
+        )
+
 
 @override_settings(SUSPEND_SIGNALS=True)
 class ReferenceTest(TestCase):