diff --git a/integration-tests/http/__tests__/returns/returns.spec.ts b/integration-tests/http/__tests__/returns/returns.spec.ts index 05b7eafbcf..326dff1bec 100644 --- a/integration-tests/http/__tests__/returns/returns.spec.ts +++ b/integration-tests/http/__tests__/returns/returns.spec.ts @@ -420,6 +420,25 @@ medusaIntegrationTestRunner({ }) ) + result = await api.post( + `/admin/returns/${returnId}`, + { + location_id: location.id, + metadata: { hello: "world" }, + no_notification: true, + }, + adminHeaders + ) + + expect(result.data.return).toEqual( + expect.objectContaining({ + id: returnId, + location_id: location.id, + metadata: { hello: "world" }, + no_notification: true, + }) + ) + const item = order.items[0] result = await api.post( diff --git a/packages/admin-next/dashboard/src/hooks/api/returns.tsx b/packages/admin-next/dashboard/src/hooks/api/returns.tsx index 7c2359070b..440b60f876 100644 --- a/packages/admin-next/dashboard/src/hooks/api/returns.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/returns.tsx @@ -151,6 +151,29 @@ export const useRemoveReturnItem = ( }) } +export const useUpdateReturn = ( + id: string, + orderId: string, + options?: UseMutationOptions< + HttpTypes.AdminReturnResponse, + Error, + HttpTypes.AdminUpdateReturnRequest + > +) => { + return useMutation({ + mutationFn: (payload: HttpTypes.AdminUpdateReturnRequest) => { + return sdk.admin.return.updateRequest(id, payload) + }, + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.preview(orderId), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + export const useAddReturnShipping = ( id: string, orderId: string, diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx index 1b92a6b473..e5cbef46e5 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx @@ -37,6 +37,7 @@ import { useConfirmReturnRequest, useDeleteReturnShipping, useRemoveReturnItem, + useUpdateReturn, useUpdateReturnItem, useUpdateReturnShipping, } from "../../../../../hooks/api/returns" @@ -91,6 +92,8 @@ export const ReturnCreateForm = ({ const { mutateAsync: cancelReturnRequest, isPending: isCanceling } = useCancelReturnRequest(activeReturn.id, order.id) + const { mutateAsync: updateReturnRequest, isPending: isUpdating } = + useUpdateReturn(activeReturn.id, order.id) const { mutateAsync: addReturnShipping, isPending: isAddingReturnShipping } = useAddReturnShipping(activeReturn.id, order.id) @@ -122,7 +125,8 @@ export const ReturnCreateForm = ({ isDeletingReturnShipping || isAddingReturnItem || isRemovingReturnItem || - isUpdatingReturnItem + isUpdatingReturnItem || + isUpdating /** * FORM @@ -254,6 +258,10 @@ export const ReturnCreateForm = ({ setIsOpen("items", false) } + const onLocationChange = async (selectedLocationId: string) => { + await updateReturnRequest({ location_id: selectedLocationId }) + } + const onShippingOptionChange = async (selectedOptionId: string) => { const promises = preview.shipping_methods .map((s) => s.actions?.find((a) => a.action === "SHIPPING_ADD")?.id) @@ -477,6 +485,7 @@ export const ReturnCreateForm = ({ value={value} onChange={(v) => { onChange(v) + onLocationChange(v) }} {...field} options={(stock_locations ?? []).map( diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index 2d2a249076..1cb2fce952 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -46,6 +46,8 @@ export * from "./return/remove-return-shipping-method" export * from "./return/request-item-return" export * from "./return/update-receive-item-return-request" export * from "./return/update-request-item-return" +export * from "./return/update-return" export * from "./return/update-return-shipping-method" export * from "./update-order-change-actions" export * from "./update-tax-lines" + diff --git a/packages/core/core-flows/src/order/workflows/return/update-return.ts b/packages/core/core-flows/src/order/workflows/return/update-return.ts new file mode 100644 index 0000000000..6b3c423f89 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/return/update-return.ts @@ -0,0 +1,73 @@ +import { OrderChangeDTO, OrderWorkflow, ReturnDTO } from "@medusajs/types" +import { OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { updateReturnsStep } from "../../steps" +import { previewOrderChangeStep } from "../../steps/preview-order-change" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +const validationStep = createStep( + "validate-update-return", + async function ({ + orderChange, + orderReturn, + }: { + orderReturn: ReturnDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(orderReturn, "Return") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const updateReturnWorkflowId = "update-return" +export const updateReturnWorkflow = createWorkflow( + updateReturnWorkflowId, + function ( + input: WorkflowData + ): WorkflowData { + const orderReturn: ReturnDTO = useRemoteQueryStep({ + entry_point: "return", + fields: ["id", "status", "order_id", "canceled_at"], + variables: { id: input.return_id }, + list: false, + throw_if_key_not_found: true, + }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "version", "actions.*"], + variables: { + filters: { + order_id: orderReturn.order_id, + return_id: orderReturn.id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ orderReturn, orderChange }) + + const updateData = transform({ input }, ({ input }) => { + return { + id: input.return_id, + location_id: input.location_id, + no_notification: input.no_notification, + metadata: input.metadata, + } + }) + + updateReturnsStep([updateData]) + + return previewOrderChangeStep(orderReturn.order_id) + } +) diff --git a/packages/core/js-sdk/src/admin/return.ts b/packages/core/js-sdk/src/admin/return.ts index da29462e48..11e876e39c 100644 --- a/packages/core/js-sdk/src/admin/return.ts +++ b/packages/core/js-sdk/src/admin/return.ts @@ -1,4 +1,4 @@ -import { FindParams, HttpTypes, SelectParams } from "@medusajs/types" +import { HttpTypes, SelectParams } from "@medusajs/types" import { Client } from "../client" import { ClientHeaders } from "../types" @@ -178,4 +178,21 @@ export class Return { } ) } + + async updateRequest( + id: string, + body: HttpTypes.AdminUpdateReturnRequest, + query?: HttpTypes.SelectParams, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/returns/${id}`, + { + method: "POST", + headers, + body, + query, + } + ) + } } diff --git a/packages/core/types/src/http/return/admin.ts b/packages/core/types/src/http/return/admin.ts index 0709eed187..61e1c00f80 100644 --- a/packages/core/types/src/http/return/admin.ts +++ b/packages/core/types/src/http/return/admin.ts @@ -78,6 +78,12 @@ export interface AdminConfirmReturnRequest { no_notification?: boolean } +export interface AdminUpdateReturnRequest { + location_id?: string + no_notification?: boolean + metadata?: Record | null +} + export interface AdminReturnFilters extends FindParams { id?: string[] | string | OperatorMap order_id?: string[] | string | OperatorMap diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts index fabb96ac8a..005083f337 100644 --- a/packages/core/types/src/workflow/order/index.ts +++ b/packages/core/types/src/workflow/order/index.ts @@ -13,3 +13,5 @@ export * from "./items" export * from "./receive-return" export * from "./request-item-return" export * from "./shipping-method" +export * from "./update-return" + diff --git a/packages/core/types/src/workflow/order/update-return.ts b/packages/core/types/src/workflow/order/update-return.ts new file mode 100644 index 0000000000..af718332d8 --- /dev/null +++ b/packages/core/types/src/workflow/order/update-return.ts @@ -0,0 +1,6 @@ +export interface UpdateReturnWorkflowInput { + return_id: string + location_id?: string + no_notification?: boolean + metadata?: Record | null +} diff --git a/packages/medusa/src/api/admin/returns/[id]/route.ts b/packages/medusa/src/api/admin/returns/[id]/route.ts index 4b4e2a1269..6d1de9e4b1 100644 --- a/packages/medusa/src/api/admin/returns/[id]/route.ts +++ b/packages/medusa/src/api/admin/returns/[id]/route.ts @@ -1,3 +1,4 @@ +import { updateReturnWorkflow } from "@medusajs/core-flows" import { ContainerRegistrationKeys, remoteQueryObjectFromString, @@ -6,6 +7,7 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" +import { AdminPostReturnsReturnReqSchemaType } from "../validators" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -32,3 +34,34 @@ export const GET = async ( return: orderReturn, }) } + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const { id } = req.params + + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const { result } = await updateReturnWorkflow(req.scope).run({ + input: { return_id: id, ...req.validatedBody }, + }) + + const queryObject = remoteQueryObjectFromString({ + entryPoint: "return", + variables: { + id, + filters: { + ...req.filterableFields, + }, + }, + fields: req.remoteQueryConfig.fields, + }) + + const [orderReturn] = await remoteQuery(queryObject) + + res.json({ + order_preview: result, + return: orderReturn, + }) +} diff --git a/packages/medusa/src/api/admin/returns/middlewares.ts b/packages/medusa/src/api/admin/returns/middlewares.ts index 371741f8fc..cfe7eeb4bf 100644 --- a/packages/medusa/src/api/admin/returns/middlewares.ts +++ b/packages/medusa/src/api/admin/returns/middlewares.ts @@ -5,13 +5,14 @@ import * as QueryConfig from "./query-config" import { AdminGetOrdersOrderParams, AdminGetOrdersParams, + AdminPostCancelReturnReqSchema, AdminPostReceiveReturnItemsReqSchema, AdminPostReceiveReturnsReqSchema, - AdminPostCancelReturnReqSchema, AdminPostReturnsConfirmRequestReqSchema, AdminPostReturnsReqSchema, AdminPostReturnsRequestItemsActionReqSchema, AdminPostReturnsRequestItemsReqSchema, + AdminPostReturnsReturnReqSchema, AdminPostReturnsShippingActionReqSchema, AdminPostReturnsShippingReqSchema, } from "./validators" @@ -37,6 +38,17 @@ export const adminReturnRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["POST"], + matcher: "/admin/returns/:id", + middlewares: [ + validateAndTransformBody(AdminPostReturnsReturnReqSchema), + validateAndTransformQuery( + AdminGetOrdersOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, { method: ["POST"], matcher: "/admin/returns", diff --git a/packages/medusa/src/api/admin/returns/query-config.ts b/packages/medusa/src/api/admin/returns/query-config.ts index afb57b9fcf..871b1aaf45 100644 --- a/packages/medusa/src/api/admin/returns/query-config.ts +++ b/packages/medusa/src/api/admin/returns/query-config.ts @@ -7,6 +7,8 @@ export const defaultAdminReturnFields = [ "location_id", "order_version", "status", + "metadata", + "no_notification", "refund_amount", "created_at", "updated_at", diff --git a/packages/medusa/src/api/admin/returns/validators.ts b/packages/medusa/src/api/admin/returns/validators.ts index 84724bab8c..7f0aa033a7 100644 --- a/packages/medusa/src/api/admin/returns/validators.ts +++ b/packages/medusa/src/api/admin/returns/validators.ts @@ -50,6 +50,15 @@ export type AdminPostReturnsReqSchemaType = z.infer< typeof AdminPostReturnsReqSchema > +export const AdminPostReturnsReturnReqSchema = z.object({ + location_id: z.string().optional(), + no_notification: z.boolean().optional(), + metadata: z.record(z.unknown()).nullish(), +}) +export type AdminPostReturnsReturnReqSchemaType = z.infer< + typeof AdminPostReturnsReturnReqSchema +> + export const AdminPostOrderExchangesReqSchema = z.object({ order_id: z.string(), description: z.string().optional(),