diff --git a/.changeset/chilled-tomatoes-vanish.md b/.changeset/chilled-tomatoes-vanish.md new file mode 100644 index 0000000000..822be9130a --- /dev/null +++ b/.changeset/chilled-tomatoes-vanish.md @@ -0,0 +1,10 @@ +--- +"@medusajs/link-modules": patch +"@medusajs/promotion": patch +"@medusajs/dashboard": patch +"@medusajs/core-flows": patch +"@medusajs/js-sdk": patch +"@medusajs/types": patch +--- + +feat(dashboard,core-flows,js-sdk,link-modules,promotion): free shipping promotion in dashboard diff --git a/packages/admin/dashboard/src/hooks/api/promotions.tsx b/packages/admin/dashboard/src/hooks/api/promotions.tsx index 8d7d1e11f7..f5b8abe08b 100644 --- a/packages/admin/dashboard/src/hooks/api/promotions.tsx +++ b/packages/admin/dashboard/src/hooks/api/promotions.tsx @@ -21,10 +21,15 @@ export const promotionsQueryKeys = { ruleType: string, query?: HttpTypes.AdminGetPromotionRuleParams ) => [PROMOTIONS_QUERY_KEY, id, ruleType, query], - listRuleAttributes: (ruleType: string, promotionType?: string) => [ + listRuleAttributes: ( + ruleType: string, + promotionType?: string, + applicationMethodTargetType?: string + ) => [ PROMOTIONS_QUERY_KEY, ruleType, promotionType, + applicationMethodTargetType, ], listRuleValues: ( ruleType: string, @@ -101,6 +106,7 @@ export const usePromotions = ( export const usePromotionRuleAttributes = ( ruleType: string, promotionType?: string, + applicationMethodTargetType?: string, options?: Omit< UseQueryOptions< HttpTypes.AdminRuleAttributeOptionsListResponse, @@ -112,9 +118,17 @@ export const usePromotionRuleAttributes = ( > ) => { const { data, ...rest } = useQuery({ - queryKey: promotionsQueryKeys.listRuleAttributes(ruleType, promotionType), + queryKey: promotionsQueryKeys.listRuleAttributes( + ruleType, + promotionType, + applicationMethodTargetType + ), queryFn: async () => - sdk.admin.promotion.listRuleAttributes(ruleType, promotionType), + sdk.admin.promotion.listRuleAttributes( + ruleType, + promotionType, + applicationMethodTargetType + ), ...options, }) diff --git a/packages/admin/dashboard/src/i18n/translations/$schema.json b/packages/admin/dashboard/src/i18n/translations/$schema.json index 6161fd2789..cd8d9397f8 100644 --- a/packages/admin/dashboard/src/i18n/translations/$schema.json +++ b/packages/admin/dashboard/src/i18n/translations/$schema.json @@ -7516,14 +7516,47 @@ "target-rules": { "type": "object", "properties": { - "title": { - "type": "string" + "order": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["title", "description"], + "additionalProperties": false }, - "description": { - "type": "string" + "shipping_methods": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["title", "description"], + "additionalProperties": false + }, + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["title", "description"], + "additionalProperties": false } }, - "required": ["title", "description"], + "required": ["order", "shipping_methods", "items"], "additionalProperties": false }, "buy-rules": { diff --git a/packages/admin/dashboard/src/i18n/translations/en.json b/packages/admin/dashboard/src/i18n/translations/en.json index 0a3bed0cc1..668be1ee21 100644 --- a/packages/admin/dashboard/src/i18n/translations/en.json +++ b/packages/admin/dashboard/src/i18n/translations/en.json @@ -2010,8 +2010,18 @@ "description": "Which customer is allowed to use the promotion code? Promotion code can be used by all customers if left untouched." }, "target-rules": { - "title": "What items will the promotion be applied to?", - "description": "The promotion will be applied to items that match the following conditions." + "order": { + "title": "What items will the promotion be applied to?", + "description": "The promotion will be applied to items that match the following conditions." + }, + "shipping_methods": { + "title": "What shipping methods will the promotion be applied to?", + "description": "The promotion will be applied to shipping methods that match the following conditions." + }, + "items": { + "title": "What items will the promotion be applied to?", + "description": "The promotion will be applied to items that match the following conditions." + } }, "buy-rules": { "title": "What needs to be in the cart to unlock the promotion?", diff --git a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx index 89a99f846c..07c9e0caf9 100644 --- a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx +++ b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx @@ -28,7 +28,13 @@ export const EditRulesForm = ({ const [rulesToRemove, setRulesToRemove] = useState([]) const form = useForm({ - defaultValues: { rules: [], type: promotion.type }, + defaultValues: { + rules: [], + type: promotion.type, + application_method: { + target_type: promotion.application_method?.target_type, + }, + }, resolver: zodResolver(EditRules), }) diff --git a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema.ts b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema.ts index e89b9f1c7b..edab3d8d4f 100644 --- a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema.ts +++ b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema.ts @@ -24,6 +24,9 @@ export const EditRules = z.object({ field_type: z.string().optional(), }) ), + application_method: z.object({ + target_type: z.enum(["order", "shipping_methods", "items"]), + }), }) export type EditRulesType = z.infer diff --git a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx index 21b7ea8e5b..806ca68071 100644 --- a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx +++ b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx @@ -1,9 +1,13 @@ -import { HttpTypes } from "@medusajs/types" +import { + ApplicationMethodTargetTypeValues, + HttpTypes, + RuleTypeValues, +} from "@medusajs/types" import { Input } from "@medusajs/ui" import { useWatch } from "react-hook-form" import { Form } from "../../../../../../components/common/form" import { Combobox } from "../../../../../../components/inputs/combobox" -import { useStore } from "../../../../../../hooks/api/store" +import { useStore } from "../../../../../../hooks/api" import { useComboboxData } from "../../../../../../hooks/use-combobox-data" import { sdk } from "../../../../../../lib/client" @@ -18,7 +22,8 @@ type RuleValueFormFieldType = { operator: string fieldRule: any attributes: HttpTypes.AdminRuleAttributeOption[] - ruleType: "rules" | "target-rules" | "buy-rules" + ruleType: RuleTypeValues + applicationMethodTargetType: ApplicationMethodTargetTypeValues | undefined } const buildFilters = (attribute?: string, store?: HttpTypes.AdminStore) => { @@ -44,6 +49,7 @@ export const RuleValueFormField = ({ fieldRule, attributes, ruleType, + applicationMethodTargetType, }: RuleValueFormFieldType) => { const attribute = attributes?.find( (attr) => attr.value === fieldRule.attribute @@ -59,6 +65,7 @@ export const RuleValueFormField = ({ { ...params, ...buildFilters(attribute?.id, store!), + application_method_target_type: applicationMethodTargetType, } ) }, diff --git a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx index 0f4fff9324..d72f1072b1 100644 --- a/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx +++ b/packages/admin/dashboard/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx @@ -41,7 +41,11 @@ export const RulesFormField = ({ }: RulesFormFieldType) => { const { t } = useTranslation() const formData = form.getValues() - const { attributes } = usePromotionRuleAttributes(ruleType, formData.type) + const { attributes } = usePromotionRuleAttributes( + ruleType, + formData.type, + formData.application_method?.target_type + ) const { fields, append, remove, update, replace } = useFieldArray({ control: form.control, @@ -61,10 +65,17 @@ export const RulesFormField = ({ defaultValue: promotion?.application_method?.type, }) + const applicationMethodTargetType = useWatch({ + control: form.control, + name: "application_method.target_type", + defaultValue: promotion?.application_method?.target_type, + }) + const query: Record = promotionType ? { promotion_type: promotionType, application_method_type: applicationMethodType, + application_method_target_type: applicationMethodTargetType, } : {} @@ -121,11 +132,19 @@ export const RulesFormField = ({ return (
- {t(`promotions.fields.conditions.${ruleType}.title`)} + {t( + ruleType === "target-rules" + ? `promotions.fields.conditions.${ruleType}.${applicationMethodTargetType}.title` + : `promotions.fields.conditions.${ruleType}.title` + )} - {t(`promotions.fields.conditions.${ruleType}.description`)} + {t( + ruleType === "target-rules" + ? `promotions.fields.conditions.${ruleType}.${applicationMethodTargetType}.description` + : `promotions.fields.conditions.${ruleType}.description` + )} {fields.map((fieldRule, index) => { @@ -307,6 +326,7 @@ export const RulesFormField = ({ fieldRule={fieldRule} attributes={attributes} ruleType={ruleType} + applicationMethodTargetType={applicationMethodTargetType} />
diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx index a9a9e94ba8..684d4dd913 100644 --- a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx +++ b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx @@ -678,60 +678,62 @@ export const CreatePromotionForm = () => { - - {!currentTemplate?.hiddenFields?.includes( "application_method.type" ) && ( - { - return ( - - - {t("promotions.fields.value_type")} - - - - + <> + + { + return ( + + + {t("promotions.fields.value_type")} + + + + - - - - - - ) - }} - /> + + + + + + ) + }} + /> + )} -
- {!currentTemplate?.hiddenFields?.includes( - "application_method.value" - ) && ( + {!currentTemplate?.hiddenFields?.includes( + "application_method.value" + ) && ( + <> + { ) }} /> - )} + + )} - {isTypeStandard && watchAllocation === "each" && ( - { - return ( - - - {t("promotions.form.max_quantity.title")} - + {isTypeStandard && watchAllocation === "each" && ( + { + return ( + + + {t("promotions.form.max_quantity.title")} + - - - + + + - - ]} - /> - - - ) - }} - /> - )} -
+ + ]} + /> + + + ) + }} + /> + )} {isTypeStandard && !currentTemplate?.hiddenFields?.includes( @@ -901,6 +903,7 @@ export const CreatePromotionForm = () => { {!isTypeStandard && ( <> + { const { t } = useTranslation() @@ -57,7 +59,11 @@ export const PromotionConditionsSection = ({
- {t(`promotions.fields.conditions.${ruleType}.title`)} + {t( + ruleType === "target-rules" + ? `promotions.fields.conditions.${ruleType}.${applicationMethodTargetType}.title` + : `promotions.fields.conditions.${ruleType}.title` + )}
diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-detail/promotion-detail.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-detail/promotion-detail.tsx index a5fefa61c7..4641830374 100644 --- a/packages/admin/dashboard/src/routes/promotions/promotion-detail/promotion-detail.tsx +++ b/packages/admin/dashboard/src/routes/promotions/promotion-detail/promotion-detail.tsx @@ -52,11 +52,15 @@ export const PromotionDetail = () => { {promotion.type === "buyget" && ( )} diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx index 3ffb35f7d3..96294aeadf 100644 --- a/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx +++ b/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx @@ -1,13 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { AdminPromotion } from "@medusajs/types" -import { - Button, - CurrencyInput, - Input, - RadioGroup, - Switch, - Text, -} from "@medusajs/ui" +import { Button, CurrencyInput, Input, RadioGroup, Text } from "@medusajs/ui" import { useForm, useWatch } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import { useEffect } from "react" @@ -33,6 +26,7 @@ const EditPromotionSchema = zod.object({ value_type: zod.enum(["fixed", "percentage"]), value: zod.number(), allocation: zod.enum(["each", "across"]), + target_type: zod.enum(["order", "shipping_methods", "items"]), }) export const EditPromotionDetailsForm = ({ @@ -50,6 +44,7 @@ export const EditPromotionDetailsForm = ({ value: promotion.application_method!.value, allocation: promotion.application_method!.allocation, value_type: promotion.application_method!.type, + target_type: promotion.application_method!.target_type, }, resolver: zodResolver(EditPromotionSchema), }) @@ -223,128 +218,139 @@ export const EditPromotionDetailsForm = ({
- { - return ( - - {t("promotions.fields.value_type")} - - - + {promotion.application_method?.target_type !== + "shipping_methods" && ( + <> + { + return ( + + + {t("promotions.fields.value_type")} + + + + - - - - - - ) - }} - /> + + + + + + ) + }} + /> + { + const currencyCode = + promotion.application_method?.currency_code ?? "USD" - { - const currencyCode = - promotion.application_method?.currency_code ?? "USD" - - return ( - - - {isFixedValueType - ? t("fields.amount") - : t("fields.percentage")} - - - {isFixedValueType ? ( - - onChange(val ? parseInt(val) : null) - } - code={currencyCode} - symbol={getCurrencySymbol(currencyCode)} - {...field} - value={field.value} - /> - ) : ( - { - onChange( - e.target.value === "" - ? null - : parseInt(e.target.value) - ) - }} - /> - )} - - - - ) - }} - /> - - { - return ( - - {t("promotions.fields.allocation")} - - - + + {isFixedValueType + ? t("fields.amount") + : t("fields.percentage")} + + + {isFixedValueType ? ( + + onChange(val ? parseInt(val) : null) + } + code={currencyCode} + symbol={getCurrencySymbol(currencyCode)} + {...field} + value={field.value} + /> + ) : ( + { + onChange( + e.target.value === "" + ? null + : parseInt(e.target.value) + ) + }} + /> )} - /> + + + + ) + }} + /> + { + return ( + + + {t("promotions.fields.allocation")} + + + + - - - - - - ) - }} - /> + + + + + + ) + }} + /> + + )} diff --git a/packages/core/core-flows/src/cart/utils/fields.ts b/packages/core/core-flows/src/cart/utils/fields.ts index 06523dd52b..0a1ed9399e 100644 --- a/packages/core/core-flows/src/cart/utils/fields.ts +++ b/packages/core/core-flows/src/cart/utils/fields.ts @@ -34,6 +34,7 @@ export const cartFieldsForRefreshSteps = [ "shipping_methods.*", "shipping_methods.adjustments.*", "shipping_methods.tax_lines.*", + "shipping_methods.shipping_option.shipping_option_type_id", "customer.*", "customer.groups.*", "promotions.id", diff --git a/packages/core/js-sdk/src/admin/promotion.ts b/packages/core/js-sdk/src/admin/promotion.ts index 14db44ab01..eebb127e96 100644 --- a/packages/core/js-sdk/src/admin/promotion.ts +++ b/packages/core/js-sdk/src/admin/promotion.ts @@ -15,27 +15,27 @@ export class Promotion { } /** - * This method retrieves a promotion by its ID. It sends a request to the + * This method retrieves a promotion by its ID. It sends a request to the * [Retrieve Promotion](https://docs.medusajs.com/api/admin#promotions_getpromotionsid) * API route. - * + * * @param id - The promotion's ID. * @param query - Configure the fields to retrieve in the promotion. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example * To retrieve a promotion by its ID: - * + * * ```ts * sdk.admin.promotion.retrieve("promo_123") * .then(({ promotion }) => { * console.log(promotion) * }) * ``` - * + * * To specify the fields and relations to retrieve: - * + * * ```ts * sdk.admin.promotion.retrieve("promo_123", { * fields: "id,*application_method" @@ -44,7 +44,7 @@ export class Promotion { * console.log(promotion) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/admin#select-fields-and-relations). */ async retrieve( @@ -62,28 +62,28 @@ export class Promotion { } /** - * This method retrieves a list of promotions. It sends a request to the + * This method retrieves a list of promotions. It sends a request to the * [List Promotions](https://docs.medusajs.com/api/admin#promotions_getpromotions) * API route. - * + * * @param query - Filters and pagination configurations. * @param headers - Headers to pass in the request. * @returns The list of promotions. - * + * * @example * To retrieve the list of promotions: - * + * * ```ts * sdk.admin.promotion.list() * .then(({ promotions, count, limit, offset }) => { * console.log(promotions) * }) * ``` - * + * * 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.promotion.list({ * limit: 10, @@ -93,10 +93,10 @@ export class Promotion { * console.log(promotions) * }) * ``` - * + * * Using the `fields` query parameter, you can specify the fields and relations to retrieve * in each promotion: - * + * * ```ts * sdk.admin.promotion.list({ * fields: "id,*application_method" @@ -105,7 +105,7 @@ export class Promotion { * console.log(promotions) * }) * ``` - * + * * Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/admin#select-fields-and-relations). */ async list( @@ -122,16 +122,16 @@ export class Promotion { } /** - * This method creates a new promotion. It sends a request to the + * This method creates a new promotion. It sends a request to the * [Create Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotions) * API route. - * + * * @param payload - The promotion to create. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example - * sdk.admin.promotion.create({ + * sdk.admin.promotion.create({ * name: "My Promotion", * description: "This is a test promotion", * code: "PROMO123", @@ -157,15 +157,15 @@ export class Promotion { } /** - * This method updates a promotion. It sends a request to the + * This method updates a promotion. It sends a request to the * [Update Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotionsid) * API route. - * + * * @param id - The promotion's ID. * @param payload - The details to update in the promotion. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example * sdk.admin.promotion.update("promo_123", { * code: "PROMO123", @@ -190,14 +190,14 @@ export class Promotion { } /** - * This method deletes a promotion. It sends a request to the + * This method deletes a promotion. It sends a request to the * [Delete Promotion](https://docs.medusajs.com/api/admin#promotions_deletepromotionsid) * API route. - * + * * @param id - The promotion's ID. * @param headers - Headers to pass in the request. * @returns The deleted promotion's details. - * + * * @example * sdk.admin.promotion.delete("promo_123") * .then(({ promotion }) => { @@ -218,20 +218,20 @@ export class Promotion { * This method creates and adds rules to a promotion. It can be the promotion's rules, * or its application method's buy or target rules. That depends on the rule type * you specify as a parameter. - * - * - If you set the `ruleType` to `rules`, the method sends a request to the + * + * - If you set the `ruleType` to `rules`, the method sends a request to the * [Manage Promotion's Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidrulesbatch). - * - If you set the `ruleType` to `buy-rules`, the method sends a request to the + * - If you set the `ruleType` to `buy-rules`, the method sends a request to the * [Manage Promotion's Buy Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidbuyrulesbatch). - * - If you set the `ruleType` to `target-rules`, the method sends a request to the + * - If you set the `ruleType` to `target-rules`, the method sends a request to the * [Manage Promotion's Target Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidtargetrulesbatch). - * + * * @param id - The promotion's ID. * @param ruleType - The type of rules to create. * @param payload - The rules to create. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example * sdk.admin.promotion.addRules("promo_123", "rules", { * rules: [ @@ -266,20 +266,20 @@ export class Promotion { * This method updates the rules of a promotion. It can be the promotion's rules, * or its application method's buy or target rules. That depends on the rule type * you specify as a parameter. - * - * - If you set the `ruleType` to `rules`, the method sends a request to the + * + * - If you set the `ruleType` to `rules`, the method sends a request to the * [Manage Promotion's Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidrulesbatch). - * - If you set the `ruleType` to `buy-rules`, the method sends a request to the + * - If you set the `ruleType` to `buy-rules`, the method sends a request to the * [Manage Promotion's Buy Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidbuyrulesbatch). - * - If you set the `ruleType` to `target-rules`, the method sends a request to the + * - If you set the `ruleType` to `target-rules`, the method sends a request to the * [Manage Promotion's Target Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidtargetrulesbatch). - * + * * @param id - The promotion's ID. * @param ruleType - The type of rules to update. * @param payload - The rules to update. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example * sdk.admin.promotion.updateRules("promo_123", "rules", { * rules: [ @@ -313,20 +313,20 @@ export class Promotion { * This method removes rules from a promotion. It can be the promotion's rules, * or its application method's buy or target rules. That depends on the rule type * you specify as a parameter. - * - * - If you set the `ruleType` to `rules`, the method sends a request to the + * + * - If you set the `ruleType` to `rules`, the method sends a request to the * [Manage Promotion's Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidrulesbatch). - * - If you set the `ruleType` to `buy-rules`, the method sends a request to the + * - If you set the `ruleType` to `buy-rules`, the method sends a request to the * [Manage Promotion's Buy Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidbuyrulesbatch). - * - If you set the `ruleType` to `target-rules`, the method sends a request to the + * - If you set the `ruleType` to `target-rules`, the method sends a request to the * [Manage Promotion's Target Rules API Route](https://docs.medusajs.com/api/admin#promotions_postpromotionsidtargetrulesbatch). - * + * * @param id - The promotion's ID. * @param ruleType - The type of rules to remove. * @param payload - The rules to remove. * @param headers - Headers to pass in the request. * @returns The promotion's details. - * + * * @example * sdk.admin.promotion.removeRules("promo_123", "rules", { * rule_ids: ["rule_123"] @@ -355,16 +355,16 @@ export class Promotion { * This method retrieves the rules of a promotion. It can be the promotion's rules, * or its application method's buy or target rules. That depends on the rule type * you specify as a parameter. - * + * * This method sends a request to the * [List Rules of a Promotion API Route](https://docs.medusajs.com/api/admin#promotions_getpromotionsidrule_type) - * + * * @param id - The promotion's ID. * @param ruleType - The type of rules to retrieve. Can be `rules`, `buy-rules`, or `target-rules`. * @param query - Configure the fields to retrieve in the rules. * @param headers - Headers to pass in the request. * @returns The promotion's rules. - * + * * @example * sdk.admin.promotion.listRules("promo_123", "rules") * .then(({ rules }) => { @@ -389,19 +389,20 @@ export class Promotion { /** * Retrieve a list of potential rule attributes for the promotion and application method types specified in the query parameters. Only the attributes of the rule type specified in the path parameter are retrieved: - * + * * - If `rule_type` is `rules`, the attributes of the promotion's type are retrieved. * - If `rule_type` is `target-rules`, the target rules' attributes of the application method's type are retrieved. * - If `rule_type` is `buy-rules`, the buy rules' attributes of the application method's type are retrieved. - * + * * This method sends a request to the * [List Rule Attribute Options API Route](https://docs.medusajs.com/api/admin#promotions_getpromotionsruleattributeoptionsrule_type) - * + * * @param ruleType - The type of rules to retrieve the attributes for. Can be `rules`, `buy-rules`, or `target-rules`. * @param promotionType - The type of promotion to retrieve the attributes for. It can be `standard` or `buyget`. + * @param applicationMethodTargetType - The type of application method to retrieve the attributes for. It can be `order`, `items` or `shipping_methods`. * @param headers - Headers to pass in the request. * @returns The list of rule attributes. - * + * * @example * sdk.admin.promotion.listRuleAttributes("rules", "standard") * .then(({ attributes }) => { @@ -411,6 +412,7 @@ export class Promotion { async listRuleAttributes( ruleType: string, promotionType?: string, + applicationMethodTargetType?: string, headers?: ClientHeaders ) { // eslint-disable-next-line max-len @@ -418,25 +420,28 @@ export class Promotion { `/admin/promotions/rule-attribute-options/${ruleType}`, { headers, - query: { promotion_type: promotionType }, + query: { + promotion_type: promotionType, + application_method_target_type: applicationMethodTargetType, + }, } ) } /** - * Retrieve all potential values for promotion rules and target and buy rules based on the specified rule attribute and type. - * For example, if you provide the ID of the `currency_code` rule attribute, and set `rule_type` to rules, + * Retrieve all potential values for promotion rules and target and buy rules based on the specified rule attribute and type. + * For example, if you provide the ID of the `currency_code` rule attribute, and set `rule_type` to rules, * a list of currencies are retrieved in label-value pairs. - * + * * This method sends a request to the * [List Rule Values API Route](https://docs.medusajs.com/api/admin#promotions_getpromotionsrulevalueoptionsrule_typerule_attribute_id) - * + * * @param ruleType - The type of rules to retrieve the values for. Can be `rules`, `buy-rules`, or `target-rules`. * @param ruleValue - The ID of the rule attribute to retrieve the values for. * @param query - Configure the fields to retrieve in the rule values. * @param headers - Headers to pass in the request. * @returns The list of rule values. - * + * * @example * sdk.admin.promotion.listRuleValues("rules", "attr_123") * .then(({ values }) => { diff --git a/packages/core/types/src/http/promotion/admin/queries.ts b/packages/core/types/src/http/promotion/admin/queries.ts index 86344b049e..6a48358568 100644 --- a/packages/core/types/src/http/promotion/admin/queries.ts +++ b/packages/core/types/src/http/promotion/admin/queries.ts @@ -1,5 +1,6 @@ import { BaseFilterable, OperatorMap } from "../../../dal" import { + ApplicationMethodTargetTypeValues, ApplicationMethodTypeValues, PromotionTypeValues, } from "../../../promotion" @@ -64,6 +65,7 @@ export interface AdminGetPromotionsParams export interface AdminGetPromotionRuleParams { promotion_type?: PromotionTypeValues application_method_type?: ApplicationMethodTypeValues + application_method_target_type?: ApplicationMethodTargetTypeValues } export interface AdminGetPromotionRuleTypeParams extends SelectParams { @@ -80,4 +82,5 @@ export interface AdminGetPromotionsRuleValueParams extends FindParams { * Filter by rule value. */ value?: string | string[] + application_method_target_type?: ApplicationMethodTargetTypeValues } diff --git a/packages/modules/link-modules/src/definitions/readonly/cart-shipping-option.ts b/packages/modules/link-modules/src/definitions/readonly/cart-shipping-option.ts new file mode 100644 index 0000000000..1b3830b484 --- /dev/null +++ b/packages/modules/link-modules/src/definitions/readonly/cart-shipping-option.ts @@ -0,0 +1,22 @@ +import { ModuleJoinerConfig } from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" + +export const CartShippingOption: ModuleJoinerConfig = { + isLink: true, + isReadOnlyLink: true, + extends: [ + { + serviceName: Modules.CART, + entity: "ShippingMethod", + relationship: { + serviceName: Modules.FULFILLMENT, + primaryKey: "id", + foreignKey: "shipping_option_id", + alias: "shipping_option", + args: { + methodSuffix: "ShippingOptions", + }, + }, + }, + ], +} diff --git a/packages/modules/link-modules/src/definitions/readonly/index.ts b/packages/modules/link-modules/src/definitions/readonly/index.ts index 379011505e..c8fcb3e1c2 100644 --- a/packages/modules/link-modules/src/definitions/readonly/index.ts +++ b/packages/modules/link-modules/src/definitions/readonly/index.ts @@ -2,6 +2,7 @@ export * from "./cart-customer" export * from "./cart-product" export * from "./cart-region" export * from "./cart-sales-channel" +export * from "./cart-shipping-option" export * from "./inventory-level-stock-location" export * from "./line-item-adjustment-promotion" export * from "./order-customer" diff --git a/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts b/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts index 1b42e0e366..8ff4979a95 100644 --- a/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts +++ b/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts @@ -1,10 +1,5 @@ import { IPromotionModuleService } from "@medusajs/framework/types" -import { - ApplicationMethodType, - Modules, - PromotionStatus, - PromotionType, -} from "@medusajs/framework/utils" +import { ApplicationMethodType, Modules, PromotionStatus, PromotionType, } from "@medusajs/framework/utils" import { moduleIntegrationTestRunner, SuiteOptions } from "@medusajs/test-utils" import { createCampaigns } from "../../../__fixtures__/campaigns" import { createDefaultPromotion } from "../../../__fixtures__/promotion" diff --git a/packages/modules/promotion/src/utils/compute-actions/index.ts b/packages/modules/promotion/src/utils/compute-actions/index.ts index ccf7494ca6..892e1694ef 100644 --- a/packages/modules/promotion/src/utils/compute-actions/index.ts +++ b/packages/modules/promotion/src/utils/compute-actions/index.ts @@ -1,3 +1,4 @@ export * from "./buy-get" export * from "./line-items" +export * from "./shipping-methods" export * from "./usage" diff --git a/packages/modules/promotion/src/utils/compute-actions/line-items.ts b/packages/modules/promotion/src/utils/compute-actions/line-items.ts index 1c1dcc89db..d4d695d20f 100644 --- a/packages/modules/promotion/src/utils/compute-actions/line-items.ts +++ b/packages/modules/promotion/src/utils/compute-actions/line-items.ts @@ -6,11 +6,11 @@ import { import { ApplicationMethodAllocation, ApplicationMethodTargetType, + ApplicationMethodTargetType as TargetType, + calculateAdjustmentAmountFromPromotion, ComputedActions, MathBN, MedusaError, - ApplicationMethodTargetType as TargetType, - calculateAdjustmentAmountFromPromotion, } from "@medusajs/framework/utils" import { areRulesValidForContext } from "../validations" import { computeActionForBudgetExceeded } from "./usage" @@ -43,29 +43,6 @@ export function getComputedActionsForItems( ) } -export function getComputedActionsForShippingMethods( - promotion: PromotionTypes.PromotionDTO, - shippingMethods: PromotionTypes.ComputeActionContext[TargetType.SHIPPING_METHODS], - appliedPromotionsMap: Map -): PromotionTypes.ComputeActions[] { - validateContext("shipping_methods", shippingMethods) - - return applyPromotionToItems(promotion, shippingMethods, appliedPromotionsMap) -} - -export function getComputedActionsForOrder( - promotion: PromotionTypes.PromotionDTO, - itemApplicationContext: PromotionTypes.ComputeActionContext, - methodIdPromoValueMap: Map -): PromotionTypes.ComputeActions[] { - return getComputedActionsForItems( - promotion, - itemApplicationContext[TargetType.ITEMS], - methodIdPromoValueMap, - ApplicationMethodAllocation.ACROSS - ) -} - function applyPromotionToItems( promotion: PromotionTypes.PromotionDTO, items: diff --git a/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts b/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts index 7f51c48071..b496eb51fd 100644 --- a/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts +++ b/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts @@ -123,20 +123,13 @@ export function applyPromotionToShippingMethods( } const promotionValue = applicationMethod?.value ?? 0 - const applicableTotal = method.subtotal const appliedPromoValue = methodIdPromoValueMap.get(method.id) ?? 0 + const applicableTotal = MathBN.sub(method.subtotal, appliedPromoValue) - const div = MathBN.eq(totalApplicableValue, 0) ? 1 : totalApplicableValue - let applicablePromotionValue = MathBN.sub( - MathBN.mult(MathBN.div(applicableTotal, div), promotionValue), - appliedPromoValue - ) + let applicablePromotionValue = MathBN.mult(MathBN.div(applicableTotal, totalApplicableValue), promotionValue) if (applicationMethod?.type === ApplicationMethodType.PERCENTAGE) { - applicablePromotionValue = MathBN.sub( - MathBN.mult(MathBN.div(promotionValue, 100), applicableTotal), - appliedPromoValue - ) + applicablePromotionValue = MathBN.mult(MathBN.div(promotionValue, 100), applicableTotal) } const amount = MathBN.min(applicablePromotionValue, applicableTotal)