@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
|
@@ -353,8 +353,7 @@ export class PlatformMembershipService {
|
|
|
353
353
|
// Update the price and dates of this active membership (could have changed)
|
|
354
354
|
try {
|
|
355
355
|
await m.calculatePrice(me, cheapestMembership.registration);
|
|
356
|
-
}
|
|
357
|
-
catch (e) {
|
|
356
|
+
} catch (e) {
|
|
358
357
|
// Ignore error: membership might not be available anymore
|
|
359
358
|
if (!silent) {
|
|
360
359
|
console.error('Failed to calculate price for active membership', m.id, e);
|
|
@@ -377,14 +376,12 @@ export class PlatformMembershipService {
|
|
|
377
376
|
console.log('Removing membership because cheaper membership found or duplicate, for: ' + me.id + ' - membership ' + m.id);
|
|
378
377
|
}
|
|
379
378
|
await m.doDelete();
|
|
380
|
-
}
|
|
381
|
-
else if (m.membershipTypeId === cheapestMembership.membership.id) {
|
|
379
|
+
} else if (m.membershipTypeId === cheapestMembership.membership.id) {
|
|
382
380
|
// Update price
|
|
383
381
|
if (!m.locked) {
|
|
384
382
|
try {
|
|
385
383
|
await m.calculatePrice(me);
|
|
386
|
-
}
|
|
387
|
-
catch (e) {
|
|
384
|
+
} catch (e) {
|
|
388
385
|
// Ignore error: membership might not be available anymore
|
|
389
386
|
if (!silent) {
|
|
390
387
|
console.error('Failed to calculate price for undeletable membership', m.id, e);
|
|
@@ -409,8 +406,7 @@ export class PlatformMembershipService {
|
|
|
409
406
|
if (me.details.memberNumber === null) {
|
|
410
407
|
try {
|
|
411
408
|
await MemberNumberService.assignMemberNumber(me, membership);
|
|
412
|
-
}
|
|
413
|
-
catch (error) {
|
|
409
|
+
} catch (error) {
|
|
414
410
|
console.error(`Failed to assign member number for id ${me.id}: ${error.message}`);
|
|
415
411
|
// If the assignment of the member number fails the membership is not created but the member is registered
|
|
416
412
|
continue;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { Model } from '@simonbackx/simple-database';
|
|
2
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
|
+
import type { EmailInterfaceBase } from '@stamhoofd/email';
|
|
4
|
+
import { Email } from '@stamhoofd/email';
|
|
5
|
+
import { BalanceItem, Organization, Platform, RegisterCode, UsedRegisterCode } from '@stamhoofd/models';
|
|
6
|
+
import { BalanceItemStatus, BalanceItemType } from '@stamhoofd/structures';
|
|
7
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
8
|
+
import { VATService } from './VATService.js';
|
|
9
|
+
|
|
10
|
+
export class ReferralService {
|
|
11
|
+
/**
|
|
12
|
+
* Prepares a register code for usage.
|
|
13
|
+
* Returns the models to save and the emails to send when succesful
|
|
14
|
+
*/
|
|
15
|
+
static async markUsed(organization: Organization, codeString: string): Promise<{ models: Model[]; emails: EmailInterfaceBase[] }> {
|
|
16
|
+
const alreadyUsed = await UsedRegisterCode.getFor(organization.id);
|
|
17
|
+
if (alreadyUsed) {
|
|
18
|
+
throw new SimpleError({
|
|
19
|
+
code: 'invalid_field',
|
|
20
|
+
message: 'Invalid register code',
|
|
21
|
+
human: $t('%1Ws'),
|
|
22
|
+
field: 'registerCode',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const code = await RegisterCode.getByID(codeString);
|
|
27
|
+
if (!code) {
|
|
28
|
+
throw new SimpleError({
|
|
29
|
+
code: 'invalid_field',
|
|
30
|
+
message: 'Invalid register code',
|
|
31
|
+
human: $t('%1Ua'),
|
|
32
|
+
field: 'registerCode',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const otherOrganization = code.organizationId ? await Organization.getByID(code.organizationId) : undefined;
|
|
37
|
+
|
|
38
|
+
const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
|
|
39
|
+
if (!membershipOrganizationId) {
|
|
40
|
+
throw new SimpleError({
|
|
41
|
+
code: 'invalid_field',
|
|
42
|
+
message: 'Referral codes unavailable',
|
|
43
|
+
human: $t('%1YF'),
|
|
44
|
+
field: 'registerCode',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
|
|
48
|
+
|
|
49
|
+
const delayEmails: EmailInterfaceBase[] = [];
|
|
50
|
+
let credit: BalanceItem | undefined = undefined;
|
|
51
|
+
|
|
52
|
+
if (code.value > 0) {
|
|
53
|
+
credit = new BalanceItem();
|
|
54
|
+
credit.type = BalanceItemType.ReferralDiscount;
|
|
55
|
+
credit.description = otherOrganization ? ('Tegoed gekregen van ' + otherOrganization.name) : code.description;
|
|
56
|
+
credit.payingOrganizationId = organization.id;
|
|
57
|
+
credit.organizationId = membershipOrganization.id;
|
|
58
|
+
credit.VATPercentage = 21;
|
|
59
|
+
credit.VATExcempt = VATService.getVATExcempt({
|
|
60
|
+
company: organization.defaultCompanies[0] ?? null,
|
|
61
|
+
sellingOrganization: membershipOrganization,
|
|
62
|
+
type: 'services',
|
|
63
|
+
});
|
|
64
|
+
credit.VATIncluded = false;
|
|
65
|
+
credit.quantity = 1;
|
|
66
|
+
credit.unitPrice = -code.value;
|
|
67
|
+
credit.createdAt = new Date();
|
|
68
|
+
credit.status = BalanceItemStatus.Due;
|
|
69
|
+
|
|
70
|
+
// Expire in one year (will get extended for every purchase or activation)
|
|
71
|
+
// credit.expireAt = new Date()
|
|
72
|
+
// credit.expireAt.setFullYear(credit.expireAt.getFullYear() + 1)
|
|
73
|
+
// credit.expireAt.setMilliseconds(0)
|
|
74
|
+
|
|
75
|
+
// Save later
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (otherOrganization) {
|
|
79
|
+
const admins = await otherOrganization.getAdminToEmails();
|
|
80
|
+
if (admins) {
|
|
81
|
+
if (code.invoiceValue) {
|
|
82
|
+
// Delay email until everything is validated and saved
|
|
83
|
+
delayEmails.push({
|
|
84
|
+
to: admins,
|
|
85
|
+
subject: organization.name + ' heeft jullie doorverwijzingslink gebruikt 🥳',
|
|
86
|
+
type: 'transactional',
|
|
87
|
+
text: 'Dag ' + otherOrganization.name + ',\n\nGoed nieuws! ' + organization.name + ' heeft jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren.\n\n— Stamhoofd',
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
// Delay email until everything is validated and saved
|
|
91
|
+
delayEmails.push({
|
|
92
|
+
to: admins,
|
|
93
|
+
subject: organization.name + ' heeft jullie doorverwijzingslink gebruikt 🥳',
|
|
94
|
+
type: 'transactional',
|
|
95
|
+
text: 'Dag ' + otherOrganization.name + ',\n\nGoed nieuws! ' + organization.name + ' heeft jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren. Als zij minstens 1 euro op Stamhoofd uitgeven ontvangen jullie een tegoed dat kan oplopen tot 100 euro per vereniging (zie daarvoor Stamhoofd > Instellingen). Lees zeker onze tips na om nog een groter bedrag te verzamelen.\n\n— Stamhoofd',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
delayEmails.push({
|
|
101
|
+
to: [
|
|
102
|
+
{ email: 'hallo@stamhoofd.be' },
|
|
103
|
+
],
|
|
104
|
+
subject: organization.name + ' heeft jullie doorverwijzingslink gebruikt 🥳',
|
|
105
|
+
type: 'transactional',
|
|
106
|
+
text: 'Dag Stamhoofd,\n\nGoed nieuws! ' + organization.name + ' heeft jullie doorverwijzingslink ' + code.code + ' gebruikt om zich op Stamhoofd te registreren. \n\n— Stamhoofd',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Save that we used this code (so we can reward the other organization)
|
|
111
|
+
const usedCode = new UsedRegisterCode();
|
|
112
|
+
usedCode.organizationId = organization.id;
|
|
113
|
+
usedCode.code = code.code;
|
|
114
|
+
|
|
115
|
+
// Save later
|
|
116
|
+
return {
|
|
117
|
+
models: credit ? [credit, usedCode] : [usedCode],
|
|
118
|
+
emails: delayEmails,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static async reward(usedCode: UsedRegisterCode) {
|
|
123
|
+
if (usedCode.balanceItemId) {
|
|
124
|
+
// Already received
|
|
125
|
+
console.error('Already rewarded for used register code ' + usedCode.id);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const code = await RegisterCode.getByID(usedCode.code);
|
|
130
|
+
if (!code || !code.organizationId) {
|
|
131
|
+
console.error("Couldn't find code " + usedCode.code + ' for used register code ' + usedCode.id);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const organization = await Organization.getByID(usedCode.organizationId);
|
|
136
|
+
if (!organization) {
|
|
137
|
+
console.error("Couldn't find organization with id " + usedCode.organizationId + ' for used register code ' + usedCode.id);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const receivingOrganization = await Organization.getByID(code.organizationId);
|
|
142
|
+
if (!receivingOrganization) {
|
|
143
|
+
console.error("Couldn't find receiving organization with id " + code.organizationId + ' for used register code ' + usedCode.id);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId!;
|
|
147
|
+
const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
|
|
148
|
+
|
|
149
|
+
const usedCount = await UsedRegisterCode.getUsedCount(usedCode.code) + 1;
|
|
150
|
+
|
|
151
|
+
const credit = new BalanceItem();
|
|
152
|
+
credit.type = BalanceItemType.ReferralDiscount;
|
|
153
|
+
credit.description = $t('%1ZE', { 'organization-name': organization.name });
|
|
154
|
+
credit.payingOrganizationId = code.organizationId;
|
|
155
|
+
credit.organizationId = (await Platform.getShared()).membershipOrganizationId!; // where do we get this discount from
|
|
156
|
+
credit.VATPercentage = 21;
|
|
157
|
+
credit.VATExcempt = VATService.getVATExcempt({
|
|
158
|
+
company: organization.defaultCompanies[0] ?? null,
|
|
159
|
+
sellingOrganization: membershipOrganization,
|
|
160
|
+
type: 'services',
|
|
161
|
+
});
|
|
162
|
+
credit.VATIncluded = false;
|
|
163
|
+
credit.quantity = 1;
|
|
164
|
+
credit.unitPrice = -(code.invoiceValue ? 0 : Math.min(100 * 100_00, usedCount * 10 * 100_00));
|
|
165
|
+
credit.createdAt = new Date();
|
|
166
|
+
credit.status = BalanceItemStatus.Due;
|
|
167
|
+
|
|
168
|
+
// Expire in one year (will get extended for every purchase or activation)
|
|
169
|
+
// credit.expireAt = new Date()
|
|
170
|
+
// credit.expireAt.setFullYear(credit.expireAt.getFullYear() + 1)
|
|
171
|
+
// credit.expireAt.setMilliseconds(0)
|
|
172
|
+
|
|
173
|
+
await credit.save();
|
|
174
|
+
|
|
175
|
+
usedCode.balanceItemId = credit.id;
|
|
176
|
+
await usedCode.save();
|
|
177
|
+
|
|
178
|
+
if (code.invoiceValue) {
|
|
179
|
+
// The receiving organization is invoiced instead of the organization for the usage of Stamhoofd.
|
|
180
|
+
const item = new BalanceItem();
|
|
181
|
+
item.type = BalanceItemType.Other;
|
|
182
|
+
item.description = 'Aankoop Stamhoofd voor ' + organization.name;
|
|
183
|
+
item.payingOrganizationId = receivingOrganization.id;
|
|
184
|
+
item.organizationId = membershipOrganizationId;
|
|
185
|
+
item.VATPercentage = 21;
|
|
186
|
+
item.VATExcempt = VATService.getVATExcempt({
|
|
187
|
+
company: organization.defaultCompanies[0] ?? null,
|
|
188
|
+
sellingOrganization: membershipOrganization,
|
|
189
|
+
type: 'services',
|
|
190
|
+
});
|
|
191
|
+
item.VATIncluded = false;
|
|
192
|
+
item.quantity = 1;
|
|
193
|
+
item.unitPrice = code.invoiceValue;
|
|
194
|
+
item.createdAt = new Date();
|
|
195
|
+
item.status = BalanceItemStatus.Due;
|
|
196
|
+
await item.save();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const admins = await receivingOrganization.getAdminRecipients();
|
|
200
|
+
if (admins) {
|
|
201
|
+
if (code.invoiceValue) {
|
|
202
|
+
Email.send({
|
|
203
|
+
to: admins,
|
|
204
|
+
from: Email.getQuickFromEmail(receivingOrganization.address.country),
|
|
205
|
+
subject: `${organization.name} heeft jullie tegoed gebruikt`,
|
|
206
|
+
text: 'Dag ' + receivingOrganization.name + ',\n\n' + organization.name + ' had jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren, en nu hebben ze dit ook gebruikt. Zoals afgesproken wordt hiervoor ' + Formatter.price(code.invoiceValue) + ' aangerekend via jullie openstaand saldo in jullie Stamhoofd account.'
|
|
207
|
+
+ '\n\n— Stamhoofd',
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
// Delay email until everything is validated and saved
|
|
211
|
+
Email.send({
|
|
212
|
+
from: Email.getQuickFromEmail(receivingOrganization.address.country),
|
|
213
|
+
to: admins,
|
|
214
|
+
subject: 'Je hebt ' + Formatter.price(-credit.unitPrice) + ' tegoed ontvangen 💰',
|
|
215
|
+
text: 'Dag ' + receivingOrganization.name + ',\n\nGeweldig nieuws! ' + organization.name + ' had jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren, en nu hebben ze ook voor het eerst minstens één euro uitgegeven. Daardoor ontvangen jullie ' + Formatter.price(-credit.unitPrice) + ' tegoed voor Stamhoofd (zie daarvoor Stamhoofd > Instellingen). '
|
|
216
|
+
+ (-credit.unitPrice <= 90 * 100_00 ? ('Bij de volgende vereniging ontvangen jullie nog meer: ' + Formatter.price(-credit.unitPrice + 10 * 100_00) + '. ') : '')
|
|
217
|
+
+ (-credit.unitPrice <= 80 * 100_00 ? ('En dat blijft oplopen tot € 100,00 per vereniging die je aanbrengt.') : '')
|
|
218
|
+
+ 'Doe zo verder! Lees zeker onze tips na om nog een groter bedrag te verzamelen.\n\n— Stamhoofd',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
|
2
2
|
import { encodeObject } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { BalanceItem, Document, Group, Member, Organization, Registration, RegistrationInvitation, sendEmailTemplate } from '@stamhoofd/models';
|
|
4
4
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
5
|
-
import { AppliedRegistrationDiscount, AuditLogSource, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, EmailTemplateType, GroupType, Recipient, Replacement, StockReservation, Version } from '@stamhoofd/structures';
|
|
5
|
+
import { AppliedRegistrationDiscount, AuditLogSource, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, EmailTemplateType, getAppHost, GroupType, Recipient, Replacement, StockReservation, Version } from '@stamhoofd/structures';
|
|
6
6
|
import { Formatter } from '@stamhoofd/utility';
|
|
7
7
|
import { AuditLogService } from './AuditLogService.js';
|
|
8
8
|
import { GroupService } from './GroupService.js';
|
|
@@ -81,7 +81,7 @@ export const RegistrationService = {
|
|
|
81
81
|
/**
|
|
82
82
|
* Delete all the invitations for the group and member linked to the registration.
|
|
83
83
|
* Will not throw if it fails.
|
|
84
|
-
* @param registration
|
|
84
|
+
* @param registration
|
|
85
85
|
*/
|
|
86
86
|
async handleWaitingListRegistrationAndInvitation(registration: Registration): Promise<void> {
|
|
87
87
|
const group = await Group.getByID(registration.groupId);
|
|
@@ -96,7 +96,7 @@ export const RegistrationService = {
|
|
|
96
96
|
.where('groupId', registration.groupId)
|
|
97
97
|
.andWhere('memberId', registration.memberId)
|
|
98
98
|
.first(false);
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
const waitingListIds = new Set<string>();
|
|
101
101
|
|
|
102
102
|
if (group.waitingListId) {
|
|
@@ -124,7 +124,7 @@ export const RegistrationService = {
|
|
|
124
124
|
.where('deactivatedAt', null)
|
|
125
125
|
.whereNot('registeredAt', null)
|
|
126
126
|
.first(false);
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
if (waitingListRegistration) {
|
|
129
129
|
// unsubscribe for waiting list (waiting lists cannot have a price -> balance items should not be updated)
|
|
130
130
|
await RegistrationService.deactivate(waitingListRegistration);
|
|
@@ -162,7 +162,7 @@ export const RegistrationService = {
|
|
|
162
162
|
}),
|
|
163
163
|
Replacement.create({
|
|
164
164
|
token: 'registerUrl',
|
|
165
|
-
value: 'https://' + organization.
|
|
165
|
+
value: 'https://' + getAppHost('registration', organization, user.permissions?.forOrganization(organization)?.isEmpty === false),
|
|
166
166
|
}),
|
|
167
167
|
Replacement.create({
|
|
168
168
|
token: 'groupName',
|
|
@@ -284,9 +284,65 @@ export const RegistrationService = {
|
|
|
284
284
|
* + in combination with validation and reading the webshop
|
|
285
285
|
*/
|
|
286
286
|
scheduleStockUpdate(id: string) {
|
|
287
|
+
this.scheduleStockUpdateAsync(id);
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Avoid calling this method directly. This can cause race conditions.
|
|
292
|
+
*/
|
|
293
|
+
async unsafeStockUpdate(updated: Registration) {
|
|
294
|
+
// Start with clearing all the stock reservations we've already made
|
|
295
|
+
if (updated.stockReservations) {
|
|
296
|
+
const groupIds = Formatter.uniqueArray(updated.stockReservations.flatMap(r => r.objectType === 'Group' ? [r.objectId] : []));
|
|
297
|
+
for (const groupId of groupIds) {
|
|
298
|
+
const stocks = StockReservation.filter('Group', groupId, updated.stockReservations);
|
|
299
|
+
|
|
300
|
+
// Technically we don't need to await this, but okay...
|
|
301
|
+
await Group.freeStockReservations(groupId, stocks);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (updated.shouldIncludeStock()) {
|
|
306
|
+
const groupStockReservations: StockReservation[] = [
|
|
307
|
+
// Group level stock reservations (stored in the group)
|
|
308
|
+
StockReservation.create({
|
|
309
|
+
objectId: updated.groupPrice.id,
|
|
310
|
+
objectType: 'GroupPrice',
|
|
311
|
+
amount: 1,
|
|
312
|
+
}),
|
|
313
|
+
...updated.options.map((o) => {
|
|
314
|
+
return StockReservation.create({
|
|
315
|
+
objectId: o.option.id,
|
|
316
|
+
objectType: 'GroupOption',
|
|
317
|
+
amount: o.amount,
|
|
318
|
+
});
|
|
319
|
+
}),
|
|
320
|
+
];
|
|
321
|
+
|
|
322
|
+
await Group.applyStockReservations(updated.groupId, groupStockReservations);
|
|
323
|
+
|
|
324
|
+
updated.stockReservations = [
|
|
325
|
+
// Global level stock reservations (stored in each group)
|
|
326
|
+
StockReservation.create({
|
|
327
|
+
objectId: updated.groupId,
|
|
328
|
+
objectType: 'Group',
|
|
329
|
+
amount: 1,
|
|
330
|
+
children: groupStockReservations,
|
|
331
|
+
}),
|
|
332
|
+
];
|
|
333
|
+
await updated.save();
|
|
334
|
+
} else {
|
|
335
|
+
if (updated.stockReservations.length) {
|
|
336
|
+
updated.stockReservations = [];
|
|
337
|
+
await updated.save();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
async scheduleStockUpdateAsync(id: string) {
|
|
287
343
|
QueueHandler.cancel('registration-stock-update-' + id);
|
|
288
344
|
|
|
289
|
-
AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
|
|
345
|
+
await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
|
|
290
346
|
await QueueHandler.schedule('registration-stock-update-' + id, async function (this: undefined) {
|
|
291
347
|
const updated = await Registration.getByID(id);
|
|
292
348
|
|
|
@@ -294,53 +350,7 @@ export const RegistrationService = {
|
|
|
294
350
|
return;
|
|
295
351
|
}
|
|
296
352
|
|
|
297
|
-
|
|
298
|
-
if (updated.stockReservations) {
|
|
299
|
-
const groupIds = Formatter.uniqueArray(updated.stockReservations.flatMap(r => r.objectType === 'Group' ? [r.objectId] : []));
|
|
300
|
-
for (const groupId of groupIds) {
|
|
301
|
-
const stocks = StockReservation.filter('Group', groupId, updated.stockReservations);
|
|
302
|
-
|
|
303
|
-
// Technically we don't need to await this, but okay...
|
|
304
|
-
await Group.freeStockReservations(groupId, stocks);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (updated.shouldIncludeStock()) {
|
|
309
|
-
const groupStockReservations: StockReservation[] = [
|
|
310
|
-
// Group level stock reservations (stored in the group)
|
|
311
|
-
StockReservation.create({
|
|
312
|
-
objectId: updated.groupPrice.id,
|
|
313
|
-
objectType: 'GroupPrice',
|
|
314
|
-
amount: 1,
|
|
315
|
-
}),
|
|
316
|
-
...updated.options.map((o) => {
|
|
317
|
-
return StockReservation.create({
|
|
318
|
-
objectId: o.option.id,
|
|
319
|
-
objectType: 'GroupOption',
|
|
320
|
-
amount: o.amount,
|
|
321
|
-
});
|
|
322
|
-
}),
|
|
323
|
-
];
|
|
324
|
-
|
|
325
|
-
await Group.applyStockReservations(updated.groupId, groupStockReservations);
|
|
326
|
-
|
|
327
|
-
updated.stockReservations = [
|
|
328
|
-
// Global level stock reservations (stored in each group)
|
|
329
|
-
StockReservation.create({
|
|
330
|
-
objectId: updated.groupId,
|
|
331
|
-
objectType: 'Group',
|
|
332
|
-
amount: 1,
|
|
333
|
-
children: groupStockReservations,
|
|
334
|
-
}),
|
|
335
|
-
];
|
|
336
|
-
await updated.save();
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
if (updated.stockReservations.length) {
|
|
340
|
-
updated.stockReservations = [];
|
|
341
|
-
await updated.save();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
353
|
+
await RegistrationService.unsafeStockUpdate(updated);
|
|
344
354
|
});
|
|
345
355
|
}).catch(console.error);
|
|
346
356
|
},
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { Platform } from '@stamhoofd/models';
|
|
4
|
+
import { LoginMethod, LoginMethodConfig, LoginProviderType, OpenIDClientConfiguration } from '@stamhoofd/structures';
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { ContextInstance } from '../helpers/Context.js';
|
|
8
|
+
import { SSOService } from './SSOService.js';
|
|
9
|
+
|
|
10
|
+
describe('SSOService', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.restoreAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('fromContext', () => {
|
|
16
|
+
it('does not validate by default', async () => {
|
|
17
|
+
mockPlatform({
|
|
18
|
+
ssoConfiguration: OpenIDClientConfiguration.create({}),
|
|
19
|
+
loginMethods: new Map(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const service = await runWithContext(() => SSOService.fromContext(LoginProviderType.SSO));
|
|
23
|
+
|
|
24
|
+
expect(service).toBeInstanceOf(SSOService);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('validates when requested explicitly', async () => {
|
|
28
|
+
mockPlatform({
|
|
29
|
+
ssoConfiguration: OpenIDClientConfiguration.create({}),
|
|
30
|
+
loginMethods: new Map(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await expect(runWithContext(() => SSOService.fromContext(LoginProviderType.SSO, { validate: true }))).rejects.toThrow('SSO not configured (correctly)');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('validateConfiguration', () => {
|
|
38
|
+
it('validates the OpenID client configuration getter', () => {
|
|
39
|
+
const service = createService({
|
|
40
|
+
googleConfiguration: null,
|
|
41
|
+
loginMethods: new Map([[LoginMethod.Google, LoginMethodConfig.create({})]]),
|
|
42
|
+
provider: LoginProviderType.Google,
|
|
43
|
+
organization: {},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(() => service.validateConfiguration()).toThrow(SimpleError);
|
|
47
|
+
expect(() => service.validateConfiguration()).toThrow('SSO not configured');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('validates the login method configuration getter', () => {
|
|
51
|
+
const service = createService({
|
|
52
|
+
ssoConfiguration: OpenIDClientConfiguration.create({}),
|
|
53
|
+
loginMethods: new Map(),
|
|
54
|
+
provider: LoginProviderType.SSO,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(() => service.validateConfiguration()).toThrow(SimpleError);
|
|
58
|
+
expect(() => service.validateConfiguration()).toThrow('SSO not configured (correctly)');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('accepts complete SSO configuration', () => {
|
|
62
|
+
const service = createService({
|
|
63
|
+
ssoConfiguration: OpenIDClientConfiguration.create({}),
|
|
64
|
+
loginMethods: new Map([[LoginMethod.SSO, LoginMethodConfig.create({})]]),
|
|
65
|
+
provider: LoginProviderType.SSO,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(() => service.validateConfiguration()).not.toThrow();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
async function runWithContext<T>(handler: () => Promise<T>) {
|
|
74
|
+
const context = new ContextInstance(new Request({
|
|
75
|
+
method: 'GET',
|
|
76
|
+
url: '/',
|
|
77
|
+
host: '',
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
return await ContextInstance.asyncLocalStorage.run(context, handler);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function mockPlatform(options: {
|
|
84
|
+
loginMethods: Map<LoginMethod, LoginMethodConfig>;
|
|
85
|
+
ssoConfiguration?: OpenIDClientConfiguration | null;
|
|
86
|
+
googleConfiguration?: OpenIDClientConfiguration | null;
|
|
87
|
+
}) {
|
|
88
|
+
vi.spyOn(Platform, 'getForEditing').mockResolvedValue(createPlatform(options));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function createService(options: {
|
|
92
|
+
provider: LoginProviderType;
|
|
93
|
+
loginMethods: Map<LoginMethod, LoginMethodConfig>;
|
|
94
|
+
ssoConfiguration?: OpenIDClientConfiguration | null;
|
|
95
|
+
googleConfiguration?: OpenIDClientConfiguration | null;
|
|
96
|
+
organization?: any;
|
|
97
|
+
}) {
|
|
98
|
+
return new SSOService({
|
|
99
|
+
provider: options.provider,
|
|
100
|
+
platform: createPlatform(options),
|
|
101
|
+
organization: options.organization,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createPlatform(options: {
|
|
106
|
+
loginMethods: Map<LoginMethod, LoginMethodConfig>;
|
|
107
|
+
ssoConfiguration?: OpenIDClientConfiguration | null;
|
|
108
|
+
googleConfiguration?: OpenIDClientConfiguration | null;
|
|
109
|
+
}) {
|
|
110
|
+
return {
|
|
111
|
+
config: {
|
|
112
|
+
loginMethods: options.loginMethods,
|
|
113
|
+
},
|
|
114
|
+
serverConfig: {
|
|
115
|
+
ssoConfiguration: options.ssoConfiguration,
|
|
116
|
+
googleConfiguration: options.googleConfiguration,
|
|
117
|
+
},
|
|
118
|
+
} as any;
|
|
119
|
+
}
|