Repository: saleor/saleor

Issue:
Checkout success page not shown for authenticated users
When logged-in users place an order, they are redirected to the "order details" view. Instead, we should show a "success page" with the link to the "order details" view.

This works correctly for anonymous users, but the views and templates use wrong names (they're called "password reset"). I suppose we should have the following views:
- `checkout_success` - standard "Thank you" page with the link to the order details view
- `checkout_success_anonymous` - same as above, but with the ability to create an account




Discussion:

Resolving patch:
diff --git a/saleor/order/models.py b/saleor/order/models.py
--- a/saleor/order/models.py
+++ b/saleor/order/models.py
@@ -303,7 +303,8 @@ def get_failure_url(self):
 
     def get_success_url(self):
         return build_absolute_uri(
-            reverse('order:create-password', kwargs={'token': self.order.token}))
+            reverse(
+                'order:checkout-success', kwargs={'token': self.order.token}))
 
     def send_confirmation_email(self):
         email = self.order.get_user_current_email()
diff --git a/saleor/order/urls.py b/saleor/order/urls.py
--- a/saleor/order/urls.py
+++ b/saleor/order/urls.py
@@ -12,8 +12,8 @@
         views.start_payment, name='payment'),
     url(r'^%s/cancel-payment/$' % (TOKEN_PATTERN,), views.cancel_payment,
         name='cancel-payment'),
-    url(r'^%s/create-password/$' % (TOKEN_PATTERN,),
-        views.create_password, name='create-password'),
+    url(r'^%s/checkout-success/$' % (TOKEN_PATTERN,),
+        views.checkout_success, name='checkout-success'),
     url(r'^%s/attach/$' % (TOKEN_PATTERN,),
         views.connect_order_with_user, name='connect-order-with-user'),
 ]
diff --git a/saleor/order/views.py b/saleor/order/views.py
--- a/saleor/order/views.py
+++ b/saleor/order/views.py
@@ -32,8 +32,8 @@ def details(request, token):
 
 def payment(request, token):
     orders = Order.objects.prefetch_related('groups__lines__product')
-    orders = orders.select_related('billing_address', 'shipping_address',
-                                   'user')
+    orders = orders.select_related(
+        'billing_address', 'shipping_address', 'user')
     order = get_object_or_404(orders, token=token)
     groups = order.groups.all()
     payments = order.payments.all()
@@ -55,14 +55,13 @@ def payment(request, token):
         # FIXME: redirect if there is only one payment method
         if payment_form.is_valid():
             payment_method = payment_form.cleaned_data['method']
-            return redirect('order:payment', token=order.token,
-                            variant=payment_method)
-    return TemplateResponse(request, 'order/payment.html',
-                            {'order': order, 'groups': groups,
-                             'payment_form': payment_form,
-                             'waiting_payment': waiting_payment,
-                             'waiting_payment_form': waiting_payment_form,
-                             'payments': payments})
+            return redirect(
+                'order:payment', token=order.token, variant=payment_method)
+    ctx = {
+        'order': order, 'groups': groups, 'payment_form': payment_form,
+        'payments': payments, 'waiting_payment': waiting_payment,
+        'waiting_payment_form': waiting_payment_form}
+    return TemplateResponse(request, 'order/payment.html', ctx)
 
 
 @check_order_status
@@ -101,17 +100,17 @@ def start_payment(request, order, variant):
             return redirect(str(redirect_to))
         except Exception:
             logger.exception('Error communicating with the payment gateway')
-            messages.error(
-                request,
-                pgettext_lazy(
-                    'Payment gateway error',
-                    'Oops, it looks like we were unable to contact the selected'
-                    ' payment service'))
+            msg = pgettext_lazy(
+                'Payment gateway error',
+                'Oops, it looks like we were unable to contact the selected '
+                'payment service')
+            messages.error(request, msg)
             payment.change_status(PaymentStatus.ERROR)
             return redirect('order:payment', token=order.token)
     template = 'order/payment/%s.html' % variant
-    return TemplateResponse(request, [template, 'order/payment/default.html'],
-                            {'form': form, 'payment': payment})
+    ctx = {'form': form, 'payment': payment}
+    return TemplateResponse(
+        request, [template, 'order/payment/default.html'], ctx)
 
 
 @check_order_status
@@ -124,39 +123,51 @@ def cancel_payment(request, order):
     return HttpResponseForbidden()
 
 
-def create_password(request, token):
-    if request.user.is_authenticated:
-        return redirect('order:details', token=token)
+def checkout_success(request, token):
+    """Redirects user to checkout success page or, in case of successful
+    registration, to order details page with attaching order to an account."""
     order = get_object_or_404(Order, token=token)
     email = order.user_email
+    ctx = {'email': email, 'order': order}
+    if request.user.is_authenticated:
+        return TemplateResponse(request, 'order/checkout_success.html', ctx)
     form_data = request.POST.copy()
     if form_data:
         form_data.update({'email': email})
     register_form = PasswordForm(form_data or None)
-    if User.objects.filter(email=email).exists():
-        login_form = LoginForm(initial={'login': email})
-    else:
-        login_form = None
     if register_form.is_valid():
         register_form.save()
         password = register_form.cleaned_data.get('password')
-        user = auth.authenticate(request=request, email=email,
-                                 password=password)
+        user = auth.authenticate(
+            request=request, email=email, password=password)
         auth.login(request, user)
         attach_order_to_user(order, user)
         return redirect('order:details', token=token)
-    ctx = {'form': register_form, 'email': email, 'order': order,
-           'login_form': login_form}
-    return TemplateResponse(request, 'order/create_password.html', ctx)
+    user_exists = User.objects.filter(email=email).exists()
+    login_form = LoginForm(
+        initial={'username': email}) if user_exists else None
+    ctx.update({'form': register_form, 'login_form': login_form})
+    return TemplateResponse(
+        request, 'order/checkout_success_anonymous.html', ctx)
 
 
 @login_required
 def connect_order_with_user(request, token):
-    order = get_object_or_404(
-        Order.objects.filter(user_email=request.user.email, token=token))
+    """Connects newly created order to authenticated user."""
+    try:
+        order = Order.objects.get(user_email=request.user.email, token=token)
+    except Order.DoesNotExist:
+        order = None
+    if not order:
+        msg = pgettext_lazy(
+            'Connect order with user warning message',
+            'We couldn\'t assign the order to your account as the email'
+            ' addresses don\'t match')
+        messages.warning(request, msg)
+        return redirect('profile:details')
     attach_order_to_user(order, request.user)
-    messages.success(
-        request, pgettext_lazy(
-            'storefront message',
-            'You\'ve successfully connected order with your account'))
+    msg = pgettext_lazy(
+        'storefront message',
+        'The order is now assigned to your account')
+    messages.success(request, msg)
     return redirect('order:details', token=order.token)


Test patch:
diff --git a/tests/test_checkout_integration.py b/tests/test_checkout_integration.py
--- a/tests/test_checkout_integration.py
+++ b/tests/test_checkout_integration.py
@@ -63,7 +63,7 @@ def test_checkout_flow(request_cart_with_item, client, shipping_method):
     payment_response = client.post(payment_page_url, data=payment_data)
     assert payment_response.status_code == 302
     order_password = reverse(
-        'order:create-password', kwargs={'token': order.token})
+        'order:checkout-success', kwargs={'token': order.token})
     assert get_redirect_location(payment_response) == order_password
 
 
@@ -117,7 +117,7 @@ def test_checkout_flow_authenticated_user(
 
     assert payment_response.status_code == 302
     order_password = reverse(
-        'order:create-password', kwargs={'token': order.token})
+        'order:checkout-success', kwargs={'token': order.token})
     assert get_redirect_location(payment_response) == order_password
 
 
@@ -347,7 +347,7 @@ def test_create_user_after_order(order, client):
     order.save()
     assert not User.objects.filter(email='hello@mirumee.com').exists()
     data = {'password': 'password'}
-    url = reverse('order:create-password', kwargs={'token': order.token})
+    url = reverse('order:checkout-success', kwargs={'token': order.token})
     response = client.post(url, data=data)
     redirect_location = get_redirect_location(response)
     detail_url = reverse('order:details', kwargs={'token': order.token})
diff --git a/tests/test_order.py b/tests/test_order.py
--- a/tests/test_order.py
+++ b/tests/test_order.py
@@ -1,7 +1,9 @@
+from django.urls import reverse
 from prices import Price
 
 from saleor.order import models, OrderStatus
 from saleor.order.utils import add_variant_to_delivery_group
+from tests.utils import get_redirect_location
 
 
 def test_total_property():
@@ -101,3 +103,30 @@ def test_order_queryset_closed_orders(open_orders, closed_orders):
     qs = models.Order.objects.closed()
     assert qs.count() == len(closed_orders)
     assert all([item in qs for item in closed_orders])
+
+
+def test_view_connect_order_with_user_authorized_user(
+        order, authorized_client, customer_user):
+    order.user_email = customer_user.email
+    order.save()
+
+    url = reverse(
+        'order:connect-order-with-user', kwargs={'token': order.token})
+    response = authorized_client.post(url)
+
+    redirect_location = get_redirect_location(response)
+    assert redirect_location == reverse('order:details', args=[order.token])
+    order.refresh_from_db()
+    assert order.user == customer_user
+
+
+def test_view_connect_order_with_user_different_email(
+        order, authorized_client):
+    url = reverse(
+        'order:connect-order-with-user', kwargs={'token': order.token})
+    response = authorized_client.post(url)
+
+    redirect_location = get_redirect_location(response)
+    assert redirect_location == reverse('profile:details')
+    order.refresh_from_db()
+    assert order.user is None