diff --git a/digitalglarus/forms.py b/digitalglarus/forms.py index 10f73b31..db3aab94 100644 --- a/digitalglarus/forms.py +++ b/digitalglarus/forms.py @@ -1,6 +1,6 @@ from django import forms from django.utils.translation import ugettext_lazy as _ - +from datetime import datetime from utils.models import BillingAddress from utils.forms import LoginFormMixin, SignupFormMixin, BillingAddressForm @@ -34,3 +34,46 @@ class MembershipBillingForm(BillingAddressForm): 'postal_code': _('Postal Code'), 'country': _('Country'), } + + +class BookingBillingForm(BillingAddressForm): + token = forms.CharField(widget=forms.HiddenInput()) + start_date = forms.DateField(widget=forms.HiddenInput()) + end_date = forms.DateField(widget=forms.HiddenInput()) + price = forms.FloatField(widget=forms.HiddenInput()) + + class Meta: + model = BillingAddress + fields = ['start_date', 'end_date', 'price', 'street_address', + 'city', 'postal_code', 'country'] + labels = { + 'street_address': _('Street Address'), + 'city': _('City'), + 'postal_code': _('Postal Code'), + 'country': _('Country'), + } + + +class BookingDateForm(forms.Form): + start_date = forms.DateField(required=False) + end_date = forms.DateField(required=False) + date_range = forms.CharField(required=False) + + def clean_date_range(self): + date_range = self.cleaned_data.get('date_range') + dates = date_range.replace(' ', '').split('-') + try: + start_date, end_date = [datetime.strptime(date_string, "%m/%d/%Y").date() + for date_string in dates] + except ValueError: + raise forms.ValidationError("Submit valid dates.") + + if start_date > end_date: + raise forms.ValidationError("Your end date must be greather than your start date.") + return start_date, end_date + + def clean(self): + start_date, end_date = self.cleaned_data.get('date_range') + self.cleaned_data['start_date'] = start_date + self.cleaned_data['end_date'] = end_date + return self.cleaned_data diff --git a/digitalglarus/migrations/0008_auto_20160820_1959.py b/digitalglarus/migrations/0008_auto_20160820_1959.py new file mode 100644 index 00000000..d71350b5 --- /dev/null +++ b/digitalglarus/migrations/0008_auto_20160820_1959.py @@ -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, + ), + ] diff --git a/digitalglarus/migrations/0009_booking_bookingorder_bookingprices.py b/digitalglarus/migrations/0009_booking_bookingorder_bookingprices.py new file mode 100644 index 00000000..784c1f54 --- /dev/null +++ b/digitalglarus/migrations/0009_booking_bookingorder_bookingprices.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-08-25 05:11 +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', '0008_auto_20160820_1959'), + ] + + operations = [ + migrations.CreateModel( + name='Booking', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_date', models.DateField()), + ('end_date', models.DateField()), + ('price', models.FloatField()), + ('membership', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='digitalglarus.Membership')), + ], + ), + migrations.CreateModel( + name='BookingOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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)), + ('billing_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.BillingAddress')), + ('booking', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='digitalglarus.Booking')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='BookingPrices', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('price_per_day', models.FloatField()), + ], + ), + ] diff --git a/digitalglarus/migrations/0010_auto_20160828_1745.py b/digitalglarus/migrations/0010_auto_20160828_1745.py new file mode 100644 index 00000000..b526562f --- /dev/null +++ b/digitalglarus/migrations/0010_auto_20160828_1745.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-08-28 17:45 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0009_booking_bookingorder_bookingprices'), + ] + + operations = [ + migrations.RenameModel( + old_name='BookingPrices', + new_name='BookingPrice', + ), + ] diff --git a/digitalglarus/migrations/0011_bookingprice_special_price_offer.py b/digitalglarus/migrations/0011_bookingprice_special_price_offer.py new file mode 100644 index 00000000..08a75e39 --- /dev/null +++ b/digitalglarus/migrations/0011_bookingprice_special_price_offer.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-08-28 21:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0010_auto_20160828_1745'), + ] + + operations = [ + migrations.AddField( + model_name='bookingprice', + name='special_price_offer', + field=models.FloatField(default=290.0), + preserve_default=False, + ), + ] diff --git a/digitalglarus/migrations/0012_booking_free_days.py b/digitalglarus/migrations/0012_booking_free_days.py new file mode 100644 index 00000000..17d95319 --- /dev/null +++ b/digitalglarus/migrations/0012_booking_free_days.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-08-30 03:28 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0011_bookingprice_special_price_offer'), + ] + + operations = [ + migrations.AddField( + model_name='booking', + name='free_days', + field=models.IntegerField(default=0), + ), + ] diff --git a/digitalglarus/migrations/0013_remove_booking_membership.py b/digitalglarus/migrations/0013_remove_booking_membership.py new file mode 100644 index 00000000..7b2ffa24 --- /dev/null +++ b/digitalglarus/migrations/0013_remove_booking_membership.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-08-30 04:56 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0012_booking_free_days'), + ] + + operations = [ + migrations.RemoveField( + model_name='booking', + name='membership', + ), + ] diff --git a/digitalglarus/migrations/0014_booking_final_price.py b/digitalglarus/migrations/0014_booking_final_price.py new file mode 100644 index 00000000..f417b714 --- /dev/null +++ b/digitalglarus/migrations/0014_booking_final_price.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-09-02 03:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0013_remove_booking_membership'), + ] + + operations = [ + migrations.AddField( + model_name='booking', + name='final_price', + field=models.FloatField(default=250), + preserve_default=False, + ), + ] diff --git a/digitalglarus/mixins.py b/digitalglarus/mixins.py new file mode 100644 index 00000000..13329f73 --- /dev/null +++ b/digitalglarus/mixins.py @@ -0,0 +1,38 @@ +from django.db import models +from django.http import HttpResponseRedirect +from membership.models import StripeCustomer +from utils.models import BillingAddress + + +class MembershipRequired(object): + membership_redirect_url = None + + def dispatch(self, request, *args, **kwargs): + from .models import Membership + if not Membership.is_digitalglarus_member(request.user): + return HttpResponseRedirect(self.membership_redirect_url) + + return super(MembershipRequired, self).dispatch(request, *args, **kwargs) + + +class Ordereable(models.Model): + 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) + + class Meta: + abstract = 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 + instance.save() + return instance diff --git a/digitalglarus/models.py b/digitalglarus/models.py index 612e1c02..49d1327f 100644 --- a/digitalglarus/models.py +++ b/digitalglarus/models.py @@ -1,7 +1,17 @@ + +import calendar +from datetime import datetime, date, timedelta +from dateutil.relativedelta import relativedelta from django.db import models +from django.db.models import Q 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 .mixins import Ordereable + +# from membership.models import StripeCustomer +# from utils.models import BillingAddress class MembershipType(models.Model): @@ -13,24 +23,132 @@ 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 is_digitalglarus_member(cls, user): + past_month = (datetime.today() - relativedelta(months=1)).month + has_booking_current_month = Q(membershiporder__created_at__month=datetime.today().month) + has_booking_past_month = Q(membershiporder__customer__user=user, + membershiporder__created_at__month=past_month) + return cls.objects.filter(has_booking_past_month | has_booking_current_month).exists() + + @classmethod + def create(cls, data): instance = cls.objects.create(**data) - instance.assign_permissions(user) return instance -class MembershipOrder(models.Model): +class MembershipOrder(Ordereable, models.Model): membership = models.ForeignKey(Membership) - 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 BookingPrice(models.Model): + price_per_day = models.FloatField() + special_price_offer = models.FloatField() + + +class Booking(models.Model): + start_date = models.DateField() + end_date = models.DateField() + price = models.FloatField() + free_days = models.IntegerField(default=0) + final_price = models.FloatField() + + @classmethod + def create(cls, data): + instance = cls.objects.create(**data) + return instance + + @classmethod + def get_ramaining_free_days(cls, user, start_date, end_date): + + TWO_DAYS = 2 + + start_month = start_date.month + end_month = end_date.month + months = abs(start_month - (end_month + 12) if end_month < start_month + else end_month - start_month) + current_date = datetime.today() + current_month_bookings = cls.objects.filter(bookingorder__customer__user=user, + start_date__month=current_date.month) + free_days_this_month = TWO_DAYS - sum(map(lambda x: x.free_days, current_month_bookings)) + total_free_days = months * TWO_DAYS + free_days_this_month + return total_free_days + + @classmethod + def booking_price(cls, user, start_date, end_date): + + MAX_MONTH_PRICE = 290 + MAX_MONTH_DAYS_PROMOTION = 31 + MIN_MONTH_DAYS_PROMOTION = 19 + + booking_prices = BookingPrice.objects.last() + price_per_day = booking_prices.price_per_day + booking_days = (end_date - start_date).days + 1 + months = booking_days // MAX_MONTH_DAYS_PROMOTION + remanent_days = booking_days % MAX_MONTH_DAYS_PROMOTION + months_prices = months * MAX_MONTH_PRICE + remanent_days_price = remanent_days * price_per_day \ + if remanent_days <= MIN_MONTH_DAYS_PROMOTION else MAX_MONTH_PRICE + normal_price = months_prices + remanent_days_price + + free_days = cls.get_ramaining_free_days(user, start_date, end_date) + final_booking_price = normal_price - (free_days * price_per_day) + + return normal_price, final_booking_price, free_days + + +class BookingOrder(Ordereable, models.Model): + booking = models.OneToOneField(Booking) + + def booking_days(self): + return (self.booking.end_date - self.booking.start_date).days + 1 class Supporter(models.Model): @@ -44,9 +162,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) diff --git a/digitalglarus/static/digitalglarus/js/booking.js b/digitalglarus/static/digitalglarus/js/booking.js new file mode 100644 index 00000000..0cb97667 --- /dev/null +++ b/digitalglarus/static/digitalglarus/js/booking.js @@ -0,0 +1,25 @@ +$( document ).ready(function() { + + + // $('#booking-date-range').daterangepicker(); + + var tomorrow = new Date(new Date().getTime() + 24 * 60 * 60 * 1000); + // var tomorrow = today.setDate(today.getDate() + 1); + $('#booking-date-range').daterangepicker({ + autoUpdateInput: false, + locale: { + cancelLabel: 'Clear' + }, + minDate: tomorrow, + }); + + + $('#booking-date-range').on('apply.daterangepicker', function(ev, picker) { + $(this).val(picker.startDate.format('MM/DD/YYYY') + ' - ' + picker.endDate.format('MM/DD/YYYY')); + }); + + $('#booking-date-range').on('cancel.daterangepicker', function(ev, picker) { + $(this).val('Select your dates'); + }); + +}); \ No newline at end of file diff --git a/digitalglarus/static/digitalglarus/js/utils.js b/digitalglarus/static/digitalglarus/js/utils.js new file mode 100644 index 00000000..d780fc25 --- /dev/null +++ b/digitalglarus/static/digitalglarus/js/utils.js @@ -0,0 +1,21 @@ +$( document ).ready(function() { + + + function printDiv(divName) { + var printContents = document.getElementById(divName).innerHTML; + var originalContents = document.body.innerHTML; + + document.body.innerHTML = printContents; + + window.print(); + + document.body.innerHTML = originalContents; + } + + + $('.print').on('click', function(e){ + printDiv(e.target.getAttribute("data-print") ); + + }); + +}); \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/booking.html b/digitalglarus/templates/digitalglarus/booking.html new file mode 100644 index 00000000..6a8d4374 --- /dev/null +++ b/digitalglarus/templates/digitalglarus/booking.html @@ -0,0 +1,53 @@ +{% 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"> + <div class="signup-box"> + <span class="glyphicon glyphicon-plus"></span> + <h2 class="section-heading">Booking</h2> + <h2 class="signup-lead">Start coworking at Digital Glarus! <br> Membership costs only + <strong>35CHF</strong> per month.<br> 2 free working days included!</h2> + <hr class="primary"> + + <div class="signup-form form-group row"> + <input type="hidden" name="next" value="{{ request.GET.next }}"> + <form action="" method="post" class="form" novalidate> + {% csrf_token %} + <input class="form-control" placeholder="Select your dates" type="text" id="booking-date-range" name="date_range"> + <button type="submit" class="btn btn-primary btn-blue">Book</button> + </form> + <br> + <div class="notice-box"> + </div> + </div> + </div> + <div class="col-xs-12 col-sm-3 col-lg-4 text-center wow fadeInDown"></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> +{% endblock %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/booking_orders_detail.html b/digitalglarus/templates/digitalglarus/booking_orders_detail.html new file mode 100644 index 00000000..6dcb604a --- /dev/null +++ b/digitalglarus/templates/digitalglarus/booking_orders_detail.html @@ -0,0 +1,88 @@ +{% extends "new_base_glarus.html" %} +{% load staticfiles bootstrap3 i18n %} +{% block content %} + + <style type="text/css"> + + .invoice-title{ + text-align: center !important; + } + </style> + +<script type="text/javascript"> + +</script> + + <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">Your Booking Detail</h2> + <hr class="greyline-long"> + <h2 class="billing-head">Invoice<btn class="btn btn-primary btn-grey btn-edit print" data-print="price">Get PDF</btn></h2> + + <h2 class="order-head">Order Number</h2> + <h2 class="member-name">#{{order.id}}</h2> + + <h2 class="order-head">Billed to :</h2> + <h2 class="history-name">{{user.name}}<br> + {{order.billing_address.street_address}},{{order.billing_address.postal_code}}<br> + {{order.billing_address.city}}, {{order.billing_address.country}}. + </h2> + + <h2 class="order-head">Payment Method</h2> + <h2 class="history-name"> + {{order.cc_brand}} ending **** {{order.last4}}<br> + {{user.email}} + </h2> + + <hr class="greyline-long"> + <h2 class="order-head">Order Summary</h2> + <h2 class="history-name"> + Dates: {{start_date}} - {{end_date}}<br> + </h2> + <h2 class="col-xs-6 payment-total text-left">Total days {{booking_days}}</h2> + <h2 class="order-sum">{{original_price|floatformat}}CHF</h2> + {% if free_days %} + <h2 class="col-xs-6 payment-total text-left">Free days {{free_days}} </h2> + <h2 class="order-sum">-{{total_discount|floatformat}}CHF</h2> + {% endif %} + <hr class="greyline-long"> + <h2 class="col-xs-6 payment-total text-left"> Total</h2> + <h2 class="order-result">{{final_price|floatformat}}CHF</h2> + <br> + </div> + </div> + + <div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown"> + <div class="order-summary"> + <div class="header text-center"> + <h2 class="order-name">Order Summary</h2> + </div> + <div class="order-box"> + <h2 class="col-xs-6 order-item" style="padding-bottom:10px">Dates: {{start_date}} - {{end_date}}<br></h2> + + <h2 class="col-xs-6 payment-total">Total days {{booking_days}}</h2> + <h2 class="order-sum">{{original_price|floatformat}}CHF</h2> + {% if free_days %} + <h2 class="col-xs-6 payment-total">Free days {{free_days}}</h2> + <h2 class="order-sum">-{{total_discount|floatformat}}CHF</h2> + {% endif %} + <hr class="greyline"> + <h2 class="col-xs-6 payment-total">Total</h2> + <h2 class="order-result">{{final_price|floatformat}}CHF</h2> + </div> + + </div> + </div> + </section> + +<!-- stripe key data --> +{% if stripe_key %} +<script type="text/javascript"> + (function () {window.stripeKey = "{{stripe_key}}";})(); +</script> + +{%endif%} + +{% endblock %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/booking_orders_list.html b/digitalglarus/templates/digitalglarus/booking_orders_list.html new file mode 100644 index 00000000..58a5e4bd --- /dev/null +++ b/digitalglarus/templates/digitalglarus/booking_orders_list.html @@ -0,0 +1,68 @@ +{% extends "new_base_glarus.html" %} +{% load staticfiles bootstrap3 i18n %} +{% block content %} + + + <!-- Header --> + <!-- Services Section --> + <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">Your Order History</h2> + <hr class="greyline-long"> + <h2 class="order-head">Member Name</h2> + <h2 class="member-name">{{request.user.name}}</h2> + <hr class="greyline-long"> + <h2 class="order-head">Active Membership</h2> + <h2 class="member-name">2016.11.13-2016.12.13</h2> + <hr class="greyline-long"> + <h2 class="order-head">Booking history</h2> + <table class="table"> + <thead> + <tr> + <th>#</th> + <th>Booking dates</th> + <th>Days</th> + <th>Invoice</th> + </tr> + </thead> + <tbody> + {% for order in orders%} + <tr> + <th scope="row">{{order.id}}</th> + <td>{{order.booking.start_date}}-{{order.booking.end_date}}</td> + <td>{{order.booking_days}}</td> + <td><a class="btn btn-xs btn-primary btn-darkgrey" href="{% url 'digitalglarus:booking_orders_detail' order.id %}">View</a></td> + </tr> + {% endfor %} + </tbody> + </table> + + <h2 class="order-head">Billing Adress<btn class="btn btn-primary btn-grey btn-edit">Edit</btn></h2> + <h2 class="history-name">Nico Schottelius<br> + In der Au 7 8762 Schwanden<br> + Switzerland + </h2> + + </div> + </div> + + + <div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown"> + <div class="order-summary"> + <h2 class="thankyou">Thank You!</h2> + <div class="order-box"> + <span class="glyphicon glyphicon-heart icon-up"></span> + <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> + + <hr class="greyline"> + + <p class="order-bottom-text text-center">This box is here just to thank you</p> + </div> + + </div> + </div> + +{% endblock %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/booking_payment.html b/digitalglarus/templates/digitalglarus/booking_payment.html new file mode 100644 index 00000000..ff639a92 --- /dev/null +++ b/digitalglarus/templates/digitalglarus/booking_payment.html @@ -0,0 +1,186 @@ +{% 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">Booking</h2> + <!-- <h2 class="membership-amount">35CHF</h2> --> + <hr class="greyline-long"> + <h2 class="billing-head">Billing Adress</h2> + <h2 class="membership-lead"> + Your Digital Glarus Membership enables + you to use our coworking space and it includes + 2 working days for the month you signed up. + The membership fee is a monthly subscription. + Additional day costs + 15CHF per day. More than 17 days a month it + will charge only 290CHF/month. + </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'%} + {{form.errors}} + + <br> + </form> + </div> + <h2 class="billing-head">Credit Card</h2> + <div class="signup-form form-group row"> + <form role="form" id="payment-form" novalidate> + <div class="row"> + <div class="col-xs-9 col-md-12"> + <div class="form-group"> + <input type="text" class="form-control" name="cardName" placeholder="Name on card" required autofocus data-stripe="name" /> + </div> + </div> + </div> + <div class="row"> + <div class="col-xs-9 col-md-12"> + <div class="form-group"> + <input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required data-stripe="number" /> + </div> + </div> + </div> + <div class="row"> + <div class="col-xs-6 col-md-6 nopadding"> + <label for="expMonth">EXPIRATION DATE</label><br/> + <div class="col-xs-6 col-lg-6 col-md-6"> + <div class="form-group"> + <input type="text" class="form-control" name="expMonth" placeholder="MM" required data-stripe="exp_month" /> + </div> + </div> + <div class="col-xs-6 col-lg-6 col-md-6 pl-ziro"> + <div class="form-group"> + <input type="text" class="form-control" name="expYear" placeholder="YY" required data-stripe="exp_year" /> + </div> + </div> + </div> + <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" /> + </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="col-xs-12"> + <p class="payment-errors"></p> + </div> + </div> + {% if paymentError %} + <div class="row"> + <div class="col-xs-12"> + <p> + {% bootstrap_alert paymentError alert_type='danger' %} + </p> + </div> + </div> + + {% endif %} + </form> + <br> + </div> + </div> + </div> + <div class="col-xs-12 col-sm-4 col-lg-4 wow fadeInDown"> + <div class="order-summary"> + <div class="header text-center"> + <h2 class="order-name">Booking Summary</h2> + </div> + <div class="order-box"> + <h3 class="col-xs-6 order-item">Dates {{start_date}} - {{end_date}}</h3> + <br/> + + <hr class="greyline"> + <h2 class="col-xs-6 payment-total">Total days {{booking_days}} </h2> + <h2 class="order-sum">{{original_price|floatformat}}CHF</h2> + {% if free_days %} + <h2 class="col-xs-6 payment-total">Free days {{free_days}}</h2> + <h2 class="order-sum"><span class="text-danger">-{{total_discount|floatformat}}CHF</span></h2> + {% endif %} + <hr class="greyline"> + <h2 class="col-xs-6 payment-total">Total</h2> + <h2 class="order-result">{{discount_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"> + </div> + + <div class="button-box"> + </div> + </div> + </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 %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/membership_activated.html b/digitalglarus/templates/digitalglarus/membership_activated.html new file mode 100644 index 00000000..f899f6ec --- /dev/null +++ b/digitalglarus/templates/digitalglarus/membership_activated.html @@ -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"> + <a class="btn btn-primary btn-blue" href={% url 'digitalglarus:booking' %}>Go to Booking</a> + </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> </p> + </span> + </div> + <p> </p> + </div> + </div> + </div> + </section> +{% endblock %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/membership_payment.html b/digitalglarus/templates/digitalglarus/membership_payment.html index 0345bda0..bde934de 100644 --- a/digitalglarus/templates/digitalglarus/membership_payment.html +++ b/digitalglarus/templates/digitalglarus/membership_payment.html @@ -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> diff --git a/digitalglarus/templates/digitalglarus/membership_pricing.html b/digitalglarus/templates/digitalglarus/membership_pricing.html new file mode 100644 index 00000000..fec32399 --- /dev/null +++ b/digitalglarus/templates/digitalglarus/membership_pricing.html @@ -0,0 +1,87 @@ +{% extends "new_base_glarus.html" %} +{% load staticfiles cms_tags bootstrap3%} +{% block title %}crowdfunding{% endblock %} + +{% block content %} + <section id="price"> + <div class="container"> + <div class="row col-md-3 text-center wow fadeInDown"></div> + <div class="row col-md-6 text-center wow fadeInDown"> + <div class="price-box"> + <span class="glyphicon glyphicon-star"></span> + <h2 class="section-heading">Digital Glarus Membership</h2> + <h2 class="price"><strong>{{membership_type.price}}CHF</strong>/month<br> (free working 2days included)</h2> + <hr class="primary"> + <img src="{% static 'digitalglarus/img/graph.png' %}" width="450" height="396" class="img-responsive center-block graph"><br> + <div class="price-exp-box" + <p class="carousel-text text-left supporter-black"> + Get your Digital Glarus Membership for only + <strong>{{membership_type.price}}CHF</strong> + and get 2 working days for free! + The membership fee is a monthly subscription and + with every payment you help us to create a better + workspace. + You want to work more than 2 days per month in our + coworking space? Great! It's only <strong> 15CHF</strong> + per additional day. + You want more? That's really cool! + If you work 17 days a month or EVERY DAY of a month, + you can do so for <strong> 290CHF</strong>/month.. + And the best? The discount is + applied automatically! + Just become a member, book your + working day and we will take care of the rest. + </p> + <div class="text-center"> + <a class="btn btn-primary btn-blue" href="{% url 'digitalglarus:membership_payment' %}">become a member</a> + </div> + </div> + + </div> + </div> + <div class="row col-md-3 text-center wow fadeInDown"></div> + </div> + </section> + <!--membership includes--> + <section id="membership-includes"> + <div class="container"> + <div class="intro-price text-center wow fadeInDown"> + <div class="intro-headline-small"> + <span class="intro-headline-small"> + Become our member + </span> + </div> + <h3 class="intro-smaller">Our membership includes:</h3> + <div class="col-lg-12 price-list"> + <li class="list-group-item row price-list"> + <div class="price-list"><span class="glyphicon glyphicon-ok "></span>Super-fast Internet</div> + <div class="price-list"><span class="glyphicon glyphicon-ok glyphicon-inverse"></span>Any workspace in our common areas</div> + <div class="price-list"><span class="glyphicon glyphicon-ok "></span>Free 2 working day passes</div> + <div class="price-list"><span class="glyphicon glyphicon-ok glyphicon-inverse"></span>Access to any of our great workshops</div> + <div class="price-list"><span class="glyphicon glyphicon-ok "></span>Special invitation to our fondue nights, cooking & hacking sessions, coworking & cohiking, and many more cool stuff.</div> + </li> + + </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> +{% endblock %} \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/signup.html b/digitalglarus/templates/digitalglarus/signup.html index 118057f8..aea83f4e 100644 --- a/digitalglarus/templates/digitalglarus/signup.html +++ b/digitalglarus/templates/digitalglarus/signup.html @@ -18,7 +18,7 @@ <div class="signup-form form-group row"> <form action="{% url 'digitalglarus:signup' %}" method="post" class="form" novalidate> {% csrf_token %} - <input type="hidden" name="" value="{{ request.GET.next }}"> + <input type="hidden" name="next" value="{{ request.GET.next }}"> {% for field in form %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %} diff --git a/digitalglarus/templates/new_base_glarus.html b/digitalglarus/templates/new_base_glarus.html index 74bcaf1d..7d5f9167 100644 --- a/digitalglarus/templates/new_base_glarus.html +++ b/digitalglarus/templates/new_base_glarus.html @@ -93,7 +93,7 @@ <a class="page-scroll" href="{% url 'digitalglarus:login' %}">Log In</a> </li> <li> - <a class="page-scroll" href="{% url 'digitalglarus:login' %}">Sign Up</a> + <a class="page-scroll" href="{% url 'digitalglarus:signup' %}">Sign Up</a> </li> </ul> </div> @@ -167,11 +167,27 @@ - <!-- Custom Fonts --> - <link href="//fonts.googleapis.com/css?family=Raleway" rel="stylesheet" type="text/css"> - <link href="{% static 'digitalglarus/font-awesome-4.1.0/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css"> - <link href="//fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css"> - <link href="//fonts.googleapis.com/css?family=Kaushan+Script" rel="stylesheet" type="text/css"> - <link href="//fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic" rel="stylesheet" type="text/css"> - <link href="//fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css"> +<!-- Include Required Prerequisites --> +<script type="text/javascript" src="//cdn.jsdelivr.net/jquery/1/jquery.min.js"></script> +<script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script> +<!-- <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap/latest/css/bootstrap.css" /> + --> +<!-- Include Date Range Picker --> +<script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script> +<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" /> + + +<!-- Booking JavaScript --> +<script src="{% static 'digitalglarus/js/booking.js' %}"></script> + +<!-- Utils JavaScript --> +<script src="{% static 'digitalglarus/js/utils.js' %}"></script> + +<!-- Custom Fonts --> +<link href="//fonts.googleapis.com/css?family=Raleway" rel="stylesheet" type="text/css"> +<link href="{% static 'digitalglarus/font-awesome-4.1.0/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css"> +<link href="//fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css"> +<link href="//fonts.googleapis.com/css?family=Kaushan+Script" rel="stylesheet" type="text/css"> +<link href="//fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic" rel="stylesheet" type="text/css"> +<link href="//fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css"> </html> \ No newline at end of file diff --git a/digitalglarus/urls.py b/digitalglarus/urls.py index 88e1ba75..63560e61 100644 --- a/digitalglarus/urls.py +++ b/digitalglarus/urls.py @@ -3,7 +3,9 @@ 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,\ + MembershipPricingView, BookingSelectDatesView, BookingPaymentView, OrdersBookingDetailView,\ + BookingOrdersListView # from membership.views import LoginRegistrationView urlpatterns = [ @@ -11,11 +13,23 @@ urlpatterns = [ url(_(r'contact/?$'), ContactView.as_view(), name='contact'), url(_(r'login/?$'), LoginView.as_view(), name='login'), url(_(r'signup/?$'), SignupView.as_view(), name='signup'), + url(r'^logout/?$', 'django.contrib.auth.views.logout', + {'next_page': '/digitalglarus/login?logged_out=true'}, name='logout'), url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'), 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'booking/?$'), BookingSelectDatesView.as_view(), name='booking'), + url(_(r'booking/payment/?$'), BookingPaymentView.as_view(), name='booking_payment'), + url(_(r'booking/orders/(?P<pk>\d+)/?$'), OrdersBookingDetailView.as_view(), + name='booking_orders_detail'), + url(_(r'booking/orders/?$'), BookingOrdersListView.as_view(), + name='booking_orders_list'), + url(_(r'membership/payment/?$'), MembershipPaymentView.as_view(), name='membership_payment'), + url(_(r'membership/activated/?$'), MembershipActivatedView.as_view(), + name='membership_activated'), + url(_(r'membership/pricing/?$'), MembershipPricingView.as_view(), + name='membership_pricing'), 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'), diff --git a/digitalglarus/views.py b/digitalglarus/views.py index 00d3f87e..dadc8abe 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -13,13 +13,13 @@ 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, ListView from .models import Supporter from utils.forms import ContactUsForm from django.views.generic.edit import FormView from membership.calendar.calendar import BookCalendar -from membership.models import Calendar as CalendarModel, CustomUser, StripeCustomer +from membership.models import Calendar as CalendarModel, StripeCustomer from utils.views import LoginViewMixin, SignupViewMixin, \ @@ -28,8 +28,13 @@ from utils.forms import PasswordResetRequestForm from utils.stripe_utils import StripeUtils -from .forms import LoginForm, SignupForm, MembershipBillingForm -from .models import MembershipType +from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\ + BookingBillingForm + +from .models import MembershipType, Membership, MembershipOrder, Booking, BookingPrice,\ + BookingOrder + +from .mixins import MembershipRequired class IndexView(TemplateView): @@ -63,7 +68,7 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin): class HistoryView(TemplateView): template_name = "digitalglarus/history.html" - def get_context_data(self, **kwargs): + def get_context_data(self, *args, **kwargs): context = super(HistoryView, self).get_context_data(**kwargs) supporters = Supporter.objects.all() context.update({ @@ -72,28 +77,177 @@ class HistoryView(TemplateView): return context +class BookingSelectDatesView(LoginRequiredMixin, MembershipRequired, FormView): + template_name = "digitalglarus/booking.html" + form_class = BookingDateForm + membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing') + login_url = reverse_lazy('digitalglarus:login') + success_url = reverse_lazy('digitalglarus:booking_payment') + + def form_valid(self, form): + user = self.request.user + start_date = form.cleaned_data.get('start_date') + end_date = form.cleaned_data.get('end_date') + booking_days = (end_date - start_date).days + 1 + original_price, discount_price, free_days = Booking.\ + booking_price(user, start_date, end_date) + self.request.session.update({ + 'original_price': original_price, + 'discount_price': discount_price, + 'booking_days': booking_days, + 'free_days': free_days, + 'start_date': start_date.strftime('%m/%d/%Y'), + 'end_date': end_date.strftime('%m/%d/%Y'), + }) + return super(BookingSelectDatesView, self).form_valid(form) + + +class BookingPaymentView(LoginRequiredMixin, MembershipRequired, FormView): + template_name = "digitalglarus/booking_payment.html" + form_class = BookingBillingForm + membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing') + # success_url = reverse_lazy('digitalglarus:booking_payment') + booking_needed_fields = ['original_price', 'discount_price', 'booking_days', 'free_days', + 'start_date', 'end_date'] + + def dispatch(self, request, *args, **kwargs): + from_booking = all(field in request.session.keys() + for field in self.booking_needed_fields) + if not from_booking: + return HttpResponseRedirect(reverse('digitalglarus:booking')) + + return super(BookingPaymentView, self).dispatch(request, *args, **kwargs) + + def get_success_url(self, order_id): + return reverse('digitalglarus:booking_orders_datail', kwargs={'pk': order_id}) + + def get_form_kwargs(self): + form_kwargs = super(BookingPaymentView, self).get_form_kwargs() + form_kwargs.update({ + 'initial': { + 'start_date': self.request.session.get('start_date'), + 'end_date': self.request.session.get('end_date'), + 'price': self.request.session.get('discount_price'), + } + }) + return form_kwargs + + def get_context_data(self, *args, **kwargs): + context = super(BookingPaymentView, self).get_context_data(*args, **kwargs) + + booking_data = {key: self.request.session.get(key) + for key in self.booking_needed_fields} + booking_price_per_day = BookingPrice.objects.get().price_per_day + total_discount = booking_price_per_day * booking_data.get('free_days') + booking_data.update({ + 'booking_price_per_day': booking_price_per_day, + 'total_discount': total_discount, + 'stripe_key': settings.STRIPE_API_PUBLIC_KEY + }) + context.update(booking_data) + return context + + def form_valid(self, form): + data = form.cleaned_data + context = self.get_context_data() + token = data.get('token') + start_date = data.get('start_date') + end_date = data.get('end_date') + + normal_price, final_price, free_days = Booking.\ + booking_price(self.request.user, start_date, end_date) + + # Get or create stripe customer + customer = StripeCustomer.get_or_create(email=self.request.user.email, + token=token) + if not customer: + form.add_error("__all__", "Invalid credit card") + return self.render_to_response(self.get_context_data(form=form)) + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + charge_response = stripe_utils.make_charge(amount=final_price, + customer=customer.stripe_id) + charge = charge_response.get('response_object') + + # Check if the payment was approved + if not charge: + context.update({ + 'paymentError': charge_response.get('error'), + 'form': form + }) + return render(self.request, self.template_name, context) + + charge = charge_response.get('response_object') + + # Create Billing Address + billing_address = 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, + 'stripe_charge': charge + } + order = BookingOrder.create(order_data) + + # request.session.update({ + # 'membership_price': membership.type.first_month_price, + # 'membership_dates': membership.type.first_month_formated_range + # }) + return HttpResponseRedirect(self.get_success_url(order.id)) + return super(BookingPaymentView, self).form_valid(form) + # return HttpResponseRedirect(reverse('digitalglarus:membership_activated')) + + +class MembershipPricingView(TemplateView): + template_name = "digitalglarus/membership_pricing.html" + + def get_context_data(self, **kwargs): + context = super(MembershipPricingView, self).get_context_data(**kwargs) + membership_type = MembershipType.objects.last() + context.update({ + 'membership_type': membership_type + }) + return context + + class MembershipPaymentView(LoginRequiredMixin, FormView): template_name = "digitalglarus/membership_payment.html" - login_url = reverse_lazy('digitalglarus:login') + login_url = reverse_lazy('digitalglarus:signup') 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 +265,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 +278,93 @@ 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 + + +class OrdersBookingDetailView(LoginRequiredMixin, DetailView): + template_name = "digitalglarus/booking_orders_detail.html" + context_object_name = "order" + login_url = reverse_lazy('digitalglarus:login') + # permission_required = ['view_hostingorder'] + model = BookingOrder + + def get_context_data(self, *args, **kwargs): + + context = super(OrdersBookingDetailView, self).get_context_data(**kwargs) + + bookig_order = self.object + booking = bookig_order.booking + + start_date = booking.start_date + end_date = booking.end_date + free_days = booking.free_days + + booking_days = (end_date - start_date).days + 1 + original_price = booking.price + final_price = booking.final_price + context.update({ + 'original_price': original_price, + 'total_discount': original_price - final_price, + 'final_price': final_price, + 'booking_days': booking_days, + 'free_days': free_days, + 'start_date': start_date.strftime('%m/%d/%Y'), + 'end_date': end_date.strftime('%m/%d/%Y'), + }) + + return context + + +class BookingOrdersListView(LoginRequiredMixin, ListView): + template_name = "digitalglarus/booking_orders_list.html" + context_object_name = "orders" + login_url = reverse_lazy('digitalglarus:login') + model = BookingOrder + paginate_by = 10 + + def get_queryset(self): + queryset = super(BookingOrdersListView, self).get_queryset() + queryset = queryset.filter(customer__user=self.request.user) + return queryset + ############## OLD VIEWS class CalendarApi(View): diff --git a/membership/models.py b/membership/models.py index 7672a7b4..88e3e902 100644 --- a/membership/models.py +++ b/membership/models.py @@ -1,5 +1,8 @@ from datetime import datetime + + + from django.db import models from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User, AbstractBaseUser, BaseUserManager, AbstractUser, PermissionsMixin