diff --git a/digitalglarus/admin.py b/digitalglarus/admin.py index 0c84adb1..a83ac4ad 100644 --- a/digitalglarus/admin.py +++ b/digitalglarus/admin.py @@ -1,5 +1,7 @@ from django.contrib import admin -from .models import Supporter, DGGallery, DGPicture +from .models import Supporter, DGGallery, DGPicture, Booking, BookingPrice,\ + MembershipOrder, Membership, MembershipType, BookingOrder + from utils.models import ContactMessage # class DGPictureInline(admin.StackedInline): @@ -10,4 +12,9 @@ class DGGalleryAdmin(admin.ModelAdmin): admin.site.register(DGGallery, DGGalleryAdmin) admin.site.register(ContactMessage) -admin.site.register(Supporter) \ No newline at end of file +admin.site.register(Booking) +admin.site.register(BookingPrice) +admin.site.register(MembershipOrder) +admin.site.register(Membership) +admin.site.register(MembershipType) +admin.site.register(BookingOrder) diff --git a/digitalglarus/forms.py b/digitalglarus/forms.py index db3aab94..99db95f7 100644 --- a/digitalglarus/forms.py +++ b/digitalglarus/forms.py @@ -1,11 +1,14 @@ from django import forms +from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from datetime import datetime + from utils.models import BillingAddress from utils.forms import LoginFormMixin, SignupFormMixin, BillingAddressForm from .models import MembershipType +from .models import Booking class LoginForm(LoginFormMixin): @@ -55,9 +58,10 @@ class BookingBillingForm(BillingAddressForm): class BookingDateForm(forms.Form): - start_date = forms.DateField(required=False) - end_date = forms.DateField(required=False) - date_range = forms.CharField(required=False) + start_date = forms.DateField(required=False, widget=forms.HiddenInput()) + end_date = forms.DateField(required=False, widget=forms.HiddenInput()) + date_range = forms.CharField(required=False, + widget=forms.TextInput(attrs={'id': 'booking-date-range'})) def clean_date_range(self): date_range = self.cleaned_data.get('date_range') @@ -70,10 +74,19 @@ class BookingDateForm(forms.Form): if start_date > end_date: raise forms.ValidationError("Your end date must be greather than your start date.") + + q1 = Q(start_date__lte=start_date, end_date__gte=start_date) + q2 = Q(start_date__gt=start_date, start_date__lte=end_date) + if Booking.objects.filter(q1 | q2).exists(): + raise forms.ValidationError("You already have a booking in these dates.") + 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 + # import pdb + # pdb.set_trace() + if self.cleaned_data.get('date_range'): + 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/0015_auto_20160908_0149.py b/digitalglarus/migrations/0015_auto_20160908_0149.py new file mode 100644 index 00000000..cd41f920 --- /dev/null +++ b/digitalglarus/migrations/0015_auto_20160908_0149.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-09-08 01:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0014_booking_final_price'), + ] + + operations = [ + migrations.AddField( + model_name='bookingorder', + name='amount', + field=models.FloatField(default=35), + preserve_default=False, + ), + migrations.AddField( + model_name='membershiporder', + name='amount', + field=models.FloatField(default=35.0), + preserve_default=False, + ), + ] diff --git a/digitalglarus/migrations/0016_auto_20160909_0110.py b/digitalglarus/migrations/0016_auto_20160909_0110.py new file mode 100644 index 00000000..03d68203 --- /dev/null +++ b/digitalglarus/migrations/0016_auto_20160909_0110.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-09-09 01:10 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('digitalglarus', '0015_auto_20160908_0149'), + ] + + operations = [ + migrations.RenameField( + model_name='bookingprice', + old_name='special_price_offer', + new_name='special_month_price', + ), + migrations.AddField( + model_name='bookingorder', + name='original_price', + field=models.FloatField(default=20), + preserve_default=False, + ), + migrations.AddField( + model_name='bookingorder', + name='special_month_price', + field=models.FloatField(default=290), + preserve_default=False, + ), + ] diff --git a/digitalglarus/mixins.py b/digitalglarus/mixins.py index 13329f73..d224d0f3 100644 --- a/digitalglarus/mixins.py +++ b/digitalglarus/mixins.py @@ -4,7 +4,7 @@ from membership.models import StripeCustomer from utils.models import BillingAddress -class MembershipRequired(object): +class MembershipRequiredMixin(object): membership_redirect_url = None def dispatch(self, request, *args, **kwargs): @@ -12,11 +12,23 @@ class MembershipRequired(object): if not Membership.is_digitalglarus_member(request.user): return HttpResponseRedirect(self.membership_redirect_url) - return super(MembershipRequired, self).dispatch(request, *args, **kwargs) + return super(MembershipRequiredMixin, self).dispatch(request, *args, **kwargs) + + +class IsNotMemberMixin(object): + already_member_redirect_url = None + + def dispatch(self, request, *args, **kwargs): + from .models import Membership + if Membership.is_digitalglarus_member(request.user): + return HttpResponseRedirect(self.already_member_redirect_url) + + return super(MembershipRequiredMixin, self).dispatch(request, *args, **kwargs) class Ordereable(models.Model): customer = models.ForeignKey(StripeCustomer) + amount = models.FloatField() billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) approved = models.BooleanField(default=False) diff --git a/digitalglarus/models.py b/digitalglarus/models.py index 49d1327f..1da796a2 100644 --- a/digitalglarus/models.py +++ b/digitalglarus/models.py @@ -64,7 +64,8 @@ class Membership(models.Model): @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_current_month = Q(membershiporder__customer__user=user, + 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() @@ -78,6 +79,25 @@ class Membership(models.Model): class MembershipOrder(Ordereable, models.Model): membership = models.ForeignKey(Membership) + @classmethod + def current_membership(cls, user): + last_payment = cls.objects.\ + filter(customer__user=user).last() + start_date = last_payment.created_at + _, days_in_month = calendar.monthrange(start_date.year, + start_date.month) + start_date.replace(day=1) + end_date = start_date + timedelta(days=days_in_month) + return start_date, end_date + + def get_membership_range_date(self): + start_date = self.created_at + _, days_in_month = calendar.monthrange(start_date.year, + start_date.month) + start_date.replace(day=1) + end_date = start_date + timedelta(days=days_in_month) + return start_date, end_date + @classmethod def create(cls, data): stripe_charge = data.pop('stripe_charge', None) @@ -85,12 +105,13 @@ class MembershipOrder(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 class BookingPrice(models.Model): price_per_day = models.FloatField() - special_price_offer = models.FloatField() + special_month_price = models.FloatField() class Booking(models.Model): @@ -124,7 +145,7 @@ class Booking(models.Model): @classmethod def booking_price(cls, user, start_date, end_date): - MAX_MONTH_PRICE = 290 + MAX_MONTH_PRICE = BookingPrice.objects.last().special_month_price MAX_MONTH_DAYS_PROMOTION = 31 MIN_MONTH_DAYS_PROMOTION = 19 @@ -146,6 +167,8 @@ class Booking(models.Model): class BookingOrder(Ordereable, models.Model): booking = models.OneToOneField(Booking) + original_price = models.FloatField() + special_month_price = models.FloatField() def booking_days(self): return (self.booking.end_date - self.booking.start_date).days + 1 diff --git a/digitalglarus/templates/digitalglarus/booking.html b/digitalglarus/templates/digitalglarus/booking.html index 6a8d4374..2cebebfb 100644 --- a/digitalglarus/templates/digitalglarus/booking.html +++ b/digitalglarus/templates/digitalglarus/booking.html @@ -14,12 +14,17 @@

Start coworking at Digital Glarus!
Membership costs only 35CHF per month.
2 free working days included!


- + {% bootstrap_form_errors form layout='inline' %}
{% csrf_token %} - + {% bootstrap_form_errors form layout='inline' %} + {% for field in form %} + {% bootstrap_field field show_label=False %} + {% endfor %} + +

diff --git a/digitalglarus/templates/digitalglarus/membership_orders_detail.html b/digitalglarus/templates/digitalglarus/membership_orders_detail.html new file mode 100644 index 00000000..a63342f9 --- /dev/null +++ b/digitalglarus/templates/digitalglarus/membership_orders_detail.html @@ -0,0 +1,80 @@ +{% 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/membership_orders_list.html b/digitalglarus/templates/digitalglarus/membership_orders_list.html new file mode 100644 index 00000000..eebf0f7b --- /dev/null +++ b/digitalglarus/templates/digitalglarus/membership_orders_list.html @@ -0,0 +1,68 @@ +{% extends "new_base_glarus.html" %} +{% load staticfiles bootstrap3 i18n %} +{% block content %} + + + + +
+
@@ -174,8 +227,6 @@ --> - - diff --git a/digitalglarus/urls.py b/digitalglarus/urls.py index 63560e61..2fbad407 100644 --- a/digitalglarus/urls.py +++ b/digitalglarus/urls.py @@ -5,7 +5,7 @@ from . import views from .views import ContactView, IndexView, AboutView, HistoryView, LoginView, SignupView,\ PasswordResetView, PasswordResetConfirmView, MembershipPaymentView, MembershipActivatedView,\ MembershipPricingView, BookingSelectDatesView, BookingPaymentView, OrdersBookingDetailView,\ - BookingOrdersListView + BookingOrdersListView, MembershipOrdersListView, OrdersMembershipDetailView # from membership.views import LoginRegistrationView urlpatterns = [ @@ -30,6 +30,10 @@ urlpatterns = [ name='membership_activated'), url(_(r'membership/pricing/?$'), MembershipPricingView.as_view(), name='membership_pricing'), + url(_(r'membership/orders/(?P\d+)/?$'), OrdersMembershipDetailView.as_view(), + name='membership_orders_detail'), + url(_(r'membership/orders/?$'), MembershipOrdersListView.as_view(), + name='membership_orders_list'), 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 bebc5d88..f37dd6eb 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -34,7 +34,7 @@ from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm from .models import MembershipType, Membership, MembershipOrder, Booking, BookingPrice,\ BookingOrder -from .mixins import MembershipRequired +from .mixins import MembershipRequiredMixin, IsNotMemberMixin class IndexView(TemplateView): @@ -44,7 +44,7 @@ class IndexView(TemplateView): class LoginView(LoginViewMixin): template_name = "digitalglarus/login.html" form_class = LoginForm - success_url = reverse_lazy('digitalglarus:landing') + success_url = reverse_lazy('digitalglarus:membership_pricing') class SignupView(SignupViewMixin): @@ -77,7 +77,7 @@ class HistoryView(TemplateView): return context -class BookingSelectDatesView(LoginRequiredMixin, MembershipRequired, FormView): +class BookingSelectDatesView(LoginRequiredMixin, MembershipRequiredMixin, FormView): template_name = "digitalglarus/booking.html" form_class = BookingDateForm membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing') @@ -102,7 +102,7 @@ class BookingSelectDatesView(LoginRequiredMixin, MembershipRequired, FormView): return super(BookingSelectDatesView, self).form_valid(form) -class BookingPaymentView(LoginRequiredMixin, MembershipRequired, FormView): +class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView): template_name = "digitalglarus/booking_payment.html" form_class = BookingBillingForm membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing') @@ -199,7 +199,10 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequired, FormView): 'booking': booking, 'customer': customer, 'billing_address': billing_address, - 'stripe_charge': charge + 'stripe_charge': charge, + 'amount': final_price, + 'original_price': normal_price, + 'special_month_price': BookingPrice.objects.last().special_month_price } order = BookingOrder.create(order_data) @@ -208,8 +211,6 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequired, FormView): # '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): @@ -224,10 +225,11 @@ class MembershipPricingView(TemplateView): return context -class MembershipPaymentView(LoginRequiredMixin, FormView): +class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView): template_name = "digitalglarus/membership_payment.html" login_url = reverse_lazy('digitalglarus:signup') form_class = MembershipBillingForm + already_member_redirect_url = reverse_lazy('digitalglarus:membership_orders_list') def get_form_kwargs(self): self.membership_type = MembershipType.objects.get(name='standard') @@ -291,7 +293,8 @@ class MembershipPaymentView(LoginRequiredMixin, FormView): 'membership': membership, 'customer': customer, 'billing_address': billing_address, - 'stripe_charge': charge + 'stripe_charge': charge, + 'amount': membership_type.first_month_price } MembershipOrder.create(order_data) @@ -319,6 +322,45 @@ class MembershipActivatedView(TemplateView): return context +class MembershipOrdersListView(LoginRequiredMixin, ListView): + template_name = "digitalglarus/membership_orders_list.html" + context_object_name = "orders" + login_url = reverse_lazy('digitalglarus:login') + model = MembershipOrder + paginate_by = 10 + + def get_context_data(self, **kwargs): + context = super(MembershipOrdersListView, self).get_context_data(**kwargs) + start_date, end_date = MembershipOrder.current_membership(self.request.user) + context.update({ + 'membership_start_date': start_date, + 'membership_end_date': end_date, + }) + return context + + def get_queryset(self): + queryset = super(MembershipOrdersListView, self).get_queryset() + queryset = queryset.filter(customer__user=self.request.user) + return queryset + + +class OrdersMembershipDetailView(LoginRequiredMixin, DetailView): + template_name = "digitalglarus/membership_orders_detail.html" + context_object_name = "order" + login_url = reverse_lazy('digitalglarus:login') + # permission_required = ['view_hostingorder'] + model = MembershipOrder + + def get_context_data(self, **kwargs): + context = super(OrdersMembershipDetailView, self).get_context_data(**kwargs) + start_date, end_date = self.object.get_membership_range_date() + context.update({ + 'membership_start_date': start_date, + 'membership_end_date': end_date, + }) + return context + + class OrdersBookingDetailView(LoginRequiredMixin, DetailView): template_name = "digitalglarus/booking_orders_detail.html" context_object_name = "order"