@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.
- package/lib/db/EnrollmentsCollection.d.ts +50 -1
- package/lib/db/EnrollmentsCollection.d.ts.map +1 -1
- package/lib/db/EnrollmentsCollection.js +7 -0
- package/lib/db/EnrollmentsCollection.js.map +1 -1
- package/lib/director/EnrollmentAdapter.js.map +1 -1
- package/lib/enrollments-index.d.ts +1 -4
- package/lib/enrollments-index.d.ts.map +1 -1
- package/lib/enrollments-index.js +1 -4
- package/lib/enrollments-index.js.map +1 -1
- package/lib/enrollments-settings.d.ts +3 -3
- package/lib/enrollments-settings.d.ts.map +1 -1
- package/lib/enrollments-settings.js.map +1 -1
- package/lib/module/configureEnrollmentsModule.d.ts +41 -54
- package/lib/module/configureEnrollmentsModule.d.ts.map +1 -1
- package/lib/module/configureEnrollmentsModule.js +22 -143
- package/lib/module/configureEnrollmentsModule.js.map +1 -1
- package/lib/module/configureEnrollmentsModule_BACKUP_54085.d.ts +59 -0
- package/lib/module/configureEnrollmentsModule_BACKUP_54085.d.ts.map +1 -0
- package/lib/module/configureEnrollmentsModule_BACKUP_54085.js +203 -0
- package/lib/module/configureEnrollmentsModule_BACKUP_54085.js.map +1 -0
- package/lib/module/configureEnrollmentsModule_BASE_54085.d.ts +70 -0
- package/lib/module/configureEnrollmentsModule_BASE_54085.d.ts.map +1 -0
- package/lib/module/configureEnrollmentsModule_BASE_54085.js +324 -0
- package/lib/module/configureEnrollmentsModule_BASE_54085.js.map +1 -0
- package/lib/module/configureEnrollmentsModule_LOCAL_54085.d.ts +58 -0
- package/lib/module/configureEnrollmentsModule_LOCAL_54085.d.ts.map +1 -0
- package/lib/module/configureEnrollmentsModule_LOCAL_54085.js +196 -0
- package/lib/module/configureEnrollmentsModule_LOCAL_54085.js.map +1 -0
- package/lib/module/configureEnrollmentsModule_REMOTE_54085.d.ts +71 -0
- package/lib/module/configureEnrollmentsModule_REMOTE_54085.d.ts.map +1 -0
- package/lib/module/configureEnrollmentsModule_REMOTE_54085.js +331 -0
- package/lib/module/configureEnrollmentsModule_REMOTE_54085.js.map +1 -0
- package/lib/types.d.ts +8 -9
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/package.json +10 -13
- package/src/db/EnrollmentsCollection.ts +49 -1
- package/src/enrollments-index.ts +1 -6
- package/src/enrollments-settings.ts +2 -3
- package/src/module/buildFindSelector.test.ts +1 -1
- package/src/module/configureEnrollmentsModule.ts +77 -339
- package/tsconfig.json +5 -7
- package/src/db/EnrollmentStatus.ts +0 -6
- package/src/director/EnrollmentAdapter.ts +0 -82
- package/src/director/EnrollmentDirector.ts +0 -60
- package/src/director/EnrollmentError.ts +0 -6
- package/src/module/periodForReferenceDate.test.ts +0 -23
- 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
|
-
|
|
9
|
-
} from '../
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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>)
|
|
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
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
): Promise<
|
|
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
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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 (
|
|
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 & {
|
|
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
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
|
228
|
+
currencyCode,
|
|
437
229
|
countryCode,
|
|
438
230
|
configuration: enrollmentData.configuration || [],
|
|
439
231
|
log: [],
|
|
440
232
|
});
|
|
441
233
|
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
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('
|
|
250
|
+
await emit('ENROLLMENT_REMOVE', { enrollmentId });
|
|
521
251
|
return deletedCount;
|
|
522
252
|
},
|
|
523
253
|
|
|
524
|
-
removeEnrollmentPeriodByOrderId: async (
|
|
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
|
|
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
|
-
"
|
|
5
|
-
"
|
|
4
|
+
"declarationDir": "./lib",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./lib"
|
|
6
7
|
},
|
|
7
|
-
"exclude": ["**/*.test.ts", "**/*.test.js", "tests"]
|
|
8
|
-
|
|
9
|
-
"./src"
|
|
10
|
-
]
|
|
11
|
-
}
|
|
8
|
+
"exclude": ["**/*.test.ts", "**/*.test.js", "tests", "lib"]
|
|
9
|
+
}
|
|
@@ -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
|
-
};
|