@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.
@@ -1,4 +1,4 @@
1
- import { Migration } from "@mikro-orm/migrations";
1
+ import { Migration } from '@mikro-orm/migrations';
2
2
  export declare class InitialSetup20240315083440 extends Migration {
3
3
  up(): Promise<void>;
4
4
  }
@@ -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 set null;');
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.Property)({ columnType: "text", nullable: true }),
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.Property)({ columnType: "text", nullable: true }),
45
- __metadata("design:type", String)
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
- index: "IDX_product_option_value_option_id",
50
- fieldName: "option_id",
58
+ nullable: true,
59
+ persist: false,
51
60
  }),
52
- __metadata("design:type", index_1.ProductOption)
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.Property)({ columnType: "text", nullable: true }),
49
- __metadata("design:type", String)
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
- fieldName: "product_id",
61
+ persist: false,
54
62
  nullable: true,
55
63
  }),
56
- __metadata("design:type", index_1.Product)
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, {
@@ -3,7 +3,7 @@ declare class ProductVariantOption {
3
3
  id: string;
4
4
  option_value_id: string;
5
5
  option_value: ProductOptionValue;
6
- variant_id: string;
6
+ variant_id: string | null;
7
7
  variant: ProductVariant;
8
8
  created_at: Date;
9
9
  updated_at: Date;
@@ -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.Property)({ columnType: "text", nullable: true }),
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
- fieldName: "option_value_id",
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.Property)({ columnType: "text", nullable: true }),
49
- __metadata("design:type", String)
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
- fieldName: "variant_id",
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", _models_1.Product)
155
+ __metadata("design:type", Object)
149
156
  ], ProductVariant.prototype, "product", void 0);
150
157
  __decorate([
151
158
  (0, core_1.Property)({
@@ -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>;
@@ -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
- if (!this.handle) {
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.Property)({ columnType: "text", nullable: true }),
125
- __metadata("design:type", String)
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
- fieldName: "collection_id",
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.Property)({ columnType: "text", nullable: true }),
136
- __metadata("design:type", String)
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
- index: "IDX_product_type_id",
142
- fieldName: "type_id",
159
+ persist: false,
143
160
  }),
144
- __metadata("design:type", product_type_1.default)
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, UpdateProductVariantInput, UpdateProductOptionInput } from "../types";
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
- updateTypes(data: ProductTypes.UpdateProductTypeDTO[], sharedContext?: Context): Promise<ProductTypes.ProductTypeDTO[]>;
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: ProductTypes.UpdateProductDTO): ProductTypes.UpdateProductDTO;
116
- protected static normalizeProductCollectionInput(collection: ProductTypes.CreateProductCollectionDTO | UpdateCollectionInput): ProductTypes.CreateProductCollectionDTO | UpdateCollectionInput;
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, { populate: true });
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.listVariants({ id: variantIdsToUpdate }, { relations: ["options"], take: null }, sharedContext);
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 await this.diffVariants_(variantsWithProductId, productOptions, sharedContext);
123
- }
124
- async diffVariants_(data, productOptions, sharedContext = {}) {
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, { populate: true });
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, { populate: true });
131
+ return await this.baseRepository_.serialize(productTags);
165
132
  }
166
133
  async createTypes(data, sharedContext = {}) {
167
- const productTypes = await this.productTypeService_.create(data, sharedContext);
168
- return await this.baseRepository_.serialize(productTypes, {
169
- populate: true,
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 updateTypes(data, sharedContext = {}) {
173
- const productTypes = await this.productTypeService_.update(data, sharedContext);
174
- return await this.baseRepository_.serialize(productTypes, {
175
- populate: true,
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, { populate: true });
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
- const productOptions = await this.diffOptions_(normalizedInput, sharedContext);
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, { populate: true });
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.normalizeProductCollectionInput);
309
- return await this.productCollectionService_.create(normalizedInput, sharedContext);
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.normalizeProductCollectionInput);
361
- return await this.productCollectionService_.update(normalizedInput, sharedContext);
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, { populate: true });
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, { populate: true });
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, { populate: true });
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 productsData = await (0, utils_1.promiseAll)(normalizedInput.map(async (product) => {
450
- const productData = { ...product };
451
- if (productData.images?.length) {
452
- productData.images = await this.productImageService_.upsert(productData.images, sharedContext);
453
- }
454
- if (productData.tags?.length) {
455
- productData.tags = await this.productTagService_.upsert(productData.tags, sharedContext);
456
- }
457
- if (productData.type) {
458
- productData.type = await this.productTypeService_.upsert(productData.type, sharedContext);
459
- }
460
- // This is not the cleanest solution, but it's the easiest way to reassign categories for now
461
- if (productData.categories) {
462
- productData.categories = await this.productCategoryService_.list({ id: productData.categories.map((c) => c.id) }, { take: null }, sharedContext);
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 (productData.variants?.length) {
468
- productData.variants = await this.productVariantService_.create(ProductModuleService.assignOptionsToVariants(productData.variants, productData.options), sharedContext);
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 await this.productService_.create(productsData, sharedContext);
416
+ return productData;
473
417
  }
474
418
  async update_(data, sharedContext = {}) {
475
419
  const normalizedInput = data.map(ProductModuleService.normalizeUpdateProductInput);
476
- const productsData = await (0, utils_1.promiseAll)(normalizedInput.map(async (product) => {
477
- const productData = { ...product };
478
- // TODO: We don't remove images, tags, and types as they can exist independently. However, how do we handle orphaned entities?
479
- if (productData.images) {
480
- productData.images = await this.productImageService_.upsert(productData.images, sharedContext);
481
- }
482
- if (productData.tags) {
483
- productData.tags = await this.productTagService_.upsert(productData.tags, sharedContext);
484
- }
485
- if (productData.type) {
486
- productData.type = await this.productTypeService_.upsert(productData.type, sharedContext);
487
- }
488
- // This is not the cleanest solution, but it's the easiest way to reassign categories for now
489
- if (productData.categories) {
490
- productData.categories = await this.productCategoryService_.list({ id: productData.categories.map((c) => c.id) }, { take: null }, sharedContext);
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
- // TODO: Maybe we also want to delete the options and variants that are not in the list?
493
- if (productData.options) {
494
- productData.options = await this.diffOptions_(productData.options, sharedContext);
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 (productData.variants) {
497
- const dbOptionsForProduct = await this.productOptionService_.list({ product_id: productData.id }, { take: null }, sharedContext);
498
- // Since the options are not flushed yet, we must do this merge here
499
- const allOptionsForProduct = uniqBy([...(productData.options ?? []), ...dbOptionsForProduct], "id");
500
- productData.variants = await this.diffVariants_(productData.variants, allOptionsForProduct, sharedContext);
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 await this.productService_.update(productsData, sharedContext);
464
+ return productData;
505
465
  }
506
466
  static normalizeCreateProductInput(product) {
507
- const productData = { ...product };
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 = utils_2.ProductStatus.DRAFT;
472
+ productData.status = utils_1.ProductStatus.DRAFT;
516
473
  }
517
474
  if (!productData.thumbnail && productData.images?.length) {
518
- productData.thumbnail = (0, utils_1.isString)(productData.images[0])
519
- ? productData.images[0]
520
- : productData.images[0].url;
475
+ productData.thumbnail = productData.images[0].url;
521
476
  }
522
- return ProductModuleService.normalizeUpdateProductInput(productData);
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 normalizeProductCollectionInput(collection) {
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.value ?? v.value) === val);
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.InjectTransactionManager)("baseRepository_"),
592
+ (0, utils_1.InjectManager)("baseRepository_"),
640
593
  __param(1, (0, utils_1.MedusaContext)()),
641
594
  __metadata("design:type", Function),
642
- __metadata("design:paramtypes", [Array, Object]),
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", [Array, Object]),
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 {};
@@ -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
- if (filters.category_id) {
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 await super.listAndCount(filters, config, sharedContext);
45
+ return filters;
56
46
  }
57
47
  }
58
48
  exports.default = ProductService;
@@ -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-20240325085803",
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.43-snapshot-20240325085803",
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.10-snapshot-20240325085803",
54
- "@medusajs/types": "1.12.0-snapshot-20240325085803",
55
- "@medusajs/utils": "1.11.8-snapshot-20240325085803",
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",