Back to Medusa

{metadata.title}

www/apps/resources/app/commerce-modules/cart/tax-lines/page.mdx

2.18.07.9 KB
Original Source

import { Table } from "docs-ui"

export const metadata = { title: Tax Lines in Cart Module, }

{metadata.title}

In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module.

What are Tax Lines?

A tax line indicates the tax rate of a line item or a shipping method. The LineItemTaxLine data model represents a line item’s tax line, and the ShippingMethodTaxLine data model represents a shipping method’s tax line.


Tax Inclusivity

By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount, and then adding them to the item/method’s subtotal.

However, line items and shipping methods have an is_tax_inclusive property that, when enabled, indicates that the item or method’s price already includes taxes.

So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal.

<Note>

The following diagram is a simplified showcase of how a subtotal is calculated from the taxes perspective.

</Note>

For example, if a line item's amount is 5000, the tax rate is 10, and tax inclusivity is enabled, the tax amount is 10% of 5000, which is 500, making the unit price of the line item 4500.


When Are Tax Lines Recalculated?

When you use the Cart Module in the Medusa application, Medusa recalculates a cart's tax lines automatically as the customer builds and updates their cart. So, in most cases, you don't need to manually recalculate them.

Whether Medusa recalculates the tax lines depends on the automatic_taxes setting of the cart's region:

  • If automatic_taxes is enabled, Medusa recalculates the affected tax lines automatically when the cart changes.
  • If automatic_taxes is disabled, Medusa only calculates tax lines when you send a request to the Calculate Cart Taxes API route.

Tax Lines Require a Shipping Address

Regardless of the automatic_taxes setting, Medusa only calculates tax lines once the cart's shipping address has a country code. Until you set the shipping address's country, the cart's line items and shipping methods have no tax lines.

Medusa determines the tax rates from the country in the cart's shipping address, not from the list of countries in the cart's region. So, a region with multiple countries doesn't have a default country for tax calculation. Medusa calculates each customer's taxes based on the country they set in their shipping address.

<Note>

If the cart's region has only one country, Medusa sets that country as the cart's shipping address country when creating the cart. So, Medusa can calculate tax lines without the customer setting their address. For a region with multiple countries, the customer must set their shipping address's country before Medusa calculates tax lines.

</Note>

Tax Recalculation by Cart Operation

The following table explains how each cart operation affects the cart's tax lines when automatic_taxes is enabled:

<Table> <Table.Header> <Table.Row> <Table.HeaderCell> Operation </Table.HeaderCell> <Table.HeaderCell> Tax behavior </Table.HeaderCell> </Table.Row> </Table.Header> <Table.Body> <Table.Row> <Table.Cell>
  Add a line item

  </Table.Cell>
  <Table.Cell>

  Medusa calculates the tax lines of the new item.

  </Table.Cell>
</Table.Row>
<Table.Row>
  <Table.Cell>

  Add a shipping method

  </Table.Cell>
  <Table.Cell>

  Medusa calculates the tax lines of the new shipping method.

  </Table.Cell>
</Table.Row>
<Table.Row>
  <Table.Cell>

  Update the cart's region or locale

  </Table.Cell>
  <Table.Cell>

  Medusa recalculates the tax lines of all the cart's items and shipping methods.

  </Table.Cell>
</Table.Row>
<Table.Row>
  <Table.Cell>

  Update a line item's quantity

  </Table.Cell>
  <Table.Cell>

  Medusa doesn't recalculate tax lines. Instead, it derives the new tax amounts from the item's existing tax rates.

  </Table.Cell>
</Table.Row>
<Table.Row>
  <Table.Cell>

  Remove a line item

  </Table.Cell>
  <Table.Cell>

  Medusa doesn't recalculate the tax lines of the cart's other items.

  </Table.Cell>
</Table.Row>

</Table.Body>

</Table> <Note>

To force Medusa to recalculate a cart's tax lines, even when automatic_taxes is disabled, send a request to the Calculate Cart Taxes API route. This also triggers a fresh calculation from your third-party tax provider, if you use one.

</Note>

Retrieve Tax Lines

When using the Cart and Tax modules together, you can use the getTaxLines method of the Tax Module’s main service. It retrieves the tax lines for a cart’s line items and shipping methods.

ts
// retrieve the cart
const cart = await cartModuleService.retrieveCart("cart_123", {
  relations: [
    "items.tax_lines",
    "shipping_methods.tax_lines",
    "shipping_address",
  ],
})

// retrieve the tax lines
const taxLines = await taxModuleService.getTaxLines(
  [
    ...(cart.items as TaxableItemDTO[]),
    ...(cart.shipping_methods as TaxableShippingDTO[]),
  ],
  {
    address: {
      ...cart.shipping_address,
      country_code:
        cart.shipping_address.country_code || "us",
    },
  }
)

Then, use the returned tax lines to set the line items and shipping methods’ tax lines:

ts
// set line item tax lines
await cartModuleService.setLineItemTaxLines(
  cart.id,
  taxLines.filter((line) => "line_item_id" in line)
)

// set shipping method tax lines
await cartModuleService.setLineItemTaxLines(
  cart.id,
  taxLines.filter((line) => "shipping_line_id" in line)
)

Customize Tax Context with Hooks

The tax line workflows (updateTaxLinesWorkflow, upsertTaxLinesWorkflow, and updateOrderTaxLinesWorkflow) expose a setTaxLineContext hook that allows you to add custom context to tax calculations.

<Note>

This hook is available since Medusa v2.16.0.

</Note>

This is useful when you need to pass additional information to your tax provider that isn't included in the default cart or order data.

For example, create the file src/workflows/hooks/tax-context.ts with the following content:

ts
import { updateTaxLinesWorkflow } from "@medusajs/medusa/core-flows"
import { Modules } from "@medusajs/framework/utils"

updateTaxLinesWorkflow.hooks.setTaxLineContext(
  async ({ cart, items, shipping_methods }, { container }) => {
    // Add custom logic to determine tax context
    const customerModule = container.resolve(Modules.CUSTOMER)
    const customer = await customerModule.retrieveCustomer(cart.customer_id)
    
    return {
      customer_type: customer.metadata?.type || "regular",
      loyalty_tier: customer.metadata?.loyalty_tier || "bronze",
      special_exemption: customer.metadata?.tax_exempt || false,
    }
  }
)

The returned object is passed as additional_context to the tax provider's getTaxLines method, allowing you to implement custom tax logic based on this context.

<Note>

Learn more about workflow hooks in the Workflow Hooks guide.

</Note>