Calculate Product Variant Price with Taxes

In this document, you'll learn how to calculate a product variant's price with taxes.

Step 0: Resolve Resources#

You'll need the following resources for the taxes calculation:

  1. Query to retrieve the product's variants' prices for a context. Learn more about that in this guide.
  2. The Tax Module's main service to get the tax lines for each product.
Code
1// other imports...2import {3  Modules,4  ContainerRegistrationKeys,5} from "@medusajs/framework/utils"6
7// In an API route, workflow step, etc...8const query = container.resolve(ContainerRegistrationKeys.QUERY)9const taxModuleService = container.resolve(10  Modules.TAX11)

Step 1: Retrieve Prices for a Context#

After resolving the resources, use Query to retrieve the products with the variants' prices for a context:

NoteLearn more about retrieving product variants' prices for a context in this guide.
Code
1import { QueryContext } from "@medusajs/framework/utils"2
3// ...4
5const { data: products } = await query.graph({6  entity: "product",7  fields: [8    "*",9    "variants.*",10    "variants.calculated_price.*",11  ],12  filters: {13    id: "prod_123",14  },15  context: {16    variants: {17      calculated_price: QueryContext({18        region_id: "region_123",19        currency_code: "usd",20      }),21    },22  },23})

Step 2: Get Tax Lines for Products#

To retrieve the tax line of each product, first, add the following utility method:

Code
1// other imports...2import {3  HttpTypes,4  TaxableItemDTO,5} from "@medusajs/framework/types"6
7// ...8const asTaxItem = (product: HttpTypes.StoreProduct): TaxableItemDTO[] => {9  return product.variants10    ?.map((variant) => {11      if (!variant.calculated_price) {12        return13      }14
15      return {16        id: variant.id,17        product_id: product.id,18        product_name: product.title,19        product_categories: product.categories?.map((c) => c.name),20        product_category_id: product.categories?.[0]?.id,21        product_sku: variant.sku,22        product_type: product.type,23        product_type_id: product.type_id,24        quantity: 1,25        unit_price: variant.calculated_price.calculated_amount,26        currency_code: variant.calculated_price.currency_code,27      }28    })29    .filter((v) => !!v) as unknown as TaxableItemDTO[]30}

This formats the products as items to calculate tax lines for.

Then, use it when retrieving the tax lines of the products retrieved earlier:

Code
1// other imports...2import {3  ItemTaxLineDTO,4} from "@medusajs/framework/types"5
6// ...7const taxLines = (await taxModuleService.getTaxLines(8  products.map(asTaxItem).flat(),9  {10    // example of context properties. You can pass other ones.11    address: {12      country_code,13    },14  }15)) as unknown as ItemTaxLineDTO[]

You use the Tax Module's main service's getTaxLines method to retrieve the tax line.

For the first parameter, you use the asTaxItem function to format the products as expected by the getTaxLines method.

For the second parameter, you pass the current context. You can pass other details such as the customer's ID.

NoteLearn about the other context properties to pass in the getTaxLines method's reference.

Step 3: Calculate Price with Tax for Variant#

To calculate the price with and without taxes for a variant, first, group the tax lines retrieved in the previous step by variant IDs:

Code
1const taxLinesMap = new Map<string, ItemTaxLineDTO[]>()2taxLines.forEach((taxLine) => {3  const variantId = taxLine.line_item_id4  if (!taxLinesMap.has(variantId)) {5    taxLinesMap.set(variantId, [])6  }7
8  taxLinesMap.get(variantId)?.push(taxLine)9})

Notice that the variant's ID is stored in the line_item_id property of a tax line since tax lines are used for line items in a cart.

Then, loop over the products and their variants to retrieve the prices with and without taxes:

Code
1// other imports...2import {3  calculateAmountsWithTax,4} from "@medusajs/framework/utils"5
6// ...7products.forEach((product) => {8  product.variants?.forEach((variant) => {9    if (!variant.calculated_price) {10      return11    }12
13    const taxLinesForVariant = taxLinesMap.get(variant.id) || []14    const { priceWithTax, priceWithoutTax } = calculateAmountsWithTax({15      taxLines: taxLinesForVariant,16      amount: variant.calculated_price!.calculated_amount!,17      includesTax:18        variant.calculated_price!.is_calculated_price_tax_inclusive!,19    })20
21    // do something with prices...22  })23})

For each product variant, you:

  1. Retrieve its tax lines from the taxLinesMap.
  2. Calculate its prices with and without taxes using the calculateAmountsWithTax from the Medusa Framework.
  3. The calculateAmountsWithTax function returns an object having two properties:
    • priceWithTax: The variant's price with the taxes applied.
    • priceWithoutTax: The variant's price without taxes applied.
Was this page helpful?
Edit this page