From e3d1761d455874d4d3867421373d0ccf5414e674 Mon Sep 17 00:00:00 2001 From: Levi Date: Mon, 5 Sep 2016 19:45:45 -0500 Subject: [PATCH] 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 %} + + + + +
+