feat(core-flows, dashboard, fulfillment, fulfillment-manual, utils, types): create shipping options with calculated prices (#10495)
**What** - support creating SO with calculated price - support updating SO for both types of pricing - update `validateShippingOptionPricesStep` to handle both SO price_types - add the `validateShippingOptionsForPriceCalculation` method to `FulfillementModule` - add `canCalculate` and `calculatePrice` to fulfillment provider service service / interface / manual provider - disable SO pricing edit on Admin if SO price type is calculated --- CLOSES CMRC-776
This commit is contained in:
@@ -25,7 +25,7 @@ interface PriceRegionId {
|
||||
|
||||
export type SetShippingOptionsPricesStepInput = {
|
||||
id: string
|
||||
prices?: FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput["prices"]
|
||||
prices?: FulfillmentWorkflow.UpdateShippingOptionPriceRecord[]
|
||||
}[]
|
||||
|
||||
async function getCurrentShippingOptionPrices(
|
||||
|
||||
@@ -1,26 +1,85 @@
|
||||
import { FulfillmentWorkflow } from "@medusajs/framework/types"
|
||||
import { MedusaError, Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
MedusaError,
|
||||
Modules,
|
||||
ShippingOptionPriceType,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
type OptionsInput = (
|
||||
| FulfillmentWorkflow.CreateShippingOptionsWorkflowInput
|
||||
| FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput
|
||||
)[]
|
||||
|
||||
export const validateShippingOptionPricesStepId =
|
||||
"validate-shipping-option-prices"
|
||||
|
||||
/**
|
||||
* Validate that regions exist for the shipping option prices.
|
||||
* Validate that shipping options can be crated based on provided price configuration.
|
||||
*
|
||||
* For flat rate prices, it validates that regions exist for the shipping option prices.
|
||||
* For calculated prices, it validates with the fulfillment provider if the price can be calculated.
|
||||
*/
|
||||
export const validateShippingOptionPricesStep = createStep(
|
||||
validateShippingOptionPricesStepId,
|
||||
async (
|
||||
options: {
|
||||
prices?: FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput["prices"]
|
||||
}[],
|
||||
{ container }
|
||||
) => {
|
||||
const allPrices = options.flatMap((option) => option.prices ?? [])
|
||||
async (options: OptionsInput, { container }) => {
|
||||
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
|
||||
|
||||
const optionIds = options.map(
|
||||
(option) =>
|
||||
(option as FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput).id
|
||||
)
|
||||
|
||||
if (optionIds.length) {
|
||||
/**
|
||||
* This means we are validating an update of shipping options.
|
||||
* We need to ensure that all shipping options have price_type set
|
||||
* to correctly determine price updates.
|
||||
*
|
||||
* (On create, price_type must be defined already.)
|
||||
*/
|
||||
const shippingOptions =
|
||||
await fulfillmentModuleService.listShippingOptions(
|
||||
{
|
||||
id: optionIds,
|
||||
},
|
||||
{ select: ["id", "price_type", "provider_id"] }
|
||||
)
|
||||
|
||||
const optionsMap = new Map(
|
||||
shippingOptions.map((option) => [option.id, option])
|
||||
)
|
||||
|
||||
;(
|
||||
options as FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput[]
|
||||
).forEach((option) => {
|
||||
option.price_type =
|
||||
option.price_type ?? optionsMap.get(option.id)?.price_type
|
||||
option.provider_id =
|
||||
option.provider_id ?? optionsMap.get(option.id)?.provider_id
|
||||
})
|
||||
}
|
||||
|
||||
const flatRatePrices: FulfillmentWorkflow.UpdateShippingOptionPriceRecord[] =
|
||||
[]
|
||||
const calculatedOptions: OptionsInput = []
|
||||
|
||||
options.forEach((option) => {
|
||||
if (option.price_type === ShippingOptionPriceType.FLAT) {
|
||||
flatRatePrices.push(...(option.prices ?? []))
|
||||
}
|
||||
if (option.price_type === ShippingOptionPriceType.CALCULATED) {
|
||||
calculatedOptions.push(option)
|
||||
}
|
||||
})
|
||||
|
||||
await fulfillmentModuleService.validateShippingOptionsForPriceCalculation(
|
||||
calculatedOptions as FulfillmentWorkflow.CreateShippingOptionsWorkflowInput[]
|
||||
)
|
||||
|
||||
const regionIdSet = new Set<string>()
|
||||
|
||||
allPrices.forEach((price) => {
|
||||
flatRatePrices.forEach((price) => {
|
||||
if ("region_id" in price && price.region_id) {
|
||||
regionIdSet.add(price.region_id)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,13 @@ export const createShippingOptionsWorkflow = createWorkflow(
|
||||
|
||||
const data = transform(input, (data) => {
|
||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||
const prices = option.prices
|
||||
/**
|
||||
* Flat rate ShippingOptions always needs to provide a price array.
|
||||
*
|
||||
* For calculated pricing we create an "empty" price set
|
||||
* so we can have simpler update flow for both cases and allow updating price_type.
|
||||
*/
|
||||
const prices = (option as any).prices ?? []
|
||||
return {
|
||||
shipping_option_index: index,
|
||||
prices,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "../steps"
|
||||
import { validateFulfillmentProvidersStep } from "../steps/validate-fulfillment-providers"
|
||||
import { validateShippingOptionPricesStep } from "../steps/validate-shipping-option-prices"
|
||||
import { ShippingOptionPriceType } from "@medusajs/framework/utils"
|
||||
|
||||
export const updateShippingOptionsWorkflowId =
|
||||
"update-shipping-options-workflow"
|
||||
@@ -32,11 +33,22 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
||||
|
||||
const data = transform(input, (data) => {
|
||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||
const prices = option.prices
|
||||
delete option.prices
|
||||
const prices = (
|
||||
option as FulfillmentWorkflow.UpdateFlatRateShippingOptionInput
|
||||
).prices
|
||||
|
||||
delete (option as FulfillmentWorkflow.UpdateFlatRateShippingOptionInput)
|
||||
.prices
|
||||
|
||||
/**
|
||||
* When we are updating an option to be calculated, remove the prices.
|
||||
*/
|
||||
const isCalculatedOption =
|
||||
option.price_type === ShippingOptionPriceType.CALCULATED
|
||||
|
||||
return {
|
||||
shipping_option_index: index,
|
||||
prices,
|
||||
prices: isCalculatedOption ? [] : prices,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -58,8 +70,10 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
||||
(data) => {
|
||||
const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map(
|
||||
({ shipping_option_index, prices }) => {
|
||||
const option = data.shippingOptions[shipping_option_index]
|
||||
|
||||
return {
|
||||
id: data.shippingOptions[shipping_option_index].id,
|
||||
id: option.id,
|
||||
prices,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user