Bladeren bron

Create line discount models so we know what discount was applied to what line

master
Viggodevries 2 jaren geleden
bovenliggende
commit
0bd835a068

+ 27
- 0
src/oscar/apps/order/abstract_models.py Bestand weergeven

1286
         return self.offer_name or ""
1286
         return self.offer_name or ""
1287
 
1287
 
1288
 
1288
 
1289
+class AbstractOrderLineDiscount(models.Model):
1290
+    line = models.ForeignKey(
1291
+        "order.Line",
1292
+        on_delete=models.CASCADE,
1293
+        related_name="discounts",
1294
+        verbose_name=_("Line"),
1295
+    )
1296
+    order_discount = models.ForeignKey(
1297
+        "order.OrderDiscount",
1298
+        on_delete=models.CASCADE,
1299
+        related_name="discount_lines",
1300
+        verbose_name=_("Order discount"),
1301
+    )
1302
+
1303
+    is_incl_tax = models.BooleanField()
1304
+    amount = models.DecimalField(
1305
+        _("Line discount (excl. tax)"), decimal_places=2, max_digits=12, default=0
1306
+    )
1307
+
1308
+    class Meta:
1309
+        abstract = True
1310
+        app_label = "order"
1311
+        ordering = ["pk"]
1312
+        verbose_name = _("Order line discount")
1313
+        verbose_name_plural = _("Order line discounts")
1314
+
1315
+
1289
 class AbstractSurcharge(models.Model):
1316
 class AbstractSurcharge(models.Model):
1290
     order = models.ForeignKey(
1317
     order = models.ForeignKey(
1291
         "order.Order",
1318
         "order.Order",

+ 66
- 0
src/oscar/apps/order/migrations/0015_orderlinediscount.py Bestand weergeven

1
+# Generated by Django 4.2.2 on 2023-07-19 13:16
2
+
3
+from django.db import migrations, models
4
+import django.db.models.deletion
5
+
6
+from django.utils.module_loading import import_string
7
+from django.conf import settings
8
+
9
+models_AutoField = import_string(settings.DEFAULT_AUTO_FIELD)
10
+
11
+
12
+class Migration(migrations.Migration):
13
+    dependencies = [
14
+        ("order", "0014_tax_code"),
15
+    ]
16
+
17
+    operations = [
18
+        migrations.CreateModel(
19
+            name="OrderLineDiscount",
20
+            fields=[
21
+                (
22
+                    "id",
23
+                    models_AutoField(
24
+                        auto_created=True,
25
+                        primary_key=True,
26
+                        serialize=False,
27
+                        verbose_name="ID",
28
+                    ),
29
+                ),
30
+                ("is_incl_tax", models.BooleanField()),
31
+                (
32
+                    "amount",
33
+                    models.DecimalField(
34
+                        decimal_places=2,
35
+                        default=0,
36
+                        max_digits=12,
37
+                        verbose_name="Line discount (excl. tax)",
38
+                    ),
39
+                ),
40
+                (
41
+                    "line",
42
+                    models.ForeignKey(
43
+                        on_delete=django.db.models.deletion.CASCADE,
44
+                        related_name="discounts",
45
+                        to="order.line",
46
+                        verbose_name="Line",
47
+                    ),
48
+                ),
49
+                (
50
+                    "order_discount",
51
+                    models.ForeignKey(
52
+                        on_delete=django.db.models.deletion.CASCADE,
53
+                        related_name="discount_lines",
54
+                        to="order.orderdiscount",
55
+                        verbose_name="Order discount",
56
+                    ),
57
+                ),
58
+            ],
59
+            options={
60
+                "verbose_name": "Order line discount",
61
+                "verbose_name_plural": "Order line discounts",
62
+                "ordering": ["pk"],
63
+                "abstract": False,
64
+            },
65
+        ),
66
+    ]

+ 9
- 0
src/oscar/apps/order/models.py Bestand weergeven

120
 
120
 
121
     __all__.append("OrderDiscount")
121
     __all__.append("OrderDiscount")
122
 
122
 
123
+
124
+if not is_model_registered("order", "OrderLineDiscount"):
125
+
126
+    class OrderLineDiscount(AbstractOrderLineDiscount):
127
+        pass
128
+
129
+    __all__.append("OrderDiscount")
130
+
131
+
123
 if not is_model_registered("order", "Surcharge"):
132
 if not is_model_registered("order", "Surcharge"):
124
 
133
 
125
     class Surcharge(AbstractSurcharge):
134
     class Surcharge(AbstractSurcharge):

+ 16
- 4
src/oscar/apps/order/utils.py Bestand weergeven

14
 Order = get_model("order", "Order")
14
 Order = get_model("order", "Order")
15
 Line = get_model("order", "Line")
15
 Line = get_model("order", "Line")
16
 OrderDiscount = get_model("order", "OrderDiscount")
16
 OrderDiscount = get_model("order", "OrderDiscount")
17
+OrderLineDiscount = get_model("order", "OrderLineDiscount")
17
 CommunicationEvent = get_model("order", "CommunicationEvent")
18
 CommunicationEvent = get_model("order", "CommunicationEvent")
18
 CommunicationEventType = get_model("communication", "CommunicationEventType")
19
 CommunicationEventType = get_model("communication", "CommunicationEventType")
19
 Dispatcher = get_class("communication.utils", "Dispatcher")
20
 Dispatcher = get_class("communication.utils", "Dispatcher")
88
                 request,
89
                 request,
89
                 **kwargs
90
                 **kwargs
90
             )
91
             )
91
-            for line in basket.all_lines():
92
-                self.create_line_models(order, line)
93
-                self.update_stock_records(line)
94
-
95
             for voucher in basket.vouchers.select_for_update():
92
             for voucher in basket.vouchers.select_for_update():
96
                 if not voucher.is_active():  # basket ignores inactive vouchers
93
                 if not voucher.is_active():  # basket ignores inactive vouchers
97
                     basket.vouchers.remove(voucher)
94
                     basket.vouchers.remove(voucher)
123
             for voucher in basket.vouchers.all():
120
             for voucher in basket.vouchers.all():
124
                 self.record_voucher_usage(order, voucher, user)
121
                 self.record_voucher_usage(order, voucher, user)
125
 
122
 
123
+            for line in basket.all_lines():
124
+                self.create_line_models(order, line)
125
+                self.update_stock_records(line)
126
+
126
         # Send signal for analytics to pick up
127
         # Send signal for analytics to pick up
127
         order_placed.send(sender=self, order=order, user=user)
128
         order_placed.send(sender=self, order=order, user=user)
128
 
129
 
232
         order_line = Line._default_manager.create(**line_data)
233
         order_line = Line._default_manager.create(**line_data)
233
         self.create_line_price_models(order, order_line, basket_line)
234
         self.create_line_price_models(order, order_line, basket_line)
234
         self.create_line_attributes(order, order_line, basket_line)
235
         self.create_line_attributes(order, order_line, basket_line)
236
+        self.create_line_discount_models(order, order_line, basket_line)
235
         self.create_additional_line_models(order, order_line, basket_line)
237
         self.create_additional_line_models(order, order_line, basket_line)
236
 
238
 
237
         return order_line
239
         return order_line
243
         if line.product.get_product_class().track_stock:
245
         if line.product.get_product_class().track_stock:
244
             line.stockrecord.allocate(line.quantity)
246
             line.stockrecord.allocate(line.quantity)
245
 
247
 
248
+    def create_line_discount_models(self, order, order_line, basket_line):
249
+        for discount in basket_line.discounts:
250
+            order_discount = order.discounts.filter(offer_id=discount.offer.id).first()
251
+            if order_discount:
252
+                order_line.discounts.create(
253
+                    order_discount=order_discount,
254
+                    is_incl_tax=discount.incl_tax,
255
+                    amount=discount.amount,
256
+                )
257
+
246
     def create_additional_line_models(self, order, order_line, basket_line):
258
     def create_additional_line_models(self, order, order_line, basket_line):
247
         """
259
         """
248
         Empty method designed to be overridden.
260
         Empty method designed to be overridden.

+ 52
- 0
tests/integration/order/test_creator.py Bestand weergeven

13
 from oscar.apps.catalogue.models import Product, ProductClass
13
 from oscar.apps.catalogue.models import Product, ProductClass
14
 from oscar.apps.checkout import calculators
14
 from oscar.apps.checkout import calculators
15
 from oscar.apps.offer.utils import Applicator
15
 from oscar.apps.offer.utils import Applicator
16
+from oscar.apps.offer import models
16
 from oscar.apps.order.models import Order
17
 from oscar.apps.order.models import Order
17
 from oscar.apps.order.utils import OrderCreator
18
 from oscar.apps.order.utils import OrderCreator
18
 from oscar.apps.shipping.methods import FixedPrice, Free
19
 from oscar.apps.shipping.methods import FixedPrice, Free
27
 Benefit = get_class("offer.models", "Benefit")
28
 Benefit = get_class("offer.models", "Benefit")
28
 
29
 
29
 SurchargeApplicator = get_class("checkout.applicator", "SurchargeApplicator")
30
 SurchargeApplicator = get_class("checkout.applicator", "SurchargeApplicator")
31
+UK = get_class("partner.strategy", "UK")
30
 
32
 
31
 
33
 
32
 def place_order(creator, **kwargs):
34
 def place_order(creator, **kwargs):
263
         self.assertEqual(D("34.00"), order.total_incl_tax)
265
         self.assertEqual(D("34.00"), order.total_incl_tax)
264
 
266
 
265
 
267
 
268
+class TestOrderOfferCreation(TestCase):
269
+    def setUp(self):
270
+        self.creator = OrderCreator()
271
+        self.basket = factories.create_basket(empty=True)
272
+        self.basket.strategy = UK()
273
+        self.surcharges = SurchargeApplicator().get_applicable_surcharges(self.basket)
274
+        product_range = Range.objects.create(
275
+            name="All products range", includes_all_products=True
276
+        )
277
+        condition = models.CountCondition.objects.create(
278
+            range=product_range, type=models.Condition.COUNT, value=1
279
+        )
280
+        benefit = models.PercentageDiscountBenefit.objects.create(
281
+            range=product_range,
282
+            type=models.Benefit.PERCENTAGE,
283
+            value=20,
284
+        )
285
+        self.offer = models.ConditionalOffer(
286
+            name="Test",
287
+            offer_type=models.ConditionalOffer.SITE,
288
+            condition=condition,
289
+            benefit=benefit,
290
+        )
291
+        self.offer.save()
292
+        self.applicator = Applicator()
293
+
294
+    def test_multi_lines_discount(self):
295
+        add_product(self.basket, D(10))
296
+        add_product(self.basket, D(20))
297
+
298
+        self.applicator.apply_offers(self.basket, [self.offer])
299
+
300
+        place_order(
301
+            self.creator,
302
+            surcharges=self.surcharges,
303
+            basket=self.basket,
304
+            order_number="klatsieknoekie666",
305
+        )
306
+
307
+        order = Order.objects.get(number="klatsieknoekie666")
308
+
309
+        discount = order.discounts.first()
310
+        self.assertEqual(discount.amount, D("7.20"))
311
+        self.assertEqual(discount.discount_lines.count(), 2)
312
+        self.assertEqual(
313
+            discount.amount,
314
+            sum([discount.amount for discount in discount.discount_lines.all()]),
315
+        )
316
+
317
+
266
 class TestMultiSiteOrderCreation(TestCase):
318
 class TestMultiSiteOrderCreation(TestCase):
267
     def setUp(self):
319
     def setUp(self):
268
         self.creator = OrderCreator()
320
         self.creator = OrderCreator()

Laden…
Annuleren
Opslaan