|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+from decimal import Decimal
|
|
|
2
|
+import datetime
|
|
|
3
|
+
|
|
|
4
|
+from django.core import exceptions
|
|
|
5
|
+from django.db import models
|
|
|
6
|
+from django.utils.translation import ugettext as _
|
|
|
7
|
+
|
|
|
8
|
+
|
|
|
9
|
+class AbstractVoucher(models.Model):
|
|
|
10
|
+ """
|
|
|
11
|
+ A voucher. This is simply a link to a collection of offers
|
|
|
12
|
+
|
|
|
13
|
+ Note that there are three possible "usage" models:
|
|
|
14
|
+ (a) Single use
|
|
|
15
|
+ (b) Multi-use
|
|
|
16
|
+ (c) Once per customer
|
|
|
17
|
+ """
|
|
|
18
|
+ name = models.CharField(_("Name"), max_length=128,
|
|
|
19
|
+ help_text=_("""This will be shown in the checkout and basket once the voucher is entered"""))
|
|
|
20
|
+ code = models.CharField(_("Code"), max_length=128, db_index=True, unique=True,
|
|
|
21
|
+ help_text=_("""Case insensitive / No spaces allowed"""))
|
|
|
22
|
+ offers = models.ManyToManyField('offer.ConditionalOFfer', related_name='vouchers',
|
|
|
23
|
+ limit_choices_to={'offer_type': "Voucher"})
|
|
|
24
|
+
|
|
|
25
|
+ SINGLE_USE, MULTI_USE, ONCE_PER_CUSTOMER = ('Single use', 'Multi-use', 'Once per customer')
|
|
|
26
|
+ USAGE_CHOICES = (
|
|
|
27
|
+ (SINGLE_USE, _("Can only be used by one customer")),
|
|
|
28
|
+ (MULTI_USE, _("Can only be used any number of times")),
|
|
|
29
|
+ (ONCE_PER_CUSTOMER, _("Can be used once by each customer")),
|
|
|
30
|
+ )
|
|
|
31
|
+ usage = models.CharField(_("Usage"), max_length=128, choices=USAGE_CHOICES, default=MULTI_USE)
|
|
|
32
|
+
|
|
|
33
|
+ start_date = models.DateField()
|
|
|
34
|
+ end_date = models.DateField()
|
|
|
35
|
+
|
|
|
36
|
+ # Summary information
|
|
|
37
|
+ num_basket_additions = models.PositiveIntegerField(default=0)
|
|
|
38
|
+ num_orders = models.PositiveIntegerField(default=0)
|
|
|
39
|
+ total_discount = models.DecimalField(decimal_places=2, max_digits=12, default=Decimal('0.00'))
|
|
|
40
|
+
|
|
|
41
|
+ date_created = models.DateField(auto_now_add=True)
|
|
|
42
|
+
|
|
|
43
|
+ class Meta:
|
|
|
44
|
+ get_latest_by = 'date_created'
|
|
|
45
|
+ abstract = True
|
|
|
46
|
+
|
|
|
47
|
+ def __unicode__(self):
|
|
|
48
|
+ return self.name
|
|
|
49
|
+
|
|
|
50
|
+ def clean(self):
|
|
|
51
|
+ if self.start_date and self.end_date and self.start_date > self.end_date:
|
|
|
52
|
+ raise exceptions.ValidationError(_('End date should be later than start date'))
|
|
|
53
|
+
|
|
|
54
|
+ def save(self, *args, **kwargs):
|
|
|
55
|
+ self.code = self.code.upper()
|
|
|
56
|
+ super(AbstractVoucher, self).save(*args, **kwargs)
|
|
|
57
|
+
|
|
|
58
|
+ def is_active(self, test_date=None):
|
|
|
59
|
+ """
|
|
|
60
|
+ Tests whether this voucher is currently active.
|
|
|
61
|
+ """
|
|
|
62
|
+ if not test_date:
|
|
|
63
|
+ test_date = datetime.date.today()
|
|
|
64
|
+ return self.start_date <= test_date and test_date < self.end_date
|
|
|
65
|
+
|
|
|
66
|
+ def is_available_to_user(self, user=None):
|
|
|
67
|
+ """
|
|
|
68
|
+ Tests whether this voucher is available to the passed user.
|
|
|
69
|
+
|
|
|
70
|
+ Returns a tuple of a boolean for whether it is successulf, and a message
|
|
|
71
|
+ """
|
|
|
72
|
+ is_available, message = False, ''
|
|
|
73
|
+ if self.usage == self.SINGLE_USE:
|
|
|
74
|
+ is_available = self.applications.count() == 0
|
|
|
75
|
+ if not is_available:
|
|
|
76
|
+ message = "This voucher has already been used"
|
|
|
77
|
+ elif self.usage == self.MULTI_USE:
|
|
|
78
|
+ is_available = True
|
|
|
79
|
+ elif self.usage == self.ONCE_PER_CUSTOMER:
|
|
|
80
|
+ if not user.is_authenticated():
|
|
|
81
|
+ is_available = False
|
|
|
82
|
+ message = "This voucher is only available to signed in users"
|
|
|
83
|
+ else:
|
|
|
84
|
+ is_available = self.applications.filter(voucher=self, user=user).count() == 0
|
|
|
85
|
+ if not is_available:
|
|
|
86
|
+ message = "You have already used this voucher in a previous order"
|
|
|
87
|
+ return is_available, message
|
|
|
88
|
+
|
|
|
89
|
+ def record_usage(self, order, user):
|
|
|
90
|
+ """
|
|
|
91
|
+ Records a usage of this voucher in an order.
|
|
|
92
|
+ """
|
|
|
93
|
+ self.applications.create(voucher=self, order=order, user=user)
|
|
|
94
|
+
|
|
|
95
|
+
|
|
|
96
|
+class AbstractVoucherApplication(models.Model):
|
|
|
97
|
+ """
|
|
|
98
|
+ For tracking how often a voucher has been used
|
|
|
99
|
+ """
|
|
|
100
|
+ voucher = models.ForeignKey('voucher.Voucher', related_name="applications")
|
|
|
101
|
+ # It is possible for an anonymous user to apply a voucher so we need to allow
|
|
|
102
|
+ # the user to be nullable
|
|
|
103
|
+ user = models.ForeignKey('auth.User', blank=True, null=True)
|
|
|
104
|
+ order = models.ForeignKey('order.Order')
|
|
|
105
|
+ date_created = models.DateField(auto_now_add=True)
|
|
|
106
|
+
|
|
|
107
|
+ class Meta:
|
|
|
108
|
+ abstract = True
|
|
|
109
|
+
|
|
|
110
|
+ def __unicode__(self):
|
|
|
111
|
+ return u"'%s' used by '%s'" % (self.voucher, self.user)
|