diff --git a/config/urls.py b/config/urls.py index 9d3b64f..3eafd7c 100644 --- a/config/urls.py +++ b/config/urls.py @@ -35,6 +35,7 @@ urlpatterns = [ url(r'^latest/$', LatestEntriesFeed()), url(r'membership/join/$', subscription.MembershipJoinView.as_view(), name='join_hackspace'), + url(r'membership/cancel/$', subscription.MembershipCancelView.as_view(), name='cancel_membership'), url(r'membership/(?P[\w\-]+)/success$', subscription.MembershipJoinSuccessView.as_view(), name='join_hackspace_success'), url(r'membership/(?P\w{0,50})/failure$', subscription.MembershipJoinFailureView.as_view(), name='join_hackspace_failure'), url(r'^admin/password_reset/$', auth_views.password_reset, name='admin_password_reset'), diff --git a/mhackspace/static/sass/components/_membership.scss b/mhackspace/static/sass/components/_membership.scss index be935e9..5708a5f 100644 --- a/mhackspace/static/sass/components/_membership.scss +++ b/mhackspace/static/sass/components/_membership.scss @@ -9,4 +9,12 @@ margin: auto; text-shadow: 1px 1px #FFF; position: relative; + .middle { + font-size: 2rem; + text-align: center; + margin: 50px; + } + .date { + padding:14px; + } } diff --git a/mhackspace/subscriptions/payments.py b/mhackspace/subscriptions/payments.py index a92bc78..ed1f24a 100644 --- a/mhackspace/subscriptions/payments.py +++ b/mhackspace/subscriptions/payments.py @@ -101,9 +101,14 @@ class gocardless_provider: def get_token(self): return 'N/A' - def cancel_subscribe(self, reference): - subscription = gocardless.client.subscription(reference) - response = subscription.cancel() + def cancel_subscription(self, reference): + try: + subscription = gocardless.client.subscription(reference) + response = subscription.cancel() + except gocardless.exceptions.ClientError: + return { + 'success': False + } return { 'amount': subscription.amount, 'start_date': subscription.created_at, diff --git a/mhackspace/subscriptions/views.py b/mhackspace/subscriptions/views.py index ac33132..19638f1 100644 --- a/mhackspace/subscriptions/views.py +++ b/mhackspace/subscriptions/views.py @@ -9,11 +9,42 @@ from django.contrib import messages from django.contrib.auth.models import Group from django.contrib.auth.mixins import LoginRequiredMixin -from mhackspace.users.models import User +from mhackspace.users.models import User, Membership +from mhackspace.users.models import MEMBERSHIP_CANCELLED from mhackspace.users.forms import MembershipJoinForm from mhackspace.subscriptions.payments import select_provider +class MembershipCancelView(LoginRequiredMixin, RedirectView): + permanent = False + pattern_name = 'users:detail' + + def get_redirect_url(self, *args, **kwargs): + payment_provider = 'gocardless' + provider = select_provider(payment_provider) + + member = Membership.objects.filter(user=self.request.user).first() + + result = provider.cancel_subscription( + reference=member.reference + ) + if result.get('success') is True: + # set membership to cancelled on success + member.status = MEMBERSHIP_CANCELLED + member.save() + + # remove user from group on success + group = Group.objects.get(name='members') + self.request.user.groups.remove(group) + messages.add_message( + self.request, + messages.SUCCESS, + 'Your membership has now been cancelled') + + kwargs['username'] = self.request.user.get_username() + return super(MembershipCancelView, self).get_redirect_url(*args, **kwargs) + + class MembershipJoinView(LoginRequiredMixin, UpdateView): model = User fields = [] diff --git a/mhackspace/templates/users/user_detail.html b/mhackspace/templates/users/user_detail.html index 34d728d..a39ff54 100644 --- a/mhackspace/templates/users/user_detail.html +++ b/mhackspace/templates/users/user_detail.html @@ -26,24 +26,25 @@
{% if membership.get_status %} -
-
Joined {{membership.date}}
-
-
-

MHS{{ user.id|stringformat:"05d" }}

{{user.name}}{{user.last_name}}

- Cancel Membership +
+
Joined {{membership.date}}
+
+
+

MHS{{ user.id|stringformat:"05d" }}

{{user.name}}{{user.last_name}}

+
+ Cancel Membership
-
+ {% else %} +
+

Sign up below

+
+ {% csrf_token %} + {{ membership_form|crispy }} + +
+
{% endif %} -
-

Sign up below

-
- {% csrf_token %} - {{ membership_form|crispy }} - -
-
diff --git a/mhackspace/users/admin.py b/mhackspace/users/admin.py index 2c0a1cf..5ec411f 100644 --- a/mhackspace/users/admin.py +++ b/mhackspace/users/admin.py @@ -1,20 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals +from django.contrib.admin import ModelAdmin from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin as AuthUserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm -from .models import User +from .models import User, Membership, MEMBERSHIP_STATUS_CHOICES class MyUserChangeForm(UserChangeForm): class Meta(UserChangeForm.Meta): model = User - class MyUserCreationForm(UserCreationForm): - error_message = UserCreationForm.error_messages.update({ 'duplicate_username': 'This username has already been taken.' }) @@ -40,3 +39,8 @@ class MyUserAdmin(AuthUserAdmin): ) + AuthUserAdmin.fieldsets list_display = ('username', 'name', 'is_superuser') search_fields = ['name'] + +@admin.register(Membership) +class MembershipAdmin(ModelAdmin): + list_display = ('user', 'payment', 'date', 'status') + list_filter = ('status',) diff --git a/mhackspace/users/models.py b/mhackspace/users/models.py index 71e8255..ccf0afc 100644 --- a/mhackspace/users/models.py +++ b/mhackspace/users/models.py @@ -29,10 +29,18 @@ class User(AbstractUser): class Blurb(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='+') + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='+') skills = models.CharField(max_length=255) description = models.TextField() +MEMBERSHIP_CANCELLED = 0 + +MEMBERSHIP_STATUS_CHOICES = ( + (0, 'Guest user'), + (1, 'Active membership'), + (3, 'Membership Expired'), + (4, 'Membership Cancelled') +) MEMBERSHIP_STRING = { 0: 'Guest user', @@ -55,7 +63,7 @@ class Membership(models.Model): payment = models.DecimalField(max_digits=6, decimal_places=2, default=0.0) date = models.DateTimeField() reference = models.CharField(max_length=255) - status = models.PositiveSmallIntegerField(default=0) + status = models.PositiveSmallIntegerField(default=0, choices=MEMBERSHIP_STATUS_CHOICES) email = models.CharField(max_length=255) @property