|
|
@@ -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())
|