@unchainedshop/core-enrollments 3.0.0-alpha7 → 3.0.0-rc2

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 (48) hide show
  1. package/lib/db/EnrollmentsCollection.d.ts +50 -1
  2. package/lib/db/EnrollmentsCollection.d.ts.map +1 -1
  3. package/lib/db/EnrollmentsCollection.js +7 -0
  4. package/lib/db/EnrollmentsCollection.js.map +1 -1
  5. package/lib/director/EnrollmentAdapter.js.map +1 -1
  6. package/lib/enrollments-index.d.ts +1 -4
  7. package/lib/enrollments-index.d.ts.map +1 -1
  8. package/lib/enrollments-index.js +1 -4
  9. package/lib/enrollments-index.js.map +1 -1
  10. package/lib/enrollments-settings.d.ts +3 -3
  11. package/lib/enrollments-settings.d.ts.map +1 -1
  12. package/lib/enrollments-settings.js.map +1 -1
  13. package/lib/module/configureEnrollmentsModule.d.ts +41 -54
  14. package/lib/module/configureEnrollmentsModule.d.ts.map +1 -1
  15. package/lib/module/configureEnrollmentsModule.js +22 -143
  16. package/lib/module/configureEnrollmentsModule.js.map +1 -1
  17. package/lib/module/configureEnrollmentsModule_BACKUP_54085.d.ts +59 -0
  18. package/lib/module/configureEnrollmentsModule_BACKUP_54085.d.ts.map +1 -0
  19. package/lib/module/configureEnrollmentsModule_BACKUP_54085.js +203 -0
  20. package/lib/module/configureEnrollmentsModule_BACKUP_54085.js.map +1 -0
  21. package/lib/module/configureEnrollmentsModule_BASE_54085.d.ts +70 -0
  22. package/lib/module/configureEnrollmentsModule_BASE_54085.d.ts.map +1 -0
  23. package/lib/module/configureEnrollmentsModule_BASE_54085.js +324 -0
  24. package/lib/module/configureEnrollmentsModule_BASE_54085.js.map +1 -0
  25. package/lib/module/configureEnrollmentsModule_LOCAL_54085.d.ts +58 -0
  26. package/lib/module/configureEnrollmentsModule_LOCAL_54085.d.ts.map +1 -0
  27. package/lib/module/configureEnrollmentsModule_LOCAL_54085.js +196 -0
  28. package/lib/module/configureEnrollmentsModule_LOCAL_54085.js.map +1 -0
  29. package/lib/module/configureEnrollmentsModule_REMOTE_54085.d.ts +71 -0
  30. package/lib/module/configureEnrollmentsModule_REMOTE_54085.d.ts.map +1 -0
  31. package/lib/module/configureEnrollmentsModule_REMOTE_54085.js +331 -0
  32. package/lib/module/configureEnrollmentsModule_REMOTE_54085.js.map +1 -0
  33. package/lib/types.d.ts +8 -9
  34. package/lib/types.d.ts.map +1 -1
  35. package/lib/types.js.map +1 -1
  36. package/package.json +10 -13
  37. package/src/db/EnrollmentsCollection.ts +49 -1
  38. package/src/enrollments-index.ts +1 -6
  39. package/src/enrollments-settings.ts +2 -3
  40. package/src/module/buildFindSelector.test.ts +1 -1
  41. package/src/module/configureEnrollmentsModule.ts +77 -339
  42. package/tsconfig.json +5 -7
  43. package/src/db/EnrollmentStatus.ts +0 -6
  44. package/src/director/EnrollmentAdapter.ts +0 -82
  45. package/src/director/EnrollmentDirector.ts +0 -60
  46. package/src/director/EnrollmentError.ts +0 -6
  47. package/src/module/periodForReferenceDate.test.ts +0 -23
  48. package/src/types.ts +0 -113
@@ -1,12 +1,10 @@
1
1
  import { SortDirection, SortOption } from '@unchainedshop/utils';
2
- import { UnchainedCore } from '@unchainedshop/core';
3
2
  import {
4
3
  Enrollment,
5
- EnrollmentData,
6
4
  EnrollmentPeriod,
7
5
  EnrollmentPlan,
8
- EnrollmentQuery,
9
- } from '../types.js';
6
+ EnrollmentStatus,
7
+ } from '../db/EnrollmentsCollection.js';
10
8
  import { emit, registerEvents } from '@unchainedshop/events';
11
9
  import {
12
10
  generateDbFilterById,
@@ -18,102 +16,13 @@ import {
18
16
  ModuleInput,
19
17
  } from '@unchainedshop/mongodb';
20
18
  import { EnrollmentsCollection } from '../db/EnrollmentsCollection.js';
21
- import { EnrollmentStatus } from '../db/EnrollmentStatus.js';
22
- import { EnrollmentDirector } from '../enrollments-index.js';
23
19
  import { enrollmentsSettings, EnrollmentsSettingsOptions } from '../enrollments-settings.js';
24
- import { resolveBestCurrency } from '@unchainedshop/utils';
25
- import { Order } from '@unchainedshop/core-orders';
26
- import { OrderPosition } from '@unchainedshop/core-orders';
27
- import { Product } from '@unchainedshop/core-products';
28
-
29
- // Queries
30
-
31
- export interface EnrollmentQueries {
32
- findEnrollment: (
33
- params: { enrollmentId?: string; orderId?: string },
34
- options?: mongodb.FindOptions,
35
- ) => Promise<Enrollment>;
36
- findEnrollments: (
37
- params: EnrollmentQuery & {
38
- limit?: number;
39
- offset?: number;
40
- sort?: Array<SortOption>;
41
- },
42
- ) => Promise<Array<Enrollment>>;
43
- openEnrollmentWithProduct(params: { productId: string }): Promise<Enrollment | null>;
44
- count: (params: EnrollmentQuery) => Promise<number>;
45
- }
46
-
47
- // Transformations
48
-
49
- export interface EnrollmentTransformations {
50
- normalizedStatus: (enrollment: Enrollment) => string;
51
- isExpired: (enrollment: Enrollment, params: { referenceDate?: Date }) => boolean;
52
- }
53
-
54
- // Processing
55
-
56
- export type EnrollmentContextParams = (
57
- enrollment: Enrollment,
58
- unchainedAPI: UnchainedCore,
59
- ) => Promise<Enrollment>;
60
-
61
- export interface EnrollmentProcessing {
62
- terminateEnrollment: EnrollmentContextParams;
63
- activateEnrollment: EnrollmentContextParams;
64
- }
65
-
66
- export interface EnrollmentMutations {
67
- addEnrollmentPeriod: (enrollmentId: string, period: EnrollmentPeriod) => Promise<Enrollment>;
68
-
69
- create: (doc: EnrollmentData, unchainedAPI: UnchainedCore) => Promise<Enrollment>;
70
-
71
- createFromCheckout: (
72
- order: Order,
73
- params: {
74
- items: Array<{
75
- orderPosition: OrderPosition;
76
- product: Product;
77
- }>;
78
- context: {
79
- paymentContext?: any;
80
- deliveryContext?: any;
81
- };
82
- },
83
- unchainedAPI: UnchainedCore,
84
- ) => Promise<void>;
85
-
86
- delete: (enrollmentId: string) => Promise<number>;
87
-
88
- removeEnrollmentPeriodByOrderId: (enrollmentId: string, orderId: string) => Promise<Enrollment>;
89
-
90
- updateBillingAddress: (enrollmentId: string, billingAddress: Address) => Promise<Enrollment>;
91
-
92
- updateContact: (enrollmentId: string, contact: Contact) => Promise<Enrollment>;
93
-
94
- updateContext: (enrollmentId: string, context: any) => Promise<Enrollment | null>;
95
-
96
- updateDelivery: (enrollmentId: string, delivery: Enrollment['delivery']) => Promise<Enrollment>;
97
-
98
- updatePayment: (enrollmentId: string, payment: Enrollment['payment']) => Promise<Enrollment>;
99
-
100
- updatePlan: (
101
- enrollmentId: string,
102
- plan: EnrollmentPlan,
103
- unchainedAPI: UnchainedCore,
104
- ) => Promise<Enrollment>;
105
20
 
106
- updateStatus: (
107
- enrollmentId: string,
108
- params: { status: EnrollmentStatus; info?: string },
109
- unchainedAPI: UnchainedCore,
110
- ) => Promise<Enrollment>;
111
- }
112
-
113
- export type EnrollmentsModule = EnrollmentQueries &
114
- EnrollmentTransformations &
115
- EnrollmentProcessing &
116
- EnrollmentMutations;
21
+ export type EnrollmentQuery = {
22
+ status?: Array<EnrollmentStatus>;
23
+ userId?: string;
24
+ queryString?: string;
25
+ };
117
26
 
118
27
  const ENROLLMENT_EVENTS: string[] = [
119
28
  'ENROLLMENT_ADD_PERIOD',
@@ -142,13 +51,19 @@ export const buildFindSelector = ({ queryString, status, userId }: EnrollmentQue
142
51
  export const configureEnrollmentsModule = async ({
143
52
  db,
144
53
  options: enrollmentOptions = {},
145
- }: ModuleInput<EnrollmentsSettingsOptions>): Promise<EnrollmentsModule> => {
54
+ }: ModuleInput<EnrollmentsSettingsOptions>) => {
146
55
  registerEvents(ENROLLMENT_EVENTS);
147
56
 
148
57
  enrollmentsSettings.configureSettings(enrollmentOptions);
149
58
 
150
59
  const Enrollments = await EnrollmentsCollection(db);
151
60
 
61
+ const isExpired = (enrollment: Enrollment, { referenceDate }: { referenceDate?: Date }) => {
62
+ const relevantDate = referenceDate ? new Date(referenceDate) : new Date();
63
+ const expiryDate = new Date(enrollment.expires);
64
+ return relevantDate.getTime() > expiryDate.getTime();
65
+ };
66
+
152
67
  const findNewEnrollmentNumber = async (enrollment: Enrollment, index = 0): Promise<string> => {
153
68
  const newHashID = enrollmentsSettings.enrollmentNumberHashFn(enrollment, index);
154
69
  if ((await Enrollments.countDocuments({ enrollmentNumber: newHashID }, { limit: 1 })) === 0) {
@@ -157,32 +72,10 @@ export const configureEnrollmentsModule = async ({
157
72
  return findNewEnrollmentNumber(enrollment, index + 1);
158
73
  };
159
74
 
160
- const findNextStatus = async (
161
- enrollment: Enrollment,
162
- unchainedAPI: UnchainedCore,
163
- ): Promise<EnrollmentStatus> => {
164
- let status = enrollment.status as EnrollmentStatus;
165
- const director = await EnrollmentDirector.actions({ enrollment }, unchainedAPI);
166
-
167
- if (status === EnrollmentStatus.INITIAL || status === EnrollmentStatus.PAUSED) {
168
- if (await director.isValidForActivation()) {
169
- status = EnrollmentStatus.ACTIVE;
170
- }
171
- } else if (status === EnrollmentStatus.ACTIVE) {
172
- if (await director.isOverdue()) {
173
- status = EnrollmentStatus.PAUSED;
174
- }
175
- } else if (unchainedAPI.modules.enrollments.isExpired(enrollment, {})) {
176
- status = EnrollmentStatus.TERMINATED;
177
- }
178
-
179
- return status;
180
- };
181
-
182
- const updateStatus: EnrollmentsModule['updateStatus'] = async (
183
- enrollmentId,
184
- { status, info = '' },
185
- ) => {
75
+ const updateStatus = async (
76
+ enrollmentId: string,
77
+ { status, info = '' }: { status: EnrollmentStatus; info?: string },
78
+ ): Promise<Enrollment> => {
186
79
  const selector = generateDbFilterById(enrollmentId);
187
80
  const enrollment = await Enrollments.findOne(selector, {});
188
81
 
@@ -223,84 +116,22 @@ export const configureEnrollmentsModule = async ({
223
116
  return updatedEnrollment;
224
117
  };
225
118
 
226
- const reactivateEnrollment = async (enrollment: Enrollment) => {
227
- return enrollment;
228
- };
229
-
230
- const processEnrollment = async (enrollment: Enrollment, unchainedAPI: UnchainedCore) => {
231
- let status = await findNextStatus(enrollment, unchainedAPI);
232
-
233
- if (status === EnrollmentStatus.ACTIVE) {
234
- const nextEnrollment = await reactivateEnrollment(enrollment);
235
- status = await findNextStatus(nextEnrollment, unchainedAPI);
236
- }
237
-
238
- return updateStatus(enrollment._id, { status, info: 'enrollment processed' }, unchainedAPI);
239
- };
240
-
241
- const initializeEnrollment = async (
242
- enrollment: Enrollment,
243
- params: { orderIdForFirstPeriod?: string; reason: string },
244
- unchainedAPI: UnchainedCore,
245
- ) => {
246
- const { modules } = unchainedAPI;
247
-
248
- const director = await EnrollmentDirector.actions({ enrollment }, unchainedAPI);
249
- const period = await director.nextPeriod();
250
-
251
- if (period && (params.orderIdForFirstPeriod || period.isTrial)) {
252
- const intializedEnrollment = await modules.enrollments.addEnrollmentPeriod(enrollment._id, {
253
- ...period,
254
- orderId: params.orderIdForFirstPeriod,
255
- });
256
-
257
- return processEnrollment(intializedEnrollment, unchainedAPI);
258
- }
259
-
260
- return processEnrollment(enrollment, unchainedAPI);
261
- };
262
-
263
- const sendStatusToCustomer = async (
264
- enrollment: Enrollment,
265
- params: { locale?: Intl.Locale; reason?: string },
266
- unchainedAPI: UnchainedCore,
267
- ) => {
268
- const { modules } = unchainedAPI;
269
-
270
- let { locale } = params;
271
- if (!locale) {
272
- const user = await modules.users.findUserById(enrollment.userId);
273
- locale = modules.users.userLocale(user);
274
- }
275
-
276
- await modules.worker.addWork({
277
- type: 'MESSAGE',
278
- retries: 0,
279
- input: {
280
- reason: params.reason || 'status_change',
281
- locale,
282
- template: 'ENROLLMENT_STATUS',
283
- enrollmentId: enrollment._id,
284
- },
285
- });
286
-
287
- return enrollment;
288
- };
289
-
290
- const updateEnrollmentField = (fieldKey: string) => async (enrollmentId: string, fieldValue: any) => {
291
- const enrollment = await Enrollments.findOneAndUpdate(
292
- generateDbFilterById(enrollmentId),
293
- {
294
- $set: {
295
- updated: new Date(),
296
- [fieldKey]: fieldValue,
119
+ const updateEnrollmentField =
120
+ <T>(fieldKey: string) =>
121
+ async (enrollmentId: string, fieldValue: T) => {
122
+ const enrollment = await Enrollments.findOneAndUpdate(
123
+ generateDbFilterById(enrollmentId),
124
+ {
125
+ $set: {
126
+ updated: new Date(),
127
+ [fieldKey]: fieldValue,
128
+ },
297
129
  },
298
- },
299
- { returnDocument: 'after' },
300
- );
301
- await emit('ENROLLMENT_UPDATE', { enrollment, field: fieldKey });
302
- return enrollment;
303
- };
130
+ { returnDocument: 'after' },
131
+ );
132
+ await emit('ENROLLMENT_UPDATE', { enrollment, field: fieldKey });
133
+ return enrollment;
134
+ };
304
135
 
305
136
  return {
306
137
  // Queries
@@ -308,13 +139,16 @@ export const configureEnrollmentsModule = async ({
308
139
  const enrollmentCount = await Enrollments.countDocuments(buildFindSelector(query));
309
140
  return enrollmentCount;
310
141
  },
311
- openEnrollmentWithProduct: async ({ productId }) => {
142
+ openEnrollmentWithProduct: async ({ productId }: { productId: string }): Promise<Enrollment> => {
312
143
  const selector: mongodb.Filter<Enrollment> = { productId };
313
144
  selector.status = { $in: [EnrollmentStatus.ACTIVE, EnrollmentStatus.PAUSED] };
314
145
  return Enrollments.findOne(selector);
315
146
  },
316
147
 
317
- findEnrollment: async ({ enrollmentId, orderId }, options) => {
148
+ findEnrollment: async (
149
+ { enrollmentId, orderId }: { enrollmentId?: string; orderId?: string },
150
+ options?: mongodb.FindOptions,
151
+ ): Promise<Enrollment> => {
318
152
  const selector = enrollmentId
319
153
  ? generateDbFilterById(enrollmentId)
320
154
  : { 'periods.orderId': orderId };
@@ -327,7 +161,11 @@ export const configureEnrollmentsModule = async ({
327
161
  offset,
328
162
  sort,
329
163
  ...query
330
- }: EnrollmentQuery & { limit?: number; offset?: number; sort?: Array<SortOption> }) => {
164
+ }: EnrollmentQuery & {
165
+ limit?: number;
166
+ offset?: number;
167
+ sort?: Array<SortOption>;
168
+ }): Promise<Array<Enrollment>> => {
331
169
  const defaultSortOption: Array<SortOption> = [{ key: 'created', value: SortDirection.ASC }];
332
170
  const enrollments = Enrollments.find(buildFindSelector(query), {
333
171
  skip: offset,
@@ -339,56 +177,16 @@ export const configureEnrollmentsModule = async ({
339
177
  },
340
178
 
341
179
  // Transformations
342
- normalizedStatus: (enrollment) => {
180
+ normalizedStatus: (enrollment: Enrollment): EnrollmentStatus => {
343
181
  return enrollment.status === null
344
182
  ? EnrollmentStatus.INITIAL
345
183
  : (enrollment.status as EnrollmentStatus);
346
184
  },
347
185
 
348
- isExpired: (enrollment, { referenceDate }) => {
349
- const relevantDate = referenceDate ? new Date(referenceDate) : new Date();
350
- const expiryDate = new Date(enrollment.expires);
351
- const isExpired = relevantDate.getTime() > expiryDate.getTime();
352
- return isExpired;
353
- },
354
-
355
- // Processing
356
- terminateEnrollment: async (enrollment, unchainedAPI) => {
357
- if (enrollment.status === EnrollmentStatus.TERMINATED) return enrollment;
358
-
359
- let updatedEnrollment = await updateStatus(
360
- enrollment._id,
361
- {
362
- status: EnrollmentStatus.TERMINATED,
363
- info: 'terminated manually',
364
- },
365
- unchainedAPI,
366
- );
367
-
368
- updatedEnrollment = await processEnrollment(updatedEnrollment, unchainedAPI);
369
-
370
- return sendStatusToCustomer(updatedEnrollment, {}, unchainedAPI);
371
- },
372
-
373
- activateEnrollment: async (enrollment, unchainedAPI) => {
374
- if (enrollment.status === EnrollmentStatus.TERMINATED) return enrollment;
375
-
376
- let updatedEnrollment = await updateStatus(
377
- enrollment._id,
378
- {
379
- status: EnrollmentStatus.ACTIVE,
380
- info: 'activated manually',
381
- },
382
- unchainedAPI,
383
- );
384
-
385
- updatedEnrollment = await processEnrollment(updatedEnrollment, unchainedAPI);
386
-
387
- return sendStatusToCustomer(updatedEnrollment, {}, unchainedAPI);
388
- },
186
+ isExpired,
389
187
 
390
188
  // Mutations
391
- addEnrollmentPeriod: async (enrollmentId, period) => {
189
+ addEnrollmentPeriod: async (enrollmentId: string, period: EnrollmentPeriod): Promise<Enrollment> => {
392
190
  const { start, end, orderId, isTrial } = period;
393
191
  const selector = generateDbFilterById(enrollmentId);
394
192
  const enrollment = await Enrollments.findOneAndUpdate(
@@ -416,99 +214,31 @@ export const configureEnrollmentsModule = async ({
416
214
  return enrollment;
417
215
  },
418
216
 
419
- create: async (
420
- { countryCode, currencyCode, orderIdForFirstPeriod, ...enrollmentData },
421
- unchainedAPI,
422
- ) => {
423
- const { modules } = unchainedAPI;
424
-
425
- const countryObject = await modules.countries.findCountry({ isoCode: countryCode });
426
- const currencies = await modules.currencies.findCurrencies({ includeInactive: false });
427
- const currency =
428
- currencyCode || resolveBestCurrency(countryObject.defaultCurrencyCode, currencies);
429
-
217
+ create: async ({
218
+ countryCode,
219
+ currencyCode,
220
+ ...enrollmentData
221
+ }: Omit<Enrollment, 'status' | 'periods' | 'log'>): Promise<Enrollment> => {
430
222
  const { insertedId: enrollmentId } = await Enrollments.insertOne({
431
223
  _id: generateDbObjectId(),
432
224
  created: new Date(),
433
225
  ...enrollmentData,
434
226
  status: EnrollmentStatus.INITIAL,
435
227
  periods: [],
436
- currencyCode: currency,
228
+ currencyCode,
437
229
  countryCode,
438
230
  configuration: enrollmentData.configuration || [],
439
231
  log: [],
440
232
  });
441
233
 
442
- const newEnrollment = await Enrollments.findOne(generateDbFilterById(enrollmentId), {});
443
-
444
- const reason = 'new_enrollment';
445
-
446
- const initializedEnrollment = await initializeEnrollment(
447
- newEnrollment,
448
- {
449
- orderIdForFirstPeriod,
450
- reason,
451
- },
452
- unchainedAPI,
453
- );
454
-
455
- const enrollment = await sendStatusToCustomer(
456
- initializedEnrollment,
457
- {
458
- reason,
459
- },
460
- unchainedAPI,
461
- );
462
-
234
+ const enrollment = await Enrollments.findOne({
235
+ _id: enrollmentId,
236
+ });
463
237
  await emit('ENROLLMENT_CREATE', { enrollment });
464
-
465
238
  return enrollment;
466
239
  },
467
240
 
468
- createFromCheckout: async (order, { items, context }, unchainedAPI) => {
469
- const { modules } = unchainedAPI;
470
- const orderId = order._id;
471
-
472
- const payment = await modules.orders.payments.findOrderPayment({
473
- orderPaymentId: order.paymentId,
474
- });
475
- const delivery = await modules.orders.deliveries.findDelivery({
476
- orderDeliveryId: order.deliveryId,
477
- });
478
-
479
- const template = {
480
- billingAddress: order.billingAddress,
481
- contact: order.contact,
482
- countryCode: order.countryCode,
483
- currencyCode: order.currency,
484
- delivery: {
485
- deliveryProviderId: delivery.deliveryProviderId,
486
- context: delivery.context,
487
- },
488
- orderIdForFirstPeriod: orderId,
489
- payment: {
490
- paymentProviderId: payment.paymentProviderId,
491
- context: payment.context,
492
- },
493
- userId: order.userId,
494
- meta: order.context,
495
- ...context,
496
- };
497
-
498
- await Promise.all(
499
- items.map(async (item) => {
500
- const enrollmentData = await EnrollmentDirector.transformOrderItemToEnrollment(
501
- item,
502
- template,
503
- unchainedAPI,
504
- );
505
-
506
- return modules.enrollments.create(enrollmentData, unchainedAPI);
507
- }),
508
- );
509
- },
510
-
511
- delete: async (enrollmentId) => {
241
+ delete: async (enrollmentId: string) => {
512
242
  const { modifiedCount: deletedCount } = await Enrollments.updateOne(
513
243
  generateDbFilterById(enrollmentId),
514
244
  {
@@ -517,11 +247,14 @@ export const configureEnrollmentsModule = async ({
517
247
  },
518
248
  },
519
249
  );
520
- await emit('ORDER_REMOVE', { enrollmentId });
250
+ await emit('ENROLLMENT_REMOVE', { enrollmentId });
521
251
  return deletedCount;
522
252
  },
523
253
 
524
- removeEnrollmentPeriodByOrderId: async (enrollmentId, orderId) => {
254
+ removeEnrollmentPeriodByOrderId: async (
255
+ enrollmentId: string,
256
+ orderId: string,
257
+ ): Promise<Enrollment> => {
525
258
  const selector = generateDbFilterById(enrollmentId);
526
259
  return Enrollments.findOneAndUpdate(
527
260
  selector,
@@ -537,13 +270,13 @@ export const configureEnrollmentsModule = async ({
537
270
  );
538
271
  },
539
272
 
540
- updateBillingAddress: updateEnrollmentField('billingAddress'),
541
- updateContact: updateEnrollmentField('contact'),
542
- updateContext: updateEnrollmentField('meta'),
543
- updateDelivery: updateEnrollmentField('delivery'),
544
- updatePayment: updateEnrollmentField('payment'),
273
+ updateBillingAddress: updateEnrollmentField<Address>('billingAddress'),
274
+ updateContact: updateEnrollmentField<Contact>('contact'),
275
+ updateContext: updateEnrollmentField<any>('meta'),
276
+ updateDelivery: updateEnrollmentField<Enrollment['delivery']>('delivery'),
277
+ updatePayment: updateEnrollmentField<Enrollment['payment']>('payment'),
545
278
 
546
- updatePlan: async (enrollmentId, plan, unchainedAPI) => {
279
+ updatePlan: async (enrollmentId: string, plan: EnrollmentPlan): Promise<Enrollment> => {
547
280
  const enrollment = await Enrollments.findOneAndUpdate(
548
281
  generateDbFilterById(enrollmentId),
549
282
  {
@@ -558,13 +291,18 @@ export const configureEnrollmentsModule = async ({
558
291
  );
559
292
 
560
293
  await emit('ENROLLMENT_UPDATE', { enrollment, field: 'plan' });
561
-
562
- const reason = 'updated_plan';
563
- const initializedEnrollment = await initializeEnrollment(enrollment, { reason }, unchainedAPI);
564
-
565
- return sendStatusToCustomer(initializedEnrollment, { reason }, unchainedAPI);
294
+ return enrollment;
566
295
  },
567
296
 
568
297
  updateStatus,
298
+ deleteInactiveUserEnrollments: async (userId: string) => {
299
+ const { deletedCount } = await Enrollments.deleteMany({
300
+ userId,
301
+ status: { $in: [null, EnrollmentStatus.INITIAL, EnrollmentStatus.TERMINATED] },
302
+ });
303
+ return deletedCount;
304
+ },
569
305
  };
570
306
  };
307
+
308
+ export type EnrollmentsModule = Awaited<ReturnType<typeof configureEnrollmentsModule>>;
package/tsconfig.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "extends": "../shared/base.tsconfig.json",
3
3
  "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "lib",
4
+ "declarationDir": "./lib",
5
+ "rootDir": "./src",
6
+ "outDir": "./lib"
6
7
  },
7
- "exclude": ["**/*.test.ts", "**/*.test.js", "tests"],
8
- "include": [
9
- "./src"
10
- ]
11
- }
8
+ "exclude": ["**/*.test.ts", "**/*.test.js", "tests", "lib"]
9
+ }
@@ -1,6 +0,0 @@
1
- export enum EnrollmentStatus {
2
- INITIAL = 'INITIAL',
3
- ACTIVE = 'ACTIVE',
4
- PAUSED = 'PAUSED',
5
- TERMINATED = 'TERMINATED',
6
- }
@@ -1,82 +0,0 @@
1
- import { log, LogLevel } from '@unchainedshop/logger';
2
- import { add } from 'date-fns/add';
3
- import { IEnrollmentAdapter } from '../types.js';
4
-
5
- export const periodForReferenceDate = (referenceDate: Date, intervalCount = 1, interval = 'WEEKS') => {
6
- const lowerCaseInterval = interval.toLowerCase();
7
-
8
- const start = new Date(referenceDate);
9
- if (lowerCaseInterval === 'hours') {
10
- start.setMinutes(0, 0, 0);
11
- } else {
12
- start.setSeconds(0, 0);
13
- }
14
-
15
- return {
16
- start,
17
- end: add(start, {
18
- [lowerCaseInterval]: intervalCount,
19
- }),
20
- };
21
- };
22
-
23
- export const EnrollmentAdapter: Omit<IEnrollmentAdapter, 'key' | 'label' | 'version'> = {
24
- isActivatedFor: () => {
25
- return false;
26
- },
27
-
28
- transformOrderItemToEnrollmentPlan: async (item) => {
29
- return {
30
- configuration: item.configuration,
31
- productId: item.productId,
32
- quantity: item.quantity,
33
- };
34
- },
35
-
36
- actions: (context) => {
37
- return {
38
- configurationForOrder: async () => {
39
- throw new Error(`Not implemented on EnrollmentAdapter`);
40
- },
41
-
42
- isOverdue: async () => false,
43
- isValidForActivation: async () => false,
44
-
45
- nextPeriod: async () => {
46
- const { enrollment } = context;
47
- const product = await context.modules.products.findProduct({
48
- productId: enrollment.productId,
49
- });
50
- const plan = product?.plan;
51
- const referenceDate = new Date();
52
- if (!plan) return null;
53
-
54
- if (plan.trialIntervalCount && !enrollment?.periods?.length) {
55
- return {
56
- ...periodForReferenceDate(referenceDate, plan.trialIntervalCount, plan.trialInterval),
57
- isTrial: true,
58
- };
59
- }
60
-
61
- const lastEnd = enrollment?.periods?.reduce((acc, item) => {
62
- if (!acc) return item.end;
63
- const endDate = new Date(item.end);
64
- if (acc.getTime() < endDate.getTime()) {
65
- return endDate;
66
- }
67
- return acc;
68
- }, referenceDate);
69
-
70
- return {
71
- ...periodForReferenceDate(lastEnd, plan.billingIntervalCount, plan.billingInterval),
72
- isTrial: false,
73
- };
74
- },
75
- };
76
- },
77
-
78
- // eslint-disable-next-line
79
- log(message: string, { level = LogLevel.Debug, ...options } = {}) {
80
- return log(message, { level, ...options });
81
- },
82
- };