@stamhoofd/backend 2.15.0 → 2.16.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 (31) hide show
  1. package/.env.template.json +2 -1
  2. package/index.ts +15 -1
  3. package/package.json +14 -12
  4. package/src/email-recipient-loaders/members.ts +61 -0
  5. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
  6. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +5 -183
  7. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +163 -0
  8. package/src/endpoints/global/files/GetFileCache.ts +69 -0
  9. package/src/endpoints/global/files/UploadFile.ts +4 -1
  10. package/src/endpoints/global/files/UploadImage.ts +14 -2
  11. package/src/endpoints/global/members/GetMembersEndpoint.ts +12 -299
  12. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +22 -2
  13. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -134
  14. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -3
  15. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -3
  16. package/src/excel-loaders/members.ts +101 -0
  17. package/src/excel-loaders/payments.ts +539 -0
  18. package/src/helpers/AdminPermissionChecker.ts +0 -3
  19. package/src/helpers/AuthenticatedStructures.ts +2 -0
  20. package/src/helpers/FileCache.ts +158 -0
  21. package/src/helpers/fetchToAsyncIterator.ts +34 -0
  22. package/src/sql-filters/balance-item-payments.ts +13 -0
  23. package/src/sql-filters/members.ts +179 -0
  24. package/src/sql-filters/organizations.ts +115 -0
  25. package/src/sql-filters/payments.ts +78 -0
  26. package/src/sql-filters/registrations.ts +24 -0
  27. package/src/sql-sorters/members.ts +46 -0
  28. package/src/sql-sorters/organizations.ts +71 -0
  29. package/src/sql-sorters/payments.ts +50 -0
  30. package/tsconfig.json +3 -4
  31. package/src/endpoints/organization/dashboard/payments/legacy/GetPaymentsEndpoint.ts +0 -170
@@ -0,0 +1,539 @@
1
+ import { XlsxBuiltInNumberFormat, XlsxTransformerColumn } from "@stamhoofd/excel-writer";
2
+ import { StripeAccount as StripeAccountStruct, BalanceItemPaymentDetailed, BalanceItemRelationType, CountryHelper, ExcelExportType, getBalanceItemRelationTypeName, getBalanceItemTypeName, PaymentGeneral, PaymentMethodHelper, PaymentStatusHelper, PaginatedResponse, PaymentProvider } from "@stamhoofd/structures";
3
+ import { ExportToExcelEndpoint } from "../endpoints/global/files/ExportToExcelEndpoint";
4
+ import { GetPaymentsEndpoint } from "../endpoints/organization/dashboard/payments/GetPaymentsEndpoint";
5
+ import { Formatter } from "@stamhoofd/utility";
6
+ import { StripeAccount } from "@stamhoofd/models";
7
+ import { field } from "@simonbackx/simple-encoding";
8
+
9
+ type PaymentWithItem = {
10
+ payment: PaymentGeneral,
11
+ balanceItemPayment: BalanceItemPaymentDetailed
12
+ }
13
+
14
+ export class PaymentGeneralWithStripeAccount extends PaymentGeneral {
15
+ @field({ decoder: StripeAccountStruct, nullable: true })
16
+ stripeAccount: StripeAccountStruct|null = null
17
+ }
18
+
19
+ ExportToExcelEndpoint.loaders.set(ExcelExportType.Payments, {
20
+ fetch: async (requestQuery) => {
21
+ const data = await GetPaymentsEndpoint.buildData(requestQuery);
22
+
23
+ // Also load Stripe Account Ids
24
+ const stripeAccountIds = Formatter.uniqueArray(data.results.map(p => p.stripeAccountId).filter(id => id !== null));
25
+
26
+ let accounts: StripeAccountStruct[] = [];
27
+ if (stripeAccountIds.length > 0) {
28
+ accounts = (await StripeAccount.getByIDs(...stripeAccountIds)).map(s => StripeAccountStruct.create(s));
29
+ }
30
+
31
+ return new PaginatedResponse({
32
+ ...data,
33
+ results: data.results.map(p => {
34
+ const payment = PaymentGeneralWithStripeAccount.create(p);
35
+ payment.stripeAccount = p.stripeAccountId ? (accounts.find(a => a.id === p.stripeAccountId) ?? null) : null;
36
+ return payment;
37
+ })
38
+ })
39
+ },
40
+ sheets: [
41
+ {
42
+ id: 'payments',
43
+ name: 'Betalingen',
44
+ columns: [
45
+ ...getGeneralColumns(),
46
+ ...getInvoiceColumns(),
47
+ ...getSettlementColumns(),
48
+ ...getStripeColumns(),
49
+ ...getTransferColumns(),
50
+ ]
51
+ },
52
+ {
53
+ id: 'balanceItemPayments',
54
+ name: 'Betaallijnen',
55
+ transform: (data: PaymentGeneral) => data.balanceItemPayments.map(p => ({
56
+ payment: data,
57
+ balanceItemPayment: p
58
+ })),
59
+ columns: [
60
+ ...getBalanceItemColumns()
61
+ ]
62
+ }
63
+ ]
64
+ })
65
+
66
+ function getBalanceItemColumns(): XlsxTransformerColumn<PaymentWithItem>[] {
67
+ return [
68
+ {
69
+ id: 'id',
70
+ name: 'ID',
71
+ width: 40,
72
+ getValue: (object: PaymentWithItem) => ({
73
+ value: object.balanceItemPayment.id,
74
+ style: {
75
+ font: {
76
+ bold: true
77
+ }
78
+ }
79
+ })
80
+ },
81
+ {
82
+ id: 'paymentId',
83
+ name: 'Betaling ID',
84
+ width: 40,
85
+ getValue: (object: PaymentWithItem) => ({
86
+ value: object.payment.id
87
+ })
88
+ },
89
+ {
90
+ id: 'balanceItem.type',
91
+ name: 'Type',
92
+ width: 30,
93
+ getValue: (object: PaymentWithItem) => ({
94
+ value: getBalanceItemTypeName(object.balanceItemPayment.balanceItem.type)
95
+ })
96
+ },
97
+ {
98
+ id: 'balanceItem.description',
99
+ name: 'Beschrijving',
100
+ width: 40,
101
+ getValue: (object: PaymentWithItem) => ({
102
+ value: object.balanceItemPayment.balanceItem.description
103
+ })
104
+ },
105
+ {
106
+ match: (id) => {
107
+ if (id.startsWith('balanceItem.relations.')) {
108
+ const type = id.split('.')[2] as BalanceItemRelationType;
109
+ if (Object.values(BalanceItemRelationType).includes(type)) {
110
+ return [
111
+ {
112
+ id: `balanceItem.relations.${type}`,
113
+ name: getBalanceItemRelationTypeName(type),
114
+ width: 35,
115
+ getValue: (object: PaymentWithItem) => ({
116
+ value: object.balanceItemPayment.balanceItem.relations.get(type)?.name || ''
117
+ })
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ }
123
+ },
124
+ {
125
+ id: 'amount',
126
+ name: 'Aantal',
127
+ width: 20,
128
+ getValue: (object: PaymentWithItem) => ({
129
+ value: object.balanceItemPayment.amount,
130
+ style: {
131
+ numberFormat: {
132
+ id: XlsxBuiltInNumberFormat.Number
133
+ }
134
+ }
135
+ })
136
+ },
137
+ {
138
+ id: 'unitPrice',
139
+ name: 'Eenheidsprijs',
140
+ width: 20,
141
+ getValue: (object: PaymentWithItem) => ({
142
+ value: object.balanceItemPayment.unitPrice / 100,
143
+ style: {
144
+ numberFormat: {
145
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
146
+ }
147
+ }
148
+ })
149
+ },
150
+ {
151
+ id: 'price',
152
+ name: 'Prijs',
153
+ width: 20,
154
+ getValue: (object: PaymentWithItem) => ({
155
+ value: object.balanceItemPayment.price / 100,
156
+ style: {
157
+ numberFormat: {
158
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
159
+ }
160
+ }
161
+ })
162
+ },
163
+ ]
164
+ }
165
+
166
+
167
+ function getGeneralColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
168
+ return [
169
+ {
170
+ id: 'id',
171
+ name: 'ID',
172
+ width: 40,
173
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
174
+ value: object.id,
175
+ style: {
176
+ font: {
177
+ bold: true
178
+ }
179
+ }
180
+ })
181
+ },
182
+ {
183
+ id: 'price',
184
+ name: 'Bedrag',
185
+ width: 10,
186
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
187
+ value: object.price / 100,
188
+ style: {
189
+ numberFormat: {
190
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
191
+ }
192
+ }
193
+ })
194
+ },
195
+ {
196
+ id: 'status',
197
+ name: 'Status',
198
+ width: 18,
199
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
200
+ value: PaymentStatusHelper.getNameCapitalized(object.status)
201
+ })
202
+ },
203
+ {
204
+ id: 'method',
205
+ name: 'Betaalmethode',
206
+ width: 18,
207
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
208
+ value: PaymentMethodHelper.getNameCapitalized(object.method)
209
+ })
210
+ },
211
+ {
212
+ id: 'provider',
213
+ name: 'Betaalprovider',
214
+ width: 16,
215
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
216
+ value: object.provider
217
+ })
218
+ },
219
+ {
220
+ id: 'createdAt',
221
+ name: 'Aangemaakt op',
222
+ width: 16,
223
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
224
+ value: object.createdAt,
225
+ style: {
226
+ numberFormat: {
227
+ id: XlsxBuiltInNumberFormat.DateTimeSlash
228
+ }
229
+ }
230
+ })
231
+ },
232
+ {
233
+ id: 'paidAt',
234
+ name: 'Betaald op',
235
+ width: 16,
236
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
237
+ value: object.paidAt,
238
+ style: {
239
+ numberFormat: {
240
+ id: XlsxBuiltInNumberFormat.DateTimeSlash
241
+ }
242
+ }
243
+ })
244
+ },
245
+ {
246
+ id: 'description',
247
+ name: 'Detail',
248
+ width: 60,
249
+ getValue: (object: PaymentGeneralWithStripeAccount) => ({
250
+ value: object.balanceItemPayments.map(p => p.toString()).join('\n'),
251
+ style: {
252
+ alignment: {
253
+ wrapText: true
254
+ }
255
+ }
256
+ })
257
+ },
258
+ ]
259
+ }
260
+
261
+ function getSettlementColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
262
+ return [
263
+ {
264
+ id: 'settlement.reference',
265
+ name: 'Uitbetalingsmededeling',
266
+ width: 21,
267
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
268
+ return {
269
+ value: object.settlement?.reference || ''
270
+ }
271
+ }
272
+ },
273
+ {
274
+ id: 'settlement.settledAt',
275
+ name: 'Uitbetalingsdatum',
276
+ width: 16,
277
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
278
+ return {
279
+ value: object.settlement?.settledAt ?? null,
280
+ style: {
281
+ numberFormat: {
282
+ id: XlsxBuiltInNumberFormat.DateSlash
283
+ }
284
+ }
285
+ }
286
+ }
287
+ },
288
+ {
289
+ id: 'settlement.amount',
290
+ name: 'Uitbetalingsbedrag',
291
+ width: 18,
292
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
293
+ return {
294
+ value: object.settlement?.amount !== undefined ? (object.settlement?.amount / 100) : null,
295
+ style: {
296
+ numberFormat: {
297
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
298
+ }
299
+ }
300
+ }
301
+ }
302
+ },
303
+ {
304
+ id: 'settlement.fee',
305
+ name: 'Uitbetalingstransactiekosten',
306
+ width: 25,
307
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
308
+ return {
309
+ value: object.settlement?.fee !== undefined ? (object.settlement?.fee / 100) : null,
310
+ style: {
311
+ numberFormat: {
312
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
318
+ ]
319
+ }
320
+
321
+ function getStripeColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
322
+ return [
323
+ {
324
+ id: 'transferFee',
325
+ name: 'Transactiekosten',
326
+ width: 16,
327
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
328
+ return {
329
+ value: object.transferFee / 100,
330
+ style: {
331
+ numberFormat: {
332
+ id: XlsxBuiltInNumberFormat.Currency2DecimalWithRed
333
+ }
334
+ }
335
+ }
336
+ }
337
+ },
338
+ {
339
+ id: 'stripeAccountId',
340
+ name: 'Account ID',
341
+ width: 20,
342
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
343
+ return {
344
+ value: object.stripeAccount?.accountId || ''
345
+ }
346
+ }
347
+ },
348
+ {
349
+ id: 'iban',
350
+ name: 'Kaartnummer',
351
+ width: 20,
352
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
353
+ return {
354
+ value: object.iban || ''
355
+ }
356
+ }
357
+ },
358
+ {
359
+ id: 'ibanName',
360
+ name: 'Kaarthouder',
361
+ width: 20,
362
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
363
+ return {
364
+ value: object.ibanName || ''
365
+ }
366
+ }
367
+ }
368
+ ]
369
+ }
370
+
371
+ function getTransferColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
372
+ return [
373
+ {
374
+ id: 'transferDescription',
375
+ name: 'Mededeling',
376
+ width: 25,
377
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
378
+ return {
379
+ value: object.transferDescription || ''
380
+ }
381
+ }
382
+ },
383
+ {
384
+ id: 'transferSettings.creditor',
385
+ name: 'Begunstigde',
386
+ width: 25,
387
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
388
+ if (!object.transferSettings && object.stripeAccount && object.stripeAccount?.meta.bank_account_bank_name) {
389
+ return {
390
+ value: (object.stripeAccount.meta.bank_account_name || object.stripeAccount.meta.business_profile?.name || object.stripeAccount.meta.company?.name) + ' (' + (object.stripeAccount?.meta.bank_account_bank_name || '') + ')'
391
+ }
392
+ }
393
+
394
+ return {
395
+ value: object.transferSettings?.creditor || ''
396
+ }
397
+ }
398
+ },
399
+ {
400
+ id: 'transferSettings.iban',
401
+ name: 'Rekeningnummer begunstigde',
402
+ width: 30,
403
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
404
+ if (!object.transferSettings && object.stripeAccount && object.stripeAccount?.meta.bank_account_last4) {
405
+ return {
406
+ value: 'xxxx ' + object.stripeAccount?.meta.bank_account_last4
407
+ }
408
+ }
409
+
410
+ return {
411
+ value: object.transferSettings?.iban || ''
412
+ }
413
+ }
414
+ },
415
+ ]
416
+ }
417
+
418
+ function getInvoiceColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
419
+ return [
420
+ {
421
+ id: 'customer.name',
422
+ name: 'Naam',
423
+ width: 30,
424
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
425
+ return {
426
+ value: object.customer?.name || ''
427
+ }
428
+ }
429
+ },
430
+ {
431
+ id: 'customer.email',
432
+ name: 'E-mailadres',
433
+ width: 40,
434
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
435
+ return {
436
+ value: object.customer?.email || ''
437
+ }
438
+ }
439
+ },
440
+ {
441
+ id: 'customer.company.name',
442
+ name: 'Bedrijfsnaam',
443
+ width: 30,
444
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
445
+ return {
446
+ value: object.customer?.company?.name || ''
447
+ }
448
+ }
449
+ },
450
+ {
451
+ id: 'customer.company.VATNumber',
452
+ name: 'BTW-nummer',
453
+ width: 20,
454
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
455
+ return {
456
+ value: object.customer?.company?.VATNumber || ''
457
+ }
458
+ }
459
+ },
460
+ {
461
+ id: 'customer.company.companyNumber',
462
+ name: 'Ondernemingsnummer',
463
+ width: 20,
464
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
465
+ return {
466
+ value: object.customer?.company?.companyNumber || ''
467
+ }
468
+ }
469
+ },
470
+ {
471
+ match: (id) => {
472
+ if (id === 'customer.company.address') {
473
+ return [
474
+ {
475
+ id: 'customer.company.address.street',
476
+ name: 'Adres - Straat',
477
+ width: 30,
478
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
479
+ return {
480
+ value: object.customer?.company?.address?.street || ''
481
+ }
482
+ }
483
+ },
484
+ {
485
+ id: 'customer.company.address.number',
486
+ name: 'Adres - Nummer',
487
+ width: 20,
488
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
489
+ return {
490
+ value: object.customer?.company?.address?.number || ''
491
+ }
492
+ }
493
+ },
494
+ {
495
+ id: 'customer.company.address.postalCode',
496
+ name: 'Adres - Postcode',
497
+ width: 20,
498
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
499
+ return {
500
+ value: object.customer?.company?.address?.postalCode || ''
501
+ }
502
+ }
503
+ },
504
+ {
505
+ id: 'customer.company.address.city',
506
+ name: 'Adres - Stad',
507
+ width: 20,
508
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
509
+ return {
510
+ value: object.customer?.company?.address?.city || ''
511
+ }
512
+ }
513
+ },
514
+ {
515
+ id: 'customer.company.address.country',
516
+ name: 'Adres - Land',
517
+ width: 20,
518
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
519
+ return {
520
+ value: object.customer?.company?.address?.country ? CountryHelper.getName(object.customer?.company?.address?.country) : ''
521
+ }
522
+ }
523
+ }
524
+ ]
525
+ }
526
+ }
527
+ },
528
+ {
529
+ id: 'customer.company.administrationEmail',
530
+ name: 'E-mailadres administratie',
531
+ width: 30,
532
+ getValue: (object: PaymentGeneralWithStripeAccount) => {
533
+ return {
534
+ value: object.customer?.company?.administrationEmail || ''
535
+ }
536
+ }
537
+ },
538
+ ]
539
+ }
@@ -286,13 +286,10 @@ export class AdminPermissionChecker {
286
286
  }
287
287
 
288
288
  if (organizationPermissions.hasResourceAccess(PermissionsResourceType.Webshops, webshop.id, permissionLevel)) {
289
- console.warn('has access organizationPermissions.hasResourceAccess')
290
289
  return true;
291
290
  }
292
291
 
293
292
  if (permissionLevel === PermissionLevel.Read && organizationPermissions.hasResourceAccessRight(PermissionsResourceType.Webshops, webshop.id, AccessRight.WebshopScanTickets)) {
294
- console.warn('has access organizationPermissions.hasResourceAccessRight')
295
-
296
293
  return true;
297
294
  }
298
295
 
@@ -41,6 +41,8 @@ export class AuthenticatedStructures {
41
41
 
42
42
  const includeSettlements = checkPermissions && !!Context.user && !!Context.user.permissions
43
43
 
44
+ console.log('includeSettlements', includeSettlements)
45
+
44
46
  return Payment.getGeneralStructureFromRelations({
45
47
  payments,
46
48
  balanceItemPayments,