Skip to content
This repository
  • 4 commits
  • 14 files changed
  • 0 comments
  • 1 contributor
Aug 24, 2012
David Winterbottom Improve django admin for basket app 7ddb7f3
David Winterbottom Update order creator to create payment event quantity objects
This fixes an issue with payment events not having any lines set.  Also
includes some admin improvements.
f7c20f9
David Winterbottom Amend demo site to have a custom checkout app
It only creates a payment event at the moment but it will do more.  Oh
yes.
446585d
David Winterbottom Altered default partner wrapper to not specify a dispatch date
Turns out this is a bit confusing.  Better for it to do nothing by
default and this behaviour can be enabled when it is needed.
3507b07
11  oscar/apps/basket/abstract_models.py
@@ -112,7 +112,8 @@ def add_product(self, product, quantity=1, options=None):
112 112
         # product (eg T-shirts with different personalisations)
113 113
         line_ref = self._create_line_reference(product, options)
114 114
 
115  
-        # Determine price to store (if one exists)
  115
+        # Determine price to store (if one exists).  It is only stored for audit
  116
+        # and sometimes caching.
116 117
         price = None
117 118
         if product.has_stockrecord:
118 119
             stockrecord = product.stockrecord
@@ -366,6 +367,10 @@ def time_since_creation(self, test_datetime=None):
366 367
             test_datetime = datetime.datetime.now()
367 368
         return test_datetime - self.date_created
368 369
 
  370
+    @property
  371
+    def contains_a_voucher(self):
  372
+        return self.vouchers.all().count() > 0
  373
+
369 374
     def contains_voucher(self, code):
370 375
         """
371 376
         Test whether the basket contains a voucher with a given code
@@ -392,6 +397,10 @@ class AbstractLine(models.Model):
392 397
 
393 398
     product = models.ForeignKey('catalogue.Product', related_name='basket_lines')
394 399
     quantity = models.PositiveIntegerField(_('Quantity'), default=1)
  400
+
  401
+    # We store the unit price incl tax of the product when it is first added to
  402
+    # the basket.  This allows us to tell if a product has changed price since a
  403
+    # person first added it to their basket.
395 404
     price_incl_tax = models.DecimalField(_('Price incl. Tax'), decimal_places=2, max_digits=12,
396 405
                                          null=True)
397 406
 
12  oscar/apps/basket/admin.py
... ...
@@ -1,11 +1,21 @@
1 1
 from django.contrib import admin
2 2
 from django.db.models import get_model
3 3
 
  4
+Line = get_model('basket', 'line')
  5
+
  6
+
  7
+class LineInline(admin.TabularInline):
  8
+    model = Line
  9
+
4 10
 
5 11
 class BasketAdmin(admin.ModelAdmin):
  12
+    list_display = ('id', 'owner', 'status', 'num_lines', 'total_incl_tax',
  13
+                    'contains_a_voucher', 'date_created', 'date_submitted',
  14
+                    'time_before_submit')
6 15
     read_only_fields = ('date_merged', 'date_submitted')
  16
+    inlines = [LineInline]
7 17
 
8 18
 
9 19
 admin.site.register(get_model('basket', 'basket'), BasketAdmin)
10  
-admin.site.register(get_model('basket', 'line'))
  20
+admin.site.register(Line)
11 21
 admin.site.register(get_model('basket', 'LineAttribute'))
9  oscar/apps/checkout/mixins.py
@@ -15,6 +15,7 @@
15 15
 CommunicationEvent = get_model('order', 'CommunicationEvent')
16 16
 PaymentEventType = get_model('order', 'PaymentEventType')
17 17
 PaymentEvent = get_model('order', 'PaymentEvent')
  18
+PaymentEventQuantity = get_model('order', 'PaymentEventQuantity')
18 19
 UserAddress = get_model('address', 'UserAddress')
19 20
 Basket = get_model('basket', 'Basket')
20 21
 CommunicationEventType = get_model('customer', 'CommunicationEventType')
@@ -56,7 +57,7 @@ def add_payment_source(self, source):
56 57
         self._payment_sources.append(source)
57 58
 
58 59
     def add_payment_event(self, event_type_name, amount):
59  
-        event_type, n = PaymentEventType.objects.get_or_create(name=event_type_name)
  60
+        event_type, __ = PaymentEventType.objects.get_or_create(name=event_type_name)
60 61
         if self._payment_events is None:
61 62
             self._payment_events = []
62 63
         event = PaymentEvent(event_type=event_type, amount=amount)
@@ -196,6 +197,12 @@ def save_payment_events(self, order):
196 197
         for event in self._payment_events:
197 198
             event.order = order
198 199
             event.save()
  200
+        # We assume all lines are involved in the initial payment event
  201
+        for line in order.lines.all():
  202
+            PaymentEventQuantity.objects.create(
  203
+                event=event,
  204
+                line=line,
  205
+                quantity=line.quantity)
199 206
 
200 207
     def save_payment_sources(self, order):
201 208
         """
3  oscar/apps/order/abstract_models.py
@@ -528,6 +528,9 @@ class Meta:
528 528
     def __unicode__(self):
529 529
         return _("Payment event for order %s") % self.order
530 530
 
  531
+    def num_affected_lines(self):
  532
+        return self.lines.all().count()
  533
+
531 534
 
532 535
 class PaymentEventQuantity(models.Model):
533 536
     """
35  oscar/apps/order/admin.py
@@ -12,14 +12,21 @@
12 12
 ShippingEventType = get_model('order', 'ShippingEventType')
13 13
 PaymentEvent = get_model('order', 'PaymentEvent')
14 14
 PaymentEventType = get_model('order', 'PaymentEventType')
  15
+PaymentEventQuantity = get_model('order', 'PaymentEventQuantity')
15 16
 LineAttribute = get_model('order', 'LineAttribute')
16 17
 OrderDiscount = get_model('order', 'OrderDiscount')
17 18
 
18 19
 
  20
+class LineInline(admin.TabularInline):
  21
+    model = Line
  22
+    extra = 0
  23
+
  24
+
19 25
 class OrderAdmin(admin.ModelAdmin):
20 26
     raw_id_fields = ['user','billing_address','shipping_address', ]
21 27
     list_display = ('number', 'total_incl_tax', 'site', 'user', 'billing_address', 'date_placed')
22 28
     readonly_fields = ('number', 'total_incl_tax', 'total_excl_tax', 'shipping_incl_tax', 'shipping_excl_tax')
  29
+    inlines = [LineInline]
23 30
 
24 31
 
25 32
 class LineAdmin(admin.ModelAdmin):
@@ -33,25 +40,35 @@ class LinePriceAdmin(admin.ModelAdmin):
33 40
 class ShippingEventTypeAdmin(admin.ModelAdmin):
34 41
     list_display = ('name', 'is_required', 'sequence_number')
35 42
     exclude = ('code',)
36  
-    
37  
-    
  43
+
  44
+
  45
+class PaymentEventQuantityInline(admin.TabularInline):
  46
+    model = PaymentEventQuantity
  47
+    extra = 0
  48
+
  49
+
  50
+class PaymentEventAdmin(admin.ModelAdmin):
  51
+    list_display = 'order', 'event_type', 'amount', 'num_affected_lines', 'date'
  52
+    inlines = [PaymentEventQuantityInline]
  53
+
  54
+
38 55
 class PaymentEventTypeAdmin(admin.ModelAdmin):
39 56
     exclude = ('code',)
40  
-    
41  
-    
  57
+
  58
+
42 59
 class OrderNoteAdmin(admin.ModelAdmin):
43 60
     exclude = ('user',)
44  
-    
  61
+
45 62
     def save_model(self, request, obj, form, change):
46 63
         if not change:
47 64
             obj.user = request.user
48 65
         obj.save()
49  
-        
50  
-        
  66
+
  67
+
51 68
 class OrderDiscountAdmin(admin.ModelAdmin):
52 69
     readonly_fields = ('order' ,'offer_id', 'voucher_id', 'voucher_code', 'amount')
53 70
     list_display = ('order', 'offer', 'voucher', 'voucher_code', 'amount')
54  
-    
  71
+
55 72
 
56 73
 admin.site.register(Order, OrderAdmin)
57 74
 admin.site.register(OrderNote, OrderNoteAdmin)
@@ -60,7 +77,7 @@ class OrderDiscountAdmin(admin.ModelAdmin):
60 77
 admin.site.register(LinePrice, LinePriceAdmin)
61 78
 admin.site.register(ShippingEvent)
62 79
 admin.site.register(ShippingEventType, ShippingEventTypeAdmin)
63  
-admin.site.register(PaymentEvent)
  80
+admin.site.register(PaymentEvent, PaymentEventAdmin)
64 81
 admin.site.register(PaymentEventType, PaymentEventTypeAdmin)
65 82
 admin.site.register(LineAttribute)
66 83
 admin.site.register(OrderDiscount, OrderDiscountAdmin)
17  oscar/apps/partner/wrappers.py
... ...
@@ -1,4 +1,3 @@
1  
-import datetime
2 1
 from decimal import Decimal as D
3 2
 
4 3
 from django.utils.translation import ugettext_lazy as _
@@ -8,7 +7,7 @@ class DefaultWrapper(object):
8 7
     """
9 8
     Default stockrecord wrapper
10 9
     """
11  
-    
  10
+
12 11
     def is_available_to_buy(self, stockrecord):
13 12
         """
14 13
         Test whether a product is available to buy.
@@ -56,7 +55,7 @@ def availability_code(self, stockrecord):
56 55
         if self.is_available_to_buy(stockrecord):
57 56
             return 'available'
58 57
         return 'outofstock'
59  
-    
  58
+
60 59
     def availability(self, stockrecord):
61 60
         """
62 61
         Return an availability message for the passed stockrecord.
@@ -68,16 +67,12 @@ def availability(self, stockrecord):
68 67
         if self.is_available_to_buy(stockrecord):
69 68
             return _('Available')
70 69
         return _("Not available")
71  
-    
  70
+
72 71
     def dispatch_date(self, stockrecord):
73  
-        if stockrecord.net_stock_level:
74  
-            # Assume next day for in-stock items
75  
-            return datetime.date.today() + datetime.timedelta(days=1)
76  
-        # Assume one week for out-of-stock items
77  
-        return datetime.date.today() + datetime.timedelta(days=7)
78  
-    
  72
+        return None
  73
+
79 74
     def lead_time(self, stockrecord):
80 75
         return 1
81  
-    
  76
+
82 77
     def calculate_tax(self, stockrecord):
83 78
         return D('0.00')
1  oscar/apps/payment/admin.py
... ...
@@ -1,5 +1,6 @@
1 1
 from django.contrib import admin
2 2
 from django.db.models import get_model
  3
+
3 4
 Source = get_model('payment', 'Source')
4 5
 Transaction = get_model('payment', 'Transaction')
5 6
 SourceType = get_model('payment', 'SourceType')
11  sites/demo/apps/app.py
... ...
@@ -0,0 +1,11 @@
  1
+from oscar.app import Shop
  2
+
  3
+from apps.checkout.app import application as checkout_app
  4
+
  5
+
  6
+class Application(Shop):
  7
+    # Use local checkout app so we can mess with the view classes
  8
+    checkout_app = checkout_app
  9
+
  10
+
  11
+application = Application()
0  sites/demo/apps/checkout/__init__.py
No changes.
11  sites/demo/apps/checkout/app.py
... ...
@@ -0,0 +1,11 @@
  1
+from oscar.apps.checkout import app
  2
+
  3
+from apps.checkout import views
  4
+
  5
+
  6
+class CheckoutApplication(app.CheckoutApplication):
  7
+    # Replace the payment details view with our own
  8
+    payment_details_view = views.PaymentDetailsView
  9
+
  10
+
  11
+application = CheckoutApplication()
8  sites/demo/apps/checkout/views.py
... ...
@@ -0,0 +1,8 @@
  1
+from oscar.apps.checkout import views
  2
+
  3
+
  4
+class PaymentDetailsView(views.PaymentDetailsView):
  5
+
  6
+    def handle_payment(self, order_number, total_incl_tax, **kwargs):
  7
+        # Create a payment event
  8
+        self.add_payment_event('Authorize', total_incl_tax)
8  sites/demo/urls.py
... ...
@@ -1,18 +1,20 @@
1  
-from django.conf.urls.defaults import *
  1
+from django.conf.urls.defaults import patterns, url, include
2 2
 from django.conf import settings
3 3
 from django.contrib import admin
4 4
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
5 5
 from django.conf.urls.static import static
6 6
 from django.views.generic import TemplateView
7 7
 
8  
-from oscar.app import shop
  8
+from apps.app import application
  9
+
  10
+# These need to be imported into this namespace
9 11
 from oscar.views import handler500, handler404
10 12
 
11 13
 admin.autodiscover()
12 14
 
13 15
 urlpatterns = patterns('',
14 16
     (r'^admin/', include(admin.site.urls)),
15  
-    (r'', include(shop.urls)),
  17
+    (r'', include(application.urls)),
16 18
 )
17 19
 
18 20
 if settings.DEBUG:
6  tests/unit/partner/model_tests.py
@@ -81,13 +81,11 @@ def test_default_wrapper_for_out_of_stock(self):
81 81
 
82 82
     def test_dispatch_date_for_in_stock(self):
83 83
         product = create_product(price=D('10.00'), partner="Acme", num_in_stock=1)
84  
-        tomorrow = datetime.date.today() + datetime.timedelta(days=1)
85  
-        self.assertEquals(tomorrow, product.stockrecord.dispatch_date)
  84
+        self.assertIsNone(product.stockrecord.dispatch_date)
86 85
 
87 86
     def test_dispatch_date_for_out_of_stock(self):
88 87
         product = create_product(price=D('10.00'), partner="Acme", num_in_stock=0)
89  
-        date = datetime.date.today() + datetime.timedelta(days=7)
90  
-        self.assertEquals(date, product.stockrecord.dispatch_date)
  88
+        self.assertIsNone(product.stockrecord.dispatch_date)
91 89
 
92 90
 
93 91
 class CustomWrapperTests(TestCase):
10  tests/unit/partner/wrapper_tests.py
@@ -75,13 +75,3 @@ def test_backorder_purchase_is_permitted(self):
75 75
             m.return_value = None
76 76
             result, reason = self.wrapper.is_purchase_permitted(record)
77 77
             self.assertTrue(result)
78  
-
79  
-    def test_dispatch_date_for_in_stock(self):
80  
-        tomorrow = datetime.date.today() + datetime.timedelta(days=1)
81  
-        record = StockRecord(product=self.product, num_in_stock=4)
82  
-        self.assertEquals(tomorrow, self.wrapper.dispatch_date(record))
83  
-
84  
-    def test_dispatch_date_for_out_of_stock(self):
85  
-        date = datetime.date.today() + datetime.timedelta(days=7)
86  
-        record = StockRecord(product=self.product)
87  
-        self.assertEquals(date, self.wrapper.dispatch_date(record))

No commit comments for this range

Something went wrong with that request. Please try again.