chore(core-flows): return fulfillment link (#8304)

This commit is contained in:
Carlos R. L. Rodrigues
2024-07-27 10:20:48 -03:00
committed by GitHub
parent feabe0e6c0
commit d63ca00214
10 changed files with 261 additions and 53 deletions

View File

@@ -428,14 +428,7 @@ medusaIntegrationTestRunner({
variables: {
id: order.id,
},
fields: [
"*",
"items.*",
"shipping_methods.*",
"total",
"item_total",
"fulfillments.*",
],
fields: ["*", "items.*", "shipping_methods.*", "total", "item_total"],
})
const [returnOrder] = await remoteQuery(remoteQueryObject)
@@ -501,18 +494,6 @@ medusaIntegrationTestRunner({
order_id: expect.any(String),
}),
]),
fulfillments: [
expect.objectContaining({
id: expect.any(String),
location_id: location.id,
provider_id: providerId,
shipping_option_id: shippingOption.id,
// TODO: Validate the address once we are fixed on it
/*delivery_address: {
id: "fuladdr_01HY0RTAP0P1EEAFK7BXJ0BKBN",
},*/
}),
],
})
)
})

View File

@@ -1,12 +1,24 @@
import { OrderChangeDTO, OrderDTO, ReturnDTO } from "@medusajs/types"
import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils"
import {
FulfillmentWorkflow,
OrderChangeDTO,
OrderDTO,
ReturnDTO,
} from "@medusajs/types"
import {
ChangeActionType,
MedusaError,
Modules,
OrderChangeStatus,
} from "@medusajs/utils"
import {
WorkflowData,
createStep,
createWorkflow,
transform,
when,
} from "@medusajs/workflows-sdk"
import { useRemoteQueryStep } from "../../../common"
import { createRemoteLinkStep, useRemoteQueryStep } from "../../../common"
import { createReturnFulfillmentWorkflow } from "../../../fulfillment/workflows/create-return-fulfillment"
import { previewOrderChangeStep } from "../../steps"
import { confirmOrderChanges } from "../../steps/confirm-order-changes"
import { createReturnItemsStep } from "../../steps/create-return-items"
@@ -36,13 +48,76 @@ const validationStep = createStep(
}
)
function prepareFulfillmentData({
order,
items,
returnShippingOption,
}: {
order: OrderDTO
items: any[]
returnShippingOption: {
id: string
provider_id: string
service_zone: {
fulfillment_set: {
location?: {
id: string
address: Record<string, any>
}
}
}
}
}) {
const orderItemsMap = new Map<string, Required<OrderDTO>["items"][0]>(
order.items!.map((i) => [i.id, i])
)
const fulfillmentItems = items.map((i) => {
const orderItem = orderItemsMap.get(i.item_id)!
return {
line_item_id: i.item_id,
quantity: i.quantity,
return_quantity: i.quantity,
title: orderItem.variant_title ?? orderItem.title,
sku: orderItem.variant_sku || "",
barcode: orderItem.variant_barcode || "",
} as FulfillmentWorkflow.CreateFulfillmentItemWorkflowDTO
})
const locationId =
returnShippingOption.service_zone.fulfillment_set.location?.id
// delivery address is the stock location address
const address =
returnShippingOption.service_zone.fulfillment_set.location?.address ?? {}
delete address.id
if (!locationId) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot create return without stock location, either provide a location or you should link the shipping option ${returnShippingOption.id} to a stock location.`
)
}
return {
input: {
location_id: locationId,
provider_id: returnShippingOption.provider_id,
shipping_option_id: returnShippingOption.id,
items: fulfillmentItems,
delivery_address: address,
order: order,
},
}
}
export const confirmReturnRequestWorkflowId = "confirm-return-request"
export const confirmReturnRequestWorkflow = createWorkflow(
confirmReturnRequestWorkflowId,
function (input: WorkflowInput): WorkflowData<OrderDTO> {
const orderReturn: ReturnDTO = useRemoteQueryStep({
entry_point: "return",
fields: ["id", "status", "order_id", "canceled_at"],
fields: ["id", "status", "order_id", "location_id", "canceled_at"],
variables: { id: input.return_id },
list: false,
throw_if_key_not_found: true,
@@ -50,7 +125,16 @@ export const confirmReturnRequestWorkflow = createWorkflow(
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "version", "canceled_at"],
fields: [
"id",
"version",
"canceled_at",
"items.id",
"items.title",
"items.variant_title",
"items.variant_sku",
"items.variant_barcode",
],
variables: { id: orderReturn.order_id },
list: false,
throw_if_key_not_found: true,
@@ -85,13 +169,79 @@ export const confirmReturnRequestWorkflow = createWorkflow(
validationStep({ order, orderReturn, orderChange })
createReturnItemsStep({
const createdReturnItems = createReturnItemsStep({
returnId: orderReturn.id,
changes: returnItemActions,
})
confirmOrderChanges({ changes: [orderChange], orderId: order.id })
const returnModified = useRemoteQueryStep({
entry_point: "return",
fields: [
"id",
"status",
"order_id",
"canceled_at",
"shipping_methods.shipping_option_id",
],
variables: { id: input.return_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "return-query" })
const returnShippingOptionId = transform(
{ returnModified },
({ returnModified }) => {
if (!returnModified.shipping_methods?.length) {
return
}
return returnModified.shipping_methods[0].shipping_option_id
}
)
when({ returnShippingOptionId }, ({ returnShippingOptionId }) => {
return !!returnShippingOptionId
}).then(() => {
const returnShippingOption = useRemoteQueryStep({
entry_point: "shipping_options",
fields: [
"id",
"provider_id",
"service_zone.fulfillment_set.location.id",
"service_zone.fulfillment_set.location.address.*",
],
variables: {
id: returnShippingOptionId,
},
list: false,
throw_if_key_not_found: true,
}).config({ name: "return-shipping-option" })
const fulfillmentData = transform(
{ order, items: createdReturnItems, returnShippingOption },
prepareFulfillmentData
)
const returnFulfillment =
createReturnFulfillmentWorkflow.runAsStep(fulfillmentData)
const link = transform(
{ orderReturn, fulfillment: returnFulfillment },
(data) => {
return [
{
[Modules.ORDER]: { return_id: data.orderReturn.id },
[Modules.FULFILLMENT]: { fulfillment_id: data.fulfillment.id },
},
]
}
)
createRemoteLinkStep(link)
})
return previewOrderChangeStep(order.id)
}
)

View File

@@ -12,7 +12,6 @@ import {
WorkflowData,
createStep,
createWorkflow,
parallelize,
transform,
} from "@medusajs/workflows-sdk"
import { createRemoteLinkStep, useRemoteQueryStep } from "../../../common"
@@ -50,7 +49,10 @@ function prepareShippingMethodData({
adjustments: [],
}
if (isDefined(inputShippingOption.price) && inputShippingOption.price >= 0) {
if (
isDefined(inputShippingOption.price) &&
MathBN.gte(inputShippingOption.price, 0)
) {
obj.amount = inputShippingOption.price
} else {
if (returnShippingOption.price_type === "calculated") {
@@ -288,27 +290,26 @@ export const createAndCompleteReturnOrderWorkflow = createWorkflow(
const returnFulfillment =
createReturnFulfillmentWorkflow.runAsStep(fulfillmentData)
const returnCreated = createCompleteReturnStep({
order_id: input.order_id,
items: input.items,
shipping_method: shippingMethodData,
created_by: input.created_by,
})
const link = transform(
{ order_id: input.order_id, fulfillment: returnFulfillment },
{ returnCreated, fulfillment: returnFulfillment },
(data) => {
return [
{
[Modules.ORDER]: { order_id: data.order_id },
[Modules.ORDER]: { return_id: data.returnCreated.id },
[Modules.FULFILLMENT]: { fulfillment_id: data.fulfillment.id },
},
]
}
)
const [returnCreated] = parallelize(
createCompleteReturnStep({
order_id: input.order_id,
items: input.items,
shipping_method: shippingMethodData,
created_by: input.created_by,
}),
createRemoteLinkStep(link)
)
createRemoteLinkStep(link)
const receiveItems = transform(
{

View File

@@ -65,7 +65,7 @@ export interface CreateFulfillmentDTO {
/**
* The labels associated with the fulfillment.
*/
labels: Omit<CreateFulfillmentLabelDTO, "fulfillment_id">[]
labels?: Omit<CreateFulfillmentLabelDTO, "fulfillment_id">[]
/**
* The associated order to be sent to the provider.

View File

@@ -174,7 +174,7 @@ export type CreateFulfillmentWorkflowInput = {
/**
* The labels associated with the fulfillment.
*/
labels: CreateFulfillmentLabelWorkflowDTO[]
labels?: CreateFulfillmentLabelWorkflowDTO[]
/**
* The associated fulfillment order to be sent to the provider.

View File

@@ -14,6 +14,8 @@ export interface DeleteRequestItemReturnWorkflowInput {
export interface UpdateRequestItemReturnWorkflowInput {
return_id: string
claim_id?: string
exchange_id?: string
action_id: string
data: {
quantity?: BigNumberInput

View File

@@ -110,4 +110,10 @@ export const LINKS = {
Modules.FULFILLMENT,
"fulfillment_id"
),
ReturnFulfillment: composeLinkName(
Modules.ORDER,
"return_id",
Modules.FULFILLMENT,
"fulfillment_id"
),
}

View File

@@ -14,18 +14,18 @@ import {
UpdateServiceZoneDTO,
} from "@medusajs/types"
import {
arrayDifference,
deepEqualObj,
EmitEvents,
getSetDifference,
InjectManager,
InjectTransactionManager,
isDefined,
isPresent,
isString,
MedusaContext,
MedusaError,
ModulesSdkUtils,
arrayDifference,
deepEqualObj,
getSetDifference,
isDefined,
isPresent,
isString,
promiseAll,
} from "@medusajs/utils"
import {
@@ -617,9 +617,8 @@ export default class FulfillmentModuleService
...fulfillmentRest
} = fulfillment
let fulfillmentThirdPartyData!: any
try {
fulfillmentThirdPartyData =
const providerResult =
await this.fulfillmentProviderService_.createFulfillment(
provider_id,
fulfillmentData || {},
@@ -630,7 +629,8 @@ export default class FulfillmentModuleService
await this.fulfillmentService_.update(
{
id: fulfillment.id,
data: fulfillmentThirdPartyData ?? {},
data: providerResult.data ?? {},
labels: providerResult.labels ?? [],
},
sharedContext
)
@@ -662,9 +662,8 @@ export default class FulfillmentModuleService
sharedContext
)
let fulfillmentThirdPartyData!: any
try {
fulfillmentThirdPartyData =
const providerResult =
await this.fulfillmentProviderService_.createReturn(
fulfillment.provider_id,
fulfillment as Record<any, any>
@@ -672,7 +671,8 @@ export default class FulfillmentModuleService
await this.fulfillmentService_.update(
{
id: fulfillment.id,
data: fulfillmentThirdPartyData ?? {},
data: providerResult.data ?? {},
labels: providerResult.labels ?? [],
},
sharedContext
)

View File

@@ -6,6 +6,7 @@ export * from "./order-cart"
export * from "./order-fulfillment"
export * from "./order-payment-collection"
export * from "./order-promotion"
export * from "./order-return-fulfillment"
export * from "./product-sales-channel"
export * from "./product-variant-inventory-item"
export * from "./product-variant-price-set"

View File

@@ -0,0 +1,67 @@
import { ModuleJoinerConfig } from "@medusajs/types"
import { LINKS, Modules } from "@medusajs/utils"
export const ReturnFulfillment: ModuleJoinerConfig = {
serviceName: LINKS.ReturnFulfillment,
isLink: true,
databaseConfig: {
tableName: "return_fulfillment",
idPrefix: "retful",
},
alias: [
{
name: ["return_fulfillment", "return_fulfillments"],
args: {
entity: "LinkReturnFulfillment",
},
},
],
primaryKeys: ["id", "return_id", "fulfillment_id"],
relationships: [
{
serviceName: Modules.ORDER,
primaryKey: "id",
foreignKey: "return_id",
alias: "return",
args: {
methodSuffix: "Returns",
},
},
{
serviceName: Modules.FULFILLMENT,
primaryKey: "id",
foreignKey: "fulfillment_id",
alias: "fulfillments",
args: {
methodSuffix: "Fulfillments",
},
},
],
extends: [
{
serviceName: Modules.ORDER,
fieldAlias: {
return_fulfillments: {
path: "return_fulfillment_link.fulfillments",
isList: true,
},
},
relationship: {
serviceName: LINKS.OrderFulfillment,
primaryKey: "return_id",
foreignKey: "id",
alias: "return_fulfillment_link",
isList: true,
},
},
{
serviceName: Modules.FULFILLMENT,
relationship: {
serviceName: LINKS.OrderFulfillment,
primaryKey: "fulfillment_id",
foreignKey: "id",
alias: "return_link",
},
},
],
}