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:
Riqwan Thamir
2024-03-12 15:36:22 +01:00
committed by GitHub
parent 87e63c024e
commit c3c4f49fc2
14 changed files with 296 additions and 12 deletions

View File

@@ -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({

View File

@@ -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"

View File

@@ -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,
}))
)

View 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 })
}

View File

@@ -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

View File

@@ -114,3 +114,5 @@ export class StorePostCartsCartReq {
// @Type(() => Discount)
// discounts?: Discount[]
}
export class StorePostCartsCartTaxesReq {}

View File

@@ -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"])
})

View File

@@ -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" (

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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.
*/

View File

@@ -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.
*/