internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: saleor/saleor

Issue:
Create sub-categories for random data
I noticed that the demo doesn't have any sub-categories anymore, thus we don't have access to this feature. Shouldn't we add some again? (#2056)


Discussion:
Just mentiion it here the demo doesn't have sales neither.
We can re-iterate on the products catalog and introduce some subcategories. They were removed when we made new products for Saleor 2.0 release. It wasn't intentional, I think we just forgot that we can have them 😉 
@timcla Maybe you'd had some time to take a look at the current structure and propose some subcategories?

(Current...)

ACCESSORIES
Homewares 
Paints

APPAREL
T-shirts
Polo shirts
Hoodies
Footwear

GROCERIES
Juices
Alcohol

----

(planned...)

ACCESSORIES
Homewares 
Paints
Music
Mugs

APPAREL
T-shirts
Polo shirts
Hoodies
Footwear

GROCERIES
Juices
Alcohol
Coffee
Tea
Oil


This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


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
@@ -11,6 +11,7 @@
 from django.conf import settings
 from django.contrib.sites.models import Site
 from django.core.files import File
+from django.db.models import Q
 from django.utils import timezone
 from faker import Factory
 from faker.providers import BaseProvider
@@ -140,9 +141,14 @@ def create_categories(categories_data, placeholder_dir):
     for category in categories_data:
         pk = category["pk"]
         defaults = category["fields"]
-        image_name = CATEGORY_IMAGES[pk]
+        parent = defaults["parent"]
+        image_name = (
+            CATEGORY_IMAGES[pk] if pk in CATEGORY_IMAGES else CATEGORY_IMAGES[parent]
+        )
         background_image = get_image(placeholder_dir, image_name)
         defaults["background_image"] = background_image
+        if parent:
+            defaults["parent"] = Category.objects.get(pk=parent)
         Category.objects.update_or_create(pk=pk, defaults=defaults)
         create_category_background_image_thumbnails.delay(pk)
 
@@ -1022,11 +1028,18 @@ def generate_menu_items(menu: Menu, category: Category, parent_menu_item):
 
 
 def generate_menu_tree(menu):
-    categories = Category.tree.get_queryset().filter(products__isnull=False)
+    categories = (
+        Category.tree.get_queryset()
+        .filter(
+            Q(parent__isnull=True) & Q(products__isnull=False)
+            | Q(children__products__isnull=False)
+        )
+        .distinct()
+    )
+
     for category in categories:
-        if not category.parent_id:
-            for msg in generate_menu_items(menu, category, None):
-                yield msg
+        for msg in generate_menu_items(menu, category, None):
+            yield msg
 
 
 def create_menus():


Test patch:
Repository: saleor/saleor

Issue:
Deprecate `localized` field
Deprecate the `localized` field in the `Money` type. In the API it's enough to expose the `amount` and `currency` fields, while price formatting according to the current locale should be handled by the frontend client (we already have a library for that in our dashboard). 


Discussion:

Resolving patch:
diff --git a/saleor/graphql/core/types/money.py b/saleor/graphql/core/types/money.py
--- a/saleor/graphql/core/types/money.py
+++ b/saleor/graphql/core/types/money.py
@@ -8,7 +8,11 @@ class Money(graphene.ObjectType):
     currency = graphene.String(description="Currency code.", required=True)
     amount = graphene.Float(description="Amount of money.", required=True)
     localized = graphene.String(
-        description="Money formatted according to the current locale.", required=True
+        description="Money formatted according to the current locale.",
+        required=True,
+        deprecation_reason="DEPRECATED: Will be removed in Saleor 2.11. "
+        "Price formatting according to the current locale should be "
+        "handled by the frontend client.",
     )
 
     class Meta:


Test patch:
Repository: saleor/saleor

Issue:
Populatedb fail when running second or next time
Populatedb shouldn't failed when it's run more than ones.

### Steps to reproduce the problem
1.  Run populateb
2. Run populatedb again

### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
![image](https://user-images.githubusercontent.com/40886528/74442049-1f341780-4e71-11ea-8506-492887413b53.png)




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
@@ -585,7 +585,7 @@ def create_permission_groups():
 
 
 def create_group(name, permissions, users):
-    group = Group.objects.create(name=name)
+    group, _ = Group.objects.get_or_create(name=name)
     group.permissions.add(*permissions)
     group.user_set.add(*users)
     return group


Test patch:
Repository: saleor/saleor

Issue:
Customer shouldn't be able to query draft order. 
Customer shouldn't be able to query draft order, by id and by token. 


Discussion:

Resolving patch:
diff --git a/saleor/graphql/order/resolvers.py b/saleor/graphql/order/resolvers.py
--- a/saleor/graphql/order/resolvers.py
+++ b/saleor/graphql/order/resolvers.py
@@ -65,4 +65,8 @@ def resolve_homepage_events():
 
 
 def resolve_order_by_token(token):
-    return models.Order.objects.filter(token=token).first()
+    return (
+        models.Order.objects.exclude(status=OrderStatus.DRAFT)
+        .filter(token=token)
+        .first()
+    )


Test patch:
diff --git a/tests/api/test_order.py b/tests/api/test_order.py
--- a/tests/api/test_order.py
+++ b/tests/api/test_order.py
@@ -2027,6 +2027,36 @@ def test_authorized_access_to_order_by_token(
     assert content["data"]["orderByToken"]["user"]["id"] == customer_user_id
 
 
+def test_query_draft_order_by_token_with_requester_as_customer(
+    user_api_client, draft_order
+):
+    draft_order.user = user_api_client.user
+    draft_order.save(update_fields=["user"])
+    query = """
+    query OrderByToken($token: UUID!) {
+        orderByToken(token: $token) {
+            id
+        }
+    }
+    """
+    response = user_api_client.post_graphql(query, {"token": draft_order.token})
+    content = get_graphql_content(response)
+    assert not content["data"]["orderByToken"]
+
+
+def test_query_draft_order_by_token_as_anonymous_customer(api_client, draft_order):
+    query = """
+    query OrderByToken($token: UUID!) {
+        orderByToken(token: $token) {
+            id
+        }
+    }
+    """
+    response = api_client.post_graphql(query, {"token": draft_order.token})
+    content = get_graphql_content(response)
+    assert not content["data"]["orderByToken"]
+
+
 MUTATION_CANCEL_ORDERS = """
     mutation CancelManyOrders($ids: [ID]!, $restock: Boolean!) {
         orderBulkCancel(ids: $ids, restock: $restock) {
Repository: saleor/saleor

Issue:
The clear database command should be runnable with debug disabled
We should be able to run `cleardb` when `DEBUG=False` but we should have a `--force` flag to actually allow that action when the debug mode is turned off as it is a dangerous command.

Definition of done:
- Prints an error to stderr when `DEBUG=False` and `--force` is not passed (flagged)
- Exits with 1 (raises `SystemExit` which allows Django to handle it and cleanup the opened connections, such as the database)
- User can clear the database when debug mode is turned off and only when `--force` was passed


Discussion:

Resolving patch:
diff --git a/saleor/core/management/commands/cleardb.py b/saleor/core/management/commands/cleardb.py
--- a/saleor/core/management/commands/cleardb.py
+++ b/saleor/core/management/commands/cleardb.py
@@ -31,10 +31,16 @@ def add_arguments(self, parser):
             action="store_true",
             help="Delete staff user accounts (doesn't delete superuser accounts).",
         )
+        parser.add_argument(
+            "--force",
+            action="store_true",
+            help="Allows running the cleardb command in DEBUG=False mode.",
+        )
 
     def handle(self, **options):
-        if not settings.DEBUG:
-            raise CommandError("Cannot clear the database in DEBUG=True mode.")
+        force = options.get("force", False)
+        if not settings.DEBUG and not force:
+            raise CommandError("Cannot clear the database in DEBUG=False mode.")
 
         Checkout.objects.all().delete()
         self.stdout.write("Removed checkouts")


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -302,6 +302,11 @@ def test_cleardb_exits_with_debug_off():
         call_command("cleardb")
 
 
+@override_settings(DEBUG=False)
+def test_cleardb_passes_with_force_flag_in_debug_off():
+    call_command("cleardb", "--force")
+
+
 @override_settings(DEBUG=True)
 def test_cleardb_delete_staff_parameter(staff_user):
     # cleardb without delete_staff flag keeps staff users
Repository: saleor/saleor

Issue:
Broken multiple interface notation in schema
### What I'm trying to achieve
To use Apollo tooling to generate TS types for the application queries. However, it fails because Saleor's schema uses comma as a separator instead of ampersand. More: https://github.com/apollographql/apollo-tooling/issues/434 

### Steps to reproduce the problem
1. Go to mirumee/saleor-dashboard repository and clone it
2. Copy schema from core to dashboard
3. `npm run build-types`
4. Notice that it fails at multiple interface implementation.


Discussion:

Resolving patch:
diff --git a/saleor/graphql/management/commands/get_graphql_schema.py b/saleor/graphql/management/commands/get_graphql_schema.py
--- a/saleor/graphql/management/commands/get_graphql_schema.py
+++ b/saleor/graphql/management/commands/get_graphql_schema.py
@@ -8,4 +8,14 @@ class Command(BaseCommand):
     help = "Writes SDL for GraphQL API schema to stdout"
 
     def handle(self, *args, **options):
-        self.stdout.write(print_schema(schema))
+        """Support multiple interface notation in schema for Apollo tooling.
+
+        In `graphql-core` V2 separator for interaces is `,`.
+        Apollo tooling to generate TypeScript types using `&` as interfaces separator.
+        https://github.com/graphql-python/graphql-core/pull/258
+        """
+        printed_schema = print_schema(schema)
+        for line in printed_schema.splitlines():
+            if "implements" in line:
+                line = line.replace(",", " &")
+            self.stdout.write(f"{line}\n")


Test patch:
Repository: saleor/saleor

Issue:
It should not be possible to have negative price on shipping rate
### Steps to reproduce the problem
1. Go to the dashboard's shipping section
2. Add new rate or edit existing one
3. Set -1 in price field

### What I expected to happen
To get an error like "shipping rate price cannot be lower than 0".


Discussion:
Related https://github.com/mirumee/saleor/issues/5326

Resolving patch:
diff --git a/saleor/graphql/shipping/mutations.py b/saleor/graphql/shipping/mutations.py
--- a/saleor/graphql/shipping/mutations.py
+++ b/saleor/graphql/shipping/mutations.py
@@ -179,6 +179,15 @@ def clean_input(cls, info, instance, data, input_cls=None):
         # Rename the price field to price_amount (the model's)
         price_amount = cleaned_input.pop("price", None)
         if price_amount is not None:
+            if price_amount < 0:
+                raise ValidationError(
+                    {
+                        "price": ValidationError(
+                            ("Shipping rate price cannot be lower than 0."),
+                            code=ShippingErrorCode.INVALID,
+                        )
+                    }
+                )
             cleaned_input["price_amount"] = price_amount
 
         cleaned_type = cleaned_input.get("type")


Test patch:
diff --git a/tests/api/test_shipping_method.py b/tests/api/test_shipping_method.py
--- a/tests/api/test_shipping_method.py
+++ b/tests/api/test_shipping_method.py
@@ -468,6 +468,10 @@ def test_delete_shipping_zone(
             field
             code
         }
+        shippingErrors {
+          field
+          code
+        }
         shippingZone {
             id
         }
@@ -530,6 +534,30 @@ def test_create_shipping_method(
     assert data["shippingZone"]["id"] == shipping_zone_id
 
 
+def test_create_shipping_method_with_invalid_price(
+    staff_api_client, shipping_zone, permission_manage_shipping,
+):
+    query = PRICE_BASED_SHIPPING_QUERY
+    name = "DHL"
+    price = -12.34
+    shipping_zone_id = graphene.Node.to_global_id("ShippingZone", shipping_zone.pk)
+    variables = {
+        "shippingZone": shipping_zone_id,
+        "name": name,
+        "price": price,
+        "minimumOrderPrice": 0,
+        "maximumOrderPrice": 20,
+        "type": ShippingMethodTypeEnum.PRICE.name,
+    }
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping]
+    )
+    content = get_graphql_content(response)
+    data = content["data"]["shippingPriceCreate"]
+    assert data["shippingErrors"][0]["field"] == "price"
+    assert data["shippingErrors"][0]["code"] == ShippingErrorCode.INVALID.name
+
+
 def test_create_price_shipping_method_errors(
     shipping_zone, staff_api_client, permission_manage_shipping
 ):
Repository: saleor/saleor

Issue:
Uploading a product picture without a file rasies an exception


### Steps to reproduce the problem
1. Upload an image without sending a file through `productImageCreate`
```
Traceback (most recent call last):
  File "./saleor/graphql/core/mutations.py", line 279, in mutate
    response = cls.perform_mutation(root, info, **data)
  File "./saleor/graphql/product/mutations/products.py", line 1400, in perform_mutation
    validate_image_file(image_data, "image")
  File "./saleor/graphql/core/utils/__init__.py", line 33, in validate_image_file
    if not file.content_type.startswith("image/"):
AttributeError: 'NoneType' object has no attribute 'content_type'
```

### What I expected to happen
A mutation error.



Discussion:

Resolving patch:
diff --git a/saleor/graphql/core/utils/__init__.py b/saleor/graphql/core/utils/__init__.py
--- a/saleor/graphql/core/utils/__init__.py
+++ b/saleor/graphql/core/utils/__init__.py
@@ -36,6 +36,10 @@ def str_to_enum(name):
 
 def validate_image_file(file, field_name):
     """Validate if the file is an image."""
+    if not file:
+        raise ValidationError(
+            {field_name: ValidationError("File is required", code="required")}
+        )
     if not file.content_type.startswith("image/"):
         raise ValidationError(
             {field_name: ValidationError("Invalid file type", code="invalid")}


Test patch:
diff --git a/tests/api/test_product.py b/tests/api/test_product.py
--- a/tests/api/test_product.py
+++ b/tests/api/test_product.py
@@ -2519,6 +2519,33 @@ def test_product_image_create_mutation(
     mock_create_thumbnails.assert_called_once_with(product_image.pk)
 
 
+def test_product_image_create_mutation_without_file(
+    monkeypatch, staff_api_client, product, permission_manage_products, media_root
+):
+    query = """
+    mutation createProductImage($image: Upload!, $product: ID!) {
+        productImageCreate(input: {image: $image, product: $product}) {
+            productErrors {
+                code
+                field
+            }
+        }
+    }
+    """
+    variables = {
+        "product": graphene.Node.to_global_id("Product", product.id),
+        "image": "image name",
+    }
+    body = get_multipart_request_body(query, variables, file="", file_name="name")
+    response = staff_api_client.post_multipart(
+        body, permissions=[permission_manage_products]
+    )
+    content = get_graphql_content(response)
+    errors = content["data"]["productImageCreate"]["productErrors"]
+    assert errors[0]["field"] == "image"
+    assert errors[0]["code"] == ProductErrorCode.REQUIRED.name
+
+
 def test_invalid_product_image_create_mutation(
     staff_api_client, product, permission_manage_products
 ):
Repository: saleor/saleor

Issue:
Creating a new sale raises error in Celery task
### Steps to reproduce the problem
1. Run the following mutation as an admin user (with `MANAGE_DISCOUNTS` permission):
```
mutation {
  saleCreate(input: {name: "Test"}) {
    errors {
      field
      message
    }
    sale {
      id
      name
    }
  }
}
```

The response from API is successful, but in the Django server console I'm getting the following error:

```
ERROR celery.app.trace Task saleor.product.tasks.update_products_minimal_variant_prices_of_discount_task[4ec46245-d1f1-47ae-ab23-0c0ab73a9981] raised unexpected: ValueError('Provide at least one of the ID lists:\n\tproduct_ids,\n\tcategory_ids,\n\tcollection_ids.') [PID:31316:Thread-175]
Traceback (most recent call last):
  File "/Users/marcin/.pyenv/versions/saleor3.8.1/lib/python3.8/site-packages/celery/app/trace.py", line 385, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/tasks.py", line 64, in update_products_minimal_variant_prices_of_discount_task
    update_products_minimal_variant_prices_of_discount(discount)
  File "/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/utils/variant_prices.py", line 76, in update_products_minimal_variant_prices_of_discount
    update_products_minimal_variant_prices_of_catalogues(
  File "/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/utils/variant_prices.py", line 62, in update_products_minimal_variant_prices_of_catalogues
    raise ValueError(
ValueError: Provide at least one of the ID lists:
	product_ids,
	category_ids,
	collection_ids.
```

I suppose that the Celery task that recalculates minimal variant prices is run even there are no products to update. Probably an additional check needs to be added to not run the task in this case.


Discussion:

Resolving patch:
diff --git a/saleor/product/utils/variant_prices.py b/saleor/product/utils/variant_prices.py
--- a/saleor/product/utils/variant_prices.py
+++ b/saleor/product/utils/variant_prices.py
@@ -58,18 +58,12 @@ def update_products_minimal_variant_prices_of_catalogues(
     if collection_ids:
         q_list.append(Q(collectionproduct__collection_id__in=collection_ids))
     # Asserting that the function was called with some ids
-    if not q_list:
-        raise ValueError(
-            "Provide at least one of the ID lists:\n"
-            "\tproduct_ids,\n"
-            "\tcategory_ids,\n"
-            "\tcollection_ids."
-        )
-    # Querying the products
-    q_or = reduce(operator.or_, q_list)
-    products = Product.objects.filter(q_or).distinct()
+    if q_list:
+        # Querying the products
+        q_or = reduce(operator.or_, q_list)
+        products = Product.objects.filter(q_or).distinct()
 
-    update_products_minimal_variant_prices(products)
+        update_products_minimal_variant_prices(products)
 
 
 def update_products_minimal_variant_prices_of_discount(discount):


Test patch:
Repository: saleor/saleor

Issue:
Avalara plugin sends product's entire description instead of its name to the Avatax API
### What I'm trying to achieve
I'm trying to make sure we're sending correct information when doing tax calculations.

### Steps to reproduce the problem
1. Use the Avatax plugin

### What I expected to happen
Product variant SKU and its name is sent instead of SKU + long description


Discussion:

Resolving patch:
diff --git a/saleor/plugins/avatax/__init__.py b/saleor/plugins/avatax/__init__.py
--- a/saleor/plugins/avatax/__init__.py
+++ b/saleor/plugins/avatax/__init__.py
@@ -178,7 +178,7 @@ def append_line_to_data(
     amount: Decimal,
     tax_code: str,
     item_code: str,
-    description: str = None,
+    name: str = None,
     tax_included: Optional[bool] = None,
 ):
     if tax_included is None:
@@ -190,7 +190,7 @@ def append_line_to_data(
             "taxCode": tax_code,
             "taxIncluded": tax_included,
             "itemCode": item_code,
-            "description": description[:2000] if description else "",
+            "description": name,
         }
     )
 
@@ -221,7 +221,7 @@ def get_checkout_lines_data(
     for line in lines:
         if not line.variant.product.charge_taxes:
             continue
-        description = line.variant.product.plain_text_description
+        name = line.variant.product.name
         product = line.variant.product
         product_type = line.variant.product.product_type
         tax_code = retrieve_tax_code_from_meta(product)
@@ -234,7 +234,7 @@ def get_checkout_lines_data(
             ).gross.amount,
             tax_code=tax_code,
             item_code=line.variant.sku,
-            description=description,
+            name=name,
         )
 
     append_shipping_to_data(data, checkout.shipping_method)
@@ -263,7 +263,7 @@ def get_order_lines_data(
             amount=line.unit_price_net_amount * line.quantity,
             tax_code=tax_code,
             item_code=line.variant.sku,
-            description=line.variant.product.plain_text_description,
+            name=line.variant.product.name,
         )
     if order.discount_amount:
         append_line_to_data(
@@ -272,7 +272,7 @@ def get_order_lines_data(
             amount=order.discount_amount * -1,
             tax_code=COMMON_DISCOUNT_VOUCHER_CODE,
             item_code="Voucher",
-            description=order.discount_name,
+            name=order.discount_name,
             tax_included=True,  # Voucher should be always applied as a gross amount
         )
     append_shipping_to_data(data, order.shipping_method)


Test patch:
Repository: saleor/saleor

Issue:
Change AccountErrorCode.INVALID_PASSWORD to AccountErrorCode.INVALID_CREDENTIALS
In typed errors, we are sending `field` information so we do not need such granular errors.
Change `AccountErrorCode.INVALID_PASSWORD` to `AccountErrorCode.INVALID_CREDENTIALS` where possible.


Discussion:

Resolving patch:
diff --git a/saleor/graphql/account/mutations/account.py b/saleor/graphql/account/mutations/account.py
--- a/saleor/graphql/account/mutations/account.py
+++ b/saleor/graphql/account/mutations/account.py
@@ -384,7 +384,8 @@ def perform_mutation(cls, _root, info, **data):
             raise ValidationError(
                 {
                     "password": ValidationError(
-                        "Password isn't valid.", code=AccountErrorCode.INVALID_PASSWORD
+                        "Password isn't valid.",
+                        code=AccountErrorCode.INVALID_CREDENTIALS,
                     )
                 }
             )
diff --git a/saleor/graphql/account/mutations/base.py b/saleor/graphql/account/mutations/base.py
--- a/saleor/graphql/account/mutations/base.py
+++ b/saleor/graphql/account/mutations/base.py
@@ -213,7 +213,7 @@ def perform_mutation(cls, _root, info, **data):
                 {
                     "old_password": ValidationError(
                         "Old password isn't valid.",
-                        code=AccountErrorCode.INVALID_PASSWORD,
+                        code=AccountErrorCode.INVALID_CREDENTIALS,
                     )
                 }
             )


Test patch:
diff --git a/tests/api/test_account.py b/tests/api/test_account.py
--- a/tests/api/test_account.py
+++ b/tests/api/test_account.py
@@ -3269,7 +3269,7 @@ def test_request_email_change_with_invalid_password(user_api_client, customer_us
     content = get_graphql_content(response)
     data = content["data"]["requestEmailChange"]
     assert not data["user"]
-    assert data["accountErrors"][0]["code"] == AccountErrorCode.INVALID_PASSWORD.name
+    assert data["accountErrors"][0]["code"] == AccountErrorCode.INVALID_CREDENTIALS.name
     assert data["accountErrors"][0]["field"] == "password"