Repository: saleor/saleor
Pull request #17582: Prevent race condition when creating customers
Author: login: lkostrowski
Labels: name: skip changelog
Created: 2025-04-08T06:10:43Z | Merged: 2025-04-15T06:48:15Z

I want to merge this change because it fixes race condition with CustomerCreate

It's ported solution from #17380

<!-- Please mention all relevant issue numbers. -->
<!-- GitHub issue number is required for external contributions. -->

# Impact

- [ ] New migrations
- [ ] New/Updated API fields or mutations
- [ ] Deprecated API fields or mutations
- [ ] Removed API types, fields, or mutations

# Docs

<!-- Docs are stored in a separate repository: https://github.com/saleor/saleor-docs/. -->
<!-- Please provide a link to the PR that updates documentation for your changes. -->
<!-- If changes in docs are not required, please mention that in the description. -->

- [ ] Link to documentation:

# Pull Request Checklist

<!-- Please keep this section. It will make the maintainer's life easier. -->

- [ ] Privileged queries and mutations are either absent or guarded by proper permission checks
- [ ] Database queries are optimized and the number of queries is constant
- [ ] Database migrations are either absent or optimized for zero downtime
- [ ] The changes are covered by test cases
- [ ] All new fields/inputs/mutations have proper labels added (`ADDED_IN_X`, `PREVIEW_FEATURE`, etc.)
- [ ] All migrations have proper dependencies
- [ ] All indexes are added concurrently in migrations
- [ ] All RunSql and RunPython migrations have revert option defined



Diff:
diff --git a/saleor/graphql/account/mutations/account/account_update.py b/saleor/graphql/account/mutations/account/account_update.py
index d0eabc5a44d..d3658a8e1f6 100644
--- a/saleor/graphql/account/mutations/account/account_update.py
+++ b/saleor/graphql/account/mutations/account/account_update.py
@@ -100,9 +100,7 @@ def perform_mutation(cls, root, info: ResolveInfo, /, **data):
     def save(cls, info: ResolveInfo, instance: models.User, cleaned_input):
         manager = get_plugin_manager_promise(info.context).get()
 
-        cls.save_default_addresses(
-            cleaned_input=cleaned_input, user_instance=instance, save_user=False
-        )
+        cls.save_default_addresses(cleaned_input=cleaned_input, user_instance=instance)
 
         instance.search_document = prepare_user_search_document_value(instance)
         instance.save()
diff --git a/saleor/graphql/account/mutations/base.py b/saleor/graphql/account/mutations/base.py
index b5f6fe1760f..89bf03bc5df 100644
--- a/saleor/graphql/account/mutations/base.py
+++ b/saleor/graphql/account/mutations/base.py
@@ -20,9 +20,7 @@
 from ...account.types import Address, AddressInput, User
 from ...app.dataloaders import get_app_promise
 from ...core import ResolveInfo, SaleorContext
-from ...core.descriptions import (
-    DEPRECATED_IN_3X_INPUT,
-)
+from ...core.descriptions import DEPRECATED_IN_3X_INPUT
 from ...core.doc_category import DOC_CATEGORY_USERS
 from ...core.enums import LanguageCodeEnum
 from ...core.mutations import DeprecatedModelMutation, ModelDeleteMutation
@@ -355,15 +353,14 @@ def post_save_action(cls, info: ResolveInfo, instance, cleaned_input):
                 mark_gift_cards_search_index_as_dirty(user_gift_cards)
 
     @classmethod
-    def save_default_addresses(
-        cls, *, cleaned_input: dict, user_instance: models.User, save_user: bool
-    ):
+    def save_default_addresses(cls, *, cleaned_input: dict, user_instance: models.User):
         default_shipping_address: models.Address | None = cleaned_input.get(
             SHIPPING_ADDRESS_FIELD
         )
 
         if default_shipping_address:
             default_shipping_address.save()
+            user_instance.addresses.add(default_shipping_address)
             user_instance.default_shipping_address = default_shipping_address
 
         default_billing_address: models.Address | None = cleaned_input.get(
@@ -372,15 +369,8 @@ def save_default_addresses(
 
         if default_billing_address:
             default_billing_address.save()
-            user_instance.default_billing_address = default_billing_address
-
-        if save_user:
-            user_instance.save()
-
-        if default_billing_address:
             user_instance.addresses.add(default_billing_address)
-        if default_shipping_address:
-            user_instance.addresses.add(default_shipping_address)
+            user_instance.default_billing_address = default_billing_address
 
 
 class UserDeleteMixin:
diff --git a/saleor/graphql/account/mutations/staff/customer_create.py b/saleor/graphql/account/mutations/staff/customer_create.py
index 5c82b19eacb..0ffb020939b 100644
--- a/saleor/graphql/account/mutations/staff/customer_create.py
+++ b/saleor/graphql/account/mutations/staff/customer_create.py
@@ -1,6 +1,8 @@
 from urllib.parse import urlencode
 
 from django.contrib.auth.tokens import default_token_generator
+from django.core.exceptions import ValidationError
+from django.db import IntegrityError, transaction
 
 from .....account import events as account_events
 from .....account import models
@@ -19,7 +21,7 @@
 from ....core.types import AccountError
 from ....core.utils import WebhookEventInfo
 from ....plugins.dataloaders import get_plugin_manager_promise
-from ..base import BaseCustomerCreate
+from ..base import BILLING_ADDRESS_FIELD, SHIPPING_ADDRESS_FIELD, BaseCustomerCreate
 
 
 class CustomerCreate(BaseCustomerCreate):
@@ -53,17 +55,68 @@ class Meta:
             ),
         ]
 
+    @classmethod
+    def _save(cls, instance):
+        """Prevent race condition when saving customer.
+
+        This is a hacky solution that catches DB error, to deduct that entity was
+        created before.
+
+        Normally we would use get_or_create here, but it will lose reference -
+        get_or_create returns new instance of model, but original "instance" is being
+        returned in mutation.
+
+        This limitation comes from the Base/Model mutation. We can solve this e.g. by
+        returning actual instance from save()
+
+        https://linear.app/saleor/issue/EXT-2162
+        """
+        with transaction.atomic():
+            try:
+                with transaction.atomic():
+                    instance.save()
+            except IntegrityError:
+                try:
+                    # Verify if object already exists in DB.
+                    # If yes, it means we have a race-condition
+                    # This eventually leads to ValidationError because this user
+                    # already exists
+                    models.User.objects.get(email=instance.email)
+
+                    raise ValidationError(
+                        {
+                            # This validation error mimics built-in validation error
+                            # So graphQL response is the same
+                            "email": ValidationError(
+                                "User with this Email already exists.",
+                                code=AccountErrorCode.UNIQUE.value,
+                            )
+                        }
+                    )
+                except instance.DoesNotExist:
+                    pass
+                raise
+
     @classmethod
     @traced_atomic_transaction()
     def save(cls, info: ResolveInfo, instance, cleaned_input):
-        manager = get_plugin_manager_promise(info.context).get()
+        addresses_to_set_on_user = []
+        if default_shipping_address := cleaned_input.get(SHIPPING_ADDRESS_FIELD):
+            addresses_to_set_on_user.append(default_shipping_address)
+            default_shipping_address.save()
+        if default_billing_address := cleaned_input.get(BILLING_ADDRESS_FIELD):
+            addresses_to_set_on_user.append(default_billing_address)
+            default_billing_address.save()
 
-        cls.save_default_addresses(
-            cleaned_input=cleaned_input, user_instance=instance, save_user=True
-        )
+        cls._save(instance)
+
+        if addresses_to_set_on_user:
+            instance.addresses.set(addresses_to_set_on_user)
+
+        manager = get_plugin_manager_promise(info.context).get()
 
         instance.search_document = prepare_user_search_document_value(instance)
-        instance.save(update_fields=["search_document"])
+        instance.save()
 
         cls.call_event(manager.customer_created, instance)
         account_events.customer_account_created_event(user=instance)
diff --git a/saleor/graphql/account/mutations/staff/customer_update.py b/saleor/graphql/account/mutations/staff/customer_update.py
index 6d349d32b28..bbb41701dd2 100644
--- a/saleor/graphql/account/mutations/staff/customer_update.py
+++ b/saleor/graphql/account/mutations/staff/customer_update.py
@@ -21,10 +21,7 @@
 from ....core.utils import WebhookEventInfo
 from ....meta.inputs import MetadataInput
 from ....plugins.dataloaders import get_plugin_manager_promise
-from ..base import (
-    BaseCustomerCreate,
-    CustomerInput,
-)
+from ..base import BaseCustomerCreate, CustomerInput
 
 
 class CustomerUpdate(BaseCustomerCreate, ModelWithExtRefMutation):
@@ -173,7 +170,8 @@ def save(cls, info: ResolveInfo, instance, cleaned_input):
         manager = get_plugin_manager_promise(info.context).get()
 
         cls.save_default_addresses(
-            cleaned_input=cleaned_input, user_instance=instance, save_user=False
+            cleaned_input=cleaned_input,
+            user_instance=instance,
         )
 
         instance.search_document = prepare_user_search_document_value(instance)
diff --git a/saleor/graphql/account/tests/mutations/staff/test_customer_create.py b/saleor/graphql/account/tests/mutations/staff/test_customer_create.py
index 310977024a5..0987b6d5451 100644
--- a/saleor/graphql/account/tests/mutations/staff/test_customer_create.py
+++ b/saleor/graphql/account/tests/mutations/staff/test_customer_create.py
@@ -3,7 +3,7 @@
 
 from ......account import events as account_events
 from ......account.error_codes import AccountErrorCode
-from ......account.models import User
+from ......account.models import Address, User
 from ......account.notifications import get_default_user_payload
 from ......account.search import (
     generate_address_search_document_value,
@@ -12,6 +12,7 @@
 from ......core.notify import NotifyEventType
 from ......core.tests.utils import get_site_context_payload
 from ......core.utils.url import prepare_url
+from ......tests import race_condition
 from .....tests.utils import get_graphql_content
 from ....tests.utils import convert_dict_keys_to_camel_case
 
@@ -554,3 +555,55 @@ def test_customer_create_webhook_event_triggered(
     # then
     User.objects.get(email=email)
     mocked_trigger_webhooks_async.assert_called()
+
+
+def test_customer_create_race_condition(
+    staff_api_client, site_settings, permission_manage_users, address
+):
+    """Context.
+
+    This test checks case when two concurrent mutations fail,
+    due to unique constraint on email field. In race-condition scenario it's possible
+    that two calls will pass validation (user doesn't exist yet), but the second one
+    will fail due to DB having a user created already.
+    """
+
+    # given
+    site_settings.enable_account_confirmation_by_email = False
+    site_settings.save(update_fields=["enable_account_confirmation_by_email"])
+
+    email_to_create = "test-user@example.com"
+
+    address_data = convert_dict_keys_to_camel_case(address.as_data())
+    address_data.pop("privateMetadata")
+    address_data.pop("validationSkipped")
+
+    variables = {
+        "shipping": address_data,
+        "billing": address_data,
+        "email": email_to_create,
+        "firstName": "api_first_name",
+        "lastName": "api_last_name",
+    }
+
+    def create_existing_customer(*args, **kwargs):
+        User.objects.create(email=email_to_create)
+
+    with race_condition.RunBefore(
+        # CustomerCreate.save,
+        "saleor.graphql.account.mutations.staff.customer_create.CustomerCreate._save",
+        create_existing_customer,
+    ):
+        response = staff_api_client.post_graphql(
+            CUSTOMER_CREATE_MUTATION, variables, permissions=[permission_manage_users]
+        )
+
+        content = get_graphql_content(response)
+
+        errors_list = content["data"]["customerCreate"]["errors"]
+
+        assert len(errors_list) == 1
+        assert errors_list[0]["code"] == "UNIQUE"
+
+        # make sure that addresses were not saved.
+        assert not Address.objects.exclude(id=address.id).exists()
diff --git a/saleor/tests/race_condition.py b/saleor/tests/race_condition.py
index b2feb9f9420..7a9cd8af8c1 100644
--- a/saleor/tests/race_condition.py
+++ b/saleor/tests/race_condition.py
@@ -11,6 +11,14 @@ def __init__(self, target, callback):
     def __enter__(self):
         self.patched_function = patch(self.target)
         original_function, _ = self.patched_function.get_original()
+        if type(original_function) is classmethod:
+            # classmethod needs to have an initialized class. get_original
+            # returns the direct handler to the classmethod. Calling it without class
+            # produces "'classmethod' object is not callable" error
+            target_class = self.patched_function.getter()
+            target_method = self.patched_function.attribute
+            original_function = getattr(target_class, target_method)
+
         self.patched_function.new = self.wrap(original_function)
         return self.patched_function.__enter__()
 


Reviews:
user: login: korycins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T05:21:26Z
user: login: fowczarek
body: Issue (blocking): Missing tests form orgianl PR. 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T09:27:02Z
user: login: lkostrowski
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T09:34:32Z
user: login: korycins
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T13:16:49Z
user: login: fowczarek
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T10:25:18Z

Review comments:
diff_hunk: @@ -63,7 +65,44 @@ def save(cls, info: ResolveInfo, instance, cleaned_input):
         )
 
         instance.search_document = prepare_user_search_document_value(instance)
-        instance.save(update_fields=["search_document"])
+
+        """
+        Prevent race condition when saving customer.
+
+        This is a hacky solution that catches DB error, to deduct that entity was created before.
+
+        Normally we would use get_or_create here, but it will lose reference - get_or_create
+        returns new instance of model, but original "instance" is being returned in mutation.
+
+        This limitation comes from the Base/Model mutation. We can solve this e.g. by returning
+        actual instance from save()
+
+        https://linear.app/saleor/issue/EXT-2162
+        """
+        try:
+            with transaction.atomic():
+                instance.save()
path: saleor/graphql/account/mutations/staff/customer_create.py
user: login: korycins
body: You have:

```python
cls.save_default_addresses(
            cleaned_input=cleaned_input, user_instance=instance, save_user=True
        )
```

This is called first, meaning the instance will always be saved to the DB before your logic is executed. It also opens up the potential for a race condition between `save_default_addresses` and a different `create/register-user` mutation.

To avoid this, your newly added logic should be executed first, followed by the actions on the addresses. 

I also recommend wrapping your new logic inside a method, like `_save`, to keep the "hack" contained in one place. This will make it easier to replace with `get_or_create` once `BaseMutation.save` is fixed.
created_at: 2025-04-09T05:21:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -106,4 +106,5 @@ All notable, unreleased changes to this project will be documented in this file.
   certain types of data. - #17506 by @NyanKiyoshi
 - Improve status calculation for orders with waiting-for-approval fulfillments - #17471 by @delemeator
 - Allow to change Admin email plugin custom templates back to default - #17563 by @wcislo-saleor
+- Prevent possible race condition when running CustomerCreate mutation. Now second call will return ValidationError - #17582 by @lkostrowski
path: CHANGELOG.md
user: login: fowczarek
body: Issue (non-blocking): We don't need an entry in changlog. This bug never appeared in 3.21 and was fixed in 3.20. 
created_at: 2025-04-09T09:23:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -106,4 +106,5 @@ All notable, unreleased changes to this project will be documented in this file.
   certain types of data. - #17506 by @NyanKiyoshi
 - Improve status calculation for orders with waiting-for-approval fulfillments - #17471 by @delemeator
 - Allow to change Admin email plugin custom templates back to default - #17563 by @wcislo-saleor
+- Prevent possible race condition when running CustomerCreate mutation. Now second call will return ValidationError - #17582 by @lkostrowski
path: CHANGELOG.md
user: login: lkostrowski
body: Right!
created_at: 2025-04-09T09:34:32Z
author_association: MEMBER

PR comments:
user: login: db-queries[bot]
created_at: 2025-04-08T11:54:26Z
body: 
Here is the report for 3dc466e563073174669639dac1ab342a85a131cd (saleor:EXT-1968-3.21)
Base comparison is e5ff65b54ecd6a7152675ab953ae5315a7304fa7.
<details>
<summary>No differences were found.</summary>
<p>

```diff
# saleor.graphql.accountbenchmark account
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  addresses for federation query count                                    	         11	         11	              2
  customers query                                                         	         53	         53	              0
  delete staff members                                                    	         42	         42	              0
  query staff user                                                        	         24	         24	              3
  staff create                                                            	         24	         24	              3
  staff update groups and permissions                                     	         37	         37	              6
  users for federation query count                                        	         10	         10	              3

# saleor.graphql.accountbenchmark permission group
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  groups for federation query count                                       	          3	          3	              1
  permission group create                                                 	         26	         26	              0
  permission group delete                                                 	         31	         31	              3
  permission group query                                                  	         16	         16	              0
  permission group update                                                 	         45	         45	              2
  permission group update remove users with manage staff                  	         37	         37	              3

# saleor.graphql.appbenchmarks app extensions
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  app extensions                                                          	         27	         27	             10
  app extensions with filter[filter0]                                     	         26	         26	             10
  app extensions with filter[filter1]                                     	         14	         14	              2
  app extensions with filter[filter2]                                     	         26	         26	             10
  app extensions with filter[filter3]                                     	         14	         14	              2

# saleor.graphql.appbenchmarks apps
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  apps for federation query count                                         	         11	         11	              3
  apps with tokens and webhooks                                           	         11	         11	              0

# saleor.graphql.attributebenchmark attribute
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  attribute translation                                                   	          8	          8	              0
  attribute value translation                                             	         22	         22	              0
  query attribute                                                         	          8	          8	              0
  query attributes                                                        	         10	         10	              0

# saleor.graphql.channelbenchmark channel
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  channels query                                                          	          7	          7	              0

# saleor.graphql.channelmutations channel update
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  channel update mutation remove warehouse                                	         41	         41	              6

# saleor.graphql.checkoutbenchmark checkout mutations
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  add billing address to checkout                                         	         74	         74	              7
  add checkout lines                                                      	        105	        105	             15
  add checkout lines catalogue discount applies                           	        202	        202	             16
  add checkout lines gift discount applies                                	        166	        166	             24
  add checkout lines multiple catalogue discount applies                  	        217	        217	             23
  add checkout lines order discount applies                               	        179	        179	             19
  add checkout lines with external shipping                               	        186	        186	             73
  add checkout lines with reservations                                    	        215	        215	             78
  add delivery to checkout                                                	         88	         88	             10
  add shipping to checkout                                                	         90	         90	             15
  checkout customer note update                                           	         53	         53	              4
  checkout email update                                                   	         52	         52	              3
  checkout payment charge                                                 	         69	         69	             19
  checkout shipping address update                                        	         85	         85	              9
  checkout voucher code                                                   	         94	         94	             13
  complete checkout                                                       	        204	        204	             59
  complete checkout preorder                                              	        215	        215	             63
  complete checkout with digital line                                     	        243	        243	             65
  complete checkout with out of stock webhook                             	        206	        206	             59
  complete checkout with single line                                      	        206	        206	             59
  create checkout                                                         	         81	         81	              5
  create checkout with gift promotion                                     	        118	        118	             14
  create checkout with order promotion                                    	        105	        105	              5
  create checkout with reservations                                       	        181	        181	             37
  customer complete checkout                                              	        217	        217	             65
  customer complete checkout for cc                                       	        242	        242	             74
  update checkout lines                                                   	         95	         95	             15
  update checkout lines with reservations                                 	        228	        228	             91

# saleor.graphql.checkoutbenchmark checkouts
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  staff multiple checkouts                                                	          8	          8	              0

# saleor.graphql.checkoutbenchmark homepage
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  user checkout details                                                   	         65	         65	              5
  user checkout details with tax app                                      	         98	         98	             33

# saleor.graphql.discountbenchmark promotion create
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotion create                                                        	         25	         25	              1
  promotion create order promotion                                        	         39	         39	              7

# saleor.graphql.discountbenchmark promotion delete
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  gift promotion delete                                                   	         27	         27	              1
  promotion delete                                                        	         32	         32	              1

# saleor.graphql.discountbenchmark promotion rule create
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotion rule create                                                   	         25	         25	              1
  promotion rule create gift                                              	        135	        135	              7

# saleor.graphql.discountbenchmark promotion rule delete
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotion rule delete                                                   	         25	         25	              0

# saleor.graphql.discountbenchmark promotion rule update
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotion rule update                                                   	         40	         40	              0

# saleor.graphql.discountbenchmark promotion update
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotion update                                                        	         18	         18	              0

# saleor.graphql.discountbenchmark promotions
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  promotions querytest promotions query                                   	         11	         11	              0

# saleor.graphql.discountbenchmark sales
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  sales query with channel slug                                           	         11	         11	              0
  sales query without channel slug                                        	          9	          9	              0

# saleor.graphql.discountbenchmark voucher code bulk delete
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  voucher code bulk delete queries                                        	         22	         22	              3

# saleor.graphql.discountbenchmark vouchers
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  vouchers query with channel slug                                        	         22	         22	              0
  vouchers query withot channel slug                                      	         21	         21	              0

# saleor.graphql.giftcardbenchmark gift card mutations
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  create never expiry gift card                                           	         29	         29	              4
  gift card bulk activate by staff                                        	         17	         17	              3
  update gift card                                                        	         37	         37	              4

# saleor.graphql.giftcardbenchmark gift card queries
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  filter gift cards by products                                           	         16	         16	              3
  filter gift cards by tags                                               	         14	         14	              3
  filter gift cards by used by user                                       	         15	         15	              3
  query gift card details                                                 	         16	         16	              3
  query gift cards                                                        	         14	         14	              3

# saleor.graphql.orderbenchmark fulfillment
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  fulfillment query                                                       	          8	          8	              0

# saleor.graphql.orderbenchmark fulfillment refund and return products
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  fulfillment refund products order lines                                 	         50	         50	              0
  fulfillment return products order lines                                 	         95	         95	              3

# saleor.graphql.orderbenchmark order
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  staff multiple draft orders                                             	          9	          9	              0
  staff multiple orders                                                   	          9	          9	              0
  staff order details                                                     	         54	         54	              7
  user order details                                                      	         51	         51	              8

# saleor.graphql.orderbenchmark order bulk create
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  order bulk create                                                       	         24	         24	              2

# saleor.graphql.orderbenchmark order fulfill
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  order fulfill                                                           	         15	         15	              0
  order fulfill with gift cards                                           	         15	         15	              0

# saleor.graphql.orderbenchmark order lines create
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  order lines create                                                      	        100	        100	             17
  order lines create variants on promotion                                	        161	        161	             22

# saleor.graphql.pagebenchmark page type
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  page types for federation query count                                   	          3	          3	              0
  query page type                                                         	         20	         20	              6
  query page types                                                        	         27	         27	             12

# saleor.graphql.paymentbenchmark payment transactions
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  payment transactions                                                    	         26	         26	              0

# saleor.graphql.productbenchmark category
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  categories children                                                     	          4	          4	              1
  categories for federation query count                                   	          3	          3	              0
  category delete                                                         	         32	         32	              3
  category view                                                           	         27	         27	              1

# saleor.graphql.productbenchmark collection
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  collection add products                                                 	         24	         24	              5
  collection bulk delete                                                  	         29	         29	              4
  collection view                                                         	          7	          7	              0
  collections for federation query count                                  	          4	          4	              1
  create collection                                                       	         25	         25	              6
  delete collection                                                       	         28	         28	              5
  remove products from collection                                         	         21	         21	              5
  retrieve collection channel listings                                    	          7	          7	              0

# saleor.graphql.productbenchmark homepage
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  report product sales                                                    	         12	         12	              3
  retrieve product list                                                   	          4	          4	              0

# saleor.graphql.productbenchmark product
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  filter products by attributes                                           	          7	          7	              0
  filter products by boolean attributes                                   	         14	         14	              0
  filter products by gift card                                            	         14	         14	              1
  filter products by numeric attributes                                   	         13	         13	              0
  product create                                                          	         45	         45	              3
  product details                                                         	         39	         39	              2
  product translations                                                    	          5	          5	              0
  products for federation query count                                     	          6	          6	              2
  products media for federation query count                               	          5	          5	              0
  products types for federation query count                               	          2	          2	              0
  retrieve channel listings                                               	         22	         22	              0
  retrieve product attributes                                             	          8	          8	              0
  retrieve product images                                                 	          4	          4	              0
  retrieve product media                                                  	          4	          4	              0
  retrive products with product types and attributes                      	          7	          7	              0
  update product                                                          	         59	         59	              4

# saleor.graphql.productbenchmark product variant channel listing update
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  variant channel listing update                                          	         37	         37	              3

# saleor.graphql.productbenchmark variant
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  product variant bulk create                                             	         47	         47	              1
  product variant create                                                  	         63	         63	              3
  products variants for federation query count                            	          6	          6	              2
  retrieve variant list                                                   	         33	         33	              1
  update product variant                                                  	         61	         61	              6

# saleor.graphql.productbenchmark variant stocks
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  product variants stocks create                                          	         27	         27	              4
  product variants stocks create with single webhook called               	         27	         27	              4
  product variants stocks delete by id                                    	         32	         32	              4
  product variants stocks delete by sku                                   	         32	         32	              4
  product variants stocks delete with out of stock webhook many calls     	         28	         28	              3
  product variants stocks update by sku                                   	         30	         30	              4
  product variants stocks update byid                                     	         30	         30	              4
  query product variants stocks                                           	         12	         12	              0

# saleor.graphql.producttest product sorting attributes
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  sort product not having attribute data                                  	         20	         20	              0

# saleor.graphql.shippingbenchmark shipping methods
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  vouchers query with channel slug                                        	          9	          9	              0
  vouchers query without channel slug                                     	          9	          9	              0

# saleor.graphql.shippingbenchmark shipping zones
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  shipping zones query                                                    	          8	          8	              0

# saleor.graphql.shopbenchmark homepage
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  retrieve shop                                                           	          2	          2	              0

# saleor.graphql.warehousebenchmark stock bulk update
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  stocks bulk update queries count                                        	         33	         33	              6

# saleor.graphql.warehousebenchmark stocks
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  stocks query                                                            	          7	          7	              0

# saleor.graphql.webhookbenchmark webhook events
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  webhooks                                                                	          8	          8	              0

# saleor.orderbenchmark fetch order prices
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  fetch order prices                                                      	         28	         28	              2

# saleor.ordertest fetch
  test name                                                               	left count 	right count	duplicate count
  ------------------------------------------------------------------------	-----------	-----------	---------------
  fetch draft order lines info                                            	         16	         16	              1
  fetch draft order lines info extended                                   	         23	         23	              3

```

</p>
</details>

author_association: NONE
user: login: codecov[bot]
created_at: 2025-04-08T11:54:52Z
body: ## [Codecov](https://app.codecov.io/gh/saleor/saleor/pull/17582?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) Report
Attention: Patch coverage is `94.11765%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 96.46%. Comparing base [(`e5ff65b`)](https://app.codecov.io/gh/saleor/saleor/commit/e5ff65b54ecd6a7152675ab953ae5315a7304fa7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) to head [(`3dc466e`)](https://app.codecov.io/gh/saleor/saleor/commit/3dc466e563073174669639dac1ab342a85a131cd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).
> Report is 14 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/saleor/saleor/pull/17582?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) | Patch % | Lines |
|---|---|---|
| [...graphql/account/mutations/staff/customer\_create.py](https://app.codecov.io/gh/saleor/saleor/pull/17582?src=pr&el=tree&filepath=saleor%2Fgraphql%2Faccount%2Fmutations%2Fstaff%2Fcustomer_create.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor#diff-c2FsZW9yL2dyYXBocWwvYWNjb3VudC9tdXRhdGlvbnMvc3RhZmYvY3VzdG9tZXJfY3JlYXRlLnB5) | 92.85% | [2 Missing :warning: ](https://app.codecov.io/gh/saleor/saleor/pull/17582?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main   #17582      +/-   ##
==========================================
- Coverage   96.47%   96.46%   -0.01%     
==========================================
  Files         958      962       +4     
  Lines       76782    76933     +151     
==========================================
+ Hits        74078    74217     +139     
- Misses       2704     2716      +12     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/saleor/saleor/pull/17582?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE