Repository: saleor/saleor

Issue:
Associate guest checkout with an account if it exists
### What I'm trying to achieve
I have an active customer account at Saleor. When I place an order as a guest but use my account's email, the order is not associated with the account. If the email is confirmed (email confirmation is enabled), my order should be automatically associated with my account.

### Describe a proposed solution
Adjust the `complete_checkout` function to assign the user account if the checkout's email is a valid customer account in Saleor.

### Other solutions I've tried and won't work
…

### Screenshots or mockups
<!-- Please provide any illustrations that could help others understand the problem or the proposed solution. -->



Discussion:

Resolving patch:
diff --git a/saleor/checkout/complete_checkout.py b/saleor/checkout/complete_checkout.py
--- a/saleor/checkout/complete_checkout.py
+++ b/saleor/checkout/complete_checkout.py
@@ -23,7 +23,7 @@
 
 from ..account.error_codes import AccountErrorCode
 from ..account.models import User
-from ..account.utils import store_user_address
+from ..account.utils import retrieve_user_by_email, store_user_address
 from ..channel import MarkAsPaidStrategy
 from ..checkout import CheckoutAuthorizeStatus, calculations
 from ..checkout.error_codes import CheckoutErrorCode
@@ -1275,6 +1275,14 @@ def complete_checkout(
     metadata_list: Optional[List] = None,
     private_metadata_list: Optional[List] = None,
 ) -> Tuple[Optional[Order], bool, dict]:
+    # Assign checkout user to an existing user if checkout email matches a valid
+    #  customer account
+    if user is None and checkout_info.checkout.email:
+        existing_user = retrieve_user_by_email(checkout_info.checkout.email)
+        checkout_info.user = (
+            existing_user if existing_user and existing_user.is_active else None
+        )
+
     transactions = checkout_info.checkout.payment_transactions.all()
     fetch_checkout_data(checkout_info, manager, lines)
 


Test patch:
diff --git a/saleor/checkout/tests/test_associate_checkout_with_account.py b/saleor/checkout/tests/test_associate_checkout_with_account.py
new file mode 100644
--- /dev/null
+++ b/saleor/checkout/tests/test_associate_checkout_with_account.py
@@ -0,0 +1,121 @@
+import pytest
+
+from saleor.channel import MarkAsPaidStrategy
+from saleor.checkout.complete_checkout import complete_checkout
+
+from ...plugins.manager import get_plugins_manager
+from ..fetch import fetch_checkout_info, fetch_checkout_lines
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize(
+    "paid_strategy",
+    [
+        MarkAsPaidStrategy.TRANSACTION_FLOW,
+        MarkAsPaidStrategy.PAYMENT_FLOW,
+    ],
+)
+def test_associate_guest_checkout_with_account_if_exists(
+    paid_strategy, app, address, checkout, customer_user
+):
+    # set the checkout email
+    checkout.email = "test@example.com"
+    checkout.save()
+    user = None
+    manager = get_plugins_manager()
+    lines, _ = fetch_checkout_lines(checkout)
+    checkout_info = fetch_checkout_info(checkout, lines, [], manager)
+    checkout_info.billing_address = address
+    checkout_info.channel.order_mark_as_paid_strategy == paid_strategy
+
+    # call the complete_checkout function with the checkout object
+    order, _, _ = complete_checkout(
+        checkout_info=checkout_info,
+        manager=manager,
+        lines=lines,
+        payment_data={},
+        store_source=False,
+        user=user,
+        app=app,
+    )
+
+    # assert that the order is associated with the correct user
+    assert order.user == customer_user
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize(
+    "paid_strategy",
+    [
+        MarkAsPaidStrategy.TRANSACTION_FLOW,
+        MarkAsPaidStrategy.PAYMENT_FLOW,
+    ],
+)
+def test_associate_guest_checkout_with_account_if_exists_with_guest_user(
+    paid_strategy,
+    app,
+    address,
+    checkout,
+):
+    # set the checkout email
+    checkout.email = "guest@email.com"
+    checkout.save()
+    user = None
+    manager = get_plugins_manager()
+    lines, _ = fetch_checkout_lines(checkout)
+    checkout_info = fetch_checkout_info(checkout, lines, [], manager)
+    checkout_info.billing_address = address
+    checkout_info.channel.order_mark_as_paid_strategy == paid_strategy
+
+    # call the complete_checkout function with the checkout object
+    order, _, _ = complete_checkout(
+        checkout_info=checkout_info,
+        manager=manager,
+        lines=lines,
+        payment_data={},
+        store_source=False,
+        user=user,
+        app=app,
+    )
+
+    # assert that the order is associated with the correct user
+    assert order.user is None
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize(
+    "paid_strategy",
+    [
+        MarkAsPaidStrategy.TRANSACTION_FLOW,
+        MarkAsPaidStrategy.PAYMENT_FLOW,
+    ],
+)
+def test_associate_guest_checkout_with_account_if_exists_with_inactive_user(
+    paid_strategy, app, address, checkout, customer_user
+):
+    # set the checkout email
+    checkout.email = "test@example.com"
+    checkout.save()
+    # deactivate the customer user
+    customer_user.is_active = False
+    customer_user.save()
+    user = None
+    manager = get_plugins_manager()
+    lines, _ = fetch_checkout_lines(checkout)
+    checkout_info = fetch_checkout_info(checkout, lines, [], manager)
+    checkout_info.billing_address = address
+    checkout_info.channel.order_mark_as_paid_strategy == paid_strategy
+
+    # call the complete_checkout function with the checkout object
+    order, _, _ = complete_checkout(
+        checkout_info=checkout_info,
+        manager=manager,
+        lines=lines,
+        payment_data={},
+        store_source=False,
+        user=user,
+        app=app,
+    )
+
+    # assert that the order is associated with the correct user
+    assert order.user is None