Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core-flows,types): cancel order changes workflow #8035

Merged
merged 15 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
cancelOrderChangeWorkflow,
cancelOrderChangeWorkflowId,
createOrderChangeWorkflow,
} from "@medusajs/core-flows"
import { IOrderModuleService, OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createOrderFixture, prepareDataFixtures } from "./__fixtures__"

jest.setTimeout(50000)

medusaIntegrationTestRunner({
env: { MEDUSA_FF_MEDUSA_V2: true },
testSuite: ({ getContainer }) => {
let container

beforeAll(() => {
container = getContainer()
})

describe("Order change workflows", () => {
let order: OrderDTO
let service: IOrderModuleService

describe("createOrderChangeWorkflow", () => {
beforeEach(async () => {
const fixtures = await prepareDataFixtures({
container,
})

order = await createOrderFixture({
container,
product: fixtures.product,
location: fixtures.location,
inventoryItem: fixtures.inventoryItem,
})
})

it("should successfully create an order change", async () => {
const { result } = await createOrderChangeWorkflow(container).run({
input: {
order_id: order.id,
},
})

expect(result).toEqual(
expect.objectContaining({
id: expect.any(String),
order_id: order.id,
})
)
})

it("should throw an error when creating an order change when an active one already exists", async () => {
await createOrderChangeWorkflow(container).run({
input: {
order_id: order.id,
},
})

const {
errors: [error],
} = await createOrderChangeWorkflow(container).run({
input: {
order_id: order.id,
},
throwOnError: false,
})

expect(error.error).toEqual(
expect.objectContaining({
message: `Order (${order.id}) already has an existing active order change`,
})
)
})
})

describe("cancelOrderChangeWorkflow", () => {
let orderChange: OrderChangeDTO

beforeEach(async () => {
const fixtures = await prepareDataFixtures({
container,
})

order = await createOrderFixture({
container,
product: fixtures.product,
location: fixtures.location,
inventoryItem: fixtures.inventoryItem,
})

const { result } = await createOrderChangeWorkflow(container).run({
input: { order_id: order.id },
})

orderChange = result
service = container.resolve(ModuleRegistrationName.ORDER)
})

it("should successfully cancel an order change", async () => {
await cancelOrderChangeWorkflow(container).run({
input: {
id: orderChange.id,
canceled_by: "test",
},
})

const orderChange2 = await service.retrieveOrderChange(orderChange.id)

expect(orderChange2).toEqual(
expect.objectContaining({
id: expect.any(String),
canceled_by: "test",
canceled_at: expect.any(Date),
})
)
})

it("should throw an error when creating an order change when an active one already exists", async () => {
const workflow = cancelOrderChangeWorkflow(container)

workflow.appendAction("throw", cancelOrderChangeWorkflowId, {
invoke: async function failStep() {
throw new Error(`Fail`)
},
})

const {
errors: [error],
} = await workflow.run({
input: {
id: orderChange.id,
canceled_by: "test",
},
throwOnError: false,
})

expect(error.error).toEqual(
expect.objectContaining({
message: `Fail`,
})
)

const orderChange2 = await service.retrieveOrderChange(orderChange.id)

expect(orderChange2).toEqual(
expect.objectContaining({
id: expect.any(String),
canceled_by: null,
canceled_at: null,
})
)
})
})
})
},
})
38 changes: 38 additions & 0 deletions packages/core/core-flows/src/order/steps/cancel-order-change.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { CancelOrderChangeDTO, IOrderModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"

export const cancelOrderChangeStepId = "cancel-order-change"
export const cancelOrderChangeStep = createStep(
cancelOrderChangeStepId,
async (data: CancelOrderChangeDTO, { container }) => {
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)

const dataBeforeUpdate = await service.retrieveOrderChange(data.id, {
select: ["status", "metadata"],
})

await service.cancelOrderChange(data)

return new StepResponse(void 0, {
id: data.id,
status: dataBeforeUpdate.status,
canceled_at: null,
canceled_by: null,
metadata: dataBeforeUpdate.metadata,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the metadata here?
that probably won't be touched if we only update the other fields no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, made it dynamic based on inputs

})
},
async (rollbackData, { container }) => {
if (!rollbackData) {
return
}

const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)

await service.updateOrderChanges(rollbackData)
}
)
1 change: 1 addition & 0 deletions packages/core/core-flows/src/order/steps/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./archive-orders"
export * from "./cancel-claim"
export * from "./cancel-exchange"
export * from "./cancel-order-change"
export * from "./cancel-orders"
export * from "./cancel-return"
export * from "./complete-orders"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CancelOrderChangeDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { cancelOrderChangeStep } from "../steps"

export const cancelOrderChangeWorkflowId = "cancel-order-change"
export const cancelOrderChangeWorkflow = createWorkflow(
cancelOrderChangeWorkflowId,
(input: WorkflowData<CancelOrderChangeDTO>): WorkflowData<void> => {
cancelOrderChangeStep(input)
}
)
1 change: 1 addition & 0 deletions packages/core/core-flows/src/order/workflows/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./archive-orders"
export * from "./cancel-order"
export * from "./cancel-order-change"
export * from "./cancel-order-fulfillment"
export * from "./cancel-return"
export * from "./complete-orders"
Expand Down
3 changes: 2 additions & 1 deletion packages/core/types/src/order/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ export interface UpdateOrderChangeDTO {
declined_by?: string
declined_reason?: string
declined_at?: Date
canceled_by?: string
canceled_by?: string | null
canceled_at?: Date | null
metadata?: Record<string, unknown> | null
}

Expand Down
111 changes: 111 additions & 0 deletions packages/core/types/src/order/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
RegisterOrderFulfillmentDTO,
RegisterOrderShipmentDTO,
UpdateOrderAddressDTO,
UpdateOrderChangeDTO,
UpdateOrderDTO,
UpdateOrderItemDTO,
UpdateOrderItemWithSelectorDTO,
Expand Down Expand Up @@ -1075,7 +1076,30 @@ export interface IOrderModuleService extends IModuleService {
selector: FilterableOrderShippingMethodTaxLineProps,
sharedContext?: Context
): Promise<void>

// Order Change

/**
* This method retrieves a {return type} by its ID.
*
* @param {string} orderChangeId - The order change ID.
* @param {FindConfig<OrderChangeDTO>} config - The configurations determining how the order is retrieved. Its properties, such as `select` or `relations`, accept the
* attributes or relations associated with a order.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderChangeDTO>} The retrieved {return type}(s).
*
* @example
* ```typescript
* const result = await orderModuleService.retrieveOrder("orderId123");
* ```
*
*/
retrieveOrderChange(
orderChangeId: string,
config?: FindConfig<OrderChangeDTO>,
sharedContext?: Context
): Promise<OrderChangeDTO>

createOrderChange(
data: CreateOrderChangeDTO,
sharedContext?: Context
Expand Down Expand Up @@ -1127,6 +1151,93 @@ export interface IOrderModuleService extends IModuleService {
sharedContext?: Context
): Promise<OrderChangeDTO | OrderChangeDTO[]>

updateOrderChanges(
data: UpdateOrderChangeDTO,
sharedContext?: Context
): Promise<OrderChangeDTO>

/**
* This method updates {return type}(s)
*
* @param {UpdateOrderChangeDTO[]} data - The order change to be updated.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderChangeDTO[]>} The updated {return type}(s).
*
* @example
* ```typescript
* // Example call to updateOrderChanges
*
* const updateOrderChangesData: UpdateOrderChangeDTO[] = [{
* id: "orderchange123",
* description: "Change due to customer request"
* }];
*
* const result = await orderModuleService.updateOrderChanges(updateOrderChangesData);
* ```
*
*/
updateOrderChanges(
data: UpdateOrderChangeDTO[],
sharedContext?: Context
): Promise<OrderChangeDTO[]>

/**
* This method updates {return type}(s)
*
* @param {UpdateOrderChangeDTO | UpdateOrderChangeDTO[]} data - The order change d t o | order change to be updated.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderChangeDTO | OrderChangeDTO[]>} The updated {return type}(s).
*
* @example
* ```typescript
* const result = await orderModuleService.createOrderChange({
* order_id: "order123",
* description: "Adding new item to the order"
* });
* ```
*
*/
updateOrderChanges(
data: UpdateOrderChangeDTO | UpdateOrderChangeDTO[],
sharedContext?: Context
): Promise<OrderChangeDTO | OrderChangeDTO[]>

/**
* This method deletes order change by its ID.
*
* @param {string[]} orderChangeId - The list of {summary}
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<void>} Resolves when {summary}
*
* @example
* ```typescript
* await orderModuleService.deleteOrderChanges(["orderChangeId1", "orderChangeId2"]);
* ```
*
*/
deleteOrderChanges(
orderChangeId: string[],
sharedContext?: Context
): Promise<void>

/**
* This method deletes order change by its ID.
*
* @param {string} orderChangeId - The order's ID.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<void>} Resolves when {summary}
*
* @example
* ```typescript
* await orderModuleService.deleteOrderChanges("orderChangeId");
* ```
*
*/
deleteOrderChanges(
orderChangeId: string,
sharedContext?: Context
): Promise<void>

/**
* This method deletes order change by its ID.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/modules/order/src/models/order-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export default class OrderChange {
columnType: "timestamptz",
nullable: true,
})
canceled_at?: Date
canceled_at?: Date | null = null

@Property({
onCreate: () => new Date(),
Expand Down
Loading