@stamhoofd/backend 2.121.0 → 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 +69 -52
- 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 +24 -24
- package/src/audit-logs/init.ts +8 -2
- 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 +5 -0
- package/src/crons/invoices.ts +32 -38
- package/src/crons/members-fees.ts +50 -0
- package/src/crons/mollie-chargebacks.ts +14 -15
- 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 +7 -64
- 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 +4 -6
- 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 +1 -1
- 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 +11 -11
- 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 +35 -34
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +62 -63
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +6 -9
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +51 -52
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +6 -7
- 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 +7 -9
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +1 -5
- package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +11 -11
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +9 -9
- 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 +4 -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 +60 -57
- package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +20 -20
- 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 +10 -6
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +5 -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 +50 -12
- 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 +21 -14
- package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +35 -28
- 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 +19 -18
- 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 +22 -7
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +20 -27
- 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 +2 -2
- 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 +16 -33
- 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 +50 -63
- package/src/helpers/AuthenticatedStructures.ts +69 -55
- 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 +2 -2
- 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 +105 -110
- package/src/helpers/RecordAnswerHelper.ts +7 -7
- 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 +14 -36
- 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 +7 -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/{seeds → migrations}/1750090030-records-configuration.ts +33 -32
- 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 +70 -26
- 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/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 +141 -44
- 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 +204 -251
- 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 +114 -121
- package/src/services/PaymentMandateService.ts +45 -45
- package/src/services/PaymentReallocationService.test.ts +1 -1
- package/src/services/PaymentReallocationService.ts +7 -9
- package/src/services/PaymentService.ts +444 -308
- package/src/services/PlatformMembershipService.ts +4 -8
- package/src/services/ReferralService.ts +223 -0
- package/src/services/RegistrationService.ts +63 -53
- package/src/services/SSOService.test.ts +119 -0
- package/src/services/SSOService.ts +56 -49
- package/src/services/STPackageService.ts +97 -61
- package/src/services/UniqueMemberNumberService.ts +69 -0
- package/src/services/UniqueUserService.ts +3 -6
- package/src/services/VATService.ts +85 -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 +5 -0
- 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/platform-memberships.ts +3 -3
- package/src/sql-filters/receivable-balances.ts +1 -1
- 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 +2 -2
- 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 +6 -0
- package/.nvmrc +0 -1
- 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/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}/1779121239-default-invoice-email-template.sql +0 -0
- /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
|
@@ -9,7 +9,7 @@ export class RecordAnswerHelper {
|
|
|
9
9
|
if (original.length === 0) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
const updatedRecordSettings = this.getAllRecords(patchOrPut)
|
|
14
14
|
// only check if the types changed
|
|
15
15
|
.filter(p => p.type !== undefined || p.fileType !== undefined);
|
|
@@ -42,7 +42,7 @@ export class RecordAnswerHelper {
|
|
|
42
42
|
Key extends string & keyof Put,
|
|
43
43
|
Id extends string | number,
|
|
44
44
|
Put extends (Identifiable<Id> & Encodeable & Patchable<Patch> & Record<Key, RecordCategory[]>),
|
|
45
|
-
Patch extends (Identifiable<Id> & Encodeable) | Put
|
|
45
|
+
Patch extends (Identifiable<Id> & Encodeable) | Put,
|
|
46
46
|
>(originals: Put[], patchOrPuts: Put[] | Patch[] | PatchableArray<Id, Put, Patch>, key: string) {
|
|
47
47
|
if (isPatchableArray(patchOrPuts)) {
|
|
48
48
|
const puts = patchOrPuts.getPuts().map(p => p.put);
|
|
@@ -71,16 +71,16 @@ export class RecordAnswerHelper {
|
|
|
71
71
|
throw new SimpleError({
|
|
72
72
|
code: 'invalid_record_type',
|
|
73
73
|
message: `Cannot change record type from ${original.type} to ${updated.type}`,
|
|
74
|
-
human: $t(`%1Qn`, {name: original.name}),
|
|
75
|
-
})
|
|
74
|
+
human: $t(`%1Qn`, { name: original.name }),
|
|
75
|
+
});
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
if (updated.fileType !== undefined && ((original.fileType ?? null) !== (updated.fileType ?? null))) {
|
|
79
79
|
throw new SimpleError({
|
|
80
80
|
code: 'invalid_record_type',
|
|
81
81
|
message: `Cannot change record file type from ${original.fileType} to ${updated.fileType}`,
|
|
82
|
-
human: $t(`%1SP`, {name: original.name}),
|
|
83
|
-
})
|
|
82
|
+
human: $t(`%1SP`, { name: original.name }),
|
|
83
|
+
});
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -100,7 +100,7 @@ export class RecordAnswerHelper {
|
|
|
100
100
|
private static getAllRecordsFromRecordCategoryPatch(category: AutoEncoderPatchType<RecordCategory>) {
|
|
101
101
|
const results: (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] = [
|
|
102
102
|
...category.records.getPatches(),
|
|
103
|
-
...category.records.getPuts().map(p => p.put)
|
|
103
|
+
...category.records.getPuts().map(p => p.put),
|
|
104
104
|
];
|
|
105
105
|
|
|
106
106
|
if (category.childCategories) {
|
|
@@ -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
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
2
|
import type { I18n } from '@stamhoofd/backend-i18n';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Organization, StripeAccount } from '@stamhoofd/models';
|
|
4
4
|
import { Payment, StripeCheckoutSession, StripePaymentIntent } from '@stamhoofd/models';
|
|
5
|
-
import {
|
|
5
|
+
import { PaymentMethod, PaymentMethodHelper, PaymentStatus } from '@stamhoofd/structures';
|
|
6
6
|
import { Formatter } from '@stamhoofd/utility';
|
|
7
7
|
import Stripe from 'stripe';
|
|
8
8
|
import { passthroughFetch } from './passthroughFetch.js';
|
|
@@ -68,15 +68,13 @@ export class StripeHelper {
|
|
|
68
68
|
if (charge.payment_method_details.sepa_debit.last4) {
|
|
69
69
|
if (charge.payment_method_details.sepa_debit.country === 'BE') {
|
|
70
70
|
payment.iban = charge.payment_method_details.sepa_debit.country + '•• ' + charge.payment_method_details.sepa_debit.bank_code + '• •••• ' + charge.payment_method_details.sepa_debit.last4;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
71
|
+
} else {
|
|
73
72
|
payment.iban = '•••• ' + charge.payment_method_details.sepa_debit.last4;
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
await payment.save();
|
|
78
|
-
}
|
|
79
|
-
catch (e) {
|
|
77
|
+
} catch (e) {
|
|
80
78
|
console.error('Failed processing charge', e);
|
|
81
79
|
}
|
|
82
80
|
}
|
|
@@ -145,8 +143,7 @@ export class StripeHelper {
|
|
|
145
143
|
if (intent.status === 'canceled' || intent.status === 'requires_payment_method') {
|
|
146
144
|
return PaymentStatus.Failed;
|
|
147
145
|
}
|
|
148
|
-
}
|
|
149
|
-
catch (e) {
|
|
146
|
+
} catch (e) {
|
|
150
147
|
console.error('Error cancelling payment intent', e);
|
|
151
148
|
}
|
|
152
149
|
}
|
|
@@ -161,6 +158,10 @@ export class StripeHelper {
|
|
|
161
158
|
const [model] = await StripeCheckoutSession.where({ paymentId: payment.id }, { limit: 1 });
|
|
162
159
|
|
|
163
160
|
if (!model) {
|
|
161
|
+
if (payment.createdAt.getTime() > Date.now() - 1000 * 10) {
|
|
162
|
+
// Possibly missing model data because the payment has only been created just yet
|
|
163
|
+
return payment.status;
|
|
164
|
+
}
|
|
164
165
|
return PaymentStatus.Failed;
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -218,7 +219,7 @@ export class StripeHelper {
|
|
|
218
219
|
};
|
|
219
220
|
statementDescriptor: string;
|
|
220
221
|
i18n: I18n;
|
|
221
|
-
metadata: { [key: string]: string | undefined};
|
|
222
|
+
metadata: { [key: string]: string | undefined };
|
|
222
223
|
organization: Organization;
|
|
223
224
|
},
|
|
224
225
|
): Promise<{ paymentUrl: string }> {
|
|
@@ -239,36 +240,14 @@ export class StripeHelper {
|
|
|
239
240
|
});
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const vat = calculateVATPercentage(organization.address, organization.meta.VATNumber);
|
|
245
|
-
function calculateFee(fixed: number, percentageTimes100: number) {
|
|
246
|
-
return Math.round(Math.round(fixed + Math.max(1, totalPrice * percentageTimes100 / 100 / 100)) * (100 + vat) / 100); // € 0,21 + 0,2%
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (payment.method === PaymentMethod.iDEAL) {
|
|
250
|
-
fee = calculateFee(21, 20); // € 0,21 + 0,2%
|
|
251
|
-
}
|
|
252
|
-
else if (payment.method === PaymentMethod.Bancontact) {
|
|
253
|
-
fee = calculateFee(24, 20); // € 0,24 + 0,2%
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
fee = calculateFee(25, 150); // € 0,25 + 1,5%
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (stripeAccount.meta.type === 'standard') {
|
|
260
|
-
// Submerchant is charged by Stripe for the fees directly
|
|
261
|
-
fee = 0;
|
|
262
|
-
directCharge = true;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
payment.transferFee = fee * 100; // Convert back to 4 decimal places for storage
|
|
243
|
+
const directCharge = (stripeAccount.meta.type === 'standard');
|
|
244
|
+
const fee = directCharge ? 0 : Math.round(payment.transferFee / 100);
|
|
266
245
|
const serviceFee = Math.round(payment.serviceFeePayout / 100);
|
|
267
246
|
|
|
268
247
|
const fullMetadata = {
|
|
269
248
|
...(metadata ?? {}),
|
|
270
249
|
organizationVATNumber: organization.meta.VATNumber,
|
|
271
|
-
|
|
250
|
+
transferFee: fee,
|
|
272
251
|
serviceFee: serviceFee, // For historic reasons, this is stored in cents
|
|
273
252
|
};
|
|
274
253
|
|
|
@@ -335,8 +314,7 @@ export class StripeHelper {
|
|
|
335
314
|
paymentIntentModel.accountId = stripeAccount.accountId;
|
|
336
315
|
}
|
|
337
316
|
await paymentIntentModel.save();
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
317
|
+
} else {
|
|
340
318
|
// Use checkout flow
|
|
341
319
|
const data: Stripe.Checkout.SessionCreateParams = {
|
|
342
320
|
mode: 'payment',
|