Repository: django-oscar/django-oscar

Issue:
Baskets can be stuck frozen
Found a bug? Please fill out the sections below.

### Issue Summary

If `self.handle_order_placement` has an exception besides `UnableToPlaceOrder`, the `restore_frozen_basket` method is never called.

### Steps to Reproduce

In my example, I overrode `OrderCreator.create_line_models` and had a rare error in there.

### Technical details

* Python version: 2.7.
* Django version: 1.11.
* Oscar version: 1.6.7.


###
I recognize this is not the latest version, and we're doing something a little outside of the norm. That said, `restore_frozen_basket` should probably be in a `finally` clause.



Discussion:
Yeah, makes sense - we basically need to [duplicate this logic](https://github.com/django-oscar/django-oscar/blob/05e4594230013c50ddafdf7458d51fc4b71277b3/src/oscar/apps/checkout/views.py#L608-L616) in the try/except block for `handle_order_placement`.

Fix patch:
diff --git a/src/oscar/apps/checkout/views.py b/src/oscar/apps/checkout/views.py
--- a/src/oscar/apps/checkout/views.py
+++ b/src/oscar/apps/checkout/views.py
@@ -608,9 +608,9 @@ def submit(self, user, basket, shipping_address, shipping_method,  # noqa (too c
         except Exception as e:
             # Unhandled exception - hopefully, you will only ever see this in
             # development...
-            logger.error(
+            logger.exception(
                 "Order #%s: unhandled exception while taking payment (%s)",
-                order_number, e, exc_info=True)
+                order_number, e)
             self.restore_frozen_basket()
             return self.render_preview(
                 self.request, error=error_msg, **payment_kwargs)
@@ -635,6 +635,12 @@ def submit(self, user, basket, shipping_address, shipping_method,  # noqa (too c
             self.restore_frozen_basket()
             return self.render_preview(
                 self.request, error=msg, **payment_kwargs)
+        except Exception as e:
+            # Hopefully you only ever reach this in development
+            logger.exception("Order #%s: unhandled exception while placing order (%s)", order_number, e)
+            error_msg = _("A problem occurred while placing this order. Please contact customer services.")
+            self.restore_frozen_basket()
+            return self.render_preview(self.request, error=error_msg, **payment_kwargs)
 
     def get_template_names(self):
         return [self.template_name_preview] if self.preview else [


Test patch:
diff --git a/tests/functional/checkout/test_guest_checkout.py b/tests/functional/checkout/test_guest_checkout.py
--- a/tests/functional/checkout/test_guest_checkout.py
+++ b/tests/functional/checkout/test_guest_checkout.py
@@ -399,7 +399,7 @@ def test_handles_bad_errors_during_payments(
         preview = self.ready_to_place_an_order(is_guest=True)
         response = preview.forms['place_order_form'].submit()
         self.assertIsOk(response)
-        self.assertTrue(mock_logger.error.called)
+        self.assertTrue(mock_logger.exception.called)
         basket = Basket.objects.get()
         self.assertEqual(basket.status, Basket.OPEN)
 
@@ -416,6 +416,17 @@ def test_handles_unexpected_order_placement_errors_gracefully(
         basket = Basket.objects.get()
         self.assertEqual(basket.status, Basket.OPEN)
 
+    @mock.patch('oscar.apps.checkout.views.logger')
+    @mock.patch('oscar.apps.checkout.views.PaymentDetailsView.handle_order_placement')
+    def test_handles_all_other_exceptions_gracefully(self, mock_method, mock_logger):
+        mock_method.side_effect = Exception()
+        preview = self.ready_to_place_an_order(is_guest=True)
+        response = preview.forms['place_order_form'].submit()
+        self.assertIsOk(response)
+        self.assertTrue(mock_logger.exception.called)
+        basket = Basket.objects.get()
+        self.assertEqual(basket.status, Basket.OPEN)
+
 
 @override_settings(OSCAR_ALLOW_ANON_CHECKOUT=True)
 class TestPaymentDetailsWithPreview(CheckoutMixin, WebTestCase):