소스 검색

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

master
Viggodevries 2 년 전
부모
커밋
0bd835a068

+ 27
- 0
src/oscar/apps/order/abstract_models.py 파일 보기

@@ -1286,6 +1286,33 @@ class AbstractOrderDiscount(models.Model):
1286 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 1316
 class AbstractSurcharge(models.Model):
1290 1317
     order = models.ForeignKey(
1291 1318
         "order.Order",

+ 66
- 0
src/oscar/apps/order/migrations/0015_orderlinediscount.py 파일 보기

@@ -0,0 +1,66 @@
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 파일 보기

@@ -120,6 +120,15 @@ if not is_model_registered("order", "OrderDiscount"):
120 120
 
121 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 132
 if not is_model_registered("order", "Surcharge"):
124 133
 
125 134
     class Surcharge(AbstractSurcharge):

+ 16
- 4
src/oscar/apps/order/utils.py 파일 보기

@@ -14,6 +14,7 @@ from . import exceptions
14 14
 Order = get_model("order", "Order")
15 15
 Line = get_model("order", "Line")
16 16
 OrderDiscount = get_model("order", "OrderDiscount")
17
+OrderLineDiscount = get_model("order", "OrderLineDiscount")
17 18
 CommunicationEvent = get_model("order", "CommunicationEvent")
18 19
 CommunicationEventType = get_model("communication", "CommunicationEventType")
19 20
 Dispatcher = get_class("communication.utils", "Dispatcher")
@@ -88,10 +89,6 @@ class OrderCreator(object):
88 89
                 request,
89 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 92
             for voucher in basket.vouchers.select_for_update():
96 93
                 if not voucher.is_active():  # basket ignores inactive vouchers
97 94
                     basket.vouchers.remove(voucher)
@@ -123,6 +120,10 @@ class OrderCreator(object):
123 120
             for voucher in basket.vouchers.all():
124 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 127
         # Send signal for analytics to pick up
127 128
         order_placed.send(sender=self, order=order, user=user)
128 129
 
@@ -232,6 +233,7 @@ class OrderCreator(object):
232 233
         order_line = Line._default_manager.create(**line_data)
233 234
         self.create_line_price_models(order, order_line, basket_line)
234 235
         self.create_line_attributes(order, order_line, basket_line)
236
+        self.create_line_discount_models(order, order_line, basket_line)
235 237
         self.create_additional_line_models(order, order_line, basket_line)
236 238
 
237 239
         return order_line
@@ -243,6 +245,16 @@ class OrderCreator(object):
243 245
         if line.product.get_product_class().track_stock:
244 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 258
     def create_additional_line_models(self, order, order_line, basket_line):
247 259
         """
248 260
         Empty method designed to be overridden.

+ 52
- 0
tests/integration/order/test_creator.py 파일 보기

@@ -13,6 +13,7 @@ from django.utils import timezone
13 13
 from oscar.apps.catalogue.models import Product, ProductClass
14 14
 from oscar.apps.checkout import calculators
15 15
 from oscar.apps.offer.utils import Applicator
16
+from oscar.apps.offer import models
16 17
 from oscar.apps.order.models import Order
17 18
 from oscar.apps.order.utils import OrderCreator
18 19
 from oscar.apps.shipping.methods import FixedPrice, Free
@@ -27,6 +28,7 @@ Range = get_class("offer.models", "Range")
27 28
 Benefit = get_class("offer.models", "Benefit")
28 29
 
29 30
 SurchargeApplicator = get_class("checkout.applicator", "SurchargeApplicator")
31
+UK = get_class("partner.strategy", "UK")
30 32
 
31 33
 
32 34
 def place_order(creator, **kwargs):
@@ -263,6 +265,56 @@ class TestShippingOfferForOrder(TestCase):
263 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 318
 class TestMultiSiteOrderCreation(TestCase):
267 319
     def setUp(self):
268 320
         self.creator = OrderCreator()

Loading…
취소
저장