prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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.
  
  </issue>
  <code>
  [start of saleor/product/utils/variant_prices.py]
  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 of saleor/product/utils/variant_prices.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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):
  
verification_info: {"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"}]}