@stamhoofd/backend 2.55.2 → 2.56.0

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.
@@ -1,15 +1,12 @@
1
- import { createMollieClient, PaymentStatus as MolliePaymentStatus } from '@mollie/api-client';
2
1
  import { AutoEncoder, BooleanDecoder, Decoder, field } from '@simonbackx/simple-encoding';
3
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
4
3
  import { SimpleError } from '@simonbackx/simple-errors';
5
- import { BalanceItem, BalanceItemPayment, MolliePayment, MollieToken, Order, Organization, PayconiqPayment, Payment } from '@stamhoofd/models';
6
- import { QueueHandler } from '@stamhoofd/queues';
7
- import { PaymentGeneral, PaymentMethod, PaymentProvider, PaymentStatus } from '@stamhoofd/structures';
4
+ import { Order } from '@stamhoofd/models';
5
+ import { PaymentGeneral } from '@stamhoofd/structures';
8
6
 
9
7
  import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
10
- import { BuckarooHelper } from '../../../helpers/BuckarooHelper';
11
8
  import { Context } from '../../../helpers/Context';
12
- import { StripeHelper } from '../../../helpers/StripeHelper';
9
+ import { PaymentService } from '../../../services/PaymentService';
13
10
 
14
11
  type Params = { id: string };
15
12
  class Query extends AutoEncoder {
@@ -52,7 +49,7 @@ export class ExchangePaymentEndpoint extends Endpoint<Params, Query, Body, Respo
52
49
  }
53
50
 
54
51
  // Not method on payment because circular references (not supprted in ts)
55
- const payment = await ExchangePaymentEndpoint.pollStatus(request.params.id, organization, request.query.cancel);
52
+ const payment = await PaymentService.pollStatus(request.params.id, organization, request.query.cancel);
56
53
  if (!payment) {
57
54
  throw new SimpleError({
58
55
  code: '',
@@ -82,303 +79,4 @@ export class ExchangePaymentEndpoint extends Endpoint<Params, Query, Body, Respo
82
79
  await AuthenticatedStructures.paymentGeneral(payment, checkPermissions),
83
80
  );
84
81
  }
85
-
86
- static async handlePaymentStatusUpdate(payment: Payment, organization: Organization, status: PaymentStatus) {
87
- if (payment.status === status) {
88
- return;
89
- }
90
-
91
- if (status === PaymentStatus.Succeeded) {
92
- payment.status = PaymentStatus.Succeeded;
93
- payment.paidAt = new Date();
94
- await payment.save();
95
-
96
- // Prevent concurrency issues
97
- await QueueHandler.schedule('balance-item-update/' + organization.id, async () => {
98
- const unloaded = (await BalanceItemPayment.where({ paymentId: payment.id })).map(r => r.setRelation(BalanceItemPayment.payment, payment));
99
- const balanceItemPayments = await BalanceItemPayment.balanceItem.load(
100
- unloaded,
101
- );
102
-
103
- for (const balanceItemPayment of balanceItemPayments) {
104
- await balanceItemPayment.markPaid(organization);
105
- }
106
-
107
- await BalanceItem.updateOutstanding(balanceItemPayments.map(p => p.balanceItem));
108
- });
109
- return;
110
- }
111
-
112
- const oldStatus = payment.status;
113
-
114
- // Save before updating balance items
115
- payment.status = status;
116
- payment.paidAt = null;
117
- await payment.save();
118
-
119
- // If OLD status was succeeded, we need to revert the actions
120
- if (oldStatus === PaymentStatus.Succeeded) {
121
- // No longer succeeded
122
- await QueueHandler.schedule('balance-item-update/' + organization.id, async () => {
123
- const balanceItemPayments = await BalanceItemPayment.balanceItem.load(
124
- (await BalanceItemPayment.where({ paymentId: payment.id })).map(r => r.setRelation(BalanceItemPayment.payment, payment)),
125
- );
126
-
127
- for (const balanceItemPayment of balanceItemPayments) {
128
- await balanceItemPayment.undoPaid(organization);
129
- }
130
-
131
- await BalanceItem.updateOutstanding(balanceItemPayments.map(p => p.balanceItem));
132
- });
133
- }
134
-
135
- // Moved to failed
136
- if (status == PaymentStatus.Failed) {
137
- await QueueHandler.schedule('balance-item-update/' + organization.id, async () => {
138
- const balanceItemPayments = await BalanceItemPayment.balanceItem.load(
139
- (await BalanceItemPayment.where({ paymentId: payment.id })).map(r => r.setRelation(BalanceItemPayment.payment, payment)),
140
- );
141
-
142
- for (const balanceItemPayment of balanceItemPayments) {
143
- await balanceItemPayment.markFailed(organization);
144
- }
145
-
146
- await BalanceItem.updateOutstanding(balanceItemPayments.map(p => p.balanceItem));
147
- });
148
- }
149
-
150
- // If OLD status was FAILED, we need to revert the actions
151
- if (oldStatus === PaymentStatus.Failed) { // OLD FAILED!! -> NOW PENDING
152
- await QueueHandler.schedule('balance-item-update/' + organization.id, async () => {
153
- const balanceItemPayments = await BalanceItemPayment.balanceItem.load(
154
- (await BalanceItemPayment.where({ paymentId: payment.id })).map(r => r.setRelation(BalanceItemPayment.payment, payment)),
155
- );
156
-
157
- for (const balanceItemPayment of balanceItemPayments) {
158
- await balanceItemPayment.undoFailed(organization);
159
- }
160
-
161
- await BalanceItem.updateOutstanding(balanceItemPayments.map(p => p.balanceItem));
162
- });
163
- }
164
- }
165
-
166
- /**
167
- * ID of payment is needed because of race conditions (need to fetch payment in a race condition save queue)
168
- */
169
- static async pollStatus(paymentId: string, org: Organization | null, cancel = false): Promise<Payment | undefined> {
170
- // Prevent polling the same payment multiple times at the same time: create a queue to prevent races
171
- return await QueueHandler.schedule('payments/' + paymentId, async () => {
172
- // Get a new copy of the payment (is required to prevent concurreny bugs)
173
- const payment = await Payment.getByID(paymentId);
174
- if (!payment) {
175
- return;
176
- }
177
-
178
- if (!payment.organizationId) {
179
- console.error('Payment without organization not supported', payment.id);
180
- return;
181
- }
182
-
183
- const organization = org ?? await Organization.getByID(payment.organizationId);
184
- if (!organization) {
185
- console.error('Organization not found for payment', payment.id);
186
- return;
187
- }
188
-
189
- const testMode = organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production';
190
-
191
- if (payment.status == PaymentStatus.Pending || payment.status == PaymentStatus.Created || (payment.provider === PaymentProvider.Buckaroo && payment.status == PaymentStatus.Failed)) {
192
- if (payment.provider === PaymentProvider.Stripe) {
193
- try {
194
- let status = await StripeHelper.getStatus(payment, cancel || this.shouldTryToCancel(payment.status, payment), testMode);
195
-
196
- if (this.isManualExpired(status, payment)) {
197
- console.error('Manually marking Stripe payment as expired', payment.id);
198
- status = PaymentStatus.Failed;
199
- }
200
-
201
- await this.handlePaymentStatusUpdate(payment, organization, status);
202
- }
203
- catch (e) {
204
- console.error('Payment check failed Stripe', payment.id, e);
205
- if (this.isManualExpired(payment.status, payment)) {
206
- console.error('Manually marking Stripe payment as expired', payment.id);
207
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
208
- }
209
- }
210
- }
211
- else if (payment.provider === PaymentProvider.Mollie) {
212
- // check status via mollie
213
- const molliePayments = await MolliePayment.where({ paymentId: payment.id }, { limit: 1 });
214
- if (molliePayments.length == 1) {
215
- const molliePayment = molliePayments[0];
216
- // check status
217
- const token = await MollieToken.getTokenFor(organization.id);
218
-
219
- if (token) {
220
- try {
221
- const mollieClient = createMollieClient({ accessToken: await token.getAccessToken() });
222
- const mollieData = await mollieClient.payments.get(molliePayment.mollieId, {
223
- testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production',
224
- });
225
-
226
- console.log(mollieData); // log to log files to check issues
227
-
228
- const details = mollieData.details as any;
229
- if (details?.consumerName) {
230
- payment.ibanName = details.consumerName;
231
- }
232
- if (details?.consumerAccount) {
233
- payment.iban = details.consumerAccount;
234
- }
235
- if (details?.cardHolder) {
236
- payment.ibanName = details.cardHolder;
237
- }
238
- if (details?.cardNumber) {
239
- payment.iban = 'xxxx xxxx xxxx ' + details.cardNumber;
240
- }
241
-
242
- if (mollieData.status === MolliePaymentStatus.paid) {
243
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Succeeded);
244
- }
245
- else if (mollieData.status === MolliePaymentStatus.failed || mollieData.status === MolliePaymentStatus.expired || mollieData.status === MolliePaymentStatus.canceled) {
246
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
247
- }
248
- else if (this.isManualExpired(payment.status, payment)) {
249
- // Mollie still returning pending after 1 day: mark as failed
250
- console.error('Manually marking Mollie payment as expired', payment.id);
251
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
252
- }
253
- }
254
- catch (e) {
255
- console.error('Payment check failed Mollie', payment.id, e);
256
- if (this.isManualExpired(payment.status, payment)) {
257
- console.error('Manually marking Mollie payment as expired', payment.id);
258
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
259
- }
260
- }
261
- }
262
- else {
263
- console.warn('Mollie payment is missing for organization ' + organization.id + ' while checking payment status...');
264
-
265
- if (this.isManualExpired(payment.status, payment)) {
266
- console.error('Manually marking payment without mollie token as expired', payment.id);
267
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
268
- }
269
- }
270
- }
271
- else {
272
- if (this.isManualExpired(payment.status, payment)) {
273
- console.error('Manually marking payment without mollie payments as expired', payment.id);
274
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
275
- }
276
- }
277
- }
278
- else if (payment.provider == PaymentProvider.Buckaroo) {
279
- const helper = new BuckarooHelper(organization.privateMeta.buckarooSettings?.key ?? '', organization.privateMeta.buckarooSettings?.secret ?? '', organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production');
280
- try {
281
- let status = await helper.getStatus(payment);
282
-
283
- if (this.isManualExpired(status, payment)) {
284
- console.error('Manually marking Buckaroo payment as expired', payment.id);
285
- status = PaymentStatus.Failed;
286
- }
287
-
288
- await this.handlePaymentStatusUpdate(payment, organization, status);
289
- }
290
- catch (e) {
291
- console.error('Payment check failed Buckaroo', payment.id, e);
292
- if (this.isManualExpired(payment.status, payment)) {
293
- console.error('Manually marking Buckaroo payment as expired', payment.id);
294
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
295
- }
296
- }
297
- }
298
- else if (payment.provider == PaymentProvider.Payconiq) {
299
- // Check status
300
-
301
- const payconiqPayments = await PayconiqPayment.where({ paymentId: payment.id }, { limit: 1 });
302
- if (payconiqPayments.length == 1) {
303
- const payconiqPayment = payconiqPayments[0];
304
-
305
- if (cancel) {
306
- console.error('Cancelling Payconiq payment on request', payment.id);
307
- await payconiqPayment.cancel(organization);
308
- }
309
-
310
- let status = await payconiqPayment.getStatus(organization);
311
-
312
- if (!cancel && this.shouldTryToCancel(status, payment)) {
313
- console.error('Manually cancelling Payconiq payment', payment.id);
314
- if (await payconiqPayment.cancel(organization)) {
315
- status = PaymentStatus.Failed;
316
- }
317
- }
318
-
319
- if (this.isManualExpired(status, payment)) {
320
- console.error('Manually marking Payconiq payment as expired', payment.id);
321
- status = PaymentStatus.Failed;
322
- }
323
-
324
- await this.handlePaymentStatusUpdate(payment, organization, status);
325
- }
326
- else {
327
- console.warn('Payconiq payment is missing for organization ' + organization.id + ' while checking payment status...');
328
-
329
- if (this.isManualExpired(payment.status, payment)) {
330
- console.error('Manually marking Payconiq payment as expired because not found', payment.id);
331
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
332
- }
333
- }
334
- }
335
- else {
336
- console.error('Invalid payment provider', payment.provider, 'for payment', payment.id);
337
- if (this.isManualExpired(payment.status, payment)) {
338
- console.error('Manually marking unknown payment as expired', payment.id);
339
- await this.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
340
- }
341
- }
342
- }
343
- else {
344
- // Do a manual update if needed
345
- if (payment.status === PaymentStatus.Succeeded) {
346
- if (payment.provider === PaymentProvider.Stripe) {
347
- // Update the status
348
- await StripeHelper.getStatus(payment, false, testMode);
349
- }
350
- }
351
- }
352
- return payment;
353
- });
354
- }
355
-
356
- static isManualExpired(status: PaymentStatus, payment: Payment) {
357
- if ((status == PaymentStatus.Pending || status === PaymentStatus.Created) && payment.method !== PaymentMethod.DirectDebit) {
358
- // If payment is not succeeded after one day, mark as failed
359
- if (payment.createdAt < new Date(new Date().getTime() - 60 * 1000 * 60 * 24)) {
360
- return true;
361
- }
362
- }
363
- return false;
364
- }
365
-
366
- /**
367
- * Try to cancel a payment that is still pending
368
- */
369
- static shouldTryToCancel(status: PaymentStatus, payment: Payment) {
370
- if ((status == PaymentStatus.Pending || status === PaymentStatus.Created) && payment.method !== PaymentMethod.DirectDebit) {
371
- let timeout = STAMHOOFD.environment === 'development' ? 60 * 1000 * 2 : 60 * 1000 * 30;
372
-
373
- // If payconiq and not yet 'identified' (scanned), cancel after 5 minutes
374
- if (payment.provider === PaymentProvider.Payconiq && status === PaymentStatus.Created) {
375
- timeout = STAMHOOFD.environment === 'development' ? 60 * 1000 * 1 : 60 * 1000 * 5;
376
- }
377
-
378
- if (payment.createdAt < new Date(new Date().getTime() - timeout)) {
379
- return true;
380
- }
381
- }
382
- return false;
383
- }
384
82
  }
@@ -520,7 +520,18 @@ export class AdminPermissionChecker {
520
520
  }
521
521
 
522
522
  if (!user.organizationId) {
523
- return this.hasPlatformFullAccess();
523
+ if (this.hasPlatformFullAccess()) {
524
+ return true;
525
+ }
526
+
527
+ // Check if this user has permissions for the current scoped organization
528
+ if (this.organization && await this.hasFullAccess(this.organization.id)) {
529
+ if (user.permissions?.forOrganization(this.organization)) {
530
+ return true;
531
+ }
532
+ }
533
+
534
+ return false;
524
535
  }
525
536
 
526
537
  return await this.canManageAdmins(user.organizationId);
@@ -932,6 +943,60 @@ export class AdminPermissionChecker {
932
943
  return false;
933
944
  }
934
945
 
946
+ /**
947
+ * Return a list of RecordSettings the current user can view or edit
948
+ */
949
+ async hasNRNAccess(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
950
+ const isUserManager = this.isUserManager(member);
951
+
952
+ if (isUserManager) {
953
+ return true;
954
+ }
955
+
956
+ if (!await this.canAccessMember(member, level)) {
957
+ return false;
958
+ }
959
+
960
+ // First list all organizations this member is part of
961
+ const organizations: Organization[] = [];
962
+
963
+ if (member.organizationId) {
964
+ if (this.checkScope(member.organizationId)) {
965
+ organizations.push(await this.getOrganization(member.organizationId));
966
+ }
967
+ }
968
+
969
+ for (const registration of member.registrations) {
970
+ if (this.checkScope(registration.organizationId)) {
971
+ if (!organizations.find(o => o.id === registration.organizationId)) {
972
+ organizations.push(await this.getOrganization(registration.organizationId));
973
+ }
974
+ }
975
+ }
976
+
977
+ // Loop all organizations.
978
+ for (const organization of organizations) {
979
+ const permissions = await this.getOrganizationPermissions(organization);
980
+ if (!permissions) {
981
+ continue;
982
+ }
983
+
984
+ if (permissions.hasAccessRight(AccessRight.MemberManageNRN)) {
985
+ return true;
986
+ }
987
+ }
988
+
989
+ // Platform data
990
+ const platformPermissions = this.platformPermissions;
991
+ if (platformPermissions) {
992
+ if (platformPermissions.hasAccessRight(AccessRight.MemberManageNRN)) {
993
+ return true;
994
+ }
995
+ }
996
+
997
+ return false;
998
+ }
999
+
935
1000
  /**
936
1001
  * Return a list of RecordSettings the current user can view or edit
937
1002
  */
@@ -1006,6 +1071,14 @@ export class AdminPermissionChecker {
1006
1071
  cloned.details.securityCode = null;
1007
1072
  }
1008
1073
 
1074
+ if (!await this.hasNRNAccess(member, PermissionLevel.Read)) {
1075
+ cloned.details.nationalRegisterNumber = null;
1076
+
1077
+ for (const parent of cloned.details.parents) {
1078
+ parent.nationalRegisterNumber = null;
1079
+ }
1080
+ }
1081
+
1009
1082
  return cloned;
1010
1083
  }
1011
1084
 
@@ -1021,6 +1094,14 @@ export class AdminPermissionChecker {
1021
1094
  });
1022
1095
  }
1023
1096
 
1097
+ if (Array.isArray(data.details.parents)) {
1098
+ throw new SimpleError({
1099
+ code: 'invalid_request',
1100
+ message: 'Cannot PUT a full member details parents',
1101
+ statusCode: 400,
1102
+ });
1103
+ }
1104
+
1024
1105
  const hasRecordAnswers = !!data.details.recordAnswers;
1025
1106
  const hasNotes = data.details.notes !== undefined;
1026
1107
  const isSetFinancialSupportTrue = data.details.shouldApplyReducedPrice;
@@ -1155,6 +1236,26 @@ export class AdminPermissionChecker {
1155
1236
  }
1156
1237
  }
1157
1238
 
1239
+ if (!await this.hasNRNAccess(member, PermissionLevel.Write)) {
1240
+ if (data.details.nationalRegisterNumber) {
1241
+ throw new SimpleError({
1242
+ code: 'permission_denied',
1243
+ message: 'Je hebt geen toegangsrechten om het rijksregisternummer van dit lid aan te passen',
1244
+ statusCode: 400,
1245
+ });
1246
+ }
1247
+
1248
+ for (const parent of data.details.parents.getPatches()) {
1249
+ if (parent.nationalRegisterNumber) {
1250
+ throw new SimpleError({
1251
+ code: 'permission_denied',
1252
+ message: 'Je hebt geen toegangsrechten om het rijksregisternummer van een ouder aan te passen',
1253
+ statusCode: 400,
1254
+ });
1255
+ }
1256
+ }
1257
+ }
1258
+
1158
1259
  return data;
1159
1260
  }
1160
1261
 
@@ -1,6 +1,6 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
- import { BalanceItem, CachedBalance, Document, Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, Ticket, User, Webshop } from '@stamhoofd/models';
3
- import { AccessRight, Document as DocumentStruct, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
2
+ import { AuditLog, BalanceItem, CachedBalance, Document, Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, Ticket, User, Webshop } from '@stamhoofd/models';
3
+ import { AuditLog as AuditLogStruct, Document as DocumentStruct, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
4
4
 
5
5
  import { Formatter } from '@stamhoofd/utility';
6
6
  import { Context } from './Context';
@@ -639,4 +639,48 @@ export class AuthenticatedStructures {
639
639
 
640
640
  return result;
641
641
  }
642
+
643
+ static async auditLogs(logs: AuditLog[]): Promise<AuditLogStruct[]> {
644
+ const structs: AuditLogStruct[] = [];
645
+
646
+ const userIds = Formatter.uniqueArray(logs.map(l => l.userId).filter(id => id !== null));
647
+ const users = await User.getByIDs(...userIds);
648
+
649
+ for (const log of logs) {
650
+ const user = log.userId ? (users.find(u => u.id === log.userId) ?? null) : null;
651
+ let userStruct: NamedObject | null = null;
652
+
653
+ if (user) {
654
+ if (!await Context.auth.canAccessUser(user)) {
655
+ if (user.permissions?.platform !== null) {
656
+ userStruct = NamedObject.create({
657
+ id: '',
658
+ name: 'Beheerder van ' + Platform.shared.config.name,
659
+ });
660
+ }
661
+ else {
662
+ userStruct = NamedObject.create({
663
+ id: '',
664
+ name: 'Onbekend',
665
+ });
666
+ }
667
+ }
668
+ else {
669
+ userStruct = NamedObject.create({
670
+ id: user.id,
671
+ name: (user.firstName || user.lastName) ? (user.firstName + ' ' + user.lastName) : user.email,
672
+ });
673
+ }
674
+ }
675
+
676
+ structs.push(
677
+ AuditLogStruct.create({
678
+ ...log,
679
+ user: userStruct,
680
+ }),
681
+ );
682
+ }
683
+
684
+ return structs;
685
+ }
642
686
  }
@@ -30,8 +30,13 @@ export async function resumeEmails() {
30
30
  continue;
31
31
  }
32
32
 
33
- await ContextInstance.startForUser(user, organization, async () => {
34
- await email.send();
35
- });
33
+ try {
34
+ await ContextInstance.startForUser(user, organization, async () => {
35
+ await email.send();
36
+ });
37
+ }
38
+ catch (e) {
39
+ console.error('Error resuming email', email.id, e);
40
+ }
36
41
  }
37
42
  }
@@ -0,0 +1,77 @@
1
+ import { Migration } from '@simonbackx/simple-database';
2
+ import { Member } from '@stamhoofd/models';
3
+ import { MemberUserSyncer } from '../helpers/MemberUserSyncer';
4
+ import { logger } from '@simonbackx/simple-logging';
5
+
6
+ export default new Migration(async () => {
7
+ if (STAMHOOFD.environment == 'test') {
8
+ console.log('skipped in tests');
9
+ return;
10
+ }
11
+
12
+ if (STAMHOOFD.userMode !== 'platform') {
13
+ console.log('skipped seed update-membership because usermode not platform');
14
+ return;
15
+ }
16
+
17
+ process.stdout.write('\n');
18
+ let c = 0;
19
+ let id: string = '';
20
+
21
+ await logger.setContext({ tags: ['silent-seed', 'seed'] }, async () => {
22
+ while (true) {
23
+ const rawMembers = await Member.where({
24
+ id: {
25
+ value: id,
26
+ sign: '>',
27
+ },
28
+ }, { limit: 500, sort: ['id'] });
29
+
30
+ if (rawMembers.length === 0) {
31
+ break;
32
+ }
33
+
34
+ const promises: Promise<any>[] = [];
35
+
36
+ for (const member of rawMembers) {
37
+ promises.push((async () => {
38
+ const idR = '2b7d8f1c-ce67-4612-880b-fb1fb19affbb';
39
+ const valR = member.details.recordAnswers.get(idR);
40
+
41
+ const idRS = 'd381acce-9603-4246-af62-f3ea5292eec7';
42
+ const valRS = member.details.recordAnswers.get(idRS);
43
+
44
+ let save = false;
45
+
46
+ if (valR && !member.details.nationalRegisterNumber) {
47
+ member.details.nationalRegisterNumber = valR.stringValue;
48
+ save = true;
49
+ }
50
+
51
+ if (valRS && member.details.parents.length > 0 && !member.details.parents.find(p => p.nationalRegisterNumber)) {
52
+ member.details.parents[0].nationalRegisterNumber = valRS.stringValue;
53
+ save = true;
54
+ }
55
+
56
+ if (save) {
57
+ await member.save();
58
+ }
59
+ c++;
60
+
61
+ if (c % 1000 === 0) {
62
+ process.stdout.write('.');
63
+ }
64
+ if (c % 10000 === 0) {
65
+ process.stdout.write('\n');
66
+ }
67
+ })());
68
+ }
69
+
70
+ await Promise.all(promises);
71
+ id = rawMembers[rawMembers.length - 1].id;
72
+ }
73
+ });
74
+
75
+ // Do something here
76
+ return Promise.resolve();
77
+ });