Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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