@stamhoofd/backend 2.120.6 → 2.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/output-2.txt +1 -0
  2. package/output.txt +1 -0
  3. package/package.json +70 -53
  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 +46 -0
  8. package/src/audit-logs/init.ts +9 -1
  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 +7 -0
  19. package/src/crons/invoices.ts +160 -0
  20. package/src/crons/members-fees.ts +50 -0
  21. package/src/crons/mollie-chargebacks.ts +86 -0
  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 +54 -74
  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 +87 -46
  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 +51 -0
  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 +32 -13
  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 +38 -33
  122. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
  123. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
  124. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
  125. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
  126. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
  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 +19 -6
  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/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
  142. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
  143. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
  144. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
  145. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
  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 +12 -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 +311 -0
  152. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
  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 +15 -4
  168. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
  169. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
  170. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -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 +67 -20
  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 +24 -17
  185. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
  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 +32 -22
  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 +29 -8
  215. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
  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 +15 -13
  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 +28 -50
  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 +60 -65
  246. package/src/helpers/AuthenticatedStructures.ts +153 -51
  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 +21 -0
  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 +741 -0
  268. package/src/helpers/RecordAnswerHelper.ts +116 -0
  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 +15 -38
  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 +11 -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/migrations/1750090030-records-configuration.ts +92 -0
  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 +95 -27
  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/1779121239-default-invoice-email-template.sql +3 -0
  300. package/src/migrations/1779121240-invoice-email-template.sql +6 -0
  301. package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
  302. package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
  303. package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
  304. package/src/migrations/1780933598-document-groups.ts +88 -0
  305. package/src/migrations/1781692342-balance-item-relations.ts +13 -0
  306. package/src/migrations/1781857866-platform-admins.ts +103 -0
  307. package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
  308. package/src/seeds/0000000001-throw.ts +5 -0
  309. package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
  310. package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
  311. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
  312. package/src/seeds/1773754928-force-save-members.ts +41 -8
  313. package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
  314. package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
  315. package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
  316. package/src/seeds/1780915002-update-orders.ts +39 -0
  317. package/src/seeds/1782047708-organization-company-address.ts +44 -0
  318. package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
  319. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
  320. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
  321. package/src/services/AuditLogService.test.ts +51 -0
  322. package/src/services/AuditLogService.ts +45 -0
  323. package/src/services/BalanceItemService.ts +148 -55
  324. package/src/services/CpuService.ts +3 -6
  325. package/src/services/DatabaseCollationService.test.ts +1 -2
  326. package/src/services/DocumentService.ts +1 -2
  327. package/src/services/EventNotificationService.ts +1 -1
  328. package/src/services/FileSignService.ts +2 -4
  329. package/src/services/InvoicePdfService.ts +286 -0
  330. package/src/services/InvoiceService.ts +330 -77
  331. package/src/services/InvoiceXMLService.ts +424 -0
  332. package/src/services/MemberNumberService.ts +6 -11
  333. package/src/services/MemberRecordStore.ts +8 -10
  334. package/src/services/MollieService.ts +530 -0
  335. package/src/services/PaymentMandateService.ts +214 -0
  336. package/src/services/PaymentReallocationService.test.ts +1 -1
  337. package/src/services/PaymentReallocationService.ts +7 -9
  338. package/src/services/PaymentService.ts +820 -328
  339. package/src/services/PlatformMembershipService.ts +5 -9
  340. package/src/services/ReferralService.ts +223 -0
  341. package/src/services/RegistrationService.ts +125 -54
  342. package/src/services/SSOService.test.ts +119 -0
  343. package/src/services/SSOService.ts +56 -49
  344. package/src/services/STPackageService.ts +97 -68
  345. package/src/services/UniqueMemberNumberService.ts +69 -0
  346. package/src/services/UniqueUserService.ts +3 -6
  347. package/src/services/VATService.ts +85 -0
  348. package/src/services/data/invoice.hbs.html +686 -0
  349. package/src/services/uitpas/PassholderEndpoints.ts +3 -6
  350. package/src/services/uitpas/UitpasService.ts +6 -10
  351. package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
  352. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
  353. package/src/services/uitpas/searchUitpasEvents.ts +1 -2
  354. package/src/sql-filters/audit-logs.ts +1 -1
  355. package/src/sql-filters/balance-item-payments.ts +1 -1
  356. package/src/sql-filters/balance-items.ts +1 -1
  357. package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
  358. package/src/sql-filters/document-templates.ts +1 -1
  359. package/src/sql-filters/documents.ts +1 -1
  360. package/src/sql-filters/email-recipients.ts +1 -1
  361. package/src/sql-filters/emails.ts +1 -1
  362. package/src/sql-filters/event-notifications.ts +1 -1
  363. package/src/sql-filters/events.ts +6 -1
  364. package/src/sql-filters/groups.ts +16 -1
  365. package/src/sql-filters/invoiced-balance-items.ts +1 -1
  366. package/src/sql-filters/invoices.ts +1 -1
  367. package/src/sql-filters/member-responsibility-records.ts +1 -1
  368. package/src/sql-filters/members.ts +1 -1
  369. package/src/sql-filters/orders.ts +4 -4
  370. package/src/sql-filters/organization-registration-periods.ts +1 -1
  371. package/src/sql-filters/organizations.ts +1 -1
  372. package/src/sql-filters/payments.ts +5 -0
  373. package/src/sql-filters/platform-memberships.ts +3 -3
  374. package/src/sql-filters/receivable-balances.ts +1 -1
  375. package/src/sql-filters/registration-invitations.ts +90 -0
  376. package/src/sql-filters/registration-periods.ts +1 -1
  377. package/src/sql-filters/registrations.ts +1 -1
  378. package/src/sql-filters/tickets.ts +1 -1
  379. package/src/sql-filters/users.ts +1 -1
  380. package/src/sql-sorters/groups.ts +23 -0
  381. package/src/sql-sorters/platform-memberships.ts +16 -16
  382. package/src/sql-sorters/registration-invitations.ts +36 -0
  383. package/tests/actions/patchOrganizationMember.ts +1 -1
  384. package/tests/actions/patchUserMember.ts +1 -1
  385. package/tests/assertions/assertBalances.ts +6 -12
  386. package/tests/e2e/api-rate-limits.test.ts +12 -24
  387. package/tests/e2e/bundle-discounts.test.ts +4 -1
  388. package/tests/e2e/charge-members.test.ts +13 -11
  389. package/tests/e2e/documents.test.ts +4 -1
  390. package/tests/e2e/register.test.ts +7 -9
  391. package/tests/e2e/stock.test.ts +4 -3
  392. package/tests/e2e/tickets.test.ts +1 -1
  393. package/tests/helpers/MollieMocker.ts +462 -0
  394. package/tests/helpers/PayconiqMocker.ts +138 -45
  395. package/tests/helpers/StripeMocker.ts +9 -6
  396. package/tests/helpers/index.ts +4 -0
  397. package/tests/init/initAdmin.ts +2 -2
  398. package/tests/init/initBundleDiscount.ts +1 -1
  399. package/tests/init/initMembershipOrganization.ts +13 -0
  400. package/tests/init/initPermissionRole.ts +3 -4
  401. package/tests/vitest.global.setup.ts +1 -1
  402. package/vitest.config.js +7 -0
  403. package/.nvmrc +0 -1
  404. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
  405. package/src/helpers/GroupBuilder.ts +0 -418
  406. package/src/seeds/0000000000-example.ts +0 -11
  407. package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
  408. package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
  409. package/src/seeds/1726847064-setup-steps.ts +0 -16
  410. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
  411. package/src/seeds/1729253172-update-orders.ts +0 -21
  412. package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
  413. package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
  414. package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
  415. package/src/seeds/1740046783-update-membership.ts +0 -17
  416. package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
  417. package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
  418. package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
  419. package/src/seeds/1750090030-records-configuration.ts +0 -26
  420. package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
  421. package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
  422. package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
  423. package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
  424. package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
  425. package/src/seeds/1756303697-update-email-counts.ts +0 -76
  426. package/src/seeds/1761665607-sync-member-users.ts +0 -59
  427. package/src/seeds/1765896674-document-update-year.ts +0 -66
  428. /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
  429. /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
  430. /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
  431. /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
  432. /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
  433. /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
  434. /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
@@ -0,0 +1,600 @@
1
+ import {
2
+ BalanceItem,
3
+ Document,
4
+ Member,
5
+ MemberPlatformMembership,
6
+ MemberResponsibilityRecord,
7
+ MergedMember,
8
+ Registration,
9
+ User,
10
+ } from '@stamhoofd/models';
11
+ import { QueryableModel, SQL } from '@stamhoofd/sql';
12
+ import type {
13
+ Address,
14
+ BooleanStatus,
15
+ MemberDetails,
16
+ Parent,
17
+ RecordAnswer,
18
+ UitpasNumberDetails,
19
+ } from '@stamhoofd/structures';
20
+ import {
21
+ Gender,
22
+ ParentType,
23
+ } from '@stamhoofd/structures';
24
+ import { Formatter } from '@stamhoofd/utility';
25
+ import { PlatformMembershipService } from '../services/PlatformMembershipService.js';
26
+ import { RegistrationService } from '../services/RegistrationService.js';
27
+ import { MemberUserSyncer } from './MemberUserSyncer.js';
28
+
29
+ export async function mergeMultipleMembers(members: Member[]) {
30
+ const { base, others } = selectBaseMember(members);
31
+
32
+ if (!base.existsInDatabase) {
33
+ throw Error('Base member does not exist in database');
34
+ }
35
+
36
+ for (const other of others) {
37
+ await mergeTwoMembers(base, other);
38
+ }
39
+ }
40
+
41
+ export async function findEqualMembers({
42
+ firstName,
43
+ lastName,
44
+ birthDay,
45
+ }: {
46
+ firstName: string;
47
+ lastName: string;
48
+ birthDay: string;
49
+ }): Promise<Member[]> {
50
+ return await Member.where({
51
+ firstName,
52
+ lastName,
53
+ birthDay,
54
+ });
55
+ }
56
+
57
+ export async function mergeTwoMembers(base: Member, other: Member, options?: { updateDocuments?: boolean }): Promise<void> {
58
+ console.log('Merging two member', base.id, other.id, base.details.name, other.details.name);
59
+
60
+ if (base.id === other.id) {
61
+ throw new Error('Cannot merge the same member');
62
+ }
63
+
64
+ if (!base.existsInDatabase) {
65
+ throw new Error('Cannot merge to base member that does not exist in database');
66
+ }
67
+
68
+ mergeMemberDetails(base, other);
69
+
70
+ if (other.existsInDatabase) {
71
+ await mergeRegistrations(base, other);
72
+ await mergeUsers(base, other);
73
+ await mergeResponsibilities(base, other);
74
+ await mergeBalanceItems(base, other);
75
+ await mergeDocuments(base, other);
76
+ await mergeMemberPlatformMemberships(base, other);
77
+ }
78
+
79
+ // Force review of all details
80
+ base.details.reviewTimes.clearAll();
81
+ base.details.cleanData();
82
+
83
+ if (other.existsInDatabase && other.details.memberNumber && other.details.memberNumber === base.details.memberNumber) {
84
+ // Clear member number first, because that will cause duplicate member number mysql errors
85
+ other.details.memberNumber = null;
86
+ await other.save();
87
+ }
88
+
89
+ await base.save();
90
+
91
+ if (other.existsInDatabase) {
92
+ console.log('Deleting duplicate member', other.id, other.details.name);
93
+
94
+ // store other member in merged_member table
95
+ const mergedMember = MergedMember.fromMember(other, base.id);
96
+ await mergedMember.save();
97
+
98
+ await other.delete();
99
+ }
100
+
101
+ await MemberUserSyncer.onChangeMember(base);
102
+ await PlatformMembershipService.updateMembershipsForId(base.id);
103
+
104
+ if (options?.updateDocuments !== false) {
105
+ await Document.updateForMember(base);
106
+ }
107
+ }
108
+
109
+ async function mergeRegistrations(base: Member, other: Member) {
110
+ await mergeModels(base, other, Registration);
111
+ }
112
+
113
+ async function mergeUsers(base: Member, other: Member) {
114
+ await mergeModels(base, other, User);
115
+ }
116
+
117
+ async function mergeResponsibilities(base: Member, other: Member) {
118
+ async function getResponsibilities(memberId: string) {
119
+ const rows = await SQL.select()
120
+ .from(SQL.table(MemberResponsibilityRecord.table))
121
+ .where(SQL.column('memberId'), memberId)
122
+ .fetch();
123
+
124
+ return MemberResponsibilityRecord.fromRows(
125
+ rows,
126
+ MemberResponsibilityRecord.table,
127
+ );
128
+ }
129
+
130
+ const otherResponsibilities = await getResponsibilities(other.id);
131
+ const baseResponsibilities = await getResponsibilities(base.id);
132
+
133
+ // Delete duplicate responsibilities where endDate is null -> keep responsibility with oldest start date
134
+ for (const otherResponsibility of otherResponsibilities) {
135
+ // check if equal responsibilities exist
136
+ const otherResponsibilitiesWithoutCurrent
137
+ = otherResponsibilities.filter(
138
+ o => o.id !== otherResponsibility.id,
139
+ );
140
+ const equalResponsibilities = baseResponsibilities
141
+ .concat(otherResponsibilitiesWithoutCurrent)
142
+ .filter((baseResponsibility) => {
143
+ return (
144
+ baseResponsibility.responsibilityId
145
+ === otherResponsibility.responsibilityId
146
+ && baseResponsibility.organizationId
147
+ === otherResponsibility.organizationId
148
+ && baseResponsibility.groupId
149
+ === otherResponsibility.groupId
150
+ && baseResponsibility.endDate === null
151
+ && otherResponsibility.endDate === null
152
+ );
153
+ });
154
+
155
+ if (equalResponsibilities.length > 0) {
156
+ const allEqualResponsibilities = [
157
+ ...equalResponsibilities,
158
+ otherResponsibility,
159
+ ]
160
+ // sort on startDate
161
+ .sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
162
+
163
+ const responsibilityWithOldestStartDate
164
+ = allEqualResponsibilities[0];
165
+
166
+ const responsibilitiesToDelete = allEqualResponsibilities.slice(
167
+ 1,
168
+ undefined,
169
+ );
170
+
171
+ for (const responsibilityToDelete of responsibilitiesToDelete) {
172
+ const baseIndex = baseResponsibilities.indexOf(
173
+ responsibilityToDelete,
174
+ );
175
+ if (baseIndex !== -1) baseResponsibilities.splice(baseIndex, 1);
176
+ else {
177
+ const otherIndex = otherResponsibilities.indexOf(
178
+ responsibilityToDelete,
179
+ );
180
+ if (otherIndex !== -1)
181
+ otherResponsibilities.splice(otherIndex, 1);
182
+ }
183
+
184
+ if (responsibilityToDelete.existsInDatabase) {
185
+ await responsibilityToDelete.delete();
186
+ }
187
+ }
188
+
189
+ if (responsibilityWithOldestStartDate.memberId !== base.id) {
190
+ responsibilityWithOldestStartDate.memberId = base.id;
191
+ await responsibilityWithOldestStartDate.save();
192
+ }
193
+ } else {
194
+ otherResponsibility.memberId = base.id;
195
+ await otherResponsibility.save();
196
+ }
197
+ }
198
+ }
199
+
200
+ async function mergeBalanceItems(base: Member, other: Member) {
201
+ await mergeModels(base, other, BalanceItem);
202
+ }
203
+
204
+ async function mergeDocuments(base: Member, other: Member) {
205
+ await mergeModels(base, other, Document);
206
+ }
207
+
208
+ async function mergeMemberPlatformMemberships(base: Member, other: Member) {
209
+ await mergeModels(base, other, MemberPlatformMembership);
210
+ }
211
+
212
+ class ModelWithMemberId extends QueryableModel {
213
+ memberId: string | null;
214
+ }
215
+
216
+ async function mergeModels<M extends typeof ModelWithMemberId>(
217
+ base: Member,
218
+ other: Member,
219
+ model: M,
220
+ ) {
221
+ const baseId = base.id;
222
+ const otherModels = await model.where({
223
+ memberId: other.id,
224
+ });
225
+
226
+ for (const otherModel of otherModels) {
227
+ otherModel.memberId = baseId;
228
+
229
+ if (otherModel instanceof Document) {
230
+ await otherModel.save({
231
+ forceSave: true,
232
+ skipMarkSaved: true,
233
+ skipSendEvents: true,
234
+ });
235
+ } else {
236
+ await otherModel.save({
237
+ skipMarkSaved: true,
238
+ skipSendEvents: true,
239
+ });
240
+ }
241
+
242
+ if (otherModel instanceof Registration) {
243
+ await RegistrationService.scheduleStockUpdateAsync(otherModel.id);
244
+ }
245
+ }
246
+ }
247
+
248
+ export function mergeMemberDetails(base: Member, other: Member): void {
249
+ const baseDetails = base.details;
250
+ const otherDetails = other.details;
251
+ baseDetails.cleanData();
252
+ otherDetails.cleanData();
253
+
254
+ // string details
255
+ mergeStringIfBaseNotSet(baseDetails, otherDetails, 'firstName');
256
+ mergeStringIfBaseNotSet(baseDetails, otherDetails, 'lastName');
257
+
258
+ mergeStringIfBaseNotSet(baseDetails, otherDetails, 'memberNumber');
259
+
260
+ // uitpas number
261
+ mergeUitpasNumberDetails(baseDetails, otherDetails, baseDetails);
262
+
263
+ // email
264
+ mergeEmail(baseDetails, otherDetails);
265
+
266
+ // phone
267
+ mergePhone(baseDetails, otherDetails, baseDetails);
268
+
269
+ // gender
270
+ if (otherDetails.gender !== Gender.Other) {
271
+ baseDetails.gender = otherDetails.gender;
272
+ }
273
+
274
+ // notes
275
+ mergeNotes(baseDetails, otherDetails);
276
+
277
+ // date
278
+ merge(baseDetails, otherDetails, 'birthDay');
279
+
280
+ // boolean status
281
+ mergeBooleanStatusIfBaseNotSet(
282
+ baseDetails,
283
+ otherDetails,
284
+ 'requiresFinancialSupport',
285
+ );
286
+
287
+ mergeBooleanStatusIfBaseNotSet(
288
+ baseDetails,
289
+ otherDetails,
290
+ 'dataPermissions',
291
+ );
292
+
293
+ // address
294
+ mergeAddress(baseDetails, otherDetails, baseDetails);
295
+
296
+ // parents
297
+ mergeParents(baseDetails, otherDetails);
298
+
299
+ // emergency contacts
300
+ baseDetails.emergencyContacts = baseDetails.emergencyContacts.concat(
301
+ // add contacts that are not yet in the list
302
+ otherDetails.emergencyContacts.filter(
303
+ otherContact =>
304
+ !baseDetails.emergencyContacts.some(baseContact =>
305
+ baseContact.isEqual(otherContact),
306
+ ),
307
+ ),
308
+ );
309
+
310
+ // review times
311
+ // todo: is this correct?
312
+ baseDetails.reviewTimes.merge(otherDetails.reviewTimes);
313
+
314
+ // answers
315
+ mergeAnswers(baseDetails, otherDetails);
316
+
317
+ // unverified data
318
+ baseDetails.unverifiedEmails = Formatter.uniqueArray(
319
+ baseDetails.unverifiedEmails.concat(
320
+ otherDetails.unverifiedEmails.filter(
321
+ email => !isNullOrEmpty(email),
322
+ ),
323
+ ),
324
+ );
325
+ baseDetails.unverifiedPhones = Formatter.uniqueArray(
326
+ baseDetails.unverifiedPhones.concat(
327
+ otherDetails.unverifiedPhones.filter(
328
+ phone => !isNullOrEmpty(phone),
329
+ ),
330
+ ),
331
+ );
332
+
333
+ // unverified addresses
334
+ for (const address of otherDetails.unverifiedAddresses) {
335
+ if (!baseDetails.unverifiedAddresses.some(a => a.id === address.id)) {
336
+ baseDetails.unverifiedAddresses.push(address);
337
+ }
338
+ }
339
+
340
+ // Ids
341
+ baseDetails.oldIds = Formatter.uniqueArray([...baseDetails.oldIds, ...otherDetails.oldIds]).filter(b => b !== base.id);
342
+
343
+ if (other.id) {
344
+ baseDetails.oldIds = Formatter.uniqueArray([...baseDetails.oldIds, other.id]);
345
+ }
346
+ }
347
+
348
+ export function selectBaseMember(members: Member[]): {
349
+ base: Member;
350
+ others: Member[];
351
+ } {
352
+ if (members.length < 2) {
353
+ throw Error('Members array length is less than 2.');
354
+ }
355
+ const sorted = members.sort(
356
+ (m1, m2) => (m2.existsInDatabase ? 0 : m2.createdAt.getTime()) - (m1.existsInDatabase ? 0 : m1.createdAt.getTime()),
357
+ );
358
+
359
+ return { base: sorted[0], others: sorted.slice(1, undefined) };
360
+ }
361
+
362
+ function mergeAnswers(base: MemberDetails, other: MemberDetails) {
363
+ const newAnswers: Map<string, RecordAnswer> = new Map(base.recordAnswers);
364
+ for (const otherAnswer of other.recordAnswers.values()) {
365
+ const otherId = otherAnswer.settings.id;
366
+ const baseAnswer = newAnswers.get(otherId);
367
+
368
+ if (!baseAnswer) {
369
+ newAnswers.set(otherId, otherAnswer);
370
+ } else if (otherAnswer.date >= baseAnswer.date) {
371
+ newAnswers.set(otherId, otherAnswer);
372
+ } else {
373
+ // keep existing, this one is more up-to-date, don't add the other answer
374
+ }
375
+ }
376
+ base.recordAnswers = newAnswers;
377
+ }
378
+
379
+ function mergeNotes(base: MemberDetails, other: MemberDetails) {
380
+ if (base.notes && other.notes && base.notes !== other.notes) {
381
+ base.notes = `${base.notes}\n${other.notes}`;
382
+ } else if (other.notes) {
383
+ base.notes = other.notes;
384
+ return;
385
+ }
386
+ }
387
+
388
+ function mergeParents(base: MemberDetails, other: MemberDetails) {
389
+ const baseParents = base.parents;
390
+ const otherParents = other.parents;
391
+ const parentsToAdd: Parent[] = [];
392
+
393
+ for (const otherParent of otherParents) {
394
+ // equal if same first and last name
395
+ const equalBaseParent = baseParents.find(
396
+ baseParent =>
397
+ hasEqualStringValue(baseParent, otherParent, 'firstName')
398
+ && hasEqualStringValue(baseParent, otherParent, 'lastName'),
399
+ );
400
+
401
+ if (!equalBaseParent) {
402
+ parentsToAdd.push(otherParent);
403
+ continue;
404
+ }
405
+
406
+ mergeParent(equalBaseParent, otherParent, base);
407
+ }
408
+
409
+ base.parents = baseParents.concat(parentsToAdd);
410
+ }
411
+
412
+ function mergeParent(base: Parent, other: Parent, baseDetails: MemberDetails) {
413
+ if (other.type !== ParentType.Other) {
414
+ base.type = other.type;
415
+ }
416
+ mergeString(base, other, 'firstName');
417
+ mergeString(base, other, 'lastName');
418
+ // add other emails to alternative emails
419
+ mergeEmail(base, other);
420
+ mergePhone(base, other, baseDetails);
421
+ mergeAddress(base, other, baseDetails);
422
+ }
423
+
424
+ function mergeEmail(
425
+ base: { email: string | null; alternativeEmails: string[] },
426
+ other: { email: string | null; alternativeEmails: string[] },
427
+ ) {
428
+ const allEmails = Formatter.uniqueArray(
429
+ [other.email, ...other.alternativeEmails, base.email, ...base.alternativeEmails]
430
+ .filter(f => f !== null)
431
+ .filter(f => !isNullOrEmpty(f)),
432
+ );
433
+ base.email = allEmails.shift() ?? null;
434
+ base.alternativeEmails = allEmails;
435
+ }
436
+
437
+ function mergePhone(
438
+ base: { phone: string | null | undefined },
439
+ other: { phone: string | null | undefined },
440
+ baseDetails: MemberDetails,
441
+ ) {
442
+ const originalPhone = base.phone;
443
+ const isPhoneMerged = mergeString(base, other, 'phone');
444
+ if (isPhoneMerged && !isNullOrEmpty(originalPhone)) {
445
+ if (
446
+ !baseDetails.unverifiedPhones.some(phone => phone === originalPhone)
447
+ ) {
448
+ baseDetails.unverifiedPhones.push(originalPhone!);
449
+ }
450
+ }
451
+ }
452
+
453
+ function mergeAddress(
454
+ base: { address: Address | null | undefined },
455
+ other: { address: Address | null | undefined },
456
+ baseDetails: MemberDetails,
457
+ ) {
458
+ const baseAddress = base.address;
459
+ const otherAddress = other.address;
460
+
461
+ if (!otherAddress) {
462
+ return;
463
+ }
464
+
465
+ base.address = otherAddress;
466
+
467
+ if (baseAddress && baseAddress.id !== otherAddress.id) {
468
+ // add base address to unverified addresses
469
+ if (
470
+ !baseDetails.unverifiedAddresses.some(
471
+ address => address.id === baseAddress.id,
472
+ )
473
+ ) {
474
+ baseDetails.unverifiedAddresses.push(baseAddress);
475
+ }
476
+ }
477
+ }
478
+
479
+ function mergeUitpasNumberDetails(base: { uitpasNumberDetails: UitpasNumberDetails | null }, other: { uitpasNumberDetails: UitpasNumberDetails | null }, baseDetails: MemberDetails) {
480
+ const baseUitpasNumberDetails = base.uitpasNumberDetails;
481
+ const otherUitpasNumberDetails = other.uitpasNumberDetails;
482
+
483
+ if (!otherUitpasNumberDetails) {
484
+ return;
485
+ }
486
+
487
+ if ((baseUitpasNumberDetails?.socialTariff?.updatedAt.getTime() ?? 0) >= (otherUitpasNumberDetails.socialTariff?.updatedAt.getTime() ?? 0)) {
488
+ return;
489
+ }
490
+
491
+ baseDetails.uitpasNumberDetails = otherUitpasNumberDetails;
492
+ }
493
+
494
+ function mergeString<T, K extends keyof T>(
495
+ base: T,
496
+ other: T,
497
+ key: K & (T[K] extends string | null | undefined ? K : never),
498
+ ): boolean {
499
+ const otherValue = other[key] as string | null | undefined;
500
+ if (isNullOrEmpty(otherValue)) {
501
+ return false;
502
+ }
503
+
504
+ (base[key] as string | null | undefined) = otherValue;
505
+
506
+ return true;
507
+ }
508
+
509
+ function mergeStringIfBaseNotSet<T, K extends keyof T>(
510
+ base: T,
511
+ other: T,
512
+ key: K & (T[K] extends string | null | undefined ? K : never),
513
+ ): boolean {
514
+ const otherValue = other[key] as string | null | undefined;
515
+ if (isNullOrEmpty(otherValue)) {
516
+ return false;
517
+ }
518
+
519
+ if (!isNullOrEmpty(base[key] as string | null | undefined)) {
520
+ return false;
521
+ }
522
+
523
+ (base[key] as string | null | undefined) = otherValue;
524
+
525
+ return true;
526
+ }
527
+
528
+ function merge<T, K extends keyof T>(
529
+ base: T,
530
+ other: T,
531
+ key: K
532
+ & (T[K] extends number | Date | boolean | null | undefined ? K : never),
533
+ ): boolean {
534
+ const otherValue = other[key] as number | Date | boolean | null | undefined;
535
+ if (otherValue === null || otherValue === undefined) return false;
536
+ (base[key] as number | Date | boolean | null | undefined) = otherValue;
537
+ return true;
538
+ }
539
+
540
+ function mergeBooleanStatusIfBaseNotSet<T, K extends keyof T>(
541
+ base: T,
542
+ other: T,
543
+ key: K & (T[K] extends BooleanStatus | null | undefined ? K : never),
544
+ ): boolean {
545
+ const otherValue = other[key] as BooleanStatus | null | undefined;
546
+ if (otherValue === null || otherValue === undefined) return false;
547
+
548
+ const baseValue = base[key] as BooleanStatus | null | undefined;
549
+ if (baseValue === undefined || baseValue === null) {
550
+ (base[key] as BooleanStatus | null | undefined) = otherValue;
551
+ return true;
552
+ }
553
+
554
+ if (baseValue.date < otherValue.date) {
555
+ (base[key] as BooleanStatus | null | undefined) = otherValue;
556
+ return true;
557
+ }
558
+
559
+ return false;
560
+ }
561
+
562
+ /**
563
+ * Returns true if the values of the key for a and b
564
+ * are not null or undefined
565
+ * and both are equal.
566
+ * @param a
567
+ * @param b
568
+ * @param key
569
+ * @returns
570
+ */
571
+ function hasEqualStringValue<T, K extends keyof T>(
572
+ a: T,
573
+ b: T,
574
+ key: K & (T[K] extends string | null | undefined ? K : never),
575
+ ) {
576
+ return hasValueAndIsEqual(
577
+ a[key] as string | null | undefined,
578
+ b[key] as string | null | undefined,
579
+ );
580
+ }
581
+
582
+ function hasValueAndIsEqual(
583
+ a: string | null | undefined,
584
+ b: string | null | undefined,
585
+ ): boolean {
586
+ if (isNullOrEmpty(a) || isNullOrEmpty(b)) return false;
587
+ return isStringEqual(a as string, b as string);
588
+ }
589
+
590
+ function isStringEqual(a: string, b: string): boolean {
591
+ return toLowerTrim(a) === toLowerTrim(b);
592
+ }
593
+
594
+ function toLowerTrim(name: string) {
595
+ return name.toLowerCase().trim();
596
+ }
597
+
598
+ function isNullOrEmpty(value: string | null | undefined) {
599
+ return value === null || value === undefined || value.trim() === '';
600
+ }
@@ -86,14 +86,12 @@ export class MemberUserSyncerStatic {
86
86
  if (!allEmails.includes(user.email.toLocaleLowerCase())) {
87
87
  if (!user.hasAccount()) {
88
88
  await this.unlinkUser(user, member);
89
- }
90
- else {
89
+ } else {
91
90
  // Make sure only linked as a parent, not as user self
92
91
  // This makes sure we don't inherit permissions and aren't counted as 'being' the member
93
92
  await this.linkUser(user.email, member, true);
94
93
  }
95
- }
96
- else if (!member.details.calculatedParentsHaveAccess && parentEmails.includes(user.email.toLocaleLowerCase()) && !userEmails.includes(user.email.toLocaleLowerCase())) {
94
+ } else if (!member.details.calculatedParentsHaveAccess && parentEmails.includes(user.email.toLocaleLowerCase()) && !userEmails.includes(user.email.toLocaleLowerCase())) {
97
95
  await this.unlinkUser(user, member);
98
96
  }
99
97
  }
@@ -196,8 +194,7 @@ export class MemberUserSyncerStatic {
196
194
  }),
197
195
  );
198
196
  user.permissions = user.permissions.patch(patch);
199
- }
200
- else {
197
+ } else {
201
198
  const patch = user.permissions.convertPatch(
202
199
  Permissions.patch({
203
200
  responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any,
@@ -351,8 +348,7 @@ export class MemberUserSyncerStatic {
351
348
  user.memberId = member.id;
352
349
  }
353
350
  await this.updateInheritedPermissions(user);
354
- }
355
- else {
351
+ } else {
356
352
  let shouldSave = false;
357
353
 
358
354
  // Clear clearly wrong name
@@ -381,8 +377,7 @@ export class MemberUserSyncerStatic {
381
377
  await user.save();
382
378
  }
383
379
  }
384
- }
385
- else {
380
+ } else {
386
381
  // Create a new placeholder user
387
382
  user = new User();
388
383
  user.organizationId = member.organizationId;
@@ -395,8 +390,7 @@ export class MemberUserSyncerStatic {
395
390
  if (!asParent) {
396
391
  user.memberId = member.id;
397
392
  await this.updateInheritedPermissions(user);
398
- }
399
- else {
393
+ } else {
400
394
  if (!name && user.firstName?.toLocaleLowerCase() === member.details.firstName?.toLocaleLowerCase() && user.lastName?.toLocaleLowerCase() === member.details.lastName?.toLocaleLowerCase()) {
401
395
  user.firstName = null;
402
396
  user.lastName = null;
@@ -79,8 +79,7 @@ export const MembershipCharger = {
79
79
  // Force price update (required because could have changed - especially for free memberships in combination with deletes)
80
80
  try {
81
81
  await membership.calculatePrice(member);
82
- }
83
- catch (e) {
82
+ } catch (e) {
84
83
  console.error('Failed to update price for membership. Not charged.', membership.id, e);
85
84
  continue;
86
85
  }
@@ -193,8 +192,7 @@ export const MembershipCharger = {
193
192
  try {
194
193
  await membership.calculatePrice(member);
195
194
  await membership.save();
196
- }
197
- catch (e) {
195
+ } catch (e) {
198
196
  console.error('Failed to update price for membership', membership.id, e);
199
197
  continue;
200
198
  }