From 27cbfee9fc4b7abc1e8b58a53215b465683fdadb Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Wed, 6 Nov 2024 14:19:18 +0100 Subject: [PATCH 1/2] fix: Make product type tax override work --- .../__tests__/cart/store/carts.spec.ts | 85 ++++- .../__tests__/order/draft-order.spec.ts | 3 +- .../modules/__tests__/order/order.spec.ts | 3 +- .../src/cart/steps/get-item-tax-lines.ts | 142 -------- .../core/core-flows/src/cart/steps/index.ts | 2 +- .../src/cart/utils/prepare-line-item-data.ts | 1 + .../src/cart/workflows/update-tax-lines.ts | 6 +- .../core/core-flows/src/order/steps/index.ts | 2 +- .../src/order/workflows/update-tax-lines.ts | 10 +- .../steps/get-item-tax-lines.ts | 58 ++-- packages/core/types/src/cart/common.ts | 5 + packages/core/types/src/cart/mutations.ts | 5 + packages/core/types/src/http/order/common.ts | 6 +- packages/core/types/src/order/common.ts | 5 + packages/core/types/src/tax/common.ts | 25 -- .../src/api/store/carts/[id]/taxes/route.ts | 2 +- .../src/api/store/carts/query-config.ts | 1 + .../medusa/src/api/store/products/helpers.ts | 16 +- .../services/cart-module/index.spec.ts | 4 +- packages/modules/cart/package.json | 1 + .../src/migrations/.snapshot-medusa-cart.json | 317 +++++++++++++----- .../src/migrations/Migration20241106085918.ts | 15 + packages/modules/cart/src/models/line-item.ts | 11 + .../__tests__/create-order.ts | 1 + .../__tests__/order-claim.ts | 1 + .../integration-tests/__tests__/order-edit.ts | 2 + .../__tests__/order-exchange.ts | 1 + .../__tests__/order-return.ts | 1 + .../migrations/.snapshot-medusa-order.json | 19 ++ .../src/migrations/Migration20241106085223.ts | 15 + .../modules/order/src/models/line-item.ts | 11 + packages/modules/order/src/schema/index.ts | 1 + .../tax/src/services/tax-module-service.ts | 9 +- yarn.lock | 1 + 34 files changed, 478 insertions(+), 309 deletions(-) delete mode 100644 packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts rename packages/core/core-flows/src/{order => tax}/steps/get-item-tax-lines.ts (68%) create mode 100644 packages/modules/cart/src/migrations/Migration20241106085918.ts create mode 100644 packages/modules/order/src/migrations/Migration20241106085223.ts diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 8383e34876d86..57779912034bf 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -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) diff --git a/integration-tests/modules/__tests__/order/draft-order.spec.ts b/integration-tests/modules/__tests__/order/draft-order.spec.ts index b57e3812b883f..b097ed54c0cc2 100644 --- a/integration-tests/modules/__tests__/order/draft-order.spec.ts +++ b/integration-tests/modules/__tests__/order/draft-order.spec.ts @@ -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, diff --git a/integration-tests/modules/__tests__/order/order.spec.ts b/integration-tests/modules/__tests__/order/order.spec.ts index 24cac5f8c95e5..8e0bc18afc55a 100644 --- a/integration-tests/modules/__tests__/order/order.spec.ts +++ b/integration-tests/modules/__tests__/order/order.spec.ts @@ -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, diff --git a/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts deleted file mode 100644 index dca1e2f8b2013..0000000000000 --- a/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts +++ /dev/null @@ -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(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, - }) - } -) diff --git a/packages/core/core-flows/src/cart/steps/index.ts b/packages/core/core-flows/src/cart/steps/index.ts index 7673cf56d8dc1..5f9cddd02f526 100644 --- a/packages/core/core-flows/src/cart/steps/index.ts +++ b/packages/core/core-flows/src/cart/steps/index.ts @@ -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" + diff --git a/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts b/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts index e054422f2d629..433e36a8db790 100644 --- a/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts +++ b/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts @@ -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, diff --git a/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts b/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts index 5ab9d6bcc857a..dd02e0c1f314b 100644 --- a/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts +++ b/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts @@ -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, diff --git a/packages/core/core-flows/src/order/steps/index.ts b/packages/core/core-flows/src/order/steps/index.ts index 130835a79738b..b9e6957ffb1c6 100644 --- a/packages/core/core-flows/src/order/steps/index.ts +++ b/packages/core/core-flows/src/order/steps/index.ts @@ -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" + diff --git a/packages/core/core-flows/src/order/workflows/update-tax-lines.ts b/packages/core/core-flows/src/order/workflows/update-tax-lines.ts index 57506c33b984d..e91ebb1ddee2f 100644 --- a/packages/core/core-flows/src/order/workflows/update-tax-lines.ts +++ b/packages/core/core-flows/src/order/workflows/update-tax-lines.ts @@ -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, diff --git a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts similarity index 68% rename from packages/core/core-flows/src/order/steps/get-item-tax-lines.ts rename to packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts index 7e9421fade240..b500bd7ae2043 100644 --- a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts +++ b/packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts @@ -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(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[] } diff --git a/packages/core/types/src/cart/common.ts b/packages/core/types/src/cart/common.ts index 9bca5845177c6..d066c491468b4 100644 --- a/packages/core/types/src/cart/common.ts +++ b/packages/core/types/src/cart/common.ts @@ -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. */ diff --git a/packages/core/types/src/cart/mutations.ts b/packages/core/types/src/cart/mutations.ts index d484ab4686b8c..a8e13860c8ea5 100644 --- a/packages/core/types/src/cart/mutations.ts +++ b/packages/core/types/src/cart/mutations.ts @@ -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. */ diff --git a/packages/core/types/src/http/order/common.ts b/packages/core/types/src/http/order/common.ts index 020b85c78132c..de2af2f31f5c0 100644 --- a/packages/core/types/src/http/order/common.ts +++ b/packages/core/types/src/http/order/common.ts @@ -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 /** diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index a4c27f7c59dae..3b41fa5d48a55 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -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. */ diff --git a/packages/core/types/src/tax/common.ts b/packages/core/types/src/tax/common.ts index e4309ab1bb70d..219013babe0f5 100644 --- a/packages/core/types/src/tax/common.ts +++ b/packages/core/types/src/tax/common.ts @@ -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. */ diff --git a/packages/medusa/src/api/store/carts/[id]/taxes/route.ts b/packages/medusa/src/api/store/carts/[id]/taxes/route.ts index 1f8f198a50bf0..1c01867de7f5e 100644 --- a/packages/medusa/src/api/store/carts/[id]/taxes/route.ts +++ b/packages/medusa/src/api/store/carts/[id]/taxes/route.ts @@ -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 ( diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index 48fc1bb2bb92c..d236c198ecbb5 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -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", diff --git a/packages/medusa/src/api/store/products/helpers.ts b/packages/medusa/src/api/store/products/helpers.ts index 528361a7597fa..cc73a4df77633 100644 --- a/packages/medusa/src/api/store/products/helpers.ts +++ b/packages/medusa/src/api/store/products/helpers.ts @@ -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, diff --git a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts index 5c6f58ad31048..23d05ee4d7adc 100644 --- a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts +++ b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts @@ -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({ 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({ product_description: null, product_subtitle: null, product_type: null, + product_type_id: null, product_collection: null, product_handle: null, variant_sku: null, diff --git a/packages/modules/cart/package.json b/packages/modules/cart/package.json index be01fd227845a..918903e584265 100644 --- a/packages/modules/cart/package.json +++ b/packages/modules/cart/package.json @@ -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", diff --git a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json index a1b27ba076215..a48b7e87a0894 100644 --- a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json +++ b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json @@ -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" + } + } } ] } diff --git a/packages/modules/cart/src/migrations/Migration20241106085918.ts b/packages/modules/cart/src/migrations/Migration20241106085918.ts new file mode 100644 index 0000000000000..836c5adb2eed2 --- /dev/null +++ b/packages/modules/cart/src/migrations/Migration20241106085918.ts @@ -0,0 +1,15 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241106085918 extends Migration { + + async up(): Promise { + 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 { + 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";'); + } + +} diff --git a/packages/modules/cart/src/models/line-item.ts b/packages/modules/cart/src/models/line-item.ts index 51656320004b8..86d9792a0c391 100644 --- a/packages/modules/cart/src/models/line-item.ts +++ b/packages/modules/cart/src/models/line-item.ts @@ -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 diff --git a/packages/modules/order/integration-tests/__tests__/create-order.ts b/packages/modules/order/integration-tests/__tests__/create-order.ts index 1d268b16b4cb5..1490c5097377d 100644 --- a/packages/modules/order/integration-tests/__tests__/create-order.ts +++ b/packages/modules/order/integration-tests/__tests__/create-order.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-claim.ts b/packages/modules/order/integration-tests/__tests__/order-claim.ts index 8a59866636d9a..9a5badaa58efd 100644 --- a/packages/modules/order/integration-tests/__tests__/order-claim.ts +++ b/packages/modules/order/integration-tests/__tests__/order-claim.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-edit.ts b/packages/modules/order/integration-tests/__tests__/order-edit.ts index 51fa21447b88e..f4522b3c0588d 100644 --- a/packages/modules/order/integration-tests/__tests__/order-edit.ts +++ b/packages/modules/order/integration-tests/__tests__/order-edit.ts @@ -27,6 +27,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", @@ -284,6 +285,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_sku: "SKU1", diff --git a/packages/modules/order/integration-tests/__tests__/order-exchange.ts b/packages/modules/order/integration-tests/__tests__/order-exchange.ts index da907a02c5e93..be846138a5d47 100644 --- a/packages/modules/order/integration-tests/__tests__/order-exchange.ts +++ b/packages/modules/order/integration-tests/__tests__/order-exchange.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-return.ts b/packages/modules/order/integration-tests/__tests__/order-return.ts index 4a7006169459e..8d8ffbac083b2 100644 --- a/packages/modules/order/integration-tests/__tests__/order-return.ts +++ b/packages/modules/order/integration-tests/__tests__/order-return.ts @@ -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", diff --git a/packages/modules/order/src/migrations/.snapshot-medusa-order.json b/packages/modules/order/src/migrations/.snapshot-medusa-order.json index 295200767b382..deb6066344986 100644 --- a/packages/modules/order/src/migrations/.snapshot-medusa-order.json +++ b/packages/modules/order/src/migrations/.snapshot-medusa-order.json @@ -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": [ diff --git a/packages/modules/order/src/migrations/Migration20241106085223.ts b/packages/modules/order/src/migrations/Migration20241106085223.ts new file mode 100644 index 0000000000000..287bf475720ee --- /dev/null +++ b/packages/modules/order/src/migrations/Migration20241106085223.ts @@ -0,0 +1,15 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241106085223 extends Migration { + + async up(): Promise { + 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 { + 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";'); + } + +} diff --git a/packages/modules/order/src/models/line-item.ts b/packages/modules/order/src/models/line-item.ts index 23008ce81b2b3..401d00f5ffbb8 100644 --- a/packages/modules/order/src/models/line-item.ts +++ b/packages/modules/order/src/models/line-item.ts @@ -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 diff --git a/packages/modules/order/src/schema/index.ts b/packages/modules/order/src/schema/index.ts index a6df4e439394e..5096dc8ed09b9 100644 --- a/packages/modules/order/src/schema/index.ts +++ b/packages/modules/order/src/schema/index.ts @@ -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 diff --git a/packages/modules/tax/src/services/tax-module-service.ts b/packages/modules/tax/src/services/tax-module-service.ts index 303e66bde01d3..497b40c5fd2aa 100644 --- a/packages/modules/tax/src/services/tax-module-service.ts +++ b/packages/modules/tax/src/services/tax-module-service.ts @@ -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 { @@ -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 diff --git a/yarn.lock b/yarn.lock index de119f89d23e9..474f62eed0640 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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 From b496170d871992690d39bf79c1cfaea3684ef709 Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Wed, 6 Nov 2024 14:19:18 +0100 Subject: [PATCH 2/2] fix: Make product type tax override work --- .../__tests__/cart/store/carts.spec.ts | 85 ++++- .../__tests__/order/draft-order.spec.ts | 3 +- .../modules/__tests__/order/order.spec.ts | 3 +- .../src/cart/steps/get-item-tax-lines.ts | 142 -------- .../core/core-flows/src/cart/steps/index.ts | 2 +- .../core/core-flows/src/cart/utils/fields.ts | 1 + .../src/cart/utils/prepare-line-item-data.ts | 1 + .../src/cart/workflows/update-tax-lines.ts | 6 +- .../core/core-flows/src/order/steps/index.ts | 2 +- .../src/order/workflows/update-tax-lines.ts | 10 +- .../steps/get-item-tax-lines.ts | 58 ++-- packages/core/types/src/cart/common.ts | 5 + packages/core/types/src/cart/mutations.ts | 5 + packages/core/types/src/http/order/common.ts | 6 +- packages/core/types/src/order/common.ts | 5 + packages/core/types/src/tax/common.ts | 25 -- .../src/api/store/carts/[id]/taxes/route.ts | 2 +- .../src/api/store/carts/query-config.ts | 1 + .../medusa/src/api/store/products/helpers.ts | 16 +- .../services/cart-module/index.spec.ts | 4 +- packages/modules/cart/package.json | 1 + .../src/migrations/.snapshot-medusa-cart.json | 317 +++++++++++++----- .../src/migrations/Migration20241106085918.ts | 15 + packages/modules/cart/src/models/line-item.ts | 11 + .../__tests__/create-order.ts | 1 + .../__tests__/order-claim.ts | 1 + .../integration-tests/__tests__/order-edit.ts | 2 + .../__tests__/order-exchange.ts | 1 + .../__tests__/order-return.ts | 1 + .../migrations/.snapshot-medusa-order.json | 19 ++ .../src/migrations/Migration20241106085223.ts | 15 + .../modules/order/src/models/line-item.ts | 11 + packages/modules/order/src/schema/index.ts | 1 + .../tax/src/services/tax-module-service.ts | 9 +- yarn.lock | 1 + 35 files changed, 479 insertions(+), 309 deletions(-) delete mode 100644 packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts rename packages/core/core-flows/src/{order => tax}/steps/get-item-tax-lines.ts (68%) create mode 100644 packages/modules/cart/src/migrations/Migration20241106085918.ts create mode 100644 packages/modules/order/src/migrations/Migration20241106085223.ts diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 8383e34876d86..57779912034bf 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -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) diff --git a/integration-tests/modules/__tests__/order/draft-order.spec.ts b/integration-tests/modules/__tests__/order/draft-order.spec.ts index b57e3812b883f..b097ed54c0cc2 100644 --- a/integration-tests/modules/__tests__/order/draft-order.spec.ts +++ b/integration-tests/modules/__tests__/order/draft-order.spec.ts @@ -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, diff --git a/integration-tests/modules/__tests__/order/order.spec.ts b/integration-tests/modules/__tests__/order/order.spec.ts index 24cac5f8c95e5..8e0bc18afc55a 100644 --- a/integration-tests/modules/__tests__/order/order.spec.ts +++ b/integration-tests/modules/__tests__/order/order.spec.ts @@ -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, diff --git a/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts deleted file mode 100644 index dca1e2f8b2013..0000000000000 --- a/packages/core/core-flows/src/cart/steps/get-item-tax-lines.ts +++ /dev/null @@ -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(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, - }) - } -) diff --git a/packages/core/core-flows/src/cart/steps/index.ts b/packages/core/core-flows/src/cart/steps/index.ts index 7673cf56d8dc1..5f9cddd02f526 100644 --- a/packages/core/core-flows/src/cart/steps/index.ts +++ b/packages/core/core-flows/src/cart/steps/index.ts @@ -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" + diff --git a/packages/core/core-flows/src/cart/utils/fields.ts b/packages/core/core-flows/src/cart/utils/fields.ts index b26f4984a8614..1de3548e7d612 100644 --- a/packages/core/core-flows/src/cart/utils/fields.ts +++ b/packages/core/core-flows/src/cart/utils/fields.ts @@ -114,6 +114,7 @@ export const productVariantsFields = [ "product.subtitle", "product.thumbnail", "product.type.value", + "product.type.id", "product.collection.title", "product.handle", "product.discountable", diff --git a/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts b/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts index e054422f2d629..433e36a8db790 100644 --- a/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts +++ b/packages/core/core-flows/src/cart/utils/prepare-line-item-data.ts @@ -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, diff --git a/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts b/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts index 5ab9d6bcc857a..dd02e0c1f314b 100644 --- a/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts +++ b/packages/core/core-flows/src/cart/workflows/update-tax-lines.ts @@ -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, diff --git a/packages/core/core-flows/src/order/steps/index.ts b/packages/core/core-flows/src/order/steps/index.ts index 130835a79738b..b9e6957ffb1c6 100644 --- a/packages/core/core-flows/src/order/steps/index.ts +++ b/packages/core/core-flows/src/order/steps/index.ts @@ -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" + diff --git a/packages/core/core-flows/src/order/workflows/update-tax-lines.ts b/packages/core/core-flows/src/order/workflows/update-tax-lines.ts index 57506c33b984d..e91ebb1ddee2f 100644 --- a/packages/core/core-flows/src/order/workflows/update-tax-lines.ts +++ b/packages/core/core-flows/src/order/workflows/update-tax-lines.ts @@ -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, diff --git a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts similarity index 68% rename from packages/core/core-flows/src/order/steps/get-item-tax-lines.ts rename to packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts index 7e9421fade240..b500bd7ae2043 100644 --- a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts +++ b/packages/core/core-flows/src/tax/steps/get-item-tax-lines.ts @@ -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(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[] } diff --git a/packages/core/types/src/cart/common.ts b/packages/core/types/src/cart/common.ts index 9bca5845177c6..d066c491468b4 100644 --- a/packages/core/types/src/cart/common.ts +++ b/packages/core/types/src/cart/common.ts @@ -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. */ diff --git a/packages/core/types/src/cart/mutations.ts b/packages/core/types/src/cart/mutations.ts index d484ab4686b8c..a8e13860c8ea5 100644 --- a/packages/core/types/src/cart/mutations.ts +++ b/packages/core/types/src/cart/mutations.ts @@ -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. */ diff --git a/packages/core/types/src/http/order/common.ts b/packages/core/types/src/http/order/common.ts index 020b85c78132c..de2af2f31f5c0 100644 --- a/packages/core/types/src/http/order/common.ts +++ b/packages/core/types/src/http/order/common.ts @@ -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 /** diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index a4c27f7c59dae..3b41fa5d48a55 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -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. */ diff --git a/packages/core/types/src/tax/common.ts b/packages/core/types/src/tax/common.ts index e4309ab1bb70d..219013babe0f5 100644 --- a/packages/core/types/src/tax/common.ts +++ b/packages/core/types/src/tax/common.ts @@ -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. */ diff --git a/packages/medusa/src/api/store/carts/[id]/taxes/route.ts b/packages/medusa/src/api/store/carts/[id]/taxes/route.ts index 1f8f198a50bf0..1c01867de7f5e 100644 --- a/packages/medusa/src/api/store/carts/[id]/taxes/route.ts +++ b/packages/medusa/src/api/store/carts/[id]/taxes/route.ts @@ -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 ( diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index 48fc1bb2bb92c..d236c198ecbb5 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -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", diff --git a/packages/medusa/src/api/store/products/helpers.ts b/packages/medusa/src/api/store/products/helpers.ts index 528361a7597fa..cc73a4df77633 100644 --- a/packages/medusa/src/api/store/products/helpers.ts +++ b/packages/medusa/src/api/store/products/helpers.ts @@ -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, diff --git a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts index 5c6f58ad31048..23d05ee4d7adc 100644 --- a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts +++ b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts @@ -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({ 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({ product_description: null, product_subtitle: null, product_type: null, + product_type_id: null, product_collection: null, product_handle: null, variant_sku: null, diff --git a/packages/modules/cart/package.json b/packages/modules/cart/package.json index be01fd227845a..918903e584265 100644 --- a/packages/modules/cart/package.json +++ b/packages/modules/cart/package.json @@ -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", diff --git a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json index a1b27ba076215..a48b7e87a0894 100644 --- a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json +++ b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json @@ -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" + } + } } ] } diff --git a/packages/modules/cart/src/migrations/Migration20241106085918.ts b/packages/modules/cart/src/migrations/Migration20241106085918.ts new file mode 100644 index 0000000000000..836c5adb2eed2 --- /dev/null +++ b/packages/modules/cart/src/migrations/Migration20241106085918.ts @@ -0,0 +1,15 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241106085918 extends Migration { + + async up(): Promise { + 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 { + 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";'); + } + +} diff --git a/packages/modules/cart/src/models/line-item.ts b/packages/modules/cart/src/models/line-item.ts index 51656320004b8..86d9792a0c391 100644 --- a/packages/modules/cart/src/models/line-item.ts +++ b/packages/modules/cart/src/models/line-item.ts @@ -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 diff --git a/packages/modules/order/integration-tests/__tests__/create-order.ts b/packages/modules/order/integration-tests/__tests__/create-order.ts index 1d268b16b4cb5..1490c5097377d 100644 --- a/packages/modules/order/integration-tests/__tests__/create-order.ts +++ b/packages/modules/order/integration-tests/__tests__/create-order.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-claim.ts b/packages/modules/order/integration-tests/__tests__/order-claim.ts index 8a59866636d9a..9a5badaa58efd 100644 --- a/packages/modules/order/integration-tests/__tests__/order-claim.ts +++ b/packages/modules/order/integration-tests/__tests__/order-claim.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-edit.ts b/packages/modules/order/integration-tests/__tests__/order-edit.ts index 51fa21447b88e..f4522b3c0588d 100644 --- a/packages/modules/order/integration-tests/__tests__/order-edit.ts +++ b/packages/modules/order/integration-tests/__tests__/order-edit.ts @@ -27,6 +27,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", @@ -284,6 +285,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_sku: "SKU1", diff --git a/packages/modules/order/integration-tests/__tests__/order-exchange.ts b/packages/modules/order/integration-tests/__tests__/order-exchange.ts index da907a02c5e93..be846138a5d47 100644 --- a/packages/modules/order/integration-tests/__tests__/order-exchange.ts +++ b/packages/modules/order/integration-tests/__tests__/order-exchange.ts @@ -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", diff --git a/packages/modules/order/integration-tests/__tests__/order-return.ts b/packages/modules/order/integration-tests/__tests__/order-return.ts index 4a7006169459e..8d8ffbac083b2 100644 --- a/packages/modules/order/integration-tests/__tests__/order-return.ts +++ b/packages/modules/order/integration-tests/__tests__/order-return.ts @@ -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", diff --git a/packages/modules/order/src/migrations/.snapshot-medusa-order.json b/packages/modules/order/src/migrations/.snapshot-medusa-order.json index 295200767b382..deb6066344986 100644 --- a/packages/modules/order/src/migrations/.snapshot-medusa-order.json +++ b/packages/modules/order/src/migrations/.snapshot-medusa-order.json @@ -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": [ diff --git a/packages/modules/order/src/migrations/Migration20241106085223.ts b/packages/modules/order/src/migrations/Migration20241106085223.ts new file mode 100644 index 0000000000000..287bf475720ee --- /dev/null +++ b/packages/modules/order/src/migrations/Migration20241106085223.ts @@ -0,0 +1,15 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241106085223 extends Migration { + + async up(): Promise { + 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 { + 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";'); + } + +} diff --git a/packages/modules/order/src/models/line-item.ts b/packages/modules/order/src/models/line-item.ts index 23008ce81b2b3..401d00f5ffbb8 100644 --- a/packages/modules/order/src/models/line-item.ts +++ b/packages/modules/order/src/models/line-item.ts @@ -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 diff --git a/packages/modules/order/src/schema/index.ts b/packages/modules/order/src/schema/index.ts index a6df4e439394e..5096dc8ed09b9 100644 --- a/packages/modules/order/src/schema/index.ts +++ b/packages/modules/order/src/schema/index.ts @@ -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 diff --git a/packages/modules/tax/src/services/tax-module-service.ts b/packages/modules/tax/src/services/tax-module-service.ts index 303e66bde01d3..497b40c5fd2aa 100644 --- a/packages/modules/tax/src/services/tax-module-service.ts +++ b/packages/modules/tax/src/services/tax-module-service.ts @@ -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 { @@ -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 diff --git a/yarn.lock b/yarn.lock index de119f89d23e9..474f62eed0640 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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