@medusajs/product 3.0.0-snapshot-20251106181920 → 3.0.0-snapshot-20251126221441

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/migrations/Migration20250911092221.d.ts +1 -1
  2. package/dist/migrations/Migration20250911092221.d.ts.map +1 -1
  3. package/dist/migrations/Migration20250911092221.js +1 -1
  4. package/dist/migrations/Migration20250911092221.js.map +1 -1
  5. package/dist/migrations/Migration20251011090511.d.ts +1 -1
  6. package/dist/migrations/Migration20251011090511.d.ts.map +1 -1
  7. package/dist/migrations/Migration20251011090511.js +1 -1
  8. package/dist/migrations/Migration20251011090511.js.map +1 -1
  9. package/dist/models/index.d.ts +0 -1
  10. package/dist/models/index.d.ts.map +1 -1
  11. package/dist/models/index.js +1 -3
  12. package/dist/models/index.js.map +1 -1
  13. package/dist/models/product-category.d.ts +6 -14
  14. package/dist/models/product-category.d.ts.map +1 -1
  15. package/dist/models/product-collection.d.ts +6 -14
  16. package/dist/models/product-collection.d.ts.map +1 -1
  17. package/dist/models/product-image.d.ts +14 -26
  18. package/dist/models/product-image.d.ts.map +1 -1
  19. package/dist/models/product-option-value.d.ts +12 -18
  20. package/dist/models/product-option-value.d.ts.map +1 -1
  21. package/dist/models/product-option-value.js +0 -1
  22. package/dist/models/product-option-value.js.map +1 -1
  23. package/dist/models/product-option.d.ts +6 -10
  24. package/dist/models/product-option.d.ts.map +1 -1
  25. package/dist/models/product-option.js +12 -4
  26. package/dist/models/product-option.js.map +1 -1
  27. package/dist/models/product-tag.d.ts +6 -14
  28. package/dist/models/product-tag.d.ts.map +1 -1
  29. package/dist/models/product-type.d.ts +6 -14
  30. package/dist/models/product-type.d.ts.map +1 -1
  31. package/dist/models/product-variant-product-image.d.ts +26 -47
  32. package/dist/models/product-variant-product-image.d.ts.map +1 -1
  33. package/dist/models/product-variant.d.ts +12 -21
  34. package/dist/models/product-variant.d.ts.map +1 -1
  35. package/dist/models/product.d.ts +6 -14
  36. package/dist/models/product.d.ts.map +1 -1
  37. package/dist/models/product.js +3 -5
  38. package/dist/models/product.js.map +1 -1
  39. package/dist/repositories/product-category.d.ts +18 -36
  40. package/dist/repositories/product-category.d.ts.map +1 -1
  41. package/dist/repositories/product.d.ts +6 -14
  42. package/dist/repositories/product.d.ts.map +1 -1
  43. package/dist/repositories/product.js +5 -2
  44. package/dist/repositories/product.js.map +1 -1
  45. package/dist/schema/index.d.ts +1 -1
  46. package/dist/schema/index.d.ts.map +1 -1
  47. package/dist/schema/index.js +1 -0
  48. package/dist/schema/index.js.map +1 -1
  49. package/dist/services/product-module-service.d.ts +4 -20
  50. package/dist/services/product-module-service.d.ts.map +1 -1
  51. package/dist/services/product-module-service.js +90 -312
  52. package/dist/services/product-module-service.js.map +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +4 -4
  55. package/dist/migrations/Migration20251022153442.d.ts +0 -6
  56. package/dist/migrations/Migration20251022153442.d.ts.map +0 -1
  57. package/dist/migrations/Migration20251022153442.js +0 -104
  58. package/dist/migrations/Migration20251022153442.js.map +0 -1
  59. package/dist/migrations/Migration20251029150809.d.ts +0 -6
  60. package/dist/migrations/Migration20251029150809.d.ts.map +0 -1
  61. package/dist/migrations/Migration20251029150809.js +0 -14
  62. package/dist/migrations/Migration20251029150809.js.map +0 -1
  63. package/dist/models/product-product-option.d.ts +0 -668
  64. package/dist/models/product-product-option.d.ts.map +0 -1
  65. package/dist/models/product-product-option.js +0 -19
  66. package/dist/models/product-product-option.js.map +0 -1
@@ -11,7 +11,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
- var _a, _b;
14
+ var _a;
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  const types_1 = require("@medusajs/framework/types");
17
17
  const _models_1 = require("../models");
@@ -29,7 +29,7 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
29
29
  ProductVariant: _models_1.ProductVariant,
30
30
  ProductImage: _models_1.ProductImage,
31
31
  }) {
32
- constructor({ baseRepository, productRepository, productService, productVariantService, productTagService, productCategoryService, productCollectionService, productImageService, productTypeService, productOptionService, productProductOptionService, productOptionValueService, productVariantProductImageService, [utils_1.Modules.EVENT_BUS]: eventBusModuleService, }, moduleDeclaration) {
32
+ constructor({ baseRepository, productRepository, productService, productVariantService, productTagService, productCategoryService, productCollectionService, productImageService, productTypeService, productOptionService, productOptionValueService, productVariantProductImageService, [utils_1.Modules.EVENT_BUS]: eventBusModuleService, }, moduleDeclaration) {
33
33
  // @ts-ignore
34
34
  // eslint-disable-next-line prefer-rest-params
35
35
  super(...arguments);
@@ -44,7 +44,6 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
44
44
  this.productImageService_ = productImageService;
45
45
  this.productTypeService_ = productTypeService;
46
46
  this.productOptionService_ = productOptionService;
47
- this.productProductOptionService_ = productProductOptionService;
48
47
  this.productOptionValueService_ = productOptionValueService;
49
48
  this.productVariantProductImageService_ = productVariantProductImageService;
50
49
  this.eventBusModuleService_ = eventBusModuleService;
@@ -143,11 +142,9 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
143
142
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Unable to create variants without specifying a product_id");
144
143
  }
145
144
  const productOptions = await this.productOptionService_.list({
146
- products: {
147
- id: [...new Set(data.map((v) => v.product_id))],
148
- },
145
+ product_id: [...new Set(data.map((v) => v.product_id))],
149
146
  }, {
150
- relations: ["values", "products"],
147
+ relations: ["values"],
151
148
  }, sharedContext);
152
149
  const variants = await this.productVariantService_.list({
153
150
  product_id: [...new Set(data.map((v) => v.product_id))],
@@ -207,10 +204,8 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
207
204
  product_id: v.product_id,
208
205
  }));
209
206
  const productOptions = await this.productOptionService_.list({
210
- products: {
211
- id: Array.from(new Set(variantsWithProductId.map((v) => v.product_id))),
212
- },
213
- }, { relations: ["values", "products"] }, sharedContext);
207
+ product_id: Array.from(new Set(variantsWithProductId.map((v) => v.product_id))),
208
+ }, { relations: ["values"] }, sharedContext);
214
209
  const productVariantsWithOptions = ProductModuleService.assignOptionsToVariants(variantsWithProductId, productOptions);
215
210
  if (data.some((d) => !!d.options)) {
216
211
  ProductModuleService.checkIfVariantWithOptionsAlreadyExists(productVariantsWithOptions, allVariants);
@@ -318,25 +313,18 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
318
313
  return Array.isArray(data) ? createdOptions : createdOptions[0];
319
314
  }
320
315
  async createOptions_(data, sharedContext = {}) {
316
+ if (data.some((v) => !v.product_id)) {
317
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Tried to create options without specifying a product_id");
318
+ }
321
319
  const normalizedInput = data.map((opt) => {
322
- Object.keys(opt.ranks ?? []).forEach((value) => {
323
- if (!opt.values.includes(value)) {
324
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Value "${value}" is assigned a rank but is not defined in the list of values.`);
325
- }
326
- });
327
320
  return {
328
321
  ...opt,
329
322
  values: opt.values?.map((v) => {
330
- // Normalize each value into an object and attach rank if available
331
- const valueObj = (0, utils_1.isString)(v) ? { value: v } : v;
332
- const rank = opt.ranks && (0, utils_1.isString)(v)
333
- ? opt.ranks[v]
334
- : opt.ranks?.[valueObj.value];
335
- return rank !== undefined ? { ...valueObj, rank } : valueObj;
323
+ return typeof v === "string" ? { value: v } : v;
336
324
  }),
337
325
  };
338
326
  });
339
- return this.productOptionService_.create(normalizedInput, sharedContext);
327
+ return await this.productOptionService_.create(normalizedInput, sharedContext);
340
328
  }
341
329
  async upsertProductOptions(data, sharedContext = {}) {
342
330
  const input = Array.isArray(data) ? data : [data];
@@ -383,77 +371,35 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
383
371
  }
384
372
  // Data normalization
385
373
  const normalizedInput = data.map((opt) => {
386
- const dbOption = dbOptions.find(({ id }) => id === opt.id);
387
- const dbValues = dbOption?.values || [];
388
- if (opt.ranks) {
389
- const validValues = opt.values ?? dbValues.map((v) => v.value);
390
- Object.keys(opt.ranks).forEach((value) => {
391
- if (!validValues.includes(value)) {
392
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Value "${value}" is assigned a rank but is not defined in the list of values.`);
393
- }
394
- });
395
- }
396
- let normalizedValues;
397
- if (opt.values) {
398
- // If new values are provided → normalize and apply ranks
399
- normalizedValues = opt.values.map((v) => {
400
- const valueObj = (0, utils_1.isString)(v) ? { value: v } : v;
401
- const rank = opt.ranks && (0, utils_1.isString)(v)
402
- ? opt.ranks[v]
403
- : opt.ranks?.[valueObj.value];
404
- const rankedValue = rank !== undefined ? { ...valueObj, rank } : valueObj;
405
- if ("id" in rankedValue) {
406
- return rankedValue;
407
- }
408
- const dbVal = dbValues.find((dbVal) => dbVal.value === rankedValue.value);
409
- if (!dbVal) {
410
- return rankedValue;
411
- }
412
- return {
413
- id: dbVal.id,
414
- ...rankedValue,
415
- };
416
- });
417
- }
418
- else if (opt.ranks) {
419
- // If only ranks were provided → update existing DB values with ranks
420
- normalizedValues = dbValues.map((dbVal) => {
421
- const rank = opt.ranks[dbVal.value];
422
- return rank !== undefined
423
- ? { id: dbVal.id, value: dbVal.value, rank }
424
- : { id: dbVal.id, value: dbVal.value };
425
- });
426
- }
427
- const { ranks, ...cleanOpt } = opt;
374
+ const dbValues = dbOptions.find(({ id }) => id === opt.id)?.values || [];
375
+ const normalizedValues = opt.values?.map((v) => {
376
+ return typeof v === "string" ? { value: v } : v;
377
+ });
428
378
  return {
429
- ...cleanOpt,
430
- ...(normalizedValues ? { values: normalizedValues } : {}),
379
+ ...opt,
380
+ ...(normalizedValues
381
+ ? {
382
+ // Oftentimes the options are only passed by value without an id, even if they exist in the DB
383
+ values: normalizedValues.map((normVal) => {
384
+ if ("id" in normVal) {
385
+ return normVal;
386
+ }
387
+ const dbVal = dbValues.find((dbVal) => dbVal.value === normVal.value);
388
+ if (!dbVal) {
389
+ return normVal;
390
+ }
391
+ return {
392
+ id: dbVal.id,
393
+ value: normVal.value,
394
+ };
395
+ }),
396
+ }
397
+ : {}),
431
398
  };
432
399
  });
433
400
  const { entities: productOptions } = await this.productOptionService_.upsertWithReplace(normalizedInput, { relations: ["values"] }, sharedContext);
434
401
  return productOptions;
435
402
  }
436
- async addProductOptionToProduct(data, sharedContext = {}) {
437
- const productOptionProducts = await this.addProductOptionToProduct_(data, sharedContext);
438
- return productOptionProducts;
439
- }
440
- async addProductOptionToProduct_(data, sharedContext = {}) {
441
- const productOptionProducts = await this.productProductOptionService_.create(data, sharedContext);
442
- if (Array.isArray(data)) {
443
- return productOptionProducts.map((ppo) => ({ id: ppo.id }));
444
- }
445
- return { id: productOptionProducts.id };
446
- }
447
- async removeProductOptionFromProduct(data, sharedContext = {}) {
448
- await this.removeProductOptionFromProduct_(data, sharedContext);
449
- }
450
- async removeProductOptionFromProduct_(data, sharedContext = {}) {
451
- const pairs = Array.isArray(data) ? data : [data];
452
- const productOptionsProducts = await this.productProductOptionService_.list({
453
- $or: pairs,
454
- });
455
- await this.productProductOptionService_.delete(productOptionsProducts.map(({ id }) => id), sharedContext);
456
- }
457
403
  // @ts-expect-error
458
404
  async createProductCollections(data, sharedContext = {}) {
459
405
  const input = Array.isArray(data) ? data : [data];
@@ -678,40 +624,7 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
678
624
  return (0, utils_1.isString)(idOrSelector) ? updatedProducts[0] : updatedProducts;
679
625
  }
680
626
  async createProducts_(data, sharedContext = {}) {
681
- const existingOptionIds = data
682
- .flatMap((p) => p.options ?? [])
683
- .filter((o) => "id" in o)
684
- .map((o) => o.id);
685
- let existingOptions = [];
686
- if (existingOptionIds.length > 0) {
687
- existingOptions = await this.productOptionService_.list({ id: existingOptionIds }, { relations: ["values"] }, sharedContext);
688
- const fetchedIds = new Set(existingOptions.map((opt) => opt.id));
689
- const missingIds = existingOptionIds.filter((id) => !fetchedIds.has(id));
690
- if (missingIds.length) {
691
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Some product options were not found: [${missingIds.join(", ")}]`);
692
- }
693
- }
694
- const existingOptionsMap = new Map(existingOptions.map((opt) => [opt.id, opt]));
695
- const hydratedData = data.map((product) => {
696
- if (!product.options?.length)
697
- return product;
698
- const hydratedOptions = product.options.map((option) => {
699
- if ("id" in option) {
700
- const dbOption = existingOptionsMap.get(option.id);
701
- if (!dbOption) {
702
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Product option with id ${option.id} not found.`);
703
- }
704
- return {
705
- id: dbOption.id,
706
- title: dbOption.title,
707
- values: dbOption.values?.map((v) => ({ value: v.value })),
708
- };
709
- }
710
- return option;
711
- });
712
- return { ...product, options: hydratedOptions };
713
- });
714
- const normalizedProducts = this.normalizeCreateProductInput(hydratedData, sharedContext);
627
+ const normalizedProducts = await this.normalizeCreateProductInput(data, sharedContext);
715
628
  for (const product of normalizedProducts) {
716
629
  this.validateProductCreatePayload(product);
717
630
  }
@@ -725,7 +638,6 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
725
638
  }, {}, sharedContext);
726
639
  }
727
640
  const existingTagsMap = new Map(existingTags.map((tag) => [tag.id, tag]));
728
- const productOptionsToCreate = new Map();
729
641
  const productsToCreate = normalizedProducts.map((product) => {
730
642
  const productId = (0, utils_1.generateEntityId)(product.id, "prod");
731
643
  product.id = productId;
@@ -733,12 +645,6 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
733
645
  ;
734
646
  product.categories = product.categories.map((category) => category.id);
735
647
  }
736
- if (product.options?.length) {
737
- const newOptions = product.options.filter((o) => !("id" in o));
738
- if (newOptions.length) {
739
- productOptionsToCreate.set(productId, newOptions);
740
- }
741
- }
742
648
  if (product.variants?.length) {
743
649
  const normalizedVariants = product.variants.map((variant) => {
744
650
  const variantId = (0, utils_1.generateEntityId)(variant.id, "variant");
@@ -764,66 +670,10 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
764
670
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Tag with id ${tag.id} not found. Please create the tag before associating it with the product.`);
765
671
  });
766
672
  }
767
- delete product.options;
768
673
  return product;
769
674
  });
770
- const productToOptionIdsMap = new Map();
771
- const allOptionsWithIds = [];
772
- for (const [productId, options] of productOptionsToCreate.entries()) {
773
- const optionIds = [];
774
- for (const option of options) {
775
- const optionId = (0, utils_1.generateEntityId)(undefined, "opt");
776
- optionIds.push(optionId);
777
- allOptionsWithIds.push({
778
- ...option,
779
- id: optionId,
780
- });
781
- }
782
- productToOptionIdsMap.set(productId, optionIds);
783
- }
784
- const [createdProducts] = await Promise.all([
785
- this.productService_.create(productsToCreate, sharedContext),
786
- allOptionsWithIds.length > 0
787
- ? this.createOptions_(allOptionsWithIds, sharedContext)
788
- : Promise.resolve([]),
789
- ]);
790
- const linkPairs = [];
791
- for (const product of createdProducts) {
792
- const hydratedProduct = hydratedData.find((p) => p.title === product.title);
793
- const allOptionIds = [];
794
- if (hydratedProduct?.options?.length) {
795
- for (const option of hydratedProduct.options) {
796
- if ("id" in option) {
797
- allOptionIds.push(option.id);
798
- }
799
- }
800
- }
801
- const newOptionIds = productToOptionIdsMap.get(product.id) ?? [];
802
- const optionIds = [...new Set([...allOptionIds, ...newOptionIds])];
803
- for (const optionId of optionIds) {
804
- linkPairs.push({
805
- product_id: product.id,
806
- product_option_id: optionId,
807
- });
808
- }
809
- }
810
- if (linkPairs.length > 0) {
811
- await this.addProductOptionToProduct_(linkPairs, sharedContext);
812
- }
813
- const productIds = createdProducts.map((p) => p.id);
814
- const productsWithOptions = await this.productService_.list({ id: productIds }, {
815
- relations: [
816
- "options",
817
- "options.values",
818
- "options.products",
819
- "variants",
820
- "images",
821
- "tags",
822
- ],
823
- }, sharedContext);
824
- const productIdOrder = new Map(productIds.map((id, index) => [id, index]));
825
- const orderedProductsWithOptions = [...productsWithOptions].sort((a, b) => (productIdOrder.get(a.id) ?? 0) - (productIdOrder.get(b.id) ?? 0));
826
- return orderedProductsWithOptions;
675
+ const createdProducts = await this.productService_.create(productsToCreate, sharedContext);
676
+ return createdProducts;
827
677
  }
828
678
  async updateProducts_(data, sharedContext = {}) {
829
679
  // We have to do that manually because this method is bypassing the product service and goes
@@ -836,69 +686,12 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
836
686
  .getEventManager()
837
687
  .registerSubscriber(new subscriber(sharedContext));
838
688
  }
839
- const allOptionIds = data
840
- .flatMap((p) => p.option_ids ?? [])
841
- .filter((id) => !!id);
842
- const [originalProducts, existingOptions] = await Promise.all([
843
- this.productService_.list({ id: data.map((d) => d.id) }, {
844
- relations: [
845
- "options",
846
- "options.values",
847
- "options.products",
848
- "variants",
849
- "images",
850
- "tags",
851
- ],
852
- }, sharedContext),
853
- allOptionIds.length
854
- ? this.productOptionService_.list({ id: allOptionIds }, {
855
- relations: ["values", "products"],
856
- }, sharedContext)
857
- : Promise.resolve([]),
858
- ]);
859
- if (allOptionIds.length && existingOptions.length !== allOptionIds.length) {
860
- const found = new Set(existingOptions.map((opt) => opt.id));
861
- const missing = allOptionIds.filter((id) => !found.has(id));
862
- if (missing.length) {
863
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Some product options were not found: [${missing.join(", ")}]`);
864
- }
865
- }
866
- const linkPairs = [];
867
- const unlinkPairs = [];
868
- for (const product of data) {
869
- if (!product.option_ids) {
870
- continue;
871
- }
872
- const newOptionIds = new Set(product.option_ids);
873
- const existingOptionIds = new Set(originalProducts
874
- .find((p) => p.id === product.id)
875
- ?.options?.map((o) => o.id) ?? []);
876
- for (const optionId of newOptionIds) {
877
- if (!existingOptionIds.has(optionId)) {
878
- linkPairs.push({
879
- product_id: product.id,
880
- product_option_id: optionId,
881
- });
882
- }
883
- }
884
- for (const optionId of existingOptionIds) {
885
- if (!newOptionIds.has(optionId)) {
886
- unlinkPairs.push({
887
- product_id: product.id,
888
- product_option_id: optionId,
889
- });
890
- }
891
- }
892
- delete product.option_ids;
893
- }
894
- await Promise.all([
895
- linkPairs.length &&
896
- this.addProductOptionToProduct_(linkPairs, sharedContext),
897
- unlinkPairs.length &&
898
- this.removeProductOptionFromProduct_(unlinkPairs, sharedContext),
899
- ]);
900
- await sharedContext.transactionManager.flush();
901
- const normalizedProducts = this.normalizeUpdateProductInput(data);
689
+ const originalProducts = await this.productService_.list({
690
+ id: data.map((d) => d.id),
691
+ }, {
692
+ relations: ["options", "options.values", "variants", "images", "tags"],
693
+ }, sharedContext);
694
+ const normalizedProducts = await this.normalizeUpdateProductInput(data, originalProducts);
902
695
  for (const product of normalizedProducts) {
903
696
  this.validateProductUpdatePayload(product);
904
697
  }
@@ -907,7 +700,7 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
907
700
  }
908
701
  // @ts-expect-error
909
702
  async updateProductOptionValues(idOrSelector, data, sharedContext = {}) {
910
- // TODO: There is a mismatch in the API which lead to function with different number of
703
+ // TODO: There is a missmatch in the API which lead to function with different number of
911
704
  // arguments. Therefore, applying the MedusaContext() decorator to the function will not work
912
705
  // because the context arg index will differ from method to method.
913
706
  sharedContext.messageAggregator ??= new utils_1.MessageAggregator();
@@ -979,24 +772,10 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
979
772
  validateProductUpdatePayload(productData) {
980
773
  this.validateProductPayload(productData);
981
774
  }
982
- normalizeCreateProductInput(products, sharedContext = {}) {
775
+ async normalizeCreateProductInput(products, sharedContext = {}) {
983
776
  const products_ = Array.isArray(products) ? products : [products];
984
- const normalizedProducts = this.normalizeUpdateProductInput(products_);
777
+ const normalizedProducts = (await this.normalizeUpdateProductInput(products_));
985
778
  for (const productData of normalizedProducts) {
986
- if (productData.options?.length) {
987
- ;
988
- productData.options = productData.options?.map((option) => {
989
- return {
990
- title: option.title,
991
- values: option.values?.map((value) => {
992
- return {
993
- value: value,
994
- };
995
- }),
996
- ...(option.id ? { id: option.id } : {}),
997
- };
998
- });
999
- }
1000
779
  if (!productData.handle && productData.title) {
1001
780
  productData.handle = (0, utils_1.toHandle)(productData.title);
1002
781
  }
@@ -1037,18 +816,46 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
1037
816
  * @param originalProducts - The original products to use for the normalization (must include options and option values relations)
1038
817
  * @returns The normalized products
1039
818
  */
1040
- normalizeUpdateProductInput(products) {
819
+ async normalizeUpdateProductInput(products, originalProducts) {
1041
820
  const products_ = Array.isArray(products) ? products : [products];
821
+ const productsIds = products_.map((p) => p.id).filter(Boolean);
822
+ let dbOptions = [];
823
+ if (productsIds.length) {
824
+ // Re map options to handle non serialized data as well
825
+ dbOptions =
826
+ originalProducts
827
+ ?.map((originalProduct) => originalProduct.options.map((option) => option))
828
+ .flat()
829
+ .filter(Boolean) ?? [];
830
+ }
1042
831
  const normalizedProducts = [];
1043
832
  for (const product of products_) {
1044
833
  const productData = { ...product };
1045
834
  if (productData.is_giftcard) {
1046
835
  productData.discountable = false;
1047
836
  }
837
+ if (productData.options?.length) {
838
+ ;
839
+ productData.options = productData.options?.map((option) => {
840
+ const dbOption = dbOptions.find((o) => (o.title === option.title || o.id === option.id) &&
841
+ o.product_id === productData.id);
842
+ return {
843
+ title: option.title,
844
+ values: option.values?.map((value) => {
845
+ const dbValue = dbOption?.values?.find((val) => val.value === value);
846
+ return {
847
+ value: value,
848
+ ...(dbValue ? { id: dbValue.id } : {}),
849
+ };
850
+ }),
851
+ ...(dbOption ? { id: dbOption.id } : {}),
852
+ };
853
+ });
854
+ }
1048
855
  if (productData.tag_ids) {
1049
856
  ;
1050
- productData.tags = productData.tag_ids.map((tid) => ({
1051
- id: tid,
857
+ productData.tags = productData.tag_ids.map((cid) => ({
858
+ id: cid,
1052
859
  }));
1053
860
  delete productData.tag_ids;
1054
861
  }
@@ -1083,10 +890,7 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
1083
890
  const variantsWithOptions = ProductModuleService.assignOptionsToVariants(variants.map((v) => ({
1084
891
  ...v,
1085
892
  // adding product_id to the variant to make it valid for the assignOptionsToVariants function
1086
- // get product_id from the first product in the products array of the first option
1087
- ...(options.length && options[0].products?.length
1088
- ? { product_id: options[0].products[0].id }
1089
- : {}),
893
+ ...(options.length ? { product_id: options[0].product_id } : {}),
1090
894
  })), options);
1091
895
  ProductModuleService.checkIfVariantsHaveUniqueOptionsCombinations(variantsWithOptions);
1092
896
  }
@@ -1096,13 +900,7 @@ class ProductModuleService extends (0, utils_1.MedusaService)({
1096
900
  }
1097
901
  const variantsWithOptions = variants.map((variant) => {
1098
902
  const numOfProvidedVariantOptionValues = Object.keys(variant.options || {}).length;
1099
- const productsOptions = options.filter((o) => {
1100
- // products could be a Collection object or array, normalize to array
1101
- const productsArray = Array.isArray(o.products)
1102
- ? o.products
1103
- : o.products?.toArray?.() ?? [];
1104
- return productsArray.some((p) => p.id === variant.product_id);
1105
- });
903
+ const productsOptions = options.filter((o) => o.product_id === variant.product_id);
1106
904
  if (numOfProvidedVariantOptionValues &&
1107
905
  productsOptions.length !== numOfProvidedVariantOptionValues) {
1108
906
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Product has ${productsOptions.length} option values but there were ${numOfProvidedVariantOptionValues} provided option values for the variant: ${variant.title}.`);
@@ -1514,36 +1312,6 @@ __decorate([
1514
1312
  __metadata("design:paramtypes", [Array, Object]),
1515
1313
  __metadata("design:returntype", Promise)
1516
1314
  ], ProductModuleService.prototype, "updateOptions_", null);
1517
- __decorate([
1518
- (0, utils_1.InjectManager)(),
1519
- (0, utils_1.EmitEvents)(),
1520
- __param(1, (0, utils_1.MedusaContext)()),
1521
- __metadata("design:type", Function),
1522
- __metadata("design:paramtypes", [Object, Object]),
1523
- __metadata("design:returntype", Promise)
1524
- ], ProductModuleService.prototype, "addProductOptionToProduct", null);
1525
- __decorate([
1526
- (0, utils_1.InjectTransactionManager)(),
1527
- __param(1, (0, utils_1.MedusaContext)()),
1528
- __metadata("design:type", Function),
1529
- __metadata("design:paramtypes", [Object, Object]),
1530
- __metadata("design:returntype", Promise)
1531
- ], ProductModuleService.prototype, "addProductOptionToProduct_", null);
1532
- __decorate([
1533
- (0, utils_1.InjectManager)(),
1534
- (0, utils_1.EmitEvents)(),
1535
- __param(1, (0, utils_1.MedusaContext)()),
1536
- __metadata("design:type", Function),
1537
- __metadata("design:paramtypes", [Object, Object]),
1538
- __metadata("design:returntype", Promise)
1539
- ], ProductModuleService.prototype, "removeProductOptionFromProduct", null);
1540
- __decorate([
1541
- (0, utils_1.InjectTransactionManager)(),
1542
- __param(1, (0, utils_1.MedusaContext)()),
1543
- __metadata("design:type", Function),
1544
- __metadata("design:paramtypes", [Object, Object]),
1545
- __metadata("design:returntype", Promise)
1546
- ], ProductModuleService.prototype, "removeProductOptionFromProduct_", null);
1547
1315
  __decorate([
1548
1316
  (0, utils_1.InjectManager)(),
1549
1317
  (0, utils_1.EmitEvents)()
@@ -1677,6 +1445,16 @@ __decorate([
1677
1445
  __metadata("design:paramtypes", [Array, Object]),
1678
1446
  __metadata("design:returntype", Promise)
1679
1447
  ], ProductModuleService.prototype, "updateProducts_", null);
1448
+ __decorate([
1449
+ (0, utils_1.InjectManager)(),
1450
+ (0, utils_1.EmitEvents)()
1451
+ // @ts-expect-error
1452
+ ,
1453
+ __param(2, (0, utils_1.MedusaContext)()),
1454
+ __metadata("design:type", Function),
1455
+ __metadata("design:paramtypes", [Object, Object, Object]),
1456
+ __metadata("design:returntype", Promise)
1457
+ ], ProductModuleService.prototype, "updateProductOptionValues", null);
1680
1458
  __decorate([
1681
1459
  (0, utils_1.InjectTransactionManager)(),
1682
1460
  __param(1, (0, utils_1.MedusaContext)()),
@@ -1688,7 +1466,7 @@ __decorate([
1688
1466
  __param(1, (0, utils_1.MedusaContext)()),
1689
1467
  __metadata("design:type", Function),
1690
1468
  __metadata("design:paramtypes", [typeof (_a = typeof T !== "undefined" && T) === "function" ? _a : Object, Object]),
1691
- __metadata("design:returntype", typeof (_b = typeof TOutput !== "undefined" && TOutput) === "function" ? _b : Object)
1469
+ __metadata("design:returntype", Promise)
1692
1470
  ], ProductModuleService.prototype, "normalizeCreateProductInput", null);
1693
1471
  __decorate([
1694
1472
  (0, utils_1.InjectManager)()