Repository: saleor/saleor

Issue:
Broken checkoutComplete mutation.
1. Create checkout with products. 
2. Add required data like(Address, Shipping e.g.) 
3. Add voucher code (Voucher without name)
4. Send `checkoutComplete` mutation. 
5. System return `IntegrityError`. 


Discussion:

Resolving patch:
diff --git a/saleor/core/utils/random_data.py b/saleor/core/utils/random_data.py
--- a/saleor/core/utils/random_data.py
+++ b/saleor/core/utils/random_data.py
@@ -988,6 +988,19 @@ def create_vouchers():
     else:
         yield "Value voucher already exists"
 
+    voucher, created = Voucher.objects.get_or_create(
+        code="VCO9KV98LC",
+        defaults={
+            "type": VoucherType.ENTIRE_ORDER,
+            "discount_value_type": DiscountValueType.PERCENTAGE,
+            "discount_value": 5,
+        },
+    )
+    if created:
+        yield "Voucher #%d" % voucher.id
+    else:
+        yield "Value voucher already exists"
+
 
 def create_gift_card():
     user = random.choice(
diff --git a/saleor/order/migrations/0079_auto_20200304_0752.py b/saleor/order/migrations/0079_auto_20200304_0752.py
new file mode 100644
--- /dev/null
+++ b/saleor/order/migrations/0079_auto_20200304_0752.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.10 on 2020-03-04 13:52
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("order", "0078_auto_20200221_0257"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="order",
+            name="discount_name",
+            field=models.CharField(blank=True, max_length=255, null=True),
+        ),
+        migrations.AlterField(
+            model_name="order",
+            name="translated_discount_name",
+            field=models.CharField(blank=True, max_length=255, null=True),
+        ),
+    ]
diff --git a/saleor/order/models.py b/saleor/order/models.py
--- a/saleor/order/models.py
+++ b/saleor/order/models.py
@@ -161,8 +161,8 @@ class Order(ModelWithMetadata):
         default=0,
     )
     discount = MoneyField(amount_field="discount_amount", currency_field="currency")
-    discount_name = models.CharField(max_length=255, default="", blank=True)
-    translated_discount_name = models.CharField(max_length=255, default="", blank=True)
+    discount_name = models.CharField(max_length=255, blank=True, null=True)
+    translated_discount_name = models.CharField(max_length=255, blank=True, null=True)
     display_gross_prices = models.BooleanField(default=True)
     customer_note = models.TextField(blank=True, default="")
     weight = MeasurementField(


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
@@ -1257,6 +1257,73 @@ def test_checkout_complete(
     ).exists(), "Checkout should have been deleted"
 
 
+@pytest.mark.integration
+def test_checkout_with_voucher_complete(
+    user_api_client,
+    checkout_with_voucher_percentage,
+    voucher_percentage,
+    payment_dummy,
+    address,
+    shipping_method,
+):
+    voucher_used_count = voucher_percentage.used
+
+    checkout = checkout_with_voucher_percentage
+    checkout.shipping_address = address
+    checkout.shipping_method = shipping_method
+    checkout.billing_address = address
+    checkout.store_value_in_metadata(items={"accepted": "true"})
+    checkout.store_value_in_private_metadata(items={"accepted": "false"})
+    checkout.save()
+
+    checkout_line = checkout.lines.first()
+    checkout_line_quantity = checkout_line.quantity
+    checkout_line_variant = checkout_line.variant
+
+    total = calculations.checkout_total(checkout)
+    payment = payment_dummy
+    payment.is_active = True
+    payment.order = None
+    payment.total = total.gross.amount
+    payment.currency = total.gross.currency
+    payment.checkout = checkout
+    payment.save()
+    assert not payment.transactions.exists()
+
+    orders_count = Order.objects.count()
+    checkout_id = graphene.Node.to_global_id("Checkout", checkout.pk)
+    variables = {"checkoutId": checkout_id, "redirectUrl": "https://www.example.com"}
+    response = user_api_client.post_graphql(MUTATION_CHECKOUT_COMPLETE, variables)
+
+    content = get_graphql_content(response)
+    data = content["data"]["checkoutComplete"]
+    assert not data["errors"]
+
+    order_token = data["order"]["token"]
+    assert Order.objects.count() == orders_count + 1
+    order = Order.objects.first()
+    assert order.token == order_token
+    assert order.metadata == checkout.metadata
+    assert order.private_metadata == checkout.private_metadata
+
+    order_line = order.lines.first()
+    assert checkout_line_quantity == order_line.quantity
+    assert checkout_line_variant == order_line.variant
+    assert order.shipping_address == address
+    assert order.shipping_method == checkout.shipping_method
+    assert order.payments.exists()
+    order_payment = order.payments.first()
+    assert order_payment == payment
+    assert payment.transactions.count() == 1
+
+    voucher_percentage.refresh_from_db()
+    assert voucher_percentage.used == voucher_used_count + 1
+
+    assert not Checkout.objects.filter(
+        pk=checkout.pk
+    ).exists(), "Checkout should have been deleted"
+
+
 ERROR_GATEWAY_RESPONSE = GatewayResponse(
     is_success=False,
     action_required=False,
diff --git a/tests/test_core.py b/tests/test_core.py
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -160,7 +160,7 @@ def test_create_vouchers(db):
     assert Voucher.objects.all().count() == 0
     for _ in random_data.create_vouchers():
         pass
-    assert Voucher.objects.all().count() == 2
+    assert Voucher.objects.all().count() == 3
 
 
 def test_create_gift_card(db):