We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/checkout/urls.py`
Content:
```
1 from django.conf.urls import url
2 
3 from . import views
4 from .views.discount import remove_voucher_view
5 
6 checkout_urlpatterns = [
7     url(r'^$', views.checkout_index, name='index'),
8     url(r'^shipping-address/', views.checkout_shipping_address,
9         name='shipping-address'),
10     url(r'^shipping-method/', views.checkout_shipping_method,
11         name='shipping-method'),
12     url(r'^summary/', views.checkout_summary, name='summary'),
13     url(r'^remove_voucher/', remove_voucher_view,
14         name='remove-voucher'),
15     url(r'^login/', views.checkout_login, name='login')]
16 
17 
18 cart_urlpatterns = [
19     url(r'^$', views.cart_index, name='index'),
20     url(r'^update/(?P<variant_id>\d+)/$',
21         views.update_cart_line, name='update-line'),
22     url(r'^summary/$', views.cart_summary, name='summary'),
23     url(r'^shipping-options/$', views.cart_shipping_options,
24         name='shipping-options')]
25
```

Path: `saleor/checkout/views/__init__.py`
Content:
```
1 """Cart and checkout related views."""
2 from django.http import JsonResponse
3 from django.shortcuts import get_object_or_404, redirect, render
4 from django.template.response import TemplateResponse
5 
6 from ...account.forms import LoginForm
7 from ...core.utils import (
8     format_money, get_user_shipping_country, to_local_currency)
9 from ...product.models import ProductVariant
10 from ...shipping.utils import get_shipment_options
11 from ..forms import CartShippingMethodForm, CountryForm, ReplaceCartLineForm
12 from ..models import Cart
13 from ..utils import (
14     check_product_availability_and_warn, check_shipping_method, get_cart_data,
15     get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart)
16 from .discount import add_voucher_form, validate_voucher
17 from .shipping import (
18     anonymous_user_shipping_address_view, user_shipping_address_view)
19 from .summary import (
20     anonymous_summary_without_shipping, summary_with_shipping_view,
21     summary_without_shipping)
22 from .validators import (
23     validate_cart, validate_is_shipping_required, validate_shipping_address,
24     validate_shipping_method)
25 
26 
27 @get_or_empty_db_cart(Cart.objects.for_display())
28 @validate_cart
29 def checkout_login(request, cart):
30     """Allow the user to log in prior to checkout."""
31     if request.user.is_authenticated:
32         return redirect('checkout:index')
33     ctx = {'form': LoginForm()}
34     return TemplateResponse(request, 'checkout/login.html', ctx)
35 
36 
37 @get_or_empty_db_cart(Cart.objects.for_display())
38 @validate_cart
39 @validate_is_shipping_required
40 def checkout_index(request, cart):
41     """Redirect to the initial step of checkout."""
42     return redirect('checkout:shipping-address')
43 
44 
45 @get_or_empty_db_cart(Cart.objects.for_display())
46 @validate_voucher
47 @validate_cart
48 @validate_is_shipping_required
49 @add_voucher_form
50 def checkout_shipping_address(request, cart):
51     """Display the correct shipping address step."""
52     if request.user.is_authenticated:
53         return user_shipping_address_view(request, cart)
54     return anonymous_user_shipping_address_view(request, cart)
55 
56 
57 @get_or_empty_db_cart(Cart.objects.for_display())
58 @validate_voucher
59 @validate_cart
60 @validate_is_shipping_required
61 @validate_shipping_address
62 @add_voucher_form
63 def checkout_shipping_method(request, cart):
64     """Display the shipping method selection step."""
65     taxes = get_taxes_for_cart(cart, request.taxes)
66     check_shipping_method(cart)
67     form = CartShippingMethodForm(
68         request.POST or None, taxes=taxes, instance=cart,
69         initial={'shipping_method': cart.shipping_method})
70 
71     if form.is_valid():
72         form.save()
73         return redirect('checkout:summary')
74 
75     ctx = get_cart_data_for_checkout(cart, request.discounts, taxes)
76     ctx.update({'shipping_method_form': form})
77     return TemplateResponse(request, 'checkout/shipping_method.html', ctx)
78 
79 
80 @get_or_empty_db_cart(Cart.objects.for_display())
81 @validate_voucher
82 @validate_cart
83 @add_voucher_form
84 def checkout_summary(request, cart):
85     """Display the correct order summary."""
86     if cart.is_shipping_required():
87         view = validate_shipping_method(summary_with_shipping_view)
88         view = validate_shipping_address(view)
89         return view(request, cart)
90     if request.user.is_authenticated:
91         return summary_without_shipping(request, cart)
92     return anonymous_summary_without_shipping(request, cart)
93 
94 
95 @get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())
96 def cart_index(request, cart):
97     """Display cart details."""
98     discounts = request.discounts
99     taxes = request.taxes
100     cart_lines = []
101     check_product_availability_and_warn(request, cart)
102 
103     # refresh required to get updated cart lines and it's quantity
104     try:
105         cart = Cart.objects.prefetch_related(
106             'lines__variant__product__category').get(pk=cart.pk)
107     except Cart.DoesNotExist:
108         pass
109 
110     lines = cart.lines.select_related(
111         'variant__product__product_type',
112         'variant__product__category')
113     lines = lines.prefetch_related(
114         'variant__product__collections',
115         'variant__product__images',
116         'variant__product__product_type__variant_attributes')
117     for line in lines:
118         initial = {'quantity': line.quantity}
119         form = ReplaceCartLineForm(
120             None, cart=cart, variant=line.variant, initial=initial,
121             discounts=discounts, taxes=taxes)
122         cart_lines.append({
123             'variant': line.variant,
124             'get_price': line.variant.get_price(discounts, taxes),
125             'get_total': line.get_total(discounts, taxes),
126             'form': form})
127 
128     default_country = get_user_shipping_country(request)
129     country_form = CountryForm(initial={'country': default_country})
130     default_country_options = get_shipment_options(default_country, taxes)
131 
132     cart_data = get_cart_data(
133         cart, default_country_options, request.currency, discounts, taxes)
134     ctx = {
135         'cart_lines': cart_lines,
136         'country_form': country_form,
137         'default_country_options': default_country_options}
138     ctx.update(cart_data)
139 
140     return TemplateResponse(request, 'checkout/index.html', ctx)
141 
142 
143 @get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())
144 def cart_shipping_options(request, cart):
145     """Display shipping options to get a price estimate."""
146     country_form = CountryForm(request.POST or None, taxes=request.taxes)
147     if country_form.is_valid():
148         shipments = country_form.get_shipment_options()
149     else:
150         shipments = None
151     ctx = {
152         'default_country_options': shipments,
153         'country_form': country_form}
154     cart_data = get_cart_data(
155         cart, shipments, request.currency, request.discounts, request.taxes)
156     ctx.update(cart_data)
157     return TemplateResponse(request, 'checkout/_subtotal_table.html', ctx)
158 
159 
160 @get_or_empty_db_cart()
161 def update_cart_line(request, cart, variant_id):
162     """Update the line quantities."""
163     if not request.is_ajax():
164         return redirect('cart:index')
165     variant = get_object_or_404(ProductVariant, pk=variant_id)
166     discounts = request.discounts
167     taxes = request.taxes
168     status = None
169     form = ReplaceCartLineForm(
170         request.POST, cart=cart, variant=variant, discounts=discounts,
171         taxes=taxes)
172     if form.is_valid():
173         form.save()
174         response = {
175             'variantId': variant_id,
176             'subtotal': 0,
177             'total': 0,
178             'cart': {
179                 'numItems': cart.quantity,
180                 'numLines': len(cart)}}
181         updated_line = cart.get_line(form.cart_line.variant)
182         if updated_line:
183             response['subtotal'] = format_money(
184                 updated_line.get_total(discounts, taxes).gross)
185         if cart:
186             cart_total = cart.get_subtotal(discounts, taxes)
187             response['total'] = format_money(cart_total.gross)
188             local_cart_total = to_local_currency(cart_total, request.currency)
189             if local_cart_total is not None:
190                 response['localTotal'] = format_money(local_cart_total.gross)
191         status = 200
192     elif request.POST is not None:
193         response = {'error': form.errors}
194         status = 400
195     return JsonResponse(response, status=status)
196 
197 
198 @get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())
199 def cart_summary(request, cart):
200     """Display a cart summary suitable for displaying on all pages."""
201     discounts = request.discounts
202     taxes = request.taxes
203 
204     def prepare_line_data(line):
205         first_image = line.variant.get_first_image()
206         return {
207             'product': line.variant.product,
208             'variant': line.variant.name,
209             'quantity': line.quantity,
210             'image': first_image,
211             'line_total': line.get_total(discounts, taxes),
212             'variant_url': line.variant.get_absolute_url()}
213 
214     if cart.quantity == 0:
215         data = {'quantity': 0}
216     else:
217         data = {
218             'quantity': cart.quantity,
219             'total': cart.get_subtotal(discounts, taxes),
220             'lines': [prepare_line_data(line) for line in cart]}
221 
222     return render(request, 'cart_dropdown.html', data)
223
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     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."""

{"golden_diff": "diff --git a/saleor/checkout/urls.py b/saleor/checkout/urls.py\n--- a/saleor/checkout/urls.py\n+++ b/saleor/checkout/urls.py\n@@ -19,6 +19,7 @@\n     url(r'^$', views.cart_index, name='index'),\n     url(r'^update/(?P<variant_id>\\d+)/$',\n         views.update_cart_line, name='update-line'),\n+    url(r'^clear-cart/$', views.clear_cart, name='clear-cart'),\n     url(r'^summary/$', views.cart_summary, name='summary'),\n     url(r'^shipping-options/$', views.cart_shipping_options,\n         name='shipping-options')]\ndiff --git a/saleor/checkout/views/__init__.py b/saleor/checkout/views/__init__.py\n--- a/saleor/checkout/views/__init__.py\n+++ b/saleor/checkout/views/__init__.py\n@@ -12,7 +12,8 @@\n from ..models import Cart\n from ..utils import (\n     check_product_availability_and_warn, check_shipping_method, get_cart_data,\n-    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart)\n+    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart,\n+    update_cart_quantity)\n from .discount import add_voucher_form, validate_voucher\n from .shipping import (\n     anonymous_user_shipping_address_view, user_shipping_address_view)\n@@ -195,6 +196,17 @@\n     return JsonResponse(response, status=status)\n \n \n+@get_or_empty_db_cart()\n+def clear_cart(request, cart):\n+    \"\"\"Clear cart\"\"\"\n+    if not request.is_ajax():\n+        return redirect('cart:index')\n+    cart.lines.all().delete()\n+    update_cart_quantity(cart)\n+    response = {'numItems': 0}\n+    return JsonResponse(response)\n+\n+\n @get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\n def cart_summary(request, cart):\n     \"\"\"Display a cart summary suitable for displaying on all pages.\"\"\"\n", "issue": "Checkout process improvements\nDuring my work on PR #2206 I found out few things that may improve checkout process.\r\n\r\n#### Going forward/back between steps\r\n\r\nAlthough 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.\r\n\r\n#### Clearing cart\r\n\r\nWe 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.\n", "before_files": [{"content": "from django.conf.urls import url\n\nfrom . import views\nfrom .views.discount import remove_voucher_view\n\ncheckout_urlpatterns = [\n    url(r'^$', views.checkout_index, name='index'),\n    url(r'^shipping-address/', views.checkout_shipping_address,\n        name='shipping-address'),\n    url(r'^shipping-method/', views.checkout_shipping_method,\n        name='shipping-method'),\n    url(r'^summary/', views.checkout_summary, name='summary'),\n    url(r'^remove_voucher/', remove_voucher_view,\n        name='remove-voucher'),\n    url(r'^login/', views.checkout_login, name='login')]\n\n\ncart_urlpatterns = [\n    url(r'^$', views.cart_index, name='index'),\n    url(r'^update/(?P<variant_id>\\d+)/$',\n        views.update_cart_line, name='update-line'),\n    url(r'^summary/$', views.cart_summary, name='summary'),\n    url(r'^shipping-options/$', views.cart_shipping_options,\n        name='shipping-options')]\n", "path": "saleor/checkout/urls.py"}, {"content": "\"\"\"Cart and checkout related views.\"\"\"\nfrom django.http import JsonResponse\nfrom django.shortcuts import get_object_or_404, redirect, render\nfrom django.template.response import TemplateResponse\n\nfrom ...account.forms import LoginForm\nfrom ...core.utils import (\n    format_money, get_user_shipping_country, to_local_currency)\nfrom ...product.models import ProductVariant\nfrom ...shipping.utils import get_shipment_options\nfrom ..forms import CartShippingMethodForm, CountryForm, ReplaceCartLineForm\nfrom ..models import Cart\nfrom ..utils import (\n    check_product_availability_and_warn, check_shipping_method, get_cart_data,\n    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart)\nfrom .discount import add_voucher_form, validate_voucher\nfrom .shipping import (\n    anonymous_user_shipping_address_view, user_shipping_address_view)\nfrom .summary import (\n    anonymous_summary_without_shipping, summary_with_shipping_view,\n    summary_without_shipping)\nfrom .validators import (\n    validate_cart, validate_is_shipping_required, validate_shipping_address,\n    validate_shipping_method)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_cart\ndef checkout_login(request, cart):\n    \"\"\"Allow the user to log in prior to checkout.\"\"\"\n    if request.user.is_authenticated:\n        return redirect('checkout:index')\n    ctx = {'form': LoginForm()}\n    return TemplateResponse(request, 'checkout/login.html', ctx)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_cart\n@validate_is_shipping_required\ndef checkout_index(request, cart):\n    \"\"\"Redirect to the initial step of checkout.\"\"\"\n    return redirect('checkout:shipping-address')\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@validate_is_shipping_required\n@add_voucher_form\ndef checkout_shipping_address(request, cart):\n    \"\"\"Display the correct shipping address step.\"\"\"\n    if request.user.is_authenticated:\n        return user_shipping_address_view(request, cart)\n    return anonymous_user_shipping_address_view(request, cart)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@validate_is_shipping_required\n@validate_shipping_address\n@add_voucher_form\ndef checkout_shipping_method(request, cart):\n    \"\"\"Display the shipping method selection step.\"\"\"\n    taxes = get_taxes_for_cart(cart, request.taxes)\n    check_shipping_method(cart)\n    form = CartShippingMethodForm(\n        request.POST or None, taxes=taxes, instance=cart,\n        initial={'shipping_method': cart.shipping_method})\n\n    if form.is_valid():\n        form.save()\n        return redirect('checkout:summary')\n\n    ctx = get_cart_data_for_checkout(cart, request.discounts, taxes)\n    ctx.update({'shipping_method_form': form})\n    return TemplateResponse(request, 'checkout/shipping_method.html', ctx)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@add_voucher_form\ndef checkout_summary(request, cart):\n    \"\"\"Display the correct order summary.\"\"\"\n    if cart.is_shipping_required():\n        view = validate_shipping_method(summary_with_shipping_view)\n        view = validate_shipping_address(view)\n        return view(request, cart)\n    if request.user.is_authenticated:\n        return summary_without_shipping(request, cart)\n    return anonymous_summary_without_shipping(request, cart)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_index(request, cart):\n    \"\"\"Display cart details.\"\"\"\n    discounts = request.discounts\n    taxes = request.taxes\n    cart_lines = []\n    check_product_availability_and_warn(request, cart)\n\n    # refresh required to get updated cart lines and it's quantity\n    try:\n        cart = Cart.objects.prefetch_related(\n            'lines__variant__product__category').get(pk=cart.pk)\n    except Cart.DoesNotExist:\n        pass\n\n    lines = cart.lines.select_related(\n        'variant__product__product_type',\n        'variant__product__category')\n    lines = lines.prefetch_related(\n        'variant__product__collections',\n        'variant__product__images',\n        'variant__product__product_type__variant_attributes')\n    for line in lines:\n        initial = {'quantity': line.quantity}\n        form = ReplaceCartLineForm(\n            None, cart=cart, variant=line.variant, initial=initial,\n            discounts=discounts, taxes=taxes)\n        cart_lines.append({\n            'variant': line.variant,\n            'get_price': line.variant.get_price(discounts, taxes),\n            'get_total': line.get_total(discounts, taxes),\n            'form': form})\n\n    default_country = get_user_shipping_country(request)\n    country_form = CountryForm(initial={'country': default_country})\n    default_country_options = get_shipment_options(default_country, taxes)\n\n    cart_data = get_cart_data(\n        cart, default_country_options, request.currency, discounts, taxes)\n    ctx = {\n        'cart_lines': cart_lines,\n        'country_form': country_form,\n        'default_country_options': default_country_options}\n    ctx.update(cart_data)\n\n    return TemplateResponse(request, 'checkout/index.html', ctx)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_shipping_options(request, cart):\n    \"\"\"Display shipping options to get a price estimate.\"\"\"\n    country_form = CountryForm(request.POST or None, taxes=request.taxes)\n    if country_form.is_valid():\n        shipments = country_form.get_shipment_options()\n    else:\n        shipments = None\n    ctx = {\n        'default_country_options': shipments,\n        'country_form': country_form}\n    cart_data = get_cart_data(\n        cart, shipments, request.currency, request.discounts, request.taxes)\n    ctx.update(cart_data)\n    return TemplateResponse(request, 'checkout/_subtotal_table.html', ctx)\n\n\n@get_or_empty_db_cart()\ndef update_cart_line(request, cart, variant_id):\n    \"\"\"Update the line quantities.\"\"\"\n    if not request.is_ajax():\n        return redirect('cart:index')\n    variant = get_object_or_404(ProductVariant, pk=variant_id)\n    discounts = request.discounts\n    taxes = request.taxes\n    status = None\n    form = ReplaceCartLineForm(\n        request.POST, cart=cart, variant=variant, discounts=discounts,\n        taxes=taxes)\n    if form.is_valid():\n        form.save()\n        response = {\n            'variantId': variant_id,\n            'subtotal': 0,\n            'total': 0,\n            'cart': {\n                'numItems': cart.quantity,\n                'numLines': len(cart)}}\n        updated_line = cart.get_line(form.cart_line.variant)\n        if updated_line:\n            response['subtotal'] = format_money(\n                updated_line.get_total(discounts, taxes).gross)\n        if cart:\n            cart_total = cart.get_subtotal(discounts, taxes)\n            response['total'] = format_money(cart_total.gross)\n            local_cart_total = to_local_currency(cart_total, request.currency)\n            if local_cart_total is not None:\n                response['localTotal'] = format_money(local_cart_total.gross)\n        status = 200\n    elif request.POST is not None:\n        response = {'error': form.errors}\n        status = 400\n    return JsonResponse(response, status=status)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_summary(request, cart):\n    \"\"\"Display a cart summary suitable for displaying on all pages.\"\"\"\n    discounts = request.discounts\n    taxes = request.taxes\n\n    def prepare_line_data(line):\n        first_image = line.variant.get_first_image()\n        return {\n            'product': line.variant.product,\n            'variant': line.variant.name,\n            'quantity': line.quantity,\n            'image': first_image,\n            'line_total': line.get_total(discounts, taxes),\n            'variant_url': line.variant.get_absolute_url()}\n\n    if cart.quantity == 0:\n        data = {'quantity': 0}\n    else:\n        data = {\n            'quantity': cart.quantity,\n            'total': cart.get_subtotal(discounts, taxes),\n            'lines': [prepare_line_data(line) for line in cart]}\n\n    return render(request, 'cart_dropdown.html', data)\n", "path": "saleor/checkout/views/__init__.py"}], "after_files": [{"content": "from django.conf.urls import url\n\nfrom . import views\nfrom .views.discount import remove_voucher_view\n\ncheckout_urlpatterns = [\n    url(r'^$', views.checkout_index, name='index'),\n    url(r'^shipping-address/', views.checkout_shipping_address,\n        name='shipping-address'),\n    url(r'^shipping-method/', views.checkout_shipping_method,\n        name='shipping-method'),\n    url(r'^summary/', views.checkout_summary, name='summary'),\n    url(r'^remove_voucher/', remove_voucher_view,\n        name='remove-voucher'),\n    url(r'^login/', views.checkout_login, name='login')]\n\n\ncart_urlpatterns = [\n    url(r'^$', views.cart_index, name='index'),\n    url(r'^update/(?P<variant_id>\\d+)/$',\n        views.update_cart_line, name='update-line'),\n    url(r'^clear-cart/$', views.clear_cart, name='clear-cart'),\n    url(r'^summary/$', views.cart_summary, name='summary'),\n    url(r'^shipping-options/$', views.cart_shipping_options,\n        name='shipping-options')]\n", "path": "saleor/checkout/urls.py"}, {"content": "\"\"\"Cart and checkout related views.\"\"\"\nfrom django.http import JsonResponse\nfrom django.shortcuts import get_object_or_404, redirect, render\nfrom django.template.response import TemplateResponse\n\nfrom ...account.forms import LoginForm\nfrom ...core.utils import (\n    format_money, get_user_shipping_country, to_local_currency)\nfrom ...product.models import ProductVariant\nfrom ...shipping.utils import get_shipment_options\nfrom ..forms import CartShippingMethodForm, CountryForm, ReplaceCartLineForm\nfrom ..models import Cart\nfrom ..utils import (\n    check_product_availability_and_warn, check_shipping_method, get_cart_data,\n    get_cart_data_for_checkout, get_or_empty_db_cart, get_taxes_for_cart,\n    update_cart_quantity)\nfrom .discount import add_voucher_form, validate_voucher\nfrom .shipping import (\n    anonymous_user_shipping_address_view, user_shipping_address_view)\nfrom .summary import (\n    anonymous_summary_without_shipping, summary_with_shipping_view,\n    summary_without_shipping)\nfrom .validators import (\n    validate_cart, validate_is_shipping_required, validate_shipping_address,\n    validate_shipping_method)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_cart\ndef checkout_login(request, cart):\n    \"\"\"Allow the user to log in prior to checkout.\"\"\"\n    if request.user.is_authenticated:\n        return redirect('checkout:index')\n    ctx = {'form': LoginForm()}\n    return TemplateResponse(request, 'checkout/login.html', ctx)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_cart\n@validate_is_shipping_required\ndef checkout_index(request, cart):\n    \"\"\"Redirect to the initial step of checkout.\"\"\"\n    return redirect('checkout:shipping-address')\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@validate_is_shipping_required\n@add_voucher_form\ndef checkout_shipping_address(request, cart):\n    \"\"\"Display the correct shipping address step.\"\"\"\n    if request.user.is_authenticated:\n        return user_shipping_address_view(request, cart)\n    return anonymous_user_shipping_address_view(request, cart)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@validate_is_shipping_required\n@validate_shipping_address\n@add_voucher_form\ndef checkout_shipping_method(request, cart):\n    \"\"\"Display the shipping method selection step.\"\"\"\n    taxes = get_taxes_for_cart(cart, request.taxes)\n    check_shipping_method(cart)\n    form = CartShippingMethodForm(\n        request.POST or None, taxes=taxes, instance=cart,\n        initial={'shipping_method': cart.shipping_method})\n\n    if form.is_valid():\n        form.save()\n        return redirect('checkout:summary')\n\n    ctx = get_cart_data_for_checkout(cart, request.discounts, taxes)\n    ctx.update({'shipping_method_form': form})\n    return TemplateResponse(request, 'checkout/shipping_method.html', ctx)\n\n\n@get_or_empty_db_cart(Cart.objects.for_display())\n@validate_voucher\n@validate_cart\n@add_voucher_form\ndef checkout_summary(request, cart):\n    \"\"\"Display the correct order summary.\"\"\"\n    if cart.is_shipping_required():\n        view = validate_shipping_method(summary_with_shipping_view)\n        view = validate_shipping_address(view)\n        return view(request, cart)\n    if request.user.is_authenticated:\n        return summary_without_shipping(request, cart)\n    return anonymous_summary_without_shipping(request, cart)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_index(request, cart):\n    \"\"\"Display cart details.\"\"\"\n    discounts = request.discounts\n    taxes = request.taxes\n    cart_lines = []\n    check_product_availability_and_warn(request, cart)\n\n    # refresh required to get updated cart lines and it's quantity\n    try:\n        cart = Cart.objects.prefetch_related(\n            'lines__variant__product__category').get(pk=cart.pk)\n    except Cart.DoesNotExist:\n        pass\n\n    lines = cart.lines.select_related(\n        'variant__product__product_type',\n        'variant__product__category')\n    lines = lines.prefetch_related(\n        'variant__product__collections',\n        'variant__product__images',\n        'variant__product__product_type__variant_attributes')\n    for line in lines:\n        initial = {'quantity': line.quantity}\n        form = ReplaceCartLineForm(\n            None, cart=cart, variant=line.variant, initial=initial,\n            discounts=discounts, taxes=taxes)\n        cart_lines.append({\n            'variant': line.variant,\n            'get_price': line.variant.get_price(discounts, taxes),\n            'get_total': line.get_total(discounts, taxes),\n            'form': form})\n\n    default_country = get_user_shipping_country(request)\n    country_form = CountryForm(initial={'country': default_country})\n    default_country_options = get_shipment_options(default_country, taxes)\n\n    cart_data = get_cart_data(\n        cart, default_country_options, request.currency, discounts, taxes)\n    ctx = {\n        'cart_lines': cart_lines,\n        'country_form': country_form,\n        'default_country_options': default_country_options}\n    ctx.update(cart_data)\n\n    return TemplateResponse(request, 'checkout/index.html', ctx)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_shipping_options(request, cart):\n    \"\"\"Display shipping options to get a price estimate.\"\"\"\n    country_form = CountryForm(request.POST or None, taxes=request.taxes)\n    if country_form.is_valid():\n        shipments = country_form.get_shipment_options()\n    else:\n        shipments = None\n    ctx = {\n        'default_country_options': shipments,\n        'country_form': country_form}\n    cart_data = get_cart_data(\n        cart, shipments, request.currency, request.discounts, request.taxes)\n    ctx.update(cart_data)\n    return TemplateResponse(request, 'checkout/_subtotal_table.html', ctx)\n\n\n@get_or_empty_db_cart()\ndef update_cart_line(request, cart, variant_id):\n    \"\"\"Update the line quantities.\"\"\"\n    if not request.is_ajax():\n        return redirect('cart:index')\n    variant = get_object_or_404(ProductVariant, pk=variant_id)\n    discounts = request.discounts\n    taxes = request.taxes\n    status = None\n    form = ReplaceCartLineForm(\n        request.POST, cart=cart, variant=variant, discounts=discounts,\n        taxes=taxes)\n    if form.is_valid():\n        form.save()\n        response = {\n            'variantId': variant_id,\n            'subtotal': 0,\n            'total': 0,\n            'cart': {\n                'numItems': cart.quantity,\n                'numLines': len(cart)}}\n        updated_line = cart.get_line(form.cart_line.variant)\n        if updated_line:\n            response['subtotal'] = format_money(\n                updated_line.get_total(discounts, taxes).gross)\n        if cart:\n            cart_total = cart.get_subtotal(discounts, taxes)\n            response['total'] = format_money(cart_total.gross)\n            local_cart_total = to_local_currency(cart_total, request.currency)\n            if local_cart_total is not None:\n                response['localTotal'] = format_money(local_cart_total.gross)\n        status = 200\n    elif request.POST is not None:\n        response = {'error': form.errors}\n        status = 400\n    return JsonResponse(response, status=status)\n\n\n@get_or_empty_db_cart()\ndef clear_cart(request, cart):\n    \"\"\"Clear cart\"\"\"\n    if not request.is_ajax():\n        return redirect('cart:index')\n    cart.lines.all().delete()\n    update_cart_quantity(cart)\n    response = {'numItems': 0}\n    return JsonResponse(response)\n\n\n@get_or_empty_db_cart(cart_queryset=Cart.objects.for_display())\ndef cart_summary(request, cart):\n    \"\"\"Display a cart summary suitable for displaying on all pages.\"\"\"\n    discounts = request.discounts\n    taxes = request.taxes\n\n    def prepare_line_data(line):\n        first_image = line.variant.get_first_image()\n        return {\n            'product': line.variant.product,\n            'variant': line.variant.name,\n            'quantity': line.quantity,\n            'image': first_image,\n            'line_total': line.get_total(discounts, taxes),\n            'variant_url': line.variant.get_absolute_url()}\n\n    if cart.quantity == 0:\n        data = {'quantity': 0}\n    else:\n        data = {\n            'quantity': cart.quantity,\n            'total': cart.get_subtotal(discounts, taxes),\n            'lines': [prepare_line_data(line) for line in cart]}\n\n    return render(request, 'cart_dropdown.html', data)\n", "path": "saleor/checkout/views/__init__.py"}]}