@stamhoofd/backend 2.17.3 → 2.18.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 +5 -5
- package/src/crons/setup-steps.ts +9 -0
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +6 -14
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +66 -4
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +6 -7
- package/src/endpoints/organization/dashboard/billing/GetDetailedBillingStatusEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +4 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -2
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +78 -0
- package/src/excel-loaders/payments.ts +36 -4
- package/src/helpers/AuthenticatedStructures.ts +6 -7
- package/src/helpers/SetupStepsUpdater.ts +210 -0
- package/src/seeds/1724076679-setup-steps.ts +16 -0
- package/src/sql-filters/members.ts +2 -0
- package/src/sql-sorters/members.ts +25 -14
- package/src/sql-sorters/organizations.ts +8 -0
- package/src/sql-sorters/payments.ts +8 -0
- package/src/endpoints/admin/invoices/GetInvoicesCountEndpoint.ts +0 -47
- package/src/endpoints/admin/invoices/GetInvoicesEndpoint.ts +0 -185
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +0 -424
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +0 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"@stamhoofd/backend-i18n": "^2.17.0",
|
|
40
40
|
"@stamhoofd/backend-middleware": "^2.17.0",
|
|
41
41
|
"@stamhoofd/email": "^2.17.0",
|
|
42
|
-
"@stamhoofd/models": "^2.
|
|
42
|
+
"@stamhoofd/models": "^2.18.0",
|
|
43
43
|
"@stamhoofd/queues": "^2.17.3",
|
|
44
|
-
"@stamhoofd/sql": "^2.
|
|
45
|
-
"@stamhoofd/structures": "^2.
|
|
44
|
+
"@stamhoofd/sql": "^2.18.0",
|
|
45
|
+
"@stamhoofd/structures": "^2.18.0",
|
|
46
46
|
"@stamhoofd/utility": "^2.17.0",
|
|
47
47
|
"archiver": "^7.0.1",
|
|
48
48
|
"aws-sdk": "^2.885.0",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"postmark": "4.0.2",
|
|
61
61
|
"stripe": "^16.6.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "a47aa5bb6ad71d1e6a2f88bc203caa5acf4f497f"
|
|
64
64
|
}
|
|
@@ -63,12 +63,8 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
|
|
|
63
63
|
new SQLAlias('data__price')
|
|
64
64
|
)
|
|
65
65
|
)
|
|
66
|
-
.from(
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
.where(SQL.column('invoiceId'), null)
|
|
70
|
-
.andWhere(SQL.column('invoiceItemDetailId'), null);
|
|
71
|
-
|
|
66
|
+
.from('member_platform_memberships')
|
|
67
|
+
.where('balanceItemId', null);
|
|
72
68
|
|
|
73
69
|
const result = await query.fetch();
|
|
74
70
|
const members = result[0]['data']['members'] as number;
|
|
@@ -122,17 +118,13 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
|
|
|
122
118
|
new SQLAlias('data__price')
|
|
123
119
|
)
|
|
124
120
|
)
|
|
125
|
-
.from(
|
|
126
|
-
|
|
121
|
+
.from('member_platform_memberships')
|
|
122
|
+
.where('balanceItemId', null)
|
|
123
|
+
.groupBy(
|
|
124
|
+
SQL.column('member_platform_memberships', 'membershipTypeId')
|
|
127
125
|
);
|
|
128
|
-
query.where(SQL.column('invoiceId'), null)
|
|
129
|
-
query.andWhere(SQL.column('invoiceItemDetailId'), null)
|
|
130
|
-
query.groupBy(SQL.column('member_platform_memberships', 'membershipTypeId'));
|
|
131
|
-
|
|
132
126
|
|
|
133
127
|
const result = await query.fetch();
|
|
134
|
-
console.log(result);
|
|
135
|
-
|
|
136
128
|
const membershipsPerType = new Map<string, ChargeMembershipsTypeSummary>();
|
|
137
129
|
|
|
138
130
|
for (const row of result) {
|
|
@@ -160,7 +160,7 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
160
160
|
const writer = new XlsxWriter(zipWriterAdapter);
|
|
161
161
|
|
|
162
162
|
// Limit to pages of 100
|
|
163
|
-
request.filter.limit = 100;
|
|
163
|
+
request.filter.limit = STAMHOOFD.environment === 'development' ? 1 : 100; // in development, we need to check if total count matches and pagination is working correctly
|
|
164
164
|
|
|
165
165
|
await exportToExcel({
|
|
166
166
|
definitions: loader.sheets,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, Decoder, patchObject } from "@simonbackx/simple-encoding";
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
|
-
import { Platform, RegistrationPeriod } from "@stamhoofd/models";
|
|
4
|
-
import { Platform as PlatformStruct } from "@stamhoofd/structures";
|
|
3
|
+
import { Organization, Platform, RegistrationPeriod } from "@stamhoofd/models";
|
|
4
|
+
import { PlatformPremiseType, Platform as PlatformStruct } from "@stamhoofd/structures";
|
|
5
5
|
|
|
6
|
-
import { Context } from "../../../helpers/Context";
|
|
7
6
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
7
|
+
import { Context } from "../../../helpers/Context";
|
|
8
|
+
import { SetupStepUpdater } from "../../../helpers/SetupStepsUpdater";
|
|
8
9
|
|
|
9
10
|
type Params = Record<string, never>;
|
|
10
11
|
type Query = undefined;
|
|
@@ -64,7 +65,18 @@ export class PatchPlatformEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
// Update config
|
|
67
|
-
|
|
68
|
+
if(request.body.config.premiseTypes) {
|
|
69
|
+
const oldConfig = platform.config.clone();
|
|
70
|
+
platform.config = patchObject(platform.config, request.body.config);
|
|
71
|
+
const newPremiseTypes = platform.config.premiseTypes;
|
|
72
|
+
|
|
73
|
+
// update setup step premise types
|
|
74
|
+
if(this.shouldUpdateSetupStepPremise(newPremiseTypes, oldConfig.premiseTypes)) {
|
|
75
|
+
await SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod({premiseTypes: newPremiseTypes});
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
platform.config = patchObject(platform.config, request.body.config)
|
|
79
|
+
}
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
if (request.body.period && request.body.period.id !== platform.periodId) {
|
|
@@ -78,7 +90,57 @@ export class PatchPlatformEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
78
90
|
platform.periodId = period.id
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
if (request.body.membershipOrganizationId !== undefined) {
|
|
94
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
95
|
+
throw Context.auth.error()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (request.body.membershipOrganizationId) {
|
|
99
|
+
const organization = await Organization.getByID(request.body.membershipOrganizationId)
|
|
100
|
+
if (!organization) {
|
|
101
|
+
throw new SimpleError({
|
|
102
|
+
code: "invalid_organization",
|
|
103
|
+
message: "Invalid organization"
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
platform.membershipOrganizationId = organization.id
|
|
107
|
+
} else {
|
|
108
|
+
platform.membershipOrganizationId = null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
81
112
|
await platform.save()
|
|
82
113
|
return new Response(await Platform.getSharedPrivateStruct());
|
|
83
114
|
}
|
|
115
|
+
|
|
116
|
+
private shouldUpdateSetupStepPremise(newPremiseTypes: PlatformPremiseType[], oldPremiseTypes: PlatformPremiseType[]) {
|
|
117
|
+
for(const premiseType of newPremiseTypes) {
|
|
118
|
+
const id = premiseType.id;
|
|
119
|
+
const oldVersion = oldPremiseTypes.find(x => x.id === id);
|
|
120
|
+
|
|
121
|
+
// if premise type is not new
|
|
122
|
+
if(oldVersion) {
|
|
123
|
+
if(oldVersion.min !== premiseType.min || oldVersion.max !== premiseType.max) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// if premise type is new
|
|
130
|
+
if(premiseType.min || premiseType.max) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for(const oldPremiseType of oldPremiseTypes) {
|
|
136
|
+
const id = oldPremiseType.id;
|
|
137
|
+
|
|
138
|
+
// if premise type is removed
|
|
139
|
+
if(!newPremiseTypes.some(x => x.id === id)) {
|
|
140
|
+
if(oldPremiseType.min || oldPremiseType.max) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
84
146
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
-
import {
|
|
3
|
-
import { STInvoice } from "@stamhoofd/models";
|
|
4
|
-
import { Token } from "@stamhoofd/models";
|
|
5
|
-
import { STBillingStatus } from "@stamhoofd/structures";
|
|
2
|
+
import { OrganizationBillingStatus } from "@stamhoofd/structures";
|
|
6
3
|
|
|
7
4
|
import { Context } from "../../../../helpers/Context";
|
|
8
5
|
|
|
9
6
|
type Params = Record<string, never>;
|
|
10
7
|
type Query = undefined;
|
|
11
|
-
type ResponseBody =
|
|
8
|
+
type ResponseBody = OrganizationBillingStatus;
|
|
12
9
|
type Body = undefined;
|
|
13
10
|
|
|
14
11
|
export class GetBillingStatusEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
@@ -32,8 +29,10 @@ export class GetBillingStatusEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
32
29
|
// If the user has permission, we'll also search if he has access to the organization's key
|
|
33
30
|
if (!await Context.auth.canManageFinances(organization.id)) {
|
|
34
31
|
throw Context.auth.error()
|
|
35
|
-
}
|
|
32
|
+
}
|
|
36
33
|
|
|
37
|
-
return new Response(
|
|
34
|
+
return new Response(
|
|
35
|
+
OrganizationBillingStatus.create({})
|
|
36
|
+
)
|
|
38
37
|
}
|
|
39
38
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { OrganizationDetailedBillingStatus, OrganizationDetailedBillingStatusItem, PaymentMethod } from "@stamhoofd/structures";
|
|
3
|
+
|
|
4
|
+
import { BalanceItem, Organization, Payment } from "@stamhoofd/models";
|
|
5
|
+
import { SQL } from "@stamhoofd/sql";
|
|
6
|
+
import { Formatter } from "@stamhoofd/utility";
|
|
7
|
+
import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
|
|
8
|
+
import { Context } from "../../../../helpers/Context";
|
|
9
|
+
|
|
10
|
+
type Params = Record<string, never>;
|
|
11
|
+
type Query = undefined;
|
|
12
|
+
type ResponseBody = OrganizationDetailedBillingStatus;
|
|
13
|
+
type Body = undefined;
|
|
14
|
+
|
|
15
|
+
export class GetDetailedBillingStatusEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
+
if (request.method != "GET") {
|
|
18
|
+
return [false];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, "/billing/status/detailed", {});
|
|
22
|
+
|
|
23
|
+
if (params) {
|
|
24
|
+
return [true, params as Params];
|
|
25
|
+
}
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
30
|
+
const organization = await Context.setOrganizationScope();
|
|
31
|
+
await Context.authenticate()
|
|
32
|
+
|
|
33
|
+
// If the user has permission, we'll also search if he has access to the organization's key
|
|
34
|
+
if (!await Context.auth.canManageFinances(organization.id)) {
|
|
35
|
+
throw Context.auth.error()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const balanceItemModels = await BalanceItem.balanceItemsForOrganization(organization.id);
|
|
39
|
+
|
|
40
|
+
// Hide pending online payments
|
|
41
|
+
const paymentModels = await Payment.select()
|
|
42
|
+
.where('payingOrganizationId', organization.id)
|
|
43
|
+
.andWhere(
|
|
44
|
+
SQL.whereNot('paidAt', null)
|
|
45
|
+
.or('method', [PaymentMethod.Transfer, PaymentMethod.DirectDebit, PaymentMethod.PointOfSale, PaymentMethod.Unknown])
|
|
46
|
+
)
|
|
47
|
+
.fetch()
|
|
48
|
+
|
|
49
|
+
const organizationIds = Formatter.uniqueArray([
|
|
50
|
+
...balanceItemModels.map(b => b.organizationId),
|
|
51
|
+
...paymentModels.map(p => p.organizationId).filter(p => p !== null)
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
// Group by organization you'll have to pay to
|
|
55
|
+
if (organizationIds.length === 0) {
|
|
56
|
+
return new Response(
|
|
57
|
+
OrganizationDetailedBillingStatus.create({})
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const balanceItems = await BalanceItem.getStructureWithPayments(balanceItemModels)
|
|
62
|
+
const organizationModels = await Organization.getByIDs(...organizationIds)
|
|
63
|
+
const organizations = await AuthenticatedStructures.organizations(organizationModels)
|
|
64
|
+
const payments = await AuthenticatedStructures.paymentsGeneral(paymentModels, false)
|
|
65
|
+
|
|
66
|
+
return new Response(
|
|
67
|
+
OrganizationDetailedBillingStatus.create({
|
|
68
|
+
organizations: organizations.map(o => {
|
|
69
|
+
return OrganizationDetailedBillingStatusItem.create({
|
|
70
|
+
organization: o,
|
|
71
|
+
balanceItems: balanceItems.filter(b => b.organizationId == o.id),
|
|
72
|
+
payments: payments.filter(p => p.organizationId === o.id)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -8,6 +8,7 @@ import { Formatter } from '@stamhoofd/utility';
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
9
9
|
import { BuckarooHelper } from '../../../../helpers/BuckarooHelper';
|
|
10
10
|
import { Context } from '../../../../helpers/Context';
|
|
11
|
+
import { SetupStepUpdater } from '../../../../helpers/SetupStepsUpdater';
|
|
11
12
|
import { ViesHelper } from '../../../../helpers/ViesHelper';
|
|
12
13
|
|
|
13
14
|
type Params = Record<string, never>;
|
|
@@ -100,6 +101,9 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
100
101
|
if (request.body.privateMeta && request.body.privateMeta.isPatch()) {
|
|
101
102
|
organization.privateMeta.emails = request.body.privateMeta.emails.applyTo(organization.privateMeta.emails)
|
|
102
103
|
organization.privateMeta.premises = patchObject(organization.privateMeta.premises, request.body.privateMeta.premises);
|
|
104
|
+
if(request.body.privateMeta.premises) {
|
|
105
|
+
await SetupStepUpdater.updateForOrganization(organization);
|
|
106
|
+
}
|
|
103
107
|
organization.privateMeta.roles = request.body.privateMeta.roles.applyTo(organization.privateMeta.roles)
|
|
104
108
|
organization.privateMeta.responsibilities = request.body.privateMeta.responsibilities.applyTo(organization.privateMeta.responsibilities)
|
|
105
109
|
organization.privateMeta.inheritedResponsibilityRoles = request.body.privateMeta.inheritedResponsibilityRoles.applyTo(organization.privateMeta.inheritedResponsibilityRoles)
|
|
@@ -4,7 +4,7 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
|
|
|
4
4
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
5
|
import { Payment } from '@stamhoofd/models';
|
|
6
6
|
import { SQL, compileToSQLFilter, compileToSQLSorter } from "@stamhoofd/sql";
|
|
7
|
-
import { CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, PaymentGeneral, StamhoofdFilter, getSortFilter } from '@stamhoofd/structures';
|
|
7
|
+
import { CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, PaymentGeneral, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
8
8
|
|
|
9
9
|
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
10
10
|
import { Context } from '../../../../helpers/Context';
|
|
@@ -134,7 +134,11 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
134
134
|
query.where(compileToSQLFilter(q.pageFilter, filterCompilers))
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
query.orderBy(compileToSQLSorter(q.sort,
|
|
137
|
+
query.orderBy(compileToSQLSorter(assertSort(q.sort, [
|
|
138
|
+
{
|
|
139
|
+
key: 'id'
|
|
140
|
+
}
|
|
141
|
+
]), sorters))
|
|
138
142
|
query.limit(q.limit)
|
|
139
143
|
}
|
|
140
144
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, SetupStepType } from "@stamhoofd/structures";
|
|
3
|
+
|
|
4
|
+
import { AutoEncoder, BooleanDecoder, Decoder, EnumDecoder, field } from "@simonbackx/simple-encoding";
|
|
5
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
6
|
+
import { OrganizationRegistrationPeriod } from "@stamhoofd/models";
|
|
7
|
+
import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
|
|
8
|
+
import { Context } from "../../../../helpers/Context";
|
|
9
|
+
|
|
10
|
+
type Params = { readonly id: string };
|
|
11
|
+
type Query = undefined;
|
|
12
|
+
|
|
13
|
+
class Body extends AutoEncoder {
|
|
14
|
+
@field({decoder: new EnumDecoder(SetupStepType)})
|
|
15
|
+
type: SetupStepType
|
|
16
|
+
|
|
17
|
+
@field({decoder: BooleanDecoder})
|
|
18
|
+
isReviewed: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type ResponseBody = OrganizationRegistrationPeriodStruct
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Endpoint to mark a setup step as reviewed
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export class SetupStepReviewEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
28
|
+
bodyDecoder = Body as Decoder<Body>;
|
|
29
|
+
|
|
30
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
31
|
+
if (request.method != "POST") {
|
|
32
|
+
return [false];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const params = Endpoint.parseParameters(request.url, "/organization/registration-period/@id/setup-steps/review", {id: String});
|
|
36
|
+
|
|
37
|
+
if (params) {
|
|
38
|
+
return [true, params as Params];
|
|
39
|
+
}
|
|
40
|
+
return [false];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
44
|
+
const organization = await Context.setOrganizationScope();
|
|
45
|
+
await Context.authenticate()
|
|
46
|
+
|
|
47
|
+
if (!await Context.auth.hasFullAccess(organization.id)) {
|
|
48
|
+
throw Context.auth.error()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const periodId = request.params.id;
|
|
52
|
+
const stepType = request.body.type;
|
|
53
|
+
const isReviewed = request.body.isReviewed;
|
|
54
|
+
|
|
55
|
+
const organizationPeriod = await OrganizationRegistrationPeriod.getByID(periodId);
|
|
56
|
+
if (!organizationPeriod || organizationPeriod.organizationId !== organization.id) {
|
|
57
|
+
throw new SimpleError({
|
|
58
|
+
code: "not_found",
|
|
59
|
+
message: "Period not found",
|
|
60
|
+
statusCode: 404
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const setupSteps = organizationPeriod.setupSteps;
|
|
65
|
+
|
|
66
|
+
if(isReviewed) {
|
|
67
|
+
setupSteps.markReviewed(stepType);
|
|
68
|
+
} else {
|
|
69
|
+
setupSteps.resetReviewed(stepType);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await organizationPeriod.save();
|
|
73
|
+
|
|
74
|
+
return new Response(
|
|
75
|
+
await AuthenticatedStructures.organizationRegistrationPeriod(organizationPeriod),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { XlsxBuiltInNumberFormat, XlsxTransformerColumn } from "@stamhoofd/excel-writer";
|
|
1
|
+
import { XlsxBuiltInNumberFormat, XlsxTransformerColumn, XlsxTransformerConcreteColumn } from "@stamhoofd/excel-writer";
|
|
2
2
|
import { StripeAccount as StripeAccountStruct, BalanceItemPaymentDetailed, BalanceItemRelationType, CountryHelper, ExcelExportType, getBalanceItemRelationTypeName, getBalanceItemTypeName, PaymentGeneral, PaymentMethodHelper, PaymentStatusHelper, PaginatedResponse, PaymentProvider } from "@stamhoofd/structures";
|
|
3
3
|
import { ExportToExcelEndpoint } from "../endpoints/global/files/ExportToExcelEndpoint";
|
|
4
4
|
import { GetPaymentsEndpoint } from "../endpoints/organization/dashboard/payments/GetPaymentsEndpoint";
|
|
@@ -52,12 +52,44 @@ ExportToExcelEndpoint.loaders.set(ExcelExportType.Payments, {
|
|
|
52
52
|
{
|
|
53
53
|
id: 'balanceItemPayments',
|
|
54
54
|
name: 'Betaallijnen',
|
|
55
|
-
transform: (data: PaymentGeneral) => data.balanceItemPayments.map(p => ({
|
|
55
|
+
transform: (data: PaymentGeneral): PaymentWithItem[] => data.balanceItemPayments.map(p => ({
|
|
56
56
|
payment: data,
|
|
57
57
|
balanceItemPayment: p
|
|
58
58
|
})),
|
|
59
59
|
columns: [
|
|
60
|
-
...getBalanceItemColumns()
|
|
60
|
+
...getBalanceItemColumns(),
|
|
61
|
+
|
|
62
|
+
// Repeating columns need to de-transform again
|
|
63
|
+
...[
|
|
64
|
+
...getGeneralColumns(),
|
|
65
|
+
...getInvoiceColumns(),
|
|
66
|
+
].map(c => {
|
|
67
|
+
if ('match' in c) {
|
|
68
|
+
return {
|
|
69
|
+
...c,
|
|
70
|
+
match: (id: string) => {
|
|
71
|
+
const result = c.match(id)
|
|
72
|
+
if (!result) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result.map(cc => ({
|
|
77
|
+
...cc,
|
|
78
|
+
getValue: (object: PaymentWithItem) => {
|
|
79
|
+
return cc.getValue(object.payment)
|
|
80
|
+
},
|
|
81
|
+
}))
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
...c,
|
|
88
|
+
getValue: (object: PaymentWithItem) => {
|
|
89
|
+
return c.getValue(object.payment)
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
})
|
|
61
93
|
]
|
|
62
94
|
}
|
|
63
95
|
]
|
|
@@ -164,7 +196,7 @@ function getBalanceItemColumns(): XlsxTransformerColumn<PaymentWithItem>[] {
|
|
|
164
196
|
}
|
|
165
197
|
|
|
166
198
|
|
|
167
|
-
function getGeneralColumns():
|
|
199
|
+
function getGeneralColumns(): XlsxTransformerConcreteColumn<PaymentGeneral>[] {
|
|
168
200
|
return [
|
|
169
201
|
{
|
|
170
202
|
id: 'id',
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
2
2
|
import { Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, User, Webshop } from "@stamhoofd/models";
|
|
3
|
-
import { Event as EventStruct,
|
|
4
|
-
import { OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, GroupCategory, GroupPrivateSettings, GroupSettings, GroupStatus, Group as GroupStruct, GroupType } from '@stamhoofd/structures';
|
|
3
|
+
import { Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, PrivateWebshop, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
|
|
5
4
|
|
|
6
|
-
import { Context } from "./Context";
|
|
7
5
|
import { Formatter } from "@stamhoofd/utility";
|
|
6
|
+
import { Context } from "./Context";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Builds authenticated structures for the current user
|
|
@@ -55,7 +54,7 @@ export class AuthenticatedStructures {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
static async groups(groups: Group[]) {
|
|
58
|
-
const waitingListIds = Formatter.uniqueArray(groups.map(g => g.waitingListId).filter(id => id !== null)
|
|
57
|
+
const waitingListIds = Formatter.uniqueArray(groups.map(g => g.waitingListId).filter(id => id !== null))
|
|
59
58
|
const waitingLists = waitingListIds.length > 0 ? await Group.getByIDs(...waitingListIds) : []
|
|
60
59
|
|
|
61
60
|
const structs: GroupStruct[] = []
|
|
@@ -194,7 +193,7 @@ export class AuthenticatedStructures {
|
|
|
194
193
|
*/
|
|
195
194
|
static async usersWithMembers(users: User[]): Promise<UserWithMembers[]> {
|
|
196
195
|
const structs: UserWithMembers[] = [];
|
|
197
|
-
const memberIds = Formatter.uniqueArray(users.map(u => u.memberId).filter(id => id !== null)
|
|
196
|
+
const memberIds = Formatter.uniqueArray(users.map(u => u.memberId).filter(id => id !== null))
|
|
198
197
|
const members = memberIds.length > 0 ? await Member.getBlobByIds(...memberIds) : []
|
|
199
198
|
|
|
200
199
|
for (const user of users) {
|
|
@@ -251,7 +250,7 @@ export class AuthenticatedStructures {
|
|
|
251
250
|
const platformMemberships = members.length > 0 ? await MemberPlatformMembership.where({ deletedAt: null, memberId: { sign: 'IN', value: members.map(m => m.id) } }) : []
|
|
252
251
|
|
|
253
252
|
// Load missing organizations
|
|
254
|
-
const organizationIds = Formatter.uniqueArray(responsibilities.map(r => r.organizationId).filter(id => id !== null)
|
|
253
|
+
const organizationIds = Formatter.uniqueArray(responsibilities.map(r => r.organizationId).filter(id => id !== null))
|
|
255
254
|
for (const id of organizationIds) {
|
|
256
255
|
if (includeContextOrganization || id !== Context.auth.organization?.id) {
|
|
257
256
|
const found = organizations.get(id);
|
|
@@ -275,7 +274,7 @@ export class AuthenticatedStructures {
|
|
|
275
274
|
|
|
276
275
|
static async events(events: Event[]): Promise<EventStruct[]> {
|
|
277
276
|
// Load groups
|
|
278
|
-
const groupIds = events.map(e => e.groupId).filter(id => id !== null)
|
|
277
|
+
const groupIds = events.map(e => e.groupId).filter(id => id !== null)
|
|
279
278
|
const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : []
|
|
280
279
|
const groupStructs = await this.groups(groups)
|
|
281
280
|
|