feat(core-flows,medusa,order): remove return shipping (#8137)

What:
* `DELETE /admin/returs/:id/shipping_methods/:action_id`

FIXES: CC-187
This commit is contained in:
Carlos R. L. Rodrigues
2024-07-16 10:27:16 +00:00
committed by GitHub
parent b7e6b1461b
commit 7123f9ff63
22 changed files with 383 additions and 23 deletions
@@ -268,6 +268,26 @@ medusaIntegrationTestRunner({
})
)
expect(result.data.order.shipping_methods).toHaveLength(2)
// remove shipping method
const action_id = result.data.order.shipping_methods[1].actions[0].id
result = await api.delete(
`/admin/returns/${returnId}/shipping-method/${action_id}`,
adminHeaders
)
expect(result.data.order_preview.shipping_methods).toHaveLength(1)
// recreate shipping
result = await api.post(
`/admin/returns/${returnId}/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
result = await api.post(
`/admin/returns/${returnId}/request`,
{},
@@ -184,7 +184,7 @@ medusaIntegrationTestRunner({
it("should successfully delete an order change", async () => {
await deleteOrderChangeWorkflow(container).run({
input: { id: orderChange.id },
input: { ids: orderChange.id },
})
const orderChange2 = await service.retrieveOrderChange(
@@ -6,7 +6,7 @@ interface StepInput {
items: CreateOrderLineItemDTO[]
}
export const createOrderLineItemsStepId = "create-line-items-step"
export const createOrderLineItemsStepId = "create-order-line-items-step"
export const createOrderLineItemsStep = createStep(
createOrderLineItemsStepId,
async (input: StepInput, { container }) => {
@@ -0,0 +1,31 @@
import { IOrderModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
interface StepInput {
ids: string[]
}
export const deleteOrderLineItems = createStep(
"delete-order-line-items",
async (input: StepInput, { container }) => {
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
const deleted = await service.softDeleteLineItems(input.ids)
return new StepResponse(deleted, input.ids)
},
async (ids, { container }) => {
if (!ids) {
return
}
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
await service.restoreLineItems(ids)
}
)
@@ -2,20 +2,20 @@ import { IOrderModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
export const deleteOrderChangeStepId = "delete-order-change"
export const deleteOrderChangeStep = createStep(
deleteOrderChangeStepId,
async (data: { id: string }, { container }) => {
export const deleteOrderChangesStepId = "delete-order-change"
export const deleteOrderChangesStep = createStep(
deleteOrderChangesStepId,
async (data: { ids: string[] }, { container }) => {
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
const deleted = await service.softDeleteOrderChanges(data.id)
const deleted = await service.softDeleteOrderChanges(data.ids)
return new StepResponse(deleted, data.id)
return new StepResponse(deleted, data.ids)
},
async (id, { container }) => {
if (!id) {
async (ids, { container }) => {
if (!ids) {
return
}
@@ -23,6 +23,6 @@ export const deleteOrderChangeStep = createStep(
ModuleRegistrationName.ORDER
)
await service.restoreOrderChanges(id)
await service.restoreOrderChanges(ids)
}
)
@@ -0,0 +1,31 @@
import { IOrderModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
interface StepInput {
ids: string[]
}
export const deleteOrderShippingMethods = createStep(
"delete-order-shipping-methods",
async (input: StepInput, { container }) => {
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
const deleted = await service.softDeleteShippingMethods(input.ids)
return new StepResponse(deleted, input.ids)
},
async (ids, { container }) => {
if (!ids) {
return
}
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
await service.restoreShippingMethods(ids)
}
)
@@ -14,8 +14,10 @@ export * from "./create-order-change-actions"
export * from "./create-orders"
export * from "./create-returns"
export * from "./decline-order-change"
export * from "./delete-order-change"
export * from "./delete-line-items"
export * from "./delete-order-change-actions"
export * from "./delete-order-changes"
export * from "./delete-order-shipping-methods"
export * from "./get-item-tax-lines"
export * from "./preview-order-change"
export * from "./register-fulfillment"
@@ -1,10 +1,10 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { deleteOrderChangeStep } from "../steps"
import { deleteOrderChangesStep } from "../steps"
export const deleteOrderChangeWorkflowId = "delete-order-change"
export const deleteOrderChangeWorkflow = createWorkflow(
deleteOrderChangeWorkflowId,
(input: WorkflowData<{ id: string }>): WorkflowData<void> => {
deleteOrderChangeStep(input)
(input: WorkflowData<{ ids: string[] }>): WorkflowData<void> => {
deleteOrderChangesStep(input)
}
)
@@ -26,7 +26,7 @@ export * from "./exchange-request-item-return"
export * from "./get-order-detail"
export * from "./get-orders-list"
export * from "./receive-return"
export * from "./remove-return-shipping-method"
export * from "./request-item-return"
export * from "./update-order-change-actions"
export * from "./update-tax-lines"
@@ -0,0 +1,102 @@
import {
OrderChangeActionDTO,
OrderChangeDTO,
ReturnDTO,
} from "@medusajs/types"
import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils"
import {
WorkflowData,
createStep,
createWorkflow,
parallelize,
transform,
} from "@medusajs/workflows-sdk"
import { useRemoteQueryStep } from "../../common"
import { deleteOrderShippingMethods } from "../steps"
import { deleteOrderChangeActionsStep } from "../steps/delete-order-change-actions"
import { previewOrderChangeStep } from "../steps/preview-order-change"
import {
throwIfIsCancelled,
throwIfOrderChangeIsNotActive,
} from "../utils/order-validation"
const validationStep = createStep(
"validate-remove-return-shipping-method",
async function ({
orderChange,
orderReturn,
input,
}: {
input: { return_id: string; action_id: string }
orderReturn: ReturnDTO
orderChange: OrderChangeDTO
}) {
throwIfIsCancelled(orderReturn, "Return")
throwIfOrderChangeIsNotActive({ orderChange })
const associatedAction = orderChange.actions.find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
if (!associatedAction) {
throw new Error(
`No shipping method found for return ${input.return_id} in order change ${orderChange.id}`
)
} else if (associatedAction.action !== ChangeActionType.SHIPPING_ADD) {
throw new Error(
`Action ${associatedAction.id} is not adding a shipping method`
)
}
}
)
export const removeReturnShippingMethodWorkflowId =
"remove-return-shipping-method"
export const removeReturnShippingMethodWorkflow = createWorkflow(
removeReturnShippingMethodWorkflowId,
function (input: { return_id: string; action_id: string }): WorkflowData {
const orderReturn: ReturnDTO = useRemoteQueryStep({
entry_point: "return",
fields: ["id", "status", "order_id"],
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, input })
const dataToRemove = transform(
{ orderChange, input },
({ orderChange, input }) => {
const associatedAction = orderChange.actions.find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
return {
actionId: associatedAction.id,
shippingMethodId: associatedAction.reference_id,
}
}
)
parallelize(
deleteOrderChangeActionsStep({ ids: [dataToRemove.actionId] }),
deleteOrderShippingMethods({ ids: [dataToRemove.shippingMethodId] })
)
return previewOrderChangeStep(orderReturn.order_id)
}
)
@@ -0,0 +1,44 @@
import { removeReturnShippingMethodWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../types/routing"
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const { id, action_id } = req.params
const { result: orderPreview } = await removeReturnShippingMethodWorkflow(
req.scope
).run({
input: {
return_id: id,
action_id,
},
})
const queryObject = remoteQueryObjectFromString({
entryPoint: "return",
variables: {
id,
filters: {
...req.filterableFields,
},
},
fields: req.remoteQueryConfig.fields,
})
const [orderReturn] = await remoteQuery(queryObject)
res.json({
order_preview: orderPreview,
return: orderReturn,
})
}
@@ -65,6 +65,16 @@ export const adminReturnRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["DELETE"],
matcher: "/admin/returns/:id/shipping-method/:action_id",
middlewares: [
validateAndTransformQuery(
AdminGetOrdersOrderParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/returns/:id/request",
@@ -211,7 +211,7 @@ moduleIntegrationTestRunner({
}),
}),
]),
shipping_methods: [
shipping_methods: expect.arrayContaining([
expect.objectContaining({
name: "return shipping method",
amount: 10,
@@ -220,7 +220,7 @@ moduleIntegrationTestRunner({
name: "Exchange method",
amount: 35,
}),
],
]),
difference_due: 14,
})
)
@@ -0,0 +1,20 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20240715102100 extends Migration {
async up(): Promise<void> {
const sql = `
DROP INDEX IF EXISTS "IDX_order_shipping_method_shipping_option_id";
ALTER TABLE "order_shipping_method"
ADD COLUMN if NOT exists "deleted_at" timestamptz NULL;
CREATE INDEX IF NOT EXISTS "IDX_order_shipping_method_shipping_option_id" ON "order_shipping_method" (
shipping_option_id
) WHERE deleted_at IS NULL;
`
this.addSql(sql)
}
}
@@ -1,11 +1,13 @@
import { DAL } from "@medusajs/types"
import {
DALUtils,
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
@@ -30,6 +32,7 @@ const ClaimItemIdIndex = createPsqlIndexStatementHelper({
})
@Entity({ tableName: "order_claim_item_image" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ClaimItemImage {
[OptionalProps]?: OptionalClaimItemImageProps
@@ -1,6 +1,7 @@
import { BigNumberRawValue, DAL } from "@medusajs/types"
import {
BigNumber,
DALUtils,
MikroOrmBigNumberProperty,
createPsqlIndexStatementHelper,
generateEntityId,
@@ -8,6 +9,7 @@ import {
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
@@ -66,6 +68,7 @@ const ActionOrderingIndex = createPsqlIndexStatementHelper({
})
@Entity({ tableName: "order_change_action" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class OrderChangeAction {
[OptionalProps]?: OptionalLineItemProps
@@ -1,6 +1,7 @@
import { DAL } from "@medusajs/types"
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
OrderChangeStatus,
OrderChangeType,
@@ -11,6 +12,7 @@ import {
Collection,
Entity,
Enum,
Filter,
ManyToOne,
OneToMany,
OnInit,
@@ -77,6 +79,7 @@ const VersionIndex = createPsqlIndexStatementHelper({
})
@Entity({ tableName: "order_change" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
@VersionIndex.MikroORMIndex()
export default class OrderChange {
[OptionalProps]?: OptionalLineItemProps
@@ -1,5 +1,6 @@
import { DAL } from "@medusajs/types"
import {
DALUtils,
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
@@ -7,6 +8,7 @@ import {
BeforeCreate,
Cascade,
Entity,
Filter,
ManyToOne,
OnInit,
OneToMany,
@@ -37,6 +39,7 @@ const ParentIndex = createPsqlIndexStatementHelper({
type OptionalOrderProps = "parent_return_reason" | DAL.ModelDateColumns
@Entity({ tableName: "return_reason" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ReturnReason {
[OptionalProps]?: OptionalOrderProps
@@ -2,6 +2,7 @@ import { BigNumberRawValue } from "@medusajs/types"
import {
BigNumber,
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/utils"
@@ -10,6 +11,7 @@ import {
Cascade,
Collection,
Entity,
Filter,
OneToMany,
OnInit,
PrimaryKey,
@@ -19,12 +21,20 @@ import {
import ShippingMethodAdjustment from "./shipping-method-adjustment"
import ShippingMethodTaxLine from "./shipping-method-tax-line"
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "order_shipping_method",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const ShippingOptionIdIndex = createPsqlIndexStatementHelper({
tableName: "order_shipping_method",
columns: "shipping_option_id",
where: "deleted_at IS NOT NULL",
})
@Entity({ tableName: "order_shipping_method" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ShippingMethod {
@PrimaryKey({ columnType: "text" })
id: string
@@ -61,7 +71,7 @@ export default class ShippingMethod {
() => ShippingMethodTaxLine,
(taxLine) => taxLine.shipping_method,
{
cascade: [Cascade.PERSIST],
cascade: [Cascade.PERSIST, "soft-remove"] as any,
}
)
tax_lines = new Collection<Rel<ShippingMethodTaxLine>>(this)
@@ -70,7 +80,7 @@ export default class ShippingMethod {
() => ShippingMethodAdjustment,
(adjustment) => adjustment.shipping_method,
{
cascade: [Cascade.PERSIST],
cascade: [Cascade.PERSIST, "soft-remove"] as any,
}
)
adjustments = new Collection<Rel<ShippingMethodAdjustment>>(this)
@@ -90,6 +100,10 @@ export default class ShippingMethod {
})
updated_at: Date
@Property({ columnType: "timestamptz", nullable: true })
@DeletedAtIndex.MikroORMIndex()
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "ordsm")
@@ -1,6 +1,7 @@
import { BigNumberRawValue, DAL } from "@medusajs/types"
import {
BigNumber,
DALUtils,
MikroOrmBigNumberProperty,
createPsqlIndexStatementHelper,
generateEntityId,
@@ -8,6 +9,7 @@ import {
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
@@ -71,6 +73,7 @@ const OrderIdVersionIndex = createPsqlIndexStatementHelper({
})
@Entity({ tableName: "order_transaction" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
@OrderIdVersionIndex.MikroORMIndex()
export default class Transaction {
[OptionalProps]?: OptionalLineItemProps
@@ -1252,6 +1252,66 @@ export default class OrderModuleService<
return sm.map((s) => s.shipping_method)
}
@InjectManager("baseRepository_")
// @ts-ignore
async softDeleteShippingMethods<TReturnableLinkableKeys extends string>(
ids: string | object | string[] | object[],
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
@MedusaContext() sharedContext?: Context
): Promise<Record<string, string[]> | void> {
const rel = await super.listOrderShippingMethods(
{
shipping_method_id: ids,
},
{
select: ["id"],
},
sharedContext
)
const orderShippingMethodIds = rel.map((r) => r.id)
const [returned] = await promiseAll([
super.softDeleteShippingMethods(ids, config, sharedContext),
super.softDeleteOrderShippingMethods(
orderShippingMethodIds,
config,
sharedContext
),
])
return returned
}
@InjectManager("baseRepository_")
// @ts-ignore
async restoreShippingMethods<TReturnableLinkableKeys extends string>(
transactionIds: string | object | string[] | object[],
config?: RestoreReturn<TReturnableLinkableKeys>,
@MedusaContext() sharedContext?: Context
): Promise<Record<string, string[]> | void> {
const rel = await super.listOrderShippingMethods(
{
shipping_method_id: transactionIds,
},
{
select: ["id"],
},
sharedContext
)
const orderShippingMethodIds = rel.map((r) => r.id)
const [returned] = await promiseAll([
super.restoreShippingMethods(transactionIds, config, sharedContext),
super.restoreOrderShippingMethods(
orderShippingMethodIds,
config,
sharedContext
),
])
return returned
}
// @ts-ignore
async createLineItemAdjustments(
adjustments: OrderTypes.CreateOrderLineItemAdjustmentDTO[]
@@ -2014,6 +2074,7 @@ export default class OrderModuleService<
delete item.actions
const newItem = itemsToUpsert.find((d) => d.item_id === item.id)!
calculated.order.items[idx] = {
...lineItem,
actions,
@@ -69,6 +69,13 @@ export function setFindMethods<T>(klass: Constructor<T>, entity: any) {
if (isRelatedEntity) {
popWhere.order ??= {}
popWhere.shipping_methods ??= {}
popWhere.shipping_methods.version = version
popWhere.shipping_methods.deleted_at ??= null
popWhere.shipping_methods.shipping_method ??= {}
popWhere.shipping_methods.shipping_method.deleted_at ??= null
}
const orderWhere = isRelatedEntity ? popWhere.order : popWhere
@@ -80,9 +87,12 @@ export function setFindMethods<T>(klass: Constructor<T>, entity: any) {
orderWhere.items.version = version
orderWhere.items.deleted_at ??= null
popWhere.shipping_methods ??= {}
popWhere.shipping_methods.version = version
popWhere.shipping_methods.deleted_at ??= null
orderWhere.shipping_methods ??= {}
orderWhere.shipping_methods.version = version
orderWhere.shipping_methods.deleted_at ??= null
orderWhere.shipping_methods.shipping_method ??= {}
orderWhere.shipping_methods.shipping_method.deleted_at ??= null
if (!config.options.orderBy) {
config.options.orderBy = { id: "ASC" }