@stamhoofd/models 2.120.6 → 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.
- package/dist/factories/RegistrationInvitationFactory.d.ts +15 -0
- package/dist/factories/RegistrationInvitationFactory.d.ts.map +1 -0
- package/dist/factories/RegistrationInvitationFactory.js +18 -0
- package/dist/factories/RegistrationInvitationFactory.js.map +1 -0
- package/dist/factories/index.d.ts +1 -0
- package/dist/factories/index.d.ts.map +1 -1
- package/dist/factories/index.js +1 -0
- package/dist/factories/index.js.map +1 -1
- package/dist/helpers/Handlebars.d.ts.map +1 -1
- package/dist/helpers/Handlebars.js +3 -0
- package/dist/helpers/Handlebars.js.map +1 -1
- package/dist/helpers/InvoiceCounter.d.ts +24 -0
- package/dist/helpers/InvoiceCounter.d.ts.map +1 -0
- package/dist/helpers/InvoiceCounter.js +133 -0
- package/dist/helpers/InvoiceCounter.js.map +1 -0
- package/dist/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
- package/dist/migrations/1763216320-bigint-balance-items.sql +5 -0
- package/dist/migrations/1763216320-bigint-orders.sql +2 -0
- package/dist/migrations/1763216320-bigint-payments.sql +2 -0
- package/dist/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
- package/dist/migrations/1776873089-create-registration-invitations-table.sql +13 -0
- package/dist/migrations/1778657958-payments-create-mandate.sql +2 -0
- package/dist/migrations/1778657959-payments-mandate-id.sql +2 -0
- package/dist/migrations/1778796615-payments-reversing-payment-id.sql +3 -0
- package/dist/migrations/1778950642-price-invoiced.sql +2 -0
- package/dist/models/BalanceItem.d.ts +9 -1
- package/dist/models/BalanceItem.d.ts.map +1 -1
- package/dist/models/BalanceItem.js +52 -5
- package/dist/models/BalanceItem.js.map +1 -1
- package/dist/models/Group.d.ts +4 -0
- package/dist/models/Group.d.ts.map +1 -1
- package/dist/models/Group.js +17 -0
- package/dist/models/Group.js.map +1 -1
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/Invoice.js +0 -8
- package/dist/models/Invoice.js.map +1 -1
- package/dist/models/MollieToken.d.ts +4 -8
- package/dist/models/MollieToken.d.ts.map +1 -1
- package/dist/models/MollieToken.js +37 -90
- package/dist/models/MollieToken.js.map +1 -1
- package/dist/models/Organization.d.ts +11 -2
- package/dist/models/Organization.d.ts.map +1 -1
- package/dist/models/Organization.js +27 -3
- package/dist/models/Organization.js.map +1 -1
- package/dist/models/Payment.d.ts +14 -1
- package/dist/models/Payment.d.ts.map +1 -1
- package/dist/models/Payment.js +23 -1
- package/dist/models/Payment.js.map +1 -1
- package/dist/models/Registration.d.ts +1 -0
- package/dist/models/Registration.d.ts.map +1 -1
- package/dist/models/Registration.js +1 -0
- package/dist/models/Registration.js.map +1 -1
- package/dist/models/RegistrationInvitation.d.ts +14 -0
- package/dist/models/RegistrationInvitation.d.ts.map +1 -0
- package/dist/models/RegistrationInvitation.js +45 -0
- package/dist/models/RegistrationInvitation.js.map +1 -0
- package/dist/models/User.d.ts +1 -1
- package/dist/models/User.d.ts.map +1 -1
- package/dist/models/User.js +1 -1
- package/dist/models/User.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -1
- package/package.json +11 -2
- package/src/factories/RegistrationInvitationFactory.ts +24 -0
- package/src/factories/index.ts +1 -0
- package/src/helpers/Handlebars.ts +4 -0
- package/src/helpers/InvoiceCounter.test.ts +220 -0
- package/src/helpers/InvoiceCounter.ts +162 -0
- package/src/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
- package/src/migrations/1763216320-bigint-balance-items.sql +5 -0
- package/src/migrations/1763216320-bigint-orders.sql +2 -0
- package/src/migrations/1763216320-bigint-payments.sql +2 -0
- package/src/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
- package/src/migrations/1776873089-create-registration-invitations-table.sql +13 -0
- package/src/migrations/1778657958-payments-create-mandate.sql +2 -0
- package/src/migrations/1778657959-payments-mandate-id.sql +2 -0
- package/src/migrations/1778796615-payments-reversing-payment-id.sql +3 -0
- package/src/migrations/1778950642-price-invoiced.sql +2 -0
- package/src/models/BalanceItem.ts +59 -5
- package/src/models/Group.ts +24 -3
- package/src/models/Invoice.ts +1 -9
- package/src/models/MollieToken.ts +42 -102
- package/src/models/Organization.ts +31 -3
- package/src/models/Payment.ts +21 -2
- package/src/models/Registration.ts +3 -2
- package/src/models/RegistrationInvitation.ts +40 -0
- package/src/models/User.ts +6 -7
- package/src/models/index.ts +1 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { column } from '@simonbackx/simple-database';
|
|
2
|
-
import type {
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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
|
|
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
|
}
|
package/src/models/Payment.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/models/User.ts
CHANGED
|
@@ -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.
|
|
152
|
-
SQL.jsonExtract(SQL.column('permissions'), '$.value.organizationPermissions'),
|
|
153
|
-
|
|
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
|
-
|
|
155
|
+
'=',
|
|
156
|
+
1,
|
|
158
157
|
)
|
|
159
158
|
;
|
|
160
159
|
|