fix(medusa): Default sales channel on product create (#3249)

What:
Assign the default sales channel if none is provided while creating a new product.


FIXES: CORE-1114

Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Carlos R. L. Rodrigues
2023-02-14 08:46:14 +00:00
committed by GitHub
co-authored by Oliver Windall Juhl
parent 10ff72c30a
commit 80452332d8
8 changed files with 409 additions and 702 deletions
@@ -3,6 +3,7 @@ import { request } from "../../../../../helpers/test-request"
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant"
import { ShippingProfileServiceMock } from "../../../../../services/__mocks__/shipping-profile"
import { SalesChannelServiceMock } from "../../../../../services/__mocks__/sales-channel"
describe("POST /admin/products", () => {
describe("successful creation with variants", () => {
@@ -46,6 +47,7 @@ describe("POST /admin/products", () => {
})
it("returns 200", () => {
expect(SalesChannelServiceMock.retrieveDefault).toHaveBeenCalledTimes(1)
expect(subject.status).toEqual(200)
})
@@ -114,6 +116,13 @@ describe("POST /admin/products", () => {
is_giftcard: false,
options: [{ title: "Denominations" }],
profile_id: IdMap.getId("default_shipping_profile"),
sales_channels: [
{
description: "sales channel 1 description",
is_disabled: false,
name: "sales channel 1 name",
},
],
})
})
@@ -173,6 +182,13 @@ describe("POST /admin/products", () => {
is_giftcard: true,
status: "draft",
profile_id: IdMap.getId("giftCardProfile"),
sales_channels: [
{
description: "sales channel 1 description",
is_disabled: false,
name: "sales channel 1 name",
},
],
})
})
@@ -14,6 +14,7 @@ import {
ProductService,
ProductVariantInventoryService,
ProductVariantService,
SalesChannelService,
ShippingProfileService,
} from "../../../../services"
import {
@@ -121,6 +122,10 @@ export default async (req, res) => {
const inventoryService: IInventoryService | undefined =
req.scope.resolve("inventoryService")
const salesChannelService: SalesChannelService = req.scope.resolve(
"salesChannelService"
)
const entityManager: EntityManager = req.scope.resolve("manager")
const newProduct = await entityManager.transaction(async (manager) => {
@@ -143,6 +148,14 @@ export default async (req, res) => {
.retrieveDefault()
}
// If no sales channel available, set the default one
if (!validated?.sales_channels?.length) {
const defaultSalesChannel = await salesChannelService
.withTransaction(manager)
.retrieveDefault()
validated.sales_channels = [defaultSalesChannel]
}
const newProduct = await productService
.withTransaction(manager)
.create({ ...validated, profile_id: shippingProfile.id })
@@ -1,4 +1,4 @@
import { IdMap } from "medusa-test-utils";
import { IdMap } from "medusa-test-utils"
export const SalesChannelServiceMock = {
withTransaction: function () {
@@ -19,12 +19,14 @@ export const SalesChannelServiceMock = {
listAndCount: jest.fn().mockImplementation(() => {
return Promise.resolve([
[{
id: IdMap.getId("sales_channel_1"),
name: "sales channel 1 name",
description: "sales channel 1 description",
is_disabled: false,
}],
[
{
id: IdMap.getId("sales_channel_1"),
name: "sales channel 1 name",
description: "sales channel 1 description",
is_disabled: false,
},
],
1,
])
}),
@@ -48,6 +50,14 @@ export const SalesChannelServiceMock = {
})
}),
retrieveDefault: jest.fn().mockImplementation(() => {
return Promise.resolve({
name: "sales channel 1 name",
description: "sales channel 1 description",
is_disabled: false,
})
}),
removeProducts: jest.fn().mockImplementation((id, productIds) => {
return Promise.resolve()
}),
+27 -2
View File
@@ -49,6 +49,10 @@ class SalesChannelService extends TransactionBaseService {
this.storeService_ = storeService
}
private getManager(): EntityManager {
return this.transactionManager_ ?? this.manager_
}
/**
* A generic retrieve used to find a sales channel by different attributes.
*
@@ -60,7 +64,7 @@ class SalesChannelService extends TransactionBaseService {
selector: Selector<SalesChannel>,
config: FindConfig<SalesChannel> = {}
): Promise<SalesChannel> {
const manager = this.manager_
const manager = this.getManager()
const salesChannelRepo = manager.getCustomRepository(
this.salesChannelRepository_
@@ -145,7 +149,7 @@ class SalesChannelService extends TransactionBaseService {
take: 20,
}
): Promise<[SalesChannel[], number]> {
const manager = this.manager_
const manager = this.getManager()
const salesChannelRepo = manager.getCustomRepository(
this.salesChannelRepository_
)
@@ -289,6 +293,27 @@ class SalesChannelService extends TransactionBaseService {
})
}
/**
* Retrieves the default sales channel.
* @return the sales channel
*/
async retrieveDefault(): Promise<SalesChannel> {
const manager = this.getManager()
const store = await this.storeService_.withTransaction(manager).retrieve({
relations: ["default_sales_channel"],
})
if (!store.default_sales_channel) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Default Sales channel was not found`
)
}
return store.default_sales_channel
}
/**
* Remove a batch of product from a sales channel
* @param salesChannelId - The id of the sales channel on which to remove the products