fix(medusa): Update cart sales channel should not remove all line items (#5980)
This commit is contained in:
1 parent
42cc8ae3f8
commit
278b7fb203
6 files changed
+118
-54
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(medusa): Update cart sales channel should not remove all line items
|
||||||
@@ -42,6 +42,11 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
|
|
||||||
describe("POST /store/carts/:id", () => {
|
describe("POST /store/carts/:id", () => {
|
||||||
let product
|
let product
|
||||||
|
let product2
|
||||||
|
let product3
|
||||||
|
|
||||||
|
const salesChannelId = "sales-channel"
|
||||||
|
const defaultSalesChannelId = "default-sales-channel"
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await simpleRegionFactory(dbConnection, {
|
await simpleRegionFactory(dbConnection, {
|
||||||
@@ -55,13 +60,13 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
product = await simpleProductFactory(dbConnection, {
|
product = await simpleProductFactory(dbConnection, {
|
||||||
sales_channels: [
|
sales_channels: [
|
||||||
{
|
{
|
||||||
id: "sales-channel",
|
id: salesChannelId,
|
||||||
name: "Sales channel",
|
name: "Sales channel",
|
||||||
description: "Sales channel",
|
description: "Sales channel",
|
||||||
is_disabled: false,
|
is_disabled: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "default-sales-channel",
|
id: defaultSalesChannelId,
|
||||||
name: "Main sales channel",
|
name: "Main sales channel",
|
||||||
description: "Main sales channel",
|
description: "Main sales channel",
|
||||||
is_default: true,
|
is_default: true,
|
||||||
@@ -69,6 +74,10 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
product2 = await simpleProductFactory(dbConnection)
|
||||||
|
|
||||||
|
product3 = await simpleProductFactory(dbConnection)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -91,7 +100,7 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sales_channel_id: "sales-channel",
|
sales_channel_id: salesChannelId,
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = createCartRes.data.cart
|
const cart = createCartRes.data.cart
|
||||||
@@ -110,7 +119,7 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
expect(createdOrder.status).toEqual(200)
|
expect(createdOrder.status).toEqual(200)
|
||||||
expect(createdOrder.data.data).toEqual(
|
expect(createdOrder.data.data).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
sales_channel_id: "sales-channel",
|
sales_channel_id: salesChannelId,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -152,5 +161,40 @@ describe("[MEDUSA_FF_SALES_CHANNELS] /store/carts", () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should remove the line items that does not belong to the new sales channel", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
let createCartRes = await api.post("/store/carts", {
|
||||||
|
region_id: "test-region",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant_id: product2.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant_id: product3.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sales_channel_id: defaultSalesChannelId,
|
||||||
|
})
|
||||||
|
|
||||||
|
let items = createCartRes.data.cart.items
|
||||||
|
expect(items).toHaveLength(3)
|
||||||
|
|
||||||
|
const cartId = createCartRes.data.cart.id
|
||||||
|
createCartRes = await api.post(`/store/carts/${cartId}`, {
|
||||||
|
sales_channel_id: salesChannelId,
|
||||||
|
})
|
||||||
|
|
||||||
|
items = createCartRes.data.cart.items
|
||||||
|
expect(items).toHaveLength(1)
|
||||||
|
expect(items.map((i) => i.variant.id)).toEqual([product.variants[0].id])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -744,9 +744,9 @@ describe("CartService", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(lineItemService.delete).toHaveBeenCalledTimes(1)
|
expect(lineItemService.delete).toHaveBeenCalledTimes(1)
|
||||||
expect(lineItemService.delete).toHaveBeenCalledWith(
|
expect(lineItemService.delete).toHaveBeenCalledWith([
|
||||||
IdMap.getId("itemToRemove")
|
IdMap.getId("itemToRemove"),
|
||||||
)
|
])
|
||||||
|
|
||||||
expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledTimes(1)
|
expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||||
expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledWith({
|
expect(LineItemAdjustmentServiceMock.delete).toHaveBeenCalledWith({
|
||||||
|
|||||||
@@ -267,8 +267,9 @@ const unknownVariantId = "unknown-variant"
|
|||||||
})
|
})
|
||||||
describe("delete", () => {
|
describe("delete", () => {
|
||||||
const lineItemRepository = MockRepository({
|
const lineItemRepository = MockRepository({
|
||||||
findOne: () =>
|
find: () =>
|
||||||
Promise.resolve({
|
Promise.resolve([
|
||||||
|
{
|
||||||
id: IdMap.getId("test-line-item"),
|
id: IdMap.getId("test-line-item"),
|
||||||
variant_id: IdMap.getId("test-variant"),
|
variant_id: IdMap.getId("test-variant"),
|
||||||
variant: {
|
variant: {
|
||||||
@@ -281,7 +282,8 @@ const unknownVariantId = "unknown-variant"
|
|||||||
thumbnail: "",
|
thumbnail: "",
|
||||||
unit_price: 50,
|
unit_price: 50,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
}),
|
},
|
||||||
|
]),
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineItemService = new LineItemService({
|
const lineItemService = new LineItemService({
|
||||||
@@ -297,7 +299,8 @@ const unknownVariantId = "unknown-variant"
|
|||||||
await lineItemService.delete(IdMap.getId("test-line-item"))
|
await lineItemService.delete(IdMap.getId("test-line-item"))
|
||||||
|
|
||||||
expect(lineItemRepository.remove).toHaveBeenCalledTimes(1)
|
expect(lineItemRepository.remove).toHaveBeenCalledTimes(1)
|
||||||
expect(lineItemRepository.remove).toHaveBeenCalledWith({
|
expect(lineItemRepository.remove).toHaveBeenCalledWith([
|
||||||
|
{
|
||||||
id: IdMap.getId("test-line-item"),
|
id: IdMap.getId("test-line-item"),
|
||||||
variant_id: IdMap.getId("test-variant"),
|
variant_id: IdMap.getId("test-variant"),
|
||||||
variant: {
|
variant: {
|
||||||
@@ -310,7 +313,8 @@ const unknownVariantId = "unknown-variant"
|
|||||||
thumbnail: "",
|
thumbnail: "",
|
||||||
unit_price: 50,
|
unit_price: 50,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
})
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -560,23 +560,29 @@ class CartService extends TransactionBaseService {
|
|||||||
/**
|
/**
|
||||||
* Removes a line item from the cart.
|
* Removes a line item from the cart.
|
||||||
* @param cartId - the id of the cart that we will remove from
|
* @param cartId - the id of the cart that we will remove from
|
||||||
* @param lineItemId - the line item to remove.
|
* @param lineItemId - the line item(s) to remove.
|
||||||
* @return the result of the update operation
|
* @return the result of the update operation
|
||||||
*/
|
*/
|
||||||
async removeLineItem(cartId: string, lineItemId: string): Promise<Cart> {
|
async removeLineItem(
|
||||||
|
cartId: string,
|
||||||
|
lineItemId: string | string[]
|
||||||
|
): Promise<void> {
|
||||||
return await this.atomicPhase_(
|
return await this.atomicPhase_(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
const cart = await this.retrieve(cartId, {
|
const cart = await this.retrieve(cartId, {
|
||||||
relations: [
|
relations: ["items.variant.product.profiles", "shipping_methods"],
|
||||||
"items.variant.product.profiles",
|
|
||||||
"payment_sessions",
|
|
||||||
"shipping_methods",
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineItem = cart.items.find((item) => item.id === lineItemId)
|
const lineItemIdsToRemove = new Set(
|
||||||
if (!lineItem) {
|
Array.isArray(lineItemId) ? lineItemId : [lineItemId]
|
||||||
return cart
|
)
|
||||||
|
|
||||||
|
const lineItems = cart.items.filter((item) =>
|
||||||
|
lineItemIdsToRemove.has(item.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!lineItems.length) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cart.shipping_methods?.length) {
|
if (cart.shipping_methods?.length) {
|
||||||
@@ -599,12 +605,11 @@ class CartService extends TransactionBaseService {
|
|||||||
|
|
||||||
await this.lineItemService_
|
await this.lineItemService_
|
||||||
.withTransaction(transactionManager)
|
.withTransaction(transactionManager)
|
||||||
.delete(lineItem.id)
|
.delete([...lineItemIdsToRemove])
|
||||||
|
|
||||||
const result = await this.retrieve(cartId, {
|
const result = await this.retrieve(cartId, {
|
||||||
relations: [
|
relations: [
|
||||||
"items.variant.product.profiles",
|
"items.variant.product.profiles",
|
||||||
"discounts",
|
|
||||||
"discounts.rule",
|
"discounts.rule",
|
||||||
"region",
|
"region",
|
||||||
],
|
],
|
||||||
@@ -618,8 +623,6 @@ class CartService extends TransactionBaseService {
|
|||||||
.emit(CartService.Events.UPDATED, {
|
.emit(CartService.Events.UPDATED, {
|
||||||
id: cart.id,
|
id: cart.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.retrieve(cartId)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1423,14 +1426,13 @@ class CartService extends TransactionBaseService {
|
|||||||
return !productIdsToKeep.has(item.variant.product_id)
|
return !productIdsToKeep.has(item.variant.product_id)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (itemsToRemove.length) {
|
if (!itemsToRemove.length) {
|
||||||
const results = await promiseAll(
|
return
|
||||||
itemsToRemove.map(async (item) => {
|
|
||||||
return this.removeLineItem(cart.id, item.id)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
cart.items = results.pop()?.items ?? []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const itemIdsToRemove = new Set(itemsToRemove.map((item) => item.id))
|
||||||
|
await this.removeLineItem(cart.id, [...itemIdsToRemove])
|
||||||
|
cart.items = cart.items.filter((item) => !itemIdsToRemove.has(item.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
import {MedusaError} from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import {EntityManager, In} from "typeorm"
|
import { EntityManager, In } from "typeorm"
|
||||||
import {DeepPartial} from "typeorm/common/DeepPartial"
|
import { DeepPartial } from "typeorm/common/DeepPartial"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FlagRouter,
|
FlagRouter,
|
||||||
MedusaV2Flag,
|
MedusaV2Flag,
|
||||||
selectorConstraintsToString
|
selectorConstraintsToString,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import {TransactionBaseService} from "../interfaces"
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import TaxInclusivePricingFeatureFlag
|
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||||
from "../loaders/feature-flags/tax-inclusive-pricing"
|
|
||||||
import {
|
import {
|
||||||
LineItem,
|
LineItem,
|
||||||
LineItemAdjustment,
|
LineItemAdjustment,
|
||||||
LineItemTaxLine,
|
LineItemTaxLine,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
} from "../models"
|
} from "../models"
|
||||||
import {CartRepository} from "../repositories/cart"
|
import { CartRepository } from "../repositories/cart"
|
||||||
import {LineItemRepository} from "../repositories/line-item"
|
import { LineItemRepository } from "../repositories/line-item"
|
||||||
import {LineItemTaxLineRepository} from "../repositories/line-item-tax-line"
|
import { LineItemTaxLineRepository } from "../repositories/line-item-tax-line"
|
||||||
import {FindConfig, Selector} from "../types/common"
|
import { FindConfig, Selector } from "../types/common"
|
||||||
import {GenerateInputData, GenerateLineItemContext} from "../types/line-item"
|
import { GenerateInputData, GenerateLineItemContext } from "../types/line-item"
|
||||||
import {ProductVariantPricing} from "../types/pricing"
|
import { ProductVariantPricing } from "../types/pricing"
|
||||||
import {buildQuery, isString, setMetadata} from "../utils"
|
import { buildQuery, isString, setMetadata } from "../utils"
|
||||||
import {
|
import {
|
||||||
PricingService,
|
PricingService,
|
||||||
ProductService,
|
ProductService,
|
||||||
@@ -484,23 +483,33 @@ class LineItemService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async delete(ids: string[]): Promise<LineItem[]>
|
||||||
|
async delete(id: string): Promise<LineItem | void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a line item.
|
* Deletes a line item.
|
||||||
* @param id - the id of the line item to delete
|
* @param id - the id of the line item to delete
|
||||||
* @return the result of the delete operation
|
* @return the result of the delete operation
|
||||||
*/
|
*/
|
||||||
async delete(id: string): Promise<LineItem | undefined | null> {
|
async delete(id: string | string[]): Promise<LineItem[] | LineItem | void> {
|
||||||
|
const ids = Array.isArray(id) ? id : [id]
|
||||||
|
|
||||||
return await this.atomicPhase_(
|
return await this.atomicPhase_(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
const lineItemRepository = transactionManager.withRepository(
|
const lineItemRepository = transactionManager.withRepository(
|
||||||
this.lineItemRepository_
|
this.lineItemRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
return await lineItemRepository
|
const lineItems = await lineItemRepository.find({
|
||||||
.findOne({ where: { id } })
|
where: { id: In(ids) },
|
||||||
.then(
|
})
|
||||||
async (lineItem) => lineItem && lineItemRepository.remove(lineItem)
|
|
||||||
)
|
if (!lineItems?.length) {
|
||||||
|
return Array.isArray(id) ? [] : void 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const removedItems = await lineItemRepository.remove(lineItems)
|
||||||
|
return Array.isArray(id) ? removedItems : removedItems[0]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -511,7 +520,7 @@ class LineItemService extends TransactionBaseService {
|
|||||||
* @param id - the id of the line item to delete
|
* @param id - the id of the line item to delete
|
||||||
* @return the result of the delete operation
|
* @return the result of the delete operation
|
||||||
*/
|
*/
|
||||||
async deleteWithTaxLines(id: string): Promise<LineItem | undefined | null> {
|
async deleteWithTaxLines(id: string): Promise<LineItem | void> {
|
||||||
return await this.atomicPhase_(
|
return await this.atomicPhase_(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
await this.taxProviderService_
|
await this.taxProviderService_
|
||||||
|
|||||||
Reference in new issue
Block a user