diff --git a/www/apps/resources/app/commerce-modules/product/guides/price-with-taxes/page.mdx b/www/apps/resources/app/commerce-modules/product/guides/price-with-taxes/page.mdx new file mode 100644 index 0000000000..5dd86ab925 --- /dev/null +++ b/www/apps/resources/app/commerce-modules/product/guides/price-with-taxes/page.mdx @@ -0,0 +1,213 @@ +--- +sidebar_label: "Get Variant Price with Taxes" +--- + +export const metadata = { + title: `Calculate Product Variant Price with Taxes`, +} + +# {metadata.title} + +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. Remote query to retrieve the product's variants' prices for a context. Learn more about that in [this guide](../price/page.mdx). +2. The Tax Module's main service to get the tax lines for each product. + +```ts +// other imports... +import { + ModuleRegistrationName +} from "@medusajs/utils" + +// In an API route, workflow step, etc... +const remoteQuery = container.resolve("remoteQuery") +const taxModuleService = container.resolve( + ModuleRegistrationName.TAX +) +``` + +--- + +## Step 1: Retrieve Prices for a Context + +After resolving the resources, use the remote query to retrieve the products with the variants' prices for a context: + +```ts +// other imports... +import { + remoteQueryObjectFromString, +} from "@medusajs/utils" + +// ... +const query = remoteQueryObjectFromString({ + entryPoint: "product", + fields: [ + "*", + "variants.*", + "variants.calculated_price.*" + ], + variables: { + filters: { + id + }, + "variants.calculated_price": { + context: { + region_id, + currency_code, + } + } + } +}) + +const products = await remoteQuery(query) +``` + + + +Learn more about retrieving product variants' prices for a context in [this guide](../price/page.mdx). + + + +--- + +## Step 2: Get Tax Lines for Products + +To retrieve the tax line of each product, first, add the following utility method: + +```ts +// other imports... +import { + HttpTypes, + TaxableItemDTO +} from "@medusajs/types" + +// ... +const asTaxItem = (product: HttpTypes.StoreProduct): TaxableItemDTO[] => { + return product.variants + ?.map((variant) => { + if (!variant.calculated_price) { + return + } + + return { + id: variant.id, + product_id: product.id, + product_name: product.title, + product_categories: product.categories?.map((c) => c.name), + product_category_id: product.categories?.[0]?.id, + product_sku: variant.sku, + product_type: product.type, + product_type_id: product.type_id, + quantity: 1, + unit_price: variant.calculated_price.calculated_amount, + currency_code: variant.calculated_price.currency_code, + } + }) + .filter((v) => !!v) as unknown as TaxableItemDTO[] +} +``` + +This formats the products as items to calculate tax lines for. + +Then, use it when retrieving the tax lines of the products retrieved earlier: + +```ts +// other imports... +import { + ItemTaxLineDTO +} from "@medusajs/types" + +// ... +const taxLines = (await taxModuleService.getTaxLines( + products.map(asTaxItem).flat(), + { + // example of context properties. You can pass other ones. + address: { + country_code, + } + } +)) as unknown as ItemTaxLineDTO[] +``` + +You use the Tax Module's main service's [getTaxLines method](/references/tax/getTaxLines) 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. + + + +Learn about the other context properties to pass in [the getTaxLines method's reference](/references/tax/getTaxLines). + + + +--- + +## 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: + +export const taxLineHighlights = [ + ["3", "variantId", "The variant's ID is stored in the `line_item_id` property since tax lines are for cart items."] +] + +```ts highlights={taxLineHighlights} +const taxLinesMap = new Map() +taxLines.forEach((taxLine) => { + const variantId = taxLine.line_item_id + if (!taxLinesMap.has(variantId)) { + taxLinesMap.set(variantId, []) + } + + taxLinesMap.get(variantId)?.push(taxLine) +}) +``` + +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: + +export const calculateTaxHighlights = [ + ["13", "taxLinesForVariant", "Retrieve the variant's tax line from the map."], + ["14", "priceWithTax", "The product variant's price with taxes applied."], + ["14", "priceWithoutTax", "The product variant's price without taxes applied."], + ["14", "calculateAmountsWithTax", "Use utility to calculate prices with and without taxes."] +] + +```ts highlights={calculateTaxHighlights} +// other imports... +import { + calculateAmountsWithTax +} from "@medusajs/utils" + +// ... +products.forEach((product) => { + product.variants?.forEach((variant) => { + if (!variant.calculated_price) { + return + } + + const taxLinesForVariant = taxLinesMap.get(variant.id) || [] + const { priceWithTax, priceWithoutTax } = calculateAmountsWithTax({ + taxLines: taxLinesForVariant, + amount: variant.calculated_price!.calculated_amount!, + includesTax: + variant.calculated_price!.is_calculated_price_tax_inclusive!, + }) + + // do something with prices... + }) +}) +``` + +For each product variant, you: + +1. Retrieve its tax lines from the `taxLinesMap`. +2. Calculate its prices with and without taxes using the `calculateAmountsWithTax` function imported from `@medusajs/utils`. +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. diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs index 4608e583c7..148365d903 100644 --- a/www/apps/resources/generated/files-map.mjs +++ b/www/apps/resources/generated/files-map.mjs @@ -451,6 +451,10 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/product/guides/price/page.mdx", "pathname": "/commerce-modules/product/guides/price" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/product/guides/price-with-taxes/page.mdx", + "pathname": "/commerce-modules/product/guides/price-with-taxes" + }, { "filePath": "/www/apps/resources/app/commerce-modules/product/page.mdx", "pathname": "/commerce-modules/product" diff --git a/www/apps/resources/generated/sidebar.mjs b/www/apps/resources/generated/sidebar.mjs index 544346f8c5..ac82420732 100644 --- a/www/apps/resources/generated/sidebar.mjs +++ b/www/apps/resources/generated/sidebar.mjs @@ -4279,6 +4279,13 @@ export const generatedSidebar = [ "path": "/commerce-modules/product/guides/price", "title": "Get Product Variant Prices", "children": [] + }, + { + "loaded": true, + "isPathHref": true, + "path": "/commerce-modules/product/guides/price-with-taxes", + "title": "Get Variant Price with Taxes", + "children": [] } ] },