Selaa lähdekoodia

Fix logic for checking whether an offer is active based on date.

- If start_datetime is null, then the offer has no start date.
- If end_datetime is null, then the offer never expires.

Use the ActiveOfferManager in Applicator.get_site_offers instead of
reimplementing the same logic there.

Fixes #2344.
master
Samir Shah 8 vuotta sitten
vanhempi
commit
1784dfef42

+ 5
- 2
src/oscar/apps/offer/abstract_models.py Näytä tiedosto

89
     # dates are ignored and only the dates from the voucher are used to
89
     # dates are ignored and only the dates from the voucher are used to
90
     # determine availability.
90
     # determine availability.
91
     start_datetime = models.DateTimeField(
91
     start_datetime = models.DateTimeField(
92
-        _("Start date"), blank=True, null=True)
92
+        _("Start date"), blank=True, null=True,
93
+        help_text=_("Offers are active from the start date. "
94
+                    "Leave this empty if the offer has no start date."))
93
     end_datetime = models.DateTimeField(
95
     end_datetime = models.DateTimeField(
94
         _("End date"), blank=True, null=True,
96
         _("End date"), blank=True, null=True,
95
-        help_text=_("Offers are active until the end of the 'end date'"))
97
+        help_text=_("Offers are active until the end date. "
98
+                    "Leave this empty if the offer has no expiry date."))
96
 
99
 
97
     # Use this field to limit the number of times this offer can be applied in
100
     # Use this field to limit the number of times this offer can be applied in
98
     # total.  Note that a single order can apply an offer multiple times so
101
     # total.  Note that a single order can apply an offer multiple times so

+ 1
- 12
src/oscar/apps/offer/applicator.py Näytä tiedosto

67
         """
67
         """
68
         Return site offers that are available to all users
68
         Return site offers that are available to all users
69
         """
69
         """
70
-        cutoff = now()
71
-        date_based = Q(
72
-            Q(start_datetime__lte=cutoff),
73
-            Q(end_datetime__gte=cutoff) | Q(end_datetime=None),
74
-        )
75
-
76
-        nondate_based = Q(start_datetime=None, end_datetime=None)
77
-
78
         ConditionalOffer = get_model('offer', 'ConditionalOffer')
70
         ConditionalOffer = get_model('offer', 'ConditionalOffer')
79
-        qs = ConditionalOffer.objects.filter(
80
-            date_based | nondate_based,
81
-            offer_type=ConditionalOffer.SITE,
82
-            status=ConditionalOffer.OPEN)
71
+        qs = ConditionalOffer.active.filter(offer_type=ConditionalOffer.SITE)
83
         # Using select_related with the condition/benefit ranges doesn't seem
72
         # Using select_related with the condition/benefit ranges doesn't seem
84
         # to work.  I think this is because both the related objects have the
73
         # to work.  I think this is because both the related objects have the
85
         # FK to range with the same name.
74
         # FK to range with the same name.

+ 2
- 1
src/oscar/apps/offer/managers.py Näytä tiedosto

10
         cutoff = now()
10
         cutoff = now()
11
         return super(ActiveOfferManager, self).get_queryset().filter(
11
         return super(ActiveOfferManager, self).get_queryset().filter(
12
             models.Q(end_datetime__gte=cutoff) | models.Q(end_datetime=None),
12
             models.Q(end_datetime__gte=cutoff) | models.Q(end_datetime=None),
13
-            start_datetime__lte=cutoff).filter(status=self.model.OPEN)
13
+            models.Q(start_datetime__lte=cutoff) | models.Q(start_datetime=None),
14
+        ).filter(status=self.model.OPEN)
14
 
15
 
15
 
16
 
16
 class BrowsableRangeManager(models.Manager):
17
 class BrowsableRangeManager(models.Manager):

+ 25
- 0
src/oscar/apps/offer/migrations/0006_auto_20170504_0616.py Näytä tiedosto

1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11 on 2017-05-04 05:16
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('offer', '0005_auto_20170423_1217'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='conditionaloffer',
17
+            name='end_datetime',
18
+            field=models.DateTimeField(blank=True, help_text='Offers are active until the end date. Leave this empty if the offer has no expiry date.', null=True, verbose_name='End date'),
19
+        ),
20
+        migrations.AlterField(
21
+            model_name='conditionaloffer',
22
+            name='start_datetime',
23
+            field=models.DateTimeField(blank=True, help_text='Offers are active from the start date. Leave this empty if the offer has no start date.', null=True, verbose_name='Start date'),
24
+        ),
25
+    ]

+ 13
- 0
tests/integration/offer/test_applicator.py Näytä tiedosto

58
         priorities = [offer.priority for offer in offers]
58
         priorities = [offer.priority for offer in offers]
59
         self.assertEqual(sorted(priorities, reverse=True), priorities)
59
         self.assertEqual(sorted(priorities, reverse=True), priorities)
60
 
60
 
61
+    def test_get_site_offers(self):
62
+        site_offer = models.ConditionalOffer.objects.create(
63
+            name="globaloffer", condition=self.condition,
64
+            benefit=self.benefit, offer_type=models.ConditionalOffer.SITE)
65
+        session_offer = models.ConditionalOffer.objects.create(
66
+            name="sessionoffer", condition=self.condition,
67
+            benefit=self.benefit, offer_type=models.ConditionalOffer.SESSION)
68
+
69
+        site_offers = Applicator().get_site_offers()
70
+        # Only one offer should be returned
71
+        self.assertEqual(len(site_offers), 1)
72
+        self.assertEqual(site_offers[0].name, "globaloffer")
73
+
61
 
74
 
62
 class TestOfferApplicationsWrapper(TestCase):
75
 class TestOfferApplicationsWrapper(TestCase):
63
 
76
 

+ 20
- 0
tests/integration/offer/test_availability.py Näytä tiedosto

59
     def test_is_active_on_end_datetime(self):
59
     def test_is_active_on_end_datetime(self):
60
         self.assertTrue(self.offer.is_available(test_date=self.end))
60
         self.assertTrue(self.offer.is_available(test_date=self.end))
61
 
61
 
62
+    def test_active_on_null_end_datetime(self):
63
+        # null end_datetime means offer should never expire
64
+        offer = models.ConditionalOffer(start_datetime=self.start,
65
+                                        end_datetime=None)
66
+        test = datetime.date(2017, 3, 10)
67
+        self.assertTrue(offer.is_available(test_date=test))
68
+
69
+    def test_active_on_null_start_datetime(self):
70
+        # null start_datetime means offer is active from the beginning of time
71
+        offer = models.ConditionalOffer(start_datetime=None,
72
+                                        end_datetime=self.end)
73
+        test = datetime.date(2000, 3, 10)
74
+        self.assertTrue(offer.is_available(test_date=test))
75
+
76
+    def test_active_on_null_start_and_end_datetime(self):
77
+        # null datetimes - offer is always available
78
+        offer = models.ConditionalOffer(start_datetime=None, end_datetime=None)
79
+        test = datetime.date(2017, 3, 10)
80
+        self.assertTrue(offer.is_available(test_date=test))
81
+
62
 
82
 
63
 class TestAConsumptionFrequencyBasedConditionalOffer(TestCase):
83
 class TestAConsumptionFrequencyBasedConditionalOffer(TestCase):
64
 
84
 

+ 19
- 1
tests/integration/offer/test_manager.py Näytä tiedosto

10
 class TestActiveOfferManager(TestCase):
10
 class TestActiveOfferManager(TestCase):
11
 
11
 
12
     def test_includes_offers_in_date_range(self):
12
     def test_includes_offers_in_date_range(self):
13
-        # Create offer that is available but with the wrong status
14
         now = timezone.now()
13
         now = timezone.now()
15
         start = now - datetime.timedelta(days=1)
14
         start = now - datetime.timedelta(days=1)
16
         end = now + datetime.timedelta(days=1)
15
         end = now + datetime.timedelta(days=1)
19
         filtered_offers = models.ConditionalOffer.active.all()
18
         filtered_offers = models.ConditionalOffer.active.all()
20
         self.assertEqual(1, len(filtered_offers))
19
         self.assertEqual(1, len(filtered_offers))
21
 
20
 
21
+    def test_includes_offers_with_null_start_date(self):
22
+        now = timezone.now()
23
+        end = now + datetime.timedelta(days=1)
24
+        factories.create_offer(start=None, end=end)
25
+        filtered_offers = models.ConditionalOffer.active.all()
26
+        self.assertEqual(1, len(filtered_offers))
27
+
28
+    def test_includes_offers_with_null_end_date(self):
29
+        now = timezone.now()
30
+        start = now - datetime.timedelta(days=1)
31
+        factories.create_offer(start=start, end=None)
32
+        filtered_offers = models.ConditionalOffer.active.all()
33
+        self.assertEqual(1, len(filtered_offers))
34
+
35
+    def test_includes_offers_with_null_start_and_end_date(self):
36
+        factories.create_offer(start=None, end=None)
37
+        filtered_offers = models.ConditionalOffer.active.all()
38
+        self.assertEqual(1, len(filtered_offers))
39
+
22
     def test_filters_out_expired_offers(self):
40
     def test_filters_out_expired_offers(self):
23
         # Create offer that is available but with the wrong status
41
         # Create offer that is available but with the wrong status
24
         now = timezone.now()
42
         now = timezone.now()

Loading…
Peruuta
Tallenna