feat(core-flows,dashboard,js-sdk,medusa,types): support Fulfillment Options (#10622)
**What** - add a list point for fetching fulfillment options for a provider - add FO support on SO create & update on dashboard - pass `cart` and `stockLocation` to `validateFufillmentData` context --- CLOSES CMRC-789 CLOSES CMRC-790
This commit is contained in:
@@ -1,16 +1,18 @@
|
||||
import { Modules, promiseAll } from "@medusajs/framework/utils"
|
||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
||||
import {
|
||||
CartDTO,
|
||||
IFulfillmentModuleService,
|
||||
StockLocationDTO,
|
||||
} from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
export interface ValidateShippingMethodsDataInput {
|
||||
context: Record<string, unknown>
|
||||
options_to_validate: {
|
||||
id: string
|
||||
provider_id: string
|
||||
option_data: Record<string, unknown>
|
||||
method_data: Record<string, unknown>
|
||||
}[]
|
||||
}
|
||||
export type ValidateShippingMethodsDataInput = {
|
||||
id: string
|
||||
provider_id: string
|
||||
option_data: Record<string, unknown>
|
||||
method_data: Record<string, unknown>
|
||||
context: CartDTO & { from_location: StockLocationDTO; [k: string]: unknown }
|
||||
}[]
|
||||
|
||||
export const validateAndReturnShippingMethodsDataStepId =
|
||||
"validate-and-return-shipping-methods-data"
|
||||
@@ -20,9 +22,9 @@ export const validateAndReturnShippingMethodsDataStepId =
|
||||
export const validateAndReturnShippingMethodsDataStep = createStep(
|
||||
validateAndReturnShippingMethodsDataStepId,
|
||||
async (data: ValidateShippingMethodsDataInput, { container }) => {
|
||||
const { options_to_validate = [] } = data
|
||||
const optionsToValidate = data ?? []
|
||||
|
||||
if (!options_to_validate.length) {
|
||||
if (!optionsToValidate.length) {
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
|
||||
@@ -31,12 +33,12 @@ export const validateAndReturnShippingMethodsDataStep = createStep(
|
||||
)
|
||||
|
||||
const validatedData = await promiseAll(
|
||||
options_to_validate.map(async (option) => {
|
||||
optionsToValidate.map(async (option) => {
|
||||
const validated = await fulfillmentModule.validateFulfillmentData(
|
||||
option.provider_id,
|
||||
option.option_data,
|
||||
option.method_data,
|
||||
data.context
|
||||
option.context
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -68,8 +68,8 @@ export const addShippingMethodToCartWorkflow = createWorkflow(
|
||||
validateCartShippingOptionsPriceStep({ shippingOptions })
|
||||
|
||||
const validateShippingMethodsDataInput = transform(
|
||||
{ input, shippingOptions },
|
||||
({ input, shippingOptions }) => {
|
||||
{ input, shippingOptions, cart },
|
||||
({ input, shippingOptions, cart }) => {
|
||||
return input.options.map((inputOption) => {
|
||||
const shippingOption = shippingOptions.find(
|
||||
(so) => so.id === inputOption.id
|
||||
@@ -80,15 +80,18 @@ export const addShippingMethodToCartWorkflow = createWorkflow(
|
||||
provider_id: shippingOption?.provider_id,
|
||||
option_data: shippingOption?.data ?? {},
|
||||
method_data: inputOption.data ?? {},
|
||||
context: {
|
||||
...cart,
|
||||
from_location: shippingOption?.stock_location ?? {},
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const validatedMethodData = validateAndReturnShippingMethodsDataStep({
|
||||
options_to_validate: validateShippingMethodsDataInput,
|
||||
context: {}, // TODO: Add cart, when we have a better idea about what's appropriate to pass
|
||||
})
|
||||
const validatedMethodData = validateAndReturnShippingMethodsDataStep(
|
||||
validateShippingMethodsDataInput
|
||||
)
|
||||
|
||||
const shippingMethodInput = transform(
|
||||
{ input, shippingOptions, validatedMethodData },
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { deepFlatMap } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
@@ -41,7 +40,12 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
const scFulfillmentSetQuery = useQueryGraphStep({
|
||||
entity: "sales_channels",
|
||||
filters: { id: cart.sales_channel_id },
|
||||
fields: ["stock_locations.fulfillment_sets.id"],
|
||||
fields: [
|
||||
"stock_locations.fulfillment_sets.id",
|
||||
"stock_locations.id",
|
||||
"stock_locations.name",
|
||||
"stock_locations.address.*",
|
||||
],
|
||||
}).config({ name: "sales_channels-fulfillment-query" })
|
||||
|
||||
const scFulfillmentSets = transform(
|
||||
@@ -49,22 +53,23 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
({ scFulfillmentSetQuery }) => scFulfillmentSetQuery.data[0]
|
||||
)
|
||||
|
||||
const fulfillmentSetIds = transform(
|
||||
{ options: scFulfillmentSets },
|
||||
(data) => {
|
||||
const { fulfillmentSetIds, fulfillmentSetLocationMap } = transform(
|
||||
{ scFulfillmentSets },
|
||||
({ scFulfillmentSets }) => {
|
||||
const fulfillmentSetIds = new Set<string>()
|
||||
const fulfillmentSetLocationMap = {}
|
||||
|
||||
deepFlatMap(
|
||||
data.options,
|
||||
"stock_locations.fulfillment_sets",
|
||||
({ fulfillment_sets: fulfillmentSet }) => {
|
||||
if (fulfillmentSet?.id) {
|
||||
fulfillmentSetIds.add(fulfillmentSet.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
scFulfillmentSets.stock_locations.forEach((stockLocation) => {
|
||||
stockLocation.fulfillment_sets.forEach((fulfillmentSet) => {
|
||||
fulfillmentSetLocationMap[fulfillmentSet.id] = stockLocation
|
||||
fulfillmentSetIds.add(fulfillmentSet.id)
|
||||
})
|
||||
})
|
||||
|
||||
return Array.from(fulfillmentSetIds)
|
||||
return {
|
||||
fulfillmentSetIds: Array.from(fulfillmentSetIds),
|
||||
fulfillmentSetLocationMap,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -103,6 +108,7 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
"shipping_profile_id",
|
||||
"provider_id",
|
||||
"data",
|
||||
"service_zone.fulfillment_set_id",
|
||||
|
||||
"type.id",
|
||||
"type.label",
|
||||
@@ -124,15 +130,19 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
}).config({ name: "shipping-options-query" })
|
||||
|
||||
const shippingOptionsWithPrice = transform(
|
||||
{ shippingOptions },
|
||||
({ shippingOptions }) =>
|
||||
{ shippingOptions, fulfillmentSetLocationMap },
|
||||
({ shippingOptions, fulfillmentSetLocationMap }) =>
|
||||
shippingOptions.map((shippingOption) => {
|
||||
const price = shippingOption.calculated_price
|
||||
const fulfillmentSetId =
|
||||
shippingOption.service_zone.fulfillment_set_id
|
||||
const stockLocation = fulfillmentSetLocationMap[fulfillmentSetId]
|
||||
|
||||
return {
|
||||
...shippingOption,
|
||||
amount: price?.calculated_amount,
|
||||
is_tax_inclusive: !!price?.is_calculated_price_tax_inclusive,
|
||||
stock_location: stockLocation,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
@@ -18,25 +18,25 @@ export class FulfillmentProvider {
|
||||
* This method retrieves a paginated list of fulfillment providers. It sends a request to the
|
||||
* [List Fulfillment Providers](https://docs.medusajs.com/api/admin#fulfillment-providers_getfulfillmentproviders)
|
||||
* API route.
|
||||
*
|
||||
*
|
||||
* @param query - Filters and pagination configurations.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The paginated list of providers.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* To retrieve the list of fulfillment providers:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.fulfillmentProvider.list()
|
||||
* .then(({ fulfillment_providers, count, limit, offset }) => {
|
||||
* console.log(fulfillment_providers)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* To configure the pagination, pass the `limit` and `offset` query parameters.
|
||||
*
|
||||
*
|
||||
* For example, to retrieve only 10 items and skip 10 items:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.fulfillmentProvider.list({
|
||||
* limit: 10,
|
||||
@@ -46,10 +46,10 @@ export class FulfillmentProvider {
|
||||
* console.log(fulfillment_providers)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Using the `fields` query parameter, you can specify the fields and relations to retrieve
|
||||
* in each fulfillment provider:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.fulfillmentProvider.list({
|
||||
* fields: "id"
|
||||
@@ -58,7 +58,7 @@ export class FulfillmentProvider {
|
||||
* console.log(fulfillment_providers)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/store#select-fields-and-relations).
|
||||
*/
|
||||
async list(
|
||||
@@ -74,4 +74,23 @@ export class FulfillmentProvider {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves a list of fulfillment options for a given fulfillment provider. It sends a request to the
|
||||
* [List Fulfillment Options](https://docs.medusajs.com/api/admin#fulfillment-providers_getfulfillmentprovideroptions)
|
||||
* API route.
|
||||
*
|
||||
* @param id - The ID of the fulfillment provider.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The list of fulfillment options.
|
||||
*/
|
||||
async listFulfillmentOptions(id: string, headers?: ClientHeaders) {
|
||||
return await this.client.fetch<HttpTypes.AdminFulfillmentProviderOptionsListResponse>(
|
||||
`/admin/fulfillment-providers/${id}/options`,
|
||||
{
|
||||
method: "GET",
|
||||
headers,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,6 +834,23 @@ export class Store {
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
calculate: async (
|
||||
id: string,
|
||||
body: HttpTypes.StoreCalculateShippingOptionPrice,
|
||||
query?: HttpTypes.SelectParams,
|
||||
headers?: ClientHeaders
|
||||
) => {
|
||||
return await this.client.fetch<HttpTypes.StoreShippingOptionResponse>(
|
||||
`/store/shipping-options/${id}/calculate`,
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body,
|
||||
query,
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
public payment = {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { BaseFulfillmentProvider } from "../common"
|
||||
import {
|
||||
BaseFulfillmentProvider,
|
||||
BaseFulfillmentProviderOption,
|
||||
} from "../common"
|
||||
|
||||
export interface AdminFulfillmentProvider extends BaseFulfillmentProvider {}
|
||||
|
||||
export interface AdminFulfillmentProviderOption
|
||||
extends BaseFulfillmentProviderOption {}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { PaginatedResponse } from "../../common"
|
||||
import { AdminFulfillmentProvider } from "./entities"
|
||||
import {
|
||||
AdminFulfillmentProvider,
|
||||
AdminFulfillmentProviderOption,
|
||||
} from "./entities"
|
||||
|
||||
export interface AdminFulfillmentProviderListResponse
|
||||
extends PaginatedResponse<{
|
||||
@@ -8,3 +11,8 @@ export interface AdminFulfillmentProviderListResponse
|
||||
*/
|
||||
fulfillment_providers: AdminFulfillmentProvider[]
|
||||
}> {}
|
||||
|
||||
export interface AdminFulfillmentProviderOptionsListResponse
|
||||
extends PaginatedResponse<{
|
||||
fulfillment_options: AdminFulfillmentProviderOption[]
|
||||
}> {}
|
||||
|
||||
@@ -8,3 +8,14 @@ export interface BaseFulfillmentProvider {
|
||||
*/
|
||||
is_enabled: boolean
|
||||
}
|
||||
|
||||
export interface BaseFulfillmentProviderOption {
|
||||
/**
|
||||
* The fulfillment provider option's ID.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* Whether the fulfillment provider option can be used for returns.
|
||||
*/
|
||||
is_return: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user