@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.
Files changed (429) hide show
  1. package/output-2.txt +1 -0
  2. package/output.txt +1 -0
  3. package/package.json +69 -52
  4. package/src/audit-logs/EventNotificationLogger.ts +40 -0
  5. package/src/audit-logs/ModelLogger.ts +19 -15
  6. package/src/audit-logs/OrganizationLogger.ts +1 -1
  7. package/src/audit-logs/RegistrationInvitationLogger.ts +24 -24
  8. package/src/audit-logs/init.ts +8 -2
  9. package/src/boot.ts +19 -18
  10. package/src/crons/amazon-ses.ts +13 -26
  11. package/src/crons/balance-emails.ts +3 -5
  12. package/src/crons/clearExcelCache.test.ts +1 -1
  13. package/src/crons/clearExcelCache.ts +1 -2
  14. package/src/crons/drip-emails.ts +42 -0
  15. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
  16. package/src/crons/helpers/isOutside.ts +11 -0
  17. package/src/crons/helpers/useSavedIterator.ts +32 -0
  18. package/src/crons/index.ts +5 -0
  19. package/src/crons/invoices.ts +32 -38
  20. package/src/crons/members-fees.ts +50 -0
  21. package/src/crons/mollie-chargebacks.ts +14 -15
  22. package/src/crons/service-fees.ts +152 -0
  23. package/src/crons/stripe-invoices.ts +48 -0
  24. package/src/crons/transfer-fees.ts +176 -0
  25. package/src/crons/update-cached-balances.ts +4 -4
  26. package/src/crons.ts +7 -64
  27. package/src/debug.ts +4 -2
  28. package/src/email-recipient-loaders/documents.ts +1 -1
  29. package/src/email-recipient-loaders/members.ts +1 -1
  30. package/src/email-recipient-loaders/payments.ts +4 -6
  31. package/src/email-recipient-loaders/registrations.ts +1 -1
  32. package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
  33. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
  34. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
  35. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
  36. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
  37. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
  38. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
  39. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
  40. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
  41. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
  42. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
  43. package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
  44. package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
  45. package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
  46. package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
  47. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
  48. package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
  49. package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
  50. package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
  51. package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
  52. package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
  53. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
  54. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
  55. package/src/endpoints/auth/SignupEndpoint.ts +2 -3
  56. package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
  57. package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
  58. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
  59. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
  60. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
  61. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
  62. package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
  63. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
  64. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
  65. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
  66. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  67. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
  68. package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
  69. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
  70. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
  71. package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
  72. package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
  73. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
  74. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
  75. package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
  76. package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
  77. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
  78. package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
  79. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
  80. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
  81. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
  82. package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
  83. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
  84. package/src/endpoints/global/files/GetFileCache.ts +3 -4
  85. package/src/endpoints/global/files/UploadFile.ts +3 -4
  86. package/src/endpoints/global/files/UploadImage.ts +3 -4
  87. package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +1 -1
  88. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
  89. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
  90. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
  91. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
  92. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
  93. package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
  94. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
  95. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
  96. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
  97. package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
  98. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
  99. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
  100. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
  101. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
  102. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
  103. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
  104. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
  105. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
  106. package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
  107. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
  108. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +11 -11
  109. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
  110. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
  111. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
  112. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
  113. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
  114. package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
  115. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
  116. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
  117. package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
  118. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
  119. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
  120. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
  121. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +35 -34
  122. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +1 -1
  123. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +62 -63
  124. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +6 -9
  125. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +51 -52
  126. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +6 -7
  127. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
  128. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
  129. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
  130. package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
  131. package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
  132. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  133. package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
  134. package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
  135. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
  136. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
  137. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
  138. package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +7 -9
  139. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
  140. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
  141. package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +1 -5
  142. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +11 -11
  143. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +2 -2
  144. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +8 -8
  145. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +9 -9
  146. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
  147. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
  148. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +4 -4
  149. package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
  150. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
  151. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +60 -57
  152. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +20 -20
  153. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  154. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
  155. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
  156. package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
  157. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
  158. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
  159. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
  160. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
  161. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
  162. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
  163. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
  164. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
  165. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
  166. package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
  167. package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +10 -6
  168. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
  169. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +4 -5
  170. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +5 -5
  171. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
  172. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
  173. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
  174. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
  175. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
  176. package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
  177. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
  178. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +50 -12
  179. package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
  180. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
  181. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
  182. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
  183. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
  184. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +21 -14
  185. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +35 -28
  186. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
  187. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
  188. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
  189. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  190. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
  191. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
  192. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  193. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +19 -18
  194. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
  195. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  196. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
  197. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
  198. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
  199. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
  200. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
  201. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
  202. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
  203. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
  204. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
  205. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
  206. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
  207. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
  208. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
  209. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
  210. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
  211. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
  212. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
  213. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
  214. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +22 -7
  215. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +20 -27
  216. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  217. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
  218. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
  219. package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
  220. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +2 -2
  221. package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
  222. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
  223. package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
  224. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
  225. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
  226. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
  227. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
  228. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
  229. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
  230. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
  231. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
  232. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +16 -33
  233. package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
  234. package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
  235. package/src/endpoints/system/HealthEndpoint.ts +1 -1
  236. package/src/excel-loaders/balance-items.ts +1 -1
  237. package/src/excel-loaders/event-notifications.ts +1 -1
  238. package/src/excel-loaders/members.ts +1 -1
  239. package/src/excel-loaders/organizations.ts +3 -3
  240. package/src/excel-loaders/payments.ts +31 -1
  241. package/src/excel-loaders/platform-memberships.ts +13 -14
  242. package/src/excel-loaders/receivable-balances.ts +2 -2
  243. package/src/excel-loaders/registrations.ts +2 -3
  244. package/src/helpers/AddressValidator.ts +7 -14
  245. package/src/helpers/AdminPermissionChecker.ts +50 -63
  246. package/src/helpers/AuthenticatedStructures.ts +69 -55
  247. package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
  248. package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
  249. package/src/helpers/BuckarooHelper.ts +2 -4
  250. package/src/helpers/CheckSettlements.ts +12 -24
  251. package/src/helpers/Context.ts +15 -10
  252. package/src/helpers/CookieHelper.ts +1 -2
  253. package/src/helpers/EmailResumer.ts +1 -2
  254. package/src/helpers/FileCache.ts +2 -3
  255. package/src/helpers/FinancialSupportHelper.ts +2 -2
  256. package/src/helpers/FlagMomentCleanup.ts +1 -2
  257. package/src/helpers/ForwardHandler.ts +5 -10
  258. package/src/helpers/GlobalHelper.ts +3 -3
  259. package/src/helpers/LegacyBillingConverter.ts +787 -0
  260. package/src/helpers/MemberMerger.test.ts +782 -0
  261. package/src/helpers/MemberMerger.ts +600 -0
  262. package/src/helpers/MemberUserSyncer.ts +6 -12
  263. package/src/helpers/MembershipCharger.ts +2 -4
  264. package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
  265. package/src/helpers/PaymentCustomerResolver.ts +144 -0
  266. package/src/helpers/PeriodHelper.ts +2 -3
  267. package/src/helpers/RecordAnswerHelper.test.ts +105 -110
  268. package/src/helpers/RecordAnswerHelper.ts +7 -7
  269. package/src/helpers/SeedTools.ts +106 -0
  270. package/src/helpers/ServiceFeeHelper.ts +125 -15
  271. package/src/helpers/SetupStepUpdater.ts +38 -32
  272. package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
  273. package/src/helpers/StripeHelper.ts +14 -36
  274. package/src/helpers/StripeInvoicer.ts +419 -0
  275. package/src/helpers/StripePayoutChecker.ts +5 -10
  276. package/src/helpers/TagHelper.ts +7 -8
  277. package/src/helpers/TemporaryMemberAccess.ts +1 -2
  278. package/src/helpers/ThrottledQueue.ts +1 -2
  279. package/src/helpers/UitpasTokenRepository.ts +1 -2
  280. package/src/helpers/ViesHelper.ts +7 -11
  281. package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
  282. package/src/helpers/outstandingBalanceJoin.ts +2 -2
  283. package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
  284. package/src/migrate.ts +12 -5
  285. package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
  286. package/src/migrations/0000000006-default-organization.ts +139 -0
  287. package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
  288. package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
  289. package/src/migrations/1750090029-document-update-year.ts +103 -0
  290. package/src/{seeds → migrations}/1750090030-records-configuration.ts +33 -32
  291. package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
  292. package/src/migrations/1752848540-national-numbers.ts +585 -0
  293. package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
  294. package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +70 -26
  295. package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
  296. package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
  297. package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
  298. package/src/migrations/1761665607-sync-member-users.ts +40 -0
  299. package/src/migrations/1779121240-invoice-email-template.sql +6 -0
  300. package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
  301. package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
  302. package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
  303. package/src/migrations/1780933598-document-groups.ts +88 -0
  304. package/src/migrations/1781692342-balance-item-relations.ts +13 -0
  305. package/src/migrations/1781857866-platform-admins.ts +103 -0
  306. package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
  307. package/src/seeds/0000000001-throw.ts +5 -0
  308. package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
  309. package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
  310. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
  311. package/src/seeds/1773754928-force-save-members.ts +41 -8
  312. package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
  313. package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
  314. package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
  315. package/src/seeds/1780915002-update-orders.ts +39 -0
  316. package/src/seeds/1782047708-organization-company-address.ts +44 -0
  317. package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
  318. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
  319. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
  320. package/src/services/AuditLogService.test.ts +51 -0
  321. package/src/services/AuditLogService.ts +45 -0
  322. package/src/services/BalanceItemService.ts +141 -44
  323. package/src/services/CpuService.ts +3 -6
  324. package/src/services/DatabaseCollationService.test.ts +1 -2
  325. package/src/services/DocumentService.ts +1 -2
  326. package/src/services/EventNotificationService.ts +1 -1
  327. package/src/services/FileSignService.ts +2 -4
  328. package/src/services/InvoicePdfService.ts +286 -0
  329. package/src/services/InvoiceService.ts +204 -251
  330. package/src/services/InvoiceXMLService.ts +424 -0
  331. package/src/services/MemberNumberService.ts +6 -11
  332. package/src/services/MemberRecordStore.ts +8 -10
  333. package/src/services/MollieService.ts +114 -121
  334. package/src/services/PaymentMandateService.ts +45 -45
  335. package/src/services/PaymentReallocationService.test.ts +1 -1
  336. package/src/services/PaymentReallocationService.ts +7 -9
  337. package/src/services/PaymentService.ts +444 -308
  338. package/src/services/PlatformMembershipService.ts +4 -8
  339. package/src/services/ReferralService.ts +223 -0
  340. package/src/services/RegistrationService.ts +63 -53
  341. package/src/services/SSOService.test.ts +119 -0
  342. package/src/services/SSOService.ts +56 -49
  343. package/src/services/STPackageService.ts +97 -61
  344. package/src/services/UniqueMemberNumberService.ts +69 -0
  345. package/src/services/UniqueUserService.ts +3 -6
  346. package/src/services/VATService.ts +85 -0
  347. package/src/services/uitpas/PassholderEndpoints.ts +3 -6
  348. package/src/services/uitpas/UitpasService.ts +6 -10
  349. package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
  350. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
  351. package/src/services/uitpas/searchUitpasEvents.ts +1 -2
  352. package/src/sql-filters/audit-logs.ts +1 -1
  353. package/src/sql-filters/balance-item-payments.ts +1 -1
  354. package/src/sql-filters/balance-items.ts +1 -1
  355. package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
  356. package/src/sql-filters/document-templates.ts +1 -1
  357. package/src/sql-filters/documents.ts +1 -1
  358. package/src/sql-filters/email-recipients.ts +1 -1
  359. package/src/sql-filters/emails.ts +1 -1
  360. package/src/sql-filters/event-notifications.ts +1 -1
  361. package/src/sql-filters/events.ts +6 -1
  362. package/src/sql-filters/groups.ts +5 -0
  363. package/src/sql-filters/invoiced-balance-items.ts +1 -1
  364. package/src/sql-filters/invoices.ts +1 -1
  365. package/src/sql-filters/member-responsibility-records.ts +1 -1
  366. package/src/sql-filters/members.ts +1 -1
  367. package/src/sql-filters/orders.ts +4 -4
  368. package/src/sql-filters/organization-registration-periods.ts +1 -1
  369. package/src/sql-filters/organizations.ts +1 -1
  370. package/src/sql-filters/platform-memberships.ts +3 -3
  371. package/src/sql-filters/receivable-balances.ts +1 -1
  372. package/src/sql-filters/registration-periods.ts +1 -1
  373. package/src/sql-filters/registrations.ts +1 -1
  374. package/src/sql-filters/tickets.ts +1 -1
  375. package/src/sql-filters/users.ts +1 -1
  376. package/src/sql-sorters/groups.ts +23 -0
  377. package/src/sql-sorters/platform-memberships.ts +16 -16
  378. package/src/sql-sorters/registration-invitations.ts +2 -2
  379. package/tests/actions/patchOrganizationMember.ts +1 -1
  380. package/tests/actions/patchUserMember.ts +1 -1
  381. package/tests/assertions/assertBalances.ts +6 -12
  382. package/tests/e2e/api-rate-limits.test.ts +12 -24
  383. package/tests/e2e/bundle-discounts.test.ts +4 -1
  384. package/tests/e2e/charge-members.test.ts +13 -11
  385. package/tests/e2e/documents.test.ts +4 -1
  386. package/tests/e2e/register.test.ts +7 -9
  387. package/tests/e2e/stock.test.ts +4 -3
  388. package/tests/e2e/tickets.test.ts +1 -1
  389. package/tests/helpers/MollieMocker.ts +462 -0
  390. package/tests/helpers/PayconiqMocker.ts +138 -45
  391. package/tests/helpers/StripeMocker.ts +9 -6
  392. package/tests/helpers/index.ts +4 -0
  393. package/tests/init/initAdmin.ts +2 -2
  394. package/tests/init/initBundleDiscount.ts +1 -1
  395. package/tests/init/initMembershipOrganization.ts +13 -0
  396. package/tests/init/initPermissionRole.ts +3 -4
  397. package/tests/vitest.global.setup.ts +1 -1
  398. package/vitest.config.js +6 -0
  399. package/.nvmrc +0 -1
  400. package/src/helpers/GroupBuilder.ts +0 -418
  401. package/src/seeds/0000000000-example.ts +0 -11
  402. package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
  403. package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
  404. package/src/seeds/1726847064-setup-steps.ts +0 -16
  405. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
  406. package/src/seeds/1729253172-update-orders.ts +0 -21
  407. package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
  408. package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
  409. package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
  410. package/src/seeds/1740046783-update-membership.ts +0 -17
  411. package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
  412. package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
  413. package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
  414. package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
  415. package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
  416. package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
  417. package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
  418. package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
  419. package/src/seeds/1756303697-update-email-counts.ts +0 -76
  420. package/src/seeds/1761665607-sync-member-users.ts +0 -59
  421. package/src/seeds/1765896674-document-update-year.ts +0 -66
  422. /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
  423. /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
  424. /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
  425. /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
  426. /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
  427. /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
  428. /package/src/{seeds → migrations}/1779121239-default-invoice-email-template.sql +0 -0
  429. /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
@@ -1,22 +1,17 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
- import { Image, Organization, Platform} from '@stamhoofd/models';
3
- import { BalanceItem, Invoice, InvoicedBalanceItem, Payment } from '@stamhoofd/models';
4
- import { render } from '@stamhoofd/models/helpers/Handlebars.js';
2
+ import { Email } from '@stamhoofd/email';
3
+ import { BalanceItem, Invoice, InvoicedBalanceItem, Organization, Payment, sendEmailTemplate } from '@stamhoofd/models';
5
4
  import { InvoiceCounter } from '@stamhoofd/models/helpers/InvoiceCounter.js';
6
- import type { Address, Invoice as InvoiceStruct } from '@stamhoofd/structures';
7
- import { CountryHelper, File, getVATExcemptInvoiceNote, getVATExcemptReasonName, PaymentMethod, PaymentMethodHelper, PaymentStatus } from '@stamhoofd/structures';
8
- import type { OrganizationInvoiceSettings } from '@stamhoofd/structures/OrganizationInvoiceSettings.js';
5
+ import type { Invoice as InvoiceStruct } from '@stamhoofd/structures';
6
+ import { EmailTemplateType, PaymentStatus, Recipient, Replacement } from '@stamhoofd/structures';
9
7
  import { Formatter } from '@stamhoofd/utility';
10
- import fs from 'fs/promises';
11
8
  import { ViesHelper } from '../helpers/ViesHelper.js';
12
9
  import { BalanceItemService } from './BalanceItemService.js';
13
- import { VERSION } from 'luxon';
14
- import {v4 as uuidv4} from 'uuid'
15
- import { PutObjectCommand } from '@aws-sdk/client-s3';
16
- import { signInternal } from '@stamhoofd/backend-env';
10
+ import { InvoicePdfService } from './InvoicePdfService.js';
11
+ import { InvoiceXMlService } from './InvoiceXMLService.js';
17
12
 
18
13
  export class InvoiceService {
19
- static async createFrom(organization: { id: string, privateMeta: {invoiceSettings: OrganizationInvoiceSettings} }, struct: InvoiceStruct, options?: { payments?: Payment[]; balanceItems?: BalanceItem[] }) {
14
+ static async createFrom(organization: Organization, struct: InvoiceStruct, options?: { payments?: Payment[]; balanceItems?: BalanceItem[] }) {
20
15
  if (struct.number) {
21
16
  throw new SimpleError({
22
17
  code: 'invalid_field',
@@ -46,7 +41,7 @@ export class InvoiceService {
46
41
  throw new SimpleError({
47
42
  code: 'invalid_invoiced_amount',
48
43
  message: 'Unexpected 0 totalBalanceInvoicedAmount',
49
- statusCode: 400
44
+ statusCode: 400,
50
45
  });
51
46
  }
52
47
 
@@ -83,7 +78,8 @@ export class InvoiceService {
83
78
  model.stripeAccountId = struct.stripeAccountId;
84
79
  model.reference = struct.reference;
85
80
 
86
- model.negativeInvoiceId = struct.negativeInvoiceId;
81
+ // todo: permission checks
82
+ // model.negativeInvoiceId = struct.negativeInvoiceId;
87
83
 
88
84
  if (Math.abs(model.payableRoundingAmount) > 10_00) {
89
85
  throw new SimpleError({
@@ -118,6 +114,14 @@ export class InvoiceService {
118
114
  message: 'You cannot invoice a payment multiple times',
119
115
  });
120
116
  }
117
+
118
+ if (payment.status === PaymentStatus.Failed) {
119
+ throw new SimpleError({
120
+ code: 'failed_payment',
121
+ message: 'You cannot invoice failed payments',
122
+ human: $t('%1ZW'),
123
+ });
124
+ }
121
125
  }
122
126
 
123
127
  const balanceItemIds = Formatter.uniqueArray(struct.items.map(i => i.balanceItemId));
@@ -129,7 +133,7 @@ export class InvoiceService {
129
133
  // Force update
130
134
  options!.balanceItems = undefined;
131
135
  }
132
-
136
+
133
137
  const balanceItems = options?.balanceItems ?? await BalanceItem.getByIDs(...balanceItemIds);
134
138
  await model.save();
135
139
 
@@ -146,18 +150,18 @@ export class InvoiceService {
146
150
  }
147
151
 
148
152
  // Todo: check we are not invoicing more than maximum invoiceable for these items
149
- const maximumInvoiceable = balanceItem.priceTotal; // € - 10
153
+ const maximumInvoiceable = balanceItem.priceDue; // € - 10
150
154
  const alreadyInvoiced = balanceItem.priceInvoiced; // € 5
151
155
  const left = maximumInvoiceable - alreadyInvoiced; // € -15
152
156
  const goingToInvoice = item.balanceInvoicedAmount;
153
157
 
154
158
  if (item.quantity === 0) {
155
159
  // should not be saved!
156
- throw new SimpleError({
160
+ throw new SimpleError({
157
161
  statusCode: 400,
158
162
  code: 'zero_quantity',
159
163
  message: 'Cannot invoice a quantity of zero',
160
- human: $t('%1RZ')
164
+ human: $t('%1RZ'),
161
165
  });
162
166
  }
163
167
 
@@ -171,8 +175,8 @@ export class InvoiceService {
171
175
  'a-euro': Formatter.price(goingToInvoice),
172
176
  'name': balanceItem.getStructure().itemTitle,
173
177
  'left-euro': Formatter.price(-left),
174
- })
175
- })
178
+ }),
179
+ });
176
180
  }
177
181
 
178
182
  if (goingToInvoice < left) {
@@ -184,21 +188,22 @@ export class InvoiceService {
184
188
  'a-euro': Formatter.price(-goingToInvoice),
185
189
  'name': balanceItem.getStructure().itemTitle,
186
190
  'left-euro': Formatter.price(-left),
187
- })
188
- })
191
+ }),
192
+ });
189
193
  }
190
194
  }
191
195
 
192
196
  if (left === 0) {
193
197
  if (goingToInvoice < 0) {
198
+ console.log(item);
194
199
  throw new SimpleError({
195
200
  code: 'error',
196
201
  message: 'Cannot invoice',
197
202
  human: $t('%1R7', {
198
203
  'a-euro': Formatter.price(-goingToInvoice),
199
204
  'name': balanceItem.getStructure().itemTitle,
200
- })
201
- })
205
+ }),
206
+ });
202
207
  } else if (goingToInvoice > 0) {
203
208
  throw new SimpleError({
204
209
  code: 'error',
@@ -206,8 +211,8 @@ export class InvoiceService {
206
211
  human: $t('%1QE', {
207
212
  'a-euro': Formatter.price(-goingToInvoice),
208
213
  'name': balanceItem.getStructure().itemTitle,
209
- })
210
- })
214
+ }),
215
+ });
211
216
  }
212
217
  }
213
218
 
@@ -220,8 +225,8 @@ export class InvoiceService {
220
225
  'a-euro': Formatter.price(-goingToInvoice),
221
226
  'name': balanceItem.getStructure().itemTitle,
222
227
  'left-euro': Formatter.price(left),
223
- })
224
- })
228
+ }),
229
+ });
225
230
  } else if (goingToInvoice > left) {
226
231
  throw new SimpleError({
227
232
  code: 'error',
@@ -230,8 +235,8 @@ export class InvoiceService {
230
235
  'a-euro': Formatter.price(-goingToInvoice),
231
236
  'name': balanceItem.getStructure().itemTitle,
232
237
  'left-euro': Formatter.price(left),
233
- })
234
- })
238
+ }),
239
+ });
235
240
  }
236
241
  }
237
242
 
@@ -267,16 +272,19 @@ export class InvoiceService {
267
272
  await InvoiceCounter.assignNextNumber(model, organization.privateMeta.invoiceSettings);
268
273
 
269
274
  // Update invoiced cache
270
- await BalanceItemService.updateInvoiced(struct.items.map(i => i.balanceItemId))
275
+ await BalanceItemService.updateInvoiced(struct.items.map(i => i.balanceItemId));
271
276
 
272
277
  // Create PDF
273
- await this.generatePdf(model)
274
- }
275
- catch (e) {
278
+ await InvoicePdfService.generatePdf(model);
279
+ await InvoiceXMlService.generateXml(model);
280
+
281
+ if (!await this.forwardInvoice(model, organization)) {
282
+ await this.sendCustomerEmail(model, organization);
283
+ }
284
+ } catch (e) {
276
285
  try {
277
286
  await model.delete();
278
- }
279
- catch (ee) {
287
+ } catch (ee) {
280
288
  console.error('Error while trying to delete invoice because of fail save', ee, 'Deleting because of error', e);
281
289
  }
282
290
  throw e;
@@ -285,244 +293,189 @@ export class InvoiceService {
285
293
  return model;
286
294
  }
287
295
 
288
- static async generateHtml(invoice: Invoice) {
289
- const organization = await Organization.getByID(invoice.organizationId, true)
290
- const platform = await Platform.getShared()
291
- const payments = await Payment.select().where('invoiceId', invoice.id).fetch();
292
- const payment = payments[0] ?? null;
293
-
294
- const invoicedItems = await InvoicedBalanceItem.select().where('invoiceId', invoice.id).fetch();
295
-
296
- const seller = invoice.seller;
297
- const customer = invoice.customer;
298
- const company = customer.company;
296
+ private static shouldForwardInvoice(invoice: Invoice, organization: Organization) {
297
+ if (invoice.didSendPeppol) {
298
+ return {
299
+ value: false,
300
+ };
301
+ }
299
302
 
300
- const formatAddress = (address: Address | null, includeCountry = false): string => {
301
- if (!address) {
302
- return '';
303
- }
304
- const arr = [
305
- `${address.street} ${address.number}, ${address.city}`,
306
- ];
307
- if (includeCountry) {
308
- arr.push(CountryHelper.getName(address.country))
309
- }
310
- return arr.join('\n');
311
- };
303
+ if (!invoice.customer.company?.peppolEndpointId) {
304
+ return {
305
+ value: false,
306
+ reason: 'No customer peppol endpoint id found for this invoice',
307
+ };
308
+ }
312
309
 
313
- const totalPrice = invoice.totalWithVAT;
314
- const isCreditNote = totalPrice < 0;
315
-
316
- const date = invoice.invoicedAt ?? invoice.createdAt;
317
- const isPaid = payment?.status === PaymentStatus.Succeeded;
318
- const dueDate = isPaid
319
- ? date
320
- : (invoice.dueAt ?? new Date(date.getTime() + 30 * 24 * 60 * 60 * 1000));
321
-
322
- const VATTotal = invoice.VATTotal.map(subtotal => ({
323
- VATPercentage: subtotal.VATPercentage,
324
- percentageLabel: Formatter.percentage(subtotal.VATPercentage * 100),
325
- taxablePrice: subtotal.taxablePrice,
326
- VAT: subtotal.VAT,
327
- VATExcempt: subtotal.VATExcempt,
328
- VATExcemptName: subtotal.VATExcempt ? getVATExcemptReasonName(subtotal.VATExcempt) : null,
329
- }));
330
-
331
- const hasMultipleVATRates = VATTotal.length > 1;
332
- const firstVATSubtotal = invoice.VATTotal[0] ?? null;
333
- const singleVATRateLabel = !hasMultipleVATRates && firstVATSubtotal
334
- ? Formatter.percentage(firstVATSubtotal.VATPercentage * 100)
335
- : '';
336
-
337
- let vatExcemptNote: string | null = null;
338
- if (!hasMultipleVATRates && firstVATSubtotal) {
339
- if (firstVATSubtotal.VATExcempt) {
340
- vatExcemptNote = getVATExcemptInvoiceNote(firstVATSubtotal.VATExcempt);
341
- }
342
- else if (firstVATSubtotal.VATPercentage === 0) {
343
- vatExcemptNote = $t('%1Q3');
310
+ // Legally sending an invoice via PEPPOL is not always required.
311
+ // Logic: send if required or a custom peppol endpoint id is set
312
+ if (organization.privateMeta.invoiceSettings.forwardOnlyVAT) {
313
+ if (!invoice.customer.company?.customPeppolEndpointId) {
314
+ if (!invoice.customer?.company?.VATNumber || !invoice.customer?.company?.VATNumber.startsWith('BE')) {
315
+ return {
316
+ value: false,
317
+ reason: 'Skipping PEPPOL for invoice ' + invoice.id + ', recipient no Belgian VAT number and no custom peppol endpoint id set',
318
+ };
319
+ }
344
320
  }
345
321
  }
346
322
 
347
- const trimmedVATNumber = company?.VATNumber?.replace(/\D+/g, '').replace(/^\d{0,2}/, '');
348
- const showCompanyNumber = !!company?.companyNumber
349
- && (!company?.VATNumber || company.companyNumber.replace(/\D+/g, '') !== trimmedVATNumber);
350
-
351
- const showDueDate = !!invoice.number && totalPrice >= 0;
352
- const hasRoundingAmount = invoice.payableRoundingAmount !== 0;
353
-
354
- const showPaidMessage = !!payment && payment.method !== null && payment.status === PaymentStatus.Succeeded && totalPrice >= 0;
355
- const showTransferMessage = !!payment && payment.method === PaymentMethod.Transfer && payment.status !== PaymentStatus.Succeeded && totalPrice >= 0;
356
- const showDirectDebitMessage = !!payment && payment.method === PaymentMethod.DirectDebit && payment.status !== PaymentStatus.Succeeded && totalPrice >= 0;
357
- const showStripeMessage = !payment && !!invoice.stripeAccountId && totalPrice >= 0;
358
-
359
- const customerName = customer.firstName && customer.lastName
360
- ? `${customer.firstName} ${customer.lastName}`
361
- : (customer.firstName ?? customer.lastName ?? '');
362
-
363
- const context = {
364
- // TODO: replace with hosted asset URLs
365
- logoUrl: organization.meta.horizontalLogo?.getPathForSize(400, undefined),
366
- firstPageBackgroundUrl: organization.privateMeta.invoiceSettings.background?.getPublicPath(),
367
- otherPageBackgroundUrl: organization.privateMeta.invoiceSettings.secondBackground?.getPublicPath() ?? organization.privateMeta.invoiceSettings.background?.getPublicPath(),
368
- fontSemiBoldUrl: '',
369
- fontMediumUrl: '',
370
- colors: {
371
- primary: organization.meta.color ?? platform.config.color ?? '#0053ff',
372
- },
373
-
374
- sender: {
375
- name: seller.name,
376
- address: formatAddress(seller.address, false),
377
- vatNumber: seller.VATNumber ?? '',
378
- bankAccount: organization.meta.registrationPaymentConfiguration.transferSettings.iban ?? '',
379
- },
380
-
381
- invoice: {
382
- id: invoice.id,
383
- number: invoice.number,
384
- meta: {
385
- date,
386
- items: invoicedItems.map(item => ({
387
- amount: item.quantity / 1_00_00,
388
- name: item.name,
389
- description: item.description,
390
- unitPriceExclVAT: item.unitPrice,
391
- priceExclVAT: item.totalWithoutVAT,
392
- percentageLabel: Formatter.percentage(item.VATPercentage * 100),
393
- })),
394
- priceWithoutVAT: invoice.totalWithoutVAT,
395
- VAT: invoice.VATTotalAmount,
396
- VATTotal,
397
- hasMultipleVATRates,
398
- singleVATRateLabel,
399
- vatExcemptNote,
400
- payableRoundingAmount: invoice.payableRoundingAmount,
401
- totalPrice,
402
- },
403
- customer: {
404
- name: customer.dynamicName,
405
- contactName: !!customer.company && !!customer.name ? customer.name : null,
406
- address: company?.address ? formatAddress(company.address ?? null, seller.address?.country !== company.address.country ) : null,
407
- companyNumber: company?.companyNumber ?? '',
408
- VATNumber: company?.VATNumber ?? '',
409
- },
410
- },
411
-
412
- payment: payment
413
- ? {
414
- methodName: payment.method ? PaymentMethodHelper.getName(payment.method) : '',
415
- transferDescription: payment.transferDescription ?? '',
416
- }
417
- : null,
418
-
419
- isCreditNote,
420
- showDueDate,
421
- dueDate,
422
- showCompanyNumber,
423
- hasRoundingAmount,
424
- showPaidMessage,
425
- showTransferMessage,
426
- showDirectDebitMessage,
427
- showStripeMessage,
323
+ return {
324
+ value: true,
428
325
  };
429
-
430
- const file = await fs.readFile(import.meta.dirname+'/data/invoice.hbs.html', 'utf-8')
431
- const renderedHtml = await render(file , context);
432
- return renderedHtml;
433
326
  }
434
327
 
435
- static async uploadPdf(invoice: Invoice, fileContent: Buffer) {
436
- const fileId = uuidv4();
328
+ /**
329
+ * This is used for PEPPOL.
330
+ * Invoice is forwarded to an external invoicing tool which will send it via PEPPOL.
331
+ */
332
+ static async forwardInvoice(invoice: Invoice, organization: Organization) {
333
+ const { value, reason } = this.shouldForwardInvoice(invoice, organization);
437
334
 
438
- let prefix = (STAMHOOFD.SPACES_PREFIX ?? '');
439
- if (prefix.length > 0) {
440
- prefix += '/';
335
+ if (!value) {
336
+ if (reason) {
337
+ console.log('Skipped sending PEPPOL: ', reason);
338
+ }
339
+ return;
441
340
  }
442
341
 
443
- const envPrefix = STAMHOOFD.environment !== 'production' ? STAMHOOFD.environment : null;
444
-
445
- if (envPrefix && envPrefix !== (STAMHOOFD.SPACES_PREFIX ?? '')) {
446
- prefix += envPrefix + '/';
342
+ if (!organization.privateMeta.invoiceSettings.forwardEmailHandlers.length) {
343
+ console.error('PEPPOL email handlers NOT CONFIGURED');
344
+ return;
447
345
  }
448
346
 
449
- const key = prefix + 'invoices/' + fileId + '.pdf';
347
+ const xml = await invoice.xml?.withSignedUrl();
450
348
 
451
- const fileStruct = new File({
452
- id: fileId,
453
- server: 'https://' + STAMHOOFD.SPACES_BUCKET + '.' + STAMHOOFD.SPACES_ENDPOINT,
454
- path: key,
455
- size: fileContent.byteLength,
456
- name: (invoice.number ?? invoice.id),
457
- isPrivate: true,
458
- contentType: 'application/pdf',
459
- });
349
+ if (!xml) {
350
+ console.error('Could not send PEPPOL for invoice ' + invoice.id + ', xml not set');
351
+ return;
352
+ }
460
353
 
461
- const cmd = new PutObjectCommand({
462
- Bucket: STAMHOOFD.SPACES_BUCKET,
463
- Key: key,
464
- Body: fileContent,
465
- ContentType: 'application/pdf',
466
- ACL: 'private'
354
+ // Send the e-mail
355
+ Email.send({
356
+ // From address is fixed for sender validation
357
+ from: Email.getWebmasterFromEmail(),
358
+ to: organization.privateMeta.invoiceSettings.forwardEmailHandlers.map((d) => {
359
+ return {
360
+ email: d,
361
+ };
362
+ }),
363
+ subject: $t('%1Zw') + ' ' + invoice.number,
364
+ text: $t('%1X5'),
365
+ attachments: [
366
+ {
367
+ filename: invoice.generateCustomerFilename('xml'),
368
+ href: xml.getPublicPath(),
369
+ contentType: 'application/xml',
370
+ },
371
+ ],
467
372
  });
468
- await Image.getS3Client().send(cmd);
469
373
 
470
- // Sign the structure so it is accessible
471
- if (!await fileStruct.sign()) {
472
- throw new SimpleError({
473
- code: 'failed_to_sign',
474
- message: 'Failed to sign file',
475
- human: $t('%B6'),
476
- statusCode: 500,
477
- });
478
- }
479
-
480
- return fileStruct
374
+ invoice.didSendPeppol = true;
375
+ await invoice.save();
376
+ return true;
481
377
  }
482
378
 
483
- static async generatePdf(invoice: Invoice) {
484
- const html = await this.generateHtml(invoice);
485
- if (!html) {
486
- throw new Error('Failed to render invoice ' + invoice.id)
487
- }
488
- const form = new FormData();
379
+ static async sendCustomerEmail(invoice: Invoice, organization: Organization) {
380
+ let email = (invoice.customer.email || invoice.customer.company?.administrationEmail) ?? undefined;
381
+ let bcc = invoice.customer.company?.administrationEmail;
489
382
 
490
- // File field
383
+ if (!email && invoice.payingOrganizationId) {
384
+ const payingOrganization = await Organization.getByID(invoice.payingOrganizationId);
385
+ if (payingOrganization) {
386
+ email = (await payingOrganization.getInvoicingToEmail()) ?? undefined;
387
+ }
388
+ }
491
389
 
492
- // Sign fields
493
- const cacheId = 'invoice-' + invoice.id;
494
- console.log('html length:', html.length);
495
- form.append('html', new Blob([html], { type: 'text/html' }));
496
- form.append('cacheId', cacheId);
497
- form.append('signature', signInternal(cacheId, invoice.updatedAt.getTime().toString(), html));
498
- form.append('timestamp', invoice.updatedAt.getTime().toString());
390
+ if (bcc === email) {
391
+ bcc = undefined;
392
+ }
499
393
 
500
- const controller = new AbortController();
501
- const timeout = setTimeout(() => controller.abort(), 30_000);
394
+ if (!email) {
395
+ console.log('Skipped sending invoice email: no email address found for ' + invoice.id);
396
+ return;
397
+ }
502
398
 
503
- try {
504
- // Issue with system trusted CA in development
505
- const result = await fetch((STAMHOOFD.environment === 'development' ? 'http://' : 'https://')+ STAMHOOFD.domains.rendererApi + '/v'+VERSION+'/html-to-pdf', {
506
- method: 'POST',
507
- body: form,
508
- signal: controller.signal,
509
- });
510
- if (result.status === 200) {
511
- // todo
512
- const buffer = Buffer.from(await result.arrayBuffer())
513
- const file = await this.uploadPdf(invoice, buffer);
514
- invoice.pdf = file;
515
- await invoice.save();
516
- } else {
517
- // todo
518
- }
519
- } catch (err) {
520
- if (err instanceof DOMException && err.name === 'AbortError') {
521
- throw new Error('Request timed out after 30s');
522
- }
523
- console.error(err);
524
- } finally {
525
- clearTimeout(timeout);
399
+ const payments = await Payment.select().where('invoiceId', invoice.id).fetch();
400
+ const isPaid = payments.every(p => p.status === PaymentStatus.Succeeded);
401
+
402
+ const pdf = await invoice.pdf?.withSignedUrl();
403
+ const xml = await invoice.xml?.withSignedUrl();
404
+
405
+ const recipients = [
406
+ Recipient.create({
407
+ email,
408
+ replacements: [
409
+ Replacement.create({
410
+ token: 'invoiceNumber',
411
+ value: invoice.number ?? '',
412
+ }),
413
+ Replacement.create({
414
+ token: 'totalPrice',
415
+ value: Formatter.price(Math.abs(invoice.totalWithVAT)),
416
+ }),
417
+ ],
418
+ }),
419
+ ];
420
+
421
+ if (bcc) {
422
+ recipients.push(Recipient.create({
423
+ email: bcc,
424
+ replacements: [
425
+ Replacement.create({
426
+ token: 'invoiceNumber',
427
+ value: invoice.number ?? '',
428
+ }),
429
+ Replacement.create({
430
+ token: 'totalPrice',
431
+ value: Formatter.price(Math.abs(invoice.totalWithVAT)),
432
+ }),
433
+ ],
434
+ }));
526
435
  }
436
+
437
+ await sendEmailTemplate(organization, {
438
+ recipients: [
439
+ Recipient.create({
440
+ email,
441
+ replacements: [
442
+ Replacement.create({
443
+ token: 'invoiceNumber',
444
+ value: invoice.number ?? '',
445
+ }),
446
+ Replacement.create({
447
+ token: 'totalPrice',
448
+ value: Formatter.price(Math.abs(invoice.totalWithVAT)),
449
+ }),
450
+ ],
451
+ }),
452
+ ],
453
+ template: {
454
+ type: isPaid
455
+ ? (invoice.totalWithVAT >= 0 ? EmailTemplateType.InvoicePaid : EmailTemplateType.CreditNotePaid)
456
+ : (invoice.totalWithVAT >= 0 ? EmailTemplateType.InvoiceUnpaid : EmailTemplateType.CreditNoteUnpaid),
457
+ },
458
+ type: 'transactional',
459
+ attachments: [
460
+ ...(pdf
461
+ ? [
462
+ {
463
+ filename: invoice.generateCustomerFilename('pdf'),
464
+ href: pdf.getPublicPath(),
465
+ contentType: 'application/pdf',
466
+ },
467
+ ]
468
+ : []),
469
+ ...(xml
470
+ ? [
471
+ {
472
+ filename: invoice.generateCustomerFilename('xml'),
473
+ href: xml.getPublicPath(),
474
+ contentType: 'application/xml',
475
+ },
476
+ ]
477
+ : []),
478
+ ],
479
+ });
527
480
  }
528
481
  }