Repository: svthalia/concrexit

Issue:
Implement HR changes to discounted membership upgrades/renewal
### What?
The huishoudelijk reglement has changed wrt the time in which (ex-)
members can use the discount when renewing/upgrading their membership to be study-long.

Check the HR precisely for the exact requirements.

### Why?
We need to match the official rules. It has to be released at the latest towards the end of this lecture year, to cover the wave of renewals in august/september and after. 

### How?
I think there's a nice place where the discount is applied either in the RenewalForm or in the Renewal model.



Discussion:

Resolving patch:
diff --git a/website/registrations/forms.py b/website/registrations/forms.py
--- a/website/registrations/forms.py
+++ b/website/registrations/forms.py
@@ -1,3 +1,5 @@
+from datetime import timedelta
+
 from django import forms
 from django.conf import settings
 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
@@ -222,7 +224,7 @@ def clean(self):
             if Membership.objects.filter(
                 user=self.cleaned_data["member"],
                 type=Membership.MEMBER,
-                until__gte=now,
+                until__gte=now - timedelta(days=366),
                 since__lte=now,
             ).exists():
                 # The membership upgrade discount applies if, at the time a Renewal is


Test patch:
diff --git a/website/registrations/tests/test_forms.py b/website/registrations/tests/test_forms.py
--- a/website/registrations/tests/test_forms.py
+++ b/website/registrations/tests/test_forms.py
@@ -177,6 +177,21 @@ def test_price_calculation(self):
                 )
                 renewal.delete()
 
+        with self.subTest("Member, membership upgrade discount (last year)"):
+            with freeze_time("2024-10-20"):
+                # The user had a membership last year, but still should get a discount.
+                self.data["length"] = Entry.MEMBERSHIP_STUDY
+                self.data["membership_type"] = Membership.MEMBER
+                form = forms.RenewalForm(self.data)
+                self.assertTrue(form.is_valid())
+                renewal = form.save()
+                self.assertEqual(
+                    renewal.contribution,
+                    settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_STUDY]
+                    - settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_YEAR],
+                )
+                renewal.delete()
+
         with self.subTest("Member, new year membership before expiry"):
             with freeze_time("2024-08-20"):
                 self.data["length"] = Entry.MEMBERSHIP_YEAR
@@ -203,8 +218,9 @@ def test_price_calculation(self):
                 )
                 renewal.delete()
 
-        with self.subTest("Member, study membership after expiry"):
-            with freeze_time("2024-09-10"):
+        with self.subTest("Member, study membership after more than a year"):
+            with freeze_time("2025-09-10"):
+                # The membership discount applies up to 1 year after the user last had a membership.
                 self.data["length"] = Entry.MEMBERSHIP_STUDY
                 self.data["membership_type"] = Membership.MEMBER
                 form = forms.RenewalForm(self.data)