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:
committed by
GitHub
parent
10ff72c30a
commit
80452332d8
5
.changeset/new-shoes-tickle.md
Normal file
5
.changeset/new-shoes-tickle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): default sales channel on product create
|
||||
@@ -1,448 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/admin/products GET /admin/products returns a list of products with only giftcard in list 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"categories": Array [],
|
||||
"collection": null,
|
||||
"collection_id": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-giftcard-description",
|
||||
"discountable": false,
|
||||
"external_id": null,
|
||||
"handle": "test-giftcard",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^prod_\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": true,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"title": "Denominations",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"sales_channels": Array [],
|
||||
"status": "draft",
|
||||
"subtitle": null,
|
||||
"tags": Array [],
|
||||
"thumbnail": null,
|
||||
"title": "Test Giftcard",
|
||||
"type": null,
|
||||
"type_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variants": Array [
|
||||
Object {
|
||||
"allow_backorder": false,
|
||||
"barcode": null,
|
||||
"calculated_price": null,
|
||||
"calculated_price_incl_tax": null,
|
||||
"calculated_tax": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"ean": null,
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^variant_\\*/,
|
||||
"inventory_quantity": 0,
|
||||
"length": null,
|
||||
"manage_inventory": true,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"option_id": StringMatching /\\^opt_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"value": "100",
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"original_price": null,
|
||||
"original_price_incl_tax": null,
|
||||
"original_tax": null,
|
||||
"prices": Array [
|
||||
Object {
|
||||
"amount": 100,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "usd",
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"max_quantity": null,
|
||||
"min_quantity": null,
|
||||
"price_list": null,
|
||||
"price_list_id": null,
|
||||
"region_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"sku": null,
|
||||
"tax_rates": null,
|
||||
"title": "Test variant",
|
||||
"upc": null,
|
||||
"updated_at": Any<String>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
],
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`/admin/products POST /admin/products creates a product 1`] = `
|
||||
Object {
|
||||
"categories": Array [],
|
||||
"collection": Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"handle": "test-collection",
|
||||
"id": "test-collection",
|
||||
"metadata": null,
|
||||
"title": "Test collection",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
"collection_id": "test-collection",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"external_id": null,
|
||||
"handle": "test",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^prod_\\*/,
|
||||
"images": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"url": "test-image.png",
|
||||
},
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"url": "test-image-2.png",
|
||||
},
|
||||
],
|
||||
"is_giftcard": false,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"title": "size",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"title": "color",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"sales_channels": Array [],
|
||||
"status": "draft",
|
||||
"subtitle": null,
|
||||
"tags": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"value": "123",
|
||||
},
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"value": "456",
|
||||
},
|
||||
],
|
||||
"thumbnail": "test-image.png",
|
||||
"title": "Test",
|
||||
"type": Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": "test-type",
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"value": "test-type",
|
||||
},
|
||||
"type_id": "test-type",
|
||||
"updated_at": Any<String>,
|
||||
"variants": Array [
|
||||
Object {
|
||||
"allow_backorder": false,
|
||||
"barcode": null,
|
||||
"calculated_price": null,
|
||||
"calculated_price_incl_tax": null,
|
||||
"calculated_tax": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"ean": null,
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^variant_\\*/,
|
||||
"inventory_quantity": 10,
|
||||
"length": null,
|
||||
"manage_inventory": true,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^optval_\\*/,
|
||||
"metadata": null,
|
||||
"option_id": StringMatching /\\^opt_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"value": "large",
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^optval_\\*/,
|
||||
"metadata": null,
|
||||
"option_id": StringMatching /\\^opt_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"value": "green",
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"original_price": null,
|
||||
"original_price_incl_tax": null,
|
||||
"original_tax": null,
|
||||
"prices": Array [
|
||||
Object {
|
||||
"amount": 100,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "usd",
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^ma_\\*/,
|
||||
"max_quantity": null,
|
||||
"min_quantity": null,
|
||||
"price_list": null,
|
||||
"price_list_id": null,
|
||||
"region_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
Object {
|
||||
"amount": 45,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "eur",
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^ma_\\*/,
|
||||
"max_quantity": null,
|
||||
"min_quantity": null,
|
||||
"price_list": null,
|
||||
"price_list_id": null,
|
||||
"region_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
Object {
|
||||
"amount": 30,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "dkk",
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^ma_\\*/,
|
||||
"max_quantity": null,
|
||||
"min_quantity": null,
|
||||
"price_list": null,
|
||||
"price_list_id": null,
|
||||
"region_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"product_id": StringMatching /\\^prod_\\*/,
|
||||
"sku": null,
|
||||
"tax_rates": null,
|
||||
"title": "Test variant",
|
||||
"upc": null,
|
||||
"updated_at": Any<String>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
],
|
||||
"weight": null,
|
||||
"width": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`/admin/products POST /admin/products updates a product (update prices, tags, update status, delete collection, delete type, replaces images) 1`] = `
|
||||
Object {
|
||||
"categories": Array [],
|
||||
"collection": null,
|
||||
"collection_id": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"external_id": null,
|
||||
"handle": "test-product",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": "test-product",
|
||||
"images": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^img_\\*/,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"url": "test-image-2.png",
|
||||
},
|
||||
],
|
||||
"is_giftcard": false,
|
||||
"length": null,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": "test-option",
|
||||
"metadata": null,
|
||||
"product_id": "test-product",
|
||||
"title": "test-option",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"sales_channels": Array [],
|
||||
"status": "published",
|
||||
"subtitle": null,
|
||||
"tags": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": "tag1",
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"value": "123",
|
||||
},
|
||||
],
|
||||
"thumbnail": "test-image-2.png",
|
||||
"title": "Test product",
|
||||
"type": Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^ptyp_\\*/,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
"value": "test-type-2",
|
||||
},
|
||||
"type_id": StringMatching /\\^ptyp_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"variants": Array [
|
||||
Object {
|
||||
"allow_backorder": false,
|
||||
"barcode": "test-barcode",
|
||||
"calculated_price": null,
|
||||
"calculated_price_incl_tax": null,
|
||||
"calculated_tax": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"ean": "test-ean",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": "test-variant",
|
||||
"inventory_quantity": 10,
|
||||
"length": null,
|
||||
"manage_inventory": true,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": "test-variant-option",
|
||||
"metadata": null,
|
||||
"option_id": "test-option",
|
||||
"updated_at": Any<String>,
|
||||
"value": "Default variant",
|
||||
"variant_id": "test-variant",
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"original_price": null,
|
||||
"original_price_incl_tax": null,
|
||||
"original_tax": null,
|
||||
"prices": Array [
|
||||
Object {
|
||||
"amount": 75,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "usd",
|
||||
"deleted_at": null,
|
||||
"id": "test-price",
|
||||
"max_quantity": null,
|
||||
"min_quantity": null,
|
||||
"price_list": null,
|
||||
"price_list_id": null,
|
||||
"region_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variant_id": "test-variant",
|
||||
},
|
||||
],
|
||||
"product_id": "test-product",
|
||||
"sku": "test-sku",
|
||||
"tax_rates": null,
|
||||
"title": "Test variant",
|
||||
"upc": "test-upc",
|
||||
"updated_at": Any<String>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
],
|
||||
"weight": null,
|
||||
"width": null,
|
||||
}
|
||||
`;
|
||||
@@ -20,6 +20,7 @@ const {
|
||||
simpleProductFactory,
|
||||
simpleDiscountFactory,
|
||||
simpleProductCategoryFactory,
|
||||
simpleSalesChannelFactory,
|
||||
simpleRegionFactory,
|
||||
} = require("../../factories")
|
||||
const { DiscountRuleType, AllocationType } = require("@medusajs/medusa/dist")
|
||||
@@ -59,6 +60,12 @@ describe("/admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -686,55 +693,57 @@ describe("/admin/products", () => {
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toMatchSnapshot([
|
||||
{
|
||||
title: "Test Giftcard",
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
is_giftcard: true,
|
||||
description: "test-giftcard-description",
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
options: [
|
||||
{
|
||||
title: "Denominations",
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
expect(response.data.products).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test Giftcard",
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
is_giftcard: true,
|
||||
description: "test-giftcard-description",
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Denominations",
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
prices: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
created_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
])
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
created_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of products not containing a giftcard in list", async () => {
|
||||
@@ -1029,6 +1038,12 @@ describe("/admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -1077,126 +1092,128 @@ describe("/admin/products", () => {
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.product).toMatchSnapshot({
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
title: "Test",
|
||||
discountable: true,
|
||||
is_giftcard: false,
|
||||
handle: "test",
|
||||
status: "draft",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
images: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
url: "test-image.png",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
url: "test-image-2.png",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
thumbnail: "test-image.png",
|
||||
tags: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
value: "123",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
value: "456",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
type: {
|
||||
value: "test-type",
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
title: "Test",
|
||||
discountable: true,
|
||||
is_giftcard: false,
|
||||
handle: "test",
|
||||
status: "draft",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
collection: {
|
||||
id: "test-collection",
|
||||
title: "Test collection",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
options: [
|
||||
{
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
title: "size",
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
url: "test-image.png",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
url: "test-image-2.png",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
thumbnail: "test-image.png",
|
||||
tags: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "123",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "456",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
type: expect.objectContaining({
|
||||
value: "test-type",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
title: "color",
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: "test-collection",
|
||||
title: "Test collection",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
updated_at: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
title: "Test variant",
|
||||
prices: [
|
||||
{
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
value: "large",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
id: expect.stringMatching(/^optval_*/),
|
||||
},
|
||||
{
|
||||
value: "green",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
id: expect.stringMatching(/^optval_*/),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
title: "size",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
title: "color",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
updated_at: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
title: "Test variant",
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.stringMatching(/^ma_*/),
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
id: expect.stringMatching(/^optval_*/),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
id: expect.stringMatching(/^optval_*/),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a product that is not discountable", async () => {
|
||||
@@ -1356,92 +1373,94 @@ describe("/admin/products", () => {
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.product).toMatchSnapshot({
|
||||
id: "test-product",
|
||||
created_at: expect.any(String),
|
||||
description: "test-product-description",
|
||||
discountable: true,
|
||||
handle: "test-product",
|
||||
images: [
|
||||
{
|
||||
created_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
id: expect.stringMatching(/^img_*/),
|
||||
metadata: null,
|
||||
updated_at: expect.any(String),
|
||||
url: "test-image-2.png",
|
||||
},
|
||||
],
|
||||
is_giftcard: false,
|
||||
options: [
|
||||
{
|
||||
created_at: expect.any(String),
|
||||
id: "test-option",
|
||||
product_id: "test-product",
|
||||
title: "test-option",
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
status: "published",
|
||||
tags: [
|
||||
{
|
||||
created_at: expect.any(String),
|
||||
id: "tag1",
|
||||
updated_at: expect.any(String),
|
||||
value: "123",
|
||||
},
|
||||
],
|
||||
thumbnail: "test-image-2.png",
|
||||
title: "Test product",
|
||||
type: {
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-product",
|
||||
created_at: expect.any(String),
|
||||
id: expect.stringMatching(/^ptyp_*/),
|
||||
updated_at: expect.any(String),
|
||||
value: "test-type-2",
|
||||
},
|
||||
type_id: expect.stringMatching(/^ptyp_*/),
|
||||
updated_at: expect.any(String),
|
||||
variants: [
|
||||
{
|
||||
allow_backorder: false,
|
||||
barcode: "test-barcode",
|
||||
description: "test-product-description",
|
||||
discountable: true,
|
||||
handle: "test-product",
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
created_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
id: expect.stringMatching(/^img_*/),
|
||||
metadata: null,
|
||||
updated_at: expect.any(String),
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
is_giftcard: false,
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
created_at: expect.any(String),
|
||||
id: "test-option",
|
||||
product_id: "test-product",
|
||||
title: "test-option",
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
status: "published",
|
||||
tags: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
created_at: expect.any(String),
|
||||
id: "tag1",
|
||||
updated_at: expect.any(String),
|
||||
value: "123",
|
||||
}),
|
||||
]),
|
||||
thumbnail: "test-image-2.png",
|
||||
title: "Test product",
|
||||
type: expect.objectContaining({
|
||||
created_at: expect.any(String),
|
||||
ean: "test-ean",
|
||||
id: "test-variant",
|
||||
inventory_quantity: 10,
|
||||
manage_inventory: true,
|
||||
options: [
|
||||
{
|
||||
created_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
id: "test-variant-option",
|
||||
metadata: null,
|
||||
option_id: "test-option",
|
||||
updated_at: expect.any(String),
|
||||
value: "Default variant",
|
||||
variant_id: "test-variant",
|
||||
},
|
||||
],
|
||||
origin_country: null,
|
||||
prices: [
|
||||
{
|
||||
amount: 75,
|
||||
created_at: expect.any(String),
|
||||
currency_code: "usd",
|
||||
id: "test-price",
|
||||
updated_at: expect.any(String),
|
||||
variant_id: "test-variant",
|
||||
},
|
||||
],
|
||||
product_id: "test-product",
|
||||
sku: "test-sku",
|
||||
title: "Test variant",
|
||||
upc: "test-upc",
|
||||
id: expect.stringMatching(/^ptyp_*/),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
})
|
||||
value: "test-type-2",
|
||||
}),
|
||||
type_id: expect.stringMatching(/^ptyp_*/),
|
||||
updated_at: expect.any(String),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
allow_backorder: false,
|
||||
barcode: "test-barcode",
|
||||
created_at: expect.any(String),
|
||||
ean: "test-ean",
|
||||
id: "test-variant",
|
||||
inventory_quantity: 10,
|
||||
manage_inventory: true,
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
created_at: expect.any(String),
|
||||
deleted_at: null,
|
||||
id: "test-variant-option",
|
||||
metadata: null,
|
||||
option_id: "test-option",
|
||||
updated_at: expect.any(String),
|
||||
value: "Default variant",
|
||||
variant_id: "test-variant",
|
||||
}),
|
||||
]),
|
||||
origin_country: null,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: 75,
|
||||
created_at: expect.any(String),
|
||||
currency_code: "usd",
|
||||
id: "test-price",
|
||||
updated_at: expect.any(String),
|
||||
variant_id: "test-variant",
|
||||
}),
|
||||
]),
|
||||
product_id: "test-product",
|
||||
sku: "test-sku",
|
||||
title: "Test variant",
|
||||
upc: "test-upc",
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("updates product (removes images when empty array included)", async () => {
|
||||
@@ -1813,6 +1832,11 @@ describe("/admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -1859,6 +1883,11 @@ describe("/admin/products", () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await priceListSeeder(dbConnection)
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -2326,6 +2355,11 @@ describe("/admin/products", () => {
|
||||
try {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
@@ -2398,6 +2432,11 @@ describe("/admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -2871,6 +2910,11 @@ describe("/admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
await productSeeder(dbConnection)
|
||||
await adminSeeder(dbConnection)
|
||||
await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "Default channel",
|
||||
id: "default-channel",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -850,6 +850,7 @@ describe("sales channels", () => {
|
||||
salesChannel = await simpleSalesChannelFactory(dbConnection, {
|
||||
name: "test name",
|
||||
description: "test description",
|
||||
is_default: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -899,6 +900,47 @@ describe("sales channels", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should assign the default sales channel to a product if none is provided when creating it", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
title: "Product-no-saleschannel",
|
||||
description: "test-product-description",
|
||||
type: { value: "test-type" },
|
||||
options: [{ title: "size" }],
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
inventory_quantity: 10,
|
||||
prices: [{ currency_code: "usd", amount: 100 }],
|
||||
options: [{ value: "large" }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api
|
||||
.post("/admin/products", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
sales_channels: [
|
||||
expect.objectContaining({
|
||||
id: salesChannel.id,
|
||||
name: salesChannel.name,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/products/:id", () => {
|
||||
|
||||
@@ -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()
|
||||
}),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user