@unchainedshop/core-enrollments 5.0.0-alpha.1 → 5.0.0-alpha.3
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.js +25 -26
- package/lib/migrations/20260625120100-normalize-contact-phone.d.ts +2 -0
- package/lib/migrations/20260625120100-normalize-contact-phone.js +27 -0
- package/lib/module/configureEnrollmentsModule.d.ts +2 -2
- package/lib/module/configureEnrollmentsModule.js +21 -5
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mongodb, buildDbIndexes
|
|
1
|
+
import { mongodb, buildDbIndexes } from '@unchainedshop/mongodb';
|
|
2
2
|
import {} from '@unchainedshop/mongodb';
|
|
3
3
|
export const EnrollmentStatus = {
|
|
4
4
|
INITIAL: 'INITIAL',
|
|
@@ -8,34 +8,33 @@ export const EnrollmentStatus = {
|
|
|
8
8
|
};
|
|
9
9
|
export const EnrollmentsCollection = async (db) => {
|
|
10
10
|
const Enrollments = db.collection('enrollments');
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
status: 1,
|
|
30
|
-
},
|
|
31
|
-
name: 'enrollment_fulltext_search',
|
|
11
|
+
await buildDbIndexes(Enrollments, [
|
|
12
|
+
{
|
|
13
|
+
index: {
|
|
14
|
+
_id: 'text',
|
|
15
|
+
userId: 'text',
|
|
16
|
+
enrollmentNumber: 'text',
|
|
17
|
+
status: 'text',
|
|
18
|
+
'contact.telNumber': 'text',
|
|
19
|
+
'contact.emailAddress': 'text',
|
|
20
|
+
},
|
|
21
|
+
options: {
|
|
22
|
+
weights: {
|
|
23
|
+
_id: 8,
|
|
24
|
+
userId: 3,
|
|
25
|
+
enrollmentNumber: 6,
|
|
26
|
+
'contact.telNumber': 5,
|
|
27
|
+
'contact.emailAddress': 4,
|
|
28
|
+
status: 1,
|
|
32
29
|
},
|
|
30
|
+
name: 'enrollment_fulltext_search',
|
|
33
31
|
},
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
36
34
|
await buildDbIndexes(Enrollments, [
|
|
37
|
-
{ index: {
|
|
38
|
-
{ index: {
|
|
35
|
+
{ index: { 'periods.orderId': 1 } },
|
|
36
|
+
{ index: { userId: 1, status: 1 } },
|
|
37
|
+
{ index: { productId: 1, status: 1 } },
|
|
39
38
|
{ index: { status: 1 } },
|
|
40
39
|
{ index: { enrollmentNumber: 1 } },
|
|
41
40
|
]);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { normalizePhoneNumber } from '@unchainedshop/utils';
|
|
2
|
+
import { EnrollmentsCollection } from "../db/EnrollmentsCollection.js";
|
|
3
|
+
export default function normalizeContactPhone(repository) {
|
|
4
|
+
repository?.register({
|
|
5
|
+
id: 20260625120100,
|
|
6
|
+
name: 'Normalize enrollment.contact.telNumber to E.164 format',
|
|
7
|
+
up: async ({ logger }) => {
|
|
8
|
+
const Enrollments = await EnrollmentsCollection(repository.db);
|
|
9
|
+
const enrollments = await Enrollments.find({ 'contact.telNumber': { $exists: true, $nin: [null, ''] } }, { projection: { _id: true, contact: true, billingAddress: true, countryCode: true } }).toArray();
|
|
10
|
+
let changed = 0;
|
|
11
|
+
let skipped = 0;
|
|
12
|
+
for (const enrollment of enrollments) {
|
|
13
|
+
const current = enrollment.contact?.telNumber;
|
|
14
|
+
const defaultCountry = enrollment.billingAddress?.countryCode || enrollment.countryCode;
|
|
15
|
+
const normalized = normalizePhoneNumber(current, defaultCountry);
|
|
16
|
+
if (!normalized || normalized === current) {
|
|
17
|
+
if (!normalized)
|
|
18
|
+
skipped += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
await Enrollments.updateOne({ _id: enrollment._id }, { $set: { 'contact.telNumber': normalized } });
|
|
22
|
+
changed += 1;
|
|
23
|
+
}
|
|
24
|
+
logger?.info(`Normalize enrollment.contact.telNumber: ${changed} updated, ${skipped} left unchanged (unparseable)`);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -8,7 +8,7 @@ export interface EnrollmentQuery {
|
|
|
8
8
|
queryString?: string;
|
|
9
9
|
}
|
|
10
10
|
export declare const buildFindSelector: ({ queryString, status, userId }: EnrollmentQuery) => mongodb.Filter<Enrollment>;
|
|
11
|
-
export declare const configureEnrollmentsModule: ({ db, options: enrollmentOptions, }: ModuleInput<EnrollmentsSettingsOptions>) => Promise<{
|
|
11
|
+
export declare const configureEnrollmentsModule: ({ db, migrationRepository, options: enrollmentOptions, }: ModuleInput<EnrollmentsSettingsOptions>) => Promise<{
|
|
12
12
|
count: (query: EnrollmentQuery) => Promise<number>;
|
|
13
13
|
openEnrollmentWithProduct: ({ productId }: {
|
|
14
14
|
productId: string;
|
|
@@ -32,7 +32,7 @@ export declare const configureEnrollmentsModule: ({ db, options: enrollmentOptio
|
|
|
32
32
|
delete: (enrollmentId: string) => Promise<number>;
|
|
33
33
|
removeEnrollmentPeriodByOrderId: (enrollmentId: string, orderId: string) => Promise<mongodb.WithId<Enrollment> | null>;
|
|
34
34
|
updateBillingAddress: (enrollmentId: string, fieldValue: Address) => Promise<mongodb.WithId<Enrollment> | null>;
|
|
35
|
-
updateContact: (enrollmentId: string,
|
|
35
|
+
updateContact: (enrollmentId: string, contact: Contact) => Promise<mongodb.WithId<Enrollment> | null>;
|
|
36
36
|
updateContext: (enrollmentId: string, fieldValue: any) => Promise<mongodb.WithId<Enrollment> | null>;
|
|
37
37
|
updateDelivery: (enrollmentId: string, fieldValue: {
|
|
38
38
|
deliveryProviderId?: string;
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { SortDirection } from '@unchainedshop/utils';
|
|
1
|
+
import { SortDirection, normalizePhoneNumber } from '@unchainedshop/utils';
|
|
2
2
|
import { EnrollmentStatus, } from "../db/EnrollmentsCollection.js";
|
|
3
3
|
import { emit, registerEvents } from '@unchainedshop/events';
|
|
4
|
-
import { generateDbFilterById, buildSortOptions, mongodb, generateDbObjectId,
|
|
4
|
+
import { generateDbFilterById, buildSortOptions, mongodb, generateDbObjectId, } from '@unchainedshop/mongodb';
|
|
5
5
|
import { EnrollmentsCollection } from "../db/EnrollmentsCollection.js";
|
|
6
6
|
import { enrollmentsSettings } from "../enrollments-settings.js";
|
|
7
|
+
import normalizeContactPhoneMigration from "../migrations/20260625120100-normalize-contact-phone.js";
|
|
7
8
|
const ENROLLMENT_EVENTS = [
|
|
8
9
|
'ENROLLMENT_ADD_PERIOD',
|
|
9
10
|
'ENROLLMENT_CREATE',
|
|
10
11
|
'ENROLLMENT_REMOVE',
|
|
11
12
|
'ENROLLMENT_UPDATE',
|
|
12
13
|
];
|
|
14
|
+
const normalizeContactPhone = (contact, defaultCountry) => {
|
|
15
|
+
if (!contact?.telNumber)
|
|
16
|
+
return contact;
|
|
17
|
+
return {
|
|
18
|
+
...contact,
|
|
19
|
+
telNumber: normalizePhoneNumber(contact.telNumber, defaultCountry) || contact.telNumber,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
13
22
|
export const buildFindSelector = ({ queryString, status, userId }) => {
|
|
14
23
|
const selector = {
|
|
15
24
|
deleted: null,
|
|
@@ -19,12 +28,12 @@ export const buildFindSelector = ({ queryString, status, userId }) => {
|
|
|
19
28
|
if (userId)
|
|
20
29
|
selector.userId = userId;
|
|
21
30
|
if (queryString) {
|
|
22
|
-
assertDocumentDBCompatMode();
|
|
23
31
|
selector.$text = { $search: queryString };
|
|
24
32
|
}
|
|
25
33
|
return selector;
|
|
26
34
|
};
|
|
27
|
-
export const configureEnrollmentsModule = async ({ db, options: enrollmentOptions = {}, }) => {
|
|
35
|
+
export const configureEnrollmentsModule = async ({ db, migrationRepository, options: enrollmentOptions = {}, }) => {
|
|
36
|
+
normalizeContactPhoneMigration(migrationRepository);
|
|
28
37
|
registerEvents(ENROLLMENT_EVENTS);
|
|
29
38
|
enrollmentsSettings.configureSettings(enrollmentOptions);
|
|
30
39
|
const Enrollments = await EnrollmentsCollection(db);
|
|
@@ -149,6 +158,7 @@ export const configureEnrollmentsModule = async ({ db, options: enrollmentOption
|
|
|
149
158
|
periods: [],
|
|
150
159
|
currencyCode,
|
|
151
160
|
countryCode,
|
|
161
|
+
contact: normalizeContactPhone(enrollmentData.contact, enrollmentData.billingAddress?.countryCode || countryCode),
|
|
152
162
|
configuration: enrollmentData.configuration || [],
|
|
153
163
|
log: [],
|
|
154
164
|
});
|
|
@@ -179,7 +189,13 @@ export const configureEnrollmentsModule = async ({ db, options: enrollmentOption
|
|
|
179
189
|
}, { returnDocument: 'after' });
|
|
180
190
|
},
|
|
181
191
|
updateBillingAddress: updateEnrollmentField('billingAddress'),
|
|
182
|
-
updateContact:
|
|
192
|
+
updateContact: async (enrollmentId, contact) => {
|
|
193
|
+
const existing = await Enrollments.findOne(generateDbFilterById(enrollmentId), {
|
|
194
|
+
projection: { billingAddress: true, countryCode: true },
|
|
195
|
+
});
|
|
196
|
+
const defaultCountry = existing?.billingAddress?.countryCode || existing?.countryCode;
|
|
197
|
+
return updateEnrollmentField('contact')(enrollmentId, normalizeContactPhone(contact, defaultCountry));
|
|
198
|
+
},
|
|
183
199
|
updateContext: updateEnrollmentField('meta'),
|
|
184
200
|
updateDelivery: updateEnrollmentField('delivery'),
|
|
185
201
|
updatePayment: updateEnrollmentField('payment'),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/core-enrollments",
|
|
3
3
|
"description": "Subscription and recurring billing module for the Unchained Engine",
|
|
4
|
-
"version": "5.0.0-alpha.
|
|
4
|
+
"version": "5.0.0-alpha.3",
|
|
5
5
|
"main": "lib/enrollments-index.js",
|
|
6
6
|
"types": "lib/enrollments-index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@unchainedshop/events": "^5.0.0-alpha.1",
|
|
39
|
-
"@unchainedshop/
|
|
39
|
+
"@unchainedshop/mongodb": "^5.0.0-alpha.1",
|
|
40
40
|
"@unchainedshop/utils": "^5.0.0-alpha.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^26.2.0",
|
|
44
44
|
"typescript": "^5.8.3"
|
|
45
45
|
}
|
|
46
46
|
}
|