feat(medusa, core-flows, fulfillment): calculate SO price endpoint (#10532)
**What** - endpoint + flow for fetching calculated price for a shipping option --- CLOSES CMRC-777
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
CalculateShippingOptionPriceDTO,
|
||||
IFulfillmentModuleService,
|
||||
} from "@medusajs/framework/types"
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
export const calculateShippingOptionsPricesStepId =
|
||||
"calculate-shipping-options-prices"
|
||||
/**
|
||||
* This step calculates the prices for one or more shipping options.
|
||||
*/
|
||||
export const calculateShippingOptionsPricesStep = createStep(
|
||||
calculateShippingOptionsPricesStepId,
|
||||
async (input: CalculateShippingOptionPriceDTO[], { container }) => {
|
||||
const service = container.resolve<IFulfillmentModuleService>(
|
||||
Modules.FULFILLMENT
|
||||
)
|
||||
|
||||
const prices = await service.calculateShippingOptionsPrices(input)
|
||||
|
||||
return new StepResponse(prices)
|
||||
}
|
||||
)
|
||||
@@ -15,3 +15,4 @@ export * from "./update-fulfillment"
|
||||
export * from "./update-shipping-profiles"
|
||||
export * from "./upsert-shipping-options"
|
||||
export * from "./validate-shipment"
|
||||
export * from "./calculate-shipping-options-prices"
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { FulfillmentWorkflow } from "@medusajs/framework/types"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { calculateShippingOptionsPricesStep } from "../steps"
|
||||
import { useQueryGraphStep } from "../../common"
|
||||
|
||||
export const calculateShippingOptionsPricesWorkflowId =
|
||||
"calculate-shipping-options-prices-workflow"
|
||||
/**
|
||||
* This workflow calculates the prices for one or more shipping options.
|
||||
*/
|
||||
export const calculateShippingOptionsPricesWorkflow = createWorkflow(
|
||||
calculateShippingOptionsPricesWorkflowId,
|
||||
(
|
||||
input: WorkflowData<FulfillmentWorkflow.CalculateShippingOptionsPricesWorkflowInput>
|
||||
): WorkflowResponse<FulfillmentWorkflow.CalculateShippingOptionsPricesWorkflowOutput> => {
|
||||
const ids = transform({ input }, ({ input }) =>
|
||||
input.shipping_options.map((so) => so.id)
|
||||
)
|
||||
|
||||
const shippingOptionsQuery = useQueryGraphStep({
|
||||
entity: "shipping_option",
|
||||
filters: { id: ids },
|
||||
fields: ["id", "provider_id", "data"],
|
||||
}).config({ name: "shipping-options-query" })
|
||||
|
||||
const cartQuery = useQueryGraphStep({
|
||||
entity: "cart",
|
||||
filters: { id: input.cart_id },
|
||||
fields: ["id", "items.*", "shipping_address.*"],
|
||||
}).config({ name: "cart-query" })
|
||||
|
||||
const data = transform(
|
||||
{ shippingOptionsQuery, cartQuery, input },
|
||||
({ shippingOptionsQuery, cartQuery, input }) => {
|
||||
const shippingOptions = shippingOptionsQuery.data
|
||||
const cart = cartQuery.data[0]
|
||||
|
||||
const shippingOptionDataMap = new Map(
|
||||
input.shipping_options.map((so) => [so.id, so.data])
|
||||
)
|
||||
|
||||
return shippingOptions.map((shippingOption) => ({
|
||||
id: shippingOption.id,
|
||||
provider_id: shippingOption.provider_id,
|
||||
optionData: shippingOption.data,
|
||||
data: shippingOptionDataMap.get(shippingOption.id) ?? {},
|
||||
context: {
|
||||
cart,
|
||||
},
|
||||
}))
|
||||
}
|
||||
)
|
||||
|
||||
const prices = calculateShippingOptionsPricesStep(data)
|
||||
|
||||
return new WorkflowResponse(prices)
|
||||
}
|
||||
)
|
||||
@@ -14,3 +14,4 @@ export * from "./update-fulfillment"
|
||||
export * from "./update-service-zones"
|
||||
export * from "./update-shipping-options"
|
||||
export * from "./update-shipping-profiles"
|
||||
export * from "./calculate-shipping-options-prices"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CreateShippingOptionTypeDTO } from "./shipping-option-type"
|
||||
import { ShippingOptionPriceType } from "../common"
|
||||
import { CreateShippingOptionRuleDTO } from "./shipping-option-rule"
|
||||
import { CartDTO } from "../../cart"
|
||||
|
||||
/**
|
||||
* The shipping option to be created.
|
||||
@@ -118,3 +119,39 @@ export interface UpdateShippingOptionDTO {
|
||||
* A shipping option to be created or updated.
|
||||
*/
|
||||
export interface UpsertShippingOptionDTO extends UpdateShippingOptionDTO {}
|
||||
|
||||
/**
|
||||
* The data needed for the associated fulfillment provider to calculate the price of a shipping option.
|
||||
*/
|
||||
export interface CalculateShippingOptionPriceDTO {
|
||||
/**
|
||||
* The ID of the shipping option.
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* The ID of the fulfillment provider.
|
||||
*/
|
||||
provider_id: string
|
||||
|
||||
/**
|
||||
* The option data from the provider.
|
||||
*/
|
||||
optionData: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
data: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* The calculation context needed for the associated fulfillment provider to calculate the price of a shipping option.
|
||||
*/
|
||||
context: {
|
||||
cart: Pick<CartDTO, "id" | "items" | "shipping_address" | "email">
|
||||
} & Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export type FulfillmentOption = {
|
||||
}
|
||||
|
||||
export type CalculatedShippingOptionPrice = {
|
||||
calculated_amount: number
|
||||
calculated_price: number
|
||||
is_calculated_price_tax_inclusive: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
ShippingProfileDTO,
|
||||
} from "./common"
|
||||
import {
|
||||
CalculateShippingOptionPriceDTO,
|
||||
CreateFulfillmentSetDTO,
|
||||
CreateGeoZoneDTO,
|
||||
CreateServiceZoneDTO,
|
||||
@@ -44,6 +45,7 @@ import {
|
||||
CreateShippingProfileDTO,
|
||||
UpsertShippingProfileDTO,
|
||||
} from "./mutations/shipping-profile"
|
||||
import { CalculatedShippingOptionPrice } from "./provider"
|
||||
|
||||
/**
|
||||
* The main service interface for the Fulfillment Module.
|
||||
@@ -2647,6 +2649,33 @@ export interface IFulfillmentModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<boolean[]>
|
||||
|
||||
/**
|
||||
* This method calculates the prices for one or more shipping options.
|
||||
*
|
||||
* @param {CalculateShippingOptionPriceDTO[]} shippingOptionsData - The shipping options data to calculate the prices for.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<CalculatedShippingOptionPrice[]>} The calculated shipping option prices.
|
||||
*
|
||||
* @example
|
||||
* const prices =
|
||||
* await fulfillmentModuleService.calculateShippingOptionsPrices(
|
||||
* [
|
||||
* {
|
||||
* provider_id: "webshipper",
|
||||
* data: {
|
||||
* cart: {
|
||||
* id: "cart_123",
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* ]
|
||||
* )
|
||||
*/
|
||||
calculateShippingOptionsPrices(
|
||||
shippingOptionsData: CalculateShippingOptionPriceDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<CalculatedShippingOptionPrice[]>
|
||||
|
||||
/**
|
||||
* This method retrieves a paginated list of fulfillment providers based on optional filters and configuration.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./entities"
|
||||
export * from "./queries"
|
||||
export * from "./responses"
|
||||
export * from "./payloads"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type StoreCalculateShippingOptionPrice = {
|
||||
cart_id: string
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
@@ -3,3 +3,7 @@ import { StoreCartShippingOption } from "../../fulfillment"
|
||||
export interface StoreShippingOptionListResponse {
|
||||
shipping_options: StoreCartShippingOption[]
|
||||
}
|
||||
|
||||
export interface StoreShippingOptionResponse {
|
||||
shipping_option: StoreCartShippingOption
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CalculatedShippingOptionPrice } from "../../fulfillment"
|
||||
|
||||
export type CalculateShippingOptionsPricesWorkflowInput = {
|
||||
cart_id: string
|
||||
shipping_options: { id: string; data: Record<string, unknown> }[]
|
||||
}
|
||||
|
||||
export type CalculateShippingOptionsPricesWorkflowOutput =
|
||||
CalculatedShippingOptionPrice[]
|
||||
@@ -6,3 +6,4 @@ export * from "./service-zones"
|
||||
export * from "./shipping-profiles"
|
||||
export * from "./update-fulfillment"
|
||||
export * from "./update-shipping-options"
|
||||
export * from "./calculate-shipping-options-prices"
|
||||
|
||||
@@ -202,19 +202,18 @@ export class AbstractFulfillmentProviderService
|
||||
* 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 - The `data` property of a shipping option.
|
||||
* @param data - If the price is calculated for a shipping option, it's the `data` of the shipping option. Otherwise, it's the `data of the shipping method.
|
||||
* @param cart - The cart details.
|
||||
* @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
|
||||
*
|
||||
* @example
|
||||
* class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
|
||||
* // ...
|
||||
* async calculatePrice(optionData: any, data: any, cart: any): Promise<number> {
|
||||
* async calculatePrice(optionData: any, data: any, context: any): Promise<number> {
|
||||
* // assuming the client can calculate the price using
|
||||
* // the third-party service
|
||||
* const price = await this.client.calculate(data)
|
||||
*
|
||||
* return price
|
||||
* }
|
||||
* }
|
||||
|
||||
Reference in New Issue
Block a user