Skip to main content

Upgrading From 3.20 To 3.21

info

To follow the zero-downtime strategy when upgrading to 3.21, It is recommended to first migrate to latest 3.20.X and turn on the Celery worker to process all data migrations asynchronously. Otherwise, you will need to downtime your solution to ensure correct data migration.

Authentication required for changing Order and OrderLine metadata​

The updateMetadata mutation for Order and OrderLine now requires the MANAGE_ORDERS permission. Once an order is created, customers cannot edit its metadata. However, merchants and apps with the necessary permissions can modify this data. Fetching metadata remains unchanged.

Merging metadata in transactionUpdate mutation​

The transactionUpdate mutation now merges the provided metadata and private metadata with existing values, instead of overwriting them. To delete a specific metadata entry, use the deleteMetadata mutation. For private metadata, use the deletePrivateMetadata mutation.

Deprecated manager.perform_mutation method​

Note: This change only affects open-source Saleor users with custom plugins overriding the perform_mutation plugin method.

As part of gradual plugin deprecation, Saleor 3.20 deprecates the perform_mutation plugin hook. This method allowed to customize the mutation execution flow and call arbitrary logic during mutation execution. From now on, we recommend building such customizations via apps by reacting to webhooks triggered by mutations. The perform_mutation plugin method will be removed in Saleor 3.21.

Note: This change only affects open-source Saleor users with custom plugins that override webhook-related plugin hooks (see the list below).

The WebhookPlugin is deprecated in Saleor 3.20 and will be removed in Saleor 3.21, as all webhook-related functionality will be moved from plugin to core modules. Rationale behind this change is that webhooks are essential part of Saleor and should be available out of the box, without the need for additional plugins. As a result, multiple plugin manager hooks will be deprecated, as they are only used by the WebhookPlugin.

If you have custom plugins that override any of the following plugin hooks, you should migrate your custom logic to Saleor App that listens to webhooks.

The list of deprecated plugin hooks is as follows:

  • account_change_email_requested
  • account_confirmation_requested
  • account_confirmed
  • account_delete_requested
  • account_deleted
  • account_email_changed
  • account_set_password_requested
  • address_created
  • address_deleted
  • address_updated
  • app_deleted
  • app_installed
  • app_status_changed
  • app_updated
  • attribute_created
  • attribute_deleted
  • attribute_updated
  • attribute_value_created
  • attribute_value_deleted
  • attribute_value_updated
  • category_created
  • category_deleted
  • category_updated
  • channel_created
  • channel_deleted
  • channel_metadata_updated
  • channel_status_changed
  • channel_updated
  • checkout_created
  • checkout_fully_paid
  • checkout_metadata_updated
  • checkout_updated
  • collection_created
  • collection_deleted
  • collection_metadata_updated
  • collection_updated
  • customer_created
  • customer_deleted
  • customer_metadata_updated
  • customer_updated
  • draft_order_created
  • draft_order_deleted
  • draft_order_updated
  • event_delivery_retry
  • fulfillment_approved
  • fulfillment_canceled
  • fulfillment_created
  • fulfillment_metadata_updated
  • get_shipping_methods_for_checkout
  • get_taxes_for_checkout
  • get_taxes_for_order
  • gift_card_created
  • gift_card_deleted
  • gift_card_export_completed
  • gift_card_metadata_updated
  • gift_card_status_changed
  • gift_card_updated
  • invoice_delete
  • invoice_sent
  • list_stored_payment_methods
  • menu_created
  • menu_deleted
  • menu_item_created
  • menu_item_deleted
  • menu_item_updated
  • menu_updated
  • order_bulk_created
  • order_cancelled
  • order_created
  • order_expired
  • order_fulfilled
  • order_fully_paid
  • order_fully_refunded
  • order_metadata_updated
  • order_paid
  • order_refunded
  • order_updated
  • page_created
  • page_deleted
  • page_type_created
  • page_type_deleted
  • page_type_updated
  • page_updated
  • payment_gateway_initialize_session
  • payment_gateway_initialize_tokenization
  • payment_method_initialize_tokenization
  • payment_method_process_tokenization
  • permission_group_created
  • permission_group_deleted
  • permission_group_updated
  • product_created
  • product_deleted
  • product_export_completed
  • product_media_created
  • product_media_deleted
  • product_media_updated
  • product_metadata_updated
  • product_updated
  • product_variant_back_in_stock
  • product_variant_created
  • product_variant_deleted
  • product_variant_metadata_updated
  • product_variant_out_of_stock
  • product_variant_stock_updated
  • product_variant_updated
  • promotion_created
  • promotion_deleted
  • promotion_ended
  • promotion_rule_created
  • promotion_rule_deleted
  • promotion_rule_updated
  • promotion_started
  • promotion_updated
  • sale_created
  • sale_deleted
  • sale_updated
  • shipping_price_created
  • shipping_price_deleted
  • shipping_price_updated
  • shipping_zone_created
  • shipping_zone_deleted
  • shipping_zone_metadata_updated
  • shipping_zone_updated
  • shop_metadata_updated
  • staff_created
  • staff_deleted
  • staff_set_password_requested
  • staff_updated
  • stored_payment_method_request_delete
  • thumbnail_created
  • transaction_cancelation_requested
  • transaction_charge_requested
  • transaction_initialize_session
  • transaction_item_metadata_updated
  • transaction_process_session
  • transaction_refund_requested
  • voucher_code_export_completed
  • voucher_codes_created
  • voucher_codes_deleted
  • voucher_created
  • voucher_deleted
  • voucher_metadata_updated
  • voucher_updated
  • warehouse_created
  • warehouse_deleted
  • warehouse_metadata_updated
  • warehouse_updated

checkout.shippingMethods and checkout.availableShippingMethods do not return external shipping methods if their currency differs from the checkout's currency​

If an app subscribed to SHIPPING_LIST_METHODS_FOR_CHECKOUT returns an external shipping method with a currency different from the checkout's currency, Saleor will filter out that shipping method. As a result, external shipping methods with a different currency than the checkout's currency will not be returned via the API (in the checkout.shippingMethods and checkout.availableShippingMethods fields). Any potential currency validation for external shipping methods on the frontend / app side is no longer necessary.

Blocked external calls for checkout and order bulk queries​

To enhance performance and efficiency, bulk queries that fetch multiple checkout or order objects will no longer trigger external calls to third-party services. The following synchronous webhooks will be affected:

  • CHECKOUT_CALCULATE_TAXES webhooks
  • ORDER_CALCULATE_TAXES webhooks
  • SHIPPING_LIST_METHODS_FOR_CHECKOUT webhooks
  • CHECKOUT_FILTER_SHIPPING_METHODS webhooks
  • ORDER_FILTER_SHIPPING_METHODS webhooks

External calls from the AvataxPlugin will also be skipped.

Instead of recalculating taxes externally, the system will return pre-stored prices from the database. Additionally, only built-in shipping methods will be returned. The CHECKOUT_FILTER_SHIPPING_METHODS and ORDER_FILTER_SHIPPING_METHODS webhooks will not be triggered, and all built-in shipping methods will be listed as available.

Any external shipping methods assigned to the checkout will still be visible through denormalized data stored in the database.

Affected queries​

The following queries will not trigger external calls:

  • checkouts(...)
  • me.checkouts(...)
  • checkoutLines(...)
  • orders(...)
  • me.orders(...)
  • draftOrders(...)

Example of impacted queries​

{
checkouts(first: 10) {
edges {
node {
id
deliveryMethod {
... on ShippingMethod {
id
name
}
}
shippingMethods {
id
name
}
totalPrice {
gross {
amount
}
}
}
}
}
}
Expand â–¼
{
orders(first: 100) {
edges {
node {
deliveryMethod {
... on ShippingMethod {
id
name
}
}
shippingMethods {
id
name
}
total {
gross {
amount
}
}
}
}
}
}
Expand â–¼
note

The checkout and order queries will still work as before. If tax information is outdated, external calls will be made to recalculate taxes. External shipping methods and filters will be fetched if necessary.

checkout.privateMetadata no longer stores external-shipping-method-id​

The external-shipping-method-id is no longer stored within checkout.privateMetadata. To retrieve this ID, use the checkout.deliveryMethod field instead.

checkoutDeliveryMethodUpdate and checkoutShippingMethodUpdate mutations do not trigger CHECKOUT_UPDATED webhook or tax invalidation when adding the same method already assigned​

Previously, the checkoutDeliveryMethodUpdate and checkoutShippingMethodUpdate mutations would trigger the CHECKOUT_UPDATED webhook and invalidate taxes regardless of whether the same delivery method was already assigned to the checkout. Starting from version 3.21, if the same method that is already assigned to the checkout is provided, the CHECKOUT_UPDATED webhook will not be triggered, and taxes will not be invalidated. From Saleor's perspective, nothing has changed, as the checkout remains in the same state as it was before the mutation was called.

Introduced price freeze period for editable orders​

In previous versions, all editable orders (those with DRAFT and UNCONFIRMED statuses) refreshed their base prices by fetching them from the current product catalog during each recalculation. Additionally, all discounts were applied based on the latest reward values.

To ensure price consistency over time, regardless of updates to the product catalog, Saleor has introduced a price freeze period. During this period, order lines use denormalized base prices instead of retrieving them from the catalog. More details can be found here

  • Draft orders: the base prices are frozen for 24 hours by default. This duration can be modified using the Channel.draftOrderLinePriceFreezePeriod setting.
  • Unconfirmed orders: the base prices remain frozen indefinitely, and this setting is not configurable.
  • Other orders: remain unchanged and cannot be recalculated.