@stamhoofd/backend 2.143.1 → 2.145.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 +19 -19
- package/src/crons/fake-settlements.test.ts +13 -11
- package/src/crons/fake-settlements.ts +22 -38
- package/src/crons/mollie-chargebacks.ts +31 -29
- package/src/crons/mollie-refunds.ts +61 -59
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +16 -5
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +210 -4
- package/src/endpoints/auth/CreateTokenEndpoint.ts +9 -20
- package/src/endpoints/auth/DeleteTokenEndpoint.test.ts +29 -0
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +2 -1
- package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -11
- package/src/endpoints/auth/MFA.security.test.ts +9 -7
- package/src/endpoints/auth/MFA.test.ts +42 -15
- package/src/endpoints/auth/PatchUserEndpoint.ts +3 -2
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +5 -4
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +4 -3
- package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +6 -5
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +5 -4
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +5 -4
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +2 -1
- package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +2 -1
- package/src/endpoints/global/files/UploadFile.test.ts +2 -1
- package/src/endpoints/global/files/UploadFile.ts +53 -25
- package/src/endpoints/global/files/UploadImage.test.ts +2 -1
- package/src/endpoints/global/files/UploadImage.ts +32 -6
- package/src/endpoints/global/files/upload-security.test.ts +3 -2
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +4 -3
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +26 -25
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +57 -56
- package/src/endpoints/global/members/SendMemberSecurityCodeEndpoint.test.ts +4 -3
- package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +6 -4
- package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +6 -6
- package/src/endpoints/global/platform/SignOutPlatformAdminsEndpoint.test.ts +9 -8
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +14 -13
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +24 -23
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +4 -3
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +2 -1
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +2 -1
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +20 -19
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemBreakdownEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +3 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +4 -3
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +5 -4
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +5 -4
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +0 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +23 -22
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/payments/GetPaymentBreakdownEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.test.ts +4 -3
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +5 -4
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +7 -6
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +9 -8
- package/src/endpoints/organization/dashboard/settlements/SettlementsExportEndpoint.test.ts +3 -2
- package/src/endpoints/organization/dashboard/settlements/SettlementsSyncEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +5 -4
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +2 -1
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.test.ts +3 -2
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +6 -5
- package/src/endpoints/organization/dashboard/users/SignOutOrganizationAdminsEndpoint.test.ts +16 -15
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.test.ts +2 -1
- package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +2 -1
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +4 -3
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -5
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +94 -8
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +18 -18
- package/src/helpers/ApplicationFeeInvoicer.ts +1 -1
- package/src/helpers/Context.ts +14 -21
- package/src/helpers/ContextStorage.ts +5 -0
- package/src/helpers/MollieSettlementSync.test.ts +94 -41
- package/src/helpers/MollieSettlementSync.ts +174 -78
- package/src/helpers/StripeSettlementSync.test.ts +0 -85
- package/src/helpers/StripeSettlementSync.ts +47 -70
- package/src/helpers/TwoFactorHelper.ts +21 -22
- package/src/helpers/streamForBreakdown.ts +1 -1
- package/src/services/AdminSessionService.ts +5 -3
- package/src/services/InvoiceService.ts +2 -2
- package/src/services/MollieService.ts +168 -126
- package/src/services/PaymentService.ts +2 -2
- package/src/services/SSOService.test.ts +25 -1
- package/src/services/SSOService.ts +17 -6
- package/src/services/SessionService.test.ts +319 -0
- package/src/services/SessionService.ts +357 -0
- package/src/services/SettlementService.test.ts +13 -85
- package/src/services/SettlementService.ts +5 -113
- package/src/services/WebshopCrowdfundingService.test.ts +4 -3
- package/src/sql-filters/balance-items.ts +10 -0
- package/src/sql-filters/registrations.ts +59 -1
- package/tests/actions/patchUserMember.ts +2 -1
- package/tests/e2e/api-rate-limits.test.ts +2 -1
- package/tests/e2e/bundle-discounts.test.ts +4 -3
- package/tests/e2e/charge-members.test.ts +6 -5
- package/tests/e2e/documents.test.ts +4 -3
- package/tests/e2e/private-files.test.ts +9 -8
- package/tests/e2e/register.test.ts +4 -3
- package/tests/e2e/stock.test.ts +4 -3
- package/tests/e2e/tickets.test.ts +4 -3
- package/tests/helpers/ExportSlice.ts +2 -1
- package/tests/helpers/MollieMocker.ts +173 -38
- package/tests/init/initAdmin.ts +2 -1
- package/tests/init/initPlatformAdmin.ts +2 -1
- package/src/helpers/ViesHelper.test.ts +0 -401
- package/src/helpers/ViesHelper.ts +0 -215
- package/src/services/PeppolDirectoryService.test.ts +0 -139
- package/src/services/PeppolDirectoryService.ts +0 -111
|
@@ -6,7 +6,8 @@ import { ApplicationFee } from '@stamhoofd/models/models/ApplicationFee.js';
|
|
|
6
6
|
import { PaymentSettlement } from '@stamhoofd/models/models/PaymentSettlement.js';
|
|
7
7
|
import { Settlement } from '@stamhoofd/models/models/Settlement.js';
|
|
8
8
|
import { SettlementCharge } from '@stamhoofd/models/models/SettlementCharge.js';
|
|
9
|
-
import { AbortSignal
|
|
9
|
+
import type { AbortSignal } from '@stamhoofd/queues';
|
|
10
|
+
import { QueueHandler } from '@stamhoofd/queues';
|
|
10
11
|
import { SQL } from '@stamhoofd/sql';
|
|
11
12
|
import type { PaymentProvider } from '@stamhoofd/structures';
|
|
12
13
|
import { PaymentMethod, SettlementReference } from '@stamhoofd/structures';
|
|
@@ -67,44 +68,6 @@ const CHARGE_UPDATE_BATCH_SIZE = 500;
|
|
|
67
68
|
*/
|
|
68
69
|
const FEE_BATCH_SIZE = 500;
|
|
69
70
|
|
|
70
|
-
/**
|
|
71
|
-
* Collects which rows the provider still reports in a settlement while a sync walks it. A stored
|
|
72
|
-
* row of the settlement that is not in here after the walk has moved or disappeared at the
|
|
73
|
-
* provider, and gets swept.
|
|
74
|
-
*/
|
|
75
|
-
export class ReportedRows {
|
|
76
|
-
readonly paymentLineExternalIds = new Set<string>();
|
|
77
|
-
readonly chargeExternalIds = new Set<string>();
|
|
78
|
-
readonly applicationFeeIds = new Set<string>();
|
|
79
|
-
|
|
80
|
-
paymentLine(line: PaymentSettlement) {
|
|
81
|
-
if (line.externalId === null) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
this.paymentLineExternalIds.add(line.externalId);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
charge(charge: SettlementCharge) {
|
|
88
|
-
this.chargeExternalIds.add(charge.externalId);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
charges(charges: SettlementCharge[]) {
|
|
92
|
-
for (const charge of charges) {
|
|
93
|
-
this.charge(charge);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
applicationFee(fee: ApplicationFee) {
|
|
98
|
-
this.applicationFeeIds.add(fee.id);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
applicationFees(fees: ApplicationFee[]) {
|
|
102
|
-
for (const fee of fees) {
|
|
103
|
-
this.applicationFee(fee);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
71
|
/**
|
|
109
72
|
* All writes to the settlements tables go through this service. Every write is an upsert on the
|
|
110
73
|
* deterministic unique key of its table, so re-running a sync can never duplicate rows.
|
|
@@ -317,77 +280,6 @@ export class SettlementService {
|
|
|
317
280
|
}
|
|
318
281
|
}
|
|
319
282
|
|
|
320
|
-
/**
|
|
321
|
-
* After a complete walk of a settlement: remove rows the provider no longer reports in this
|
|
322
|
-
* settlement (a transaction can move to another payout). Rows that only exist because of the
|
|
323
|
-
* payout are deleted; rows that outlive the payout link are only unlinked: derived fee lines
|
|
324
|
-
* (owned by updatePaymentSettlementsForAccountDeductionPayment), deduction charges referenced
|
|
325
|
-
* by an application fee, and the application fee rows themselves.
|
|
326
|
-
*
|
|
327
|
-
* Returns the fees unlinked from this settlement, so the caller can refresh the derived lines
|
|
328
|
-
* of their fee payments.
|
|
329
|
-
*/
|
|
330
|
-
static async sweepSettlement(settlement: Settlement, reported: ReportedRows): Promise<{ unlinkedFees: ApplicationFee[] }> {
|
|
331
|
-
const lines = await PaymentSettlement.select()
|
|
332
|
-
.where('settlementId', settlement.id)
|
|
333
|
-
.fetch();
|
|
334
|
-
|
|
335
|
-
const sweptPaymentIds = new Set<string>();
|
|
336
|
-
for (const line of lines) {
|
|
337
|
-
if (line.externalId === null) {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
if (!reported.paymentLineExternalIds.has(line.externalId)) {
|
|
341
|
-
await line.delete();
|
|
342
|
-
sweptPaymentIds.add(line.paymentId);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// The legacy blob points at one of the lines, so a payment that lost one has to be
|
|
347
|
-
// repointed at what is left
|
|
348
|
-
if (sweptPaymentIds.size > 0) {
|
|
349
|
-
const payments = await PaymentModel.getByIDs(...sweptPaymentIds);
|
|
350
|
-
for (const payment of payments) {
|
|
351
|
-
await this.updateLegacySettlementReference(payment);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const charges = await SettlementCharge.select()
|
|
356
|
-
.where('settlementId', settlement.id)
|
|
357
|
-
.fetch();
|
|
358
|
-
|
|
359
|
-
const unreportedCharges = charges.filter(charge => !reported.chargeExternalIds.has(charge.externalId));
|
|
360
|
-
const referencedChargeIds = unreportedCharges.length > 0
|
|
361
|
-
? new Set((await ApplicationFee.select()
|
|
362
|
-
.where('settlementChargeId', unreportedCharges.map(c => c.id))
|
|
363
|
-
.fetch()).map(fee => fee.settlementChargeId))
|
|
364
|
-
: new Set<string>();
|
|
365
|
-
|
|
366
|
-
for (const charge of unreportedCharges) {
|
|
367
|
-
if (referencedChargeIds.has(charge.id)) {
|
|
368
|
-
charge.settlementId = null;
|
|
369
|
-
await charge.save();
|
|
370
|
-
} else {
|
|
371
|
-
await charge.delete();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const fees = await ApplicationFee.select()
|
|
376
|
-
.where('settlementId', settlement.id)
|
|
377
|
-
.fetch();
|
|
378
|
-
|
|
379
|
-
const unlinkedFees: ApplicationFee[] = [];
|
|
380
|
-
for (const fee of fees) {
|
|
381
|
-
if (!reported.applicationFeeIds.has(fee.id)) {
|
|
382
|
-
fee.settlementId = null;
|
|
383
|
-
await fee.save();
|
|
384
|
-
unlinkedFees.push(fee);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return { unlinkedFees };
|
|
389
|
-
}
|
|
390
|
-
|
|
391
283
|
/**
|
|
392
284
|
* Recomputes the cached reconciliation columns from the stored rows: `unexplainedAmount` should
|
|
393
285
|
* be 0 — a non-zero value is a real question to answer — and `pendingFees` holds what is
|
|
@@ -500,7 +392,7 @@ export class SettlementService {
|
|
|
500
392
|
* payout that contains fees billed by this payment, amount = the sum of those fees. When the
|
|
501
393
|
* total of the lines matches the payment's price, the payment is completely paid out.
|
|
502
394
|
*/
|
|
503
|
-
static async
|
|
395
|
+
static async updatePaymentSettlementsForApplicationFeePayment(payment: Payment): Promise<void> {
|
|
504
396
|
if (payment.method !== PaymentMethod.AccountDeductions) {
|
|
505
397
|
return;
|
|
506
398
|
}
|
|
@@ -567,7 +459,7 @@ export class SettlementService {
|
|
|
567
459
|
* Refreshes the derived lines of every fee payment that billed one of these balance items:
|
|
568
460
|
* called after a walk linked or unlinked invoiced fees, so the lines follow the fees.
|
|
569
461
|
*/
|
|
570
|
-
static async
|
|
462
|
+
static async updatePaymentSettlementsForApplicationFeeBalanceItems(balanceItemIds: string[]): Promise<void> {
|
|
571
463
|
if (balanceItemIds.length === 0) {
|
|
572
464
|
return;
|
|
573
465
|
}
|
|
@@ -582,7 +474,7 @@ export class SettlementService {
|
|
|
582
474
|
|
|
583
475
|
const payments = await PaymentModel.getByIDs(...paymentIds);
|
|
584
476
|
for (const payment of payments) {
|
|
585
|
-
await this.
|
|
477
|
+
await this.updatePaymentSettlementsForApplicationFeePayment(payment);
|
|
586
478
|
}
|
|
587
479
|
}
|
|
588
480
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { PatchableArray } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
4
|
-
import type { Organization, StripeAccount } from '@stamhoofd/models';
|
|
5
|
-
import { BalanceItem, BalanceItemFactory, BalanceItemPayment, Order, OrderFactory, OrganizationFactory, Payment,
|
|
4
|
+
import type { Organization, StripeAccount, Token } from '@stamhoofd/models';
|
|
5
|
+
import { BalanceItem, BalanceItemFactory, BalanceItemPayment, Order, OrderFactory, OrganizationFactory, Payment, UserFactory, Webshop, WebshopFactory } from '@stamhoofd/models';
|
|
6
6
|
import type { OrderResponse } from '@stamhoofd/structures';
|
|
7
7
|
import { BalanceItemStatus, Cart, CartItem, Customer, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PaymentStatus, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, PrivateWebshop, Product, ProductPrice, TransferSettings, WebshopCrowdfunding, WebshopMetaData, WebshopPrivateMetaData } from '@stamhoofd/structures';
|
|
8
8
|
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import { SessionService } from './SessionService.js';
|
|
9
10
|
|
|
10
11
|
import type { StripeObject } from '../../tests/helpers/StripeMocker.js';
|
|
11
12
|
import { StripeMocker } from '../../tests/helpers/StripeMocker.js';
|
|
@@ -234,7 +235,7 @@ describe('WebshopCrowdfundingService', () => {
|
|
|
234
235
|
level: PermissionLevel.Full,
|
|
235
236
|
}),
|
|
236
237
|
}).create();
|
|
237
|
-
token = await
|
|
238
|
+
token = await SessionService.createSession(user);
|
|
238
239
|
});
|
|
239
240
|
|
|
240
241
|
afterAll(() => {
|
|
@@ -99,6 +99,16 @@ export const balanceItemFilterCompilers: SQLFilterDefinitions = {
|
|
|
99
99
|
type: SQLValueType.String,
|
|
100
100
|
nullable: true,
|
|
101
101
|
}),
|
|
102
|
+
orderId: createColumnFilter({
|
|
103
|
+
expression: SQL.column('balance_items', 'orderId'),
|
|
104
|
+
type: SQLValueType.String,
|
|
105
|
+
nullable: true,
|
|
106
|
+
}),
|
|
107
|
+
registrationId: createColumnFilter({
|
|
108
|
+
expression: SQL.column('balance_items', 'registrationId'),
|
|
109
|
+
type: SQLValueType.String,
|
|
110
|
+
nullable: true,
|
|
111
|
+
}),
|
|
102
112
|
type: createColumnFilter({
|
|
103
113
|
expression: SQL.column('balance_items', 'type'),
|
|
104
114
|
type: SQLValueType.String,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Group, Member, Organization, Registration } from '@stamhoofd/models';
|
|
2
2
|
import type { SQLFilterDefinitions } from '@stamhoofd/sql';
|
|
3
|
-
import { baseSQLFilterCompilers, createColumnFilter, createJoinedRelationFilter, SQL, SQLValueType } from '@stamhoofd/sql';
|
|
3
|
+
import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, createJoinedRelationFilter, SQL, SQLValueType } from '@stamhoofd/sql';
|
|
4
4
|
import { SQLTranslatedString } from '../helpers/SQLTranslatedString.js';
|
|
5
5
|
import { baseRegistrationFilterCompilers } from './base-registration-filter-compilers.js';
|
|
6
6
|
import { memberFilterCompilers } from './members.js';
|
|
@@ -55,6 +55,64 @@ export const registrationFilterCompilers: SQLFilterDefinitions = {
|
|
|
55
55
|
}),
|
|
56
56
|
},
|
|
57
57
|
),
|
|
58
|
+
/**
|
|
59
|
+
* There is a bug in MySQL 8 that is fixed in 9.3
|
|
60
|
+
* where EXISTS (select * from json_table(...)) does not work
|
|
61
|
+
* To fix this, we do a double select with join inside the select
|
|
62
|
+
* It is a bit slower, but it works for now.
|
|
63
|
+
*/
|
|
64
|
+
options: createExistsFilter(
|
|
65
|
+
SQL.select()
|
|
66
|
+
.from(SQL.table('registrations', 'innerRegistrations'))
|
|
67
|
+
.join(
|
|
68
|
+
SQL.join(
|
|
69
|
+
SQL.jsonTable(
|
|
70
|
+
SQL.jsonExtract(SQL.column('innerRegistrations', 'options'), '$.value'),
|
|
71
|
+
'options',
|
|
72
|
+
)
|
|
73
|
+
.addColumn(
|
|
74
|
+
'amount',
|
|
75
|
+
'INT',
|
|
76
|
+
'$.amount',
|
|
77
|
+
)
|
|
78
|
+
.addColumn(
|
|
79
|
+
'optionId',
|
|
80
|
+
'TEXT',
|
|
81
|
+
'$.option.id',
|
|
82
|
+
)
|
|
83
|
+
.addColumn(
|
|
84
|
+
'optionMenuId',
|
|
85
|
+
'TEXT',
|
|
86
|
+
'$.optionMenu.id',
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
.where(SQL.column('innerRegistrations', 'id'), SQL.column('registrations', 'id')),
|
|
91
|
+
{
|
|
92
|
+
...baseSQLFilterCompilers,
|
|
93
|
+
amount: createColumnFilter({
|
|
94
|
+
expression: SQL.column('options', 'amount'),
|
|
95
|
+
type: SQLValueType.Number,
|
|
96
|
+
nullable: false,
|
|
97
|
+
}),
|
|
98
|
+
option: {
|
|
99
|
+
...baseSQLFilterCompilers,
|
|
100
|
+
id: createColumnFilter({
|
|
101
|
+
expression: SQL.column('options', 'optionId'),
|
|
102
|
+
type: SQLValueType.String,
|
|
103
|
+
nullable: false,
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
optionMenu: {
|
|
107
|
+
...baseSQLFilterCompilers,
|
|
108
|
+
id: createColumnFilter({
|
|
109
|
+
expression: SQL.column('options', 'optionMenuId'),
|
|
110
|
+
type: SQLValueType.String,
|
|
111
|
+
nullable: false,
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
),
|
|
58
116
|
organization: createJoinedRelationFilter(
|
|
59
117
|
organizationJoin,
|
|
60
118
|
organizationFilterCompilers,
|
|
@@ -6,11 +6,12 @@ import { Token } from '@stamhoofd/models';
|
|
|
6
6
|
import type { MemberWithRegistrationsBlob } from '@stamhoofd/structures';
|
|
7
7
|
import { PatchUserMembersEndpoint } from '../../src/endpoints/global/registration/PatchUserMembersEndpoint.js';
|
|
8
8
|
import { testServer } from '../helpers/TestServer.js';
|
|
9
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
9
10
|
|
|
10
11
|
export async function patchUserMember({ patch, organization, user }: { patch: AutoEncoderPatchType<MemberWithRegistrationsBlob>; organization: Organization; user: User }) {
|
|
11
12
|
expect(patch.id).toEqual(expect.any(String));
|
|
12
13
|
|
|
13
|
-
const token = await
|
|
14
|
+
const token = await SessionService.createSession(user);
|
|
14
15
|
|
|
15
16
|
const arr = new PatchableArray();
|
|
16
17
|
arr.addPatch(patch);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import type { Organization } from '@stamhoofd/models';
|
|
3
3
|
import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
4
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
4
5
|
|
|
5
6
|
import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
|
|
6
7
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
@@ -29,7 +30,7 @@ describe('E2E.APIRateLimits', () => {
|
|
|
29
30
|
level: PermissionLevel.Full,
|
|
30
31
|
}),
|
|
31
32
|
}).create();
|
|
32
|
-
const token = await
|
|
33
|
+
const token = await SessionService.createSession(user, { authenticatedAt: new Date() });
|
|
33
34
|
|
|
34
35
|
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
35
36
|
permissions: UserPermissions.create({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import type { BalanceItem, Organization, RegistrationPeriod } from '@stamhoofd/models';
|
|
3
|
-
import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, Registration, RegistrationFactory, RegistrationPeriodFactory,
|
|
2
|
+
import type { BalanceItem, Organization, RegistrationPeriod, Token } from '@stamhoofd/models';
|
|
3
|
+
import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, Registration, RegistrationFactory, RegistrationPeriodFactory, UserFactory } from '@stamhoofd/models';
|
|
4
4
|
import { AccessRight, AppliedRegistrationDiscount, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, BooleanStatus, GroupPriceDiscount, GroupPriceDiscountType, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, PaymentMethod, PermissionLevel, Permissions, PermissionsResourceType, ReduceablePrice, ResourcePermissions } from '@stamhoofd/structures';
|
|
5
5
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
6
6
|
import { RegisterMembersEndpoint } from '../../src/endpoints/global/registration/RegisterMembersEndpoint.js';
|
|
@@ -12,6 +12,7 @@ import { initBundleDiscount } from '../init/initBundleDiscount.js';
|
|
|
12
12
|
import { initPermissionRole } from '../init/initPermissionRole.js';
|
|
13
13
|
import { initStripe } from '../init/initStripe.js';
|
|
14
14
|
import { initMembershipOrganization } from '../init/initMembershipOrganization.js';
|
|
15
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
15
16
|
|
|
16
17
|
const baseUrl = `/members/register`;
|
|
17
18
|
|
|
@@ -64,7 +65,7 @@ describe('E2E.Bundle Discounts', () => {
|
|
|
64
65
|
permissions: null,
|
|
65
66
|
}).create();
|
|
66
67
|
|
|
67
|
-
const token = await
|
|
68
|
+
const token = await SessionService.createSession(user);
|
|
68
69
|
|
|
69
70
|
const member = await new MemberFactory({ organization, user })
|
|
70
71
|
.create();
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import type { Organization, RegistrationPeriod } from '@stamhoofd/models';
|
|
3
|
-
import { GroupFactory, MemberFactory, OrganizationFactory, RegistrationFactory, RegistrationPeriodFactory,
|
|
2
|
+
import type { Organization, RegistrationPeriod, Token } from '@stamhoofd/models';
|
|
3
|
+
import { GroupFactory, MemberFactory, OrganizationFactory, RegistrationFactory, RegistrationPeriodFactory, UserFactory } from '@stamhoofd/models';
|
|
4
4
|
import type { BalanceItemWithPayments, StamhoofdFilter } from '@stamhoofd/structures';
|
|
5
5
|
import { AccessRight, ChargeRequest, LimitedFilteredRequest, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, ResourcePermissions, Version } from '@stamhoofd/structures';
|
|
6
6
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
7
7
|
import { ChargeMembersEndpoint } from '../../src/endpoints/admin/members/ChargeMembersEndpoint.js';
|
|
8
8
|
import { testServer } from '../helpers/TestServer.js';
|
|
9
9
|
import { GetReceivableBalanceEndpoint } from '../../src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.js';
|
|
10
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
10
11
|
|
|
11
12
|
describe('E2E.ChargeMembers', () => {
|
|
12
13
|
const chargeMembersEndpoint = new ChargeMembersEndpoint();
|
|
@@ -45,7 +46,7 @@ describe('E2E.ChargeMembers', () => {
|
|
|
45
46
|
})
|
|
46
47
|
.create();
|
|
47
48
|
|
|
48
|
-
const token = await
|
|
49
|
+
const token = await SessionService.createSession(user);
|
|
49
50
|
|
|
50
51
|
return { organization, user, token };
|
|
51
52
|
};
|
|
@@ -148,7 +149,7 @@ describe('E2E.ChargeMembers', () => {
|
|
|
148
149
|
for (const userFactory of testUserFactories) {
|
|
149
150
|
const user = await userFactory.create();
|
|
150
151
|
|
|
151
|
-
const token = await
|
|
152
|
+
const token = await SessionService.createSession(user);
|
|
152
153
|
|
|
153
154
|
await expect(postCharge(filter, organization, body, token))
|
|
154
155
|
.rejects
|
|
@@ -234,7 +235,7 @@ describe('E2E.ChargeMembers', () => {
|
|
|
234
235
|
})
|
|
235
236
|
.create();
|
|
236
237
|
|
|
237
|
-
const otherFinancialDirectorToken = await
|
|
238
|
+
const otherFinancialDirectorToken = await SessionService.createSession(otherFinancialDirector);
|
|
238
239
|
|
|
239
240
|
const body = ChargeRequest.create({
|
|
240
241
|
description: 'test description',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import type { Group, Member, Organization, RegistrationPeriod } from '@stamhoofd/models';
|
|
3
|
-
import { Document, DocumentTemplateFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, Registration, RegistrationPeriodFactory,
|
|
2
|
+
import type { Group, Member, Organization, RegistrationPeriod, Token } from '@stamhoofd/models';
|
|
3
|
+
import { Document, DocumentTemplateFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, Registration, RegistrationPeriodFactory, UserFactory } from '@stamhoofd/models';
|
|
4
4
|
import { IDRegisterCart, IDRegisterCheckout, IDRegisterItem, MemberDetails, MemberWithRegistrationsBlob, PaymentMethod } from '@stamhoofd/structures';
|
|
5
5
|
import { TestUtils } from '@stamhoofd/test-utils';
|
|
6
6
|
import { Formatter } from '@stamhoofd/utility';
|
|
@@ -14,6 +14,7 @@ import { initStripe } from '../init/initStripe.js';
|
|
|
14
14
|
import { registrationUpdateQueue } from '../../src/services/BalanceItemService.js';
|
|
15
15
|
import { DocumentRenderService } from '../../src/services/DocumentRenderService.js';
|
|
16
16
|
import { initMembershipOrganization } from '../init/initMembershipOrganization.js';
|
|
17
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
17
18
|
|
|
18
19
|
const baseUrl = `/members/register`;
|
|
19
20
|
|
|
@@ -73,7 +74,7 @@ describe('E2E.Documents', () => {
|
|
|
73
74
|
permissions: null,
|
|
74
75
|
}).create();
|
|
75
76
|
|
|
76
|
-
const token = await
|
|
77
|
+
const token = await SessionService.createSession(user);
|
|
77
78
|
|
|
78
79
|
const member = await new MemberFactory({ organization, user })
|
|
79
80
|
.create();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { File, MemberDetails, MemberWithRegistrationsBlob, RecordCategory, RecordFileAnswer, RecordSettings, RecordType, TranslatedString, Version } from '@stamhoofd/structures';
|
|
2
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
2
3
|
|
|
3
4
|
import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
|
|
4
5
|
import { encodeObject, EncodeMedium, PatchableArray } from '@simonbackx/simple-encoding';
|
|
@@ -48,7 +49,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
48
49
|
level: PermissionLevel.Full,
|
|
49
50
|
}),
|
|
50
51
|
}).create();
|
|
51
|
-
const token = await
|
|
52
|
+
const token = await SessionService.createSession(user);
|
|
52
53
|
|
|
53
54
|
// Create a user member
|
|
54
55
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -91,7 +92,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
91
92
|
level: PermissionLevel.Full,
|
|
92
93
|
}),
|
|
93
94
|
}).create();
|
|
94
|
-
const token = await
|
|
95
|
+
const token = await SessionService.createSession(user);
|
|
95
96
|
|
|
96
97
|
// Create a user member
|
|
97
98
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -135,7 +136,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
135
136
|
level: PermissionLevel.Full,
|
|
136
137
|
}),
|
|
137
138
|
}).create();
|
|
138
|
-
const token = await
|
|
139
|
+
const token = await SessionService.createSession(user);
|
|
139
140
|
|
|
140
141
|
// Create a user member
|
|
141
142
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -197,7 +198,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
197
198
|
level: PermissionLevel.Full,
|
|
198
199
|
}),
|
|
199
200
|
}).create();
|
|
200
|
-
const token = await
|
|
201
|
+
const token = await SessionService.createSession(user);
|
|
201
202
|
|
|
202
203
|
// Public files are not signed, so a malicious user can put anything in them. The url of a file ends up
|
|
203
204
|
// in a link (and in the source of an image), so a javascript: url would run on our own domain.
|
|
@@ -260,7 +261,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
260
261
|
level: PermissionLevel.Full,
|
|
261
262
|
}),
|
|
262
263
|
}).create();
|
|
263
|
-
const token = await
|
|
264
|
+
const token = await SessionService.createSession(user);
|
|
264
265
|
|
|
265
266
|
// Create a user member
|
|
266
267
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -324,7 +325,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
324
325
|
level: PermissionLevel.Full,
|
|
325
326
|
}),
|
|
326
327
|
}).create();
|
|
327
|
-
const token = await
|
|
328
|
+
const token = await SessionService.createSession(user);
|
|
328
329
|
|
|
329
330
|
// Create a user member
|
|
330
331
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -404,7 +405,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
404
405
|
level: PermissionLevel.Full,
|
|
405
406
|
}),
|
|
406
407
|
}).create();
|
|
407
|
-
const token = await
|
|
408
|
+
const token = await SessionService.createSession(user);
|
|
408
409
|
|
|
409
410
|
// Create a user member
|
|
410
411
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -662,7 +663,7 @@ describe('E2E.PrivateFiles', () => {
|
|
|
662
663
|
level: PermissionLevel.Full,
|
|
663
664
|
}),
|
|
664
665
|
}).create();
|
|
665
|
-
const token = await
|
|
666
|
+
const token = await SessionService.createSession(user);
|
|
666
667
|
|
|
667
668
|
// Create a user member
|
|
668
669
|
const member = MemberWithRegistrationsBlob.create({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import type { MemberWithUsersRegistrationsAndGroups, Organization, RegistrationPeriod } from '@stamhoofd/models';
|
|
3
|
-
import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriod, Platform, RegistrationPeriodFactory,
|
|
2
|
+
import type { MemberWithUsersRegistrationsAndGroups, Organization, RegistrationPeriod, Token } from '@stamhoofd/models';
|
|
3
|
+
import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriod, Platform, RegistrationPeriodFactory, UserFactory } from '@stamhoofd/models';
|
|
4
4
|
import { AdministrationFeeSettings, BalanceItemCartItem, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, BooleanStatus, DefaultAgeGroup, FreeContributionSettings, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, PaymentMethod, PermissionLevel, Permissions, PlatformMembershipType, PlatformMembershipTypeConfig, ReceivableBalanceType, ReduceablePrice, RegisterItemOption, TranslatedString, Version } from '@stamhoofd/structures';
|
|
5
5
|
import { TestUtils } from '@stamhoofd/test-utils';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -11,6 +11,7 @@ import { BalanceItemService } from '../../src/services/BalanceItemService.js';
|
|
|
11
11
|
import { PlatformMembershipService } from '../../src/services/PlatformMembershipService.js';
|
|
12
12
|
import { assertBalances } from '../assertions/assertBalances.js';
|
|
13
13
|
import { testServer } from '../helpers/TestServer.js';
|
|
14
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
14
15
|
|
|
15
16
|
describe('E2E.Register', () => {
|
|
16
17
|
const registerEndpoint = new RegisterMembersEndpoint();
|
|
@@ -74,7 +75,7 @@ describe('E2E.Register', () => {
|
|
|
74
75
|
})
|
|
75
76
|
.create();
|
|
76
77
|
|
|
77
|
-
const token = await
|
|
78
|
+
const token = await SessionService.createSession(user);
|
|
78
79
|
|
|
79
80
|
const member = await new MemberFactory({ organization, user }).create();
|
|
80
81
|
|
package/tests/e2e/stock.test.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { PatchableArray } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
4
|
-
import type { Organization, StripeAccount } from '@stamhoofd/models';
|
|
5
|
-
import { Order, OrganizationFactory,
|
|
4
|
+
import type { Organization, StripeAccount, Token } from '@stamhoofd/models';
|
|
5
|
+
import { Order, OrganizationFactory, UserFactory, Webshop, WebshopFactory } from '@stamhoofd/models';
|
|
6
6
|
import { Address, Cart, CartItem, CartItemOption, CartReservedSeat, Customer, Option, OptionMenu, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, Product, ProductPrice, ProductType, ReservedSeat, SeatingPlan, SeatingPlanRow, SeatingPlanSeat, SeatingPlanSection, TransferSettings, ValidatedAddress, WebshopDeliveryMethod, WebshopMetaData, WebshopOnSiteMethod, WebshopPrivateMetaData, WebshopTakeoutMethod, WebshopTimeSlot } from '@stamhoofd/structures';
|
|
7
7
|
import { Country } from '@stamhoofd/types/Country';
|
|
8
8
|
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
9
10
|
|
|
10
11
|
import { PatchWebshopOrdersEndpoint } from '../../src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.js';
|
|
11
12
|
import { PlaceOrderEndpoint } from '../../src/endpoints/organization/webshops/PlaceOrderEndpoint.js';
|
|
@@ -253,7 +254,7 @@ describe('E2E.Stock', () => {
|
|
|
253
254
|
level: PermissionLevel.Full,
|
|
254
255
|
}),
|
|
255
256
|
}).create();
|
|
256
|
-
token = await
|
|
257
|
+
token = await SessionService.createSession(user);
|
|
257
258
|
|
|
258
259
|
await initMembershipOrganization();
|
|
259
260
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { PatchableArray } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
4
|
-
import type { Organization, StripeAccount, Webshop } from '@stamhoofd/models';
|
|
5
|
-
import { OrganizationFactory, Ticket,
|
|
4
|
+
import type { Organization, StripeAccount, Webshop, Token } from '@stamhoofd/models';
|
|
5
|
+
import { OrganizationFactory, Ticket, UserFactory, WebshopFactory } from '@stamhoofd/models';
|
|
6
6
|
import { Cart, CartItem, CartReservedSeat, Customer, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, Product, ProductType, SeatingPlan, SeatingPlanRow, SeatingPlanSeat, SeatingPlanSection, TransferSettings, WebshopMetaData, WebshopPrivateMetaData, WebshopTicketType } from '@stamhoofd/structures';
|
|
7
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
7
8
|
|
|
8
9
|
import { PatchWebshopOrdersEndpoint } from '../../src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.js';
|
|
9
10
|
import { PlaceOrderEndpoint } from '../../src/endpoints/organization/webshops/PlaceOrderEndpoint.js';
|
|
@@ -66,7 +67,7 @@ describe('E2E.Tickets', () => {
|
|
|
66
67
|
level: PermissionLevel.Full,
|
|
67
68
|
}),
|
|
68
69
|
}).create();
|
|
69
|
-
token = await
|
|
70
|
+
token = await SessionService.createSession(user);
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
afterAll(() => {
|
|
@@ -6,6 +6,7 @@ import { ExcelExportType, LimitedFilteredRequest, SortItemDirection } from '@sta
|
|
|
6
6
|
import { ExportToExcelEndpoint } from '../../src/endpoints/global/files/ExportToExcelEndpoint.js';
|
|
7
7
|
import { Context } from '../../src/helpers/Context.js';
|
|
8
8
|
import { testServer } from './TestServer.js';
|
|
9
|
+
import { SessionService } from '../../src/services/SessionService.js';
|
|
9
10
|
|
|
10
11
|
// Registers the balance item payment export, which is what a row that holds a part of its objects is
|
|
11
12
|
// written out with
|
|
@@ -56,7 +57,7 @@ class ExportSliceEndpoint extends Endpoint<Record<string, never>, undefined, und
|
|
|
56
57
|
* How many balance item payments a filter selects, and what they are worth together.
|
|
57
58
|
*/
|
|
58
59
|
export async function exportSlice({ organization, user, filter, search = null }: { organization: Organization; user: User; filter: StamhoofdFilter; search?: string | null }) {
|
|
59
|
-
const token = await
|
|
60
|
+
const token = await SessionService.createSession(user);
|
|
60
61
|
|
|
61
62
|
const request = Request.get({
|
|
62
63
|
path: '/exports/balance-item-payments',
|