@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
@@ -0,0 +1,424 @@
1
+ import { PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import { SimpleError } from '@simonbackx/simple-errors';
3
+ import type { Invoice } from '@stamhoofd/models';
4
+ import { Image, InvoicedBalanceItem, Organization, Payment } from '@stamhoofd/models';
5
+ import { File, getPeppolCategoryCode, getVATExcemptInvoiceNote, PaymentMethod, PaymentStatus } from '@stamhoofd/structures';
6
+ import { Country } from '@stamhoofd/types/Country';
7
+ import { Formatter, STMath } from '@stamhoofd/utility';
8
+ import { v4 as uuidv4 } from 'uuid';
9
+ import { InvoicePdfService } from './InvoicePdfService.js';
10
+
11
+ export class InvoiceXMlService {
12
+ static async buildXml(invoice: Invoice) {
13
+ if (!invoice.number || !invoice.invoicedAt) {
14
+ throw new Error('Cannot generate UBL for invoice without number');
15
+ }
16
+
17
+ if (!invoice.pdf) {
18
+ throw new Error('Cannot generate UBL for invoice without pdf');
19
+ }
20
+
21
+ const company = invoice.customer.company;
22
+
23
+ if (!company || !company.address) {
24
+ throw new Error('Cannot generate UBL for invoice without company');
25
+ }
26
+
27
+ const seller = invoice.seller;
28
+ if (!seller.address) {
29
+ throw new Error('Missing seller address');
30
+ }
31
+
32
+ const sellerPeppolEndpointId = seller.peppolEndpointId;
33
+ const sellerPeppolCompanyId = seller.peppolCompanyId;
34
+
35
+ const customerPeppolEndpointId = company.peppolEndpointId;
36
+ const customerPeppolCompanyId = company.peppolCompanyId;
37
+
38
+ if (!sellerPeppolEndpointId || !sellerPeppolCompanyId) {
39
+ throw new Error('Missing seller peppol id');
40
+ }
41
+
42
+ if (!customerPeppolEndpointId || !customerPeppolCompanyId) {
43
+ throw new Error('Missing customer peppol id');
44
+ }
45
+
46
+ const companyNumberOrVAT = company.VATNumber ?? company.companyNumber;
47
+ if (!companyNumberOrVAT) {
48
+ throw new Error('Cannot generate UBL for invoice without VAT or company number');
49
+ }
50
+
51
+ if (company.address.country !== Country.Belgium) {
52
+ throw new Error('Cannot generate UBL for invoice outside belgium');
53
+ }
54
+ const companyNumber = (company.VATNumber ? company.VATNumber.substring(2) : companyNumberOrVAT).replace(/\D+/g, '');
55
+
56
+ const payments = await Payment.select().where('invoiceId', invoice.id).fetch();
57
+ const payment = payments[0] ?? null;
58
+
59
+ const pdfBuffer = await InvoicePdfService.downloadPdf(invoice);
60
+ if (!pdfBuffer) {
61
+ throw new Error('Pdf unavailable');
62
+ }
63
+
64
+ const type = invoice.totalWithVAT < 0 ? 'CreditNote' : 'Invoice';
65
+ const multiplyAmounts = invoice.totalWithVAT < 0 ? -1 : 1;
66
+
67
+ let customerEmail: string | null = company.administrationEmail ?? invoice.customer.email ?? null;
68
+ if (!customerEmail && invoice.payingOrganizationId) {
69
+ const payingOrganization = await Organization.getByID(invoice.payingOrganizationId);
70
+ if (payingOrganization) {
71
+ customerEmail = (await payingOrganization.getInvoicingToEmail()) ?? null;
72
+ }
73
+ }
74
+
75
+ let ubl = ``;
76
+
77
+ function esc(a: string) {
78
+ return Formatter.escapeHtml(a);
79
+ }
80
+
81
+ function price(price: number, options?: { invert?: boolean; round?: boolean }) {
82
+ if (options?.round ?? true) {
83
+ return esc((STMath.round((options?.invert ? multiplyAmounts : 1) * price / 100) / 100).toFixed(2));
84
+ }
85
+ return esc(((options?.invert ? multiplyAmounts : 1) * price / 100_00).toString());
86
+ }
87
+
88
+ // Docs: https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/
89
+ // Note: order is important
90
+
91
+ // General
92
+ ubl += `<cbc:ID>${esc(invoice.number)}</cbc:ID>`;
93
+ const date = invoice.invoicedAt;
94
+ ubl += `<cbc:IssueDate>${esc(Formatter.dateIso(date))}</cbc:IssueDate>`;
95
+
96
+ // PEPPOL allows credit notes as negative invoices, so we always use invoice type code = invoice
97
+
98
+ if (type === 'Invoice') {
99
+ ubl += `<cbc:DueDate>${esc(Formatter.dateIso(new Date(date.getTime() + 1000 * 60 * 60 * 24 * 30)))}</cbc:DueDate>`;
100
+ ubl += `<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>`;
101
+ } else {
102
+ ubl += `<cbc:CreditNoteTypeCode>381</cbc:CreditNoteTypeCode>`;
103
+ }
104
+
105
+ ubl += `<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>`;
106
+ ubl += `<cbc:BuyerReference>${esc(invoice.organizationId ?? invoice.id)}</cbc:BuyerReference>`;
107
+
108
+ // Attachments
109
+ const filename = invoice.generateCustomerFilename('pdf');
110
+ const base64 = pdfBuffer.toString('base64'); // No escaping needed for invoice charset
111
+ ubl += `
112
+ <cac:AdditionalDocumentReference>
113
+ <cbc:ID>${esc(filename)}</cbc:ID>
114
+ <cac:Attachment>
115
+ <cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="${esc(filename)}">${base64}</cbc:EmbeddedDocumentBinaryObject>
116
+ </cac:Attachment>
117
+ </cac:AdditionalDocumentReference>`;
118
+
119
+ // Supplier
120
+ ubl += `
121
+ <cac:AccountingSupplierParty>
122
+ <cac:Party>
123
+ <cbc:EndpointID schemeID="${esc(sellerPeppolEndpointId.schemeID)}">${esc(sellerPeppolEndpointId.id)}</cbc:EndpointID>
124
+ <cac:PartyName>
125
+ <cbc:Name>${esc(seller.name)}</cbc:Name>
126
+ </cac:PartyName>
127
+ <cac:PostalAddress>
128
+ <cbc:StreetName>${esc(seller.address.street)} ${esc(seller.address.number)}</cbc:StreetName>
129
+ <cbc:CityName>${esc(seller.address.city)}</cbc:CityName>
130
+ <cbc:PostalZone>${esc(seller.address.postalCode)}</cbc:PostalZone>
131
+ <cac:Country>
132
+ <cbc:IdentificationCode>${esc(seller.address.country)}</cbc:IdentificationCode>
133
+ </cac:Country>
134
+ </cac:PostalAddress>
135
+ ${
136
+ seller.VATNumber
137
+ ? `
138
+ <cac:PartyTaxScheme>
139
+ <cbc:CompanyID>${esc(seller.VATNumber)}</cbc:CompanyID>
140
+ <cac:TaxScheme>
141
+ <cbc:ID>VAT</cbc:ID>
142
+ </cac:TaxScheme>
143
+ </cac:PartyTaxScheme>`
144
+ : ``
145
+ }
146
+ <cac:PartyLegalEntity>
147
+ <cbc:RegistrationName>${esc(seller.name)}</cbc:RegistrationName>
148
+ <cbc:CompanyID schemeID="${esc(sellerPeppolCompanyId.schemeID)}">${esc(sellerPeppolCompanyId.id)}</cbc:CompanyID>
149
+ </cac:PartyLegalEntity>
150
+ ${
151
+ seller.administrationEmail
152
+ ? `
153
+ <cac:Contact>
154
+ <cbc:ElectronicMail>${esc(seller.administrationEmail)}</cbc:ElectronicMail>
155
+ </cac:Contact>`
156
+ : ``
157
+ }
158
+ </cac:Party>
159
+ </cac:AccountingSupplierParty>`;
160
+
161
+ /* const vatUbl = invoice.meta.companyVATNumber
162
+ ? `<cac:PartyTaxScheme>
163
+ <cbc:CompanyID>${esc(invoice.meta.companyVATNumber.replace(/[^A-z0-9]+/g, ''))}</cbc:CompanyID>
164
+ <cac:TaxScheme>
165
+ <cbc:ID>VAT</cbc:ID>
166
+ </cac:TaxScheme>
167
+ </cac:PartyTaxScheme>`
168
+ : ``;
169
+
170
+ const contactUbl = customerEmail
171
+ ? `<cac:Contact>
172
+ <cbc:ElectronicMail>${esc(customerEmail)}</cbc:ElectronicMail>
173
+ </cac:Contact>`
174
+ : ''; */
175
+
176
+ // Customer
177
+ ubl += `
178
+ <cac:AccountingCustomerParty>
179
+ <cac:Party>
180
+ <cbc:EndpointID schemeID="${esc(customerPeppolEndpointId.schemeID)}">${esc(customerPeppolEndpointId.id)}</cbc:EndpointID>
181
+ <cac:PartyName>
182
+ <cbc:Name>${esc(company.name)}</cbc:Name>
183
+ </cac:PartyName>
184
+ <cac:PostalAddress>
185
+ <cbc:StreetName>${esc(company.address.street)} ${esc(company.address.number)}</cbc:StreetName>
186
+ <cbc:CityName>${esc(company.address.city)}</cbc:CityName>
187
+ <cbc:PostalZone>${esc(company.address.postalCode)}</cbc:PostalZone>
188
+ <cac:Country>
189
+ <cbc:IdentificationCode>${esc(company.address.country)}</cbc:IdentificationCode>
190
+ </cac:Country>
191
+ </cac:PostalAddress>
192
+ ${
193
+ company.VATNumber
194
+ ? `
195
+ <cac:PartyTaxScheme>
196
+ <cbc:CompanyID>${esc(company.VATNumber)}</cbc:CompanyID>
197
+ <cac:TaxScheme>
198
+ <cbc:ID>VAT</cbc:ID>
199
+ </cac:TaxScheme>
200
+ </cac:PartyTaxScheme>`
201
+ : ``
202
+ }
203
+ <cac:PartyLegalEntity>
204
+ <cbc:RegistrationName>${esc(company.name)}</cbc:RegistrationName>
205
+ <cbc:CompanyID schemeID="0208">${esc(companyNumber)}</cbc:CompanyID>
206
+ </cac:PartyLegalEntity>
207
+ ${
208
+ customerEmail
209
+ ? `
210
+ <cac:Contact>
211
+ <cbc:ElectronicMail>${esc(customerEmail)}</cbc:ElectronicMail>
212
+ </cac:Contact>`
213
+ : ``
214
+ }
215
+ </cac:Party>
216
+ </cac:AccountingCustomerParty>`;
217
+
218
+ // Payment means
219
+ // Codes:
220
+ // 49 = direct debit
221
+ // 48 = bank card
222
+ // 54 = credit card
223
+ // 55 = debit card
224
+ // 59 = SEPA direct debit
225
+ // 68 = Online payment service <- probably way to go for all except tranfer
226
+ // 30 = credit transfer (Payment by credit movement of funds from one account to another.) <- way to go for
227
+
228
+ if (!payment) {
229
+ // Het bedrag werd reeds ingehouden van jouw Stripe balans.
230
+ // Don't include payment means information
231
+ } else {
232
+ if (invoice.totalWithVAT < 0) {
233
+ // todo: add how we are going to pay back
234
+ // 99% chance we already have paid back using a refund
235
+ // in other cases we might transfer it manually or it will not have been paid already
236
+ } else {
237
+ if (payment.method === PaymentMethod.Transfer) {
238
+ ubl += `<cac:PaymentMeans>
239
+ <cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
240
+ <cbc:PaymentID>${esc(payment.transferDescription ?? '')}</cbc:PaymentID>
241
+ <cac:PayeeFinancialAccount>
242
+ <cbc:ID>BE93733058873067</cbc:ID>
243
+ <cbc:Name>Stamhoofd</cbc:Name>
244
+ </cac:PayeeFinancialAccount>
245
+ </cac:PaymentMeans>`;
246
+ } else if (payment.status === PaymentStatus.Succeeded) {
247
+ const code = 48; // card - don't specify more
248
+
249
+ ubl += `<cac:PaymentMeans>
250
+ <cbc:PaymentMeansCode>${esc(code.toFixed(0))}</cbc:PaymentMeansCode>
251
+ </cac:PaymentMeans>`;
252
+ }
253
+ }
254
+ }
255
+
256
+ // Tax breakdown
257
+ ubl += `<cac:TaxTotal>
258
+ <cbc:TaxAmount currencyID="EUR">${price(invoice.VATTotalAmount)}</cbc:TaxAmount>`;
259
+
260
+ if (invoice.VATTotal.length > 0) {
261
+ for (const c of invoice.VATTotal) {
262
+ ubl += `<cac:TaxSubtotal>
263
+ <cbc:TaxableAmount currencyID="EUR">${price(c.taxablePrice)}</cbc:TaxableAmount>
264
+ <cbc:TaxAmount currencyID="EUR">${price(c.VAT)}</cbc:TaxAmount>
265
+ <cac:TaxCategory>
266
+ <cbc:ID>${esc(c.peppolCategoryCode)}</cbc:ID>
267
+ <cbc:Percent>${esc(c.VATPercentage.toFixed(2))}</cbc:Percent>
268
+ ${c.VATExcempt ? `<cbc:TaxExemptionReasonCode>${esc(getVATExcemptInvoiceNote(c.VATExcempt))}</cbc:TaxExemptionReasonCode>` : ``}
269
+ ${c.VATExcempt ? `<cbc:TaxExemptionReason>${esc(getVATExcemptInvoiceNote(c.VATExcempt))}</cbc:TaxExemptionReason>` : ``}
270
+ <cac:TaxScheme>
271
+ <cbc:ID>VAT</cbc:ID>
272
+ </cac:TaxScheme>
273
+ </cac:TaxCategory>
274
+ </cac:TaxSubtotal>`;
275
+ }
276
+ }
277
+
278
+ ubl += `</cac:TaxTotal>`;
279
+
280
+ // Totals
281
+ // Since sometimes we have invoices that start with a given paid amount, and prices inclusive VAT, we can have rounding issues.
282
+ // In UBL, prices are always exclusive VAT.
283
+ // so start with calculating the rounding error.
284
+ // PayableRoundingAmount
285
+ ubl += `<cac:LegalMonetaryTotal>
286
+ <cbc:LineExtensionAmount currencyID="EUR">${price(invoice.totalWithoutVAT)}</cbc:LineExtensionAmount>
287
+ <cbc:TaxExclusiveAmount currencyID="EUR">${price(invoice.totalWithoutVAT)}</cbc:TaxExclusiveAmount>
288
+ <cbc:TaxInclusiveAmount currencyID="EUR">${price(invoice.totalWithVAT - invoice.payableRoundingAmount)}</cbc:TaxInclusiveAmount>
289
+ <cbc:PrepaidAmount currencyID="EUR">${!payment || payment.status === PaymentStatus.Succeeded ? price(invoice.totalWithVAT) : '0'}</cbc:PrepaidAmount>
290
+ <cbc:PayableRoundingAmount currencyID="EUR">${price(invoice.payableRoundingAmount)}</cbc:PayableRoundingAmount>
291
+ <cbc:PayableAmount currencyID="EUR">${!payment || payment.status === PaymentStatus.Succeeded ? '0' : price(invoice.totalWithVAT)}</cbc:PayableAmount>
292
+ </cac:LegalMonetaryTotal>`;
293
+
294
+ // Invoice lines
295
+ const invoicedItems = await InvoicedBalanceItem.select().where('invoiceId', invoice.id).fetch();
296
+
297
+ for (const item of invoicedItems) {
298
+ // We need to show prices exluding VAT and round here if needed
299
+ let unitPrice = item.unitPrice;
300
+ const totalPrice = item.totalWithoutVAT;
301
+ let quantity = item.quantity;
302
+ //
303
+ // if (invoice.meta.areItemsIncludingVAT) {
304
+ // unitPrice = invoice.meta.includingVATToExcludingVAT(unitPrice)
305
+ // price = invoice.meta.includingVATToExcludingVAT(price)
306
+ // }
307
+
308
+ // unitPrice cant be negative, quantity can.
309
+ if (unitPrice < 0) {
310
+ quantity = -quantity;
311
+ unitPrice = -unitPrice;
312
+ }
313
+
314
+ // Update sign for credit notes
315
+ quantity = quantity * multiplyAmounts;
316
+
317
+ ubl += `
318
+ <cac:${type}Line>
319
+ <cbc:ID>${item.id}</cbc:ID>
320
+ <cbc:${type === 'Invoice' ? 'Invoiced' : 'Credited'}Quantity unitCode="EA">${(quantity / 100_00).toString()}</cbc:${type === 'Invoice' ? 'Invoiced' : 'Credited'}Quantity>
321
+ <cbc:LineExtensionAmount currencyID="EUR">${price(totalPrice)}</cbc:LineExtensionAmount>
322
+ <cac:Item>
323
+ ${item.description ? `<cbc:Description>${esc(item.description)}</cbc:Description>` : ''}
324
+ <cbc:Name>${esc(item.name)}</cbc:Name>
325
+ <cac:ClassifiedTaxCategory>
326
+ <cbc:ID>${esc(getPeppolCategoryCode(item))}</cbc:ID>
327
+ <cbc:Percent>${esc(item.VATPercentage.toFixed(2))}</cbc:Percent>
328
+ <cac:TaxScheme>
329
+ <cbc:ID>VAT</cbc:ID>
330
+ </cac:TaxScheme>
331
+ </cac:ClassifiedTaxCategory>
332
+ </cac:Item>
333
+ <cac:Price>
334
+ <cbc:PriceAmount currencyID="EUR">${price(unitPrice, { invert: false, round: false })}</cbc:PriceAmount>
335
+ </cac:Price>
336
+ </cac:${type}Line>`;
337
+ }
338
+
339
+ return `<?xml version="1.0" encoding="UTF-8"?>
340
+ <${type} xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:${type}-2">
341
+ <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
342
+ <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
343
+ ${ubl}
344
+ </${type}>`;
345
+ }
346
+
347
+ static async uploadXml(invoice: Invoice, fileContent: Buffer) {
348
+ const fileId = uuidv4();
349
+
350
+ let prefix = (STAMHOOFD.SPACES_PREFIX ?? '');
351
+ if (prefix.length > 0) {
352
+ prefix += '/';
353
+ }
354
+
355
+ const envPrefix = STAMHOOFD.environment !== 'production' ? STAMHOOFD.environment : null;
356
+
357
+ if (envPrefix && envPrefix !== (STAMHOOFD.SPACES_PREFIX ?? '')) {
358
+ prefix += envPrefix + '/';
359
+ }
360
+
361
+ const key = prefix + 'invoices/' + fileId + '.xml';
362
+
363
+ const fileStruct = new File({
364
+ id: fileId,
365
+ server: 'https://' + STAMHOOFD.SPACES_BUCKET + '.' + STAMHOOFD.SPACES_ENDPOINT,
366
+ path: key,
367
+ size: fileContent.byteLength,
368
+ name: (invoice.number ?? invoice.id),
369
+ isPrivate: true,
370
+ contentType: 'application/xml',
371
+ });
372
+
373
+ const cmd = new PutObjectCommand({
374
+ Bucket: STAMHOOFD.SPACES_BUCKET,
375
+ Key: key,
376
+ Body: fileContent,
377
+ ContentType: 'application/xml',
378
+ ACL: 'private',
379
+ });
380
+ await Image.getS3Client().send(cmd);
381
+
382
+ // Sign the structure so it is accessible
383
+ if (!await fileStruct.sign()) {
384
+ throw new SimpleError({
385
+ code: 'failed_to_sign',
386
+ message: 'Failed to sign file',
387
+ human: $t('%B6'),
388
+ statusCode: 500,
389
+ });
390
+ }
391
+
392
+ return fileStruct;
393
+ }
394
+
395
+ static async generateXml(invoice: Invoice) {
396
+ if (invoice.didSendPeppol) {
397
+ // Can't update if already generated/sent
398
+ return;
399
+ }
400
+
401
+ if (invoice.customer.company?.VATNumber === null) {
402
+ console.log('Skipping PEPPOL for invoice ' + invoice.id + ', recipient not subject to VAT');
403
+ return;
404
+ }
405
+
406
+ // Check VAT number is belgian
407
+ if (!invoice.customer.company?.VATNumber.startsWith('BE')) {
408
+ console.log('Skipping PEPPOL for invoice ' + invoice.id + ', recipient outside Belgium');
409
+ return;
410
+ }
411
+
412
+ try {
413
+ const content = await this.buildXml(invoice);
414
+
415
+ const file = await this.uploadXml(invoice, Buffer.from(content, 'utf-8'));
416
+ invoice.xml = file;
417
+
418
+ await invoice.save();
419
+ } catch (e) {
420
+ console.error('Failed to generate XML', invoice.id, e);
421
+ return;
422
+ }
423
+ }
424
+ }
@@ -1,5 +1,5 @@
1
1
  import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
2
- import type { Member, MemberPlatformMembership} from '@stamhoofd/models';
2
+ import type { Member, MemberPlatformMembership } from '@stamhoofd/models';
3
3
  import { Organization } from '@stamhoofd/models';
4
4
  import { QueueHandler } from '@stamhoofd/queues';
5
5
  import { scalarToSQLExpression, SQL, SQLCharLength, SQLWhereLike } from '@stamhoofd/sql';
@@ -28,12 +28,10 @@ export class MemberNumberService {
28
28
 
29
29
  member.details.memberNumber = memberNumber;
30
30
  await member.save();
31
- }
32
- catch (error) {
31
+ } catch (error) {
33
32
  if (isSimpleError(error) || isSimpleErrors(error)) {
34
33
  throw error;
35
- }
36
- else {
34
+ } else {
37
35
  console.error(error);
38
36
  throw new SimpleError({
39
37
  code: 'assign_member_number',
@@ -48,8 +46,7 @@ export class MemberNumberService {
48
46
  private static async createMemberNumber(member: Member, membership: MemberPlatformMembership): Promise<string | undefined> {
49
47
  if (STAMHOOFD.MEMBER_NUMBER_ALGORITHM === MemberNumberAlgorithm.Incremental) {
50
48
  return this.createIncrementalMemberNumber();
51
- }
52
- else if (STAMHOOFD.MEMBER_NUMBER_ALGORITHM === MemberNumberAlgorithm.KSA) {
49
+ } else if (STAMHOOFD.MEMBER_NUMBER_ALGORITHM === MemberNumberAlgorithm.KSA) {
53
50
  return this.createKSAMemberNumber(member, membership);
54
51
  }
55
52
  }
@@ -128,8 +125,7 @@ export class MemberNumberService {
128
125
  human: $t(`%Fy`),
129
126
  });
130
127
  }
131
- }
132
- else {
128
+ } else {
133
129
  doesExist = false;
134
130
  }
135
131
  }
@@ -153,8 +149,7 @@ export class MemberNumberService {
153
149
 
154
150
  if (this.largestMemberNumberCache !== null) {
155
151
  nextNumber = this.largestMemberNumberCache + 1;
156
- }
157
- else {
152
+ } else {
158
153
  // Find largest member number in the database
159
154
  // The required length prevents string-sorting with different lengths, causing an unexpected order
160
155
  const query = await SQL.select('memberNumber')
@@ -11,7 +11,7 @@ export type RecordCacheEntry = { record: RecordSettings; rootCategoryId: string;
11
11
  * - It allows fast retrieving of record settings by id (for permission checking)
12
12
  */
13
13
  class MemberRecordStoreService {
14
- private cache = new Map<string, RecordCacheEntry>();
14
+ private cache = new Map<string, RecordCacheEntry>();
15
15
 
16
16
  init() {
17
17
  // Should only be used in userMode platform
@@ -23,9 +23,9 @@ class MemberRecordStoreService {
23
23
  this.loadIfNeeded().catch(console.error);
24
24
  }
25
25
 
26
- private listening = false;
26
+ private listening = false;
27
27
 
28
- private listen() {
28
+ private listen() {
29
29
  // Create listeners to update data as organizations and platform is updated
30
30
  if (this.listening) {
31
31
  return;
@@ -86,8 +86,8 @@ class MemberRecordStoreService {
86
86
  });
87
87
  }
88
88
 
89
- private _loadAllPromise: Promise<void> | null = null;
90
- private _didLoadAll = false;
89
+ private _loadAllPromise: Promise<void> | null = null;
90
+ private _didLoadAll = false;
91
91
 
92
92
  private async loadIfNeeded() {
93
93
  if (this._didLoadAll) {
@@ -96,8 +96,7 @@ class MemberRecordStoreService {
96
96
 
97
97
  if (this._loadAllPromise) {
98
98
  await this._loadAllPromise;
99
- }
100
- else {
99
+ } else {
101
100
  if (STAMHOOFD.userMode !== 'platform') {
102
101
  // Important to throw an error because in tests the userMode can change in between tests
103
102
  throw new Error('Should only be loaded in userMode platform.');
@@ -106,8 +105,7 @@ class MemberRecordStoreService {
106
105
  this._loadAllPromise = (async () => {
107
106
  try {
108
107
  await this.loadAll();
109
- }
110
- catch (e) {
108
+ } catch (e) {
111
109
  // Failed to load
112
110
  this._loadAllPromise = null;
113
111
  throw e;
@@ -164,7 +162,7 @@ class MemberRecordStoreService {
164
162
  this.listen();
165
163
  }
166
164
 
167
- async getRecord(id: string): Promise<RecordCacheEntry | null> {
165
+ async getRecord(id: string): Promise<RecordCacheEntry | null> {
168
166
  await this.loadIfNeeded();
169
167
  return this.cache.get(id) ?? null;
170
168
  }