feat: Add necessary middlewares for tax inclusive pricing (#7827)

We are adding tax inclusive pricing calculation when listing products.

Two things to keep in mind:
- `region_id` will be required if you request calculated prices.
- We won't accept `currency_code` anymore, as that will come from the region info (since ultimately a cart and its currency are tied to a region)

REF CORE-2376
DEPENDS ON #8003
This commit is contained in:
Stevche Radevski
2024-07-09 09:37:13 +00:00
committed by GitHub
parent db6969578f
commit 1c3ef13371
22 changed files with 824 additions and 121 deletions
@@ -0,0 +1,105 @@
export interface BaseCalculatedPriceSet {
/**
* The ID of the price set.
*/
id: string
/**
* Whether the calculated price is associated with a price list. During the calculation process, if no valid price list is found,
* the calculated price is set to the original price, which doesn't belong to a price list. In that case, the value of this property is `false`.
*/
is_calculated_price_price_list?: boolean
/**
* Whether the calculated price is tax inclusive.
*/
is_calculated_price_tax_inclusive?: boolean
/**
* The amount of the calculated price, or `null` if there isn't a calculated price.
*/
calculated_amount: number | null
/**
* The amount of the calculated price with taxes included. If the calculated price is tax inclusive, this field will be the same as `calculated_amount`.
*/
calculated_amount_with_tax?: number | null
/**
* The amount of the calculated price without taxes included. If the calculated price is tax exclusive, this field will be the same as `calculated_amount`.
*/
calculated_amount_without_tax?: number | null
/**
* Whether the original price is associated with a price list. During the calculation process, if the price list of the calculated price is of type override,
* the original price will be the same as the calculated price. In that case, the value of this property is `true`.
*/
is_original_price_price_list?: boolean
/**
* Whether the original price is tax inclusive.
*/
is_original_price_tax_inclusive?: boolean
/**
* The amount of the original price, or `null` if there isn't a calculated price.
*/
original_amount: number | null
/**
* The currency code of the calculated price, or null if there isn't a calculated price.
*/
currency_code: string | null
/**
* The details of the calculated price.
*/
calculated_price?: {
/**
* The ID of the price selected as the calculated price.
*/
id: string | null
/**
* The ID of the associated price list, if any.
*/
price_list_id: string | null
/**
* The type of the associated price list, if any.
*/
price_list_type: string | null
/**
* The `min_quantity` field defined on a price.
*/
min_quantity: number | null
/**
* The `max_quantity` field defined on a price.
*/
max_quantity: number | null
}
/**
* The details of the original price.
*/
original_price?: {
/**
* The ID of the price selected as the original price.
*/
id: string | null
/**
* The ID of the associated price list, if any.
*/
price_list_id: string | null
/**
* The type of the associated price list, if any.
*/
price_list_type: string | null
/**
* The `min_quantity` field defined on a price.
*/
min_quantity: number | null
/**
* The `max_quantity` field defined on a price.
*/
max_quantity: number | null
}
}
@@ -22,6 +22,8 @@ export interface AdminCreateProductVariantPrice {
amount: number
min_quantity?: number | null
max_quantity?: number | null
// Note: Although the BE is generic, we only use region_id for price rules for now, so it's better to keep the typings stricter.
rules?: { region_id: string } | null
}
export interface AdminCreateProductVariant {
@@ -1,6 +1,7 @@
import { BaseFilterable, OperatorMap } from "../../dal"
import { BaseCollection } from "../collection/common"
import { FindParams } from "../common"
import { BaseCalculatedPriceSet } from "../pricing/common"
import { BaseProductCategory } from "../product-category/common"
import { BaseProductType } from "../product-type/common"
@@ -56,10 +57,11 @@ export interface BaseProductVariant {
length: number | null
height: number | null
width: number | null
variant_rank?: number | null
options: BaseProductOptionValue[] | null
product?: BaseProduct | null
product_id?: string
variant_rank?: number | null
calculated_price?: BaseCalculatedPriceSet
created_at: string
updated_at: string
deleted_at: string | null
+6 -2
View File
@@ -305,11 +305,11 @@ export interface ProductCategoryDTO {
*
* @expandable
*/
parent_category?: ProductCategoryDTO | null
parent_category: ProductCategoryDTO | null
/**
* The associated parent category id.
*/
parent_category_id?: string | null
parent_category_id: string | null
/**
* The associated child categories.
*
@@ -330,6 +330,10 @@ export interface ProductCategoryDTO {
* When the product category was updated.
*/
updated_at: string | Date
/**
* When the product category was deleted.
*/
deleted_at?: string | Date
}
/**
+1 -1
View File
@@ -513,7 +513,7 @@ interface TaxLineDTO {
/**
* The rate of the tax line.
*/
rate: number | null
rate: number
/**
* The code of the tax line.
+1
View File
@@ -2,6 +2,7 @@ export * from "./cart"
export * from "./create-raw-properties-from-bignumber"
export * from "./line-item"
export * from "./math"
export * from "./tax"
export * from "./promotion"
export * from "./shipping-method"
export * from "./transform-properties-to-bignumber"
@@ -31,3 +31,24 @@ export function calculateTaxTotal({
return taxTotal
}
export function calculateAmountsWithTax({
taxLines,
amount,
includesTax,
}: {
taxLines: Pick<TaxLineDTO, "rate">[]
amount: number
includesTax?: boolean
}) {
const tax = calculateTaxTotal({
taxLines,
includesTax,
taxableAmount: amount,
})
return {
priceWithTax: includesTax ? amount : MathBN.add(tax, amount).toNumber(),
priceWithoutTax: includesTax ? MathBN.sub(amount, tax).toNumber() : amount,
}
}