More Py3 compatibility fixes.
[wolnelektury.git] / src / club / models.py
1 # -*- coding: utf-8
2 from __future__ import unicode_literals
3
4 from datetime import timedelta
5 from django.conf import settings
6 from django.urls import reverse
7 from django.db import models
8 from django.utils.timezone import now
9 from django.utils.translation import ugettext_lazy as _, ungettext
10 from catalogue.utils import get_random_hash
11 from .payment_methods import methods, method_by_slug
12
13
14 class Plan(models.Model):
15     """ Plans are set up by administrators. """
16     MONTH = 30
17     YEAR = 365
18     PERPETUAL = 999
19     intervals = [
20         (MONTH, _('a month')),
21         (YEAR, _('a year')),
22         (PERPETUAL, _('in perpetuity')),
23     ]
24
25     interval = models.SmallIntegerField(_('inteval'), choices=intervals)
26     min_amount = models.DecimalField(_('min_amount'), max_digits=10, decimal_places=2)
27     allow_recurring = models.BooleanField(_('allow recurring'))
28     allow_one_time = models.BooleanField(_('allow one time'))
29
30     class Meta:
31         verbose_name = _('plan')
32         verbose_name_plural = _('plans')
33
34     def __str__(self):
35         return "%s %s" % (self.min_amount, self.get_interval_display())
36     
37     class Meta:
38         ordering = ('interval',)
39
40     def payment_methods(self):
41         for method in methods:
42             if self.allow_recurring and method.is_recurring or self.allow_one_time and not method.is_recurring:
43                 yield method
44
45     def get_next_installment(self, date):
46         if self.interval == self.PERPETUAL:
47             return None
48         elif self.interval == self.YEAR:
49             return date.replace(year=date.year + 1)
50         elif self.interval == self.MONTH:
51             day = date.day
52             date = (date.replace(day=1) + timedelta(31)).replace(day=1) + timedelta(day - 1)
53             if date.day != day:
54                 date = date.replace(day=1)
55             return date
56             
57
58
59 class Schedule(models.Model):
60     """ Represents someone taking up a plan. """
61     key = models.CharField(_('key'), max_length=255, unique=True)
62     email = models.EmailField(_('email'))
63     membership = models.ForeignKey('Membership', verbose_name=_('membership'), null=True, blank=True, on_delete=models.PROTECT)
64     plan = models.ForeignKey(Plan, verbose_name=_('plan'), on_delete=models.PROTECT)
65     amount = models.DecimalField(_('amount'), max_digits=10, decimal_places=2)
66     method = models.CharField(_('method'), max_length=255, choices=[(method.slug, method.name) for method in methods])
67     is_active = models.BooleanField(_('active'), default=False)
68     is_cancelled = models.BooleanField(_('cancelled'), default=False)
69     started_at = models.DateTimeField(_('started at'), auto_now_add=True)
70     expires_at = models.DateTimeField(_('expires_at'), null=True, blank=True)
71     # extra info?
72
73     class Meta:
74         verbose_name = _('schedule')
75         verbose_name_plural = _('schedules')
76
77     def __str__(self):
78         return self.key
79
80     def save(self, *args, **kwargs):
81         if not self.key:
82             self.key = get_random_hash(self.email)
83         return super(Schedule, self).save(*args, **kwargs)
84
85     def get_absolute_url(self):
86         return reverse('club_schedule', args=[self.key])
87
88     def get_payment_method(self):
89         return method_by_slug[self.method]
90
91
92     def is_expired(self):
93         return self.expires_at is not None and self.expires_at < now()
94
95     def create_payment(self):
96         n = now()
97         self.expires_at = self.plan.get_next_installment(n)
98         self.is_active = True
99         self.save()
100         self.payment_set.create(payed_at=n)
101
102
103 class Payment(models.Model):
104     schedule = models.ForeignKey(Schedule, verbose_name=_('schedule'), on_delete=models.PROTECT)
105     created_at = models.DateTimeField(_('created at'), auto_now_add=True)
106     payed_at = models.DateTimeField(_('payed at'), null=True, blank=True)
107
108     class Meta:
109         verbose_name = _('payment')
110         verbose_name_plural = _('payments')
111
112     def __str__(self):
113         return "%s %s" % (self.schedule, self.payed_at)
114
115
116 class Membership(models.Model):
117     """ Represents a user being recognized as a member of the club. """
118     user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
119     created_at = models.DateTimeField(_('created at'), auto_now_add=True)
120
121     class Meta:
122         verbose_name = _('membership')
123         verbose_name_plural = _('memberships')
124
125     def __str__(self):
126         return u'tow. ' + str(self.user)
127
128
129 class ReminderEmail(models.Model):
130     days_before = models.SmallIntegerField(_('days before'))
131     subject = models.CharField(_('subject'), max_length=1024)
132     body = models.TextField(_('body'))
133
134     class Meta:
135         verbose_name = _('reminder email')
136         verbose_name_plural = _('reminder emails')
137         ordering = ['days_before']
138
139     def __str__(self):
140         if self.days_before >= 0:
141             return ungettext('a day before expiration', '%d days before expiration', n=self.days_before)
142         else:
143             return ungettext('a day after expiration', '%d days after expiration', n=-self.days_before)
144