From db6969578fef46738ce22c1295c101d34235e66d Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Tue, 9 Jul 2024 11:10:42 +0200 Subject: [PATCH] feat: Plug tax inclusivity in cart and order workflows (#8013) --- .../cart/store/cart.workflows.spec.ts | 21 ++++++++++++++++ .../__tests__/cart/store/carts.spec.ts | 24 +++++++++++++++++++ .../__tests__/order/draft-order.spec.ts | 13 +++++++++- .../order/workflows/create-return.spec.ts | 16 ++++++++++++- .../src/definition/cart/utils/fields.ts | 1 + .../cart/utils/prepare-line-item-data.ts | 3 +++ .../workflows/add-shipping-method-to-cart.ts | 10 +++++++- .../definition/cart/workflows/add-to-cart.ts | 2 ++ .../definition/cart/workflows/create-carts.ts | 2 ++ .../list-shipping-options-for-cart.ts | 3 +++ .../workflows/update-line-item-in-cart.ts | 2 ++ .../utils/prepare-custom-line-item-data.ts | 5 +++- .../src/order/workflows/create-orders.ts | 4 ++++ .../src/order/workflows/create-return.ts | 5 ++++ .../core/types/src/common/with-calculated.ts | 5 +++- packages/core/types/src/order/mutations.ts | 1 + packages/core/utils/src/totals/cart/index.ts | 4 ++-- .../src/api/store/carts/query-config.ts | 2 ++ 18 files changed, 116 insertions(+), 7 deletions(-) diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index 62aa79aa7b..8f99661ee0 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -143,6 +143,12 @@ medusaIntegrationTestRunner({ ], }) + await pricingModule.createPricePreferences({ + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }) + await remoteLink.create([ { [Modules.PRODUCT]: { @@ -200,6 +206,7 @@ medusaIntegrationTestRunner({ expect.objectContaining({ quantity: 1, unit_price: 3000, + is_tax_inclusive: true, }), ]), }) @@ -596,6 +603,12 @@ medusaIntegrationTestRunner({ ], }) + await pricingModule.createPricePreferences({ + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }) + await remoteLink.create([ { [Modules.PRODUCT]: { @@ -650,6 +663,7 @@ medusaIntegrationTestRunner({ items: expect.arrayContaining([ expect.objectContaining({ unit_price: 3000, + is_tax_inclusive: true, quantity: 1, title: "Test variant", }), @@ -1458,6 +1472,12 @@ medusaIntegrationTestRunner({ priceSet = await pricingModule.createPriceSets({ prices: [{ amount: 3000, currency_code: "usd" }], }) + + await pricingModule.createPricePreferences({ + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }) }) it("should add shipping method to cart", async () => { @@ -1526,6 +1546,7 @@ medusaIntegrationTestRunner({ shipping_methods: [ expect.objectContaining({ amount: 3000, + is_tax_inclusive: true, name: "Test shipping option", }), ], diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 6e7a5e5505..b35b9c0407 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -1280,6 +1280,16 @@ medusaIntegrationTestRunner({ }, ]) + await api.post( + "/admin/price-preferences", + { + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }, + adminHeaders + ) + const cart = await cartModule.createCarts({ currency_code: "usd", customer_id: customer.id, @@ -1376,6 +1386,7 @@ medusaIntegrationTestRunner({ items: expect.arrayContaining([ expect.objectContaining({ unit_price: 3000, + is_tax_inclusive: true, quantity: 1, title: "Test variant", tax_lines: [ @@ -1395,6 +1406,7 @@ medusaIntegrationTestRunner({ }), expect.objectContaining({ unit_price: 2000, + is_tax_inclusive: false, quantity: 1, title: "Test item", tax_lines: [], @@ -1422,6 +1434,7 @@ medusaIntegrationTestRunner({ items: expect.arrayContaining([ expect.objectContaining({ unit_price: 2000, + is_tax_inclusive: true, quantity: 1, title: "Test variant default tax", tax_lines: [ @@ -1734,6 +1747,16 @@ medusaIntegrationTestRunner({ ], }) + await api.post( + "/admin/price-preferences", + { + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }, + adminHeaders + ) + const priceSet = await pricingModule.createPriceSets({ prices: [{ amount: 3000, currency_code: "usd" }], }) @@ -1783,6 +1806,7 @@ medusaIntegrationTestRunner({ { shipping_option_id: shippingOption.id, amount: 3000, + is_tax_inclusive: true, id: expect.any(String), tax_lines: [], adjustments: [], diff --git a/integration-tests/modules/__tests__/order/draft-order.spec.ts b/integration-tests/modules/__tests__/order/draft-order.spec.ts index 74a395fa4a..09da466b8a 100644 --- a/integration-tests/modules/__tests__/order/draft-order.spec.ts +++ b/integration-tests/modules/__tests__/order/draft-order.spec.ts @@ -132,6 +132,16 @@ medusaIntegrationTestRunner({ }, ]) + await api.post( + "/admin/price-preferences", + { + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }, + adminHeaders + ) + await remoteLink.create([ { [Modules.PRODUCT]: { @@ -252,7 +262,7 @@ medusaIntegrationTestRunner({ variant_option_values: null, requires_shipping: true, is_discountable: true, - is_tax_inclusive: false, + is_tax_inclusive: true, raw_compare_at_unit_price: null, raw_unit_price: expect.objectContaining({ value: "3000", @@ -306,6 +316,7 @@ medusaIntegrationTestRunner({ note: "reduced price", }, unit_price: 200, + is_tax_inclusive: true, quantity: 1, raw_quantity: expect.objectContaining({ value: "1", diff --git a/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts index bdb55806d9..ef74e3e8b6 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts @@ -40,6 +40,7 @@ async function prepareDataFixtures({ container }) { ModuleRegistrationName.STOCK_LOCATION ) const productModule = container.resolve(ModuleRegistrationName.PRODUCT) + const pricingModule = container.resolve(ModuleRegistrationName.PRICING) const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) const shippingProfile = await fulfillmentService.createShippingProfiles({ @@ -145,6 +146,19 @@ async function prepareDataFixtures({ container }) { }, ]) + await pricingModule.createPricePreferences([ + { + attribute: "currency_code", + value: "usd", + is_tax_inclusive: true, + }, + { + attribute: "region_id", + value: region.id, + is_tax_inclusive: true, + }, + ]) + const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = { name: "Return shipping option", @@ -471,7 +485,7 @@ medusaIntegrationTestRunner({ id: expect.any(String), name: shippingOption.name, description: null, - is_tax_inclusive: false, + is_tax_inclusive: true, shipping_option_id: shippingOption.id, amount: 10, order_id: expect.any(String), diff --git a/packages/core/core-flows/src/definition/cart/utils/fields.ts b/packages/core/core-flows/src/definition/cart/utils/fields.ts index 4b66ff3729..a57c0dee90 100644 --- a/packages/core/core-flows/src/definition/cart/utils/fields.ts +++ b/packages/core/core-flows/src/definition/cart/utils/fields.ts @@ -105,6 +105,7 @@ export const productVariantsFields = [ "product.collection", "product.handle", "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", "inventory_items.inventory_item_id", "inventory_items.required_quantity", "inventory_items.inventory.location_levels.stock_locations.id", diff --git a/packages/core/core-flows/src/definition/cart/utils/prepare-line-item-data.ts b/packages/core/core-flows/src/definition/cart/utils/prepare-line-item-data.ts index 622954699c..a809779dcb 100644 --- a/packages/core/core-flows/src/definition/cart/utils/prepare-line-item-data.ts +++ b/packages/core/core-flows/src/definition/cart/utils/prepare-line-item-data.ts @@ -9,6 +9,7 @@ interface Input { quantity: BigNumberInput metadata?: Record unitPrice: BigNumberInput + isTaxInclusive?: boolean variant: ProductVariantDTO taxLines?: CreateOrderLineItemTaxLineDTO[] adjustments?: CreateOrderAdjustmentDTO[] @@ -19,6 +20,7 @@ export function prepareLineItemData(data: Input) { const { variant, unitPrice, + isTaxInclusive, quantity, metadata, cartId, @@ -51,6 +53,7 @@ export function prepareLineItemData(data: Input) { variant_title: variant.title, unit_price: unitPrice, + is_tax_inclusive: !!isTaxInclusive, metadata, } diff --git a/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts index 8f7445962c..e8435e89d5 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts @@ -47,7 +47,12 @@ export const addShippingMethodToWorkflow = createWorkflow( const shippingOptions = useRemoteQueryStep({ entry_point: "shipping_option", - fields: ["id", "name", "calculated_price.calculated_amount"], + fields: [ + "id", + "name", + "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", + ], variables: { id: optionIds, calculated_price: { @@ -67,6 +72,9 @@ export const addShippingMethodToWorkflow = createWorkflow( return { shipping_option_id: shippingOption.id, amount: shippingOption.calculated_price.calculated_amount, + is_tax_inclusive: + !!shippingOption.calculated_price + .is_calculated_price_tax_inclusive, data: option.data ?? {}, name: shippingOption.name, cart_id: data.input.cart_id, diff --git a/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts index d04e7b66d8..d36d59991c 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts @@ -75,6 +75,8 @@ export const addToCartWorkflow = createWorkflow( return prepareLineItemData({ variant: variant, unitPrice: variant.calculated_price.calculated_amount, + isTaxInclusive: + variant.calculated_price.is_calculated_price_tax_inclusive, quantity: item.quantity, metadata: item?.metadata ?? {}, cartId: data.input.cart.id, diff --git a/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts b/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts index 90e128f1d1..6e7cacab24 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts @@ -122,6 +122,8 @@ export const createCartWorkflow = createWorkflow( return prepareLineItemData({ variant: variant, unitPrice: data.priceSets[item.variant_id].calculated_amount, + isTaxInclusive: + data.priceSets[item.variant_id].is_calculated_price_tax_inclusive, quantity: item.quantity, metadata: item?.metadata ?? {}, }) diff --git a/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts index 326b895787..cf6e71f800 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts @@ -42,6 +42,7 @@ export const listShippingOptionsForCartWorkflow = createWorkflow( "stock_locations.fulfillment_sets.service_zones.shipping_options.provider.is_enabled", "stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price.calculated_amount", + "stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price.is_calculated_price_tax_inclusive", ], variables: { id: input.sales_channel_id, @@ -88,6 +89,8 @@ export const listShippingOptionsForCartWorkflow = createWorkflow( return { ...options, amount: calculated_price?.calculated_amount, + is_tax_inclusive: + !!calculated_price?.is_calculated_price_tax_inclusive, } } ) diff --git a/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts index 826f9a7b54..b955208698 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts @@ -71,6 +71,8 @@ export const updateLineItemInCartWorkflow = createWorkflow( data: { ...data.input.update, unit_price: variant.calculated_price.calculated_amount, + is_tax_inclusive: + !!variant.calculated_price.is_calculated_price_tax_inclusive, }, selector: { id: item.id, diff --git a/packages/core/core-flows/src/order/utils/prepare-custom-line-item-data.ts b/packages/core/core-flows/src/order/utils/prepare-custom-line-item-data.ts index bd92d2e3fa..869281efc2 100644 --- a/packages/core/core-flows/src/order/utils/prepare-custom-line-item-data.ts +++ b/packages/core/core-flows/src/order/utils/prepare-custom-line-item-data.ts @@ -4,6 +4,7 @@ interface Input { quantity: BigNumberInput metadata?: Record unitPrice: BigNumberInput + isTaxInclusive?: boolean variant: { title: string sku?: string @@ -18,11 +19,12 @@ interface Output { variant_barcode?: string variant_title?: string unit_price: BigNumberInput + is_tax_inclusive: boolean metadata?: Record } export function prepareCustomLineItemData(data: Input): Output { - const { variant, unitPrice, quantity, metadata } = data + const { variant, unitPrice, isTaxInclusive, quantity, metadata } = data const lineItem: any = { quantity, @@ -32,6 +34,7 @@ export function prepareCustomLineItemData(data: Input): Output { variant_title: variant.title, unit_price: unitPrice, + is_tax_inclusive: !!isTaxInclusive, metadata, } diff --git a/packages/core/core-flows/src/order/workflows/create-orders.ts b/packages/core/core-flows/src/order/workflows/create-orders.ts index c478ffd5a2..3a81a8ed4d 100644 --- a/packages/core/core-flows/src/order/workflows/create-orders.ts +++ b/packages/core/core-flows/src/order/workflows/create-orders.ts @@ -28,6 +28,7 @@ function prepareLineItems(data) { ...item, }, unitPrice: MathBN.max(0, item.unit_price), + isTaxInclusive: item.is_tax_inclusive, quantity: item.quantity as number, metadata: item?.metadata ?? {}, }) @@ -39,6 +40,9 @@ function prepareLineItems(data) { 0, item.unit_price ?? data.priceSets[item.variant_id!]?.calculated_amount ), + isTaxInclusive: + item.is_tax_inclusive ?? + data.priceSets[item.variant_id!]?.is_calculated_price_tax_inclusive, quantity: item.quantity as number, metadata: item?.metadata ?? {}, taxLines: item.tax_lines || [], diff --git a/packages/core/core-flows/src/order/workflows/create-return.ts b/packages/core/core-flows/src/order/workflows/create-return.ts index 5495eed526..b2675b1f5d 100644 --- a/packages/core/core-flows/src/order/workflows/create-return.ts +++ b/packages/core/core-flows/src/order/workflows/create-return.ts @@ -109,6 +109,7 @@ function prepareShippingMethodData({ order_id: orderId, shipping_option_id: returnShippingOption.id, amount: 0, + is_tax_inclusive: false, data: {}, // Computed later in the flow tax_lines: [], @@ -122,6 +123,9 @@ function prepareShippingMethodData({ // TODO: retrieve calculated price and assign to amount } else { obj.amount = returnShippingOption.calculated_price.calculated_amount + obj.is_tax_inclusive = + !!returnShippingOption.calculated_price + .is_calculated_price_tax_inclusive } } @@ -317,6 +321,7 @@ export const createReturnOrderWorkflow = createWorkflow( "name", "provider_id", "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", "service_zone.fulfillment_set.location.id", ], variables: returnShippingOptionsVariables, diff --git a/packages/core/types/src/common/with-calculated.ts b/packages/core/types/src/common/with-calculated.ts index 374fc116a1..80daa3306d 100644 --- a/packages/core/types/src/common/with-calculated.ts +++ b/packages/core/types/src/common/with-calculated.ts @@ -1,3 +1,6 @@ export interface WithCalculatedPrice { - calculated_price: { calculated_amount: number } + calculated_price: { + calculated_amount: number + is_calculated_price_tax_inclusive?: boolean + } } diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index 3d6231fdab..681d71f052 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -212,6 +212,7 @@ export interface CreateOrderShippingMethodDTO { exchange_id?: string version?: number amount: BigNumberInput + is_tax_inclusive?: boolean shipping_option_id?: string data?: Record tax_lines?: CreateOrderTaxLineDTO[] diff --git a/packages/core/utils/src/totals/cart/index.ts b/packages/core/utils/src/totals/cart/index.ts index d37cbf6276..b26c73c63b 100644 --- a/packages/core/utils/src/totals/cart/index.ts +++ b/packages/core/utils/src/totals/cart/index.ts @@ -16,20 +16,20 @@ export interface DecorateCartLikeInputDTO { items?: { id?: string unit_price: BigNumberInput + is_tax_inclusive?: boolean quantity: BigNumberInput adjustments?: { amount: BigNumberInput }[] tax_lines?: { rate: BigNumberInput - is_tax_inclusive?: boolean }[] }[] shipping_methods?: { id?: string amount: BigNumberInput + is_tax_inclusive?: boolean adjustments?: { amount: BigNumberInput }[] tax_lines?: { rate: BigNumberInput - is_tax_inclusive?: boolean }[] }[] region?: { diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index 59a18dfad9..6b02bc8b97 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -51,6 +51,7 @@ export const defaultStoreCartFields = [ "items.title", "items.quantity", "items.unit_price", + "items.is_tax_inclusive", "items.tax_lines.id", "items.tax_lines.description", "items.tax_lines.code", @@ -69,6 +70,7 @@ export const defaultStoreCartFields = [ "shipping_methods.tax_lines.rate", "shipping_methods.tax_lines.provider_id", "shipping_methods.amount", + "shipping_methods.is_tax_inclusive", "shipping_methods.adjustments.id", "shipping_methods.adjustments.code", "shipping_methods.adjustments.amount",