diff --git a/digitalglarus/forms.py b/digitalglarus/forms.py index cb3a0465..4084ced5 100644 --- a/digitalglarus/forms.py +++ b/digitalglarus/forms.py @@ -7,7 +7,7 @@ from datetime import datetime from utils.models import BillingAddress from utils.forms import LoginFormMixin, SignupFormMixin, BillingAddressForm -from .models import MembershipType +from .models import MembershipType, MembershipOrder from .models import Booking @@ -39,6 +39,23 @@ class MembershipBillingForm(BillingAddressForm): } +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()) start_date = forms.DateField(widget=forms.HiddenInput()) @@ -60,37 +77,14 @@ class BookingBillingForm(BillingAddressForm): class BookingDateForm(forms.Form): start_date = forms.DateField(required=False, widget=forms.TextInput(attrs={'id': 'booking-date-1', - 'value': ''})) + 'value': 'Select your date'})) end_date = forms.DateField(required=False, widget=forms.TextInput(attrs={'id': 'booking-date-2'})) - # def clean_date_range(self): - # date_range = self.cleaned_data.get('date_range') - # dates = date_range.replace(' ', '').split('-') - # try: - # start_date, end_date = [datetime.strptime(date_string, "%m/%d/%Y").date() - # for date_string in dates] - # except ValueError: - # raise forms.ValidationError("Submit valid dates.") - - # if start_date > end_date: - # raise forms.ValidationError("Your end date must be greather than your start date.") - - # 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_start_date(self): start_date = self.cleaned_data.get('start_date') if not start_date: raise forms.ValidationError("This field is required.") - # try: - # start_date = datetime.strptime(start_date, "%m/%d/%Y").date() - # except ValueError: - # raise forms.ValidationError("Submit valid dates.") return start_date def clean_end_date(self): diff --git a/digitalglarus/management/commands/make_membership_charge.py b/digitalglarus/management/commands/make_membership_charge.py new file mode 100644 index 00000000..c727c734 --- /dev/null +++ b/digitalglarus/management/commands/make_membership_charge.py @@ -0,0 +1,166 @@ +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) + import pdb + pdb.set_trace() + 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 + # for donator_status in donators: + # donator = donator_status.user.stripecustomer + # try: + # Donation.objects.get(created_at__month=current_month, + # created_at__year=current_year, + # donator=donator) + # except Donation.DoesNotExist: + # try: + # # Get donator last donation amount + # last_donation = Donation.objects.filter(donator=donator).last() + # donation_amount = last_donation.donation + + # # Make stripe charge to a customer + # stripe_utils = StripeUtils() + # stripe_utils.CURRENCY = self.CURRENCY + # charge_response = stripe_utils.make_charge(amount=donation_amount, + # customer=donator.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') + # donation_data = { + # 'cc_brand': charge.source.brand, + # 'stripe_charge_id': charge.id, + # 'last4': charge.source.last4, + # 'billing_address': last_donation.billing_address.id, + # 'donator': donator.id, + # 'donation': donation_amount + # } + # donation_form = DonationForm(donation_data) + # if donation_form.is_valid(): + # donation = donation_form.save() + + # context = { + # 'donation': donation, + # 'base_url': "{0}://{1}".format('https', 'dynamicweb.ungleich.ch') + + # } + # email_data = { + # 'subject': 'Your donation have been charged', + # 'to': donation.donator.user.email, + # 'context': context, + # 'template_name': 'donation_charge', + # 'template_path': 'nosystemd/emails/' + # } + # email = BaseEmail(**email_data) + # email.send() + + # print("--------- PAYMENT DONATION SUCCESSFULL ---------") + # print("Donator: %s" % donation.donator.user.email) + # print("Amount: %s %s" % (donation.donation, self.CURRENCY)) + # print("-----------------------------------------------") + # except Exception as e: + # print("--------- ERROR ---------") + # print(e) + # print("-------------------------") + # continue 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/mixins.py b/digitalglarus/mixins.py index 40777bf3..013cdd78 100644 --- a/digitalglarus/mixins.py +++ b/digitalglarus/mixins.py @@ -9,7 +9,7 @@ class MembershipRequiredMixin(object): def dispatch(self, request, *args, **kwargs): from .models import Membership - if not Membership.is_digitalglarus_member(request.user): + if not Membership.is_digitalglarus_active_member(request.user): return HttpResponseRedirect(self.membership_redirect_url) return super(MembershipRequiredMixin, self).dispatch(request, *args, **kwargs) @@ -20,7 +20,7 @@ class IsNotMemberMixin(object): def dispatch(self, request, *args, **kwargs): from .models import Membership - if Membership.is_digitalglarus_member(request.user): + if Membership.is_digitalglarus_active_member(request.user): return HttpResponseRedirect(self.already_member_redirect_url) return super(IsNotMemberMixin, self).dispatch(request, *args, **kwargs) diff --git a/digitalglarus/models.py b/digitalglarus/models.py index 5fdf6607..17621bfd 100644 --- a/digitalglarus/models.py +++ b/digitalglarus/models.py @@ -16,8 +16,9 @@ from .mixins import Ordereable class MembershipType(models.Model): + STANDARD = 'standard' MEMBERSHIP_TYPES = ( - ('standard', 'Standard'), + (STANDARD, 'Standard'), ) name = models.CharField(choices=MEMBERSHIP_TYPES, max_length=20) @@ -63,7 +64,12 @@ class Membership(models.Model): active = models.BooleanField(default=True) @classmethod - def is_digitalglarus_member(cls, user): + 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_booking_current_month = Q(membershiporder__customer__user=user, membershiporder__created_at__month=datetime.today().month) @@ -73,11 +79,6 @@ class Membership(models.Model): return cls.objects.filter(has_booking_past_month | has_booking_current_month).\ filter(active_membership).exists() - @classmethod - def create(cls, data): - instance = cls.objects.create(**data) - return instance - def deactivate(self): self.active = False self.save() @@ -158,6 +159,14 @@ class Booking(models.Model): total_free_days = months * TWO_DAYS + free_days_this_month return total_free_days + @staticmethod + def membership_required_booking_months(start_date, end_date): + 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) + return months + @classmethod def booking_price(cls, user, start_date, end_date): @@ -178,13 +187,34 @@ class Booking(models.Model): 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 + # Calculating membership required months price for booking + required_membership_months = 0 + membership_booking_price = 0.0 + if BookingOrder.user_has_not_bookings(user): + today = datetime.today().date() + membership_price = MembershipType.objects.get(name=MembershipType.STANDARD).price + required_membership_months = cls.membership_required_booking_months(today, end_date) + membership_booking_price = membership_price * required_membership_months + + # Add required membership months to final prices + final_booking_price += membership_booking_price + + return normal_price, final_booking_price, free_days,\ + required_membership_months, membership_booking_price class BookingOrder(Ordereable, models.Model): booking = models.OneToOneField(Booking) original_price = models.FloatField() special_month_price = models.FloatField() + membership_required_months = models.IntegerField(default=0) + membership_required_months_price = models.FloatField(default=0) + + + @classmethod + def user_has_not_bookings(cls, user): + return cls.objects.filter(customer__user=user).exists() + def booking_days(self): return (self.booking.end_date - self.booking.start_date).days + 1 diff --git a/digitalglarus/static/digitalglarus/js/booking.js b/digitalglarus/static/digitalglarus/js/booking.js index 6ec5e0fd..814d1a3c 100644 --- a/digitalglarus/static/digitalglarus/js/booking.js +++ b/digitalglarus/static/digitalglarus/js/booking.js @@ -7,14 +7,16 @@ $( document ).ready(function() { var tomorrow = new Date(new Date().getTime() + 24 * 60 * 60 * 1000); + $('#booking-date-1').datetimepicker({ - // minDate: tomorrow, - format: 'MM/d/YYYY', - defaultDate: false + minDate: tomorrow, + format: 'MM/DD/YYYY', + // defaultDate: false }); + $('#booking-date-1').val(''); $('#booking-date-2').datetimepicker({ useCurrent: false, //Important! See issue #1075 - format: 'MM/d/YYYY', + format: 'MM/DD/YYYY', }); $("#booking-date-1").on("dp.change", function (e) { $('#booking-date-2').data("DateTimePicker").minDate(e.date); @@ -23,24 +25,4 @@ $( document ).ready(function() { $('#booking-date-1').data("DateTimePicker").maxDate(e.date); }); - - // var tomorrow = new Date(new Date().getTime() + 24 * 60 * 60 * 1000); - // // var tomorrow = today.setDate(today.getDate() + 1); - // $('#booking-date-range').daterangepicker({ - // autoUpdateInput: false, - // locale: { - // cancelLabel: 'Clear' - // }, - // minDate: tomorrow, - // }); - - - // $('#booking-date-range').on('apply.daterangepicker', function(ev, picker) { - // $(this).val(picker.startDate.format('MM/DD/YYYY') + ' - ' + picker.endDate.format('MM/DD/YYYY')); - // }); - - // $('#booking-date-range').on('dp.cancel', function(ev, picker) { - // $(this).val('Select your dates'); - // }); - }); \ No newline at end of file diff --git a/digitalglarus/templates/digitalglarus/booking_orders_detail.html b/digitalglarus/templates/digitalglarus/booking_orders_detail.html index 6dcb604a..f3d7b049 100644 --- a/digitalglarus/templates/digitalglarus/booking_orders_detail.html +++ b/digitalglarus/templates/digitalglarus/booking_orders_detail.html @@ -7,6 +7,7 @@ .invoice-title{ text-align: center !important; } +