fix(product, types, workflows): Update product variant workflow (#5668)
**What** - Fix issues with update-variant workflow: - other variants than the updated variant are no longer removed - options are updated properly Co-authored-by: Riqwan Thamir <5105988+riqwan@users.noreply.github.com>
This commit is contained in:
co-authored by
Riqwan Thamir
parent
b25b29fe7b
commit
a39ce125cc
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/workflows": patch
|
||||||
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(workflows, product, types): Fix issues relating to update-variant workflow and options
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
"@medusajs/workflows": patch
|
"@medusajs/workflows": patch
|
||||||
"@medusajs/product": patch
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/pricing": patch
|
||||||
"@medusajs/medusa": patch
|
"@medusajs/medusa": patch
|
||||||
"@medusajs/types": patch
|
"@medusajs/types": patch
|
||||||
"@medusajs/utils": patch
|
"@medusajs/utils": patch
|
||||||
---
|
---
|
||||||
|
|
||||||
feat(medusa,types,workflows,utils,product): PricingModule Integration of PriceLists into Core
|
feat(medusa,types,workflows,utils,product,pricing): PricingModule Integration of PriceLists into Core
|
||||||
|
|||||||
@@ -125,7 +125,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DB_PASSWORD: postgres
|
DB_PASSWORD: postgres
|
||||||
DB_USERNAME: postgres
|
DB_USERNAME: postgres
|
||||||
SPLIT: ${{ steps['split-tests'].outputs['split'] }}
|
|
||||||
|
|
||||||
integration-tests-api:
|
integration-tests-api:
|
||||||
needs: setup
|
needs: setup
|
||||||
@@ -186,7 +185,8 @@ jobs:
|
|||||||
run: yarn test:integration:api
|
run: yarn test:integration:api
|
||||||
env:
|
env:
|
||||||
DB_PASSWORD: postgres
|
DB_PASSWORD: postgres
|
||||||
SPLIT: ${{ steps['split-tests'].outputs['split'] }}
|
DB_USERNAME: postgres
|
||||||
|
|
||||||
|
|
||||||
integration-tests-plugins:
|
integration-tests-plugins:
|
||||||
needs: setup
|
needs: setup
|
||||||
@@ -237,6 +237,7 @@ jobs:
|
|||||||
- name: Run plugin integration tests
|
- name: Run plugin integration tests
|
||||||
run: yarn test:integration:plugins
|
run: yarn test:integration:plugins
|
||||||
env:
|
env:
|
||||||
|
DB_USERNAME: postgres
|
||||||
DB_PASSWORD: postgres
|
DB_PASSWORD: postgres
|
||||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
|
||||||
@@ -286,4 +287,5 @@ jobs:
|
|||||||
- name: Run repository integration tests
|
- name: Run repository integration tests
|
||||||
run: yarn test:integration:repositories
|
run: yarn test:integration:repositories
|
||||||
env:
|
env:
|
||||||
|
DB_USERNAME: postgres
|
||||||
DB_PASSWORD: postgres
|
DB_PASSWORD: postgres
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { useApi } from "../../../../environment-helpers/use-api"
|
|
||||||
import { getContainer } from "../../../../environment-helpers/use-container"
|
|
||||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
import {
|
import {
|
||||||
simpleProductFactory,
|
simpleProductFactory,
|
||||||
@@ -7,11 +5,13 @@ import {
|
|||||||
} from "../../../../factories"
|
} from "../../../../factories"
|
||||||
|
|
||||||
import { AxiosInstance } from "axios"
|
import { AxiosInstance } from "axios"
|
||||||
import path from "path"
|
|
||||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
|
||||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ describe("POST /admin/products/:id/variants/:id", () => {
|
|||||||
{
|
{
|
||||||
options: [{ option_id: "test-product-option-1", value: "test" }],
|
options: [{ option_id: "test-product-option-1", value: "test" }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test 2" }],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
@@ -250,4 +253,231 @@ describe("POST /admin/products/:id/variants/:id", () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update variant option value", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: "test-product-option-1",
|
||||||
|
value: "updated",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
options: [
|
||||||
|
expect.objectContaining({
|
||||||
|
option_id: "test-product-option-1",
|
||||||
|
value: "updated",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: product.variants[1].id,
|
||||||
|
options: [
|
||||||
|
expect.objectContaining({
|
||||||
|
option_id: "test-product-option-1",
|
||||||
|
value: "test 2",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update variant metadata", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
metadata: {
|
||||||
|
test: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
metadata: {
|
||||||
|
test: "string",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove options not present in update", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-multiple-options",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [
|
||||||
|
{ option_id: "test-product-multi-option-1", value: "test" },
|
||||||
|
{ option_id: "test-product-multi-option-2", value: "test value" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-multi-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-product-multi-option-2",
|
||||||
|
title: "Test option 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: "test-product-multi-option-1",
|
||||||
|
value: "updated",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
options: [
|
||||||
|
expect.objectContaining({
|
||||||
|
option_id: "test-product-multi-option-1",
|
||||||
|
value: "updated",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update several options in the same api call", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-multiple-options",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [
|
||||||
|
{ option_id: "test-product-multi-option-1", value: "test" },
|
||||||
|
{ option_id: "test-product-multi-option-2", value: "test value" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-multi-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-product-multi-option-2",
|
||||||
|
title: "Test option 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: "test-product-multi-option-1",
|
||||||
|
value: "updated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option_id: "test-product-multi-option-2",
|
||||||
|
value: "updated 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
options: [
|
||||||
|
expect.objectContaining({
|
||||||
|
option_id: "test-product-multi-option-1",
|
||||||
|
value: "updated",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
option_id: "test-product-multi-option-2",
|
||||||
|
value: "updated 2",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
ProductCollectionRepository,
|
ProductCollectionRepository,
|
||||||
ProductImageRepository,
|
ProductImageRepository,
|
||||||
ProductOptionRepository,
|
ProductOptionRepository,
|
||||||
|
ProductOptionValueRepository,
|
||||||
ProductRepository,
|
ProductRepository,
|
||||||
ProductTagRepository,
|
ProductTagRepository,
|
||||||
ProductTypeRepository,
|
ProductTypeRepository,
|
||||||
@@ -17,6 +18,7 @@ import {
|
|||||||
ProductImageService,
|
ProductImageService,
|
||||||
ProductModuleService,
|
ProductModuleService,
|
||||||
ProductOptionService,
|
ProductOptionService,
|
||||||
|
ProductOptionValueService,
|
||||||
ProductService,
|
ProductService,
|
||||||
ProductTagService,
|
ProductTagService,
|
||||||
ProductTypeService,
|
ProductTypeService,
|
||||||
@@ -48,6 +50,7 @@ export default async ({
|
|||||||
productImageService: asClass(ProductImageService).singleton(),
|
productImageService: asClass(ProductImageService).singleton(),
|
||||||
productTypeService: asClass(ProductTypeService).singleton(),
|
productTypeService: asClass(ProductTypeService).singleton(),
|
||||||
productOptionService: asClass(ProductOptionService).singleton(),
|
productOptionService: asClass(ProductOptionService).singleton(),
|
||||||
|
productOptionValueService: asClass(ProductOptionValueService).singleton(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (customRepositories) {
|
if (customRepositories) {
|
||||||
@@ -69,6 +72,9 @@ function loadDefaultRepositories({ container }) {
|
|||||||
productTagRepository: asClass(ProductTagRepository).singleton(),
|
productTagRepository: asClass(ProductTagRepository).singleton(),
|
||||||
productTypeRepository: asClass(ProductTypeRepository).singleton(),
|
productTypeRepository: asClass(ProductTypeRepository).singleton(),
|
||||||
productOptionRepository: asClass(ProductOptionRepository).singleton(),
|
productOptionRepository: asClass(ProductOptionRepository).singleton(),
|
||||||
|
productOptionValueRepository: asClass(
|
||||||
|
ProductOptionValueRepository
|
||||||
|
).singleton(),
|
||||||
productVariantRepository: asClass(ProductVariantRepository).singleton(),
|
productVariantRepository: asClass(ProductVariantRepository).singleton(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export { default as ProductTag } from "./product-tag"
|
|||||||
export { default as ProductType } from "./product-type"
|
export { default as ProductType } from "./product-type"
|
||||||
export { default as ProductVariant } from "./product-variant"
|
export { default as ProductVariant } from "./product-variant"
|
||||||
export { default as ProductOption } from "./product-option"
|
export { default as ProductOption } from "./product-option"
|
||||||
|
export { default as ProductOptionValue } from "./product-option-value"
|
||||||
export { default as Image } from "./product-image"
|
export { default as Image } from "./product-image"
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export { ProductCategoryRepository } from "./product-category"
|
|||||||
export { ProductImageRepository } from "./product-image"
|
export { ProductImageRepository } from "./product-image"
|
||||||
export { ProductTypeRepository } from "./product-type"
|
export { ProductTypeRepository } from "./product-type"
|
||||||
export { ProductOptionRepository } from "./product-option"
|
export { ProductOptionRepository } from "./product-option"
|
||||||
|
export { ProductOptionValueRepository } from "./product-option-value"
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import { Context, DAL } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
CreateProductOptionValueDTO,
|
||||||
|
UpdateProductOptionValueDTO,
|
||||||
|
} from "../types/services/product-option-value"
|
||||||
|
|
||||||
|
import { DALUtils } from "@medusajs/utils"
|
||||||
|
import { FilterQuery as MikroFilterQuery } from "@mikro-orm/core/typings"
|
||||||
|
import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver"
|
||||||
|
import { ProductOptionValue } from "@models"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
|
||||||
|
export class ProductOptionValueRepository extends DALUtils.MikroOrmBaseRepository {
|
||||||
|
protected readonly manager_: SqlEntityManager
|
||||||
|
|
||||||
|
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
super(...arguments)
|
||||||
|
this.manager_ = manager
|
||||||
|
}
|
||||||
|
|
||||||
|
async find(
|
||||||
|
findOptions: DAL.FindOptions<ProductOptionValue> = { where: {} },
|
||||||
|
context: Context = {}
|
||||||
|
): Promise<ProductOptionValue[]> {
|
||||||
|
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||||
|
const findOptions_ = { ...findOptions }
|
||||||
|
|
||||||
|
findOptions_.options ??= {}
|
||||||
|
|
||||||
|
return await manager.find(
|
||||||
|
ProductOptionValue,
|
||||||
|
findOptions_.where as MikroFilterQuery<ProductOptionValue>,
|
||||||
|
findOptions_.options as MikroOptions<ProductOptionValue>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsert(
|
||||||
|
optionValues: (UpdateProductOptionValueDTO | CreateProductOptionValueDTO)[],
|
||||||
|
context: Context = {}
|
||||||
|
): Promise<ProductOptionValue[]> {
|
||||||
|
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
|
const optionValueIds: string[] = []
|
||||||
|
|
||||||
|
for (const optionValue of optionValues) {
|
||||||
|
if (optionValue.id) {
|
||||||
|
optionValueIds.push(optionValue.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingOptionValues = await this.find(
|
||||||
|
{
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
$in: optionValueIds,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
const existingOptionValuesMap = new Map(
|
||||||
|
existingOptionValues.map<[string, ProductOptionValue]>((optionValue) => [
|
||||||
|
optionValue.id,
|
||||||
|
optionValue,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const upsertedOptionValues: ProductOptionValue[] = []
|
||||||
|
const optionValuesToCreate: ProductOptionValue[] = []
|
||||||
|
const optionValuesToUpdate: ProductOptionValue[] = []
|
||||||
|
|
||||||
|
optionValues.forEach(({ option_id, ...optionValue }) => {
|
||||||
|
const existingOptionValue = optionValue.id
|
||||||
|
? existingOptionValuesMap.get(optionValue.id)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
if (optionValue.id && existingOptionValue) {
|
||||||
|
const updatedOptionValue = manager.assign(existingOptionValue, {
|
||||||
|
option: option_id,
|
||||||
|
...optionValue,
|
||||||
|
})
|
||||||
|
optionValuesToUpdate.push(updatedOptionValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOptionValue = manager.create(ProductOptionValue, {
|
||||||
|
option: option_id,
|
||||||
|
variant: (optionValue as CreateProductOptionValueDTO).variant_id,
|
||||||
|
...optionValue,
|
||||||
|
})
|
||||||
|
optionValuesToCreate.push(newOptionValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (optionValuesToCreate.length) {
|
||||||
|
manager.persist(optionValuesToCreate)
|
||||||
|
upsertedOptionValues.push(...optionValuesToCreate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionValuesToUpdate.length) {
|
||||||
|
manager.persist(optionValuesToUpdate)
|
||||||
|
upsertedOptionValues.push(...optionValuesToUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return upsertedOptionValues
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(ids: string[], context: Context = {}): Promise<void> {
|
||||||
|
const manager = this.getActiveManager<SqlEntityManager>(context)
|
||||||
|
await manager.nativeDelete(ProductOptionValue, { id: { $in: ids } }, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,3 +7,4 @@ export { default as ProductVariantService } from "./product-variant"
|
|||||||
export { default as ProductTypeService } from "./product-type"
|
export { default as ProductTypeService } from "./product-type"
|
||||||
export { default as ProductOptionService } from "./product-option"
|
export { default as ProductOptionService } from "./product-option"
|
||||||
export { default as ProductImageService } from "./product-image"
|
export { default as ProductImageService } from "./product-image"
|
||||||
|
export { default as ProductOptionValueService } from "./product-option-value"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
ProductCategory,
|
ProductCategory,
|
||||||
ProductCollection,
|
ProductCollection,
|
||||||
ProductOption,
|
ProductOption,
|
||||||
|
ProductOptionValue,
|
||||||
ProductTag,
|
ProductTag,
|
||||||
ProductType,
|
ProductType,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
@@ -24,6 +25,7 @@ import {
|
|||||||
ProductCategoryService,
|
ProductCategoryService,
|
||||||
ProductCollectionService,
|
ProductCollectionService,
|
||||||
ProductOptionService,
|
ProductOptionService,
|
||||||
|
ProductOptionValueService,
|
||||||
ProductService,
|
ProductService,
|
||||||
ProductTagService,
|
ProductTagService,
|
||||||
ProductTypeService,
|
ProductTypeService,
|
||||||
@@ -53,6 +55,8 @@ import {
|
|||||||
} from "../types/services/product"
|
} from "../types/services/product"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
arrayDifference,
|
||||||
|
groupBy,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
isDefined,
|
isDefined,
|
||||||
@@ -68,6 +72,10 @@ import {
|
|||||||
joinerConfig,
|
joinerConfig,
|
||||||
LinkableKeys,
|
LinkableKeys,
|
||||||
} from "./../joiner-config"
|
} from "./../joiner-config"
|
||||||
|
import {
|
||||||
|
CreateProductOptionValueDTO,
|
||||||
|
UpdateProductOptionValueDTO,
|
||||||
|
} from "../types/services/product-option-value"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -79,6 +87,7 @@ type InjectedDependencies = {
|
|||||||
productImageService: ProductImageService<any>
|
productImageService: ProductImageService<any>
|
||||||
productTypeService: ProductTypeService<any>
|
productTypeService: ProductTypeService<any>
|
||||||
productOptionService: ProductOptionService<any>
|
productOptionService: ProductOptionService<any>
|
||||||
|
productOptionValueService: ProductOptionValueService<any>
|
||||||
eventBusModuleService?: IEventBusModuleService
|
eventBusModuleService?: IEventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +99,8 @@ export default class ProductModuleService<
|
|||||||
TProductCategory extends ProductCategory = ProductCategory,
|
TProductCategory extends ProductCategory = ProductCategory,
|
||||||
TProductImage extends Image = Image,
|
TProductImage extends Image = Image,
|
||||||
TProductType extends ProductType = ProductType,
|
TProductType extends ProductType = ProductType,
|
||||||
TProductOption extends ProductOption = ProductOption
|
TProductOption extends ProductOption = ProductOption,
|
||||||
|
TProductOptionValue extends ProductOptionValue = ProductOptionValue
|
||||||
> implements ProductTypes.IProductModuleService
|
> implements ProductTypes.IProductModuleService
|
||||||
{
|
{
|
||||||
protected baseRepository_: DAL.RepositoryService
|
protected baseRepository_: DAL.RepositoryService
|
||||||
@@ -108,6 +118,8 @@ export default class ProductModuleService<
|
|||||||
protected readonly productImageService_: ProductImageService<TProductImage>
|
protected readonly productImageService_: ProductImageService<TProductImage>
|
||||||
protected readonly productTypeService_: ProductTypeService<TProductType>
|
protected readonly productTypeService_: ProductTypeService<TProductType>
|
||||||
protected readonly productOptionService_: ProductOptionService<TProductOption>
|
protected readonly productOptionService_: ProductOptionService<TProductOption>
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
protected readonly productOptionValueService_: ProductOptionValueService<TProductOptionValue>
|
||||||
protected readonly eventBusModuleService_?: IEventBusModuleService
|
protected readonly eventBusModuleService_?: IEventBusModuleService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -121,6 +133,7 @@ export default class ProductModuleService<
|
|||||||
productImageService,
|
productImageService,
|
||||||
productTypeService,
|
productTypeService,
|
||||||
productOptionService,
|
productOptionService,
|
||||||
|
productOptionValueService,
|
||||||
eventBusModuleService,
|
eventBusModuleService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
@@ -134,6 +147,7 @@ export default class ProductModuleService<
|
|||||||
this.productImageService_ = productImageService
|
this.productImageService_ = productImageService
|
||||||
this.productTypeService_ = productTypeService
|
this.productTypeService_ = productTypeService
|
||||||
this.productOptionService_ = productOptionService
|
this.productOptionService_ = productOptionService
|
||||||
|
this.productOptionValueService_ = productOptionValueService
|
||||||
this.eventBusModuleService_ = eventBusModuleService
|
this.eventBusModuleService_ = eventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +321,131 @@ export default class ProductModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async updateVariants(
|
||||||
|
data: ProductTypes.UpdateProductVariantOnlyDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<ProductTypes.ProductVariantDTO[]> {
|
||||||
|
const productVariants = await this.updateVariants_(data, sharedContext)
|
||||||
|
|
||||||
|
const updatedVariants = await this.baseRepository_.serialize<
|
||||||
|
ProductTypes.ProductVariantDTO[]
|
||||||
|
>(productVariants, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return updatedVariants
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
protected async updateVariants_(
|
||||||
|
data: ProductTypes.UpdateProductVariantOnlyDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TProductVariant[]> {
|
||||||
|
const variantIdsToUpdate = data.map(({ id }) => id)
|
||||||
|
const variants = await this.listVariants(
|
||||||
|
{ id: variantIdsToUpdate },
|
||||||
|
{ relations: ["options", "options.option"] },
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
const variantsMap = new Map(
|
||||||
|
variants.map((variant) => [variant.id, variant])
|
||||||
|
)
|
||||||
|
|
||||||
|
if (variants.length !== data.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Cannot update non-existing variants with ids: ${arrayDifference(
|
||||||
|
variantIdsToUpdate,
|
||||||
|
[...variantsMap.keys()]
|
||||||
|
).join(", ")}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionValuesToUpsert: (
|
||||||
|
| CreateProductOptionValueDTO
|
||||||
|
| UpdateProductOptionValueDTO
|
||||||
|
)[] = []
|
||||||
|
const optionsValuesToDelete: string[] = []
|
||||||
|
|
||||||
|
const toUpdate = data.map(({ id, options, ...rest }) => {
|
||||||
|
const variant = variantsMap.get(id)!
|
||||||
|
|
||||||
|
const toUpdate: UpdateProductVariantDTO = {
|
||||||
|
id,
|
||||||
|
product_id: variant.product_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.length) {
|
||||||
|
const optionIdToUpdateValueMap = new Map(
|
||||||
|
options.map(({ option, option_id, value }) => {
|
||||||
|
const computedOptionId = option_id ?? option.id ?? option
|
||||||
|
return [computedOptionId, value]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const existingOptionValue of variant.options) {
|
||||||
|
if (!optionIdToUpdateValueMap.has(existingOptionValue.option.id)) {
|
||||||
|
optionsValuesToDelete.push(existingOptionValue.id)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
optionValuesToUpsert.push({
|
||||||
|
id: existingOptionValue.id,
|
||||||
|
option_id: existingOptionValue.option.id,
|
||||||
|
value: optionIdToUpdateValueMap.get(existingOptionValue.option.id)!,
|
||||||
|
})
|
||||||
|
optionIdToUpdateValueMap.delete(existingOptionValue.option.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [option_id, value] of optionIdToUpdateValueMap.entries()) {
|
||||||
|
optionValuesToUpsert.push({
|
||||||
|
option_id,
|
||||||
|
value,
|
||||||
|
variant_id: id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(rest)) {
|
||||||
|
if (variant[key] !== value) {
|
||||||
|
toUpdate[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toUpdate
|
||||||
|
})
|
||||||
|
|
||||||
|
const groups = groupBy(toUpdate, "product_id")
|
||||||
|
|
||||||
|
const [, , productVariants]: [
|
||||||
|
void,
|
||||||
|
TProductOptionValue[],
|
||||||
|
TProductVariant[][]
|
||||||
|
] = await promiseAll([
|
||||||
|
await this.productOptionValueService_.delete(
|
||||||
|
optionsValuesToDelete,
|
||||||
|
sharedContext
|
||||||
|
),
|
||||||
|
await this.productOptionValueService_.upsert(
|
||||||
|
optionValuesToUpsert,
|
||||||
|
sharedContext
|
||||||
|
),
|
||||||
|
await promiseAll(
|
||||||
|
[...groups.entries()].map(async ([product_id, update]) => {
|
||||||
|
return await this.productVariantService_.update(
|
||||||
|
product_id,
|
||||||
|
update.map(({ product_id, ...update }) => update),
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
return productVariants.flat()
|
||||||
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
async retrieveTag(
|
async retrieveTag(
|
||||||
tagId: string,
|
tagId: string,
|
||||||
@@ -1098,7 +1237,11 @@ export default class ProductModuleService<
|
|||||||
if (!productData.thumbnail && productData.images?.length) {
|
if (!productData.thumbnail && productData.images?.length) {
|
||||||
productData.thumbnail = isString(productData.images[0])
|
productData.thumbnail = isString(productData.images[0])
|
||||||
? (productData.images[0] as string)
|
? (productData.images[0] as string)
|
||||||
: (productData.images[0] as { url: string }).url
|
: (
|
||||||
|
productData.images[0] as {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
).url
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productData.images?.length) {
|
if (productData.images?.length) {
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { ProductOptionValue } from "@models"
|
||||||
|
import { Context, DAL } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ProductOptionRepository,
|
||||||
|
ProductOptionValueRepository,
|
||||||
|
} from "@repositories"
|
||||||
|
import { InjectTransactionManager, MedusaContext } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
CreateProductOptionValueDTO,
|
||||||
|
UpdateProductOptionValueDTO,
|
||||||
|
} from "../types/services/product-option-value"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
productOptionValueRepository: DAL.RepositoryService
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ProductOptionValueService<
|
||||||
|
TEntity extends ProductOptionValue = ProductOptionValue
|
||||||
|
> {
|
||||||
|
protected readonly productOptionValueRepository_: DAL.RepositoryService
|
||||||
|
|
||||||
|
constructor({ productOptionValueRepository }: InjectedDependencies) {
|
||||||
|
this.productOptionValueRepository_ =
|
||||||
|
productOptionValueRepository as ProductOptionRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("productOptionValueRepository_")
|
||||||
|
async delete(
|
||||||
|
ids: string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.productOptionValueRepository_.delete(ids, sharedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("productOptionValueRepository_")
|
||||||
|
async upsert(
|
||||||
|
data: (UpdateProductOptionValueDTO | CreateProductOptionValueDTO)[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<TEntity[]> {
|
||||||
|
return (await (
|
||||||
|
this.productOptionValueRepository_ as ProductOptionValueRepository
|
||||||
|
).upsert!(data, sharedContext)) as TEntity[]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export interface UpdateProductOptionValueDTO {
|
||||||
|
id: string
|
||||||
|
value: string
|
||||||
|
option_id: string
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductOptionValueDTO {
|
||||||
|
id?: string
|
||||||
|
value: string
|
||||||
|
option_id: string
|
||||||
|
variant_id: string
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { CreateProductVariantOptionDTO } from "@medusajs/types"
|
|||||||
|
|
||||||
export interface UpdateProductVariantDTO {
|
export interface UpdateProductVariantDTO {
|
||||||
id: string
|
id: string
|
||||||
|
product_id: string
|
||||||
title?: string
|
title?: string
|
||||||
sku?: string
|
sku?: string
|
||||||
barcode?: string
|
barcode?: string
|
||||||
@@ -18,6 +19,6 @@ export interface UpdateProductVariantDTO {
|
|||||||
length?: number
|
length?: number
|
||||||
height?: number
|
height?: number
|
||||||
width?: number
|
width?: number
|
||||||
options?: CreateProductVariantOptionDTO[]
|
options?: (CreateProductVariantOptionDTO & { id?: string })[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,43 +83,43 @@ export interface ProductDTO {
|
|||||||
material?: string | null
|
material?: string | null
|
||||||
/**
|
/**
|
||||||
* The associated product collection.
|
* The associated product collection.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
collection: ProductCollectionDTO
|
collection: ProductCollectionDTO
|
||||||
/**
|
/**
|
||||||
* The associated product categories.
|
* The associated product categories.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
categories?: ProductCategoryDTO[] | null
|
categories?: ProductCategoryDTO[] | null
|
||||||
/**
|
/**
|
||||||
* The associated product type.
|
* The associated product type.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
type: ProductTypeDTO[]
|
type: ProductTypeDTO[]
|
||||||
/**
|
/**
|
||||||
* The associated product tags.
|
* The associated product tags.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
tags: ProductTagDTO[]
|
tags: ProductTagDTO[]
|
||||||
/**
|
/**
|
||||||
* The associated product variants.
|
* The associated product variants.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
variants: ProductVariantDTO[]
|
variants: ProductVariantDTO[]
|
||||||
/**
|
/**
|
||||||
* The associated product options.
|
* The associated product options.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
options: ProductOptionDTO[]
|
options: ProductOptionDTO[]
|
||||||
/**
|
/**
|
||||||
* The associated product images.
|
* The associated product images.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
images: ProductImageDTO[]
|
images: ProductImageDTO[]
|
||||||
@@ -226,17 +226,17 @@ export interface ProductVariantDTO {
|
|||||||
width?: number | null
|
width?: number | null
|
||||||
/**
|
/**
|
||||||
* The associated product options.
|
* The associated product options.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
options: ProductOptionValueDTO
|
options: ProductOptionValueDTO[]
|
||||||
/**
|
/**
|
||||||
* Holds custom data in key-value pairs.
|
* Holds custom data in key-value pairs.
|
||||||
*/
|
*/
|
||||||
metadata?: Record<string, unknown> | null
|
metadata?: Record<string, unknown> | null
|
||||||
/**
|
/**
|
||||||
* The associated product.
|
* The associated product.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
product: ProductDTO
|
product: ProductDTO
|
||||||
@@ -298,13 +298,13 @@ export interface ProductCategoryDTO {
|
|||||||
rank?: number
|
rank?: number
|
||||||
/**
|
/**
|
||||||
* The associated parent category.
|
* The associated parent category.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
parent_category?: ProductCategoryDTO
|
parent_category?: ProductCategoryDTO
|
||||||
/**
|
/**
|
||||||
* The associated child categories.
|
* The associated child categories.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
category_children: ProductCategoryDTO[]
|
category_children: ProductCategoryDTO[]
|
||||||
@@ -410,7 +410,7 @@ export interface ProductTagDTO {
|
|||||||
metadata?: Record<string, unknown> | null
|
metadata?: Record<string, unknown> | null
|
||||||
/**
|
/**
|
||||||
* The associated products.
|
* The associated products.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
products?: ProductDTO[]
|
products?: ProductDTO[]
|
||||||
@@ -444,7 +444,7 @@ export interface ProductCollectionDTO {
|
|||||||
deleted_at?: string | Date
|
deleted_at?: string | Date
|
||||||
/**
|
/**
|
||||||
* The associated products.
|
* The associated products.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
products?: ProductDTO[]
|
products?: ProductDTO[]
|
||||||
@@ -478,7 +478,7 @@ export interface ProductTypeDTO {
|
|||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product option's data.
|
* A product option's data.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface ProductOptionDTO {
|
export interface ProductOptionDTO {
|
||||||
/**
|
/**
|
||||||
@@ -491,13 +491,13 @@ export interface ProductOptionDTO {
|
|||||||
title: string
|
title: string
|
||||||
/**
|
/**
|
||||||
* The associated product.
|
* The associated product.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
product: ProductDTO
|
product: ProductDTO
|
||||||
/**
|
/**
|
||||||
* The associated product option values.
|
* The associated product option values.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
values: ProductOptionValueDTO[]
|
values: ProductOptionValueDTO[]
|
||||||
@@ -563,13 +563,13 @@ export interface ProductOptionValueDTO {
|
|||||||
value: string
|
value: string
|
||||||
/**
|
/**
|
||||||
* The associated product option.
|
* The associated product option.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
option: ProductOptionDTO
|
option: ProductOptionDTO
|
||||||
/**
|
/**
|
||||||
* The associated product variant.
|
* The associated product variant.
|
||||||
*
|
*
|
||||||
* @expandable
|
* @expandable
|
||||||
*/
|
*/
|
||||||
variant: ProductVariantDTO
|
variant: ProductVariantDTO
|
||||||
@@ -612,7 +612,7 @@ export interface FilterableProductProps
|
|||||||
/**
|
/**
|
||||||
* Filters on a product's tags.
|
* Filters on a product's tags.
|
||||||
*/
|
*/
|
||||||
tags?: {
|
tags?: {
|
||||||
/**
|
/**
|
||||||
* Values to filter product tags by.
|
* Values to filter product tags by.
|
||||||
*/
|
*/
|
||||||
@@ -761,7 +761,7 @@ export interface FilterableProductVariantProps
|
|||||||
/**
|
/**
|
||||||
* Filter product variants by their associated options.
|
* Filter product variants by their associated options.
|
||||||
*/
|
*/
|
||||||
options?: {
|
options?: {
|
||||||
/**
|
/**
|
||||||
* IDs to filter options by.
|
* IDs to filter options by.
|
||||||
*/
|
*/
|
||||||
@@ -986,6 +986,7 @@ export interface CreateProductVariantOptionDTO {
|
|||||||
* The value of a product variant option.
|
* The value of a product variant option.
|
||||||
*/
|
*/
|
||||||
value: string
|
value: string
|
||||||
|
|
||||||
option_id?: string
|
option_id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,13 @@ import {
|
|||||||
UpdateProductOptionDTO,
|
UpdateProductOptionDTO,
|
||||||
UpdateProductTagDTO,
|
UpdateProductTagDTO,
|
||||||
UpdateProductTypeDTO,
|
UpdateProductTypeDTO,
|
||||||
|
UpdateProductVariantDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
import { FindConfig } from "../common"
|
|
||||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
|
import { FindConfig } from "../common"
|
||||||
|
import { ModuleJoinerConfig } from "../modules-sdk"
|
||||||
|
|
||||||
export interface IProductModuleService {
|
export interface IProductModuleService {
|
||||||
/**
|
/**
|
||||||
@@ -1490,6 +1491,11 @@ export interface IProductModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ProductVariantDTO[]>
|
): Promise<ProductVariantDTO[]>
|
||||||
|
|
||||||
|
updateVariants(
|
||||||
|
data: UpdateProductVariantDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductVariantDTO[]>
|
||||||
|
|
||||||
createVariants(
|
createVariants(
|
||||||
data: CreateProductVariantDTO[],
|
data: CreateProductVariantDTO[],
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
import { ProductTypes } from "@medusajs/types"
|
import { ProductTypes, UpdateProductVariantOnlyDTO } from "@medusajs/types"
|
||||||
import { WorkflowArguments } from "../../helper"
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
type HandlerInput = {
|
type HandlerInput = {
|
||||||
@@ -14,21 +14,22 @@ export async function updateProductVariants({
|
|||||||
> {
|
> {
|
||||||
const { productVariantsMap } = data
|
const { productVariantsMap } = data
|
||||||
const productsVariants: ProductTypes.UpdateProductVariantDTO[] = []
|
const productsVariants: ProductTypes.UpdateProductVariantDTO[] = []
|
||||||
const updateProductsData: ProductTypes.UpdateProductDTO[] = []
|
const updateVariantsData: ProductTypes.UpdateProductVariantOnlyDTO[] = []
|
||||||
const productModuleService: ProductTypes.IProductModuleService =
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
for (const [productId, variantsData = []] of productVariantsMap) {
|
for (const [product_id, variantsUpdateData = []] of productVariantsMap) {
|
||||||
updateProductsData.push({
|
updateVariantsData.push(
|
||||||
id: productId,
|
...(variantsUpdateData as unknown as UpdateProductVariantOnlyDTO[]).map(
|
||||||
variants: variantsData,
|
(update) => ({ ...update, product_id })
|
||||||
})
|
)
|
||||||
|
)
|
||||||
|
|
||||||
productsVariants.push(...variantsData)
|
productsVariants.push(...variantsUpdateData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateProductsData.length) {
|
if (updateVariantsData.length) {
|
||||||
await productModuleService.update(updateProductsData)
|
await productModuleService.updateVariants(updateVariantsData)
|
||||||
}
|
}
|
||||||
|
|
||||||
return productsVariants
|
return productsVariants
|
||||||
|
|||||||
Reference in New Issue
Block a user