fix(dashboard, medusa): validate provider for top level tax regions (#12450)

* fix(dashboard, medusa): validate provider for top level tax regions

* chore: changesets, message

* chore: add test case for province

* fix: remove comment
This commit is contained in:
Frane Polić
2025-05-12 18:04:48 +02:00
committed by GitHub
parent 92af52d133
commit 32e0194772
6 changed files with 106 additions and 17 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/dashboard": patch
"@medusajs/medusa": patch
---
fix(dashboard, medusa): validate provider exists when creating a top level tax region
@@ -20,12 +20,22 @@ medusaIntegrationTestRunner({
let taxRegion let taxRegion
beforeEach(async () => { beforeEach(async () => {
const parentRegion = await api.post(
"/admin/tax-regions",
{
country_code: "us",
provider_id: "tp_system",
},
adminHeaders
)
taxRegion = ( taxRegion = (
await api.post( await api.post(
"/admin/tax-regions", "/admin/tax-regions",
{ {
country_code: "us", country_code: "us",
province_code: "tx", province_code: "tx",
parent_id: parentRegion.data.tax_region.id,
metadata: { test: "created" }, metadata: { test: "created" },
}, },
adminHeaders adminHeaders
@@ -83,6 +93,49 @@ medusaIntegrationTestRunner({
) )
}) })
it("should create a province tax region without a provider", async () => {
const response = await api.post(
`/admin/tax-regions`,
{
country_code: "us",
parent_id: taxRegion.id,
province_code: "ny",
},
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.tax_region).toEqual(
expect.objectContaining({
id: expect.any(String),
country_code: "us",
province_code: "ny",
provider_id: null,
})
)
})
it("should fail to create a country tax region without a provider", async () => {
const {
response: { status, data },
} = await api
.post(
`/admin/tax-regions`,
{
country_code: "uk",
},
adminHeaders
)
.catch((err) => err)
expect(status).toEqual(400)
expect(data).toEqual({
message:
"Invalid request: Provider is required when creating a non-province tax region.",
type: "invalid_data",
})
})
it("should throw if tax region does not exist", async () => { it("should throw if tax region does not exist", async () => {
const { const {
response: { status, data }, response: { status, data },
@@ -6462,14 +6462,14 @@
"errors": { "errors": {
"type": "object", "type": "object",
"properties": { "properties": {
"rateIsRequired": { "missingProvider": {
"type": "string" "type": "string"
}, },
"nameIsRequired": { "missingCountry": {
"type": "string" "type": "string"
} }
}, },
"required": ["rateIsRequired", "nameIsRequired"], "required": ["missingProvider", "missingCountry"],
"additionalProperties": false "additionalProperties": false
}, },
"successToast": { "successToast": {
@@ -1718,8 +1718,8 @@
"header": "Create Tax Region", "header": "Create Tax Region",
"hint": "Create a new tax region to define tax rates for a specific country.", "hint": "Create a new tax region to define tax rates for a specific country.",
"errors": { "errors": {
"rateIsRequired": "Tax rate is required when creating a default tax rate.", "missingProvider": "Provider is required when creating a tax region.",
"nameIsRequired": "Name is required when creating a default tax rate." "missingCountry": "Country is required when creating a tax region."
}, },
"successToast": "The tax region was successfully created." "successToast": "The tax region was successfully created."
}, },
@@ -18,21 +18,40 @@ import { useComboboxData } from "../../../../../hooks/use-combobox-data"
import { Combobox } from "../../../../../components/inputs/combobox" import { Combobox } from "../../../../../components/inputs/combobox"
import { formatProvider } from "../../../../../lib/format-provider" import { formatProvider } from "../../../../../lib/format-provider"
import { sdk } from "../../../../../lib/client" import { sdk } from "../../../../../lib/client"
import { i18n } from "../../../../../components/utilities/i18n"
type TaxRegionCreateFormProps = { type TaxRegionCreateFormProps = {
parentId?: string parentId?: string
} }
const TaxRegionCreateSchema = z.object({ const TaxRegionCreateSchema = z
.object({
name: z.string().optional(), name: z.string().optional(),
code: z.string().optional(), code: z.string().optional(),
rate: z.object({ rate: z.object({
float: z.number().optional(), float: z.number().optional(),
value: z.string().optional(), value: z.string().optional(),
}), }),
country_code: z.string().min(1), country_code: z.string(),
provider_id: z.string(), provider_id: z.string(),
}) })
.superRefine(({ provider_id, country_code }, ctx) => {
if (!provider_id) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: i18n.t("taxRegions.create.errors.missingProvider"),
path: ["provider_id"],
})
}
if (!country_code) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: i18n.t("taxRegions.create.errors.missingCountry"),
path: ["country_code"],
})
}
})
export const TaxRegionCreateForm = ({ parentId }: TaxRegionCreateFormProps) => { export const TaxRegionCreateForm = ({ parentId }: TaxRegionCreateFormProps) => {
const { t } = useTranslation() const { t } = useTranslation()
@@ -3,6 +3,7 @@ import {
createFindParams, createFindParams,
createOperatorMap, createOperatorMap,
createSelectParams, createSelectParams,
WithAdditionalData,
} from "../../utils/validators" } from "../../utils/validators"
import { applyAndAndOrOperators } from "../../utils/common-validators" import { applyAndAndOrOperators } from "../../utils/common-validators"
@@ -39,8 +40,8 @@ export const AdminGetTaxRegionsParams = createFindParams({
.merge(AdminGetTaxRegionsParamsFields) .merge(AdminGetTaxRegionsParamsFields)
.merge(applyAndAndOrOperators(AdminGetTaxRegionsParamsFields)) .merge(applyAndAndOrOperators(AdminGetTaxRegionsParamsFields))
export type AdminCreateTaxRegionType = z.infer<typeof AdminCreateTaxRegion> export type AdminCreateTaxRegionType = z.infer<typeof CreateTaxRegion>
export const AdminCreateTaxRegion = z.object({ export const CreateTaxRegion = z.object({
country_code: z.string(), country_code: z.string(),
provider_id: z.string().nullish(), provider_id: z.string().nullish(),
province_code: z.string().nullish(), province_code: z.string().nullish(),
@@ -57,6 +58,16 @@ export const AdminCreateTaxRegion = z.object({
metadata: z.record(z.unknown()).nullish(), metadata: z.record(z.unknown()).nullish(),
}) })
export const AdminCreateTaxRegion = WithAdditionalData(
CreateTaxRegion,
(schema) => {
return schema.refine((data) => data.parent_id || data.provider_id, {
path: ["provider_id"],
message: "Provider is required when creating a non-province tax region.",
})
}
)
export type AdminUpdateTaxRegionType = z.infer<typeof AdminUpdateTaxRegion> export type AdminUpdateTaxRegionType = z.infer<typeof AdminUpdateTaxRegion>
export const AdminUpdateTaxRegion = z.object({ export const AdminUpdateTaxRegion = z.object({
province_code: z.string().nullish(), province_code: z.string().nullish(),