Fixed membership payment view issues, Membership creation after a stripe charge, Membership order creation after stripe charge, Added membership activated view, Added membership activated html, Fixing membership cost function, Added function to format membership date ranges, Added membership calculated dates for first month into membership_payment html and membership_activated html

This commit is contained in:
Levi 2016-08-22 02:52:29 -05:00
parent fbaf439ebc
commit 1152e41b6e
6 changed files with 212 additions and 29 deletions

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-08-20 19:59
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('utils', '0002_billingaddress'),
('membership', '0006_auto_20160526_0445'),
('digitalglarus', '0007_auto_20160820_0408'),
]
operations = [
migrations.AddField(
model_name='membershiporder',
name='billing_address',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='utils.BillingAddress'),
preserve_default=False,
),
migrations.AddField(
model_name='membershiporder',
name='customer',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer'),
preserve_default=False,
),
]

View file

@ -1,7 +1,14 @@
import calendar
from datetime import datetime, date, timedelta
from django.db import models
from cms.models import CMSPlugin
from filer.fields.image import FilerImageField
from django.core.urlresolvers import reverse
from django.utils.functional import cached_property
from membership.models import StripeCustomer
from utils.models import BillingAddress
class MembershipType(models.Model):
@ -13,25 +20,69 @@ class MembershipType(models.Model):
name = models.CharField(choices=MEMBERSHIP_TYPES, max_length=20)
price = models.FloatField()
@cached_property
def days_left(self):
current_date = date.today()
_, days_in_month = calendar.monthrange(current_date.year, current_date.month)
pass_days = current_date.day
days_left = days_in_month - pass_days + 1
return days_left
@cached_property
def first_month_price(self):
current_date = date.today()
_, days_in_month = calendar.monthrange(current_date.year, current_date.month)
pass_days = current_date.day
days_left = days_in_month - pass_days + 1
percentage = days_left / days_in_month
membership_price = self.price
final_price = membership_price * percentage
return final_price
@cached_property
def first_month_range(self):
current_date = date.today()
_, days_in_month = calendar.monthrange(current_date.year, current_date.month)
pass_days = current_date.day
days_left = days_in_month - pass_days
end_date = current_date + timedelta(days=days_left)
return current_date, end_date
@cached_property
def first_month_formated_range(self):
start_date, end_date = self.first_month_range
return "{} - {}".format(datetime.strftime(start_date, "%b, %d %Y"),
datetime.strftime(end_date, "%b, %d %Y"))
class Membership(models.Model):
type = models.ForeignKey(MembershipType)
@classmethod
def create(cls, data, user):
def create(cls, data):
instance = cls.objects.create(**data)
instance.assign_permissions(user)
return instance
class MembershipOrder(models.Model):
membership = models.ForeignKey(Membership)
customer = models.ForeignKey(StripeCustomer)
billing_address = models.ForeignKey(BillingAddress)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
last4 = models.CharField(max_length=4)
cc_brand = models.CharField(max_length=10)
stripe_charge_id = models.CharField(max_length=100, null=True)
@classmethod
def create(cls, data):
stripe_charge = data.pop('stripe_charge', None)
instance = cls.objects.create(**data)
instance.stripe_charge_id = stripe_charge.id
instance.last4 = stripe_charge.source.last4
instance.cc_brand = stripe_charge.source.brand
return instance
class Supporter(models.Model):
name = models.CharField(max_length=200)
@ -44,9 +95,6 @@ class Supporter(models.Model):
return reverse('dgSupporters_view', args=[self.pk])
class DGGallery(models.Model):
parent = models.ForeignKey('self', blank=True, null=True)
name = models.CharField(max_length=30)

View file

@ -0,0 +1,57 @@
{% extends "new_base_glarus.html" %}
{% load staticfiles cms_tags bootstrap3%}
{% block title %}crowdfunding{% endblock %}
{% block content %}
<section id="price">
<div class="signup-container">
<div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"> </div>
<div class="col-xs-12 col-sm-6 col-lg-4 text-center wow fadeInDown">
<!-- <span class="glyphicon glyphicon-user"></span> -->
<div class="payment-box">
<h2 class="billing-head">Membership Activated</h2>
<hr class="greyline-long">
<h2 class="membership-lead">Your Digital Glarus membership is successfully activated for the following date.</h2>
<div class="date-box">
<h2 class="date-oneline">{{membership_dates}}</h2>
<h2 class="activation-lead">Now you can book your next coworking!</h2>
</div>
<!--<hr class="primary">-->
<div class="signup-form form-group row">
<div class="button-booking-box form-inline row">
<button type="submit" class="btn btn-primary btn-blue">Go to Booking</button>
</div>
<div class="notice-box text-left">
<p class="order-bottom-text">Your membership will be automatically renewed each month. For deactivating go to<a href=#>my page</a></p>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"> </div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="contact">
<div class="fill">
<div class="row" class="wow fadeInDown">
<div class="col-lg-12 text-center wow fadeInDown">
<div class="col-md-4 map-title">
Digital Glarus<br>
<span class="map-caption">In der Au 7 Schwanden 8762 Switzerland
<br>info@digitalglarus.ch
<br>
(044) 534-66-22
<p>&nbsp;</p>
</span>
</div>
<p>&nbsp;</p>
</div>
</div>
</div>
</section>
{% endblock %}

View file

@ -8,6 +8,16 @@
padding: 0 !important;
margin: 0 !important;
}
.form-control#id_country{
-webkit-appearance: none;
-moz-appearance: none;
background-position: right 50%;
background-repeat: no-repeat;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAMCAYAAABSgIzaAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDZFNDEwNjlGNzFEMTFFMkJEQ0VDRTM1N0RCMzMyMkIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDZFNDEwNkFGNzFEMTFFMkJEQ0VDRTM1N0RCMzMyMkIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NkU0MTA2N0Y3MUQxMUUyQkRDRUNFMzU3REIzMzIyQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NkU0MTA2OEY3MUQxMUUyQkRDRUNFMzU3REIzMzIyQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuGsgwQAAAA5SURBVHjaYvz//z8DOYCJgUxAf42MQIzTk0D/M+KzkRGPoQSdykiKJrBGpOhgJFYTWNEIiEeAAAMAzNENEOH+do8AAAAASUVORK5CYII=);
padding: .5em;
padding-right: 1.5em
}
</style>
<section id="price">
@ -23,9 +33,13 @@
Additional day costs
15CHF per day. More than 17 days a month it
will charge only 290CHF/month.</h2>
<h2 class="membership-lead">
You will be charged on the first of the month until you
cancel your subscription. Previous charges won't be refunded.
</h2>
<hr class="greyline-long">
<h2 class="billing-head">Member Name</h2>
<h2 class="member-name">Nico Schottelius</h2>
<h2 class="member-name">{{request.user.name}}</h2>
<hr class="greyline-long">
<h2 class="billing-head">Billing Adress</h2>
<div class="signup-form form-group row">
@ -44,20 +58,14 @@
<div class="row">
<div class="col-xs-9 col-md-12">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" />
<span class="input-group-addon"><i class="fa fa-user" aria-hidden="true"></i></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-9 col-md-12">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" />
<span class="input-group-addon"><i class="fa fa-credit-card"></i></span>
</div>
</div>
</div>
</div>
@ -75,7 +83,7 @@
</div>
</div>
</div>
<div class="col-xs-4 col-md-6 pull-right nopadding">
<div class="col-xs-4 col-md-6 pull-right">
<div class="form-group">
<label for="cvCode">CV CODE</label>
<input type="password" class="form-control" name="cvCode" placeholder="CV" required data-stripe="cvc" />
@ -84,7 +92,7 @@
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-success btn-lg btn-block" type="submit">Purchase membership</button>
<button class="btn btn-primary btn-lg btn-blck " type="submit">Purchase membership</button>
</div>
</div>
<div class="row" style="display:none;">
@ -115,25 +123,25 @@
<div class="order-box">
<h2 class="col-xs-6 order-item">Digital Glarus Membership</h2>
<br>
<h2 class="col-xs-6 order-duration">valid 2016.09.08 - 2016.10.08</h2>
<h2 class="col-xs-6 order-duration">valid {{membership_type.first_month_formated_range}}</h2>
<br/>
<h2 class="order-person">1 person</h2>
<h2 class="col-xs-6 payment-total">Today's Total</h2>
<h2 class="order-sum">0.00CHF</h2>
<h2 class="col-xs-6 payment-total">Today's Total for {{membership_type.days_left}} days </h2>
<h2 class="order-sum">{{membership_type.first_month_price|floatformat}}CHF</h2>
<hr class="greyline">
<h2 class="col-xs-6 payment-total">Total</h2>
<h2 class="order-result">35CHF</h2>
<h2 class="order-result">{{membership_type.first_month_price|floatformat}}CHF</h2>
<div class="text-center">
<label class="custom-control custom-checkbox">
<br/>
<input type="checkbox" class="custom-control-input">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">I accept the Digital Glarus <a href=#>Terms and Conditions</a>, <a href=#>Community Guidelines</a> and <a href=#>Privacy Policy</a></span>
</label>
<div class="button-box">
<button type="submit" class="btn btn-primary">Continue to Review</button>
</div>
<div class="button-box">
<p class="order-bottom-text">You can checkout on the next page</p>
</div>
</div>
</div>

View file

@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from . import views
from .views import ContactView, IndexView, AboutView, HistoryView, LoginView, SignupView,\
PasswordResetView, PasswordResetConfirmView, MembershipPaymentView
PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView
# from membership.views import LoginRegistrationView
urlpatterns = [
@ -15,7 +15,9 @@ urlpatterns = [
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
url(_(r'history/?$'), HistoryView.as_view(), name='history'),
url(_(r'membership/payment?$'), MembershipPaymentView.as_view(), name='membership_payment'),
url(_(r'membership/payment/?$'), MembershipPaymentView.as_view(), name='membership_payment'),
url(_(r'membership/activated/?$'), MembershipActivatedView.as_view(),
name='membership_activated'),
url(_(r'supporters/?$'), views.supporters, name='supporters'),
url(r'calendar_api/(?P<month>\d+)/(?P<year>\d+)?$', views.CalendarApi.as_view(),name='calendar_api_1'),
url(r'calendar_api/', views.CalendarApi.as_view(),name='calendar_api'),

View file

@ -13,7 +13,7 @@ from django.utils.translation import get_language
from djangocms_blog.models import Post
from django.contrib import messages
from django.http import JsonResponse
from django.views.generic import View
from django.views.generic import View, DetailView
from .models import Supporter
from utils.forms import ContactUsForm
@ -29,7 +29,7 @@ from utils.stripe_utils import StripeUtils
from .forms import LoginForm, SignupForm, MembershipBillingForm
from .models import MembershipType
from .models import MembershipType, Membership, MembershipOrder
class IndexView(TemplateView):
@ -78,22 +78,24 @@ class MembershipPaymentView(LoginRequiredMixin, FormView):
form_class = MembershipBillingForm
def get_form_kwargs(self):
membership_type = MembershipType.objects.get(name='standard')
self.membership_type = MembershipType.objects.get(name='standard')
form_kwargs = super(MembershipPaymentView, self).get_form_kwargs()
form_kwargs.update({
'initial': {'membership_type': membership_type.id}
'initial': {
'membership_type': self.membership_type.id
}
})
return form_kwargs
def get_context_data(self, **kwargs):
context = super(MembershipPaymentView, self).get_context_data(**kwargs)
context.update({
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
'membership_type': self.membership_type
})
return context
def post(self, request, *args, **kwargs):
import pdb;pdb.set_trace()
form = self.get_form()
if form.is_valid():
@ -111,7 +113,7 @@ class MembershipPaymentView(LoginRequiredMixin, FormView):
# Make stripe charge to a customer
stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(amount=membership_type.price,
charge_response = stripe_utils.make_charge(amount=membership_type.first_month_price,
customer=customer.stripe_id)
charge = charge_response.get('response_object')
@ -124,10 +126,46 @@ class MembershipPaymentView(LoginRequiredMixin, FormView):
return render(request, self.template_name, context)
charge = charge_response.get('response_object')
# Create Billing Address
billing_address = form.save()
# Create membership plan
membership_data = {'type': membership_type}
membership = Membership.create(membership_data)
# Create membership order
order_data = {
'membership': membership,
'customer': customer,
'billing_address': billing_address,
'stripe_charge': charge
}
MembershipOrder.create(order_data)
request.session.update({
'membership_price': membership.type.first_month_price,
'membership_dates': membership.type.first_month_formated_range
})
return HttpResponseRedirect(reverse('digitalglarus:membership_activated'))
else:
return self.form_invalid(form)
class MembershipActivatedView(TemplateView):
template_name = "digitalglarus/membership_activated.html"
def get_context_data(self, **kwargs):
context = super(MembershipActivatedView, self).get_context_data(**kwargs)
membership_price = self.request.session.get('membership_price')
membership_dates = self.request.session.get('membership_dates')
context.update({
'membership_price': membership_price,
'membership_dates': membership_dates,
})
return context
############## OLD VIEWS
class CalendarApi(View):