feat(core-flows,medusa,types): add automatic-taxes to region + generate tax lines endpoint (#6667)
what: - endpoint to generate tax lines - update workflows to force calculate tax lines with a flag - added automatic_taxes to region
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
TaxableItemDTO,
|
||||
TaxableShippingDTO,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "../../../../../modules-sdk/dist"
|
||||
|
||||
@@ -16,14 +17,28 @@ interface StepInput {
|
||||
cart: CartWorkflowDTO
|
||||
items: CartLineItemDTO[]
|
||||
shipping_methods: CartShippingMethodDTO[]
|
||||
force_tax_calculation?: boolean
|
||||
}
|
||||
|
||||
function normalizeTaxModuleContext(
|
||||
cart: CartWorkflowDTO
|
||||
cart: CartWorkflowDTO,
|
||||
forceTaxCalculation: boolean
|
||||
): TaxCalculationContext | null {
|
||||
const address = cart.shipping_address
|
||||
const shouldCalculateTax = forceTaxCalculation || cart.region?.automatic_taxes
|
||||
|
||||
if (!address || !address.country_code) {
|
||||
if (!shouldCalculateTax) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (forceTaxCalculation && !address?.country_code) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`country code is required to calculate taxes`
|
||||
)
|
||||
}
|
||||
|
||||
if (!address?.country_code) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -83,12 +98,17 @@ export const getItemTaxLinesStepId = "get-item-tax-lines"
|
||||
export const getItemTaxLinesStep = createStep(
|
||||
getItemTaxLinesStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { cart, items, shipping_methods: shippingMethods } = data
|
||||
const {
|
||||
cart,
|
||||
items,
|
||||
shipping_methods: shippingMethods,
|
||||
force_tax_calculation: forceTaxCalculation = false,
|
||||
} = data
|
||||
const taxService = container.resolve<ITaxModuleService>(
|
||||
ModuleRegistrationName.TAX
|
||||
)
|
||||
|
||||
const taxContext = normalizeTaxModuleContext(cart)
|
||||
const taxContext = normalizeTaxModuleContext(cart, forceTaxCalculation)
|
||||
|
||||
if (!taxContext) {
|
||||
return new StepResponse({
|
||||
|
||||
@@ -10,6 +10,7 @@ interface StepInput {
|
||||
cart_or_cart_id: CartWorkflowDTO | string
|
||||
items?: CartLineItemDTO[]
|
||||
shipping_methods?: CartShippingMethodDTO[]
|
||||
force_tax_calculation?: boolean
|
||||
}
|
||||
|
||||
export const updateTaxLinesStepId = "update-tax-lines-step"
|
||||
|
||||
@@ -18,6 +18,8 @@ const cartFields = [
|
||||
"id",
|
||||
"currency_code",
|
||||
"email",
|
||||
"region.id",
|
||||
"region.automatic_taxes",
|
||||
"items.id",
|
||||
"items.variant_id",
|
||||
"items.product_id",
|
||||
@@ -62,6 +64,7 @@ type WorkflowInput = {
|
||||
cart_or_cart_id: string | CartWorkflowDTO
|
||||
items?: CartLineItemDTO[]
|
||||
shipping_methods?: CartShippingMethodDTO[]
|
||||
force_tax_calculation?: boolean
|
||||
}
|
||||
|
||||
export const updateTaxLinesWorkflowId = "update-tax-lines"
|
||||
@@ -79,6 +82,7 @@ export const updateTaxLinesWorkflow = createWorkflow(
|
||||
items: data.input.items || data.cart.items,
|
||||
shipping_methods:
|
||||
data.input.shipping_methods || data.cart.shipping_methods,
|
||||
force_tax_calculation: data.input.force_tax_calculation,
|
||||
}))
|
||||
)
|
||||
|
||||
|
||||
43
packages/medusa/src/api-v2/store/carts/[id]/taxes/route.ts
Normal file
43
packages/medusa/src/api-v2/store/carts/[id]/taxes/route.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { updateTaxLinesWorkflow } from "@medusajs/core-flows"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||
import { defaultStoreCartFields } from "../../query-config"
|
||||
import { StorePostCartsCartTaxesReq } from "../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest<StorePostCartsCartTaxesReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const workflow = updateTaxLinesWorkflow(req.scope)
|
||||
|
||||
const { errors } = await workflow.run({
|
||||
input: {
|
||||
cart_or_cart_id: req.params.id,
|
||||
force_tax_calculation: true,
|
||||
},
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: Modules.CART,
|
||||
fields: defaultStoreCartFields,
|
||||
})
|
||||
|
||||
const [cart] = await remoteQuery(query, {
|
||||
cart: { id: req.params.id },
|
||||
})
|
||||
|
||||
// TODO: wrap result with totals when totals calculation is ready
|
||||
|
||||
res.status(200).json({ cart })
|
||||
}
|
||||
@@ -66,6 +66,7 @@ export const defaultStoreCartFields = [
|
||||
"region.id",
|
||||
"region.name",
|
||||
"region.currency_code",
|
||||
"region.automatic_taxes",
|
||||
"sales_channel_id",
|
||||
|
||||
// TODO: To be updated when payment sessions are introduces in the Rest API
|
||||
|
||||
@@ -114,3 +114,5 @@ export class StorePostCartsCartReq {
|
||||
// @Type(() => Discount)
|
||||
// discounts?: Discount[]
|
||||
}
|
||||
|
||||
export class StorePostCartsCartTaxesReq {}
|
||||
|
||||
@@ -34,6 +34,7 @@ describe("Region Module Service", () => {
|
||||
const createdRegion = await service.create({
|
||||
name: "Europe",
|
||||
currency_code: "EUR",
|
||||
automatic_taxes: false,
|
||||
})
|
||||
|
||||
expect(createdRegion).toEqual(
|
||||
@@ -42,6 +43,7 @@ describe("Region Module Service", () => {
|
||||
name: "Europe",
|
||||
currency_code: "eur",
|
||||
countries: [],
|
||||
automatic_taxes: false,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -75,6 +77,7 @@ describe("Region Module Service", () => {
|
||||
id: region.id,
|
||||
name: "North America",
|
||||
currency_code: "usd",
|
||||
automatic_taxes: true,
|
||||
countries: [
|
||||
expect.objectContaining({
|
||||
display_name: "Canada",
|
||||
@@ -148,6 +151,7 @@ describe("Region Module Service", () => {
|
||||
name: "Americas",
|
||||
currency_code: "MXN",
|
||||
countries: ["us", "mx"],
|
||||
automatic_taxes: false,
|
||||
})
|
||||
|
||||
const latestRegion = await service.retrieve(createdRegion.id, {
|
||||
@@ -158,6 +162,7 @@ describe("Region Module Service", () => {
|
||||
id: createdRegion.id,
|
||||
name: "Americas",
|
||||
currency_code: "mxn",
|
||||
automatic_taxes: false,
|
||||
})
|
||||
expect(latestRegion.countries.map((c) => c.iso_2)).toEqual(["mx", "us"])
|
||||
})
|
||||
|
||||
@@ -20,9 +20,10 @@ ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_3bdd5896ec93be2f1c62a3309a5";
|
||||
ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_91f88052197680f9790272aaf5b";
|
||||
${generatePostgresAlterColummnIfExistStatement(
|
||||
"region",
|
||||
["tax_rate", "gift_cards_taxable", "automatic_taxes", "includes_tax"],
|
||||
["tax_rate", "gift_cards_taxable", "includes_tax"],
|
||||
"DROP NOT NULL"
|
||||
)}
|
||||
ALTER TABLE "region" ADD COLUMN IF NOT EXISTS "automatic_taxes" BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
CREATE INDEX IF NOT EXISTS "IDX_region_deleted_at" ON "region" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
|
||||
-- Create or update "region_country" table
|
||||
CREATE TABLE IF NOT EXISTS "region_country" (
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
OptionalProps,
|
||||
@@ -31,6 +30,9 @@ export default class Region {
|
||||
@Property({ columnType: "text" })
|
||||
currency_code: string
|
||||
|
||||
@Property({ columnType: "boolean" })
|
||||
automatic_taxes = true
|
||||
|
||||
@OneToMany(() => Country, (country) => country.region)
|
||||
countries = new Collection<Country>(this)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CustomerDTO } from "../customer"
|
||||
import { ProductDTO } from "../product"
|
||||
import { RegionDTO } from "../region"
|
||||
import { CartDTO, CartLineItemDTO } from "./common"
|
||||
import { UpdateLineItemDTO } from "./mutations"
|
||||
|
||||
@@ -98,4 +99,5 @@ export interface CreatePaymentCollectionForCartWorkflowInputDTO {
|
||||
export interface CartWorkflowDTO extends CartDTO {
|
||||
customer?: CustomerDTO
|
||||
product?: ProductDTO
|
||||
region?: RegionDTO
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ export interface RegionDTO {
|
||||
*/
|
||||
currency_code: string
|
||||
|
||||
/**
|
||||
* Setting to indicate whether taxes need to be applied automatically
|
||||
*/
|
||||
automatic_taxes: boolean
|
||||
|
||||
/**
|
||||
* The countries of the region.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,10 @@ export interface CreateRegionDTO {
|
||||
* The currency code of the region.
|
||||
*/
|
||||
currency_code: string
|
||||
/**
|
||||
* Setting to indicate whether taxes need to be applied automatically
|
||||
*/
|
||||
automatic_taxes?: boolean
|
||||
/**
|
||||
* The region's countries.
|
||||
*/
|
||||
@@ -33,6 +37,10 @@ export interface UpsertRegionDTO {
|
||||
* The currency code of the region.
|
||||
*/
|
||||
currency_code?: string
|
||||
/**
|
||||
* Setting to indicate whether taxes need to be applied automatically
|
||||
*/
|
||||
automatic_taxes?: boolean
|
||||
/**
|
||||
* The region's countries.
|
||||
*/
|
||||
@@ -52,6 +60,10 @@ export interface UpdateRegionDTO {
|
||||
* The currency code of the region.
|
||||
*/
|
||||
currency_code?: string
|
||||
/**
|
||||
* Setting to indicate whether taxes need to be applied automatically
|
||||
*/
|
||||
automatic_taxes?: boolean
|
||||
/**
|
||||
* The region's countries.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user