We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/product/utils/variant_prices.py`
Content:
```
1 import operator
2 from functools import reduce
3 
4 from django.db.models.query_utils import Q
5 from prices import Money
6 
7 from ...discount.utils import fetch_active_discounts
8 from ..models import Product
9 
10 
11 def _get_product_minimal_variant_price(product, discounts) -> Money:
12     # Start with the product's price as the minimal one
13     minimal_variant_price = product.price
14     for variant in product.variants.all():
15         variant_price = variant.get_price(discounts=discounts)
16         minimal_variant_price = min(minimal_variant_price, variant_price)
17     return minimal_variant_price
18 
19 
20 def update_product_minimal_variant_price(product, discounts=None, save=True):
21     if discounts is None:
22         discounts = fetch_active_discounts()
23     minimal_variant_price = _get_product_minimal_variant_price(product, discounts)
24     if product.minimal_variant_price != minimal_variant_price:
25         product.minimal_variant_price_amount = minimal_variant_price.amount
26         if save:
27             product.save(update_fields=["minimal_variant_price_amount", "updated_at"])
28     return product
29 
30 
31 def update_products_minimal_variant_prices(products, discounts=None):
32     if discounts is None:
33         discounts = fetch_active_discounts()
34     changed_products_to_update = []
35     for product in products:
36         old_minimal_variant_price = product.minimal_variant_price
37         updated_product = update_product_minimal_variant_price(
38             product, discounts, save=False
39         )
40         # Check if the "minimal_variant_price" has changed
41         if updated_product.minimal_variant_price != old_minimal_variant_price:
42             changed_products_to_update.append(updated_product)
43     # Bulk update the changed products
44     Product.objects.bulk_update(
45         changed_products_to_update, ["minimal_variant_price_amount"]
46     )
47 
48 
49 def update_products_minimal_variant_prices_of_catalogues(
50     product_ids=None, category_ids=None, collection_ids=None
51 ):
52     # Building the matching products query
53     q_list = []
54     if product_ids:
55         q_list.append(Q(pk__in=product_ids))
56     if category_ids:
57         q_list.append(Q(category_id__in=category_ids))
58     if collection_ids:
59         q_list.append(Q(collectionproduct__collection_id__in=collection_ids))
60     # Asserting that the function was called with some ids
61     if not q_list:
62         raise ValueError(
63             "Provide at least one of the ID lists:\n"
64             "\tproduct_ids,\n"
65             "\tcategory_ids,\n"
66             "\tcollection_ids."
67         )
68     # Querying the products
69     q_or = reduce(operator.or_, q_list)
70     products = Product.objects.filter(q_or).distinct()
71 
72     update_products_minimal_variant_prices(products)
73 
74 
75 def update_products_minimal_variant_prices_of_discount(discount):
76     update_products_minimal_variant_prices_of_catalogues(
77         product_ids=discount.products.all().values_list("id", flat=True),
78         category_ids=discount.categories.all().values_list("id", flat=True),
79         collection_ids=discount.collections.all().values_list("id", flat=True),
80     )
81
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     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):

{"golden_diff": "diff --git a/saleor/product/utils/variant_prices.py b/saleor/product/utils/variant_prices.py\n--- a/saleor/product/utils/variant_prices.py\n+++ b/saleor/product/utils/variant_prices.py\n@@ -58,18 +58,12 @@\n     if collection_ids:\n         q_list.append(Q(collectionproduct__collection_id__in=collection_ids))\n     # Asserting that the function was called with some ids\n-    if not q_list:\n-        raise ValueError(\n-            \"Provide at least one of the ID lists:\\n\"\n-            \"\\tproduct_ids,\\n\"\n-            \"\\tcategory_ids,\\n\"\n-            \"\\tcollection_ids.\"\n-        )\n-    # Querying the products\n-    q_or = reduce(operator.or_, q_list)\n-    products = Product.objects.filter(q_or).distinct()\n+    if q_list:\n+        # Querying the products\n+        q_or = reduce(operator.or_, q_list)\n+        products = Product.objects.filter(q_or).distinct()\n \n-    update_products_minimal_variant_prices(products)\n+        update_products_minimal_variant_prices(products)\n \n \n def update_products_minimal_variant_prices_of_discount(discount):\n", "issue": "Creating a new sale raises error in Celery task\n### Steps to reproduce the problem\r\n1. Run the following mutation as an admin user (with `MANAGE_DISCOUNTS` permission):\r\n```\r\nmutation {\r\n  saleCreate(input: {name: \"Test\"}) {\r\n    errors {\r\n      field\r\n      message\r\n    }\r\n    sale {\r\n      id\r\n      name\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nThe response from API is successful, but in the Django server console I'm getting the following error:\r\n\r\n```\r\nERROR 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]\r\nTraceback (most recent call last):\r\n  File \"/Users/marcin/.pyenv/versions/saleor3.8.1/lib/python3.8/site-packages/celery/app/trace.py\", line 385, in trace_task\r\n    R = retval = fun(*args, **kwargs)\r\n  File \"/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/tasks.py\", line 64, in update_products_minimal_variant_prices_of_discount_task\r\n    update_products_minimal_variant_prices_of_discount(discount)\r\n  File \"/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/utils/variant_prices.py\", line 76, in update_products_minimal_variant_prices_of_discount\r\n    update_products_minimal_variant_prices_of_catalogues(\r\n  File \"/Users/marcin/mirumee/saleor-platform/saleor/saleor/product/utils/variant_prices.py\", line 62, in update_products_minimal_variant_prices_of_catalogues\r\n    raise ValueError(\r\nValueError: Provide at least one of the ID lists:\r\n\tproduct_ids,\r\n\tcategory_ids,\r\n\tcollection_ids.\r\n```\r\n\r\nI 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.\n", "before_files": [{"content": "import operator\nfrom functools import reduce\n\nfrom django.db.models.query_utils import Q\nfrom prices import Money\n\nfrom ...discount.utils import fetch_active_discounts\nfrom ..models import Product\n\n\ndef _get_product_minimal_variant_price(product, discounts) -> Money:\n    # Start with the product's price as the minimal one\n    minimal_variant_price = product.price\n    for variant in product.variants.all():\n        variant_price = variant.get_price(discounts=discounts)\n        minimal_variant_price = min(minimal_variant_price, variant_price)\n    return minimal_variant_price\n\n\ndef update_product_minimal_variant_price(product, discounts=None, save=True):\n    if discounts is None:\n        discounts = fetch_active_discounts()\n    minimal_variant_price = _get_product_minimal_variant_price(product, discounts)\n    if product.minimal_variant_price != minimal_variant_price:\n        product.minimal_variant_price_amount = minimal_variant_price.amount\n        if save:\n            product.save(update_fields=[\"minimal_variant_price_amount\", \"updated_at\"])\n    return product\n\n\ndef update_products_minimal_variant_prices(products, discounts=None):\n    if discounts is None:\n        discounts = fetch_active_discounts()\n    changed_products_to_update = []\n    for product in products:\n        old_minimal_variant_price = product.minimal_variant_price\n        updated_product = update_product_minimal_variant_price(\n            product, discounts, save=False\n        )\n        # Check if the \"minimal_variant_price\" has changed\n        if updated_product.minimal_variant_price != old_minimal_variant_price:\n            changed_products_to_update.append(updated_product)\n    # Bulk update the changed products\n    Product.objects.bulk_update(\n        changed_products_to_update, [\"minimal_variant_price_amount\"]\n    )\n\n\ndef update_products_minimal_variant_prices_of_catalogues(\n    product_ids=None, category_ids=None, collection_ids=None\n):\n    # Building the matching products query\n    q_list = []\n    if product_ids:\n        q_list.append(Q(pk__in=product_ids))\n    if category_ids:\n        q_list.append(Q(category_id__in=category_ids))\n    if collection_ids:\n        q_list.append(Q(collectionproduct__collection_id__in=collection_ids))\n    # Asserting that the function was called with some ids\n    if not q_list:\n        raise ValueError(\n            \"Provide at least one of the ID lists:\\n\"\n            \"\\tproduct_ids,\\n\"\n            \"\\tcategory_ids,\\n\"\n            \"\\tcollection_ids.\"\n        )\n    # Querying the products\n    q_or = reduce(operator.or_, q_list)\n    products = Product.objects.filter(q_or).distinct()\n\n    update_products_minimal_variant_prices(products)\n\n\ndef update_products_minimal_variant_prices_of_discount(discount):\n    update_products_minimal_variant_prices_of_catalogues(\n        product_ids=discount.products.all().values_list(\"id\", flat=True),\n        category_ids=discount.categories.all().values_list(\"id\", flat=True),\n        collection_ids=discount.collections.all().values_list(\"id\", flat=True),\n    )\n", "path": "saleor/product/utils/variant_prices.py"}], "after_files": [{"content": "import operator\nfrom functools import reduce\n\nfrom django.db.models.query_utils import Q\nfrom prices import Money\n\nfrom ...discount.utils import fetch_active_discounts\nfrom ..models import Product\n\n\ndef _get_product_minimal_variant_price(product, discounts) -> Money:\n    # Start with the product's price as the minimal one\n    minimal_variant_price = product.price\n    for variant in product.variants.all():\n        variant_price = variant.get_price(discounts=discounts)\n        minimal_variant_price = min(minimal_variant_price, variant_price)\n    return minimal_variant_price\n\n\ndef update_product_minimal_variant_price(product, discounts=None, save=True):\n    if discounts is None:\n        discounts = fetch_active_discounts()\n    minimal_variant_price = _get_product_minimal_variant_price(product, discounts)\n    if product.minimal_variant_price != minimal_variant_price:\n        product.minimal_variant_price_amount = minimal_variant_price.amount\n        if save:\n            product.save(update_fields=[\"minimal_variant_price_amount\", \"updated_at\"])\n    return product\n\n\ndef update_products_minimal_variant_prices(products, discounts=None):\n    if discounts is None:\n        discounts = fetch_active_discounts()\n    changed_products_to_update = []\n    for product in products:\n        old_minimal_variant_price = product.minimal_variant_price\n        updated_product = update_product_minimal_variant_price(\n            product, discounts, save=False\n        )\n        # Check if the \"minimal_variant_price\" has changed\n        if updated_product.minimal_variant_price != old_minimal_variant_price:\n            changed_products_to_update.append(updated_product)\n    # Bulk update the changed products\n    Product.objects.bulk_update(\n        changed_products_to_update, [\"minimal_variant_price_amount\"]\n    )\n\n\ndef update_products_minimal_variant_prices_of_catalogues(\n    product_ids=None, category_ids=None, collection_ids=None\n):\n    # Building the matching products query\n    q_list = []\n    if product_ids:\n        q_list.append(Q(pk__in=product_ids))\n    if category_ids:\n        q_list.append(Q(category_id__in=category_ids))\n    if collection_ids:\n        q_list.append(Q(collectionproduct__collection_id__in=collection_ids))\n    # Asserting that the function was called with some ids\n    if q_list:\n        # Querying the products\n        q_or = reduce(operator.or_, q_list)\n        products = Product.objects.filter(q_or).distinct()\n\n        update_products_minimal_variant_prices(products)\n\n\ndef update_products_minimal_variant_prices_of_discount(discount):\n    update_products_minimal_variant_prices_of_catalogues(\n        product_ids=discount.products.all().values_list(\"id\", flat=True),\n        category_ids=discount.categories.all().values_list(\"id\", flat=True),\n        collection_ids=discount.collections.all().values_list(\"id\", flat=True),\n    )\n", "path": "saleor/product/utils/variant_prices.py"}]}