feat(promotion, dashboard, core-flows, cart, types, utils, medusa): tax inclusive promotions (#12412)
* feat: tax inclusive promotions * feat: add a totals test case * feat: add integration test * chore: changeset * fix: typo * chore: refactor * fix: tests * fix: rest of buyget action tests * fix: cart spec * chore: expand integration test with item level totals * feat: add a few more test cases --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
@@ -7362,6 +7362,9 @@
|
||||
"clearAll": {
|
||||
"type": "string"
|
||||
},
|
||||
"taxInclusive": {
|
||||
"type": "string"
|
||||
},
|
||||
"amount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7428,6 +7431,7 @@
|
||||
"allocation",
|
||||
"addCondition",
|
||||
"clearAll",
|
||||
"taxInclusive",
|
||||
"amount",
|
||||
"conditions"
|
||||
],
|
||||
@@ -7626,6 +7630,19 @@
|
||||
"required": ["existing", "new", "none"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"taxInclusive": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "description"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7852,6 +7869,7 @@
|
||||
"and",
|
||||
"selectAttribute",
|
||||
"campaign",
|
||||
"taxInclusive",
|
||||
"status",
|
||||
"method",
|
||||
"max_quantity",
|
||||
|
||||
@@ -1969,6 +1969,7 @@
|
||||
"allocation": "Allocation",
|
||||
"addCondition": "Add condition",
|
||||
"clearAll": "Clear all",
|
||||
"taxInclusive": "Tax Inclusive",
|
||||
"amount": {
|
||||
"tooltip": "Select the currency code to enable setting the amount"
|
||||
},
|
||||
@@ -2045,6 +2046,10 @@
|
||||
"description": "Proceed without associating promotion with campaign"
|
||||
}
|
||||
},
|
||||
"taxInclusive": {
|
||||
"title": "Does promotion include taxes?",
|
||||
"description": "Whether the promotion will be applied before or after taxes"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status",
|
||||
"draft": {
|
||||
|
||||
+47
@@ -19,6 +19,7 @@ import {
|
||||
ProgressStatus,
|
||||
ProgressTabs,
|
||||
RadioGroup,
|
||||
Switch,
|
||||
Text,
|
||||
toast,
|
||||
} from "@medusajs/ui"
|
||||
@@ -52,6 +53,7 @@ const defaultValues = {
|
||||
type: "standard" as PromotionTypeValues,
|
||||
status: "draft" as PromotionStatusValues,
|
||||
rules: [],
|
||||
is_tax_inclusive: false,
|
||||
application_method: {
|
||||
allocation: "each" as ApplicationMethodAllocationValues,
|
||||
type: "fixed" as ApplicationMethodTypeValues,
|
||||
@@ -89,6 +91,7 @@ export const CreatePromotionForm = () => {
|
||||
const {
|
||||
campaign_choice: _campaignChoice,
|
||||
is_automatic,
|
||||
is_tax_inclusive,
|
||||
template_id: _templateId,
|
||||
application_method,
|
||||
rules,
|
||||
@@ -142,6 +145,7 @@ export const CreatePromotionForm = () => {
|
||||
target_rules: buildRulesData(targetRulesData),
|
||||
buy_rules: buildRulesData(buyRulesData),
|
||||
},
|
||||
is_tax_inclusive,
|
||||
is_automatic: is_automatic === "true",
|
||||
},
|
||||
{
|
||||
@@ -583,6 +587,49 @@ export const CreatePromotionForm = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!currentTemplate?.hiddenFields?.includes(
|
||||
"is_tax_inclusive"
|
||||
) && (
|
||||
<>
|
||||
<Divider />
|
||||
<div className="flex gap-x-2 gap-y-4">
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
name="is_tax_inclusive"
|
||||
render={({
|
||||
field: { onChange, value, ...field },
|
||||
}) => {
|
||||
return (
|
||||
<Form.Item className="basis-full">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="block">
|
||||
<Form.Label>
|
||||
{t("promotions.form.taxInclusive.title")}
|
||||
</Form.Label>
|
||||
<Form.Hint className="!mt-1">
|
||||
{t(
|
||||
"promotions.form.taxInclusive.description"
|
||||
)}
|
||||
</Form.Hint>
|
||||
</div>
|
||||
<Form.Control className="mr-2 self-center">
|
||||
<Switch
|
||||
className="mt-[2px]"
|
||||
checked={!!value}
|
||||
onCheckedChange={onChange}
|
||||
{...field}
|
||||
/>
|
||||
</Form.Control>
|
||||
</div>
|
||||
<Form.ErrorMessage />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!currentTemplate?.hiddenFields?.includes("type") && (
|
||||
<Form.Field
|
||||
control={form.control}
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ export const CreatePromotionSchema = z
|
||||
type: z.enum(["buyget", "standard"]),
|
||||
status: z.enum(["draft", "active", "inactive"]),
|
||||
rules: RuleSchema,
|
||||
is_tax_inclusive: z.boolean().optional(),
|
||||
application_method: z.object({
|
||||
allocation: z.enum(["each", "across"]),
|
||||
value: z.number().min(0),
|
||||
|
||||
+5
-1
@@ -74,7 +74,11 @@ export const templates = [
|
||||
type: "buy_get",
|
||||
title: "Buy X Get Y",
|
||||
description: "Buy X product(s), get Y product(s)",
|
||||
hiddenFields: [...commonHiddenFields, "application_method.value"],
|
||||
hiddenFields: [
|
||||
...commonHiddenFields,
|
||||
"application_method.value",
|
||||
"is_tax_inclusive",
|
||||
],
|
||||
defaults: {
|
||||
is_automatic: "false",
|
||||
type: "buyget",
|
||||
|
||||
+12
@@ -180,6 +180,18 @@ export const PromotionGeneralSection = ({
|
||||
{promotion.application_method?.allocation!}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="text-ui-fg-subtle grid grid-cols-2 items-start px-6 py-4">
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{t("promotions.fields.taxInclusive")}
|
||||
</Text>
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Text className="inline" size="small" leading="compact">
|
||||
{promotion.is_tax_inclusive ? t("fields.true") : t("fields.false")}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user