fix(medusa): Fix store return API (#12284)

what:

- fixes the middleware to point to the right API route
- adds a test to store return API
This commit is contained in:
Riqwan Thamir
2025-04-24 15:25:22 +02:00
committed by GitHub
parent 97daa5a41c
commit db9483140a
7 changed files with 255 additions and 8 deletions

View File

@@ -0,0 +1,246 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { Modules, RuleOperator } from "@medusajs/utils"
import {
adminHeaders,
createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
import { createAuthenticatedCustomer } from "../../../../modules/helpers/create-authenticated-customer"
import { createOrderSeeder } from "../../fixtures/order"
jest.setTimeout(300000)
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let order
let shippingProfile
let fulfillmentSet
let location
let item
let returnShippingOption
let storeHeadersWithCustomer, storeHeaders
const shippingProviderId = "manual_test-provider"
beforeEach(async () => {
const container = getContainer()
await setupTaxStructure(container.resolve(Modules.TAX))
await createAdminUser(dbConnection, adminHeaders, container)
const publishableKey = await generatePublishableKey(container)
storeHeaders = generateStoreHeaders({ publishableKey })
const result = await createAuthenticatedCustomer(api, storeHeaders, {
first_name: "tony",
last_name: "stark",
email: "tony@stark-industries.com",
})
storeHeadersWithCustomer = {
headers: {
...storeHeaders.headers,
authorization: `Bearer ${result.jwt}`,
},
}
const inventoryItemOverride = (
await api.post(
`/admin/inventory-items`,
{ sku: "test-variant", requires_shipping: false },
adminHeaders
)
).data.inventory_item
const seeders = await createOrderSeeder({
api,
container,
inventoryItemOverride,
withoutShipping: true,
})
order = seeders.order
shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{ name: "Test", type: "default" },
adminHeaders
)
).data.shipping_profile
location = (
await api.post(
`/admin/stock-locations`,
{ name: "Test location" },
adminHeaders
)
).data.stock_location
location = (
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
{ name: "Test", type: "test-type" },
adminHeaders
)
).data.stock_location
fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${location.fulfillment_sets[0].id}/service-zones`,
{
name: "Test",
geo_zones: [{ type: "country", country_code: "us" }],
},
adminHeaders
)
).data.fulfillment_set
const inventoryItem = (
await api.get(`/admin/inventory-items?sku=test-variant`, adminHeaders)
).data.inventory_items[0]
await api.post(
`/admin/inventory-items/${inventoryItem.id}/location-levels`,
{
location_id: location.id,
stocked_quantity: 10,
},
adminHeaders
)
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-providers`,
{ add: [shippingProviderId] },
adminHeaders
)
const shippingOptionPayload = {
name: "Return shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: shippingProviderId,
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [
{
currency_code: "usd",
amount: 15,
},
],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "true",
},
],
}
returnShippingOption = (
await api.post(
"/admin/shipping-options",
shippingOptionPayload,
adminHeaders
)
).data.shipping_option
item = order.items[0]
})
describe("POST /store/returns", () => {
it("should request a return", async () => {
let orderResult = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const returnReason = (
await api.post(
"/admin/return-reasons",
{
value: "return-reason-test",
label: "Test return reason",
},
adminHeaders
)
).data.return_reason
let fulfillableItem = orderResult.items.find(
(item) => item.detail.fulfilled_quantity < item.detail.quantity
)
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: location.id,
items: [
{
id: fulfillableItem.id,
quantity: item.detail.quantity - item.detail.fulfilled_quantity,
},
],
},
adminHeaders
)
orderResult = (await api.get(`/admin/orders/${order.id}`, adminHeaders))
.data.order
fulfillableItem = orderResult.items.find(
(item) => item.detail.fulfilled_quantity < item.detail.quantity
)
expect(fulfillableItem).toBeUndefined()
orderResult = (await api.get(`/admin/orders/${order.id}`, adminHeaders))
.data.order
const returnPayload = {
order_id: order.id,
items: [
{
id: order.items[0].id,
quantity: 1,
reason_id: returnReason.id,
note: "This is a test note",
},
],
return_shipping: {
option_id: returnShippingOption.id,
price: 100,
},
}
const returnResponse = (
await api.post(
"/store/returns",
returnPayload,
storeHeadersWithCustomer
)
).data.return
expect(returnResponse).toEqual(
expect.objectContaining({
order_id: order.id,
items: expect.arrayContaining([
expect.objectContaining({
quantity: 1,
reason_id: returnReason.id,
note: "This is a test note",
}),
]),
shipping_methods: [
expect.objectContaining({
shipping_option_id: returnShippingOption.id,
amount: 100,
}),
],
})
)
})
})
},
})

View File

@@ -53,11 +53,10 @@ import { storeOrderRoutesMiddlewares } from "./store/orders/middlewares"
import { storePaymentCollectionsMiddlewares } from "./store/payment-collections/middlewares"
import { storePaymentProvidersMiddlewares } from "./store/payment-providers/middlewares"
import { storeProductCategoryRoutesMiddlewares } from "./store/product-categories/middlewares"
import { storeProductRoutesMiddlewares } from "./store/products/middlewares"
import { storeProductTagRoutesMiddlewares } from "./store/product-tags/middlewares"
import { storeProductTypeRoutesMiddlewares } from "./store/product-types/middlewares"
import { storeProductRoutesMiddlewares } from "./store/products/middlewares"
import { storeRegionRoutesMiddlewares } from "./store/regions/middlewares"
import { storeReturnRoutesMiddlewares } from "./store/return/middlewares"
import { storeReturnReasonRoutesMiddlewares } from "./store/return-reasons/middlewares"
import { storeShippingOptionRoutesMiddlewares } from "./store/shipping-options/middlewares"
@@ -119,7 +118,6 @@ export default defineMiddlewares([
...adminReturnReasonRoutesMiddlewares,
...adminClaimRoutesMiddlewares,
...adminRefundReasonsRoutesMiddlewares,
...storeReturnRoutesMiddlewares,
...adminExchangeRoutesMiddlewares,
...adminProductVariantRoutesMiddlewares,
...adminOrderEditRoutesMiddlewares,

View File

@@ -1,3 +1,6 @@
import { MiddlewareRoute } from "@medusajs/framework/http"
import { storeReturnsRoutesMiddlewares } from "./returns/middlewares"
export const storeRoutesMiddlewares: MiddlewareRoute[] = []
export const storeRoutesMiddlewares: MiddlewareRoute[] = [
...storeReturnsRoutesMiddlewares,
]

View File

@@ -1,12 +1,12 @@
import { MiddlewareRoute } from "@medusajs/framework/http"
import {
validateAndTransformBody,
validateAndTransformQuery,
} from "@medusajs/framework"
import { MiddlewareRoute } from "@medusajs/framework/http"
import * as QueryConfig from "./query-config"
import { ReturnsParams, StorePostReturnsReqSchema } from "./validators"
export const storeReturnRoutesMiddlewares: MiddlewareRoute[] = [
export const storeReturnsRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["POST"],
matcher: "/store/returns",

View File

@@ -1,6 +1,6 @@
import { z } from "zod"
import { createFindParams, createSelectParams } from "../../utils/validators"
import { applyAndAndOrOperators } from "../../utils/common-validators"
import { createFindParams, createSelectParams } from "../../utils/validators"
export type ReturnParamsType = z.infer<typeof ReturnParams>
export const ReturnParams = createSelectParams()
@@ -28,7 +28,7 @@ const ItemSchema = z.object({
})
export const StorePostReturnsReqSchema = z.object({
order_id: z.string(),
order_id: z.string().min(1),
items: z.array(ItemSchema),
return_shipping: ReturnShippingSchema,
note: z.string().nullish(),