feat(tax): add support for updating tax rates (#6537)

This commit is contained in:
Sebastian Rindom
2024-02-29 12:03:18 +01:00
committed by GitHub
parent 2407b443f1
commit 6279fb3c67
11 changed files with 262 additions and 1 deletions

View File

@@ -205,4 +205,94 @@ describe("Taxes - Admin", () => {
])
)
})
it("can create a tax rate and update it", async () => {
const api = useApi() as any
const regionRes = await api.post(
`/admin/tax-regions`,
{
country_code: "us",
default_tax_rate: { code: "default", rate: 2, name: "default rate" },
},
adminHeaders
)
const usRegionId = regionRes.data.tax_region.id
expect(regionRes.status).toEqual(200)
expect(regionRes.data).toEqual({
tax_region: {
id: expect.any(String),
country_code: "us",
parent_id: null,
province_code: null,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
created_by: "admin_user",
provider_id: null,
metadata: null,
},
})
const rateRes = await api.post(
`/admin/tax-rates`,
{
tax_region_id: usRegionId,
code: "RATE2",
name: "another rate",
rate: 10,
rules: [{ reference: "product", reference_id: "prod_1234" }],
},
adminHeaders
)
expect(rateRes.status).toEqual(200)
expect(rateRes.data).toEqual({
tax_rate: {
id: expect.any(String),
code: "RATE2",
rate: 10,
name: "another rate",
is_default: false,
metadata: null,
tax_region_id: usRegionId,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
created_by: "admin_user",
is_combinable: false,
},
})
const updateRes = await api.post(
`/admin/tax-rates/${rateRes.data.tax_rate.id}`,
{
code: "updatedcode",
rate: 12,
is_combinable: true,
name: "Another Name",
metadata: { you: "know it" },
},
adminHeaders
)
expect(updateRes.status).toEqual(200)
expect(updateRes.data).toEqual({
tax_rate: {
id: expect.any(String),
code: "updatedcode",
rate: 12,
name: "Another Name",
is_default: false,
metadata: { you: "know it" },
tax_region_id: usRegionId,
deleted_at: null,
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
is_combinable: true,
},
})
})
})

View File

@@ -1,2 +1,3 @@
export * from "./create-tax-regions"
export * from "./create-tax-rates"
export * from "./update-tax-rates"

View File

@@ -0,0 +1,47 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
FilterableTaxRateProps,
ITaxModuleService,
UpdateTaxRateDTO,
} from "@medusajs/types"
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
type UpdateTaxRatesStepInput = {
selector: FilterableTaxRateProps
update: UpdateTaxRateDTO
}
export const updateTaxRatesStepId = "update-tax-rates"
export const updateTaxRatesStep = createStep(
updateTaxRatesStepId,
async (data: UpdateTaxRatesStepInput, { container }) => {
const service = container.resolve<ITaxModuleService>(
ModuleRegistrationName.TAX
)
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
data.update,
])
const prevData = await service.list(data.selector, {
select: selects,
relations,
})
const taxRates = await service.update(data.selector, data.update)
return new StepResponse(taxRates, prevData)
},
async (prevData, { container }) => {
if (!prevData?.length) {
return
}
const service = container.resolve<ITaxModuleService>(
ModuleRegistrationName.TAX
)
await service.upsert(prevData)
}
)

View File

@@ -1,2 +1,3 @@
export * from "./create-tax-regions"
export * from "./create-tax-rates"
export * from "./update-tax-rates"

View File

@@ -0,0 +1,22 @@
import {
FilterableTaxRateProps,
TaxRateDTO,
UpdateTaxRateDTO,
} from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { updateTaxRatesStep } from "../steps"
type UpdateTaxRatesStepInput = {
selector: FilterableTaxRateProps
update: UpdateTaxRateDTO
}
type WorkflowInput = UpdateTaxRatesStepInput
export const updateTaxRatesWorkflowId = "update-tax-rates"
export const updateTaxRatesWorkflow = createWorkflow(
updateTaxRatesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<TaxRateDTO[]> => {
return updateTaxRatesStep(input)
}
)

View File

@@ -5,6 +5,37 @@ import {
import { defaultAdminTaxRateFields } from "../query-config"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { AdminPostTaxRatesTaxRateReq } from "../../../../api/routes/admin/tax-rates"
import { updateTaxRatesWorkflow } from "@medusajs/core-flows"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostTaxRatesTaxRateReq>,
res: MedusaResponse
) => {
const { errors } = await updateTaxRatesWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
update: req.validatedBody,
},
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const remoteQuery = req.scope.resolve("remoteQuery")
const queryObject = remoteQueryObjectFromString({
entryPoint: "tax_rate",
variables: { id: req.params.id },
fields: defaultAdminTaxRateFields,
})
const [taxRate] = await remoteQuery(queryObject)
res.status(200).json({ tax_rate: taxRate })
}
export const GET = async (
req: AuthenticatedMedusaRequest,

View File

@@ -3,6 +3,7 @@ import * as QueryConfig from "./query-config"
import {
AdminGetTaxRatesTaxRateParams,
AdminPostTaxRatesReq,
AdminPostTaxRatesTaxRateReq,
} from "./validators"
import { transformBody, transformQuery } from "../../../api/middlewares"
@@ -20,6 +21,11 @@ export const adminTaxRateRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/tax-rates",
middlewares: [transformBody(AdminPostTaxRatesReq)],
},
{
method: "POST",
matcher: "/admin/tax-rates/:id",
middlewares: [transformBody(AdminPostTaxRatesTaxRateReq)],
},
{
method: "GET",
matcher: "/admin/tax-rates/:id",

View File

@@ -1,4 +1,3 @@
// HEAD
import { Type } from "class-transformer"
import {
IsBoolean,
@@ -52,3 +51,29 @@ export class AdminPostTaxRatesReq {
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostTaxRatesTaxRateReq {
@IsOptional()
@IsNumber()
rate?: number | null
@IsOptional()
@IsString()
code?: string | null
@IsString()
@IsOptional()
name?: string
@IsBoolean()
@IsOptional()
is_default?: boolean
@IsBoolean()
@IsOptional()
is_combinable?: boolean
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}

View File

@@ -196,6 +196,27 @@ export default class TaxModuleService<
return await this.taxRateService_.update({ selector, data }, sharedContext)
}
async upsert(
data: TaxTypes.UpsertTaxRateDTO[],
sharedContext?: Context
): Promise<TaxTypes.TaxRateDTO[]>
async upsert(
data: TaxTypes.UpsertTaxRateDTO,
sharedContext?: Context
): Promise<TaxTypes.TaxRateDTO>
@InjectTransactionManager("baseRepository_")
async upsert(
data: TaxTypes.UpsertTaxRateDTO | TaxTypes.UpsertTaxRateDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TaxTypes.TaxRateDTO | TaxTypes.TaxRateDTO[]> {
const result = await this.taxRateService_.upsert(data, sharedContext)
const serialized = await this.baseRepository_.serialize<
TaxTypes.TaxRateDTO[]
>(result, { populate: true })
return Array.isArray(data) ? serialized : serialized[0]
}
createTaxRegions(
data: TaxTypes.CreateTaxRegionDTO,
sharedContext?: Context

View File

@@ -9,6 +9,16 @@ export interface CreateTaxRateDTO {
metadata?: Record<string, unknown>
}
export interface UpsertTaxRateDTO {
id?: string
rate?: number | null
code?: string | null
name?: string
is_default?: boolean
created_by?: string | null
metadata?: Record<string, unknown> | null
}
export interface UpdateTaxRateDTO {
rate?: number | null
code?: string | null

View File

@@ -20,6 +20,7 @@ import {
CreateTaxRateDTO,
CreateTaxRegionDTO,
UpdateTaxRateDTO,
UpsertTaxRateDTO,
} from "./mutations"
export interface ITaxModuleService extends IModuleService {
@@ -63,6 +64,12 @@ export interface ITaxModuleService extends IModuleService {
sharedContext?: Context
): Promise<TaxRateDTO[]>
upsert(data: UpsertTaxRateDTO, sharedContext?: Context): Promise<TaxRateDTO>
upsert(
data: UpsertTaxRateDTO[],
sharedContext?: Context
): Promise<TaxRateDTO[]>
delete(taxRateIds: string[], sharedContext?: Context): Promise<void>
delete(taxRateId: string, sharedContext?: Context): Promise<void>