models.py 6.66 KB
Newer Older
1
from django.core.exceptions import ValidationError
2
from django.db import models
3
from django.db.models import Q
4
5
6
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

7
from events.models import Event
8
import members
9
10
from members.models import Member
from pushnotifications.models import ScheduledMessage, Category
11
from utils.translation import ModelTranslateMeta, MultilingualField
12
13
14
15
16


class PizzaEvent(models.Model):
    start = models.DateTimeField(_("Order from"))
    end = models.DateTimeField(_("Order until"))
17
    event = models.OneToOneField(Event, on_delete=models.CASCADE)
18

19
20
21
22
    send_notification = models.BooleanField(
        _("Send an order notification"),
        default=True
    )
23
24
25
26
27
    end_reminder = models.OneToOneField(
        ScheduledMessage,
        models.CASCADE,
        null=True
    )
28

29
30
31
32
33
34
35
36
    @property
    def title(self):
        return self.event.title

    @property
    def in_the_future(self):
        return self.start > timezone.now()

37
38
39
40
    @property
    def has_ended(self):
        return self.end < timezone.now()

41
42
    @property
    def just_ended(self):
43
44
        return (self.has_ended and
                self.end + timezone.timedelta(hours=8) > timezone.now())
45
46
47

    @classmethod
    def current(cls):
48
49
50
51
52
        """
        Get the currently relevant pizza event: the first one
        that starts within 8 hours from now.
        """

53
        try:
54
            events = PizzaEvent.objects.filter(
55
                end__gt=timezone.now() - timezone.timedelta(hours=8),
56
57
58
59
60
61
                start__lte=timezone.now() + timezone.timedelta(hours=8),
            ).order_by('start')
            if events.count() > 1:
                return events.exclude(end__lt=timezone.now()).first()
            else:
                return events.get()
62
63
64
        except PizzaEvent.DoesNotExist:
            return None

65
66
67
68
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._end = self.end

69
70
71
72
73
74
75
76
77
78
79
80
81
82
    def validate_unique(self, exclude=None):
        super().validate_unique(exclude)
        for other in PizzaEvent.objects.filter(
                Q(end__gte=self.start, end__lte=self.end) |
                Q(start=self.start, start__lte=self.start)):
            if other.pk == self.pk:
                continue
            raise ValidationError({
                'start': _(
                    'This event cannot overlap with {}.').format(other),
                'end': _(
                    'This event cannot overlap with {}.').format(other),
            })

83
    def clean(self):
84
85
86
87
88
        if self.start >= self.end:
            raise ValidationError({
                'start': _('The start is after the end of this event.'),
                'end': _('The end is before the start of this event.'),
            })
89

90
    def save(self, *args, **kwargs):
91
        if self.send_notification and not self.end_reminder:
92
93
94
95
96
97
            end_reminder = ScheduledMessage()
            end_reminder.title_en = 'Order pizza'
            end_reminder.title_nl = 'Pizza bestellen'
            end_reminder.body_en = 'You can order pizzas for 10 more minutes'
            end_reminder.body_nl = "Je kan nog 10 minuten pizza's bestellen"
            end_reminder.category = Category.objects.get(key='pizza')
98
            end_reminder.time = self.end - timezone.timedelta(minutes=10)
99
100
101
            end_reminder.save()

            if self.event.registration_required:
102
103
104
                end_reminder.users.set(self.event.registrations
                                       .select_related('member')
                                       .values_list('member', flat=True))
105
            else:
Luko van der Maas's avatar
Luko van der Maas committed
106
                end_reminder.users.set(Member.current_members.all())
107

108
            self.end_reminder = end_reminder
109
110
        elif (self.send_notification and self.end_reminder and
                self._end != self.end):
111
112
            self.end_reminder.time = self.end
            self.end_reminder.save()
113
        elif not self.send_notification and self.end_reminder:
114
115
            self.end_reminder.delete()

116
        super().save(*args, **kwargs)
117

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
118
119
120
121
122
123
    def delete(self, using=None, keep_parents=False):
        if (self.end_reminder is not None
                and not self.end_reminder.sent):
            self.end_reminder.delete()
        return super().delete(using, keep_parents)

124
125
126
127
    def __str__(self):
        return 'Pizzas for ' + str(self.event)


Thom Wiggers's avatar
Thom Wiggers committed
128
129
130
131
132
133
134
class AvailableProductManager(models.Manager):
    """Only shows available products"""

    def get_queryset(self):
        return super().get_queryset().filter(available=True)


135
class Product(models.Model, metaclass=ModelTranslateMeta):
Thom Wiggers's avatar
Thom Wiggers committed
136
137
138
    objects = models.Manager()
    available_products = AvailableProductManager()

139
140
141
142
    name = models.CharField(max_length=50)
    description = MultilingualField(models.TextField)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    available = models.BooleanField(default=True)
Thom Wiggers's avatar
Thom Wiggers committed
143
144
145
146
    restricted = models.BooleanField(
        default=False,
        help_text=_("Only allow to be ordered by people with the "
                    "'order restricted products' permission."))
147
148
149
150

    def __str__(self):
        return self.name

151
152
    class Meta:
        ordering = ('name', )
153
154
155
        permissions = (
            ('order_restricted_products', _('Order restricted products')),
        )
156

157
158
159

class Order(models.Model):
    member = models.ForeignKey(
160
        members.models.Member,
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )

    paid = models.BooleanField(default=False)

    name = models.CharField(
        max_length=50,
        help_text=_('Use this for non-members'),
        null=True,
        blank=True,
    )

    product = models.ForeignKey(Product, on_delete=models.PROTECT)
    pizza_event = models.ForeignKey(PizzaEvent, on_delete=models.CASCADE)

    def clean(self):
        if ((self.member is None and not self.name) or
                (self.member and self.name)):
            raise ValidationError({
                'member': _('Either specify a member or a name'),
                'name': _('Either specify a member or a name'),
            })

186
187
188
189
190
191
192
193
194
195
196
197
    def save(self, *args, **kwargs):
        if not self.id:
            self.pizza_event.end_reminder.users.remove(self.member)

        super().save(*args, **kwargs)

    def delete(self, using=None, keep_parents=False):
        if not self.id:
            self.pizza_event.end_reminder.users.add(self.member)

        super().delete(using, keep_parents)

198
199
200
201
202
203
    @property
    def member_name(self):
        if self.member is not None:
            return self.member.get_full_name()
        return self.name

204
205
206
207
    @property
    def can_be_changed(self):
        return not self.paid and not self.pizza_event.has_ended

208
    class Meta:
209
        unique_together = ('pizza_event', 'member',)