Repository: saleor/saleor

Issue:
Checkout process improvements
During my work on PR #2206 I found out few things that may improve checkout process.

#### Going forward/back between steps

Although we can go back to shipping address step from choosing shipping method, we can't go back from summary step, where billing address is set. I think that passing between steps should be allowed.

#### Clearing cart

We can remove every single item from cart, but there is no button like `Clear cart`. It would be useful, when customer wants to cancel all cart lines at once.


Discussion:
Hi everyone, I started to handle with that issue.

Resolving patch:
diff --git a/saleor/checkout/urls.py b/saleor/checkout/urls.py
--- a/saleor/checkout/urls.py
+++ b/saleor/checkout/urls.py
@@ -19,6 +19,7 @@
     url(r'^$', views.cart_index, name='index'),
     url(r'^update/(?P<variant_id>\d+)/$',
         views.update_cart_line, name='update-line'),
+    url(r'^clear-cart/$', views.clear_cart, name='clear-cart'),
     url(r'^summary/$', views.cart_summary, name='summary'),
     url(r'^shipping-options/$', views.cart_shipping_options,
         name='shipping-options')]
diff --git a/saleor/checkout/views/__init__.py b/saleor/checkout/views/__init__.py
--- a/saleor/checkout/views/__init__.py
+++ b/saleor/checkout/views/__init__.py
@@ -12,7 +12,8 @@
 from ..models import Cart
 from ..utils import (
     check_product_availability_and_warn, check_shipping_method, get_cart_data,
-    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart)
+    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart,
+    update_cart_quantity)
 from .discount import add_voucher_form, validate_voucher
 from .shipping import (
     anonymous_user_shipping_address_view, user_shipping_address_view)
@@ -195,6 +196,17 @@ def update_cart_line(request, cart, variant_id):
     return JsonResponse(response, status=status)
 
 
+@get_or_empty_db_cart()
+def clear_cart(request, cart):
+    """Clear cart"""
+    if not request.is_ajax():
+        return redirect('cart:index')
+    cart.lines.all().delete()
+    update_cart_quantity(cart)
+    response = {'numItems': 0}
+    return JsonResponse(response)
+
+
 @get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())
 def cart_summary(request, cart):
     """Display a cart summary suitable for displaying on all pages."""


Test patch:
diff --git a/tests/test_cart.py b/tests/test_cart.py
--- a/tests/test_cart.py
+++ b/tests/test_cart.py
@@ -15,7 +15,7 @@
 from saleor.checkout.models import Cart
 from saleor.checkout.utils import (
     add_variant_to_cart, change_cart_user, find_open_cart_for_user)
-from saleor.checkout.views import update_cart_line
+from saleor.checkout.views import update_cart_line, clear_cart
 from saleor.core.exceptions import InsufficientStock
 from saleor.core.utils.taxes import ZERO_TAXED_MONEY
 from saleor.discount.models import Sale
@@ -654,3 +654,20 @@ def test_cart_taxes(request_cart_with_item, shipping_method, vatlayer):
     taxed_price = TaxedMoney(net=Money('8.13', 'USD'), gross=Money(10, 'USD'))
     assert cart.get_shipping_price(taxes=vatlayer) == taxed_price
     assert cart.get_subtotal(taxes=vatlayer) == taxed_price
+
+
+def test_clear_cart_must_be_ajax(rf, customer_user):
+    request = rf.post(reverse('home'))
+    request.user = customer_user
+    request.discounts = None
+    response = clear_cart(request)
+    assert response.status_code == 302
+
+
+def test_clear_cart(request_cart_with_item, client):
+    cart = request_cart_with_item
+    response = client.post(
+        reverse('cart:clear-cart'), data={},
+        HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+    assert response.status_code == 200
+    assert len(cart.lines.all()) == 0