feat: Use tag ids instead of values wherever possible (#8394)
This commit is contained in:
1 parent
71f0d24359
commit
3a068c6b27
26 files changed
+225
-195
No files matched your search
@@ -8,7 +8,6 @@ export const getProductFixture = (
|
|||||||
status: "draft",
|
status: "draft",
|
||||||
// BREAKING: Images input changed from string[] to {url: string}[]
|
// BREAKING: Images input changed from string[] to {url: string}[]
|
||||||
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
|
||||||
// BREAKING: Options input changed from {title: string}[] to {title: string, values: string[]}[]
|
// BREAKING: Options input changed from {title: string}[] to {title: string, values: string[]}[]
|
||||||
options: [
|
options: [
|
||||||
{ title: "size", values: ["large", "small"] },
|
{ title: "size", values: ["large", "small"] },
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ medusaIntegrationTestRunner({
|
|||||||
let baseType
|
let baseType
|
||||||
let baseRegion
|
let baseRegion
|
||||||
let baseCategory
|
let baseCategory
|
||||||
|
let baseTag1
|
||||||
|
let baseTag2
|
||||||
|
let newTag
|
||||||
|
|
||||||
let eventBus: IEventBusModuleService
|
let eventBus: IEventBusModuleService
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -102,6 +105,22 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.product_category
|
).data.product_category
|
||||||
|
|
||||||
|
baseTag1 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "123" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
baseTag2 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "456" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
newTag = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/product-tags",
|
||||||
|
{ value: "new-tag" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
baseProduct = (
|
baseProduct = (
|
||||||
await api.post(
|
await api.post(
|
||||||
"/admin/products",
|
"/admin/products",
|
||||||
@@ -111,6 +130,7 @@ medusaIntegrationTestRunner({
|
|||||||
collection_id: baseCollection.id,
|
collection_id: baseCollection.id,
|
||||||
type_id: baseType.id,
|
type_id: baseType.id,
|
||||||
categories: [{ id: baseCategory.id }],
|
categories: [{ id: baseCategory.id }],
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: "Test variant",
|
title: "Test variant",
|
||||||
@@ -166,7 +186,7 @@ medusaIntegrationTestRunner({
|
|||||||
getProductFixture({
|
getProductFixture({
|
||||||
title: "Proposed product",
|
title: "Proposed product",
|
||||||
status: "proposed",
|
status: "proposed",
|
||||||
tags: [{ value: "new-tag" }],
|
tags: [{ id: newTag.id }],
|
||||||
type_id: baseType.id,
|
type_id: baseType.id,
|
||||||
}),
|
}),
|
||||||
adminHeaders
|
adminHeaders
|
||||||
@@ -258,6 +278,7 @@ medusaIntegrationTestRunner({
|
|||||||
"/admin/products",
|
"/admin/products",
|
||||||
getProductFixture({
|
getProductFixture({
|
||||||
title: "Product with prices",
|
title: "Product with prices",
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: "Test variant",
|
title: "Test variant",
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ medusaIntegrationTestRunner({
|
|||||||
let baseProduct
|
let baseProduct
|
||||||
let baseRegion
|
let baseRegion
|
||||||
let baseCategory
|
let baseCategory
|
||||||
|
let baseTag1
|
||||||
|
let baseTag2
|
||||||
|
let baseTag3
|
||||||
|
let newTag
|
||||||
|
|
||||||
let eventBus: IEventBusModuleService
|
let eventBus: IEventBusModuleService
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -57,11 +61,32 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.product_type
|
).data.product_type
|
||||||
|
|
||||||
|
baseTag1 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "123" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
baseTag2 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "123_1" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
baseTag3 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "456" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
newTag = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/product-tags",
|
||||||
|
{ value: "new-tag" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
baseProduct = (
|
baseProduct = (
|
||||||
await api.post(
|
await api.post(
|
||||||
"/admin/products",
|
"/admin/products",
|
||||||
getProductFixture({
|
getProductFixture({
|
||||||
title: "Base product",
|
title: "Base product",
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
}),
|
}),
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ medusaIntegrationTestRunner({
|
|||||||
let publishedCollection
|
let publishedCollection
|
||||||
|
|
||||||
let baseType
|
let baseType
|
||||||
|
let baseTag1
|
||||||
|
let baseTag2
|
||||||
|
let newTag
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createAdminUser(dbConnection, adminHeaders, getContainer())
|
await createAdminUser(dbConnection, adminHeaders, getContainer())
|
||||||
@@ -46,6 +49,22 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.product_type
|
).data.product_type
|
||||||
|
|
||||||
|
baseTag1 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "123" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
baseTag2 = (
|
||||||
|
await api.post("/admin/product-tags", { value: "456" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
|
newTag = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/product-tags",
|
||||||
|
{ value: "new-tag" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.product_tag
|
||||||
|
|
||||||
baseProduct = (
|
baseProduct = (
|
||||||
await api.post(
|
await api.post(
|
||||||
"/admin/products",
|
"/admin/products",
|
||||||
@@ -54,6 +73,7 @@ medusaIntegrationTestRunner({
|
|||||||
collection_id: baseCollection.id,
|
collection_id: baseCollection.id,
|
||||||
// BREAKING: Type input changed from {type: {value: string}} to {type_id: string}
|
// BREAKING: Type input changed from {type: {value: string}} to {type_id: string}
|
||||||
type_id: baseType.id,
|
type_id: baseType.id,
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
}),
|
}),
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
@@ -65,7 +85,7 @@ medusaIntegrationTestRunner({
|
|||||||
getProductFixture({
|
getProductFixture({
|
||||||
title: "Proposed product",
|
title: "Proposed product",
|
||||||
status: "proposed",
|
status: "proposed",
|
||||||
tags: [{ value: "new-tag" }],
|
tags: [{ id: newTag.id }],
|
||||||
type_id: baseType.id,
|
type_id: baseType.id,
|
||||||
}),
|
}),
|
||||||
adminHeaders
|
adminHeaders
|
||||||
@@ -79,6 +99,7 @@ medusaIntegrationTestRunner({
|
|||||||
title: "Published product",
|
title: "Published product",
|
||||||
status: "published",
|
status: "published",
|
||||||
collection_id: publishedCollection.id,
|
collection_id: publishedCollection.id,
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
}),
|
}),
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
@@ -507,7 +528,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("returns a list of products with tags", async () => {
|
it("returns a list of products with tags", async () => {
|
||||||
const response = await api.get(
|
const response = await api.get(
|
||||||
`/admin/products?tags[]=${baseProduct.tags[0].id}`,
|
`/admin/products?tag_id[]=${baseProduct.tags[0].id}`,
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -534,7 +555,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("returns a list of products with tags in a collection", async () => {
|
it("returns a list of products with tags in a collection", async () => {
|
||||||
const response = await api.get(
|
const response = await api.get(
|
||||||
`/admin/products?collection_id[]=${baseCollection.id}&tags[]=${baseProduct.tags[0].id}`,
|
`/admin/products?collection_id[]=${baseCollection.id}&tag_id[]=${baseProduct.tags[0].id}`,
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1082,6 +1103,7 @@ medusaIntegrationTestRunner({
|
|||||||
title: "Test create",
|
title: "Test create",
|
||||||
collection_id: baseCollection.id,
|
collection_id: baseCollection.id,
|
||||||
type_id: baseType.id,
|
type_id: baseType.id,
|
||||||
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
adminHeaders
|
adminHeaders
|
||||||
@@ -1276,7 +1298,7 @@ medusaIntegrationTestRunner({
|
|||||||
description: "test-product-description",
|
description: "test-product-description",
|
||||||
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
||||||
collection_id: baseCollection.id,
|
collection_id: baseCollection.id,
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: "Test variant",
|
title: "Test variant",
|
||||||
@@ -1306,7 +1328,7 @@ medusaIntegrationTestRunner({
|
|||||||
description: "test-product-description 1",
|
description: "test-product-description 1",
|
||||||
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
images: [{ url: "test-image.png" }, { url: "test-image-2.png" }],
|
||||||
collection_id: baseCollection.id,
|
collection_id: baseCollection.id,
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: "Test variant 1",
|
title: "Test variant 1",
|
||||||
@@ -1406,7 +1428,7 @@ medusaIntegrationTestRunner({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tags: [{ value: "123" }],
|
tags: [{ id: baseTag1.id }],
|
||||||
images: [{ url: "test-image-2.png" }],
|
images: [{ url: "test-image-2.png" }],
|
||||||
status: "published",
|
status: "published",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ medusaIntegrationTestRunner({
|
|||||||
let store
|
let store
|
||||||
let appContainer
|
let appContainer
|
||||||
let collection
|
let collection
|
||||||
|
let tag
|
||||||
let product
|
let product
|
||||||
let product1
|
let product1
|
||||||
let product2
|
let product2
|
||||||
@@ -496,6 +497,9 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
).data.collection
|
).data.collection
|
||||||
|
tag = (
|
||||||
|
await api.post("/admin/product-tags", { value: "tag1" }, adminHeaders)
|
||||||
|
).data.product_tag
|
||||||
;[product, [variant]] = await createProducts({
|
;[product, [variant]] = await createProducts({
|
||||||
title: "test product 1",
|
title: "test product 1",
|
||||||
collection_id: collection.id,
|
collection_id: collection.id,
|
||||||
@@ -504,7 +508,7 @@ medusaIntegrationTestRunner({
|
|||||||
{ title: "size", values: ["large", "small"] },
|
{ title: "size", values: ["large", "small"] },
|
||||||
{ title: "color", values: ["green"] },
|
{ title: "color", values: ["green"] },
|
||||||
],
|
],
|
||||||
tags: [{ value: "tag1" }],
|
tags: [{ id: tag.id }],
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: "test variant 1",
|
title: "test variant 1",
|
||||||
@@ -726,7 +730,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("returns a list of products with a given tag", async () => {
|
it("returns a list of products with a given tag", async () => {
|
||||||
const response = await api.get(
|
const response = await api.get(
|
||||||
`/store/products?tags[]=${product.tags[0].id}`
|
`/store/products?tag_id[]=${product.tags[0].id}`
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
|
|||||||
@@ -1899,7 +1899,6 @@ medusaIntegrationTestRunner({
|
|||||||
"/admin/products",
|
"/admin/products",
|
||||||
{
|
{
|
||||||
title: "Test fixture",
|
title: "Test fixture",
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
|
||||||
options: [
|
options: [
|
||||||
{ title: "size", values: ["large", "small"] },
|
{ title: "size", values: ["large", "small"] },
|
||||||
{ title: "color", values: ["green"] },
|
{ title: "color", values: ["green"] },
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ medusaIntegrationTestRunner({
|
|||||||
"/admin/products",
|
"/admin/products",
|
||||||
{
|
{
|
||||||
title: "Test fixture",
|
title: "Test fixture",
|
||||||
tags: [{ value: "123" }, { value: "456" }],
|
|
||||||
options: [
|
options: [
|
||||||
{ title: "size", values: ["large", "small"] },
|
{ title: "size", values: ["large", "small"] },
|
||||||
{ title: "color", values: ["green"] },
|
{ title: "color", values: ["green"] },
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ export const Notifications = () => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleOnOpen = (isOpen: boolean) => {
|
const handleOnOpen = (shouldOpen: boolean) => {
|
||||||
if (isOpen) {
|
if (shouldOpen) {
|
||||||
setHasUnread(false)
|
setHasUnread(false)
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
localStorage.setItem(LAST_READ_NOTIFICATION_KEY, new Date().toISOString())
|
localStorage.setItem(LAST_READ_NOTIFICATION_KEY, new Date().toISOString())
|
||||||
|
|||||||
+1
-16
@@ -95,7 +95,7 @@ export const useProductTableFilters = (
|
|||||||
|
|
||||||
if (product_tags && !isProductTagExcluded) {
|
if (product_tags && !isProductTagExcluded) {
|
||||||
const tagFilter: Filter = {
|
const tagFilter: Filter = {
|
||||||
key: "tags",
|
key: "tag_id",
|
||||||
label: t("fields.tag"),
|
label: t("fields.tag"),
|
||||||
type: "select",
|
type: "select",
|
||||||
multiple: true,
|
multiple: true,
|
||||||
@@ -108,21 +108,6 @@ export const useProductTableFilters = (
|
|||||||
filters = [...filters, tagFilter]
|
filters = [...filters, tagFilter]
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (product_tags) {
|
|
||||||
// const tagFilter: Filter = {
|
|
||||||
// key: "tags",
|
|
||||||
// label: t("fields.tag"),
|
|
||||||
// type: "select",
|
|
||||||
// multiple: true,
|
|
||||||
// options: product_tags.map((t) => ({
|
|
||||||
// label: t.value,
|
|
||||||
// value: t.id,
|
|
||||||
// })),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// filters = [...filters, tagFilter]
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (sales_channels) {
|
if (sales_channels) {
|
||||||
const salesChannelFilter: Filter = {
|
const salesChannelFilter: Filter = {
|
||||||
key: "sales_channel_id",
|
key: "sales_channel_id",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const useProductTableQuery = ({
|
|||||||
"category_id",
|
"category_id",
|
||||||
"collection_id",
|
"collection_id",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
"tags",
|
"tag_id",
|
||||||
"type_id",
|
"type_id",
|
||||||
"status",
|
"status",
|
||||||
"id",
|
"id",
|
||||||
@@ -36,7 +36,7 @@ export const useProductTableQuery = ({
|
|||||||
updated_at,
|
updated_at,
|
||||||
category_id,
|
category_id,
|
||||||
collection_id,
|
collection_id,
|
||||||
tags,
|
tag_id,
|
||||||
type_id,
|
type_id,
|
||||||
is_giftcard,
|
is_giftcard,
|
||||||
status,
|
status,
|
||||||
@@ -54,7 +54,7 @@ export const useProductTableQuery = ({
|
|||||||
collection_id: collection_id?.split(","),
|
collection_id: collection_id?.split(","),
|
||||||
is_giftcard: is_giftcard ? is_giftcard === "true" : undefined,
|
is_giftcard: is_giftcard ? is_giftcard === "true" : undefined,
|
||||||
order: order,
|
order: order,
|
||||||
tags: tags ? { value: tags.split(",") } : undefined,
|
tag_id: tag_id ? tag_id.split(",") : undefined,
|
||||||
type_id: type_id?.split(","),
|
type_id: type_id?.split(","),
|
||||||
status: status?.split(",") as HttpTypes.AdminProductStatus[],
|
status: status?.split(",") as HttpTypes.AdminProductStatus[],
|
||||||
q,
|
q,
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ export const ProductTagProductSection = ({
|
|||||||
|
|
||||||
const { products, count, isPending, isError, error } = useProducts({
|
const { products, count, isPending, isError, error } = useProducts({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
tags: productTag.id,
|
tag_id: productTag.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
const filters = useProductTableFilters(["product_tags"])
|
const filters = useProductTableFilters(["product_tags"])
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const normalizeProductFormValues = (
|
|||||||
status: values.status,
|
status: values.status,
|
||||||
is_giftcard: false,
|
is_giftcard: false,
|
||||||
tags: values?.tags?.length
|
tags: values?.tags?.length
|
||||||
? values.tags?.map((tag) => ({ value: tag }))
|
? values.tags?.map((tag) => ({ id: tag }))
|
||||||
: undefined,
|
: undefined,
|
||||||
sales_channels: values?.sales_channels?.length
|
sales_channels: values?.sales_channels?.length
|
||||||
? values.sales_channels?.map((sc) => ({ id: sc.id }))
|
? values.sales_channels?.map((sc) => ({ id: sc.id }))
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ export const ProductOrganizationSection = ({
|
|||||||
product.tags?.length
|
product.tags?.length
|
||||||
? product.tags.map((tag) => (
|
? product.tags.map((tag) => (
|
||||||
<Badge key={tag.id} className="w-fit" size="2xsmall" asChild>
|
<Badge key={tag.id} className="w-fit" size="2xsmall" asChild>
|
||||||
<Link to={`/products?tags=${tag.id}`}>{tag.value}</Link>
|
<Link to={`/products?tag_id=${tag.id}`}>{tag.value}</Link>
|
||||||
</Badge>
|
</Badge>
|
||||||
))
|
))
|
||||||
: undefined
|
: undefined
|
||||||
|
|||||||
+3
-3
@@ -78,9 +78,9 @@ export const ProductOrganizationForm = ({
|
|||||||
collection_id: data.collection_id || undefined,
|
collection_id: data.collection_id || undefined,
|
||||||
categories: data.category_ids.map((id) => ({ id })) || undefined,
|
categories: data.category_ids.map((id) => ({ id })) || undefined,
|
||||||
tags:
|
tags:
|
||||||
data.tag_ids?.map((t) => {
|
data.tag_ids?.map((t) => ({
|
||||||
t
|
id: t,
|
||||||
}) || undefined,
|
})) || undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: ({ product }) => {
|
onSuccess: ({ product }) => {
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
import { ProductTypes } from "@medusajs/types"
|
||||||
import { HttpTypes, RegionTypes } from "@medusajs/types"
|
import { HttpTypes, RegionTypes } from "@medusajs/types"
|
||||||
import { MedusaError, lowerCaseFirst } from "@medusajs/utils"
|
import { MedusaError, lowerCaseFirst } from "@medusajs/utils"
|
||||||
|
|
||||||
// We want to convert the csv data format to a standard DTO format.
|
// We want to convert the csv data format to a standard DTO format.
|
||||||
export const normalizeForImport = (
|
export const normalizeForImport = (
|
||||||
rawProducts: object[],
|
rawProducts: object[],
|
||||||
|
additional: {
|
||||||
regions: RegionTypes.RegionDTO[]
|
regions: RegionTypes.RegionDTO[]
|
||||||
|
tags: ProductTypes.ProductTagDTO[]
|
||||||
|
}
|
||||||
): HttpTypes.AdminCreateProduct[] => {
|
): HttpTypes.AdminCreateProduct[] => {
|
||||||
const productMap = new Map<
|
const productMap = new Map<
|
||||||
string,
|
string,
|
||||||
@@ -15,13 +19,16 @@ export const normalizeForImport = (
|
|||||||
>()
|
>()
|
||||||
|
|
||||||
// Currently region names are treated as case-insensitive.
|
// Currently region names are treated as case-insensitive.
|
||||||
const regionsMap = new Map(regions.map((r) => [r.name.toLowerCase(), r]))
|
const regionsMap = new Map(
|
||||||
|
additional.regions.map((r) => [r.name.toLowerCase(), r])
|
||||||
|
)
|
||||||
|
const tagsMap = new Map(additional.tags.map((t) => [t.value, t]))
|
||||||
|
|
||||||
rawProducts.forEach((rawProduct) => {
|
rawProducts.forEach((rawProduct) => {
|
||||||
const productInMap = productMap.get(rawProduct["Product Handle"])
|
const productInMap = productMap.get(rawProduct["Product Handle"])
|
||||||
if (!productInMap) {
|
if (!productInMap) {
|
||||||
productMap.set(rawProduct["Product Handle"], {
|
productMap.set(rawProduct["Product Handle"], {
|
||||||
product: normalizeProductForImport(rawProduct),
|
product: normalizeProductForImport(rawProduct, tagsMap),
|
||||||
variants: [normalizeVariantForImport(rawProduct, regionsMap)],
|
variants: [normalizeVariantForImport(rawProduct, regionsMap)],
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -86,7 +93,8 @@ const booleanFields = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const normalizeProductForImport = (
|
const normalizeProductForImport = (
|
||||||
rawProduct: object
|
rawProduct: object,
|
||||||
|
tagsMap: Map<string, ProductTypes.ProductTagDTO>
|
||||||
): HttpTypes.AdminCreateProduct => {
|
): HttpTypes.AdminCreateProduct => {
|
||||||
const response = {}
|
const response = {}
|
||||||
|
|
||||||
@@ -108,10 +116,15 @@ const normalizeProductForImport = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (normalizedKey.startsWith("product_tag_")) {
|
if (normalizedKey.startsWith("product_tag_")) {
|
||||||
response["tags"] = [
|
const tag = tagsMap.get(normalizedValue)
|
||||||
...(response["tags"] || []),
|
if (!tag) {
|
||||||
{ value: normalizedValue },
|
throw new MedusaError(
|
||||||
]
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Tag with value ${normalizedValue} not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
response["tags"] = [...(response["tags"] || []), { id: tag.id }]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,13 @@ export const groupProductsForBatchStep = createStep(
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
// New products will be created with a new ID, even if there is one present in the CSV.
|
// New products and variants will be created with a new ID, even if there is one present in the CSV.
|
||||||
// To add support for creating with predefined IDs we will need to do changes to the upsert method.
|
// To add support for creating with predefined IDs we will need to do changes to the upsert method.
|
||||||
delete product.id
|
delete product.id
|
||||||
|
product.variants?.forEach((variant) => {
|
||||||
|
delete (variant as any).id
|
||||||
|
})
|
||||||
|
|
||||||
acc.toCreate.push(product)
|
acc.toCreate.push(product)
|
||||||
return acc
|
return acc
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,12 +52,21 @@ export const parseProductCsvStep = createStep(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const allRegions = await regionService.listRegions(
|
const [allRegions, allTags] = await Promise.all([
|
||||||
|
regionService.listRegions(
|
||||||
{},
|
{},
|
||||||
{ select: ["id", "name", "currency_code"], take: null }
|
{ select: ["id", "name", "currency_code"], take: null }
|
||||||
)
|
),
|
||||||
|
productService.listProductTags(
|
||||||
|
{},
|
||||||
|
{ select: ["id", "value"], take: null }
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
const normalizedData = normalizeForImport(v1Normalized, allRegions)
|
const normalizedData = normalizeForImport(v1Normalized, {
|
||||||
|
regions: allRegions,
|
||||||
|
tags: allTags,
|
||||||
|
})
|
||||||
return new StepResponse(normalizedData)
|
return new StepResponse(normalizedData)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -65,7 +65,7 @@ export interface AdminCreateProduct {
|
|||||||
type_id?: string
|
type_id?: string
|
||||||
collection_id?: string
|
collection_id?: string
|
||||||
categories?: { id: string }[]
|
categories?: { id: string }[]
|
||||||
tags?: { id?: string; value?: string }[]
|
tags?: { id: string }[]
|
||||||
options?: AdminCreateProductOption[]
|
options?: AdminCreateProductOption[]
|
||||||
variants?: AdminCreateProductVariant[]
|
variants?: AdminCreateProductVariant[]
|
||||||
sales_channels?: { id: string }[]
|
sales_channels?: { id: string }[]
|
||||||
@@ -115,9 +115,9 @@ export interface AdminUpdateProduct {
|
|||||||
type_id?: string | null
|
type_id?: string | null
|
||||||
collection_id?: string | null
|
collection_id?: string | null
|
||||||
categories?: { id: string }[]
|
categories?: { id: string }[]
|
||||||
tags?: { id?: string; value?: string }[]
|
tags?: { id: string }[]
|
||||||
options?: AdminUpdateProductOption[]
|
options?: AdminUpdateProductOption[]
|
||||||
variants?: AdminCreateProductVariant[]
|
variants?: (AdminCreateProductVariant | AdminUpdateProductVariant)[]
|
||||||
sales_channels?: { id: string }[]
|
sales_channels?: { id: string }[]
|
||||||
weight?: number | null
|
weight?: number | null
|
||||||
length?: number | null
|
length?: number | null
|
||||||
|
|||||||
@@ -702,25 +702,20 @@ export interface FilterableProductProps
|
|||||||
*/
|
*/
|
||||||
tags?: {
|
tags?: {
|
||||||
/**
|
/**
|
||||||
* Values to filter product tags by.
|
* Filter a product by the IDs of their associated tags.
|
||||||
*/
|
*/
|
||||||
value?: string[]
|
id?: string[]
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Filters on a product's variant properties.
|
* Filters on a product's variant properties.
|
||||||
*/
|
*/
|
||||||
variants?: {
|
variants?: {
|
||||||
options: { value: string; option_id: string }
|
options?: { value: string; option_id: string }
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Filter a product by the ID of the associated type
|
* Filter a product by the ID of the associated type
|
||||||
*/
|
*/
|
||||||
type_id?: string | string[]
|
type_id?: string | string[]
|
||||||
/**
|
|
||||||
* @deprecated - Use `categories` instead
|
|
||||||
* Filter a product by the IDs of their associated categories.
|
|
||||||
*/
|
|
||||||
category_id?: string | string[] | OperatorMap<string>
|
|
||||||
/**
|
/**
|
||||||
* Filter a product by the IDs of their associated categories.
|
* Filter a product by the IDs of their associated categories.
|
||||||
*/
|
*/
|
||||||
@@ -1417,9 +1412,9 @@ export interface CreateProductDTO {
|
|||||||
*/
|
*/
|
||||||
collection_id?: string
|
collection_id?: string
|
||||||
/**
|
/**
|
||||||
* The associated tags to be created or updated.
|
* The tags to be associated with the product.
|
||||||
*/
|
*/
|
||||||
tags?: UpsertProductTagDTO[]
|
tag_ids?: string[]
|
||||||
/**
|
/**
|
||||||
* The product categories to associate with the product.
|
* The product categories to associate with the product.
|
||||||
*/
|
*/
|
||||||
@@ -1533,9 +1528,9 @@ export interface UpdateProductDTO {
|
|||||||
*/
|
*/
|
||||||
collection_id?: string | null
|
collection_id?: string | null
|
||||||
/**
|
/**
|
||||||
* The associated tags to create or update.
|
* The tags to associate with the product
|
||||||
*/
|
*/
|
||||||
tags?: UpsertProductTagDTO[]
|
tag_ids?: string[]
|
||||||
/**
|
/**
|
||||||
* The product categories to associate with the product.
|
* The product categories to associate with the product.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { BatchMethodRequest } from "@medusajs/types"
|
import { BatchMethodRequest } from "@medusajs/types"
|
||||||
import { ProductStatus } from "@medusajs/utils"
|
import { ProductStatus } from "@medusajs/utils"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { GetProductsParams } from "../../utils/common-validators"
|
import {
|
||||||
|
GetProductsParams,
|
||||||
|
transformProductParams,
|
||||||
|
} from "../../utils/common-validators"
|
||||||
import {
|
import {
|
||||||
createFindParams,
|
createFindParams,
|
||||||
createOperatorMap,
|
createOperatorMap,
|
||||||
@@ -38,7 +41,8 @@ export type AdminGetProductsParamsType = z.infer<typeof AdminGetProductsParams>
|
|||||||
export const AdminGetProductsParams = createFindParams({
|
export const AdminGetProductsParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
|
.merge(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
variants: AdminGetProductVariantsParams.optional(),
|
variants: AdminGetProductVariantsParams.optional(),
|
||||||
@@ -48,7 +52,8 @@ export const AdminGetProductsParams = createFindParams({
|
|||||||
$or: z.lazy(() => AdminGetProductsParams.array()).optional(),
|
$or: z.lazy(() => AdminGetProductsParams.array()).optional(),
|
||||||
})
|
})
|
||||||
.merge(GetProductsParams)
|
.merge(GetProductsParams)
|
||||||
)
|
)
|
||||||
|
.transform(transformProductParams)
|
||||||
|
|
||||||
export type AdminGetProductOptionsParamsType = z.infer<
|
export type AdminGetProductOptionsParamsType = z.infer<
|
||||||
typeof AdminGetProductOptionsParams
|
typeof AdminGetProductOptionsParams
|
||||||
@@ -195,7 +200,7 @@ export const AdminBatchUpdateProductVariant = AdminUpdateProductVariant.extend({
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const AdminCreateProductProductCategory = z.object({
|
export const IdAssociation = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -213,8 +218,8 @@ export const AdminCreateProduct = z
|
|||||||
status: statusEnum.nullish().default(ProductStatus.DRAFT),
|
status: statusEnum.nullish().default(ProductStatus.DRAFT),
|
||||||
type_id: z.string().nullish(),
|
type_id: z.string().nullish(),
|
||||||
collection_id: z.string().nullish(),
|
collection_id: z.string().nullish(),
|
||||||
categories: z.array(AdminCreateProductProductCategory).optional(),
|
categories: z.array(IdAssociation).optional(),
|
||||||
tags: z.array(AdminUpdateProductTag).optional(),
|
tags: z.array(IdAssociation).optional(),
|
||||||
options: z.array(AdminCreateProductOption).optional(),
|
options: z.array(AdminCreateProductOption).optional(),
|
||||||
variants: z.array(AdminCreateProductVariant).optional(),
|
variants: z.array(AdminCreateProductVariant).optional(),
|
||||||
sales_channels: z.array(z.object({ id: z.string() })).optional(),
|
sales_channels: z.array(z.object({ id: z.string() })).optional(),
|
||||||
@@ -246,8 +251,8 @@ export const AdminUpdateProduct = z
|
|||||||
handle: z.string().nullish(),
|
handle: z.string().nullish(),
|
||||||
type_id: z.string().nullish(),
|
type_id: z.string().nullish(),
|
||||||
collection_id: z.string().nullish(),
|
collection_id: z.string().nullish(),
|
||||||
categories: z.array(AdminCreateProductProductCategory).optional(),
|
categories: z.array(IdAssociation).optional(),
|
||||||
tags: z.array(AdminUpdateProductTag).optional(),
|
tags: z.array(IdAssociation).optional(),
|
||||||
sales_channels: z.array(z.object({ id: z.string() })).optional(),
|
sales_channels: z.array(z.object({ id: z.string() })).optional(),
|
||||||
weight: z.number().nullish(),
|
weight: z.number().nullish(),
|
||||||
length: z.number().nullish(),
|
length: z.number().nullish(),
|
||||||
@@ -271,13 +276,6 @@ export const AdminBatchUpdateProduct = AdminUpdateProduct.extend({
|
|||||||
export type AdminExportProductType = z.infer<typeof AdminExportProduct>
|
export type AdminExportProductType = z.infer<typeof AdminExportProduct>
|
||||||
export const AdminExportProduct = z.object({})
|
export const AdminExportProduct = z.object({})
|
||||||
|
|
||||||
// TODO: Handle in create and update product once ready
|
|
||||||
// @IsOptional()
|
|
||||||
// @Type(() => ProductProductCategoryReq)
|
|
||||||
// @ValidateNested({ each: true })
|
|
||||||
// @IsArray()
|
|
||||||
// categories?: ProductProductCategoryReq[]
|
|
||||||
|
|
||||||
export const AdminCreateVariantInventoryItem = z.object({
|
export const AdminCreateVariantInventoryItem = z.object({
|
||||||
required_quantity: z.number(),
|
required_quantity: z.number(),
|
||||||
inventory_item_id: z.string(),
|
inventory_item_id: z.string(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { z } from "zod"
|
|||||||
import {
|
import {
|
||||||
GetProductsParams,
|
GetProductsParams,
|
||||||
ProductStatusEnum,
|
ProductStatusEnum,
|
||||||
|
transformProductParams,
|
||||||
} from "../../utils/common-validators"
|
} from "../../utils/common-validators"
|
||||||
import {
|
import {
|
||||||
createFindParams,
|
createFindParams,
|
||||||
@@ -45,7 +46,8 @@ export type StoreGetProductsParamsType = z.infer<typeof StoreGetProductsParams>
|
|||||||
export const StoreGetProductsParams = createFindParams({
|
export const StoreGetProductsParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
|
.merge(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
// These are used to populate the tax and pricing context
|
// These are used to populate the tax and pricing context
|
||||||
@@ -69,4 +71,5 @@ export const StoreGetProductsParams = createFindParams({
|
|||||||
})
|
})
|
||||||
.merge(GetProductsParams)
|
.merge(GetProductsParams)
|
||||||
.strict()
|
.strict()
|
||||||
)
|
)
|
||||||
|
.transform(transformProductParams)
|
||||||
@@ -2,21 +2,58 @@ import { ProductStatus } from "@medusajs/utils"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createOperatorMap } from "../../validators"
|
import { createOperatorMap } from "../../validators"
|
||||||
import { OptionalBooleanValidator } from "../common"
|
import { OptionalBooleanValidator } from "../common"
|
||||||
|
import { FilterableProductProps } from "@medusajs/types"
|
||||||
|
|
||||||
export const ProductStatusEnum = z.nativeEnum(ProductStatus)
|
export const ProductStatusEnum = z.nativeEnum(ProductStatus)
|
||||||
|
|
||||||
export const GetProductsParams = z.object({
|
export const GetProductsParams = z.object({
|
||||||
q: z.string().optional(),
|
q: z.string().optional(),
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
title: z.string().nullish(),
|
title: z.string().optional(),
|
||||||
handle: z.string().nullish(),
|
handle: z.string().optional(),
|
||||||
is_giftcard: OptionalBooleanValidator,
|
is_giftcard: OptionalBooleanValidator,
|
||||||
category_id: z.union([z.string(), z.array(z.string())]).nullish(),
|
category_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
sales_channel_id: z.union([z.string(), z.array(z.string())]).nullish(),
|
sales_channel_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
collection_id: z.union([z.string(), z.array(z.string())]).nullish(),
|
collection_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
tags: z.union([z.string(), z.array(z.string())]).optional(),
|
tag_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
type_id: z.union([z.string(), z.array(z.string())]).optional(),
|
type_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
created_at: createOperatorMap().optional(),
|
created_at: createOperatorMap().optional(),
|
||||||
updated_at: createOperatorMap().optional(),
|
updated_at: createOperatorMap().optional(),
|
||||||
deleted_at: createOperatorMap().optional(),
|
deleted_at: createOperatorMap().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type HttpProductFilters = FilterableProductProps & {
|
||||||
|
tag_id?: string | string[]
|
||||||
|
category_id?: string | string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transformProductParams = (
|
||||||
|
data: HttpProductFilters
|
||||||
|
): FilterableProductProps => {
|
||||||
|
const res = {
|
||||||
|
...data,
|
||||||
|
tags: normalizeArray(data, "tag_id"),
|
||||||
|
categories: normalizeArray(data, "category_id"),
|
||||||
|
}
|
||||||
|
|
||||||
|
delete res.tag_id
|
||||||
|
delete res.category_id
|
||||||
|
|
||||||
|
return res as FilterableProductProps
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeArray = (filters: HttpProductFilters, key: string) => {
|
||||||
|
if (filters[key]) {
|
||||||
|
if (Array.isArray(filters[key])) {
|
||||||
|
return {
|
||||||
|
id: { $in: filters[key] },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
id: filters[key] as string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
+14
-3
@@ -1,4 +1,8 @@
|
|||||||
import { IProductModuleService, ProductCategoryDTO } from "@medusajs/types"
|
import {
|
||||||
|
IProductModuleService,
|
||||||
|
ProductCategoryDTO,
|
||||||
|
ProductTagDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { kebabCase, Modules, ProductStatus } from "@medusajs/utils"
|
import { kebabCase, Modules, ProductStatus } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
Product,
|
Product,
|
||||||
@@ -104,6 +108,11 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
categories.push(await service.createProductCategories(entry))
|
categories.push(await service.createProductCategories(entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tags: ProductTagDTO[] = []
|
||||||
|
for (const entry of tagsData) {
|
||||||
|
tags.push(await service.createProductTags(entry))
|
||||||
|
}
|
||||||
|
|
||||||
productCategoryOne = categories[0]
|
productCategoryOne = categories[0]
|
||||||
productCategoryTwo = categories[1]
|
productCategoryTwo = categories[1]
|
||||||
|
|
||||||
@@ -125,7 +134,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
status: ProductStatus.PUBLISHED,
|
status: ProductStatus.PUBLISHED,
|
||||||
categories: [{ id: productCategoryOne.id }],
|
categories: [{ id: productCategoryOne.id }],
|
||||||
collection_id: productCollectionOne.id,
|
collection_id: productCollectionOne.id,
|
||||||
tags: tagsData,
|
tags: [{ id: tags[0].id }],
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
title: "size",
|
title: "size",
|
||||||
@@ -437,6 +446,8 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
value: "tag 2",
|
value: "tag 2",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await service.createProductTags(newTagData)
|
||||||
|
|
||||||
const updateData = {
|
const updateData = {
|
||||||
id: productTwo.id,
|
id: productTwo.id,
|
||||||
categories: [
|
categories: [
|
||||||
@@ -446,7 +457,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
],
|
],
|
||||||
collection_id: productCollectionTwo.id,
|
collection_id: productCollectionTwo.id,
|
||||||
type_id: productTypeTwo.id,
|
type_id: productTypeTwo.id,
|
||||||
tags: [newTagData],
|
tags: [{ id: newTagData.id }],
|
||||||
}
|
}
|
||||||
|
|
||||||
await service.upsertProducts([updateData])
|
await service.upsertProducts([updateData])
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export { default as ProductService } from "./product"
|
|
||||||
export { default as ProductCategoryService } from "./product-category"
|
export { default as ProductCategoryService } from "./product-category"
|
||||||
export { default as ProductModuleService } from "./product-module-service"
|
export { default as ProductModuleService } from "./product-module-service"
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
ProductType,
|
ProductType,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
import { ProductCategoryService, ProductService } from "@services"
|
import { ProductCategoryService } from "@services"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
arrayDifference,
|
arrayDifference,
|
||||||
@@ -55,7 +55,7 @@ import { joinerConfig } from "./../joiner-config"
|
|||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
productService: ProductService
|
productService: ModulesSdkTypes.IMedusaInternalService<any, any>
|
||||||
productVariantService: ModulesSdkTypes.IMedusaInternalService<any, any>
|
productVariantService: ModulesSdkTypes.IMedusaInternalService<any, any>
|
||||||
productTagService: ModulesSdkTypes.IMedusaInternalService<any>
|
productTagService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
productCategoryService: ProductCategoryService
|
productCategoryService: ProductCategoryService
|
||||||
@@ -102,7 +102,7 @@ export default class ProductModuleService
|
|||||||
implements ProductTypes.IProductModuleService
|
implements ProductTypes.IProductModuleService
|
||||||
{
|
{
|
||||||
protected baseRepository_: DAL.RepositoryService
|
protected baseRepository_: DAL.RepositoryService
|
||||||
protected readonly productService_: ProductService
|
protected readonly productService_: ModulesSdkTypes.IMedusaInternalService<Product>
|
||||||
protected readonly productVariantService_: ModulesSdkTypes.IMedusaInternalService<ProductVariant>
|
protected readonly productVariantService_: ModulesSdkTypes.IMedusaInternalService<ProductVariant>
|
||||||
protected readonly productCategoryService_: ProductCategoryService
|
protected readonly productCategoryService_: ProductCategoryService
|
||||||
protected readonly productTagService_: ModulesSdkTypes.IMedusaInternalService<ProductTag>
|
protected readonly productTagService_: ModulesSdkTypes.IMedusaInternalService<ProductTag>
|
||||||
@@ -1609,26 +1609,6 @@ export default class ProductModuleService
|
|||||||
productData.discountable = false
|
productData.discountable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productData.tags?.length && productData.tags.some((t) => !t.id)) {
|
|
||||||
const dbTags = await this.productTagService_.list(
|
|
||||||
{
|
|
||||||
value: productData.tags
|
|
||||||
.map((t) => t.value)
|
|
||||||
.filter((v) => !!v) as string[],
|
|
||||||
},
|
|
||||||
{ take: null },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
productData.tags = productData.tags.map((tag) => {
|
|
||||||
const dbTag = dbTags.find((t) => t.value === tag.value)
|
|
||||||
return {
|
|
||||||
...tag,
|
|
||||||
...(dbTag ? { id: dbTag.id } : {}),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (productData.options?.length) {
|
if (productData.options?.length) {
|
||||||
const dbOptions = await this.productOptionService_.list(
|
const dbOptions = await this.productOptionService_.list(
|
||||||
{ product_id: productData.id },
|
{ product_id: productData.id },
|
||||||
@@ -1652,6 +1632,13 @@ export default class ProductModuleService
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (productData.tag_ids) {
|
||||||
|
;(productData as any).tags = productData.tag_ids.map((cid) => ({
|
||||||
|
id: cid,
|
||||||
|
}))
|
||||||
|
delete productData.tag_ids
|
||||||
|
}
|
||||||
|
|
||||||
if (productData.category_ids) {
|
if (productData.category_ids) {
|
||||||
;(productData as any).categories = productData.category_ids.map(
|
;(productData as any).categories = productData.category_ids.map(
|
||||||
(cid) => ({
|
(cid) => ({
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
import {
|
|
||||||
Context,
|
|
||||||
DAL,
|
|
||||||
FilterableProductProps,
|
|
||||||
FindConfig,
|
|
||||||
ProductTypes,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils"
|
|
||||||
import { Product } from "@models"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
|
||||||
productRepository: DAL.RepositoryService
|
|
||||||
}
|
|
||||||
|
|
||||||
type NormalizedFilterableProductProps = ProductTypes.FilterableProductProps & {
|
|
||||||
categories?: {
|
|
||||||
id: string | { $in: string[] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ProductService extends ModulesSdkUtils.MedusaInternalService<
|
|
||||||
InjectedDependencies,
|
|
||||||
Product
|
|
||||||
>(Product) {
|
|
||||||
protected readonly productRepository_: DAL.RepositoryService<Product>
|
|
||||||
|
|
||||||
constructor({ productRepository }: InjectedDependencies) {
|
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line prefer-rest-params
|
|
||||||
super(...arguments)
|
|
||||||
|
|
||||||
this.productRepository_ = productRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectManager("productRepository_")
|
|
||||||
async list(
|
|
||||||
filters: ProductTypes.FilterableProductProps = {},
|
|
||||||
config: FindConfig<Product> = {},
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<Product[]> {
|
|
||||||
return await super.list(
|
|
||||||
ProductService.normalizeFilters(filters),
|
|
||||||
config,
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectManager("productRepository_")
|
|
||||||
async listAndCount(
|
|
||||||
filters: ProductTypes.FilterableProductProps = {},
|
|
||||||
config: FindConfig<any> = {},
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<[Product[], number]> {
|
|
||||||
return await super.listAndCount(
|
|
||||||
ProductService.normalizeFilters(filters),
|
|
||||||
config,
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static normalizeFilters(
|
|
||||||
filters: FilterableProductProps = {}
|
|
||||||
): NormalizedFilterableProductProps {
|
|
||||||
const normalized = filters as NormalizedFilterableProductProps
|
|
||||||
if (normalized.category_id) {
|
|
||||||
if (Array.isArray(normalized.category_id)) {
|
|
||||||
normalized.categories = {
|
|
||||||
id: { $in: normalized.category_id },
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
normalized.categories = {
|
|
||||||
id: normalized.category_id as string,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete normalized.category_id
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user