import calendar import time 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")) @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 class Membership(models.Model): type = models.ForeignKey(MembershipType) active = models.BooleanField(default=True) start_date = models.DateField() end_date = models.DateField() def __str__(self): return str(self.id) @classmethod def get_current_membership(cls, user): has_order_current_month = Q( membershiporder__customer__user=user, membershiporder__created_at__month=datetime.today().month ) # import pdb;pdb.set_trace() return cls.objects.filter(has_order_current_month).last() # def get_current_active_membership(cls, user): # membership = cls.get_current_membership(user) # return membership if membership and membership.active else None @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 activate_or_crete(cls, data, user): membership = cls.get_by_user(user) membership_id = membership.id if membership else None obj, created = cls.objects.update_or_create( id=membership_id, defaults=data ) return obj @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).\ return cls.objects.filter(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() def activate(self): self.active = True self.save() class MembershipOrder(Ordereable, models.Model): membership = models.ForeignKey(Membership) start_date = models.DateField() end_date = models.DateField() stripe_subscription_id = models.CharField(max_length=100, null=True) @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) stripe_subscription_id = data.pop('stripe_subscription_id', 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.stripe_subscription_id = stripe_subscription_id 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): APPROVED, CANCELLED = range(1, 3) STATUS_CHOICES = ( (APPROVED, 'Approved'), (CANCELLED, 'Cancelled') ) booking = models.OneToOneField(Booking) original_price = models.FloatField() special_month_price = models.FloatField() status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=1) @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 def refund_required(self): days_to_start = (self.booking.start_date - datetime.today().date()).days return True if days_to_start < 7 else False def cancel(self): self.status = self.CANCELLED self.save() 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) class Supporter(models.Model): name = models.CharField(max_length=200) description = models.TextField(null=True, blank=True) def __str__(self): return "%s" % (self.name) def get_absolute_url(self): 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) 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' class DGPicture(models.Model): gallery = models.ForeignKey(DGGallery) image = FilerImageField(related_name='dg_gallery') description = models.CharField(max_length=60) def __str__(self): return "%s" % (self.image.name) class DGGalleryPlugin(CMSPlugin): dgGallery = models.ForeignKey(DGGallery) class DGSupportersPlugin(CMSPlugin): pass