Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tax): add endpoints to manage tax rate rules #6557

Merged
merged 9 commits into from
Mar 4, 2024
135 changes: 135 additions & 0 deletions integration-tests/modules/__tests__/tax/admin/tax.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,139 @@ describe("Taxes - Admin", () => {
expect(rates.length).toEqual(1)
expect(rates[0].deleted_at).not.toBeNull()
})

it("can create a tax rate add rules and remove them", 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
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
)
const rateId = rateRes.data.tax_rate.id
let rules = await service.listTaxRateRules({ tax_rate_id: rateId })

expect(rules).toEqual([
{
id: expect.any(String),
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_1234",
created_by: "admin_user",
created_at: expect.any(Date),
updated_at: expect.any(Date),
deleted_at: null,
tax_rate: { id: rateId },
metadata: null,
},
])

await api.post(
`/admin/tax-rates/${rateId}/rules`,
{
reference: "product",
reference_id: "prod_1111",
},
adminHeaders
)

await api.post(
`/admin/tax-rates/${rateId}/rules`,
{
reference: "product",
reference_id: "prod_2222",
},
adminHeaders
)
rules = await service.listTaxRateRules({ tax_rate_id: rateId })
expect(rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_1234",
created_by: "admin_user",
}),
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_1111",
created_by: "admin_user",
}),
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_2222",
created_by: "admin_user",
}),
])
)

const toDeleteId = rules.find((r) => r.reference_id === "prod_1111")!.id
await api.delete(
`/admin/tax-rates/${rateId}/rules/${toDeleteId}`,
adminHeaders
)

rules = await service.listTaxRateRules({ tax_rate_id: rateId })
expect(rules.length).toEqual(2)

await api.post(
`/admin/tax-rates/${rateId}`,
{
rules: [
{ reference: "product", reference_id: "prod_3333" },
{ reference: "product", reference_id: "prod_4444" },
{ reference: "product", reference_id: "prod_5555" },
{ reference: "product", reference_id: "prod_6666" },
],
},
adminHeaders
)
rules = await service.listTaxRateRules({ tax_rate_id: rateId })
expect(rules.length).toEqual(4)
expect(rules).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_3333",
created_by: "admin_user",
}),
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_4444",
created_by: "admin_user",
}),
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_5555",
created_by: "admin_user",
}),
expect.objectContaining({
tax_rate_id: rateId,
reference: "product",
reference_id: "prod_6666",
created_by: "admin_user",
}),
])
)
})
})
209 changes: 209 additions & 0 deletions integration-tests/modules/__tests__/tax/workflow/tax.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import path from "path"
import { ITaxModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"

import { createAdminUser } from "../../../helpers/create-admin-user"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import { getContainer } from "../../../../environment-helpers/use-container"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import {
createTaxRateRulesStepId,
maybeSetTaxRateRulesStepId,
updateTaxRatesStepId,
updateTaxRatesWorkflow,
} from "@medusajs/core-flows"

jest.setTimeout(50000)

const env = { MEDUSA_FF_MEDUSA_V2: true }
const adminHeaders = {
headers: { "x-medusa-access-token": "test_token" },
}

describe("Taxes - Workflow", () => {
let dbConnection
let appContainer
let shutdownServer
let service: ITaxModuleService

beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd, env } as any)
shutdownServer = await startBootstrapApp({ cwd, env })
appContainer = getContainer()
service = appContainer.resolve(ModuleRegistrationName.TAX)
})

beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders)
})

afterAll(async () => {
const db = useDb()
await db.shutdown()
await shutdownServer()
})

afterEach(async () => {
const db = useDb()
await db.teardown()
})

it("compensates rules correctly", async () => {
const taxRegion = await service.createTaxRegions({
country_code: "us",
})

const [rateOne, rateTwo] = await service.create([
{
tax_region_id: taxRegion.id,
rate: 10,
code: "standard",
name: "Standard",
rules: [
{ reference: "shipping", reference_id: "shipping_12354" },
{ reference: "shipping", reference_id: "shipping_11111" },
{ reference: "shipping", reference_id: "shipping_22222" },
],
},
{
tax_region_id: taxRegion.id,
rate: 2,
code: "reduced",
name: "Reduced",
rules: [
{ reference: "product", reference_id: "product_12354" },
{ reference: "product", reference_id: "product_11111" },
{ reference: "product", reference_id: "product_22222" },
],
},
])

const workflow = updateTaxRatesWorkflow(appContainer)

workflow.appendAction("throw", createTaxRateRulesStepId, {
invoke: async function failStep() {
throw new Error(`Failed to update`)
},
})

await workflow.run({
input: {
selector: { tax_region_id: taxRegion.id },
update: {
rate: 2,
rules: [
{ reference: "product", reference_id: "product_12354" },
{ reference: "shipping", reference_id: "shipping_12354" },
],
},
},
throwOnError: false,
})

const taxRateRules = await service.listTaxRateRules({
tax_rate: { tax_region_id: taxRegion.id },
})

expect(taxRateRules.length).toEqual(6)
expect(taxRateRules).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_rate_id: rateOne.id,
reference_id: "shipping_12354",
}),
expect.objectContaining({
tax_rate_id: rateOne.id,
reference_id: "shipping_11111",
}),
expect.objectContaining({
tax_rate_id: rateOne.id,
reference_id: "shipping_22222",
}),
expect.objectContaining({
tax_rate_id: rateTwo.id,
reference_id: "product_12354",
}),
expect.objectContaining({
tax_rate_id: rateTwo.id,
reference_id: "product_11111",
}),
expect.objectContaining({
tax_rate_id: rateTwo.id,
reference_id: "product_22222",
}),
])
)
})

it("creates rules correctly", async () => {
const taxRegion = await service.createTaxRegions({
country_code: "us",
})

const [rateOne, rateTwo] = await service.create([
{
tax_region_id: taxRegion.id,
rate: 10,
code: "standard",
name: "Standard",
rules: [
{ reference: "shipping", reference_id: "shipping_12354" },
{ reference: "shipping", reference_id: "shipping_11111" },
{ reference: "shipping", reference_id: "shipping_22222" },
],
},
{
tax_region_id: taxRegion.id,
rate: 2,
code: "reduced",
name: "Reduced",
rules: [
{ reference: "product", reference_id: "product_12354" },
{ reference: "product", reference_id: "product_11111" },
{ reference: "product", reference_id: "product_22222" },
],
},
])

await updateTaxRatesWorkflow(appContainer).run({
input: {
selector: { tax_region_id: taxRegion.id },
update: {
rate: 2,
rules: [
{ reference: "product", reference_id: "product_12354" },
{ reference: "shipping", reference_id: "shipping_12354" },
],
},
},
})

const taxRateRules = await service.listTaxRateRules({
tax_rate: { tax_region_id: taxRegion.id },
})

expect(taxRateRules.length).toEqual(4)
expect(taxRateRules).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_rate_id: rateOne.id,
reference_id: "shipping_12354",
}),
expect.objectContaining({
tax_rate_id: rateTwo.id,
reference_id: "shipping_12354",
}),
expect.objectContaining({
tax_rate_id: rateOne.id,
reference_id: "product_12354",
}),
expect.objectContaining({
tax_rate_id: rateTwo.id,
reference_id: "product_12354",
}),
])
)
})
})
31 changes: 31 additions & 0 deletions packages/core-flows/src/tax/steps/create-tax-rate-rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CreateTaxRateRuleDTO, ITaxModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

export const createTaxRateRulesStepId = "create-tax-rate-rules"
export const createTaxRateRulesStep = createStep(
createTaxRateRulesStepId,
async (data: CreateTaxRateRuleDTO[], { container }) => {
const service = container.resolve<ITaxModuleService>(
ModuleRegistrationName.TAX
)

const created = await service.createTaxRateRules(data)

return new StepResponse(
created,
created.map((rule) => rule.id)
)
},
async (createdIds, { container }) => {
if (!createdIds?.length) {
return
}

const service = container.resolve<ITaxModuleService>(
ModuleRegistrationName.TAX
)

await service.deleteTaxRateRules(createdIds)
}
)
Loading
Loading