feat(medusa): variant creation with prices in productservice.create (#5410)

This commit is contained in:
Philip Korsholm authored and GitHub committed 2023-10-30 17:22:57 +01:00
1 parent 397da6c2ba
commit 4d52082bf0
4 files changed
+190 -13

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
feat(medusa): variant creation with prices in productservice.create
@@ -0,0 +1,147 @@
import path from "path"
import { initDb, useDb } from "../../../environment-helpers/use-db"
import { bootstrapApp } from "../../../environment-helpers/bootstrap-app"
import { setPort } from "../../../environment-helpers/use-api"
jest.setTimeout(30000)
describe("product", () => {
let dbConnection
let medusaContainer
let productService
let express
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd } as any)
const { container, port, app } = await bootstrapApp({ cwd })
setPort(port)
express = app.listen(port, () => {
process.send!(port)
})
medusaContainer = container
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
express.close()
})
afterEach(async () => {
jest.clearAllMocks()
const db = useDb()
await db.teardown()
})
describe("product service", () => {
it("should create variant prices correctly in service creation", async () => {
productService = medusaContainer.resolve("productService")
const payload = {
title: "test-product",
handle: "test-product",
options: [{ title: "test-option" }],
variants: [
{
title: "test-variant",
inventory_quantity: 10,
sku: "test",
options: [{ value: "large", title: "test-option" }],
prices: [{ amount: "100", currency_code: "usd" }],
},
],
}
const { id } = await productService.create(payload)
const result = await productService.retrieve(id, {
relations: ["variants", "variants.prices", "variants.options"],
})
expect(result).toEqual(
expect.objectContaining({
variants: [
expect.objectContaining({
options: [expect.objectContaining({ value: "large" })],
prices: [
expect.objectContaining({ amount: 100, currency_code: "usd" }),
],
}),
],
})
)
})
it("should fail to create a variant without options on for a product with options", async () => {
const payload = {
title: "test-product",
handle: "test-product",
options: [{ title: "test-option" }],
variants: [
{
title: "test-variant",
inventory_quantity: 10,
sku: "test",
prices: [{ amount: "100", currency_code: "usd" }],
},
],
}
let error
try {
await productService.create(payload)
} catch (err) {
error = err
}
expect(error.message).toEqual(
"Product options length does not match variant options length. Product has 1 and variant has 0."
)
})
it("should create a product and variant without options", async () => {
const payload = {
title: "test-product",
handle: "test-product",
variants: [
{
title: "test-variant",
inventory_quantity: 10,
sku: "test",
prices: [{ amount: "100", currency_code: "usd" }],
},
],
}
const { id } = await productService.create(payload)
const result = await productService.retrieve(id, {
relations: [
"options",
"variants",
"variants.prices",
"variants.options",
],
})
expect(result).toEqual(
expect.objectContaining({
options: [],
variants: [
expect.objectContaining({
prices: [
expect.objectContaining({ amount: 100, currency_code: "usd" }),
],
}),
],
})
)
})
})
})
@@ -78,7 +78,7 @@ describe("ProductService", () => {
title: "Suit", title: "Suit",
options: [], options: [],
collection: { id: IdMap.getId("cat"), title: "Suits" }, collection: { id: IdMap.getId("cat"), title: "Suits" },
variants: product.variants, variants: product.variants ?? [],
}), }),
findOneWithRelations: () => ({ findOneWithRelations: () => ({
id: IdMap.getId("ironman"), id: IdMap.getId("ironman"),
@@ -117,6 +117,13 @@ describe("ProductService", () => {
Promise.resolve({ id: IdMap.getId("cat"), title: "Suits" }), Promise.resolve({ id: IdMap.getId("cat"), title: "Suits" }),
} }
const productVariantService = {
withTransaction: function () {
return this
},
create: (id, data) => Promise.resolve(data),
}
const productService = new ProductService({ const productService = new ProductService({
manager: MockManager, manager: MockManager,
productRepository, productRepository,
@@ -124,6 +131,7 @@ describe("ProductService", () => {
productCollectionService, productCollectionService,
productTagRepository, productTagRepository,
productTypeRepository, productTypeRepository,
productVariantService,
featureFlagRouter: new FlagRouter({}), featureFlagRouter: new FlagRouter({}),
}) })
@@ -131,7 +139,7 @@ describe("ProductService", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
it("successfully create a product", async () => { it("should successfully create a product", async () => {
await productService.create({ await productService.create({
title: "Suit", title: "Suit",
options: [], options: [],
@@ -158,16 +166,6 @@ describe("ProductService", () => {
expect(productRepository.create).toHaveBeenCalledTimes(1) expect(productRepository.create).toHaveBeenCalledTimes(1)
expect(productRepository.create).toHaveBeenCalledWith({ expect(productRepository.create).toHaveBeenCalledWith({
title: "Suit", title: "Suit",
variants: [
{
id: "test1",
title: "green",
},
{
id: "test2",
title: "blue",
},
],
}) })
expect(productTagRepository.upsertTags).toHaveBeenCalledTimes(1) expect(productTagRepository.upsertTags).toHaveBeenCalledTimes(1)
@@ -197,10 +195,12 @@ describe("ProductService", () => {
variants: [ variants: [
{ {
id: "test1", id: "test1",
options: [],
title: "green", title: "green",
}, },
{ {
id: "test2", id: "test2",
options: [],
title: "blue", title: "blue",
}, },
], ],
+26 -1
View File
@@ -1,6 +1,8 @@
import { import {
buildRelations, buildRelations,
buildSelects, FlagRouter, objectToStringPath buildSelects,
FlagRouter,
objectToStringPath,
} from "@medusajs/utils" } from "@medusajs/utils"
import { isDefined, MedusaError } from "medusa-core-utils" import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager, In } from "typeorm" import { EntityManager, In } from "typeorm"
@@ -38,6 +40,7 @@ import {
} from "../types/product" } from "../types/product"
import { buildQuery, isString, setMetadata } from "../utils" import { buildQuery, isString, setMetadata } from "../utils"
import EventBusService from "./event-bus" import EventBusService from "./event-bus"
import { CreateProductVariantInput } from "../types/product-variant"
type InjectedDependencies = { type InjectedDependencies = {
manager: EntityManager manager: EntityManager
@@ -429,6 +432,7 @@ class ProductService extends TransactionBaseService {
tags, tags,
type, type,
images, images,
variants,
sales_channels: salesChannels, sales_channels: salesChannels,
categories: categories, categories: categories,
...rest ...rest
@@ -501,6 +505,27 @@ class ProductService extends TransactionBaseService {
}) })
) )
if (variants) {
const toCreate = variants.map((variant) => {
return {
...variant,
options:
variant.options?.map((option, index) => {
return {
option_id: product.options[index].id,
...option,
}
}) ?? [],
}
})
product.variants = await this.productVariantService_
.withTransaction(manager)
.create(
product.id,
toCreate as unknown as CreateProductVariantInput[]
)
}
const result = await this.retrieve(product.id, { const result = await this.retrieve(product.id, {
relations: ["options"], relations: ["options"],
}) })