feat(core-flows, fulfillment): Add create return specific method and add more tests (#7357)

* feat(core-flows, fulfillment): Add create return specific method and add more tests

* fix defautl providers in tests fixtures

* more tests

* wip fixes

* fix flow and tests

* cleanup
This commit is contained in:
Adrien de Peretti
2024-05-21 13:48:59 +02:00
committed by GitHub
parent 35dc3c5cf7
commit c4fde7ea5c
9 changed files with 318 additions and 91 deletions
@@ -1,5 +1,10 @@
import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk"
import { import {
ModuleRegistrationName,
Modules,
RemoteLink,
} from "@medusajs/modules-sdk"
import {
FulfillmentSetDTO,
FulfillmentWorkflow, FulfillmentWorkflow,
IOrderModuleService, IOrderModuleService,
IRegionModuleService, IRegionModuleService,
@@ -208,6 +213,7 @@ async function prepareDataFixtures({ container }) {
salesChannel, salesChannel,
location, location,
product, product,
fulfillmentSet,
} }
} }
@@ -312,6 +318,17 @@ async function createOrderFixture({ container, product }) {
}, },
]) ])
const returnReason = await orderService.createReturnReasons({
value: "Test reason",
label: "Test reason",
})
await orderService.createReturnReasons({
value: "Test child reason",
label: "Test child reason",
parent_return_reason_id: returnReason.id,
})
await orderService.applyPendingOrderActions(order.id) await orderService.applyPendingOrderActions(order.id)
order = await orderService.retrieve(order.id, { order = await orderService.retrieve(order.id, {
@@ -335,6 +352,7 @@ medusaIntegrationTestRunner({
let region: RegionDTO let region: RegionDTO
let location: StockLocationDTO let location: StockLocationDTO
let product: ProductDTO let product: ProductDTO
let fulfillmentSet: FulfillmentSetDTO
let orderService: IOrderModuleService let orderService: IOrderModuleService
@@ -347,12 +365,18 @@ medusaIntegrationTestRunner({
region = fixtures.region region = fixtures.region
location = fixtures.location location = fixtures.location
product = fixtures.product product = fixtures.product
fulfillmentSet = fixtures.fulfillmentSet
orderService = container.resolve(ModuleRegistrationName.ORDER) orderService = container.resolve(ModuleRegistrationName.ORDER)
}) })
it("should create a return order", async () => { it("should create a return order", async () => {
const order = await createOrderFixture({ container, product }) const order = await createOrderFixture({ container, product })
const reasons = await orderService.listReturnReasons({})
const testReason = reasons.find(
(r) => r.value.toLowerCase() === "test child reason"
)!
const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput = const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput =
{ {
order_id: order.id, order_id: order.id,
@@ -363,6 +387,7 @@ medusaIntegrationTestRunner({
{ {
id: order.items![0].id, id: order.items![0].id,
quantity: 1, quantity: 1,
reason_id: testReason.id,
}, },
], ],
} }
@@ -468,6 +493,80 @@ medusaIntegrationTestRunner({
}) })
) )
}) })
it("should fail when location is not linked", async () => {
const order = await createOrderFixture({ container, product })
const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput =
{
order_id: order.id,
return_shipping: {
option_id: shippingOption.id,
},
items: [
{
id: order.items![0].id,
quantity: 1,
},
],
}
// Remove the location link
const remoteLink = container.resolve(
ContainerRegistrationKeys.REMOTE_LINK
) as RemoteLink
await remoteLink.dismiss([
{
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
[Modules.FULFILLMENT]: {
fulfillment_set_id: fulfillmentSet.id,
},
},
])
const { errors } = await createReturnOrderWorkflow(container).run({
input: createReturnOrderData,
throwOnError: false,
})
await expect(errors[0].error.message).toBe(
`Cannot create return without stock location, either provide a location or you should link the shipping option ${shippingOption.id} to a stock location.`
)
})
it("should fail when a reason with children is provided", async () => {
const order = await createOrderFixture({ container, product })
const reasons = await orderService.listReturnReasons({})
const testReason = reasons.find(
(r) => r.value.toLowerCase() === "test reason"
)!
const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput =
{
order_id: order.id,
return_shipping: {
option_id: shippingOption.id,
},
items: [
{
id: order.items![0].id,
quantity: 1,
reason_id: testReason.id,
},
],
}
const { errors } = await createReturnOrderWorkflow(container).run({
input: createReturnOrderData,
throwOnError: false,
})
expect(errors[0].error.message).toBe(
`Cannot apply return reason with id ${testReason.id} to order with id ${order.id}. Return reason has nested reasons.`
)
})
}) })
}, },
}) })
@@ -7,6 +7,7 @@ import {
WithCalculatedPrice, WithCalculatedPrice,
} from "@medusajs/types" } from "@medusajs/types"
import { import {
createStep,
createWorkflow, createWorkflow,
transform, transform,
WorkflowData, WorkflowData,
@@ -19,6 +20,7 @@ import {
MathBN, MathBN,
MedusaError, MedusaError,
Modules, Modules,
remoteQueryObjectFromString,
} from "@medusajs/utils" } from "@medusajs/utils"
import { updateOrderTaxLinesStep } from "../steps" import { updateOrderTaxLinesStep } from "../steps"
import { createReturnStep } from "../steps/create-return" import { createReturnStep } from "../steps/create-return"
@@ -55,7 +57,7 @@ function throwIfItemsDoesNotExistsInOrder({
} }
} }
function validateReturnReasons( async function validateReturnReasons(
{ {
orderId, orderId,
inputItems, inputItems,
@@ -66,24 +68,32 @@ function validateReturnReasons(
{ container } { container }
) { ) {
const reasonIds = inputItems.map((i) => i.reason_id).filter(Boolean) const reasonIds = inputItems.map((i) => i.reason_id).filter(Boolean)
if (!reasonIds.length) { if (!reasonIds.length) {
return return
} }
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const returnReasons = remoteQuery({ const remoteQueryObject = remoteQueryObjectFromString({
entry_point: "return_reasons", entryPoint: "return_reasons",
fields: ["return_reason_children.*"], fields: [
variables: { id: [inputItems.map((item) => item.reason_id)] }, "id",
"parent_return_reason_id",
"parent_return_reason",
"return_reason_children.id",
],
variables: { id: [inputItems.map((item) => item.reason_id)], limit: null },
}) })
const returnReasons = await remoteQuery(remoteQueryObject)
const reasons = returnReasons.map((r) => r.id) const reasons = returnReasons.map((r) => r.id)
const hasInvalidReasons = reasons.filter( const hasInvalidReasons = returnReasons
// We do not allow for root reason to be applied .filter(
(reason) => reason.return_reason_children.length > 0 // We do not allow for root reason to be applied
) (reason) => reason.return_reason_children.length > 0
)
.map((r) => r.id)
const hasNonExistingReasons = arrayDifference(reasonIds, reasons) const hasNonExistingReasons = arrayDifference(reasonIds, reasons)
if (hasNonExistingReasons.length) { if (hasNonExistingReasons.length) {
@@ -95,7 +105,7 @@ function validateReturnReasons(
) )
} }
if (hasInvalidReasons.length()) { if (hasInvalidReasons.length) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot apply return reason with id ${hasInvalidReasons.join( `Cannot apply return reason with id ${hasInvalidReasons.join(
@@ -238,12 +248,34 @@ function prepareReturnShippingOptionQueryVariables({
return variables return variables
} }
const validationStep = createStep(
"create-return-order-validation",
async function (
{
order,
input,
}: {
order
input: OrderWorkflow.CreateOrderReturnWorkflowInput
},
context
) {
throwIfOrderIsCancelled({ order })
throwIfItemsDoesNotExistsInOrder({ order, inputItems: input.items })
await validateReturnReasons(
{ orderId: input.order_id, inputItems: input.items },
context
)
validateCustomRefundAmount({ order, refundAmount: input.refund_amount })
}
)
export const createReturnOrderWorkflowId = "create-return-order" export const createReturnOrderWorkflowId = "create-return-order"
export const createReturnOrderWorkflow = createWorkflow( export const createReturnOrderWorkflow = createWorkflow(
createReturnOrderWorkflowId, createReturnOrderWorkflowId,
( function (
input: WorkflowData<OrderWorkflow.CreateOrderReturnWorkflowInput> input: WorkflowData<OrderWorkflow.CreateOrderReturnWorkflowInput>
): WorkflowData<void> => { ): WorkflowData<void> {
const order: OrderDTO = useRemoteQueryStep({ const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders", entry_point: "orders",
fields: [ fields: [
@@ -259,19 +291,7 @@ export const createReturnOrderWorkflow = createWorkflow(
throw_if_key_not_found: true, throw_if_key_not_found: true,
}) })
transform({ order }, throwIfOrderIsCancelled) validationStep({ order, input })
transform(
{ order, inputItems: input.items },
throwIfItemsDoesNotExistsInOrder
)
transform(
{ orderId: input.order_id, inputItems: input.items },
validateReturnReasons
)
transform(
{ order, refundAmount: input.refund_amount },
validateCustomRefundAmount
)
const returnShippingOptionsVariables = transform( const returnShippingOptionsVariables = transform(
{ input, order }, { input, order },
+40 -1
View File
@@ -2367,7 +2367,7 @@ export interface IFulfillmentModuleService extends IModuleService {
): Promise<[FulfillmentDTO[], number]> ): Promise<[FulfillmentDTO[], number]>
/** /**
* This method creates a fulfillment. * This method creates a fulfillment and call the provider to create a fulfillment.
* *
* @param {CreateFulfillmentDTO} data - The fulfillment to be created. * @param {CreateFulfillmentDTO} data - The fulfillment to be created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
@@ -2405,6 +2405,45 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentDTO> ): Promise<FulfillmentDTO>
/**
* This method creates a fulfillment and call the provider to create a return.
*
* @param {CreateFulfillmentDTO} data - The fulfillment to be created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<FulfillmentDTO>} The created fulfillment.
*
* @example
* const fulfillment =
* await fulfillmentModuleService.createReturnFulfillment({
* location_id: "loc_123",
* provider_id: "webshipper",
* delivery_address: {
* address_1: "4120 Auto Park Cir",
* country_code: "us",
* },
* items: [
* {
* title: "Shirt",
* sku: "SHIRT",
* quantity: 1,
* barcode: "ABCED",
* },
* ],
* labels: [
* {
* tracking_number: "1234567",
* tracking_url: "https://example.com/tracking",
* label_url: "https://example.com/label",
* },
* ],
* order: {},
* })
*/
createReturnFulfillment(
data: CreateFulfillmentDTO,
sharedContext?: Context
): Promise<FulfillmentDTO>
/** /**
* This method updates an existing fulfillment. * This method updates an existing fulfillment.
* *
@@ -14,6 +14,10 @@ export class FulfillmentProviderServiceFixtures extends AbstractFulfillmentProvi
async getFulfillmentOptions(): Promise<any> { async getFulfillmentOptions(): Promise<any> {
return {} return {}
} }
async createReturnFulfillment(fulfillment): Promise<any> {
return {}
}
} }
export const services = [FulfillmentProviderServiceFixtures] export const services = [FulfillmentProviderServiceFixtures]
@@ -137,6 +137,63 @@ moduleIntegrationTestRunner({
}) })
) )
}) })
it("should create a return fulfillment", async () => {
const shippingProfile = await service.createShippingProfiles({
name: "test",
type: "default",
})
const fulfillmentSet = await service.create({
name: "test",
type: "test-type",
})
const serviceZone = await service.createServiceZones({
name: "test",
fulfillment_set_id: fulfillmentSet.id,
})
const shippingOption = await service.createShippingOptions(
generateCreateShippingOptionsData({
provider_id: providerId,
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
})
)
const fulfillment = await service.createReturnFulfillment(
generateCreateFulfillmentData({
provider_id: providerId,
shipping_option_id: shippingOption.id,
})
)
expect(fulfillment).toEqual(
expect.objectContaining({
id: expect.any(String),
packed_at: null,
shipped_at: null,
delivered_at: null,
canceled_at: null,
data: null,
provider_id: providerId,
shipping_option_id: shippingOption.id,
metadata: null,
delivery_address: expect.objectContaining({
id: expect.any(String),
}),
items: [
expect.objectContaining({
id: expect.any(String),
}),
],
labels: [
expect.objectContaining({
id: expect.any(String),
}),
],
})
)
})
}) })
describe("on cancel", () => { describe("on cancel", () => {
@@ -193,9 +193,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.ShippingOptionDTO[] FulfillmentTypes.ShippingOptionDTO[]
>(shippingOptions, { >(shippingOptions)
populate: true,
})
} }
@InjectManager("baseRepository_") @InjectManager("baseRepository_")
@@ -211,10 +209,7 @@ export default class FulfillmentModuleService<
) )
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>( return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment, fulfillment
{
populate: true,
}
) )
} }
@@ -232,9 +227,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.FulfillmentDTO[] FulfillmentTypes.FulfillmentDTO[]
>(fulfillments, { >(fulfillments)
populate: true,
})
} }
@InjectManager("baseRepository_") @InjectManager("baseRepository_")
@@ -251,10 +244,7 @@ export default class FulfillmentModuleService<
return [ return [
await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO[]>( await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO[]>(
fulfillments, fulfillments
{
populate: true,
}
), ),
count, count,
] ]
@@ -283,9 +273,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[] FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[]
>(createdFulfillmentSets, { >(createdFulfillmentSets)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -351,9 +339,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[] FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
>(createdServiceZones, { >(createdServiceZones)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -410,9 +396,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[] FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[]
>(createdShippingOptions, { >(createdShippingOptions)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -469,9 +453,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingProfileDTO | FulfillmentTypes.ShippingProfileDTO
| FulfillmentTypes.ShippingProfileDTO[] | FulfillmentTypes.ShippingProfileDTO[]
>(createdShippingProfiles, { >(createdShippingProfiles)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -523,10 +505,7 @@ export default class FulfillmentModuleService<
) )
return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>( return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>(
Array.isArray(data) ? createdGeoZones : createdGeoZones[0], Array.isArray(data) ? createdGeoZones : createdGeoZones[0]
{
populate: true,
}
) )
} }
@@ -557,9 +536,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingOptionRuleDTO | FulfillmentTypes.ShippingOptionRuleDTO
| FulfillmentTypes.ShippingOptionRuleDTO[] | FulfillmentTypes.ShippingOptionRuleDTO[]
>(createdShippingOptionRules, { >(createdShippingOptionRules)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -627,10 +604,43 @@ export default class FulfillmentModuleService<
} }
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>( return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment, fulfillment
{ )
populate: true, }
}
@InjectManager("baseRepository_")
async createReturnFulfillment(
data: FulfillmentTypes.CreateFulfillmentDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<FulfillmentTypes.FulfillmentDTO> {
const { order, ...fulfillmentDataToCreate } = data
const fulfillment = await this.fulfillmentService_.create(
fulfillmentDataToCreate,
sharedContext
)
let fulfillmentThirdPartyData!: any
try {
fulfillmentThirdPartyData =
await this.fulfillmentProviderService_.createReturn(
fulfillment.provider_id,
fulfillment as Record<any, any>
)
await this.fulfillmentService_.update(
{
id: fulfillment.id,
data: fulfillmentThirdPartyData ?? {},
},
sharedContext
)
} catch (error) {
await this.fulfillmentService_.delete(fulfillment.id, sharedContext)
throw error
}
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment
) )
} }
@@ -654,9 +664,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[] FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[]
>(updatedFulfillmentSets, { >(updatedFulfillmentSets)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -865,9 +873,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[] FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
>(toReturn, { >(toReturn)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -1110,9 +1116,7 @@ export default class FulfillmentModuleService<
const serialized = await this.baseRepository_.serialize< const serialized = await this.baseRepository_.serialize<
FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[] FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[]
>(updatedShippingOptions, { >(updatedShippingOptions)
populate: true,
})
return isString(idOrSelector) ? serialized[0] : serialized return isString(idOrSelector) ? serialized[0] : serialized
} }
@@ -1359,9 +1363,7 @@ export default class FulfillmentModuleService<
const serialized = await this.baseRepository_.serialize< const serialized = await this.baseRepository_.serialize<
FulfillmentTypes.GeoZoneDTO[] FulfillmentTypes.GeoZoneDTO[]
>(updatedGeoZones, { >(updatedGeoZones)
populate: true,
})
return Array.isArray(data) ? serialized : serialized[0] return Array.isArray(data) ? serialized : serialized[0]
} }
@@ -1393,9 +1395,7 @@ export default class FulfillmentModuleService<
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingOptionRuleDTO | FulfillmentTypes.ShippingOptionRuleDTO
| FulfillmentTypes.ShippingOptionRuleDTO[] | FulfillmentTypes.ShippingOptionRuleDTO[]
>(updatedShippingOptionRules, { >(updatedShippingOptionRules)
populate: true,
})
} }
@InjectTransactionManager("baseRepository_") @InjectTransactionManager("baseRepository_")
@@ -1434,10 +1434,7 @@ export default class FulfillmentModuleService<
const serialized = const serialized =
await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>( await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment, fulfillment
{
populate: true,
}
) )
return Array.isArray(serialized) ? serialized[0] : serialized return Array.isArray(serialized) ? serialized[0] : serialized
@@ -1478,9 +1475,7 @@ export default class FulfillmentModuleService<
) )
} }
const result = await this.baseRepository_.serialize(fulfillment, { const result = await this.baseRepository_.serialize(fulfillment)
populate: true,
})
return Array.isArray(result) ? result[0] : result return Array.isArray(result) ? result[0] : result
} }
@@ -101,4 +101,12 @@ export default class FulfillmentProviderService extends ModulesSdkUtils.internal
const provider = this.retrieveProviderRegistration(providerId) const provider = this.retrieveProviderRegistration(providerId)
return await provider.cancelFulfillment(fulfillment) return await provider.cancelFulfillment(fulfillment)
} }
async createReturn(
providerId: string,
fulfillment: Record<string, unknown>,
) {
const provider = this.retrieveProviderRegistration(providerId)
return await provider.createReturnFulfillment(fulfillment)
}
} }
+2 -1
View File
@@ -1,6 +1,6 @@
import { Modules } from "@medusajs/modules-sdk" import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types" import { ModuleJoinerConfig } from "@medusajs/types"
import { MapToConfig } from "@medusajs/utils" import { MapToConfig, pluralize } from "@medusajs/utils"
import { LineItem, ReturnReason } from "@models" import { LineItem, ReturnReason } from "@models"
import Order from "./models/order" import Order from "./models/order"
@@ -37,6 +37,7 @@ export const joinerConfig: ModuleJoinerConfig = {
name: ["return_reason", "return_reasons"], name: ["return_reason", "return_reasons"],
args: { args: {
entity: ReturnReason.name, entity: ReturnReason.name,
methodSuffix: pluralize(ReturnReason.name),
}, },
}, },
], ],
@@ -29,16 +29,20 @@ export class ManualFulfillmentService extends AbstractFulfillmentProviderService
return data return data
} }
async validateOption(data: Record<string, unknown>): Promise<boolean> { async validateOption(data: Record<string, any>): Promise<boolean> {
return true return true
} }
async createFulfillment(): Promise<Record<string, unknown>> { async createFulfillment(): Promise<Record<string, any>> {
// No data is being sent anywhere // No data is being sent anywhere
return {} return {}
} }
async cancelFulfillment(fulfillment: Record<string, unknown>): Promise<any> { async cancelFulfillment(): Promise<any> {
return {}
}
async createReturnFulfillment(): Promise<any> {
return {} return {}
} }
} }