diff --git a/rowers/forms.py b/rowers/forms.py index effeadf1..b4f03bb9 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -2,7 +2,8 @@ from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from rowers.models import ( Workout,Rower,Team,PlannedSession,GeoCourse, - VirtualRace,VirtualRaceResult,IndoorVirtualRaceResult + VirtualRace,VirtualRaceResult,IndoorVirtualRaceResult, + PaidPlan ) from rowers.rows import validate_file_extension,must_be_csv,validate_image_extension,validate_kml from django.contrib.auth.forms import UserCreationForm @@ -712,7 +713,19 @@ class StatsOptionsForm(forms.Form): for type in mytypes.checktypes: self.fields[type] = forms.BooleanField(initial=True,required=False) +class PlanSelectForm(forms.Form): + plan = forms.ModelChoiceField(queryset=PaidPlan.objects.all(), + widget=forms.RadioSelect,required=True) + def __init__(self, *args, **kwargs): + paymentprocessor = kwargs.pop('paymentprocessor',None) + super(PlanSelectForm, self).__init__(*args, **kwargs) + self.fields['plan'].empty_label = None + if paymentprocessor: + self.fields['plan'].queryset = PaidPlan.objects.filter( + paymentprocessor=paymentprocessor + ).exclude(shortname="basic").order_by("price","clubsize","shortname") + class CourseSelectForm(forms.Form): course = forms.ModelChoiceField(queryset=GeoCourse.objects.all()) diff --git a/rowers/models.py b/rowers/models.py index a6d84e94..50fb9cc1 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -571,14 +571,14 @@ class PaidPlan(models.Model): clubsize = models.IntegerField(default=0) def __unicode__(self): - return '{name} - {shortname} at {price} EURO ({paymenttype} payment) / {paymentprocessor}'.format( + return '{name} - {shortname} at {price} EURO ({paymenttype} payment)'.format( name = self.name, shortname = self.shortname, price = self.price, paymenttype = self.paymenttype, paymentprocessor = self.paymentprocessor, ) - + # Extension of User with rowing specific data class Rower(models.Model): @@ -3002,6 +3002,20 @@ class RowerImportExportForm(ModelForm): 'trainingpeaks_auto_export', ] +# Form to collect rower's Billing Info +class RowerBillingAddressForm(ModelForm): + class Meta: + model = Rower + fields = [ + 'street_address', + 'city', + 'postal_code', + 'country' + ] + + def __init__(self, *args, **kwargs): + super(RowerBillingAddressForm, self).__init__(*args, **kwargs) + self.fields['country'].required = True # Form to set rower's Email and Weight category diff --git a/rowers/payments.py b/rowers/payments.py index da477b0b..ecc5691d 100644 --- a/rowers/payments.py +++ b/rowers/payments.py @@ -27,4 +27,9 @@ def setrowerplans(): else: print 'Could not set plan for ',r +def is_existing_customer(rower): + if rower.country is not None and rower.customer_id is not None and r.country != '': + return True + + return False diff --git a/rowers/templates/billing.html b/rowers/templates/billing.html new file mode 100644 index 00000000..ea725daa --- /dev/null +++ b/rowers/templates/billing.html @@ -0,0 +1,36 @@ +{% extends "newbase.html" %} +{% block title %}Rowsandall Paid Membership{% endblock title %} +{% load rowerfilters %} +{% block main %} + +

Upgrade

+ +
+
    +
  • +

    Fill in Billing Details

    +

    For tax reasons, we need your country of residence

    + + {{ billingaddressform.as_table }} +
    +
  • +
  • +

    Choose your Plan

    + + {{ planselectform.as_table }} +
    +
  • +
  • + {% csrf_token %} + + You will be able to review your order before purchase. +
  • +
+
+ +{% endblock %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/paidplans.html b/rowers/templates/paidplans.html index 0cd4a34f..1c3c30e4 100644 --- a/rowers/templates/paidplans.html +++ b/rowers/templates/paidplans.html @@ -211,14 +211,22 @@ {% elif rower and rower.rowerplan == 'basic' %} {% elif rower and rower.rowerplan == 'pro' %}   {% elif rower and rower.rowerplan == 'plan' %} @@ -226,7 +234,11 @@   {% else %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index c9b81bd3..e2329849 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -24,6 +24,8 @@ from rowers.models import checkaccessuser from rowers.mytypes import otwtypes from rowers.utils import NoTokenError +import rowers.payments as payments + def strfdelta(tdelta): minutes,seconds = divmod(tdelta.seconds,60) tenths = int(tdelta.microseconds/1e5) @@ -59,6 +61,13 @@ def secondstotimestring(tdelta): return res +@register.filter +def existing_customer(user): + if user.is_anonymous(): + return False + else: + return payments.is_existing_customer(user.rower) + @register.filter def aantalcomments(workout): try: diff --git a/rowers/urls.py b/rowers/urls.py index a74d085d..63504a03 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -438,6 +438,7 @@ urlpatterns = [ url(r'^analysis/$', views.analysis_view,name='analysis'), url(r'^laboratory/$', views.laboratory_view,name='laboratory'), url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'), + url(r'^billing',views.billing_view,name='billing'), url(r'^paidplans',views.paidplans_view,name='paidplans'), url(r'^checkouts',views.checkouts_view,name='checkouts'), url(r'^payments',views.payments_view,name='payments'), diff --git a/rowers/views.py b/rowers/views.py index 069773d8..5aba8d49 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -28,7 +28,7 @@ import isodate import re import cgi from icalendar import Calendar, Event -import braintree +import rowers.braintreestuff as braintreestuff from django.shortcuts import render from django.template.loader import render_to_string @@ -51,7 +51,7 @@ from rowers.forms import ( RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm, FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm, MetricsForm,DisqualificationForm,disqualificationreasons, - disqualifiers,SearchForm,BillingForm + disqualifiers,SearchForm,BillingForm,PlanSelectForm ) from django.core.urlresolvers import reverse, reverse_lazy @@ -84,7 +84,7 @@ from rowers.models import ( createmicrofillers, createmesofillers, microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates, TrainingMesoCycleForm, TrainingMicroCycleForm, - RaceLogo, + RaceLogo,RowerBillingAddressForm, ) from rowers.models import ( RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm, @@ -1034,12 +1034,41 @@ def paidplans_view(request): r = getrequestrower(request) else: r = None - + + return render(request, 'paidplans.html', {'rower':r}) +@login_required() +def billing_view(request): + r = getrequestrower(request) + + if request.method == 'POST': + billingaddressform = RowerBillingAddressForm(request.POST) + planselectform = PlanSelectForm(request.POST,paymentprocessor='braintree') + if billingaddressform.is_valid(): + cd = billingaddressform.cleaned_data + for attr, value in cd.items(): + setattr(r, attr, value) + r.save() + + if planselectform.is_valid(): + plan = planselectform.cleaned_data['plan'] + else: + billingaddressform = RowerBillingAddressForm(instance=r) + planselectform = PlanSelectForm(paymentprocessor='braintree') + + return render(request, + 'billing.html', + {'rower':r, + 'billingaddressform':billingaddressform, + 'planselectform':planselectform, + }) + + + # Experimental - Payments @login_required() def payments_view(request):