@stamhoofd/backend 2.120.6 → 2.122.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/output-2.txt +1 -0
- package/output.txt +1 -0
- package/package.json +70 -53
- package/src/audit-logs/EventNotificationLogger.ts +40 -0
- package/src/audit-logs/ModelLogger.ts +19 -15
- package/src/audit-logs/OrganizationLogger.ts +1 -1
- package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
- package/src/audit-logs/init.ts +9 -1
- package/src/boot.ts +19 -18
- package/src/crons/amazon-ses.ts +13 -26
- package/src/crons/balance-emails.ts +3 -5
- package/src/crons/clearExcelCache.test.ts +1 -1
- package/src/crons/clearExcelCache.ts +1 -2
- package/src/crons/drip-emails.ts +42 -0
- package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
- package/src/crons/helpers/isOutside.ts +11 -0
- package/src/crons/helpers/useSavedIterator.ts +32 -0
- package/src/crons/index.ts +7 -0
- package/src/crons/invoices.ts +160 -0
- package/src/crons/members-fees.ts +50 -0
- package/src/crons/mollie-chargebacks.ts +86 -0
- package/src/crons/service-fees.ts +152 -0
- package/src/crons/stripe-invoices.ts +48 -0
- package/src/crons/transfer-fees.ts +176 -0
- package/src/crons/update-cached-balances.ts +4 -4
- package/src/crons.ts +54 -74
- package/src/debug.ts +4 -2
- package/src/email-recipient-loaders/documents.ts +1 -1
- package/src/email-recipient-loaders/members.ts +1 -1
- package/src/email-recipient-loaders/payments.ts +87 -46
- package/src/email-recipient-loaders/registrations.ts +1 -1
- package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
- package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
- package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
- package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/SignupEndpoint.ts +2 -3
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
- package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
- package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
- package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
- package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
- package/src/endpoints/global/files/GetFileCache.ts +3 -4
- package/src/endpoints/global/files/UploadFile.ts +3 -4
- package/src/endpoints/global/files/UploadImage.ts +3 -4
- package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
- package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -13
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
- package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +38 -33
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
- package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
- package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +19 -6
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
- package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
- package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
- package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +12 -4
- package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +311 -0
- package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +15 -4
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -5
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +67 -20
- package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
- package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +24 -17
- package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +32 -22
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +29 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +15 -13
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +28 -50
- package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
- package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
- package/src/endpoints/system/HealthEndpoint.ts +1 -1
- package/src/excel-loaders/balance-items.ts +1 -1
- package/src/excel-loaders/event-notifications.ts +1 -1
- package/src/excel-loaders/members.ts +1 -1
- package/src/excel-loaders/organizations.ts +3 -3
- package/src/excel-loaders/payments.ts +31 -1
- package/src/excel-loaders/platform-memberships.ts +13 -14
- package/src/excel-loaders/receivable-balances.ts +2 -2
- package/src/excel-loaders/registrations.ts +2 -3
- package/src/helpers/AddressValidator.ts +7 -14
- package/src/helpers/AdminPermissionChecker.ts +60 -65
- package/src/helpers/AuthenticatedStructures.ts +153 -51
- package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
- package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
- package/src/helpers/BuckarooHelper.ts +2 -4
- package/src/helpers/CheckSettlements.ts +12 -24
- package/src/helpers/Context.ts +15 -10
- package/src/helpers/CookieHelper.ts +1 -2
- package/src/helpers/EmailResumer.ts +1 -2
- package/src/helpers/FileCache.ts +2 -3
- package/src/helpers/FinancialSupportHelper.ts +21 -0
- package/src/helpers/FlagMomentCleanup.ts +1 -2
- package/src/helpers/ForwardHandler.ts +5 -10
- package/src/helpers/GlobalHelper.ts +3 -3
- package/src/helpers/LegacyBillingConverter.ts +787 -0
- package/src/helpers/MemberMerger.test.ts +782 -0
- package/src/helpers/MemberMerger.ts +600 -0
- package/src/helpers/MemberUserSyncer.ts +6 -12
- package/src/helpers/MembershipCharger.ts +2 -4
- package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
- package/src/helpers/PaymentCustomerResolver.ts +144 -0
- package/src/helpers/PeriodHelper.ts +2 -3
- package/src/helpers/RecordAnswerHelper.test.ts +741 -0
- package/src/helpers/RecordAnswerHelper.ts +116 -0
- package/src/helpers/SeedTools.ts +106 -0
- package/src/helpers/ServiceFeeHelper.ts +125 -15
- package/src/helpers/SetupStepUpdater.ts +38 -32
- package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
- package/src/helpers/StripeHelper.ts +15 -38
- package/src/helpers/StripeInvoicer.ts +419 -0
- package/src/helpers/StripePayoutChecker.ts +5 -10
- package/src/helpers/TagHelper.ts +7 -8
- package/src/helpers/TemporaryMemberAccess.ts +1 -2
- package/src/helpers/ThrottledQueue.ts +1 -2
- package/src/helpers/UitpasTokenRepository.ts +1 -2
- package/src/helpers/ViesHelper.ts +11 -11
- package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
- package/src/helpers/outstandingBalanceJoin.ts +2 -2
- package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
- package/src/migrate.ts +12 -5
- package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
- package/src/migrations/0000000006-default-organization.ts +139 -0
- package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
- package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
- package/src/migrations/1750090029-document-update-year.ts +103 -0
- package/src/migrations/1750090030-records-configuration.ts +92 -0
- package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
- package/src/migrations/1752848540-national-numbers.ts +585 -0
- package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
- package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +95 -27
- package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
- package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
- package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
- package/src/migrations/1761665607-sync-member-users.ts +40 -0
- package/src/migrations/1779121239-default-invoice-email-template.sql +3 -0
- package/src/migrations/1779121240-invoice-email-template.sql +6 -0
- package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
- package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
- package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
- package/src/migrations/1780933598-document-groups.ts +88 -0
- package/src/migrations/1781692342-balance-item-relations.ts +13 -0
- package/src/migrations/1781857866-platform-admins.ts +103 -0
- package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
- package/src/seeds/0000000001-throw.ts +5 -0
- package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
- package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
- package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
- package/src/seeds/1773754928-force-save-members.ts +41 -8
- package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
- package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
- package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
- package/src/seeds/1780915002-update-orders.ts +39 -0
- package/src/seeds/1782047708-organization-company-address.ts +44 -0
- package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
- package/src/services/AuditLogService.test.ts +51 -0
- package/src/services/AuditLogService.ts +45 -0
- package/src/services/BalanceItemService.ts +148 -55
- package/src/services/CpuService.ts +3 -6
- package/src/services/DatabaseCollationService.test.ts +1 -2
- package/src/services/DocumentService.ts +1 -2
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/FileSignService.ts +2 -4
- package/src/services/InvoicePdfService.ts +286 -0
- package/src/services/InvoiceService.ts +330 -77
- package/src/services/InvoiceXMLService.ts +424 -0
- package/src/services/MemberNumberService.ts +6 -11
- package/src/services/MemberRecordStore.ts +8 -10
- package/src/services/MollieService.ts +530 -0
- package/src/services/PaymentMandateService.ts +214 -0
- package/src/services/PaymentReallocationService.test.ts +1 -1
- package/src/services/PaymentReallocationService.ts +7 -9
- package/src/services/PaymentService.ts +820 -328
- package/src/services/PlatformMembershipService.ts +5 -9
- package/src/services/ReferralService.ts +223 -0
- package/src/services/RegistrationService.ts +125 -54
- package/src/services/SSOService.test.ts +119 -0
- package/src/services/SSOService.ts +56 -49
- package/src/services/STPackageService.ts +97 -68
- package/src/services/UniqueMemberNumberService.ts +69 -0
- package/src/services/UniqueUserService.ts +3 -6
- package/src/services/VATService.ts +85 -0
- package/src/services/data/invoice.hbs.html +686 -0
- package/src/services/uitpas/PassholderEndpoints.ts +3 -6
- package/src/services/uitpas/UitpasService.ts +6 -10
- package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
- package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
- package/src/services/uitpas/searchUitpasEvents.ts +1 -2
- package/src/sql-filters/audit-logs.ts +1 -1
- package/src/sql-filters/balance-item-payments.ts +1 -1
- package/src/sql-filters/balance-items.ts +1 -1
- package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
- package/src/sql-filters/document-templates.ts +1 -1
- package/src/sql-filters/documents.ts +1 -1
- package/src/sql-filters/email-recipients.ts +1 -1
- package/src/sql-filters/emails.ts +1 -1
- package/src/sql-filters/event-notifications.ts +1 -1
- package/src/sql-filters/events.ts +6 -1
- package/src/sql-filters/groups.ts +16 -1
- package/src/sql-filters/invoiced-balance-items.ts +1 -1
- package/src/sql-filters/invoices.ts +1 -1
- package/src/sql-filters/member-responsibility-records.ts +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/src/sql-filters/orders.ts +4 -4
- package/src/sql-filters/organization-registration-periods.ts +1 -1
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-filters/payments.ts +5 -0
- package/src/sql-filters/platform-memberships.ts +3 -3
- package/src/sql-filters/receivable-balances.ts +1 -1
- package/src/sql-filters/registration-invitations.ts +90 -0
- package/src/sql-filters/registration-periods.ts +1 -1
- package/src/sql-filters/registrations.ts +1 -1
- package/src/sql-filters/tickets.ts +1 -1
- package/src/sql-filters/users.ts +1 -1
- package/src/sql-sorters/groups.ts +23 -0
- package/src/sql-sorters/platform-memberships.ts +16 -16
- package/src/sql-sorters/registration-invitations.ts +36 -0
- package/tests/actions/patchOrganizationMember.ts +1 -1
- package/tests/actions/patchUserMember.ts +1 -1
- package/tests/assertions/assertBalances.ts +6 -12
- package/tests/e2e/api-rate-limits.test.ts +12 -24
- package/tests/e2e/bundle-discounts.test.ts +4 -1
- package/tests/e2e/charge-members.test.ts +13 -11
- package/tests/e2e/documents.test.ts +4 -1
- package/tests/e2e/register.test.ts +7 -9
- package/tests/e2e/stock.test.ts +4 -3
- package/tests/e2e/tickets.test.ts +1 -1
- package/tests/helpers/MollieMocker.ts +462 -0
- package/tests/helpers/PayconiqMocker.ts +138 -45
- package/tests/helpers/StripeMocker.ts +9 -6
- package/tests/helpers/index.ts +4 -0
- package/tests/init/initAdmin.ts +2 -2
- package/tests/init/initBundleDiscount.ts +1 -1
- package/tests/init/initMembershipOrganization.ts +13 -0
- package/tests/init/initPermissionRole.ts +3 -4
- package/tests/vitest.global.setup.ts +1 -1
- package/vitest.config.js +7 -0
- package/.nvmrc +0 -1
- package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
- package/src/helpers/GroupBuilder.ts +0 -418
- package/src/seeds/0000000000-example.ts +0 -11
- package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
- package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
- package/src/seeds/1726847064-setup-steps.ts +0 -16
- package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
- package/src/seeds/1729253172-update-orders.ts +0 -21
- package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
- package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
- package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
- package/src/seeds/1740046783-update-membership.ts +0 -17
- package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
- package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
- package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
- package/src/seeds/1750090030-records-configuration.ts +0 -26
- package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
- package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
- package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
- package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
- package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
- package/src/seeds/1756303697-update-email-counts.ts +0 -76
- package/src/seeds/1761665607-sync-member-users.ts +0 -59
- package/src/seeds/1765896674-document-update-year.ts +0 -66
- /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
- /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
- /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { AutoEncoderPatchType, Encodeable, Identifiable, Patchable, PatchableArray } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { isPatchableArray } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
+
import type { RecordCategory, RecordSettings, RecordType } from '@stamhoofd/structures';
|
|
5
|
+
import { RecordAnswerDecoder } from '@stamhoofd/structures';
|
|
6
|
+
|
|
7
|
+
export class RecordAnswerHelper {
|
|
8
|
+
static throwIfPatchOrPutIsInvalid(original: RecordCategory[], patchOrPut: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>) {
|
|
9
|
+
if (original.length === 0) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const updatedRecordSettings = this.getAllRecords(patchOrPut)
|
|
14
|
+
// only check if the types changed
|
|
15
|
+
.filter(p => p.type !== undefined || p.fileType !== undefined);
|
|
16
|
+
|
|
17
|
+
if (updatedRecordSettings.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const updatRecordSettingsMap = new Map<string, RecordSettings | AutoEncoderPatchType<RecordSettings>>(updatedRecordSettings.map(p => [p.id, p]));
|
|
22
|
+
|
|
23
|
+
for (const category of original) {
|
|
24
|
+
for (const originalRecord of category.getAllRecords()) {
|
|
25
|
+
const updatedSettings = updatRecordSettingsMap.get(originalRecord.id);
|
|
26
|
+
if (updatedSettings) {
|
|
27
|
+
this.throwIfInvalidRecordSettingsUpdate(originalRecord, updatedSettings);
|
|
28
|
+
|
|
29
|
+
// remove from map
|
|
30
|
+
updatRecordSettingsMap.delete(originalRecord.id);
|
|
31
|
+
|
|
32
|
+
// stop looping if all updated records are checked
|
|
33
|
+
if (updatRecordSettingsMap.size === 0) {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static throwIfPatchOrPutsAreInvalid<
|
|
42
|
+
Key extends string & keyof Put,
|
|
43
|
+
Id extends string | number,
|
|
44
|
+
Put extends (Identifiable<Id> & Encodeable & Patchable<Patch> & Record<Key, RecordCategory[]>),
|
|
45
|
+
Patch extends (Identifiable<Id> & Encodeable) | Put,
|
|
46
|
+
>(originals: Put[], patchOrPuts: Put[] | Patch[] | PatchableArray<Id, Put, Patch>, key: string) {
|
|
47
|
+
if (isPatchableArray(patchOrPuts)) {
|
|
48
|
+
const puts = patchOrPuts.getPuts().map(p => p.put);
|
|
49
|
+
this.throwIfPatchOrPutsAreInvalid<Key, Id, Put, Patch>(originals, puts, key);
|
|
50
|
+
const patches = patchOrPuts.getPatches();
|
|
51
|
+
this.throwIfPatchOrPutsAreInvalid<Key, Id, Put, Patch>(originals, patches, key);
|
|
52
|
+
} else {
|
|
53
|
+
for (const patchOrPut of patchOrPuts) {
|
|
54
|
+
const original = originals.find(o => o.id === patchOrPut.id);
|
|
55
|
+
if (original) {
|
|
56
|
+
const originalRecordCategories: RecordCategory[] = original[key];
|
|
57
|
+
const newRecordCategories: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>> = patchOrPut[key];
|
|
58
|
+
if (originalRecordCategories && newRecordCategories) {
|
|
59
|
+
this.throwIfPatchOrPutIsInvalid(originalRecordCategories, newRecordCategories);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private static throwIfInvalidRecordSettingsUpdate(original: RecordSettings, updated: RecordSettings | AutoEncoderPatchType<RecordSettings>) {
|
|
67
|
+
if (updated.type !== undefined
|
|
68
|
+
&& original.type !== updated.type
|
|
69
|
+
// changing between types with the same class should be allowed
|
|
70
|
+
&& !this.haveTypesSameClass(original.type, updated.type)) {
|
|
71
|
+
throw new SimpleError({
|
|
72
|
+
code: 'invalid_record_type',
|
|
73
|
+
message: `Cannot change record type from ${original.type} to ${updated.type}`,
|
|
74
|
+
human: $t(`%1Qn`, { name: original.name }),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (updated.fileType !== undefined && ((original.fileType ?? null) !== (updated.fileType ?? null))) {
|
|
79
|
+
throw new SimpleError({
|
|
80
|
+
code: 'invalid_record_type',
|
|
81
|
+
message: `Cannot change record file type from ${original.fileType} to ${updated.fileType}`,
|
|
82
|
+
human: $t(`%1SP`, { name: original.name }),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static haveTypesSameClass(a: RecordType, b: RecordType) {
|
|
88
|
+
return RecordAnswerDecoder.getClassForType(a) === RecordAnswerDecoder.getClassForType(b);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private static getAllRecords(patchOrPut: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>): (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] {
|
|
92
|
+
if (isPatchableArray(patchOrPut)) {
|
|
93
|
+
const patches = patchOrPut.getPatches().flatMap(p => this.getAllRecordsFromRecordCategoryPatch(p));
|
|
94
|
+
const puts = patchOrPut.getPuts().flatMap(p => p.put.getAllRecords());
|
|
95
|
+
return [...patches, ...puts];
|
|
96
|
+
}
|
|
97
|
+
return patchOrPut.flatMap(c => c.getAllRecords());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private static getAllRecordsFromRecordCategoryPatch(category: AutoEncoderPatchType<RecordCategory>) {
|
|
101
|
+
const results: (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] = [
|
|
102
|
+
...category.records.getPatches(),
|
|
103
|
+
...category.records.getPuts().map(p => p.put),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
if (category.childCategories) {
|
|
107
|
+
const childCategoryPatches = category.childCategories.getPatches().flatMap(p => this.getAllRecordsFromRecordCategoryPatch(p));
|
|
108
|
+
results.push(...childCategoryPatches);
|
|
109
|
+
|
|
110
|
+
const childCategoryPuts = category.childCategories.getPuts().flatMap(p => p.put.getAllRecords());
|
|
111
|
+
results.push(...childCategoryPuts);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Database } from '@simonbackx/simple-database';
|
|
2
|
+
import type { SQLSelect } from '@stamhoofd/sql';
|
|
3
|
+
import type { ProgressLogger } from '@stamhoofd/utility';
|
|
4
|
+
import { LoggingTools } from '@stamhoofd/utility';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* If an error is thrown, first await all pending items before throwing, otherwise we risk quiting mid running actions
|
|
8
|
+
*/
|
|
9
|
+
export function allSettledButThrowFirst<T>(promises: Promise<T>[]): Promise<T[]> {
|
|
10
|
+
return Promise.allSettled(promises).then((results) => {
|
|
11
|
+
return results.map((r) => {
|
|
12
|
+
if (r.status === 'fulfilled') {
|
|
13
|
+
return r.value;
|
|
14
|
+
}
|
|
15
|
+
throw r.reason;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class SeedTools {
|
|
21
|
+
static createBatchProcessor<T>(options: BatchProcessorArgs<T>) {
|
|
22
|
+
return new BatchProcessor(options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static async loop<T extends { id: string }>(options: BatchProcessorArgs<T> & { query: SQLSelect<T> }) {
|
|
26
|
+
const batchProcessor = SeedTools.createBatchProcessor(options);
|
|
27
|
+
|
|
28
|
+
const progressLogger = await LoggingTools.createProgressLoggerFromQuery(options.query.clone());
|
|
29
|
+
batchProcessor.setProgressLogger(progressLogger);
|
|
30
|
+
|
|
31
|
+
for await (const item of options.query.limit(options.batchSize).all()) {
|
|
32
|
+
await batchProcessor.execute(item as T);
|
|
33
|
+
}
|
|
34
|
+
await batchProcessor.finish();
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
total: progressLogger.total,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async loopBatched<T extends { id: string }>(options: { batchSize: number; batchAction: (items: T[]) => Promise<void>; query: SQLSelect<T> }) {
|
|
42
|
+
const progressLogger = await LoggingTools.createProgressLoggerFromQuery(options.query.clone());
|
|
43
|
+
|
|
44
|
+
for await (const batch of options.query.limit(options.batchSize).allBatched()) {
|
|
45
|
+
await options.batchAction(batch as T[]);
|
|
46
|
+
progressLogger.update(batch.length);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
total: progressLogger.total,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type BatchProcessorArgs<T> = {
|
|
56
|
+
batchSize: number;
|
|
57
|
+
useTransactionPerBatch?: boolean;
|
|
58
|
+
action: (item: T) => Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
class BatchProcessor<T> {
|
|
62
|
+
private currentBatch: Promise<void>[] = [];
|
|
63
|
+
private readonly batchSize: number;
|
|
64
|
+
private readonly originalAction: (item: T) => Promise<void>;
|
|
65
|
+
private action: (item: T) => Promise<void>;
|
|
66
|
+
private readonly useTransactionPerBatch: boolean;
|
|
67
|
+
|
|
68
|
+
constructor({ batchSize, action, useTransactionPerBatch }: BatchProcessorArgs<T>) {
|
|
69
|
+
this.batchSize = batchSize;
|
|
70
|
+
this.originalAction = action;
|
|
71
|
+
this.action = action;
|
|
72
|
+
this.useTransactionPerBatch = useTransactionPerBatch ?? false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setProgressLogger(progressLogger: ProgressLogger) {
|
|
76
|
+
this.action = async (item: T) => {
|
|
77
|
+
await this.originalAction(item);
|
|
78
|
+
progressLogger.update();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async execute(item: T): Promise<void> {
|
|
83
|
+
if (this.currentBatch.length >= this.batchSize) {
|
|
84
|
+
await this.finishCurrentBatch();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.currentBatch.push(this.action(item));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async finishCurrentBatch() {
|
|
91
|
+
const batch = this.currentBatch;
|
|
92
|
+
this.currentBatch = [];
|
|
93
|
+
|
|
94
|
+
if (this.useTransactionPerBatch) {
|
|
95
|
+
await Database.beginTransaction(async () => {
|
|
96
|
+
await allSettledButThrowFirst(batch);
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
await allSettledButThrowFirst(batch);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async finish(): Promise<void> {
|
|
104
|
+
await this.finishCurrentBatch();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,21 +1,64 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
|
+
import type { Payment, StripeAccount } from '@stamhoofd/models';
|
|
3
|
+
import { Organization, Platform } from '@stamhoofd/models';
|
|
4
|
+
import { PaymentMethod, PaymentProvider, STPackageType } from '@stamhoofd/structures';
|
|
5
|
+
import { VATService } from '../services/VATService.js';
|
|
6
|
+
import { STMath } from '@stamhoofd/utility';
|
|
7
|
+
|
|
8
|
+
const CACHE_TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
|
3
9
|
|
|
4
10
|
export class ServiceFeeHelper {
|
|
5
|
-
static
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
static #cachedOrganization: Organization | null = null;
|
|
12
|
+
static #cachedOrganizationDate: Date | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Organization that is invoicing
|
|
16
|
+
*/
|
|
17
|
+
static async getInvoicingOrganization() {
|
|
18
|
+
if (this.#cachedOrganization && this.#cachedOrganizationDate && this.#cachedOrganizationDate > new Date(Date.now() - CACHE_TIMEOUT)) {
|
|
19
|
+
return this.#cachedOrganization;
|
|
20
|
+
}
|
|
21
|
+
const platform = await Platform.getShared();
|
|
22
|
+
const membershipId = platform.membershipOrganizationId;
|
|
23
|
+
if (!membershipId) {
|
|
24
|
+
return null;
|
|
8
25
|
}
|
|
9
26
|
|
|
10
|
-
|
|
27
|
+
const org = await Organization.getByID(membershipId, true);
|
|
28
|
+
this.#cachedOrganization = org;
|
|
29
|
+
this.#cachedOrganizationDate = new Date();
|
|
11
30
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
31
|
+
return org;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* VAT rate used to charge service fees or transfer fees to organization by the invoicing organization (membershipOrganizationId)
|
|
36
|
+
*/
|
|
37
|
+
static async getVATPercentage(organization: Organization) {
|
|
38
|
+
const invoicingOrganization = await this.getInvoicingOrganization();
|
|
39
|
+
if (!invoicingOrganization) {
|
|
40
|
+
throw new SimpleError({
|
|
41
|
+
code: 'unavailable',
|
|
42
|
+
message: 'Payments are temporarily unavailable due to a lack of VAT settings',
|
|
43
|
+
human: $t('%1ay'),
|
|
44
|
+
});
|
|
16
45
|
}
|
|
17
|
-
|
|
18
|
-
|
|
46
|
+
|
|
47
|
+
const customer = organization.defaultCompanies[0];
|
|
48
|
+
|
|
49
|
+
if (VATService.isVATExcempt({
|
|
50
|
+
sellingOrganization: invoicingOrganization,
|
|
51
|
+
company: customer,
|
|
52
|
+
})) {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 21;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static async setServiceFee(payment: Payment, organization: Organization, type: 'webshop' | 'members' | 'tickets' | 'system', itemPrices: number[]): Promise<void> {
|
|
60
|
+
if (type === 'system') {
|
|
61
|
+
return;
|
|
19
62
|
}
|
|
20
63
|
|
|
21
64
|
function calculateFee(price: number, minimumFee: number | null, maximumFee: number | null, fixed: number, percentageTimes100: number) {
|
|
@@ -55,7 +98,18 @@ export class ServiceFeeHelper {
|
|
|
55
98
|
}
|
|
56
99
|
|
|
57
100
|
// Add VAT
|
|
58
|
-
|
|
101
|
+
if (serviceFee !== 0) {
|
|
102
|
+
let vat = 0;
|
|
103
|
+
|
|
104
|
+
// Calculate item count
|
|
105
|
+
if (payment.provider === PaymentProvider.Stripe && payment.stripeAccountId) {
|
|
106
|
+
// Don't chagne
|
|
107
|
+
vat = await this.getVATPercentage(organization);
|
|
108
|
+
} else {
|
|
109
|
+
// don't charge VAT (we'll add the VAT to the invoice)
|
|
110
|
+
}
|
|
111
|
+
serviceFee = serviceFee * (100 + vat) / 100;
|
|
112
|
+
}
|
|
59
113
|
|
|
60
114
|
// Round service fee to 2 decimal places
|
|
61
115
|
serviceFee = Math.round(serviceFee / 100) * 100;
|
|
@@ -63,9 +117,65 @@ export class ServiceFeeHelper {
|
|
|
63
117
|
console.log('Service fee for payment', payment.id, type, 'is', serviceFee);
|
|
64
118
|
if (payment.provider === PaymentProvider.Stripe && payment.stripeAccountId) {
|
|
65
119
|
payment.serviceFeePayout = serviceFee;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
120
|
+
} else {
|
|
68
121
|
payment.serviceFeeManual = serviceFee;
|
|
69
122
|
}
|
|
70
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Depends on the situation.
|
|
127
|
+
*/
|
|
128
|
+
static async setTransferFee({
|
|
129
|
+
payment,
|
|
130
|
+
organization,
|
|
131
|
+
stripeAccount,
|
|
132
|
+
}: {
|
|
133
|
+
payment: Payment;
|
|
134
|
+
organization: Organization;
|
|
135
|
+
stripeAccount?: StripeAccount | null | undefined;
|
|
136
|
+
}) {
|
|
137
|
+
if (payment.provider !== PaymentProvider.Stripe && payment.provider !== PaymentProvider.Buckaroo) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (payment.provider === PaymentProvider.Stripe && !stripeAccount) {
|
|
142
|
+
throw new Error('Missing StripeAccount');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const totalPrice = STMath.round(payment.price / 100); // Convert from 4 decimal places to 2 decimal places for this calculation
|
|
146
|
+
|
|
147
|
+
if (totalPrice <= 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (stripeAccount && stripeAccount.meta.type === 'standard') {
|
|
152
|
+
// Submerchant is charged by Stripe for the fees directly
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let fee = 0;
|
|
157
|
+
const vat = await this.getVATPercentage(organization);
|
|
158
|
+
|
|
159
|
+
function calculateFee(fixed: number, percentageTimes100: number) {
|
|
160
|
+
return STMath.round(STMath.round(fixed + Math.max(1, totalPrice * percentageTimes100 / 100 / 100)) * (100 + vat) / 100); // € 0,21 + 0,2%
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (payment.method === PaymentMethod.iDEAL) {
|
|
164
|
+
fee = calculateFee(21, 20); // € 0,21 + 0,2%
|
|
165
|
+
} else if (payment.method === PaymentMethod.Bancontact) {
|
|
166
|
+
fee = calculateFee(24, 20); // € 0,24 + 0,2%
|
|
167
|
+
} else {
|
|
168
|
+
fee = calculateFee(25, 150); // € 0,25 + 1,5%
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
payment.transferFee = fee * 100; // Convert back to 4 decimal places for storage
|
|
172
|
+
|
|
173
|
+
if (payment.provider === PaymentProvider.Buckaroo) {
|
|
174
|
+
// We'll need to charge
|
|
175
|
+
payment.transferFeeManual = payment.transferFee;
|
|
176
|
+
console.log('Transfer fee for payment', payment.id, 'is', payment.transferFee, '(manually charged)');
|
|
177
|
+
} else {
|
|
178
|
+
console.log('Transfer fee for payment', payment.id, 'is', payment.transferFee);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
71
181
|
}
|
|
@@ -14,17 +14,17 @@ import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
|
14
14
|
import type {
|
|
15
15
|
MemberResponsibility,
|
|
16
16
|
Platform as PlatformStruct,
|
|
17
|
-
SetupSteps
|
|
17
|
+
SetupSteps,
|
|
18
|
+
} from '@stamhoofd/structures';
|
|
18
19
|
import {
|
|
19
20
|
AuditLogSource,
|
|
20
21
|
GroupType,
|
|
21
22
|
RecordCategory,
|
|
22
|
-
SetupStepType
|
|
23
|
+
SetupStepType,
|
|
23
24
|
} from '@stamhoofd/structures';
|
|
24
25
|
import { Formatter } from '@stamhoofd/utility';
|
|
25
26
|
import { AuditLogService } from '../services/AuditLogService.js';
|
|
26
27
|
import { GroupedThrottledQueue } from './GroupedThrottledQueue.js';
|
|
27
|
-
import { AuthenticatedStructures } from './AuthenticatedStructures.js';
|
|
28
28
|
|
|
29
29
|
type SetupStepOperation = (setupSteps: SetupSteps, organization: Organization, platform: PlatformStruct) => void | Promise<void>;
|
|
30
30
|
|
|
@@ -71,6 +71,10 @@ export class SetupStepUpdater {
|
|
|
71
71
|
static isListening = false;
|
|
72
72
|
|
|
73
73
|
static listen() {
|
|
74
|
+
if (STAMHOOFD.userMode === 'organization') {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
if (this.isListening) {
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
@@ -111,14 +115,14 @@ export class SetupStepUpdater {
|
|
|
111
115
|
SetupStepType,
|
|
112
116
|
SetupStepOperation
|
|
113
117
|
> = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
[SetupStepType.Responsibilities]: this.updateStepResponsibilities,
|
|
119
|
+
[SetupStepType.Companies]: this.updateStepCompanies,
|
|
120
|
+
[SetupStepType.Groups]: this.updateStepGroups,
|
|
121
|
+
[SetupStepType.Premises]: this.updateStepPremises,
|
|
122
|
+
[SetupStepType.Emails]: this.updateStepEmails,
|
|
123
|
+
[SetupStepType.Payment]: this.updateStepPayment,
|
|
124
|
+
[SetupStepType.Registrations]: this.updateStepRegistrations,
|
|
125
|
+
};
|
|
122
126
|
|
|
123
127
|
static async updateSetupStepsForAllOrganizationsInCurrentPeriod({
|
|
124
128
|
batchSize,
|
|
@@ -191,6 +195,10 @@ export class SetupStepUpdater {
|
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
static async updateForOrganizationId(id: string, options?: { types?: SetupStepType[] }) {
|
|
198
|
+
if (STAMHOOFD.userMode === 'organization') {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
const organization = await Organization.getByID(id);
|
|
195
203
|
if (!organization) {
|
|
196
204
|
throw new SimpleError({
|
|
@@ -215,6 +223,10 @@ export class SetupStepUpdater {
|
|
|
215
223
|
types?: SetupStepType[];
|
|
216
224
|
} = {},
|
|
217
225
|
) {
|
|
226
|
+
if (STAMHOOFD.userMode === 'organization') {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
218
230
|
if (!platform) {
|
|
219
231
|
platform = await Platform.getSharedPrivateStruct();
|
|
220
232
|
if (!platform) {
|
|
@@ -312,8 +324,7 @@ export class SetupStepUpdater {
|
|
|
312
324
|
totalSteps,
|
|
313
325
|
finishedSteps,
|
|
314
326
|
});
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
327
|
+
} else {
|
|
317
328
|
// if no premise types, remove step
|
|
318
329
|
setupSteps.remove(SetupStepType.Premises);
|
|
319
330
|
}
|
|
@@ -346,8 +357,7 @@ export class SetupStepUpdater {
|
|
|
346
357
|
platform.config.organizationLevelRecordsConfiguration.recordCategories,
|
|
347
358
|
organization.getBaseStructureWithPrivateMeta(), // private data needed for answers
|
|
348
359
|
);
|
|
349
|
-
}
|
|
350
|
-
catch (error) {
|
|
360
|
+
} catch (error) {
|
|
351
361
|
console.error('Error validating record categories for organization', organization.id, error);
|
|
352
362
|
finishedSteps = 0;
|
|
353
363
|
}
|
|
@@ -375,10 +385,10 @@ export class SetupStepUpdater {
|
|
|
375
385
|
const allRecords = responsibilityIds.length === 0
|
|
376
386
|
? []
|
|
377
387
|
: await MemberResponsibilityRecord.select()
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
388
|
+
.where('responsibilityId', responsibilityIds)
|
|
389
|
+
.where('organizationId', organization.id)
|
|
390
|
+
.where(SQL.where('endDate', SQLWhereSign.Greater, now).or('endDate', null))
|
|
391
|
+
.fetch();
|
|
382
392
|
|
|
383
393
|
// Remove invalid responsibilities: members that are not registered in the current period
|
|
384
394
|
const memberIds = Formatter.uniqueArray(allRecords.map(r => r.memberId));
|
|
@@ -444,8 +454,7 @@ export class SetupStepUpdater {
|
|
|
444
454
|
totalRecordsWithThisResponsibility++;
|
|
445
455
|
}
|
|
446
456
|
}
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
457
|
+
} else {
|
|
449
458
|
for (const record of records) {
|
|
450
459
|
if (record.responsibilityId === responsibilityId && record.groupId === group.id) {
|
|
451
460
|
totalRecordsWithThisResponsibility++;
|
|
@@ -488,8 +497,7 @@ export class SetupStepUpdater {
|
|
|
488
497
|
|
|
489
498
|
if (finishedSteps >= totalSteps) {
|
|
490
499
|
setupSteps.markReviewed(SetupStepType.Emails, { userId: 'backend', userName: 'backend' });
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
500
|
+
} else {
|
|
493
501
|
setupSteps.resetReviewed(SetupStepType.Emails);
|
|
494
502
|
}
|
|
495
503
|
}
|
|
@@ -510,11 +518,11 @@ export class SetupStepUpdater {
|
|
|
510
518
|
|
|
511
519
|
const groupsWithDefaultAgeGroups = defaultAgeGroupIds.length > 0
|
|
512
520
|
? await Group.select()
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
521
|
+
.where('organizationId', organization.id)
|
|
522
|
+
.where('periodId', platform.period.id)
|
|
523
|
+
.where('defaultAgeGroupId', defaultAgeGroupIds)
|
|
524
|
+
.where('deletedAt', null)
|
|
525
|
+
.fetch()
|
|
518
526
|
: [];
|
|
519
527
|
|
|
520
528
|
let totalSteps = 0;
|
|
@@ -530,8 +538,7 @@ export class SetupStepUpdater {
|
|
|
530
538
|
if (!processedDefaultAgeGroupIds.has(defaultAgeGroup.id)) {
|
|
531
539
|
totalSteps += defaultAgeGroup.minimumRequiredMembers;
|
|
532
540
|
processedDefaultAgeGroupIds.set(defaultAgeGroup.id, Math.min(defaultAgeGroup.minimumRequiredMembers, group.settings.registeredMembers ?? 0));
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
541
|
+
} else {
|
|
535
542
|
processedDefaultAgeGroupIds.set(defaultAgeGroup.id,
|
|
536
543
|
Math.min(
|
|
537
544
|
defaultAgeGroup.minimumRequiredMembers,
|
|
@@ -550,8 +557,7 @@ export class SetupStepUpdater {
|
|
|
550
557
|
|
|
551
558
|
if (finishedSteps >= totalSteps) {
|
|
552
559
|
setupSteps.markReviewed(SetupStepType.Registrations, { userId: 'backend', userName: 'backend' });
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
560
|
+
} else {
|
|
555
561
|
setupSteps.resetReviewed(SetupStepType.Registrations);
|
|
556
562
|
}
|
|
557
563
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
|
+
import type { StamhoofdCompareValue, StamhoofdFilter } from '@stamhoofd/structures';
|
|
3
|
+
import { FilterWrapperMarker, getMagicRelationValue, isMagicFilter, isMagicRelationFilter, unwrapFilter } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
export class StamhoofdFilterAccessHelper {
|
|
6
|
+
static getGroupIdsFromFilter(filter: StamhoofdFilter): StamhoofdFilter<StamhoofdCompareValue>[] {
|
|
7
|
+
let rawGroupIds: StamhoofdFilter<StamhoofdCompareValue>[] | null = null;
|
|
8
|
+
|
|
9
|
+
if (typeof filter === 'string' || isMagicFilter(filter)) {
|
|
10
|
+
rawGroupIds = [filter];
|
|
11
|
+
} else {
|
|
12
|
+
const { markerValue } = unwrapFilter(filter, {
|
|
13
|
+
$in: FilterWrapperMarker,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (!Array.isArray(markerValue)) {
|
|
17
|
+
throw new SimpleError({
|
|
18
|
+
code: 'invalid_field',
|
|
19
|
+
field: 'filter',
|
|
20
|
+
message: 'You must filter on a group of the organization you are trying to access',
|
|
21
|
+
human: $t(`%1HG`),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (markerValue.length === 0) {
|
|
26
|
+
throw new SimpleError({
|
|
27
|
+
code: 'invalid_field',
|
|
28
|
+
field: 'filter',
|
|
29
|
+
message: 'Filtering on an empty list of groups is not supported',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
rawGroupIds = markerValue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return rawGroupIds.map((raw) => {
|
|
37
|
+
if (typeof raw === 'string') {
|
|
38
|
+
return raw;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isMagicRelationFilter(raw)) {
|
|
42
|
+
return getMagicRelationValue(raw);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new SimpleError({
|
|
46
|
+
code: 'invalid_field',
|
|
47
|
+
field: 'filter',
|
|
48
|
+
message: 'Invalid group ID in filter',
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|