@stamhoofd/backend 2.24.0 → 2.25.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.24.0",
3
+ "version": "2.25.0",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -36,14 +36,14 @@
36
36
  "@simonbackx/simple-encoding": "2.15.0",
37
37
  "@simonbackx/simple-endpoints": "1.14.0",
38
38
  "@simonbackx/simple-logging": "^1.0.1",
39
- "@stamhoofd/backend-i18n": "2.24.0",
40
- "@stamhoofd/backend-middleware": "2.24.0",
41
- "@stamhoofd/email": "2.24.0",
42
- "@stamhoofd/models": "2.24.0",
43
- "@stamhoofd/queues": "2.24.0",
44
- "@stamhoofd/sql": "2.24.0",
45
- "@stamhoofd/structures": "2.24.0",
46
- "@stamhoofd/utility": "2.24.0",
39
+ "@stamhoofd/backend-i18n": "2.25.0",
40
+ "@stamhoofd/backend-middleware": "2.25.0",
41
+ "@stamhoofd/email": "2.25.0",
42
+ "@stamhoofd/models": "2.25.0",
43
+ "@stamhoofd/queues": "2.25.0",
44
+ "@stamhoofd/sql": "2.25.0",
45
+ "@stamhoofd/structures": "2.25.0",
46
+ "@stamhoofd/utility": "2.25.0",
47
47
  "archiver": "^7.0.1",
48
48
  "aws-sdk": "^2.885.0",
49
49
  "axios": "1.6.8",
@@ -60,5 +60,5 @@
60
60
  "postmark": "4.0.2",
61
61
  "stripe": "^16.6.0"
62
62
  },
63
- "gitHead": "bad7d2adfa412af0b0de101274e46ed0b539ae38"
63
+ "gitHead": "48963030dd9e90bef4ea1c96db2cf6ddb6e1908a"
64
64
  }
package/src/crons.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { Database } from '@simonbackx/simple-database';
2
2
  import { logger, StyledText } from "@simonbackx/simple-logging";
3
- import { I18n } from '@stamhoofd/backend-i18n';
4
3
  import { Email, EmailAddress } from '@stamhoofd/email';
5
- import { Group, Organization, Payment, Registration, STPackage, STPendingInvoice, Webshop } from '@stamhoofd/models';
6
- import { QueueHandler } from '@stamhoofd/queues';
4
+ import { Group, Organization, Payment, Registration, STPackage, Webshop } from '@stamhoofd/models';
7
5
  import { PaymentMethod, PaymentProvider, PaymentStatus } from '@stamhoofd/structures';
8
6
  import { Formatter, sleep } from '@stamhoofd/utility';
9
7
  import AWS from 'aws-sdk';
@@ -123,9 +121,7 @@ async function checkWebshopDNS() {
123
121
  console.log("[DNS] Checking webshop DNS...")
124
122
 
125
123
  for (const webshop of webshops) {
126
- if (STAMHOOFD.environment === "production" || true) {
127
- console.log("[DNS] Webshop "+webshop.meta.name+" ("+webshop.id+")"+" ("+webshop.domain+")")
128
- }
124
+ console.log("[DNS] Webshop "+webshop.meta.name+" ("+webshop.id+")"+" ("+webshop.domain+")")
129
125
  await webshop.updateDNSRecords()
130
126
  }
131
127
 
@@ -133,10 +129,10 @@ async function checkWebshopDNS() {
133
129
  }
134
130
 
135
131
  async function checkReplies() {
136
- if (STAMHOOFD.environment !== "production") {
137
- return;
132
+ if (STAMHOOFD.environment !== "production" || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
133
+ return
138
134
  }
139
-
135
+
140
136
  console.log("Checking replies from AWS SQS")
141
137
  const sqs = new AWS.SQS();
142
138
  const messages = await sqs.receiveMessage({ QueueUrl: "https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-email-forwarding", MaxNumberOfMessages: 10 }).promise()
@@ -261,7 +257,7 @@ async function checkPostmarkBounces() {
261
257
  }
262
258
 
263
259
  async function checkBounces() {
264
- if (STAMHOOFD.environment !== "production") {
260
+ if (STAMHOOFD.environment !== "production" || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
265
261
  return
266
262
  }
267
263
 
@@ -343,7 +339,7 @@ async function checkBounces() {
343
339
  }
344
340
 
345
341
  async function checkComplaints() {
346
- if (STAMHOOFD.environment !== "production") {
342
+ if (STAMHOOFD.environment !== "production" || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
347
343
  return
348
344
  }
349
345
 
@@ -396,11 +392,10 @@ async function checkComplaints() {
396
392
  console.error("[AWS COMPLAINTS] Received virus / fraud complaint!")
397
393
  console.error("[AWS COMPLAINTS]", complaint)
398
394
  if (STAMHOOFD.environment === "production") {
399
- Email.sendInternal({
400
- to: "simon@stamhoofd.be",
395
+ Email.sendWebmaster({
401
396
  subject: "Received a "+type+" email notification",
402
397
  text: "We received a "+type+" notification for an e-mail from the organization: "+organization?.name+". Please check and adjust if needed.\n"
403
- }, new I18n("nl", "BE"))
398
+ })
404
399
  }
405
400
  }
406
401
  } else {
@@ -1,13 +1,12 @@
1
1
  import { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
3
  import { SimpleError } from "@simonbackx/simple-errors";
4
- import { Email } from '@stamhoofd/email';
5
- import { PasswordToken, User } from '@stamhoofd/models';
6
- import { User as UserStruct,UserPermissions, UserWithMembers } from "@stamhoofd/structures";
4
+ import { PasswordToken, sendEmailTemplate, User } from '@stamhoofd/models';
5
+ import { EmailTemplateType, Recipient, Replacement, UserPermissions, User as UserStruct, UserWithMembers } from "@stamhoofd/structures";
7
6
  import { Formatter } from '@stamhoofd/utility';
8
7
 
9
- import { Context } from '../../helpers/Context';
10
8
  import { AuthenticatedStructures } from '../../helpers/AuthenticatedStructures';
9
+ import { Context } from '../../helpers/Context';
11
10
  type Params = Record<string, never>;
12
11
  type Query = undefined;
13
12
  type Body = UserStruct
@@ -95,11 +94,6 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
95
94
 
96
95
  await admin.save();
97
96
 
98
- const { from, replyTo } = {
99
- from: organization ? organization.getStrongEmail(request.i18n) : Email.getInternalEmailFor(request.i18n),
100
- replyTo: undefined
101
- }
102
-
103
97
  // Create a password token that is valid for 7 days
104
98
  const validUntil = new Date();
105
99
  validUntil.setTime(validUntil.getTime() + 7 * 24 * 3600 * 1000);
@@ -110,28 +104,46 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
110
104
  const name = organization?.name ?? request.i18n.t("shared.platformName")
111
105
  const what = organization ? `de vereniging ${name} op ${request.i18n.t("shared.platformName")}` : `${request.i18n.t("shared.platformName")}`
112
106
 
113
- if (admin.hasAccount()) {
114
- const url = "https://"+(STAMHOOFD.domains.dashboard ?? "stamhoofd.app")+"/"+request.i18n.locale;
115
-
116
- Email.send({
117
- from,
118
- replyTo,
119
- to: admin.getEmailTo(),
120
- subject: "✉️ Beheerder van "+name,
121
- type: "transactional",
122
- text: (admin.firstName ? "Dag "+admin.firstName : "Hallo") + `, \n\n${user.firstName ?? 'Iemand'} heeft je toegevoegd als beheerder van ${what}. Je kan inloggen met je bestaande account (${admin.email}) door te surfen naar:\n${url}\n\nDaar kan je jouw vereniging zoeken en aanklikken.\n\n----\n\nWeet je jouw wachtwoord niet meer? Dan kan je een nieuw wachtwoord instellen via de onderstaande link:\n`+recoveryUrl+"\n\nDeze link is geldig tot "+dateTime+".\n\nKen je deze vereniging niet? Dan kan je deze e-mail veilig negeren.\n\nMet vriendelijke groeten,\n"+request.i18n.t("shared.platformName")+"\n"
123
- });
124
- } else {
125
- // Send email
126
- Email.send({
127
- from,
128
- replyTo,
129
- to: admin.getEmailTo(),
130
- subject: "✉️ Uitnodiging beheerder van "+name,
131
- type: "transactional",
132
- text: (admin.firstName ? "Dag "+admin.firstName : "Hallo") + `, \n\n${user.firstName ?? 'Iemand'} heeft je uitgenodigd om beheerder te worden van ${what}. Je kan een account aanmaken door op de volgende link te klikken of door deze te kopiëren in de URL-balk van je browser:\n`+recoveryUrl+"\n\nDeze link is geldig tot "+dateTime+".\n\nKen je deze vereniging niet? Dan kan je deze e-mail veilig negeren.\n\nMet vriendelijke groeten,\n"+request.i18n.t("shared.platformName")+"\n"
133
- });
134
- }
107
+ const emailTo = admin.getEmailTo();
108
+ const email: string = typeof emailTo === 'string' ? emailTo : emailTo[0]?.email;
109
+
110
+ await sendEmailTemplate(organization, {
111
+ recipients: [
112
+ Recipient.create({
113
+ email,
114
+ replacements: [
115
+ Replacement.create({
116
+ token: 'greeting',
117
+ value: admin.firstName ? `Dag ${admin.firstName},` : 'Hallo!'
118
+ }),
119
+ Replacement.create({
120
+ token: 'resetUrl',
121
+ value: recoveryUrl
122
+ }),
123
+ Replacement.create({
124
+ token: 'platformOrOrganizationName',
125
+ value: what
126
+ }),
127
+ Replacement.create({
128
+ token: 'inviterName',
129
+ value: user.firstName ?? 'Iemand'
130
+ }),
131
+ Replacement.create({
132
+ token: 'validUntil',
133
+ value: dateTime
134
+ }),
135
+ Replacement.create({
136
+ token: 'email',
137
+ value: admin.email
138
+ })
139
+ ]
140
+ })
141
+ ],
142
+ template: {
143
+ type: admin.hasAccount() ? EmailTemplateType.AdminInvitation : EmailTemplateType.AdminInvitationNewUser
144
+ },
145
+ type: 'transactional'
146
+ });
135
147
 
136
148
  return new Response(
137
149
  await AuthenticatedStructures.userWithMembers(admin)
@@ -60,7 +60,7 @@ export class SignupEndpoint extends Endpoint<Params, Query, Body, ResponseBody>
60
60
  // Send an e-mail to say you already have an account + follow password forgot flow
61
61
  const recoveryUrl = await PasswordToken.getPasswordRecoveryUrl(user, organization, request.i18n)
62
62
  const { from, replyTo } = {
63
- from: (user.permissions || !organization ? Email.getInternalEmailFor(request.i18n) : organization.getStrongEmail(request.i18n)),
63
+ from: (user.permissions || !organization ? Email.getInternalEmailFor(request.i18n) : organization.getDefaultFrom(request.i18n)),
64
64
  replyTo: undefined
65
65
  }
66
66
 
@@ -107,19 +107,14 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
107
107
  return url;
108
108
  }).catch(async (error) => {
109
109
  if (sendEmail) {
110
- const builder = await getEmailBuilderForTemplate(organization, {
110
+ await sendEmailTemplate(null, {
111
111
  template: {
112
112
  type: EmailTemplateType.ExcelExportFailed
113
113
  },
114
114
  recipients: [
115
115
  user.createRecipient()
116
- ],
117
- from: Email.getInternalEmailFor(Context.i18n)
116
+ ]
118
117
  })
119
-
120
- if (builder) {
121
- Email.schedule(builder)
122
- }
123
118
  }
124
119
  throw error
125
120
  }),
@@ -83,11 +83,10 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
83
83
  try {
84
84
  limiter.track(organization.id, 1);
85
85
  } catch (e) {
86
- Email.sendInternal({
87
- to: "hallo@stamhoofd.be",
86
+ Email.sendWebmaster({
88
87
  subject: "[Limiet] Limiet bereikt voor aantal inschrijvingen",
89
88
  text: "Beste, \nDe limiet werd bereikt voor het aantal inschrijvingen per dag. \nVereniging: "+organization.id+" ("+organization.name+")" + "\n\n" + e.message + "\n\nStamhoofd"
90
- }, new I18n("nl", "BE"))
89
+ })
91
90
 
92
91
  throw new SimpleError({
93
92
  code: "too_many_emails_period",
@@ -95,11 +95,10 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
95
95
  try {
96
96
  limiter.track(organization.id, request.body.recipients.length);
97
97
  } catch (e) {
98
- Email.sendInternal({
99
- to: "hallo@stamhoofd.be",
98
+ Email.sendWebmaster({
100
99
  subject: "[Limiet] Limiet bereikt voor aantal e-mails",
101
100
  text: "Beste, \nDe limiet werd bereikt voor het aantal e-mails per dag. \nVereniging: "+organization.id+" ("+organization.name+")" + "\n\n" + e.message + "\n\nStamhoofd"
102
- }, new I18n("nl", "BE"))
101
+ })
103
102
 
104
103
  throw new SimpleError({
105
104
  code: "too_many_emails_period",
@@ -178,7 +177,7 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
178
177
  }
179
178
  })
180
179
 
181
- let from = organization.uri+"@stamhoofd.email";
180
+ let from = organization.getDefaultFrom(request.i18n, false, 'broadcast');
182
181
  let replyTo: string | undefined = sender.email;
183
182
 
184
183
  // Can we send from this e-mail or reply-to?
@@ -86,11 +86,10 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
86
86
  try {
87
87
  limiter.track(organization.id, 1);
88
88
  } catch (e) {
89
- Email.sendInternal({
90
- to: "hallo@stamhoofd.be",
89
+ Email.sendWebmaster({
91
90
  subject: "[Limiet] Limiet bereikt voor aantal bestellingen",
92
91
  text: "Beste, \nDe limiet werd bereikt voor het aantal bestellingen per dag. \nVereniging: "+organization.id+" ("+organization.name+")" + "\n\n" + e.message + "\n\nStamhoofd"
93
- }, new I18n("nl", "BE"))
92
+ })
94
93
 
95
94
  throw new SimpleError({
96
95
  code: "too_many_emails_period",
@@ -1,4 +1,4 @@
1
- import { EmailAddress, EmailInterfaceRecipient } from "@stamhoofd/email";
1
+ import { Email, EmailAddress, EmailInterfaceRecipient } from "@stamhoofd/email";
2
2
  import { Organization } from "@stamhoofd/models";
3
3
  import { Formatter } from "@stamhoofd/utility";
4
4
  import { simpleParser } from "mailparser";
@@ -26,32 +26,34 @@ export class ForwardHandler {
26
26
  }
27
27
 
28
28
  // Unsubscribe email?
29
- if (email && email?.startsWith("unsubscribe+") && email.endsWith('@stamhoofd.email')) {
30
- // Get id
31
- const id = email.substring("unsubscribe+".length, email.indexOf('@stamhoofd.email'))
32
- const model = await EmailAddress.getByID(id)
33
-
34
- if (model) {
35
- console.log('[Unsubscribe] Received an unsubscribe request for ' + model.email + ' from ' + from)
36
- if (model.unsubscribedAll) {
37
- // Ignore
38
- return;
39
- }
40
- model.unsubscribedAll = true
41
- await model.save()
42
- } else {
43
- console.error('[Unsubscribe] Received an unsubscribe request for unknown ID ' + id + ' from ' + from)
29
+ for (const domain of Object.values(STAMHOOFD.domains.defaultBroadcastEmail ?? {})) {
30
+ if (email && email?.startsWith("unsubscribe+") && email.endsWith('@' + domain)) {
31
+ // Get id
32
+ const id = email.substring("unsubscribe+".length, email.indexOf('@' + domain))
33
+ const model = await EmailAddress.getByID(id)
34
+
35
+ if (model) {
36
+ console.log('[Unsubscribe] Received an unsubscribe request for ' + model.email + ' from ' + from)
37
+ if (model.unsubscribedAll) {
38
+ // Ignore
39
+ return;
40
+ }
41
+ model.unsubscribedAll = true
42
+ await model.save()
43
+ } else {
44
+ console.error('[Unsubscribe] Received an unsubscribe request for unknown ID ' + id + ' from ' + from)
44
45
 
45
- // Forward
46
- return {
47
- from: "unsubscribe@stamhoofd.be",
48
- to: "hallo@stamhoofd.be",
49
- subject: "E-mail unsubscribe mislukt",
50
- text: "Beste,\n\nEr werd een unsubscribe gemeld op "+email+" die niet kon worden verwerkt. Gelieve dit na te kijken.\n\nStamhoofd"
51
- }
46
+ // Forward
47
+ return {
48
+ from: Email.getWebmasterFromEmail(),
49
+ to: Email.getWebmasterToEmail(),
50
+ subject: "E-mail unsubscribe mislukt",
51
+ text: "Beste,\n\nEr werd een unsubscribe gemeld op "+email+" die niet kon worden verwerkt. Gelieve dit na te kijken.\n\nStamhoofd"
52
+ }
52
53
 
54
+ }
55
+ return;
53
56
  }
54
- return;
55
57
  }
56
58
 
57
59
  if (receipt.spamVerdict.status != "PASS" || receipt.virusVerdict.status != "PASS" || !(receipt.spfVerdict.status == "PASS" || receipt.dkimVerdict.status == "PASS")) {
@@ -60,7 +62,7 @@ export class ForwardHandler {
60
62
  }
61
63
 
62
64
  // Send a new e-mail
63
- let defaultEmail: EmailInterfaceRecipient[]|string = "hallo@stamhoofd.be"
65
+ let defaultEmail: EmailInterfaceRecipient[]|string = Email.getWebmasterToEmail()
64
66
  let organizationEmails: EmailInterfaceRecipient[] = []
65
67
  const extraDescription = "Dit bericht werd verstuurd naar "+email+", en werd automatisch doorgestuurd naar alle beheerders. Stel in Stamhoofd de e-mailadressen in om ervoor te zorgen dat antwoorden naar een specifiek e-mailadres worden verstuurd."
66
68
 
@@ -76,7 +78,7 @@ export class ForwardHandler {
76
78
 
77
79
  // Send back to receiver without including the original message to avoid spam
78
80
  return {
79
- from: email ?? "unknown@stamhoofd.be",
81
+ from: email ?? Email.getWebmasterToEmail(),
80
82
  to: from,
81
83
  subject: "Ongeldig e-mailadres",
82
84
  text: "Beste,\n\nDe vereniging die je probeert te bereiken via "+email+" is helaas niet bereikbaar via dit e-mailadres. Dit e-mailadres wordt enkel gebruikt voor het versturen van automatische e-mails in naam van een vereniging. Probeer de vereniging te contacteren via een ander e-mailadres.\n\nBedankt."
@@ -116,7 +118,7 @@ export class ForwardHandler {
116
118
  }
117
119
 
118
120
  const options = {
119
- from: email ?? "unknown@stamhoofd.be",
121
+ from: email ?? Email.getWebmasterToEmail(),
120
122
  to: defaultEmail,
121
123
  replyTo: parsed.from?.text,
122
124
  subject: parsed.subject ?? "Doorgestuurd bericht",
@@ -137,4 +139,4 @@ export class ForwardHandler {
137
139
 
138
140
  return options
139
141
  }
140
- }
142
+ }
@@ -164,7 +164,7 @@ export class MemberUserSyncerStatic {
164
164
  let user = member.users.find(u => u.email.toLocaleLowerCase() === email.toLocaleLowerCase()) ?? await User.getForAuthentication(member.organizationId, email, {allowWithoutAccount: true})
165
165
 
166
166
  if (user) {
167
- console.log("Giving an existing user access to a member: " + user.id + ' - ' + member.id)
167
+ //console.log("Giving an existing user access to a member: " + user.id + ' - ' + member.id)
168
168
  if (!asParent) {
169
169
  if (user.memberId && user.memberId !== member.id) {
170
170
  console.error('Found conflicting user with multiple members', user.id, 'members', user.memberId, 'to', member.id)
@@ -1,9 +1,12 @@
1
1
 
2
- import { Organization, OrganizationRegistrationPeriod, RegistrationPeriod } from "@stamhoofd/models";
2
+ import { Member, MemberResponsibilityRecord, Organization, OrganizationRegistrationPeriod, Platform, RegistrationPeriod } from "@stamhoofd/models";
3
3
  import { AuthenticatedStructures } from "./AuthenticatedStructures";
4
4
  import { PatchOrganizationRegistrationPeriodsEndpoint } from "../endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint";
5
5
  import { QueueHandler } from "@stamhoofd/queues";
6
6
  import { SetupStepUpdater } from "./SetupStepsUpdater";
7
+ import { PermissionLevel } from "@stamhoofd/structures";
8
+ import { MemberUserSyncer } from "./MemberUserSyncer";
9
+ import { SimpleError } from "@simonbackx/simple-errors";
7
10
 
8
11
  export class PeriodHelper {
9
12
  static async moveOrganizationToPeriod(organization: Organization, period: RegistrationPeriod) {
@@ -14,6 +17,89 @@ export class PeriodHelper {
14
17
  await organization.save()
15
18
  }
16
19
 
20
+ static async stopAllResponsibilities() {
21
+ console.log('Stopping all responsibilities')
22
+ const platform = await Platform.getSharedPrivateStruct()
23
+ const keepPlatformResponsibilityIds = platform.config.responsibilities.filter(r => !r.organizationBased).map(r => r.id)
24
+ const keepResponsibilityIds = platform.config.responsibilities.filter(r => !r.organizationBased || r.permissions?.level === PermissionLevel.Full).map(r => r.id)
25
+ const batchSize = 100;
26
+
27
+ let lastId = "";
28
+ let c = 0;
29
+
30
+ while (true) {
31
+ const records = await MemberResponsibilityRecord.where(
32
+ {
33
+ id: { sign: ">", value: lastId },
34
+ endDate: null
35
+ },
36
+ {
37
+ limit: batchSize,
38
+ sort: ["id"]
39
+ }
40
+ );
41
+
42
+ for (const record of records) {
43
+ lastId = record.id;
44
+
45
+ const invalid = keepPlatformResponsibilityIds.includes(record.responsibilityId) && record.organizationId
46
+
47
+ if (!keepResponsibilityIds.includes(record.responsibilityId) || invalid) {
48
+ record.endDate = new Date()
49
+ await record.save()
50
+ c++;
51
+ }
52
+ }
53
+
54
+ if (records.length < batchSize) {
55
+ break;
56
+ }
57
+
58
+ }
59
+
60
+ console.log('Done: stopped all responsibilities: ' + c)
61
+ }
62
+
63
+ static async syncAllMemberUsers() {
64
+ console.log('Syncing all members')
65
+
66
+ let c = 0;
67
+ let lastId: string = '';
68
+
69
+ while(true) {
70
+ const rawMembers = await Member.where({
71
+ id: {
72
+ value: lastId,
73
+ sign: '>'
74
+ }
75
+ }, {limit: 500, sort: ['id']});
76
+
77
+ if (rawMembers.length === 0) {
78
+ break;
79
+ }
80
+
81
+ const membersWithRegistrations = await Member.getBlobByIds(...rawMembers.map(m => m.id));
82
+
83
+ const promises: Promise<any>[] = [];
84
+
85
+ for (const memberWithRegistrations of membersWithRegistrations) {
86
+ promises.push((async () => {
87
+ await MemberUserSyncer.onChangeMember(memberWithRegistrations);
88
+ c++;
89
+
90
+ if (c%10000 === 0) {
91
+ console.log('Synced ' + c + ' members');
92
+ }
93
+ })());
94
+ }
95
+
96
+ await Promise.all(promises);
97
+ lastId = rawMembers[rawMembers.length - 1].id;
98
+ }
99
+
100
+ console.log('Done: synced all members: ' + c)
101
+ }
102
+
17
103
  static async createOrganizationPeriodForPeriod(organization: Organization, period: RegistrationPeriod) {
18
104
  const oPeriods = await OrganizationRegistrationPeriod.where({ periodId: period.id, organizationId: organization.id }, {limit: 1})
19
105
 
@@ -35,9 +121,14 @@ export class PeriodHelper {
35
121
 
36
122
  static async moveAllOrganizationsToPeriod(period: RegistrationPeriod) {
37
123
  const tag = "moveAllOrganizationsToPeriod";
38
- const batchSize = 10;
39
- QueueHandler.cancel(tag);
124
+ if (QueueHandler.isRunning(tag)) {
125
+ throw new SimpleError({
126
+ code: 'move_period_pending',
127
+ message: 'Er is al een jaarovergang bezig. Wacht tot deze klaar is.'
128
+ })
129
+ }
40
130
 
131
+ const batchSize = 10;
41
132
  await QueueHandler.schedule(tag, async () => {
42
133
  let lastId = "";
43
134
 
@@ -62,9 +153,14 @@ export class PeriodHelper {
62
153
  }
63
154
 
64
155
  }
156
+
157
+ await this.stopAllResponsibilities()
158
+ await this.syncAllMemberUsers()
65
159
  });
66
160
 
67
161
  // When done: update setup steps
68
162
  await SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod()
69
163
  }
164
+
165
+
70
166
  }
@@ -151,9 +151,7 @@ export class SetupStepUpdater {
151
151
  ) {
152
152
  const setupSteps = organizationRegistrationPeriod.setupSteps;
153
153
 
154
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
155
154
  for (const stepType of Object.values(SetupStepType)) {
156
- console.log(`[STEP TYPE] ${stepType}`);
157
155
  const operation = this.STEP_TYPE_OPERATIONS[stepType];
158
156
  await operation(setupSteps, organization, platform);
159
157
  }