From 71afb172a47066199241e9efabe1ec0970513775 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Thu, 20 Apr 2023 17:14:21 +0300 Subject: [PATCH] docs: added returns conceptual guide (#3889) --- docs/content/modules/orders/overview.mdx | 3 +- docs/content/modules/orders/returns.md | 137 +++++++++++++++++++++++ docs/content/modules/orders/swaps.md | 10 +- www/docs/sidebars.js | 7 +- 4 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 docs/content/modules/orders/returns.md diff --git a/docs/content/modules/orders/overview.mdx b/docs/content/modules/orders/overview.mdx index f10a6b6f8a..4a779efabe 100644 --- a/docs/content/modules/orders/overview.mdx +++ b/docs/content/modules/orders/overview.mdx @@ -278,12 +278,11 @@ Learn how order-related entities are built, their relation to other modules, and }, { type: 'link', - href: '#', + href: '/modules/orders/returns', label: 'Architecture: Return', customProps: { icon: Icons['circle-stack-solid'], description: 'Learn about the Return architecture.', - isSoon: true, } }, { diff --git a/docs/content/modules/orders/returns.md b/docs/content/modules/orders/returns.md new file mode 100644 index 0000000000..80559dfcdf --- /dev/null +++ b/docs/content/modules/orders/returns.md @@ -0,0 +1,137 @@ +--- +description: "Learn about returns, how the return process is implemented in the Medusa backend, and a return’s relation to other entities." +--- + +# Returns Architecture Overview + +In this document, you’ll learn about returns, how the return process is implemented in the Medusa backend, and a return’s relation to other entities. + +## Overview + +After a customer receives an item they ordered, they might want to return it back. This process includes returning the item into the commerce store, and refunding the payment back to the customer. + +The Medusa core provides the necessary implementation and functionalities that allow you to integrate this process into your store and automate the Return Merchandise Authorization (RMA) flow. + +--- + +## Return Entity Overview + +Some of the `Return` entity’s attributes include: + +- `status`: A string indicating the status of the return. Its value can be one of the following: + - `requested`: The customer requested the return. + - `requires_action`: The return couldn’t be marked as received because there’s a mismatch between the items specified in the return request and the received items. + - `received`: the merchant marks the return as received. + - `canceled`: the merchant canceled the return. +- `refund_amount`: A number indicating the amount that should be refunded. +- `received_at`: the date indicating when the return was marked as received. +- `no_notification`: a boolean value indicating whether the customer should receive notification updates when there are any changes in the return. +- `shipping_data`: this is a JSONB object that can hold any data related to the fulfillment associated with the return. + +There are other important attributes discussed in later sections. Check out the [full Return entity in the entities reference](../../references/entities/classes/Return.md). + +--- + +## Returns Process + +Returns can be created in three ways: + +1. Created directly, typically by a client or customer. +2. Created automatically and linked to a swap when a swap is created. You can learn more about the overall swap process in the Swaps documentation. The Mark Return as Received section applies for swaps as well. +3. Created automatically and linked to a claim when a claim is created. The Mark Return as Received section applies for claims as well. + +### Idempotency Key + +An Idempotency Key is a unique key associated with a return. It is generated when the return creation process is started by the customer. The idempotency key is stored in the `Return` entity in the attribute `idempotency_key`. + +That Idempotency Key is then set in the header under the `Idempotency-Key` response header field along with the header field `Access-Control-Expose-Headers` set to `Idempotency-Key`. + +The backend wraps essential parts of the return creation process in its own step and stores the current step (known as recovery point) of the process with its associated Idempotency Key. + +Then, if the request is interrupted for any reason or an error occurs, the client can retry creating the return using the Idempotency Key, and the flow will continue from the last stored step. + +### Return Creation Process Through the Customer + +This section explains how the return is created by the customer or any type of client. This is the process created within the Medusa core. + +The following process occurs within the Create Return storefront endpoint: + +![Return Client Process Flowchart](https://res.cloudinary.com/dza7lstvk/image/upload/v1681994516/Medusa%20Docs/Diagrams/return-client-process_evbjf5.jpg) + +1. The customer creates a return, specifying the items they want to return and optionally the shipping option they want to use to return the items. This is done using the `ReturnService`'s [create method](../../references/services/classes/ReturnService.md#create). +2. If they specify the return shipping option they want to use, a shipping method is created from the chosen shipping option and the return is automatically fulfilled in the backend using the `ReturnService`'s [fulfill method](../../references/services/classes/ReturnService.md#fulfill). + +After this process, the return will be available for the merchant or the admin to view and handle it. The merchant can either mark the return as received or cancel the return. + +### Mark Return as Received Process + +Marking a return as received would refund the amount to the customer and adjust the store’s inventory for the returned items. + +The following process occurs within the [Receive Return admin endpoint](/api/admin#tag/Returns/operation/PostReturnsReturnReceive): + +![Receive Return Process Flowchart](https://res.cloudinary.com/dza7lstvk/image/upload/v1681996834/Medusa%20Docs/Diagrams/return-admin-process_e99skk.jpg) + +1. The return is marked as received using the `ReturnService`'s receive method. In this method: + 1. The return’s status is checked to ensure it’s not canceled or received. If so, the process is terminated with an error. + 2. The received items are validated to ensure they match the items that were previously requested to be returned. If there’s a mismatch, the return’s status is set to `requires_more`. This is useful in situations where a custom refund amount is requested, but the returned items don’t match the requested items. + 3. If there’s no mismatch issue, the return’s status is set to `received`. + 4. The inventory of the returned items is adjusted using the `ProductVariantInventoryService`'s [method adjustInventory](../../references/services/classes/ProductVariantInventoryService.md#adjustinventory) +2. After the return is marked as received, if the return is associated with a swap, the `SwapService`'s [registerReceived method](../../references/services/classes/SwapService.md#registerreceived) is used to validate the return, emit the swap event `received`, and return the swap. +3. On the other hand, after the return is marked as received, if the return is associated with an order, the `OrderService`'s [registerReturnReceived method](../../references/services/classes/OrderService.md#registerreturnreceived). In this method: + 1. If the amount that is expected to be refunded is greater than the amount that can be refunded, the `fulfillment_status` of the order is set to `requires_action`. And the process is terminated. + 2. If there are no validation issues, the payment is refunded and the fulfillment status is set to `returned` if all items of the order were returned, or `partially_returned` if only some items were returned. + +### Cancel a Return + +The merchant can cancel a return if it hasn’t be marked as `received` before. This can be done either using the `ReturnService`'s [cancel method](../../references/services/classes/ReturnService.md#cancel) or using the [Cancel Return endpoint](/api/admin#tag/Returns/operation/PostReturnsReturnCancel). + +Canceling a return would change its status to canceled. + +--- + +## RMA Automation with Returns + +Giving the client control over the creation of a return allows businesses to automate their RMA flow. + +The client, typically the customer, creates the return, and specifies the shipping option they’ll use to return the item. The admin, typically the merchant, can view the requested return, mark it as received, and refund the payment to the customer. The core will also take care of adjusting the inventory automatically. + +![RMA Automation Flowchart](https://res.cloudinary.com/dza7lstvk/image/upload/v1681996158/Medusa%20Docs/Diagrams/rma-automation-return_prleib.jpg) + +This removes any friction and support required between the client and the admin. + +--- + +## Relation to Other Entities + +This section includes relations that weren’t mentioned in other sections. + +## Order + +The return can be associated with an [order](./orders.md). An order is represented by the `Order` entity. + +You can access the order’s ID using the `order_id` property. You can also access the order by expanding the `order` relation and accessing `return.order`. + +### Swap + +The return can be associated with a [swap](./swaps.md). A swap is represented by the `Swap` entity. + +You can access the swap’s ID using the `swap_id` property. You can also access the swap by expanding the `swap` relation and accessing `return.swap`. + +### ClaimOrder + +The return can be associated with a claim. A claim is represented by the `ClaimOrder` entity. + +You can access the claim’s ID using the `claim_order_id` property. You can also access the claim by expanding the `claim_order` relation and accessing `return.claim_order`. + +### ShippingMethod + +The return can be associated with a [shipping method](../carts-and-checkout/shipping.md#shipping-method). This would be the shipping method the customer chose when they created the swap. A shipping method is represented by the `ShippingMethod` entity. + +You can access the shipping method by expanding the `shipping_method` relation and accessing `return.shipping_method`. + +--- + +## See Also + +- [Orders architecture overview](./orders.md) +- [Swaps architecture overview](./swaps.md) diff --git a/docs/content/modules/orders/swaps.md b/docs/content/modules/orders/swaps.md index 3be625adee..2350d300e6 100644 --- a/docs/content/modules/orders/swaps.md +++ b/docs/content/modules/orders/swaps.md @@ -101,6 +101,12 @@ A swap’s fulfillments can be accessed by expanding the `fulfillments` relation --- +## Handling a Swap's Return + +A swap's return can be marked as received, which would adjust the inventory and change the status of the return. This process is explained within the [Returns documentation](./returns.md#mark-return-as-received-process). + +--- + ## Canceling a Swap A swap can be canceled by the merchant if necessary. It can be done either through the `SwapService`'s [cancel method](../../references/services/classes/SwapService.md#cancel) or the [Cancel Swap endpoint](/api/admin#tag/Orders/operation/PostOrdersSwapCancel). @@ -109,7 +115,7 @@ A swap can’t be canceled if: - The payment has been refunded (the `payment_status` is either `difference_refunded`, `partially_refunded`, or `refunded`). - Its fulfillment isn’t canceled. You’ll have to cancel the swap’s fulfillment as explained in the [Handling Swap Fulfillment section](#handling-swap-fulfillment). -- Its return isn’t canceled. You’ll have to cancel the return associated with the swap first before canceling the swap. +- Its return isn’t canceled. You’ll have to [cancel the return](./returns.md#cancel-a-return) associated with the swap first before canceling the swap. --- @@ -149,7 +155,7 @@ You can access the swap’s new items by expanding the `additional_items` relati ### Return -The swap is associated with a return that is created when the swap is created. A return is represented by the `Return` entity. +The swap is associated with a [return](./returns.md) that is created when the swap is created. A return is represented by the `Return` entity. You can access the swap’s return by expanding the `return_order` relation and accessing `swap.return_order`. diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 4788c742ee..36c325a029 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -727,12 +727,9 @@ module.exports = { label: "Swaps", }, { - type: "link", - href: "#", + type: "doc", + id: "modules/orders/returns", label: "Returns", - customProps: { - sidebar_is_soon: true, - }, }, { type: "link",