@medusajs/pricing 0.1.13-snapshot-20240701122250 → 0.1.13-snapshot-20240704061641

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1 @@
1
- import { MapToConfig } from "@medusajs/utils";
2
1
  export declare const joinerConfig: Omit<import("@medusajs/types").ModuleJoinerConfig, "serviceName" | "primaryKeys" | "alias" | "linkableKeys"> & Required<Pick<import("@medusajs/types").ModuleJoinerConfig, "serviceName" | "primaryKeys" | "alias" | "linkableKeys">>;
3
- export declare const entityNameToLinkableKeysMap: MapToConfig;
@@ -1,14 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.entityNameToLinkableKeysMap = exports.joinerConfig = void 0;
3
+ exports.joinerConfig = void 0;
4
4
  const utils_1 = require("@medusajs/utils");
5
5
  const _models_1 = require("./models");
6
6
  exports.joinerConfig = (0, utils_1.defineJoinerConfig)(utils_1.Modules.PRICING, {
7
- entityQueryingConfig: [_models_1.PriceSet, _models_1.PriceList, _models_1.Price],
7
+ models: [_models_1.PriceSet, _models_1.PriceList, _models_1.Price],
8
8
  linkableKeys: {
9
9
  price_set_id: _models_1.PriceSet.name,
10
10
  price_list_id: _models_1.PriceList.name,
11
11
  price_id: _models_1.Price.name,
12
12
  },
13
13
  });
14
- exports.entityNameToLinkableKeysMap = (0, utils_1.buildEntitiesNameToLinkableKeysMap)(exports.joinerConfig.linkableKeys);
@@ -117,6 +117,7 @@ class PricingRepository extends utils_1.MikroOrmBase {
117
117
  pl_rules_count: "price.pl_rules_count",
118
118
  price_list_type: "price.pl_type",
119
119
  price_list_id: "price.price_list_id",
120
+ all_rules_count: knex.raw("COALESCE(price.rules_count, 0) + COALESCE(price.pl_rules_count, 0)"),
120
121
  })
121
122
  .join(priceSubQueryKnex.as("price"), "price.price_set_id", "ps.id")
122
123
  .leftJoin("price_rule as pr", "pr.price_id", "price.id")
@@ -124,8 +125,8 @@ class PricingRepository extends utils_1.MikroOrmBase {
124
125
  .andWhere("price.currency_code", "=", currencyCode)
125
126
  .orderBy([
126
127
  { column: "price.has_price_list", order: "asc" },
128
+ { column: "all_rules_count", order: "desc" },
127
129
  { column: "amount", order: "asc" },
128
- { column: "rules_count", order: "desc" },
129
130
  ]);
130
131
  if (quantity) {
131
132
  priceSetQueryKnex.where("price.min_quantity", "<=", quantity);
@@ -1,5 +1,4 @@
1
- import { AddPricesDTO, Context, CreatePriceRuleDTO, DAL, FindConfig, InternalModuleDeclaration, ModuleJoinerConfig, ModulesSdkTypes, PriceDTO, PriceSetDTO, PricingContext, PricingFilters, PricingRepositoryService, PricingTypes, UpsertPriceSetDTO } from "@medusajs/types";
2
- import { ModulesSdkUtils } from "@medusajs/utils";
1
+ import { AddPricesDTO, Context, CreatePriceRuleDTO, DAL, FindConfig, InternalModuleDeclaration, ModuleJoinerConfig, ModulesSdkTypes, PriceSetDTO, PricingContext, PricingFilters, PricingRepositoryService, PricingTypes, UpsertPriceSetDTO } from "@medusajs/types";
3
2
  import { Price, PriceList, PriceListRule, PriceRule, PriceSet } from "../models";
4
3
  import { ServiceTypes } from "../types";
5
4
  import { CreatePriceListDTO } from "src/types/services";
@@ -12,7 +11,7 @@ type InjectedDependencies = {
12
11
  priceListService: ModulesSdkTypes.IMedusaInternalService<any>;
13
12
  priceListRuleService: ModulesSdkTypes.IMedusaInternalService<any>;
14
13
  };
15
- declare const PricingModuleService_base: new (...args: any[]) => ModulesSdkUtils.AbstractModuleService<{
14
+ declare const PricingModuleService_base: import("@medusajs/utils/dist/modules-sdk/types/medusa-service").MedusaServiceReturnType<{
16
15
  PriceSet: {
17
16
  dto: PricingTypes.PriceSetDTO;
18
17
  };
@@ -43,6 +42,7 @@ export default class PricingModuleService extends PricingModuleService_base impl
43
42
  constructor({ baseRepository, pricingRepository, priceSetService, priceRuleService, priceService, priceListService, priceListRuleService, }: InjectedDependencies, moduleDeclaration: InternalModuleDeclaration);
44
43
  __joinerConfig(): ModuleJoinerConfig;
45
44
  private setupCalculatedPriceConfig_;
45
+ retrievePriceSet(id: string, config?: FindConfig<PriceSetDTO> | undefined, sharedContext?: Context | undefined): Promise<PriceSetDTO>;
46
46
  listPriceSets(filters?: PricingTypes.FilterablePriceSetProps, config?: FindConfig<PricingTypes.PriceSetDTO>, sharedContext?: Context): Promise<PriceSetDTO[]>;
47
47
  listAndCountPriceSets(filters?: PricingTypes.FilterablePriceSetProps, config?: FindConfig<PricingTypes.PriceSetDTO>, sharedContext?: Context): Promise<[PriceSetDTO[], number]>;
48
48
  calculatePrices(pricingFilters: PricingFilters, pricingContext?: PricingContext, sharedContext?: Context): Promise<PricingTypes.CalculatedPriceSet[]>;
@@ -52,8 +52,9 @@ export default class PricingModuleService extends PricingModuleService_base impl
52
52
  upsertPriceSets(data: UpsertPriceSetDTO, sharedContext?: Context): Promise<PriceSetDTO>;
53
53
  updatePriceSets(id: string, data: PricingTypes.UpdatePriceSetDTO, sharedContext?: Context): Promise<PriceSetDTO>;
54
54
  updatePriceSets(selector: PricingTypes.FilterablePriceSetProps, data: PricingTypes.UpdatePriceSetDTO, sharedContext?: Context): Promise<PriceSetDTO[]>;
55
- private normalizeUpdateData;
56
55
  protected updatePriceSets_(data: ServiceTypes.UpdatePriceSetInput[], sharedContext?: Context): Promise<PriceSet[]>;
56
+ private normalizeUpdateData;
57
+ private normalizePrices;
57
58
  addPrices(data: AddPricesDTO, sharedContext?: Context): Promise<PricingTypes.PriceSetDTO>;
58
59
  addPrices(data: AddPricesDTO[], sharedContext?: Context): Promise<PricingTypes.PriceSetDTO[]>;
59
60
  createPriceLists(data: PricingTypes.CreatePriceListDTO[], sharedContext?: Context): Promise<PricingTypes.PriceListDTO[]>;
@@ -73,5 +74,17 @@ export default class PricingModuleService extends PricingModuleService_base impl
73
74
  protected setPriceListRules_(data: PricingTypes.SetPriceListRulesDTO[], sharedContext?: Context): Promise<PriceList[]>;
74
75
  protected removePriceListRules_(data: PricingTypes.RemovePriceListRulesDTO[], sharedContext?: Context): Promise<PriceList[]>;
75
76
  protected normalizePriceListDate(data: (ServiceTypes.UpdatePriceListDTO | ServiceTypes.CreatePriceListDTO | CreatePriceListDTO)[]): any[];
77
+ protected normalizePriceSetConfig(config: FindConfig<PricingTypes.PriceSetDTO> | undefined): {
78
+ select?: string[] | undefined;
79
+ skip?: number | null | undefined;
80
+ take?: number | null | undefined;
81
+ relations?: string[] | undefined;
82
+ order?: {
83
+ [K: string]: "ASC" | "DESC";
84
+ } | undefined;
85
+ withDeleted?: boolean | undefined;
86
+ filters?: Record<string, any> | undefined;
87
+ options: Record<string, any>;
88
+ };
76
89
  }
77
90
  export {};
@@ -24,7 +24,7 @@ const generateMethodForModels = {
24
24
  PriceRule: _models_1.PriceRule,
25
25
  Price: _models_1.Price,
26
26
  };
27
- class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generateMethodForModels, joiner_config_1.entityNameToLinkableKeysMap) {
27
+ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generateMethodForModels) {
28
28
  constructor({ baseRepository, pricingRepository, priceSetService, priceRuleService, priceService, priceListService, priceListRuleService, }, moduleDeclaration) {
29
29
  // @ts-ignore
30
30
  super(...arguments);
@@ -53,53 +53,60 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
53
53
  return pricingContext;
54
54
  }
55
55
  // @ts-expect-error
56
+ async retrievePriceSet(id, config, sharedContext) {
57
+ const priceSet = await this.priceSetService_.retrieve(id, this.normalizePriceSetConfig(config), sharedContext);
58
+ return await this.baseRepository_.serialize(priceSet);
59
+ }
60
+ // @ts-expect-error
56
61
  async listPriceSets(filters = {}, config = {}, sharedContext = {}) {
57
- const pricingContext = this.setupCalculatedPriceConfig_(filters, config);
58
- const priceSets = await super.listPriceSets(filters, config, sharedContext);
62
+ const normalizedConfig = this.normalizePriceSetConfig(config);
63
+ const pricingContext = this.setupCalculatedPriceConfig_(filters, normalizedConfig);
64
+ const priceSets = await super.listPriceSets(filters, normalizedConfig, sharedContext);
59
65
  if (!pricingContext || !priceSets.length) {
60
66
  return priceSets;
61
67
  }
62
- const priceSetIds = [];
63
- const priceSetMap = new Map();
64
- for (const priceSet of priceSets) {
65
- priceSetIds.push(priceSet.id);
66
- priceSetMap.set(priceSet.id, priceSet);
67
- }
68
- const calculatedPrices = await this.calculatePrices({ id: priceSetIds }, { context: pricingContext }, sharedContext);
68
+ const calculatedPrices = await this.calculatePrices({ id: priceSets.map((p) => p.id) }, { context: pricingContext }, sharedContext);
69
+ const calculatedPricesMap = new Map();
69
70
  for (const calculatedPrice of calculatedPrices) {
70
- const priceSet = priceSetMap.get(calculatedPrice.id);
71
- priceSet.calculated_price = calculatedPrice;
71
+ calculatedPricesMap.set(calculatedPrice.id, calculatedPrice);
72
+ }
73
+ for (const priceSet of priceSets) {
74
+ const calculatedPrice = calculatedPricesMap.get(priceSet.id);
75
+ priceSet.calculated_price = calculatedPrice ?? null;
72
76
  }
73
77
  return priceSets;
74
78
  }
75
79
  // @ts-expect-error
76
80
  async listAndCountPriceSets(filters = {}, config = {}, sharedContext = {}) {
77
- const pricingContext = this.setupCalculatedPriceConfig_(filters, config);
78
- const [priceSets, count] = await super.listAndCountPriceSets(filters, config, sharedContext);
81
+ const normalizedConfig = this.normalizePriceSetConfig(config);
82
+ const pricingContext = this.setupCalculatedPriceConfig_(filters, normalizedConfig);
83
+ const [priceSets, count] = await super.listAndCountPriceSets(filters, normalizedConfig, sharedContext);
79
84
  if (!pricingContext || !priceSets.length) {
80
85
  return [priceSets, count];
81
86
  }
82
- const priceSetIds = [];
83
- const priceSetMap = new Map();
84
- for (const priceSet of priceSets) {
85
- priceSetIds.push(priceSet.id);
86
- priceSetMap.set(priceSet.id, priceSet);
87
- }
88
- const calculatedPrices = await this.calculatePrices({ id: priceSetIds }, { context: pricingContext }, sharedContext);
87
+ const calculatedPrices = await this.calculatePrices({ id: priceSets.map((p) => p.id) }, { context: pricingContext }, sharedContext);
88
+ const calculatedPricesMap = new Map();
89
89
  for (const calculatedPrice of calculatedPrices) {
90
- const priceSet = priceSetMap.get(calculatedPrice.id);
91
- priceSet.calculated_price = calculatedPrice;
90
+ calculatedPricesMap.set(calculatedPrice.id, calculatedPrice);
91
+ }
92
+ for (const priceSet of priceSets) {
93
+ const calculatedPrice = calculatedPricesMap.get(priceSet.id);
94
+ priceSet.calculated_price = calculatedPrice ?? null;
92
95
  }
93
96
  return [priceSets, count];
94
97
  }
95
98
  async calculatePrices(pricingFilters, pricingContext = { context: {} }, sharedContext = {}) {
96
99
  const results = await this.pricingRepository_.calculatePrices(pricingFilters, pricingContext, sharedContext);
97
100
  const pricesSetPricesMap = (0, utils_1.groupBy)(results, "price_set_id");
98
- const calculatedPrices = pricingFilters.id.map((priceSetId) => {
101
+ const calculatedPrices = pricingFilters.id
102
+ .map((priceSetId) => {
99
103
  // This is where we select prices, for now we just do a first match based on the database results
100
104
  // which is prioritized by rules_count first for exact match and then deafult_priority of the rule_type
101
105
  // TODO: inject custom price selection here
102
106
  const prices = pricesSetPricesMap.get(priceSetId) || [];
107
+ if (!prices.length) {
108
+ return null;
109
+ }
103
110
  const priceListPrice = prices.find((p) => p.price_list_id);
104
111
  const defaultPrice = prices?.find((p) => !p.price_list_id);
105
112
  let calculatedPrice = defaultPrice;
@@ -132,16 +139,17 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
132
139
  max_quantity: parseInt(originalPrice?.max_quantity || "") || null,
133
140
  },
134
141
  };
135
- });
142
+ })
143
+ .filter(Boolean);
136
144
  return JSON.parse(JSON.stringify(calculatedPrices));
137
145
  }
138
146
  async createPriceSets(data, sharedContext = {}) {
139
147
  const input = Array.isArray(data) ? data : [data];
140
148
  const priceSets = await this.createPriceSets_(input, sharedContext);
141
149
  // TODO: Remove the need to refetch the data here
142
- const dbPriceSets = await this.listPriceSets({ id: priceSets.map((p) => p.id) }, {
150
+ const dbPriceSets = await this.listPriceSets({ id: priceSets.map((p) => p.id) }, this.normalizePriceSetConfig({
143
151
  relations: ["prices", "prices.price_rules"],
144
- }, sharedContext);
152
+ }), sharedContext);
145
153
  // Ensure the output to be in the same order as the input
146
154
  const results = priceSets.map((priceSet) => {
147
155
  return dbPriceSets.find((p) => p.id === priceSet.id);
@@ -180,30 +188,6 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
180
188
  const priceSets = await this.baseRepository_.serialize(updateResult);
181
189
  return (0, utils_1.isString)(idOrSelector) ? priceSets[0] : priceSets;
182
190
  }
183
- async normalizeUpdateData(data) {
184
- return data.map((priceSet) => {
185
- const prices = priceSet.prices?.map((price) => {
186
- const rules = Object.entries(price.rules ?? {}).map(([attribute, value]) => {
187
- return {
188
- attribute,
189
- value,
190
- };
191
- });
192
- const hasRulesInput = (0, utils_1.isPresent)(price.rules);
193
- delete price.rules;
194
- return {
195
- ...price,
196
- price_set_id: priceSet.id,
197
- price_rules: hasRulesInput ? rules : undefined,
198
- rules_count: hasRulesInput ? rules.length : undefined,
199
- };
200
- });
201
- return {
202
- ...priceSet,
203
- prices,
204
- };
205
- });
206
- }
207
191
  async updatePriceSets_(data, sharedContext = {}) {
208
192
  // TODO: Since money IDs are rarely passed, this will delete all previous data and insert new entries.
209
193
  // We can make the `insert` inside upsertWithReplace do an `upsert` instead to avoid this
@@ -226,6 +210,49 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
226
210
  const { entities: priceSets } = await this.priceSetService_.upsertWithReplace(priceSetsToUpsert, { relations: ["prices"] }, sharedContext);
227
211
  return priceSets;
228
212
  }
213
+ async normalizeUpdateData(data) {
214
+ return data.map((priceSet) => {
215
+ return {
216
+ ...priceSet,
217
+ prices: this.normalizePrices(priceSet.prices?.map((p) => ({ ...p, price_set_id: priceSet.id })), []),
218
+ };
219
+ });
220
+ }
221
+ normalizePrices(data, existingPrices, priceListId) {
222
+ const pricesToUpsert = new Map();
223
+ const existingPricesMap = new Map();
224
+ existingPrices?.forEach((price) => {
225
+ existingPricesMap.set(hashPrice(price), price);
226
+ });
227
+ data?.forEach((price) => {
228
+ const cleanRules = price.rules ? (0, utils_1.removeNullish)(price.rules) : {};
229
+ const ruleEntries = Object.entries(cleanRules);
230
+ const rules = ruleEntries.map(([attribute, value]) => {
231
+ return {
232
+ attribute,
233
+ value,
234
+ };
235
+ });
236
+ const hasRulesInput = (0, utils_1.isPresent)(price.rules);
237
+ const entry = {
238
+ ...price,
239
+ price_list_id: priceListId,
240
+ price_rules: hasRulesInput ? rules : undefined,
241
+ rules_count: hasRulesInput ? ruleEntries.length : undefined,
242
+ };
243
+ delete entry.rules;
244
+ const entryHash = hashPrice(entry);
245
+ // We want to keep the existing rules as they might already have ids, but any other data should come from the updated input
246
+ const existing = existingPricesMap.get(entryHash);
247
+ pricesToUpsert.set(entryHash, {
248
+ ...entry,
249
+ id: existing?.id ?? entry.id,
250
+ price_rules: existing?.price_rules ?? entry.price_rules,
251
+ });
252
+ return entry;
253
+ });
254
+ return Array.from(pricesToUpsert.values());
255
+ }
229
256
  async addPrices(data, sharedContext = {}) {
230
257
  const input = Array.isArray(data) ? data : [data];
231
258
  await this.addPrices_(input, sharedContext);
@@ -269,30 +296,8 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
269
296
  const toCreate = input.map((inputData) => {
270
297
  const entry = {
271
298
  ...inputData,
299
+ prices: this.normalizePrices(inputData.prices, []),
272
300
  };
273
- if (!inputData.prices) {
274
- return entry;
275
- }
276
- const pricesData = inputData.prices.map((price) => {
277
- let { rules: priceRules = {}, ...rest } = price;
278
- const cleanRules = priceRules ? (0, utils_1.removeNullish)(priceRules) : {};
279
- const rules = Object.entries(cleanRules);
280
- const numberOfRules = rules.length;
281
- const rulesDataMap = new Map();
282
- rules.map(([attribute, value]) => {
283
- const rule = {
284
- attribute,
285
- value,
286
- };
287
- rulesDataMap.set(JSON.stringify(rule), rule);
288
- });
289
- return {
290
- ...rest,
291
- rules_count: numberOfRules,
292
- price_rules: Array.from(rulesDataMap.values()),
293
- };
294
- });
295
- entry.prices = pricesData;
296
301
  return entry;
297
302
  });
298
303
  // Bulk create price sets
@@ -332,58 +337,50 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
332
337
  return createdPriceSets;
333
338
  }
334
339
  async addPrices_(input, sharedContext = {}) {
335
- const priceSets = await this.listPriceSets({ id: input.map((d) => d.priceSetId) }, {}, sharedContext);
340
+ const priceSets = await this.listPriceSets({ id: input.map((d) => d.priceSetId) }, { take: null, relations: ["prices", "prices.price_rules"] }, sharedContext);
341
+ const existingPrices = priceSets
342
+ .map((p) => p.prices)
343
+ .flat();
344
+ const pricesToUpsert = input
345
+ .map((addPrice) => this.normalizePrices(addPrice.prices?.map((p) => ({
346
+ ...p,
347
+ price_set_id: addPrice.priceSetId,
348
+ })), existingPrices))
349
+ .filter(Boolean)
350
+ .flat();
336
351
  const priceSetMap = new Map(priceSets.map((p) => [p.id, p]));
337
- input.forEach(({ priceSetId }) => {
338
- const priceSet = priceSetMap.get(priceSetId);
352
+ pricesToUpsert.forEach((price) => {
353
+ const priceSet = priceSetMap.get(price.price_set_id);
339
354
  if (!priceSet) {
340
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Price set with id: ${priceSetId} not found`);
355
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Price set with id: ${price.price_set_id} not found`);
341
356
  }
342
357
  });
343
- const pricesToCreate = input.flatMap(({ priceSetId, prices }) => prices.map((price) => {
344
- const numberOfRules = Object.entries(price?.rules ?? {}).length;
345
- const priceRules = Object.entries(price.rules ?? {}).map(([attribute, value]) => ({
346
- price_set_id: priceSetId,
347
- attribute: attribute,
348
- value,
349
- }));
350
- return {
351
- ...price,
352
- price_set_id: priceSetId,
353
- rules_count: numberOfRules,
354
- price_rules: priceRules,
355
- };
356
- }));
357
- const prices = await this.priceService_.create(pricesToCreate, sharedContext);
358
- /**
359
- * Preparing data for emitting events
360
- */
361
- const eventsData = prices.reduce((eventsData, price) => {
362
- eventsData.prices.push({
363
- id: price.id,
364
- });
365
- price.price_rules.map((priceRule) => {
366
- eventsData.priceRules.push({
367
- id: priceRule.id,
368
- });
369
- });
370
- return eventsData;
371
- }, {
372
- priceRules: [],
373
- prices: [],
374
- });
375
- /**
376
- * Emitting events for all created entities
377
- */
358
+ const { entities, performedActions } = await this.priceService_.upsertWithReplace(pricesToUpsert, { relations: ["price_rules"] }, sharedContext);
378
359
  _utils_1.eventBuilders.createdPrice({
379
- data: eventsData.prices,
360
+ data: performedActions.created[_models_1.Price.name] ?? [],
361
+ sharedContext,
362
+ });
363
+ _utils_1.eventBuilders.updatedPrice({
364
+ data: performedActions.updated[_models_1.Price.name] ?? [],
365
+ sharedContext,
366
+ });
367
+ _utils_1.eventBuilders.deletedPrice({
368
+ data: performedActions.deleted[_models_1.Price.name] ?? [],
380
369
  sharedContext,
381
370
  });
382
371
  _utils_1.eventBuilders.createdPriceRule({
383
- data: eventsData.priceRules,
372
+ data: performedActions.created[_models_1.PriceRule.name] ?? [],
384
373
  sharedContext,
385
374
  });
386
- return prices;
375
+ _utils_1.eventBuilders.updatedPriceRule({
376
+ data: performedActions.updated[_models_1.PriceRule.name] ?? [],
377
+ sharedContext,
378
+ });
379
+ _utils_1.eventBuilders.deletedPriceRule({
380
+ data: performedActions.deleted[_models_1.PriceRule.name] ?? [],
381
+ sharedContext,
382
+ });
383
+ return entities;
387
384
  }
388
385
  async createPriceLists_(data, sharedContext = {}) {
389
386
  const normalized = this.normalizePriceListDate(data);
@@ -393,26 +390,7 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
393
390
  rules: undefined,
394
391
  };
395
392
  if (priceListData.prices) {
396
- const pricesData = priceListData.prices.map((price) => {
397
- let { rules: priceRules = {}, ...rest } = price;
398
- const cleanRules = priceRules ? (0, utils_1.removeNullish)(priceRules) : {};
399
- const rules = Object.entries(cleanRules);
400
- const numberOfRules = rules.length;
401
- const rulesDataMap = new Map();
402
- rules.map(([attribute, value]) => {
403
- const rule = {
404
- attribute,
405
- value,
406
- };
407
- rulesDataMap.set(JSON.stringify(rule), rule);
408
- });
409
- return {
410
- ...rest,
411
- rules_count: numberOfRules,
412
- price_rules: Array.from(rulesDataMap.values()),
413
- };
414
- });
415
- entry.prices = pricesData;
393
+ entry.prices = this.normalizePrices(priceListData.prices, []);
416
394
  }
417
395
  if (priceListData.rules) {
418
396
  const cleanRules = priceListData.rules
@@ -521,111 +499,69 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
521
499
  return entities;
522
500
  }
523
501
  async updatePriceListPrices_(data, sharedContext = {}) {
524
- const priceListIds = [];
525
- const priceIds = [];
526
- for (const priceListData of data) {
527
- priceListIds.push(priceListData.price_list_id);
528
- for (const price of priceListData.prices) {
529
- priceIds.push(price.id);
530
- }
531
- }
532
- const prices = await this.listPrices({ id: priceIds }, { take: null, relations: ["price_rules"] }, sharedContext);
533
- const priceMap = new Map(prices.map((price) => [price.id, price]));
534
- const priceLists = await this.listPriceLists({ id: priceListIds }, { take: null }, sharedContext);
502
+ const priceLists = await this.listPriceLists({ id: data.map((p) => p.price_list_id) }, { take: null, relations: ["prices", "prices.price_rules"] }, sharedContext);
503
+ const existingPrices = priceLists
504
+ .map((p) => p.prices ?? [])
505
+ .flat();
506
+ const pricesToUpsert = data
507
+ .map((addPrice) => this.normalizePrices(addPrice.prices, existingPrices, addPrice.price_list_id))
508
+ .filter(Boolean)
509
+ .flat();
535
510
  const priceListMap = new Map(priceLists.map((p) => [p.id, p]));
536
- const pricesToUpdate = [];
537
- const priceRuleIdsToDelete = [];
538
- const priceRulesToCreate = [];
539
511
  for (const { price_list_id: priceListId, prices } of data) {
540
512
  const priceList = priceListMap.get(priceListId);
541
513
  if (!priceList) {
542
514
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Price list with id: ${priceListId} not found`);
543
515
  }
544
- for (const priceData of prices) {
545
- const { rules = {}, price_set_id, ...rest } = priceData;
546
- const price = priceMap.get(rest.id);
547
- const priceRules = price.price_rules;
548
- priceRulesToCreate.push(...Object.entries(rules).map(([ruleAttribute, ruleValue]) => ({
549
- price_set_id,
550
- attribute: ruleAttribute,
551
- value: ruleValue,
552
- price_id: price.id,
553
- })));
554
- pricesToUpdate.push({
555
- ...rest,
556
- rules_count: Object.keys(rules).length,
557
- });
558
- priceRuleIdsToDelete.push(...priceRules.map((pr) => pr.id));
559
- }
560
516
  }
561
- const [_deletedPriceRule, _createdPriceRule, updatedPrices] = await (0, utils_1.promiseAll)([
562
- this.priceRuleService_.delete(priceRuleIdsToDelete),
563
- this.priceRuleService_.create(priceRulesToCreate),
564
- this.priceService_.update(pricesToUpdate),
565
- ]);
566
- return updatedPrices;
517
+ const { entities } = await this.priceService_.upsertWithReplace(pricesToUpsert, { relations: ["price_rules"] }, sharedContext);
518
+ return entities;
567
519
  }
568
520
  async removePrices_(ids, sharedContext = {}) {
569
521
  await this.priceService_.delete(ids, sharedContext);
570
522
  }
571
523
  async addPriceListPrices_(data, sharedContext = {}) {
572
- const priceListIds = [];
573
- for (const priceListData of data) {
574
- priceListIds.push(priceListData.price_list_id);
575
- }
576
- const priceLists = await this.listPriceLists({ id: priceListIds }, {}, sharedContext);
524
+ const priceLists = await this.listPriceLists({ id: data.map((p) => p.price_list_id) }, { take: null, relations: ["prices", "prices.price_rules"] }, sharedContext);
525
+ const existingPrices = priceLists
526
+ .map((p) => p.prices ?? [])
527
+ .flat();
528
+ const pricesToUpsert = data
529
+ .map((addPrice) => this.normalizePrices(addPrice.prices, existingPrices, addPrice.price_list_id))
530
+ .filter(Boolean)
531
+ .flat();
577
532
  const priceListMap = new Map(priceLists.map((p) => [p.id, p]));
578
- const pricesToCreate = [];
579
- for (const { price_list_id: priceListId, prices } of data) {
580
- const priceList = priceListMap.get(priceListId);
533
+ pricesToUpsert.forEach((price) => {
534
+ const priceList = priceListMap.get(price.price_list_id);
581
535
  if (!priceList) {
582
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Price list with id: ${priceListId} not found`);
536
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Price list with id: ${price.price_list_id} not found`);
583
537
  }
584
- const priceListPricesToCreate = prices.map((priceData) => {
585
- const priceRules = priceData.rules || {};
586
- const noOfRules = Object.keys(priceRules).length;
587
- const priceRulesToCreate = Object.entries(priceRules).map(([ruleAttribute, ruleValue]) => {
588
- return {
589
- price_list_id: priceData.price_set_id,
590
- attribute: ruleAttribute,
591
- value: ruleValue,
592
- };
593
- });
594
- return {
595
- ...priceData,
596
- price_set_id: priceData.price_set_id,
597
- title: "test",
598
- price_list_id: priceList.id,
599
- rules_count: noOfRules,
600
- price_rules: priceRulesToCreate,
601
- };
602
- });
603
- pricesToCreate.push(...priceListPricesToCreate);
604
- }
605
- const createdPrices = await this.priceService_.create(pricesToCreate, sharedContext);
606
- const eventsData = createdPrices.reduce((eventsData, price) => {
607
- eventsData.prices.push({
608
- id: price.id,
609
- });
610
- price.price_rules.map((priceRule) => {
611
- eventsData.priceRules.push({
612
- id: priceRule.id,
613
- });
614
- });
615
- return eventsData;
616
- }, {
617
- priceRules: [],
618
- prices: [],
619
538
  });
539
+ const { entities, performedActions } = await this.priceService_.upsertWithReplace(pricesToUpsert, { relations: ["price_rules"] }, sharedContext);
620
540
  _utils_1.eventBuilders.createdPrice({
621
- data: eventsData.prices,
541
+ data: performedActions.created[_models_1.Price.name] ?? [],
542
+ sharedContext,
543
+ });
544
+ _utils_1.eventBuilders.updatedPrice({
545
+ data: performedActions.updated[_models_1.Price.name] ?? [],
546
+ sharedContext,
547
+ });
548
+ _utils_1.eventBuilders.deletedPrice({
549
+ data: performedActions.deleted[_models_1.Price.name] ?? [],
622
550
  sharedContext,
623
551
  });
624
552
  _utils_1.eventBuilders.createdPriceRule({
625
- data: eventsData.priceRules,
553
+ data: performedActions.created[_models_1.PriceRule.name] ?? [],
626
554
  sharedContext,
627
555
  });
628
- return createdPrices;
556
+ _utils_1.eventBuilders.updatedPriceRule({
557
+ data: performedActions.updated[_models_1.PriceRule.name] ?? [],
558
+ sharedContext,
559
+ });
560
+ _utils_1.eventBuilders.deletedPriceRule({
561
+ data: performedActions.deleted[_models_1.PriceRule.name] ?? [],
562
+ sharedContext,
563
+ });
564
+ return entities;
629
565
  }
630
566
  async setPriceListRules_(data, sharedContext = {}) {
631
567
  // TODO: re think this method
@@ -719,8 +655,24 @@ class PricingModuleService extends utils_1.ModulesSdkUtils.MedusaService(generat
719
655
  return priceListData;
720
656
  });
721
657
  }
658
+ normalizePriceSetConfig(config) {
659
+ return {
660
+ options: {
661
+ populateWhere: { prices: { price_list_id: null } },
662
+ },
663
+ ...config,
664
+ };
665
+ }
722
666
  }
723
667
  exports.default = PricingModuleService;
668
+ __decorate([
669
+ (0, utils_1.InjectManager)("baseRepository_")
670
+ // @ts-expect-error
671
+ ,
672
+ __metadata("design:type", Function),
673
+ __metadata("design:paramtypes", [String, Object, Object]),
674
+ __metadata("design:returntype", Promise)
675
+ ], PricingModuleService.prototype, "retrievePriceSet", null);
724
676
  __decorate([
725
677
  (0, utils_1.InjectManager)("baseRepository_")
726
678
  // @ts-expect-error
@@ -896,3 +848,19 @@ __decorate([
896
848
  __metadata("design:paramtypes", [Array, Object]),
897
849
  __metadata("design:returntype", Promise)
898
850
  ], PricingModuleService.prototype, "removePriceListRules_", null);
851
+ const hashPrice = (price) => {
852
+ const data = Object.entries({
853
+ currency_code: price.currency_code,
854
+ price_set_id: "price_set_id" in price ? price.price_set_id ?? null : null,
855
+ price_list_id: "price_list_id" in price ? price.price_list_id ?? null : null,
856
+ min_quantity: price.min_quantity ? price.min_quantity.toString() : null,
857
+ max_quantity: price.max_quantity ? price.max_quantity.toString() : null,
858
+ ...("price_rules" in price
859
+ ? price.price_rules?.reduce((agg, pr) => {
860
+ agg[pr.attribute] = pr.value;
861
+ return agg;
862
+ }, {})
863
+ : {}),
864
+ }).sort(([a], [b]) => a.localeCompare(b));
865
+ return (0, utils_1.simpleHash)(JSON.stringify(data));
866
+ };
@@ -1,2 +1,3 @@
1
1
  export * from "./price-list";
2
2
  export * from "./price-set";
3
+ export * from "./price";
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./price-list"), exports);
18
18
  __exportStar(require("./price-set"), exports);
19
+ __exportStar(require("./price"), exports);
@@ -0,0 +1,6 @@
1
+ import { PricingTypes } from "@medusajs/types";
2
+ export interface UpsertPriceDTO extends Omit<PricingTypes.CreatePriceDTO, "rules"> {
3
+ id?: string;
4
+ price_list_id?: string;
5
+ price_rules: PricingTypes.CreatePriceRuleDTO[];
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -35,4 +35,28 @@ export declare const eventBuilders: {
35
35
  }[];
36
36
  sharedContext: import("@medusajs/types").Context;
37
37
  }) => void;
38
+ updatedPrice: ({ data, sharedContext, }: {
39
+ data: {
40
+ id: string;
41
+ }[];
42
+ sharedContext: import("@medusajs/types").Context;
43
+ }) => void;
44
+ updatedPriceRule: ({ data, sharedContext, }: {
45
+ data: {
46
+ id: string;
47
+ }[];
48
+ sharedContext: import("@medusajs/types").Context;
49
+ }) => void;
50
+ deletedPrice: ({ data, sharedContext, }: {
51
+ data: {
52
+ id: string;
53
+ }[];
54
+ sharedContext: import("@medusajs/types").Context;
55
+ }) => void;
56
+ deletedPriceRule: ({ data, sharedContext, }: {
57
+ data: {
58
+ id: string;
59
+ }[];
60
+ sharedContext: import("@medusajs/types").Context;
61
+ }) => void;
38
62
  };
@@ -39,4 +39,28 @@ exports.eventBuilders = {
39
39
  object: "price_list_rule",
40
40
  eventsEnum: utils_1.PricingEvents,
41
41
  }),
42
+ updatedPrice: (0, utils_1.eventBuilderFactory)({
43
+ source: utils_1.Modules.PRICING,
44
+ action: utils_1.CommonEvents.UPDATED,
45
+ object: "price",
46
+ eventsEnum: utils_1.PricingEvents,
47
+ }),
48
+ updatedPriceRule: (0, utils_1.eventBuilderFactory)({
49
+ source: utils_1.Modules.PRICING,
50
+ action: utils_1.CommonEvents.UPDATED,
51
+ object: "price_rule",
52
+ eventsEnum: utils_1.PricingEvents,
53
+ }),
54
+ deletedPrice: (0, utils_1.eventBuilderFactory)({
55
+ source: utils_1.Modules.PRICING,
56
+ action: utils_1.CommonEvents.DELETED,
57
+ object: "price",
58
+ eventsEnum: utils_1.PricingEvents,
59
+ }),
60
+ deletedPriceRule: (0, utils_1.eventBuilderFactory)({
61
+ source: utils_1.Modules.PRICING,
62
+ action: utils_1.CommonEvents.DELETED,
63
+ object: "price_rule",
64
+ eventsEnum: utils_1.PricingEvents,
65
+ }),
42
66
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medusajs/pricing",
3
- "version": "0.1.13-snapshot-20240701122250",
3
+ "version": "0.1.13-snapshot-20240704061641",
4
4
  "description": "Medusa Pricing module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "@mikro-orm/cli": "5.9.7",
38
38
  "cross-env": "^5.2.1",
39
39
  "jest": "^29.7.0",
40
- "medusa-test-utils": "1.1.45-snapshot-20240701122250",
40
+ "medusa-test-utils": "1.1.45-snapshot-20240704061641",
41
41
  "rimraf": "^3.0.2",
42
42
  "ts-jest": "^29.1.1",
43
43
  "ts-node": "^10.9.1",
@@ -45,9 +45,9 @@
45
45
  "typescript": "^5.1.6"
46
46
  },
47
47
  "dependencies": {
48
- "@medusajs/modules-sdk": "1.13.0-snapshot-20240701122250",
49
- "@medusajs/types": "1.12.0-snapshot-20240701122250",
50
- "@medusajs/utils": "1.12.0-snapshot-20240701122250",
48
+ "@medusajs/modules-sdk": "1.13.0-snapshot-20240704061641",
49
+ "@medusajs/types": "1.12.0-snapshot-20240704061641",
50
+ "@medusajs/utils": "1.12.0-snapshot-20240704061641",
51
51
  "@mikro-orm/core": "5.9.7",
52
52
  "@mikro-orm/migrations": "5.9.7",
53
53
  "@mikro-orm/postgresql": "5.9.7",