models.py 8.39 KB
Newer Older
1
from django.core.exceptions import ValidationError, ObjectDoesNotExist
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
from members.models import Member
10
from payments.models import Payment
11
from pushnotifications.models import ScheduledMessage, Category
12
from utils.translation import ModelTranslateMeta, MultilingualField
13
14
15
16
17


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

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

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

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

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

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

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

54
        try:
55
            events = PizzaEvent.objects.filter(
56
                end__gt=timezone.now() - timezone.timedelta(hours=8),
57
58
59
60
61
62
                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()
63
64
65
        except PizzaEvent.DoesNotExist:
            return None

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

70
71
72
73
74
75
76
77
78
79
80
81
82
83
    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),
            })

84
    def clean(self):
85
86
        super().clean()

87
88
89
90
91
        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.'),
            })
92

93
    def save(self, *args, **kwargs):
94
        if self.send_notification and not self.end_reminder:
95
96
97
98
99
100
            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')
101
            end_reminder.time = self.end - timezone.timedelta(minutes=10)
102
103
104
            end_reminder.save()

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

111
            self.end_reminder = end_reminder
112
113
        elif (self.send_notification and self.end_reminder and
                self._end != self.end):
114
115
            self.end_reminder.time = self.end
            self.end_reminder.save()
116
        elif not self.send_notification and self.end_reminder:
117
118
119
120
            end_reminder = self.end_reminder
            self.end_reminder = None
            if not self.end_reminder.sent:
                end_reminder.delete()
121

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

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
124
125
126
127
128
129
    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)

130
131
132
133
    def __str__(self):
        return 'Pizzas for ' + str(self.event)


Thom Wiggers's avatar
Thom Wiggers committed
134
135
136
137
138
139
140
class AvailableProductManager(models.Manager):
    """Only shows available products"""

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


141
class Product(models.Model, metaclass=ModelTranslateMeta):
Thom Wiggers's avatar
Thom Wiggers committed
142
143
144
    objects = models.Manager()
    available_products = AvailableProductManager()

145
146
147
148
    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
149
150
151
152
    restricted = models.BooleanField(
        default=False,
        help_text=_("Only allow to be ordered by people with the "
                    "'order restricted products' permission."))
153
154
155
156

    def __str__(self):
        return self.name

157
158
    class Meta:
        ordering = ('name', )
159
160
161
        permissions = (
            ('order_restricted_products', _('Order restricted products')),
        )
162

163
164
165

class Order(models.Model):
    member = models.ForeignKey(
166
        members.models.Member,
167
168
169
170
171
172
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )

    name = models.CharField(
173
        verbose_name=_('name'),
174
175
176
177
178
179
        max_length=50,
        help_text=_('Use this for non-members'),
        null=True,
        blank=True,
    )

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    payment = models.OneToOneField(
        verbose_name=_('payment'),
        to='payments.Payment',
        related_name='pizzas_order',
        on_delete=models.CASCADE,
    )

    product = models.ForeignKey(
        verbose_name=_('product'),
        to=Product,
        on_delete=models.PROTECT,
    )

    pizza_event = models.ForeignKey(
        verbose_name=_('event'),
        to=PizzaEvent,
        on_delete=models.CASCADE
    )
198
199
200
201
202
203
204
205
206

    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'),
            })

207
    def save(self, *args, **kwargs):
208
        if not self.id and self.pizza_event.end_reminder:
209
210
            self.pizza_event.end_reminder.users.remove(self.member)

211
212
213
214
215
216
217
218
219
220
221
222
223
224
        notes = (f'Pizza order by {self.member_name} '
                 f'for {self.pizza_event.event.title_en}')
        try:
            self.payment.notes = notes
            self.payment.paid_by = self.member
            self.payment.amount = self.product.price
            self.payment.save()
        except ObjectDoesNotExist:
            self.payment = Payment.objects.create(
                amount=self.product.price,
                notes=notes,
                paid_by=self.member
            )

225
226
227
        super().save(*args, **kwargs)

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

231
        payment = self.payment
232
        super().delete(using, keep_parents)
233
        payment.delete()
234

235
236
237
238
239
240
    @property
    def member_name(self):
        if self.member is not None:
            return self.member.get_full_name()
        return self.name

241
242
243
244
245
246
247
248
249
250
251
252
    @property
    def member_last_name(self):
        if self.member is not None:
            return self.member.last_name
        return ' '.join(self.name.split(' ')[1:])

    @property
    def member_first_name(self):
        if self.member is not None:
            return self.member.first_name
        return self.name.strip(' ').split(' ')[0]

253
254
    @property
    def can_be_changed(self):
255
256
257
258
259
        try:
            return (self.payment and not self.payment.processed
                    and not self.pizza_event.has_ended)
        except ObjectDoesNotExist:
            return False
260

261
    class Meta:
262
        unique_together = ('pizza_event', 'member',)
263
264
265
266
267
268

    def __str__(self):
        return _("Order by {member_name}: {product}").format(
            member_name=self.member_name,
            product=self.product
        )