From 1152e41b6ef2d222bce4d1c29048d14f500ebbf6 Mon Sep 17 00:00:00 2001 From: Levi Date: Mon, 22 Aug 2016 02:52:29 -0500 Subject: [PATCH 1/3] Fixed membership payment view issues, Membership creation after a stripe charge, Membership order creation after stripe charge, Added membership activated view, Added membership activated html, Fixing membership cost function, Added function to format membership date ranges, Added membership calculated dates for first month into membership_payment html and membership_activated html --- .../migrations/0008_auto_20160820_1959.py | 30 ++++++++++ digitalglarus/models.py | 58 +++++++++++++++++-- .../digitalglarus/membership_activated.html | 57 ++++++++++++++++++ .../digitalglarus/membership_payment.html | 38 +++++++----- digitalglarus/urls.py | 6 +- digitalglarus/views.py | 52 ++++++++++++++--- 6 files changed, 212 insertions(+), 29 deletions(-) create mode 100644 digitalglarus/migrations/0008_auto_20160820_1959.py create mode 100644 digitalglarus/templates/digitalglarus/membership_activated.html 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/models.py b/digitalglarus/models.py index 612e1c02..e5b3ab88 100644 --- a/digitalglarus/models.py +++ b/digitalglarus/models.py @@ -1,7 +1,14 @@ + +import calendar +from datetime import datetime, date, timedelta from django.db import models from cms.models import CMSPlugin from filer.fields.image import FilerImageField from django.core.urlresolvers import reverse +from django.utils.functional import cached_property + +from membership.models import StripeCustomer +from utils.models import BillingAddress class MembershipType(models.Model): @@ -13,25 +20,69 @@ class MembershipType(models.Model): name = models.CharField(choices=MEMBERSHIP_TYPES, max_length=20) price = models.FloatField() + @cached_property + def days_left(self): + current_date = date.today() + _, days_in_month = calendar.monthrange(current_date.year, current_date.month) + pass_days = current_date.day + days_left = days_in_month - pass_days + 1 + return days_left + + @cached_property + def first_month_price(self): + current_date = date.today() + _, days_in_month = calendar.monthrange(current_date.year, current_date.month) + pass_days = current_date.day + days_left = days_in_month - pass_days + 1 + percentage = days_left / days_in_month + membership_price = self.price + final_price = membership_price * percentage + return final_price + + @cached_property + def first_month_range(self): + current_date = date.today() + _, days_in_month = calendar.monthrange(current_date.year, current_date.month) + pass_days = current_date.day + days_left = days_in_month - pass_days + end_date = current_date + timedelta(days=days_left) + return current_date, end_date + + @cached_property + def first_month_formated_range(self): + start_date, end_date = self.first_month_range + return "{} - {}".format(datetime.strftime(start_date, "%b, %d %Y"), + datetime.strftime(end_date, "%b, %d %Y")) + class Membership(models.Model): type = models.ForeignKey(MembershipType) @classmethod - def create(cls, data, user): + def create(cls, data): instance = cls.objects.create(**data) - instance.assign_permissions(user) return instance class MembershipOrder(models.Model): membership = models.ForeignKey(Membership) + customer = models.ForeignKey(StripeCustomer) + billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) approved = models.BooleanField(default=False) last4 = models.CharField(max_length=4) cc_brand = models.CharField(max_length=10) stripe_charge_id = models.CharField(max_length=100, null=True) + @classmethod + def create(cls, data): + stripe_charge = data.pop('stripe_charge', None) + instance = cls.objects.create(**data) + instance.stripe_charge_id = stripe_charge.id + instance.last4 = stripe_charge.source.last4 + instance.cc_brand = stripe_charge.source.brand + return instance + class Supporter(models.Model): name = models.CharField(max_length=200) @@ -44,9 +95,6 @@ class Supporter(models.Model): return reverse('dgSupporters_view', args=[self.pk]) - - - class DGGallery(models.Model): parent = models.ForeignKey('self', blank=True, null=True) name = models.CharField(max_length=30) diff --git a/digitalglarus/templates/digitalglarus/membership_activated.html b/digitalglarus/templates/digitalglarus/membership_activated.html new file mode 100644 index 00000000..891ea27f --- /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 %} +
+ + + + +
+
+
+
+
+
+ Digital Glarus
+ In der Au 7 Schwanden 8762 Switzerland +
info@digitalglarus.ch +
+ (044) 534-66-22 +

 

+
+
+

 

+
+
+
+
+{% 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 + }
@@ -23,9 +33,13 @@ Additional day costs 15CHF per day. More than 17 days a month it will charge only 290CHF/month. +

+ You will be charged on the first of the month until you + cancel your subscription. Previous charges won't be refunded. +


Member Name

-

Nico Schottelius

+

{{request.user.name}}


Billing Adress

-
+
@@ -84,7 +92,7 @@
- +
+
+
+
+ + + +
+
+
+
+
+ Digital Glarus
+ In der Au 7 Schwanden 8762 Switzerland +
info@digitalglarus.ch +
+ (044) 534-66-22 +

 

+
+
+

 

+
+
+
+
+ + +{% if stripe_key %} + + +{%endif%} + +{% endblock %} \ No newline at end of file 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 %} +
+
+
+
+
+ +

Digital Glarus Membership

+

{{membership_type.price}}CHF/month
(free working 2days included)

+
+
+
+ Get your Digital Glarus Membership for only + {{membership_type.price}}CHF + 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 15CHF + 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 290CHF/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. +

+ +
+ +
+
+
+
+
+ +
+
+
+
+ + Become our member + +
+

Our membership includes:

+
+
  • +
    Super-fast Internet
    +
    Any workspace in our common areas
    +
    Free 2 working day passes
    +
    Access to any of our great workshops
    +
    Special invitation to our fondue nights, cooking & hacking sessions, coworking & cohiking, and many more cool stuff.
    +
  • + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + Digital Glarus
    + In der Au 7 Schwanden 8762 Switzerland +
    info@digitalglarus.ch +
    + (044) 534-66-22 +

     

    +
    +
    +

     

    +
    +
    +
    +
    +{% 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 @@
    {% csrf_token %} - + {% 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..b7223997 100644 --- a/digitalglarus/templates/new_base_glarus.html +++ b/digitalglarus/templates/new_base_glarus.html @@ -93,7 +93,7 @@ Log In
  • - Sign Up + Sign Up
  • @@ -167,11 +167,24 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/digitalglarus/urls.py b/digitalglarus/urls.py index e81bd8b5..45884468 100644 --- a/digitalglarus/urls.py +++ b/digitalglarus/urls.py @@ -3,7 +3,8 @@ from django.utils.translation import ugettext_lazy as _ from . import views from .views import ContactView, IndexView, AboutView, HistoryView, LoginView, SignupView,\ - PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView + PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView,\ + MembershipPricingView, BookingSelectDatesView, BookingPaymentView # from membership.views import LoginRegistrationView urlpatterns = [ @@ -11,13 +12,19 @@ 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[0-9A-Za-z]+)-(?P.+)/$', PasswordResetConfirmView.as_view(), name='reset_password_confirm'), url(_(r'history/?$'), HistoryView.as_view(), name='history'), + url(_(r'booking/?$'), BookingSelectDatesView.as_view(), name='booking'), + url(_(r'booking/payment/?$'), BookingPaymentView.as_view(), name='booking_payment'), 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\d+)/(?P\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 d0edcd05..479eadfc 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -28,8 +28,11 @@ from utils.forms import PasswordResetRequestForm from utils.stripe_utils import StripeUtils -from .forms import LoginForm, SignupForm, MembershipBillingForm -from .models import MembershipType, Membership, MembershipOrder +from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\ + BookingBillingForm + +from .models import MembershipType, Membership, MembershipOrder, Booking, BookingPrice,\ + BookingOrder class IndexView(TemplateView): @@ -63,7 +66,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,9 +75,149 @@ class HistoryView(TemplateView): return context +class BookingSelectDatesView(LoginRequiredMixin, FormView): + template_name = "digitalglarus/booking.html" + form_class = BookingDateForm + 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 + 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, FormView): + template_name = "digitalglarus/booking_payment.html" + form_class = BookingBillingForm + 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_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') + + original_price, discount_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=original_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': discount_price, + } + booking = Booking.create(booking_data) + + # Create membership order + order_data = { + 'booking': booking, + 'customer': customer, + 'billing_address': billing_address, + 'stripe_charge': charge + } + BookingOrder.create(order_data) + + # request.session.update({ + # 'membership_price': membership.type.first_month_price, + # 'membership_dates': membership.type.first_month_formated_range + # }) + 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): From e3d1761d455874d4d3867421373d0ccf5414e674 Mon Sep 17 00:00:00 2001 From: Levi Date: Mon, 5 Sep 2016 19:45:45 -0500 Subject: [PATCH 3/3] Now booking is only for users with memberships. Added method to know if a membership is active or not. Added new formula for booking pricing. handling border cases when an user want to book. Added booking order detail view. Added booking order detail html. --- digitalglarus/forms.py | 2 +- .../migrations/0014_booking_final_price.py | 21 +++++ digitalglarus/mixins.py | 13 +++ digitalglarus/models.py | 67 ++++++++------ .../static/digitalglarus/js/booking.js | 7 +- .../static/digitalglarus/js/utils.js | 21 +++++ .../digitalglarus/booking_orders_detail.html | 88 +++++++++++++++++++ .../digitalglarus/booking_orders_list.html | 68 ++++++++++++++ .../digitalglarus/booking_payment.html | 12 ++- .../digitalglarus/membership_activated.html | 2 +- digitalglarus/templates/new_base_glarus.html | 3 + digitalglarus/urls.py | 7 +- digitalglarus/views.py | 76 +++++++++++++--- membership/models.py | 3 + 14 files changed, 345 insertions(+), 45 deletions(-) create mode 100644 digitalglarus/migrations/0014_booking_final_price.py create mode 100644 digitalglarus/static/digitalglarus/js/utils.js create mode 100644 digitalglarus/templates/digitalglarus/booking_orders_detail.html create mode 100644 digitalglarus/templates/digitalglarus/booking_orders_list.html diff --git a/digitalglarus/forms.py b/digitalglarus/forms.py index 03a5e6cd..db3aab94 100644 --- a/digitalglarus/forms.py +++ b/digitalglarus/forms.py @@ -68,7 +68,7 @@ class BookingDateForm(forms.Form): except ValueError: raise forms.ValidationError("Submit valid dates.") - if start_date >= end_date: + if start_date > end_date: raise forms.ValidationError("Your end date must be greather than your start date.") return start_date, end_date 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 index 6d555f24..13329f73 100644 --- a/digitalglarus/mixins.py +++ b/digitalglarus/mixins.py @@ -1,8 +1,20 @@ 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) @@ -22,4 +34,5 @@ class Ordereable(models.Model): 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 e229fabd..49d1327f 100644 --- a/digitalglarus/models.py +++ b/digitalglarus/models.py @@ -1,7 +1,9 @@ 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 @@ -59,6 +61,14 @@ class MembershipType(models.Model): class Membership(models.Model): type = models.ForeignKey(MembershipType) + @classmethod + 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) @@ -88,6 +98,7 @@ class Booking(models.Model): end_date = models.DateField() price = models.FloatField() free_days = models.IntegerField(default=0) + final_price = models.FloatField() @classmethod def create(cls, data): @@ -95,52 +106,50 @@ class Booking(models.Model): return instance @classmethod - def get_ramaining_free_days(cls, user): - # ZERO_DAYS = 0 - # ONE_DAY = 1 + 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 = TWO_DAYS - sum(map(lambda x: x.days, current_month_bookings)) - return free_days - - # free_days = ZERO_DAYS if current_month_bookings.count() > 2 else TWO_DAYS - # if current_month_bookings.count() == 1: - # booking = current_month_bookings.get() - # booked_days = (booking.end_date - booking.start_date).days - # free_days = ONE_DAY if booked_days == 1 else ZERO_DAYS - # return free_days - - # free_days = ZERO_DAYS if current_month_bookings.count() > 2 else TWO_DAYS - # return free_days + 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): - """ - Calculate the booking price for requested dates - How it does: - 1. Check if the user has booked the current month - 2. Get how many days user wants to book - 3. Get price per day from BookingPrices instance - 4. Get available free days - 5. Calculate price by this formula -> (booking_days - free_days) * price_per_day - """ + + 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 + 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) - final_booking_price = (booking_days - free_days) * price_per_day - original_booking_price = (booking_days) * price_per_day + free_days = cls.get_ramaining_free_days(user, start_date, end_date) + final_booking_price = normal_price - (free_days * price_per_day) - return original_booking_price, final_booking_price, free_days + 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): name = models.CharField(max_length=200) diff --git a/digitalglarus/static/digitalglarus/js/booking.js b/digitalglarus/static/digitalglarus/js/booking.js index ed1929d4..0cb97667 100644 --- a/digitalglarus/static/digitalglarus/js/booking.js +++ b/digitalglarus/static/digitalglarus/js/booking.js @@ -1,13 +1,16 @@ $( 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, }); 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_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 %} + + + + + +
    +
    + + +{% if stripe_key %} + + +{%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 %} + + + + +
    +