We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Setting external methods should throw errors when the method does not exist
### What I'm trying to achieve
I'm setting an external method that does not exist

### Steps to reproduce the problem
I base64 encoded `app:1234:some-id` that was never a real id external method id:

<img width="1440" alt="image" src="https://user-images.githubusercontent.com/2566928/154252619-496c1b91-ca79-4fe8-bc1d-abcd0cbb743c.png">

There is no error, but the delivery method is still null.


### What I expected to happen
I would expect an error response, rather than noop.

### Screenshots and logs
<!-- If applicable, add screenshots to help explain your problem. -->

**System information**
<!-- Provide the version of Saleor or whether you're using it from the `main` branch. If using Saleor Dashboard or Storefront, provide their versions too. -->
Saleor version:
- [ ] dev (current main)
- [ ] 3.0
- [ ] 2.11
- [ ] 2.10

Operating system:
- [ ] Windows
- [ ] Linux
- [ ] MacOS
- [ ] Other

Setting external methods should throw errors when the method does not exist
### What I'm trying to achieve
I'm setting an external method that does not exist

### Steps to reproduce the problem
I base64 encoded `app:1234:some-id` that was never a real id external method id:

<img width="1440" alt="image" src="https://user-images.githubusercontent.com/2566928/154252619-496c1b91-ca79-4fe8-bc1d-abcd0cbb743c.png">

There is no error, but the delivery method is still null.


### What I expected to happen
I would expect an error response, rather than noop.

### Screenshots and logs
<!-- If applicable, add screenshots to help explain your problem. -->

**System information**
<!-- Provide the version of Saleor or whether you're using it from the `main` branch. If using Saleor Dashboard or Storefront, provide their versions too. -->
Saleor version:
- [ ] dev (current main)
- [ ] 3.0
- [ ] 2.11
- [ ] 2.10

Operating system:
- [ ] Windows
- [ ] Linux
- [ ] MacOS
- [ ] Other


--- 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/graphql/checkout/mutations/checkout_delivery_method_update.py`
Content:
```
1 from typing import Optional
2 
3 import graphene
4 from django.core.exceptions import ValidationError
5 
6 from ....checkout.error_codes import CheckoutErrorCode
7 from ....checkout.fetch import fetch_checkout_info, fetch_checkout_lines
8 from ....checkout.utils import (
9     delete_external_shipping_id,
10     is_shipping_required,
11     recalculate_checkout_discount,
12     set_external_shipping_id,
13 )
14 from ....plugins.webhook.utils import APP_ID_PREFIX
15 from ....shipping import interface as shipping_interface
16 from ....shipping import models as shipping_models
17 from ....shipping.utils import convert_to_shipping_method_data
18 from ....warehouse import models as warehouse_models
19 from ...core.descriptions import ADDED_IN_31, PREVIEW_FEATURE
20 from ...core.mutations import BaseMutation
21 from ...core.scalars import UUID
22 from ...core.types import CheckoutError
23 from ...core.utils import from_global_id_or_error
24 from ...shipping.types import ShippingMethod
25 from ...warehouse.types import Warehouse
26 from ..types import Checkout
27 from .utils import ERROR_DOES_NOT_SHIP, clean_delivery_method, get_checkout_by_token
28 
29 
30 class CheckoutDeliveryMethodUpdate(BaseMutation):
31     checkout = graphene.Field(Checkout, description="An updated checkout.")
32 
33     class Arguments:
34         token = UUID(description="Checkout token.", required=False)
35         delivery_method_id = graphene.ID(
36             description="Delivery Method ID (`Warehouse` ID or `ShippingMethod` ID).",
37             required=False,
38         )
39 
40     class Meta:
41         description = (
42             f"{ADDED_IN_31} Updates the delivery method "
43             f"(shipping method or pick up point) of the checkout. {PREVIEW_FEATURE}"
44         )
45         error_type_class = CheckoutError
46 
47     @classmethod
48     def perform_on_shipping_method(
49         cls, info, shipping_method_id, checkout_info, lines, checkout, manager
50     ):
51         shipping_method = cls.get_node_or_error(
52             info,
53             shipping_method_id,
54             only_type=ShippingMethod,
55             field="delivery_method_id",
56             qs=shipping_models.ShippingMethod.objects.prefetch_related(
57                 "postal_code_rules"
58             ),
59         )
60 
61         delivery_method = convert_to_shipping_method_data(
62             shipping_method,
63             shipping_models.ShippingMethodChannelListing.objects.filter(
64                 shipping_method=shipping_method,
65                 channel=checkout_info.channel,
66             ).first(),
67         )
68         cls._check_delivery_method(
69             checkout_info, lines, shipping_method=delivery_method, collection_point=None
70         )
71 
72         cls._update_delivery_method(
73             manager,
74             checkout,
75             shipping_method=shipping_method,
76             external_shipping_method=None,
77             collection_point=None,
78         )
79         recalculate_checkout_discount(
80             manager, checkout_info, lines, info.context.discounts
81         )
82         return CheckoutDeliveryMethodUpdate(checkout=checkout)
83 
84     @classmethod
85     def perform_on_external_shipping_method(
86         cls, info, shipping_method_id, checkout_info, lines, checkout, manager
87     ):
88         delivery_method = manager.get_shipping_method(
89             checkout=checkout,
90             channel_slug=checkout.channel.slug,
91             shipping_method_id=shipping_method_id,
92         )
93 
94         cls._check_delivery_method(
95             checkout_info, lines, shipping_method=delivery_method, collection_point=None
96         )
97 
98         cls._update_delivery_method(
99             manager,
100             checkout,
101             shipping_method=None,
102             external_shipping_method=delivery_method,
103             collection_point=None,
104         )
105         recalculate_checkout_discount(
106             manager, checkout_info, lines, info.context.discounts
107         )
108         return CheckoutDeliveryMethodUpdate(checkout=checkout)
109 
110     @classmethod
111     def perform_on_collection_point(
112         cls, info, collection_point_id, checkout_info, lines, checkout, manager
113     ):
114         collection_point = cls.get_node_or_error(
115             info,
116             collection_point_id,
117             only_type=Warehouse,
118             field="delivery_method_id",
119             qs=warehouse_models.Warehouse.objects.select_related("address"),
120         )
121         cls._check_delivery_method(
122             checkout_info,
123             lines,
124             shipping_method=None,
125             collection_point=collection_point,
126         )
127         cls._update_delivery_method(
128             manager,
129             checkout,
130             shipping_method=None,
131             external_shipping_method=None,
132             collection_point=collection_point,
133         )
134         return CheckoutDeliveryMethodUpdate(checkout=checkout)
135 
136     @staticmethod
137     def _check_delivery_method(
138         checkout_info,
139         lines,
140         *,
141         shipping_method: Optional[shipping_interface.ShippingMethodData],
142         collection_point: Optional[Warehouse]
143     ) -> None:
144         delivery_method = shipping_method
145         error_msg = "This shipping method is not applicable."
146 
147         if collection_point is not None:
148             delivery_method = collection_point
149             error_msg = "This pick up point is not applicable."
150 
151         delivery_method_is_valid = clean_delivery_method(
152             checkout_info=checkout_info, lines=lines, method=delivery_method
153         )
154         if not delivery_method_is_valid:
155             raise ValidationError(
156                 {
157                     "delivery_method_id": ValidationError(
158                         error_msg,
159                         code=CheckoutErrorCode.DELIVERY_METHOD_NOT_APPLICABLE.value,
160                     )
161                 }
162             )
163 
164     @staticmethod
165     def _update_delivery_method(
166         manager,
167         checkout: Checkout,
168         *,
169         shipping_method: Optional[ShippingMethod],
170         external_shipping_method: Optional[shipping_interface.ShippingMethodData],
171         collection_point: Optional[Warehouse]
172     ) -> None:
173         if external_shipping_method:
174             set_external_shipping_id(
175                 checkout=checkout, app_shipping_id=external_shipping_method.id
176             )
177         else:
178             delete_external_shipping_id(checkout=checkout)
179         checkout.shipping_method = shipping_method
180         checkout.collection_point = collection_point
181         checkout.save(
182             update_fields=[
183                 "private_metadata",
184                 "shipping_method",
185                 "collection_point",
186                 "last_change",
187             ]
188         )
189         manager.checkout_updated(checkout)
190 
191     @staticmethod
192     def _resolve_delivery_method_type(id_) -> Optional[str]:
193         if id_ is None:
194             return None
195 
196         possible_types = ("Warehouse", "ShippingMethod", APP_ID_PREFIX)
197         type_, id_ = from_global_id_or_error(id_)
198         str_type = str(type_)
199 
200         if str_type not in possible_types:
201             raise ValidationError(
202                 {
203                     "delivery_method_id": ValidationError(
204                         "ID does not belong to Warehouse or ShippingMethod",
205                         code=CheckoutErrorCode.INVALID.value,
206                     )
207                 }
208             )
209 
210         return str_type
211 
212     @classmethod
213     def perform_mutation(
214         cls,
215         _,
216         info,
217         token,
218         delivery_method_id=None,
219     ):
220 
221         checkout = get_checkout_by_token(token)
222 
223         manager = info.context.plugins
224         lines, unavailable_variant_pks = fetch_checkout_lines(checkout)
225         if unavailable_variant_pks:
226             not_available_variants_ids = {
227                 graphene.Node.to_global_id("ProductVariant", pk)
228                 for pk in unavailable_variant_pks
229             }
230             raise ValidationError(
231                 {
232                     "lines": ValidationError(
233                         "Some of the checkout lines variants are unavailable.",
234                         code=CheckoutErrorCode.UNAVAILABLE_VARIANT_IN_CHANNEL.value,
235                         params={"variants": not_available_variants_ids},
236                     )
237                 }
238             )
239 
240         if not is_shipping_required(lines):
241             raise ValidationError(
242                 {
243                     "delivery_method": ValidationError(
244                         ERROR_DOES_NOT_SHIP,
245                         code=CheckoutErrorCode.SHIPPING_NOT_REQUIRED,
246                     )
247                 }
248             )
249         type_name = cls._resolve_delivery_method_type(delivery_method_id)
250 
251         checkout_info = fetch_checkout_info(
252             checkout, lines, info.context.discounts, manager
253         )
254         if type_name == "Warehouse":
255             return cls.perform_on_collection_point(
256                 info, delivery_method_id, checkout_info, lines, checkout, manager
257             )
258         if type_name == "ShippingMethod":
259             return cls.perform_on_shipping_method(
260                 info, delivery_method_id, checkout_info, lines, checkout, manager
261             )
262         return cls.perform_on_external_shipping_method(
263             info, delivery_method_id, checkout_info, lines, checkout, manager
264         )
265
```
--- 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/checkout/mutations/checkout_delivery_method_update.py b/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py
--- a/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py
+++ b/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py
@@ -91,6 +91,16 @@
             shipping_method_id=shipping_method_id,
         )
 
+        if delivery_method is None and shipping_method_id:
+            raise ValidationError(
+                {
+                    "delivery_method_id": ValidationError(
+                        f"Couldn't resolve to a node: ${shipping_method_id}",
+                        code=CheckoutErrorCode.NOT_FOUND,
+                    )
+                }
+            )
+
         cls._check_delivery_method(
             checkout_info, lines, shipping_method=delivery_method, collection_point=None
         )

{"golden_diff": "diff --git a/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py b/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py\n--- a/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py\n+++ b/saleor/graphql/checkout/mutations/checkout_delivery_method_update.py\n@@ -91,6 +91,16 @@\n             shipping_method_id=shipping_method_id,\n         )\n \n+        if delivery_method is None and shipping_method_id:\n+            raise ValidationError(\n+                {\n+                    \"delivery_method_id\": ValidationError(\n+                        f\"Couldn't resolve to a node: ${shipping_method_id}\",\n+                        code=CheckoutErrorCode.NOT_FOUND,\n+                    )\n+                }\n+            )\n+\n         cls._check_delivery_method(\n             checkout_info, lines, shipping_method=delivery_method, collection_point=None\n         )\n", "issue": "Setting external methods should throw errors when the method does not exist\n### What I'm trying to achieve\r\nI'm setting an external method that does not exist\r\n\r\n### Steps to reproduce the problem\r\nI base64 encoded `app:1234:some-id` that was never a real id external method id:\r\n\r\n<img width=\"1440\" alt=\"image\" src=\"https://user-images.githubusercontent.com/2566928/154252619-496c1b91-ca79-4fe8-bc1d-abcd0cbb743c.png\">\r\n\r\nThere is no error, but the delivery method is still null.\r\n\r\n\r\n### What I expected to happen\r\nI would expect an error response, rather than noop.\r\n\r\n### Screenshots and logs\r\n<!-- If applicable, add screenshots to help explain your problem. -->\r\n\r\n**System information**\r\n<!-- Provide the version of Saleor or whether you're using it from the `main` branch. If using Saleor Dashboard or Storefront, provide their versions too. -->\r\nSaleor version:\r\n- [ ] dev (current main)\r\n- [ ] 3.0\r\n- [ ] 2.11\r\n- [ ] 2.10\r\n\r\nOperating system:\r\n- [ ] Windows\r\n- [ ] Linux\r\n- [ ] MacOS\r\n- [ ] Other\r\n\nSetting external methods should throw errors when the method does not exist\n### What I'm trying to achieve\r\nI'm setting an external method that does not exist\r\n\r\n### Steps to reproduce the problem\r\nI base64 encoded `app:1234:some-id` that was never a real id external method id:\r\n\r\n<img width=\"1440\" alt=\"image\" src=\"https://user-images.githubusercontent.com/2566928/154252619-496c1b91-ca79-4fe8-bc1d-abcd0cbb743c.png\">\r\n\r\nThere is no error, but the delivery method is still null.\r\n\r\n\r\n### What I expected to happen\r\nI would expect an error response, rather than noop.\r\n\r\n### Screenshots and logs\r\n<!-- If applicable, add screenshots to help explain your problem. -->\r\n\r\n**System information**\r\n<!-- Provide the version of Saleor or whether you're using it from the `main` branch. If using Saleor Dashboard or Storefront, provide their versions too. -->\r\nSaleor version:\r\n- [ ] dev (current main)\r\n- [ ] 3.0\r\n- [ ] 2.11\r\n- [ ] 2.10\r\n\r\nOperating system:\r\n- [ ] Windows\r\n- [ ] Linux\r\n- [ ] MacOS\r\n- [ ] Other\r\n\n", "before_files": [{"content": "from typing import Optional\n\nimport graphene\nfrom django.core.exceptions import ValidationError\n\nfrom ....checkout.error_codes import CheckoutErrorCode\nfrom ....checkout.fetch import fetch_checkout_info, fetch_checkout_lines\nfrom ....checkout.utils import (\n    delete_external_shipping_id,\n    is_shipping_required,\n    recalculate_checkout_discount,\n    set_external_shipping_id,\n)\nfrom ....plugins.webhook.utils import APP_ID_PREFIX\nfrom ....shipping import interface as shipping_interface\nfrom ....shipping import models as shipping_models\nfrom ....shipping.utils import convert_to_shipping_method_data\nfrom ....warehouse import models as warehouse_models\nfrom ...core.descriptions import ADDED_IN_31, PREVIEW_FEATURE\nfrom ...core.mutations import BaseMutation\nfrom ...core.scalars import UUID\nfrom ...core.types import CheckoutError\nfrom ...core.utils import from_global_id_or_error\nfrom ...shipping.types import ShippingMethod\nfrom ...warehouse.types import Warehouse\nfrom ..types import Checkout\nfrom .utils import ERROR_DOES_NOT_SHIP, clean_delivery_method, get_checkout_by_token\n\n\nclass CheckoutDeliveryMethodUpdate(BaseMutation):\n    checkout = graphene.Field(Checkout, description=\"An updated checkout.\")\n\n    class Arguments:\n        token = UUID(description=\"Checkout token.\", required=False)\n        delivery_method_id = graphene.ID(\n            description=\"Delivery Method ID (`Warehouse` ID or `ShippingMethod` ID).\",\n            required=False,\n        )\n\n    class Meta:\n        description = (\n            f\"{ADDED_IN_31} Updates the delivery method \"\n            f\"(shipping method or pick up point) of the checkout. {PREVIEW_FEATURE}\"\n        )\n        error_type_class = CheckoutError\n\n    @classmethod\n    def perform_on_shipping_method(\n        cls, info, shipping_method_id, checkout_info, lines, checkout, manager\n    ):\n        shipping_method = cls.get_node_or_error(\n            info,\n            shipping_method_id,\n            only_type=ShippingMethod,\n            field=\"delivery_method_id\",\n            qs=shipping_models.ShippingMethod.objects.prefetch_related(\n                \"postal_code_rules\"\n            ),\n        )\n\n        delivery_method = convert_to_shipping_method_data(\n            shipping_method,\n            shipping_models.ShippingMethodChannelListing.objects.filter(\n                shipping_method=shipping_method,\n                channel=checkout_info.channel,\n            ).first(),\n        )\n        cls._check_delivery_method(\n            checkout_info, lines, shipping_method=delivery_method, collection_point=None\n        )\n\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=shipping_method,\n            external_shipping_method=None,\n            collection_point=None,\n        )\n        recalculate_checkout_discount(\n            manager, checkout_info, lines, info.context.discounts\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @classmethod\n    def perform_on_external_shipping_method(\n        cls, info, shipping_method_id, checkout_info, lines, checkout, manager\n    ):\n        delivery_method = manager.get_shipping_method(\n            checkout=checkout,\n            channel_slug=checkout.channel.slug,\n            shipping_method_id=shipping_method_id,\n        )\n\n        cls._check_delivery_method(\n            checkout_info, lines, shipping_method=delivery_method, collection_point=None\n        )\n\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=None,\n            external_shipping_method=delivery_method,\n            collection_point=None,\n        )\n        recalculate_checkout_discount(\n            manager, checkout_info, lines, info.context.discounts\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @classmethod\n    def perform_on_collection_point(\n        cls, info, collection_point_id, checkout_info, lines, checkout, manager\n    ):\n        collection_point = cls.get_node_or_error(\n            info,\n            collection_point_id,\n            only_type=Warehouse,\n            field=\"delivery_method_id\",\n            qs=warehouse_models.Warehouse.objects.select_related(\"address\"),\n        )\n        cls._check_delivery_method(\n            checkout_info,\n            lines,\n            shipping_method=None,\n            collection_point=collection_point,\n        )\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=None,\n            external_shipping_method=None,\n            collection_point=collection_point,\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @staticmethod\n    def _check_delivery_method(\n        checkout_info,\n        lines,\n        *,\n        shipping_method: Optional[shipping_interface.ShippingMethodData],\n        collection_point: Optional[Warehouse]\n    ) -> None:\n        delivery_method = shipping_method\n        error_msg = \"This shipping method is not applicable.\"\n\n        if collection_point is not None:\n            delivery_method = collection_point\n            error_msg = \"This pick up point is not applicable.\"\n\n        delivery_method_is_valid = clean_delivery_method(\n            checkout_info=checkout_info, lines=lines, method=delivery_method\n        )\n        if not delivery_method_is_valid:\n            raise ValidationError(\n                {\n                    \"delivery_method_id\": ValidationError(\n                        error_msg,\n                        code=CheckoutErrorCode.DELIVERY_METHOD_NOT_APPLICABLE.value,\n                    )\n                }\n            )\n\n    @staticmethod\n    def _update_delivery_method(\n        manager,\n        checkout: Checkout,\n        *,\n        shipping_method: Optional[ShippingMethod],\n        external_shipping_method: Optional[shipping_interface.ShippingMethodData],\n        collection_point: Optional[Warehouse]\n    ) -> None:\n        if external_shipping_method:\n            set_external_shipping_id(\n                checkout=checkout, app_shipping_id=external_shipping_method.id\n            )\n        else:\n            delete_external_shipping_id(checkout=checkout)\n        checkout.shipping_method = shipping_method\n        checkout.collection_point = collection_point\n        checkout.save(\n            update_fields=[\n                \"private_metadata\",\n                \"shipping_method\",\n                \"collection_point\",\n                \"last_change\",\n            ]\n        )\n        manager.checkout_updated(checkout)\n\n    @staticmethod\n    def _resolve_delivery_method_type(id_) -> Optional[str]:\n        if id_ is None:\n            return None\n\n        possible_types = (\"Warehouse\", \"ShippingMethod\", APP_ID_PREFIX)\n        type_, id_ = from_global_id_or_error(id_)\n        str_type = str(type_)\n\n        if str_type not in possible_types:\n            raise ValidationError(\n                {\n                    \"delivery_method_id\": ValidationError(\n                        \"ID does not belong to Warehouse or ShippingMethod\",\n                        code=CheckoutErrorCode.INVALID.value,\n                    )\n                }\n            )\n\n        return str_type\n\n    @classmethod\n    def perform_mutation(\n        cls,\n        _,\n        info,\n        token,\n        delivery_method_id=None,\n    ):\n\n        checkout = get_checkout_by_token(token)\n\n        manager = info.context.plugins\n        lines, unavailable_variant_pks = fetch_checkout_lines(checkout)\n        if unavailable_variant_pks:\n            not_available_variants_ids = {\n                graphene.Node.to_global_id(\"ProductVariant\", pk)\n                for pk in unavailable_variant_pks\n            }\n            raise ValidationError(\n                {\n                    \"lines\": ValidationError(\n                        \"Some of the checkout lines variants are unavailable.\",\n                        code=CheckoutErrorCode.UNAVAILABLE_VARIANT_IN_CHANNEL.value,\n                        params={\"variants\": not_available_variants_ids},\n                    )\n                }\n            )\n\n        if not is_shipping_required(lines):\n            raise ValidationError(\n                {\n                    \"delivery_method\": ValidationError(\n                        ERROR_DOES_NOT_SHIP,\n                        code=CheckoutErrorCode.SHIPPING_NOT_REQUIRED,\n                    )\n                }\n            )\n        type_name = cls._resolve_delivery_method_type(delivery_method_id)\n\n        checkout_info = fetch_checkout_info(\n            checkout, lines, info.context.discounts, manager\n        )\n        if type_name == \"Warehouse\":\n            return cls.perform_on_collection_point(\n                info, delivery_method_id, checkout_info, lines, checkout, manager\n            )\n        if type_name == \"ShippingMethod\":\n            return cls.perform_on_shipping_method(\n                info, delivery_method_id, checkout_info, lines, checkout, manager\n            )\n        return cls.perform_on_external_shipping_method(\n            info, delivery_method_id, checkout_info, lines, checkout, manager\n        )\n", "path": "saleor/graphql/checkout/mutations/checkout_delivery_method_update.py"}], "after_files": [{"content": "from typing import Optional\n\nimport graphene\nfrom django.core.exceptions import ValidationError\n\nfrom ....checkout.error_codes import CheckoutErrorCode\nfrom ....checkout.fetch import fetch_checkout_info, fetch_checkout_lines\nfrom ....checkout.utils import (\n    delete_external_shipping_id,\n    is_shipping_required,\n    recalculate_checkout_discount,\n    set_external_shipping_id,\n)\nfrom ....plugins.webhook.utils import APP_ID_PREFIX\nfrom ....shipping import interface as shipping_interface\nfrom ....shipping import models as shipping_models\nfrom ....shipping.utils import convert_to_shipping_method_data\nfrom ....warehouse import models as warehouse_models\nfrom ...core.descriptions import ADDED_IN_31, PREVIEW_FEATURE\nfrom ...core.mutations import BaseMutation\nfrom ...core.scalars import UUID\nfrom ...core.types import CheckoutError\nfrom ...core.utils import from_global_id_or_error\nfrom ...shipping.types import ShippingMethod\nfrom ...warehouse.types import Warehouse\nfrom ..types import Checkout\nfrom .utils import ERROR_DOES_NOT_SHIP, clean_delivery_method, get_checkout_by_token\n\n\nclass CheckoutDeliveryMethodUpdate(BaseMutation):\n    checkout = graphene.Field(Checkout, description=\"An updated checkout.\")\n\n    class Arguments:\n        token = UUID(description=\"Checkout token.\", required=False)\n        delivery_method_id = graphene.ID(\n            description=\"Delivery Method ID (`Warehouse` ID or `ShippingMethod` ID).\",\n            required=False,\n        )\n\n    class Meta:\n        description = (\n            f\"{ADDED_IN_31} Updates the delivery method \"\n            f\"(shipping method or pick up point) of the checkout. {PREVIEW_FEATURE}\"\n        )\n        error_type_class = CheckoutError\n\n    @classmethod\n    def perform_on_shipping_method(\n        cls, info, shipping_method_id, checkout_info, lines, checkout, manager\n    ):\n        shipping_method = cls.get_node_or_error(\n            info,\n            shipping_method_id,\n            only_type=ShippingMethod,\n            field=\"delivery_method_id\",\n            qs=shipping_models.ShippingMethod.objects.prefetch_related(\n                \"postal_code_rules\"\n            ),\n        )\n\n        delivery_method = convert_to_shipping_method_data(\n            shipping_method,\n            shipping_models.ShippingMethodChannelListing.objects.filter(\n                shipping_method=shipping_method,\n                channel=checkout_info.channel,\n            ).first(),\n        )\n        cls._check_delivery_method(\n            checkout_info, lines, shipping_method=delivery_method, collection_point=None\n        )\n\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=shipping_method,\n            external_shipping_method=None,\n            collection_point=None,\n        )\n        recalculate_checkout_discount(\n            manager, checkout_info, lines, info.context.discounts\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @classmethod\n    def perform_on_external_shipping_method(\n        cls, info, shipping_method_id, checkout_info, lines, checkout, manager\n    ):\n        delivery_method = manager.get_shipping_method(\n            checkout=checkout,\n            channel_slug=checkout.channel.slug,\n            shipping_method_id=shipping_method_id,\n        )\n\n        if delivery_method is None and shipping_method_id:\n            raise ValidationError(\n                {\n                    \"delivery_method_id\": ValidationError(\n                        f\"Couldn't resolve to a node: ${shipping_method_id}\",\n                        code=CheckoutErrorCode.NOT_FOUND,\n                    )\n                }\n            )\n\n        cls._check_delivery_method(\n            checkout_info, lines, shipping_method=delivery_method, collection_point=None\n        )\n\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=None,\n            external_shipping_method=delivery_method,\n            collection_point=None,\n        )\n        recalculate_checkout_discount(\n            manager, checkout_info, lines, info.context.discounts\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @classmethod\n    def perform_on_collection_point(\n        cls, info, collection_point_id, checkout_info, lines, checkout, manager\n    ):\n        collection_point = cls.get_node_or_error(\n            info,\n            collection_point_id,\n            only_type=Warehouse,\n            field=\"delivery_method_id\",\n            qs=warehouse_models.Warehouse.objects.select_related(\"address\"),\n        )\n        cls._check_delivery_method(\n            checkout_info,\n            lines,\n            shipping_method=None,\n            collection_point=collection_point,\n        )\n        cls._update_delivery_method(\n            manager,\n            checkout,\n            shipping_method=None,\n            external_shipping_method=None,\n            collection_point=collection_point,\n        )\n        return CheckoutDeliveryMethodUpdate(checkout=checkout)\n\n    @staticmethod\n    def _check_delivery_method(\n        checkout_info,\n        lines,\n        *,\n        shipping_method: Optional[shipping_interface.ShippingMethodData],\n        collection_point: Optional[Warehouse]\n    ) -> None:\n        delivery_method = shipping_method\n        error_msg = \"This shipping method is not applicable.\"\n\n        if collection_point is not None:\n            delivery_method = collection_point\n            error_msg = \"This pick up point is not applicable.\"\n\n        delivery_method_is_valid = clean_delivery_method(\n            checkout_info=checkout_info, lines=lines, method=delivery_method\n        )\n        if not delivery_method_is_valid:\n            raise ValidationError(\n                {\n                    \"delivery_method_id\": ValidationError(\n                        error_msg,\n                        code=CheckoutErrorCode.DELIVERY_METHOD_NOT_APPLICABLE.value,\n                    )\n                }\n            )\n\n    @staticmethod\n    def _update_delivery_method(\n        manager,\n        checkout: Checkout,\n        *,\n        shipping_method: Optional[ShippingMethod],\n        external_shipping_method: Optional[shipping_interface.ShippingMethodData],\n        collection_point: Optional[Warehouse]\n    ) -> None:\n        if external_shipping_method:\n            set_external_shipping_id(\n                checkout=checkout, app_shipping_id=external_shipping_method.id\n            )\n        else:\n            delete_external_shipping_id(checkout=checkout)\n        checkout.shipping_method = shipping_method\n        checkout.collection_point = collection_point\n        checkout.save(\n            update_fields=[\n                \"private_metadata\",\n                \"shipping_method\",\n                \"collection_point\",\n                \"last_change\",\n            ]\n        )\n        manager.checkout_updated(checkout)\n\n    @staticmethod\n    def _resolve_delivery_method_type(id_) -> Optional[str]:\n        if id_ is None:\n            return None\n\n        possible_types = (\"Warehouse\", \"ShippingMethod\", APP_ID_PREFIX)\n        type_, id_ = from_global_id_or_error(id_)\n        str_type = str(type_)\n\n        if str_type not in possible_types:\n            raise ValidationError(\n                {\n                    \"delivery_method_id\": ValidationError(\n                        \"ID does not belong to Warehouse or ShippingMethod\",\n                        code=CheckoutErrorCode.INVALID.value,\n                    )\n                }\n            )\n\n        return str_type\n\n    @classmethod\n    def perform_mutation(\n        cls,\n        _,\n        info,\n        token,\n        delivery_method_id=None,\n    ):\n\n        checkout = get_checkout_by_token(token)\n\n        manager = info.context.plugins\n        lines, unavailable_variant_pks = fetch_checkout_lines(checkout)\n        if unavailable_variant_pks:\n            not_available_variants_ids = {\n                graphene.Node.to_global_id(\"ProductVariant\", pk)\n                for pk in unavailable_variant_pks\n            }\n            raise ValidationError(\n                {\n                    \"lines\": ValidationError(\n                        \"Some of the checkout lines variants are unavailable.\",\n                        code=CheckoutErrorCode.UNAVAILABLE_VARIANT_IN_CHANNEL.value,\n                        params={\"variants\": not_available_variants_ids},\n                    )\n                }\n            )\n\n        if not is_shipping_required(lines):\n            raise ValidationError(\n                {\n                    \"delivery_method\": ValidationError(\n                        ERROR_DOES_NOT_SHIP,\n                        code=CheckoutErrorCode.SHIPPING_NOT_REQUIRED,\n                    )\n                }\n            )\n        type_name = cls._resolve_delivery_method_type(delivery_method_id)\n\n        checkout_info = fetch_checkout_info(\n            checkout, lines, info.context.discounts, manager\n        )\n        if type_name == \"Warehouse\":\n            return cls.perform_on_collection_point(\n                info, delivery_method_id, checkout_info, lines, checkout, manager\n            )\n        if type_name == \"ShippingMethod\":\n            return cls.perform_on_shipping_method(\n                info, delivery_method_id, checkout_info, lines, checkout, manager\n            )\n        return cls.perform_on_external_shipping_method(\n            info, delivery_method_id, checkout_info, lines, checkout, manager\n        )\n", "path": "saleor/graphql/checkout/mutations/checkout_delivery_method_update.py"}]}