ソースを参照

Rewrote submit order as a class.

Split all the steps into separate methods.
Also finished removing all references to "delivery"
master
David Winterbottom 15年前
コミット
f10d8fbe6a

+ 2
- 1
README.md ファイルの表示

@@ -140,5 +140,6 @@ is one of the installed app
140 140
 
141 141
 Look in the TODO file for things to hack on...
142 142
     
143
-
143
+### Conventions
144
+* URLs use hyphens not underscores
144 145
 

+ 1
- 0
examples/defaultshop/settings.py ファイルの表示

@@ -73,6 +73,7 @@ MIDDLEWARE_CLASSES = (
73 73
     'django.middleware.csrf.CsrfViewMiddleware',
74 74
     'django.contrib.auth.middleware.AuthenticationMiddleware',
75 75
     'django.contrib.messages.middleware.MessageMiddleware',
76
+    'django.middleware.transaction.TransactionMiddleware',
76 77
 )
77 78
 
78 79
 ROOT_URLCONF = 'urls'

+ 19
- 13
oscar/address/abstract_models.py ファイルの表示

@@ -12,7 +12,7 @@ class AbstractAddress(models.Model):
12 12
     Core address object
13 13
     
14 14
     This is normally subclassed and extended to provide models for 
15
-    delivery and billing addresses.
15
+    shipping and billing addresses.
16 16
     """
17 17
     # @todo: Need a way of making these choice lists configurable 
18 18
     # per project
@@ -71,40 +71,45 @@ class AbstractAddress(models.Model):
71 71
         return " ".join([part for part in [self.title, self.first_name, self.last_name] if part])
72 72
         
73 73
     def __unicode__(self):
74
-        parts = (self.get_salutation(), self.line1, self.line2, self.line3, self.line4,
74
+        parts = (self.salutation(), self.line1, self.line2, self.line3, self.line4,
75 75
                  self.postcode, self.country)
76 76
         return u", ".join([part for part in parts if part])
77 77
 
78 78
 
79
-class AbstractDeliveryAddress(AbstractAddress):
80
-    """
81
-    Delivery address 
79
+class AbstractShippingAddress(AbstractAddress):
80
+    u"""
81
+    Shipping address 
82 82
     """
83 83
     phone_number = models.CharField(max_length=32, blank=True, null=True)
84 84
     notes = models.TextField(blank=True, null=True) 
85 85
     
86 86
     class Meta:
87 87
         abstract = True
88
-        verbose_name_plural = "Delivery addresses"
88
+        verbose_name_plural = "shipping addresses"
89 89
         
90 90
         
91
-class AbstractUserAddress(AbstractDeliveryAddress):
92
-    """
91
+class AbstractUserAddress(AbstractShippingAddress):
92
+    u"""
93 93
     A user address which forms an "AddressBook".
94 94
     
95
-    We use a separate model to delivery and billing (even though there will be
96
-    some data duplication) because we don't want delivery/billing addresses changed
95
+    We use a separate model to shipping and billing (even though there will be
96
+    some data duplication) because we don't want shipping/billing addresses changed
97 97
     or deleted once an order has been placed.  By having a separate model, we allow
98 98
     users  
99 99
     """
100 100
     user = models.ForeignKey('auth.User', related_name='addresses')
101
-    is_primary = models.BooleanField(max_length=32, default=False)
101
+    
102
+    # We keep track of the number of times an address has been used
103
+    # as a shipping address so we can show the most popular ones 
104
+    # first at the checkout.
105
+    num_orders = models.PositiveIntegerField(default=0)
102 106
     hash = models.CharField(max_length=255, db_index=True)
103 107
     date_created = models.DateTimeField(auto_now_add=True)
104 108
     
105 109
     def generate_hash(self):
106
-        return zlib.crc32(self.summary())
107
-    
110
+        # We use an uppercase version of the summary
111
+        return zlib.crc32(self.summary().strip().upper())
112
+
108 113
     def save(self, *args, **kwargs):
109 114
         # Save a hash of the address fields so we can check whether two 
110 115
         # addresses are the same to avoid saving duplicates
@@ -114,6 +119,7 @@ class AbstractUserAddress(AbstractDeliveryAddress):
114 119
     class Meta:
115 120
         abstract = True
116 121
         verbose_name_plural = "User addresses"
122
+        ordering = ['-num_orders']
117 123
 
118 124
 
119 125
 class AbstractBillingAddress(AbstractAddress):

+ 6
- 6
oscar/basket/models.py ファイルの表示

@@ -23,9 +23,9 @@ class LineAttribute(AbstractLineAttribute):
23 23
     pass
24 24
 
25 25
 # Example signal handler to enforce stock rules
26
-#@receiver(pre_save, sender=Line)
27
-#def handle_line_save(sender, **kwargs):
28
-#    if 'instance' in kwargs:
29
-#        quantity = int(kwargs['instance'].quantity)
30
-#        if quantity > 4:
31
-#            raise InvalidBasketLineError("You are only allowed to purchase a maximum of 4 of these")
26
+@receiver(pre_save, sender=Line)
27
+def handle_line_save(sender, **kwargs):
28
+    if 'instance' in kwargs:
29
+        quantity = int(kwargs['instance'].quantity)
30
+        if quantity > 4:
31
+            raise InvalidBasketLineError("You are only allowed to purchase a maximum of 4 of these")

+ 5
- 5
oscar/basket/views.py ファイルの表示

@@ -84,9 +84,9 @@ class BasketView(ModelView):
84 84
         try:
85 85
             super(BasketView, self).handle_POST()
86 86
         except basket_models.Basket.DoesNotExist:
87
-            messages.error(request, "Unable to find your basket")
87
+            messages.error(self.request, "Unable to find your basket")
88 88
         except basket_models.InvalidBasketLineError, e:
89
-            messages.error(request, str(e))
89
+            messages.error(self.request, str(e))
90 90
             
91 91
     def do_flush(self, basket):
92 92
         basket.flush()
@@ -123,11 +123,11 @@ class LineView(ModelView):
123 123
         try:
124 124
             super(LineView, self).handle_POST()
125 125
         except basket_models.Basket.DoesNotExist:
126
-                messages.error(request, "You don't have a basket to adjust the lines of")
126
+                messages.error(self.request, "You don't have a basket to adjust the lines of")
127 127
         except basket_models.Line.DoesNotExist:
128
-            messages.error(request, "Unable to find a line with reference %s in your basket" % self.kwargs['line_reference'])
128
+            messages.error(self.request, "Unable to find a line with reference %s in your basket" % self.kwargs['line_reference'])
129 129
         except basket_models.InvalidBasketLineError, e:
130
-            messages.error(request, str(e))
130
+            messages.error(self.request, str(e))
131 131
             
132 132
     def _get_quantity(self):
133 133
         if 'quantity' in self.request.POST:

+ 1
- 1
oscar/checkout/calculators.py ファイルの表示

@@ -10,7 +10,7 @@ class OrderTotalCalculator(object):
10 10
     def __init__(self, request):
11 11
         # We store a reference to the request as the total may 
12 12
         # depend on the user or the other checkout data in the session.
13
-        # Further, it is very likely that it will as delivery method
13
+        # Further, it is very likely that it will as shipping method
14 14
         # always changes the order total.
15 15
         self.request = request
16 16
     

+ 3
- 3
oscar/checkout/forms.py ファイルの表示

@@ -2,12 +2,12 @@ from django.forms import ModelForm
2 2
 
3 3
 from oscar.services import import_module
4 4
 
5
-order_models = import_module('order.models', ['DeliveryAddress'])
5
+order_models = import_module('order.models', ['ShippingAddress'])
6 6
 
7 7
 
8
-class DeliveryAddressForm(ModelForm):
8
+class ShippingAddressForm(ModelForm):
9 9
     
10 10
     class Meta:
11
-        model = order_models.DeliveryAddress
11
+        model = order_models.ShippingAddress
12 12
         exclude = ('user',)
13 13
 

+ 1
- 1
oscar/checkout/templates/checkout/gateway.html ファイルの表示

@@ -16,7 +16,7 @@
16 16
            order is viewable in your order history.</p>
17 17
     </dl>
18 18
     <dt>
19
-        <a href="{% url oscar-checkout-delivery-address %}">Checkout anonymously</a>
19
+        <a href="{% url oscar-checkout-shipping-address %}">Checkout anonymously</a>
20 20
     </dt>
21 21
     <dl>
22 22
         <p>You can checkout without signing in or registering.  You will be given the opportunity to 

+ 12
- 12
oscar/checkout/templates/checkout/preview.html ファイルの表示

@@ -7,33 +7,33 @@
7 7
 
8 8
 {% block content %}
9 9
 
10
-<h3>Delivery address</h3>
10
+<h3>Shipping address</h3>
11 11
 <hr />
12 12
 <table>
13 13
     <tr>
14 14
         <th>Address</th>
15 15
         <td>
16
-            {% for field in delivery_addr.active_address_fields %}
16
+            {% for field in shipping_addr.active_address_fields %}
17 17
             {{ field }}<br/>
18 18
             {% endfor %}
19 19
         </td>
20 20
     </tr>
21
-    {% if delivery_addr.phone_number %}
21
+    {% if shipping_addr.phone_number %}
22 22
     <tr>
23 23
         <th>Concact number</th>
24
-        <td>delivery_addr.phone_number</td>
24
+        <td>shipping_addr.phone_number</td>
25 25
     </tr>
26 26
     {% endif %}
27
-    {% if delivery_addr.notes %}
27
+    {% if shipping_addr.notes %}
28 28
     <tr>
29
-        <th>Delivery notes</th>
30
-        <td>delivery_addr.notes</td>
29
+        <th>Shipping notes</th>
30
+        <td>shipping_addr.notes</td>
31 31
     </tr>
32 32
     {% endif %} 
33 33
 </table>
34
-<a href="{% url oscar-checkout-delivery-address %}">Change delivery address</a>
34
+<a href="{% url oscar-checkout-shipping-address %}">Change shipping address</a>
35 35
 
36
-<h3>Delivery method</h3>
36
+<h3>Shipping method</h3>
37 37
 <hr />
38 38
 
39 39
 <h3>Payment</h3>
@@ -69,9 +69,9 @@
69 69
         <td>{{ basket.total_incl_tax|floatformat:2 }}</td>
70 70
     </tr>
71 71
     <tr>
72
-        <td colspan="5">Delivery charge</td>
73
-        <td>{{ delivery_total_excl_tax|floatformat:2 }}</td>
74
-        <td>{{ delivery_total_incl_tax|floatformat:2 }}</td>
72
+        <td colspan="5">Shipping charge</td>
73
+        <td>{{ shipping_total_excl_tax|floatformat:2 }}</td>
74
+        <td>{{ shipping_total_incl_tax|floatformat:2 }}</td>
75 75
     </tr>
76 76
     <tr>
77 77
         <td colspan="6">Order total</td>

oscar/checkout/templates/checkout/delivery_address.html → oscar/checkout/templates/checkout/shipping_address.html ファイルの表示

@@ -6,22 +6,22 @@
6 6
 
7 7
 
8 8
 {% block content %}
9
-<h3>Delivery address</h3>
9
+<h3>Shipping address</h3>
10 10
 <hr />
11 11
 
12 12
 {% if addresses %}
13
-<h4>Choose a delivery address</h4>
13
+<h4>Choose a shipping address</h4>
14 14
 <ul>
15 15
     {% for address in addresses %}
16 16
     <li>
17 17
     {{ address.summary }}
18
-        <form action="{% url oscar-checkout-delivery-address %}" method="post">
18
+        <form action="{% url oscar-checkout-shipping-address %}" method="post">
19 19
             {% csrf_token %}
20
-            <input type="hidden" name="action" value="deliver_to" />
20
+            <input type="hidden" name="action" value="ship_to" />
21 21
             <input type="hidden" name="address_id" value="{{ address.id }}" />
22
-            <input type="submit" value="Delivery to this address" />
22
+            <input type="submit" value="Ship to this address" />
23 23
         </form>
24
-        <form action="{% url oscar-checkout-delivery-address %}" method="post">
24
+        <form action="{% url oscar-checkout-shipping-address %}" method="post">
25 25
             {% csrf_token %}
26 26
             <input type="hidden" name="action" value="delete" />
27 27
             <input type="hidden" name="address_id" value="{{ address.id }}" />
@@ -33,15 +33,14 @@
33 33
 </form>
34 34
 {% endif %}
35 35
     
36
-<h4>Enter a delivery address</h4>
37
-<form action="{% url oscar-checkout-delivery-address %}" method="post">
36
+<h4>Enter a shipping address</h4>
37
+<form action="{% url oscar-checkout-shipping-address %}" method="post">
38 38
     {% csrf_token %}
39 39
     {{ form.as_p }}
40
-    <p><label><input type="checkbox" id="save_as_default" name="save_as_default"/>Save as default delivery address</label></p>
41
-    <p><input type="submit" value="Save delivery address" /></p>
40
+    <p><input type="submit" value="Save shipping address" /></p>
42 41
 </form>
43 42
 
44
-<h3>Delivery method</h3>
43
+<h3>Shipping method</h3>
45 44
 <hr />
46 45
 
47 46
 <h3>Payment</h3>
@@ -77,9 +76,9 @@
77 76
         <td>{{ basket.total_incl_tax|floatformat:2 }}</td>
78 77
     </tr>
79 78
     <tr>
80
-        <td colspan="5">Delivery charge</td>
81
-        <td>{{ delivery_total_excl_tax|floatformat:2 }}</td>
82
-        <td>{{ delivery_total_incl_tax|floatformat:2 }}</td>
79
+        <td colspan="5">shipping charge</td>
80
+        <td>{{ shipping_total_excl_tax|floatformat:2 }}</td>
81
+        <td>{{ shipping_total_incl_tax|floatformat:2 }}</td>
83 82
     </tr>
84 83
     <tr>
85 84
         <td colspan="6">Order total</td>

+ 9
- 4
oscar/checkout/urls.py ファイルの表示

@@ -1,12 +1,17 @@
1 1
 from django.conf.urls.defaults import *
2 2
 
3
+from oscar.checkout.views import SubmitView
4
+
5
+def submit_view(request, *args, **kwargs):
6
+    return SubmitView()(request, *args, **kwargs)
7
+
3 8
 urlpatterns = patterns('oscar.checkout.views',
4 9
     url(r'^$', 'index', name='oscar-checkout-index'),
5
-    url(r'delivery_address/$', 'delivery_address', name='oscar-checkout-delivery-address'),
6
-    url(r'delivery_method/$', 'delivery_method', name='oscar-checkout-delivery-method'),
10
+    url(r'shipping-address/$', 'shipping_address', name='oscar-checkout-shipping-address'),
11
+    url(r'shipping-method/$', 'shipping_method', name='oscar-checkout-shipping-method'),
7 12
     url(r'payment/$', 'payment', name='oscar-checkout-payment'),
8 13
     url(r'preview/$', 'preview', name='oscar-checkout-preview'),
9
-    url(r'submit/$', 'submit', name='oscar-checkout-submit'),
10
-    url(r'thank_you/$', 'thank_you', name='oscar-checkout-thank-you'),
14
+    url(r'submit/$', submit_view, name='oscar-checkout-submit'),
15
+    url(r'thank-you/$', 'thank_you', name='oscar-checkout-thank-you'),
11 16
 )
12 17
 

+ 2
- 2
oscar/checkout/utils.py ファイルの表示

@@ -8,8 +8,8 @@ class ProgressChecker(object):
8 8
     """
9 9
     
10 10
     # List of URL names that have to be completed (in this order)
11
-    urls_for_steps = ['oscar-checkout-delivery-address',
12
-                      'oscar-checkout-delivery-method',
11
+    urls_for_steps = ['oscar-checkout-shipping-address',
12
+                      'oscar-checkout-shipping-method',
13 13
                       'oscar-checkout-payment',
14 14
                       'oscar-checkout-preview',
15 15
                       'oscar-checkout-submit',]

+ 139
- 85
oscar/checkout/views.py ファイルの表示

@@ -12,10 +12,10 @@ from oscar.services import import_module
12 12
 
13 13
 basket_factory = import_module('basket.factory', ['get_or_create_open_basket', 'get_open_basket', 
14 14
                                                   'get_or_create_saved_basket', 'get_saved_basket'])
15
-checkout_forms = import_module('checkout.forms', ['DeliveryAddressForm'])
15
+checkout_forms = import_module('checkout.forms', ['ShippingAddressForm'])
16 16
 checkout_calculators = import_module('checkout.calculators', ['OrderTotalCalculator'])
17 17
 checkout_utils = import_module('checkout.utils', ['ProgressChecker'])
18
-order_models = import_module('order.models', ['DeliveryAddress', 'Order'])
18
+order_models = import_module('order.models', ['ShippingAddress', 'Order', 'Batch', 'BatchLine', 'BatchLinePrice'])
19 19
 address_models = import_module('address.models', ['UserAddress'])
20 20
 
21 21
 class CheckoutSessionData(object):
@@ -53,26 +53,23 @@ class CheckoutSessionData(object):
53 53
     def flush(self):
54 54
         self.request.session[self.session_key] = {}
55 55
         
56
-    # Delivery methods    
56
+    # Shipping methods    
57 57
         
58
-    def deliver_to_user_address(self, address):
59
-        self._set('delivery', 'user_address_id', address.id)
60
-        self._unset('delivery', 'new_address_fields')
61
-        self._unset('delivery', 'is_default')
58
+    def ship_to_user_address(self, address):
59
+        self._set('shipping', 'user_address_id', address.id)
60
+        self._unset('shipping', 'new_address_fields')
61
+        self._unset('shipping', 'is_default')
62 62
         
63
-    def deliver_to_new_address(self, address_fields, is_default=False):
64
-        self._set('delivery', 'new_address_fields', address_fields)
65
-        self._set('delivery', 'is_default', is_default)
66
-        self._unset('delivery', 'user_address_id')
63
+    def ship_to_new_address(self, address_fields, is_default=False):
64
+        self._set('shipping', 'new_address_fields', address_fields)
65
+        self._set('shipping', 'is_default', is_default)
66
+        self._unset('shipping', 'user_address_id')
67 67
         
68 68
     def new_address_fields(self):
69
-        return self._get('delivery', 'new_address_fields')
69
+        return self._get('shipping', 'new_address_fields')
70 70
         
71 71
     def user_address_id(self):
72
-        return self._get('delivery', 'user_address_id')
73
-    
74
-    def should_new_address_be_default(self):
75
-        return self._get('delivery', 'is_default') == True
72
+        return self._get('shipping', 'user_address_id')
76 73
         
77 74
 
78 75
 def prev_steps_must_be_complete(view_fn):
@@ -117,62 +114,62 @@ def index(request):
117 114
     Need to check here if the user is ready to start the checkout
118 115
     """
119 116
     if request.user.is_authenticated():
120
-        return HttpResponseRedirect(reverse('oscar-checkout-delivery-address'))
117
+        return HttpResponseRedirect(reverse('oscar-checkout-shipping-address'))
121 118
     return render(request, 'checkout/gateway.html', locals())
122 119
 
123 120
 @basket_required
124
-def delivery_address(request):
121
+def shipping_address(request):
125 122
     """
126
-    Handle the selection of a delivery address.
123
+    Handle the selection of a shipping address.
127 124
     """
128 125
     co_data = CheckoutSessionData(request)
129 126
     if request.method == 'POST':
130 127
         if request.user.is_authenticated and 'address_id' in request.POST:
131 128
             address = address_models.UserAddress.objects.get(pk=request.POST['address_id'])
132
-            if 'action' in request.POST and request.POST['action'] == 'deliver_to':
133
-                # User has selected a previous address to deliver to
134
-                co_data.deliver_to_user_address(address)
129
+            if 'action' in request.POST and request.POST['action'] == 'ship_to':
130
+                # User has selected a previous address to ship to
131
+                co_data.ship_to_user_address(address)
135 132
                 mark_step_as_complete(request)
136
-                return HttpResponseRedirect(reverse('oscar-checkout-delivery-method'))
133
+                return HttpResponseRedirect(reverse('oscar-checkout-shipping-method'))
137 134
             elif 'action' in request.POST and request.POST['action'] == 'delete':
138 135
                 address.delete()
139 136
                 messages.info(request, "Address deleted from your address book")
140
-                return HttpResponseRedirect(reverse('oscar-checkout-delivery-method'))
137
+                return HttpResponseRedirect(reverse('oscar-checkout-shipping-method'))
141 138
         else:
142
-            form = checkout_forms.DeliveryAddressForm(request.POST)
139
+            form = checkout_forms.ShippingAddressForm(request.POST)
143 140
             if form.is_valid():
144 141
                 # Address data is valid - store in session and redirect to next step.
145 142
                 is_default = False
146 143
                 if 'save_as_default' in request.POST and request.POST['save_as_default'] == 'on':
147 144
                     is_default = True
148
-                co_data.deliver_to_new_address(form.clean(), is_default)
145
+                co_data.ship_to_new_address(form.clean(), is_default)
149 146
                 mark_step_as_complete(request)
150
-                return HttpResponseRedirect(reverse('oscar-checkout-delivery-method'))
147
+                return HttpResponseRedirect(reverse('oscar-checkout-shipping-method'))
151 148
     else:
152 149
         addr_fields = co_data.new_address_fields()
153 150
         if addr_fields:
154
-            form = checkout_forms.DeliveryAddressForm(addr_fields)
151
+            form = checkout_forms.ShippingAddressForm(addr_fields)
155 152
         else:
156
-            form = checkout_forms.DeliveryAddressForm()
153
+            form = checkout_forms.ShippingAddressForm()
157 154
     
158 155
     # Add in extra template bindings
159 156
     basket = basket_factory.get_open_basket(request)
160 157
     calc = checkout_calculators.OrderTotalCalculator(request)
161 158
     order_total = calc.order_total_incl_tax(basket)
162
-    delivery_total_excl_tax = 0
163
-    delivery_total_incl_tax = 0
159
+    shipping_total_excl_tax = 0
160
+    shipping_total_incl_tax = 0
164 161
     
165 162
     # Look up address book data
166 163
     if request.user.is_authenticated():
167 164
         addresses = address_models.UserAddress.objects.filter(user=request.user)
168 165
     
169
-    return render(request, 'checkout/delivery_address.html', locals())
166
+    return render(request, 'checkout/shipping_address.html', locals())
170 167
     
171 168
     
172 169
 @prev_steps_must_be_complete    
173
-def delivery_method(request):
170
+def shipping_method(request):
174 171
     """
175
-    Delivery methods are domain-specific and so need implementing in a 
172
+    Shipping methods are domain-specific and so need implementing in a 
176 173
     subclass of this class.
177 174
     """
178 175
     mark_step_as_complete(request)
@@ -198,10 +195,10 @@ def preview(request):
198 195
     # Load address data into a blank address model
199 196
     addr_data = co_data.new_address_fields()
200 197
     if addr_data:
201
-        delivery_addr = order_models.DeliveryAddress(**addr_data)
198
+        shipping_addr = order_models.ShippingAddress(**addr_data)
202 199
     addr_id = co_data.user_address_id()
203 200
     if addr_id:
204
-        delivery_addr = address_models.UserAddress.objects.get(pk=addr_id)
201
+        shipping_addr = address_models.UserAddress.objects.get(pk=addr_id)
205 202
     
206 203
     # Calculate order total
207 204
     calc = checkout_calculators.OrderTotalCalculator(request)
@@ -210,63 +207,120 @@ def preview(request):
210 207
     mark_step_as_complete(request)
211 208
     return render(request, 'checkout/preview.html', locals())
212 209
 
213
-@prev_steps_must_be_complete
214
-def submit(request):
215
-    """
216
-    Do several things then redirect to the thank-you page
217
-    """
218
-    co_data = CheckoutSessionData(request)
210
+
211
+class SubmitView(object):
219 212
     
220
-    # Save the address data
221
-    addr_data = co_data.new_address_fields()
222
-    if addr_data:
223
-        # A new delivery address has been entered
224
-        delivery_addr = order_models.DeliveryAddress(**addr_data)
225
-        delivery_addr.save()
213
+    def __call__(self, request):
214
+        
215
+        # Set up the instance variables that are needed to place an order
216
+        self.request = request
217
+        self.co_data = CheckoutSessionData(request)
218
+        self.basket = basket_factory.get_open_basket(request)
219
+        
220
+        # All the heavy lifting happens here
221
+        self._place_order()
222
+        
223
+        # Now, reset the states of the basket and checkout 
224
+        self.basket.set_as_submitted()
225
+        self.co_data.flush()
226
+        checkout_utils.ProgressChecker().all_steps_complete(request)
227
+        
228
+        # @todo Save order id in session so thank-you page can load it
229
+        
230
+        return HttpResponseRedirect(reverse('oscar-checkout-thank-you'))
231
+        
232
+    def _place_order(self):
233
+        order = self._create_order_model()
234
+        for line in self.basket.lines.all():
235
+            batch = self._get_or_create_batch_for_line(order, line)
236
+            self._create_line_model(order, batch, line)
237
+        
238
+    def _create_order_model(self):
239
+        calc = checkout_calculators.OrderTotalCalculator(self.request)
240
+        order_data = {'basket': self.basket,
241
+                      'total_incl_tax': calc.order_total_incl_tax(self.basket),
242
+                      'total_excl_tax': calc.order_total_excl_tax(self.basket),
243
+                      'shipping_incl_tax': 0,
244
+                      'shipping_excl_tax': 0,}
245
+        if self.request.user.is_authenticated():
246
+            order_data['user_id'] = self.request.user.id
247
+        order = order_models.Order(**order_data)
248
+        order.save()
249
+        return order
250
+    
251
+    def _create_line_model(self, order, batch, line):
252
+        batch_line = order_models.BatchLine.objects.create(batch=batch, 
253
+                                                           product=line.product, 
254
+                                                           quantity=line.quantity, 
255
+                                                           line_price_excl_tax=line.line_price_excl_tax, 
256
+                                                           line_price_incl_tax=line.line_price_incl_tax)
257
+        order_models.BatchLinePrice.objects.create(line=batch_line, quantity=line.quantity, 
258
+                                                   price_incl_tax=line.unit_price_incl_tax,
259
+                                                   price_excl_tax=line.unit_price_excl_tax)
260
+    
261
+    def _get_or_create_batch_for_line(self, order, line):
262
+         partner = self._get_partner_for_product(line.product)
263
+         shipping_addr = self._get_shipping_address_for_line(line)
264
+         batch,_ = order_models.Batch.objects.get_or_create(order=order, partner=partner, shipping_address=shipping_addr)
265
+         return batch
266
+                
267
+    def _get_partner_for_product(self, product):
268
+        if product.has_stockrecord:
269
+            return product.stockrecord.partner
270
+        raise AttributeError("No partner found for product '%s'" % product)
271
+
272
+    def _get_shipping_address_for_line(self, line):
273
+        try:
274
+            addr = self.shipping_addr
275
+        except AttributeError:
276
+            # No cached version - create a shipping address
277
+            addr_data = self.co_data.new_address_fields()
278
+            addr_id = self.co_data.user_address_id()
279
+            if addr_data:
280
+                addr = self._create_shipping_address_from_form_fields(addr_data)
281
+                self._create_user_address(addr_data)
282
+            elif addr_id:
283
+                addr = self._create_shipping_address_from_user_address(addr_id)
284
+            else:
285
+                raise AttributeError("No shipping address data found")
286
+            
287
+            # Cache it as our default behaviour is to have only one
288
+            # shipping address per order.
289
+            self.shipping_addr = addr
290
+        return addr
291
+            
226 292
         
227
-        # Save a new user address
228
-        if request.user.is_authenticated():
229
-            addr_data['user_id'] = request.user.id
293
+    def _create_shipping_address_from_form_fields(self, addr_data):
294
+        shipping_addr = order_models.ShippingAddress(**addr_data)
295
+        shipping_addr.save() 
296
+        return shipping_addr
297
+    
298
+    def _create_user_address(self, addr_data):
299
+        """
300
+        For signed-in users, we create a user address model which will go 
301
+        into their address book.
302
+        """
303
+        if self.request.user.is_authenticated():
304
+            addr_data['user_id'] = self.request.user.id
230 305
             user_addr = address_models.UserAddress(**addr_data)
231
-            # Check that this address isn't already in the db
306
+            # Check that this address isn't already in the db as we don't want
307
+            # to fill up the customer address book with duplicate addresses
232 308
             try:
233 309
                 duplicate_addr = address_models.UserAddress.objects.get(hash=user_addr.generate_hash())
234 310
             except ObjectDoesNotExist:
235
-                if co_data.should_new_address_be_default():
236
-                    user_addr.is_primary = True 
237 311
                 user_addr.save()
238
-            
239
-    addr_id = co_data.user_address_id()
240
-    if addr_id:
241
-        # A previously used address has been selected.  We need to convert it
242
-        # to a delivery address and save it
243
-        address = address_models.UserAddress.objects.get(pk=addr_id)
244
-        delivery_addr = order_models.DeliveryAddress()
245
-        address.populate_alternative_model(delivery_addr)
246
-        delivery_addr.save()
247 312
     
248
-    # Save the order model
249
-    calc = checkout_calculators.OrderTotalCalculator(request)
250
-    basket = basket_factory.get_open_basket(request)
251
-    order_data = {'basket': basket,
252
-                  'total_incl_tax': calc.order_total_incl_tax(basket),
253
-                  'total_excl_tax': calc.order_total_excl_tax(basket),
254
-                  'shipping_incl_tax': 0,
255
-                  'shipping_excl_tax': 0,}
256
-    if request.user.is_authenticated():
257
-        order_data['user_id'] = request.user.id
258
-    order = order_models.Order(**order_data).save()
259
-    
260
-    # @todo set basket as submitted
261
-    basket.set_as_submitted()
262
-    
263
-    # @todo unset all session data
264
-    co_data.flush()
265
-    
266
-    # @todo Save order id in session so thank-you page can load it
267
-    checkout_utils.ProgressChecker().all_steps_complete(request)
268
-    return HttpResponseRedirect(reverse('oscar-checkout-thank-you'))
313
+    def _create_shipping_address_from_user_address(self, addr_id):
314
+        address = address_models.UserAddress.objects.get(pk=addr_id)
315
+        # Increment the number of orders to help determine popularity of orders 
316
+        address.num_orders += 1
317
+        address.save()
318
+        
319
+        shipping_addr = order_models.ShippingAddress()
320
+        address.populate_alternative_model(shipping_addr)
321
+        shipping_addr.save()
322
+        return shipping_addr
269 323
 
270 324
 
271 325
 def thank_you(request):
272
-    return render(request, 'checkout/thank_you.html', locals())
326
+    return render(request, 'checkout/thank_you.html', locals())

+ 7
- 7
oscar/order/abstract_models.py ファイルの表示

@@ -32,6 +32,7 @@ class AbstractOrder(models.Model):
32 32
     def __unicode__(self):
33 33
         return "#%d (customer: %s, amount: %.2f)" % (self.number, self.customer.username, self.total_incl_tax)
34 34
 
35
+
35 36
 class AbstractBatch(models.Model):
36 37
     """
37 38
     A batch of items from a single fulfillment partner
@@ -40,11 +41,9 @@ class AbstractBatch(models.Model):
40 41
     """
41 42
     order = models.ForeignKey('order.Order')
42 43
     partner = models.ForeignKey('stock.Partner')
43
-    delivery_method = models.CharField(_("Delivery method"), max_length=128)
44
-    # Not all batches are actually delivered (such as downloads)
45
-    delivery_address = models.ForeignKey('order.DeliveryAddress', null=True, blank=True)
46
-    # Whether the batch should be dispatched in one go, or as they become available
47
-    dispatch_option = models.CharField(_("Dispatch option"), max_length=128, null=True, blank=True)
44
+    # Not all batches are actually shipped (such as downloads)
45
+    shipping_address = models.ForeignKey('order.ShippingAddress', null=True, blank=True)
46
+    shipping_method = models.CharField(_("Shipping method"), max_length=128, null=True, blank=True)
48 47
     
49 48
     def get_num_items(self):
50 49
         return len(self.lines.all())
@@ -56,6 +55,7 @@ class AbstractBatch(models.Model):
56 55
     def __unicode__(self):
57 56
         return "%s batch for order #%d" % (self.partner.name, self.order.number)
58 57
         
58
+        
59 59
 class AbstractBatchLine(models.Model):
60 60
     """
61 61
     A line within a batch.
@@ -96,8 +96,8 @@ class AbstractBatchLinePrice(models.Model):
96 96
     quantity = models.PositiveIntegerField(default=1)
97 97
     price_incl_tax = models.DecimalField(decimal_places=2, max_digits=12)
98 98
     price_excl_tax = models.DecimalField(decimal_places=2, max_digits=12)
99
-    delivery_incl_tax = models.DecimalField(decimal_places=2, max_digits=12, default=0)
100
-    delivery_excl_tax = models.DecimalField(decimal_places=2, max_digits=12, default=0)
99
+    shipping_incl_tax = models.DecimalField(decimal_places=2, max_digits=12, default=0)
100
+    shipping_excl_tax = models.DecimalField(decimal_places=2, max_digits=12, default=0)
101 101
     
102 102
     class Meta:
103 103
         abstract = True

+ 2
- 2
oscar/order/admin.py ファイルの表示

@@ -2,7 +2,7 @@ from django.contrib import admin
2 2
 from oscar.order.models import *
3 3
 
4 4
 class BatchAdmin(admin.ModelAdmin):
5
-    list_display = ('order', 'partner', 'get_num_items', 'delivery_method')
5
+    list_display = ('order', 'partner', 'get_num_items', 'shipping_method')
6 6
 
7 7
 class BatchLineAdmin(admin.ModelAdmin):
8 8
     list_display = ('batch', 'product', 'quantity')
@@ -10,7 +10,7 @@ class BatchLineAdmin(admin.ModelAdmin):
10 10
 admin.site.register(Order)
11 11
 admin.site.register(BillingAddress)
12 12
 admin.site.register(Batch, BatchAdmin)
13
-admin.site.register(DeliveryAddress)
13
+admin.site.register(ShippingAddress)
14 14
 admin.site.register(BatchLine, BatchLineAdmin)
15 15
 admin.site.register(BatchLinePrice)
16 16
 admin.site.register(BatchLineEvent)

+ 2
- 2
oscar/order/models.py ファイルの表示

@@ -4,7 +4,7 @@ Vanilla implementation of order models
4 4
 from django.db import models
5 5
 
6 6
 from oscar.order.abstract_models import *
7
-from oscar.address.abstract_models import AbstractAddress, AbstractDeliveryAddress, AbstractBillingAddress
7
+from oscar.address.abstract_models import AbstractAddress, AbstractShippingAddress, AbstractBillingAddress
8 8
 
9 9
 class Order(AbstractOrder):
10 10
     pass
@@ -12,7 +12,7 @@ class Order(AbstractOrder):
12 12
 class Batch(AbstractBatch):
13 13
     pass
14 14
 
15
-class DeliveryAddress(AbstractDeliveryAddress):
15
+class ShippingAddress(AbstractShippingAddress):
16 16
     pass
17 17
 
18 18
 class BillingAddress(AbstractBillingAddress):

+ 2
- 2
oscar/order/tests.py ファイルの表示

@@ -4,13 +4,13 @@ from django.test import TestCase
4 4
 from oscar.basket.models import Basket
5 5
 from oscar.order.models import *
6 6
 
7
-class DeliveryAddressTest(unittest.TestCase):
7
+class ShippingAddressTest(unittest.TestCase):
8 8
     
9 9
     def setUp(self):
10 10
         pass
11 11
     
12 12
     def test_titleless_salutation_is_stripped(self):
13
-        a = DeliveryAddress.objects.create(last_name='Barrington', line1="75 Smith Road", postcode="N4 8TY")
13
+        a = ShippingAddress.objects.create(last_name='Barrington', line1="75 Smith Road", postcode="N4 8TY")
14 14
         self.assertEquals("Barrington", a.get_salutation())
15 15
     
16 16
         

+ 0
- 1
oscar/services.py ファイルの表示

@@ -37,7 +37,6 @@ def import_module(mod_name, classes=[]):
37 37
                 # Passing classes to __import__ here does not actually filter out the 
38 38
                 # classes, we need to iterate through and assign them individually.
39 39
                 mod = new_module(real_app)
40
-                print real_app
41 40
                 imported_mod = __import__(real_app, fromlist=classes)
42 41
                 for classname in classes:
43 42
                     mod.__setattr__(classname, getattr(imported_mod, classname))

読み込み中…
キャンセル
保存