2016-08-22 07:52:29 +00:00
|
|
|
import calendar
|
2017-12-24 11:46:36 +00:00
|
|
|
import time
|
2017-12-24 12:35:53 +00:00
|
|
|
|
2016-08-22 07:52:29 +00:00
|
|
|
from datetime import datetime, date, timedelta
|
2016-09-06 00:45:45 +00:00
|
|
|
from dateutil.relativedelta import relativedelta
|
2015-05-02 15:00:15 +00:00
|
|
|
from django.db import models
|
2016-09-06 00:45:45 +00:00
|
|
|
from django.db.models import Q
|
2016-03-05 19:57:41 +00:00
|
|
|
from cms.models import CMSPlugin
|
|
|
|
from filer.fields.image import FilerImageField
|
2016-04-23 17:00:20 +00:00
|
|
|
from django.core.urlresolvers import reverse
|
2016-08-22 07:52:29 +00:00
|
|
|
from django.utils.functional import cached_property
|
2016-08-31 02:32:45 +00:00
|
|
|
from .mixins import Ordereable
|
2016-08-22 07:52:29 +00:00
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
# from membership.models import StripeCustomer
|
|
|
|
# from utils.models import BillingAddress
|
2015-05-02 15:00:15 +00:00
|
|
|
|
|
|
|
|
2016-08-20 05:57:35 +00:00
|
|
|
class MembershipType(models.Model):
|
|
|
|
|
2016-09-29 05:04:40 +00:00
|
|
|
STANDARD = 'standard'
|
2016-08-20 05:57:35 +00:00
|
|
|
MEMBERSHIP_TYPES = (
|
2016-09-29 05:04:40 +00:00
|
|
|
(STANDARD, 'Standard'),
|
2016-08-20 05:57:35 +00:00
|
|
|
|
|
|
|
)
|
|
|
|
name = models.CharField(choices=MEMBERSHIP_TYPES, max_length=20)
|
|
|
|
price = models.FloatField()
|
|
|
|
|
2016-08-22 07:52:29 +00:00
|
|
|
@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"))
|
|
|
|
|
2017-12-24 11:45:59 +00:00
|
|
|
@cached_property
|
|
|
|
def next_month_in_sec_since_epoch(self):
|
|
|
|
"""
|
|
|
|
First day of the next month expressed in seconds since the epoch time
|
|
|
|
:return: Time in seconds
|
|
|
|
"""
|
|
|
|
start_date, end_date = self.first_month_range
|
|
|
|
first_day_next_month = end_date + timedelta(days=1)
|
|
|
|
epoch_time = int(time.mktime(first_day_next_month.timetuple()))
|
|
|
|
return epoch_time
|
|
|
|
|
2016-08-20 05:57:35 +00:00
|
|
|
|
|
|
|
class Membership(models.Model):
|
|
|
|
type = models.ForeignKey(MembershipType)
|
Reduced pixels needed to navbar transition color, Added membership deactivation form, Added membership deactivation view, Added membership status, Added membership charged.html, Added membership charged.txt, Extended class to send membership charge email, After membership, charge for first time an email is sent to the user, Fixed bug on redirecting, user to membership payment after signup, Fixed redirecting after buying a membership
2016-09-14 05:24:20 +00:00
|
|
|
active = models.BooleanField(default=True)
|
2016-10-19 04:29:07 +00:00
|
|
|
start_date = models.DateField()
|
|
|
|
end_date = models.DateField()
|
|
|
|
|
2016-12-13 15:54:05 +00:00
|
|
|
def __str__(self):
|
|
|
|
return str(self.id)
|
|
|
|
|
2016-11-01 04:01:43 +00:00
|
|
|
@classmethod
|
|
|
|
def get_current_membership(cls, user):
|
2017-12-24 12:06:23 +00:00
|
|
|
has_order_current_month = Q(
|
|
|
|
membershiporder__customer__user=user,
|
|
|
|
membershiporder__created_at__month=datetime.today().month
|
|
|
|
)
|
2016-11-01 04:01:43 +00:00
|
|
|
# import pdb;pdb.set_trace()
|
2017-12-24 12:15:48 +00:00
|
|
|
return cls.objects.filter(has_order_current_month).last()
|
2016-11-01 04:01:43 +00:00
|
|
|
|
|
|
|
# def get_current_active_membership(cls, user):
|
|
|
|
# membership = cls.get_current_membership(user)
|
|
|
|
# return membership if membership and membership.active else None
|
|
|
|
|
2016-10-19 04:29:07 +00:00
|
|
|
@classmethod
|
|
|
|
def get_by_user(cls, user):
|
2017-12-24 12:15:48 +00:00
|
|
|
return cls.objects.filter(membershiporder__customer__user=user).last()
|
2016-08-20 05:57:35 +00:00
|
|
|
|
2016-10-01 22:17:46 +00:00
|
|
|
@classmethod
|
|
|
|
def create(cls, data):
|
|
|
|
instance = cls.objects.create(**data)
|
|
|
|
return instance
|
|
|
|
|
2016-11-01 04:01:43 +00:00
|
|
|
@classmethod
|
|
|
|
def activate_or_crete(cls, data, user):
|
|
|
|
membership = cls.get_by_user(user)
|
2016-11-18 02:08:16 +00:00
|
|
|
membership_id = membership.id if membership else None
|
2017-12-24 12:06:23 +00:00
|
|
|
obj, created = cls.objects.update_or_create(
|
|
|
|
id=membership_id, defaults=data
|
|
|
|
)
|
2016-11-01 04:01:43 +00:00
|
|
|
return obj
|
|
|
|
|
2016-09-06 00:45:45 +00:00
|
|
|
@classmethod
|
2016-09-29 05:04:40 +00:00
|
|
|
def is_digitalglarus_active_member(cls, user):
|
2016-11-01 04:01:43 +00:00
|
|
|
# past_month = (datetime.today() - relativedelta(months=1)).month
|
2017-12-24 12:06:23 +00:00
|
|
|
has_order_current_month = Q(
|
|
|
|
membershiporder__customer__user=user,
|
|
|
|
membershiporder__created_at__month=datetime.today().month
|
|
|
|
)
|
2016-11-01 04:01:43 +00:00
|
|
|
# has_order_past_month = Q(membershiporder__customer__user=user,
|
2017-06-29 14:34:40 +00:00
|
|
|
# membershiporder__created_at__month=past_month)
|
Reduced pixels needed to navbar transition color, Added membership deactivation form, Added membership deactivation view, Added membership status, Added membership charged.html, Added membership charged.txt, Extended class to send membership charge email, After membership, charge for first time an email is sent to the user, Fixed bug on redirecting, user to membership payment after signup, Fixed redirecting after buying a membership
2016-09-14 05:24:20 +00:00
|
|
|
active_membership = Q(active=True)
|
2017-12-24 12:06:23 +00:00
|
|
|
# return cls.objects.filter(
|
|
|
|
# has_order_past_month | has_order_current_month).\
|
2016-11-01 04:01:43 +00:00
|
|
|
return cls.objects.filter(has_order_current_month).\
|
Reduced pixels needed to navbar transition color, Added membership deactivation form, Added membership deactivation view, Added membership status, Added membership charged.html, Added membership charged.txt, Extended class to send membership charge email, After membership, charge for first time an email is sent to the user, Fixed bug on redirecting, user to membership payment after signup, Fixed redirecting after buying a membership
2016-09-14 05:24:20 +00:00
|
|
|
filter(active_membership).exists()
|
2016-09-06 00:45:45 +00:00
|
|
|
|
2016-10-19 04:29:07 +00:00
|
|
|
def update_dates(self, start_date, end_date):
|
|
|
|
self.start_date = start_date
|
|
|
|
self.end_date = end_date
|
|
|
|
self.save()
|
|
|
|
|
Reduced pixels needed to navbar transition color, Added membership deactivation form, Added membership deactivation view, Added membership status, Added membership charged.html, Added membership charged.txt, Extended class to send membership charge email, After membership, charge for first time an email is sent to the user, Fixed bug on redirecting, user to membership payment after signup, Fixed redirecting after buying a membership
2016-09-14 05:24:20 +00:00
|
|
|
def deactivate(self):
|
|
|
|
self.active = False
|
|
|
|
self.save()
|
|
|
|
|
2016-11-01 04:01:43 +00:00
|
|
|
def activate(self):
|
|
|
|
self.active = True
|
|
|
|
self.save()
|
|
|
|
|
2016-08-20 05:57:35 +00:00
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
class MembershipOrder(Ordereable, models.Model):
|
2016-08-20 05:57:35 +00:00
|
|
|
membership = models.ForeignKey(Membership)
|
2016-10-19 04:29:07 +00:00
|
|
|
start_date = models.DateField()
|
|
|
|
end_date = models.DateField()
|
2017-12-23 23:06:21 +00:00
|
|
|
stripe_subscription_id = models.CharField(max_length=100, null=True)
|
2016-08-20 05:57:35 +00:00
|
|
|
|
2016-09-09 04:24:52 +00:00
|
|
|
@classmethod
|
2016-10-23 23:09:01 +00:00
|
|
|
def current_membership_dates(cls, user):
|
2016-10-19 04:29:07 +00:00
|
|
|
last_membership_payment = cls.objects.\
|
2016-09-09 04:24:52 +00:00
|
|
|
filter(customer__user=user).last()
|
2016-10-23 23:09:01 +00:00
|
|
|
if not last_membership_payment:
|
|
|
|
return [None, None]
|
|
|
|
|
2016-10-19 04:29:07 +00:00
|
|
|
return last_membership_payment.start_date, last_membership_payment.end_date
|
2016-09-09 04:24:52 +00:00
|
|
|
|
2016-10-23 23:09:01 +00:00
|
|
|
@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
|
|
|
|
|
2016-10-14 05:51:39 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2016-09-09 04:24:52 +00:00
|
|
|
def get_membership_range_date(self):
|
2016-10-23 04:15:23 +00:00
|
|
|
return self.start_date, self.end_date
|
2016-09-09 04:24:52 +00:00
|
|
|
|
2016-08-22 07:52:29 +00:00
|
|
|
@classmethod
|
|
|
|
def create(cls, data):
|
|
|
|
stripe_charge = data.pop('stripe_charge', None)
|
2017-12-23 23:06:21 +00:00
|
|
|
stripe_subscription_id = data.pop('stripe_subscription_id', None)
|
2016-08-22 07:52:29 +00:00
|
|
|
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
|
2017-12-23 23:06:21 +00:00
|
|
|
instance.stripe_subscription_id = stripe_subscription_id
|
2016-09-09 04:24:52 +00:00
|
|
|
instance.save()
|
2016-08-22 07:52:29 +00:00
|
|
|
return instance
|
|
|
|
|
2016-08-20 05:57:35 +00:00
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
class BookingPrice(models.Model):
|
|
|
|
price_per_day = models.FloatField()
|
2016-09-09 04:24:52 +00:00
|
|
|
special_month_price = models.FloatField()
|
2016-08-31 02:32:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Booking(models.Model):
|
|
|
|
start_date = models.DateField()
|
|
|
|
end_date = models.DateField()
|
|
|
|
price = models.FloatField()
|
|
|
|
free_days = models.IntegerField(default=0)
|
2016-09-06 00:45:45 +00:00
|
|
|
final_price = models.FloatField()
|
2016-08-31 02:32:45 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def create(cls, data):
|
|
|
|
instance = cls.objects.create(**data)
|
|
|
|
return instance
|
|
|
|
|
|
|
|
@classmethod
|
2016-09-06 00:45:45 +00:00
|
|
|
def get_ramaining_free_days(cls, user, start_date, end_date):
|
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
TWO_DAYS = 2
|
2016-10-14 04:33:48 +00:00
|
|
|
ONE_DAY = 1
|
2016-09-06 00:45:45 +00:00
|
|
|
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)
|
2016-08-31 02:32:45 +00:00
|
|
|
current_date = datetime.today()
|
|
|
|
current_month_bookings = cls.objects.filter(bookingorder__customer__user=user,
|
|
|
|
start_date__month=current_date.month)
|
2016-09-06 00:45:45 +00:00
|
|
|
free_days_this_month = TWO_DAYS - sum(map(lambda x: x.free_days, current_month_bookings))
|
2016-10-14 04:33:48 +00:00
|
|
|
|
|
|
|
if start_date == end_date and free_days_this_month == TWO_DAYS:
|
|
|
|
free_days_this_month = ONE_DAY
|
|
|
|
|
2016-09-06 00:45:45 +00:00
|
|
|
total_free_days = months * TWO_DAYS + free_days_this_month
|
|
|
|
return total_free_days
|
2016-08-31 02:32:45 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def booking_price(cls, user, start_date, end_date):
|
2016-09-06 00:45:45 +00:00
|
|
|
|
2016-09-09 04:24:52 +00:00
|
|
|
MAX_MONTH_PRICE = BookingPrice.objects.last().special_month_price
|
2016-09-06 00:45:45 +00:00
|
|
|
MAX_MONTH_DAYS_PROMOTION = 31
|
|
|
|
MIN_MONTH_DAYS_PROMOTION = 19
|
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
booking_prices = BookingPrice.objects.last()
|
|
|
|
price_per_day = booking_prices.price_per_day
|
2016-09-06 00:45:45 +00:00
|
|
|
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
|
2016-08-31 02:32:45 +00:00
|
|
|
|
2016-10-23 04:15:23 +00:00
|
|
|
free_days = cls.get_ramaining_free_days(user, start_date, end_date)
|
2016-10-14 05:51:39 +00:00
|
|
|
|
2016-10-23 04:15:23 +00:00
|
|
|
final_booking_price = normal_price - (free_days * price_per_day)
|
2016-09-29 05:04:40 +00:00
|
|
|
|
2016-10-23 04:15:23 +00:00
|
|
|
return normal_price, final_booking_price, free_days
|
2016-08-31 02:32:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BookingOrder(Ordereable, models.Model):
|
2016-11-28 15:32:37 +00:00
|
|
|
|
|
|
|
APPROVED, CANCELLED = range(1, 3)
|
|
|
|
|
|
|
|
STATUS_CHOICES = (
|
|
|
|
(APPROVED, 'Approved'),
|
|
|
|
(CANCELLED, 'Cancelled')
|
|
|
|
)
|
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
booking = models.OneToOneField(Booking)
|
2016-09-09 04:24:52 +00:00
|
|
|
original_price = models.FloatField()
|
|
|
|
special_month_price = models.FloatField()
|
2016-11-28 15:32:37 +00:00
|
|
|
status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=1)
|
2016-09-29 05:04:40 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def user_has_not_bookings(cls, user):
|
|
|
|
return cls.objects.filter(customer__user=user).exists()
|
|
|
|
|
2016-10-14 04:33:48 +00:00
|
|
|
def get_booking_cc_data(self):
|
|
|
|
return {
|
|
|
|
'last4': self.last4,
|
|
|
|
'cc_brand': self.cc_brand,
|
2016-10-14 05:51:39 +00:00
|
|
|
} if self.last4 and self.cc_brand else None
|
2016-08-31 02:32:45 +00:00
|
|
|
|
2016-09-06 00:45:45 +00:00
|
|
|
def booking_days(self):
|
|
|
|
return (self.booking.end_date - self.booking.start_date).days + 1
|
|
|
|
|
2016-12-05 00:44:26 +00:00
|
|
|
def refund_required(self):
|
|
|
|
days_to_start = (self.booking.start_date - datetime.today().date()).days
|
|
|
|
return True if days_to_start < 7 else False
|
2016-11-28 15:32:37 +00:00
|
|
|
|
|
|
|
def cancel(self):
|
|
|
|
self.status = self.CANCELLED
|
|
|
|
self.save()
|
|
|
|
|
2016-08-31 02:32:45 +00:00
|
|
|
|
2016-12-05 00:44:26 +00:00
|
|
|
class BookingCancellation(models.Model):
|
|
|
|
|
|
|
|
order = models.ForeignKey(BookingOrder)
|
|
|
|
created_at = models.DateTimeField(auto_now=True)
|
|
|
|
required_refund = models.BooleanField(default=True)
|
|
|
|
refund = models.BooleanField(default=False)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "Order: {} - Required Refund: {}".format(self.order.id, self.refund)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def create(cls, booking_order):
|
|
|
|
required_refund = booking_order.refund_required()
|
|
|
|
cls.objects.create(order=booking_order, required_refund=required_refund)
|
|
|
|
|
|
|
|
|
2016-04-04 01:39:57 +00:00
|
|
|
class Supporter(models.Model):
|
|
|
|
name = models.CharField(max_length=200)
|
|
|
|
description = models.TextField(null=True, blank=True)
|
2016-04-10 21:12:43 +00:00
|
|
|
|
2016-04-04 01:39:57 +00:00
|
|
|
def __str__(self):
|
|
|
|
return "%s" % (self.name)
|
|
|
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
return reverse('dgSupporters_view', args=[self.pk])
|
2016-03-25 18:18:07 +00:00
|
|
|
|
2016-04-01 10:37:01 +00:00
|
|
|
|
|
|
|
class DGGallery(models.Model):
|
|
|
|
parent = models.ForeignKey('self', blank=True, null=True)
|
|
|
|
name = models.CharField(max_length=30)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "%s" % (self.name)
|
|
|
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
return reverse('dgGallery_view', args=[self.pk])
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name_plural = 'dgGallery'
|
2017-06-29 14:34:40 +00:00
|
|
|
|
|
|
|
|
2016-04-01 10:37:01 +00:00
|
|
|
class DGPicture(models.Model):
|
|
|
|
gallery = models.ForeignKey(DGGallery)
|
2017-06-29 14:34:40 +00:00
|
|
|
image = FilerImageField(related_name='dg_gallery')
|
2016-04-01 10:37:01 +00:00
|
|
|
description = models.CharField(max_length=60)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "%s" % (self.image.name)
|
|
|
|
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2016-04-01 10:37:01 +00:00
|
|
|
class DGGalleryPlugin(CMSPlugin):
|
|
|
|
dgGallery = models.ForeignKey(DGGallery)
|
|
|
|
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2016-04-01 10:37:01 +00:00
|
|
|
class DGSupportersPlugin(CMSPlugin):
|
|
|
|
pass
|