Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

views.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. from decimal import Decimal
  2. import logging
  3. from django.conf import settings
  4. from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseBadRequest
  5. from django.template import RequestContext
  6. from django.shortcuts import render, get_object_or_404
  7. from django.core.urlresolvers import reverse
  8. from django.forms import ModelForm
  9. from django.contrib import messages
  10. from django.core.urlresolvers import resolve
  11. from django.core.exceptions import ObjectDoesNotExist
  12. from django.utils.translation import ugettext as _
  13. from oscar.view.generic import ModelView
  14. from oscar.core.loading import import_module
  15. basket_factory = import_module('basket.factory', ['BasketFactory'])
  16. checkout_forms = import_module('checkout.forms', ['ShippingAddressForm'])
  17. checkout_calculators = import_module('checkout.calculators', ['OrderTotalCalculator'])
  18. checkout_utils = import_module('checkout.utils', ['ProgressChecker', 'CheckoutSessionData'])
  19. checkout_signals = import_module('checkout.signals', ['pre_payment', 'post_payment'])
  20. checkout_views = import_module('checkout.core_views', ['CheckoutView', 'mark_step_as_complete'])
  21. order_models = import_module('order.models', ['Order', 'ShippingAddress'])
  22. order_utils = import_module('order.utils', ['OrderNumberGenerator', 'OrderCreator'])
  23. address_models = import_module('address.models', ['UserAddress'])
  24. shipping_repository = import_module('shipping.repository', ['Repository'])
  25. logger = logging.getLogger('oscar.checkout')
  26. class IndexView(object):
  27. template_file = 'checkout/gateway.html'
  28. def __call__(self, request):
  29. if request.user.is_authenticated():
  30. return HttpResponseRedirect(reverse('oscar-checkout-shipping-address'))
  31. return render(request, self.template_file, locals())
  32. class ShippingAddressView(checkout_views.CheckoutView):
  33. template_file = 'checkout/shipping_address.html'
  34. def handle_POST(self):
  35. if self.request.user.is_authenticated and 'address_id' in self.request.POST:
  36. address = address_models.UserAddress._default_manager.get(pk=self.request.POST['address_id'])
  37. if 'action' in self.request.POST and self.request.POST['action'] == 'ship_to':
  38. # User has selected a previous address to ship to
  39. self.co_data.ship_to_user_address(address)
  40. return self.get_success_response()
  41. elif 'action' in self.request.POST and self.request.POST['action'] == 'delete':
  42. address.delete()
  43. messages.info(self.request, "Address deleted from your address book")
  44. return HttpResponseRedirect(reverse('oscar-checkout-shipping-method'))
  45. else:
  46. return HttpResponseBadRequest()
  47. else:
  48. form = checkout_forms.ShippingAddressForm(self.request.POST)
  49. if form.is_valid():
  50. # Address data is valid - store in session and redirect to next step.
  51. self.co_data.ship_to_new_address(form.clean())
  52. return self.get_success_response()
  53. return self.handle_GET(form)
  54. def handle_GET(self, form=None):
  55. if not form:
  56. addr_fields = self.co_data.new_address_fields()
  57. if addr_fields:
  58. form = checkout_forms.ShippingAddressForm(addr_fields)
  59. else:
  60. form = checkout_forms.ShippingAddressForm()
  61. self.context['form'] = form
  62. # Look up address book data
  63. if self.request.user.is_authenticated():
  64. self.context['addresses'] = address_models.UserAddress._default_manager.filter(user=self.request.user)
  65. return render(self.request, self.template_file, self.context)
  66. class ShippingMethodView(checkout_views.CheckoutView):
  67. u"""
  68. Shipping methods are domain-specific and so need implementing in a
  69. subclass of this class.
  70. """
  71. template_file = 'checkout/shipping_methods.html';
  72. def handle_GET(self):
  73. methods = self.get_available_shipping_methods()
  74. if len(methods) == 1:
  75. # Only one method - set this and redirect onto the next step
  76. self.co_data.use_shipping_method(methods[0].code)
  77. return self.get_success_response()
  78. self.context['methods'] = methods
  79. return render(self.request, self.template_file, self.context)
  80. def get_shipping_methods_for_basket(self, basket):
  81. u"""Return available shipping methods for a basket"""
  82. return shipping_models.Method.objects.all()
  83. def get_available_shipping_methods(self):
  84. u"""
  85. Returns all applicable shipping method objects
  86. for a given basket.
  87. """
  88. repo = shipping_repository.Repository()
  89. return repo.get_shipping_methods(self.request.user, self.basket, self.get_shipping_address())
  90. def handle_POST(self):
  91. method_code = self.request.POST['method_code']
  92. self.co_data.use_shipping_method(method_code)
  93. return self.get_success_response()
  94. class PaymentMethodView(checkout_views.CheckoutView):
  95. u"""
  96. View for a user to choose which payment method(s) they want to use.
  97. This would include setting allocations if payment is to be split
  98. between multiple sources.
  99. """
  100. pass
  101. class OrderPreviewView(checkout_views.CheckoutView):
  102. u"""View a preview of the order before submitting."""
  103. template_file = 'checkout/preview.html'
  104. def handle_GET(self):
  105. checkout_views.mark_step_as_complete(self.request)
  106. return render(self.request, self.template_file, self.context)
  107. class PaymentDetailsView(checkout_views.CheckoutView):
  108. u"""
  109. For taking the details of payment and creating the order
  110. The class is deliberately split into fine-grained method, responsible for only one
  111. thing. This is to make it easier to subclass and override just one component of
  112. functionality.
  113. """
  114. # Any payment sources should be added to this list as part of the
  115. # _handle_payment method. If the order is placed successfully, then
  116. # they will be persisted.
  117. payment_sources = []
  118. def handle_GET(self):
  119. return self.handle_POST()
  120. def handle_POST(self):
  121. """
  122. This method is designed to be overridden by subclasses which will
  123. validate the forms from the payment details page. If the forms are valid
  124. then the method can call _submit()."""
  125. return self._submit()
  126. def _submit(self):
  127. # We generate the order number first as this will be used
  128. # in payment requests (ie before the order model has been
  129. # created).
  130. order_number = self._generate_order_number(self.basket)
  131. logger.info(_("Submitting order #%s" % order_number))
  132. checkout_signals.pre_payment.send_robust(sender=self, view=self)
  133. self._handle_payment(self.basket, order_number)
  134. checkout_signals.post_payment.send_robust(sender=self, view=self)
  135. order = self._place_order(self.basket, order_number)
  136. self._save_payment_sources(order)
  137. self._reset_checkout()
  138. logger.info(_("Order #%s submitted successfully" % order_number))
  139. # Save order id in session so thank-you page can load it
  140. self.request.session['checkout_order_id'] = order.id
  141. return HttpResponseRedirect(reverse('oscar-checkout-thank-you'))
  142. def _generate_order_number(self, basket):
  143. generator = order_utils.OrderNumberGenerator()
  144. return generator.order_number(basket)
  145. def _handle_payment(self, basket, order_number):
  146. u"""Handle any payment processing"""
  147. pass
  148. def _save_payment_sources(self, order):
  149. u"""
  150. Saves any payment sources used in this order.
  151. """
  152. for source in self.payment_sources:
  153. source.order = order
  154. source.save()
  155. def _reset_checkout(self):
  156. u"""Reset any checkout session state"""
  157. self.co_data.flush()
  158. checkout_utils.ProgressChecker().all_steps_complete(self.request)
  159. def _place_order(self, basket, order_number):
  160. u"""Writes the order out to the DB"""
  161. calc = checkout_calculators.OrderTotalCalculator(self.request)
  162. shipping_address = self._get_shipping_address()
  163. shipping_method = self._get_shipping_method(basket)
  164. order_creator = order_utils.OrderCreator(calc)
  165. return order_creator.place_order(self.request.user, basket,
  166. shipping_address, shipping_method, order_number)
  167. def _get_chargable_total(self, basket):
  168. u"""
  169. Returns the total amount to take payment for.
  170. """
  171. calc = checkout_calculators.OrderTotalCalculator(self.request)
  172. shipping_method = self._get_shipping_method(basket)
  173. return calc.order_total_incl_tax(basket, shipping_method)
  174. def _get_shipping_method(self, basket):
  175. u"""Returns the shipping method object"""
  176. method = self.co_data.shipping_method()
  177. method.set_basket(basket)
  178. return method
  179. def _get_shipping_address(self):
  180. u"""Returns the shipping address"""
  181. addr_data = self.co_data.new_address_fields()
  182. addr_id = self.co_data.user_address_id()
  183. if addr_data:
  184. addr = self._create_shipping_address_from_form_fields(addr_data)
  185. self._create_user_address(addr_data)
  186. elif addr_id:
  187. addr = self._create_shipping_address_from_user_address(addr_id)
  188. else:
  189. raise AttributeError("No shipping address data found")
  190. return addr
  191. def _create_shipping_address_from_form_fields(self, addr_data):
  192. u"""Creates a shipping address model from the saved form fields"""
  193. shipping_addr = order_models.ShippingAddress(**addr_data)
  194. shipping_addr.save()
  195. return shipping_addr
  196. def _create_user_address(self, addr_data):
  197. u"""
  198. For signed-in users, we create a user address model which will go
  199. into their address book.
  200. """
  201. if self.request.user.is_authenticated():
  202. addr_data['user_id'] = self.request.user.id
  203. user_addr = address_models.UserAddress(**addr_data)
  204. # Check that this address isn't already in the db as we don't want
  205. # to fill up the customer address book with duplicate addresses
  206. try:
  207. duplicate_addr = address_models.UserAddress._default_manager.get(hash=user_addr.generate_hash())
  208. except ObjectDoesNotExist:
  209. user_addr.save()
  210. def _create_shipping_address_from_user_address(self, addr_id):
  211. u"""Creates a shipping address from a user address"""
  212. address = address_models.UserAddress._default_manager.get(pk=addr_id)
  213. # Increment the number of orders to help determine popularity of orders
  214. address.num_orders += 1
  215. address.save()
  216. shipping_addr = order_models.ShippingAddress()
  217. address.populate_alternative_model(shipping_addr)
  218. shipping_addr.save()
  219. return shipping_addr
  220. class ThankYouView(object):
  221. def __call__(self, request):
  222. try:
  223. order = order_models.Order._default_manager.get(pk=request.session['checkout_order_id'])
  224. # Remove order number from session to ensure that the thank-you page is only
  225. # viewable once.
  226. del request.session['checkout_order_id']
  227. except KeyError, ObjectDoesNotExist:
  228. return HttpResponseRedirect(reverse('oscar-checkout-index'))
  229. return render(request, 'checkout/thank_you.html', locals())