@medusajs/product 0.4.0-snapshot-20240325085803 → 0.4.0-snapshot-20240401075323
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/migrations/{InitialSetup20240315083440.d.ts → InitialSetup20240325200756.d.ts} +1 -1
- package/dist/migrations/{InitialSetup20240315083440.js → InitialSetup20240325200756.js} +5 -2
- package/dist/models/product-category.js +9 -2
- package/dist/models/product-collection.js +4 -1
- package/dist/models/product-option-value.d.ts +2 -2
- package/dist/models/product-option-value.js +14 -5
- package/dist/models/product-option.d.ts +2 -2
- package/dist/models/product-option.js +12 -4
- package/dist/models/product-variant-option.d.ts +1 -1
- package/dist/models/product-variant-option.js +19 -5
- package/dist/models/product-variant.d.ts +2 -2
- package/dist/models/product-variant.js +13 -6
- package/dist/models/product.d.ts +3 -3
- package/dist/models/product.js +26 -13
- package/dist/services/product-module-service.d.ts +9 -6
- package/dist/services/product-module-service.js +151 -205
- package/dist/services/product.d.ts +8 -0
- package/dist/services/product.js +5 -15
- package/dist/types/index.d.ts +7 -4
- package/package.json +5 -5
@@ -9,9 +9,12 @@ class InitialSetup20240315083440 extends migrations_1.Migration {
|
|
9
9
|
if (productTables.length > 0) {
|
10
10
|
// This is so we can still run the api tests, remove completely once that is not needed
|
11
11
|
this.addSql(`alter table "product_option_value" alter column "variant_id" drop not null;`);
|
12
|
+
this.addSql(`alter table "product_variant" alter column "inventory_quantity" drop not null;`);
|
12
13
|
}
|
13
14
|
this.addSql('create table if not exists "product_category" ("id" text not null, "name" text not null, "description" text not null default \'\', "handle" text not null, "mpath" text not null, "is_active" boolean not null default false, "is_internal" boolean not null default false, "rank" numeric not null default 0, "parent_category_id" text null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), constraint "product_category_pkey" primary key ("id"));');
|
14
15
|
this.addSql('create index if not exists "IDX_product_category_path" on "product_category" ("mpath");');
|
16
|
+
// TODO: Re-enable when we run the migration from v1
|
17
|
+
// this.addSql('alter table if exists "product_category" add constraint "IDX_product_category_handle" unique ("handle");');
|
15
18
|
this.addSql('create table if not exists "product_collection" ("id" text not null, "title" text not null, "handle" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "product_collection_pkey" primary key ("id"));');
|
16
19
|
this.addSql('create index if not exists "IDX_product_collection_deleted_at" on "product_collection" ("deleted_at");');
|
17
20
|
this.addSql('alter table if exists "product_collection" add constraint "IDX_product_collection_handle_unique" unique ("handle");');
|
@@ -46,8 +49,8 @@ class InitialSetup20240315083440 extends migrations_1.Migration {
|
|
46
49
|
this.addSql('alter table if exists "product_category" add constraint "product_category_parent_category_id_foreign" foreign key ("parent_category_id") references "product_category" ("id") on update cascade on delete set null;');
|
47
50
|
this.addSql('alter table if exists "product" add constraint "product_collection_id_foreign" foreign key ("collection_id") references "product_collection" ("id") on update cascade on delete set null;');
|
48
51
|
this.addSql('alter table if exists "product" add constraint "product_type_id_foreign" foreign key ("type_id") references "product_type" ("id") on update cascade on delete set null;');
|
49
|
-
this.addSql('alter table if exists "product_option" add constraint "product_option_product_id_foreign" foreign key ("product_id") references "product" ("id") on update cascade on delete
|
50
|
-
this.addSql('alter table if exists "product_option_value" add constraint "product_option_value_option_id_foreign" foreign key ("option_id") references "product_option" ("id") on update cascade;');
|
52
|
+
this.addSql('alter table if exists "product_option" add constraint "product_option_product_id_foreign" foreign key ("product_id") references "product" ("id") on update cascade on delete cascade;');
|
53
|
+
this.addSql('alter table if exists "product_option_value" add constraint "product_option_value_option_id_foreign" foreign key ("option_id") references "product_option" ("id") on update cascade on delete cascade;');
|
51
54
|
this.addSql('alter table if exists "product_tags" add constraint "product_tags_product_id_foreign" foreign key ("product_id") references "product" ("id") on update cascade on delete cascade;');
|
52
55
|
this.addSql('alter table if exists "product_tags" add constraint "product_tags_product_tag_id_foreign" foreign key ("product_tag_id") references "product_tag" ("id") on update cascade on delete cascade;');
|
53
56
|
this.addSql('alter table if exists "product_images" add constraint "product_images_product_id_foreign" foreign key ("product_id") references "product" ("id") on update cascade on delete cascade;');
|
@@ -23,9 +23,11 @@ let ProductCategory = ProductCategory_1 = class ProductCategory {
|
|
23
23
|
}
|
24
24
|
async onInit() {
|
25
25
|
this.id = (0, utils_1.generateEntityId)(this.id, "pcat");
|
26
|
+
this.parent_category_id ?? (this.parent_category_id = this.parent_category?.id ?? null);
|
26
27
|
}
|
27
28
|
async onCreate(args) {
|
28
29
|
this.id = (0, utils_1.generateEntityId)(this.id, "pcat");
|
30
|
+
this.parent_category_id ?? (this.parent_category_id = this.parent_category?.id ?? null);
|
29
31
|
if (!this.handle && this.name) {
|
30
32
|
this.handle = (0, utils_1.kebabCase)(this.name);
|
31
33
|
}
|
@@ -84,11 +86,16 @@ __decorate([
|
|
84
86
|
__metadata("design:type", Number)
|
85
87
|
], ProductCategory.prototype, "rank", void 0);
|
86
88
|
__decorate([
|
87
|
-
(0, core_1.
|
89
|
+
(0, core_1.ManyToOne)(() => ProductCategory, {
|
90
|
+
columnType: "text",
|
91
|
+
fieldName: "parent_category_id",
|
92
|
+
nullable: true,
|
93
|
+
mapToPk: true,
|
94
|
+
}),
|
88
95
|
__metadata("design:type", Object)
|
89
96
|
], ProductCategory.prototype, "parent_category_id", void 0);
|
90
97
|
__decorate([
|
91
|
-
(0, core_1.ManyToOne)(() => ProductCategory, { nullable: true }),
|
98
|
+
(0, core_1.ManyToOne)(() => ProductCategory, { nullable: true, persist: false }),
|
92
99
|
__metadata("design:type", ProductCategory)
|
93
100
|
], ProductCategory.prototype, "parent_category", void 0);
|
94
101
|
__decorate([
|
@@ -21,10 +21,13 @@ let ProductCollection = class ProductCollection {
|
|
21
21
|
}
|
22
22
|
onInit() {
|
23
23
|
this.id = (0, utils_1.generateEntityId)(this.id, "pcol");
|
24
|
+
if (!this.handle && this.title) {
|
25
|
+
this.handle = (0, utils_1.kebabCase)(this.title);
|
26
|
+
}
|
24
27
|
}
|
25
28
|
onCreate() {
|
26
29
|
this.id = (0, utils_1.generateEntityId)(this.id, "pcol");
|
27
|
-
if (!this.handle) {
|
30
|
+
if (!this.handle && this.title) {
|
28
31
|
this.handle = (0, utils_1.kebabCase)(this.title);
|
29
32
|
}
|
30
33
|
}
|
@@ -7,8 +7,8 @@ declare class ProductOptionValue {
|
|
7
7
|
[OptionalProps]?: OptionalFields | OptionalRelations;
|
8
8
|
id: string;
|
9
9
|
value: string;
|
10
|
-
option_id: string;
|
11
|
-
option: ProductOption;
|
10
|
+
option_id: string | null;
|
11
|
+
option: ProductOption | null;
|
12
12
|
variant_options: Collection<ProductVariantOption, object>;
|
13
13
|
metadata?: Record<string, unknown> | null;
|
14
14
|
created_at: Date;
|
@@ -27,9 +27,11 @@ let ProductOptionValue = class ProductOptionValue {
|
|
27
27
|
}
|
28
28
|
onInit() {
|
29
29
|
this.id = (0, utils_1.generateEntityId)(this.id, "optval");
|
30
|
+
this.option_id ?? (this.option_id = this.option?.id ?? null);
|
30
31
|
}
|
31
32
|
beforeCreate() {
|
32
33
|
this.id = (0, utils_1.generateEntityId)(this.id, "optval");
|
34
|
+
this.option_id ?? (this.option_id = this.option?.id ?? null);
|
33
35
|
}
|
34
36
|
};
|
35
37
|
__decorate([
|
@@ -41,15 +43,22 @@ __decorate([
|
|
41
43
|
__metadata("design:type", String)
|
42
44
|
], ProductOptionValue.prototype, "value", void 0);
|
43
45
|
__decorate([
|
44
|
-
(0, core_1.
|
45
|
-
|
46
|
+
(0, core_1.ManyToOne)(() => index_1.ProductOption, {
|
47
|
+
columnType: "text",
|
48
|
+
fieldName: "option_id",
|
49
|
+
mapToPk: true,
|
50
|
+
nullable: true,
|
51
|
+
index: "IDX_product_option_value_option_id",
|
52
|
+
onDelete: "cascade",
|
53
|
+
}),
|
54
|
+
__metadata("design:type", Object)
|
46
55
|
], ProductOptionValue.prototype, "option_id", void 0);
|
47
56
|
__decorate([
|
48
57
|
(0, core_1.ManyToOne)(() => index_1.ProductOption, {
|
49
|
-
|
50
|
-
|
58
|
+
nullable: true,
|
59
|
+
persist: false,
|
51
60
|
}),
|
52
|
-
__metadata("design:type",
|
61
|
+
__metadata("design:type", Object)
|
53
62
|
], ProductOptionValue.prototype, "option", void 0);
|
54
63
|
__decorate([
|
55
64
|
(0, core_1.OneToMany)(() => index_1.ProductVariantOption, (value) => value.option_value, {}),
|
@@ -8,8 +8,8 @@ declare class ProductOption {
|
|
8
8
|
[OptionalProps]?: OptionalRelations | OptionalFields;
|
9
9
|
id: string;
|
10
10
|
title: string;
|
11
|
-
product_id: string;
|
12
|
-
product: Product;
|
11
|
+
product_id: string | null;
|
12
|
+
product: Product | null;
|
13
13
|
values: Collection<ProductOptionValue, object>;
|
14
14
|
metadata?: Record<string, unknown> | null;
|
15
15
|
created_at: Date;
|
@@ -31,9 +31,11 @@ let ProductOption = class ProductOption {
|
|
31
31
|
}
|
32
32
|
onInit() {
|
33
33
|
this.id = (0, utils_1.generateEntityId)(this.id, "opt");
|
34
|
+
this.product_id ?? (this.product_id = this.product?.id ?? null);
|
34
35
|
}
|
35
36
|
beforeCreate() {
|
36
37
|
this.id = (0, utils_1.generateEntityId)(this.id, "opt");
|
38
|
+
this.product_id ?? (this.product_id = this.product?.id ?? null);
|
37
39
|
}
|
38
40
|
};
|
39
41
|
__decorate([
|
@@ -45,15 +47,21 @@ __decorate([
|
|
45
47
|
__metadata("design:type", String)
|
46
48
|
], ProductOption.prototype, "title", void 0);
|
47
49
|
__decorate([
|
48
|
-
(0, core_1.
|
49
|
-
|
50
|
+
(0, core_1.ManyToOne)(() => index_1.Product, {
|
51
|
+
columnType: "text",
|
52
|
+
fieldName: "product_id",
|
53
|
+
mapToPk: true,
|
54
|
+
nullable: true,
|
55
|
+
onDelete: "cascade",
|
56
|
+
}),
|
57
|
+
__metadata("design:type", Object)
|
50
58
|
], ProductOption.prototype, "product_id", void 0);
|
51
59
|
__decorate([
|
52
60
|
(0, core_1.ManyToOne)(() => index_1.Product, {
|
53
|
-
|
61
|
+
persist: false,
|
54
62
|
nullable: true,
|
55
63
|
}),
|
56
|
-
__metadata("design:type",
|
64
|
+
__metadata("design:type", Object)
|
57
65
|
], ProductOption.prototype, "product", void 0);
|
58
66
|
__decorate([
|
59
67
|
(0, core_1.OneToMany)(() => product_option_value_1.default, (value) => value.option, {
|
@@ -24,9 +24,13 @@ variantOptionValueIndexStatement.MikroORMIndex();
|
|
24
24
|
let ProductVariantOption = class ProductVariantOption {
|
25
25
|
onInit() {
|
26
26
|
this.id = (0, utils_1.generateEntityId)(this.id, "varopt");
|
27
|
+
this.variant_id ?? (this.variant_id = this.variant?.id ?? null);
|
28
|
+
this.option_value_id ?? (this.option_value_id = this.option_value?.id ?? null);
|
27
29
|
}
|
28
30
|
beforeCreate() {
|
29
31
|
this.id = (0, utils_1.generateEntityId)(this.id, "varopt");
|
32
|
+
this.variant_id ?? (this.variant_id = this.variant?.id ?? null);
|
33
|
+
this.option_value_id ?? (this.option_value_id = this.option_value?.id ?? null);
|
30
34
|
}
|
31
35
|
};
|
32
36
|
__decorate([
|
@@ -34,23 +38,33 @@ __decorate([
|
|
34
38
|
__metadata("design:type", String)
|
35
39
|
], ProductVariantOption.prototype, "id", void 0);
|
36
40
|
__decorate([
|
37
|
-
(0, core_1.
|
41
|
+
(0, core_1.ManyToOne)(() => index_1.ProductOptionValue, {
|
42
|
+
columnType: "text",
|
43
|
+
nullable: true,
|
44
|
+
fieldName: "option_value_id",
|
45
|
+
mapToPk: true,
|
46
|
+
}),
|
38
47
|
__metadata("design:type", String)
|
39
48
|
], ProductVariantOption.prototype, "option_value_id", void 0);
|
40
49
|
__decorate([
|
41
50
|
(0, core_1.ManyToOne)(() => index_1.ProductOptionValue, {
|
42
|
-
|
51
|
+
persist: false,
|
43
52
|
nullable: true,
|
44
53
|
}),
|
45
54
|
__metadata("design:type", index_1.ProductOptionValue)
|
46
55
|
], ProductVariantOption.prototype, "option_value", void 0);
|
47
56
|
__decorate([
|
48
|
-
(0, core_1.
|
49
|
-
|
57
|
+
(0, core_1.ManyToOne)(() => index_1.ProductVariant, {
|
58
|
+
columnType: "text",
|
59
|
+
nullable: true,
|
60
|
+
fieldName: "variant_id",
|
61
|
+
mapToPk: true,
|
62
|
+
}),
|
63
|
+
__metadata("design:type", Object)
|
50
64
|
], ProductVariantOption.prototype, "variant_id", void 0);
|
51
65
|
__decorate([
|
52
66
|
(0, core_1.ManyToOne)(() => index_1.ProductVariant, {
|
53
|
-
|
67
|
+
persist: false,
|
54
68
|
nullable: true,
|
55
69
|
}),
|
56
70
|
__metadata("design:type", index_1.ProductVariant)
|
@@ -24,8 +24,8 @@ declare class ProductVariant {
|
|
24
24
|
width?: number | null;
|
25
25
|
metadata?: Record<string, unknown> | null;
|
26
26
|
variant_rank?: number | null;
|
27
|
-
product_id: string;
|
28
|
-
product: Product;
|
27
|
+
product_id: string | null;
|
28
|
+
product: Product | null;
|
29
29
|
created_at: Date;
|
30
30
|
updated_at: Date;
|
31
31
|
deleted_at?: Date;
|
@@ -28,9 +28,11 @@ let ProductVariant = class ProductVariant {
|
|
28
28
|
}
|
29
29
|
onInit() {
|
30
30
|
this.id = (0, utils_1.generateEntityId)(this.id, "variant");
|
31
|
+
this.product_id ?? (this.product_id = this.product?.id ?? null);
|
31
32
|
}
|
32
33
|
onCreate() {
|
33
34
|
this.id = (0, utils_1.generateEntityId)(this.id, "variant");
|
35
|
+
this.product_id ?? (this.product_id = this.product?.id ?? null);
|
34
36
|
}
|
35
37
|
};
|
36
38
|
__decorate([
|
@@ -134,18 +136,23 @@ __decorate([
|
|
134
136
|
}),
|
135
137
|
__metadata("design:type", Object)
|
136
138
|
], ProductVariant.prototype, "variant_rank", void 0);
|
137
|
-
__decorate([
|
138
|
-
(0, core_1.Property)({ columnType: "text", nullable: true }),
|
139
|
-
__metadata("design:type", String)
|
140
|
-
], ProductVariant.prototype, "product_id", void 0);
|
141
139
|
__decorate([
|
142
140
|
(0, core_1.ManyToOne)(() => _models_1.Product, {
|
141
|
+
columnType: "text",
|
142
|
+
nullable: true,
|
143
143
|
onDelete: "cascade",
|
144
|
-
index: "IDX_product_variant_product_id",
|
145
144
|
fieldName: "product_id",
|
145
|
+
index: "IDX_product_variant_product_id",
|
146
|
+
mapToPk: true,
|
147
|
+
}),
|
148
|
+
__metadata("design:type", Object)
|
149
|
+
], ProductVariant.prototype, "product_id", void 0);
|
150
|
+
__decorate([
|
151
|
+
(0, core_1.ManyToOne)(() => _models_1.Product, {
|
152
|
+
persist: false,
|
146
153
|
nullable: true,
|
147
154
|
}),
|
148
|
-
__metadata("design:type",
|
155
|
+
__metadata("design:type", Object)
|
149
156
|
], ProductVariant.prototype, "product", void 0);
|
150
157
|
__decorate([
|
151
158
|
(0, core_1.Property)({
|
package/dist/models/product.d.ts
CHANGED
@@ -30,10 +30,10 @@ declare class Product {
|
|
30
30
|
hs_code?: string | null;
|
31
31
|
mid_code?: string | null;
|
32
32
|
material?: string | null;
|
33
|
-
collection_id: string;
|
33
|
+
collection_id: string | null;
|
34
34
|
collection: ProductCollection | null;
|
35
|
-
type_id: string;
|
36
|
-
type: ProductType;
|
35
|
+
type_id: string | null;
|
36
|
+
type: ProductType | null;
|
37
37
|
tags: Collection<ProductTag, object>;
|
38
38
|
images: Collection<ProductImage, object>;
|
39
39
|
categories: Collection<ProductCategory, object>;
|
package/dist/models/product.js
CHANGED
@@ -31,10 +31,17 @@ let Product = class Product {
|
|
31
31
|
}
|
32
32
|
onInit() {
|
33
33
|
this.id = (0, utils_1.generateEntityId)(this.id, "prod");
|
34
|
+
this.type_id ?? (this.type_id = this.type?.id ?? null);
|
35
|
+
this.collection_id ?? (this.collection_id = this.collection?.id ?? null);
|
36
|
+
if (!this.handle && this.title) {
|
37
|
+
this.handle = (0, utils_1.kebabCase)(this.title);
|
38
|
+
}
|
34
39
|
}
|
35
40
|
beforeCreate() {
|
36
41
|
this.id = (0, utils_1.generateEntityId)(this.id, "prod");
|
37
|
-
|
42
|
+
this.type_id ?? (this.type_id = this.type?.id ?? null);
|
43
|
+
this.collection_id ?? (this.collection_id = this.collection?.id ?? null);
|
44
|
+
if (!this.handle && this.title) {
|
38
45
|
this.handle = (0, utils_1.kebabCase)(this.title);
|
39
46
|
}
|
40
47
|
}
|
@@ -121,35 +128,43 @@ __decorate([
|
|
121
128
|
__metadata("design:type", Object)
|
122
129
|
], Product.prototype, "material", void 0);
|
123
130
|
__decorate([
|
124
|
-
(0, core_1.
|
125
|
-
|
131
|
+
(0, core_1.ManyToOne)(() => product_collection_1.default, {
|
132
|
+
columnType: "text",
|
133
|
+
nullable: true,
|
134
|
+
fieldName: "collection_id",
|
135
|
+
mapToPk: true,
|
136
|
+
}),
|
137
|
+
__metadata("design:type", Object)
|
126
138
|
], Product.prototype, "collection_id", void 0);
|
127
139
|
__decorate([
|
128
140
|
(0, core_1.ManyToOne)(() => product_collection_1.default, {
|
129
141
|
nullable: true,
|
130
|
-
|
142
|
+
persist: false,
|
131
143
|
}),
|
132
144
|
__metadata("design:type", Object)
|
133
145
|
], Product.prototype, "collection", void 0);
|
134
146
|
__decorate([
|
135
|
-
(0, core_1.
|
136
|
-
|
147
|
+
(0, core_1.ManyToOne)(() => product_type_1.default, {
|
148
|
+
columnType: "text",
|
149
|
+
nullable: true,
|
150
|
+
fieldName: "type_id",
|
151
|
+
index: "IDX_product_type_id",
|
152
|
+
mapToPk: true,
|
153
|
+
}),
|
154
|
+
__metadata("design:type", Object)
|
137
155
|
], Product.prototype, "type_id", void 0);
|
138
156
|
__decorate([
|
139
157
|
(0, core_1.ManyToOne)(() => product_type_1.default, {
|
140
158
|
nullable: true,
|
141
|
-
|
142
|
-
fieldName: "type_id",
|
159
|
+
persist: false,
|
143
160
|
}),
|
144
|
-
__metadata("design:type",
|
161
|
+
__metadata("design:type", Object)
|
145
162
|
], Product.prototype, "type", void 0);
|
146
163
|
__decorate([
|
147
164
|
(0, core_1.ManyToMany)(() => product_tag_1.default, "products", {
|
148
165
|
owner: true,
|
149
166
|
pivotTable: "product_tags",
|
150
167
|
index: "IDX_product_tag_id",
|
151
|
-
cascade: ["soft-remove"],
|
152
|
-
// TODO: Do we really want to remove tags if the product is deleted?
|
153
168
|
}),
|
154
169
|
__metadata("design:type", Object)
|
155
170
|
], Product.prototype, "tags", void 0);
|
@@ -158,7 +173,6 @@ __decorate([
|
|
158
173
|
owner: true,
|
159
174
|
pivotTable: "product_images",
|
160
175
|
index: "IDX_product_image_id",
|
161
|
-
cascade: ["soft-remove"],
|
162
176
|
joinColumn: "product_id",
|
163
177
|
inverseJoinColumn: "image_id",
|
164
178
|
}),
|
@@ -168,7 +182,6 @@ __decorate([
|
|
168
182
|
(0, core_1.ManyToMany)(() => product_category_1.default, "products", {
|
169
183
|
owner: true,
|
170
184
|
pivotTable: "product_category_product",
|
171
|
-
// TODO: rm cascade: ["soft-remove"] as any,
|
172
185
|
}),
|
173
186
|
__metadata("design:type", Object)
|
174
187
|
], Product.prototype, "categories", void 0);
|
@@ -2,7 +2,7 @@ import { Context, DAL, IEventBusModuleService, InternalModuleDeclaration, Module
|
|
2
2
|
import { Image, Product, ProductCategory, ProductCollection, ProductOption, ProductOptionValue, ProductTag, ProductType, ProductVariant, ProductVariantOption } from "../models";
|
3
3
|
import { ProductCategoryService, ProductCollectionService, ProductOptionService, ProductService, ProductTagService, ProductTypeService } from ".";
|
4
4
|
import { ModulesSdkUtils } from "@medusajs/utils";
|
5
|
-
import { UpdateCollectionInput, UpdateProductInput,
|
5
|
+
import { UpdateCollectionInput, UpdateProductInput, UpdateProductOptionInput, UpdateProductVariantInput } from "../types";
|
6
6
|
type InjectedDependencies = {
|
7
7
|
baseRepository: DAL.RepositoryService;
|
8
8
|
productService: ProductService<any>;
|
@@ -78,11 +78,14 @@ export default class ProductModuleService<TProduct extends Product = Product, TP
|
|
78
78
|
updateVariants(id: string, data: ProductTypes.UpdateProductVariantDTO, sharedContext?: Context): Promise<ProductTypes.ProductVariantDTO>;
|
79
79
|
updateVariants(selector: ProductTypes.FilterableProductVariantProps, data: ProductTypes.UpdateProductVariantDTO, sharedContext?: Context): Promise<ProductTypes.ProductVariantDTO[]>;
|
80
80
|
protected updateVariants_(data: UpdateProductVariantInput[], sharedContext?: Context): Promise<TProductVariant[]>;
|
81
|
-
protected diffVariants_(data: UpdateProductVariantInput[], productOptions: ProductOption[], sharedContext?: Context): Promise<TProductVariant[]>;
|
82
81
|
createTags(data: ProductTypes.CreateProductTagDTO[], sharedContext?: Context): Promise<ProductTypes.ProductTagDTO[]>;
|
83
82
|
updateTags(data: ProductTypes.UpdateProductTagDTO[], sharedContext?: Context): Promise<ProductTypes.ProductTagDTO[]>;
|
84
83
|
createTypes(data: ProductTypes.CreateProductTypeDTO[], sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO[]>;
|
85
|
-
|
84
|
+
createTypes(data: ProductTypes.CreateProductTypeDTO, sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO>;
|
85
|
+
upsertTypes(data: ProductTypes.UpsertProductTypeDTO[], sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO[]>;
|
86
|
+
upsertTypes(data: ProductTypes.UpsertProductTypeDTO, sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO>;
|
87
|
+
updateTypes(id: string, data: ProductTypes.UpdateProductTypeDTO, sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO>;
|
88
|
+
updateTypes(selector: ProductTypes.FilterableProductTypeProps, data: ProductTypes.UpdateProductTypeDTO, sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO[]>;
|
86
89
|
createOptions(data: ProductTypes.CreateProductOptionDTO[], sharedContext?: Context): Promise<ProductTypes.ProductOptionDTO[]>;
|
87
90
|
createOptions(data: ProductTypes.CreateProductOptionDTO, sharedContext?: Context): Promise<ProductTypes.ProductOptionDTO>;
|
88
91
|
protected createOptions_(data: ProductTypes.CreateProductOptionDTO[], sharedContext?: Context): Promise<ProductOption[]>;
|
@@ -91,7 +94,6 @@ export default class ProductModuleService<TProduct extends Product = Product, TP
|
|
91
94
|
updateOptions(id: string, data: ProductTypes.UpdateProductOptionDTO, sharedContext?: Context): Promise<ProductTypes.ProductOptionDTO>;
|
92
95
|
updateOptions(selector: ProductTypes.FilterableProductOptionProps, data: ProductTypes.UpdateProductOptionDTO, sharedContext?: Context): Promise<ProductTypes.ProductOptionDTO[]>;
|
93
96
|
protected updateOptions_(data: UpdateProductOptionInput[], sharedContext?: Context): Promise<ProductOption[]>;
|
94
|
-
protected diffOptions_(data: UpdateProductOptionInput[], sharedContext?: Context): Promise<ProductOption[]>;
|
95
97
|
createCollections(data: ProductTypes.CreateProductCollectionDTO[], sharedContext?: Context): Promise<ProductTypes.ProductCollectionDTO[]>;
|
96
98
|
createCollections(data: ProductTypes.CreateProductCollectionDTO, sharedContext?: Context): Promise<ProductTypes.ProductCollectionDTO>;
|
97
99
|
createCollections_(data: ProductTypes.CreateProductCollectionDTO[], sharedContext?: Context): Promise<TProductCollection[]>;
|
@@ -112,8 +114,9 @@ export default class ProductModuleService<TProduct extends Product = Product, TP
|
|
112
114
|
protected create_(data: ProductTypes.CreateProductDTO[], sharedContext?: Context): Promise<TProduct[]>;
|
113
115
|
protected update_(data: UpdateProductInput[], sharedContext?: Context): Promise<TProduct[]>;
|
114
116
|
protected static normalizeCreateProductInput(product: ProductTypes.CreateProductDTO): ProductTypes.CreateProductDTO;
|
115
|
-
protected static normalizeUpdateProductInput(product:
|
116
|
-
protected static
|
117
|
+
protected static normalizeUpdateProductInput(product: UpdateProductInput): UpdateProductInput;
|
118
|
+
protected static normalizeCreateProductCollectionInput(collection: ProductTypes.CreateProductCollectionDTO): ProductTypes.CreateProductCollectionDTO;
|
119
|
+
protected static normalizeUpdateProductCollectionInput(collection: ProductTypes.CreateProductCollectionDTO | UpdateCollectionInput): ProductTypes.CreateProductCollectionDTO | UpdateCollectionInput;
|
117
120
|
protected static assignOptionsToVariants(variants: ProductTypes.CreateProductVariantDTO[] | ProductTypes.UpdateProductVariantDTO[], options: ProductOption[]): ProductTypes.CreateProductVariantDTO[] | ProductTypes.UpdateProductVariantDTO[];
|
118
121
|
}
|
119
122
|
export {};
|
@@ -17,7 +17,6 @@ const _models_1 = require("../models");
|
|
17
17
|
const utils_1 = require("@medusajs/utils");
|
18
18
|
const types_2 = require("../types");
|
19
19
|
const joiner_config_1 = require("./../joiner-config");
|
20
|
-
const utils_2 = require("@medusajs/utils");
|
21
20
|
const generateMethodForModels = [
|
22
21
|
{ model: _models_1.ProductCategory, singular: "Category", plural: "Categories" },
|
23
22
|
{ model: _models_1.ProductCollection, singular: "Collection", plural: "Collections" },
|
@@ -56,7 +55,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
56
55
|
async createVariants(data, sharedContext = {}) {
|
57
56
|
const input = Array.isArray(data) ? data : [data];
|
58
57
|
const variants = await this.createVariants_(input, sharedContext);
|
59
|
-
const createdVariants = await this.baseRepository_.serialize(variants
|
58
|
+
const createdVariants = await this.baseRepository_.serialize(variants);
|
60
59
|
return Array.isArray(data) ? createdVariants : createdVariants[0];
|
61
60
|
}
|
62
61
|
async createVariants_(data, sharedContext = {}) {
|
@@ -106,7 +105,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
106
105
|
async updateVariants_(data, sharedContext = {}) {
|
107
106
|
// Validation step
|
108
107
|
const variantIdsToUpdate = data.map(({ id }) => id);
|
109
|
-
const variants = await this.
|
108
|
+
const variants = await this.productVariantService_.list({ id: variantIdsToUpdate }, { relations: ["options"], take: null }, sharedContext);
|
110
109
|
if (variants.length !== data.length) {
|
111
110
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Cannot update non-existing variants with ids: ${(0, utils_1.arrayDifference)(variantIdsToUpdate, variants.map(({ id }) => id)).join(", ")}`);
|
112
111
|
}
|
@@ -119,66 +118,62 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
119
118
|
const productOptions = await this.productOptionService_.list({
|
120
119
|
product_id: Array.from(new Set(variantsWithProductId.map((v) => v.product_id))),
|
121
120
|
}, { take: null }, sharedContext);
|
122
|
-
return
|
123
|
-
|
124
|
-
|
125
|
-
const toCreate = data.filter((o) => !o.id);
|
126
|
-
const toUpdate = data.filter((o) => o.id);
|
127
|
-
let createdVariants = [];
|
128
|
-
let updatedVariants = [];
|
129
|
-
if (toCreate.length) {
|
130
|
-
createdVariants = await this.productVariantService_.create(ProductModuleService.assignOptionsToVariants(toCreate, productOptions), sharedContext);
|
131
|
-
}
|
132
|
-
if (toUpdate.length) {
|
133
|
-
const existingVariants = await this.productVariantService_.list({ id: toUpdate.map((o) => o.id) }, { take: null }, sharedContext);
|
134
|
-
const updateVariants = await (0, utils_1.promiseAll)(toUpdate.map(async (variantToUpdate) => {
|
135
|
-
const dbVariant = existingVariants.find((o) => o.id === variantToUpdate.id);
|
136
|
-
if (!dbVariant) {
|
137
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Variant with id "${variantToUpdate.id}" does not exist, but was referenced in the update request`);
|
138
|
-
}
|
139
|
-
if (!variantToUpdate.options) {
|
140
|
-
return variantToUpdate;
|
141
|
-
}
|
142
|
-
const dbVariantOptions = await this.productVariantOptionService_.list({ variant_id: dbVariant.id }, { relations: ["option_value", "option_value.option"], take: null }, sharedContext);
|
143
|
-
const variantOptionsToDelete = dbVariantOptions
|
144
|
-
.filter((variantOption) => {
|
145
|
-
return !Object.entries(variantToUpdate.options ?? {}).some(([optionTitle, optionValue]) => variantOption.option_value.value === optionValue &&
|
146
|
-
variantOption.option_value.option.title === optionTitle);
|
147
|
-
})
|
148
|
-
.map((v) => v.id);
|
149
|
-
await this.productVariantOptionService_.delete({
|
150
|
-
id: { $in: variantOptionsToDelete },
|
151
|
-
});
|
152
|
-
return variantToUpdate;
|
153
|
-
}));
|
154
|
-
updatedVariants = await this.productVariantService_.update(ProductModuleService.assignOptionsToVariants(updateVariants, productOptions), sharedContext);
|
155
|
-
}
|
156
|
-
return [...createdVariants, ...updatedVariants];
|
121
|
+
return this.productVariantService_.upsertWithReplace(ProductModuleService.assignOptionsToVariants(variantsWithProductId, productOptions), {
|
122
|
+
relations: ["options"],
|
123
|
+
}, sharedContext);
|
157
124
|
}
|
158
125
|
async createTags(data, sharedContext = {}) {
|
159
126
|
const productTags = await this.productTagService_.create(data, sharedContext);
|
160
|
-
return await this.baseRepository_.serialize(productTags
|
127
|
+
return await this.baseRepository_.serialize(productTags);
|
161
128
|
}
|
162
129
|
async updateTags(data, sharedContext = {}) {
|
163
130
|
const productTags = await this.productTagService_.update(data, sharedContext);
|
164
|
-
return await this.baseRepository_.serialize(productTags
|
131
|
+
return await this.baseRepository_.serialize(productTags);
|
165
132
|
}
|
166
133
|
async createTypes(data, sharedContext = {}) {
|
167
|
-
const
|
168
|
-
|
169
|
-
|
170
|
-
|
134
|
+
const input = Array.isArray(data) ? data : [data];
|
135
|
+
const types = await this.productTypeService_.create(input, sharedContext);
|
136
|
+
const createdTypes = await this.baseRepository_.serialize(types);
|
137
|
+
return Array.isArray(data) ? createdTypes : createdTypes[0];
|
171
138
|
}
|
172
|
-
async
|
173
|
-
const
|
174
|
-
|
175
|
-
|
176
|
-
|
139
|
+
async upsertTypes(data, sharedContext = {}) {
|
140
|
+
const input = Array.isArray(data) ? data : [data];
|
141
|
+
const forUpdate = input.filter((type) => !!type.id);
|
142
|
+
const forCreate = input.filter((type) => !type.id);
|
143
|
+
let created = [];
|
144
|
+
let updated = [];
|
145
|
+
if (forCreate.length) {
|
146
|
+
created = await this.productTypeService_.create(forCreate, sharedContext);
|
147
|
+
}
|
148
|
+
if (forUpdate.length) {
|
149
|
+
updated = await this.productTypeService_.update(forUpdate, sharedContext);
|
150
|
+
}
|
151
|
+
const result = [...created, ...updated];
|
152
|
+
const allTypes = await this.baseRepository_.serialize(result);
|
153
|
+
return Array.isArray(data) ? allTypes : allTypes[0];
|
154
|
+
}
|
155
|
+
async updateTypes(idOrSelector, data, sharedContext = {}) {
|
156
|
+
let normalizedInput = [];
|
157
|
+
if ((0, utils_1.isString)(idOrSelector)) {
|
158
|
+
// Check if the type exists in the first place
|
159
|
+
await this.productTypeService_.retrieve(idOrSelector, {}, sharedContext);
|
160
|
+
normalizedInput = [{ id: idOrSelector, ...data }];
|
161
|
+
}
|
162
|
+
else {
|
163
|
+
const types = await this.productTypeService_.list(idOrSelector, {}, sharedContext);
|
164
|
+
normalizedInput = types.map((type) => ({
|
165
|
+
id: type.id,
|
166
|
+
...data,
|
167
|
+
}));
|
168
|
+
}
|
169
|
+
const types = await this.productTypeService_.update(normalizedInput, sharedContext);
|
170
|
+
const updatedTypes = await this.baseRepository_.serialize(types);
|
171
|
+
return (0, utils_1.isString)(idOrSelector) ? updatedTypes[0] : updatedTypes;
|
177
172
|
}
|
178
173
|
async createOptions(data, sharedContext = {}) {
|
179
174
|
const input = Array.isArray(data) ? data : [data];
|
180
175
|
const options = await this.createOptions_(input, sharedContext);
|
181
|
-
const createdOptions = await this.baseRepository_.serialize(options
|
176
|
+
const createdOptions = await this.baseRepository_.serialize(options);
|
182
177
|
return Array.isArray(data) ? createdOptions : createdOptions[0];
|
183
178
|
}
|
184
179
|
async createOptions_(data, sharedContext = {}) {
|
@@ -214,6 +209,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
214
209
|
async updateOptions(idOrSelector, data, sharedContext = {}) {
|
215
210
|
let normalizedInput = [];
|
216
211
|
if ((0, utils_1.isString)(idOrSelector)) {
|
212
|
+
await this.productOptionService_.retrieve(idOrSelector, {}, sharedContext);
|
217
213
|
normalizedInput = [{ id: idOrSelector, ...data }];
|
218
214
|
}
|
219
215
|
else {
|
@@ -244,60 +240,12 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
244
240
|
if (normalizedInput.some((option) => !option.id)) {
|
245
241
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Tried to update options without specifying an ID");
|
246
242
|
}
|
247
|
-
|
248
|
-
return productOptions;
|
249
|
-
}
|
250
|
-
// TODO: Do validation
|
251
|
-
async diffOptions_(data, sharedContext = {}) {
|
252
|
-
const toCreate = data.filter((o) => !o.id);
|
253
|
-
const toUpdate = data.filter((o) => o.id);
|
254
|
-
let createdOptions = [];
|
255
|
-
let updatedOptions = [];
|
256
|
-
if (toCreate.length) {
|
257
|
-
createdOptions = await this.productOptionService_.create(toCreate, sharedContext);
|
258
|
-
}
|
259
|
-
if (toUpdate.length) {
|
260
|
-
const existingOptions = await this.productOptionService_.list({ id: toUpdate.map((o) => o.id) }, { take: null }, sharedContext);
|
261
|
-
const updateOptions = await (0, utils_1.promiseAll)(toUpdate.map(async (optionToUpdate) => {
|
262
|
-
const dbOption = existingOptions.find((o) => o.id === optionToUpdate.id);
|
263
|
-
if (!dbOption) {
|
264
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Option with id "${optionToUpdate.id}" does not exist, but was referenced in the update request`);
|
265
|
-
}
|
266
|
-
if (!optionToUpdate.values) {
|
267
|
-
return optionToUpdate;
|
268
|
-
}
|
269
|
-
const valuesToDelete = dbOption.values
|
270
|
-
.filter((dbVal) => {
|
271
|
-
return !optionToUpdate.values?.some((updateVal) => updateVal.value === dbVal.value);
|
272
|
-
})
|
273
|
-
.map((v) => v.id);
|
274
|
-
const valuesToUpsert = optionToUpdate.values?.map((val) => {
|
275
|
-
const dbValue = dbOption.values.find((v) => v.value === val.value);
|
276
|
-
if (dbValue) {
|
277
|
-
return {
|
278
|
-
...val,
|
279
|
-
id: dbValue.id,
|
280
|
-
};
|
281
|
-
}
|
282
|
-
return val;
|
283
|
-
});
|
284
|
-
await this.productOptionValueService_.delete({
|
285
|
-
id: { $in: valuesToDelete },
|
286
|
-
});
|
287
|
-
const updatedValues = await this.productOptionValueService_.upsert(valuesToUpsert, sharedContext);
|
288
|
-
return {
|
289
|
-
...optionToUpdate,
|
290
|
-
values: updatedValues,
|
291
|
-
};
|
292
|
-
}));
|
293
|
-
updatedOptions = await this.productOptionService_.update(updateOptions, sharedContext);
|
294
|
-
}
|
295
|
-
return [...createdOptions, ...updatedOptions];
|
243
|
+
return await this.productOptionService_.upsertWithReplace(normalizedInput, { relations: ["values"] }, sharedContext);
|
296
244
|
}
|
297
245
|
async createCollections(data, sharedContext = {}) {
|
298
246
|
const input = Array.isArray(data) ? data : [data];
|
299
247
|
const collections = await this.createCollections_(input, sharedContext);
|
300
|
-
const createdCollections = await this.baseRepository_.serialize(collections
|
248
|
+
const createdCollections = await this.baseRepository_.serialize(collections);
|
301
249
|
await this.eventBusModuleService_?.emit(collections.map(({ id }) => ({
|
302
250
|
eventName: types_2.ProductCollectionEvents.COLLECTION_CREATED,
|
303
251
|
data: { id },
|
@@ -305,8 +253,8 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
305
253
|
return Array.isArray(data) ? createdCollections : createdCollections[0];
|
306
254
|
}
|
307
255
|
async createCollections_(data, sharedContext = {}) {
|
308
|
-
const normalizedInput = data.map(ProductModuleService.
|
309
|
-
return await this.productCollectionService_.
|
256
|
+
const normalizedInput = data.map(ProductModuleService.normalizeCreateProductCollectionInput);
|
257
|
+
return await this.productCollectionService_.upsertWithReplace(normalizedInput, { relations: ["products"] }, sharedContext);
|
310
258
|
}
|
311
259
|
async upsertCollections(data, sharedContext = {}) {
|
312
260
|
const input = Array.isArray(data) ? data : [data];
|
@@ -339,6 +287,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
339
287
|
async updateCollections(idOrSelector, data, sharedContext = {}) {
|
340
288
|
let normalizedInput = [];
|
341
289
|
if ((0, utils_1.isString)(idOrSelector)) {
|
290
|
+
await this.productCollectionService_.retrieve(idOrSelector, {}, sharedContext);
|
342
291
|
normalizedInput = [{ id: idOrSelector, ...data }];
|
343
292
|
}
|
344
293
|
else {
|
@@ -357,8 +306,8 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
357
306
|
return (0, utils_1.isString)(idOrSelector) ? updatedCollections[0] : updatedCollections;
|
358
307
|
}
|
359
308
|
async updateCollections_(data, sharedContext = {}) {
|
360
|
-
const normalizedInput = data.map(ProductModuleService.
|
361
|
-
return await this.productCollectionService_.
|
309
|
+
const normalizedInput = data.map(ProductModuleService.normalizeUpdateProductCollectionInput);
|
310
|
+
return await this.productCollectionService_.upsertWithReplace(normalizedInput, { relations: ["products"] }, sharedContext);
|
362
311
|
}
|
363
312
|
async createCategory(data, sharedContext = {}) {
|
364
313
|
const productCategory = await this.productCategoryService_.create(data, sharedContext);
|
@@ -381,7 +330,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
381
330
|
async create(data, sharedContext = {}) {
|
382
331
|
const input = Array.isArray(data) ? data : [data];
|
383
332
|
const products = await this.create_(input, sharedContext);
|
384
|
-
const createdProducts = await this.baseRepository_.serialize(products
|
333
|
+
const createdProducts = await this.baseRepository_.serialize(products);
|
385
334
|
await this.eventBusModuleService_?.emit(createdProducts.map(({ id }) => ({
|
386
335
|
eventName: types_2.ProductEvents.PRODUCT_CREATED,
|
387
336
|
data: { id },
|
@@ -401,7 +350,7 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
401
350
|
updated = await this.update_(forUpdate, sharedContext);
|
402
351
|
}
|
403
352
|
const result = [...created, ...updated];
|
404
|
-
const allProducts = await this.baseRepository_.serialize(result
|
353
|
+
const allProducts = await this.baseRepository_.serialize(result);
|
405
354
|
if (created.length) {
|
406
355
|
await this.eventBusModuleService_?.emit(created.map(({ id }) => ({
|
407
356
|
eventName: types_2.ProductEvents.PRODUCT_CREATED,
|
@@ -419,6 +368,8 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
419
368
|
async update(idOrSelector, data, sharedContext = {}) {
|
420
369
|
let normalizedInput = [];
|
421
370
|
if ((0, utils_1.isString)(idOrSelector)) {
|
371
|
+
// This will throw if the product does not exist
|
372
|
+
await this.productService_.retrieve(idOrSelector, {}, sharedContext);
|
422
373
|
normalizedInput = [{ id: idOrSelector, ...data }];
|
423
374
|
}
|
424
375
|
else {
|
@@ -429,112 +380,109 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
429
380
|
}));
|
430
381
|
}
|
431
382
|
const products = await this.update_(normalizedInput, sharedContext);
|
432
|
-
const updatedProducts = await this.baseRepository_.serialize(products
|
383
|
+
const updatedProducts = await this.baseRepository_.serialize(products);
|
433
384
|
await this.eventBusModuleService_?.emit(updatedProducts.map(({ id }) => ({
|
434
385
|
eventName: types_2.ProductEvents.PRODUCT_UPDATED,
|
435
386
|
data: { id },
|
436
387
|
})));
|
437
388
|
return (0, utils_1.isString)(idOrSelector) ? updatedProducts[0] : updatedProducts;
|
438
389
|
}
|
439
|
-
// Orchestrate product creation (and updates follow a similar logic). For each product:
|
440
|
-
// 1. Create the base product
|
441
|
-
// 2. Upsert images, assign to product
|
442
|
-
// 3. Upsert tags, assign to product
|
443
|
-
// 4. Upsert product type, assign to product
|
444
|
-
// 5. Create options and option values
|
445
|
-
// 6. Assign options to variants
|
446
|
-
// 7. Create variants
|
447
390
|
async create_(data, sharedContext = {}) {
|
448
391
|
const normalizedInput = data.map(ProductModuleService.normalizeCreateProductInput);
|
449
|
-
const
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
if (productData.options?.length) {
|
465
|
-
productData.options = await this.productOptionService_.create(productData.options, sharedContext);
|
392
|
+
const productData = await this.productService_.upsertWithReplace(normalizedInput, {
|
393
|
+
relations: ["type", "collection", "images", "tags", "categories"],
|
394
|
+
}, sharedContext);
|
395
|
+
await (0, utils_1.promiseAll)(
|
396
|
+
// Note: It's safe to rely on the order here as `upsertWithReplace` preserves the order of the input
|
397
|
+
normalizedInput.map(async (product, i) => {
|
398
|
+
const upsertedProduct = productData[i];
|
399
|
+
upsertedProduct.options = [];
|
400
|
+
upsertedProduct.variants = [];
|
401
|
+
if (product.options?.length) {
|
402
|
+
upsertedProduct.options =
|
403
|
+
await this.productOptionService_.upsertWithReplace(product.options?.map((option) => ({
|
404
|
+
...option,
|
405
|
+
product_id: upsertedProduct.id,
|
406
|
+
})) ?? [], { relations: ["values"] }, sharedContext);
|
466
407
|
}
|
467
|
-
if (
|
468
|
-
|
408
|
+
if (product.variants?.length) {
|
409
|
+
upsertedProduct.variants =
|
410
|
+
await this.productVariantService_.upsertWithReplace(ProductModuleService.assignOptionsToVariants(product.variants?.map((v) => ({
|
411
|
+
...v,
|
412
|
+
product_id: upsertedProduct.id,
|
413
|
+
})) ?? [], upsertedProduct.options), { relations: ["options"] }, sharedContext);
|
469
414
|
}
|
470
|
-
return productData;
|
471
415
|
}));
|
472
|
-
return
|
416
|
+
return productData;
|
473
417
|
}
|
474
418
|
async update_(data, sharedContext = {}) {
|
475
419
|
const normalizedInput = data.map(ProductModuleService.normalizeUpdateProductInput);
|
476
|
-
const
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
if (
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
420
|
+
const productData = await this.productService_.upsertWithReplace(normalizedInput, {
|
421
|
+
relations: ["type", "collection", "images", "tags", "categories"],
|
422
|
+
}, sharedContext);
|
423
|
+
// There is more than 1-level depth of relations here, so we need to handle the options and variants manually
|
424
|
+
await (0, utils_1.promiseAll)(
|
425
|
+
// Note: It's safe to rely on the order here as `upsertWithReplace` preserves the order of the input
|
426
|
+
normalizedInput.map(async (product, i) => {
|
427
|
+
const upsertedProduct = productData[i];
|
428
|
+
let allOptions = [];
|
429
|
+
if (product.options?.length) {
|
430
|
+
upsertedProduct.options =
|
431
|
+
await this.productOptionService_.upsertWithReplace(product.options?.map((option) => ({
|
432
|
+
...option,
|
433
|
+
product_id: upsertedProduct.id,
|
434
|
+
})) ?? [], { relations: ["values"] }, sharedContext);
|
435
|
+
// Since we handle the options and variants outside of the product upsert, we need to clean up manuallys
|
436
|
+
await this.productOptionService_.delete({
|
437
|
+
product_id: upsertedProduct.id,
|
438
|
+
id: {
|
439
|
+
$nin: upsertedProduct.options.map(({ id }) => id),
|
440
|
+
},
|
441
|
+
}, sharedContext);
|
442
|
+
allOptions = upsertedProduct.options;
|
491
443
|
}
|
492
|
-
|
493
|
-
|
494
|
-
|
444
|
+
else {
|
445
|
+
// If the options weren't affected, but the user is changing the variants, make sure we have all options available locally
|
446
|
+
if (product.variants?.length) {
|
447
|
+
allOptions = await this.productOptionService_.list({ product_id: upsertedProduct.id }, { take: null }, sharedContext);
|
448
|
+
}
|
495
449
|
}
|
496
|
-
if (
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
450
|
+
if (product.variants?.length) {
|
451
|
+
upsertedProduct.variants =
|
452
|
+
await this.productVariantService_.upsertWithReplace(ProductModuleService.assignOptionsToVariants(product.variants?.map((v) => ({
|
453
|
+
...v,
|
454
|
+
product_id: upsertedProduct.id,
|
455
|
+
})) ?? [], allOptions), { relations: ["options"] }, sharedContext);
|
456
|
+
await this.productVariantService_.delete({
|
457
|
+
product_id: upsertedProduct.id,
|
458
|
+
id: {
|
459
|
+
$nin: upsertedProduct.variants.map(({ id }) => id),
|
460
|
+
},
|
461
|
+
}, sharedContext);
|
501
462
|
}
|
502
|
-
return productData;
|
503
463
|
}));
|
504
|
-
return
|
464
|
+
return productData;
|
505
465
|
}
|
506
466
|
static normalizeCreateProductInput(product) {
|
507
|
-
const productData =
|
467
|
+
const productData = ProductModuleService.normalizeUpdateProductInput(product);
|
508
468
|
if (!productData.handle && productData.title) {
|
509
469
|
productData.handle = (0, utils_1.kebabCase)(productData.title);
|
510
470
|
}
|
511
|
-
if (productData.is_giftcard) {
|
512
|
-
productData.discountable = false;
|
513
|
-
}
|
514
471
|
if (!productData.status) {
|
515
|
-
productData.status =
|
472
|
+
productData.status = utils_1.ProductStatus.DRAFT;
|
516
473
|
}
|
517
474
|
if (!productData.thumbnail && productData.images?.length) {
|
518
|
-
productData.thumbnail =
|
519
|
-
? productData.images[0]
|
520
|
-
: productData.images[0].url;
|
475
|
+
productData.thumbnail = productData.images[0].url;
|
521
476
|
}
|
522
|
-
return
|
477
|
+
return productData;
|
523
478
|
}
|
524
479
|
static normalizeUpdateProductInput(product) {
|
525
480
|
const productData = { ...product };
|
526
481
|
if (productData.is_giftcard) {
|
527
482
|
productData.discountable = false;
|
528
483
|
}
|
529
|
-
if (productData.images?.length) {
|
530
|
-
productData.images = productData.images?.map((image) => {
|
531
|
-
if ((0, utils_1.isString)(image)) {
|
532
|
-
return { url: image };
|
533
|
-
}
|
534
|
-
return image;
|
535
|
-
});
|
536
|
-
}
|
537
484
|
if (productData.options?.length) {
|
485
|
+
;
|
538
486
|
productData.options = productData.options?.map((option) => {
|
539
487
|
return {
|
540
488
|
title: option.title,
|
@@ -548,20 +496,32 @@ class ProductModuleService extends utils_1.ModulesSdkUtils.abstractModuleService
|
|
548
496
|
}
|
549
497
|
return productData;
|
550
498
|
}
|
551
|
-
static
|
499
|
+
static normalizeCreateProductCollectionInput(collection) {
|
500
|
+
const collectionData = ProductModuleService.normalizeUpdateProductCollectionInput(collection);
|
501
|
+
if (!collectionData.handle && collectionData.title) {
|
502
|
+
collectionData.handle = (0, utils_1.kebabCase)(collectionData.title);
|
503
|
+
}
|
504
|
+
return collectionData;
|
505
|
+
}
|
506
|
+
static normalizeUpdateProductCollectionInput(collection) {
|
552
507
|
const collectionData = { ...collection };
|
553
508
|
if (collectionData.product_ids?.length) {
|
554
509
|
;
|
555
|
-
collectionData.products = collectionData.product_ids
|
510
|
+
collectionData.products = collectionData.product_ids.map((pid) => ({
|
511
|
+
id: pid,
|
512
|
+
}));
|
556
513
|
delete collectionData.product_ids;
|
557
514
|
}
|
558
515
|
return collectionData;
|
559
516
|
}
|
560
517
|
static assignOptionsToVariants(variants, options) {
|
518
|
+
if (!variants.length) {
|
519
|
+
return variants;
|
520
|
+
}
|
561
521
|
const variantsWithOptions = variants.map((variant) => {
|
562
522
|
const variantOptions = Object.entries(variant.options ?? {}).map(([key, val]) => {
|
563
523
|
const option = options.find((o) => o.title === key);
|
564
|
-
const optionValue = option?.values?.find((v) => (v.value
|
524
|
+
const optionValue = option?.values?.find((v) => (v.value?.value ?? v.value) === val);
|
565
525
|
if (!optionValue) {
|
566
526
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Option value ${val} does not exist for option ${key}`);
|
567
527
|
}
|
@@ -614,13 +574,6 @@ __decorate([
|
|
614
574
|
__metadata("design:paramtypes", [Array, Object]),
|
615
575
|
__metadata("design:returntype", Promise)
|
616
576
|
], ProductModuleService.prototype, "updateVariants_", null);
|
617
|
-
__decorate([
|
618
|
-
(0, utils_1.InjectTransactionManager)("baseRepository_"),
|
619
|
-
__param(2, (0, utils_1.MedusaContext)()),
|
620
|
-
__metadata("design:type", Function),
|
621
|
-
__metadata("design:paramtypes", [Array, Array, Object]),
|
622
|
-
__metadata("design:returntype", Promise)
|
623
|
-
], ProductModuleService.prototype, "diffVariants_", null);
|
624
577
|
__decorate([
|
625
578
|
(0, utils_1.InjectTransactionManager)("baseRepository_"),
|
626
579
|
__param(1, (0, utils_1.MedusaContext)()),
|
@@ -636,17 +589,24 @@ __decorate([
|
|
636
589
|
__metadata("design:returntype", Promise)
|
637
590
|
], ProductModuleService.prototype, "updateTags", null);
|
638
591
|
__decorate([
|
639
|
-
(0, utils_1.
|
592
|
+
(0, utils_1.InjectManager)("baseRepository_"),
|
640
593
|
__param(1, (0, utils_1.MedusaContext)()),
|
641
594
|
__metadata("design:type", Function),
|
642
|
-
__metadata("design:paramtypes", [
|
595
|
+
__metadata("design:paramtypes", [Object, Object]),
|
643
596
|
__metadata("design:returntype", Promise)
|
644
597
|
], ProductModuleService.prototype, "createTypes", null);
|
645
598
|
__decorate([
|
646
599
|
(0, utils_1.InjectTransactionManager)("baseRepository_"),
|
647
600
|
__param(1, (0, utils_1.MedusaContext)()),
|
648
601
|
__metadata("design:type", Function),
|
649
|
-
__metadata("design:paramtypes", [
|
602
|
+
__metadata("design:paramtypes", [Object, Object]),
|
603
|
+
__metadata("design:returntype", Promise)
|
604
|
+
], ProductModuleService.prototype, "upsertTypes", null);
|
605
|
+
__decorate([
|
606
|
+
(0, utils_1.InjectManager)("baseRepository_"),
|
607
|
+
__param(2, (0, utils_1.MedusaContext)()),
|
608
|
+
__metadata("design:type", Function),
|
609
|
+
__metadata("design:paramtypes", [Object, Object, Object]),
|
650
610
|
__metadata("design:returntype", Promise)
|
651
611
|
], ProductModuleService.prototype, "updateTypes", null);
|
652
612
|
__decorate([
|
@@ -684,13 +644,6 @@ __decorate([
|
|
684
644
|
__metadata("design:paramtypes", [Array, Object]),
|
685
645
|
__metadata("design:returntype", Promise)
|
686
646
|
], ProductModuleService.prototype, "updateOptions_", null);
|
687
|
-
__decorate([
|
688
|
-
(0, utils_1.InjectTransactionManager)("baseRepository_"),
|
689
|
-
__param(1, (0, utils_1.MedusaContext)()),
|
690
|
-
__metadata("design:type", Function),
|
691
|
-
__metadata("design:paramtypes", [Array, Object]),
|
692
|
-
__metadata("design:returntype", Promise)
|
693
|
-
], ProductModuleService.prototype, "diffOptions_", null);
|
694
647
|
__decorate([
|
695
648
|
(0, utils_1.InjectManager)("baseRepository_"),
|
696
649
|
__param(1, (0, utils_1.MedusaContext)()),
|
@@ -782,10 +735,3 @@ __decorate([
|
|
782
735
|
__metadata("design:paramtypes", [Array, Object]),
|
783
736
|
__metadata("design:returntype", Promise)
|
784
737
|
], ProductModuleService.prototype, "update_", null);
|
785
|
-
const uniqBy = (arr, key) => {
|
786
|
-
const seen = new Set();
|
787
|
-
return arr.filter((item) => {
|
788
|
-
const k = item[key];
|
789
|
-
return seen.has(k) ? false : seen.add(k);
|
790
|
-
});
|
791
|
-
};
|
@@ -3,11 +3,19 @@ import { Product } from "../models";
|
|
3
3
|
type InjectedDependencies = {
|
4
4
|
productRepository: DAL.RepositoryService;
|
5
5
|
};
|
6
|
+
type NormalizedFilterableProductProps = ProductTypes.FilterableProductProps & {
|
7
|
+
categories?: {
|
8
|
+
id: string | {
|
9
|
+
$in: string[];
|
10
|
+
};
|
11
|
+
};
|
12
|
+
};
|
6
13
|
declare const ProductService_base: new <TEntity_1 extends object = any>(container: InjectedDependencies) => import("@medusajs/types").InternalModuleService<TEntity_1, InjectedDependencies>;
|
7
14
|
export default class ProductService<TEntity extends Product = Product> extends ProductService_base<TEntity> {
|
8
15
|
protected readonly productRepository_: DAL.RepositoryService<TEntity>;
|
9
16
|
constructor({ productRepository }: InjectedDependencies);
|
10
17
|
list(filters?: ProductTypes.FilterableProductProps, config?: FindConfig<TEntity>, sharedContext?: Context): Promise<TEntity[]>;
|
11
18
|
listAndCount(filters?: ProductTypes.FilterableProductProps, config?: FindConfig<any>, sharedContext?: Context): Promise<[TEntity[], number]>;
|
19
|
+
protected static normalizeFilters(filters?: NormalizedFilterableProductProps): NormalizedFilterableProductProps;
|
12
20
|
}
|
13
21
|
export {};
|
package/dist/services/product.js
CHANGED
@@ -23,22 +23,12 @@ class ProductService extends utils_1.ModulesSdkUtils.internalModuleServiceFactor
|
|
23
23
|
this.productRepository_ = productRepository;
|
24
24
|
}
|
25
25
|
async list(filters = {}, config = {}, sharedContext = {}) {
|
26
|
-
|
27
|
-
if (Array.isArray(filters.category_id)) {
|
28
|
-
filters.categories = {
|
29
|
-
id: { $in: filters.category_id },
|
30
|
-
};
|
31
|
-
}
|
32
|
-
else {
|
33
|
-
filters.categories = {
|
34
|
-
id: filters.category_id,
|
35
|
-
};
|
36
|
-
}
|
37
|
-
delete filters.category_id;
|
38
|
-
}
|
39
|
-
return await super.list(filters, config, sharedContext);
|
26
|
+
return await super.list(ProductService.normalizeFilters(filters), config, sharedContext);
|
40
27
|
}
|
41
28
|
async listAndCount(filters = {}, config = {}, sharedContext = {}) {
|
29
|
+
return await super.listAndCount(ProductService.normalizeFilters(filters), config, sharedContext);
|
30
|
+
}
|
31
|
+
static normalizeFilters(filters = {}) {
|
42
32
|
if (filters.category_id) {
|
43
33
|
if (Array.isArray(filters.category_id)) {
|
44
34
|
filters.categories = {
|
@@ -52,7 +42,7 @@ class ProductService extends utils_1.ModulesSdkUtils.internalModuleServiceFactor
|
|
52
42
|
}
|
53
43
|
delete filters.category_id;
|
54
44
|
}
|
55
|
-
return
|
45
|
+
return filters;
|
56
46
|
}
|
57
47
|
}
|
58
48
|
exports.default = ProductService;
|
package/dist/types/index.d.ts
CHANGED
@@ -19,9 +19,6 @@ export declare enum ProductEvents {
|
|
19
19
|
PRODUCT_CREATED = "product.created",
|
20
20
|
PRODUCT_DELETED = "product.deleted"
|
21
21
|
}
|
22
|
-
export type UpdateProductInput = ProductTypes.UpdateProductDTO & {
|
23
|
-
id: string;
|
24
|
-
};
|
25
22
|
export type ProductCollectionEventData = {
|
26
23
|
id: string;
|
27
24
|
};
|
@@ -30,6 +27,9 @@ export declare enum ProductCollectionEvents {
|
|
30
27
|
COLLECTION_CREATED = "product-collection.created",
|
31
28
|
COLLECTION_DELETED = "product-collection.deleted"
|
32
29
|
}
|
30
|
+
export type UpdateProductInput = ProductTypes.UpdateProductDTO & {
|
31
|
+
id: string;
|
32
|
+
};
|
33
33
|
export type UpdateProductCollection = ProductTypes.UpdateProductCollectionDTO & {
|
34
34
|
products?: string[];
|
35
35
|
};
|
@@ -39,9 +39,12 @@ export type CreateProductCollection = ProductTypes.CreateProductCollectionDTO &
|
|
39
39
|
export type UpdateCollectionInput = ProductTypes.UpdateProductCollectionDTO & {
|
40
40
|
id: string;
|
41
41
|
};
|
42
|
+
export type UpdateTypeInput = ProductTypes.UpdateProductTypeDTO & {
|
43
|
+
id: string;
|
44
|
+
};
|
42
45
|
export type UpdateProductVariantInput = ProductTypes.UpdateProductVariantDTO & {
|
43
46
|
id: string;
|
44
|
-
product_id?: string;
|
47
|
+
product_id?: string | null;
|
45
48
|
};
|
46
49
|
export type UpdateProductOptionInput = ProductTypes.UpdateProductOptionDTO & {
|
47
50
|
id: string;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@medusajs/product",
|
3
|
-
"version": "0.4.0-snapshot-
|
3
|
+
"version": "0.4.0-snapshot-20240401075323",
|
4
4
|
"description": "Medusa Product module",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"types": "dist/index.d.ts",
|
@@ -41,7 +41,7 @@
|
|
41
41
|
"cross-env": "^5.2.1",
|
42
42
|
"faker": "^6.6.6",
|
43
43
|
"jest": "^29.6.3",
|
44
|
-
"medusa-test-utils": "1.1.
|
44
|
+
"medusa-test-utils": "1.1.44-snapshot-20240401075323",
|
45
45
|
"pg-god": "^1.0.12",
|
46
46
|
"rimraf": "^3.0.2",
|
47
47
|
"ts-jest": "^29.1.1",
|
@@ -50,9 +50,9 @@
|
|
50
50
|
"typescript": "^5.1.6"
|
51
51
|
},
|
52
52
|
"dependencies": {
|
53
|
-
"@medusajs/modules-sdk": "1.12.
|
54
|
-
"@medusajs/types": "1.12.0-snapshot-
|
55
|
-
"@medusajs/utils": "1.
|
53
|
+
"@medusajs/modules-sdk": "1.12.11-snapshot-20240401075323",
|
54
|
+
"@medusajs/types": "1.12.0-snapshot-20240401075323",
|
55
|
+
"@medusajs/utils": "1.12.0-snapshot-20240401075323",
|
56
56
|
"@mikro-orm/core": "5.9.7",
|
57
57
|
"@mikro-orm/migrations": "5.9.7",
|
58
58
|
"@mikro-orm/postgresql": "5.9.7",
|