@stamhoofd/backend 2.121.0 → 2.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output-2.txt +1 -0
- package/output.txt +1 -0
- package/package.json +69 -52
- package/src/audit-logs/EventNotificationLogger.ts +40 -0
- package/src/audit-logs/ModelLogger.ts +19 -15
- package/src/audit-logs/OrganizationLogger.ts +1 -1
- package/src/audit-logs/RegistrationInvitationLogger.ts +24 -24
- package/src/audit-logs/init.ts +8 -2
- package/src/boot.ts +19 -18
- package/src/crons/amazon-ses.ts +13 -26
- package/src/crons/balance-emails.ts +3 -5
- package/src/crons/clearExcelCache.test.ts +1 -1
- package/src/crons/clearExcelCache.ts +1 -2
- package/src/crons/drip-emails.ts +42 -0
- package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
- package/src/crons/helpers/isOutside.ts +11 -0
- package/src/crons/helpers/useSavedIterator.ts +32 -0
- package/src/crons/index.ts +5 -0
- package/src/crons/invoices.ts +32 -38
- package/src/crons/members-fees.ts +50 -0
- package/src/crons/mollie-chargebacks.ts +14 -15
- package/src/crons/service-fees.ts +152 -0
- package/src/crons/stripe-invoices.ts +48 -0
- package/src/crons/transfer-fees.ts +176 -0
- package/src/crons/update-cached-balances.ts +4 -4
- package/src/crons.ts +7 -64
- package/src/debug.ts +4 -2
- package/src/email-recipient-loaders/documents.ts +1 -1
- package/src/email-recipient-loaders/members.ts +1 -1
- package/src/email-recipient-loaders/payments.ts +4 -6
- package/src/email-recipient-loaders/registrations.ts +1 -1
- package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
- package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
- package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
- package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
- package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
- package/src/endpoints/auth/SignupEndpoint.ts +2 -3
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
- package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
- package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
- package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
- package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
- package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
- package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
- package/src/endpoints/global/files/GetFileCache.ts +3 -4
- package/src/endpoints/global/files/UploadFile.ts +3 -4
- package/src/endpoints/global/files/UploadImage.ts +3 -4
- package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +1 -1
- package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
- package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +11 -11
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
- package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
- package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
- package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +35 -34
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +1 -1
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +62 -63
- package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +6 -9
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +51 -52
- package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +6 -7
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
- package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
- package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
- package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +7 -9
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
- package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +1 -5
- package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +11 -11
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
- package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +60 -57
- package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +20 -20
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +10 -6
- package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +5 -5
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +50 -12
- package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
- package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +21 -14
- package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +35 -28
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +19 -18
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +22 -7
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +20 -27
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +2 -2
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +16 -33
- package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
- package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
- package/src/endpoints/system/HealthEndpoint.ts +1 -1
- package/src/excel-loaders/balance-items.ts +1 -1
- package/src/excel-loaders/event-notifications.ts +1 -1
- package/src/excel-loaders/members.ts +1 -1
- package/src/excel-loaders/organizations.ts +3 -3
- package/src/excel-loaders/payments.ts +31 -1
- package/src/excel-loaders/platform-memberships.ts +13 -14
- package/src/excel-loaders/receivable-balances.ts +2 -2
- package/src/excel-loaders/registrations.ts +2 -3
- package/src/helpers/AddressValidator.ts +7 -14
- package/src/helpers/AdminPermissionChecker.ts +50 -63
- package/src/helpers/AuthenticatedStructures.ts +69 -55
- package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
- package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
- package/src/helpers/BuckarooHelper.ts +2 -4
- package/src/helpers/CheckSettlements.ts +12 -24
- package/src/helpers/Context.ts +15 -10
- package/src/helpers/CookieHelper.ts +1 -2
- package/src/helpers/EmailResumer.ts +1 -2
- package/src/helpers/FileCache.ts +2 -3
- package/src/helpers/FinancialSupportHelper.ts +2 -2
- package/src/helpers/FlagMomentCleanup.ts +1 -2
- package/src/helpers/ForwardHandler.ts +5 -10
- package/src/helpers/GlobalHelper.ts +3 -3
- package/src/helpers/LegacyBillingConverter.ts +787 -0
- package/src/helpers/MemberMerger.test.ts +782 -0
- package/src/helpers/MemberMerger.ts +600 -0
- package/src/helpers/MemberUserSyncer.ts +6 -12
- package/src/helpers/MembershipCharger.ts +2 -4
- package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
- package/src/helpers/PaymentCustomerResolver.ts +144 -0
- package/src/helpers/PeriodHelper.ts +2 -3
- package/src/helpers/RecordAnswerHelper.test.ts +105 -110
- package/src/helpers/RecordAnswerHelper.ts +7 -7
- package/src/helpers/SeedTools.ts +106 -0
- package/src/helpers/ServiceFeeHelper.ts +125 -15
- package/src/helpers/SetupStepUpdater.ts +38 -32
- package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
- package/src/helpers/StripeHelper.ts +14 -36
- package/src/helpers/StripeInvoicer.ts +419 -0
- package/src/helpers/StripePayoutChecker.ts +5 -10
- package/src/helpers/TagHelper.ts +7 -8
- package/src/helpers/TemporaryMemberAccess.ts +1 -2
- package/src/helpers/ThrottledQueue.ts +1 -2
- package/src/helpers/UitpasTokenRepository.ts +1 -2
- package/src/helpers/ViesHelper.ts +7 -11
- package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
- package/src/helpers/outstandingBalanceJoin.ts +2 -2
- package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
- package/src/migrate.ts +12 -5
- package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
- package/src/migrations/0000000006-default-organization.ts +139 -0
- package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
- package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
- package/src/migrations/1750090029-document-update-year.ts +103 -0
- package/src/{seeds → migrations}/1750090030-records-configuration.ts +33 -32
- package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
- package/src/migrations/1752848540-national-numbers.ts +585 -0
- package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
- package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +70 -26
- package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
- package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
- package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
- package/src/migrations/1761665607-sync-member-users.ts +40 -0
- package/src/migrations/1779121240-invoice-email-template.sql +6 -0
- package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
- package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
- package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
- package/src/migrations/1780933598-document-groups.ts +88 -0
- package/src/migrations/1781692342-balance-item-relations.ts +13 -0
- package/src/migrations/1781857866-platform-admins.ts +103 -0
- package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
- package/src/seeds/0000000001-throw.ts +5 -0
- package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
- package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
- package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
- package/src/seeds/1773754928-force-save-members.ts +41 -8
- package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
- package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
- package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
- package/src/seeds/1780915002-update-orders.ts +39 -0
- package/src/seeds/1782047708-organization-company-address.ts +44 -0
- package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
- package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
- package/src/services/AuditLogService.test.ts +51 -0
- package/src/services/AuditLogService.ts +45 -0
- package/src/services/BalanceItemService.ts +141 -44
- package/src/services/CpuService.ts +3 -6
- package/src/services/DatabaseCollationService.test.ts +1 -2
- package/src/services/DocumentService.ts +1 -2
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/FileSignService.ts +2 -4
- package/src/services/InvoicePdfService.ts +286 -0
- package/src/services/InvoiceService.ts +204 -251
- package/src/services/InvoiceXMLService.ts +424 -0
- package/src/services/MemberNumberService.ts +6 -11
- package/src/services/MemberRecordStore.ts +8 -10
- package/src/services/MollieService.ts +114 -121
- package/src/services/PaymentMandateService.ts +45 -45
- package/src/services/PaymentReallocationService.test.ts +1 -1
- package/src/services/PaymentReallocationService.ts +7 -9
- package/src/services/PaymentService.ts +444 -308
- package/src/services/PlatformMembershipService.ts +4 -8
- package/src/services/ReferralService.ts +223 -0
- package/src/services/RegistrationService.ts +63 -53
- package/src/services/SSOService.test.ts +119 -0
- package/src/services/SSOService.ts +56 -49
- package/src/services/STPackageService.ts +97 -61
- package/src/services/UniqueMemberNumberService.ts +69 -0
- package/src/services/UniqueUserService.ts +3 -6
- package/src/services/VATService.ts +85 -0
- package/src/services/uitpas/PassholderEndpoints.ts +3 -6
- package/src/services/uitpas/UitpasService.ts +6 -10
- package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
- package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
- package/src/services/uitpas/searchUitpasEvents.ts +1 -2
- package/src/sql-filters/audit-logs.ts +1 -1
- package/src/sql-filters/balance-item-payments.ts +1 -1
- package/src/sql-filters/balance-items.ts +1 -1
- package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
- package/src/sql-filters/document-templates.ts +1 -1
- package/src/sql-filters/documents.ts +1 -1
- package/src/sql-filters/email-recipients.ts +1 -1
- package/src/sql-filters/emails.ts +1 -1
- package/src/sql-filters/event-notifications.ts +1 -1
- package/src/sql-filters/events.ts +6 -1
- package/src/sql-filters/groups.ts +5 -0
- package/src/sql-filters/invoiced-balance-items.ts +1 -1
- package/src/sql-filters/invoices.ts +1 -1
- package/src/sql-filters/member-responsibility-records.ts +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/src/sql-filters/orders.ts +4 -4
- package/src/sql-filters/organization-registration-periods.ts +1 -1
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-filters/platform-memberships.ts +3 -3
- package/src/sql-filters/receivable-balances.ts +1 -1
- package/src/sql-filters/registration-periods.ts +1 -1
- package/src/sql-filters/registrations.ts +1 -1
- package/src/sql-filters/tickets.ts +1 -1
- package/src/sql-filters/users.ts +1 -1
- package/src/sql-sorters/groups.ts +23 -0
- package/src/sql-sorters/platform-memberships.ts +16 -16
- package/src/sql-sorters/registration-invitations.ts +2 -2
- package/tests/actions/patchOrganizationMember.ts +1 -1
- package/tests/actions/patchUserMember.ts +1 -1
- package/tests/assertions/assertBalances.ts +6 -12
- package/tests/e2e/api-rate-limits.test.ts +12 -24
- package/tests/e2e/bundle-discounts.test.ts +4 -1
- package/tests/e2e/charge-members.test.ts +13 -11
- package/tests/e2e/documents.test.ts +4 -1
- package/tests/e2e/register.test.ts +7 -9
- package/tests/e2e/stock.test.ts +4 -3
- package/tests/e2e/tickets.test.ts +1 -1
- package/tests/helpers/MollieMocker.ts +462 -0
- package/tests/helpers/PayconiqMocker.ts +138 -45
- package/tests/helpers/StripeMocker.ts +9 -6
- package/tests/helpers/index.ts +4 -0
- package/tests/init/initAdmin.ts +2 -2
- package/tests/init/initBundleDiscount.ts +1 -1
- package/tests/init/initMembershipOrganization.ts +13 -0
- package/tests/init/initPermissionRole.ts +3 -4
- package/tests/vitest.global.setup.ts +1 -1
- package/vitest.config.js +6 -0
- package/.nvmrc +0 -1
- package/src/helpers/GroupBuilder.ts +0 -418
- package/src/seeds/0000000000-example.ts +0 -11
- package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
- package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
- package/src/seeds/1726847064-setup-steps.ts +0 -16
- package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
- package/src/seeds/1729253172-update-orders.ts +0 -21
- package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
- package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
- package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
- package/src/seeds/1740046783-update-membership.ts +0 -17
- package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
- package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
- package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
- package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
- package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
- package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
- package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
- package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
- package/src/seeds/1756303697-update-email-counts.ts +0 -76
- package/src/seeds/1761665607-sync-member-users.ts +0 -59
- package/src/seeds/1765896674-document-update-year.ts +0 -66
- /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
- /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
- /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
- /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
- /package/src/{seeds → migrations}/1779121239-default-invoice-email-template.sql +0 -0
- /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
package/src/crons/invoices.ts
CHANGED
|
@@ -2,50 +2,43 @@ import { isSimpleError, isSimpleErrors } from '@simonbackx/simple-errors';
|
|
|
2
2
|
import { registerCron } from '@stamhoofd/crons';
|
|
3
3
|
import type { Invoice } from '@stamhoofd/models';
|
|
4
4
|
import { Organization, Payment, sendEmailTemplate } from '@stamhoofd/models';
|
|
5
|
-
import type { IterableSQLSelect } from '@stamhoofd/sql';
|
|
6
5
|
import { EmailTemplateType, InvoiceStruct, PaymentStatus, Replacement } from '@stamhoofd/structures';
|
|
7
6
|
import { Formatter, Sorter } from '@stamhoofd/utility';
|
|
8
7
|
import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures.js';
|
|
9
8
|
import { InvoiceService } from '../services/InvoiceService.js';
|
|
9
|
+
import { useSavedIterator } from './helpers/useSavedIterator.js';
|
|
10
|
+
import { isOutside } from './helpers/isOutside.js';
|
|
10
11
|
|
|
11
12
|
registerCron('invoices', invoices);
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const { iterate, isHoursAgo } = useSavedIterator(() => {
|
|
15
|
+
return Organization.select();
|
|
16
|
+
}, { limit: 10, maxQueries: 5 });
|
|
15
17
|
|
|
16
18
|
const bootAt = new Date();
|
|
17
19
|
|
|
18
20
|
async function invoices() {
|
|
19
|
-
// Do not run within
|
|
20
|
-
if (bootAt.getTime() > new Date().getTime() - 1000 * 60 *
|
|
21
|
+
// Do not run within 5 hours after boot
|
|
22
|
+
if (bootAt.getTime() > new Date().getTime() - 1000 * 60 * 60 * 5 && STAMHOOFD.environment !== 'development') {
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
if (
|
|
26
|
+
if (isOutside('03:00', '10:00')) {
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
if ((
|
|
30
|
+
if (!isHoursAgo(12)) {
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
// Get the next x organization to send e-mails for
|
|
33
|
-
|
|
34
|
-
savedIterator = Organization.select().limit(10).all();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for await (const organization of savedIterator.maxQueries(5)) {
|
|
35
|
+
for await (const organization of iterate()) {
|
|
38
36
|
if (!organization.meta.invoicesEnabled) {
|
|
39
37
|
continue;
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
// Create all invoices for this organization
|
|
43
|
-
await createInvoicesFor(organization)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (savedIterator.isDone) {
|
|
47
|
-
savedIterator = null;
|
|
48
|
-
lastFullRun = new Date();
|
|
41
|
+
await createInvoicesFor(organization);
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
44
|
|
|
@@ -57,12 +50,12 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
57
50
|
|
|
58
51
|
// Belgian rules: allowed to invoice up to the 15th day of the next month. We extend it with one month to fix mistakes.
|
|
59
52
|
const today = Formatter.luxon();
|
|
60
|
-
const startDate = today.day <= 15 ? today.minus({month: 2}).startOf('month') : today.minus({month: 1}).startOf('month');
|
|
53
|
+
const startDate = today.day <= 15 ? today.minus({ month: 2 }).startOf('month') : today.minus({ month: 1 }).startOf('month');
|
|
61
54
|
|
|
62
55
|
// Don't invoice below 1 euro - unless we reached the timeout date for invoices (end of month + 15 days - 3 days margin)
|
|
63
|
-
const invoiceLimit = 1_0000
|
|
56
|
+
const invoiceLimit = STAMHOOFD.environment === 'development' ? 0 : 1_0000;
|
|
64
57
|
function getPaymentTimeoutDate(p: Payment) {
|
|
65
|
-
return Formatter.luxon(p.paidAt ?? p.createdAt).plus({month: 1}).set({day: 15 - 3}).startOf('day').toJSDate()
|
|
58
|
+
return Formatter.luxon(p.paidAt ?? p.createdAt).plus({ month: 1 }).set({ day: 15 - 3 }).startOf('day').toJSDate();
|
|
66
59
|
}
|
|
67
60
|
|
|
68
61
|
console.log('Fetching all payments between ' + Formatter.dateTime(startDate.toJSDate()) + ' and now for ' + organization.name);
|
|
@@ -74,6 +67,7 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
74
67
|
.where('invoiceId', null)
|
|
75
68
|
.where('customer', '!=', null)
|
|
76
69
|
.where('payingOrganizationId', '!=', null)
|
|
70
|
+
.where('price', '!=', 0)
|
|
77
71
|
.limit(1_000)
|
|
78
72
|
.orderBy('payingOrganizationId')
|
|
79
73
|
.fetch();
|
|
@@ -93,21 +87,21 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
93
87
|
const id = JSON.stringify(blob);
|
|
94
88
|
const existing = groups.get(id);
|
|
95
89
|
if (existing) {
|
|
96
|
-
existing.push(payment)
|
|
90
|
+
existing.push(payment);
|
|
97
91
|
} else {
|
|
98
|
-
groups.set(id, [payment])
|
|
92
|
+
groups.set(id, [payment]);
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
console.log('Invoicing ' + groups.size + ' customers');
|
|
103
97
|
|
|
104
|
-
const errors: string[] = []
|
|
105
|
-
const invoices: Invoice[] = []
|
|
98
|
+
const errors: string[] = [];
|
|
99
|
+
const invoices: Invoice[] = [];
|
|
106
100
|
let skipped = 0;
|
|
107
101
|
|
|
108
102
|
for (const [_, payments] of groups) {
|
|
109
103
|
// Group from last to newest (so we use the last customer details if the address changed during the month)
|
|
110
|
-
payments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt))
|
|
104
|
+
payments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt));
|
|
111
105
|
const customer = payments[0].customer!.dynamicName;
|
|
112
106
|
try {
|
|
113
107
|
const generalStructs = await AuthenticatedStructures.paymentsGeneral(payments, false);
|
|
@@ -119,28 +113,28 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
119
113
|
});
|
|
120
114
|
invoice.buildFromPayments();
|
|
121
115
|
|
|
122
|
-
if (invoice.totalWithVAT < invoiceLimit) {
|
|
116
|
+
if (invoice.totalWithVAT >= 0 && invoice.totalWithVAT < invoiceLimit) {
|
|
123
117
|
const first = new Date(Math.min(...payments.map(p => getPaymentTimeoutDate(p).getTime())));
|
|
124
118
|
if (first > new Date()) {
|
|
125
|
-
console.log('Delaying invoicing ' + customer + ' at ' + organization.id + ' until ' + Formatter.dateIso(first))
|
|
119
|
+
console.log('Delaying invoicing ' + customer + ' at ' + organization.id + ' until ' + Formatter.dateIso(first));
|
|
126
120
|
skipped += 1;
|
|
127
121
|
continue;
|
|
128
122
|
} else {
|
|
129
|
-
console.log('Invoiced low priced invoice, because of date ' + Formatter.dateIso(first) + ' being in the past')
|
|
123
|
+
console.log('Invoiced low priced invoice, because of date ' + Formatter.dateIso(first) + ' being in the past');
|
|
130
124
|
}
|
|
131
125
|
}
|
|
132
126
|
|
|
133
127
|
const model = await InvoiceService.createFrom(organization, invoice);
|
|
134
|
-
invoices.push(model)
|
|
128
|
+
invoices.push(model);
|
|
135
129
|
} catch (e) {
|
|
136
130
|
console.error(payments.map(p => p.id), e);
|
|
137
131
|
|
|
138
|
-
const prefix = customer + ' ('+payments.map(p => '<a href="'+Formatter.escapeHtml('https://'+organization.getDashboardHost() + '/boekhouding/betalingen/' + p.id)+'">'+ Formatter.escapeHtml($t('%14a') + ' ' + p.id.substring(0, 8))+'</a>').join(', ') + '): ';
|
|
132
|
+
const prefix = customer + ' (' + payments.map(p => '<a href="' + Formatter.escapeHtml('https://' + organization.getDashboardHost() + '/boekhouding/betalingen/' + p.id) + '">' + Formatter.escapeHtml($t('%14a') + ' ' + p.id.substring(0, 8)) + '</a>').join(', ') + '): ';
|
|
139
133
|
|
|
140
134
|
if (isSimpleError(e) || isSimpleErrors(e)) {
|
|
141
|
-
errors.push(prefix + Formatter.escapeHtml(e.getHuman()))
|
|
135
|
+
errors.push(prefix + Formatter.escapeHtml(e.getHuman()));
|
|
142
136
|
} else {
|
|
143
|
-
errors.push(prefix + Formatter.escapeHtml($t('%1ED')))
|
|
137
|
+
errors.push(prefix + Formatter.escapeHtml($t('%1ED')));
|
|
144
138
|
}
|
|
145
139
|
}
|
|
146
140
|
}
|
|
@@ -150,7 +144,7 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
150
144
|
if (errors.length) {
|
|
151
145
|
await sendEmailTemplate(organization, {
|
|
152
146
|
template: {
|
|
153
|
-
type: EmailTemplateType.InvoiceGenerationErrors
|
|
147
|
+
type: EmailTemplateType.InvoiceGenerationErrors,
|
|
154
148
|
},
|
|
155
149
|
recipients: await organization.getAdminRecipients(),
|
|
156
150
|
type: 'transactional',
|
|
@@ -158,9 +152,9 @@ async function createInvoicesFor(organization: Organization) {
|
|
|
158
152
|
defaultReplacements: [
|
|
159
153
|
Replacement.create({
|
|
160
154
|
token: 'errors',
|
|
161
|
-
html: '<ul><li>'+errors.join('</li><li>')+'</li></ul>' + (skipped > 0 ? '<p>'+Formatter.escapeHtml(skipped === 1 ? $t('%1U4') : $t('%1Sp', {count: skipped}))+'</p>' : '')
|
|
162
|
-
})
|
|
163
|
-
]
|
|
164
|
-
})
|
|
155
|
+
html: '<ul><li>' + errors.join('</li><li>') + '</li></ul>' + (skipped > 0 ? '<p>' + Formatter.escapeHtml(skipped === 1 ? $t('%1U4') : $t('%1Sp', { count: skipped })) + '</p>' : ''),
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
});
|
|
165
159
|
}
|
|
166
160
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { registerCron } from '@stamhoofd/crons';
|
|
2
|
+
import { Organization, Platform } from '@stamhoofd/models';
|
|
3
|
+
import { useSavedIterator } from './helpers/useSavedIterator.js';
|
|
4
|
+
import { STPackageService } from '../services/STPackageService.js';
|
|
5
|
+
import { BalanceItemStatus } from '@stamhoofd/structures';
|
|
6
|
+
|
|
7
|
+
// Charge manual service fees every night
|
|
8
|
+
registerCron('members-fees', chargeMembers);
|
|
9
|
+
|
|
10
|
+
const { iterate, isHoursAgo } = useSavedIterator(() => {
|
|
11
|
+
return Organization.select();
|
|
12
|
+
}, { limit: 10, maxQueries: 5 });
|
|
13
|
+
|
|
14
|
+
async function chargeMembers() {
|
|
15
|
+
if (STAMHOOFD.environment !== 'development' && (new Date().getHours() > 5 || new Date().getHours() < 2)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!isHoursAgo(12)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
|
|
24
|
+
if (!membershipOrganizationId) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
|
|
29
|
+
|
|
30
|
+
for await (const organization of iterate()) {
|
|
31
|
+
await chargeMemberFeesForOrganization(organization, membershipOrganization);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Charge the (increase in) per-member fees for every active package of an organization.
|
|
37
|
+
* For per-member packages this only charges the members that aren't paid/pending yet.
|
|
38
|
+
*/
|
|
39
|
+
export async function chargeMemberFeesForOrganization(organization: Organization, sellingOrganization: Organization) {
|
|
40
|
+
const packages = await STPackageService.getActivePackages(organization.id);
|
|
41
|
+
|
|
42
|
+
for (const pack of packages) {
|
|
43
|
+
const item = await STPackageService.chargePackage(pack, sellingOrganization, organization.defaultCompanies[0]);
|
|
44
|
+
if (item) {
|
|
45
|
+
item.status = BalanceItemStatus.Due;
|
|
46
|
+
await item.save();
|
|
47
|
+
console.log('Charged', item.id);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -5,15 +5,15 @@ import { PaymentService } from '../services/PaymentService.js';
|
|
|
5
5
|
|
|
6
6
|
registerCron('mollie-chargebacks', checkMollieChargebacks);
|
|
7
7
|
|
|
8
|
-
let lastRun: Date | null = null
|
|
8
|
+
let lastRun: Date | null = null;
|
|
9
9
|
export async function checkMollieChargebacks() {
|
|
10
10
|
if (STAMHOOFD.environment !== 'development') {
|
|
11
11
|
if (lastRun && new Date().getTime() - lastRun.getTime() < 1000 * 60 * 60 * 24) {
|
|
12
|
-
return
|
|
12
|
+
return;
|
|
13
13
|
}
|
|
14
|
-
lastRun = new Date()
|
|
14
|
+
lastRun = new Date();
|
|
15
15
|
}
|
|
16
|
-
await doCheckMollieChargebacks(false)
|
|
16
|
+
await doCheckMollieChargebacks(false);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export async function doCheckMollieChargebacks(checkAll = false) {
|
|
@@ -24,11 +24,11 @@ export async function doCheckMollieChargebacks(checkAll = false) {
|
|
|
24
24
|
// Loop all mollie tokens
|
|
25
25
|
for await (const token of MollieToken.select().limit(1).all({}, 'organizationId')) {
|
|
26
26
|
// todo
|
|
27
|
-
const sellingOrganization = await Organization.getByID(token.id)
|
|
27
|
+
const sellingOrganization = await Organization.getByID(token.id);
|
|
28
28
|
if (sellingOrganization) {
|
|
29
|
-
const service = await MollieService.create({ sellingOrganization })
|
|
29
|
+
const service = await MollieService.create({ sellingOrganization });
|
|
30
30
|
if (service) {
|
|
31
|
-
await checkMollieChargebacksFor(service, checkAll)
|
|
31
|
+
await checkMollieChargebacksFor(service, checkAll);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -40,11 +40,11 @@ export async function checkMollieChargebacksFor(service: MollieService, checkAll
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// Check last 3 days
|
|
43
|
-
const offset = new Date(Date.now() - 1000 * 60 * 60 * 24 * 4)
|
|
43
|
+
const offset = new Date(Date.now() - 1000 * 60 * 60 * 24 * 4);
|
|
44
44
|
|
|
45
45
|
// due to a bug in mollie client code, testmode paramter is missing in the typescript definitions
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
47
|
-
for await (const chargeback of service.client.chargebacks.iterate({sort: 'desc', testmode: service.testMode} as any)) {
|
|
47
|
+
for await (const chargeback of service.client.chargebacks.iterate({ sort: 'desc', testmode: service.testMode } as any)) {
|
|
48
48
|
if (!checkAll && new Date(chargeback.createdAt) < offset) {
|
|
49
49
|
break;
|
|
50
50
|
}
|
|
@@ -63,22 +63,21 @@ export async function checkMollieChargebacksFor(service: MollieService, checkAll
|
|
|
63
63
|
if (payment) {
|
|
64
64
|
try {
|
|
65
65
|
const amount = Math.round(parseFloat(chargeback.amount.value) * 100) * 100;
|
|
66
|
-
const createdPayment = await PaymentService.registerChargeback(payment, amount)
|
|
66
|
+
const createdPayment = await PaymentService.registerChargeback(payment, amount, new Date(chargeback.createdAt));
|
|
67
67
|
|
|
68
68
|
// Link Mollie chargeback ID (so we can set settlement later in the settlements cron)
|
|
69
69
|
const molliePayment = new MolliePayment();
|
|
70
|
-
molliePayment.paymentId = createdPayment.id
|
|
70
|
+
molliePayment.paymentId = createdPayment.id;
|
|
71
71
|
molliePayment.mollieId = chargeback.id;
|
|
72
|
-
await molliePayment.save()
|
|
72
|
+
await molliePayment.save();
|
|
73
73
|
} catch (e) {
|
|
74
|
-
console.error('Failed to register chargeback ' + chargeback.id, e)
|
|
74
|
+
console.error('Failed to register chargeback ' + chargeback.id, e);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
} else {
|
|
78
|
-
console.error('Invalid chargeback payment id '+chargeback.paymentId + ', not found')
|
|
78
|
+
console.error('Invalid chargeback payment id ' + chargeback.paymentId + ', not found');
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
if (STAMHOOFD.environment !== 'development') {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { registerCron } from '@stamhoofd/crons';
|
|
2
|
+
import { BalanceItem, Organization, Payment, Platform } from '@stamhoofd/models';
|
|
3
|
+
import { SQL } from '@stamhoofd/sql';
|
|
4
|
+
import { BalanceItemStatus, BalanceItemType, PaymentMethod, PaymentStatus } from '@stamhoofd/structures';
|
|
5
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
6
|
+
import { BalanceItemService } from '../services/BalanceItemService.js';
|
|
7
|
+
import { PaymentService } from '../services/PaymentService.js';
|
|
8
|
+
import { VATService } from '../services/VATService.js';
|
|
9
|
+
|
|
10
|
+
// Charge manual service fees every night
|
|
11
|
+
registerCron('service-fees', chargeServiceFees);
|
|
12
|
+
|
|
13
|
+
async function chargeServiceFees() {
|
|
14
|
+
if (STAMHOOFD.environment !== 'development' && (new Date().getHours() > 5 || new Date().getHours() < 2)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
|
|
18
|
+
if (!membershipOrganizationId) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
|
|
23
|
+
|
|
24
|
+
// Succeeded payments
|
|
25
|
+
const payments = await Payment.select()
|
|
26
|
+
.where('status', PaymentStatus.Succeeded)
|
|
27
|
+
.where('serviceFeeManual', '>', 0)
|
|
28
|
+
.where('serviceFeeManualCharged', 0)
|
|
29
|
+
.where('createdAt', '<', STAMHOOFD.environment !== 'development' ? new Date() : Formatter.dateIso(new Date(Date.now() + 1000 * 60 * 60 * 24)))
|
|
30
|
+
.limit(200)
|
|
31
|
+
.orderBy('createdAt', 'ASC')
|
|
32
|
+
.fetch();
|
|
33
|
+
|
|
34
|
+
// Pending payments older than 14 days with method = Transfer or PointOfSale
|
|
35
|
+
const pendingPayments = await Payment.select()
|
|
36
|
+
.where('status', [PaymentStatus.Pending, PaymentStatus.Created])
|
|
37
|
+
.where('method', [PaymentMethod.Transfer, PaymentMethod.PointOfSale, PaymentMethod.Unknown])
|
|
38
|
+
.where('serviceFeeManual', '>', 0)
|
|
39
|
+
.where('serviceFeeManualCharged', 0)
|
|
40
|
+
.where('createdAt', '<', Formatter.dateIso(new Date(Date.now() - 1000 * 60 * 60 * 24 * 14)))
|
|
41
|
+
.limit(200)
|
|
42
|
+
.orderBy('createdAt', 'ASC')
|
|
43
|
+
.fetch();
|
|
44
|
+
|
|
45
|
+
const allPayments = [...payments, ...pendingPayments];
|
|
46
|
+
|
|
47
|
+
console.log('Found ' + allPayments.length + ' payments to charge service fees for');
|
|
48
|
+
|
|
49
|
+
const mappedToOrganizationId = new Map<string, Payment[]>();
|
|
50
|
+
for (const payment of allPayments) {
|
|
51
|
+
if (!payment.organizationId) {
|
|
52
|
+
console.warn('Payment without organizationId, skipping', payment.id);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!mappedToOrganizationId.has(payment.organizationId)) {
|
|
56
|
+
mappedToOrganizationId.set(payment.organizationId, []);
|
|
57
|
+
}
|
|
58
|
+
mappedToOrganizationId.get(payment.organizationId)!.push(payment);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const [organizationId, payments] of mappedToOrganizationId.entries()) {
|
|
62
|
+
const organization = await Organization.getByID(organizationId);
|
|
63
|
+
if (!organization) {
|
|
64
|
+
console.warn('[chargeServiceFees] Organization not found for ID', organizationId);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Group payments by Formatter.dateNumber(p.createdAt)
|
|
69
|
+
const groupedPayments: Map<string, Payment[]> = new Map();
|
|
70
|
+
for (const payment of payments) {
|
|
71
|
+
const date = Formatter.dateNumber(payment.createdAt);
|
|
72
|
+
if (!groupedPayments.has(date)) {
|
|
73
|
+
groupedPayments.set(date, []);
|
|
74
|
+
}
|
|
75
|
+
groupedPayments.get(date)!.push(payment);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const [_, payments] of groupedPayments) {
|
|
79
|
+
const totalFees = payments.reduce((sum, payment) => sum + payment.serviceFeeManual - payment.serviceFeeManualCharged, 0);
|
|
80
|
+
const groupedByDate = new Map<string, Payment[]>();
|
|
81
|
+
|
|
82
|
+
for (const p of payments) {
|
|
83
|
+
const date = Formatter.dateNumber(p.createdAt);
|
|
84
|
+
const existing = groupedByDate.get(date);
|
|
85
|
+
if (existing) {
|
|
86
|
+
existing.push(p);
|
|
87
|
+
} else {
|
|
88
|
+
groupedByDate.set(date, [p]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const [day, payments] of groupedByDate) {
|
|
93
|
+
const date = new Date(Math.min(...payments.map(p => p.createdAt.getTime())));
|
|
94
|
+
const startOfDay = Formatter.luxon(date).startOf('day').toJSDate();
|
|
95
|
+
const endOfDay = Formatter.luxon(date).endOf('day').toJSDate();
|
|
96
|
+
|
|
97
|
+
// Ms don't work when querying MySQL, they get lost
|
|
98
|
+
startOfDay.setMilliseconds(0);
|
|
99
|
+
endOfDay.setMilliseconds(0);
|
|
100
|
+
|
|
101
|
+
const item = new BalanceItem();
|
|
102
|
+
item.type = BalanceItemType.ServiceFee;
|
|
103
|
+
item.description = $t('%1cJ', { date: day });
|
|
104
|
+
item.payingOrganizationId = organization.id;
|
|
105
|
+
item.organizationId = membershipOrganizationId;
|
|
106
|
+
item.VATPercentage = 21;
|
|
107
|
+
item.VATExcempt = VATService.getVATExcempt({
|
|
108
|
+
company: organization.defaultCompanies[0] ?? null,
|
|
109
|
+
sellingOrganization: membershipOrganization,
|
|
110
|
+
type: 'services',
|
|
111
|
+
});
|
|
112
|
+
item.VATIncluded = false;
|
|
113
|
+
item.quantity = 1;
|
|
114
|
+
item.unitPrice = totalFees;
|
|
115
|
+
item.createdAt = new Date();
|
|
116
|
+
item.status = BalanceItemStatus.Due;
|
|
117
|
+
item.startDate = startOfDay;
|
|
118
|
+
item.endDate = endOfDay;
|
|
119
|
+
|
|
120
|
+
// Check if we find an existing one
|
|
121
|
+
const existing = await BalanceItem.select()
|
|
122
|
+
.where('type', BalanceItemType.ServiceFee)
|
|
123
|
+
.where('organizationId', membershipOrganizationId)
|
|
124
|
+
.where('VATPercentage', item.VATPercentage)
|
|
125
|
+
.where('VATIncluded', item.VATIncluded)
|
|
126
|
+
.where('VATExcempt', item.VATExcempt)
|
|
127
|
+
.where('payingOrganizationId', organization.id)
|
|
128
|
+
.where('status', BalanceItemStatus.Due)
|
|
129
|
+
.where('startDate', startOfDay)
|
|
130
|
+
.where('endDate', endOfDay)
|
|
131
|
+
.first(false);
|
|
132
|
+
|
|
133
|
+
if (existing) {
|
|
134
|
+
existing.unitPrice += totalFees;
|
|
135
|
+
await existing.save();
|
|
136
|
+
BalanceItemService.scheduleUpdate(existing);
|
|
137
|
+
} else {
|
|
138
|
+
await item.save();
|
|
139
|
+
BalanceItemService.scheduleUpdate(item);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { changedRows } = await Payment.update()
|
|
143
|
+
.where('id', payments.map(p => p.id))
|
|
144
|
+
.set('serviceFeeManualCharged', SQL.column('serviceFeeManual'))
|
|
145
|
+
.update();
|
|
146
|
+
if (changedRows !== payments.length) {
|
|
147
|
+
console.error('Unexpected change in serviceFeeManualCharged. Expected ' + payments.length + ', updated only ' + changedRows);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { registerCron } from '@stamhoofd/crons';
|
|
2
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
3
|
+
import { StripeInvoicer } from '../helpers/StripeInvoicer.js';
|
|
4
|
+
import { Organization, Platform } from '@stamhoofd/models';
|
|
5
|
+
|
|
6
|
+
registerCron('stripe-invoices', createStripeInvoices);
|
|
7
|
+
|
|
8
|
+
let lastStripeInvoice: Date | null = null;
|
|
9
|
+
|
|
10
|
+
async function createStripeInvoices() {
|
|
11
|
+
if (STAMHOOFD.environment !== 'production') {
|
|
12
|
+
// return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (STAMHOOFD.userMode === 'platform') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (STAMHOOFD.STRIPE_CONNECT_METHOD === 'standard') {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Wait for the next day before doing a new check
|
|
24
|
+
const today = new Date();
|
|
25
|
+
if (lastStripeInvoice && Formatter.dateIso(lastStripeInvoice) === Formatter.dateIso(today)) {
|
|
26
|
+
console.log('Stripe check done for this day');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log('Creating Stripe Invoices...');
|
|
30
|
+
|
|
31
|
+
if (!STAMHOOFD.STRIPE_SECRET_KEY) {
|
|
32
|
+
console.log('No stripe key set');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
|
|
37
|
+
if (!membershipOrganizationId) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
|
|
42
|
+
|
|
43
|
+
const invoicer = new StripeInvoicer({
|
|
44
|
+
secretKey: STAMHOOFD.STRIPE_SECRET_KEY,
|
|
45
|
+
});
|
|
46
|
+
await invoicer.generateAllInvoices(membershipOrganization);
|
|
47
|
+
lastStripeInvoice = new Date();
|
|
48
|
+
}
|