Repository: saleor/saleor

Issue:
Free shipping voucher applies to lower amount instead of shipment
### What I'm trying to achieve
Use the free shipping voucher to slash the shipment fee.

### Steps to reproduce the problem
1. Create a free shipping voucher in the dashboard
2. Add any number of products to the cart
3. Checkout the products with any address
4. Choose a shipping method that is **more expensive** than your subtotal

### What I expected to happen
The voucher should slash off the shipment price instead of the lower amount. Below are screenshots where the lower amount is slashed instead.

### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
<img width="473" alt="Screenshot 2019-11-01 at 12 06 01 PM" src="https://user-images.githubusercontent.com/14294811/68067030-35afda80-fd7c-11e9-88e7-a6bb9e458c06.png">
<img width="473" alt="Screenshot 2019-11-01 at 12 05 02 PM" src="https://user-images.githubusercontent.com/14294811/68067032-3e081580-fd7c-11e9-9db2-127f1140738f.png">



**System information**
Operating system: MacOS Mojave 10.14.6
Browser: Google Chrome version 78.0.3904.70 (Official Build) (64-bit)



Discussion:

Resolving patch:
diff --git a/saleor/checkout/utils.py b/saleor/checkout/utils.py
--- a/saleor/checkout/utils.py
+++ b/saleor/checkout/utils.py
@@ -784,7 +784,11 @@ def recalculate_checkout_discount(checkout, discounts):
         else:
             manager = get_extensions_manager()
             subtotal = manager.calculate_checkout_subtotal(checkout, discounts).gross
-            checkout.discount = min(discount, subtotal)
+            checkout.discount = (
+                min(discount, subtotal)
+                if voucher.type != VoucherType.SHIPPING
+                else discount
+            )
             checkout.discount_name = str(voucher)
             checkout.translated_discount_name = (
                 voucher.translated.name


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -152,6 +152,17 @@ def checkout_with_gift_card(checkout_with_item, gift_card):
     return checkout_with_item
 
 
+@pytest.fixture
+def checkout_with_voucher_percentage_and_shipping(
+    checkout_with_voucher_percentage, shipping_method, address
+):
+    checkout = checkout_with_voucher_percentage
+    checkout.shipping_method = shipping_method
+    checkout.shipping_address = address
+    checkout.save()
+    return checkout
+
+
 @pytest.fixture
 def address(db):  # pylint: disable=W0613
     return Address.objects.create(
@@ -787,6 +798,14 @@ def voucher_shipping_type():
     )
 
 
+@pytest.fixture
+def voucher_free_shipping(voucher_percentage):
+    voucher_percentage.type = VoucherType.SHIPPING
+    voucher_percentage.discount_value = 100
+    voucher_percentage.save()
+    return voucher_percentage
+
+
 @pytest.fixture
 def voucher_customer(voucher, customer_user):
     email = customer_user.email
diff --git a/tests/test_checkout.py b/tests/test_checkout.py
--- a/tests/test_checkout.py
+++ b/tests/test_checkout.py
@@ -1338,6 +1338,52 @@ def test_recalculate_checkout_discount_expired_voucher(checkout_with_voucher, vo
     assert checkout.discount == zero_money()
 
 
+def test_recalculate_checkout_discount_free_shipping_subtotal_less_than_shipping(
+    checkout_with_voucher_percentage_and_shipping,
+    voucher_free_shipping,
+    shipping_method,
+):
+    checkout = checkout_with_voucher_percentage_and_shipping
+
+    shipping_method.price = checkout.get_subtotal() + Money("10.00", "USD")
+    shipping_method.save()
+
+    recalculate_checkout_discount(checkout, None)
+
+    assert checkout.discount == shipping_method.price
+    assert checkout.discount_name == "Free shipping"
+    assert checkout.get_total() == checkout.get_subtotal()
+
+
+def test_recalculate_checkout_discount_free_shipping_subtotal_bigger_than_shipping(
+    checkout_with_voucher_percentage_and_shipping,
+    voucher_free_shipping,
+    shipping_method,
+):
+    checkout = checkout_with_voucher_percentage_and_shipping
+
+    shipping_method.price = checkout.get_subtotal() - Money("1.00", "USD")
+    shipping_method.save()
+
+    recalculate_checkout_discount(checkout, None)
+
+    assert checkout.discount == shipping_method.price
+    assert checkout.discount_name == "Free shipping"
+    assert checkout.get_total() == checkout.get_subtotal()
+
+
+def test_recalculate_checkout_discount_free_shipping_for_checkout_without_shipping(
+    checkout_with_voucher_percentage, voucher_free_shipping
+):
+    checkout = checkout_with_voucher_percentage
+
+    recalculate_checkout_discount(checkout, None)
+
+    assert not checkout.discount_name
+    assert not checkout.voucher_code
+    assert checkout.discount == zero_money()
+
+
 def test_get_checkout_context(checkout_with_voucher):
     line_price = TaxedMoney(net=Money("30.00", "USD"), gross=Money("30.00", "USD"))
     manager = get_extensions_manager()