diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index 1c47d79b..3825ff21 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -27,6 +27,9 @@ from rowers.tasks import ( #handle_send_email_transaction_notification, ) +from rowers import credits +from rowers.utils import dologging + import pandas as pd from rowsandall_app.settings import ( @@ -68,6 +71,19 @@ def process_webhook(notification): except TypeError: f.write(timestamp+'\n') if notification.kind == 'subscription_charged_successfully': + subscription = notification.subscription + rs = Rower.objects.filter(subscription_id=subscription.id) + if rs.count() == 0: + dologging('braintreewebhooks.log','Could not find rowers with subscription ID {id}'.format( + id=subscription.id + )) + else: + r = rs[0] + transactions = subscription.transactions + if transactions: + amount = int(transactions[0].amount) + eurocredits = credits.upgrade(amount,r) + eurocredits = credits.upgrade(amount,r) return send_invoice(notification.subscription) if notification.kind == 'subscription_canceled': subscription = notification.subscription @@ -268,6 +284,8 @@ def update_subscription(rower,data,method='up'): if result.is_success: yesterday = (timezone.now()-datetime.timedelta(days=1)).date() rower.paidplan = plan + amount_int = int(float(amount)) + eurocredits = credits.upgrade(amount_int,rower) rower.planexpires = result.subscription.billing_period_end_date rower.teamplanexpires = result.subscription.billing_period_end_date rower.clubsize = plan.clubsize diff --git a/rowers/credits.py b/rowers/credits.py new file mode 100644 index 00000000..e57710c9 --- /dev/null +++ b/rowers/credits.py @@ -0,0 +1,41 @@ + +class InsufficientCreditError(Exception): + """Raised when trying to subtract more than available""" + pass + +def upgrade(amount, rower): + if rower.eurocredits > amount: + return rower.eurocredits + else: + rower.eurocredits = amount + rower.save() + return rower.eurocredits + return rower.eurocredits + +def withdraw(amount, rower): + if rower.eurocredits < amount: + rower.eurocredits = 0 + rower.save() + raise InsufficientCreditError + else: + rower.eurocredits = rower.eurocredits - amount + rower.save() + return rower.eurocredits + + return rower.eurocredits + +def discount(amount,rower): + if amount < rower.eurocredits: + return amount + else: + return rower.eurocredits + + return 0 + +def discounted(amount,rower): + if amount > rower.eurocredits: + return amount-rower.eurocredits + else: + return 0 + + return 0 diff --git a/rowers/models.py b/rowers/models.py index b371ae92..feddf2cd 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -878,6 +878,8 @@ class Rower(models.Model): null=True,blank=True, default='braintree') + eurocredits = models.IntegerField(default=0) + paidplan = models.ForeignKey(PaidPlan,null=True,default=None,on_delete=models.SET_NULL) planexpires = models.DateField(default=current_day) diff --git a/rowers/templates/buy_trainingplan.html b/rowers/templates/buy_trainingplan.html index 6be1d6bf..45594a78 100644 --- a/rowers/templates/buy_trainingplan.html +++ b/rowers/templates/buy_trainingplan.html @@ -21,6 +21,10 @@

Plan: {{ plan.name }}

Price: {{ plan.price }}€

+ {% if rower.eurocredits > 0 %} +

Your Discount: {{ plan.price|discount:rower }}

+

You will pay: {{ plan.price|discounted:rower }}

+ {% endif %}
  • diff --git a/rowers/templates/confirm_trainingplan.html b/rowers/templates/confirm_trainingplan.html index af325e53..de691f11 100644 --- a/rowers/templates/confirm_trainingplan.html +++ b/rowers/templates/confirm_trainingplan.html @@ -37,7 +37,7 @@ Plan{{ plan.name }} - Total€ {{ plan.price|currency }} + Total€ {{ plan.price|discounted:rower|currency }} @@ -70,7 +70,7 @@

    + value="{{ plan.price|discounted:rower }}" readonly>
    @@ -91,7 +91,7 @@ Policy and agree with the Terms of Service.

    {% csrf_token %} - +
  • diff --git a/rowers/templates/instantplan.html b/rowers/templates/instantplan.html index 5cfbe7fc..313d75d8 100644 --- a/rowers/templates/instantplan.html +++ b/rowers/templates/instantplan.html @@ -32,6 +32,10 @@ {% else %}

    Price: {{ plan.price }}€

    {% endif %} + {% if rower.eurocredits and plan.price > 0 %} +

    Your discount: {{ plan.price|discount:rower }}€

    +

    You pay: {{ plan.price|discounted:rower }}€

    + {% endif %}
  • {% if form %}
  • @@ -47,7 +51,7 @@ If you have set a training target, you can also ask to plan by that target. Select the target from the targets list, and select "plan by target".

    - {% if plan.price == 0 %} + {% if plan.price|discounted:rower == 0 %}
    {% else %} @@ -56,7 +60,7 @@ {{ form.as_table }} {% csrf_token %} - {% if plan.price == 0 %} + {% if plan.price|discounted:rower == 0 %}

    {% else %}

    diff --git a/rowers/templates/instantplans.html b/rowers/templates/instantplans.html index 6176d9b9..2506fc8c 100644 --- a/rowers/templates/instantplans.html +++ b/rowers/templates/instantplans.html @@ -10,11 +10,19 @@
    • +

      On this page, you find trusted training plans that work. If you are looking for a plan, this page is for you. This ever growing list of plans contains a plan for everyone, whether you are a beginning rower or experienced, irrespective of your current fitness level. Click on a plan to read more and see the sessions. If you're on a Self-Coach plan or higher, you can get the plan including even more detailed workout instructions copied straight to your training calendar on this site, with the simple click of a button. +

      + {% if rower.eurocredits %} +

      + Your have discount vouchers for an mount of {{ rower.eurocredits }}€. You + will get discount for up to this amount on any paid plan. +

      + {% endif %}
    • {% for plan in plans %}
    • @@ -34,6 +42,10 @@ {% else %}

      Price: {{ plan.price }}€

      {% endif %} + {% if rower.eurocredits and plan.price > 0 %} +

      Your discount: {{ plan.price|discount:rower }}€

      +

      You pay: {{ plan.price|discounted:rower }}€

      + {% endif %}
    • {% endfor %} diff --git a/rowers/templates/rower_form.html b/rowers/templates/rower_form.html index d83d8a59..68096f27 100644 --- a/rowers/templates/rower_form.html +++ b/rowers/templates/rower_form.html @@ -52,6 +52,12 @@ {% endif %} {{ rower.planexpires }} + {% if rower.eurocredits %} + + Discount voucher for training plans + {{ rower.eurocredits }}€ + + {% endif %} {% endif %} {% csrf_token %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 5b39f2a9..d51f18ac 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -19,6 +19,7 @@ from rowers.plannedsessions import ( race_can_register, race_can_submit,race_rower_status ) +from rowers import credits from rowers import c2stuff from rowers.c2stuff import c2_open from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispromember @@ -153,6 +154,15 @@ def weekbegin(nr): return True return False +@register.filter +def discount(amount,rower): + return credits.discount(amount,rower) + + +@register.filter +def discounted(amount,rower): + return credits.discounted(amount,rower) + @register.filter def weekend(nr): week, day = divmod(nr,7) diff --git a/rowers/tests/test_payments.py b/rowers/tests/test_payments.py index 8531525d..26a8d708 100644 --- a/rowers/tests/test_payments.py +++ b/rowers/tests/test_payments.py @@ -406,8 +406,8 @@ description: "" @patch('rowers.views.braintreestuff.gateway', side_effect=MockBraintreeGateway) @patch('rowers.fakturoid.create_invoice',side_effect=mocked_invoiceid) - @patch('rowers.braintreestuff.myqueue') - def test_purchase_trainingplan_view(self, mocked_gateway,mocked_invoiceid,mocked_myqueue): + @patch('rowers.utils.myqueue') + def test_purchase_trainingplan_view(self, mocked_gateway,mocked_invoiceid, mocked_myqueue): u = UserFactory() r = Rower.objects.create(user=u, birthdate=faker.profile()['birthdate'], diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz deleted file mode 100644 index 0b404dce..00000000 Binary files a/rowers/tests/testdata/testdata.tcx.gz and /dev/null differ diff --git a/rowers/views/paymentviews.py b/rowers/views/paymentviews.py index e32824e6..88848e68 100644 --- a/rowers/views/paymentviews.py +++ b/rowers/views/paymentviews.py @@ -6,6 +6,8 @@ from __future__ import unicode_literals from rowers.views.statements import * from django.core.mail import EmailMessage +from rowers import credits + @csrf_exempt def braintree_webhook_view(request): with open('braintreewebhooks.log','a') as f: @@ -212,6 +214,11 @@ def purchase_checkouts_view(request): return HttpResponseRedirect(reverse('rower_select_instantplan')) amount, success = braintreestuff.make_payment(r,data) + diff = plan.price - int(amount) + + eurocredits = credits.withdraw(diff,r) + + if success: messages.info(request,"Your payment was completed and the sessions are copied to your calendar") @@ -243,7 +250,7 @@ def purchase_checkouts_view(request): p.rowers.add(r) create_sessions_from_json(plansteps,r,startdate,r.user) - + job = myqueue(queuehigh,handle_send_email_instantplan_notification, r.user.username, r.user.email, @@ -251,7 +258,6 @@ def purchase_checkouts_view(request): plan.name, startdate, enddate) - print(job,'noot') url = reverse('plannedsessions_view') timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index a61e27c8..6a4f1f5a 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -9,6 +9,7 @@ import json from taggit.models import Tag import rowers.garmin_stuff as gs +from rowers import credits @login_required @permission_required('plannedsession.view_session',fn=get_session_by_pk,raise_exception=True) @@ -1653,7 +1654,7 @@ def plannedsessions_manage_view(request,userid=0, user=r,date__gte=startdate, date__lte=enddate ).order_by( - "date","startdatetime","id" + "-date","-startdatetime","-id" ) @@ -2575,7 +2576,7 @@ def rower_view_instantplan(request,id='',userid=0): raise Http404("Plan does not exist") plan = InstantPlan.objects.get(uuid=id) - + discountedprice = credits.discounted(plan.price,r) authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN url = settings.WORKOUTS_FIT_URL+"/trainingplan/"+id @@ -2621,10 +2622,24 @@ def rower_view_instantplan(request,id='',userid=0): 'id':id, }) return HttpResponseRedirect(url) + + # check if plan is free or credits are sufficient + if plan.price > 0: + if plan.price > r.eurocredits: + messages.error(request,'You did not have enough credit to purchase this plan') + url = reverse('rower_view_instantplan',kwargs={ + 'id':id, + }) + return HttpResponseRedirect(url) + + form = InstantPlanSelectForm(request.POST,targets=targets) if form.is_valid(): + if plan.price > 0: + eurocredits = credits.withdraw(plan.price,r) + plansteps = response.json() name = form.cleaned_data['name'] try: