Added url in booking&price menu bar button. Fixed bug when user booked for 1 days and two free days were being given. Created UserBillingAddress. Created UserBillingAddress Form.Created UserBillingAddress after first user payment. Added Previous Billing data to booking payment view if there is one. Moved booking button. Fixed error on free days in booking invoice. Added modal to cancel subscription page. Now a member can booking without entering his billing address or credit card info. Fixed error on free days.Current user billing address is showed on billing and booking order details. Edit billing address form added.Added user_billing_address.html. Added user billing address get views. Filled user billing address view with current user billing address.Now an user can edit his billing address. Added is_free flag in Select Booking Dates in order to now if a booking is free. Changed Booking Order Model in order to allow free payments. Added free payment template. Added free payment into Booking Payment View. Fixed issue #2586: new digitalglarus navbar font change.Fixed issue #2608 : style fix new DG.Fixed #2617: fix input fields and btn responsiveness on DG payment page. Fixed #2620: responsive issue in DG order summary.Fixed #2623: Order history style fix for deativate button and message below.Fixed #2625: DG membership activation page fix. Fixed #2616: fix navbar responsivness for new DG. Fixed #2619: credit card input field responsiveness fix
This commit is contained in:
parent
30ff53c629
commit
082f39e9dc
23 changed files with 500 additions and 80 deletions
|
@ -57,7 +57,7 @@ class MembershipOrderForm(forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class BookingBillingForm(BillingAddressForm):
|
class BookingBillingForm(BillingAddressForm):
|
||||||
token = forms.CharField(widget=forms.HiddenInput())
|
token = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||||
start_date = forms.DateField(widget=forms.HiddenInput())
|
start_date = forms.DateField(widget=forms.HiddenInput())
|
||||||
end_date = forms.DateField(widget=forms.HiddenInput())
|
end_date = forms.DateField(widget=forms.HiddenInput())
|
||||||
price = forms.FloatField(widget=forms.HiddenInput())
|
price = forms.FloatField(widget=forms.HiddenInput())
|
||||||
|
|
35
digitalglarus/migrations/0020_auto_20161013_0253.py
Normal file
35
digitalglarus/migrations/0020_auto_20161013_0253.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-13 02:53
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('digitalglarus', '0019_auto_20160929_0324'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bookingorder',
|
||||||
|
name='cc_brand',
|
||||||
|
field=models.CharField(blank=True, max_length=10),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bookingorder',
|
||||||
|
name='last4',
|
||||||
|
field=models.CharField(blank=True, max_length=4),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='membershiporder',
|
||||||
|
name='cc_brand',
|
||||||
|
field=models.CharField(blank=True, max_length=10),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='membershiporder',
|
||||||
|
name='last4',
|
||||||
|
field=models.CharField(blank=True, max_length=4),
|
||||||
|
),
|
||||||
|
]
|
|
@ -32,8 +32,8 @@ class Ordereable(models.Model):
|
||||||
billing_address = models.ForeignKey(BillingAddress)
|
billing_address = models.ForeignKey(BillingAddress)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
approved = models.BooleanField(default=False)
|
approved = models.BooleanField(default=False)
|
||||||
last4 = models.CharField(max_length=4)
|
last4 = models.CharField(max_length=4, blank=True)
|
||||||
cc_brand = models.CharField(max_length=10)
|
cc_brand = models.CharField(max_length=10, blank=True)
|
||||||
stripe_charge_id = models.CharField(max_length=100, null=True)
|
stripe_charge_id = models.CharField(max_length=100, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -43,6 +43,8 @@ class Ordereable(models.Model):
|
||||||
def create(cls, data):
|
def create(cls, data):
|
||||||
stripe_charge = data.pop('stripe_charge', None)
|
stripe_charge = data.pop('stripe_charge', None)
|
||||||
instance = cls.objects.create(**data)
|
instance = cls.objects.create(**data)
|
||||||
|
if not stripe_charge:
|
||||||
|
return instance
|
||||||
instance.stripe_charge_id = stripe_charge.id
|
instance.stripe_charge_id = stripe_charge.id
|
||||||
instance.last4 = stripe_charge.source.last4
|
instance.last4 = stripe_charge.source.last4
|
||||||
instance.cc_brand = stripe_charge.source.brand
|
instance.cc_brand = stripe_charge.source.brand
|
||||||
|
|
|
@ -71,12 +71,12 @@ class Membership(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_digitalglarus_active_member(cls, user):
|
def is_digitalglarus_active_member(cls, user):
|
||||||
past_month = (datetime.today() - relativedelta(months=1)).month
|
past_month = (datetime.today() - relativedelta(months=1)).month
|
||||||
has_booking_current_month = Q(membershiporder__customer__user=user,
|
has_order_current_month = Q(membershiporder__customer__user=user,
|
||||||
membershiporder__created_at__month=datetime.today().month)
|
membershiporder__created_at__month=datetime.today().month)
|
||||||
has_booking_past_month = Q(membershiporder__customer__user=user,
|
has_order_past_month = Q(membershiporder__customer__user=user,
|
||||||
membershiporder__created_at__month=past_month)
|
membershiporder__created_at__month=past_month)
|
||||||
active_membership = Q(active=True)
|
active_membership = Q(active=True)
|
||||||
return cls.objects.filter(has_booking_past_month | has_booking_current_month).\
|
return cls.objects.filter(has_order_past_month | has_order_current_month).\
|
||||||
filter(active_membership).exists()
|
filter(active_membership).exists()
|
||||||
|
|
||||||
def deactivate(self):
|
def deactivate(self):
|
||||||
|
@ -147,7 +147,7 @@ class Booking(models.Model):
|
||||||
def get_ramaining_free_days(cls, user, start_date, end_date):
|
def get_ramaining_free_days(cls, user, start_date, end_date):
|
||||||
|
|
||||||
TWO_DAYS = 2
|
TWO_DAYS = 2
|
||||||
|
ONE_DAY = 1
|
||||||
start_month = start_date.month
|
start_month = start_date.month
|
||||||
end_month = end_date.month
|
end_month = end_date.month
|
||||||
months = abs(start_month - (end_month + 12) if end_month < start_month
|
months = abs(start_month - (end_month + 12) if end_month < start_month
|
||||||
|
@ -156,6 +156,10 @@ class Booking(models.Model):
|
||||||
current_month_bookings = cls.objects.filter(bookingorder__customer__user=user,
|
current_month_bookings = cls.objects.filter(bookingorder__customer__user=user,
|
||||||
start_date__month=current_date.month)
|
start_date__month=current_date.month)
|
||||||
free_days_this_month = TWO_DAYS - sum(map(lambda x: x.free_days, current_month_bookings))
|
free_days_this_month = TWO_DAYS - sum(map(lambda x: x.free_days, current_month_bookings))
|
||||||
|
|
||||||
|
if start_date == end_date and free_days_this_month == TWO_DAYS:
|
||||||
|
free_days_this_month = ONE_DAY
|
||||||
|
|
||||||
total_free_days = months * TWO_DAYS + free_days_this_month
|
total_free_days = months * TWO_DAYS + free_days_this_month
|
||||||
return total_free_days
|
return total_free_days
|
||||||
|
|
||||||
|
@ -190,11 +194,11 @@ class Booking(models.Model):
|
||||||
# Calculating membership required months price for booking
|
# Calculating membership required months price for booking
|
||||||
required_membership_months = 0
|
required_membership_months = 0
|
||||||
membership_booking_price = 0.0
|
membership_booking_price = 0.0
|
||||||
if BookingOrder.user_has_not_bookings(user):
|
# if not BookingOrder.user_has_not_bookings(user):
|
||||||
today = datetime.today().date()
|
today = datetime.today().date()
|
||||||
membership_price = MembershipType.objects.get(name=MembershipType.STANDARD).price
|
membership_price = MembershipType.objects.get(name=MembershipType.STANDARD).price
|
||||||
required_membership_months = cls.membership_required_booking_months(today, end_date)
|
required_membership_months = cls.membership_required_booking_months(today, end_date)
|
||||||
membership_booking_price = membership_price * required_membership_months
|
membership_booking_price = membership_price * required_membership_months
|
||||||
|
|
||||||
# Add required membership months to final prices
|
# Add required membership months to final prices
|
||||||
final_booking_price += membership_booking_price
|
final_booking_price += membership_booking_price
|
||||||
|
@ -210,11 +214,15 @@ class BookingOrder(Ordereable, models.Model):
|
||||||
membership_required_months = models.IntegerField(default=0)
|
membership_required_months = models.IntegerField(default=0)
|
||||||
membership_required_months_price = models.FloatField(default=0)
|
membership_required_months_price = models.FloatField(default=0)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def user_has_not_bookings(cls, user):
|
def user_has_not_bookings(cls, user):
|
||||||
return cls.objects.filter(customer__user=user).exists()
|
return cls.objects.filter(customer__user=user).exists()
|
||||||
|
|
||||||
|
def get_booking_cc_data(self):
|
||||||
|
return {
|
||||||
|
'last4': self.last4,
|
||||||
|
'cc_brand': self.cc_brand,
|
||||||
|
}
|
||||||
|
|
||||||
def booking_days(self):
|
def booking_days(self):
|
||||||
return (self.booking.end_date - self.booking.start_date).days + 1
|
return (self.booking.end_date - self.booking.start_date).days + 1
|
||||||
|
|
|
@ -241,6 +241,8 @@ fieldset[disabled] .btn-xl.active {
|
||||||
|
|
||||||
.navbar-default .navbar-collapse {
|
.navbar-default .navbar-collapse {
|
||||||
border-color: rgba(255,255,255,.02);
|
border-color: rgba(255,255,255,.02);
|
||||||
|
padding-right: 100px;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-default .navbar-toggle {
|
.navbar-default .navbar-toggle {
|
||||||
|
@ -259,7 +261,7 @@ fieldset[disabled] .btn-xl.active {
|
||||||
|
|
||||||
.navbar-default .nav li a {
|
.navbar-default .nav li a {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -197,7 +197,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyphicon-ok {
|
.glyphicon-ok {
|
||||||
font-size: 42px;
|
font-size: 28px;
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
@ -364,7 +364,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
color: #999;
|
color: #000;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,21 @@ $( document ).ready(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var submit_form_btn = $('#payment_button');
|
||||||
|
submit_form_btn.on('click', submit_payment);
|
||||||
|
|
||||||
|
function submit_payment(e){
|
||||||
|
$('#billing-form').submit();
|
||||||
|
// $form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var $form = $('#payment-form');
|
var $form = $('#payment-form');
|
||||||
$form.submit(payWithStripe);
|
$form.submit(payWithStripe);
|
||||||
|
|
||||||
/* If you're using Stripe for payments */
|
/* If you're using Stripe for payments */
|
||||||
function payWithStripe(e) {
|
function payWithStripe(e) {
|
||||||
|
console.log("submiting");
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
/* Visual feedback */
|
/* Visual feedback */
|
||||||
|
|
|
@ -39,11 +39,15 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2 class="order-head">Billing Adress<btn class="btn btn-primary btn-grey btn-edit">Edit</btn></h2>
|
<h2 class="order-head">Billing Adress<a class="btn btn-primary btn-grey btn-edit" href="{% url 'digitalglarus:user_billing_address' %}">Edit</a></h2>
|
||||||
<h2 class="history-name">Nico Schottelius<br>
|
<h2 class="history-name">
|
||||||
In der Au 7 8762 Schwanden<br>
|
{{request.user.name}}
|
||||||
Switzerland
|
|
||||||
</h2>
|
</h2>
|
||||||
|
<h2 class="history-name">
|
||||||
|
{{billing_address.street_address}},{{billing_address.postal_code}}<br>
|
||||||
|
{{billing_address.city}}, {{billing_address.country}}.
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<h2 class="section-heading payment-head">Booking</h2>
|
<h2 class="section-heading payment-head">Booking</h2>
|
||||||
<!-- <h2 class="membership-amount">35CHF</h2> -->
|
<!-- <h2 class="membership-amount">35CHF</h2> -->
|
||||||
<hr class="greyline-long">
|
<hr class="greyline-long">
|
||||||
<h2 class="billing-head">Billing Adress</h2>
|
|
||||||
<h2 class="membership-lead">
|
<h2 class="membership-lead">
|
||||||
Your Digital Glarus Membership enables
|
Your Digital Glarus Membership enables
|
||||||
you to use our coworking space and it includes
|
you to use our coworking space and it includes
|
||||||
|
@ -37,7 +37,25 @@
|
||||||
15CHF per day. More than 17 days a month it
|
15CHF per day. More than 17 days a month it
|
||||||
will charge only 290CHF/month.
|
will charge only 290CHF/month.
|
||||||
</h2>
|
</h2>
|
||||||
|
{% if is_free %}
|
||||||
|
<h2 class="billing-head">Billing Adress</h2>
|
||||||
|
<div class="signup-form form-group row">
|
||||||
|
<form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate>
|
||||||
|
{% for field in form %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
{% endfor %}
|
||||||
|
{% bootstrap_form_errors form type='non_fields'%}
|
||||||
|
<br>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="greyline-long">
|
||||||
|
<br/>
|
||||||
|
<h2 class="billing-head">Your Booking is TOTALLY FREE</h2>
|
||||||
|
<br/><br/>
|
||||||
|
{% else %}
|
||||||
|
<h2 class="billing-head">Billing Adress</h2>
|
||||||
<div class="signup-form form-group row">
|
<div class="signup-form form-group row">
|
||||||
<form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate>
|
<form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate>
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
|
@ -48,18 +66,26 @@
|
||||||
<br>
|
<br>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="billing-head">Credit Card</h2>
|
{% if credit_card_data %}
|
||||||
|
<form role="form" id="billing-form" method="post" action="{% url 'digitalglarus:booking_payment' %}" novalidate>
|
||||||
|
<h2 class="billing-head">Credit Card</h2>
|
||||||
|
<h2 class="membership-lead">Last 4: {{credit_card_data.last4}}</h2>
|
||||||
|
<h2 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h2>
|
||||||
|
<input type="hidden" name="credit_card_needed" value="false"/>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<h2 class="billing-head">Credit Card (Last used)</h2>
|
||||||
<div class="signup-form form-group row">
|
<div class="signup-form form-group row">
|
||||||
<form role="form" id="payment-form" novalidate>
|
<form role="form" id="payment-form" novalidate>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-12">
|
<div class="col-xs-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" />
|
<input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-12">
|
<div class="col-xs-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" />
|
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,11 +112,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<button class="btn btn-primary btn-lg btn-blck " type="submit">Book</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row" style="display:none;">
|
<div class="row" style="display:none;">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<p class="payment-errors"></p>
|
<p class="payment-errors"></p>
|
||||||
|
@ -109,6 +130,8 @@
|
||||||
</form>
|
</form>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">
|
<div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">
|
||||||
|
@ -136,18 +159,20 @@
|
||||||
<h2 class="col-xs-6 payment-total">Total</h2>
|
<h2 class="col-xs-6 payment-total">Total</h2>
|
||||||
<h2 class="order-result">{{final_price|floatformat}}CHF</h2>
|
<h2 class="order-result">{{final_price|floatformat}}CHF</h2>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<label class="custom-control custom-checkbox">
|
<label class="custom-control custom-checkbox">
|
||||||
<br/>
|
<br/>
|
||||||
<input type="checkbox" class="custom-control-input">
|
<input type="checkbox" class="custom-control-input">
|
||||||
<span class="custom-control-indicator"></span>
|
<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>
|
<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>
|
</label>
|
||||||
<div class="button-box">
|
<div class="button-box">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<button id="payment_button" class="btn btn-primary btn-lg btn-blck " type="submit">Book</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="button-box">
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<a class="btn btn-primary btn-blue" href={% url 'digitalglarus:booking' %}>Go to Booking</a>
|
<a class="btn btn-primary btn-blue" href={% url 'digitalglarus:booking' %}>Go to Booking</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="notice-box text-left">
|
<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>
|
<p class="order-bottom-text">Your membership will be automatically renewed each month. For deactivating go to<a href="{% url 'digitalglarus:membership_deactivate' %}">deactivate page</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,28 @@
|
||||||
|
|
||||||
<form method="POST" action="">
|
<form method="POST" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-primary btn-blue">Cancel my Membership</button>
|
<button type="button" class="btn btn-primary btn-blue" data-toggle="modal" data-target="#cancel-subscription-modal">Cancel my Membership</button>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="modal fade bs-example-modal-sm" id="cancel-subscription-modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title">Cancel Subscription</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Do you want to cancel your subscription?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">No</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Yes</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,6 +54,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"> </div>
|
<div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
|
|
||||||
<h2 class="col-xs-6 payment-total">Membership month {{order.created_at|date:"F"}}</h2>
|
<h2 class="col-xs-6 payment-total">Membership month {{order.created_at|date:"F"}}</h2>
|
||||||
<h2 class="order-sum">{{order.amount|floatformat}}CHF</h2>
|
<h2 class="order-sum">{{order.amount|floatformat}}CHF</h2>
|
||||||
|
<br/>
|
||||||
<hr class="greyline">
|
<hr class="greyline">
|
||||||
<h2 class="col-xs-6 payment-total">Total</h2>
|
<h2 class="col-xs-6 payment-total">Total</h2>
|
||||||
<h2 class="order-result">{{order.amount|floatformat}}CHF</h2>
|
<h2 class="order-result">{{order.amount|floatformat}}CHF</h2>
|
||||||
|
|
|
@ -39,10 +39,13 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2 class="order-head">Billing Adress<btn class="btn btn-primary btn-grey btn-edit">Edit</btn></h2>
|
<h2 class="order-head">Billing Adress<a class="btn btn-primary btn-grey btn-edit" href="{% url 'digitalglarus:user_billing_address' %}">Edit</a></h2>
|
||||||
<h2 class="history-name">Nico Schottelius<br>
|
<h2 class="history-name">
|
||||||
In der Au 7 8762 Schwanden<br>
|
{{request.user.name}}
|
||||||
Switzerland
|
</h2>
|
||||||
|
<h2 class="history-name">
|
||||||
|
{{billing_address.street_address}},{{billing_address.postal_code}}<br>
|
||||||
|
{{billing_address.city}}, {{billing_address.country}}.
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<hr class="greyline-long">
|
<hr class="greyline-long">
|
||||||
|
@ -50,14 +53,15 @@
|
||||||
<h2 class="history-name">
|
<h2 class="history-name">
|
||||||
Dates: {{membership_start_date|date}} - {{membership_end_date|date}}<br>
|
Dates: {{membership_start_date|date}} - {{membership_end_date|date}}<br>
|
||||||
</h2>
|
</h2>
|
||||||
<h2 class="history-name">
|
<div class="edit-button">
|
||||||
|
|
||||||
<a class="btn btn-primary btn-grey btn-edit print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a>
|
<a class="btn btn-primary btn-grey btn-deactivate print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a>
|
||||||
</h2>
|
</div>
|
||||||
|
<hr class="greyline-long">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10">
|
<div class="col-md-12 notice-box">
|
||||||
<span>You will be charged on the first of the month until you
|
<p class="order-bottom-text">You will be charged on the first of the month until you
|
||||||
cancel your subscription. Previous charges won't be refunded.</span>
|
cancel your subscription. Previous charges won't be refunded.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -66,18 +70,37 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">
|
<div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown">
|
||||||
|
|
||||||
<div class="order-summary">
|
<div class="order-summary">
|
||||||
<h2 class="thankyou">Thank You!</h2>
|
|
||||||
|
{% if messages %}
|
||||||
|
<div class="order-box">
|
||||||
|
<h2 class="thankyou">Message</h2>
|
||||||
|
<hr class="greyline">
|
||||||
|
{% for message in messages %}
|
||||||
|
<h2 class="signup-lead text-center">{{ message }}</h2>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<h2 class="thankyou">Thank You!</h2>
|
||||||
<div class="order-box">
|
<div class="order-box">
|
||||||
<span class="glyphicon glyphicon-heart icon-up"></span>
|
<span class="glyphicon glyphicon-heart icon-up"></span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 class="signup-lead text-center">Digital Glarus lives with your love!<br>
|
<h2 class="signup-lead text-center">Digital Glarus lives with your love!<br>
|
||||||
Our coworking space is here because of your love and support.</h2>
|
Our coworking space is here because of your love and support.</h2>
|
||||||
|
|
||||||
<hr class="greyline">
|
<hr class="greyline">
|
||||||
|
<p class="order-bottom-text text-center">This box is here just to thank you</p>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p class="order-bottom-text text-center">This box is here just to thank you</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -56,14 +56,14 @@
|
||||||
<div class="signup-form form-group row">
|
<div class="signup-form form-group row">
|
||||||
<form role="form" id="payment-form" novalidate>
|
<form role="form" id="payment-form" novalidate>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-12">
|
<div class="col-xs-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" />
|
<input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-12">
|
<div class="col-xs-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" />
|
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<button class="btn btn-primary btn-lg btn-blck " type="submit">Purchase membership</button>
|
<button class="btn btn-primary btn-md btn-blck " type="submit">Purchase membership</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="display:none;">
|
<div class="row" style="display:none;">
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
{% extends "new_base_glarus.html" %}
|
||||||
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.nopadding {
|
||||||
|
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">
|
||||||
|
<div class="signup-container">
|
||||||
|
<div class="col-xs-12 col-sm-6 col-lg-8 text-center wow fadeInDown">
|
||||||
|
<div class="payment-box">
|
||||||
|
<h2 class="section-heading payment-head">Billing Address Info</h2>
|
||||||
|
<!-- <h2 class="membership-amount">35CHF</h2> -->
|
||||||
|
<hr class="greyline-long">
|
||||||
|
<h2 class="billing-head">Billing Adress</h2>
|
||||||
|
<div class="signup-form form-group row">
|
||||||
|
<form role="form" id="billing-address-form" method="post" action="{% url 'digitalglarus:user_billing_address' %}" novalidate>
|
||||||
|
{% for field in form %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
{% endfor %}
|
||||||
|
{% bootstrap_form_errors form type='non_fields'%}
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<button id="payment_button" class="btn btn-primary btn-lg btn-blck " type="submit">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</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> </p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p> </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- stripe key data -->
|
||||||
|
{% if stripe_key %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {window.stripeKey = "{{stripe_key}}";})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{%endif%}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -107,7 +107,7 @@
|
||||||
<a href="#page-top"></a>
|
<a href="#page-top"></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="page-scroll" href="#portfolio">booking & price</a>
|
<a class="page-scroll" href="{% url 'digitalglarus:booking' %}">booking & price</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="page-scroll" href="{% url 'digitalglarus:history' %}">history</a>
|
<a class="page-scroll" href="{% url 'digitalglarus:history' %}">history</a>
|
||||||
|
|
|
@ -18,7 +18,7 @@ from utils.tests import BaseTestCase
|
||||||
|
|
||||||
from .views import LoginView, SignupView, PasswordResetView, PasswordResetConfirmView,\
|
from .views import LoginView, SignupView, PasswordResetView, PasswordResetConfirmView,\
|
||||||
MembershipPricingView, MembershipPaymentView
|
MembershipPricingView, MembershipPaymentView
|
||||||
from .models import MembershipType
|
from .models import MembershipType, MembershipOrder
|
||||||
|
|
||||||
|
|
||||||
class ContactViewTest(TestCase):
|
class ContactViewTest(TestCase):
|
||||||
|
@ -119,11 +119,11 @@ class MembershipPaymentViewTest(BaseTestCase):
|
||||||
def test_post(self, stripe_mocked_call):
|
def test_post(self, stripe_mocked_call):
|
||||||
|
|
||||||
# Anonymous user should get redirect to login
|
# Anonymous user should get redirect to login
|
||||||
response = self.client.post(self.url)
|
# response = self.client.post(self.url)
|
||||||
expected_url = "%s?next=%s" % (reverse('digitalglarus:signup'),
|
# expected_url = "%s?next=%s" % (reverse('digitalglarus:signup'),
|
||||||
reverse('digitalglarus:membership_payment'))
|
# reverse('digitalglarus:membership_payment'))
|
||||||
self.assertRedirects(response, expected_url=expected_url,
|
# self.assertRedirects(response, expected_url=expected_url,
|
||||||
status_code=302, target_status_code=200)
|
# status_code=302, target_status_code=200)
|
||||||
|
|
||||||
# Customer user should be able to pay
|
# Customer user should be able to pay
|
||||||
stripe_mocked_call.return_value = {
|
stripe_mocked_call.return_value = {
|
||||||
|
@ -132,12 +132,21 @@ class MembershipPaymentViewTest(BaseTestCase):
|
||||||
'error': None
|
'error': None
|
||||||
}
|
}
|
||||||
response = self.customer_client.post(self.url, self.billing_address)
|
response = self.customer_client.post(self.url, self.billing_address)
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertTrue(StripeCustomer.objects.filter(user__email=self.customer.email).exists())
|
self.assertTrue(StripeCustomer.objects.filter(user__email=self.customer.email).exists())
|
||||||
stripe_customer = StripeCustomer.objects.get(user__email=self.customer.email)
|
stripe_customer = StripeCustomer.objects.get(user__email=self.customer.email)
|
||||||
# self.assertEqual(stripe_customer.user, self.customer)
|
self.assertEqual(stripe_customer.user, self.customer)
|
||||||
|
self.assertTrue(MembershipOrder.objects.filter(customer=stripe_customer).exists())
|
||||||
|
membership_order = MembershipOrder.objects.filter(customer=stripe_customer).first()
|
||||||
|
session_data = {
|
||||||
|
'membership_price': membership_order.membership.type.first_month_price,
|
||||||
|
'membership_dates': membership_order.membership.type.first_month_formated_range
|
||||||
|
}
|
||||||
|
self.assertEqual(session_data.get('membership_price'),
|
||||||
|
self.session_data.get('membership_price'))
|
||||||
|
self.assertEqual(session_data.get('membership_dates'),
|
||||||
|
self.session_data.get('membership_dates'))
|
||||||
|
|
||||||
# self.assertTrue(HostingOrder.objects.filter(customer=stripe_customer).exists())
|
# self.assertTrue(HostingOrder.objects.filter(customer=stripe_customer).exists())
|
||||||
# hosting_order = HostingOrder.objects.filter(customer=stripe_customer)[0]
|
# hosting_order = HostingOrder.objects.filter(customer=stripe_customer)[0]
|
||||||
# vm_plan = {
|
# vm_plan = {
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .views import ContactView, IndexView, AboutView, HistoryView, LoginView, Si
|
||||||
PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView,\
|
PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView,\
|
||||||
MembershipPricingView, BookingSelectDatesView, BookingPaymentView, OrdersBookingDetailView,\
|
MembershipPricingView, BookingSelectDatesView, BookingPaymentView, OrdersBookingDetailView,\
|
||||||
BookingOrdersListView, MembershipOrdersListView, OrdersMembershipDetailView, \
|
BookingOrdersListView, MembershipOrdersListView, OrdersMembershipDetailView, \
|
||||||
MembershipDeactivateView, MembershipDeactivateSuccessView
|
MembershipDeactivateView, MembershipDeactivateSuccessView, UserBillingAddressView
|
||||||
# from membership.views import LoginRegistrationView
|
# from membership.views import LoginRegistrationView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -20,6 +20,7 @@ urlpatterns = [
|
||||||
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
||||||
PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
|
PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
|
||||||
url(_(r'history/?$'), HistoryView.as_view(), name='history'),
|
url(_(r'history/?$'), HistoryView.as_view(), name='history'),
|
||||||
|
url(_(r'users/billing_address/?$'), UserBillingAddressView.as_view(), name='user_billing_address'),
|
||||||
url(_(r'booking/?$'), BookingSelectDatesView.as_view(), name='booking'),
|
url(_(r'booking/?$'), BookingSelectDatesView.as_view(), name='booking'),
|
||||||
url(_(r'booking/payment/?$'), BookingPaymentView.as_view(), name='booking_payment'),
|
url(_(r'booking/payment/?$'), BookingPaymentView.as_view(), name='booking_payment'),
|
||||||
url(_(r'booking/orders/(?P<pk>\d+)/?$'), OrdersBookingDetailView.as_view(),
|
url(_(r'booking/orders/(?P<pk>\d+)/?$'), OrdersBookingDetailView.as_view(),
|
||||||
|
|
|
@ -27,8 +27,9 @@ from membership.models import Calendar as CalendarModel, StripeCustomer
|
||||||
|
|
||||||
from utils.views import LoginViewMixin, SignupViewMixin, \
|
from utils.views import LoginViewMixin, SignupViewMixin, \
|
||||||
PasswordResetViewMixin, PasswordResetConfirmViewMixin
|
PasswordResetViewMixin, PasswordResetConfirmViewMixin
|
||||||
from utils.forms import PasswordResetRequestForm
|
from utils.forms import PasswordResetRequestForm, UserBillingAddressForm
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
|
from utils.models import UserBillingAddress
|
||||||
|
|
||||||
|
|
||||||
from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\
|
from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\
|
||||||
|
@ -120,6 +121,7 @@ class BookingSelectDatesView(LoginRequiredMixin, MembershipRequiredMixin, FormVi
|
||||||
'free_days': free_days,
|
'free_days': free_days,
|
||||||
'start_date': start_date.strftime('%m/%d/%Y'),
|
'start_date': start_date.strftime('%m/%d/%Y'),
|
||||||
'end_date': end_date.strftime('%m/%d/%Y'),
|
'end_date': end_date.strftime('%m/%d/%Y'),
|
||||||
|
'is_free': final_price == 0
|
||||||
})
|
})
|
||||||
return super(BookingSelectDatesView, self).form_valid(form)
|
return super(BookingSelectDatesView, self).form_valid(form)
|
||||||
|
|
||||||
|
@ -132,7 +134,7 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
booking_needed_fields = ['original_price', 'final_price', 'booking_days', 'free_days',
|
booking_needed_fields = ['original_price', 'final_price', 'booking_days', 'free_days',
|
||||||
'start_date', 'end_date', 'membership_required_months_price',
|
'start_date', 'end_date', 'membership_required_months_price',
|
||||||
'membership_required_months', 'booking_price_per_day',
|
'membership_required_months', 'booking_price_per_day',
|
||||||
'total_discount']
|
'total_discount', 'is_free']
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
from_booking = all(field in request.session.keys()
|
from_booking = all(field in request.session.keys()
|
||||||
|
@ -146,12 +148,17 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
return reverse('digitalglarus:booking_orders_detail', kwargs={'pk': order_id})
|
return reverse('digitalglarus:booking_orders_detail', kwargs={'pk': order_id})
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
|
current_billing_address = self.request.user.billing_addresses.first()
|
||||||
form_kwargs = super(BookingPaymentView, self).get_form_kwargs()
|
form_kwargs = super(BookingPaymentView, self).get_form_kwargs()
|
||||||
form_kwargs.update({
|
form_kwargs.update({
|
||||||
'initial': {
|
'initial': {
|
||||||
'start_date': self.request.session.get('start_date'),
|
'start_date': self.request.session.get('start_date'),
|
||||||
'end_date': self.request.session.get('end_date'),
|
'end_date': self.request.session.get('end_date'),
|
||||||
'price': self.request.session.get('final_price'),
|
'price': self.request.session.get('final_price'),
|
||||||
|
'street_address': current_billing_address.street_address,
|
||||||
|
'city': current_billing_address.city,
|
||||||
|
'postal_code': current_billing_address.postal_code,
|
||||||
|
'country': current_billing_address.country,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return form_kwargs
|
return form_kwargs
|
||||||
|
@ -161,11 +168,15 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
|
|
||||||
booking_data = {key: self.request.session.get(key)
|
booking_data = {key: self.request.session.get(key)
|
||||||
for key in self.booking_needed_fields}
|
for key in self.booking_needed_fields}
|
||||||
|
user = self.request.user
|
||||||
|
last_booking_order = BookingOrder.objects.filter(customer__user=user).last()
|
||||||
# booking_price_per_day = BookingPrice.objects.get().price_per_day
|
# booking_price_per_day = BookingPrice.objects.get().price_per_day
|
||||||
# total_discount = booking_price_per_day * booking_data.get('free_days')
|
# total_discount = booking_price_per_day * booking_data.get('free_days')
|
||||||
booking_data.update({
|
booking_data.update({
|
||||||
# 'booking_price_per_day': booking_price_per_day,
|
# 'booking_price_per_day': booking_price_per_day,
|
||||||
# 'total_discount': total_discount,
|
# 'current_billing_address': self.request.user.billing_addresses.first().to_dict(),
|
||||||
|
'credit_card_data': last_booking_order.get_booking_cc_data() if last_booking_order
|
||||||
|
else None,
|
||||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||||
})
|
})
|
||||||
context.update(booking_data)
|
context.update(booking_data)
|
||||||
|
@ -177,11 +188,12 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
token = data.get('token')
|
token = data.get('token')
|
||||||
start_date = data.get('start_date')
|
start_date = data.get('start_date')
|
||||||
end_date = data.get('end_date')
|
end_date = data.get('end_date')
|
||||||
|
is_free = context.get('is_free')
|
||||||
normal_price, final_price, free_days, membership_required_months,\
|
normal_price, final_price, free_days, membership_required_months,\
|
||||||
membership_required_months_price = Booking.\
|
membership_required_months_price = Booking.\
|
||||||
booking_price(self.request.user, start_date, end_date)
|
booking_price(self.request.user, start_date, end_date)
|
||||||
|
|
||||||
|
# if not credit_card_needed:
|
||||||
# Get or create stripe customer
|
# Get or create stripe customer
|
||||||
customer = StripeCustomer.get_or_create(email=self.request.user.email,
|
customer = StripeCustomer.get_or_create(email=self.request.user.email,
|
||||||
token=token)
|
token=token)
|
||||||
|
@ -189,6 +201,44 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
form.add_error("__all__", "Invalid credit card")
|
form.add_error("__all__", "Invalid credit card")
|
||||||
return self.render_to_response(self.get_context_data(form=form))
|
return self.render_to_response(self.get_context_data(form=form))
|
||||||
|
|
||||||
|
if is_free:
|
||||||
|
billing_address = form.save()
|
||||||
|
|
||||||
|
# Create Billing Address for User if he does not have one
|
||||||
|
if not customer.user.billing_addresses.count():
|
||||||
|
data.update({
|
||||||
|
'user': customer.user.id
|
||||||
|
})
|
||||||
|
billing_address_user_form = UserBillingAddressForm(data)
|
||||||
|
billing_address_user_form.is_valid()
|
||||||
|
billing_address_user_form.save()
|
||||||
|
|
||||||
|
# Create membership plan
|
||||||
|
booking_data = {
|
||||||
|
'start_date': start_date,
|
||||||
|
'end_date': end_date,
|
||||||
|
'start_date': start_date,
|
||||||
|
'free_days': free_days,
|
||||||
|
'price': normal_price,
|
||||||
|
'final_price': final_price,
|
||||||
|
}
|
||||||
|
booking = Booking.create(booking_data)
|
||||||
|
|
||||||
|
# Create membership order
|
||||||
|
order_data = {
|
||||||
|
'booking': booking,
|
||||||
|
'customer': customer,
|
||||||
|
'billing_address': billing_address,
|
||||||
|
'amount': final_price,
|
||||||
|
'original_price': normal_price,
|
||||||
|
'special_month_price': BookingPrice.objects.last().special_month_price,
|
||||||
|
'membership_required_months': membership_required_months,
|
||||||
|
'membership_required_months_price': membership_required_months_price,
|
||||||
|
}
|
||||||
|
order = BookingOrder.create(order_data)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(self.get_success_url(order.id))
|
||||||
|
|
||||||
# Make stripe charge to a customer
|
# Make stripe charge to a customer
|
||||||
stripe_utils = StripeUtils()
|
stripe_utils = StripeUtils()
|
||||||
charge_response = stripe_utils.make_charge(amount=final_price,
|
charge_response = stripe_utils.make_charge(amount=final_price,
|
||||||
|
@ -205,9 +255,18 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
|
||||||
|
|
||||||
charge = charge_response.get('response_object')
|
charge = charge_response.get('response_object')
|
||||||
|
|
||||||
# Create Billing Address
|
# Create Billing Address for Membership Order
|
||||||
billing_address = form.save()
|
billing_address = form.save()
|
||||||
|
|
||||||
|
# Create Billing Address for User if he does not have one
|
||||||
|
if not customer.user.billing_addresses.count():
|
||||||
|
data.update({
|
||||||
|
'user': customer.user.id
|
||||||
|
})
|
||||||
|
billing_address_user_form = UserBillingAddressForm(data)
|
||||||
|
billing_address_user_form.is_valid()
|
||||||
|
billing_address_user_form.save()
|
||||||
|
|
||||||
# Create membership plan
|
# Create membership plan
|
||||||
booking_data = {
|
booking_data = {
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
|
@ -319,6 +378,7 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
|
||||||
'stripe_charge': charge,
|
'stripe_charge': charge,
|
||||||
'amount': membership_type.first_month_price
|
'amount': membership_type.first_month_price
|
||||||
}
|
}
|
||||||
|
|
||||||
membership_order = MembershipOrder.create(order_data)
|
membership_order = MembershipOrder.create(order_data)
|
||||||
|
|
||||||
request.session.update({
|
request.session.update({
|
||||||
|
@ -369,6 +429,7 @@ class MembershipActivatedView(TemplateView):
|
||||||
class MembershipDeactivateView(LoginRequiredMixin, UpdateView):
|
class MembershipDeactivateView(LoginRequiredMixin, UpdateView):
|
||||||
template_name = "digitalglarus/membership_deactivated.html"
|
template_name = "digitalglarus/membership_deactivated.html"
|
||||||
model = Membership
|
model = Membership
|
||||||
|
success_message = "Your membership has been deactivated :("
|
||||||
success_url = reverse_lazy('digitalglarus:membership_orders_list')
|
success_url = reverse_lazy('digitalglarus:membership_orders_list')
|
||||||
login_url = reverse_lazy('digitalglarus:login')
|
login_url = reverse_lazy('digitalglarus:login')
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
@ -384,9 +445,38 @@ class MembershipDeactivateView(LoginRequiredMixin, UpdateView):
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
membership = self.get_object()
|
membership = self.get_object()
|
||||||
membership.deactivate()
|
membership.deactivate()
|
||||||
|
|
||||||
|
messages.add_message(self.request, messages.SUCCESS, self.success_message)
|
||||||
|
|
||||||
return HttpResponseRedirect(self.success_url)
|
return HttpResponseRedirect(self.success_url)
|
||||||
|
|
||||||
|
|
||||||
|
class UserBillingAddressView(LoginRequiredMixin, UpdateView):
|
||||||
|
model = UserBillingAddress
|
||||||
|
form_class = UserBillingAddressForm
|
||||||
|
template_name = "digitalglarus/user_billing_address.html"
|
||||||
|
success_url = reverse_lazy('digitalglarus:user_billing_address')
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
current_billing_address = self.request.user.billing_addresses.first()
|
||||||
|
form_kwargs = super(UserBillingAddressView, self).get_form_kwargs()
|
||||||
|
form_kwargs.update({
|
||||||
|
'initial': {
|
||||||
|
'street_address': current_billing_address.street_address,
|
||||||
|
'city': current_billing_address.city,
|
||||||
|
'postal_code': current_billing_address.postal_code,
|
||||||
|
'country': current_billing_address.country,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
current_billing_address = self.request.user.billing_addresses.filter(current=True).last()
|
||||||
|
if not current_billing_address:
|
||||||
|
raise AttributeError("Billing Address does not exists")
|
||||||
|
return current_billing_address
|
||||||
|
|
||||||
|
|
||||||
class MembershipDeactivateSuccessView(LoginRequiredMixin, TemplateView):
|
class MembershipDeactivateSuccessView(LoginRequiredMixin, TemplateView):
|
||||||
template_name = "digitalglarus/membership_deactivated_success.html"
|
template_name = "digitalglarus/membership_deactivated_success.html"
|
||||||
|
|
||||||
|
@ -401,9 +491,11 @@ class MembershipOrdersListView(LoginRequiredMixin, ListView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(MembershipOrdersListView, self).get_context_data(**kwargs)
|
context = super(MembershipOrdersListView, self).get_context_data(**kwargs)
|
||||||
start_date, end_date = MembershipOrder.current_membership(self.request.user)
|
start_date, end_date = MembershipOrder.current_membership(self.request.user)
|
||||||
|
current_billing_address = self.request.user.billing_addresses.filter(current=True).last()
|
||||||
context.update({
|
context.update({
|
||||||
'membership_start_date': start_date,
|
'membership_start_date': start_date,
|
||||||
'membership_end_date': end_date,
|
'membership_end_date': end_date,
|
||||||
|
'billing_address': current_billing_address
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -477,6 +569,14 @@ class BookingOrdersListView(LoginRequiredMixin, ListView):
|
||||||
model = BookingOrder
|
model = BookingOrder
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(BookingOrdersListView, self).get_context_data(**kwargs)
|
||||||
|
current_billing_address = self.request.user.billing_addresses.filter(current=True).last()
|
||||||
|
context.update({
|
||||||
|
'billing_address': current_billing_address
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super(BookingOrdersListView, self).get_queryset()
|
queryset = super(BookingOrdersListView, self).get_queryset()
|
||||||
queryset = queryset.filter(customer__user=self.request.user)
|
queryset = queryset.filter(customer__user=self.request.user)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import ContactMessage, BillingAddress
|
from .models import ContactMessage, BillingAddress, UserBillingAddress
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -109,6 +109,19 @@ class BillingAddressForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class UserBillingAddressForm(forms.ModelForm):
|
||||||
|
user = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
|
||||||
|
widget=forms.HiddenInput())
|
||||||
|
class Meta:
|
||||||
|
model = UserBillingAddress
|
||||||
|
fields = ['street_address', 'city', 'postal_code', 'country', 'user']
|
||||||
|
labels = {
|
||||||
|
'street_address': _('Street Address'),
|
||||||
|
'city': _('City'),
|
||||||
|
'postal_code': _('Postal Code'),
|
||||||
|
'Country': _('Country'),
|
||||||
|
}
|
||||||
|
|
||||||
class ContactUsForm(forms.ModelForm):
|
class ContactUsForm(forms.ModelForm):
|
||||||
error_css_class = 'autofocus'
|
error_css_class = 'autofocus'
|
||||||
|
|
||||||
|
|
34
utils/migrations/0003_userbillingaddress.py
Normal file
34
utils/migrations/0003_userbillingaddress.py
Normal file
File diff suppressed because one or more lines are too long
22
utils/migrations/0004_auto_20161013_0253.py
Normal file
22
utils/migrations/0004_auto_20161013_0253.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-13 02:53
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('utils', '0003_userbillingaddress'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userbillingaddress',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='billing_addresses', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,20 +1,45 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core import serializers
|
||||||
|
from django.forms.models import model_to_dict
|
||||||
|
|
||||||
|
from membership.models import CustomUser
|
||||||
|
|
||||||
from .fields import CountryField
|
from .fields import CountryField
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
class BaseBillingAddress(models.Model):
|
||||||
class BillingAddress(models.Model):
|
|
||||||
street_address = models.CharField(max_length=100)
|
street_address = models.CharField(max_length=100)
|
||||||
city = models.CharField(max_length=50)
|
city = models.CharField(max_length=50)
|
||||||
postal_code = models.CharField(max_length=50)
|
postal_code = models.CharField(max_length=50)
|
||||||
country = CountryField()
|
country = CountryField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
class BillingAddress(BaseBillingAddress):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.street_address
|
return self.street_address
|
||||||
|
|
||||||
|
|
||||||
|
class UserBillingAddress(BaseBillingAddress):
|
||||||
|
user = models.ForeignKey(CustomUser, related_name='billing_addresses')
|
||||||
|
current = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.street_address
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'Street Address': self.street_address,
|
||||||
|
'City': self.city,
|
||||||
|
'Postal Code': self.postal_code,
|
||||||
|
'Country': self.country,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ContactMessage(models.Model):
|
class ContactMessage(models.Model):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
|
|
Loading…
Reference in a new issue