Fixes #3934: Error on OAI-PMH list request.
[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     def is_expired(self):
92         return self.expires_at is not None and self.expires_at < now()
93
94     def create_payment(self):
95         n = now()
96         self.expires_at = self.plan.get_next_installment(n)
97         self.is_active = True
98         self.save()
99         self.payment_set.create(payed_at=n)
100
101
102 class Payment(models.Model):
103     schedule = models.ForeignKey(Schedule, verbose_name=_('schedule'), on_delete=models.PROTECT)
104     created_at = models.DateTimeField(_('created at'), auto_now_add=True)
105     payed_at = models.DateTimeField(_('payed at'), null=True, blank=True)
106
107     class Meta:
108         verbose_name = _('payment')
109         verbose_name_plural = _('payments')
110
111     def __str__(self):
112         return "%s %s" % (self.schedule, self.payed_at)
113
114
115 class Membership(models.Model):
116     """ Represents a user being recognized as a member of the club. """
117     user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
118     created_at = models.DateTimeField(_('created at'), auto_now_add=True)
119
120     class Meta:
121         verbose_name = _('membership')
122         verbose_name_plural = _('memberships')
123
124     def __str__(self):
125         return u'tow. ' + str(self.user)
126
127     @classmethod
128     def is_active_for(self, user):
129         if user.is_anonymous:
130             return False
131         return Schedule.objects.filter(
132                 models.Q(expires_at=None) | models.Q(expires_at__lt=now()),
133                 membership__user=user,
134                 is_active=True,
135             ).exists()
136
137
138 class ReminderEmail(models.Model):
139     days_before = models.SmallIntegerField(_('days before'))
140     subject = models.CharField(_('subject'), max_length=1024)
141     body = models.TextField(_('body'))
142
143     class Meta:
144         verbose_name = _('reminder email')
145         verbose_name_plural = _('reminder emails')
146         ordering = ['days_before']
147
148     def __str__(self):
149         if self.days_before >= 0:
150             return ungettext('a day before expiration', '%d days before expiration', n=self.days_before)
151         else:
152             return ungettext('a day after expiration', '%d days after expiration', n=-self.days_before)
153