@stamhoofd/models 2.120.5 → 2.121.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.
Files changed (90) hide show
  1. package/dist/factories/RegistrationInvitationFactory.d.ts +15 -0
  2. package/dist/factories/RegistrationInvitationFactory.d.ts.map +1 -0
  3. package/dist/factories/RegistrationInvitationFactory.js +18 -0
  4. package/dist/factories/RegistrationInvitationFactory.js.map +1 -0
  5. package/dist/factories/index.d.ts +1 -0
  6. package/dist/factories/index.d.ts.map +1 -1
  7. package/dist/factories/index.js +1 -0
  8. package/dist/factories/index.js.map +1 -1
  9. package/dist/helpers/Handlebars.d.ts.map +1 -1
  10. package/dist/helpers/Handlebars.js +3 -0
  11. package/dist/helpers/Handlebars.js.map +1 -1
  12. package/dist/helpers/InvoiceCounter.d.ts +24 -0
  13. package/dist/helpers/InvoiceCounter.d.ts.map +1 -0
  14. package/dist/helpers/InvoiceCounter.js +133 -0
  15. package/dist/helpers/InvoiceCounter.js.map +1 -0
  16. package/dist/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
  17. package/dist/migrations/1763216320-bigint-balance-items.sql +5 -0
  18. package/dist/migrations/1763216320-bigint-orders.sql +2 -0
  19. package/dist/migrations/1763216320-bigint-payments.sql +2 -0
  20. package/dist/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
  21. package/dist/migrations/1776873089-create-registration-invitations-table.sql +13 -0
  22. package/dist/migrations/1778657958-payments-create-mandate.sql +2 -0
  23. package/dist/migrations/1778657959-payments-mandate-id.sql +2 -0
  24. package/dist/migrations/1778796615-payments-reversing-payment-id.sql +3 -0
  25. package/dist/migrations/1778950642-price-invoiced.sql +2 -0
  26. package/dist/models/BalanceItem.d.ts +9 -1
  27. package/dist/models/BalanceItem.d.ts.map +1 -1
  28. package/dist/models/BalanceItem.js +52 -5
  29. package/dist/models/BalanceItem.js.map +1 -1
  30. package/dist/models/Group.d.ts +4 -0
  31. package/dist/models/Group.d.ts.map +1 -1
  32. package/dist/models/Group.js +17 -0
  33. package/dist/models/Group.js.map +1 -1
  34. package/dist/models/Invoice.d.ts.map +1 -1
  35. package/dist/models/Invoice.js +0 -8
  36. package/dist/models/Invoice.js.map +1 -1
  37. package/dist/models/MollieToken.d.ts +4 -8
  38. package/dist/models/MollieToken.d.ts.map +1 -1
  39. package/dist/models/MollieToken.js +37 -90
  40. package/dist/models/MollieToken.js.map +1 -1
  41. package/dist/models/Organization.d.ts +11 -2
  42. package/dist/models/Organization.d.ts.map +1 -1
  43. package/dist/models/Organization.js +27 -3
  44. package/dist/models/Organization.js.map +1 -1
  45. package/dist/models/Payment.d.ts +14 -1
  46. package/dist/models/Payment.d.ts.map +1 -1
  47. package/dist/models/Payment.js +23 -1
  48. package/dist/models/Payment.js.map +1 -1
  49. package/dist/models/Registration.d.ts +1 -0
  50. package/dist/models/Registration.d.ts.map +1 -1
  51. package/dist/models/Registration.js +1 -0
  52. package/dist/models/Registration.js.map +1 -1
  53. package/dist/models/RegistrationInvitation.d.ts +14 -0
  54. package/dist/models/RegistrationInvitation.d.ts.map +1 -0
  55. package/dist/models/RegistrationInvitation.js +45 -0
  56. package/dist/models/RegistrationInvitation.js.map +1 -0
  57. package/dist/models/User.d.ts +1 -1
  58. package/dist/models/User.d.ts.map +1 -1
  59. package/dist/models/User.js +1 -1
  60. package/dist/models/User.js.map +1 -1
  61. package/dist/models/index.d.ts +1 -0
  62. package/dist/models/index.d.ts.map +1 -1
  63. package/dist/models/index.js +1 -0
  64. package/dist/models/index.js.map +1 -1
  65. package/package.json +11 -2
  66. package/src/factories/RegistrationInvitationFactory.ts +24 -0
  67. package/src/factories/index.ts +1 -0
  68. package/src/helpers/Handlebars.ts +4 -0
  69. package/src/helpers/InvoiceCounter.test.ts +220 -0
  70. package/src/helpers/InvoiceCounter.ts +162 -0
  71. package/src/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
  72. package/src/migrations/1763216320-bigint-balance-items.sql +5 -0
  73. package/src/migrations/1763216320-bigint-orders.sql +2 -0
  74. package/src/migrations/1763216320-bigint-payments.sql +2 -0
  75. package/src/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
  76. package/src/migrations/1776873089-create-registration-invitations-table.sql +13 -0
  77. package/src/migrations/1778657958-payments-create-mandate.sql +2 -0
  78. package/src/migrations/1778657959-payments-mandate-id.sql +2 -0
  79. package/src/migrations/1778796615-payments-reversing-payment-id.sql +3 -0
  80. package/src/migrations/1778950642-price-invoiced.sql +2 -0
  81. package/src/models/BalanceItem.ts +59 -5
  82. package/src/models/Group.ts +24 -3
  83. package/src/models/Invoice.ts +1 -9
  84. package/src/models/MollieToken.ts +42 -102
  85. package/src/models/Organization.ts +31 -3
  86. package/src/models/Payment.ts +21 -2
  87. package/src/models/Registration.ts +3 -2
  88. package/src/models/RegistrationInvitation.ts +40 -0
  89. package/src/models/User.ts +6 -7
  90. package/src/models/index.ts +1 -0
@@ -1,8 +1,7 @@
1
1
  import { column } from '@simonbackx/simple-database';
2
- import type { PartialWithoutMethods, PlainObject } from '@simonbackx/simple-encoding';
2
+ import type { PlainObject } from '@simonbackx/simple-encoding';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { QueryableModel } from '@stamhoofd/sql';
5
- import { MollieOnboarding, MollieProfile, MollieStatus } from '@stamhoofd/structures';
6
5
  import type { IncomingMessage } from 'http';
7
6
  import https from 'https';
8
7
 
@@ -12,9 +11,17 @@ function sleep(ms: number) {
12
11
  return new Promise(resolve => setTimeout(resolve, ms));
13
12
  }
14
13
 
14
+ export class MollieError extends Error {
15
+ body: PlainObject
16
+ }
17
+
15
18
  export class MollieToken extends QueryableModel {
16
19
  static table = 'mollie_tokens';
17
20
 
21
+ get id() {
22
+ return this.organizationId
23
+ }
24
+
18
25
  @column({ primary: true, type: 'string' })
19
26
  organizationId!: string;
20
27
 
@@ -184,7 +191,9 @@ export class MollieToken extends QueryableModel {
184
191
 
185
192
  if (response.statusCode < 200 || response.statusCode >= 300) {
186
193
  console.error(body);
187
- reject(new Error(response.statusCode + ' ' + response.statusMessage));
194
+ const e = new MollieError(response.statusCode + ' ' + response.statusMessage);
195
+ e.body = json;
196
+ reject(e);
188
197
  return;
189
198
  }
190
199
 
@@ -223,23 +232,38 @@ export class MollieToken extends QueryableModel {
223
232
  * Refresh the token itself, without generating a new token. Everyone who had the token has a new token now
224
233
  */
225
234
  async refresh(): Promise<void> {
226
- const data = await MollieToken.request('POST', '/oauth2/tokens', {
227
- grant_type: 'refresh_token',
228
- refresh_token: this.refreshToken,
229
- }, 'urlencoded');
235
+ try {
236
+ const data = await MollieToken.request('POST', '/oauth2/tokens', {
237
+ grant_type: 'refresh_token',
238
+ refresh_token: this.refreshToken,
239
+ }, 'urlencoded');
230
240
 
231
- if (data && data.access_token && data.refresh_token) {
232
- // Delete token if exisitng
233
- this.refreshToken = data.refresh_token;
234
- this.accessToken = data.access_token;
235
- this.expiresOn = new Date(new Date().getTime() + 3600 * 1000 - 60 * 1000);
236
- await this.save();
237
-
238
- // Update shared tokens if this object was fetched directly
239
- MollieToken.knownTokens.set(this.organizationId, this);
240
- return;
241
+ if (data && data.access_token && data.refresh_token) {
242
+ // Delete token if exisitng
243
+ this.refreshToken = data.refresh_token;
244
+ this.accessToken = data.access_token;
245
+ this.expiresOn = new Date(new Date().getTime() + 3600 * 1000 - 60 * 1000);
246
+ await this.save();
247
+
248
+ // Update shared tokens if this object was fetched directly
249
+ MollieToken.knownTokens.set(this.organizationId, this);
250
+ return;
251
+ }
252
+ throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
253
+ } catch (e) {
254
+ if (e instanceof MollieError) {
255
+ if (e.body && typeof e.body === 'object' && 'error' in e.body && e.body.error === 'invalid_grant') {
256
+ // Refresh token not valid.
257
+ console.error('Deleting Mollie Token because invalid refresh token')
258
+ await this.delete()
259
+
260
+ // Still throw
261
+ throw e;
262
+ }
263
+ }
264
+ throw e;
241
265
  }
242
- throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
266
+
243
267
  }
244
268
 
245
269
  /**
@@ -279,97 +303,13 @@ export class MollieToken extends QueryableModel {
279
303
 
280
304
  this.knownTokens.set(organization.id, token);
281
305
 
282
- organization.privateMeta.mollieOnboarding = await token.getOnboardingStatus();
283
- await token.setup(organization);
284
-
285
- await organization.save();
286
306
  return token;
287
307
  }
288
308
  throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
289
309
  }
290
310
 
291
- async getOnboardingStatus() {
292
- try {
293
- const response = await this.authRequest('GET', '/v2/onboarding/me');
294
- return MollieOnboarding.create({
295
- canReceivePayments: !!response.canReceivePayments,
296
- canReceiveSettlements: !!response.canReceiveSettlements,
297
- status: response.status === 'needs-data' ? MollieStatus.NeedsData : (response.status === 'in-review' ? MollieStatus.InReview : (MollieStatus.Completed)),
298
- });
299
- }
300
- catch (e) {
301
- console.error('Error when requesting Mollie onboarding status:');
302
- console.error(e);
303
- return null;
304
- }
305
- }
306
-
307
- async getProfiles(): Promise<MollieProfile[]> {
308
- try {
309
- const response = await this.authRequest('GET', '/v2/profiles?limit=250');
310
- const profiles = response._embedded.profiles as PartialWithoutMethods<MollieProfile>[];
311
- return profiles.map(p => MollieProfile.create(p));
312
- }
313
- catch (e) {
314
- console.error('Failed to parse mollie profiles', e);
315
- return [];
316
- }
317
- }
318
-
319
- async getProfileId(website?: string): Promise<string | null> {
320
- const response = await this.authRequest('GET', '/v2/profiles?limit=250');
321
- const profiles = response._embedded.profiles;
322
-
323
- // Search profile with Stamhoofd as name
324
- if (website) {
325
- for (const profile of profiles) {
326
- if (profile.website.toLowerCase().includes(website)) {
327
- return profile.id;
328
- }
329
- }
330
- }
331
-
332
- // Search profile with Stamhoofd as name
333
- for (const profile of profiles) {
334
- if (profile.name.toLowerCase().includes('stamhoofd')) {
335
- return profile.id;
336
- }
337
- }
338
-
339
- return response._embedded.profiles[0]?.id ?? null;
340
- }
341
-
342
311
  async getOnboardingLink() {
343
312
  const response = await this.authRequest('GET', '/v2/onboarding/me');
344
313
  return response._links.dashboard.href ?? '';
345
314
  }
346
-
347
- /**
348
- * Set initial onboarding values + enable bancontact
349
- */
350
- async setup(organization: Organization) {
351
- // Submit onboarding data
352
-
353
- if (organization.privateMeta.mollieOnboarding && organization.privateMeta.mollieOnboarding.status == MollieStatus.NeedsData) {
354
- await this.authRequest('POST', '/v2/onboarding/me', {
355
- organization: {
356
- name: organization.name,
357
- address: {
358
- streetAndNumber: organization.address.street + ' ' + organization.address.number,
359
- postalCode: organization.address.postalCode,
360
- city: organization.address.city,
361
- country: organization.address.country,
362
- },
363
-
364
- vatRegulation: 'shifted',
365
- },
366
- profile: {
367
- name: organization.name + ' - Stamhoofd',
368
- // url: "https://"+organization.getHost(),
369
- description: $t(`%x1`),
370
- categoryCode: 8398,
371
- },
372
- });
373
- }
374
- }
375
315
  }
@@ -7,7 +7,7 @@ import type { EmailInterfaceRecipient } from '@stamhoofd/email';
7
7
  import { QueueHandler } from '@stamhoofd/queues';
8
8
  import { QueryableModel, SQL } from '@stamhoofd/sql';
9
9
  import type { OrganizationEmail, PrivatePaymentConfiguration } from '@stamhoofd/structures';
10
- import { Address, appToUri, DNSRecordStatus, EmailTemplateType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
10
+ import { Address, appToUri, Company, DNSRecordStatus, EmailTemplateType, GroupType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
11
11
  import { Formatter } from '@stamhoofd/utility';
12
12
  import { v4 as uuidv4 } from 'uuid';
13
13
  import { validateDNSRecords } from '../helpers/DNSValidator.js';
@@ -17,6 +17,8 @@ import { OrganizationRegistrationPeriod } from './OrganizationRegistrationPeriod
17
17
  import { StripeAccount } from './StripeAccount.js';
18
18
  import { Country } from '@stamhoofd/types/Country';
19
19
  import { Language } from '@stamhoofd/types/Language';
20
+ import { Registration } from './Registration.js';
21
+ import type { PaymentMandate } from '@stamhoofd/structures/PaymentMandate.js';
20
22
 
21
23
  export class Organization extends QueryableModel {
22
24
  static table = 'organizations';
@@ -238,6 +240,10 @@ export class Organization extends QueryableModel {
238
240
  return this.uri + '.' + defaultDomain;
239
241
  }
240
242
 
243
+ getDashboardHost(i18n?: { language: Language; locale: string }): string {
244
+ return STAMHOOFD.domains.dashboard + '/' + (i18n?.locale ?? this.i18n.locale) + '/' + appToUri('dashboard') + '/' + this.uri;
245
+ }
246
+
241
247
  get registerUrl() {
242
248
  return 'https://' + this.getRegistrationHost();
243
249
  }
@@ -913,10 +919,17 @@ export class Organization extends QueryableModel {
913
919
  };
914
920
  }
915
921
 
916
- async getPaymentProviderFor(method: PaymentMethod, config: PrivatePaymentConfiguration): Promise<{
922
+ async getPaymentProviderFor(method: PaymentMethod, mandate: PaymentMandate | null, config: PrivatePaymentConfiguration): Promise<{
917
923
  provider: PaymentProvider | null;
918
924
  stripeAccount: StripeAccount | null;
919
925
  }> {
926
+ if (mandate) {
927
+ return {
928
+ provider: mandate.provider,
929
+ stripeAccount: null
930
+ }
931
+ }
932
+
920
933
  let stripeAccount = (config.stripeAccountId ? (await StripeAccount.getByID(config.stripeAccountId)) : null) ?? null;
921
934
  if (stripeAccount && stripeAccount.organizationId !== this.id) {
922
935
  console.warn('Stripe account ' + stripeAccount.id + ' is not linked to organization ' + this.id);
@@ -984,7 +997,7 @@ export class Organization extends QueryableModel {
984
997
  return 0;
985
998
  }
986
999
 
987
- return await this.select()
1000
+ return await Registration.select()
988
1001
  .join(
989
1002
  SQL.join(Group.table)
990
1003
  .where(SQL.column('id'), SQL.parentColumn('groupId')),
@@ -993,8 +1006,23 @@ export class Organization extends QueryableModel {
993
1006
  .where('deactivatedAt', null)
994
1007
  .where('registeredAt', '!=', null)
995
1008
  .where(SQL.column(Group.table, 'deletedAt'), null)
1009
+ .where(SQL.column(Group.table, 'type'), '!=', GroupType.WaitingList)
996
1010
  .count(
997
1011
  SQL.distinct(SQL.column('memberId')),
998
1012
  );
999
1013
  }
1014
+
1015
+ /**
1016
+ * Assures at least one company at all times
1017
+ */
1018
+ get defaultCompanies() {
1019
+ return this.meta.companies.length
1020
+ ? this.meta.companies
1021
+ : [
1022
+ Company.create({
1023
+ name: this.name,
1024
+ address: this.address,
1025
+ }),
1026
+ ];
1027
+ }
1000
1028
  }
@@ -1,10 +1,11 @@
1
1
  import { column } from '@simonbackx/simple-database';
2
- import type { PaymentMethod, PaymentStatus} from '@stamhoofd/structures';
2
+ import type { PaymentMethod, PaymentStatus } from '@stamhoofd/structures';
3
3
  import { BalanceItemDetailed, BalanceItemPaymentDetailed, BaseOrganization, PaymentCustomer, PaymentGeneral, PaymentProvider, PaymentType, Settlement, TransferSettings } from '@stamhoofd/structures';
4
4
  import { Formatter } from '@stamhoofd/utility';
5
5
  import { v4 as uuidv4 } from 'uuid';
6
6
 
7
7
  import { QueryableModel } from '@stamhoofd/sql';
8
+ import { CreateMandateSettings } from '@stamhoofd/structures/checkout/CreateMandateSettings.js';
8
9
  import type { BalanceItem } from './BalanceItem.js';
9
10
  import type { BalanceItemPayment } from './BalanceItemPayment.js';
10
11
  import { Organization } from './Organization.js';
@@ -52,6 +53,19 @@ export class Payment extends QueryableModel {
52
53
  @column({ type: 'string', nullable: true })
53
54
  payingUserId: string | null = null;
54
55
 
56
+ /**
57
+ * External ID of the mandate used for this payment
58
+ */
59
+ @column({ type: 'string', nullable: true })
60
+ mandateId: string | null = null;
61
+
62
+ /**
63
+ * Link to related payment that was reversed.
64
+ * Used for refunds and chargebacks
65
+ */
66
+ @column({ type: 'string', nullable: true })
67
+ reversingPaymentId: string | null = null;
68
+
55
69
  /**
56
70
  * @deprecated
57
71
  */
@@ -75,6 +89,9 @@ export class Payment extends QueryableModel {
75
89
  @column({ type: 'string', nullable: true })
76
90
  stripeAccountId: string | null = null;
77
91
 
92
+ @column({ type: 'json', decoder: CreateMandateSettings, nullable: true })
93
+ createMandate: CreateMandateSettings | null = null;
94
+
78
95
  /**
79
96
  * Total price
80
97
  */
@@ -84,10 +101,12 @@ export class Payment extends QueryableModel {
84
101
  /**
85
102
  * The difference between the sum of the balance item payments price and the price of the payment, caused by rounding to 1 cent.
86
103
  * This cannot be >= 100 (= 0,01 euro) or <= -100 (=-0,01 euro)
104
+ *
105
+ * For understanding the sign of the value, regard it as an extra balance item to the payment.
87
106
  *
88
107
  * Just like all prices, this price is stored per ten thousand (1 = 0,0001 ). Storing smaller units is not possible because even in balance items, the price to pay cannot be smaller than 0,0001 euro
89
108
  *
90
- * E.g. total price to pay is 0,242 because of VAT, then we round this to 0,24. The roundingAmount will be -0,002 in this case.
109
+ * E.g. total price to pay is 0,242 because of VAT, then we round this to 0,24. The roundingAmount will be -0,002 in this case, because all the items (0,242) - 0,002 = 0,24.
91
110
  */
92
111
  @column({ type: 'integer' })
93
112
  roundingAmount = 0;
@@ -1,12 +1,12 @@
1
1
  import type { ManyToOneRelation } from '@simonbackx/simple-database';
2
2
  import { column } from '@simonbackx/simple-database';
3
- import type { RecordAnswer} from '@stamhoofd/structures';
3
+ import type { RecordAnswer } from '@stamhoofd/structures';
4
4
  import { AppliedRegistrationDiscount, GroupPrice, RecordAnswerDecoder, RegisterItemOption, Registration as RegistrationStructure, StockReservation } from '@stamhoofd/structures';
5
5
  import { v4 as uuidv4 } from 'uuid';
6
6
 
7
7
  import { ArrayDecoder, MapDecoder, StringDecoder } from '@simonbackx/simple-encoding';
8
8
  import { QueryableModel } from '@stamhoofd/sql';
9
- import type {Group} from './Group.js';
9
+ import type { Group } from './Group.js';
10
10
 
11
11
  export class Registration extends QueryableModel {
12
12
  static table = 'registrations';
@@ -117,6 +117,7 @@ export class Registration extends QueryableModel {
117
117
  waitingList = false;
118
118
 
119
119
  /**
120
+ * @deprecated - use RegistrationInvitation instead
120
121
  * When a registration is on the waiting list or is invite only, set this to true to allow the user to
121
122
  * register normally.
122
123
  */
@@ -0,0 +1,40 @@
1
+ import { column } from '@simonbackx/simple-database';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+
4
+ import { QueryableModel } from '@stamhoofd/sql';
5
+
6
+ /**
7
+ * Invitation to register for a group. If an invitation exists the member can always register even if he does not meet the requirements of the group.
8
+ * Used for allowing members who are on a waiting list to register for a group.
9
+ */
10
+ export class RegistrationInvitation extends QueryableModel {
11
+ static table = 'registration_invitations';
12
+
13
+ @column({
14
+ primary: true, type: 'string', beforeSave(value) {
15
+ return value ?? uuidv4();
16
+ },
17
+ })
18
+ id: string;
19
+
20
+ @column({ type: 'string' })
21
+ groupId: string;
22
+
23
+ @column({ type: 'string' })
24
+ memberId: string;
25
+
26
+ @column({ type: 'string' })
27
+ organizationId: string;
28
+
29
+ @column({
30
+ type: 'datetime', beforeSave(old?: any) {
31
+ if (old !== undefined) {
32
+ return old;
33
+ }
34
+ const date = new Date();
35
+ date.setMilliseconds(0);
36
+ return date;
37
+ },
38
+ })
39
+ createdAt: Date;
40
+ }
@@ -141,20 +141,19 @@ export class User extends QueryableModel {
141
141
  return global;
142
142
  }
143
143
 
144
- static async getAdmins(organizationId: string, options?: { verified?: boolean }) {
144
+ static async getAdmins(organizationId: string | string[], options?: { verified?: boolean }) {
145
145
  if (STAMHOOFD.userMode === 'platform') {
146
146
  // Custom implementation
147
147
  const q = User.select()
148
148
  .where('organizationId', null)
149
149
  .where('permissions', '!=', null)
150
150
  .where(
151
- SQL.jsonExtract(
152
- SQL.jsonExtract(SQL.column('permissions'), '$.value.organizationPermissions'),
153
- '$."' + organizationId + '"',
154
- true,
151
+ SQL.jsonOverlaps(
152
+ SQL.jsonKeys(SQL.jsonExtract(SQL.column('permissions'), '$.value.organizationPermissions')),
153
+ SQL.cast(SQL.scalar(JSON.stringify(Array.isArray(organizationId) ? organizationId : [organizationId])), 'JSON'),
155
154
  ),
156
- '!=',
157
- null,
155
+ '=',
156
+ 1,
158
157
  )
159
158
  ;
160
159
 
@@ -61,5 +61,6 @@ export * from './UitpasClientCredential.js';
61
61
 
62
62
  export * from './Invoice.js';
63
63
  export * from './InvoicedBalanceItem.js';
64
+ export * from './RegistrationInvitation.js';
64
65
 
65
66
  import './_relations.js';