Repository: saleor/saleor

Issue:
Checkout - cart of death
### What I'm trying to achieve

After completing the second step at checkout (`shipping method`), I want to be able to remove all products from the cart and add others.

### Steps to reproduce the problem
1. Add any product to the cart
2. Go to checkout
3. Login to your account
4. Fill out the first 2 steps
5. Back to the cart page and remove all products
6. Try to add any product to your cart

### What I expected to happen

Products should be added to the cart.

### What I currently have

`checkoutLinesUpdate` mutation says `This checkout doesn't need shipping` and my checkout is locked. I'm not able to add any product as a logged-in user.




Discussion:
we just discovered the same bug using GraphQL. after we delete all lines using checkoutLineDelete mutation it is not possible to add any product back to that cart.
I would like to fix this bug. Any suggestions ? @bogdal
the problem lies in saleor/graphql/checkout/mutations.py:61 where there is a check for checkout.is_shipping_required() which returns `any(line.is_shipping_required() for line in self)`
obviously when there are no lines in the checkout it will always return False.
Gues, it's really wired behaviour. Maybe we can invalidate such checkout? Or remove shipping info from it.
it seems that removing shipping info is the most relevant solution. no items in the cart -> nothing to deliver -> it makes no sense to keep the shipping info.
Hello all. Yep, in my opinion, we should delete shipping information when there are no products in the cart. In addition, we need to prepare data migration to fix existing blocked checkouts.
I'm taking the `hacktoberfest` label off from this one, @fowczarek from our team will work on this issue.

Resolving patch:
diff --git a/saleor/graphql/checkout/mutations.py b/saleor/graphql/checkout/mutations.py
--- a/saleor/graphql/checkout/mutations.py
+++ b/saleor/graphql/checkout/mutations.py
@@ -77,6 +77,11 @@ def clean_shipping_method(
 
 
 def update_checkout_shipping_method_if_invalid(checkout: models.Checkout, discounts):
+    # remove shipping method when empty checkout
+    if checkout.quantity == 0:
+        checkout.shipping_method = None
+        checkout.save(update_fields=["shipping_method"])
+
     is_valid = clean_shipping_method(
         checkout=checkout, method=checkout.shipping_method, discounts=discounts
     )


Test patch:
diff --git a/tests/api/test_checkout.py b/tests/api/test_checkout.py
--- a/tests/api/test_checkout.py
+++ b/tests/api/test_checkout.py
@@ -682,6 +682,29 @@ def test_checkout_lines_update_check_lines_quantity(
     assert data["errors"][0]["field"] == "quantity"
 
 
+def test_checkout_lines_update_with_chosen_shipping(
+    user_api_client, checkout, variant, address, shipping_method
+):
+    checkout.shipping_address = address
+    checkout.shipping_method = shipping_method
+    checkout.save()
+
+    variant_id = graphene.Node.to_global_id("ProductVariant", variant.pk)
+    checkout_id = graphene.Node.to_global_id("Checkout", checkout.pk)
+
+    variables = {
+        "checkoutId": checkout_id,
+        "lines": [{"variantId": variant_id, "quantity": 1}],
+    }
+    response = user_api_client.post_graphql(MUTATION_CHECKOUT_LINES_UPDATE, variables)
+    content = get_graphql_content(response)
+
+    data = content["data"]["checkoutLinesUpdate"]
+    assert not data["errors"]
+    checkout.refresh_from_db()
+    assert checkout.quantity == 1
+
+
 MUTATION_CHECKOUT_LINES_DELETE = """
     mutation checkoutLineDelete($checkoutId: ID!, $lineId: ID!) {
         checkoutLineDelete(checkoutId: $checkoutId, lineId: $lineId) {