diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index bf02f1f4f25f1..d697838eb6a1c 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -136,23 +136,24 @@ export interface CalculateShippingOptionPriceDTO { provider_id: string /** - * The option data from the provider. + * The `data` property of the shipping option. */ optionData: Record /** - * Additional data passed when the price is calculated. - * - * @example - * When calculating the price for a shipping option upon creation of a shipping method additional data can be passed - * to the provider. + * The shipping method's `data` property with custom data passed from the frontend. */ data: Record /** * The calculation context needed for the associated fulfillment provider to calculate the price of a shipping option. */ - context: CartDTO & { from_location?: StockLocationDTO } & Record< + context: CartDTO & { + /** + * The location that the items will be shipped from. + */ + from_location?: StockLocationDTO + } & Record< string, unknown > diff --git a/packages/core/types/src/fulfillment/provider.ts b/packages/core/types/src/fulfillment/provider.ts index 9b06c992adfe6..5fdbe94c16273 100644 --- a/packages/core/types/src/fulfillment/provider.ts +++ b/packages/core/types/src/fulfillment/provider.ts @@ -2,7 +2,8 @@ import { CalculateShippingOptionPriceDTO } from "./mutations" export type FulfillmentOption = { /** - * The option's ID. + * The option's ID. This ID can be an ID in the third-party system relevant + * for later processing of fulfillment. * * @example express */ @@ -15,7 +16,15 @@ export type FulfillmentOption = { } export type CalculatedShippingOptionPrice = { + /** + * The calculated price. + */ calculated_amount: number + /** + * Whether the calculated price includes taxes. If enabled, Medusa will + * infer the taxes from the calculated price. If false, Medusa will + * add taxes to the calculated price. + */ is_calculated_price_tax_inclusive: boolean } diff --git a/packages/core/utils/src/fulfillment/provider.ts b/packages/core/utils/src/fulfillment/provider.ts index 7d152431ec507..22582d2ccb98a 100644 --- a/packages/core/utils/src/fulfillment/provider.ts +++ b/packages/core/utils/src/fulfillment/provider.ts @@ -8,8 +8,8 @@ import { /** * ### constructor * - * The constructor allows you to access resources from the module's container using the first parameter, - * and the module's options using the second parameter. + * The constructor allows you to access resources from the [module's container](https://docs.medusajs.com/learn/fundamentals/modules/container) + * using the first parameter, and the module's options using the second parameter. * * :::note * @@ -34,6 +34,7 @@ import { * } * * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { + * // other properties... * protected logger_: Logger * protected options_: Options * // assuming you're initializing a client @@ -90,9 +91,15 @@ export class AbstractFulfillmentProviderService } /** - * This method retrieves the shipping options this fulfillment provider supports. + * This method retrieves a list of fulfillment options that this provider supports. Admin users will then choose from these options when + * they're creating a shipping option. The chosen fulfillment option's object is then stored within the created shipping option's `data` property. + * The `data` property is useful to store data relevant for the third-party provider to later process the fulfillment. + * + * This method is useful if your third-party provider allows you to retrieve support options, carriers, or services from an API. You can then + * retrieve those and return then in the method, allowing the admin user to choose from the services provided by the third-party provider. * - * @returns The list of fulfillment options. + * @returns The list of fulfillment options. Each object in the array should have an `id` property unique to an item, and a `name` property + * that's used to display the option in the admin. * * @example * // other imports... @@ -101,15 +108,15 @@ export class AbstractFulfillmentProviderService * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { * // ... * async getFulfillmentOptions(): Promise { - * return [ - * { - * id: "express" - * }, - * { - * id: "return-express", - * is_return: true - * } - * ] + * // assuming you have a client + * const services = await this.client.getServices() + * + * return services.map((service) => ({ + * id: service.service_id, + * name: service.name, + * service_code: service.code, + * // can add other relevant data for the provider to later process the shipping option. + * })) * } * } */ @@ -179,17 +186,21 @@ export class AbstractFulfillmentProviderService } /** - * This method indicates whether a shippin option's price is calculated during - * checkout or is fixed. + * This method validates whether a shippin option's price can be calculated during checkout. It's executed when the admin user creates a shipping + * option of type `calculated`. If this method returns `false`, an error is thrown as the shipping option's price can't be calculated. + * + * You can perform the checking using the third-party provider if applicable. The `data` parameter will hold the shipping option's `data` property, which + * includes the data of a fulfillment option returned by {@link getFulfillmentOptions}. * * @param data - The `data` property of the shipping option. - * @returns Whether the price is calculated for the shipping option. + * @returns Whether the price can be calculated for the shipping option. * * @example * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { * // ... - * async canCalculate(data: any): Promise { - * return data.custom_type !== "fixed" + * async canCalculate(data: Record): Promise { + * // assuming you have a client + * return await this.client.hasRates(data.id) * } * } */ @@ -198,15 +209,22 @@ export class AbstractFulfillmentProviderService } /** - * This method calculates the price of a shipping option, or a shipping method when it's created. - * - * The Medusa application uses the {@link canCalculate} method first to check whether the shipping option's price is calculated. - * If it returns `true`, Medusa uses this method to retrieve the calculated price. - * - * @param optionData - Shipping option data from the provider, the `data` property of a shipping option. - * @param data - Additional data passed when the price is calculated. - * @param context - The context details, such as the cart or customer. - * @returns The calculated price + * This method calculates the price of a shipping method when it's created or its cart is refreshed. + * + * In this method, you can send a request to your third-party provider to retrieve the prices. The first + * parameters holds the `data` property of the shipping method's shipping option, which has fulfillment + * object data returned by {@link getFulfillmentOptions}. + * + * The second parameter holds the `data` property of the shipping method, which has data returned by {@link validateFulfillmentData}. + * It can also hold custom data passed from the frontend during checkout. + * + * So, using both of these data, assuming you're storing in them data related to the third-party service, + * you can retrieve the calculated price of the shipping method. + * + * @param optionData - The `data` property of a shipping option. + * @param data - The shipping method's `data` property with custom data passed from the frontend. + * @param context - The context details, such as the cart details. + * @returns The calculated price's details. * * @example * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { @@ -232,15 +250,17 @@ export class AbstractFulfillmentProviderService * `data` property, it's stored in the fulfillment's `data` property. * * The `data` property is useful when handling the fulfillment later, - * as you can access information useful for your integration. + * as you can access information useful for your integration, such as the ID in the + * third-party provider. * - * You can also use this method to perform an action with the third-party fulfillment service. + * You can also use this method to perform an action with the third-party fulfillment service + * since a fulfillment is created, such as purchase a label. * * @param data - The `data` property of the shipping method this fulfillment is created for. * @param items - The items in the fulfillment. * @param order - The order this fulfillment is created for. * @param fulfillment - The fulfillment's details. - * @returns The data to store in the fulfillment's `data` property. + * @returns An object whose `data` property is stored in the fulfillment's `data` property. * * @example * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { @@ -260,7 +280,7 @@ export class AbstractFulfillmentProviderService * * return { * data: { - * ...data, + * ...(fulfillment.data as object || {}), * ...externalData * } * } @@ -280,19 +300,22 @@ export class AbstractFulfillmentProviderService * This method is used when a fulfillment is canceled. Use it to perform operations * with the third-party fulfillment service. * - * @param fulfillment - The fulfillment's details. + * @param data - The fulfillment's `data` property. * * @example * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { * // ... - * async cancelFulfillment(fulfillment: any): Promise { + * async cancelFulfillment(data: Record): Promise { * // assuming the client cancels a fulfillment * // in the third-party service - * await this.client.cancel(fulfillment.id) + * const { external_id } = data as { + * external_id: string + * } + * await this.client.cancel(external_id) * } * } */ - async cancelFulfillment(fulfillment: Record): Promise { + async cancelFulfillment(data: Record): Promise { throw Error("cancelFulfillment must be overridden by the child class") } @@ -321,17 +344,19 @@ export class AbstractFulfillmentProviderService * `data` property, it's stored in the fulfillment's `data` property. * * The `data` property is useful when handling the fulfillment later, - * as you can access information useful for your integration. + * as you can access information useful for your integration. For example, you + * can store an ID for the fulfillment in the third-party service. * - * Use this method to perform actions necessary in the third-party fulfillment service. + * Use this method to perform actions necessary in the third-party fulfillment service, such as + * purchasing a label for the return fulfillment. * * @param fulfillment - The fulfillment's details. - * @returns The data to store in the fulfillment's `data` property. + * @returns An object whose `data` property is stored in the fulfillment's `data` property. * * @example * class MyFulfillmentProviderService extends AbstractFulfillmentProviderService { * // ... - * async createReturnFulfillment(fulfillment: any): Promise { + * async createReturnFulfillment(fulfillment: Record): Promise { * // assuming the client creates a fulfillment for a return * // in the third-party service * const externalData = await this.client.createReturn( @@ -340,7 +365,7 @@ export class AbstractFulfillmentProviderService * * return { * data: { - * ...fulfillment.data, + * ...(fulfillment.data as object || {}), * ...externalData * } * }