fix: Product type tax overrides (#9951)

* fix: Make product type tax override work

* fix: Make product type tax override work
This commit is contained in:
Oli Juhl
2024-11-06 20:33:40 +01:00
committed by GitHub
parent aa78fbf546
commit 505768dd9f
35 changed files with 479 additions and 309 deletions

View File

@@ -1,4 +1,5 @@
import { RemoteLink } from "@medusajs/modules-sdk"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
IApiKeyModuleService,
ICartModuleService,
@@ -21,7 +22,6 @@ import {
PromotionType,
RuleOperator,
} from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
createAdminUser,
generatePublishableKey,
@@ -1178,6 +1178,89 @@ medusaIntegrationTestRunner({
)
})
it("should update a carts tax lines with a reduced product type rate", async () => {
await setupTaxStructure(taxModule)
const region = await regionModule.createRegions({
name: "US",
currency_code: "usd",
automatic_taxes: false,
countries: ["ca"],
})
const cart = await cartModule.createCarts({
currency_code: "usd",
region_id: region.id,
shipping_address: {
address_1: "test address 1",
address_2: "test address 2",
city: "CA",
country_code: "CA",
province: "QC",
postal_code: "94016",
},
items: [
{
id: "item-1",
unit_price: 2000,
quantity: 1,
title: "Test item",
product_id: "prod_tshirt_reduced_tax",
product_type_id: "product_type_id_3",
} as any,
{
id: "item-2",
unit_price: 1000,
quantity: 1,
title: "Test item two",
product_id: "prod_tshirt",
} as any,
],
})
let updated = await api.post(
`/store/carts/${cart.id}/taxes`,
{},
storeHeaders
)
expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual(
expect.objectContaining({
id: cart.id,
items: expect.arrayContaining([
expect.objectContaining({
id: "item-2",
tax_lines: expect.arrayContaining([
// Regional rate for Quebec, Canada
expect.objectContaining({
description: "QC Default Rate",
code: "QCDEFAULT",
rate: 2,
provider_id: "system",
}),
]),
adjustments: [],
}),
expect.objectContaining({
id: "item-1",
tax_lines: expect.arrayContaining([
// Reduced rate for product types in Quebec, Canada
expect.objectContaining({
description: "QC Reduced Rate for Product Type",
code: "QCREDUCE_TYPE",
rate: 1,
provider_id: "system",
}),
]),
adjustments: [],
}),
]),
})
)
})
it("should throw error when shipping is not present", async () => {
await setupTaxStructure(taxModule)

View File

@@ -1,3 +1,4 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
ICartModuleService,
IFulfillmentModuleService,
@@ -10,7 +11,6 @@ import {
ITaxModuleService,
} from "@medusajs/types"
import { ContainerRegistrationKeys, Modules } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
adminHeaders,
createAdminUser,
@@ -244,6 +244,7 @@ medusaIntegrationTestRunner({
product_description: null,
product_subtitle: null,
product_type: null,
product_type_id: null,
product_collection: null,
product_handle: "test-product",
variant_sku: null,

View File

@@ -1,6 +1,6 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { IOrderModuleService } from "@medusajs/types"
import { Modules } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
adminHeaders,
createAdminUser,
@@ -157,6 +157,7 @@ medusaIntegrationTestRunner({
product_description: null,
product_subtitle: null,
product_type: null,
product_type_id: null,
product_collection: null,
product_handle: null,
variant_sku: null,

View File

@@ -1,142 +0,0 @@
import {
CartLineItemDTO,
CartShippingMethodDTO,
CartWorkflowDTO,
ITaxModuleService,
ItemTaxLineDTO,
ShippingTaxLineDTO,
TaxCalculationContext,
TaxableItemDTO,
TaxableShippingDTO,
} from "@medusajs/framework/types"
import { MedusaError, Modules } from "@medusajs/framework/utils"
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
export interface GetItemTaxLinesStepInput {
cart: CartWorkflowDTO
items: CartLineItemDTO[]
shipping_methods: CartShippingMethodDTO[]
force_tax_calculation?: boolean
is_return?: boolean
}
function normalizeTaxModuleContext(
cart: CartWorkflowDTO,
forceTaxCalculation: boolean,
isReturn?: boolean
): TaxCalculationContext | null {
const address = cart.shipping_address
const shouldCalculateTax = forceTaxCalculation || cart.region?.automatic_taxes
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
}
const customer = cart.customer
? {
id: cart.customer.id,
email: cart.customer.email,
customer_groups: cart.customer.groups?.map((g) => g.id) || [],
}
: undefined
return {
address: {
country_code: address.country_code,
province_code: address.province,
address_1: address.address_1,
address_2: address.address_2,
city: address.city,
postal_code: address.postal_code,
},
customer,
is_return: isReturn ?? false,
}
}
function normalizeLineItemsForTax(
cart: CartWorkflowDTO,
items: CartLineItemDTO[]
): TaxableItemDTO[] {
return items.map((item) => ({
id: item.id,
product_id: item.product_id!,
product_name: item.variant_title,
product_sku: item.variant_sku,
product_type: item.product_type,
product_type_id: item.product_type,
quantity: item.quantity,
unit_price: item.unit_price,
currency_code: cart.currency_code,
}))
}
function normalizeLineItemsForShipping(
cart: CartWorkflowDTO,
shippingMethods: CartShippingMethodDTO[]
): TaxableShippingDTO[] {
return shippingMethods.map((shippingMethod) => ({
id: shippingMethod.id,
shipping_option_id: shippingMethod.shipping_option_id!,
unit_price: shippingMethod.amount,
currency_code: cart.currency_code,
}))
}
export const getItemTaxLinesStepId = "get-item-tax-lines"
/**
* This step retrieves the tax lines of the specified line items in a cart.
*/
export const getItemTaxLinesStep = createStep(
getItemTaxLinesStepId,
async (data: GetItemTaxLinesStepInput, { container }) => {
const {
cart,
items,
shipping_methods: shippingMethods,
force_tax_calculation: forceTaxCalculation = false,
is_return: isReturn = false,
} = data
const taxService = container.resolve<ITaxModuleService>(Modules.TAX)
const taxContext = normalizeTaxModuleContext(
cart,
forceTaxCalculation,
isReturn
)
if (!taxContext) {
return new StepResponse({
lineItemTaxLines: [],
shippingMethodsTaxLines: [],
})
}
const lineItemTaxLines = (await taxService.getTaxLines(
normalizeLineItemsForTax(cart, items),
taxContext
)) as ItemTaxLineDTO[]
const shippingMethodsTaxLines = (await taxService.getTaxLines(
normalizeLineItemsForShipping(cart, shippingMethods),
taxContext
)) as ShippingTaxLineDTO[]
return new StepResponse({
lineItemTaxLines,
shippingMethodsTaxLines,
})
}
)

View File

@@ -9,7 +9,6 @@ export * from "./find-one-or-any-region"
export * from "./find-or-create-customer"
export * from "./find-sales-channel"
export * from "./get-actions-to-compute-from-promotions"
export * from "./get-item-tax-lines"
export * from "./get-line-item-actions"
export * from "./get-promotion-codes-to-apply"
export * from "./get-variant-price-sets"
@@ -28,3 +27,4 @@ export * from "./update-line-items"
export * from "./validate-cart-payments"
export * from "./validate-cart-shipping-options"
export * from "./validate-variant-prices"

View File

@@ -114,6 +114,7 @@ export const productVariantsFields = [
"product.subtitle",
"product.thumbnail",
"product.type.value",
"product.type.id",
"product.collection.title",
"product.handle",
"product.discountable",

View File

@@ -86,6 +86,7 @@ export function prepareLineItemData(data: Input) {
variant.product.description ?? item?.product_description,
product_subtitle: variant.product.subtitle ?? item?.product_subtitle,
product_type: variant.product.type?.value ?? item?.product_type ?? null,
product_type_id: variant.product.type?.id ?? item?.product_type_id ?? null,
product_collection:
variant.product.collection?.title ?? item?.product_collection ?? null,
product_handle: variant.product.handle ?? item?.product_handle,

View File

@@ -8,7 +8,8 @@ import {
transform,
} from "@medusajs/framework/workflows-sdk"
import { useRemoteQueryStep } from "../../common"
import { getItemTaxLinesStep, setTaxLinesForItemsStep } from "../steps"
import { getItemTaxLinesStep } from "../../tax/steps/get-item-tax-lines"
import { setTaxLinesForItemsStep } from "../steps"
const cartFields = [
"id",
@@ -23,6 +24,7 @@ const cartFields = [
"items.product_description",
"items.product_subtitle",
"items.product_type",
"items.product_type_id",
"items.product_collection",
"items.product_handle",
"items.variant_sku",
@@ -81,7 +83,7 @@ export const updateTaxLinesWorkflow = createWorkflow(
const taxLineItems = getItemTaxLinesStep(
transform({ input, cart }, (data) => ({
cart: data.cart,
orderOrCart: data.cart,
items: data.input.items || data.cart.items,
shipping_methods:
data.input.shipping_methods || data.cart.shipping_methods,

View File

@@ -20,7 +20,6 @@ export * from "./exchange/cancel-exchange"
export * from "./exchange/create-exchange"
export * from "./exchange/create-exchange-items-from-actions"
export * from "./exchange/delete-exchanges"
export * from "./get-item-tax-lines"
export * from "./preview-order-change"
export * from "./register-fulfillment"
export * from "./register-shipment"
@@ -35,3 +34,4 @@ export * from "./update-order-change-actions"
export * from "./update-order-changes"
export * from "./update-order-exchanges"
export * from "./update-shipping-methods"

View File

@@ -6,10 +6,8 @@ import {
when,
} from "@medusajs/framework/workflows-sdk"
import { useRemoteQueryStep } from "../../common"
import {
getOrderItemTaxLinesStep,
setOrderTaxLinesForItemsStep,
} from "../steps"
import { getItemTaxLinesStep } from "../../tax/steps/get-item-tax-lines"
import { setOrderTaxLinesForItemsStep } from "../steps"
const completeOrderFields = [
"id",
@@ -174,7 +172,7 @@ export const updateOrderTaxLinesWorkflow = createWorkflow(
}).config({ name: "query-order-shipping-methods" })
})
const taxLineItems = getOrderItemTaxLinesStep(
const taxLineItems = getItemTaxLinesStep(
transform(
{ input, order, items, shippingMethods, isFullOrder },
(data) => {
@@ -187,7 +185,7 @@ export const updateOrderTaxLinesWorkflow = createWorkflow(
: data.items ?? []
return {
order: data.order,
orderOrCart: data.order,
items: lineItems,
shipping_methods: shippingMethods,
force_tax_calculation: data.input.force_tax_calculation,

View File

@@ -1,4 +1,7 @@
import {
CartLineItemDTO,
CartShippingMethodDTO,
CartWorkflowDTO,
ITaxModuleService,
ItemTaxLineDTO,
OrderLineItemDTO,
@@ -12,24 +15,24 @@ import {
import { MedusaError, Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
export interface GetOrderItemTaxLinesStepInput {
order: OrderWorkflowDTO
items: OrderLineItemDTO[]
shipping_methods: OrderShippingMethodDTO[]
export interface GetItemTaxLinesStepInput {
orderOrCart: OrderWorkflowDTO | CartWorkflowDTO
items: OrderLineItemDTO[] | CartLineItemDTO[]
shipping_methods: OrderShippingMethodDTO[] | CartShippingMethodDTO[]
force_tax_calculation?: boolean
is_return?: boolean
shipping_address?: OrderWorkflowDTO["shipping_address"]
}
function normalizeTaxModuleContext(
order: OrderWorkflowDTO,
orderOrCart: OrderWorkflowDTO | CartWorkflowDTO,
forceTaxCalculation: boolean,
isReturn?: boolean,
shippingAddress?: OrderWorkflowDTO["shipping_address"]
): TaxCalculationContext | null {
const address = shippingAddress ?? order.shipping_address
const address = shippingAddress ?? orderOrCart.shipping_address
const shouldCalculateTax =
forceTaxCalculation || order.region?.automatic_taxes
forceTaxCalculation || orderOrCart.region?.automatic_taxes
if (!shouldCalculateTax) {
return null
@@ -46,10 +49,10 @@ function normalizeTaxModuleContext(
return null
}
const customer = order.customer && {
id: order.customer.id,
email: order.customer.email,
customer_groups: order.customer.groups?.map((g) => g.id) || [],
const customer = orderOrCart.customer && {
id: orderOrCart.customer.id,
email: orderOrCart.customer.email,
customer_groups: orderOrCart.customer.groups?.map((g) => g.id) || [],
}
return {
@@ -67,28 +70,25 @@ function normalizeTaxModuleContext(
}
function normalizeLineItemsForTax(
order: OrderWorkflowDTO,
items: OrderLineItemDTO[]
orderOrCart: OrderWorkflowDTO | CartWorkflowDTO,
items: OrderLineItemDTO[] | CartLineItemDTO[]
): TaxableItemDTO[] {
return items.map(
(item) =>
({
id: item.id,
product_id: item.product_id!,
product_name: item.variant_title,
product_sku: item.variant_sku,
product_type: item.product_type,
product_type_id: item.product_type,
product_type_id: item.product_type_id,
quantity: item.quantity,
unit_price: item.unit_price,
currency_code: order.currency_code,
currency_code: orderOrCart.currency_code,
} as TaxableItemDTO)
)
}
function normalizeLineItemsForShipping(
order: OrderWorkflowDTO,
shippingMethods: OrderShippingMethodDTO[]
orderOrCart: OrderWorkflowDTO | CartWorkflowDTO,
shippingMethods: OrderShippingMethodDTO[] | CartShippingMethodDTO[]
): TaxableShippingDTO[] {
return shippingMethods.map(
(shippingMethod) =>
@@ -96,20 +96,20 @@ function normalizeLineItemsForShipping(
id: shippingMethod.id,
shipping_option_id: shippingMethod.shipping_option_id!,
unit_price: shippingMethod.amount,
currency_code: order.currency_code,
currency_code: orderOrCart.currency_code,
} as TaxableShippingDTO)
)
}
export const getOrderItemTaxLinesStepId = "get-order-item-tax-lines"
export const getItemTaxLinesStepId = "get-item-tax-lines"
/**
* This step retrieves the tax lines for an order's line items and shipping methods.
*/
export const getOrderItemTaxLinesStep = createStep(
getOrderItemTaxLinesStepId,
async (data: GetOrderItemTaxLinesStepInput, { container }) => {
export const getItemTaxLinesStep = createStep(
getItemTaxLinesStepId,
async (data: GetItemTaxLinesStepInput, { container }) => {
const {
order,
orderOrCart,
items = [],
shipping_methods: shippingMethods = [],
force_tax_calculation: forceTaxCalculation = false,
@@ -119,7 +119,7 @@ export const getOrderItemTaxLinesStep = createStep(
const taxService = container.resolve<ITaxModuleService>(Modules.TAX)
const taxContext = normalizeTaxModuleContext(
order,
orderOrCart,
forceTaxCalculation,
isReturn,
shippingAddress
@@ -136,14 +136,14 @@ export const getOrderItemTaxLinesStep = createStep(
if (items.length) {
stepResponseData.lineItemTaxLines = (await taxService.getTaxLines(
normalizeLineItemsForTax(order, items),
normalizeLineItemsForTax(orderOrCart, items),
taxContext
)) as ItemTaxLineDTO[]
}
if (shippingMethods.length) {
stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines(
normalizeLineItemsForShipping(order, shippingMethods),
normalizeLineItemsForShipping(orderOrCart, shippingMethods),
taxContext
)) as ShippingTaxLineDTO[]
}

View File

@@ -608,6 +608,11 @@ export interface CartLineItemDTO extends CartLineItemTotalsDTO {
*/
product_type?: string
/**
* The type of the associated product.
*/
product_type_id?: string
/**
* The collection of the associated product.
*/

View File

@@ -500,6 +500,11 @@ export interface CreateLineItemDTO {
*/
product_type?: string
/**
* The ID of type of the associated product.
*/
product_type_id?: string
/**
* The collection of the associated product.
*/

View File

@@ -389,7 +389,11 @@ export interface BaseOrderLineItem {
*/
product_subtitle: string | null
/**
* The ID of the associated product's type.
* The ID of the associated product type.
*/
product_type_id: string | null
/**
* The associated product type.
*/
product_type: string | null
/**

View File

@@ -805,6 +805,11 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO {
*/
product_subtitle?: string | null
/**
* The ID of the type of the product associated with the line item.
*/
product_type_id?: string | null
/**
* The type of the product associated with the line item.
*/

View File

@@ -364,31 +364,6 @@ export interface TaxableItemDTO {
*/
product_id: string
/**
* The name of the item's product.
*/
product_name?: string
/**
* The ID of the category of the item's product.
*/
product_category_id?: string
/**
* The categories of the item's product.
*/
product_categories?: string[]
/**
* The SKU of the item's product.
*/
product_sku?: string
/**
* The type of the item's product.
*/
product_type?: string
/**
* The ID of the type of the item's product.
*/

View File

@@ -1,6 +1,6 @@
import { updateTaxLinesWorkflow } from "@medusajs/core-flows"
import { HttpTypes } from "@medusajs/framework/types"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { refetchCart } from "../../helpers"
export const POST = async (

View File

@@ -47,6 +47,7 @@ export const defaultStoreCartFields = [
"items.product.tags.id",
"items.product.collection_id",
"items.product.type_id",
"items.product_type_id",
"items.product_title",
"items.product_description",
"items.product_subtitle",

View File

@@ -1,3 +1,8 @@
import {
MedusaRequest,
refetchEntities,
refetchEntity,
} from "@medusajs/framework/http"
import {
HttpTypes,
ItemTaxLineDTO,
@@ -5,11 +10,6 @@ import {
TaxableItemDTO,
TaxCalculationContext,
} from "@medusajs/framework/types"
import {
MedusaRequest,
refetchEntities,
refetchEntity,
} from "@medusajs/framework/http"
import { calculateAmountsWithTax, Modules } from "@medusajs/framework/utils"
import { TaxModuleService } from "@medusajs/tax/dist/services"
@@ -114,12 +114,6 @@ const asTaxItem = (product: HttpTypes.StoreProduct): TaxableItemDTO[] => {
return {
id: variant.id,
product_id: product.id,
product_name: product.title,
product_categories: product.categories?.map((c) => c.name),
// TODO: It is strange that we only accept a single category, revisit the tax module implementation
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,

View File

@@ -1,8 +1,8 @@
import { ICartModuleService } from "@medusajs/framework/types"
import { BigNumber, Module, Modules } from "@medusajs/framework/utils"
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import { CheckConstraintViolationException } from "@mikro-orm/core"
import { CartModuleService } from "@services"
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
jest.setTimeout(50000)
@@ -2502,6 +2502,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
product_description: null,
product_subtitle: null,
product_type: null,
product_type_id: null,
product_collection: null,
product_handle: null,
variant_sku: null,
@@ -2606,6 +2607,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
product_description: null,
product_subtitle: null,
product_type: null,
product_type_id: null,
product_collection: null,
product_handle: null,
variant_sku: null,

View File

@@ -37,6 +37,7 @@
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts medusa-mikro-orm cache:clear"
},
"devDependencies": {
"@medusajs/framework": "^2.0.1",
"@medusajs/test-utils": "^2.0.1",
"@mikro-orm/cli": "5.9.7",
"@mikro-orm/core": "5.9.7",

View File

@@ -1,5 +1,7 @@
{
"namespaces": ["public"],
"namespaces": [
"public"
],
"name": "public",
"tables": [
{
@@ -159,7 +161,9 @@
"indexes": [
{
"keyName": "IDX_cart_address_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -167,7 +171,9 @@
},
{
"keyName": "cart_address_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
@@ -259,6 +265,16 @@
"nullable": true,
"mappedType": "json"
},
"completed_at": {
"name": "completed_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
@@ -295,30 +311,11 @@
"name": "cart",
"schema": "public",
"indexes": [
{
"columnNames": ["sales_channel_id"],
"composite": false,
"keyName": "IDX_cart_sales_channel_id",
"primary": false,
"unique": false
},
{
"columnNames": ["currency_code"],
"composite": false,
"keyName": "IDX_cart_curency_code",
"primary": false,
"unique": false
},
{
"columnNames": ["billing_address_id"],
"composite": false,
"keyName": "IDX_cart_billing_address_id",
"primary": false,
"unique": false
},
{
"keyName": "IDX_cart_region_id",
"columnNames": ["region_id"],
"columnNames": [
"region_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -326,7 +323,9 @@
},
{
"keyName": "IDX_cart_customer_id",
"columnNames": ["customer_id"],
"columnNames": [
"customer_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -334,15 +333,29 @@
},
{
"keyName": "IDX_cart_sales_channel_id",
"columnNames": ["sales_channel_id"],
"columnNames": [
"sales_channel_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_sales_channel_id\" ON \"cart\" (sales_channel_id) WHERE deleted_at IS NULL AND sales_channel_id IS NOT NULL"
},
{
"keyName": "IDX_cart_curency_code",
"columnNames": [
"currency_code"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_curency_code\" ON \"cart\" (currency_code) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_cart_shipping_address_id",
"columnNames": ["shipping_address_id"],
"columnNames": [
"shipping_address_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -350,7 +363,9 @@
},
{
"keyName": "IDX_cart_billing_address_id",
"columnNames": ["billing_address_id"],
"columnNames": [
"billing_address_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -358,7 +373,9 @@
},
{
"keyName": "IDX_cart_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -366,7 +383,9 @@
},
{
"keyName": "cart_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
@@ -376,18 +395,26 @@
"foreignKeys": {
"cart_shipping_address_id_foreign": {
"constraintName": "cart_shipping_address_id_foreign",
"columnNames": ["shipping_address_id"],
"columnNames": [
"shipping_address_id"
],
"localTableName": "public.cart",
"referencedColumnNames": ["id"],
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_address",
"deleteRule": "set null",
"updateRule": "cascade"
},
"cart_billing_address_id_foreign": {
"constraintName": "cart_billing_address_id_foreign",
"columnNames": ["billing_address_id"],
"columnNames": [
"billing_address_id"
],
"localTableName": "public.cart",
"referencedColumnNames": ["id"],
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_address",
"deleteRule": "set null",
"updateRule": "cascade"
@@ -504,6 +531,15 @@
"nullable": true,
"mappedType": "text"
},
"product_type_id": {
"name": "product_type_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"product_collection": {
"name": "product_collection",
"type": "text",
@@ -565,7 +601,6 @@
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "true",
"mappedType": "boolean"
},
"is_discountable": {
@@ -575,7 +610,6 @@
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "true",
"mappedType": "boolean"
},
"is_tax_inclusive": {
@@ -585,7 +619,6 @@
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "false",
"mappedType": "boolean"
},
"compare_at_unit_price": {
@@ -671,7 +704,9 @@
"indexes": [
{
"keyName": "IDX_line_item_cart_id",
"columnNames": ["cart_id"],
"columnNames": [
"cart_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -679,7 +714,9 @@
},
{
"keyName": "IDX_line_item_variant_id",
"columnNames": ["variant_id"],
"columnNames": [
"variant_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -687,15 +724,29 @@
},
{
"keyName": "IDX_line_item_product_id",
"columnNames": ["product_id"],
"columnNames": [
"product_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_product_id\" ON \"cart_line_item\" (product_id) WHERE deleted_at IS NULL AND product_id IS NOT NULL"
},
{
"keyName": "IDX_line_item_product_type_id",
"columnNames": [
"product_type_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_product_type_id\" ON \"cart_line_item\" (product_type_id) WHERE deleted_at IS NULL AND product_type_id IS NOT NULL"
},
{
"keyName": "IDX_cart_line_item_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -703,7 +754,9 @@
},
{
"keyName": "cart_line_item_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
@@ -713,9 +766,13 @@
"foreignKeys": {
"cart_line_item_cart_id_foreign": {
"constraintName": "cart_line_item_cart_id_foreign",
"columnNames": ["cart_id"],
"columnNames": [
"cart_id"
],
"localTableName": "public.cart_line_item",
"referencedColumnNames": ["id"],
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart",
"updateRule": "cascade"
}
@@ -842,7 +899,9 @@
"indexes": [
{
"keyName": "IDX_adjustment_item_id",
"columnNames": ["item_id"],
"columnNames": [
"item_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -850,15 +909,19 @@
},
{
"keyName": "IDX_line_item_adjustment_promotion_id",
"columnNames": ["promotion_id"],
"columnNames": [
"promotion_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_adjustment_promotion_id\" ON \"cart_line_item_adjustment\" (promotion_id) WHERE deleted_at IS NULL and promotion_id IS NOT NULL"
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_adjustment_promotion_id\" ON \"cart_line_item_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL"
},
{
"keyName": "IDX_cart_line_item_adjustment_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -866,7 +929,9 @@
},
{
"keyName": "cart_line_item_adjustment_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
@@ -879,7 +944,20 @@
"definition": "check ((amount >= 0))"
}
],
"foreignKeys": {}
"foreignKeys": {
"cart_line_item_adjustment_item_id_foreign": {
"constraintName": "cart_line_item_adjustment_item_id_foreign",
"columnNames": [
"item_id"
],
"localTableName": "public.cart_line_item_adjustment",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_line_item",
"updateRule": "cascade"
}
}
},
{
"columns": {
@@ -993,7 +1071,9 @@
"indexes": [
{
"keyName": "IDX_tax_line_item_id",
"columnNames": ["item_id"],
"columnNames": [
"item_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1001,7 +1081,9 @@
},
{
"keyName": "IDX_line_item_tax_line_tax_rate_id",
"columnNames": ["tax_rate_id"],
"columnNames": [
"tax_rate_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1009,7 +1091,9 @@
},
{
"keyName": "IDX_cart_line_item_tax_line_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1017,14 +1101,29 @@
},
{
"keyName": "cart_line_item_tax_line_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {
"cart_line_item_tax_line_item_id_foreign": {
"constraintName": "cart_line_item_tax_line_item_id_foreign",
"columnNames": [
"item_id"
],
"localTableName": "public.cart_line_item_tax_line",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_line_item",
"updateRule": "cascade"
}
}
},
{
"columns": {
@@ -1157,7 +1256,9 @@
"indexes": [
{
"keyName": "IDX_shipping_method_cart_id",
"columnNames": ["cart_id"],
"columnNames": [
"cart_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1165,7 +1266,9 @@
},
{
"keyName": "IDX_shipping_method_option_id",
"columnNames": ["shipping_option_id"],
"columnNames": [
"shipping_option_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1173,7 +1276,9 @@
},
{
"keyName": "IDX_cart_shipping_method_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1181,7 +1286,9 @@
},
{
"keyName": "cart_shipping_method_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
@@ -1197,9 +1304,13 @@
"foreignKeys": {
"cart_shipping_method_cart_id_foreign": {
"constraintName": "cart_shipping_method_cart_id_foreign",
"columnNames": ["cart_id"],
"columnNames": [
"cart_id"
],
"localTableName": "public.cart_shipping_method",
"referencedColumnNames": ["id"],
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart",
"updateRule": "cascade"
}
@@ -1261,6 +1372,15 @@
"nullable": true,
"mappedType": "text"
},
"metadata": {
"name": "metadata",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
@@ -1292,15 +1412,6 @@
"nullable": false,
"mappedType": "text"
},
"metadata": {
"name": "metadata",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"promotion_id": {
"name": "promotion_id",
"type": "text",
@@ -1326,7 +1437,9 @@
"indexes": [
{
"keyName": "IDX_adjustment_shipping_method_id",
"columnNames": ["shipping_method_id"],
"columnNames": [
"shipping_method_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1334,15 +1447,19 @@
},
{
"keyName": "IDX_shipping_method_adjustment_promotion_id",
"columnNames": ["promotion_id"],
"columnNames": [
"promotion_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_adjustment_promotion_id\" ON \"cart_shipping_method_adjustment\" (promotion_id) WHERE deleted_at IS NULL and promotion_id IS NOT NULL"
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_adjustment_promotion_id\" ON \"cart_shipping_method_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL"
},
{
"keyName": "IDX_cart_shipping_method_adjustment_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1350,14 +1467,29 @@
},
{
"keyName": "cart_shipping_method_adjustment_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {
"cart_shipping_method_adjustment_shipping_method_id_foreign": {
"constraintName": "cart_shipping_method_adjustment_shipping_method_id_foreign",
"columnNames": [
"shipping_method_id"
],
"localTableName": "public.cart_shipping_method_adjustment",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_shipping_method",
"updateRule": "cascade"
}
}
},
{
"columns": {
@@ -1471,7 +1603,9 @@
"indexes": [
{
"keyName": "IDX_tax_line_shipping_method_id",
"columnNames": ["shipping_method_id"],
"columnNames": [
"shipping_method_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1479,7 +1613,9 @@
},
{
"keyName": "IDX_shipping_method_tax_line_tax_rate_id",
"columnNames": ["tax_rate_id"],
"columnNames": [
"tax_rate_id"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1487,7 +1623,9 @@
},
{
"keyName": "IDX_cart_shipping_method_tax_line_deleted_at",
"columnNames": ["deleted_at"],
"columnNames": [
"deleted_at"
],
"composite": false,
"primary": false,
"unique": false,
@@ -1495,14 +1633,29 @@
},
{
"keyName": "cart_shipping_method_tax_line_pkey",
"columnNames": ["id"],
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {
"cart_shipping_method_tax_line_shipping_method_id_foreign": {
"constraintName": "cart_shipping_method_tax_line_shipping_method_id_foreign",
"columnNames": [
"shipping_method_id"
],
"localTableName": "public.cart_shipping_method_tax_line",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.cart_shipping_method",
"updateRule": "cascade"
}
}
}
]
}

View File

@@ -0,0 +1,15 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20241106085918 extends Migration {
async up(): Promise<void> {
this.addSql('alter table if exists "cart_line_item" add column if not exists "product_type_id" text null;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_line_item_product_type_id" ON "cart_line_item" (product_type_id) WHERE deleted_at IS NULL AND product_type_id IS NOT NULL;');
}
async down(): Promise<void> {
this.addSql('drop index if exists "IDX_line_item_product_type_id";');
this.addSql('alter table if exists "cart_line_item" drop column if exists "product_type_id";');
}
}

View File

@@ -53,6 +53,13 @@ const ProductIdIndex = createPsqlIndexStatementHelper({
where: "deleted_at IS NULL AND product_id IS NOT NULL",
}).MikroORMIndex
const ProductTypeIdIndex = createPsqlIndexStatementHelper({
name: "IDX_line_item_product_type_id",
tableName: "cart_line_item",
columns: "product_type_id",
where: "deleted_at IS NULL AND product_type_id IS NOT NULL",
}).MikroORMIndex
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "cart_line_item",
columns: "deleted_at",
@@ -111,6 +118,10 @@ export default class LineItem {
@Property({ columnType: "text", nullable: true })
product_type: string | null = null
@ProductTypeIdIndex()
@Property({ columnType: "text", nullable: true })
product_type_id: string | null = null
@Property({ columnType: "text", nullable: true })
product_collection: string | null = null

View File

@@ -21,6 +21,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_id: "variant1",

View File

@@ -21,6 +21,7 @@ moduleIntegrationTestRunner({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_id: "variant1",

View File

@@ -27,6 +27,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_id: "variant1",
@@ -284,6 +285,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_sku: "SKU1",

View File

@@ -21,6 +21,7 @@ moduleIntegrationTestRunner({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_id: "variant1",

View File

@@ -21,6 +21,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
product_description: "Description 1",
product_subtitle: "Product Subtitle 1",
product_type: "Type 1",
product_type_id: "type_1",
product_collection: "Collection 1",
product_handle: "handle1",
variant_id: "variant1",

View File

@@ -579,6 +579,15 @@
"nullable": true,
"mappedType": "text"
},
"product_type_id": {
"name": "product_type_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"product_collection": {
"name": "product_collection",
"type": "text",
@@ -771,6 +780,16 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_order_line_item_product_id\" ON \"order_line_item\" (product_id) WHERE deleted_at IS NOT NULL"
},
{
"keyName": "IDX_line_item_product_type_id",
"columnNames": [
"product_type_id"
],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_product_type_id\" ON \"order_line_item\" (product_type_id) WHERE deleted_at IS NOT NULL AND product_type_id IS NOT NULL"
},
{
"keyName": "IDX_order_line_item_deleted_at",
"columnNames": [

View File

@@ -0,0 +1,15 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20241106085223 extends Migration {
async up(): Promise<void> {
this.addSql('alter table if exists "order_line_item" add column if not exists "product_type_id" text null;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_line_item_product_type_id" ON "order_line_item" (product_type_id) WHERE deleted_at IS NOT NULL AND product_type_id IS NOT NULL;');
}
async down(): Promise<void> {
this.addSql('drop index if exists "IDX_line_item_product_type_id";');
this.addSql('alter table if exists "order_line_item" drop column if exists "product_type_id";');
}
}

View File

@@ -36,6 +36,13 @@ const ProductIdIndex = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL",
})
const ProductTypeIdIndex = createPsqlIndexStatementHelper({
name: "IDX_line_item_product_type_id",
tableName: "order_line_item",
columns: "product_type_id",
where: "deleted_at IS NOT NULL AND product_type_id IS NOT NULL",
}).MikroORMIndex
const VariantIdIndex = createPsqlIndexStatementHelper({
tableName: "order_line_item",
columns: "variant_id",
@@ -85,6 +92,10 @@ export default class OrderLineItem {
@Property({ columnType: "text", nullable: true })
product_type: string | null = null
@ProductTypeIdIndex()
@Property({ columnType: "text", nullable: true })
product_type_id: string | null = null
@Property({ columnType: "text", nullable: true })
product_collection: string | null = null

View File

@@ -161,6 +161,7 @@ type OrderLineItem {
product_description: String
product_subtitle: String
product_type: String
product_type_id: String
product_collection: String
product_handle: String
variant_sku: String

View File

@@ -405,7 +405,7 @@ export default class TaxModuleService
const toReturn = await promiseAll(
items.map(async (item) => {
const regionIds = regions.map((r) => r.id)
const rateQuery = this.geTaxRateQueryForItem(item, regionIds)
const rateQuery = this.getTaxRateQueryForItem(item, regionIds)
const candidateRates = await this.taxRateService_.list(
rateQuery,
{
@@ -414,7 +414,7 @@ export default class TaxModuleService
sharedContext
)
const applicableRates = await this.geTaxRatesForItem(
const applicableRates = await this.getTaxRatesForItem(
item,
candidateRates
)
@@ -566,7 +566,7 @@ export default class TaxModuleService
}
}
private async geTaxRatesForItem(
private async getTaxRatesForItem(
item: TaxTypes.TaxableItemDTO | TaxTypes.TaxableShippingDTO,
rates: TaxRate[]
): Promise<TaxRate[]> {
@@ -598,7 +598,7 @@ export default class TaxModuleService
return ratesToReturn
}
private geTaxRateQueryForItem(
private getTaxRateQueryForItem(
item: TaxTypes.TaxableItemDTO | TaxTypes.TaxableShippingDTO,
regionIds: string[]
) {
@@ -698,6 +698,7 @@ export default class TaxModuleService
} else if (isDefault && !isProvince) {
decoratedRate.priority_score = 6
}
return decoratedRate
}) as (TaxRate & {
priority_score: number

View File

@@ -5442,6 +5442,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@medusajs/cart@workspace:packages/modules/cart"
dependencies:
"@medusajs/framework": ^2.0.1
"@medusajs/test-utils": ^2.0.1
"@mikro-orm/cli": 5.9.7
"@mikro-orm/core": 5.9.7