From f4d029adc7073b639b00a2e048edd1fa56edf99d Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 10:40:52 +0100 Subject: [PATCH 01/16] feat: decline and cancel workflows --- .../core-flows/src/order/workflows/index.ts | 2 + .../order-edit/cancel-begin-order-edit.ts | 4 +- .../transfer/cancel-order-transfer.ts | 93 +++++++++++++++++++ .../transfer/decline-order-transfer.ts | 87 +++++++++++++++++ .../src/workflow/order/cancel-transfer.ts | 4 + .../src/workflow/order/decline-transfer.ts | 4 + .../core/types/src/workflow/order/index.ts | 2 + 7 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts create mode 100644 packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts create mode 100644 packages/core/types/src/workflow/order/cancel-transfer.ts create mode 100644 packages/core/types/src/workflow/order/decline-transfer.ts diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index e7950a5d04f87..921c92687e41b 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -80,3 +80,5 @@ export * from "./update-order-changes" export * from "./update-tax-lines" export * from "./transfer/request-order-transfer" export * from "./transfer/accept-order-transfer" +export * from "./transfer/cancel-order-transfer" +export * from "./transfer/decline-order-transfer" diff --git a/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts b/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts index 55eaa88702f7e..85805e26e0125 100644 --- a/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts +++ b/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts @@ -41,7 +41,9 @@ export const cancelBeginOrderEditWorkflowId = "cancel-begin-order-edit" */ export const cancelBeginOrderEditWorkflow = createWorkflow( cancelBeginOrderEditWorkflowId, - function (input: CancelBeginOrderEditWorkflowInput): WorkflowData { + function ( + input: WorkflowData + ): WorkflowData { const order: OrderDTO = useRemoteQueryStep({ entry_point: "orders", fields: ["id", "version", "canceled_at"], diff --git a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts new file mode 100644 index 0000000000000..bbd567ae3bf97 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts @@ -0,0 +1,93 @@ +import { + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/framework/types" +import { + ChangeActionType, + MedusaError, + OrderChangeStatus, +} from "@medusajs/framework/utils" +import { + WorkflowData, + createStep, + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { deleteOrderChangesStep } from "../../steps" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +/** + * This step validates that a requested order transfer can be canceled. + */ +export const cancelTransferOrderRequestValidationStep = createStep( + "validate-cancel-transfer-order-request", + async function ({ + order, + orderChange, + input, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + input: OrderWorkflow.CancelTransferOrderRequestWorkflowInput + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + + if (input.logged_in_user_id.startsWith("user_")) { + return + } + + const action = orderChange.actions?.find( + (a) => a.action === ChangeActionType.TRANSFER_CUSTOMER + ) + + if (action?.reference_id !== input.logged_in_user_id) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + "This customer is not allowed to cancel the transfer." + ) + } + } +) + +export const cancelTransferOrderRequestWorkflowId = + "cancel-transfer-order-request" +/** + * This workflow cancels a requested order transfer. + * + * Customer that requested the transfer or a store admin are allowed to cancel the order transfer request. + */ +export const cancelOrderTransferRequestWorkflow = createWorkflow( + cancelTransferOrderRequestValidationStep, + function ( + input: WorkflowData + ): WorkflowData { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "version", "canceled_at"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + cancelTransferOrderRequestValidationStep({ order, orderChange, input }) + + deleteOrderChangesStep({ ids: [orderChange.id] }) + } +) diff --git a/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts new file mode 100644 index 0000000000000..2791a778e8934 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts @@ -0,0 +1,87 @@ +import { + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/framework/types" +import { + ChangeActionType, + MedusaError, + OrderChangeStatus, +} from "@medusajs/framework/utils" +import { + WorkflowData, + createStep, + createWorkflow, +} from "@medusajs/framework/workflows-sdk" + +import { useRemoteQueryStep } from "../../../common" +import { declineOrderChangeStep } from "../../steps" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +/** + * This step validates that a requested order transfer can be declineed. + */ +export const declineTransferOrderRequestValidationStep = createStep( + "validate-decline-transfer-order-request", + async function ({ + order, + orderChange, + input, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + input: OrderWorkflow.DeclineTransferOrderRequestWorkflowInput + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + + const token = orderChange.actions?.find( + (a) => a.action === ChangeActionType.TRANSFER_CUSTOMER + )?.details!.token + + if (!input.token.length || token !== input.token) { + throw new MedusaError(MedusaError.Types.NOT_ALLOWED, "Invalid token.") + } + } +) + +export const declineTransferOrderRequestWorkflowId = + "decline-transfer-order-request" +/** + * This workflow declines a requested order transfer. + * + * Only the original customer (who has the token) is allowed to decline the transfer. + */ +export const declineOrderTransferRequestWorkflow = createWorkflow( + declineTransferOrderRequestValidationStep, + function ( + input: WorkflowData + ): WorkflowData { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "version", "declineed_at"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + declineTransferOrderRequestValidationStep({ order, orderChange, input }) + + declineOrderChangeStep({ id: orderChange.id }) + } +) diff --git a/packages/core/types/src/workflow/order/cancel-transfer.ts b/packages/core/types/src/workflow/order/cancel-transfer.ts new file mode 100644 index 0000000000000..2030401e86a98 --- /dev/null +++ b/packages/core/types/src/workflow/order/cancel-transfer.ts @@ -0,0 +1,4 @@ +export type CancelTransferOrderRequestWorkflowInput = { + order_id: string + logged_in_user_id: string +} diff --git a/packages/core/types/src/workflow/order/decline-transfer.ts b/packages/core/types/src/workflow/order/decline-transfer.ts new file mode 100644 index 0000000000000..d727eb891e1ab --- /dev/null +++ b/packages/core/types/src/workflow/order/decline-transfer.ts @@ -0,0 +1,4 @@ +export type DeclineTransferOrderRequestWorkflowInput = { + order_id: string + token: string +} diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts index 46f8034777ab3..014f9f141128e 100644 --- a/packages/core/types/src/workflow/order/index.ts +++ b/packages/core/types/src/workflow/order/index.ts @@ -17,3 +17,5 @@ export * from "./shipping-method" export * from "./update-return" export * from "./request-transfer" export * from "./accept-transfer" +export * from "./cancel-transfer" +export * from "./decline-transfer" From 1e2ca2f51cbaa8eaa18afcfdbbc9bc638ae10959 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 10:49:17 +0100 Subject: [PATCH 02/16] feat: store endpoints --- .../orders/[id]/transfer/cancel/route.ts | 34 +++++++++++++++++++ .../orders/[id]/transfer/decline/route.ts | 29 ++++++++++++++++ .../src/api/store/orders/middlewares.ts | 25 ++++++++++++++ .../medusa/src/api/store/orders/validators.ts | 12 +++++++ 4 files changed, 100 insertions(+) create mode 100644 packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts create mode 100644 packages/medusa/src/api/store/orders/[id]/transfer/decline/route.ts diff --git a/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts new file mode 100644 index 0000000000000..0e962ee06c432 --- /dev/null +++ b/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts @@ -0,0 +1,34 @@ +import { + cancelOrderTransferRequestWorkflow, + getOrderDetailWorkflow, +} from "@medusajs/core-flows" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" +import { StoreCancelOrderTransferRequestType } from "../../../validators" + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const orderId = req.params.id + const customerId = req.auth_context.actor_id + + await cancelOrderTransferRequestWorkflow(req.scope).run({ + input: { + order_id: orderId, + logged_in_user_id: customerId, + }, + }) + + const { result } = await getOrderDetailWorkflow(req.scope).run({ + input: { + fields: req.remoteQueryConfig.fields, + order_id: orderId, + }, + }) + + res.status(200).json({ order: result as HttpTypes.StoreOrder }) +} diff --git a/packages/medusa/src/api/store/orders/[id]/transfer/decline/route.ts b/packages/medusa/src/api/store/orders/[id]/transfer/decline/route.ts new file mode 100644 index 0000000000000..f82f00d9fe0ff --- /dev/null +++ b/packages/medusa/src/api/store/orders/[id]/transfer/decline/route.ts @@ -0,0 +1,29 @@ +import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework" +import { HttpTypes } from "@medusajs/framework/types" +import { + declineOrderTransferRequestWorkflow, + getOrderDetailWorkflow, +} from "@medusajs/core-flows" + +import { StoreDeclineOrderTransferRequestType } from "../../../validators" + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + await declineOrderTransferRequestWorkflow(req.scope).run({ + input: { + order_id: req.params.id, + token: req.validatedBody.token, + }, + }) + + const { result } = await getOrderDetailWorkflow(req.scope).run({ + input: { + fields: req.remoteQueryConfig.fields, + order_id: req.params.id, + }, + }) + + res.status(200).json({ order: result as HttpTypes.StoreOrder }) +} diff --git a/packages/medusa/src/api/store/orders/middlewares.ts b/packages/medusa/src/api/store/orders/middlewares.ts index a79dd7a71d225..a107a0de63455 100644 --- a/packages/medusa/src/api/store/orders/middlewares.ts +++ b/packages/medusa/src/api/store/orders/middlewares.ts @@ -10,6 +10,8 @@ import { StoreGetOrdersParams, StoreAcceptOrderTransfer, StoreRequestOrderTransfer, + StoreCancelOrderTransferRequest, + StoreDeclineOrderTransferRequest, } from "./validators" export const storeOrderRoutesMiddlewares: MiddlewareRoute[] = [ @@ -46,6 +48,18 @@ export const storeOrderRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["POST"], + matcher: "/store/orders/:id/transfer/cancel", + middlewares: [ + authenticate("customer", ["session", "bearer"]), + validateAndTransformBody(StoreCancelOrderTransferRequest), + validateAndTransformQuery( + StoreGetOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, { method: ["POST"], matcher: "/store/orders/:id/transfer/accept", @@ -57,4 +71,15 @@ export const storeOrderRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["POST"], + matcher: "/store/orders/:id/transfer/decline", + middlewares: [ + validateAndTransformBody(StoreDeclineOrderTransferRequest), + validateAndTransformQuery( + StoreGetOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, ] diff --git a/packages/medusa/src/api/store/orders/validators.ts b/packages/medusa/src/api/store/orders/validators.ts index a4c297c624e74..8f7a46444965d 100644 --- a/packages/medusa/src/api/store/orders/validators.ts +++ b/packages/medusa/src/api/store/orders/validators.ts @@ -33,3 +33,15 @@ export type StoreRequestOrderTransferType = z.infer< export const StoreRequestOrderTransfer = z.object({ description: z.string().optional(), }) + +export type StoreCancelOrderTransferRequestType = z.infer< + typeof StoreCancelOrderTransferRequest +> +export const StoreCancelOrderTransferRequest = z.object({}) + +export type StoreDeclineOrderTransferRequestType = z.infer< + typeof StoreDeclineOrderTransferRequest +> +export const StoreDeclineOrderTransferRequest = z.object({ + token: z.string().min(1), +}) From 412137077d30d6cdae02a21f3f0218c6c9bf3b68 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 10:52:12 +0100 Subject: [PATCH 03/16] feat: admin endpoint --- .../orders/[id]/transfer/cancel/route.ts | 34 +++++++++++++++++++ .../medusa/src/api/admin/orders/validators.ts | 6 ++++ 2 files changed, 40 insertions(+) create mode 100644 packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts diff --git a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts new file mode 100644 index 0000000000000..592ceefc8aea0 --- /dev/null +++ b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts @@ -0,0 +1,34 @@ +import { + cancelOrderTransferRequestWorkflow, + getOrderDetailWorkflow, +} from "@medusajs/core-flows" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" +import { AdminCancelOrderTransferRequestType } from "../../../validators" + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const orderId = req.params.id + const userId = req.auth_context.actor_id + + await cancelOrderTransferRequestWorkflow(req.scope).run({ + input: { + order_id: orderId, + logged_in_user_id: userId, + }, + }) + + const { result } = await getOrderDetailWorkflow(req.scope).run({ + input: { + fields: req.remoteQueryConfig.fields, + order_id: orderId, + }, + }) + + res.status(200).json({ order: result as HttpTypes.StoreOrder }) +} diff --git a/packages/medusa/src/api/admin/orders/validators.ts b/packages/medusa/src/api/admin/orders/validators.ts index 04fbd26758969..303e460b029b3 100644 --- a/packages/medusa/src/api/admin/orders/validators.ts +++ b/packages/medusa/src/api/admin/orders/validators.ts @@ -119,6 +119,7 @@ export type AdminOrderChangesType = z.infer export type AdminMarkOrderFulfillmentDeliveredType = z.infer< typeof AdminMarkOrderFulfillmentDelivered > + export const AdminMarkOrderFulfillmentDelivered = z.object({}) export type AdminTransferOrderType = z.infer @@ -127,3 +128,8 @@ export const AdminTransferOrder = z.object({ description: z.string().optional(), internal_note: z.string().optional(), }) + +export type AdminCancelOrderTransferRequestType = z.infer< + typeof AdminCancelOrderTransferRequest +> +export const AdminCancelOrderTransferRequest = z.object({}) From 87b78dc78cf4afd595cdb5bdc1a44bf286e49db9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 11:46:26 +0100 Subject: [PATCH 04/16] fix: workflow registration and admin endpoint --- .../order/admin/transfer-flow.spec.ts | 53 +++++++++++++++++++ .../transfer/accept-order-transfer.ts | 8 +-- .../transfer/cancel-order-transfer.ts | 2 +- .../transfer/decline-order-transfer.ts | 2 +- .../orders/[id]/transfer/cancel/route.ts | 25 +++++---- 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index 80455cc919d69..e8967d825833f 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -141,6 +141,59 @@ medusaIntegrationTestRunner({ expect(finalOrderResult.customer_id).toEqual(customer.id) }) + it("should cancel an order transfer request from admin successfully", async () => { + await api.post( + `/admin/orders/${order.id}/transfer`, + { + customer_id: customer.id, + }, + adminHeaders + ) + + await api.get( + `/admin/orders/${order.id}?fields=+customer_id,+email`, + adminHeaders + ) + + let orderPreviewResult = ( + await api.get(`/admin/orders/${order.id}/preview`, adminHeaders) + ).data.order + + expect(orderPreviewResult).toEqual( + expect.objectContaining({ + customer_id: customer.id, + order_change: expect.objectContaining({ + change_type: "transfer", + status: "requested", + requested_by: user.id, + }), + }) + ) + + await api.post( + `/admin/orders/${order.id}/transfer/cancel`, + {}, + adminHeaders + ) + + orderPreviewResult = ( + await api.get(`/admin/orders/${order.id}/preview`, adminHeaders) + ).data.order + + expect(orderPreviewResult).toEqual( + expect.objectContaining({ + customer_id: customer.id, + order_change: null, + }) + ) + + const orderChangesResult = ( + await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) + ).data.order_changes + + expect(orderChangesResult.length).toEqual(0) + }) + it("should fail to request order transfer to a guest customer", async () => { const customer = ( await api.post( diff --git a/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts index ccbe4d78cd7d2..1ae95b230b404 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts @@ -10,15 +10,15 @@ import { createWorkflow, } from "@medusajs/framework/workflows-sdk" import { OrderPreviewDTO } from "@medusajs/types" - -import { useRemoteQueryStep } from "../../../common" -import { throwIfOrderIsCancelled } from "../../utils/order-validation" -import { previewOrderChangeStep } from "../../steps" import { ChangeActionType, MedusaError, OrderChangeStatus, } from "@medusajs/utils" + +import { useRemoteQueryStep } from "../../../common" +import { throwIfOrderIsCancelled } from "../../utils/order-validation" +import { previewOrderChangeStep } from "../../steps" import { confirmOrderChanges } from "../../steps/confirm-order-changes" /** diff --git a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts index bbd567ae3bf97..21017969084b0 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts @@ -62,7 +62,7 @@ export const cancelTransferOrderRequestWorkflowId = * Customer that requested the transfer or a store admin are allowed to cancel the order transfer request. */ export const cancelOrderTransferRequestWorkflow = createWorkflow( - cancelTransferOrderRequestValidationStep, + cancelTransferOrderRequestWorkflowId, function ( input: WorkflowData ): WorkflowData { diff --git a/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts index 2791a778e8934..f265426a37329 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts @@ -56,7 +56,7 @@ export const declineTransferOrderRequestWorkflowId = * Only the original customer (who has the token) is allowed to decline the transfer. */ export const declineOrderTransferRequestWorkflow = createWorkflow( - declineTransferOrderRequestValidationStep, + declineTransferOrderRequestWorkflowId, function ( input: WorkflowData ): WorkflowData { diff --git a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts index 592ceefc8aea0..81e8c887def67 100644 --- a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts @@ -1,18 +1,21 @@ -import { - cancelOrderTransferRequestWorkflow, - getOrderDetailWorkflow, -} from "@medusajs/core-flows" +import { cancelOrderTransferRequestWorkflow } from "@medusajs/core-flows" import { AuthenticatedMedusaRequest, MedusaResponse, } from "@medusajs/framework/http" import { HttpTypes } from "@medusajs/framework/types" import { AdminCancelOrderTransferRequestType } from "../../../validators" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/framework/utils" export const POST = async ( req: AuthenticatedMedusaRequest, - res: MedusaResponse + res: MedusaResponse ) => { + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const orderId = req.params.id const userId = req.auth_context.actor_id @@ -23,12 +26,12 @@ export const POST = async ( }, }) - const { result } = await getOrderDetailWorkflow(req.scope).run({ - input: { - fields: req.remoteQueryConfig.fields, - order_id: orderId, - }, + const queryObject = remoteQueryObjectFromString({ + entryPoint: "order", + variables: { id: orderId }, + fields: req.remoteQueryConfig.fields, }) - res.status(200).json({ order: result as HttpTypes.StoreOrder }) + const [order] = await remoteQuery(queryObject) + res.status(200).json({ order }) } From ff52e90d1c900e8d136166ef51e9440e823d018b Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 11:48:36 +0100 Subject: [PATCH 05/16] fix: admin cancel route middleware --- packages/medusa/src/api/admin/orders/middlewares.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/medusa/src/api/admin/orders/middlewares.ts b/packages/medusa/src/api/admin/orders/middlewares.ts index ece0980b55b6b..ba04cbd7d4120 100644 --- a/packages/medusa/src/api/admin/orders/middlewares.ts +++ b/packages/medusa/src/api/admin/orders/middlewares.ts @@ -5,6 +5,7 @@ import { import { MiddlewareRoute } from "@medusajs/framework/http" import * as QueryConfig from "./query-config" import { + AdminCancelOrderTransferRequest, AdminCompleteOrder, AdminGetOrdersOrderItemsParams, AdminGetOrdersOrderParams, @@ -156,4 +157,15 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["POST"], + matcher: "/admin/orders/:id/transfer/cancel", + middlewares: [ + validateAndTransformBody(AdminCancelOrderTransferRequest), + validateAndTransformQuery( + AdminGetOrdersOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, ] From 41b8a70591d1a113e5307c9776a36353d0ccf05e Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 11:57:10 +0100 Subject: [PATCH 06/16] feat: admin cancel transfer test --- .../http/__tests__/order/admin/transfer-flow.spec.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index e8967d825833f..375de40de4143 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -180,12 +180,7 @@ medusaIntegrationTestRunner({ await api.get(`/admin/orders/${order.id}/preview`, adminHeaders) ).data.order - expect(orderPreviewResult).toEqual( - expect.objectContaining({ - customer_id: customer.id, - order_change: null, - }) - ) + expect(orderPreviewResult.order_change).not.toBeDefined() const orderChangesResult = ( await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) From a4907f221fa9a18e47951eb946d2350a1758a5eb Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 12:58:16 +0100 Subject: [PATCH 07/16] feat: storefront cancel test --- .../order/admin/transfer-flow.spec.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index 375de40de4143..0fb2afe59a77a 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -392,6 +392,67 @@ medusaIntegrationTestRunner({ // 4. Customer account is now associated with the order (email on the order is still as original, guest email) expect(finalOrder.customer_id).toEqual(customer.id) }) + + it("user should be able to cancel their own transfer request", async () => { + await api.post( + `/store/orders/${order.id}/transfer/request`, + {}, + { + headers: { + authorization: `Bearer ${signInToken}`, + ...storeHeaders.headers, + }, + } + ) + + let orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "requested", + requested_by: customer.id, + created_by: customer.id, + confirmed_by: null, + confirmed_at: null, + declined_by: null, + actions: expect.arrayContaining([ + expect.objectContaining({ + version: 2, + action: "TRANSFER_CUSTOMER", + reference: "customer", + reference_id: customer.id, + details: expect.objectContaining({ + token: expect.any(String), + original_email: "tony@stark-industries.com", + }), + }), + ]), + }) + ) + + await api.post( + `/store/orders/${order.id}/transfer/cancel`, + {}, + { + headers: { + authorization: `Bearer ${signInToken}`, + ...storeHeaders.headers, + }, + } + ) + + orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(0) + }) }) }, }) From e74b1d24f09212eac8b356cecee19a868972a0b4 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 13:30:13 +0100 Subject: [PATCH 08/16] feat: test admin declines customers request --- .../order/admin/transfer-flow.spec.ts | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index 0fb2afe59a77a..39a90da6349c6 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -393,7 +393,64 @@ medusaIntegrationTestRunner({ expect(finalOrder.customer_id).toEqual(customer.id) }) - it("user should be able to cancel their own transfer request", async () => { + it("customer should be able to cancel their own transfer request", async () => { + await api.post( + `/store/orders/${order.id}/transfer/request`, + {}, + { + headers: { + authorization: `Bearer ${signInToken}`, + ...storeHeaders.headers, + }, + } + ) + + let orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "requested", + requested_by: customer.id, + created_by: customer.id, + confirmed_by: null, + confirmed_at: null, + declined_by: null, + actions: expect.arrayContaining([ + expect.objectContaining({ + version: 2, + action: "TRANSFER_CUSTOMER", + reference: "customer", + reference_id: customer.id, + details: expect.objectContaining({ + token: expect.any(String), + original_email: "tony@stark-industries.com", + }), + }), + ]), + }) + ) + + // Admin cancels the transfer request + await api.post( + `/admin/orders/${order.id}/transfer/cancel`, + {}, + adminHeaders + ) + + orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(0) + }) + + it("admin should be able to cancel a transfer request from a customer", async () => { await api.post( `/store/orders/${order.id}/transfer/request`, {}, From cc5152d1f64d6e301a51585bd0681a4907ff225a Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 13:45:45 +0100 Subject: [PATCH 09/16] feat: rest of "decline" tests --- .../order/admin/transfer-flow.spec.ts | 151 +++++++++++++++++- 1 file changed, 147 insertions(+), 4 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index 39a90da6349c6..7cb18870e7ca0 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -442,10 +442,9 @@ medusaIntegrationTestRunner({ adminHeaders ) - orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + orderChanges = await orderModule.listOrderChanges({ + order_id: order.id, + }) expect(orderChanges.length).toEqual(0) }) @@ -510,6 +509,150 @@ medusaIntegrationTestRunner({ expect(orderChanges.length).toEqual(0) }) + + it("original customer should be able to decline a transfer request", async () => { + await api.post( + `/store/orders/${order.id}/transfer/request`, + {}, + { + headers: { + authorization: `Bearer ${signInToken}`, + ...storeHeaders.headers, + }, + } + ) + + let orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "requested", + requested_by: customer.id, + created_by: customer.id, + confirmed_by: null, + confirmed_at: null, + declined_by: null, + actions: expect.arrayContaining([ + expect.objectContaining({ + version: 2, + action: "TRANSFER_CUSTOMER", + reference: "customer", + reference_id: customer.id, + details: expect.objectContaining({ + token: expect.any(String), + original_email: "tony@stark-industries.com", + }), + }), + ]), + }) + ) + + await api.post( + `/store/orders/${order.id}/transfer/decline`, + { token: orderChanges[0].actions[0].details.token }, + { + headers: { + ...storeHeaders.headers, + }, + } + ) + + orderChanges = await orderModule.listOrderChanges({ + order_id: order.id, + }) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "declined", + requested_by: customer.id, + created_by: customer.id, + declined_at: expect.any(Date), + }) + ) + }) + + it("shound not decline a transfer request without proper token", async () => { + await api.post( + `/store/orders/${order.id}/transfer/request`, + {}, + { + headers: { + authorization: `Bearer ${signInToken}`, + ...storeHeaders.headers, + }, + } + ) + + let orderChanges = await orderModule.listOrderChanges( + { order_id: order.id }, + { relations: ["actions"] } + ) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "requested", + requested_by: customer.id, + created_by: customer.id, + confirmed_by: null, + confirmed_at: null, + declined_by: null, + actions: expect.arrayContaining([ + expect.objectContaining({ + version: 2, + action: "TRANSFER_CUSTOMER", + reference: "customer", + reference_id: customer.id, + details: expect.objectContaining({ + token: expect.any(String), + original_email: "tony@stark-industries.com", + }), + }), + ]), + }) + ) + + const error = await api + .post( + `/store/orders/${order.id}/transfer/decline`, + { token: "fake-token" }, + { + headers: { + ...storeHeaders.headers, + }, + } + ) + .catch((e) => e) + + expect(error.response.status).toBe(400) + expect(error.response.data).toEqual( + expect.objectContaining({ + type: "not_allowed", + message: "Invalid token.", + }) + ) + + orderChanges = await orderModule.listOrderChanges({ + order_id: order.id, + }) + + expect(orderChanges.length).toEqual(1) + expect(orderChanges[0]).toEqual( + expect.objectContaining({ + change_type: "transfer", + status: "requested", + declined_at: null, + }) + ) + }) }) }, }) From 3c2a409bfc812abfa0a1607671720744e2511c47 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 14:09:52 +0100 Subject: [PATCH 10/16] feat: js-sdk store methods --- .../transfer/request-order-transfer.ts | 2 +- packages/core/js-sdk/src/store/index.ts | 156 ++++++++++++++++++ .../core/types/src/http/order/store/index.ts | 1 + .../types/src/http/order/store/payloads.ts | 14 ++ .../medusa/src/api/store/orders/validators.ts | 7 +- 5 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 packages/core/types/src/http/order/store/payloads.ts diff --git a/packages/core/core-flows/src/order/workflows/transfer/request-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/request-order-transfer.ts index dabda451f843c..55b55998f7eb3 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/request-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/request-order-transfer.ts @@ -131,7 +131,7 @@ export const requestOrderTransferWorkflow = createWorkflow( emitEventStep({ eventName: OrderWorkflowEvents.TRANSFER_REQUESTED, - data: { id: input.order_id }, + data: { id: input.order_id, order_change_id: change.id }, }) return new WorkflowResponse(previewOrderChangeStep(input.order_id)) diff --git a/packages/core/js-sdk/src/store/index.ts b/packages/core/js-sdk/src/store/index.ts index 78e0f9c79fb54..d475427e905b2 100644 --- a/packages/core/js-sdk/src/store/index.ts +++ b/packages/core/js-sdk/src/store/index.ts @@ -1077,6 +1077,162 @@ export class Store { } ) }, + + /** + * This method requests a order transfer from a guest account to the current, logged in customer account. + * + * Customer requesting the transfer must be logged in. + * + * @param body - The transfer's details. + * @param query - Configure the fields to retrieve in the order. + * @param headers - Headers to pass in the request. + * @returns The order details. + * + * @example + * sdk.store.order.requestTransfer( + * "order_123", + * { + * description: "I want to transfer this order to my friend." + * }, + * {}, + * { + * Authorization: `Bearer ${token}` + * } + * ) + * .then(({ order }) => { + * console.log(order) + * }) + */ + requestTransfer: async ( + id: string, + body: HttpTypes.StoreRequestOrderTransfer, + query?: SelectParams, + headers?: ClientHeaders + ) => { + return this.client.fetch( + `/store/orders/${id}/transfer/request`, + { + method: "POST", + headers, + body, + query, + } + ) + }, + /** + * This method cancels an order transfer request. + * + * Customer requesting the transfer must be logged in. + * + * @param id - The order's ID. + * @param query - Configure the fields to retrieve in the order. + * @param headers - Headers to pass in the request. + * @returns The order details. + * + * @example + * sdk.store.order.cancelTransfer( + * "order_123", + * {}, + * { + * Authorization: `Bearer ${token}` + * } + * ) + * .then(({ order }) => { + * console.log(order) + * }) + */ + cancelTransfer: async ( + id: string, + query?: SelectParams, + headers?: ClientHeaders + ) => { + return this.client.fetch( + `/store/orders/${id}/transfer/cancel`, + { + method: "POST", + headers, + query, + } + ) + }, + /** + * The method called for the original owner to accept the order transfer to a new owner. + * + * @param id - The order's ID. + * @param body - The payload containing the transfer token. + * @param query - Configure the fields to retrieve in the order. + * @param headers - Headers to pass in the request. + * @returns The order details. + * + * @example + * sdk.store.order.acceptTransfer( + * "order_123", + * { + * token: "transfer_token" + * }, + * { + * Authorization: `Bearer ${token}` + * } + * ) + * .then(({ order }) => { + * console.log(order) + * }) + */ + acceptTransfer: async ( + id: string, + body: HttpTypes.StoreAcceptOrderTransfer, + query?: SelectParams, + headers?: ClientHeaders + ) => { + return this.client.fetch( + `/store/orders/${id}/transfer/accept`, + { + method: "POST", + headers, + body, + query, + } + ) + }, + /** + * The method called for the original owner to decline the order transfer to a new owner. + * + * @param id - The order's ID. + * @param body - The payload containing the transfer token. + * @param query - Configure the fields to retrieve in the order. + * @param headers - Headers to pass in the request. + * @returns The order details. + * + * @example + * sdk.store.order.declineTransfer( + * "order_123", + * { + * token: "transfer_token" + * }, + * { + * Authorization: `Bearer ${token}` + * } + * ) + * .then(({ order }) => { + * console.log(order) + * }) + */ + declineTransfer: async ( + id: string, + body: HttpTypes.StoreDeclineOrderTransfer, + query?: SelectParams, + headers?: ClientHeaders + ) => { + return this.client.fetch( + `/store/orders/${id}/transfer/decline`, + { + method: "POST", + headers, + body, + query, + } + ) + }, } public customer = { diff --git a/packages/core/types/src/http/order/store/index.ts b/packages/core/types/src/http/order/store/index.ts index 020c34f02c710..e236f0b40fef3 100644 --- a/packages/core/types/src/http/order/store/index.ts +++ b/packages/core/types/src/http/order/store/index.ts @@ -1,3 +1,4 @@ export * from "./entities" export * from "./queries" export * from "./responses" +export * from "./payloads" diff --git a/packages/core/types/src/http/order/store/payloads.ts b/packages/core/types/src/http/order/store/payloads.ts new file mode 100644 index 0000000000000..e609f83a236b1 --- /dev/null +++ b/packages/core/types/src/http/order/store/payloads.ts @@ -0,0 +1,14 @@ +export interface StoreRequestOrderTransfer { + /** + * The description of the transfer request. + */ + description?: string +} + +export interface StoreAcceptOrderTransfer { + token: string +} + +export interface StoreDeclineOrderTransfer { + token: string +} diff --git a/packages/medusa/src/api/store/orders/validators.ts b/packages/medusa/src/api/store/orders/validators.ts index 8f7a46444965d..91ee447940813 100644 --- a/packages/medusa/src/api/store/orders/validators.ts +++ b/packages/medusa/src/api/store/orders/validators.ts @@ -19,13 +19,12 @@ export const StoreGetOrdersParams = createFindParams({ export type StoreGetOrdersParamsType = z.infer -export const StoreAcceptOrderTransfer = z.object({ - token: z.string().min(1), -}) - export type StoreAcceptOrderTransferType = z.infer< typeof StoreAcceptOrderTransfer > +export const StoreAcceptOrderTransfer = z.object({ + token: z.string().min(1), +}) export type StoreRequestOrderTransferType = z.infer< typeof StoreRequestOrderTransfer From 31e3f6323ba1a7088b0d6f8a862d10063cdcd8c5 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 14:17:21 +0100 Subject: [PATCH 11/16] fix: rename test --- .../http/__tests__/order/admin/transfer-flow.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index 7cb18870e7ca0..cb4cc45235249 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -393,7 +393,7 @@ medusaIntegrationTestRunner({ expect(finalOrder.customer_id).toEqual(customer.id) }) - it("customer should be able to cancel their own transfer request", async () => { + it("should cancel a customer transfer request from as admin", async () => { await api.post( `/store/orders/${order.id}/transfer/request`, {}, @@ -449,7 +449,7 @@ medusaIntegrationTestRunner({ expect(orderChanges.length).toEqual(0) }) - it("admin should be able to cancel a transfer request from a customer", async () => { + it("customer should be able to cancel their own transfer request", async () => { await api.post( `/store/orders/${order.id}/transfer/request`, {}, From ccf1ee8e4f6b7d10670b6e3aa3ecd2b1c044971d Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 21 Nov 2024 14:21:53 +0100 Subject: [PATCH 12/16] fix: rename test --- .../http/__tests__/order/admin/transfer-flow.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index cb4cc45235249..e9c4342ad3982 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -393,7 +393,7 @@ medusaIntegrationTestRunner({ expect(finalOrder.customer_id).toEqual(customer.id) }) - it("should cancel a customer transfer request from as admin", async () => { + it("should cancel a customer transfer request as an admin", async () => { await api.post( `/store/orders/${order.id}/transfer/request`, {}, From 7747422df6c0af1a82d51558f4bcdd066409d4e9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 22 Nov 2024 15:11:56 +0100 Subject: [PATCH 13/16] chore: refactor to query, addres comments --- .../product-organization-section.tsx | 4 +- .../transfer/accept-order-transfer.ts | 38 +++++++++++-------- .../transfer/cancel-order-transfer.ts | 36 +++++++++++------- .../transfer/decline-order-transfer.ts | 38 +++++++++++-------- .../orders/[id]/transfer/cancel/route.ts | 18 ++++----- 5 files changed, 77 insertions(+), 57 deletions(-) diff --git a/packages/admin/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx b/packages/admin/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx index 9091f081166df..8387a66dbea85 100644 --- a/packages/admin/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx +++ b/packages/admin/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx @@ -65,9 +65,9 @@ export const ProductOrganizationSection = ({ title={t("fields.collection")} value={ product.collection ? ( - + - {product.collection.title} + {product.collection.title} ) : undefined diff --git a/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts index 1ae95b230b404..a48e4a22e4757 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/accept-order-transfer.ts @@ -8,6 +8,7 @@ import { WorkflowResponse, createStep, createWorkflow, + transform, } from "@medusajs/framework/workflows-sdk" import { OrderPreviewDTO } from "@medusajs/types" import { @@ -16,7 +17,7 @@ import { OrderChangeStatus, } from "@medusajs/utils" -import { useRemoteQueryStep } from "../../../common" +import { useQueryGraphStep } from "../../../common" import { throwIfOrderIsCancelled } from "../../utils/order-validation" import { previewOrderChangeStep } from "../../steps" import { confirmOrderChanges } from "../../steps/confirm-order-changes" @@ -62,16 +63,20 @@ export const acceptOrderTransferWorkflow = createWorkflow( function ( input: WorkflowData ): WorkflowResponse { - const order: OrderDTO = useRemoteQueryStep({ - entry_point: "orders", + const orderQuery = useQueryGraphStep({ + entity: "order", fields: ["id", "email", "status", "customer_id"], - variables: { id: input.order_id }, - list: false, - throw_if_key_not_found: true, - }) + filters: { id: input.order_id }, + options: { throwIfKeyNotFound: true }, + }).config({ name: "order-query" }) + + const order = transform( + { orderQuery }, + ({ orderQuery }) => orderQuery.data[0] + ) - const orderChange: OrderChangeDTO = useRemoteQueryStep({ - entry_point: "order_change", + const orderChangeQuery = useQueryGraphStep({ + entity: "order_change", fields: [ "id", "status", @@ -84,15 +89,18 @@ export const acceptOrderTransferWorkflow = createWorkflow( "actions.reference_id", "actions.internal_note", ], - variables: { - filters: { - order_id: input.order_id, - status: [OrderChangeStatus.REQUESTED], - }, + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.REQUESTED], }, - list: false, + options: { throwIfKeyNotFound: true }, }).config({ name: "order-change-query" }) + const orderChange = transform( + { orderChangeQuery }, + ({ orderChangeQuery }) => orderChangeQuery.data[0] + ) + acceptOrderTransferValidationStep({ order, orderChange, diff --git a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts index 21017969084b0..a29c50da8548d 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts @@ -12,8 +12,9 @@ import { WorkflowData, createStep, createWorkflow, + transform, } from "@medusajs/framework/workflows-sdk" -import { useRemoteQueryStep } from "../../../common" +import { useQueryGraphStep } from "../../../common" import { deleteOrderChangesStep } from "../../steps" import { throwIfIsCancelled, @@ -66,26 +67,33 @@ export const cancelOrderTransferRequestWorkflow = createWorkflow( function ( input: WorkflowData ): WorkflowData { - const order: OrderDTO = useRemoteQueryStep({ - entry_point: "orders", + const orderQuery = useQueryGraphStep({ + entity: "order", fields: ["id", "version", "canceled_at"], - variables: { id: input.order_id }, - list: false, - throw_if_key_not_found: true, + filters: { id: input.order_id }, + options: { throwIfKeyNotFound: true }, }).config({ name: "order-query" }) - const orderChange: OrderChangeDTO = useRemoteQueryStep({ - entry_point: "order_change", + const order = transform( + { orderQuery }, + ({ orderQuery }) => orderQuery.data[0] + ) + + const orderChangeQuery = useQueryGraphStep({ + entity: "order_change", fields: ["id", "status", "version", "actions.*"], - variables: { - filters: { - order_id: input.order_id, - status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], - }, + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], }, - list: false, + options: { throwIfKeyNotFound: true }, }).config({ name: "order-change-query" }) + const orderChange = transform( + { orderChangeQuery }, + ({ orderChangeQuery }) => orderChangeQuery.data[0] + ) + cancelTransferOrderRequestValidationStep({ order, orderChange, input }) deleteOrderChangesStep({ ids: [orderChange.id] }) diff --git a/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts index f265426a37329..bc48192d05faf 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/decline-order-transfer.ts @@ -12,9 +12,10 @@ import { WorkflowData, createStep, createWorkflow, + transform, } from "@medusajs/framework/workflows-sdk" -import { useRemoteQueryStep } from "../../../common" +import { useQueryGraphStep } from "../../../common" import { declineOrderChangeStep } from "../../steps" import { throwIfIsCancelled, @@ -42,7 +43,7 @@ export const declineTransferOrderRequestValidationStep = createStep( (a) => a.action === ChangeActionType.TRANSFER_CUSTOMER )?.details!.token - if (!input.token.length || token !== input.token) { + if (!input.token?.length || token !== input.token) { throw new MedusaError(MedusaError.Types.NOT_ALLOWED, "Invalid token.") } } @@ -60,26 +61,33 @@ export const declineOrderTransferRequestWorkflow = createWorkflow( function ( input: WorkflowData ): WorkflowData { - const order: OrderDTO = useRemoteQueryStep({ - entry_point: "orders", + const orderQuery = useQueryGraphStep({ + entity: "order", fields: ["id", "version", "declineed_at"], - variables: { id: input.order_id }, - list: false, - throw_if_key_not_found: true, + filters: { id: input.order_id }, + options: { throwIfKeyNotFound: true }, }).config({ name: "order-query" }) - const orderChange: OrderChangeDTO = useRemoteQueryStep({ - entry_point: "order_change", + const order = transform( + { orderQuery }, + ({ orderQuery }) => orderQuery.data[0] + ) + + const orderChangeQuery = useQueryGraphStep({ + entity: "order_change", fields: ["id", "status", "version", "actions.*"], - variables: { - filters: { - order_id: input.order_id, - status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], - }, + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], }, - list: false, + options: { throwIfKeyNotFound: true }, }).config({ name: "order-change-query" }) + const orderChange = transform( + { orderChangeQuery }, + ({ orderChangeQuery }) => orderChangeQuery.data[0] + ) + declineTransferOrderRequestValidationStep({ order, orderChange, input }) declineOrderChangeStep({ id: orderChange.id }) diff --git a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts index 81e8c887def67..bc9826032a97c 100644 --- a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts @@ -3,18 +3,15 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { HttpTypes } from "@medusajs/framework/types" +import { AdminOrder, HttpTypes } from "@medusajs/framework/types" import { AdminCancelOrderTransferRequestType } from "../../../validators" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/framework/utils" +import { ContainerRegistrationKeys } from "@medusajs/framework/utils" export const POST = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const orderId = req.params.id const userId = req.auth_context.actor_id @@ -26,12 +23,11 @@ export const POST = async ( }, }) - const queryObject = remoteQueryObjectFromString({ - entryPoint: "order", - variables: { id: orderId }, + const result = await query.graph({ + entity: "order", + filters: { id: orderId }, fields: req.remoteQueryConfig.fields, }) - const [order] = await remoteQuery(queryObject) - res.status(200).json({ order }) + res.status(200).json({ order: result.data[0] as AdminOrder }) } From ab580226be5b7dd6753ce346fac4ad61289fb7af Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 22 Nov 2024 15:22:22 +0100 Subject: [PATCH 14/16] fix: refactor test --- .../order/admin/transfer-flow.spec.ts | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index e9c4342ad3982..c9817a94a2e2c 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -286,13 +286,9 @@ medusaIntegrationTestRunner({ let storeHeaders let signInToken - let orderModule - beforeEach(async () => { const container = getContainer() - orderModule = await container.resolve(Modules.ORDER) - const publishableKey = await generatePublishableKey(container) storeHeaders = generateStoreHeaders({ publishableKey }) @@ -349,10 +345,12 @@ medusaIntegrationTestRunner({ expect(storeOrder.email).toEqual("tony@stark-industries.com") expect(storeOrder.customer_id).not.toEqual(customer.id) - const orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + const orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -405,10 +403,12 @@ medusaIntegrationTestRunner({ } ) - let orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + let orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -442,9 +442,9 @@ medusaIntegrationTestRunner({ adminHeaders ) - orderChanges = await orderModule.listOrderChanges({ - order_id: order.id, - }) + orderChanges = ( + await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) + ).data.order_changes expect(orderChanges.length).toEqual(0) }) @@ -461,10 +461,12 @@ medusaIntegrationTestRunner({ } ) - let orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + let orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -502,10 +504,12 @@ medusaIntegrationTestRunner({ } ) - orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(0) }) @@ -522,10 +526,12 @@ medusaIntegrationTestRunner({ } ) - let orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + let orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -562,9 +568,9 @@ medusaIntegrationTestRunner({ } ) - orderChanges = await orderModule.listOrderChanges({ - order_id: order.id, - }) + orderChanges = ( + await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -590,10 +596,12 @@ medusaIntegrationTestRunner({ } ) - let orderChanges = await orderModule.listOrderChanges( - { order_id: order.id }, - { relations: ["actions"] } - ) + let orderChanges = ( + await api.get( + `/admin/orders/${order.id}/changes?fields=*actions`, + adminHeaders + ) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( @@ -640,9 +648,9 @@ medusaIntegrationTestRunner({ }) ) - orderChanges = await orderModule.listOrderChanges({ - order_id: order.id, - }) + orderChanges = ( + await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) + ).data.order_changes expect(orderChanges.length).toEqual(1) expect(orderChanges[0]).toEqual( From f465216ff73b1dc212e267533635f3f7a4990db6 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 22 Nov 2024 15:34:59 +0100 Subject: [PATCH 15/16] refactor: use actor type --- .../src/order/workflows/transfer/cancel-order-transfer.ts | 2 +- packages/core/types/src/workflow/order/cancel-transfer.ts | 1 + .../medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts | 1 + .../medusa/src/api/store/orders/[id]/transfer/cancel/route.ts | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts index a29c50da8548d..4044e0a866a19 100644 --- a/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts +++ b/packages/core/core-flows/src/order/workflows/transfer/cancel-order-transfer.ts @@ -38,7 +38,7 @@ export const cancelTransferOrderRequestValidationStep = createStep( throwIfIsCancelled(order, "Order") throwIfOrderChangeIsNotActive({ orderChange }) - if (input.logged_in_user_id.startsWith("user_")) { + if (input.actor_type === "user") { return } diff --git a/packages/core/types/src/workflow/order/cancel-transfer.ts b/packages/core/types/src/workflow/order/cancel-transfer.ts index 2030401e86a98..9d0bcba83c84b 100644 --- a/packages/core/types/src/workflow/order/cancel-transfer.ts +++ b/packages/core/types/src/workflow/order/cancel-transfer.ts @@ -1,4 +1,5 @@ export type CancelTransferOrderRequestWorkflowInput = { order_id: string logged_in_user_id: string + actor_type: "customer" | "user" } diff --git a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts index bc9826032a97c..0004a2b96b47e 100644 --- a/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/transfer/cancel/route.ts @@ -20,6 +20,7 @@ export const POST = async ( input: { order_id: orderId, logged_in_user_id: userId, + actor_type: req.auth_context.actor_type as "user", }, }) diff --git a/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts b/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts index 0e962ee06c432..4c24b54c52ad1 100644 --- a/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts +++ b/packages/medusa/src/api/store/orders/[id]/transfer/cancel/route.ts @@ -20,6 +20,7 @@ export const POST = async ( input: { order_id: orderId, logged_in_user_id: customerId, + actor_type: req.auth_context.actor_type as "customer", }, }) From 79ab619b502419ddc383efefcd972ae2e3fec3a9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 22 Nov 2024 16:16:24 +0100 Subject: [PATCH 16/16] fix: list order changes endpoint params --- .../order/admin/transfer-flow.spec.ts | 7 +++++-- .../src/api/admin/orders/middlewares.ts | 4 ++-- .../medusa/src/api/admin/orders/validators.ts | 21 +++++++++++-------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts index c9817a94a2e2c..a06a4824e0c50 100644 --- a/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts +++ b/integration-tests/http/__tests__/order/admin/transfer-flow.spec.ts @@ -569,7 +569,10 @@ medusaIntegrationTestRunner({ ) orderChanges = ( - await api.get(`/admin/orders/${order.id}/changes`, adminHeaders) + await api.get( + `/admin/orders/${order.id}/changes?fields=+declined_at`, + adminHeaders + ) ).data.order_changes expect(orderChanges.length).toEqual(1) @@ -579,7 +582,7 @@ medusaIntegrationTestRunner({ status: "declined", requested_by: customer.id, created_by: customer.id, - declined_at: expect.any(Date), + declined_at: expect.any(String), }) ) }) diff --git a/packages/medusa/src/api/admin/orders/middlewares.ts b/packages/medusa/src/api/admin/orders/middlewares.ts index ba04cbd7d4120..ccb4d76287cae 100644 --- a/packages/medusa/src/api/admin/orders/middlewares.ts +++ b/packages/medusa/src/api/admin/orders/middlewares.ts @@ -12,7 +12,7 @@ import { AdminGetOrdersParams, AdminMarkOrderFulfillmentDelivered, AdminOrderCancelFulfillment, - AdminOrderChanges, + AdminOrderChangesParams, AdminOrderCreateFulfillment, AdminOrderCreateShipment, AdminTransferOrder, @@ -54,7 +54,7 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [ matcher: "/admin/orders/:id/changes", middlewares: [ validateAndTransformQuery( - AdminOrderChanges, + AdminOrderChangesParams, QueryConfig.retrieveOrderChangesTransformQueryConfig ), ], diff --git a/packages/medusa/src/api/admin/orders/validators.ts b/packages/medusa/src/api/admin/orders/validators.ts index 303e460b029b3..8b6e11b1830b7 100644 --- a/packages/medusa/src/api/admin/orders/validators.ts +++ b/packages/medusa/src/api/admin/orders/validators.ts @@ -106,15 +106,18 @@ export const AdminOrderCancelFulfillment = WithAdditionalData( OrderCancelFulfillment ) -export const AdminOrderChanges = z.object({ - id: z.union([z.string(), z.array(z.string())]).optional(), - status: z.union([z.string(), z.array(z.string())]).optional(), - change_type: z.union([z.string(), z.array(z.string())]).optional(), - created_at: createOperatorMap().optional(), - updated_at: createOperatorMap().optional(), - deleted_at: createOperatorMap().optional(), -}) -export type AdminOrderChangesType = z.infer +export const AdminOrderChangesParams = createSelectParams().merge( + z.object({ + id: z.union([z.string(), z.array(z.string())]).optional(), + status: z.union([z.string(), z.array(z.string())]).optional(), + change_type: z.union([z.string(), z.array(z.string())]).optional(), + created_at: createOperatorMap().optional(), + updated_at: createOperatorMap().optional(), + deleted_at: createOperatorMap().optional(), + }) +) + +export type AdminOrderChangesType = z.infer export type AdminMarkOrderFulfillmentDeliveredType = z.infer< typeof AdminMarkOrderFulfillmentDelivered