Просмотр исходного кода

Now including the new voucher app

master
David Winterbottom 14 лет назад
Родитель
Сommit
d56f34f5f8

+ 0
- 0
oscar/apps/voucher/__init__.py Просмотреть файл


+ 111
- 0
oscar/apps/voucher/abstract_models.py Просмотреть файл

@@ -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)        

+ 29
- 0
oscar/apps/voucher/admin.py Просмотреть файл

@@ -0,0 +1,29 @@
1
+from django.contrib import admin
2
+
3
+from oscar.core.loading import import_module
4
+import_module('voucher.models', ['Voucher', 'VoucherApplication'], locals())
5
+
6
+    
7
+class VoucherAdmin(admin.ModelAdmin):
8
+    list_display = ('name', 'code', 'usage', 'num_basket_additions', 'num_orders', 'total_discount')    
9
+    readonly_fields = ('num_basket_additions', 'num_orders', 'total_discount')
10
+    fieldsets = (
11
+        (None, {
12
+            'fields': ('name', 'code', 'usage', 'start_date', 'end_date')
13
+        }),
14
+        ('Benefit', {
15
+            'fields': ('offers',)
16
+        }),
17
+        ('Usage', {
18
+            'fields': ('num_basket_additions', 'num_orders', 'total_discount')
19
+        }),
20
+        
21
+    )
22
+    
23
+class VoucherApplicationAdmin(admin.ModelAdmin):
24
+    list_display = ('voucher', 'user', 'order', 'date_created')
25
+    readonly_fields = ('voucher', 'user', 'order')        
26
+
27
+
28
+admin.site.register(Voucher, VoucherAdmin)
29
+admin.site.register(VoucherApplication, VoucherApplicationAdmin)

+ 11
- 0
oscar/apps/voucher/models.py Просмотреть файл

@@ -0,0 +1,11 @@
1
+from oscar.apps.voucher.abstract_models import AbstractVoucher, AbstractVoucherApplication
2
+
3
+
4
+class Voucher(AbstractVoucher):
5
+    pass
6
+
7
+
8
+class VoucherApplication(AbstractVoucherApplication):
9
+    pass
10
+
11
+

+ 33
- 0
oscar/apps/voucher/tests.py Просмотреть файл

@@ -0,0 +1,33 @@
1
+import datetime
2
+
3
+from django.test import TestCase
4
+
5
+from oscar.apps.voucher.models import Voucher
6
+
7
+
8
+class VoucherTest(TestCase):
9
+    
10
+    def test_is_active(self):
11
+        start = datetime.date(2011, 01, 01)
12
+        test = datetime.date(2011, 01, 10)
13
+        end = datetime.date(2011, 02, 01)
14
+        voucher = Voucher(start_date=start, end_date=end)
15
+        self.assertTrue(voucher.is_active(test))
16
+
17
+    def test_is_inactive(self):
18
+        start = datetime.date(2011, 01, 01)
19
+        test = datetime.date(2011, 03, 10)
20
+        end = datetime.date(2011, 02, 01)
21
+        voucher = Voucher(start_date=start, end_date=end)
22
+        self.assertFalse(voucher.is_active(test))
23
+        
24
+    def test_codes_are_saved_as_uppercase(self):
25
+        start = datetime.date(2011, 01, 01)
26
+        end = datetime.date(2011, 02, 01)
27
+        voucher = Voucher(name="Dummy voucher", code="lowercase", start_date=start, end_date=end)
28
+        voucher.save()
29
+        self.assertEquals("LOWERCASE", voucher.code)
30
+        
31
+
32
+    
33
+   

Загрузка…
Отмена
Сохранить