docs: document conditional shipping option prices + expand on price rules (#12217)
* docs: document conditional shipping option prices + expand on price rules * fix errors
This commit is contained in:
+14029
-13901
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,12 @@ Service zones can be more restrictive, such as restricting to certain cities or
|
||||
|
||||
You can restrict shipping options by custom rules, such as the item’s weight or the customer’s group.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
You can also restrict a shipping option's price based on specific conditions. For example, you can make a shipping option's price free based on the cart's total. Learn more in the Pricing Module's [Price Rules](../../pricing/price-rules/page.mdx#how-to-set-rules-on-a-price) guide.
|
||||
|
||||
</Note>
|
||||
|
||||
These rules are represented by the [ShippingOptionRule data model](/references/fulfillment/models/ShippingOptionRule). Its properties define the custom rule:
|
||||
|
||||
- `attribute`: The name of a property or table that the rule applies to. For example, `customer_group`.
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
---
|
||||
tags:
|
||||
- name: product
|
||||
label: "Variant Price Rules"
|
||||
- name: fulfillment
|
||||
label: "Shipping Option Price Rules"
|
||||
- concept
|
||||
---
|
||||
|
||||
export const metadata = {
|
||||
title: `Price Rules`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn about price rules for price sets and price lists.
|
||||
In this Pricing Module guide, you'll learn about price rules for price sets and price lists, and how to add rules to a price.
|
||||
|
||||
## Price Rule
|
||||
|
||||
@@ -31,3 +40,142 @@ Rules applied to a price list are represented by the [PriceListRule data model](
|
||||
The `rules_count` property of a `PriceList` indicates how many rules are applied to it.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## How to Set Rules on a Price?
|
||||
|
||||
### Using Workflows
|
||||
|
||||
Medusa uses the Pricing Module to store prices of different resources, such as product variants and shipping options.
|
||||
|
||||
When you manage one of these resources using [Medusa's workflows](../../../medusa-workflows-reference/page.mdx) or using the API routes that use them, you can set rules on a price using the `rules` property of the price object.
|
||||
|
||||
For example, when creating a shipping option using the [createShippingOptionsWorkflow](!resources!/references/medusa-workflows/createShippingOptionsWorkflow) to create a shipping option, you can make the shipping price free based on the cart total:
|
||||
|
||||
export const workflowHighlights = [
|
||||
["19", "rules", "The default price doesn't have rules."],
|
||||
["26", "item_total", "Apply the price if the cart or order's total matches the condition."]
|
||||
]
|
||||
|
||||
```ts highlights={workflowHighlights}
|
||||
const { result } = await createShippingOptionsWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
name: "Standard Shipping",
|
||||
service_zone_id: "serzo_123",
|
||||
shipping_profile_id: "sp_123",
|
||||
provider_id: "prov_123",
|
||||
type: {
|
||||
label: "Standard",
|
||||
description: "Standard shipping",
|
||||
code: "standard",
|
||||
},
|
||||
price_type: "flat",
|
||||
prices: [
|
||||
// default price
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 10,
|
||||
rules: {},
|
||||
},
|
||||
// price if cart total >= $100
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 0,
|
||||
rules: {
|
||||
item_total: {
|
||||
operator: "gte",
|
||||
value: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}],
|
||||
})
|
||||
```
|
||||
|
||||
In this example, you create a shipping option whose default price is `$10`. When the total of the cart or order using this shipping option is greater than `$100`, the shipping option's price becomes free.
|
||||
|
||||
### Using Pricing Module's Service
|
||||
|
||||
<Note>
|
||||
|
||||
For most use cases, it's recommended to use [workflows](#using-workflows) instead of directly using the module's service.
|
||||
|
||||
</Note>
|
||||
|
||||
When adding a price using the [addPrices](!resources!/references/pricing/addPrices) method of the Pricing Module's service, pass the `rules` property to a price object.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const priceSet = await pricingModule.addPrices({
|
||||
priceSetId: "pset_1",
|
||||
prices: [
|
||||
// default price
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 10,
|
||||
rules: {},
|
||||
},
|
||||
// price if cart total >= $100
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 0,
|
||||
rules: {
|
||||
item_total: {
|
||||
operator: "gte",
|
||||
value: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
In this example, you set the default price of a resource (for example, a shipping option), to `$10`. You also add a conditioned price that sets the price to `0` when the cart or order's total is greater than or equal to `$100`.
|
||||
|
||||
### How is the Price Rule Applied?
|
||||
|
||||
The [price calculation](../price-calculation/page.mdx) mechanism considers a price applicable when the resource that this price is in matches the specified rules.
|
||||
|
||||
For example, a [cart object](!api!/store#carts_cart_schema) has an `item_total` property. So, if a shipping option has the following price:
|
||||
|
||||
```json
|
||||
{
|
||||
"currency_code": "usd",
|
||||
"amount": 0,
|
||||
"rules": {
|
||||
"item_total": {
|
||||
"operator": "gte",
|
||||
"value": 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The shipping option's price is applied when the cart's `item_total` is greater than or equal to `$100`.
|
||||
|
||||
You can also apply the rule on nested relations and properties. For example, to apply a shipping option's price based on the customer's group, you can apply a rule on the `customer.group.id` attribute:
|
||||
|
||||
```json
|
||||
{
|
||||
"currency_code": "usd",
|
||||
"amount": 0,
|
||||
"rules": {
|
||||
"customer.group.id": {
|
||||
"operator": "eq",
|
||||
"value": "cusgrp_123"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the price is only applied if a cart's customer belongs to the customer group of ID `cusgrp_123`.
|
||||
|
||||
<Note>
|
||||
|
||||
These same rules apply to product variant prices as well, or any other resource that has a price.
|
||||
|
||||
</Note>
|
||||
|
||||
@@ -628,7 +628,7 @@ export const deductStepHighlights = [
|
||||
```ts title="src/workflows/steps/deduct-purchase-points.ts" highlights={deductStepHighlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse
|
||||
StepResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { LOYALTY_MODULE } from "../../modules/loyalty"
|
||||
import LoyaltyModuleService from "../../modules/loyalty/service"
|
||||
@@ -641,7 +641,7 @@ type DeductPurchasePointsInput = {
|
||||
export const deductPurchasePointsStep = createStep(
|
||||
"deduct-purchase-points",
|
||||
async ({
|
||||
customer_id, amount
|
||||
customer_id, amount,
|
||||
}: DeductPurchasePointsInput, { container }) => {
|
||||
const loyaltyModuleService: LoyaltyModuleService = container.resolve(
|
||||
LOYALTY_MODULE
|
||||
@@ -658,7 +658,7 @@ export const deductPurchasePointsStep = createStep(
|
||||
|
||||
return new StepResponse(result, {
|
||||
customer_id,
|
||||
points: pointsToDeduct
|
||||
points: pointsToDeduct,
|
||||
})
|
||||
},
|
||||
async (data, { container }) => {
|
||||
@@ -748,7 +748,7 @@ export const addPurchaseAsPointsStep = createStep(
|
||||
|
||||
return new StepResponse(result, {
|
||||
customer_id: input.customer_id,
|
||||
points: pointsToAdd
|
||||
points: pointsToAdd,
|
||||
})
|
||||
},
|
||||
async (data, { container }) => {
|
||||
@@ -868,18 +868,18 @@ export const handleOrderPointsWorkflow = createWorkflow(
|
||||
"cart.promotions.*",
|
||||
"cart.promotions.rules.*",
|
||||
"cart.promotions.rules.values.*",
|
||||
"cart.promotions.application_method.*"
|
||||
"cart.promotions.application_method.*",
|
||||
],
|
||||
filters: {
|
||||
id: order_id
|
||||
id: order_id,
|
||||
},
|
||||
options: {
|
||||
throwIfKeyNotFound: true
|
||||
}
|
||||
throwIfKeyNotFound: true,
|
||||
},
|
||||
})
|
||||
|
||||
validateCustomerExistsStep({
|
||||
customer: orders[0].customer
|
||||
customer: orders[0].customer,
|
||||
} as ValidateCustomerExistsStepInput)
|
||||
|
||||
const loyaltyPointsPromotion = getCartLoyaltyPromoStep({
|
||||
@@ -893,14 +893,14 @@ export const handleOrderPointsWorkflow = createWorkflow(
|
||||
.then(() => {
|
||||
deductPurchasePointsStep({
|
||||
customer_id: orders[0].customer!.id,
|
||||
amount: loyaltyPointsPromotion.application_method!.value as number
|
||||
amount: loyaltyPointsPromotion.application_method!.value as number,
|
||||
})
|
||||
|
||||
updatePromotionsStep([
|
||||
{
|
||||
id: loyaltyPointsPromotion.id,
|
||||
status: "inactive",
|
||||
}
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
@@ -912,7 +912,7 @@ export const handleOrderPointsWorkflow = createWorkflow(
|
||||
.then(() => {
|
||||
addPurchaseAsPointsStep({
|
||||
customer_id: orders[0].customer!.id,
|
||||
amount: orders[0].total
|
||||
amount: orders[0].total,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1056,10 +1056,10 @@ So, to create an API route at the path `/store/customers/me/loyalty-points`, cre
|
||||
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse
|
||||
} from "@medusajs/framework/http";
|
||||
import { LOYALTY_MODULE } from "../../../../../modules/loyalty";
|
||||
import LoyaltyModuleService from "../../../../../modules/loyalty/service";
|
||||
MedusaResponse,
|
||||
} from "@medusajs/framework/http"
|
||||
import { LOYALTY_MODULE } from "../../../../../modules/loyalty"
|
||||
import LoyaltyModuleService from "../../../../../modules/loyalty/service"
|
||||
|
||||
export async function GET(
|
||||
req: AuthenticatedMedusaRequest,
|
||||
@@ -1405,17 +1405,17 @@ import {
|
||||
createPromotionsStep,
|
||||
updateCartPromotionsWorkflow,
|
||||
updateCartsStep,
|
||||
useQueryGraphStep
|
||||
useQueryGraphStep,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
import {
|
||||
validateCustomerExistsStep,
|
||||
ValidateCustomerExistsStepInput
|
||||
ValidateCustomerExistsStepInput,
|
||||
} from "./steps/validate-customer-exists"
|
||||
import {
|
||||
getCartLoyaltyPromoAmountStep,
|
||||
GetCartLoyaltyPromoAmountStepInput
|
||||
GetCartLoyaltyPromoAmountStepInput,
|
||||
} from "./steps/get-cart-loyalty-promo-amount"
|
||||
import { CartData, CUSTOMER_ID_PROMOTION_RULE_ATTRIBUTE, } from "../utils/promo"
|
||||
import { CartData, CUSTOMER_ID_PROMOTION_RULE_ATTRIBUTE } from "../utils/promo"
|
||||
import { CreatePromotionDTO } from "@medusajs/framework/types"
|
||||
import { PromotionActions } from "@medusajs/framework/utils"
|
||||
import { getCartLoyaltyPromoStep } from "./steps/get-cart-loyalty-promo"
|
||||
@@ -1433,7 +1433,7 @@ const fields = [
|
||||
"promotions.rules.values.*",
|
||||
"currency_code",
|
||||
"total",
|
||||
"metadata"
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const applyLoyaltyOnCartWorkflow = createWorkflow(
|
||||
@@ -1444,24 +1444,24 @@ export const applyLoyaltyOnCartWorkflow = createWorkflow(
|
||||
entity: "cart",
|
||||
fields,
|
||||
filters: {
|
||||
id: input.cart_id
|
||||
id: input.cart_id,
|
||||
},
|
||||
options: {
|
||||
throwIfKeyNotFound: true
|
||||
}
|
||||
throwIfKeyNotFound: true,
|
||||
},
|
||||
})
|
||||
|
||||
validateCustomerExistsStep({
|
||||
customer: carts[0].customer
|
||||
customer: carts[0].customer,
|
||||
} as ValidateCustomerExistsStepInput)
|
||||
|
||||
getCartLoyaltyPromoStep({
|
||||
cart: carts[0] as unknown as CartData,
|
||||
throwErrorOn: "found"
|
||||
throwErrorOn: "found",
|
||||
})
|
||||
|
||||
const amount = getCartLoyaltyPromoAmountStep({
|
||||
cart: carts[0]
|
||||
cart: carts[0],
|
||||
} as unknown as GetCartLoyaltyPromoAmountStepInput)
|
||||
|
||||
// TODO create and apply the promotion on the cart
|
||||
@@ -1491,7 +1491,7 @@ export const prepareLoyaltyPromoDataHighlights = [
|
||||
```ts title="src/workflows/apply-loyalty-on-cart.ts" highlights={prepareLoyaltyPromoDataHighlights}
|
||||
const promoToCreate = transform({
|
||||
carts,
|
||||
amount
|
||||
amount,
|
||||
}, (data) => {
|
||||
const randomStr = Math.random().toString(36).substring(2, 8)
|
||||
const uniqueId = (
|
||||
@@ -1512,8 +1512,8 @@ const promoToCreate = transform({
|
||||
{
|
||||
attribute: CUSTOMER_ID_PROMOTION_RULE_ATTRIBUTE,
|
||||
operator: "eq",
|
||||
values: [data.carts[0].customer!.id]
|
||||
}
|
||||
values: [data.carts[0].customer!.id],
|
||||
},
|
||||
],
|
||||
campaign: {
|
||||
name: uniqueId,
|
||||
@@ -1521,9 +1521,9 @@ const promoToCreate = transform({
|
||||
campaign_identifier: uniqueId,
|
||||
budget: {
|
||||
type: "usage",
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1559,17 +1559,17 @@ export const createLoyaltyPromoStepHighlights = [
|
||||
|
||||
```ts title="src/workflows/apply-loyalty-on-cart.ts" highlights={createLoyaltyPromoStepHighlights}
|
||||
const loyaltyPromo = createPromotionsStep([
|
||||
promoToCreate
|
||||
promoToCreate,
|
||||
] as CreatePromotionDTO[])
|
||||
|
||||
const { metadata, ...updatePromoData } = transform({
|
||||
carts,
|
||||
promoToCreate,
|
||||
loyaltyPromo
|
||||
loyaltyPromo,
|
||||
}, (data) => {
|
||||
const promos = [
|
||||
...(data.carts[0].promotions?.map((promo) => promo?.code).filter(Boolean) || []) as string[],
|
||||
data.promoToCreate.code
|
||||
data.promoToCreate.code,
|
||||
]
|
||||
|
||||
return {
|
||||
@@ -1577,20 +1577,20 @@ const { metadata, ...updatePromoData } = transform({
|
||||
promo_codes: promos,
|
||||
action: PromotionActions.ADD,
|
||||
metadata: {
|
||||
loyalty_promo_id: data.loyaltyPromo[0].id
|
||||
}
|
||||
loyalty_promo_id: data.loyaltyPromo[0].id,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
updateCartPromotionsWorkflow.runAsStep({
|
||||
input: updatePromoData
|
||||
input: updatePromoData,
|
||||
})
|
||||
|
||||
updateCartsStep([
|
||||
{
|
||||
id: input.cart_id,
|
||||
metadata
|
||||
}
|
||||
metadata,
|
||||
},
|
||||
])
|
||||
|
||||
// retrieve cart with updated promotions
|
||||
@@ -1621,8 +1621,8 @@ Next, you'll create the API route that executes this workflow.
|
||||
To create the API route, create the file `src/api/store/carts/[id]/loyalty-points/route.ts` with the following content:
|
||||
|
||||
```ts title="src/api/store/carts/[id]/loyalty-points/route.ts"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
|
||||
import { applyLoyaltyOnCartWorkflow } from "../../../../../workflows/apply-loyalty-on-cart";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
||||
import { applyLoyaltyOnCartWorkflow } from "../../../../../workflows/apply-loyalty-on-cart"
|
||||
|
||||
export async function POST(
|
||||
req: MedusaRequest,
|
||||
@@ -1633,8 +1633,8 @@ export async function POST(
|
||||
const { result: cart } = await applyLoyaltyOnCartWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
cart_id
|
||||
}
|
||||
cart_id,
|
||||
},
|
||||
})
|
||||
|
||||
res.json({ cart })
|
||||
@@ -1808,13 +1808,13 @@ export const removeLoyaltyFromCartWorkflowHighlights = [
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowResponse
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
useQueryGraphStep,
|
||||
updateCartPromotionsWorkflow,
|
||||
updateCartsStep,
|
||||
updatePromotionsStep
|
||||
updatePromotionsStep,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
import { getCartLoyaltyPromoStep } from "./steps/get-cart-loyalty-promo"
|
||||
import { PromotionActions } from "@medusajs/framework/utils"
|
||||
@@ -1833,7 +1833,7 @@ const fields = [
|
||||
"promotions.rules.values.*",
|
||||
"currency_code",
|
||||
"total",
|
||||
"metadata"
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const removeLoyaltyFromCartWorkflow = createWorkflow(
|
||||
@@ -1844,46 +1844,46 @@ export const removeLoyaltyFromCartWorkflow = createWorkflow(
|
||||
entity: "cart",
|
||||
fields,
|
||||
filters: {
|
||||
id: input.cart_id
|
||||
}
|
||||
id: input.cart_id,
|
||||
},
|
||||
})
|
||||
|
||||
const loyaltyPromo = getCartLoyaltyPromoStep({
|
||||
cart: carts[0] as unknown as CartData,
|
||||
throwErrorOn: "not-found"
|
||||
throwErrorOn: "not-found",
|
||||
})
|
||||
|
||||
updateCartPromotionsWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: input.cart_id,
|
||||
promo_codes: [loyaltyPromo.code!],
|
||||
action: PromotionActions.REMOVE
|
||||
}
|
||||
action: PromotionActions.REMOVE,
|
||||
},
|
||||
})
|
||||
|
||||
const newMetadata = transform({
|
||||
carts
|
||||
carts,
|
||||
}, (data) => {
|
||||
const { loyalty_promo_id, ...rest } = data.carts[0].metadata || {}
|
||||
|
||||
return {
|
||||
...rest,
|
||||
loyalty_promo_id: null
|
||||
loyalty_promo_id: null,
|
||||
}
|
||||
})
|
||||
|
||||
updateCartsStep([
|
||||
{
|
||||
id: input.cart_id,
|
||||
metadata: newMetadata
|
||||
}
|
||||
metadata: newMetadata,
|
||||
},
|
||||
])
|
||||
|
||||
updatePromotionsStep([
|
||||
{
|
||||
id: loyaltyPromo.id,
|
||||
status: "inactive"
|
||||
}
|
||||
status: "inactive",
|
||||
},
|
||||
])
|
||||
|
||||
// retrieve cart with updated promotions
|
||||
@@ -1920,7 +1920,7 @@ To create the API route, add the following in `src/api/store/carts/[id]/loyalty-
|
||||
|
||||
```ts title="src/api/store/carts/[id]/loyalty-points/route.ts"
|
||||
// other imports...
|
||||
import { removeLoyaltyFromCartWorkflow } from "../../../../../workflows/remove-loyalty-from-cart";
|
||||
import { removeLoyaltyFromCartWorkflow } from "../../../../../workflows/remove-loyalty-from-cart"
|
||||
|
||||
// ...
|
||||
export async function DELETE(
|
||||
@@ -1932,8 +1932,8 @@ export async function DELETE(
|
||||
const { result: cart } = await removeLoyaltyFromCartWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
cart_id
|
||||
}
|
||||
cart_id,
|
||||
},
|
||||
})
|
||||
|
||||
res.json({ cart })
|
||||
@@ -2033,11 +2033,11 @@ export const completeCartWorkflowHookHighlights = [
|
||||
]
|
||||
|
||||
```ts title="src/workflows/hooks/complete-cart.ts" highlights={completeCartWorkflowHookHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||
import { completeCartWorkflow } from "@medusajs/medusa/core-flows";
|
||||
import LoyaltyModuleService from "../../modules/loyalty/service";
|
||||
import { LOYALTY_MODULE } from "../../modules/loyalty";
|
||||
import { CartData, getCartLoyaltyPromotion } from "../../utils/promo";
|
||||
import { MedusaError } from "@medusajs/framework/utils";
|
||||
import { completeCartWorkflow } from "@medusajs/medusa/core-flows"
|
||||
import LoyaltyModuleService from "../../modules/loyalty/service"
|
||||
import { LOYALTY_MODULE } from "../../modules/loyalty"
|
||||
import { CartData, getCartLoyaltyPromotion } from "../../utils/promo"
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
|
||||
completeCartWorkflow.hooks.validate(
|
||||
async ({ cart }, { container }) => {
|
||||
@@ -2055,13 +2055,13 @@ completeCartWorkflow.hooks.validate(
|
||||
"promotions.rules.*",
|
||||
"promotions.rules.values.*",
|
||||
"promotions.application_method.*",
|
||||
"metadata"
|
||||
"metadata",
|
||||
],
|
||||
filters: {
|
||||
id: cart.id
|
||||
}
|
||||
id: cart.id,
|
||||
},
|
||||
}, {
|
||||
throwIfKeyNotFound: true
|
||||
throwIfKeyNotFound: true,
|
||||
})
|
||||
|
||||
const loyaltyPromo = getCartLoyaltyPromotion(
|
||||
|
||||
@@ -24,7 +24,7 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/fulfillment/fulfillment-provider/page.mdx": "2025-02-26T11:21:56.242Z",
|
||||
"app/commerce-modules/fulfillment/item-fulfillment/page.mdx": "2024-10-08T14:38:15.496Z",
|
||||
"app/commerce-modules/fulfillment/module-options/page.mdx": "2024-10-15T12:51:56.118Z",
|
||||
"app/commerce-modules/fulfillment/shipping-option/page.mdx": "2024-10-08T14:36:02.660Z",
|
||||
"app/commerce-modules/fulfillment/shipping-option/page.mdx": "2025-04-17T13:51:40.980Z",
|
||||
"app/commerce-modules/fulfillment/page.mdx": "2025-04-17T08:48:19.367Z",
|
||||
"app/commerce-modules/inventory/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/inventory/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
@@ -57,7 +57,7 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/pricing/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/pricing/concepts/page.mdx": "2024-10-09T13:37:25.678Z",
|
||||
"app/commerce-modules/pricing/price-calculation/page.mdx": "2024-10-09T13:43:14.038Z",
|
||||
"app/commerce-modules/pricing/price-rules/page.mdx": "2024-10-09T13:38:47.112Z",
|
||||
"app/commerce-modules/pricing/price-rules/page.mdx": "2025-04-17T14:31:26.650Z",
|
||||
"app/commerce-modules/pricing/tax-inclusive-pricing/page.mdx": "2024-10-09T13:48:23.261Z",
|
||||
"app/commerce-modules/pricing/page.mdx": "2025-04-17T08:48:29.165Z",
|
||||
"app/commerce-modules/product/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
@@ -6072,7 +6072,7 @@ export const generatedEditDates = {
|
||||
"app/integrations/guides/algolia/page.mdx": "2025-04-17T08:29:01.433Z",
|
||||
"app/integrations/guides/magento/page.mdx": "2025-04-17T08:29:01.500Z",
|
||||
"app/js-sdk/auth/overview/page.mdx": "2025-03-28T08:05:32.622Z",
|
||||
"app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-04-17T08:48:07.930Z",
|
||||
"app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-04-17T14:31:04.727Z",
|
||||
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.plugin/page.mdx": "2025-04-11T09:04:55.084Z",
|
||||
"references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.createAddress/page.mdx": "2025-04-11T09:04:54.010Z",
|
||||
"references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.deleteAddress/page.mdx": "2025-04-11T09:04:54.015Z",
|
||||
|
||||
@@ -3283,7 +3283,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Concepts",
|
||||
"autogenerate_tags": "concept+fulfillment",
|
||||
"autogenerate_tags": "fulfillment+concept",
|
||||
"autogenerate_as_ref": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -3326,6 +3326,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
|
||||
"title": "Links to Other Modules",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "ref",
|
||||
"title": "Shipping Option Price Rules",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
@@ -11018,7 +11026,7 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Concepts",
|
||||
"autogenerate_tags": "concept+product",
|
||||
"autogenerate_tags": "product+concept",
|
||||
"autogenerate_as_ref": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -11052,6 +11060,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
|
||||
"title": "Inventory Kits",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/inventory/inventory-kit",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "ref",
|
||||
"title": "Variant Price Rules",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export const fulfillmentSidebar = [
|
||||
{
|
||||
type: "category",
|
||||
title: "Concepts",
|
||||
autogenerate_tags: "concept+fulfillment",
|
||||
autogenerate_tags: "fulfillment+concept",
|
||||
autogenerate_as_ref: true,
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ export const productSidebar = [
|
||||
{
|
||||
type: "category",
|
||||
title: "Concepts",
|
||||
autogenerate_tags: "concept+product",
|
||||
autogenerate_tags: "product+concept",
|
||||
autogenerate_as_ref: true,
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ tags:
|
||||
label: "Manage Shipping Options"
|
||||
---
|
||||
|
||||
import { EllipsisHorizontal, TaxExclusive, TaxInclusive } from "@medusajs/icons"
|
||||
import { EllipsisHorizontal, TaxExclusive, TaxInclusive, ArrowsPointingOut, Plus } from "@medusajs/icons"
|
||||
|
||||
export const metadata = {
|
||||
title: `Manage Locations in Medusa Admin`,
|
||||
@@ -133,9 +133,9 @@ To create a shipping option:
|
||||
|
||||
If you chose a "Calculated" price type, you can click the "Save" button to create the shipping option. Otherwise click Continue to proceed to the Prices step.
|
||||
|
||||
In the Prices step, use the [Bulk Editor](../../../tips/bulk-editor/page.mdx) to enter the shipping option's prices for every currency and region in your store. Once you're done, click the Save button.
|
||||
#### Fixed and Conditional Shipping Option Prices
|
||||
|
||||
Learn how you can further manage the shipping option in the [Manage Shipping Options section](#manage-shipping-options).
|
||||
In the Prices step, use the [Bulk Editor](../../../tips/bulk-editor/page.mdx) to enter the shipping option's prices for every currency and region in your store.
|
||||
|
||||
<Note title="Tips">
|
||||
|
||||
@@ -148,6 +148,29 @@ Tax-inclusive pricing is configured in the [currency's](../../store/page.mdx#edi
|
||||
|
||||

|
||||
|
||||
Shipping option prices can also be conditioned based on the cart's total. For example, you can make the shipping option free if the cart's total exceeds `$100`. So, you can set a fixed price for a shipping option, and then set a conditioned price based on the cart's total.
|
||||
|
||||
To set a conditioned price for the shipping option:
|
||||
|
||||
1. Hover over a cell in the Bulk Editor based on the currency or region you want to set the conditioned price for.
|
||||
2. Click the <InlineIcon Icon={ArrowsPointingOut} alt="arrows-pointing-out" /> icon that appears to open the conditional pricing form.
|
||||
3. In the form that opens:
|
||||
- Conditional pricing card will appear, and you can add another one by clicking the "Add price" button.
|
||||
- Enter in the "Shipping option price" field the price when this condition is satisfied.
|
||||
- To set the minimum cart total required for this price to apply:
|
||||
- Click the <InlineIcon Icon={Plus} alt="plus" /> next to "Minimum cart total".
|
||||
- In the field that shows, enter the minimum cart total required for this price to apply.
|
||||
- To set the maximum cart total required for this price to apply:
|
||||
- Click the <InlineIcon Icon={Plus} alt="plus" /> next to "Maximum cart total".
|
||||
- In the field that shows, enter the maximum cart total required for this price to apply.
|
||||
- Once you're done, click the Save button.
|
||||
|
||||
Once you're done, click the Save button.
|
||||
|
||||

|
||||
|
||||
Learn how you can further manage the shipping option in the [Manage Shipping Options section](#manage-shipping-options).
|
||||
|
||||
---
|
||||
|
||||
## Manage Service Zones
|
||||
@@ -222,6 +245,7 @@ To edit the prices of shipping options whose price type is Fixed:
|
||||
2. In the "Pickup" or "Shipping" sections, find the shipping option and click on the <InlineIcon Icon={EllipsisHorizontal} alt="three-dots" /> icon at its right.
|
||||
3. Choose "Edit prices" from the dropdown.
|
||||
4. In the [Bulk Editor](../../../tips/bulk-editor/page.mdx) that opens, you can edit the shipping option's price for every currency and region in your store.
|
||||
- Refer to the [Fixed and Conditional Shipping Option Prices](#fixed-and-conditional-shipping-option-prices) section to learn how to set a conditional price for the shipping option.
|
||||
5. Once you're done, click the Save button.
|
||||
|
||||
<Note title="Tips">
|
||||
|
||||
@@ -47,7 +47,7 @@ export const generatedEditDates = {
|
||||
"app/price-lists/manage/page.mdx": "2025-02-19T10:35:49.881Z",
|
||||
"app/price-lists/page.mdx": "2025-02-19T09:51:32.546Z",
|
||||
"app/settings/tax-regions/page.mdx": "2025-02-19T17:33:43.806Z",
|
||||
"app/settings/locations-and-shipping/locations/page.mdx": "2025-02-25T15:02:02.164Z",
|
||||
"app/settings/locations-and-shipping/locations/page.mdx": "2025-04-17T13:47:40.883Z",
|
||||
"app/settings/locations-and-shipping/page.mdx": "2025-02-19T17:23:45.824Z",
|
||||
"app/settings/locations-and-shipping/shipping-profiles/page.mdx": "2025-02-19T17:36:46.339Z",
|
||||
"app/settings/product-tags/page.mdx": "2025-02-19T17:36:25.102Z",
|
||||
|
||||
@@ -3,6 +3,10 @@ export const concept = [
|
||||
"title": "Inventory Kits",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/inventory/inventory-kit"
|
||||
},
|
||||
{
|
||||
"title": "Price Rules",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules"
|
||||
},
|
||||
{
|
||||
"title": "Product Shipping Requirement",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/product/selling-products"
|
||||
|
||||
@@ -15,6 +15,10 @@ export const fulfillment = [
|
||||
"title": "Manage Shipping Profiles",
|
||||
"path": "https://docs.medusajs.com/user-guide/settings/locations-and-shipping/shipping-profiles"
|
||||
},
|
||||
{
|
||||
"title": "Shipping Option Price Rules",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules"
|
||||
},
|
||||
{
|
||||
"title": "Product Shipping Requirement",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/product/selling-products"
|
||||
|
||||
@@ -51,6 +51,10 @@ export const product = [
|
||||
"title": "Inventory Kits",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/inventory/inventory-kit"
|
||||
},
|
||||
{
|
||||
"title": "Variant Price Rules",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules"
|
||||
},
|
||||
{
|
||||
"title": "Extend Product",
|
||||
"path": "https://docs.medusajs.com/resources/commerce-modules/product/extend"
|
||||
|
||||
Reference in New Issue
Block a user