@stamhoofd/backend 2.83.4 → 2.84.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 (100) hide show
  1. package/index.ts +19 -4
  2. package/package.json +18 -14
  3. package/src/crons/amazon-ses.ts +26 -5
  4. package/src/crons/balance-emails.ts +18 -17
  5. package/src/email-recipient-loaders/registrations.ts +87 -0
  6. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +5 -2
  7. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +40 -40
  8. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +28 -22
  9. package/src/endpoints/global/events/PatchEventsEndpoint.ts +81 -49
  10. package/src/endpoints/global/files/UploadFile.ts +11 -16
  11. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +234 -0
  12. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +117 -43
  13. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +1054 -0
  14. package/src/endpoints/global/members/GetMembersEndpoint.ts +163 -141
  15. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +6 -6
  16. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +0 -16
  17. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +73 -0
  18. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +1 -2
  19. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +43 -0
  20. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1016 -0
  21. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +234 -0
  22. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +5 -5
  23. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +474 -554
  24. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +191 -52
  25. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +107 -9
  26. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +89 -0
  27. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +9 -6
  28. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +88 -0
  29. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +0 -6
  30. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -6
  31. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +10 -25
  32. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +0 -5
  33. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +0 -5
  34. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +4 -0
  35. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -0
  36. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +44 -19
  37. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +140 -25
  38. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +40 -10
  39. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +2 -2
  40. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +2 -2
  41. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +4 -1
  42. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +2 -2
  43. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +2 -2
  44. package/src/excel-loaders/members.ts +233 -232
  45. package/src/excel-loaders/payments.ts +1 -1
  46. package/src/excel-loaders/receivable-balances.ts +1 -1
  47. package/src/excel-loaders/registrations.ts +153 -0
  48. package/src/helpers/AdminPermissionChecker.ts +65 -37
  49. package/src/helpers/AuthenticatedStructures.ts +43 -3
  50. package/src/helpers/Context.ts +29 -1
  51. package/src/helpers/GlobalHelper.ts +3 -1
  52. package/src/helpers/GroupedThrottledQueue.test.ts +219 -0
  53. package/src/helpers/GroupedThrottledQueue.ts +108 -0
  54. package/src/helpers/LimitedFilteredRequestHelper.ts +26 -1
  55. package/src/helpers/MemberCharger.ts +0 -5
  56. package/src/helpers/MembershipCharger.ts +3 -9
  57. package/src/helpers/OrganizationCharger.ts +0 -5
  58. package/src/helpers/ThrottledQueue.test.ts +194 -0
  59. package/src/helpers/ThrottledQueue.ts +145 -0
  60. package/src/helpers/XlsxTransformerColumnHelper.ts +44 -1
  61. package/src/middleware/ContextMiddleware.ts +1 -1
  62. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +2 -1
  63. package/src/seeds/1735577912-update-cached-outstanding-balance-from-items.ts +2 -1
  64. package/src/services/BalanceItemPaymentService.ts +1 -33
  65. package/src/services/BalanceItemService.ts +167 -48
  66. package/src/services/FileSignService.ts +18 -13
  67. package/src/services/MemberRecordStore.ts +28 -19
  68. package/src/services/PaymentReallocationService.test.ts +25 -14
  69. package/src/services/PaymentReallocationService.ts +29 -10
  70. package/src/services/PaymentService.ts +4 -16
  71. package/src/services/PlatformMembershipService.ts +8 -4
  72. package/src/services/RegistrationService.ts +66 -2
  73. package/src/sql-filters/base-registration-filter-compilers.ts +43 -0
  74. package/src/sql-filters/groups.ts +67 -0
  75. package/src/sql-filters/members.ts +33 -58
  76. package/src/sql-filters/organization-registration-periods.ts +8 -0
  77. package/src/sql-filters/registration-periods.ts +8 -0
  78. package/src/sql-filters/registrations.ts +11 -22
  79. package/src/sql-sorters/groups.ts +24 -0
  80. package/src/sql-sorters/organization-registration-periods.ts +24 -0
  81. package/src/sql-sorters/registration-periods.ts +47 -0
  82. package/src/sql-sorters/registrations.ts +77 -0
  83. package/tests/actions/patchOrganizationMember.ts +27 -0
  84. package/tests/actions/patchPaymentStatus.ts +45 -0
  85. package/tests/actions/patchUserMember.ts +27 -0
  86. package/tests/assertions/assertBalances.ts +49 -0
  87. package/tests/e2e/api-rate-limits.test.ts +5 -5
  88. package/tests/e2e/bundle-discounts.test.ts +4060 -0
  89. package/tests/e2e/charge-members.test.ts +27 -24
  90. package/tests/e2e/documents.test.ts +398 -0
  91. package/tests/e2e/register.test.ts +292 -312
  92. package/tests/helpers/PayconiqMocker.ts +55 -0
  93. package/tests/init/index.ts +5 -0
  94. package/tests/init/initAdmin.ts +14 -0
  95. package/tests/init/initBundleDiscount.ts +47 -0
  96. package/tests/init/initPayconiq.ts +9 -0
  97. package/tests/init/initPlatformAdmin.ts +13 -0
  98. package/tests/init/initStripe.ts +21 -0
  99. package/tests/jest.setup.ts +29 -0
  100. package/src/seeds-temporary/1736266448-recall-balance-item-price-paid.ts +0 -70
@@ -1,4 +1,4 @@
1
- import { XlsxBuiltInNumberFormat, XlsxTransformerSheet } from '@stamhoofd/excel-writer';
1
+ import { XlsxBuiltInNumberFormat, XlsxTransformerColumn, XlsxTransformerSheet } from '@stamhoofd/excel-writer';
2
2
  import { Platform } from '@stamhoofd/models';
3
3
  import { ExcelExportType, Gender, GroupType, LimitedFilteredRequest, PlatformFamily, PlatformMember, Platform as PlatformStruct, UnencodeablePaginatedResponse } from '@stamhoofd/structures';
4
4
  import { Formatter } from '@stamhoofd/utility';
@@ -8,258 +8,259 @@ import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures';
8
8
  import { Context } from '../helpers/Context';
9
9
  import { XlsxTransformerColumnHelper } from '../helpers/XlsxTransformerColumnHelper';
10
10
 
11
- // Assign to a typed variable to assure we have correct type checking in place
12
- const sheet: XlsxTransformerSheet<PlatformMember, PlatformMember> = {
13
- id: 'members',
14
- name: $t(`fb35c140-e936-4e91-aa92-ef4dfc59fb51`),
15
- columns: [
16
- {
17
- id: 'id',
18
- name: $t(`29360811-3663-496c-8d8f-c9fdf9467a74`),
19
- width: 40,
20
- getValue: ({ patchedMember: object }: PlatformMember) => ({
21
- value: object.id,
22
- }),
23
- },
24
- {
25
- id: 'memberNumber',
26
- name: $t(`cc1cf4a7-0bd2-4fa7-8ff2-0a12470a738d`),
27
- width: 20,
28
- getValue: ({ patchedMember: object }: PlatformMember) => ({
29
- value: object.details.memberNumber,
30
- }),
31
- },
32
- {
33
- id: 'firstName',
34
- name: $t(`efca0579-0543-4636-a996-384bc9f0527e`),
35
- width: 20,
36
- getValue: ({ patchedMember: object }: PlatformMember) => ({
37
- value: object.details.firstName,
38
- }),
39
- },
40
- {
41
- id: 'lastName',
42
- name: $t(`4a5e438e-08a1-411e-9b66-410eea7ded73`),
43
- width: 20,
44
- getValue: ({ patchedMember: object }: PlatformMember) => ({
45
- value: object.details.lastName,
46
- }),
47
- },
48
- {
49
- id: 'birthDay',
50
- name: $t(`7d7b5a21-105a-41a1-b511-8639b59024a4`),
51
- width: 20,
52
- getValue: ({ patchedMember: object }: PlatformMember) => ({
53
- value: object.details.birthDay,
54
- style: {
55
- numberFormat: {
56
- id: XlsxBuiltInNumberFormat.DateSlash,
57
- },
11
+ export const baseMemberColumns: XlsxTransformerColumn<PlatformMember>[] = [
12
+ {
13
+ id: 'memberNumber',
14
+ name: $t(`cc1cf4a7-0bd2-4fa7-8ff2-0a12470a738d`),
15
+ width: 20,
16
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
17
+ value: object.details.memberNumber,
18
+ }),
19
+ },
20
+ {
21
+ id: 'firstName',
22
+ name: $t(`efca0579-0543-4636-a996-384bc9f0527e`),
23
+ width: 20,
24
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
25
+ value: object.details.firstName,
26
+ }),
27
+ },
28
+ {
29
+ id: 'lastName',
30
+ name: $t(`4a5e438e-08a1-411e-9b66-410eea7ded73`),
31
+ width: 20,
32
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
33
+ value: object.details.lastName,
34
+ }),
35
+ },
36
+ {
37
+ id: 'birthDay',
38
+ name: $t(`7d7b5a21-105a-41a1-b511-8639b59024a4`),
39
+ width: 20,
40
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
41
+ value: object.details.birthDay,
42
+ style: {
43
+ numberFormat: {
44
+ id: XlsxBuiltInNumberFormat.DateSlash,
58
45
  },
59
- }),
60
- },
61
- {
62
- id: 'age',
63
- name: $t(`992b79e9-8c6e-4096-aa59-9e5f546eac41`),
64
- width: 20,
65
- getValue: ({ patchedMember: object }: PlatformMember) => ({
66
- value: object.details.age,
67
- }),
68
- },
69
- {
70
- id: 'gender',
71
- name: $t(`a39908e8-62b0-487c-9a03-57dd62326d94`),
72
- width: 20,
73
- getValue: ({ patchedMember: object }: PlatformMember) => {
74
- const gender = object.details.gender;
75
-
76
- return ({
77
- value: formatGender(gender),
78
- });
79
46
  },
47
+ }),
48
+ },
49
+ {
50
+ id: 'age',
51
+ name: $t(`992b79e9-8c6e-4096-aa59-9e5f546eac41`),
52
+ width: 20,
53
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
54
+ value: object.details.age,
55
+ }),
56
+ },
57
+ {
58
+ id: 'gender',
59
+ name: $t(`a39908e8-62b0-487c-9a03-57dd62326d94`),
60
+ width: 20,
61
+ getValue: ({ patchedMember: object }: PlatformMember) => {
62
+ const gender = object.details.gender;
63
+
64
+ return ({
65
+ value: formatGender(gender),
66
+ });
80
67
  },
81
- {
82
- id: 'phone',
83
- name: $t(`856aaa1c-bc62-4e45-9ae5-4c7e7dca23ab`),
84
- width: 20,
85
- getValue: ({ patchedMember: object }: PlatformMember) => ({
86
- value: object.details.phone,
87
- }),
88
- },
89
- {
90
- id: 'email',
91
- name: $t(`82f4b6ed-afee-4655-9f07-22802e0e7ad9`),
92
- width: 40,
93
- getValue: ({ patchedMember: object }: PlatformMember) => ({
94
- value: object.details.email,
95
- }),
96
- },
97
- XlsxTransformerColumnHelper.createAddressColumns<PlatformMember>({
98
- matchId: 'address',
99
- getAddress: ({ patchedMember: object }: PlatformMember) => {
100
- // get member address if exists
101
- const memberAddress = object.details.address;
102
- if (memberAddress) {
103
- return memberAddress;
104
- }
105
-
106
- // else get address of first parent with address
107
- for (const parent of object.details.parents) {
108
- if (parent.address) {
109
- return parent.address;
110
- }
68
+ },
69
+ {
70
+ id: 'phone',
71
+ name: $t(`856aaa1c-bc62-4e45-9ae5-4c7e7dca23ab`),
72
+ width: 20,
73
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
74
+ value: object.details.phone,
75
+ }),
76
+ },
77
+ {
78
+ id: 'email',
79
+ name: $t(`82f4b6ed-afee-4655-9f07-22802e0e7ad9`),
80
+ width: 40,
81
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
82
+ value: object.details.email,
83
+ }),
84
+ },
85
+ XlsxTransformerColumnHelper.createAddressColumns<PlatformMember>({
86
+ matchId: 'address',
87
+ getAddress: ({ patchedMember: object }: PlatformMember) => {
88
+ // get member address if exists
89
+ const memberAddress = object.details.address;
90
+ if (memberAddress) {
91
+ return memberAddress;
92
+ }
93
+
94
+ // else get address of first parent with address
95
+ for (const parent of object.details.parents) {
96
+ if (parent.address) {
97
+ return parent.address;
111
98
  }
99
+ }
112
100
 
113
- return null;
114
- },
115
- }),
116
- {
117
- id: 'securityCode',
118
- name: $t(`0fa4253f-1cfd-4394-93b4-dfba8da04738`),
119
- width: 20,
120
- getValue: ({ patchedMember: object }: PlatformMember) => ({
121
- value: object.details.securityCode,
122
- }),
101
+ return null;
123
102
  },
124
- {
125
- id: 'uitpasNumber',
126
- name: $t(`87c1a48c-fef5-44c3-ae56-c83463fcfb84`),
127
- width: 20,
128
- getValue: ({ patchedMember: object }: PlatformMember) => ({
129
- value: object.details.uitpasNumber,
130
- }),
103
+ }),
104
+ {
105
+ id: 'securityCode',
106
+ name: $t(`0fa4253f-1cfd-4394-93b4-dfba8da04738`),
107
+ width: 20,
108
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
109
+ value: object.details.securityCode,
110
+ }),
111
+ },
112
+ {
113
+ id: 'uitpasNumber',
114
+ name: $t(`87c1a48c-fef5-44c3-ae56-c83463fcfb84`),
115
+ width: 20,
116
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
117
+ value: object.details.uitpasNumber,
118
+ }),
119
+ },
120
+ {
121
+ id: 'requiresFinancialSupport',
122
+ // todo: use correct term
123
+ name: $t(`030be384-9014-410c-87ba-e04920c26111`),
124
+ width: 20,
125
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
126
+ value: XlsxTransformerColumnHelper.formatBoolean(object.details.requiresFinancialSupport?.value),
127
+ }),
128
+ },
129
+ {
130
+ id: 'notes',
131
+ name: $t(`8c38d163-c01b-488f-8729-11de8af7d098`),
132
+ width: 20,
133
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
134
+ value: object.details.notes,
135
+ }),
136
+ },
137
+ {
138
+ id: 'organization',
139
+ name: $t(`afd7843d-f355-445b-a158-ddacf469a5b1`),
140
+ width: 40,
141
+ getValue: (member: PlatformMember) => {
142
+ const organizations = member.filterOrganizations({ currentPeriod: true, types: [GroupType.Membership] });
143
+ const str = Formatter.joinLast(organizations.map(o => o.name).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
144
+
145
+ return {
146
+ value: str,
147
+ };
131
148
  },
132
- {
133
- id: 'requiresFinancialSupport',
134
- // todo: use correct term
135
- name: $t(`030be384-9014-410c-87ba-e04920c26111`),
136
- width: 20,
137
- getValue: ({ patchedMember: object }: PlatformMember) => ({
138
- value: XlsxTransformerColumnHelper.formatBoolean(object.details.requiresFinancialSupport?.value),
139
- }),
149
+ },
150
+ {
151
+ id: 'uri',
152
+ name: $t(`27cfaf26-6b88-4ebc-a50a-627a9f0f9e64`),
153
+ width: 30,
154
+ getValue: (member: PlatformMember) => {
155
+ const organizations = member.filterOrganizations({ currentPeriod: true, types: [GroupType.Membership] });
156
+ const str = Formatter.joinLast(organizations.map(o => o.uri).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
157
+
158
+ return {
159
+ value: str,
160
+ };
140
161
  },
141
- {
142
- id: 'notes',
143
- name: $t(`8c38d163-c01b-488f-8729-11de8af7d098`),
144
- width: 20,
145
- getValue: ({ patchedMember: object }: PlatformMember) => ({
146
- value: object.details.notes,
147
- }),
162
+ },
163
+ {
164
+ id: 'group',
165
+ name: $t(`0c230001-c3be-4a8e-8eab-23dc3fd96e52`),
166
+ width: 40,
167
+ getValue: (member: PlatformMember) => {
168
+ const groups = member.filterRegistrations({ currentPeriod: true, types: [GroupType.Membership], organizationId: Context.organization?.id });
169
+ const str = Formatter.joinLast(Formatter.uniqueArray(groups.map(o => o.group.settings.name.toString())).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
170
+
171
+ return {
172
+ value: str,
173
+ };
148
174
  },
149
-
150
- {
151
- id: 'organization',
152
- name: $t(`afd7843d-f355-445b-a158-ddacf469a5b1`),
153
- width: 40,
154
- getValue: (member: PlatformMember) => {
155
- const organizations = member.filterOrganizations({ currentPeriod: true, types: [GroupType.Membership] });
156
- const str = Formatter.joinLast(organizations.map(o => o.name).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
157
-
158
- return {
159
- value: str,
160
- };
161
- },
175
+ },
176
+ {
177
+ id: 'defaultAgeGroup',
178
+ name: $t(`0ef2bbb3-0b3c-411a-8901-a454cff1f839`),
179
+ width: 40,
180
+ getValue: (member: PlatformMember) => {
181
+ const groups = member.filterRegistrations({ currentPeriod: true, types: [GroupType.Membership], organizationId: Context.organization?.id });
182
+ const defaultAgeGroupIds = Formatter.uniqueArray(groups.filter(o => o.group.defaultAgeGroupId));
183
+ const defaultAgeGroups = defaultAgeGroupIds.map(o => PlatformStruct.shared.config.defaultAgeGroups.find(g => g.id === o.group.defaultAgeGroupId)?.name ?? $t(`6aeee253-beb2-4548-b60e-30836afcf2f0`));
184
+ const str = Formatter.joinLast(Formatter.uniqueArray(defaultAgeGroups).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
185
+
186
+ return {
187
+ value: str,
188
+ };
162
189
  },
190
+ },
191
+ {
192
+ id: 'nationalRegisterNumber',
193
+ name: $t(`00881b27-7501-4c56-98de-55618be2bf11`),
194
+ width: 30,
195
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
196
+ value: object.details.nationalRegisterNumber?.toString() ?? '',
197
+ }),
198
+ },
163
199
 
164
- {
165
- id: 'uri',
166
- name: $t(`27cfaf26-6b88-4ebc-a50a-627a9f0f9e64`),
167
- width: 30,
168
- getValue: (member: PlatformMember) => {
169
- const organizations = member.filterOrganizations({ currentPeriod: true, types: [GroupType.Membership] });
170
- const str = Formatter.joinLast(organizations.map(o => o.uri).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
171
-
172
- return {
173
- value: str,
174
- };
175
- },
176
- },
200
+ ...XlsxTransformerColumnHelper.creatColumnsForParents(),
177
201
 
178
- {
179
- id: 'group',
180
- name: $t(`0c230001-c3be-4a8e-8eab-23dc3fd96e52`),
181
- width: 40,
182
- getValue: (member: PlatformMember) => {
183
- const groups = member.filterRegistrations({ currentPeriod: true, types: [GroupType.Membership], organizationId: Context.organization?.id });
184
- const str = Formatter.joinLast(Formatter.uniqueArray(groups.map(o => o.group.settings.name.toString())).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
202
+ // unverified data
203
+ {
204
+ id: 'unverifiedPhones',
205
+ name: $t(`506a2bd8-bd5b-48ae-8480-fbb9e9faa683`),
206
+ width: 20,
207
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
208
+ value: object.details.unverifiedPhones.join(', '),
209
+ }),
210
+ },
211
+ {
212
+ id: 'unverifiedEmails',
213
+ name: $t(`62b19231-9770-4553-ad25-500df57ccf84`),
214
+ width: 20,
215
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
216
+ value: object.details.unverifiedEmails.join(', '),
217
+ }),
218
+ },
219
+ ...XlsxTransformerColumnHelper.createColumnsForAddresses<PlatformMember>({
220
+ matchIdStart: 'unverifiedAddresses',
221
+ getAddresses: object => object.patchedMember.details.unverifiedAddresses,
222
+ limit: 2,
223
+ }),
224
+ {
225
+ id: 'unverifiedAddresses',
226
+ name: $t(`b45f1017-e859-43da-8829-a21639a9e70d`),
227
+ width: 20,
228
+ getValue: ({ patchedMember: object }: PlatformMember) => ({
229
+ value: object.details.unverifiedAddresses.map(a => a.toString()).join('; '),
230
+ }),
231
+ },
185
232
 
186
- return {
187
- value: str,
188
- };
189
- },
233
+ // Dynamic records
234
+ XlsxTransformerColumnHelper.createRecordAnswersColumns({
235
+ matchId: 'recordAnswers',
236
+ getRecordAnswers: ({ patchedMember: object }: PlatformMember) => object.details.recordAnswers,
237
+ getRecordCategories: () => {
238
+ const platform = PlatformStruct.shared;
239
+ const organization = Context.organization;
240
+
241
+ return [
242
+ ...(organization?.meta.recordsConfiguration.recordCategories ?? []),
243
+ ...platform.config.recordsConfiguration.recordCategories,
244
+ ];
190
245
  },
246
+ }),
247
+ ];
191
248
 
249
+ // Assign to a typed variable to assure we have correct type checking in place
250
+ const sheet: XlsxTransformerSheet<PlatformMember, PlatformMember> = {
251
+ id: 'members',
252
+ name: $t(`fb35c140-e936-4e91-aa92-ef4dfc59fb51`),
253
+ columns: [
192
254
  {
193
- id: 'defaultAgeGroup',
194
- name: $t(`0ef2bbb3-0b3c-411a-8901-a454cff1f839`),
255
+ id: 'id',
256
+ name: $t(`29360811-3663-496c-8d8f-c9fdf9467a74`),
195
257
  width: 40,
196
- getValue: (member: PlatformMember) => {
197
- const groups = member.filterRegistrations({ currentPeriod: true, types: [GroupType.Membership], organizationId: Context.organization?.id });
198
- const defaultAgeGroupIds = Formatter.uniqueArray(groups.filter(o => o.group.defaultAgeGroupId));
199
- const defaultAgeGroups = defaultAgeGroupIds.map(o => PlatformStruct.shared.config.defaultAgeGroups.find(g => g.id === o.group.defaultAgeGroupId)?.name ?? $t(`6aeee253-beb2-4548-b60e-30836afcf2f0`));
200
- const str = Formatter.joinLast(Formatter.uniqueArray(defaultAgeGroups).sort(), ', ', ' ' + $t(`c1843768-2bf4-42f2-baa4-42f49028463d`) + ' ') || Context.i18n.$t('1a16a32a-7ee4-455d-af3d-6073821efa8f');
201
-
202
- return {
203
- value: str,
204
- };
205
- },
206
- },
207
- {
208
- id: 'nationalRegisterNumber',
209
- name: $t(`00881b27-7501-4c56-98de-55618be2bf11`),
210
- width: 30,
211
258
  getValue: ({ patchedMember: object }: PlatformMember) => ({
212
- value: object.details.nationalRegisterNumber?.toString() ?? '',
213
- }),
214
- },
215
-
216
- ...XlsxTransformerColumnHelper.creatColumnsForParents(),
217
-
218
- // unverified data
219
- {
220
- id: 'unverifiedPhones',
221
- name: $t(`506a2bd8-bd5b-48ae-8480-fbb9e9faa683`),
222
- width: 20,
223
- getValue: ({ patchedMember: object }: PlatformMember) => ({
224
- value: object.details.unverifiedPhones.join(', '),
225
- }),
226
- },
227
- {
228
- id: 'unverifiedEmails',
229
- name: $t(`62b19231-9770-4553-ad25-500df57ccf84`),
230
- width: 20,
231
- getValue: ({ patchedMember: object }: PlatformMember) => ({
232
- value: object.details.unverifiedEmails.join(', '),
233
- }),
234
- },
235
- ...XlsxTransformerColumnHelper.createColumnsForAddresses<PlatformMember>({
236
- matchIdStart: 'unverifiedAddresses',
237
- getAddresses: object => object.patchedMember.details.unverifiedAddresses,
238
- limit: 2,
239
- }),
240
- {
241
- id: 'unverifiedAddresses',
242
- name: $t(`b45f1017-e859-43da-8829-a21639a9e70d`),
243
- width: 20,
244
- getValue: ({ patchedMember: object }: PlatformMember) => ({
245
- value: object.details.unverifiedAddresses.map(a => a.toString()).join('; '),
259
+ value: object.id,
246
260
  }),
247
261
  },
248
262
 
249
- // Dynamic records
250
- XlsxTransformerColumnHelper.createRecordAnswersColumns({
251
- matchId: 'recordAnswers',
252
- getRecordAnswers: ({ patchedMember: object }: PlatformMember) => object.details.recordAnswers,
253
- getRecordCategories: () => {
254
- const platform = PlatformStruct.shared;
255
- const organization = Context.organization;
256
-
257
- return [
258
- ...(organization?.meta.recordsConfiguration.recordCategories ?? []),
259
- ...platform.config.recordsConfiguration.recordCategories,
260
- ];
261
- },
262
- }),
263
+ ...baseMemberColumns,
263
264
 
264
265
  // Registration records
265
266
  {
@@ -184,7 +184,7 @@ function getBalanceItemColumns(): XlsxTransformerColumn<PaymentWithItem>[] {
184
184
  name: getBalanceItemRelationTypeName(type),
185
185
  width: 35,
186
186
  getValue: (object: PaymentWithItem) => ({
187
- value: object.balanceItemPayment.balanceItem.relations.get(type)?.name || '',
187
+ value: object.balanceItemPayment.balanceItem.relations.get(type)?.name?.toString() || '',
188
188
  }),
189
189
  },
190
190
  ];
@@ -214,7 +214,7 @@ function getBalanceItemColumns(): XlsxTransformerColumn<ReceivableBalanceWithIte
214
214
  name: getBalanceItemRelationTypeName(type),
215
215
  width: 35,
216
216
  getValue: (object: ReceivableBalanceWithItem) => ({
217
- value: object.balanceItem.relations.get(type)?.name || '',
217
+ value: object.balanceItem.relations.get(type)?.name?.toString() || '',
218
218
  }),
219
219
  },
220
220
  ];
@@ -0,0 +1,153 @@
1
+ import { XlsxBuiltInNumberFormat, XlsxTransformerSheet } from '@stamhoofd/excel-writer';
2
+ import { Platform } from '@stamhoofd/models';
3
+ import { ExcelExportType, LimitedFilteredRequest, PlatformMember, PlatformRegistration, UnencodeablePaginatedResponse } from '@stamhoofd/structures';
4
+ import { ExportToExcelEndpoint } from '../endpoints/global/files/ExportToExcelEndpoint';
5
+ import { GetRegistrationsEndpoint } from '../endpoints/global/registration/GetRegistrationsEndpoint';
6
+ import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures';
7
+ import { Context } from '../helpers/Context';
8
+ import { XlsxTransformerColumnHelper } from '../helpers/XlsxTransformerColumnHelper';
9
+ import { baseMemberColumns } from './members';
10
+
11
+ // Assign to a typed variable to assure we have correct type checking in place
12
+ const sheet: XlsxTransformerSheet<PlatformMember, PlatformRegistration> = {
13
+ id: 'registrations',
14
+ name: $t('938926c1-cb27-427d-aabd-638c5ec1d14a'),
15
+ columns: [
16
+ {
17
+ id: 'id',
18
+ name: $t(`29360811-3663-496c-8d8f-c9fdf9467a74`),
19
+ width: 40,
20
+ getValue: (registration: PlatformRegistration) => ({
21
+ value: registration.id,
22
+ }),
23
+ },
24
+ {
25
+ id: 'price',
26
+ name: $t(`dcc53f25-f0e9-4e3e-9f4f-e8cfa4e88755`),
27
+ width: 30,
28
+ getValue: (registration: PlatformRegistration) => {
29
+ return {
30
+ value: registration.groupPrice.name.toString(),
31
+ };
32
+ },
33
+ },
34
+ // option menu
35
+ {
36
+ match(id) {
37
+ if (!id.startsWith('optionMenu.')) {
38
+ return;
39
+ }
40
+
41
+ const splitted = id.split('.');
42
+
43
+ if (splitted.length < 2) {
44
+ return;
45
+ }
46
+
47
+ const menuId = splitted[1];
48
+
49
+ if (splitted.length > 2) {
50
+ const optionId = splitted[2];
51
+ const returnAmount = splitted.length > 3 && splitted[3] === 'amount';
52
+
53
+ return [{
54
+ id: `optionMenu.${menuId}.${optionId}${returnAmount ? '.amount' : ''}`,
55
+ name: $t(`d89d7fcd-ecf3-40a2-afb6-f51c3f6c9bc6`),
56
+ width: 30,
57
+ getValue: (registration: PlatformRegistration) => {
58
+ const options = registration.options.filter(o => o.optionMenu.id === menuId && o.option.id === optionId);
59
+
60
+ if (!options.length) {
61
+ return {
62
+ value: '',
63
+ };
64
+ }
65
+
66
+ return {
67
+ style: options.length === 1 && returnAmount
68
+ ? {
69
+ numberFormat: {
70
+ id: XlsxBuiltInNumberFormat.Number,
71
+ },
72
+ }
73
+ : {},
74
+ value: options.length === 1 && returnAmount ? options[0].amount : options.map(option => returnAmount ? option.amount : option).join(', '),
75
+ };
76
+ },
77
+ }];
78
+ }
79
+
80
+ return [
81
+ {
82
+ id: `optionMenu.${menuId}`,
83
+ name: $t(`99e37a95-6a68-4921-a8db-08fb136f87dd`),
84
+ width: 30,
85
+ getValue: (registration: PlatformRegistration) => {
86
+ const options = registration.options.filter(o => o.optionMenu.id === menuId);
87
+
88
+ if (!options.length) {
89
+ return {
90
+ value: '',
91
+ };
92
+ }
93
+
94
+ return {
95
+ value: options.map(option => (option.amount > 1 ? `${option.amount}x ` : '') + option.option.name).join(', '),
96
+ };
97
+ },
98
+ },
99
+ ];
100
+ },
101
+ },
102
+ // recordAnswers
103
+ {
104
+ match(id) {
105
+ if (!id.startsWith('recordAnswers.')) {
106
+ return;
107
+ }
108
+
109
+ const splitted = id.split('.');
110
+
111
+ if (splitted.length < 2) {
112
+ return;
113
+ }
114
+
115
+ const recordId = splitted[1];
116
+ return [
117
+ {
118
+ id: `recordAnswers.${recordId}`,
119
+ name: 'Vraag',
120
+ width: 35,
121
+ getValue: (registration: PlatformRegistration) => {
122
+ return {
123
+ value: registration.recordAnswers.get(recordId)?.excelValues[0]?.value ?? '',
124
+ };
125
+ },
126
+ },
127
+ ];
128
+ },
129
+ },
130
+ ...baseMemberColumns.map(column => XlsxTransformerColumnHelper.transformColumnForProperty({
131
+ column,
132
+ key: 'member',
133
+ getPropertyValue: (registration: PlatformRegistration) => registration.member,
134
+ })),
135
+ ],
136
+ };
137
+
138
+ ExportToExcelEndpoint.loaders.set(ExcelExportType.Registrations, {
139
+ fetch: async (query: LimitedFilteredRequest) => {
140
+ const result = await GetRegistrationsEndpoint.buildData(query);
141
+
142
+ return new UnencodeablePaginatedResponse({
143
+ results: PlatformRegistration.createSingles(result.results, {
144
+ contextOrganization: Context.organization ? (await AuthenticatedStructures.organization(Context.organization)) : null,
145
+ platform: await Platform.getSharedStruct(),
146
+ }),
147
+ next: result.next,
148
+ });
149
+ },
150
+ sheets: [
151
+ sheet,
152
+ ],
153
+ });