@stamhoofd/backend 2.120.6 → 2.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output-2.txt +1 -0
- package/output.txt +1 -0
- package/package.json +70 -53
- package/src/audit-logs/EventNotificationLogger.ts +40 -0
- package/src/audit-logs/ModelLogger.ts +19 -15
- package/src/audit-logs/OrganizationLogger.ts +1 -1
- package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
- package/src/audit-logs/init.ts +9 -1
- package/src/boot.ts +19 -18
- package/src/crons/amazon-ses.ts +13 -26
- package/src/crons/balance-emails.ts +3 -5
- package/src/crons/clearExcelCache.test.ts +1 -1
- package/src/crons/clearExcelCache.ts +1 -2
- package/src/crons/drip-emails.ts +42 -0
- package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
- package/src/crons/helpers/isOutside.ts +11 -0
- package/src/crons/helpers/useSavedIterator.ts +32 -0
- package/src/crons/index.ts +7 -0
- package/src/crons/invoices.ts +160 -0
- package/src/crons/members-fees.ts +50 -0
- package/src/crons/mollie-chargebacks.ts +86 -0
- package/src/crons/service-fees.ts +152 -0
- package/src/crons/stripe-invoices.ts +48 -0
- package/src/crons/transfer-fees.ts +176 -0
- package/src/crons/update-cached-balances.ts +4 -4
- package/src/crons.ts +54 -74
- package/src/debug.ts +4 -2
- package/src/email-recipient-loaders/documents.ts +1 -1
- package/src/email-recipient-loaders/members.ts +1 -1
- package/src/email-recipient-loaders/payments.ts +87 -46
- package/src/email-recipient-loaders/registrations.ts +1 -1
- package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
- package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
- package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
- package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/SignupEndpoint.ts +2 -3
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
- package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
- package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
- package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
- package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
- package/src/endpoints/global/files/GetFileCache.ts +3 -4
- package/src/endpoints/global/files/UploadFile.ts +3 -4
- package/src/endpoints/global/files/UploadImage.ts +3 -4
- package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
- package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -13
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
- package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +38 -33
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
- package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
- package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +19 -6
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
- package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
- package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
- package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +12 -4
- package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +311 -0
- package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +15 -4
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -5
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +67 -20
- package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
- package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +24 -17
- package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +32 -22
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +29 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +15 -13
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +28 -50
- package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
- package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
- package/src/endpoints/system/HealthEndpoint.ts +1 -1
- package/src/excel-loaders/balance-items.ts +1 -1
- package/src/excel-loaders/event-notifications.ts +1 -1
- package/src/excel-loaders/members.ts +1 -1
- package/src/excel-loaders/organizations.ts +3 -3
- package/src/excel-loaders/payments.ts +31 -1
- package/src/excel-loaders/platform-memberships.ts +13 -14
- package/src/excel-loaders/receivable-balances.ts +2 -2
- package/src/excel-loaders/registrations.ts +2 -3
- package/src/helpers/AddressValidator.ts +7 -14
- package/src/helpers/AdminPermissionChecker.ts +60 -65
- package/src/helpers/AuthenticatedStructures.ts +153 -51
- package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
- package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
- package/src/helpers/BuckarooHelper.ts +2 -4
- package/src/helpers/CheckSettlements.ts +12 -24
- package/src/helpers/Context.ts +15 -10
- package/src/helpers/CookieHelper.ts +1 -2
- package/src/helpers/EmailResumer.ts +1 -2
- package/src/helpers/FileCache.ts +2 -3
- package/src/helpers/FinancialSupportHelper.ts +21 -0
- package/src/helpers/FlagMomentCleanup.ts +1 -2
- package/src/helpers/ForwardHandler.ts +5 -10
- package/src/helpers/GlobalHelper.ts +3 -3
- package/src/helpers/LegacyBillingConverter.ts +787 -0
- package/src/helpers/MemberMerger.test.ts +782 -0
- package/src/helpers/MemberMerger.ts +600 -0
- package/src/helpers/MemberUserSyncer.ts +6 -12
- package/src/helpers/MembershipCharger.ts +2 -4
- package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
- package/src/helpers/PaymentCustomerResolver.ts +144 -0
- package/src/helpers/PeriodHelper.ts +2 -3
- package/src/helpers/RecordAnswerHelper.test.ts +741 -0
- package/src/helpers/RecordAnswerHelper.ts +116 -0
- package/src/helpers/SeedTools.ts +106 -0
- package/src/helpers/ServiceFeeHelper.ts +125 -15
- package/src/helpers/SetupStepUpdater.ts +38 -32
- package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
- package/src/helpers/StripeHelper.ts +15 -38
- package/src/helpers/StripeInvoicer.ts +419 -0
- package/src/helpers/StripePayoutChecker.ts +5 -10
- package/src/helpers/TagHelper.ts +7 -8
- package/src/helpers/TemporaryMemberAccess.ts +1 -2
- package/src/helpers/ThrottledQueue.ts +1 -2
- package/src/helpers/UitpasTokenRepository.ts +1 -2
- package/src/helpers/ViesHelper.ts +11 -11
- package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
- package/src/helpers/outstandingBalanceJoin.ts +2 -2
- package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
- package/src/migrate.ts +12 -5
- package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
- package/src/migrations/0000000006-default-organization.ts +139 -0
- package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
- package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
- package/src/migrations/1750090029-document-update-year.ts +103 -0
- package/src/migrations/1750090030-records-configuration.ts +92 -0
- package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
- package/src/migrations/1752848540-national-numbers.ts +585 -0
- package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
- package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +95 -27
- package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
- package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
- package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
- package/src/migrations/1761665607-sync-member-users.ts +40 -0
- package/src/migrations/1779121239-default-invoice-email-template.sql +3 -0
- package/src/migrations/1779121240-invoice-email-template.sql +6 -0
- package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
- package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
- package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
- package/src/migrations/1780933598-document-groups.ts +88 -0
- package/src/migrations/1781692342-balance-item-relations.ts +13 -0
- package/src/migrations/1781857866-platform-admins.ts +103 -0
- package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
- package/src/seeds/0000000001-throw.ts +5 -0
- package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
- package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
- package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
- package/src/seeds/1773754928-force-save-members.ts +41 -8
- package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
- package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
- package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
- package/src/seeds/1780915002-update-orders.ts +39 -0
- package/src/seeds/1782047708-organization-company-address.ts +44 -0
- package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
- package/src/services/AuditLogService.test.ts +51 -0
- package/src/services/AuditLogService.ts +45 -0
- package/src/services/BalanceItemService.ts +148 -55
- package/src/services/CpuService.ts +3 -6
- package/src/services/DatabaseCollationService.test.ts +1 -2
- package/src/services/DocumentService.ts +1 -2
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/FileSignService.ts +2 -4
- package/src/services/InvoicePdfService.ts +286 -0
- package/src/services/InvoiceService.ts +330 -77
- package/src/services/InvoiceXMLService.ts +424 -0
- package/src/services/MemberNumberService.ts +6 -11
- package/src/services/MemberRecordStore.ts +8 -10
- package/src/services/MollieService.ts +530 -0
- package/src/services/PaymentMandateService.ts +214 -0
- package/src/services/PaymentReallocationService.test.ts +1 -1
- package/src/services/PaymentReallocationService.ts +7 -9
- package/src/services/PaymentService.ts +820 -328
- package/src/services/PlatformMembershipService.ts +5 -9
- package/src/services/ReferralService.ts +223 -0
- package/src/services/RegistrationService.ts +125 -54
- package/src/services/SSOService.test.ts +119 -0
- package/src/services/SSOService.ts +56 -49
- package/src/services/STPackageService.ts +97 -68
- package/src/services/UniqueMemberNumberService.ts +69 -0
- package/src/services/UniqueUserService.ts +3 -6
- package/src/services/VATService.ts +85 -0
- package/src/services/data/invoice.hbs.html +686 -0
- package/src/services/uitpas/PassholderEndpoints.ts +3 -6
- package/src/services/uitpas/UitpasService.ts +6 -10
- package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
- package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
- package/src/services/uitpas/searchUitpasEvents.ts +1 -2
- package/src/sql-filters/audit-logs.ts +1 -1
- package/src/sql-filters/balance-item-payments.ts +1 -1
- package/src/sql-filters/balance-items.ts +1 -1
- package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
- package/src/sql-filters/document-templates.ts +1 -1
- package/src/sql-filters/documents.ts +1 -1
- package/src/sql-filters/email-recipients.ts +1 -1
- package/src/sql-filters/emails.ts +1 -1
- package/src/sql-filters/event-notifications.ts +1 -1
- package/src/sql-filters/events.ts +6 -1
- package/src/sql-filters/groups.ts +16 -1
- package/src/sql-filters/invoiced-balance-items.ts +1 -1
- package/src/sql-filters/invoices.ts +1 -1
- package/src/sql-filters/member-responsibility-records.ts +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/src/sql-filters/orders.ts +4 -4
- package/src/sql-filters/organization-registration-periods.ts +1 -1
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-filters/payments.ts +5 -0
- package/src/sql-filters/platform-memberships.ts +3 -3
- package/src/sql-filters/receivable-balances.ts +1 -1
- package/src/sql-filters/registration-invitations.ts +90 -0
- package/src/sql-filters/registration-periods.ts +1 -1
- package/src/sql-filters/registrations.ts +1 -1
- package/src/sql-filters/tickets.ts +1 -1
- package/src/sql-filters/users.ts +1 -1
- package/src/sql-sorters/groups.ts +23 -0
- package/src/sql-sorters/platform-memberships.ts +16 -16
- package/src/sql-sorters/registration-invitations.ts +36 -0
- package/tests/actions/patchOrganizationMember.ts +1 -1
- package/tests/actions/patchUserMember.ts +1 -1
- package/tests/assertions/assertBalances.ts +6 -12
- package/tests/e2e/api-rate-limits.test.ts +12 -24
- package/tests/e2e/bundle-discounts.test.ts +4 -1
- package/tests/e2e/charge-members.test.ts +13 -11
- package/tests/e2e/documents.test.ts +4 -1
- package/tests/e2e/register.test.ts +7 -9
- package/tests/e2e/stock.test.ts +4 -3
- package/tests/e2e/tickets.test.ts +1 -1
- package/tests/helpers/MollieMocker.ts +462 -0
- package/tests/helpers/PayconiqMocker.ts +138 -45
- package/tests/helpers/StripeMocker.ts +9 -6
- package/tests/helpers/index.ts +4 -0
- package/tests/init/initAdmin.ts +2 -2
- package/tests/init/initBundleDiscount.ts +1 -1
- package/tests/init/initMembershipOrganization.ts +13 -0
- package/tests/init/initPermissionRole.ts +3 -4
- package/tests/vitest.global.setup.ts +1 -1
- package/vitest.config.js +7 -0
- package/.nvmrc +0 -1
- package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
- package/src/helpers/GroupBuilder.ts +0 -418
- package/src/seeds/0000000000-example.ts +0 -11
- package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
- package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
- package/src/seeds/1726847064-setup-steps.ts +0 -16
- package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
- package/src/seeds/1729253172-update-orders.ts +0 -21
- package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
- package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
- package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
- package/src/seeds/1740046783-update-membership.ts +0 -17
- package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
- package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
- package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
- package/src/seeds/1750090030-records-configuration.ts +0 -26
- package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
- package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
- package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
- package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
- package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
- package/src/seeds/1756303697-update-email-counts.ts +0 -76
- package/src/seeds/1761665607-sync-member-users.ts +0 -59
- package/src/seeds/1765896674-document-update-year.ts +0 -66
- /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
- /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
- /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AuditLog } from '@stamhoofd/models';
|
|
2
|
+
import { AuditLogPatchItem, AuditLogReplacement, AuditLogReplacementType, EventNotificationStatus, getLegacyAuditLogEnumRegistrations, getLegacyAuditLogEnumReplacement } from '@stamhoofd/structures';
|
|
3
|
+
|
|
4
|
+
import { migrateAuditLogLegacyEnums } from './1780416000-migrate-audit-log-legacy-enums.js';
|
|
5
|
+
|
|
6
|
+
describe('Seed.migrate-audit-log-legacy-enums', () => {
|
|
7
|
+
test('It converts all legacy enum audit log values without changing their rendered names', () => {
|
|
8
|
+
let migratedValues = 0;
|
|
9
|
+
|
|
10
|
+
for (const registration of getLegacyAuditLogEnumRegistrations()) {
|
|
11
|
+
for (const value of Object.values(registration.enumObject)) {
|
|
12
|
+
if (typeof value !== 'string') {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!getLegacyAuditLogEnumReplacement(value)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const legacyReplacement = AuditLogReplacement.key(value);
|
|
21
|
+
const legacyName = legacyReplacement.toString();
|
|
22
|
+
const log = new AuditLog();
|
|
23
|
+
log.replacements = new Map([
|
|
24
|
+
['value', legacyReplacement.clone()],
|
|
25
|
+
]);
|
|
26
|
+
log.patchList = [
|
|
27
|
+
AuditLogPatchItem.create({
|
|
28
|
+
key: legacyReplacement.clone(),
|
|
29
|
+
oldValue: legacyReplacement.clone(),
|
|
30
|
+
value: legacyReplacement.clone(),
|
|
31
|
+
}),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
expect(migrateAuditLogLegacyEnums(log)).toBe(true);
|
|
35
|
+
|
|
36
|
+
const replacement = log.replacements.get('value');
|
|
37
|
+
expect(replacement?.type).toBe(AuditLogReplacementType.Enum);
|
|
38
|
+
expect(replacement?.toString()).toBe(legacyName);
|
|
39
|
+
|
|
40
|
+
expect(log.patchList[0].key.type).toBe(AuditLogReplacementType.Enum);
|
|
41
|
+
expect(log.patchList[0].key.toString()).toBe(legacyName);
|
|
42
|
+
expect(log.patchList[0].oldValue?.type).toBe(AuditLogReplacementType.Enum);
|
|
43
|
+
expect(log.patchList[0].oldValue?.toString()).toBe(legacyName);
|
|
44
|
+
expect(log.patchList[0].value?.type).toBe(AuditLogReplacementType.Enum);
|
|
45
|
+
expect(log.patchList[0].value?.toString()).toBe(legacyName);
|
|
46
|
+
migratedValues += 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expect(migratedValues).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('It does not migrate event notification statuses through the legacy mapping', () => {
|
|
54
|
+
expect(getLegacyAuditLogEnumRegistrations().some(({ type }) => type === 'EventNotificationStatus')).toBe(false);
|
|
55
|
+
|
|
56
|
+
const legacyReplacement = AuditLogReplacement.key(EventNotificationStatus.Accepted);
|
|
57
|
+
const log = new AuditLog();
|
|
58
|
+
log.replacements = new Map([
|
|
59
|
+
['value', legacyReplacement],
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
expect(migrateAuditLogLegacyEnums(log)).toBe(false);
|
|
63
|
+
|
|
64
|
+
expect(log.replacements.get('value')).toMatchObject({
|
|
65
|
+
value: EventNotificationStatus.Accepted,
|
|
66
|
+
type: AuditLogReplacementType.Key,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Migration } from '@simonbackx/simple-database';
|
|
2
|
+
import { AuditLog } from '@stamhoofd/models';
|
|
3
|
+
import { AuditLogReplacement, AuditLogReplacementType, getLegacyAuditLogEnumReplacement } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
export function migrateAuditLogReplacementLegacyEnums(replacement: AuditLogReplacement | undefined): { replacement: AuditLogReplacement | undefined; changed: boolean } {
|
|
6
|
+
if (!replacement) {
|
|
7
|
+
return { replacement, changed: false };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (replacement.type === AuditLogReplacementType.Key) {
|
|
11
|
+
const migrated = getLegacyAuditLogEnumReplacement(replacement.value);
|
|
12
|
+
if (migrated) {
|
|
13
|
+
return { replacement: migrated, changed: true };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (replacement.type === AuditLogReplacementType.Array) {
|
|
18
|
+
let changed = false;
|
|
19
|
+
const values = replacement.values.map((value) => {
|
|
20
|
+
const result = migrateAuditLogReplacementLegacyEnums(value);
|
|
21
|
+
changed ||= result.changed;
|
|
22
|
+
return result.replacement ?? value;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (changed) {
|
|
26
|
+
const clone = replacement.clone();
|
|
27
|
+
clone.values = values;
|
|
28
|
+
return { replacement: clone, changed: true };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { replacement, changed: false };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function migrateAuditLogLegacyEnums(log: AuditLog): boolean {
|
|
36
|
+
let changed = false;
|
|
37
|
+
|
|
38
|
+
for (const [key, replacement] of log.replacements) {
|
|
39
|
+
const result = migrateAuditLogReplacementLegacyEnums(replacement);
|
|
40
|
+
if (result.changed && result.replacement) {
|
|
41
|
+
log.replacements.set(key, result.replacement);
|
|
42
|
+
changed = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const item of log.patchList) {
|
|
47
|
+
const key = migrateAuditLogReplacementLegacyEnums(item.key);
|
|
48
|
+
if (key.changed && key.replacement) {
|
|
49
|
+
item.key = key.replacement;
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const oldValue = migrateAuditLogReplacementLegacyEnums(item.oldValue);
|
|
54
|
+
if (oldValue.changed) {
|
|
55
|
+
item.oldValue = oldValue.replacement;
|
|
56
|
+
changed = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const value = migrateAuditLogReplacementLegacyEnums(item.value);
|
|
60
|
+
if (value.changed) {
|
|
61
|
+
item.value = value.replacement;
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return changed;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default new Migration(async () => {
|
|
70
|
+
console.log('Start migrating legacy audit log enum replacements.');
|
|
71
|
+
|
|
72
|
+
const batchSize = 100;
|
|
73
|
+
let count = 0;
|
|
74
|
+
|
|
75
|
+
for await (const log of AuditLog.select().limit(batchSize).all()) {
|
|
76
|
+
if (!migrateAuditLogLegacyEnums(log)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await log.save();
|
|
81
|
+
count += 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('Finished migrating legacy audit log enum replacements for ' + count + ' audit logs.');
|
|
85
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { column, Model } from '@simonbackx/simple-database';
|
|
2
|
+
import { EnumDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { PaymentStatus } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { AuditLogService, modelLogDefinitions } from './AuditLogService.js';
|
|
6
|
+
|
|
7
|
+
enum UnregisteredAuditLogStatus {
|
|
8
|
+
Pending = 'Pending',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class UnregisteredAuditLogModel extends Model {
|
|
12
|
+
static table = 'unregistered_audit_log_models';
|
|
13
|
+
|
|
14
|
+
@column({ type: 'string', decoder: new EnumDecoder(UnregisteredAuditLogStatus) })
|
|
15
|
+
status = UnregisteredAuditLogStatus.Pending;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class RegisteredAuditLogModel extends Model {
|
|
19
|
+
static table = 'registered_audit_log_models';
|
|
20
|
+
|
|
21
|
+
@column({ type: 'string', decoder: new EnumDecoder(PaymentStatus) })
|
|
22
|
+
status = PaymentStatus.Pending;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('AuditLogService', () => {
|
|
26
|
+
const originalModelLogDefinitions = new Map(modelLogDefinitions);
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
modelLogDefinitions.clear();
|
|
30
|
+
for (const [model, definition] of originalModelLogDefinitions) {
|
|
31
|
+
modelLogDefinitions.set(model, definition);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('It throws when an audited enum column has no registered translation helper', () => {
|
|
36
|
+
modelLogDefinitions.clear();
|
|
37
|
+
modelLogDefinitions.set(UnregisteredAuditLogModel, {
|
|
38
|
+
async logEvent() {
|
|
39
|
+
// Not needed for validation.
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
modelLogDefinitions.set(RegisteredAuditLogModel, {
|
|
43
|
+
async logEvent() {
|
|
44
|
+
// Not needed for validation.
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(() => AuditLogService.validateAuditLogEnumTranslations())
|
|
49
|
+
.toThrow('Missing audit log enum translation registration for: UnregisteredAuditLogModel.status');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ModelEvent } from '@simonbackx/simple-database';
|
|
2
2
|
import { Model } from '@simonbackx/simple-database';
|
|
3
|
+
import { ArrayDecoder, EnumDecoder, MapDecoder, SymbolDecoder } from '@simonbackx/simple-encoding';
|
|
4
|
+
import { getAuditLogEnumDecoderObject, getAuditLogEnumType } from '@stamhoofd/structures';
|
|
3
5
|
import type { AuditLogSource } from '@stamhoofd/structures';
|
|
4
6
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
7
|
|
|
@@ -15,6 +17,26 @@ export type AuditLogContextSettings = {
|
|
|
15
17
|
};
|
|
16
18
|
export const modelLogDefinitions = new Map<typeof Model, { logEvent: (event: ModelEvent) => Promise<any> }>();
|
|
17
19
|
|
|
20
|
+
function getEnumDecoders(decoder: unknown): EnumDecoder<any>[] {
|
|
21
|
+
if (decoder instanceof EnumDecoder) {
|
|
22
|
+
return [decoder];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (decoder instanceof SymbolDecoder) {
|
|
26
|
+
return getEnumDecoders(decoder.decoder);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (decoder instanceof ArrayDecoder) {
|
|
30
|
+
return getEnumDecoders(decoder.decoder);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (decoder instanceof MapDecoder) {
|
|
34
|
+
return [...getEnumDecoders(decoder.keyDecoder), ...getEnumDecoders(decoder.valueDecoder)];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
export class AuditLogService {
|
|
19
41
|
private constructor() { }
|
|
20
42
|
static disableLocalStore = new AsyncLocalStorage<AuditLogContextSettings>();
|
|
@@ -47,6 +69,29 @@ export class AuditLogService {
|
|
|
47
69
|
|
|
48
70
|
static listening = false;
|
|
49
71
|
|
|
72
|
+
static validateAuditLogEnumTranslations() {
|
|
73
|
+
const missing: string[] = [];
|
|
74
|
+
|
|
75
|
+
for (const [model] of modelLogDefinitions) {
|
|
76
|
+
const columns = model.columns;
|
|
77
|
+
if (!columns) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const [key, column] of columns) {
|
|
82
|
+
for (const enumDecoder of getEnumDecoders(column.decoder)) {
|
|
83
|
+
if (!getAuditLogEnumType(getAuditLogEnumDecoderObject(enumDecoder))) {
|
|
84
|
+
missing.push(`${model.name}.${key}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (missing.length > 0) {
|
|
91
|
+
throw new Error(`Missing audit log enum translation registration for: ${missing.join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
50
95
|
static listen() {
|
|
51
96
|
if (this.listening) {
|
|
52
97
|
return;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Model } from '@simonbackx/simple-database';
|
|
2
|
-
import type { Organization
|
|
3
|
-
import { BalanceItem, CachedBalance, Document, MemberUser, Order, Webshop } from '@stamhoofd/models';
|
|
4
|
-
import {
|
|
2
|
+
import type { Organization } from '@stamhoofd/models';
|
|
3
|
+
import { BalanceItem, CachedBalance, Document, MemberUser, Order, Payment, Platform, Webshop } from '@stamhoofd/models';
|
|
4
|
+
import { SQL } from '@stamhoofd/sql';
|
|
5
|
+
import { AuditLogSource, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentStatus, PaymentType, ReceivableBalanceType } from '@stamhoofd/structures';
|
|
6
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
5
7
|
import { GroupedThrottledQueue } from '../helpers/GroupedThrottledQueue.js';
|
|
6
8
|
import { ThrottledQueue } from '../helpers/ThrottledQueue.js';
|
|
7
9
|
import { AuditLogService } from './AuditLogService.js';
|
|
@@ -32,11 +34,21 @@ const userUpdateQueue = new GroupedThrottledQueue(async (organizationId: string,
|
|
|
32
34
|
}, { maxDelay: 10_000 });
|
|
33
35
|
|
|
34
36
|
const organizationUpdateQueue = new GroupedThrottledQueue(async (organizationId: string, organizationIds: string[]) => {
|
|
35
|
-
await CachedBalance.updateForOrganizations(organizationId, organizationIds);
|
|
37
|
+
const results = await CachedBalance.updateForOrganizations(organizationId, organizationIds);
|
|
36
38
|
|
|
37
39
|
for (const payingOrganizationId of organizationIds) {
|
|
38
40
|
await PaymentReallocationService.reallocate(organizationId, payingOrganizationId, ReceivableBalanceType.organization);
|
|
39
41
|
}
|
|
42
|
+
|
|
43
|
+
const platform = await Platform.getShared();
|
|
44
|
+
if (organizationId === platform.membershipOrganizationId) {
|
|
45
|
+
// Clear failed payments
|
|
46
|
+
for (const [organizationId, result] of results) {
|
|
47
|
+
if (result.amountOpen === 0 && result.amountPending === 0) {
|
|
48
|
+
await STPackageService.markBalanceRestored(organizationId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
40
52
|
}, { maxDelay: 60_000 });
|
|
41
53
|
|
|
42
54
|
export const registrationUpdateQueue = new GroupedThrottledQueue(async (organizationId: string, registrationIds: string[]) => {
|
|
@@ -50,10 +62,10 @@ const bundleDiscountsUpdateQueue = new ThrottledQueue(async (registrationIds: st
|
|
|
50
62
|
}
|
|
51
63
|
}, { maxDelay: 10_000 });
|
|
52
64
|
|
|
53
|
-
export
|
|
54
|
-
listening
|
|
65
|
+
export class BalanceItemService {
|
|
66
|
+
static listening = false;
|
|
55
67
|
|
|
56
|
-
listen() {
|
|
68
|
+
static listen() {
|
|
57
69
|
if (this.listening) {
|
|
58
70
|
return;
|
|
59
71
|
}
|
|
@@ -64,7 +76,7 @@ export const BalanceItemService = {
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
if (event.type === 'created' || event.type === 'deleted') {
|
|
67
|
-
|
|
79
|
+
this.scheduleUpdate(event.model);
|
|
68
80
|
return;
|
|
69
81
|
}
|
|
70
82
|
|
|
@@ -79,10 +91,10 @@ export const BalanceItemService = {
|
|
|
79
91
|
|| 'payingOrganizationId' in event.changedFields
|
|
80
92
|
|| 'registrationId' in event.changedFields
|
|
81
93
|
) {
|
|
82
|
-
|
|
94
|
+
this.scheduleUpdate(event.model);
|
|
83
95
|
}
|
|
84
96
|
});
|
|
85
|
-
}
|
|
97
|
+
}
|
|
86
98
|
|
|
87
99
|
/**
|
|
88
100
|
* Schedule an update for the balance item:
|
|
@@ -90,8 +102,8 @@ export const BalanceItemService = {
|
|
|
90
102
|
*
|
|
91
103
|
* Does not execute the update immediately, but schedules it to be run in the background.
|
|
92
104
|
*/
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
static scheduleUpdate(item: BalanceItem) {
|
|
106
|
+
this.scheduleUpdates([item]);
|
|
95
107
|
|
|
96
108
|
// Todo: optimize
|
|
97
109
|
if (item.type === BalanceItemType.RegistrationBundleDiscount) {
|
|
@@ -100,49 +112,58 @@ export const BalanceItemService = {
|
|
|
100
112
|
bundleDiscountsUpdateQueue.addItem(item.registrationId);
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
|
-
}
|
|
115
|
+
}
|
|
104
116
|
|
|
105
117
|
/**
|
|
106
118
|
* Call this when a payment or payment balance items have changed.
|
|
107
119
|
* It will also call updateOutstanding automatically, so no need to call that separately again
|
|
108
120
|
*/
|
|
109
|
-
async updatePaidAndPending(items: BalanceItem[]) {
|
|
121
|
+
static async updatePaidAndPending(items: BalanceItem[]) {
|
|
110
122
|
console.log('updatePaidAndPending for', items.length, 'items');
|
|
111
|
-
await BalanceItem.updatePricePaid(items.map(i => i.id));
|
|
112
|
-
|
|
113
|
-
}
|
|
123
|
+
await BalanceItem.updatePricePaid(Formatter.uniqueArray(items.map(i => i.id)));
|
|
124
|
+
this.scheduleUpdates(items);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Call this when a payment or payment balance items have changed.
|
|
129
|
+
* It will also call updateOutstanding automatically, so no need to call that separately again
|
|
130
|
+
*/
|
|
131
|
+
static async updateInvoiced(items: (BalanceItem | string)[]) {
|
|
132
|
+
console.log('updateInvoiced for', items.length, 'items');
|
|
133
|
+
await BalanceItem.updateInvoiced(Formatter.uniqueArray(items.map(i => typeof i === 'string' ? i : i.id)));
|
|
134
|
+
}
|
|
114
135
|
|
|
115
136
|
/**
|
|
116
137
|
* In some situations we need immediate updates
|
|
117
138
|
*/
|
|
118
|
-
async flushRegistrationDiscountsCache() {
|
|
139
|
+
static async flushRegistrationDiscountsCache() {
|
|
119
140
|
await bundleDiscountsUpdateQueue.flushAndWait();
|
|
120
|
-
}
|
|
141
|
+
}
|
|
121
142
|
|
|
122
143
|
/**
|
|
123
144
|
* Make sure all the pending changes for cached balances are run
|
|
124
145
|
*/
|
|
125
|
-
async flushCaches(organizationId: string) {
|
|
146
|
+
static async flushCaches(organizationId: string) {
|
|
126
147
|
await memberUpdateQueue.flushGroupAndWait(organizationId);
|
|
127
148
|
await userUpdateQueue.flushGroupAndWait(organizationId);
|
|
128
149
|
await organizationUpdateQueue.flushGroupAndWait(organizationId);
|
|
129
150
|
await registrationUpdateQueue.flushGroupAndWait(organizationId);
|
|
130
151
|
await bundleDiscountsUpdateQueue.flushAndWait();
|
|
131
|
-
}
|
|
152
|
+
}
|
|
132
153
|
|
|
133
|
-
async flushAll() {
|
|
154
|
+
static async flushAll() {
|
|
134
155
|
await memberUpdateQueue.flushAndWait();
|
|
135
156
|
await userUpdateQueue.flushAndWait();
|
|
136
157
|
await organizationUpdateQueue.flushAndWait();
|
|
137
158
|
await registrationUpdateQueue.flushAndWait();
|
|
138
159
|
await bundleDiscountsUpdateQueue.flushAndWait();
|
|
139
|
-
}
|
|
160
|
+
}
|
|
140
161
|
|
|
141
162
|
/**
|
|
142
163
|
* Update how many every object in the system owes or needs to be reimbursed
|
|
143
164
|
* and also updates the pricePaid/pricePending cached values in Balance items and members
|
|
144
165
|
*/
|
|
145
|
-
|
|
166
|
+
static scheduleUpdates(items: BalanceItem[]) {
|
|
146
167
|
console.log('Schedule outstanding balance for', items.length, 'items');
|
|
147
168
|
|
|
148
169
|
for (const item of items) {
|
|
@@ -162,21 +183,21 @@ export const BalanceItemService = {
|
|
|
162
183
|
registrationUpdateQueue.addItem(item.organizationId, item.registrationId);
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
|
-
}
|
|
186
|
+
}
|
|
166
187
|
|
|
167
|
-
scheduleUserUpdate(organizationId: string, userId: string) {
|
|
188
|
+
static scheduleUserUpdate(organizationId: string, userId: string) {
|
|
168
189
|
userUpdateQueue.addItem(organizationId, userId);
|
|
169
|
-
}
|
|
190
|
+
}
|
|
170
191
|
|
|
171
|
-
|
|
192
|
+
static cheduleMemberUpdate(organizationId: string, memberId: string) {
|
|
172
193
|
memberUpdateQueue.addItem(organizationId, memberId);
|
|
173
|
-
}
|
|
194
|
+
}
|
|
174
195
|
|
|
175
|
-
scheduleOrganizationUpdate(organizationId: string, payingOrganizationId: string) {
|
|
196
|
+
static scheduleOrganizationUpdate(organizationId: string, payingOrganizationId: string) {
|
|
176
197
|
organizationUpdateQueue.addItem(organizationId, payingOrganizationId);
|
|
177
|
-
}
|
|
198
|
+
}
|
|
178
199
|
|
|
179
|
-
async markDue(balanceItem: BalanceItem) {
|
|
200
|
+
static async markDue(balanceItem: BalanceItem) {
|
|
180
201
|
if (balanceItem.status === BalanceItemStatus.Hidden) {
|
|
181
202
|
balanceItem.status = BalanceItemStatus.Due;
|
|
182
203
|
await balanceItem.save();
|
|
@@ -190,9 +211,21 @@ export const BalanceItemService = {
|
|
|
190
211
|
await balanceItem.save();
|
|
191
212
|
}
|
|
192
213
|
}
|
|
193
|
-
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static async markPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
217
|
+
if (payment && payment.type === PaymentType.Chargeback) {
|
|
218
|
+
const realPayment = payment.reversingPaymentId ? (await Payment.getByID(payment.reversingPaymentId) ?? null) : null;
|
|
219
|
+
await this.handleChargeback(balanceItem, payment, realPayment, organization);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (payment && payment.price !== 0 && payment.refundedAmount === payment.price) {
|
|
224
|
+
// Charged back, already handled
|
|
225
|
+
// First cancel the chargeback if the payment should resucceed again
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
194
228
|
|
|
195
|
-
async markPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
196
229
|
await this.markDue(balanceItem);
|
|
197
230
|
let shouldMarkUpdated = true;
|
|
198
231
|
|
|
@@ -250,9 +283,9 @@ export const BalanceItemService = {
|
|
|
250
283
|
|
|
251
284
|
balanceItem.paidAt = new Date();
|
|
252
285
|
await balanceItem.save();
|
|
253
|
-
}
|
|
286
|
+
}
|
|
254
287
|
|
|
255
|
-
async markUpdated(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
288
|
+
static async markUpdated(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
256
289
|
// For orders: mark order as changed (so they are refetched in front ends)
|
|
257
290
|
if (balanceItem.orderId) {
|
|
258
291
|
await AuditLogService.setContext({ source: AuditLogSource.Payment }, async () => {
|
|
@@ -264,9 +297,9 @@ export const BalanceItemService = {
|
|
|
264
297
|
}
|
|
265
298
|
});
|
|
266
299
|
}
|
|
267
|
-
}
|
|
300
|
+
}
|
|
268
301
|
|
|
269
|
-
async undoPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
302
|
+
static async undoPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
270
303
|
// If order
|
|
271
304
|
if (balanceItem.orderId) {
|
|
272
305
|
const order = await Order.getByID(balanceItem.orderId);
|
|
@@ -275,16 +308,24 @@ export const BalanceItemService = {
|
|
|
275
308
|
await order.undoPaid(payment, organization);
|
|
276
309
|
}
|
|
277
310
|
}
|
|
311
|
+
}
|
|
278
312
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
313
|
+
/**
|
|
314
|
+
*
|
|
315
|
+
*/
|
|
316
|
+
static async handleChargeback(balanceItem: BalanceItem, chargeback: Payment | null, payment: Payment | null, organization: Organization) {
|
|
317
|
+
if (balanceItem.failedAt) {
|
|
318
|
+
// Already ran side effects
|
|
319
|
+
return;
|
|
284
320
|
}
|
|
285
|
-
},
|
|
286
321
|
|
|
287
|
-
|
|
322
|
+
// todo
|
|
323
|
+
|
|
324
|
+
balanceItem.failedAt = new Date();
|
|
325
|
+
await balanceItem.save();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static async markFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
288
329
|
// If order
|
|
289
330
|
if (balanceItem.orderId) {
|
|
290
331
|
const order = await Order.getByID(balanceItem.orderId);
|
|
@@ -297,16 +338,10 @@ export const BalanceItemService = {
|
|
|
297
338
|
}
|
|
298
339
|
}
|
|
299
340
|
}
|
|
341
|
+
await this.handleChargeback(balanceItem, null, payment, organization);
|
|
342
|
+
}
|
|
300
343
|
|
|
301
|
-
|
|
302
|
-
if (this.type === BalanceItemType.Rounding && balanceItem.status !== BalanceItemStatus.Hidden) {
|
|
303
|
-
// Mark undue
|
|
304
|
-
balanceItem.status = BalanceItemStatus.Hidden;
|
|
305
|
-
await balanceItem.save();
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
async undoFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
344
|
+
static async undoFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
310
345
|
// If order
|
|
311
346
|
if (balanceItem.orderId) {
|
|
312
347
|
const order = await Order.getByID(balanceItem.orderId);
|
|
@@ -314,5 +349,63 @@ export const BalanceItemService = {
|
|
|
314
349
|
await order.undoPaymentFailed(payment, organization);
|
|
315
350
|
}
|
|
316
351
|
}
|
|
317
|
-
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Apply discounts to the balance a user is going to checkout
|
|
356
|
+
*/
|
|
357
|
+
static async applyDiscountsToCheckout({ minimumAmount, totalPrice, balanceItems, sellingOrganizationId, payingOrganizationId, payingUserId }: {
|
|
358
|
+
balanceItems: Map<BalanceItem, number>;
|
|
359
|
+
totalPrice: number;
|
|
360
|
+
minimumAmount?: number;
|
|
361
|
+
sellingOrganizationId: string;
|
|
362
|
+
payingOrganizationId?: string | null | undefined;
|
|
363
|
+
payingUserId?: string | null | undefined;
|
|
364
|
+
}) {
|
|
365
|
+
if (totalPrice <= (minimumAmount ?? 0)) {
|
|
366
|
+
return {
|
|
367
|
+
totalPrice,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
let items: BalanceItem[];
|
|
372
|
+
if (payingOrganizationId) {
|
|
373
|
+
items = await BalanceItem.select()
|
|
374
|
+
.where('organizationId', sellingOrganizationId)
|
|
375
|
+
.where('payingOrganizationId', payingOrganizationId)
|
|
376
|
+
.whereNot('status', BalanceItemStatus.Hidden)
|
|
377
|
+
.where('priceOpen', '<', 0)
|
|
378
|
+
.fetch();
|
|
379
|
+
} else if (payingUserId) {
|
|
380
|
+
const memberUsers = await MemberUser.select().where('usersId', payingUserId).fetch();
|
|
381
|
+
const memberIds = Formatter.uniqueArray(memberUsers.map(mu => mu.membersId));
|
|
382
|
+
|
|
383
|
+
items = await BalanceItem.select()
|
|
384
|
+
.where('organizationId', sellingOrganizationId)
|
|
385
|
+
.where(
|
|
386
|
+
SQL.where('userId', payingUserId)
|
|
387
|
+
.or('memberId', memberIds),
|
|
388
|
+
)
|
|
389
|
+
.whereNot('status', BalanceItemStatus.Hidden)
|
|
390
|
+
.where('priceOpen', '<', 0)
|
|
391
|
+
.fetch();
|
|
392
|
+
} else {
|
|
393
|
+
return {
|
|
394
|
+
totalPrice,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
for (const item of items) {
|
|
399
|
+
const remove = Math.min(totalPrice - (minimumAmount ?? 0), -item.priceOpen);
|
|
400
|
+
if (remove === 0) {
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
balanceItems.set(item, -remove);
|
|
404
|
+
totalPrice -= remove;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
totalPrice,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
318
411
|
};
|
|
@@ -56,8 +56,7 @@ class StaticCpuService {
|
|
|
56
56
|
}
|
|
57
57
|
if (size === this.maxSamples) {
|
|
58
58
|
return this.samples.reduce((a, b) => a + b, 0) / size;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
59
|
+
} else {
|
|
61
60
|
// To read before current index
|
|
62
61
|
const toReadBeforeIndex = Math.min(size, this.currentIndex);
|
|
63
62
|
const toReadFromEnd = size - toReadBeforeIndex;
|
|
@@ -89,13 +88,11 @@ class StaticCpuService {
|
|
|
89
88
|
if (five > 80) {
|
|
90
89
|
// Danger zone, in this case we don't want to log all slow queries any longer because the information won't be trustworthy.
|
|
91
90
|
SQLLogger.slowQueryThresholdMs = null;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
91
|
+
} else {
|
|
94
92
|
if (five < 20) {
|
|
95
93
|
// No load, safe to log all slow queries
|
|
96
94
|
SQLLogger.slowQueryThresholdMs = 300;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
95
|
+
} else {
|
|
99
96
|
SQLLogger.slowQueryThresholdMs = 500;
|
|
100
97
|
}
|
|
101
98
|
}
|
|
@@ -10,8 +10,7 @@ describe('DatabaseCollationService', () => {
|
|
|
10
10
|
expect(error).toContain('MySQL collation mismatch');
|
|
11
11
|
expect(error).toContain('expected utf8mb4_0900_ai_ci');
|
|
12
12
|
expect(error).toContain('collation_connection=utf8mb4_general_ci');
|
|
13
|
-
}
|
|
14
|
-
finally {
|
|
13
|
+
} finally {
|
|
15
14
|
await Database.statement('SET collation_connection = DEFAULT');
|
|
16
15
|
}
|
|
17
16
|
});
|
|
@@ -33,8 +33,7 @@ export class DocumentService {
|
|
|
33
33
|
// If a group is changed - update the documents for it
|
|
34
34
|
await Document.updateForGroup(event.model.id, event.model.organizationId);
|
|
35
35
|
}
|
|
36
|
-
}
|
|
37
|
-
catch (e) {
|
|
36
|
+
} catch (e) {
|
|
38
37
|
console.error('Failed to update documents after group change', e);
|
|
39
38
|
}
|
|
40
39
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
2
2
|
import { EventNotification, Member, MemberResponsibilityRecord, Organization, Platform, sendEmailTemplate, User } from '@stamhoofd/models';
|
|
3
|
-
import type { EmailTemplateType} from '@stamhoofd/structures';
|
|
3
|
+
import type { EmailTemplateType } from '@stamhoofd/structures';
|
|
4
4
|
import { PermissionLevel, Recipient, RecordCategory, Replacement } from '@stamhoofd/structures';
|
|
5
5
|
import { Formatter } from '@stamhoofd/utility';
|
|
6
6
|
import { AdminPermissionChecker } from '../helpers/AdminPermissionChecker.js';
|