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
new file mode 100644
index 00000000..f5bb53aa
--- /dev/null
+++ b/digitalglarus/forms.py
@@ -0,0 +1,117 @@
+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, MembershipOrder
+from .models import Booking
+
+
+class LoginForm(LoginFormMixin):
+ email = forms.CharField(widget=forms.EmailInput())
+ password = forms.CharField(widget=forms.PasswordInput())
+
+
+class SignupForm(SignupFormMixin):
+ confirm_password = forms.CharField(widget=forms.PasswordInput())
+ password = forms.CharField(widget=forms.PasswordInput())
+ name = forms.CharField(label='name',
+ widget=forms.TextInput(attrs={'placeholder': 'Full name'}))
+
+
+class MembershipBillingForm(BillingAddressForm):
+ token = forms.CharField(widget=forms.HiddenInput())
+ membership_type = forms.ModelChoiceField(queryset=MembershipType.objects.all(),
+ widget=forms.HiddenInput())
+
+ class Meta:
+ model = BillingAddress
+ fields = ['membership_type', 'street_address', 'city', 'postal_code', 'country']
+ labels = {
+ 'street_address': _('Street Address'),
+ 'city': _('City'),
+ 'postal_code': _('Postal Code'),
+ 'country': _('Country'),
+ }
+
+
+class MembershipOrderForm(forms.ModelForm):
+
+ class Meta:
+ model = MembershipOrder
+ fields = ['membership', 'customer', 'billing_address',
+ 'last4', 'cc_brand', 'stripe_charge_id', 'amount']
+
+ # def save(self, commit=True):
+ # instance = super(MembershipOrderForm, self).save(commit=False)
+
+ # # if commit:
+ # # DonatorStatus.create(self.cleaned_data['donator'].user)
+ # # instance.save()
+
+ # return instance
+
+
+class BookingBillingForm(BillingAddressForm):
+ token = forms.CharField(widget=forms.HiddenInput(), required=False)
+ 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,
+ widget=forms.TextInput(attrs={'id': 'booking-date-1',
+ 'value': 'Select your date'}))
+ end_date = forms.DateField(required=False,
+ widget=forms.TextInput(attrs={'id': 'booking-date-2'}))
+
+ def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop('user')
+ super(BookingDateForm, self).__init__(*args, **kwargs)
+
+ def clean_start_date(self):
+ start_date = self.cleaned_data.get('start_date')
+ if not start_date:
+ raise forms.ValidationError("This field is required.")
+ return start_date
+
+ def clean_end_date(self):
+ end_date = self.cleaned_data.get('end_date')
+ if not end_date:
+ raise forms.ValidationError("This field is required.")
+ return end_date
+
+ def clean(self):
+ start_date = self.cleaned_data.get('start_date')
+ end_date = self.cleaned_data.get('end_date')
+
+ if not start_date or not end_date:
+ return self.cleaned_data
+
+ if start_date > end_date:
+ raise forms.ValidationError("Your end date must be greather than your start date.")
+
+ q1 = Q(bookingorder__customer__user=self.user,
+ start_date__lte=start_date, end_date__gte=start_date)
+ q2 = Q(bookingorder__customer__user=self.user,
+ 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 self.cleaned_data
diff --git a/digitalglarus/management/commands/make_membership_charge.py b/digitalglarus/management/commands/make_membership_charge.py
new file mode 100644
index 00000000..41013b12
--- /dev/null
+++ b/digitalglarus/management/commands/make_membership_charge.py
@@ -0,0 +1,97 @@
+from django.core.management.base import BaseCommand
+
+from datetime import datetime
+from django.utils import translation
+
+from utils.stripe_utils import StripeUtils
+
+from utils.mailer import BaseEmail
+from digitalglarus.models import MembershipOrder
+from digitalglarus.forms import MembershipOrderForm
+# from membership.U
+# from nosystemd.models import DonatorStatus, Donation
+# from nosystemd.forms import DonationForm
+
+
+class Command(BaseCommand):
+ help = 'Make the monthly stripe charge to all donators'
+ CURRENCY = 'usd'
+
+ def handle(self, *args, **options):
+ translation.activate('en-us')
+ memberships_orders = MembershipOrder.objects.filter(membership__active=True)
+ current_month = datetime.now().month
+ current_year = datetime.now().year
+
+ print("--------- STARTING MEMBERSHIP CHARGING SCRIPT ---------")
+ print("Memberhips date: %s-%s" % (current_month, current_year))
+
+ for membership_order in memberships_orders:
+ member = membership_order.customer
+ try:
+ MembershipOrder.objects.get(created_at__month=current_month,
+ created_at__year=current_year,
+ customer=member)
+ except MembershipOrder.DoesNotExist:
+ try:
+ current_membership_price = membership_order.membership.type.price
+
+ last_membership_order = MembershipOrder.objects.filter(customer=member).last()
+
+ # Make stripe charge to a customer
+ stripe_utils = StripeUtils()
+ stripe_utils.CURRENCY = self.CURRENCY
+ charge_response = stripe_utils.make_charge(amount=current_membership_price,
+ customer=member.stripe_id)
+ charge = charge_response.get('response_object')
+ # Check if the payment was approved
+ if not charge:
+ # There is an error trying to creating the stripe charge
+ context = {
+ 'paymentError': charge_response.get('error'),
+ }
+ print("--------- STRIPE PAYMENT ERROR ---------")
+ print(context)
+ print("-------------------------")
+ continue
+
+ # Create a donation
+ charge = charge_response.get('response_object')
+ membership_order_data = {
+ 'cc_brand': charge.source.brand,
+ 'stripe_charge_id': charge.id,
+ 'last4': charge.source.last4,
+ 'membership': last_membership_order.membership.id,
+ 'billing_address': last_membership_order.billing_address.id,
+ 'customer': member.id,
+ 'amount': current_membership_price
+ }
+ membership_order_form = MembershipOrderForm(membership_order_data)
+ if membership_order_form.is_valid():
+ membership_order = membership_order_form.save()
+
+ context = {
+ 'order': membership_order,
+ 'base_url': "{0}://{1}".format('https', 'dynamicweb.ungleich.ch')
+
+ }
+ email_data = {
+ 'subject': 'Your monthly membership has been charged',
+ 'to': member.user.email,
+ 'context': context,
+ 'template_name': 'membership_monthly_charge',
+ 'template_path': 'digitalglarus/emails/'
+ }
+ email = BaseEmail(**email_data)
+ email.send()
+
+ print("--------- PAYMENT DONATION SUCCESSFULL ---------")
+ print("Member: %s" % member.user.email)
+ print("Amount: %s %s" % (current_membership_price, self.CURRENCY))
+ print("-----------------------------------------------")
+
+ except Exception as e:
+ print("--------- ERROR ---------")
+ print(e)
+ print("-------------------------")
+ continue
diff --git a/digitalglarus/migrations/0007_auto_20160820_0408.py b/digitalglarus/migrations/0007_auto_20160820_0408.py
new file mode 100644
index 00000000..0c719572
--- /dev/null
+++ b/digitalglarus/migrations/0007_auto_20160820_0408.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-08-20 04:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0006_delete_message'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Membership',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MembershipOrder',
+ 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)),
+ ('membership', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='digitalglarus.Membership')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MembershipType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(choices=[('standard', 'Standard')], max_length=20)),
+ ('price', models.FloatField()),
+ ],
+ ),
+ migrations.AddField(
+ model_name='membership',
+ name='type',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='digitalglarus.MembershipType'),
+ ),
+ ]
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/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/migrations/0017_membership_active.py b/digitalglarus/migrations/0017_membership_active.py
new file mode 100644
index 00000000..167fc18d
--- /dev/null
+++ b/digitalglarus/migrations/0017_membership_active.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-09-13 01:51
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0016_auto_20160909_0110'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='membership',
+ name='active',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/digitalglarus/migrations/0018_auto_20160928_0424.py b/digitalglarus/migrations/0018_auto_20160928_0424.py
new file mode 100644
index 00000000..10a92271
--- /dev/null
+++ b/digitalglarus/migrations/0018_auto_20160928_0424.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-09-28 04:24
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0017_membership_active'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='bookingorder',
+ name='membership_required_months',
+ field=models.IntegerField(default=0),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='bookingorder',
+ name='membership_required_months_price',
+ field=models.FloatField(default=0),
+ preserve_default=False,
+ ),
+ ]
diff --git a/digitalglarus/migrations/0019_auto_20160929_0324.py b/digitalglarus/migrations/0019_auto_20160929_0324.py
new file mode 100644
index 00000000..f78cf3e7
--- /dev/null
+++ b/digitalglarus/migrations/0019_auto_20160929_0324.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-09-29 03:24
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0018_auto_20160928_0424'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='bookingorder',
+ name='membership_required_months',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AlterField(
+ model_name='bookingorder',
+ name='membership_required_months_price',
+ field=models.FloatField(default=0),
+ ),
+ ]
diff --git a/digitalglarus/migrations/0020_auto_20161013_0253.py b/digitalglarus/migrations/0020_auto_20161013_0253.py
new file mode 100644
index 00000000..f9d49868
--- /dev/null
+++ b/digitalglarus/migrations/0020_auto_20161013_0253.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-10-13 02:53
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0019_auto_20160929_0324'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='bookingorder',
+ name='cc_brand',
+ field=models.CharField(blank=True, max_length=10),
+ ),
+ migrations.AlterField(
+ model_name='bookingorder',
+ name='last4',
+ field=models.CharField(blank=True, max_length=4),
+ ),
+ migrations.AlterField(
+ model_name='membershiporder',
+ name='cc_brand',
+ field=models.CharField(blank=True, max_length=10),
+ ),
+ migrations.AlterField(
+ model_name='membershiporder',
+ name='last4',
+ field=models.CharField(blank=True, max_length=4),
+ ),
+ ]
diff --git a/digitalglarus/migrations/0021_auto_20161017_1958.py b/digitalglarus/migrations/0021_auto_20161017_1958.py
new file mode 100644
index 00000000..69b44dde
--- /dev/null
+++ b/digitalglarus/migrations/0021_auto_20161017_1958.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-10-17 19:58
+from __future__ import unicode_literals
+
+import datetime
+from django.db import migrations, models
+from django.utils.timezone import utc
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0020_auto_20161013_0253'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='membership',
+ name='end_date',
+ field=models.DateField(default=datetime.datetime(2016, 10, 17, 19, 58, 0, 209303, tzinfo=utc)),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='membership',
+ name='start_date',
+ field=models.DateField(default=datetime.datetime(2016, 10, 17, 19, 58, 7, 361473, tzinfo=utc)),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='membershiporder',
+ name='end_date',
+ field=models.DateField(default=datetime.datetime(2016, 10, 17, 19, 58, 15, 657240, tzinfo=utc)),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='membershiporder',
+ name='start_date',
+ field=models.DateField(default=datetime.datetime(2016, 10, 17, 19, 58, 16, 897120, tzinfo=utc)),
+ preserve_default=False,
+ ),
+ ]
diff --git a/digitalglarus/migrations/0022_auto_20161023_0218.py b/digitalglarus/migrations/0022_auto_20161023_0218.py
new file mode 100644
index 00000000..cc576832
--- /dev/null
+++ b/digitalglarus/migrations/0022_auto_20161023_0218.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-10-23 02:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('digitalglarus', '0021_auto_20161017_1958'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='bookingorder',
+ name='membership_required_months',
+ ),
+ migrations.RemoveField(
+ model_name='bookingorder',
+ name='membership_required_months_price',
+ ),
+ ]
diff --git a/digitalglarus/mixins.py b/digitalglarus/mixins.py
new file mode 100644
index 00000000..5392ad37
--- /dev/null
+++ b/digitalglarus/mixins.py
@@ -0,0 +1,52 @@
+from django.db import models
+from django.http import HttpResponseRedirect
+from membership.models import StripeCustomer
+from utils.models import BillingAddress
+
+
+class MembershipRequiredMixin(object):
+ membership_redirect_url = None
+
+ def dispatch(self, request, *args, **kwargs):
+ from .models import Membership
+ if not Membership.is_digitalglarus_active_member(request.user):
+ return HttpResponseRedirect(self.membership_redirect_url)
+
+ 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_active_member(request.user):
+ return HttpResponseRedirect(self.already_member_redirect_url)
+
+ return super(IsNotMemberMixin, 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)
+ last4 = models.CharField(max_length=4, blank=True)
+ cc_brand = models.CharField(max_length=10, blank=True)
+ 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)
+ if not stripe_charge:
+ return instance
+ 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 3939dac1..876df6d7 100644
--- a/digitalglarus/models.py
+++ b/digitalglarus/models.py
@@ -1,7 +1,234 @@
+
+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):
+
+ STANDARD = 'standard'
+ MEMBERSHIP_TYPES = (
+ (STANDARD, 'Standard'),
+
+ )
+ 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)
+ active = models.BooleanField(default=True)
+ start_date = models.DateField()
+ end_date = models.DateField()
+
+ @classmethod
+ def get_by_user(cls, user):
+ return cls.objects.\
+ filter(membershiporder__customer__user=user).last()
+
+ @classmethod
+ def create(cls, data):
+ instance = cls.objects.create(**data)
+ return instance
+
+ @classmethod
+ def is_digitalglarus_active_member(cls, user):
+ past_month = (datetime.today() - relativedelta(months=1)).month
+ has_order_current_month = Q(membershiporder__customer__user=user,
+ membershiporder__created_at__month=datetime.today().month)
+ has_order_past_month = Q(membershiporder__customer__user=user,
+ membershiporder__created_at__month=past_month)
+ active_membership = Q(active=True)
+ return cls.objects.filter(has_order_past_month | has_order_current_month).\
+ filter(active_membership).exists()
+
+ def update_dates(self, start_date, end_date):
+ self.start_date = start_date
+ self.end_date = end_date
+ self.save()
+
+ def deactivate(self):
+ self.active = False
+ self.save()
+
+
+class MembershipOrder(Ordereable, models.Model):
+ membership = models.ForeignKey(Membership)
+ start_date = models.DateField()
+ end_date = models.DateField()
+
+ @classmethod
+ def current_membership_dates(cls, user):
+ last_membership_payment = cls.objects.\
+ filter(customer__user=user).last()
+ if not last_membership_payment:
+ return [None, None]
+
+ return last_membership_payment.start_date, last_membership_payment.end_date
+
+ @classmethod
+ def next_membership_dates(cls, user):
+ current_start_date, current_end_date = cls.current_membership_dates(user)
+ if not current_start_date or not current_end_date:
+ return [None, None]
+ next_start_date = current_end_date + relativedelta(months=1)
+ _, days_in_month = calendar.monthrange(next_start_date.year,
+ next_start_date.month)
+ next_start_date = next_start_date.replace(day=1)
+ next_end_date = next_start_date + timedelta(days=days_in_month)
+ return next_start_date, next_end_date
+
+ def first_membership_range_date(self):
+ start_date = self.created_at
+ _, days_in_month = calendar.monthrange(start_date.year,
+ start_date.month)
+ pass_days = start_date.day
+ days_left = days_in_month - pass_days
+ end_date = start_date + timedelta(days=days_left)
+ return start_date, end_date
+
+ def get_membership_order_cc_data(self):
+ return {
+ 'last4': self.last4,
+ 'cc_brand': self.cc_brand,
+ }
+
+ def get_membership_range_date(self):
+ return self.start_date, self.end_date
+
+ @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
+
+
+class BookingPrice(models.Model):
+ price_per_day = models.FloatField()
+ special_month_price = 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
+ ONE_DAY = 1
+ 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))
+
+ if start_date == end_date and free_days_this_month == TWO_DAYS:
+ free_days_this_month = ONE_DAY
+
+ total_free_days = months * TWO_DAYS + free_days_this_month
+ return total_free_days
+
+ @classmethod
+ def booking_price(cls, user, start_date, end_date):
+
+ MAX_MONTH_PRICE = BookingPrice.objects.last().special_month_price
+ 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)
+ original_price = models.FloatField()
+ special_month_price = models.FloatField()
+
+ @classmethod
+ def user_has_not_bookings(cls, user):
+ return cls.objects.filter(customer__user=user).exists()
+
+ def get_booking_cc_data(self):
+ return {
+ 'last4': self.last4,
+ 'cc_brand': self.cc_brand,
+ } if self.last4 and self.cc_brand else None
+
+ def booking_days(self):
+ return (self.booking.end_date - self.booking.start_date).days + 1
class Supporter(models.Model):
@@ -15,7 +242,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/css/agency.css b/digitalglarus/static/digitalglarus/css/agency.css
index 7be17307..67154295 100755
--- a/digitalglarus/static/digitalglarus/css/agency.css
+++ b/digitalglarus/static/digitalglarus/css/agency.css
@@ -241,6 +241,8 @@ fieldset[disabled] .btn-xl.active {
.navbar-default .navbar-collapse {
border-color: rgba(255,255,255,.02);
+ padding-right: 100px;
+ text-align: right;
}
.navbar-default .navbar-toggle {
@@ -259,7 +261,7 @@ fieldset[disabled] .btn-xl.active {
.navbar-default .nav li a {
text-transform: uppercase;
- font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 400;
letter-spacing: 1px;
color: #fff;
@@ -1170,11 +1172,11 @@ footer {
}
.map-caption{
- text-transform: none;
+ text-transform:uppercase;
+ font-size: 13px;
font-family:"Montserrat","Helvetica Neue",Helvetica, Arial,sans-serif;
font-weight: 400;
color: #ffffff;
- font-size: 14px;
letter-spacing: 1px;
text-align:center;
}
diff --git a/digitalglarus/static/digitalglarus/css/price.css b/digitalglarus/static/digitalglarus/css/price.css
new file mode 100644
index 00000000..359b47fe
--- /dev/null
+++ b/digitalglarus/static/digitalglarus/css/price.css
@@ -0,0 +1,622 @@
+@charset "UTF-8";
+/* CSS Document */
+#membership-includes {
+ text-align: center;
+ color: #fff;
+ background-attachment: scroll;
+ background-image: url(../img/header_bg_5.png);
+ background-position: center center;
+ background-repeat: none;
+ -webkit-background-size: cover;
+ -moz-background-size: cover;
+ background-size: cover;
+ -o-background-size: cover;
+}
+
+.intro-headline-small{
+ font-family: 'Raleway' , "Open Sans Bold", Helvetica, Arial, "Arial Bold", sans-serif;
+ font-size: 46px;
+ font-style: normal;
+ font-weight: 200;
+ text-transform: none;
+ text-transform: uppercase;
+ color: #FFF;
+}
+
+
+.intro-smaller {
+ font-family: 'Raleway' ,'Montserrat' ,"Open Sans Bold", Helvetica, Arial, "Arial Bold", sans-serif;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 100;
+ text-transform: none;
+ color: #FFF;
+}
+.intro-price {
+ padding-top: 70px;
+ padding-bottom: 50px;
+}
+
+.ul{
+ columns: 2;
+ padding: 15px 0;
+ font-size: 18px;
+ font-weight: 300;
+ color: #fff;
+ margin: 40px 0;
+}
+.price-list{
+ background-color: transparent;
+ font-size: 18px;
+ border: 0;
+ padding-top: 15px;
+ padding-bottom: 15px;
+}
+
+.price{
+ text-transform: none;
+}
+
+#price {
+ background-image: url(../img/bg-price.png);
+ background-position: center center;
+ background-repeat: none;
+ -webkit-background-size: cover;
+ -moz-background-size: cover;
+ background-size: cover;
+ -o-background-size: cover;
+}
+.price-box{
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 15px;
+ padding-bottom: 30px;
+ margin-top: 80px;
+ margin-bottom: 30px;
+ margin-left: 10px;
+ margin-right: 10px;
+ color: inherit;
+ background-color: #fff;
+}
+
+.graph{
+ padding-top: 30px;
+}
+
+.glyphicon-ok{
+ margin-right: 0.5em;
+}
+
+.glyphicon-plus{
+ font-size: 42px;
+ display: block;
+ text-align: center;
+ margin: 40px auto 20px;
+ color: #88c7d7;
+}
+
+.signup-container {
+ padding: 0;
+ margin: 0;
+ overflow-x: hidden;
+}
+
+.greyline{
+ border-color: #ddd;
+ border-width: 1px;
+ max-width: 600px;
+}
+
+.greyline-long{
+ border-color: #ddd;
+ border-width: 1px;
+ max-width: 95%;
+}
+
+.signup-form {
+ padding: 2em;
+ padding-top: 1em;
+ padding-bottom: 0.1em;
+ margin-bottom: 0px;
+}
+
+.signup-lead {
+ font-size: 16px;
+ line-height: 1.4em;
+ text-transform: none;
+}
+
+.form-control {
+ margin-bottom: 1em;
+ border-image-source: initial;
+ border-image-slice: initial;
+ border-image-width: initial;
+ border-image-outset: initial;
+ border-image-repeat: initial;
+ min-height: 20px;
+ background: rgb(255, 255, 255);
+ border-width: 1px;
+ border-style: solid;
+ border-color: rgba(37, 39, 41, 0.498039);
+ padding: 10.5px 10px;
+ transition: all 0.15s ease-in-out;
+}
+
+.notice-box {
+ padding-top: 1.5em;
+ padding-bottom: 2em;
+}
+
+.signup-text {
+ color: #777;
+ margin: 0;
+ line-height: 1.5;
+ font-size: 13px;
+ text-transform: none;
+ font-family: "Helvetica Neue" ,"Helvetica Neue" ,"Open Sans" ,"Arial" , sans-serif;
+ color: #999;
+}
+
+.signup-text a {
+ font-weight : 400;
+ margin-left: 0.5em;
+ color: #31708f;
+}
+
+.signup-notice a {
+ font-weight : 400;
+ margin-left: 0.5em;
+ color: #31708f;
+}
+
+.signup-box {
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ margin-top: 80px;
+ margin-bottom: 30px;
+ margin-left: 10px;
+ margin-right: 10px;
+ color: inherit;
+ background-color: #fff;
+}
+
+.signup-notice {
+ padding-top: 0.5em;
+ font-size: 12px;
+ color: #777;
+}
+
+.glyphicon-user{
+ font-size: 42px;
+ display: block;
+ text-align: center;
+ margin: 40px auto 20px;
+ color: #88c7d7;
+}
+
+.glyphicon-ok {
+ font-size: 28px;
+ display: block;
+ text-align: center;
+ margin-bottom: 20px;
+ color: #88c7d7;
+ margin-top: 0px;
+}
+}
+
+
+.membership-amount{
+ font-size: 18px;
+ text-align: right;
+ width: 100%;
+}
+.payment-box{
+ padding-top: 3em;
+ padding-left: 30px;
+ padding-right: 30px;
+ padding-bottom: 15px;
+ margin-top: 80px;
+ margin-bottom: 30px;
+ margin-left: 10px;
+ margin-right: 10px;
+ color: inherit;
+ background-color: #fff;
+}
+
+.payment-head{
+ font-weight: 600;
+ padding-left: 15px;
+ text-align: left;
+}
+
+.membership-lead {
+ font-size: 16px;
+ line-height: 1.4em;
+ text-transform: none;
+ text-align: left;
+ padding-left: 15px;
+ padding-right: 15px;
+ font-family: helvetica neue, helvitica, open-sans, sans-serif;
+ font-weight: 200;
+}
+
+.order-name{
+ font-size: 18px;
+ text-transform: uppercase;
+ margin: 0;
+ font-weight: 700;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ letter-spacing: 1px;
+}
+
+.order-person{
+ text-align: right;
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 20px;
+ color: #494949;
+ text-transform: none;
+}
+
+.order-item{
+ font-size: 18px;
+ text-transform: none;
+ font-weight: 400;
+ text-align: left;
+ width: 100%;
+ letter-spacing: 0.1px;
+}
+
+.order-duration {
+ padding-bottom: 1em;
+ color: #999;
+ font-size: 16px;
+ text-transform: none;
+ font-weight: 200;
+ text-align: left;
+ width: 100%;
+ margin-top: 5px;
+}
+
+.header{
+ padding: 12px 15px;
+ margin-bottom: 0;
+ border-bottom: 2px solid #f7f7f7;
+ background-color: #a1cfd7;
+}
+
+.payment-total{
+ font-size: 16px;
+ text-transform: none;
+ margin: 0;
+ font-weight: 200;
+ letter-spacing: 1px;
+}
+
+.order-sum {
+ text-align: right;
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 20px;
+ color: #494949;
+}
+.order-result {
+ font-family: open-sans, montserrat, Helvetica Neue, Helvetica, sans-serif;
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 28px;
+ color: #494949;
+ text-align: right;
+}
+
+.order-bottom-text {
+ padding-top: 0.5em;
+ color: #777;
+ padding-left: 15px;
+ font-size: 13px;
+}
+
+.order-bottom-text a {
+ font-weight : 400;
+ margin-left: 0.5em;
+ color: #31708f;
+}
+
+.order-summary {
+ background-color:#fff;
+ margin-top: 80px;
+}
+
+.order-box{
+ padding-top: 0;
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-bottom: 15px;
+ margin-top: 0;
+ margin-bottom: 30px;
+ margin-left: 10px;
+ margin-right: 10px;
+ color: inherit;
+ background-color: #fff;
+}
+
+.billing-head {
+ padding-left: 15px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-weight: 400;
+ font-size: 21px;
+ text-align: left;
+ text-transform: none;
+}
+
+.reset-head {
+ padding-left: 15px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-weight: 400;
+ font-size: 24px;
+ text-align: left;
+ text-transform: none;
+}
+
+.form-control {
+ color: #000;
+ border-radius: 0px;
+ box-shadow: none;
+}
+
+.custom-control-description {
+ color: #999;
+ font-weight: 300;
+ font-family: "helvetica neue", "helvetica", "sans-serif" ;
+ letter-spacing: 0.5px;
+ font-size: 12px;
+ line-height: 1;
+}
+
+.custom-control-description a {
+ color: #31708f;
+}
+
+.custom-control {
+ text-align: left;
+}
+
+.button-box {
+ margin-top: 20px;
+}
+
+.date-box {
+ padding-top: 1.5em;
+ padding-bottom: 0.5em;
+}
+
+.date-oneline {
+ font-size: 16px;
+ text-transform: none;
+ margin: 0;
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #555;
+}
+
+.btn-grey {
+ background-color: #b2b7b9;;
+ border-color: #b2b7b9;;
+}
+
+.btn-grey:hover,
+.btn-grey:focus,
+.btn-grey:active,
+.btn-grey.active,
+.open .dropdown-toggle.btn-grey {
+ text-transform: uppercase;
+ font-weight: 400;
+ border-color: #ddd;
+ color: #fff;
+ background-color: #ddd;
+}
+
+.btn-blue:hover,
+.btn-blue:focus,
+.btn-blue:active,
+.btn-blue.active,
+.open .dropdown-toggle.btn-grey {
+ background-color: #5699b9;
+ border-color: #5699b9;
+}
+
+.loggedin-head {
+ padding-left: 15px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-weight: 400;
+ font-size: 21px;
+ text-align: center;
+ text-transform: none;
+ padding-top: 10px;
+}
+
+.loggedin-lead {
+ font-size: 16px;
+ line-height: 1.4em;
+ text-transform: none;
+ text-align: center;
+ padding-left: 15px;
+ padding-right: 15px;
+ font-family: helvetica neue, helvitica, open-sans, sans-serif;
+ font-weight: 200;
+}
+
+.activation-lead {
+ font-size: 15px;
+ line-height: 1.4em;
+ text-transform: none;
+ text-align: center;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-bottom: 0;
+ font-family: helvetica neue, helvitica, open-sans, sans-serif;
+ font-weight: 200;
+ color: #777;
+}
+
+.button-booking-box {
+ margin-top: 0;
+}
+
+.member-name{
+ font-size: 16px;
+ text-transform: none;
+ font-weight: 400;
+ text-align: left;
+ width: 100%;
+ letter-spacing: 0.1px;
+ text-align: left;
+ padding-left: 15px;
+ line-height: 0.35em;
+}
+
+.history-name{
+ font-size: 16px;
+ text-transform: none;
+ font-weight: 400;
+ text-align: left;
+ width: 100%;
+ letter-spacing: 0.1px;
+ text-align: left;
+ padding-left: 15px;
+ line-height: 1.4em;
+}
+
+.button-center-box {
+ margin-top: 20px;
+ text-align: center;
+}
+
+.table td, .table th {
+ padding: .75rem;
+ vertical-align: top;
+ border-top: 1px solid #eceeef;
+}
+
+.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
+ padding: .75rem;
+ vertical-align: top;
+ border-top: 1px solid #eceeef;
+ border-bottom: 1px solid #eceeef;
+ color: #777;
+
+}
+
+.table{
+ margin-top: 1em;
+}
+
+.order-head {
+ font-size: 18px;
+ text-transform: uppercase;
+ margin: 0;
+ font-weight: 900;
+ padding-top: 15px;
+ letter-spacing: 1px;
+ text-align: left;
+ padding-left: 15px;
+ color: #88c7d7;
+}
+
+th {
+ text-align: center;
+}
+
+.table td {
+ color: #999;
+}
+
+.btn-edit {
+ font-size: 10px;
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ background-color: #b2b7b9;
+ border-color: #b2b7b9;
+ color: #fff;
+ font-weight: 200;
+ margin-left: 1em;
+}
+
+.edit-button {
+ text-align:left;
+ padding-left: 15px;
+ padding-top: 10px;
+}
+
+.btn-deactivate {
+ font-size: 11px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 4px;
+ padding-bottom: 4px;
+
+ color: #fff;
+ font-weight: 200;
+}
+
+.btn-darkgrey {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: 400;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+ background-color: #777;
+ border-color: #777;
+ text-transform: none;
+}
+
+.btn-darkgrey:hover,
+.btn-darkgrey:focus,
+.btn-darkgrey:active,
+.btn-darkgrey.active,
+.open .dropdown-toggle.btn-darkgrey {
+ text-transform: none;
+ font-weight: 400;
+ border-color: #88c7d7;
+ color: #fff;
+ background-color: #88c7d7;
+}
+
+.thankyou {
+
+ font-size: 30px;
+ text-align: center;
+ padding-top: 45px;
+
+}
+
+.icon-up{
+ margin-top: 30px;
+}
+
+.price-exp-box {
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 15px;
+ padding-bottom: 0px;
+ margin-top: 0px;
+ margin-bottom: 30px;
+ margin-left: 10px;
+ margin-right: 10px;
+ color: inherit;
+ background-color: #fff;
+ text-align: left;
+}
\ No newline at end of file
diff --git a/digitalglarus/static/digitalglarus/img/bg-price.png b/digitalglarus/static/digitalglarus/img/bg-price.png
new file mode 100644
index 00000000..f4597723
Binary files /dev/null and b/digitalglarus/static/digitalglarus/img/bg-price.png differ
diff --git a/digitalglarus/static/digitalglarus/img/graph.png b/digitalglarus/static/digitalglarus/img/graph.png
new file mode 100644
index 00000000..74ab22a8
Binary files /dev/null and b/digitalglarus/static/digitalglarus/img/graph.png differ
diff --git a/digitalglarus/static/digitalglarus/img/header_bg_5.png b/digitalglarus/static/digitalglarus/img/header_bg_5.png
new file mode 100644
index 00000000..063b1642
Binary files /dev/null and b/digitalglarus/static/digitalglarus/img/header_bg_5.png differ
diff --git a/digitalglarus/static/digitalglarus/js/booking.js b/digitalglarus/static/digitalglarus/js/booking.js
new file mode 100644
index 00000000..814d1a3c
--- /dev/null
+++ b/digitalglarus/static/digitalglarus/js/booking.js
@@ -0,0 +1,28 @@
+$( document ).ready(function() {
+
+
+ // $('#booking-date-range').daterangepicker();
+
+
+
+
+ var tomorrow = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
+
+ $('#booking-date-1').datetimepicker({
+ minDate: tomorrow,
+ format: 'MM/DD/YYYY',
+ // defaultDate: false
+ });
+ $('#booking-date-1').val('');
+ $('#booking-date-2').datetimepicker({
+ useCurrent: false, //Important! See issue #1075
+ format: 'MM/DD/YYYY',
+ });
+ $("#booking-date-1").on("dp.change", function (e) {
+ $('#booking-date-2').data("DateTimePicker").minDate(e.date);
+ });
+ $("#booking-date-2").on("dp.change", function (e) {
+ $('#booking-date-1').data("DateTimePicker").maxDate(e.date);
+ });
+
+});
\ No newline at end of file
diff --git a/digitalglarus/static/digitalglarus/js/cbpAnimatedHeader.js b/digitalglarus/static/digitalglarus/js/cbpAnimatedHeader.js
index 4554e2e5..dc750576 100755
--- a/digitalglarus/static/digitalglarus/js/cbpAnimatedHeader.js
+++ b/digitalglarus/static/digitalglarus/js/cbpAnimatedHeader.js
@@ -13,7 +13,7 @@ var cbpAnimatedHeader = (function() {
var docElem = document.documentElement,
header = document.querySelector( '.navbar-default' ),
didScroll = false,
- changeHeaderOn = 300;
+ changeHeaderOn = 20;
function init() {
window.addEventListener( 'scroll', function( event ) {
diff --git a/digitalglarus/static/digitalglarus/js/payment.js b/digitalglarus/static/digitalglarus/js/payment.js
new file mode 100644
index 00000000..d9c6e0f7
--- /dev/null
+++ b/digitalglarus/static/digitalglarus/js/payment.js
@@ -0,0 +1,133 @@
+$( document ).ready(function() {
+
+ $.ajaxSetup({
+ beforeSend: function(xhr, settings) {
+ function getCookie(name) {
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = jQuery.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ }
+ if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
+ // Only send the token to relative URLs i.e. locally.
+ xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
+ }
+ }
+ });
+
+ var submit_form_btn = $('#payment_button');
+ submit_form_btn.on('click', submit_payment);
+
+ function submit_payment(e){
+ $('#billing-form').submit();
+ // $form.submit();
+ }
+
+
+ var $form = $('#payment-form');
+ $form.submit(payWithStripe);
+
+ /* If you're using Stripe for payments */
+ function payWithStripe(e) {
+ console.log("submiting");
+ e.preventDefault();
+
+ /* Visual feedback */
+ $form.find('[type=submit]').html('Validating ');
+
+ var PublishableKey = window.stripeKey;
+ Stripe.setPublishableKey(PublishableKey);
+ Stripe.card.createToken($form, function stripeResponseHandler(status, response) {
+ if (response.error) {
+ /* Visual feedback */
+ $form.find('[type=submit]').html('Try again');
+ /* Show Stripe errors on the form */
+ $form.find('.payment-errors').text(response.error.message);
+ $form.find('.payment-errors').closest('.row').show();
+ } else {
+ /* Visual feedback */
+ $form.find('[type=submit]').html('Processing ');
+ /* Hide Stripe errors on the form */
+ $form.find('.payment-errors').closest('.row').hide();
+ $form.find('.payment-errors').text("");
+ // response contains id and card, which contains additional card details
+ var token = response.id;
+ // AJAX
+
+ //set token on a hidden input
+ $('#id_token').val(token);
+ $('#billing-form').submit();
+ }
+ });
+ }
+
+ /* Form validation */
+ $.validator.addMethod("month", function(value, element) {
+ return this.optional(element) || /^(01|02|03|04|05|06|07|08|09|10|11|12)$/.test(value);
+ }, "Please specify a valid 2-digit month.");
+
+ $.validator.addMethod("year", function(value, element) {
+ return this.optional(element) || /^[0-9]{2}$/.test(value);
+ }, "Please specify a valid 2-digit year.");
+
+ validator = $form.validate({
+ rules: {
+ cardNumber: {
+ required: true,
+ creditcard: true,
+ digits: true
+ },
+ expMonth: {
+ required: true,
+ month: true
+ },
+ expYear: {
+ required: true,
+ year: true
+ },
+ cvCode: {
+ required: true,
+ digits: true
+ }
+ },
+ highlight: function(element) {
+ $(element).closest('.form-control').removeClass('success').addClass('error');
+ },
+ unhighlight: function(element) {
+ $(element).closest('.form-control').removeClass('error').addClass('success');
+ },
+ errorPlacement: function(error, element) {
+ $(element).closest('.form-group').append(error);
+ }
+ });
+
+ paymentFormReady = function() {
+ if ($form.find('[name=cardNumber]').hasClass("success") &&
+ $form.find('[name=expMonth]').hasClass("success") &&
+ $form.find('[name=expYear]').hasClass("success") &&
+ $form.find('[name=cvCode]').val().length > 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ $form.find('[type=submit]').prop('disabled', true);
+ var readyInterval = setInterval(function() {
+ if (paymentFormReady()) {
+ $form.find('[type=submit]').prop('disabled', false);
+ clearInterval(readyInterval);
+ }
+ }, 250);
+
+});
+
diff --git a/digitalglarus/static/digitalglarus/js/utils.js b/digitalglarus/static/digitalglarus/js/utils.js
new file mode 100644
index 00000000..e824dafc
--- /dev/null
+++ b/digitalglarus/static/digitalglarus/js/utils.js
@@ -0,0 +1,23 @@
+$( 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..85b15fd2
--- /dev/null
+++ b/digitalglarus/templates/digitalglarus/booking.html
@@ -0,0 +1,61 @@
+{% extends "new_base_glarus.html" %}
+{% load staticfiles cms_tags bootstrap3%}
+{% block title %}crowdfunding{% endblock %}
+
+{% block content %}
+
+
+ View my bookings This box is here just to thank you
+ Change dates
+ Still have trouble? Contact us for technical support. Booking
+ Start coworking at Digital Glarus!
+
Membership costs only
+ 35CHF per month.
2 free working days included!
+
+
+ In der Au 7 Schwanden 8762 Switzerland
+
info@digitalglarus.ch
+
+ (044) 534-66-22
+ Your Booking Detail
+
+ Invoice
+
+ Order Number
+ #{{order.id}}
+
+ Billed to :
+ {{user.name}}
+
+
+ {{order.billing_address.street_address}},{{order.billing_address.postal_code}}
+ {{order.billing_address.city}}, {{order.billing_address.country}}.
+ Payment Method
+
+ {{order.cc_brand}} ending **** {{order.last4}}
+
+
+ {{user.email}}
+
+ Order Summary
+
+ Dates: {{start_date}} - {{end_date}}
+
+ Total days {{booking_days}}
+ {{original_price|floatformat}}CHF
+ {% if free_days %}
+ Free days {{free_days}}
+ -{{total_discount|floatformat}}CHF
+ {% endif %}
+
+ Total
+ {{final_price|floatformat}}CHF
+
+
+
Go to my page
+ Your Order History
+
+ Member Name
+ {{request.user.name}}
+
+ Active Membership
+ 2016.11.13-2016.12.13
+
+ Booking history
+
+
+
+
+
+
+
+
+ {% for order in orders%}
+ #
+ Booking dates
+ Days
+ Invoice
+
+
+ {% endfor %}
+
+ {{order.id}}
+ {{order.booking.start_date}}-{{order.booking.end_date}}
+ {{order.booking_days}}
+ View
+ Billing AdressEdit
+
+ {{request.user.name}}
+
+
+ {{billing_address.street_address}},{{billing_address.postal_code}}
+
+
+
+ {{billing_address.city}}, {{billing_address.country}}.
+ Thank You!
+ Digital Glarus lives with your love!
+
+
+ Our coworking space is here because of your love and support.
+
+ Booking
+
+
+
+
+ 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.
+
+ {% if is_free %}
+ Billing Adress
+
+
+ Your Booking is TOTALLY FREE
+
+ {% else %}
+ Billing Adress
+ Credit Card (Last used)
+
+ Booking Summary
+ Dates {{start_date}} - {{end_date}}
+
+ Total days: {{booking_days}}
+ {{original_price|floatformat}}CHF
+
+ {% if free_days %}
+ Free days: {{free_days}}
+ -{{total_discount|floatformat}}CHF
+ {% endif %}
+
+ Total
+ {{final_price|floatformat}}CHF
+
+ In der Au 7 Schwanden 8762 Switzerland
+
info@digitalglarus.ch
+
+ (044) 534-66-22
+ Set your new password
+
+
+
+
+ In der Au 7 Schwanden 8762 Switzerland
+
info@digitalglarus.ch
+
+ (044) 534-66-22
+
+
|
+ |||
+
|
+ |||
+
|
+
+
|
+ |||
+
|
+ |||
+
|
+
+
|
+ |||
+
|
+ |||
+
|
+
+
|
+ |||
+
|
+ |||
+
|
+
Forgot password?Find ID/Password
+Not a member yet?Sign up now.
++ +
+
Your membership will be automatically renewed each month. For deactivating go todeactivate page
++ +
+
+ Your membership wouldn't be automatically renewed each month. +
++ +
+
+ +
+
# | +Valid Month | +Date | +Invoice | +
---|---|---|---|
{{order.id}} | +{{order.created_at|date:"F"}} | +{{order.created_at|date}} | +View | +
You will be charged on the first of the month until you + cancel your subscription. Previous charges won't be refunded.
+This box is here just to thank you
+ + {% endif %} + + + + ++ +
+
+ +
+
Still have trouble? Contact us for technical support.
++ +
+
Already a member?Log in
++ +
+
+ +
+