We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: [Simple Taxes] It is possible to set negative value on tax rate.
### What are you trying to achieve?

Create new tax rate 

### Steps to reproduce the problem

1. go to taxes in configuration 
2. set in countries - in e.g US -default rate eq to "-20"
3. create checkout 

### What did you expect to happen?

It should be not possible to set tax rate as a negative value.

### Logs

![image](https://user-images.githubusercontent.com/29308910/196620931-4295f736-73d7-45d0-b05c-48221b2f5fd7.png)
![image](https://user-images.githubusercontent.com/29308910/196621315-b0aa20bd-cd63-4601-bfe7-1790b4408df1.png)
![image](https://user-images.githubusercontent.com/29308910/196622979-dc4f8a69-2d8b-458c-92f3-5eb357e5421c.png)



### Environment

Saleor version: …
OS and version: …


--- 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/tax/error_codes.py`
Content:
```
1 from enum import Enum
2 
3 
4 class TaxExemptionManageErrorCode(Enum):
5     GRAPHQL_ERROR = "graphql_error"
6     INVALID = "invalid"
7     NOT_FOUND = "not_found"
8     NOT_EDITABLE_ORDER = "not_editable_order"
9 
10 
11 class TaxConfigurationUpdateErrorCode(Enum):
12     DUPLICATED_INPUT_ITEM = "duplicated_input_item"
13     GRAPHQL_ERROR = "graphql_error"
14     INVALID = "invalid"
15     NOT_FOUND = "not_found"
16 
17 
18 class TaxClassCreateErrorCode(Enum):
19     GRAPHQL_ERROR = "graphql_error"
20     INVALID = "invalid"
21     NOT_FOUND = "not_found"
22 
23 
24 class TaxClassUpdateErrorCode(Enum):
25     DUPLICATED_INPUT_ITEM = "duplicated_input_item"
26     GRAPHQL_ERROR = "graphql_error"
27     INVALID = "invalid"
28     NOT_FOUND = "not_found"
29 
30 
31 class TaxClassDeleteErrorCode(Enum):
32     GRAPHQL_ERROR = "graphql_error"
33     INVALID = "invalid"
34     NOT_FOUND = "not_found"
35 
36 
37 class TaxCountryConfigurationUpdateErrorCode(Enum):
38     GRAPHQL_ERROR = "graphql_error"
39     INVALID = "invalid"
40     NOT_FOUND = "not_found"
41     ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED = "only_one_default_country_rate_allowed"
42     CANNOT_CREATE_WITH_NULL_RATE = "cannot_create_with_null_rate"
43 
44 
45 class TaxCountryConfigurationDeleteErrorCode(Enum):
46     GRAPHQL_ERROR = "graphql_error"
47     INVALID = "invalid"
48     NOT_FOUND = "not_found"
49
```

Path: `saleor/graphql/tax/mutations/tax_country_configuration_update.py`
Content:
```
1 import graphene
2 from django.core.exceptions import ValidationError
3 from django.db.models import Q
4 from django_countries.fields import Country
5 from graphql import GraphQLError
6 
7 from ....core.permissions import CheckoutPermissions
8 from ....tax import error_codes, models
9 from ...account.enums import CountryCodeEnum
10 from ...core.descriptions import ADDED_IN_35, PREVIEW_FEATURE
11 from ...core.mutations import BaseMutation
12 from ...core.types import Error, NonNullList
13 from ...core.utils import from_global_id_or_error
14 from ..types import TaxCountryConfiguration
15 
16 TaxCountryConfigurationUpdateErrorCode = graphene.Enum.from_enum(
17     error_codes.TaxCountryConfigurationUpdateErrorCode
18 )
19 
20 
21 class TaxCountryConfigurationUpdateError(Error):
22     code = TaxCountryConfigurationUpdateErrorCode(
23         description="The error code.", required=True
24     )
25     tax_class_ids = NonNullList(
26         graphene.String,
27         description="List of tax class IDs for which the update failed.",
28         required=True,
29     )
30 
31 
32 class TaxClassRateInput(graphene.InputObjectType):
33     tax_class_id = graphene.ID(
34         description="ID of a tax class for which to update the tax rate", required=False
35     )
36     rate = graphene.Float(description="Tax rate value.", required=False)
37 
38 
39 class TaxCountryConfigurationUpdate(BaseMutation):
40     tax_country_configuration = graphene.Field(
41         TaxCountryConfiguration,
42         description="Updated tax class rates grouped by a country.",
43     )
44 
45     class Arguments:
46         country_code = CountryCodeEnum(
47             description="Country in which to update the tax class rates.", required=True
48         )
49         update_tax_class_rates = NonNullList(
50             TaxClassRateInput,
51             description=(
52                 "List of tax rates per tax class to update. When "
53                 "`{taxClass: id, rate: null`} is passed, it deletes the rate object "
54                 "for given taxClass ID. When `{rate: Int}` is passed without a tax "
55                 "class, it updates the default tax class for this country."
56             ),
57             required=True,
58         )
59 
60     class Meta:
61         description = (
62             "Update tax class rates for a specific country."
63             + ADDED_IN_35
64             + PREVIEW_FEATURE
65         )
66         error_type_class = TaxCountryConfigurationUpdateError
67         permissions = (CheckoutPermissions.MANAGE_TAXES,)
68 
69     def _clean_default_rates(tax_rate_items):
70         # Check if only one default rate is provided (only one item without the tax
71         # class).
72         default_rate_items = [
73             item for item in tax_rate_items if item.get("tax_class_id") is None
74         ]
75         if len(default_rate_items) > 1:
76             code = (
77                 error_codes.TaxCountryConfigurationUpdateErrorCode.ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED  # noqa: E501
78             )
79             params = {"tax_class_ids": []}
80             raise ValidationError(
81                 {
82                     "update_tax_class_rates": ValidationError(
83                         code=code,
84                         message=(
85                             "Only one default country rate can be created for "
86                             "a country (a rate without a tax class)."
87                         ),
88                         params=params,
89                     )
90                 }
91             )
92 
93     def _clean_tax_class_ids(tax_rate_items):
94         cleaned_data = {}
95         failed_ids = []
96         for item in tax_rate_items:
97             global_id = item.get("tax_class_id")
98             pk = None
99             if global_id:
100                 try:
101                     _, pk = from_global_id_or_error(
102                         global_id, "TaxClass", raise_error=True
103                     )
104                 except GraphQLError:
105                     failed_ids.append(global_id)
106                     continue
107                 pk = int(pk)
108 
109             cleaned_data[pk] = item
110 
111         if failed_ids:
112             params = {"tax_class_ids": failed_ids}
113             code = error_codes.TaxCountryConfigurationUpdateErrorCode.NOT_FOUND
114             message = "Failed to resolve some of the provided tax class IDs."
115             raise ValidationError(
116                 {
117                     "update_tax_class_rates": ValidationError(
118                         code=code, message=message, params=params
119                     )
120                 }
121             )
122 
123         return cleaned_data
124 
125     @classmethod
126     def clean_input(cls, **data):
127         update_tax_class_rates = data.get("update_tax_class_rates", [])
128         cls._clean_default_rates(update_tax_class_rates)
129         return cls._clean_tax_class_ids(update_tax_class_rates)
130 
131     @classmethod
132     def update_default_rate(cls, country_code, cleaned_data):
133         # Handle the default country rate first (the one without a tax class).
134         default_rate = cleaned_data.get(None)
135         if default_rate:
136             rate = default_rate.get("rate")
137             if rate is not None:
138                 models.TaxClassCountryRate.objects.update_or_create(
139                     country=country_code,
140                     tax_class=None,
141                     defaults={"rate": default_rate["rate"]},
142                 )
143             else:
144                 default_rate_obj = models.TaxClassCountryRate.objects.filter(
145                     country=country_code,
146                     tax_class=None,
147                 ).first()
148                 if default_rate_obj:
149                     default_rate_obj.delete()
150 
151     @classmethod
152     def update_and_create_country_rates(cls, country_code, cleaned_data):
153         # Prepare IDs to create and update.
154         input_ids = [key for key in cleaned_data.keys() if key is not None]
155         update_qs = models.TaxClassCountryRate.objects.filter(
156             country=country_code, tax_class_id__in=input_ids
157         )
158         update_ids = [item.tax_class_id for item in update_qs]
159         create_ids = set(input_ids) - set(update_ids)
160         delete_ids = []
161 
162         # Update existing instances.
163         for obj in update_qs:
164             rate = cleaned_data[obj.tax_class_id].get("rate")
165             if rate is None:
166                 delete_ids.append(obj.tax_class_id)
167             else:
168                 obj.rate = rate
169         models.TaxClassCountryRate.objects.bulk_update(update_qs, fields=("rate",))
170 
171         # Create new instances.
172         to_create = []
173         for tax_class_id in create_ids:
174             input_item = cleaned_data[tax_class_id]
175             rate = input_item.get("rate")
176             if rate is None:
177                 code = (
178                     TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_WITH_NULL_RATE
179                 )
180                 raise ValidationError(
181                     {
182                         "update_tax_class_rates": ValidationError(
183                             code=code,
184                             message=(
185                                 "Cannot create a new tax rate without providing it's "
186                                 "value."
187                             ),
188                             params={
189                                 "tax_class_ids": [
190                                     graphene.Node.to_global_id(
191                                         "TaxClass", tax_class_id
192                                     ),
193                                 ]
194                             },
195                         )
196                     }
197                 )
198             else:
199                 obj = models.TaxClassCountryRate(
200                     country=country_code, tax_class_id=tax_class_id, rate=rate
201                 )
202                 to_create.append(obj)
203         models.TaxClassCountryRate.objects.bulk_create(to_create)
204 
205         # Delete instances where null rates were provided.
206         models.TaxClassCountryRate.objects.filter(
207             country=country_code,
208             tax_class_id__in=delete_ids,
209         ).delete()
210 
211     @classmethod
212     def perform_mutation(cls, _root, _info, **data):
213         country_code = data["country_code"]
214         cleaned_data = cls.clean_input(**data)
215         cls.update_default_rate(country_code, cleaned_data)
216         cls.update_and_create_country_rates(country_code, cleaned_data)
217 
218         tax_classes_lookup = Q(tax_class_id__in=cleaned_data.keys())
219         if None in cleaned_data:
220             tax_classes_lookup |= Q(tax_class=None)
221         all_rates = models.TaxClassCountryRate.objects.filter(
222             tax_classes_lookup, country=country_code
223         )
224         country_config = TaxCountryConfiguration(
225             country=Country(country_code), tax_class_country_rates=all_rates
226         )
227         return TaxCountryConfigurationUpdate(tax_country_configuration=country_config)
228
```
--- 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/graphql/tax/mutations/tax_country_configuration_update.py b/saleor/graphql/tax/mutations/tax_country_configuration_update.py
--- a/saleor/graphql/tax/mutations/tax_country_configuration_update.py
+++ b/saleor/graphql/tax/mutations/tax_country_configuration_update.py
@@ -122,9 +122,38 @@
 
         return cleaned_data
 
+    @classmethod
+    def _clean_rate_values(cls, tax_rate_items):
+        invalid_rates = []
+        for item in tax_rate_items:
+            rate = item.get("rate")
+            if rate is not None and rate < 0:
+                invalid_rates.append(item)
+
+        if invalid_rates:
+            code = (
+                error_codes.TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_NEGATIVE_RATE  # noqa: E501
+            )
+            message = "Cannot create rates with negative values."
+            params = {
+                "tax_class_ids": [
+                    item["tax_class_id"]
+                    for item in invalid_rates
+                    if item.get("tax_class_id")
+                ]
+            }
+            raise ValidationError(
+                {
+                    "update_tax_class_rates": ValidationError(
+                        code=code, message=message, params=params
+                    )
+                }
+            )
+
     @classmethod
     def clean_input(cls, **data):
         update_tax_class_rates = data.get("update_tax_class_rates", [])
+        cls._clean_rate_values(update_tax_class_rates)
         cls._clean_default_rates(update_tax_class_rates)
         return cls._clean_tax_class_ids(update_tax_class_rates)
 
diff --git a/saleor/tax/error_codes.py b/saleor/tax/error_codes.py
--- a/saleor/tax/error_codes.py
+++ b/saleor/tax/error_codes.py
@@ -40,6 +40,7 @@
     NOT_FOUND = "not_found"
     ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED = "only_one_default_country_rate_allowed"
     CANNOT_CREATE_WITH_NULL_RATE = "cannot_create_with_null_rate"
+    CANNOT_CREATE_NEGATIVE_RATE = "cannot_create_negative_rate"
 
 
 class TaxCountryConfigurationDeleteErrorCode(Enum):

{"golden_diff": "diff --git a/saleor/graphql/tax/mutations/tax_country_configuration_update.py b/saleor/graphql/tax/mutations/tax_country_configuration_update.py\n--- a/saleor/graphql/tax/mutations/tax_country_configuration_update.py\n+++ b/saleor/graphql/tax/mutations/tax_country_configuration_update.py\n@@ -122,9 +122,38 @@\n \n         return cleaned_data\n \n+    @classmethod\n+    def _clean_rate_values(cls, tax_rate_items):\n+        invalid_rates = []\n+        for item in tax_rate_items:\n+            rate = item.get(\"rate\")\n+            if rate is not None and rate < 0:\n+                invalid_rates.append(item)\n+\n+        if invalid_rates:\n+            code = (\n+                error_codes.TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_NEGATIVE_RATE  # noqa: E501\n+            )\n+            message = \"Cannot create rates with negative values.\"\n+            params = {\n+                \"tax_class_ids\": [\n+                    item[\"tax_class_id\"]\n+                    for item in invalid_rates\n+                    if item.get(\"tax_class_id\")\n+                ]\n+            }\n+            raise ValidationError(\n+                {\n+                    \"update_tax_class_rates\": ValidationError(\n+                        code=code, message=message, params=params\n+                    )\n+                }\n+            )\n+\n     @classmethod\n     def clean_input(cls, **data):\n         update_tax_class_rates = data.get(\"update_tax_class_rates\", [])\n+        cls._clean_rate_values(update_tax_class_rates)\n         cls._clean_default_rates(update_tax_class_rates)\n         return cls._clean_tax_class_ids(update_tax_class_rates)\n \ndiff --git a/saleor/tax/error_codes.py b/saleor/tax/error_codes.py\n--- a/saleor/tax/error_codes.py\n+++ b/saleor/tax/error_codes.py\n@@ -40,6 +40,7 @@\n     NOT_FOUND = \"not_found\"\n     ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED = \"only_one_default_country_rate_allowed\"\n     CANNOT_CREATE_WITH_NULL_RATE = \"cannot_create_with_null_rate\"\n+    CANNOT_CREATE_NEGATIVE_RATE = \"cannot_create_negative_rate\"\n \n \n class TaxCountryConfigurationDeleteErrorCode(Enum):\n", "issue": "Bug: [Simple Taxes] It is possible to set negative value on tax rate.\n### What are you trying to achieve?\r\n\r\nCreate new tax rate \r\n\r\n### Steps to reproduce the problem\r\n\r\n1. go to taxes in configuration \r\n2. set in countries - in e.g US -default rate eq to \"-20\"\r\n3. create checkout \r\n\r\n### What did you expect to happen?\r\n\r\nIt should be not possible to set tax rate as a negative value.\r\n\r\n### Logs\r\n\r\n![image](https://user-images.githubusercontent.com/29308910/196620931-4295f736-73d7-45d0-b05c-48221b2f5fd7.png)\r\n![image](https://user-images.githubusercontent.com/29308910/196621315-b0aa20bd-cd63-4601-bfe7-1790b4408df1.png)\r\n![image](https://user-images.githubusercontent.com/29308910/196622979-dc4f8a69-2d8b-458c-92f3-5eb357e5421c.png)\r\n\r\n\r\n\r\n### Environment\r\n\r\nSaleor version: \u2026\r\nOS and version: \u2026\r\n\n", "before_files": [{"content": "from enum import Enum\n\n\nclass TaxExemptionManageErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n    NOT_EDITABLE_ORDER = \"not_editable_order\"\n\n\nclass TaxConfigurationUpdateErrorCode(Enum):\n    DUPLICATED_INPUT_ITEM = \"duplicated_input_item\"\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassCreateErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassUpdateErrorCode(Enum):\n    DUPLICATED_INPUT_ITEM = \"duplicated_input_item\"\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassDeleteErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxCountryConfigurationUpdateErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n    ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED = \"only_one_default_country_rate_allowed\"\n    CANNOT_CREATE_WITH_NULL_RATE = \"cannot_create_with_null_rate\"\n\n\nclass TaxCountryConfigurationDeleteErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n", "path": "saleor/tax/error_codes.py"}, {"content": "import graphene\nfrom django.core.exceptions import ValidationError\nfrom django.db.models import Q\nfrom django_countries.fields import Country\nfrom graphql import GraphQLError\n\nfrom ....core.permissions import CheckoutPermissions\nfrom ....tax import error_codes, models\nfrom ...account.enums import CountryCodeEnum\nfrom ...core.descriptions import ADDED_IN_35, PREVIEW_FEATURE\nfrom ...core.mutations import BaseMutation\nfrom ...core.types import Error, NonNullList\nfrom ...core.utils import from_global_id_or_error\nfrom ..types import TaxCountryConfiguration\n\nTaxCountryConfigurationUpdateErrorCode = graphene.Enum.from_enum(\n    error_codes.TaxCountryConfigurationUpdateErrorCode\n)\n\n\nclass TaxCountryConfigurationUpdateError(Error):\n    code = TaxCountryConfigurationUpdateErrorCode(\n        description=\"The error code.\", required=True\n    )\n    tax_class_ids = NonNullList(\n        graphene.String,\n        description=\"List of tax class IDs for which the update failed.\",\n        required=True,\n    )\n\n\nclass TaxClassRateInput(graphene.InputObjectType):\n    tax_class_id = graphene.ID(\n        description=\"ID of a tax class for which to update the tax rate\", required=False\n    )\n    rate = graphene.Float(description=\"Tax rate value.\", required=False)\n\n\nclass TaxCountryConfigurationUpdate(BaseMutation):\n    tax_country_configuration = graphene.Field(\n        TaxCountryConfiguration,\n        description=\"Updated tax class rates grouped by a country.\",\n    )\n\n    class Arguments:\n        country_code = CountryCodeEnum(\n            description=\"Country in which to update the tax class rates.\", required=True\n        )\n        update_tax_class_rates = NonNullList(\n            TaxClassRateInput,\n            description=(\n                \"List of tax rates per tax class to update. When \"\n                \"`{taxClass: id, rate: null`} is passed, it deletes the rate object \"\n                \"for given taxClass ID. When `{rate: Int}` is passed without a tax \"\n                \"class, it updates the default tax class for this country.\"\n            ),\n            required=True,\n        )\n\n    class Meta:\n        description = (\n            \"Update tax class rates for a specific country.\"\n            + ADDED_IN_35\n            + PREVIEW_FEATURE\n        )\n        error_type_class = TaxCountryConfigurationUpdateError\n        permissions = (CheckoutPermissions.MANAGE_TAXES,)\n\n    def _clean_default_rates(tax_rate_items):\n        # Check if only one default rate is provided (only one item without the tax\n        # class).\n        default_rate_items = [\n            item for item in tax_rate_items if item.get(\"tax_class_id\") is None\n        ]\n        if len(default_rate_items) > 1:\n            code = (\n                error_codes.TaxCountryConfigurationUpdateErrorCode.ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED  # noqa: E501\n            )\n            params = {\"tax_class_ids\": []}\n            raise ValidationError(\n                {\n                    \"update_tax_class_rates\": ValidationError(\n                        code=code,\n                        message=(\n                            \"Only one default country rate can be created for \"\n                            \"a country (a rate without a tax class).\"\n                        ),\n                        params=params,\n                    )\n                }\n            )\n\n    def _clean_tax_class_ids(tax_rate_items):\n        cleaned_data = {}\n        failed_ids = []\n        for item in tax_rate_items:\n            global_id = item.get(\"tax_class_id\")\n            pk = None\n            if global_id:\n                try:\n                    _, pk = from_global_id_or_error(\n                        global_id, \"TaxClass\", raise_error=True\n                    )\n                except GraphQLError:\n                    failed_ids.append(global_id)\n                    continue\n                pk = int(pk)\n\n            cleaned_data[pk] = item\n\n        if failed_ids:\n            params = {\"tax_class_ids\": failed_ids}\n            code = error_codes.TaxCountryConfigurationUpdateErrorCode.NOT_FOUND\n            message = \"Failed to resolve some of the provided tax class IDs.\"\n            raise ValidationError(\n                {\n                    \"update_tax_class_rates\": ValidationError(\n                        code=code, message=message, params=params\n                    )\n                }\n            )\n\n        return cleaned_data\n\n    @classmethod\n    def clean_input(cls, **data):\n        update_tax_class_rates = data.get(\"update_tax_class_rates\", [])\n        cls._clean_default_rates(update_tax_class_rates)\n        return cls._clean_tax_class_ids(update_tax_class_rates)\n\n    @classmethod\n    def update_default_rate(cls, country_code, cleaned_data):\n        # Handle the default country rate first (the one without a tax class).\n        default_rate = cleaned_data.get(None)\n        if default_rate:\n            rate = default_rate.get(\"rate\")\n            if rate is not None:\n                models.TaxClassCountryRate.objects.update_or_create(\n                    country=country_code,\n                    tax_class=None,\n                    defaults={\"rate\": default_rate[\"rate\"]},\n                )\n            else:\n                default_rate_obj = models.TaxClassCountryRate.objects.filter(\n                    country=country_code,\n                    tax_class=None,\n                ).first()\n                if default_rate_obj:\n                    default_rate_obj.delete()\n\n    @classmethod\n    def update_and_create_country_rates(cls, country_code, cleaned_data):\n        # Prepare IDs to create and update.\n        input_ids = [key for key in cleaned_data.keys() if key is not None]\n        update_qs = models.TaxClassCountryRate.objects.filter(\n            country=country_code, tax_class_id__in=input_ids\n        )\n        update_ids = [item.tax_class_id for item in update_qs]\n        create_ids = set(input_ids) - set(update_ids)\n        delete_ids = []\n\n        # Update existing instances.\n        for obj in update_qs:\n            rate = cleaned_data[obj.tax_class_id].get(\"rate\")\n            if rate is None:\n                delete_ids.append(obj.tax_class_id)\n            else:\n                obj.rate = rate\n        models.TaxClassCountryRate.objects.bulk_update(update_qs, fields=(\"rate\",))\n\n        # Create new instances.\n        to_create = []\n        for tax_class_id in create_ids:\n            input_item = cleaned_data[tax_class_id]\n            rate = input_item.get(\"rate\")\n            if rate is None:\n                code = (\n                    TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_WITH_NULL_RATE\n                )\n                raise ValidationError(\n                    {\n                        \"update_tax_class_rates\": ValidationError(\n                            code=code,\n                            message=(\n                                \"Cannot create a new tax rate without providing it's \"\n                                \"value.\"\n                            ),\n                            params={\n                                \"tax_class_ids\": [\n                                    graphene.Node.to_global_id(\n                                        \"TaxClass\", tax_class_id\n                                    ),\n                                ]\n                            },\n                        )\n                    }\n                )\n            else:\n                obj = models.TaxClassCountryRate(\n                    country=country_code, tax_class_id=tax_class_id, rate=rate\n                )\n                to_create.append(obj)\n        models.TaxClassCountryRate.objects.bulk_create(to_create)\n\n        # Delete instances where null rates were provided.\n        models.TaxClassCountryRate.objects.filter(\n            country=country_code,\n            tax_class_id__in=delete_ids,\n        ).delete()\n\n    @classmethod\n    def perform_mutation(cls, _root, _info, **data):\n        country_code = data[\"country_code\"]\n        cleaned_data = cls.clean_input(**data)\n        cls.update_default_rate(country_code, cleaned_data)\n        cls.update_and_create_country_rates(country_code, cleaned_data)\n\n        tax_classes_lookup = Q(tax_class_id__in=cleaned_data.keys())\n        if None in cleaned_data:\n            tax_classes_lookup |= Q(tax_class=None)\n        all_rates = models.TaxClassCountryRate.objects.filter(\n            tax_classes_lookup, country=country_code\n        )\n        country_config = TaxCountryConfiguration(\n            country=Country(country_code), tax_class_country_rates=all_rates\n        )\n        return TaxCountryConfigurationUpdate(tax_country_configuration=country_config)\n", "path": "saleor/graphql/tax/mutations/tax_country_configuration_update.py"}], "after_files": [{"content": "from enum import Enum\n\n\nclass TaxExemptionManageErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n    NOT_EDITABLE_ORDER = \"not_editable_order\"\n\n\nclass TaxConfigurationUpdateErrorCode(Enum):\n    DUPLICATED_INPUT_ITEM = \"duplicated_input_item\"\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassCreateErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassUpdateErrorCode(Enum):\n    DUPLICATED_INPUT_ITEM = \"duplicated_input_item\"\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxClassDeleteErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n\n\nclass TaxCountryConfigurationUpdateErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n    ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED = \"only_one_default_country_rate_allowed\"\n    CANNOT_CREATE_WITH_NULL_RATE = \"cannot_create_with_null_rate\"\n    CANNOT_CREATE_NEGATIVE_RATE = \"cannot_create_negative_rate\"\n\n\nclass TaxCountryConfigurationDeleteErrorCode(Enum):\n    GRAPHQL_ERROR = \"graphql_error\"\n    INVALID = \"invalid\"\n    NOT_FOUND = \"not_found\"\n", "path": "saleor/tax/error_codes.py"}, {"content": "import graphene\nfrom django.core.exceptions import ValidationError\nfrom django.db.models import Q\nfrom django_countries.fields import Country\nfrom graphql import GraphQLError\n\nfrom ....core.permissions import CheckoutPermissions\nfrom ....tax import error_codes, models\nfrom ...account.enums import CountryCodeEnum\nfrom ...core.descriptions import ADDED_IN_35, PREVIEW_FEATURE\nfrom ...core.mutations import BaseMutation\nfrom ...core.types import Error, NonNullList\nfrom ...core.utils import from_global_id_or_error\nfrom ..types import TaxCountryConfiguration\n\nTaxCountryConfigurationUpdateErrorCode = graphene.Enum.from_enum(\n    error_codes.TaxCountryConfigurationUpdateErrorCode\n)\n\n\nclass TaxCountryConfigurationUpdateError(Error):\n    code = TaxCountryConfigurationUpdateErrorCode(\n        description=\"The error code.\", required=True\n    )\n    tax_class_ids = NonNullList(\n        graphene.String,\n        description=\"List of tax class IDs for which the update failed.\",\n        required=True,\n    )\n\n\nclass TaxClassRateInput(graphene.InputObjectType):\n    tax_class_id = graphene.ID(\n        description=\"ID of a tax class for which to update the tax rate\", required=False\n    )\n    rate = graphene.Float(description=\"Tax rate value.\", required=False)\n\n\nclass TaxCountryConfigurationUpdate(BaseMutation):\n    tax_country_configuration = graphene.Field(\n        TaxCountryConfiguration,\n        description=\"Updated tax class rates grouped by a country.\",\n    )\n\n    class Arguments:\n        country_code = CountryCodeEnum(\n            description=\"Country in which to update the tax class rates.\", required=True\n        )\n        update_tax_class_rates = NonNullList(\n            TaxClassRateInput,\n            description=(\n                \"List of tax rates per tax class to update. When \"\n                \"`{taxClass: id, rate: null`} is passed, it deletes the rate object \"\n                \"for given taxClass ID. When `{rate: Int}` is passed without a tax \"\n                \"class, it updates the default tax class for this country.\"\n            ),\n            required=True,\n        )\n\n    class Meta:\n        description = (\n            \"Update tax class rates for a specific country.\"\n            + ADDED_IN_35\n            + PREVIEW_FEATURE\n        )\n        error_type_class = TaxCountryConfigurationUpdateError\n        permissions = (CheckoutPermissions.MANAGE_TAXES,)\n\n    def _clean_default_rates(tax_rate_items):\n        # Check if only one default rate is provided (only one item without the tax\n        # class).\n        default_rate_items = [\n            item for item in tax_rate_items if item.get(\"tax_class_id\") is None\n        ]\n        if len(default_rate_items) > 1:\n            code = (\n                error_codes.TaxCountryConfigurationUpdateErrorCode.ONLY_ONE_DEFAULT_COUNTRY_RATE_ALLOWED  # noqa: E501\n            )\n            params = {\"tax_class_ids\": []}\n            raise ValidationError(\n                {\n                    \"update_tax_class_rates\": ValidationError(\n                        code=code,\n                        message=(\n                            \"Only one default country rate can be created for \"\n                            \"a country (a rate without a tax class).\"\n                        ),\n                        params=params,\n                    )\n                }\n            )\n\n    def _clean_tax_class_ids(tax_rate_items):\n        cleaned_data = {}\n        failed_ids = []\n        for item in tax_rate_items:\n            global_id = item.get(\"tax_class_id\")\n            pk = None\n            if global_id:\n                try:\n                    _, pk = from_global_id_or_error(\n                        global_id, \"TaxClass\", raise_error=True\n                    )\n                except GraphQLError:\n                    failed_ids.append(global_id)\n                    continue\n                pk = int(pk)\n\n            cleaned_data[pk] = item\n\n        if failed_ids:\n            params = {\"tax_class_ids\": failed_ids}\n            code = error_codes.TaxCountryConfigurationUpdateErrorCode.NOT_FOUND\n            message = \"Failed to resolve some of the provided tax class IDs.\"\n            raise ValidationError(\n                {\n                    \"update_tax_class_rates\": ValidationError(\n                        code=code, message=message, params=params\n                    )\n                }\n            )\n\n        return cleaned_data\n\n    @classmethod\n    def _clean_rate_values(cls, tax_rate_items):\n        invalid_rates = []\n        for item in tax_rate_items:\n            rate = item.get(\"rate\")\n            if rate is not None and rate < 0:\n                invalid_rates.append(item)\n\n        if invalid_rates:\n            code = (\n                error_codes.TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_NEGATIVE_RATE  # noqa: E501\n            )\n            message = \"Cannot create rates with negative values.\"\n            params = {\n                \"tax_class_ids\": [\n                    item[\"tax_class_id\"]\n                    for item in invalid_rates\n                    if item.get(\"tax_class_id\")\n                ]\n            }\n            raise ValidationError(\n                {\n                    \"update_tax_class_rates\": ValidationError(\n                        code=code, message=message, params=params\n                    )\n                }\n            )\n\n    @classmethod\n    def clean_input(cls, **data):\n        update_tax_class_rates = data.get(\"update_tax_class_rates\", [])\n        cls._clean_rate_values(update_tax_class_rates)\n        cls._clean_default_rates(update_tax_class_rates)\n        return cls._clean_tax_class_ids(update_tax_class_rates)\n\n    @classmethod\n    def update_default_rate(cls, country_code, cleaned_data):\n        # Handle the default country rate first (the one without a tax class).\n        default_rate = cleaned_data.get(None)\n        if default_rate:\n            rate = default_rate.get(\"rate\")\n            if rate is not None:\n                models.TaxClassCountryRate.objects.update_or_create(\n                    country=country_code,\n                    tax_class=None,\n                    defaults={\"rate\": default_rate[\"rate\"]},\n                )\n            else:\n                default_rate_obj = models.TaxClassCountryRate.objects.filter(\n                    country=country_code,\n                    tax_class=None,\n                ).first()\n                if default_rate_obj:\n                    default_rate_obj.delete()\n\n    @classmethod\n    def update_and_create_country_rates(cls, country_code, cleaned_data):\n        # Prepare IDs to create and update.\n        input_ids = [key for key in cleaned_data.keys() if key is not None]\n        update_qs = models.TaxClassCountryRate.objects.filter(\n            country=country_code, tax_class_id__in=input_ids\n        )\n        update_ids = [item.tax_class_id for item in update_qs]\n        create_ids = set(input_ids) - set(update_ids)\n        delete_ids = []\n\n        # Update existing instances.\n        for obj in update_qs:\n            rate = cleaned_data[obj.tax_class_id].get(\"rate\")\n            if rate is None:\n                delete_ids.append(obj.tax_class_id)\n            else:\n                obj.rate = rate\n        models.TaxClassCountryRate.objects.bulk_update(update_qs, fields=(\"rate\",))\n\n        # Create new instances.\n        to_create = []\n        for tax_class_id in create_ids:\n            input_item = cleaned_data[tax_class_id]\n            rate = input_item.get(\"rate\")\n            if rate is None:\n                code = (\n                    TaxCountryConfigurationUpdateErrorCode.CANNOT_CREATE_WITH_NULL_RATE\n                )\n                raise ValidationError(\n                    {\n                        \"update_tax_class_rates\": ValidationError(\n                            code=code,\n                            message=(\n                                \"Cannot create a new tax rate without providing it's \"\n                                \"value.\"\n                            ),\n                            params={\n                                \"tax_class_ids\": [\n                                    graphene.Node.to_global_id(\n                                        \"TaxClass\", tax_class_id\n                                    ),\n                                ]\n                            },\n                        )\n                    }\n                )\n            else:\n                obj = models.TaxClassCountryRate(\n                    country=country_code, tax_class_id=tax_class_id, rate=rate\n                )\n                to_create.append(obj)\n        models.TaxClassCountryRate.objects.bulk_create(to_create)\n\n        # Delete instances where null rates were provided.\n        models.TaxClassCountryRate.objects.filter(\n            country=country_code,\n            tax_class_id__in=delete_ids,\n        ).delete()\n\n    @classmethod\n    def perform_mutation(cls, _root, _info, **data):\n        country_code = data[\"country_code\"]\n        cleaned_data = cls.clean_input(**data)\n        cls.update_default_rate(country_code, cleaned_data)\n        cls.update_and_create_country_rates(country_code, cleaned_data)\n\n        tax_classes_lookup = Q(tax_class_id__in=cleaned_data.keys())\n        if None in cleaned_data:\n            tax_classes_lookup |= Q(tax_class=None)\n        all_rates = models.TaxClassCountryRate.objects.filter(\n            tax_classes_lookup, country=country_code\n        )\n        country_config = TaxCountryConfiguration(\n            country=Country(country_code), tax_class_country_rates=all_rates\n        )\n        return TaxCountryConfigurationUpdate(tax_country_configuration=country_config)\n", "path": "saleor/graphql/tax/mutations/tax_country_configuration_update.py"}]}