@stamhoofd/backend 2.120.6 → 2.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output-2.txt +1 -0
- package/output.txt +1 -0
- package/package.json +70 -53
- package/src/audit-logs/EventNotificationLogger.ts +40 -0
- package/src/audit-logs/ModelLogger.ts +19 -15
- package/src/audit-logs/OrganizationLogger.ts +1 -1
- package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
- package/src/audit-logs/init.ts +9 -1
- package/src/boot.ts +19 -18
- package/src/crons/amazon-ses.ts +13 -26
- package/src/crons/balance-emails.ts +3 -5
- package/src/crons/clearExcelCache.test.ts +1 -1
- package/src/crons/clearExcelCache.ts +1 -2
- package/src/crons/drip-emails.ts +42 -0
- package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
- package/src/crons/helpers/isOutside.ts +11 -0
- package/src/crons/helpers/useSavedIterator.ts +32 -0
- package/src/crons/index.ts +7 -0
- package/src/crons/invoices.ts +160 -0
- package/src/crons/members-fees.ts +50 -0
- package/src/crons/mollie-chargebacks.ts +86 -0
- package/src/crons/service-fees.ts +152 -0
- package/src/crons/stripe-invoices.ts +48 -0
- package/src/crons/transfer-fees.ts +176 -0
- package/src/crons/update-cached-balances.ts +4 -4
- package/src/crons.ts +54 -74
- package/src/debug.ts +4 -2
- package/src/email-recipient-loaders/documents.ts +1 -1
- package/src/email-recipient-loaders/members.ts +1 -1
- package/src/email-recipient-loaders/payments.ts +87 -46
- package/src/email-recipient-loaders/registrations.ts +1 -1
- package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
- package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
- package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
- package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/SignupEndpoint.ts +2 -3
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
- package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
- package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
- package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
- package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
- package/src/endpoints/global/files/GetFileCache.ts +3 -4
- package/src/endpoints/global/files/UploadFile.ts +3 -4
- package/src/endpoints/global/files/UploadImage.ts +3 -4
- package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
- package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -13
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
- package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +38 -33
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
- package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
- package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +19 -6
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
- package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
- package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
- package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +12 -4
- package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +311 -0
- package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +15 -4
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -5
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +67 -20
- package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
- package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +24 -17
- package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +32 -22
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +29 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +15 -13
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +28 -50
- package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
- package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
- package/src/endpoints/system/HealthEndpoint.ts +1 -1
- package/src/excel-loaders/balance-items.ts +1 -1
- package/src/excel-loaders/event-notifications.ts +1 -1
- package/src/excel-loaders/members.ts +1 -1
- package/src/excel-loaders/organizations.ts +3 -3
- package/src/excel-loaders/payments.ts +31 -1
- package/src/excel-loaders/platform-memberships.ts +13 -14
- package/src/excel-loaders/receivable-balances.ts +2 -2
- package/src/excel-loaders/registrations.ts +2 -3
- package/src/helpers/AddressValidator.ts +7 -14
- package/src/helpers/AdminPermissionChecker.ts +60 -65
- package/src/helpers/AuthenticatedStructures.ts +153 -51
- package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
- package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
- package/src/helpers/BuckarooHelper.ts +2 -4
- package/src/helpers/CheckSettlements.ts +12 -24
- package/src/helpers/Context.ts +15 -10
- package/src/helpers/CookieHelper.ts +1 -2
- package/src/helpers/EmailResumer.ts +1 -2
- package/src/helpers/FileCache.ts +2 -3
- package/src/helpers/FinancialSupportHelper.ts +21 -0
- package/src/helpers/FlagMomentCleanup.ts +1 -2
- package/src/helpers/ForwardHandler.ts +5 -10
- package/src/helpers/GlobalHelper.ts +3 -3
- package/src/helpers/LegacyBillingConverter.ts +787 -0
- package/src/helpers/MemberMerger.test.ts +782 -0
- package/src/helpers/MemberMerger.ts +600 -0
- package/src/helpers/MemberUserSyncer.ts +6 -12
- package/src/helpers/MembershipCharger.ts +2 -4
- package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
- package/src/helpers/PaymentCustomerResolver.ts +144 -0
- package/src/helpers/PeriodHelper.ts +2 -3
- package/src/helpers/RecordAnswerHelper.test.ts +741 -0
- package/src/helpers/RecordAnswerHelper.ts +116 -0
- package/src/helpers/SeedTools.ts +106 -0
- package/src/helpers/ServiceFeeHelper.ts +125 -15
- package/src/helpers/SetupStepUpdater.ts +38 -32
- package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
- package/src/helpers/StripeHelper.ts +15 -38
- package/src/helpers/StripeInvoicer.ts +419 -0
- package/src/helpers/StripePayoutChecker.ts +5 -10
- package/src/helpers/TagHelper.ts +7 -8
- package/src/helpers/TemporaryMemberAccess.ts +1 -2
- package/src/helpers/ThrottledQueue.ts +1 -2
- package/src/helpers/UitpasTokenRepository.ts +1 -2
- package/src/helpers/ViesHelper.ts +11 -11
- package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
- package/src/helpers/outstandingBalanceJoin.ts +2 -2
- package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
- package/src/migrate.ts +12 -5
- package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
- package/src/migrations/0000000006-default-organization.ts +139 -0
- package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
- package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
- package/src/migrations/1750090029-document-update-year.ts +103 -0
- package/src/migrations/1750090030-records-configuration.ts +92 -0
- package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
- package/src/migrations/1752848540-national-numbers.ts +585 -0
- package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
- package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +95 -27
- package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
- package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
- package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
- package/src/migrations/1761665607-sync-member-users.ts +40 -0
- package/src/migrations/1779121239-default-invoice-email-template.sql +3 -0
- package/src/migrations/1779121240-invoice-email-template.sql +6 -0
- package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
- package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
- package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
- package/src/migrations/1780933598-document-groups.ts +88 -0
- package/src/migrations/1781692342-balance-item-relations.ts +13 -0
- package/src/migrations/1781857866-platform-admins.ts +103 -0
- package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
- package/src/seeds/0000000001-throw.ts +5 -0
- package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
- package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
- package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
- package/src/seeds/1773754928-force-save-members.ts +41 -8
- package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
- package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
- package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
- package/src/seeds/1780915002-update-orders.ts +39 -0
- package/src/seeds/1782047708-organization-company-address.ts +44 -0
- package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
- package/src/services/AuditLogService.test.ts +51 -0
- package/src/services/AuditLogService.ts +45 -0
- package/src/services/BalanceItemService.ts +148 -55
- package/src/services/CpuService.ts +3 -6
- package/src/services/DatabaseCollationService.test.ts +1 -2
- package/src/services/DocumentService.ts +1 -2
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/FileSignService.ts +2 -4
- package/src/services/InvoicePdfService.ts +286 -0
- package/src/services/InvoiceService.ts +330 -77
- package/src/services/InvoiceXMLService.ts +424 -0
- package/src/services/MemberNumberService.ts +6 -11
- package/src/services/MemberRecordStore.ts +8 -10
- package/src/services/MollieService.ts +530 -0
- package/src/services/PaymentMandateService.ts +214 -0
- package/src/services/PaymentReallocationService.test.ts +1 -1
- package/src/services/PaymentReallocationService.ts +7 -9
- package/src/services/PaymentService.ts +820 -328
- package/src/services/PlatformMembershipService.ts +5 -9
- package/src/services/ReferralService.ts +223 -0
- package/src/services/RegistrationService.ts +125 -54
- package/src/services/SSOService.test.ts +119 -0
- package/src/services/SSOService.ts +56 -49
- package/src/services/STPackageService.ts +97 -68
- package/src/services/UniqueMemberNumberService.ts +69 -0
- package/src/services/UniqueUserService.ts +3 -6
- package/src/services/VATService.ts +85 -0
- package/src/services/data/invoice.hbs.html +686 -0
- package/src/services/uitpas/PassholderEndpoints.ts +3 -6
- package/src/services/uitpas/UitpasService.ts +6 -10
- package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
- package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
- package/src/services/uitpas/searchUitpasEvents.ts +1 -2
- package/src/sql-filters/audit-logs.ts +1 -1
- package/src/sql-filters/balance-item-payments.ts +1 -1
- package/src/sql-filters/balance-items.ts +1 -1
- package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
- package/src/sql-filters/document-templates.ts +1 -1
- package/src/sql-filters/documents.ts +1 -1
- package/src/sql-filters/email-recipients.ts +1 -1
- package/src/sql-filters/emails.ts +1 -1
- package/src/sql-filters/event-notifications.ts +1 -1
- package/src/sql-filters/events.ts +6 -1
- package/src/sql-filters/groups.ts +16 -1
- package/src/sql-filters/invoiced-balance-items.ts +1 -1
- package/src/sql-filters/invoices.ts +1 -1
- package/src/sql-filters/member-responsibility-records.ts +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/src/sql-filters/orders.ts +4 -4
- package/src/sql-filters/organization-registration-periods.ts +1 -1
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-filters/payments.ts +5 -0
- package/src/sql-filters/platform-memberships.ts +3 -3
- package/src/sql-filters/receivable-balances.ts +1 -1
- package/src/sql-filters/registration-invitations.ts +90 -0
- package/src/sql-filters/registration-periods.ts +1 -1
- package/src/sql-filters/registrations.ts +1 -1
- package/src/sql-filters/tickets.ts +1 -1
- package/src/sql-filters/users.ts +1 -1
- package/src/sql-sorters/groups.ts +23 -0
- package/src/sql-sorters/platform-memberships.ts +16 -16
- package/src/sql-sorters/registration-invitations.ts +36 -0
- package/tests/actions/patchOrganizationMember.ts +1 -1
- package/tests/actions/patchUserMember.ts +1 -1
- package/tests/assertions/assertBalances.ts +6 -12
- package/tests/e2e/api-rate-limits.test.ts +12 -24
- package/tests/e2e/bundle-discounts.test.ts +4 -1
- package/tests/e2e/charge-members.test.ts +13 -11
- package/tests/e2e/documents.test.ts +4 -1
- package/tests/e2e/register.test.ts +7 -9
- package/tests/e2e/stock.test.ts +4 -3
- package/tests/e2e/tickets.test.ts +1 -1
- package/tests/helpers/MollieMocker.ts +462 -0
- package/tests/helpers/PayconiqMocker.ts +138 -45
- package/tests/helpers/StripeMocker.ts +9 -6
- package/tests/helpers/index.ts +4 -0
- package/tests/init/initAdmin.ts +2 -2
- package/tests/init/initBundleDiscount.ts +1 -1
- package/tests/init/initMembershipOrganization.ts +13 -0
- package/tests/init/initPermissionRole.ts +3 -4
- package/tests/vitest.global.setup.ts +1 -1
- package/vitest.config.js +7 -0
- package/.nvmrc +0 -1
- package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
- package/src/helpers/GroupBuilder.ts +0 -418
- package/src/seeds/0000000000-example.ts +0 -11
- package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
- package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
- package/src/seeds/1726847064-setup-steps.ts +0 -16
- package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
- package/src/seeds/1729253172-update-orders.ts +0 -21
- package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
- package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
- package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
- package/src/seeds/1740046783-update-membership.ts +0 -17
- package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
- package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
- package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
- package/src/seeds/1750090030-records-configuration.ts +0 -26
- package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
- package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
- package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
- package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
- package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
- package/src/seeds/1756303697-update-email-counts.ts +0 -76
- package/src/seeds/1761665607-sync-member-users.ts +0 -59
- package/src/seeds/1765896674-document-update-year.ts +0 -66
- /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
- /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
- /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { DecodedRequest} from '@simonbackx/simple-endpoints';
|
|
1
|
+
import type { DecodedRequest } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import { LoginProviderType, OpenIDClientConfiguration, Token as TokenStruct } from '@stamhoofd/structures';
|
|
4
|
+
import { Organization, Platform, Token, User, Webshop } from '@stamhoofd/models';
|
|
5
|
+
import type { LoginMethod, StartOpenIDFlowStruct } from '@stamhoofd/structures';
|
|
6
|
+
import { getAppHost, LoginProviderType, OpenIDClientConfiguration, Token as TokenStruct } from '@stamhoofd/structures';
|
|
8
7
|
import crypto from 'crypto';
|
|
9
8
|
import { generators, Issuer } from 'openid-client';
|
|
10
9
|
import { Context } from '../helpers/Context.js';
|
|
@@ -32,6 +31,7 @@ type SSOSessionContext = {
|
|
|
32
31
|
redirectUri: string;
|
|
33
32
|
spaState: string;
|
|
34
33
|
providerType: LoginProviderType;
|
|
34
|
+
organizationId?: string | null;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Link this method to this existing user and don't create a new token
|
|
@@ -127,17 +127,19 @@ export class SSOService {
|
|
|
127
127
|
let redirectUri = 'https://' + STAMHOOFD.domains.dashboard;
|
|
128
128
|
|
|
129
129
|
if (this.organization) {
|
|
130
|
-
redirectUri = 'https://' + this.organization
|
|
130
|
+
redirectUri = 'https://' + getAppHost('registration', this.organization, true);
|
|
131
131
|
}
|
|
132
132
|
return redirectUri;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
static async fromContext(provider: LoginProviderType) {
|
|
135
|
+
static async fromContext(provider: LoginProviderType, options: { validate?: boolean } = {}) {
|
|
136
136
|
const organization = Context.organization;
|
|
137
137
|
const platform = await Platform.getForEditing();
|
|
138
138
|
|
|
139
139
|
const service = new SSOService({ provider, platform, organization });
|
|
140
|
-
|
|
140
|
+
if (options.validate ?? false) {
|
|
141
|
+
service.validateConfiguration();
|
|
142
|
+
}
|
|
141
143
|
|
|
142
144
|
return service;
|
|
143
145
|
}
|
|
@@ -148,17 +150,14 @@ export class SSOService {
|
|
|
148
150
|
if (this.provider === LoginProviderType.SSO) {
|
|
149
151
|
if (this.organization) {
|
|
150
152
|
configuration = this.organization.serverMeta.ssoConfiguration ?? OpenIDClientConfiguration.create({});
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
+
} else {
|
|
153
154
|
configuration = this.platform.serverConfig.ssoConfiguration ?? OpenIDClientConfiguration.create({});
|
|
154
155
|
}
|
|
155
|
-
}
|
|
156
|
-
else if (this.provider === LoginProviderType.Google) {
|
|
156
|
+
} else if (this.provider === LoginProviderType.Google) {
|
|
157
157
|
if (this.organization) {
|
|
158
158
|
// Not supported yet
|
|
159
159
|
configuration = null;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
160
|
+
} else {
|
|
162
161
|
configuration = this.platform.serverConfig.googleConfiguration ?? OpenIDClientConfiguration.create({});
|
|
163
162
|
}
|
|
164
163
|
}
|
|
@@ -174,15 +173,11 @@ export class SSOService {
|
|
|
174
173
|
}
|
|
175
174
|
|
|
176
175
|
get loginConfiguration() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
message: 'Login configuration not yet supported for organization users',
|
|
181
|
-
statusCode: 400,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
176
|
+
const loginMethods = this.organization
|
|
177
|
+
? this.organization.meta.loginMethods
|
|
178
|
+
: this.platform.config.loginMethods;
|
|
184
179
|
|
|
185
|
-
const loginConfiguration =
|
|
180
|
+
const loginConfiguration = loginMethods.get(this.provider as unknown as LoginMethod);
|
|
186
181
|
if (!loginConfiguration) {
|
|
187
182
|
throw new SimpleError({
|
|
188
183
|
code: 'invalid_client',
|
|
@@ -195,9 +190,9 @@ export class SSOService {
|
|
|
195
190
|
}
|
|
196
191
|
|
|
197
192
|
validateConfiguration() {
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
// Trigger getters to validate both server and login method configuration.
|
|
194
|
+
void this.configuration;
|
|
195
|
+
void this.loginConfiguration;
|
|
201
196
|
}
|
|
202
197
|
|
|
203
198
|
validateEmail(email: string) {
|
|
@@ -218,21 +213,19 @@ export class SSOService {
|
|
|
218
213
|
if (this.provider === LoginProviderType.SSO) {
|
|
219
214
|
if (this.organization) {
|
|
220
215
|
this.organization.serverMeta.ssoConfiguration = configuration;
|
|
221
|
-
await this.
|
|
216
|
+
await this.validateClientConfiguration();
|
|
222
217
|
await this.organization.save();
|
|
223
218
|
return;
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
219
|
+
} else {
|
|
226
220
|
this.platform.serverConfig.ssoConfiguration = configuration;
|
|
227
|
-
await this.
|
|
221
|
+
await this.validateClientConfiguration();
|
|
228
222
|
await this.platform.save();
|
|
229
223
|
return;
|
|
230
224
|
}
|
|
231
|
-
}
|
|
232
|
-
else if (this.provider === LoginProviderType.Google) {
|
|
225
|
+
} else if (this.provider === LoginProviderType.Google) {
|
|
233
226
|
if (!this.organization) {
|
|
234
227
|
this.platform.serverConfig.googleConfiguration = configuration;
|
|
235
|
-
await this.
|
|
228
|
+
await this.validateClientConfiguration();
|
|
236
229
|
await this.platform.save();
|
|
237
230
|
return;
|
|
238
231
|
}
|
|
@@ -245,6 +238,20 @@ export class SSOService {
|
|
|
245
238
|
});
|
|
246
239
|
}
|
|
247
240
|
|
|
241
|
+
async validateClientConfiguration() {
|
|
242
|
+
try {
|
|
243
|
+
await this.getClient();
|
|
244
|
+
} catch (e) {
|
|
245
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
246
|
+
throw new SimpleError({
|
|
247
|
+
code: 'invalid_sso_configuration',
|
|
248
|
+
message: 'Failed to validate SSO configuration: ' + message,
|
|
249
|
+
human: $t('%1Xe', { message }),
|
|
250
|
+
statusCode: 400,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
248
255
|
async getClient() {
|
|
249
256
|
const issuer = await Issuer.discover(this.configuration.issuer);
|
|
250
257
|
const client = new issuer.Client({
|
|
@@ -331,8 +338,7 @@ export class SSOService {
|
|
|
331
338
|
|
|
332
339
|
if (allowedHost.host === givenUrl.host && givenUrl.protocol === 'https:') {
|
|
333
340
|
redirectUri = givenUrl.href;
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
341
|
+
} else {
|
|
336
342
|
throw new SimpleError({
|
|
337
343
|
code: 'redirect_uri_not_allowed',
|
|
338
344
|
message: 'Redirect uri not allowed',
|
|
@@ -340,8 +346,7 @@ export class SSOService {
|
|
|
340
346
|
statusCode: 400,
|
|
341
347
|
});
|
|
342
348
|
}
|
|
343
|
-
}
|
|
344
|
-
catch (e) {
|
|
349
|
+
} catch (e) {
|
|
345
350
|
throw new SimpleError({
|
|
346
351
|
code: 'invalid_redirect_uri',
|
|
347
352
|
message: 'Invalid redirect uri',
|
|
@@ -385,8 +390,7 @@ export class SSOService {
|
|
|
385
390
|
let parsed: URL;
|
|
386
391
|
try {
|
|
387
392
|
parsed = new URL(uri);
|
|
388
|
-
}
|
|
389
|
-
catch (e) {
|
|
393
|
+
} catch (e) {
|
|
390
394
|
throw new SimpleError({
|
|
391
395
|
code: 'invalid_redirect_uri',
|
|
392
396
|
message: 'Invalid redirect uri',
|
|
@@ -429,6 +433,7 @@ export class SSOService {
|
|
|
429
433
|
redirectUri,
|
|
430
434
|
spaState, // this is the state frontend <-> backend (not backend <-> SSO provider)
|
|
431
435
|
providerType: this.provider,
|
|
436
|
+
organizationId: this.organization?.id ?? null,
|
|
432
437
|
userId: user?.id ?? null,
|
|
433
438
|
};
|
|
434
439
|
|
|
@@ -465,8 +470,7 @@ export class SSOService {
|
|
|
465
470
|
response.status = 302;
|
|
466
471
|
|
|
467
472
|
return response;
|
|
468
|
-
}
|
|
469
|
-
catch (e) {
|
|
473
|
+
} catch (e) {
|
|
470
474
|
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`%G0`));
|
|
471
475
|
console.error('Error in openID callback', e);
|
|
472
476
|
return SSOServiceWithSession.getErrorRedirectResponse(session, message);
|
|
@@ -492,7 +496,14 @@ export class SSOServiceWithSession {
|
|
|
492
496
|
throw new Error('Missing session');
|
|
493
497
|
}
|
|
494
498
|
|
|
495
|
-
const
|
|
499
|
+
const platform = await Platform.getForEditing();
|
|
500
|
+
const organization = session.organizationId ? await Organization.getByID(session.organizationId) : null;
|
|
501
|
+
if (session.organizationId && !organization) {
|
|
502
|
+
throw new Error('Missing organization');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const service = new SSOService({ provider: session.providerType, platform, organization });
|
|
506
|
+
service.validateConfiguration();
|
|
496
507
|
|
|
497
508
|
return new SSOServiceWithSession(session, service, request);
|
|
498
509
|
}
|
|
@@ -548,8 +559,7 @@ export class SSOServiceWithSession {
|
|
|
548
559
|
|
|
549
560
|
if (tokenSet.refresh_token) {
|
|
550
561
|
console.log('OK. Refresh token received!');
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
562
|
+
} else {
|
|
553
563
|
console.log('No refresh token');
|
|
554
564
|
}
|
|
555
565
|
|
|
@@ -600,8 +610,7 @@ export class SSOServiceWithSession {
|
|
|
600
610
|
statusCode: 500,
|
|
601
611
|
});
|
|
602
612
|
}
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
613
|
+
} else {
|
|
605
614
|
if (this.session.userId && user.id !== this.session.userId) {
|
|
606
615
|
throw new SimpleError({
|
|
607
616
|
code: 'invalid_user',
|
|
@@ -640,8 +649,7 @@ export class SSOServiceWithSession {
|
|
|
640
649
|
const st = new TokenStruct(token);
|
|
641
650
|
redirectUri.searchParams.set('oid_rt', st.refreshToken);
|
|
642
651
|
redirectUri.searchParams.set('s', session.spaState);
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
652
|
+
} else {
|
|
645
653
|
redirectUri.searchParams.set('s', session.spaState);
|
|
646
654
|
redirectUri.searchParams.set('msg', 'Je account is succesvol gekoppeld');
|
|
647
655
|
}
|
|
@@ -650,8 +658,7 @@ export class SSOServiceWithSession {
|
|
|
650
658
|
response.status = 302;
|
|
651
659
|
|
|
652
660
|
return response;
|
|
653
|
-
}
|
|
654
|
-
catch (e) {
|
|
661
|
+
} catch (e) {
|
|
655
662
|
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`%G0`));
|
|
656
663
|
console.error('Error in openID callback', e);
|
|
657
664
|
return SSOServiceWithSession.getErrorRedirectResponse(session, message);
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
1
|
import { BalanceItem, Organization, Platform, STPackage } from '@stamhoofd/models';
|
|
3
2
|
import { SQL } from '@stamhoofd/sql';
|
|
4
|
-
import type {
|
|
5
|
-
import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, STPricingType, TranslatedString
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { GroupBuilder } from '../helpers/GroupBuilder.js';
|
|
3
|
+
import type { Company } from '@stamhoofd/structures';
|
|
4
|
+
import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, getPricingTypeName, STPackageStatus, STPackageType, STPackageTypeHelper, STPricingType, TranslatedString } from '@stamhoofd/structures';
|
|
5
|
+
import { Formatter, STMath } from '@stamhoofd/utility';
|
|
6
|
+
import { VATService } from './VATService.js';
|
|
9
7
|
|
|
10
8
|
export class STPackageService {
|
|
11
9
|
static async getActivePackages(organizationId: string) {
|
|
@@ -35,18 +33,13 @@ export class STPackageService {
|
|
|
35
33
|
static async getChargeableQuantity(pack: STPackage) {
|
|
36
34
|
// If type is
|
|
37
35
|
let amount = 1;
|
|
38
|
-
let membersCount: number | null = null;
|
|
39
|
-
|
|
40
|
-
if (membersCount === null && pack.meta.pricingType === STPricingType.PerMember) {
|
|
41
|
-
membersCount = await Organization.getActiveMembers(pack.organizationId);
|
|
42
|
-
}
|
|
43
36
|
|
|
44
37
|
if (pack.meta.pricingType === STPricingType.PerMember) {
|
|
45
|
-
amount =
|
|
38
|
+
amount = await Organization.getActiveMembers(pack.organizationId);
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
if (amount < pack.meta.minimumAmount) {
|
|
49
|
-
amount = pack.meta.minimumAmount;
|
|
41
|
+
if (amount < (pack.meta.minimumAmount || 1)) {
|
|
42
|
+
amount = (pack.meta.minimumAmount || 1);
|
|
50
43
|
}
|
|
51
44
|
|
|
52
45
|
return amount;
|
|
@@ -74,6 +67,17 @@ export class STPackageService {
|
|
|
74
67
|
}
|
|
75
68
|
}
|
|
76
69
|
|
|
70
|
+
// Stop trials
|
|
71
|
+
const trialType = STPackageTypeHelper.getTrial(pack.meta.type);
|
|
72
|
+
if (trialType) {
|
|
73
|
+
const activePackages = await this.getActivePackages(pack.organizationId);
|
|
74
|
+
for (const p of activePackages) {
|
|
75
|
+
if (p.meta.type === trialType) {
|
|
76
|
+
await this.deactivatePackage(p);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
77
81
|
await this.updateOrganizationPackages(pack.organizationId);
|
|
78
82
|
}
|
|
79
83
|
|
|
@@ -83,6 +87,14 @@ export class STPackageService {
|
|
|
83
87
|
await old.save();
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
static async deactivatePackage(pack: STPackage) {
|
|
91
|
+
console.log('Deactivating package', pack.meta.type, 'with id', pack.id);
|
|
92
|
+
// Deactivate
|
|
93
|
+
pack.removeAt = new Date();
|
|
94
|
+
pack.removeAt.setTime(pack.removeAt.getTime() - 5_000);
|
|
95
|
+
await pack.save();
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
static async getPaidOrPendingQuantity(pack: STPackage) {
|
|
87
99
|
if (!pack.existsInDatabase || pack.createdAt.getTime() > Date.now() - 1_000) {
|
|
88
100
|
return 0;
|
|
@@ -91,7 +103,7 @@ export class STPackageService {
|
|
|
91
103
|
return await BalanceItem.select()
|
|
92
104
|
.where('packageId', pack.id)
|
|
93
105
|
.where('status', BalanceItemStatus.Due)
|
|
94
|
-
.
|
|
106
|
+
.sum(SQL.column('amount'));
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
/**
|
|
@@ -99,42 +111,33 @@ export class STPackageService {
|
|
|
99
111
|
*
|
|
100
112
|
* Convertable into STInvoiceItem (or the diffence if amount is increased)
|
|
101
113
|
* Use this to calculate prices or create an invoice
|
|
102
|
-
* This will calculate the price to expand the package to the given amount
|
|
114
|
+
* This will calculate the price to expand the package to the given amount
|
|
103
115
|
* If you want to renew a package, you need to create a new package first
|
|
104
116
|
*/
|
|
105
|
-
static async chargePackage(pack: STPackage,
|
|
117
|
+
static async chargePackage(pack: STPackage, sellingOrganization: Organization, company: Company | null | undefined, date?: Date): Promise<BalanceItem | null> {
|
|
106
118
|
let unitPrice = Math.round(pack.meta.unitPrice);
|
|
107
119
|
|
|
108
120
|
let amount = await this.getChargeableQuantity(pack);
|
|
109
|
-
|
|
110
|
-
if (amount < pack.meta.minimumAmount) {
|
|
111
|
-
// Minimum should get applied first, because we might already have paid for the minimum (paid amount)
|
|
112
|
-
amount = pack.meta.minimumAmount;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
121
|
const paid = await this.getPaidOrPendingQuantity(pack);
|
|
116
122
|
amount -= paid;
|
|
117
123
|
|
|
118
124
|
if (amount <= 0) {
|
|
119
|
-
amount
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (amount <= 0) {
|
|
125
|
+
console.log('Not charging package', pack.id, 'amount is', amount);
|
|
123
126
|
return null;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
/// When pricing type is memebrs, the price is calculated per year.
|
|
127
130
|
/// If a shorter period is remaining, we give a discount in order
|
|
128
131
|
/// to no need to handle it more complicated
|
|
129
|
-
let now = date ??
|
|
132
|
+
let now = date ?? Formatter.luxon().startOf('day').toJSDate();
|
|
130
133
|
if (now < pack.meta.startDate) {
|
|
131
134
|
// When creating a new package, we sometimes buy it for the future, so use that date instead of now
|
|
132
135
|
now = pack.meta.startDate;
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
if (pack.
|
|
136
|
-
const totalDays = Math.round((pack.
|
|
137
|
-
let remainingDays = Math.round((pack.
|
|
138
|
+
if (pack.endDate && pack.meta.pricingType !== STPricingType.Fixed) {
|
|
139
|
+
const totalDays = Math.round((pack.endDate.getTime() - pack.meta.startDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
140
|
+
let remainingDays = Math.round((pack.endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
138
141
|
|
|
139
142
|
/// First 3 months are full price
|
|
140
143
|
const paidDays = 30 * 3;
|
|
@@ -150,8 +153,7 @@ export class STPackageService {
|
|
|
150
153
|
|
|
151
154
|
if (pack.meta.pricingType === STPricingType.PerMember) {
|
|
152
155
|
unitPrice = Math.round(Math.min(unitPrice, unitPrice * remainingDays / (Math.max(365, totalDays) - paidDays)));
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
156
|
+
} else {
|
|
155
157
|
unitPrice = Math.round(unitPrice);
|
|
156
158
|
}
|
|
157
159
|
}
|
|
@@ -159,46 +161,40 @@ export class STPackageService {
|
|
|
159
161
|
const item = new BalanceItem();
|
|
160
162
|
item.type = BalanceItemType.STPackage;
|
|
161
163
|
item.description = pack.meta.name + ' ' + (
|
|
162
|
-
pack.
|
|
163
|
-
? $t('%1LM', { startDate: Formatter.
|
|
164
|
-
: $t('%1LN', { startDate: Formatter.
|
|
164
|
+
pack.endDate
|
|
165
|
+
? $t('%1LM', { startDate: Formatter.startDate(now, false, true), endDate: Formatter.endDate(pack.endDate, false, true) })
|
|
166
|
+
: $t('%1LN', { startDate: Formatter.startDate(pack.meta.startDate, false, true) })
|
|
165
167
|
);
|
|
166
168
|
item.relations.set(BalanceItemRelationType.STPackage, BalanceItemRelation.create({
|
|
167
169
|
id: pack.id,
|
|
168
170
|
name: TranslatedString.create(pack.meta.name),
|
|
169
171
|
}));
|
|
172
|
+
item.relations.set(BalanceItemRelationType.STPricingType, BalanceItemRelation.create({
|
|
173
|
+
id: pack.meta.pricingType,
|
|
174
|
+
name: TranslatedString.create(getPricingTypeName(pack.meta.pricingType)),
|
|
175
|
+
}));
|
|
176
|
+
|
|
170
177
|
item.packageId = pack.id;
|
|
171
178
|
item.startDate = now;
|
|
172
179
|
|
|
173
|
-
if (pack.
|
|
174
|
-
item.endDate = pack.
|
|
180
|
+
if (pack.endDate) {
|
|
181
|
+
item.endDate = pack.endDate;
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
item.payingOrganizationId = pack.organizationId;
|
|
178
|
-
|
|
179
|
-
if (!membershipOrganizationId) {
|
|
180
|
-
throw new SimpleError({
|
|
181
|
-
code: 'unavailable',
|
|
182
|
-
message: 'No membership organization id set on the platform',
|
|
183
|
-
human: 'Package purchases are currently unavailable',
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
item.organizationId = membershipOrganizationId;
|
|
185
|
+
item.organizationId = sellingOrganization.id;
|
|
187
186
|
item.VATPercentage = 21;
|
|
188
187
|
item.VATIncluded = false;
|
|
189
|
-
item.VATExcempt =
|
|
188
|
+
item.VATExcempt = VATService.getVATExcempt({
|
|
189
|
+
company,
|
|
190
|
+
sellingOrganization,
|
|
191
|
+
type: 'services',
|
|
192
|
+
});
|
|
190
193
|
item.quantity = amount;
|
|
191
|
-
item.unitPrice = unitPrice;
|
|
192
|
-
item.createdAt =
|
|
194
|
+
item.unitPrice = STMath.round(unitPrice / 100) * 100;
|
|
195
|
+
item.createdAt = new Date();
|
|
193
196
|
item.status = BalanceItemStatus.Hidden;
|
|
194
197
|
|
|
195
|
-
if (customer && customer.company && customer.company.address) {
|
|
196
|
-
// Reverse charged vat applicable?
|
|
197
|
-
if (customer.company.address.country !== Country.Belgium) {
|
|
198
|
-
item.VATExcempt = VATExcemptReason.IntraCommunity;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
198
|
return item;
|
|
203
199
|
}
|
|
204
200
|
|
|
@@ -213,15 +209,26 @@ export class STPackageService {
|
|
|
213
209
|
}
|
|
214
210
|
|
|
215
211
|
private static async getOrganizationPackagesMap(organizationId: string): Promise<Map<STPackageType, STPackageStatus>> {
|
|
212
|
+
const map = new Map<STPackageType, STPackageStatus>();
|
|
213
|
+
|
|
214
|
+
if (STAMHOOFD.userMode === 'platform' || organizationId === ((await Platform.getShared()).membershipOrganizationId)) {
|
|
215
|
+
map.set(STPackageType.Webshops, STPackageStatus.create({
|
|
216
|
+
startDate: new Date(0),
|
|
217
|
+
}));
|
|
218
|
+
|
|
219
|
+
map.set(STPackageType.Members, STPackageStatus.create({
|
|
220
|
+
startDate: new Date(0),
|
|
221
|
+
}));
|
|
222
|
+
return map;
|
|
223
|
+
}
|
|
224
|
+
|
|
216
225
|
const packages = await this.getForOrganizationIncludingExpired(organizationId);
|
|
217
226
|
|
|
218
|
-
const map = new Map<STPackageType, STPackageStatus>();
|
|
219
227
|
for (const pack of packages) {
|
|
220
228
|
const exist = map.get(pack.meta.type);
|
|
221
229
|
if (exist) {
|
|
222
230
|
exist.merge(pack.createStatus());
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
231
|
+
} else {
|
|
225
232
|
map.set(pack.meta.type, pack.createStatus());
|
|
226
233
|
}
|
|
227
234
|
}
|
|
@@ -235,18 +242,40 @@ export class STPackageService {
|
|
|
235
242
|
|
|
236
243
|
const organization = await Organization.getByID(organizationId);
|
|
237
244
|
if (organization) {
|
|
238
|
-
const didUseMembers = organization.meta.packages.useMembers && organization.meta.packages.useActivities;
|
|
239
245
|
organization.meta.packages.packages = map;
|
|
240
246
|
await organization.save();
|
|
247
|
+
} else {
|
|
248
|
+
console.error("Couldn't find organization when updating packages " + organizationId);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
241
251
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
static async markFailedPayment(organizationId: string) {
|
|
253
|
+
console.log('Marking packages with failed payment for ' + organizationId);
|
|
254
|
+
|
|
255
|
+
// Mark all active packages as failed
|
|
256
|
+
const activePackages = await this.getActivePackages(organizationId);
|
|
257
|
+
for (const pack of activePackages) {
|
|
258
|
+
console.log('Marking package with failed payment ' + pack.id);
|
|
259
|
+
pack.meta.firstFailedPayment = pack.meta.firstFailedPayment ?? new Date();
|
|
260
|
+
pack.meta.paymentFailedCount++;
|
|
261
|
+
await pack.save();
|
|
247
262
|
}
|
|
248
|
-
|
|
249
|
-
|
|
263
|
+
await this.updateOrganizationPackages(organizationId);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
static async markBalanceRestored(organizationId: string) {
|
|
267
|
+
console.log('Marking packages as restored for ' + organizationId);
|
|
268
|
+
|
|
269
|
+
// Mark all active packages as failed
|
|
270
|
+
const activePackages = await this.getActivePackages(organizationId);
|
|
271
|
+
for (const pack of activePackages) {
|
|
272
|
+
if (pack.meta.firstFailedPayment || pack.meta.paymentFailedCount) {
|
|
273
|
+
console.log('Marking package restored ' + pack.id);
|
|
274
|
+
pack.meta.paymentFailedCount = 0;
|
|
275
|
+
pack.meta.firstFailedPayment = null;
|
|
276
|
+
await pack.save();
|
|
277
|
+
}
|
|
250
278
|
}
|
|
279
|
+
await this.updateOrganizationPackages(organizationId);
|
|
251
280
|
}
|
|
252
281
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Database } from '@simonbackx/simple-database';
|
|
2
|
+
import { logger, StyledText } from '@simonbackx/simple-logging';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
const UNIQUE_KEY_PLATFORM = 'memberNumber';
|
|
6
|
+
const UNIQUE_KEY_ORGANIZATION = 'memberNumber_organizationId';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This service is responsible for creating MySQL unique constraints on boot depending on the environment configuration.
|
|
10
|
+
* If STAMHOOFD.userMode = 'platform' then we'll create a unique constraint on the memberNumber column of the member table.
|
|
11
|
+
* If 'organization' we will not create a unique constraint on memberNumber and organizationId (we did in the past)
|
|
12
|
+
* */
|
|
13
|
+
export class UniqueMemberNumberService {
|
|
14
|
+
static async check() {
|
|
15
|
+
await logger.setContext({
|
|
16
|
+
prefixes: [
|
|
17
|
+
new StyledText(`[UniqueMemberNumberService] `).addClass('unique-member-number-service', 'tag'),
|
|
18
|
+
],
|
|
19
|
+
tags: ['unique-member-number-service'],
|
|
20
|
+
}, async () => {
|
|
21
|
+
if (STAMHOOFD.userMode === 'platform') {
|
|
22
|
+
if (await this.hasUniqueConstraint(UNIQUE_KEY_ORGANIZATION)) {
|
|
23
|
+
console.warn('Unique constraint for userMode organization exists but should be removed. Deleting it now...');
|
|
24
|
+
await this.dropConstraint(UNIQUE_KEY_ORGANIZATION);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!(await this.hasUniqueConstraint(UNIQUE_KEY_PLATFORM))) {
|
|
28
|
+
console.warn('Unique constraint for userMode platform is missing. Creating it now...');
|
|
29
|
+
await this.createConstraintForPlatform();
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
if (await this.hasUniqueConstraint(UNIQUE_KEY_PLATFORM)) {
|
|
33
|
+
console.warn('Unique constraint for userMode platform exists but should be removed. Deleting it now...');
|
|
34
|
+
await this.dropConstraint(UNIQUE_KEY_PLATFORM);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (await this.hasUniqueConstraint(UNIQUE_KEY_ORGANIZATION)) {
|
|
38
|
+
console.warn('Unique constraint for userMode organization exists but should be removed. Deleting it now...');
|
|
39
|
+
await this.dropConstraint(UNIQUE_KEY_ORGANIZATION);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static async hasUniqueConstraint(key: string) {
|
|
46
|
+
const [results] = await Database.select('SHOW INDEX FROM `members` WHERE Key_name = ?', [key]);
|
|
47
|
+
return results.length > 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private static async dropConstraint(key: string) {
|
|
51
|
+
try {
|
|
52
|
+
await Database.statement('ALTER TABLE `members` DROP INDEX `' + key + '`;');
|
|
53
|
+
console.log('Unique constraint dropped.');
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error(chalk.red(`Failed to drop unique constraint "${key}" of members table:`));
|
|
56
|
+
console.error(e);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static async createConstraintForPlatform() {
|
|
61
|
+
try {
|
|
62
|
+
await Database.statement('ALTER TABLE `members` ADD UNIQUE INDEX `' + UNIQUE_KEY_PLATFORM + '` (`memberNumber`) USING BTREE;');
|
|
63
|
+
console.log('Unique constraint created.');
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error(chalk.red('Failed to create unique constraint on memberNumber column of members table:'));
|
|
66
|
+
console.error(e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -27,8 +27,7 @@ export class UniqueUserService {
|
|
|
27
27
|
console.warn('Unique constraint is missing. Creating it now...');
|
|
28
28
|
await this.createConstraint();
|
|
29
29
|
}
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
30
|
+
} else {
|
|
32
31
|
if (await this.hasUniqueConstraint()) {
|
|
33
32
|
console.warn('Unique constraint exists but should be removed. Deleting it now...');
|
|
34
33
|
await this.dropConstraint();
|
|
@@ -41,8 +40,7 @@ export class UniqueUserService {
|
|
|
41
40
|
try {
|
|
42
41
|
await Database.statement('ALTER TABLE `users` ADD UNIQUE INDEX `' + UNIQUE_KEY_NAME + '` (`email`) USING BTREE;');
|
|
43
42
|
console.log('Unique constraint created.');
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
43
|
+
} catch (e) {
|
|
46
44
|
console.error(chalk.red('Failed to create unique constraint on email column of users table:'));
|
|
47
45
|
console.error(e);
|
|
48
46
|
}
|
|
@@ -52,8 +50,7 @@ export class UniqueUserService {
|
|
|
52
50
|
try {
|
|
53
51
|
await Database.statement('ALTER TABLE `users` DROP INDEX `' + UNIQUE_KEY_NAME + '`;');
|
|
54
52
|
console.log('Unique constraint dropped.');
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
53
|
+
} catch (e) {
|
|
57
54
|
console.error(chalk.red('Failed to drop unique constraint on email column of users table:'));
|
|
58
55
|
console.error(e);
|
|
59
56
|
}
|