Skip to content

Commit 5ea32aa

Browse files
authored
fix(): Cart workflow price calculation for different items but same variant (#13511)
RESOLVES CORE-1204 **What** - Fix wrong price tier when multiple items are targetting the same variant - fix type import from the wrong package **Notes** If you are struggling navigating the changes, you can focus on the following files: ``` integration-tests/http/__tests__/cart/store/cart.spec.ts integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts packages/core/core-flows/src/cart/steps/get-promotion-codes-to-apply.ts packages/core/core-flows/src/cart/steps/get-variant-price-sets.ts packages/core/core-flows/src/cart/workflows/add-to-cart.ts packages/core/core-flows/src/cart/workflows/create-carts.ts packages/core/core-flows/src/cart/workflows/get-variants-and-items-with-prices.ts packages/core/core-flows/src/cart/workflows/refresh-cart-items.ts packages/core/core-flows/src/order/workflows/add-line-items.ts packages/core/core-flows/src/order/workflows/create-order.ts ```
1 parent 3960c80 commit 5ea32aa

281 files changed

Lines changed: 1862 additions & 1464 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/shiny-cups-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@medusajs/core-flows": patch
3+
---
4+
5+
fix(): Cart workflow price calculation for different items but same variant

integration-tests/http/__tests__/cart/store/cart.spec.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,156 @@ medusaIntegrationTestRunner({
285285
)
286286
})
287287

288+
it("should successfully create a cart with a line items for the same variant with different quantities and calculate prices based on the correct quantity", async () => {
289+
const productData = {
290+
title: "Medusa T-Shirt based quantity",
291+
handle: "t-shirt-with-quantity-prices",
292+
status: ProductStatus.PUBLISHED,
293+
options: [
294+
{
295+
title: "Size",
296+
values: ["S"],
297+
},
298+
],
299+
variants: [
300+
{
301+
title: "S",
302+
sku: "SHIRT-S-BLACK-w-quantity-prices",
303+
options: {
304+
Size: "S",
305+
},
306+
manage_inventory: false,
307+
prices: [
308+
{
309+
amount: 1500,
310+
currency_code: "usd",
311+
min_quantity: 1,
312+
max_quantity: 4,
313+
},
314+
{
315+
amount: 1000,
316+
currency_code: "usd",
317+
min_quantity: 5,
318+
max_quantity: 10,
319+
},
320+
],
321+
},
322+
],
323+
}
324+
325+
const newProduct = await api.post(
326+
`/admin/products`,
327+
productData,
328+
adminHeaders
329+
)
330+
331+
const variantId = newProduct.data.product.variants[0].id
332+
333+
const newCart = (
334+
await api.post(
335+
`/store/carts`,
336+
{
337+
currency_code: "usd",
338+
sales_channel_id: salesChannel.id,
339+
region_id: region.id,
340+
shipping_address: shippingAddressData,
341+
items: [{ variant_id: variantId, quantity: 6 }],
342+
},
343+
storeHeaders
344+
)
345+
).data.cart
346+
347+
expect(newCart).toEqual(
348+
expect.objectContaining({
349+
item_subtotal: 5714.285714285715,
350+
item_tax_total: 285.7142857142857,
351+
item_total: 6000,
352+
items: [
353+
expect.objectContaining({
354+
quantity: 6,
355+
title: "Medusa T-Shirt based quantity",
356+
unit_price: 1000,
357+
updated_at: expect.any(String),
358+
variant_barcode: null,
359+
variant_id: expect.any(String),
360+
variant_sku: "SHIRT-S-BLACK-w-quantity-prices",
361+
variant_title: "S",
362+
}),
363+
],
364+
original_item_subtotal: 5714.285714285715,
365+
original_item_tax_total: 285.7142857142857,
366+
original_item_total: 6000,
367+
original_shipping_subtotal: 0,
368+
original_shipping_tax_total: 0,
369+
original_shipping_total: 0,
370+
original_tax_total: 285.7142857142857,
371+
original_total: 6000,
372+
shipping_subtotal: 0,
373+
shipping_tax_total: 0,
374+
shipping_total: 0,
375+
subtotal: 5714.285714285715,
376+
tax_total: 285.7142857142857,
377+
total: 6000,
378+
})
379+
)
380+
381+
const updatedCart = (
382+
await api.post(
383+
`/store/carts/${newCart.id}/line-items`,
384+
{
385+
variant_id: variantId,
386+
quantity: 1,
387+
metadata: { custom: true },
388+
},
389+
storeHeaders
390+
)
391+
).data.cart
392+
393+
expect(updatedCart).toEqual(
394+
expect.objectContaining({
395+
item_subtotal: 7142.857142857143,
396+
item_tax_total: 357.14285714285717,
397+
item_total: 7500,
398+
items: expect.arrayContaining([
399+
expect.objectContaining({
400+
quantity: 6,
401+
title: "Medusa T-Shirt based quantity",
402+
unit_price: 1000,
403+
updated_at: expect.any(String),
404+
variant_barcode: null,
405+
variant_id: expect.any(String),
406+
variant_sku: "SHIRT-S-BLACK-w-quantity-prices",
407+
variant_title: "S",
408+
}),
409+
expect.objectContaining({
410+
quantity: 1,
411+
title: "Medusa T-Shirt based quantity",
412+
unit_price: 1500,
413+
updated_at: expect.any(String),
414+
variant_barcode: null,
415+
variant_id: expect.any(String),
416+
variant_sku: "SHIRT-S-BLACK-w-quantity-prices",
417+
variant_title: "S",
418+
}),
419+
]),
420+
original_item_subtotal: 7142.857142857143,
421+
original_item_tax_total: 357.14285714285717,
422+
original_item_total: 7500,
423+
original_shipping_subtotal: 0,
424+
original_shipping_tax_total: 0,
425+
original_shipping_total: 0,
426+
original_tax_total: 357.14285714285717,
427+
original_total: 7500,
428+
shipping_subtotal: 0,
429+
shipping_tax_total: 0,
430+
shipping_total: 0,
431+
subtotal: 7142.857142857143,
432+
tax_total: 357.14285714285717,
433+
total: 7500,
434+
})
435+
)
436+
})
437+
288438
describe("with sale price lists", () => {
289439
let priceList
290440

integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,7 @@ medusaIntegrationTestRunner({
18811881

18821882
expect(errors).toEqual([
18831883
{
1884-
action: "get-variant-price-sets",
1884+
action: "get-variant-items-with-prices-workflow-as-step",
18851885
handlerType: "invoke",
18861886
error: expect.objectContaining({
18871887
message: expect.stringContaining(
@@ -4307,18 +4307,20 @@ medusaIntegrationTestRunner({
43074307
})
43084308
}
43094309

4310-
const { result: result1 } = await listShippingOptionsForCartWorkflow(
4311-
appContainer
4312-
).run({ input: { cart_id: cart.id } })
4310+
const { result: result1 } =
4311+
await listShippingOptionsForCartWorkflow(appContainer).run({
4312+
input: { cart_id: cart.id },
4313+
})
43134314

43144315
expect(result1).toHaveLength(1)
43154316
expect(result1[0].name).toEqual(shippingOption.name)
43164317

43174318
setShippingOptionsContextHook = undefined
43184319

4319-
const { result: result2 } = await listShippingOptionsForCartWorkflow(
4320-
appContainer
4321-
).run({ input: { cart_id: cart.id } })
4320+
const { result: result2 } =
4321+
await listShippingOptionsForCartWorkflow(appContainer).run({
4322+
input: { cart_id: cart.id },
4323+
})
43224324

43234325
expect(result2).toHaveLength(0)
43244326
})

packages/core/core-flows/src/api-key/steps/delete-api-keys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IApiKeyModuleService } from "@medusajs/framework/types"
1+
import type { IApiKeyModuleService } from "@medusajs/framework/types"
22
import { Modules } from "@medusajs/framework/utils"
33
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
44

packages/core/core-flows/src/api-key/steps/link-sales-channels-to-publishable-key.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LinkWorkflowInput } from "@medusajs/framework/types"
1+
import type { LinkWorkflowInput } from "@medusajs/framework/types"
22
import {
33
ContainerRegistrationKeys,
44
Modules,
@@ -8,7 +8,7 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
88

99
/**
1010
* The data to manage the sales channels of a publishable API key.
11-
*
11+
*
1212
* @property id - The ID of the publishable API key.
1313
* @property add - The sales channel IDs to add to the publishable API key.
1414
* @property remove - The sales channel IDs to remove from the publishable API key.
@@ -18,7 +18,7 @@ export type LinkSalesChannelsToApiKeyStepInput = LinkWorkflowInput
1818
export const linkSalesChannelsToApiKeyStepId = "link-sales-channels-to-api-key"
1919
/**
2020
* This step manages the sales channels of a publishable API key.
21-
*
21+
*
2222
* @example
2323
* const data = linkSalesChannelsToApiKeyStep({
2424
* id: "apk_123",

packages/core/core-flows/src/api-key/steps/validate-sales-channel-exists.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ISalesChannelModuleService } from "@medusajs/framework/types"
1+
import type { ISalesChannelModuleService } from "@medusajs/framework/types"
22
import {
33
MedusaError,
44
Modules,

packages/core/core-flows/src/api-key/workflows/create-api-keys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ApiKeyDTO, CreateApiKeyDTO } from "@medusajs/framework/types"
1+
import type { ApiKeyDTO, CreateApiKeyDTO } from "@medusajs/framework/types"
22
import {
33
WorkflowData,
44
WorkflowResponse,

packages/core/core-flows/src/api-key/workflows/link-sales-channels-to-publishable-key.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LinkWorkflowInput } from "@medusajs/framework/types"
1+
import type { LinkWorkflowInput } from "@medusajs/framework/types"
22
import { WorkflowData, createWorkflow } from "@medusajs/framework/workflows-sdk"
33
import {
44
linkSalesChannelsToApiKeyStep,
@@ -7,7 +7,7 @@ import {
77

88
/**
99
* The data to manage the sales channels of a publishable API key.
10-
*
10+
*
1111
* @property id - The ID of the publishable API key.
1212
* @property add - The sales channel IDs to add to the publishable API key.
1313
* @property remove - The sales channel IDs to remove from the publishable API key.
@@ -19,10 +19,10 @@ export const linkSalesChannelsToApiKeyWorkflowId =
1919
/**
2020
* This workflow manages the sales channels of a publishable API key. It's used by the
2121
* [Manage Sales Channels API Route](https://docs.medusajs.com/api/admin#api-keys_postapikeysidsaleschannels).
22-
*
22+
*
2323
* You can use this workflow within your customizations or your own custom workflows, allowing you to
2424
* manage the sales channels of a publishable API key within your custom flows.
25-
*
25+
*
2626
* @example
2727
* const { result } = await linkSalesChannelsToApiKeyWorkflow(container)
2828
* .run({
@@ -32,7 +32,7 @@ export const linkSalesChannelsToApiKeyWorkflowId =
3232
* remove: ["sc_321"]
3333
* }
3434
* })
35-
*
35+
*
3636
* @summary
3737
* Manage the sales channels of a publishable API key.
3838
*/

packages/core/core-flows/src/auth/steps/set-auth-app-metadata.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
22

3-
import { IAuthModuleService } from "@medusajs/framework/types"
3+
import type { IAuthModuleService } from "@medusajs/framework/types"
44
import { isDefined, Modules } from "@medusajs/framework/utils"
55

66
export type SetAuthAppMetadataStepInput = {
@@ -14,26 +14,26 @@ export const setAuthAppMetadataStepId = "set-auth-app-metadata"
1414
* This step sets the `app_metadata` property of an auth identity. This is useful to
1515
* associate a user (whether it's an admin user or customer) with an auth identity
1616
* that allows them to authenticate into Medusa.
17-
*
18-
* You can learn more about auth identites in
17+
*
18+
* You can learn more about auth identites in
1919
* [this documentation](https://docs.medusajs.com/resources/commerce-modules/auth/auth-identity-and-actor-types).
20-
*
20+
*
2121
* To use this for a custom actor type, check out [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type)
2222
* that explains how to create a custom `manager` actor type and manage its users.
23-
*
23+
*
2424
* @example
2525
* To associate an auth identity with an actor type (user, customer, or other actor types):
26-
*
26+
*
2727
* ```ts
2828
* const data = setAuthAppMetadataStep({
2929
* authIdentityId: "au_1234",
3030
* actorType: "user", // or `customer`, or custom type
3131
* value: "user_123"
3232
* })
3333
* ```
34-
*
34+
*
3535
* To remove the association with an actor type, such as when deleting the user:
36-
*
36+
*
3737
* ```ts
3838
* const data = setAuthAppMetadataStep({
3939
* authIdentityId: "au_1234",

packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
WorkflowResponse,
1010
} from "@medusajs/framework/workflows-sdk"
1111
import { emitEventStep, useRemoteQueryStep } from "../../common"
12-
import { ProjectConfigOptions } from "@medusajs/framework/types"
12+
import type { ProjectConfigOptions } from "@medusajs/framework/types"
1313

1414
/**
1515
* This workflow generates a reset password token for a user. It's used by the

0 commit comments

Comments
 (0)