@stamhoofd/models 2.64.0 → 2.65.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 (279) hide show
  1. package/dist/src/helpers/EmailBuilder.d.ts +6 -1
  2. package/dist/src/helpers/EmailBuilder.d.ts.map +1 -1
  3. package/dist/src/helpers/EmailBuilder.js +60 -20
  4. package/dist/src/helpers/EmailBuilder.js.map +1 -1
  5. package/dist/src/helpers/MemberMerger.d.ts.map +1 -1
  6. package/dist/src/helpers/MemberMerger.js +1 -2
  7. package/dist/src/helpers/MemberMerger.js.map +1 -1
  8. package/dist/src/migrations/1605262045-import-postcodes.d.ts +2 -2
  9. package/dist/src/migrations/1605262045-import-postcodes.js +9 -9
  10. package/dist/src/migrations/1735573520-emails-email-type.sql +2 -0
  11. package/dist/src/migrations/1735573521-email-recipients-email-type.sql +4 -0
  12. package/dist/src/migrations/1735573522-emails-indexes.sql +3 -0
  13. package/dist/src/migrations/1735982691-cached-balance-email-reminder-counts.sql +4 -0
  14. package/dist/src/migrations/1735994471-default-email-templates.sql +5 -0
  15. package/dist/src/models/AuditLog.d.ts +2 -7
  16. package/dist/src/models/AuditLog.d.ts.map +1 -1
  17. package/dist/src/models/AuditLog.js +1 -15
  18. package/dist/src/models/AuditLog.js.map +1 -1
  19. package/dist/src/models/BalanceItem.d.ts +2 -11
  20. package/dist/src/models/BalanceItem.d.ts.map +1 -1
  21. package/dist/src/models/BalanceItem.js +8 -22
  22. package/dist/src/models/BalanceItem.js.map +1 -1
  23. package/dist/src/models/BalanceItemPayment.d.ts +3 -7
  24. package/dist/src/models/BalanceItemPayment.d.ts.map +1 -1
  25. package/dist/src/models/BalanceItemPayment.js +1 -15
  26. package/dist/src/models/BalanceItemPayment.js.map +1 -1
  27. package/dist/src/models/BuckarooPayment.d.ts +2 -2
  28. package/dist/src/models/BuckarooPayment.d.ts.map +1 -1
  29. package/dist/src/models/BuckarooPayment.js +2 -1
  30. package/dist/src/models/BuckarooPayment.js.map +1 -1
  31. package/dist/src/models/CachedBalance.d.ts +7 -9
  32. package/dist/src/models/CachedBalance.d.ts.map +1 -1
  33. package/dist/src/models/CachedBalance.js +61 -22
  34. package/dist/src/models/CachedBalance.js.map +1 -1
  35. package/dist/src/models/Document.d.ts +3 -3
  36. package/dist/src/models/Document.d.ts.map +1 -1
  37. package/dist/src/models/Document.js +2 -1
  38. package/dist/src/models/Document.js.map +1 -1
  39. package/dist/src/models/DocumentTemplate.d.ts +2 -2
  40. package/dist/src/models/DocumentTemplate.d.ts.map +1 -1
  41. package/dist/src/models/DocumentTemplate.js +2 -1
  42. package/dist/src/models/DocumentTemplate.js.map +1 -1
  43. package/dist/src/models/Email.d.ts +10 -4
  44. package/dist/src/models/Email.d.ts.map +1 -1
  45. package/dist/src/models/Email.js +68 -25
  46. package/dist/src/models/Email.js.map +1 -1
  47. package/dist/src/models/EmailRecipient.d.ts +14 -8
  48. package/dist/src/models/EmailRecipient.d.ts.map +1 -1
  49. package/dist/src/models/EmailRecipient.js +19 -14
  50. package/dist/src/models/EmailRecipient.js.map +1 -1
  51. package/dist/src/models/EmailTemplate.d.ts +2 -7
  52. package/dist/src/models/EmailTemplate.d.ts.map +1 -1
  53. package/dist/src/models/EmailTemplate.js +1 -15
  54. package/dist/src/models/EmailTemplate.js.map +1 -1
  55. package/dist/src/models/EmailVerificationCode.d.ts +2 -2
  56. package/dist/src/models/EmailVerificationCode.d.ts.map +1 -1
  57. package/dist/src/models/EmailVerificationCode.js +2 -1
  58. package/dist/src/models/EmailVerificationCode.js.map +1 -1
  59. package/dist/src/models/Event.d.ts +2 -2
  60. package/dist/src/models/Event.d.ts.map +1 -1
  61. package/dist/src/models/Event.js +2 -1
  62. package/dist/src/models/Event.js.map +1 -1
  63. package/dist/src/models/Group.d.ts +2 -7
  64. package/dist/src/models/Group.d.ts.map +1 -1
  65. package/dist/src/models/Group.js +1 -17
  66. package/dist/src/models/Group.js.map +1 -1
  67. package/dist/src/models/Image.d.ts +2 -2
  68. package/dist/src/models/Image.d.ts.map +1 -1
  69. package/dist/src/models/Image.js +2 -1
  70. package/dist/src/models/Image.js.map +1 -1
  71. package/dist/src/models/Member.d.ts +3 -2
  72. package/dist/src/models/Member.d.ts.map +1 -1
  73. package/dist/src/models/Member.js +1 -1
  74. package/dist/src/models/Member.js.map +1 -1
  75. package/dist/src/models/MemberPlatformMembership.d.ts +2 -7
  76. package/dist/src/models/MemberPlatformMembership.d.ts.map +1 -1
  77. package/dist/src/models/MemberPlatformMembership.js +1 -15
  78. package/dist/src/models/MemberPlatformMembership.js.map +1 -1
  79. package/dist/src/models/MemberResponsibilityRecord.d.ts +2 -7
  80. package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -1
  81. package/dist/src/models/MemberResponsibilityRecord.js +1 -15
  82. package/dist/src/models/MemberResponsibilityRecord.js.map +1 -1
  83. package/dist/src/models/MemberUser.d.ts +8 -0
  84. package/dist/src/models/MemberUser.d.ts.map +1 -0
  85. package/dist/src/models/MemberUser.js +26 -0
  86. package/dist/src/models/MemberUser.js.map +1 -0
  87. package/dist/src/models/MergedMember.d.ts +2 -2
  88. package/dist/src/models/MergedMember.d.ts.map +1 -1
  89. package/dist/src/models/MergedMember.js +2 -1
  90. package/dist/src/models/MergedMember.js.map +1 -1
  91. package/dist/src/models/MolliePayment.d.ts +2 -2
  92. package/dist/src/models/MolliePayment.d.ts.map +1 -1
  93. package/dist/src/models/MolliePayment.js +2 -1
  94. package/dist/src/models/MolliePayment.js.map +1 -1
  95. package/dist/src/models/MollieToken.d.ts +2 -2
  96. package/dist/src/models/MollieToken.d.ts.map +1 -1
  97. package/dist/src/models/MollieToken.js +2 -1
  98. package/dist/src/models/MollieToken.js.map +1 -1
  99. package/dist/src/models/OneTimeToken.d.ts +2 -2
  100. package/dist/src/models/OneTimeToken.d.ts.map +1 -1
  101. package/dist/src/models/OneTimeToken.js +2 -1
  102. package/dist/src/models/OneTimeToken.js.map +1 -1
  103. package/dist/src/models/Order.d.ts +3 -2
  104. package/dist/src/models/Order.d.ts.map +1 -1
  105. package/dist/src/models/Order.js +2 -1
  106. package/dist/src/models/Order.js.map +1 -1
  107. package/dist/src/models/Organization.d.ts +2 -2
  108. package/dist/src/models/Organization.d.ts.map +1 -1
  109. package/dist/src/models/Organization.js +2 -1
  110. package/dist/src/models/Organization.js.map +1 -1
  111. package/dist/src/models/OrganizationRegistrationPeriod.d.ts +2 -2
  112. package/dist/src/models/OrganizationRegistrationPeriod.d.ts.map +1 -1
  113. package/dist/src/models/OrganizationRegistrationPeriod.js +2 -1
  114. package/dist/src/models/OrganizationRegistrationPeriod.js.map +1 -1
  115. package/dist/src/models/PasswordToken.d.ts +3 -2
  116. package/dist/src/models/PasswordToken.d.ts.map +1 -1
  117. package/dist/src/models/PasswordToken.js +2 -1
  118. package/dist/src/models/PasswordToken.js.map +1 -1
  119. package/dist/src/models/PayconiqPayment.d.ts +2 -2
  120. package/dist/src/models/PayconiqPayment.d.ts.map +1 -1
  121. package/dist/src/models/PayconiqPayment.js +2 -1
  122. package/dist/src/models/PayconiqPayment.js.map +1 -1
  123. package/dist/src/models/Payment.d.ts +2 -7
  124. package/dist/src/models/Payment.d.ts.map +1 -1
  125. package/dist/src/models/Payment.js +1 -15
  126. package/dist/src/models/Payment.js.map +1 -1
  127. package/dist/src/models/Platform.d.ts +3 -3
  128. package/dist/src/models/Platform.d.ts.map +1 -1
  129. package/dist/src/models/Platform.js +2 -1
  130. package/dist/src/models/Platform.js.map +1 -1
  131. package/dist/src/models/RegisterCode.d.ts +2 -2
  132. package/dist/src/models/RegisterCode.d.ts.map +1 -1
  133. package/dist/src/models/RegisterCode.js +2 -1
  134. package/dist/src/models/RegisterCode.js.map +1 -1
  135. package/dist/src/models/Registration.d.ts +3 -2
  136. package/dist/src/models/Registration.d.ts.map +1 -1
  137. package/dist/src/models/Registration.js +2 -27
  138. package/dist/src/models/Registration.js.map +1 -1
  139. package/dist/src/models/RegistrationPeriod.d.ts +2 -7
  140. package/dist/src/models/RegistrationPeriod.d.ts.map +1 -1
  141. package/dist/src/models/RegistrationPeriod.js +1 -15
  142. package/dist/src/models/RegistrationPeriod.js.map +1 -1
  143. package/dist/src/models/STCredit.d.ts +2 -2
  144. package/dist/src/models/STCredit.d.ts.map +1 -1
  145. package/dist/src/models/STCredit.js +2 -1
  146. package/dist/src/models/STCredit.js.map +1 -1
  147. package/dist/src/models/STInvoice.d.ts +3 -2
  148. package/dist/src/models/STInvoice.d.ts.map +1 -1
  149. package/dist/src/models/STInvoice.js +2 -1
  150. package/dist/src/models/STInvoice.js.map +1 -1
  151. package/dist/src/models/STPackage.d.ts +2 -2
  152. package/dist/src/models/STPackage.d.ts.map +1 -1
  153. package/dist/src/models/STPackage.js +2 -1
  154. package/dist/src/models/STPackage.js.map +1 -1
  155. package/dist/src/models/STPendingInvoice.d.ts +3 -2
  156. package/dist/src/models/STPendingInvoice.d.ts.map +1 -1
  157. package/dist/src/models/STPendingInvoice.js +2 -1
  158. package/dist/src/models/STPendingInvoice.js.map +1 -1
  159. package/dist/src/models/StripeAccount.d.ts +2 -2
  160. package/dist/src/models/StripeAccount.d.ts.map +1 -1
  161. package/dist/src/models/StripeAccount.js +2 -1
  162. package/dist/src/models/StripeAccount.js.map +1 -1
  163. package/dist/src/models/StripeCheckoutSession.d.ts +2 -2
  164. package/dist/src/models/StripeCheckoutSession.d.ts.map +1 -1
  165. package/dist/src/models/StripeCheckoutSession.js +2 -1
  166. package/dist/src/models/StripeCheckoutSession.js.map +1 -1
  167. package/dist/src/models/StripePaymentIntent.d.ts +2 -2
  168. package/dist/src/models/StripePaymentIntent.d.ts.map +1 -1
  169. package/dist/src/models/StripePaymentIntent.js +2 -1
  170. package/dist/src/models/StripePaymentIntent.js.map +1 -1
  171. package/dist/src/models/Ticket.d.ts +3 -2
  172. package/dist/src/models/Ticket.d.ts.map +1 -1
  173. package/dist/src/models/Ticket.js +2 -1
  174. package/dist/src/models/Ticket.js.map +1 -1
  175. package/dist/src/models/Token.d.ts +3 -2
  176. package/dist/src/models/Token.d.ts.map +1 -1
  177. package/dist/src/models/Token.js +2 -1
  178. package/dist/src/models/Token.js.map +1 -1
  179. package/dist/src/models/UsedRegisterCode.d.ts +2 -2
  180. package/dist/src/models/UsedRegisterCode.d.ts.map +1 -1
  181. package/dist/src/models/UsedRegisterCode.js +2 -1
  182. package/dist/src/models/UsedRegisterCode.js.map +1 -1
  183. package/dist/src/models/User.d.ts +7 -2
  184. package/dist/src/models/User.d.ts.map +1 -1
  185. package/dist/src/models/User.js +27 -4
  186. package/dist/src/models/User.js.map +1 -1
  187. package/dist/src/models/UserPermissions.d.ts +3 -2
  188. package/dist/src/models/UserPermissions.d.ts.map +1 -1
  189. package/dist/src/models/UserPermissions.js +2 -1
  190. package/dist/src/models/UserPermissions.js.map +1 -1
  191. package/dist/src/models/Webshop.d.ts +3 -2
  192. package/dist/src/models/Webshop.d.ts.map +1 -1
  193. package/dist/src/models/Webshop.js +2 -1
  194. package/dist/src/models/Webshop.js.map +1 -1
  195. package/dist/src/models/WebshopDiscountCode.d.ts +2 -2
  196. package/dist/src/models/WebshopDiscountCode.d.ts.map +1 -1
  197. package/dist/src/models/WebshopDiscountCode.js +2 -1
  198. package/dist/src/models/WebshopDiscountCode.js.map +1 -1
  199. package/dist/src/models/addresses/City.d.ts +3 -2
  200. package/dist/src/models/addresses/City.d.ts.map +1 -1
  201. package/dist/src/models/addresses/City.js +2 -1
  202. package/dist/src/models/addresses/City.js.map +1 -1
  203. package/dist/src/models/addresses/PostalCode.d.ts +3 -2
  204. package/dist/src/models/addresses/PostalCode.d.ts.map +1 -1
  205. package/dist/src/models/addresses/PostalCode.js +2 -1
  206. package/dist/src/models/addresses/PostalCode.js.map +1 -1
  207. package/dist/src/models/addresses/Province.d.ts +2 -2
  208. package/dist/src/models/addresses/Province.d.ts.map +1 -1
  209. package/dist/src/models/addresses/Province.js +2 -1
  210. package/dist/src/models/addresses/Province.js.map +1 -1
  211. package/dist/src/models/addresses/Street.d.ts +3 -2
  212. package/dist/src/models/addresses/Street.d.ts.map +1 -1
  213. package/dist/src/models/addresses/Street.js +2 -1
  214. package/dist/src/models/addresses/Street.js.map +1 -1
  215. package/dist/src/models/index.d.ts +1 -0
  216. package/dist/src/models/index.d.ts.map +1 -1
  217. package/dist/src/models/index.js +1 -0
  218. package/dist/src/models/index.js.map +1 -1
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/package.json +2 -2
  221. package/src/helpers/EmailBuilder.ts +82 -27
  222. package/src/helpers/MemberMerger.ts +2 -3
  223. package/src/migrations/1605262045-import-postcodes.ts +4 -5
  224. package/src/migrations/1735573520-emails-email-type.sql +2 -0
  225. package/src/migrations/1735573521-email-recipients-email-type.sql +4 -0
  226. package/src/migrations/1735573522-emails-indexes.sql +3 -0
  227. package/src/migrations/1735982691-cached-balance-email-reminder-counts.sql +4 -0
  228. package/src/migrations/1735994471-default-email-templates.sql +5 -0
  229. package/src/models/AuditLog.ts +3 -21
  230. package/src/models/BalanceItem.ts +13 -30
  231. package/src/models/BalanceItemPayment.ts +3 -21
  232. package/src/models/BuckarooPayment.ts +3 -2
  233. package/src/models/CachedBalance.ts +82 -27
  234. package/src/models/Document.ts +4 -3
  235. package/src/models/DocumentTemplate.ts +4 -3
  236. package/src/models/Email.ts +80 -32
  237. package/src/models/EmailRecipient.ts +20 -20
  238. package/src/models/EmailTemplate.ts +3 -21
  239. package/src/models/EmailVerificationCode.ts +3 -2
  240. package/src/models/Event.ts +3 -2
  241. package/src/models/Group.ts +4 -23
  242. package/src/models/Image.ts +3 -2
  243. package/src/models/Member.ts +4 -4
  244. package/src/models/MemberPlatformMembership.ts +3 -21
  245. package/src/models/MemberResponsibilityRecord.ts +3 -21
  246. package/src/models/MemberUser.ts +18 -0
  247. package/src/models/MergedMember.ts +3 -2
  248. package/src/models/MolliePayment.ts +3 -2
  249. package/src/models/MollieToken.ts +3 -2
  250. package/src/models/OneTimeToken.ts +3 -2
  251. package/src/models/Order.ts +3 -2
  252. package/src/models/Organization.ts +3 -2
  253. package/src/models/OrganizationRegistrationPeriod.ts +3 -2
  254. package/src/models/PasswordToken.ts +3 -2
  255. package/src/models/PayconiqPayment.ts +3 -2
  256. package/src/models/Payment.ts +3 -21
  257. package/src/models/Platform.ts +4 -3
  258. package/src/models/RegisterCode.ts +3 -2
  259. package/src/models/Registration.ts +3 -28
  260. package/src/models/RegistrationPeriod.ts +3 -21
  261. package/src/models/STCredit.ts +3 -2
  262. package/src/models/STInvoice.ts +3 -2
  263. package/src/models/STPackage.ts +3 -2
  264. package/src/models/STPendingInvoice.ts +3 -2
  265. package/src/models/StripeAccount.ts +3 -2
  266. package/src/models/StripeCheckoutSession.ts +3 -2
  267. package/src/models/StripePaymentIntent.ts +3 -2
  268. package/src/models/Ticket.ts +3 -2
  269. package/src/models/Token.ts +3 -2
  270. package/src/models/UsedRegisterCode.ts +3 -2
  271. package/src/models/User.ts +31 -3
  272. package/src/models/UserPermissions.ts +3 -2
  273. package/src/models/Webshop.ts +3 -2
  274. package/src/models/WebshopDiscountCode.ts +3 -2
  275. package/src/models/addresses/City.ts +3 -2
  276. package/src/models/addresses/PostalCode.ts +3 -2
  277. package/src/models/addresses/Province.ts +3 -2
  278. package/src/models/addresses/Street.ts +3 -2
  279. package/src/models/index.ts +1 -0
@@ -0,0 +1,5 @@
1
+ INSERT INTO `email_templates` (`id`, `subject`, `groupId`, `webshopId`, `organizationId`, `type`, `text`, `html`, `json`, `updatedAt`, `createdAt`) VALUES
2
+ ('8aec66ec-a39c-4849-9ebf-973e489f8017', 'Jouw openstaand saldo bij {{organizationName}}', NULL, NULL, NULL, 'OrganizationBalanceIncreaseNotification', '{{greeting}}Jouw groep ({{objectName}}) heeft een nieuw openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.\n\n \n \n \n \n Betalen via het beheerdersportaal\n \n \n \n \n\nTwijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.Vragen over je openstaand bedrag? Neem dan contact op met {{organizationName}} via {{fromAddress}}OverzichtDit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.{{balanceTable}}Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via deze knop — hierna zal je nooit meer een e-mail van ons ontvangen.Uitschrijven', '<!DOCTYPE html>\n<html>\n\n<head>\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n<title>Jouw openstaand saldo bij {{organizationName}}</title>\n<style type=\"text/css\">body {\n color: #000716;\n color: var(--color-dark, #000716);\n font-family: -apple-system-body, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 12pt;\n line-height: 1.4;\n}\n\np {\n margin: 0;\n padding: 0;\n line-height: 1.4;\n}\n\np.description {\n color: var(--color-gray-4, #5e5e5e);\n}\np.description a, p.description a:link, p.description a:visited, p.description a:active, p.description a:hover {\n text-decoration: underline;\n color: var(--color-gray-4, #5e5e5e);\n}\n\nstrong {\n font-weight: bold;\n}\n\nem {\n font-style: italic;\n}\n\nh1 {\n font-size: 30px;\n font-weight: bold;\n line-height: 1.2;\n margin: 0;\n padding: 0;\n}\n@media (max-width: 350px) {\n h1 {\n font-size: 24px;\n }\n}\n\nh2 {\n font-size: 20px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh3 {\n font-size: 16px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh4 {\n line-height: 1.2;\n font-weight: 500;\n margin: 0;\n padding: 0;\n}\n\nol, ul {\n list-style-position: outside;\n padding-left: 30px;\n}\n\nhr {\n height: 1px;\n background: var(--color-border, var(--color-gray-2, #dcdcdc));\n border-radius: 1px;\n padding: 0;\n margin: 20px 0;\n outline: none;\n border: 0;\n}\n\n.button {\n touch-action: inherit;\n user-select: auto;\n cursor: pointer;\n display: inline-block !important;\n line-height: 42px;\n font-size: 16px;\n font-weight: bold;\n}\n.button:active {\n transform: none;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\na, a:link, a:visited, a:active, a:hover {\n text-decoration: underline;\n color: blue;\n}\n\n.email-data-table {\n width: 100%;\n border-collapse: collapse;\n}\n.email-data-table th, .email-data-table td {\n text-align: left;\n padding: 10px 10px 10px 0;\n border-bottom: 1px solid var(--color-border, var(--color-gray-2, #dcdcdc));\n vertical-align: middle;\n}\n.email-data-table th:last-child, .email-data-table td:last-child {\n text-align: right;\n padding-right: 0;\n}\n.email-data-table thead {\n font-weight: bold;\n}\n.email-data-table thead th {\n font-size: 10pt;\n}\n.email-data-table h4 ~ p {\n padding-top: 3px;\n opacity: 0.8;\n font-size: 11pt;\n}\n\n.email-style-inline-code {\n font-family: monospace;\n white-space: pre-wrap;\n display: inline-block;\n}\n\n.email-style-description-small {\n font-size: 14px;\n line-height: 1.5;\n font-weight: normal;\n color: var(--color-gray-4, #5e5e5e);\n font-variation-settings: \"opsz\" 19;\n}\n\n.email-style-title-list {\n font-size: 16px;\n line-height: 1.3;\n font-weight: 500;\n}\n.email-style-title-list + p {\n padding-top: 3px;\n}\n\n.email-style-title-prefix-list {\n font-size: 11px;\n line-height: 1.5;\n font-weight: bold;\n color: {{primaryColor}};\n text-transform: uppercase;\n margin-bottom: 3px;\n}\n.email-style-title-prefix-list.error {\n color: #f0153d;\n}\n\n.email-style-price-base, .email-style-discount-price, .email-style-discount-old-price, .email-style-price {\n font-size: 15px;\n line-height: 1.4;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n}\n.email-style-price-base.disabled, .disabled.email-style-discount-price, .disabled.email-style-discount-old-price, .disabled.email-style-price {\n opacity: 0.6;\n}\n.email-style-price-base.negative, .negative.email-style-discount-price, .negative.email-style-discount-old-price, .negative.email-style-price {\n color: #f0153d;\n}\n\n.email-style-price {\n font-weight: bold;\n color: {{primaryColor}};\n}\n\n.email-style-discount-old-price {\n text-decoration: line-through;\n color: var(--color-gray-4, #5e5e5e);\n}\n\n.email-style-discount-price {\n font-weight: bold;\n color: #ff4747;\n margin-left: 5px;\n}\n\n.pre-wrap {\n white-space: pre-wrap;\n} hr {height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;} .button.primary { margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity; } .button.primary:active { transform: scale(0.95, 0.95); } .inline-link, .inline-link:link, .inline-link:visited, .inline-link:active, .inline-link:hover { margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation; } .inline-link:active { opacity: 0.5; } .description { color: #5e5e5e; } </style>\n</head>\n\n<body>\n<p style=\"margin: 0; padding: 0; line-height: 1.4;\">{{greeting}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Jouw groep ({{objectName}}) heeft een nieuw openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><strong>Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.</strong></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartButton\" data-id=\"paymentUrl\"><table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin: 5px 0;\">\n<tbody><tr>\n <td>\n <table cellspacing=\"0\" cellpadding=\"0\">\n <tbody><tr>\n <td style=\"border-radius: 7px;\" bgcolor=\"{{primaryColor}}\">\n <a class=\"button primary\" href=\"{{paymentUrl}}\" target=\"\" style=\"margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity;\">Betalen via het beheerdersportaal</a>\n </td>\n </tr>\n </tbody></table>\n </td>\n</tr>\n</tbody></table></div><p class=\"description\" style=\"color: #5e5e5e;\"><em>Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><strong><em>Vragen over je openstaand bedrag</em></strong><em>? Neem dan contact op met </em>{{organizationName}} <em>via </em>{{fromAddress}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><hr style=\"height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;\"><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><h2 style=\"margin: 0; padding: 0;\">Overzicht</h2><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Dit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartVariableBlock\" data-id=\"balanceTable\">{{balanceTable}}</div><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><em>Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via </em><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\"><em>deze knop</em></a><em> — hierna zal je nooit meer een e-mail van ons ontvangen.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\">Uitschrijven</a></p>\n</body>\n\n</html>', '{\"value\": {\"type\": \"doc\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"smartVariable\", \"attrs\": {\"id\": \"greeting\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Jouw groep (\", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"objectName\"}}, {\"text\": \") heeft een nieuw openstaand bedrag van \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"outstandingBalance\"}}, {\"text\": \" bij \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \". Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartButton\", \"attrs\": {\"id\": \"paymentUrl\"}, \"content\": [{\"text\": \"Betalen via het beheerdersportaal\", \"type\": \"text\"}]}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Vragen over je openstaand bedrag\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}, {\"type\": \"italic\"}]}, {\"text\": \"? Neem dan contact op met \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \" \", \"type\": \"text\"}, {\"text\": \"via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"fromAddress\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"horizontalRule\"}, {\"type\": \"paragraph\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"Overzicht\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Dit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartVariableBlock\", \"attrs\": {\"id\": \"balanceTable\"}}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"deze knop\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"text\": \" — hierna zal je nooit meer een e-mail van ons ontvangen.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\", \"content\": [{\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"Uitschrijven\", \"type\": \"text\"}]}]}]}, \"version\": 354}', '2025-01-04 12:09:03', '2025-01-04 12:09:03'),
3
+ ('01f3c594-43e1-45de-9ca9-e450befb4426', '[Herinnering] Openstaand bedrag te betalen aan {{organizationName}}', NULL, NULL, NULL, 'OrganizationBalanceReminder', '{{greeting}}Jouw groep ({{objectName}}) heeft nog steeds een openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.\n\n \n \n \n \n Betalen via het beheerdersportaal\n \n \n \n \n\nTwijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.Vragen over je openstaand bedrag? Neem dan contact op met {{organizationName}} via {{fromAddress}}OverzichtDit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.{{balanceTable}}Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via deze knop — hierna zal je nooit meer een e-mail van ons ontvangen.Uitschrijven', '<!DOCTYPE html>\n<html>\n\n<head>\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n<title>[Herinnering] Openstaand bedrag te betalen aan {{organizationName}}</title>\n<style type=\"text/css\">body {\n color: #000716;\n color: var(--color-dark, #000716);\n font-family: -apple-system-body, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 12pt;\n line-height: 1.4;\n}\n\np {\n margin: 0;\n padding: 0;\n line-height: 1.4;\n}\n\np.description {\n color: var(--color-gray-4, #5e5e5e);\n}\np.description a, p.description a:link, p.description a:visited, p.description a:active, p.description a:hover {\n text-decoration: underline;\n color: var(--color-gray-4, #5e5e5e);\n}\n\nstrong {\n font-weight: bold;\n}\n\nem {\n font-style: italic;\n}\n\nh1 {\n font-size: 30px;\n font-weight: bold;\n line-height: 1.2;\n margin: 0;\n padding: 0;\n}\n@media (max-width: 350px) {\n h1 {\n font-size: 24px;\n }\n}\n\nh2 {\n font-size: 20px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh3 {\n font-size: 16px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh4 {\n line-height: 1.2;\n font-weight: 500;\n margin: 0;\n padding: 0;\n}\n\nol, ul {\n list-style-position: outside;\n padding-left: 30px;\n}\n\nhr {\n height: 1px;\n background: var(--color-border, var(--color-gray-2, #dcdcdc));\n border-radius: 1px;\n padding: 0;\n margin: 20px 0;\n outline: none;\n border: 0;\n}\n\n.button {\n touch-action: inherit;\n user-select: auto;\n cursor: pointer;\n display: inline-block !important;\n line-height: 42px;\n font-size: 16px;\n font-weight: bold;\n}\n.button:active {\n transform: none;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\na, a:link, a:visited, a:active, a:hover {\n text-decoration: underline;\n color: blue;\n}\n\n.email-data-table {\n width: 100%;\n border-collapse: collapse;\n}\n.email-data-table th, .email-data-table td {\n text-align: left;\n padding: 10px 10px 10px 0;\n border-bottom: 1px solid var(--color-border, var(--color-gray-2, #dcdcdc));\n vertical-align: middle;\n}\n.email-data-table th:last-child, .email-data-table td:last-child {\n text-align: right;\n padding-right: 0;\n}\n.email-data-table thead {\n font-weight: bold;\n}\n.email-data-table thead th {\n font-size: 10pt;\n}\n.email-data-table h4 ~ p {\n padding-top: 3px;\n opacity: 0.8;\n font-size: 11pt;\n}\n\n.email-style-inline-code {\n font-family: monospace;\n white-space: pre-wrap;\n display: inline-block;\n}\n\n.email-style-description-small {\n font-size: 14px;\n line-height: 1.5;\n font-weight: normal;\n color: var(--color-gray-4, #5e5e5e);\n font-variation-settings: \"opsz\" 19;\n}\n\n.email-style-title-list {\n font-size: 16px;\n line-height: 1.3;\n font-weight: 500;\n}\n.email-style-title-list + p {\n padding-top: 3px;\n}\n\n.email-style-title-prefix-list {\n font-size: 11px;\n line-height: 1.5;\n font-weight: bold;\n color: {{primaryColor}};\n text-transform: uppercase;\n margin-bottom: 3px;\n}\n.email-style-title-prefix-list.error {\n color: #f0153d;\n}\n\n.email-style-price-base, .email-style-discount-price, .email-style-discount-old-price, .email-style-price {\n font-size: 15px;\n line-height: 1.4;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n}\n.email-style-price-base.disabled, .disabled.email-style-discount-price, .disabled.email-style-discount-old-price, .disabled.email-style-price {\n opacity: 0.6;\n}\n.email-style-price-base.negative, .negative.email-style-discount-price, .negative.email-style-discount-old-price, .negative.email-style-price {\n color: #f0153d;\n}\n\n.email-style-price {\n font-weight: bold;\n color: {{primaryColor}};\n}\n\n.email-style-discount-old-price {\n text-decoration: line-through;\n color: var(--color-gray-4, #5e5e5e);\n}\n\n.email-style-discount-price {\n font-weight: bold;\n color: #ff4747;\n margin-left: 5px;\n}\n\n.pre-wrap {\n white-space: pre-wrap;\n} hr {height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;} .button.primary { margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity; } .button.primary:active { transform: scale(0.95, 0.95); } .inline-link, .inline-link:link, .inline-link:visited, .inline-link:active, .inline-link:hover { margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation; } .inline-link:active { opacity: 0.5; } .description { color: #5e5e5e; } </style>\n</head>\n\n<body>\n<p style=\"margin: 0; padding: 0; line-height: 1.4;\">{{greeting}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Jouw groep ({{objectName}}) heeft nog steeds een openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><strong>Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.</strong></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartButton\" data-id=\"paymentUrl\"><table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin: 5px 0;\">\n<tbody><tr>\n <td>\n <table cellspacing=\"0\" cellpadding=\"0\">\n <tbody><tr>\n <td style=\"border-radius: 7px;\" bgcolor=\"{{primaryColor}}\">\n <a class=\"button primary\" href=\"{{paymentUrl}}\" target=\"\" style=\"margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity;\">Betalen via het beheerdersportaal</a>\n </td>\n </tr>\n </tbody></table>\n </td>\n</tr>\n</tbody></table></div><p class=\"description\" style=\"color: #5e5e5e;\"><em>Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><strong><em>Vragen over je openstaand bedrag</em></strong><em>? Neem dan contact op met </em>{{organizationName}} <em>via </em>{{fromAddress}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><hr style=\"height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;\"><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><h2 style=\"margin: 0; padding: 0;\">Overzicht</h2><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Dit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartVariableBlock\" data-id=\"balanceTable\">{{balanceTable}}</div><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><em>Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via </em><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\"><em>deze knop</em></a><em> — hierna zal je nooit meer een e-mail van ons ontvangen.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\">Uitschrijven</a></p>\n</body>\n\n</html>', '{\"value\": {\"type\": \"doc\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"smartVariable\", \"attrs\": {\"id\": \"greeting\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Jouw groep (\", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"objectName\"}}, {\"text\": \") heeft nog steeds een openstaand bedrag van \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"outstandingBalance\"}}, {\"text\": \" bij \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \". Je kan de betaling in orde brengen door naar het beheerdersportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het beheerdersportaal.\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartButton\", \"attrs\": {\"id\": \"paymentUrl\"}, \"content\": [{\"text\": \"Betalen via het beheerdersportaal\", \"type\": \"text\"}]}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het beheerdersportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Vragen over je openstaand bedrag\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}, {\"type\": \"italic\"}]}, {\"text\": \"? Neem dan contact op met \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \" \", \"type\": \"text\"}, {\"text\": \"via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"fromAddress\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"horizontalRule\"}, {\"type\": \"paragraph\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"Overzicht\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Dit is een momentopname van jullie huidige openstaande bedrag. Je kan op elk moment naar het beheerdersportaal gaan om de huidige status te bekijken en te betalen.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartVariableBlock\", \"attrs\": {\"id\": \"balanceTable\"}}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"deze knop\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"text\": \" — hierna zal je nooit meer een e-mail van ons ontvangen.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\", \"content\": [{\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"Uitschrijven\", \"type\": \"text\"}]}]}]}, \"version\": 354}', '2025-01-04 12:09:03', '2025-01-04 12:09:03'),
4
+ ('b313f990-1f86-4571-931e-e3a46999cbea', '[Herinnering] Openstaand bedrag te betalen aan {{organizationName}}', NULL, NULL, NULL, 'UserBalanceReminder', '{{greeting}}Je hebt een openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.\n\n \n \n \n \n Betalen via het ledenportaal\n \n \n \n \n\nTwijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.Vragen over je openstaand bedrag? Neem dan contact op met {{organizationName}} via {{fromAddress}}OverzichtDit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.{{balanceTable}}Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via deze knop — hierna zal je nooit meer een e-mail van ons ontvangen.Uitschrijven', '<!DOCTYPE html>\n<html>\n\n<head>\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n<title>[Herinnering] Openstaand bedrag te betalen aan {{organizationName}}</title>\n<style type=\"text/css\">body {\n color: #000716;\n color: var(--color-dark, #000716);\n font-family: -apple-system-body, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 12pt;\n line-height: 1.4;\n}\n\np {\n margin: 0;\n padding: 0;\n line-height: 1.4;\n}\n\np.description {\n color: var(--color-gray-4, #5e5e5e);\n}\np.description a, p.description a:link, p.description a:visited, p.description a:active, p.description a:hover {\n text-decoration: underline;\n color: var(--color-gray-4, #5e5e5e);\n}\n\nstrong {\n font-weight: bold;\n}\n\nem {\n font-style: italic;\n}\n\nh1 {\n font-size: 30px;\n font-weight: bold;\n line-height: 1.2;\n margin: 0;\n padding: 0;\n}\n@media (max-width: 350px) {\n h1 {\n font-size: 24px;\n }\n}\n\nh2 {\n font-size: 20px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh3 {\n font-size: 16px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh4 {\n line-height: 1.2;\n font-weight: 500;\n margin: 0;\n padding: 0;\n}\n\nol, ul {\n list-style-position: outside;\n padding-left: 30px;\n}\n\nhr {\n height: 1px;\n background: var(--color-border, var(--color-gray-2, #dcdcdc));\n border-radius: 1px;\n padding: 0;\n margin: 20px 0;\n outline: none;\n border: 0;\n}\n\n.button {\n touch-action: inherit;\n user-select: auto;\n cursor: pointer;\n display: inline-block !important;\n line-height: 42px;\n font-size: 16px;\n font-weight: bold;\n}\n.button:active {\n transform: none;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\na, a:link, a:visited, a:active, a:hover {\n text-decoration: underline;\n color: blue;\n}\n\n.email-data-table {\n width: 100%;\n border-collapse: collapse;\n}\n.email-data-table th, .email-data-table td {\n text-align: left;\n padding: 10px 10px 10px 0;\n border-bottom: 1px solid var(--color-border, var(--color-gray-2, #dcdcdc));\n vertical-align: middle;\n}\n.email-data-table th:last-child, .email-data-table td:last-child {\n text-align: right;\n padding-right: 0;\n}\n.email-data-table thead {\n font-weight: bold;\n}\n.email-data-table thead th {\n font-size: 10pt;\n}\n.email-data-table h4 ~ p {\n padding-top: 3px;\n opacity: 0.8;\n font-size: 11pt;\n}\n\n.email-style-inline-code {\n font-family: monospace;\n white-space: pre-wrap;\n display: inline-block;\n}\n\n.email-style-description-small {\n font-size: 14px;\n line-height: 1.5;\n font-weight: normal;\n color: var(--color-gray-4, #5e5e5e);\n font-variation-settings: \"opsz\" 19;\n}\n\n.email-style-title-list {\n font-size: 16px;\n line-height: 1.3;\n font-weight: 500;\n}\n.email-style-title-list + p {\n padding-top: 3px;\n}\n\n.email-style-title-prefix-list {\n font-size: 11px;\n line-height: 1.5;\n font-weight: bold;\n color: {{primaryColor}};\n text-transform: uppercase;\n margin-bottom: 3px;\n}\n.email-style-title-prefix-list.error {\n color: #f0153d;\n}\n\n.email-style-price-base, .email-style-discount-price, .email-style-discount-old-price, .email-style-price {\n font-size: 15px;\n line-height: 1.4;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n}\n.email-style-price-base.disabled, .disabled.email-style-discount-price, .disabled.email-style-discount-old-price, .disabled.email-style-price {\n opacity: 0.6;\n}\n.email-style-price-base.negative, .negative.email-style-discount-price, .negative.email-style-discount-old-price, .negative.email-style-price {\n color: #f0153d;\n}\n\n.email-style-price {\n font-weight: bold;\n color: {{primaryColor}};\n}\n\n.email-style-discount-old-price {\n text-decoration: line-through;\n color: var(--color-gray-4, #5e5e5e);\n}\n\n.email-style-discount-price {\n font-weight: bold;\n color: #ff4747;\n margin-left: 5px;\n}\n\n.pre-wrap {\n white-space: pre-wrap;\n} hr {height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;} .button.primary { margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity; } .button.primary:active { transform: scale(0.95, 0.95); } .inline-link, .inline-link:link, .inline-link:visited, .inline-link:active, .inline-link:hover { margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation; } .inline-link:active { opacity: 0.5; } .description { color: #5e5e5e; } </style>\n</head>\n\n<body>\n<p style=\"margin: 0; padding: 0; line-height: 1.4;\">{{greeting}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Je hebt een openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><strong>Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.</strong></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartButton\" data-id=\"paymentUrl\"><table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin: 5px 0;\">\n<tbody><tr>\n <td>\n <table cellspacing=\"0\" cellpadding=\"0\">\n <tbody><tr>\n <td style=\"border-radius: 7px;\" bgcolor=\"{{primaryColor}}\">\n <a class=\"button primary\" href=\"{{paymentUrl}}\" target=\"\" style=\"margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity;\">Betalen via het ledenportaal</a>\n </td>\n </tr>\n </tbody></table>\n </td>\n</tr>\n</tbody></table></div><p class=\"description\" style=\"color: #5e5e5e;\"><em>Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><strong><em>Vragen over je openstaand bedrag</em></strong><em>? Neem dan contact op met </em>{{organizationName}} <em>via </em>{{fromAddress}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><hr style=\"height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;\"><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><h2 style=\"margin: 0; padding: 0;\">Overzicht</h2><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Dit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartVariableBlock\" data-id=\"balanceTable\">{{balanceTable}}</div><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><em>Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via </em><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\"><em>deze knop</em></a><em> — hierna zal je nooit meer een e-mail van ons ontvangen.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\">Uitschrijven</a></p>\n</body>\n\n</html>', '{\"value\": {\"type\": \"doc\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"smartVariable\", \"attrs\": {\"id\": \"greeting\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Je hebt een openstaand bedrag van \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"outstandingBalance\"}}, {\"text\": \" bij \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \". Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartButton\", \"attrs\": {\"id\": \"paymentUrl\"}, \"content\": [{\"text\": \"Betalen via het ledenportaal\", \"type\": \"text\"}]}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Vragen over je openstaand bedrag\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}, {\"type\": \"italic\"}]}, {\"text\": \"? Neem dan contact op met \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \" \", \"type\": \"text\"}, {\"text\": \"via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"fromAddress\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"horizontalRule\"}, {\"type\": \"paragraph\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"Overzicht\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Dit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartVariableBlock\", \"attrs\": {\"id\": \"balanceTable\"}}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"deze knop\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"text\": \" — hierna zal je nooit meer een e-mail van ons ontvangen.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\", \"content\": [{\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"Uitschrijven\", \"type\": \"text\"}]}]}]}, \"version\": 354}', '2025-01-04 12:04:03', '2025-01-04 12:04:03'),
5
+ ('3624fe92-3021-4b70-a740-7acac32db6ef', 'Jouw openstaand saldo bij {{organizationName}}', NULL, NULL, NULL, 'UserBalanceIncreaseNotification', '{{greeting}}Je hebt een nieuw openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.\n\n \n \n \n \n Betalen via het ledenportaal\n \n \n \n \n\nTwijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.Vragen over je openstaand bedrag? Neem dan contact op met {{organizationName}} via {{fromAddress}}OverzichtDit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.{{balanceTable}}Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via deze knop — hierna zal je nooit meer een e-mail van ons ontvangen.Uitschrijven', '<!DOCTYPE html>\n<html>\n\n<head>\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n<title>Jouw openstaand saldo bij {{organizationName}}</title>\n<style type=\"text/css\">body {\n color: #000716;\n color: var(--color-dark, #000716);\n font-family: -apple-system-body, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 12pt;\n line-height: 1.4;\n}\n\np {\n margin: 0;\n padding: 0;\n line-height: 1.4;\n}\n\np.description {\n color: var(--color-gray-4, #5e5e5e);\n}\np.description a, p.description a:link, p.description a:visited, p.description a:active, p.description a:hover {\n text-decoration: underline;\n color: var(--color-gray-4, #5e5e5e);\n}\n\nstrong {\n font-weight: bold;\n}\n\nem {\n font-style: italic;\n}\n\nh1 {\n font-size: 30px;\n font-weight: bold;\n line-height: 1.2;\n margin: 0;\n padding: 0;\n}\n@media (max-width: 350px) {\n h1 {\n font-size: 24px;\n }\n}\n\nh2 {\n font-size: 20px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh3 {\n font-size: 16px;\n line-height: 1.2;\n font-weight: bold;\n margin: 0;\n padding: 0;\n}\n\nh4 {\n line-height: 1.2;\n font-weight: 500;\n margin: 0;\n padding: 0;\n}\n\nol, ul {\n list-style-position: outside;\n padding-left: 30px;\n}\n\nhr {\n height: 1px;\n background: var(--color-border, var(--color-gray-2, #dcdcdc));\n border-radius: 1px;\n padding: 0;\n margin: 20px 0;\n outline: none;\n border: 0;\n}\n\n.button {\n touch-action: inherit;\n user-select: auto;\n cursor: pointer;\n display: inline-block !important;\n line-height: 42px;\n font-size: 16px;\n font-weight: bold;\n}\n.button:active {\n transform: none;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\na, a:link, a:visited, a:active, a:hover {\n text-decoration: underline;\n color: blue;\n}\n\n.email-data-table {\n width: 100%;\n border-collapse: collapse;\n}\n.email-data-table th, .email-data-table td {\n text-align: left;\n padding: 10px 10px 10px 0;\n border-bottom: 1px solid var(--color-border, var(--color-gray-2, #dcdcdc));\n vertical-align: middle;\n}\n.email-data-table th:last-child, .email-data-table td:last-child {\n text-align: right;\n padding-right: 0;\n}\n.email-data-table thead {\n font-weight: bold;\n}\n.email-data-table thead th {\n font-size: 10pt;\n}\n.email-data-table h4 ~ p {\n padding-top: 3px;\n opacity: 0.8;\n font-size: 11pt;\n}\n\n.email-style-inline-code {\n font-family: monospace;\n white-space: pre-wrap;\n display: inline-block;\n}\n\n.email-style-description-small {\n font-size: 14px;\n line-height: 1.5;\n font-weight: normal;\n color: var(--color-gray-4, #5e5e5e);\n font-variation-settings: \"opsz\" 19;\n}\n\n.email-style-title-list {\n font-size: 16px;\n line-height: 1.3;\n font-weight: 500;\n}\n.email-style-title-list + p {\n padding-top: 3px;\n}\n\n.email-style-title-prefix-list {\n font-size: 11px;\n line-height: 1.5;\n font-weight: bold;\n color: {{primaryColor}};\n text-transform: uppercase;\n margin-bottom: 3px;\n}\n.email-style-title-prefix-list.error {\n color: #f0153d;\n}\n\n.email-style-price-base, .email-style-discount-price, .email-style-discount-old-price, .email-style-price {\n font-size: 15px;\n line-height: 1.4;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n}\n.email-style-price-base.disabled, .disabled.email-style-discount-price, .disabled.email-style-discount-old-price, .disabled.email-style-price {\n opacity: 0.6;\n}\n.email-style-price-base.negative, .negative.email-style-discount-price, .negative.email-style-discount-old-price, .negative.email-style-price {\n color: #f0153d;\n}\n\n.email-style-price {\n font-weight: bold;\n color: {{primaryColor}};\n}\n\n.email-style-discount-old-price {\n text-decoration: line-through;\n color: var(--color-gray-4, #5e5e5e);\n}\n\n.email-style-discount-price {\n font-weight: bold;\n color: #ff4747;\n margin-left: 5px;\n}\n\n.pre-wrap {\n white-space: pre-wrap;\n} hr {height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;} .button.primary { margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity; } .button.primary:active { transform: scale(0.95, 0.95); } .inline-link, .inline-link:link, .inline-link:visited, .inline-link:active, .inline-link:hover { margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation; } .inline-link:active { opacity: 0.5; } .description { color: #5e5e5e; } </style>\n</head>\n\n<body>\n<p style=\"margin: 0; padding: 0; line-height: 1.4;\">{{greeting}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Je hebt een nieuw openstaand bedrag van {{outstandingBalance}} bij {{organizationName}}. Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><strong>Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.</strong></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartButton\" data-id=\"paymentUrl\"><table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin: 5px 0;\">\n<tbody><tr>\n <td>\n <table cellspacing=\"0\" cellpadding=\"0\">\n <tbody><tr>\n <td style=\"border-radius: 7px;\" bgcolor=\"{{primaryColor}}\">\n <a class=\"button primary\" href=\"{{paymentUrl}}\" target=\"\" style=\"margin: 0; text-decoration: none; font-size: 16px; font-weight: bold; color: {{primaryColorContrast}}; padding: 0 27px; line-height: 42px; background: {{primaryColor}}; text-align: center; border-radius: 7px; touch-action: manipulation; display: inline-block; transition: 0.2s transform, 0.2s opacity;\">Betalen via het ledenportaal</a>\n </td>\n </tr>\n </tbody></table>\n </td>\n</tr>\n</tbody></table></div><p class=\"description\" style=\"color: #5e5e5e;\"><em>Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><strong><em>Vragen over je openstaand bedrag</em></strong><em>? Neem dan contact op met </em>{{organizationName}} <em>via </em>{{fromAddress}}</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><hr style=\"height: 2px;background: #e7e7e7; border-radius: 1px; padding: 0; margin: 20px 0; outline: none; border: 0;\"><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><h2 style=\"margin: 0; padding: 0;\">Overzicht</h2><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\">Dit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.</p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><div data-type=\"smartVariableBlock\" data-id=\"balanceTable\">{{balanceTable}}</div><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><br></p><p class=\"description\" style=\"color: #5e5e5e;\"><em>Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via </em><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\"><em>deze knop</em></a><em> — hierna zal je nooit meer een e-mail van ons ontvangen.</em></p><p style=\"margin: 0; padding: 0; line-height: 1.4;\"><a class=\"inline-link\" href=\"{{unsubscribeUrl}}\" target=\"\" style=\"margin: 0; text-decoration: underline; font-size: inherit; font-weight: inherit; color: inherit; touch-action: manipulation;\">Uitschrijven</a></p>\n</body>\n\n</html>', '{\"value\": {\"type\": \"doc\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"smartVariable\", \"attrs\": {\"id\": \"greeting\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Je hebt een nieuw openstaand bedrag van \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"outstandingBalance\"}}, {\"text\": \" bij \", \"type\": \"text\"}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \". Je kan de betaling in orde brengen door naar het ledenportaal te gaan. Daar kan je ook een detail bekijken van het openstaande bedrag.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Gelieve niet zomaar geld over te schrijven op onze rekening, maar altijd te betalen via het ledenportaal.\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartButton\", \"attrs\": {\"id\": \"paymentUrl\"}, \"content\": [{\"text\": \"Betalen via het ledenportaal\", \"type\": \"text\"}]}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Twijfel je of één van onze e-mails wel echt is? Ga dan zelf rechtstreeks naar het ledenportaal via onze website zonder op een link in een e-mail te klikken. Via die weg kan je ook je openstaand bedrag bekijken en eventueel betalen - dan ben je altijd zeker dat je aan ons betaalt en het geen phishing is.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Vragen over je openstaand bedrag\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}, {\"type\": \"italic\"}]}, {\"text\": \"? Neem dan contact op met \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"organizationName\"}}, {\"text\": \" \", \"type\": \"text\"}, {\"text\": \"via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartVariable\", \"attrs\": {\"id\": \"fromAddress\"}}]}, {\"type\": \"paragraph\"}, {\"type\": \"horizontalRule\"}, {\"type\": \"paragraph\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"Overzicht\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"Dit is een momentopname van je huidige openstaande bedrag. Je kan op elk moment naar het ledenportaal gaan om de huidige status te bekijken en te betalen.\", \"type\": \"text\"}]}, {\"type\": \"paragraph\"}, {\"type\": \"smartVariableBlock\", \"attrs\": {\"id\": \"balanceTable\"}}, {\"type\": \"paragraph\"}, {\"type\": \"paragraph\"}, {\"type\": \"descriptiveText\", \"content\": [{\"text\": \"Komt deze e-mail bij jou terecht, maar weet je niet waarover dit gaat en denk je dat dit aan een andere persoon is gericht (bv. een typefout)? Dan schrijf je best uit voor onze e-mails via \", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}, {\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"deze knop\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"text\": \" — hierna zal je nooit meer een e-mail van ons ontvangen.\", \"type\": \"text\", \"marks\": [{\"type\": \"italic\"}]}]}, {\"type\": \"paragraph\", \"content\": [{\"type\": \"smartButtonInline\", \"attrs\": {\"id\": \"unsubscribeUrl\"}, \"content\": [{\"text\": \"Uitschrijven\", \"type\": \"text\"}]}]}]}, \"version\": 354}', '2025-01-04 10:09:48', '2025-01-04 10:09:48');
@@ -1,10 +1,10 @@
1
- import { column, Model, SQLResultNamespacedRow } from '@simonbackx/simple-database';
1
+ import { column } from '@simonbackx/simple-database';
2
2
  import { ArrayDecoder, Decoder, MapDecoder, StringDecoder } from '@simonbackx/simple-encoding';
3
- import { SQL, SQLSelect } from '@stamhoofd/sql';
3
+ import { QueryableModel } from '@stamhoofd/sql';
4
4
  import { AuditLogPatchItem, AuditLogReplacement, AuditLogSource, AuditLogType } from '@stamhoofd/structures';
5
5
  import { v7 as uuidv7 } from 'uuid';
6
6
 
7
- export class AuditLog extends Model {
7
+ export class AuditLog extends QueryableModel {
8
8
  static table = 'audit_logs';
9
9
 
10
10
  // Columns
@@ -67,22 +67,4 @@ export class AuditLog extends Model {
67
67
  },
68
68
  })
69
69
  createdAt: Date;
70
-
71
- /**
72
- * Experimental: needs to move to library
73
- */
74
- static select() {
75
- const transformer = (row: SQLResultNamespacedRow): AuditLog => {
76
- const d = (this as typeof AuditLog & typeof Model).fromRow(row[this.table] as any) as AuditLog | undefined;
77
-
78
- if (!d) {
79
- throw new Error('EmailTemplate not found');
80
- }
81
-
82
- return d;
83
- };
84
-
85
- const select = new SQLSelect(transformer, SQL.wildcard());
86
- return select.from(SQL.table(this.table));
87
- }
88
70
  }
@@ -1,17 +1,17 @@
1
- import { column, Database, Model, SQLResultNamespacedRow } from '@simonbackx/simple-database';
1
+ import { column, Database } from '@simonbackx/simple-database';
2
2
  import { BalanceItemPaymentWithPayment, BalanceItemPaymentWithPrivatePayment, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItem as BalanceItemStruct, BalanceItemType, BalanceItemWithPayments, BalanceItemWithPrivatePayments, Payment as PaymentStruct, PrivatePayment } from '@stamhoofd/structures';
3
3
  import { Formatter } from '@stamhoofd/utility';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
 
6
6
  import { EnumDecoder, MapDecoder } from '@simonbackx/simple-encoding';
7
- import { SQL, SQLSelect } from '@stamhoofd/sql';
8
- import { Document, Payment } from './';
7
+ import { QueryableModel, SQL } from '@stamhoofd/sql';
8
+ import { Document, MemberUser, Payment } from './';
9
9
  import { CachedBalance } from './CachedBalance';
10
10
 
11
11
  /**
12
12
  * Keeps track of how much a member/user owes or needs to be reimbursed.
13
13
  */
14
- export class BalanceItem extends Model {
14
+ export class BalanceItem extends QueryableModel {
15
15
  static table = 'balance_items';
16
16
 
17
17
  @column({
@@ -254,7 +254,15 @@ export class BalanceItem extends Model {
254
254
  const memberIds = Formatter.uniqueArray(filteredItems.map(p => p.memberId).filter(id => id !== null));
255
255
  await CachedBalance.updateForMembers(organizationId, memberIds);
256
256
 
257
- const userIds = Formatter.uniqueArray(filteredItems.filter(p => p.memberId === null && p.userId !== null).map(p => p.userId!));
257
+ let userIds = filteredItems.filter(p => p.userId !== null).map(p => p.userId!);
258
+
259
+ if (memberIds.length) {
260
+ // Now also include the userIds of the members
261
+ const userMemberIds = (await MemberUser.select().where('membersId', memberIds).fetch()).map(m => m.usersId);
262
+ userIds.push(...userMemberIds);
263
+ }
264
+ userIds = Formatter.uniqueArray(userIds);
265
+
258
266
  await CachedBalance.updateForUsers(organizationId, userIds);
259
267
 
260
268
  const organizationIds = Formatter.uniqueArray(filteredItems.map(p => p.payingOrganizationId).filter(id => id !== null));
@@ -329,13 +337,6 @@ export class BalanceItem extends Model {
329
337
  await Database.update(query, params);
330
338
  }
331
339
 
332
- /**
333
- * @deprecated
334
- */
335
- static async updatePricePending(balanceItemIds: string[] | 'all') {
336
- // deprecated
337
- }
338
-
339
340
  static async loadPayments(items: BalanceItem[]) {
340
341
  if (items.length == 0) {
341
342
  return { balanceItemPayments: [], payments: [] };
@@ -462,22 +463,4 @@ export class BalanceItem extends Model {
462
463
  .whereNot('status', BalanceItemStatus.Hidden)
463
464
  .fetch();
464
465
  }
465
-
466
- /**
467
- * Experimental: needs to move to library
468
- */
469
- static select() {
470
- const transformer = (row: SQLResultNamespacedRow): BalanceItem => {
471
- const d = (this as typeof BalanceItem & typeof Model).fromRow(row[this.table] as any) as BalanceItem | undefined;
472
-
473
- if (!d) {
474
- throw new Error('EmailTemplate not found');
475
- }
476
-
477
- return d;
478
- };
479
-
480
- const select = new SQLSelect(transformer, SQL.wildcard());
481
- return select.from(SQL.table(this.table));
482
- }
483
466
  }
@@ -1,14 +1,14 @@
1
- import { column, ManyToOneRelation, Model, SQLResultNamespacedRow } from '@simonbackx/simple-database';
1
+ import { column, ManyToOneRelation } from '@simonbackx/simple-database';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
 
4
4
  import { BalanceItem, Payment } from './';
5
- import { SQLSelect, SQL } from '@stamhoofd/sql';
5
+ import { QueryableModel } from '@stamhoofd/sql';
6
6
 
7
7
  /**
8
8
  * Keeps track of all the created payments of a balance item, which contains the (tries) to pay a balance item.
9
9
  * It also keeps track of how much a given payment is split between multiple balance items, which makes it possible to pay a balance item partially.
10
10
  */
11
- export class BalanceItemPayment extends Model {
11
+ export class BalanceItemPayment extends QueryableModel {
12
12
  static table = 'balance_item_payments';
13
13
 
14
14
  @column({
@@ -57,22 +57,4 @@ export class BalanceItemPayment extends Model {
57
57
 
58
58
  static balanceItem = new ManyToOneRelation(BalanceItem, 'balanceItem');
59
59
  static payment = new ManyToOneRelation(Payment, 'payment');
60
-
61
- /**
62
- * Experimental: needs to move to library
63
- */
64
- static select() {
65
- const transformer = (row: SQLResultNamespacedRow): BalanceItemPayment => {
66
- const d = (this as typeof BalanceItemPayment & typeof Model).fromRow(row[this.table] as any) as BalanceItemPayment | undefined;
67
-
68
- if (!d) {
69
- throw new Error('EmailTemplate not found');
70
- }
71
-
72
- return d;
73
- };
74
-
75
- const select = new SQLSelect(transformer, SQL.wildcard());
76
- return select.from(SQL.table(this.table));
77
- }
78
60
  }
@@ -1,7 +1,8 @@
1
- import { column, Model } from '@simonbackx/simple-database';
1
+ import { column } from '@simonbackx/simple-database';
2
+ import { QueryableModel } from '@stamhoofd/sql';
2
3
  import { v4 as uuidv4 } from 'uuid';
3
4
 
4
- export class BuckarooPayment extends Model {
5
+ export class BuckarooPayment extends QueryableModel {
5
6
  static table = 'buckaroo_payments';
6
7
 
7
8
  @column({
@@ -1,13 +1,14 @@
1
- import { column, Model, SQLResultNamespacedRow } from '@simonbackx/simple-database';
2
- import { SQL, SQLAlias, SQLMin, SQLSelect, SQLSelectAs, SQLSum, SQLWhere, SQLWhereSign } from '@stamhoofd/sql';
1
+ import { column } from '@simonbackx/simple-database';
2
+ import { QueryableModel, SQL, SQLAlias, SQLMin, SQLSelectAs, SQLSum, SQLWhere, SQLWhereSign } from '@stamhoofd/sql';
3
3
  import { BalanceItemStatus, BalanceItem as BalanceItemStruct, ReceivableBalanceType } from '@stamhoofd/structures';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
  import { BalanceItem } from './BalanceItem';
6
+ import { MemberUser } from './MemberUser';
6
7
 
7
8
  /**
8
9
  * Keeps track of how much a member/user owes or needs to be reimbursed.
9
10
  */
10
- export class CachedBalance extends Model {
11
+ export class CachedBalance extends QueryableModel {
11
12
  static table = 'cached_outstanding_balances';
12
13
 
13
14
  @column({
@@ -50,6 +51,15 @@ export class CachedBalance extends Model {
50
51
  @column({ type: 'datetime', nullable: true })
51
52
  nextDueAt: Date | null = null;
52
53
 
54
+ @column({ type: 'datetime', nullable: true })
55
+ lastReminderEmail: Date | null = null;
56
+
57
+ @column({ type: 'integer' })
58
+ lastReminderAmountOpen = 0;
59
+
60
+ @column({ type: 'integer' })
61
+ reminderEmailCount = 0;
62
+
53
63
  @column({
54
64
  type: 'datetime', beforeSave(old?: any) {
55
65
  if (old !== undefined) {
@@ -108,7 +118,7 @@ export class CachedBalance extends Model {
108
118
  }
109
119
  }
110
120
 
111
- static async balanceForObjects(organizationId: string, objectIds: string[], objectType: ReceivableBalanceType) {
121
+ static async balanceForObjects(organizationId: string, objectIds: string[], objectType: ReceivableBalanceType, includeUserMembers = false) {
112
122
  if (objectIds.length === 0) {
113
123
  return [];
114
124
  }
@@ -119,7 +129,7 @@ export class CachedBalance extends Model {
119
129
  case ReceivableBalanceType.member:
120
130
  return await this.balanceForMembers(organizationId, objectIds);
121
131
  case ReceivableBalanceType.user:
122
- return await this.balanceForUsers(organizationId, objectIds);
132
+ return await this.balanceForUsers(organizationId, objectIds, includeUserMembers);
123
133
  case ReceivableBalanceType.registration:
124
134
  return await this.balanceForRegistrations(organizationId, objectIds);
125
135
  }
@@ -323,7 +333,7 @@ export class CachedBalance extends Model {
323
333
  if (result.length === 0) {
324
334
  return;
325
335
  }
326
- const query = SQL.insert(this.table)
336
+ const query = this.insert()
327
337
  .columns(
328
338
  'id',
329
339
  'organizationId',
@@ -335,6 +345,7 @@ export class CachedBalance extends Model {
335
345
  'nextDueAt',
336
346
  'createdAt',
337
347
  'updatedAt',
348
+ 'reminderEmailCount',
338
349
  )
339
350
  .values(...result.map(([objectId, { amountPaid, amountOpen, amountPending, nextDueAt }]) => {
340
351
  return [
@@ -348,6 +359,7 @@ export class CachedBalance extends Model {
348
359
  nextDueAt,
349
360
  new Date(),
350
361
  new Date(),
362
+ 0,
351
363
  ];
352
364
  }))
353
365
  .as('v')
@@ -357,6 +369,22 @@ export class CachedBalance extends Model {
357
369
  SQL.assignment('amountPending', SQL.column('v', 'amountPending')),
358
370
  SQL.assignment('nextDueAt', SQL.column('v', 'nextDueAt')),
359
371
  SQL.assignment('updatedAt', SQL.column('v', 'updatedAt')),
372
+
373
+ // Reset email count if amountOpen is zero
374
+ SQL.assignment(
375
+ 'reminderEmailCount',
376
+ SQL.if(SQL.column('v', 'amountOpen'), 0)
377
+ .then(0)
378
+ .else(SQL.column('reminderEmailCount')),
379
+ ),
380
+
381
+ // Reset lastReminderEmail if amountOpen is zero
382
+ SQL.assignment(
383
+ 'lastReminderEmail',
384
+ SQL.if(SQL.column('v', 'amountOpen'), 0)
385
+ .then(null)
386
+ .else(SQL.column('lastReminderEmail')),
387
+ ),
360
388
  );
361
389
 
362
390
  await query.insert();
@@ -390,7 +418,43 @@ export class CachedBalance extends Model {
390
418
  if (userIds.length === 0) {
391
419
  return;
392
420
  }
393
- const results = await this.fetchForObjects(organizationId, userIds, 'userId', SQL.where('memberId', null));
421
+ const results = await this.fetchForObjects(
422
+ organizationId,
423
+ userIds,
424
+ 'userId',
425
+ SQL.where('memberId', null),
426
+ );
427
+
428
+ // We also need to include the balance of the related members for each user
429
+
430
+ // Fetch members of these users
431
+ const memberUsers = await MemberUser.select().where('usersId', userIds).fetch();
432
+
433
+ const memberCachedBalances = await this.getForObjects(memberUsers.map(mu => mu.membersId), organizationId);
434
+
435
+ for (const memberCachedBalance of memberCachedBalances) {
436
+ const userIds = memberUsers.filter(mu => mu.membersId === memberCachedBalance.objectId).map(mu => mu.usersId);
437
+
438
+ for (const userId of userIds) {
439
+ // if already in results: append
440
+ const result = results.find(([objectId]) => objectId === userId);
441
+
442
+ if (result) {
443
+ result[1].amountPaid += memberCachedBalance.amountPaid;
444
+ result[1].amountOpen += memberCachedBalance.amountOpen;
445
+ result[1].amountPending += memberCachedBalance.amountPending;
446
+ if (memberCachedBalance.nextDueAt && (!result[1].nextDueAt || memberCachedBalance.nextDueAt > result[1].nextDueAt)) {
447
+ result[1].nextDueAt = memberCachedBalance.nextDueAt;
448
+ }
449
+ }
450
+ else {
451
+ // Not possible
452
+ throw new Error('User not found in results');
453
+ }
454
+ }
455
+ }
456
+
457
+ // Fetch cached balance of these members and merge the results
394
458
  await this.setForResults(organizationId, results, ReceivableBalanceType.user);
395
459
  }
396
460
 
@@ -408,11 +472,20 @@ export class CachedBalance extends Model {
408
472
  return await this.fetchBalanceItems(organizationId, memberIds, 'memberId');
409
473
  }
410
474
 
411
- static async balanceForUsers(organizationId: string, userIds: string[]) {
475
+ static async balanceForUsers(organizationId: string, userIds: string[], includeUserMembers = false) {
412
476
  if (userIds.length === 0) {
413
477
  return [];
414
478
  }
415
- return await this.fetchBalanceItems(organizationId, userIds, 'userId', SQL.where('memberId', null));
479
+ const base = await this.fetchBalanceItems(organizationId, userIds, 'userId', SQL.where('memberId', null));
480
+
481
+ if (!includeUserMembers) {
482
+ return base;
483
+ }
484
+
485
+ const memberUsers = await MemberUser.select().where('usersId', userIds).fetch();
486
+ const memberIds = memberUsers.map(mu => mu.membersId);
487
+ const memberCachedBalances = await this.balanceForMembers(organizationId, memberIds);
488
+ return base.concat(memberCachedBalances);
416
489
  }
417
490
 
418
491
  static async balanceForRegistrations(organizationId: string, registrationIds: string[]) {
@@ -421,22 +494,4 @@ export class CachedBalance extends Model {
421
494
  }
422
495
  return await this.fetchBalanceItems(organizationId, registrationIds, 'registrationId');
423
496
  }
424
-
425
- /**
426
- * Experimental: needs to move to library
427
- */
428
- static select() {
429
- const transformer = (row: SQLResultNamespacedRow): CachedBalance => {
430
- const d = (this as typeof CachedBalance & typeof Model).fromRow(row[this.table] as any) as CachedBalance | undefined;
431
-
432
- if (!d) {
433
- throw new Error('EmailTemplate not found');
434
- }
435
-
436
- return d;
437
- };
438
-
439
- const select = new SQLSelect(transformer, SQL.wildcard());
440
- return select.from(SQL.table(this.table));
441
- }
442
497
  }
@@ -1,14 +1,15 @@
1
- import { column, Model } from '@simonbackx/simple-database';
2
- import { Document as DocumentStruct, DocumentData, DocumentStatus, Platform, Version } from '@stamhoofd/structures';
1
+ import { column } from '@simonbackx/simple-database';
2
+ import { DocumentData, DocumentStatus, Document as DocumentStruct, Platform, Version } from '@stamhoofd/structures';
3
3
  import { Formatter } from '@stamhoofd/utility';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
 
6
+ import { QueryableModel } from '@stamhoofd/sql';
6
7
  import { render } from '../helpers/Handlebars';
7
8
  import { RegistrationWithMember } from './Member';
8
9
  import { Organization } from './Organization';
9
10
  import { Registration } from './Registration';
10
11
 
11
- export class Document extends Model {
12
+ export class Document extends QueryableModel {
12
13
  static table = 'documents';
13
14
 
14
15
  @column({ primary: true, type: 'string', beforeSave(value) {
@@ -1,20 +1,21 @@
1
- import { column, Model } from '@simonbackx/simple-database';
1
+ import { column } from '@simonbackx/simple-database';
2
2
  import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
3
3
  import { QueueHandler } from '@stamhoofd/queues';
4
4
  import { BalanceItemStatus, DocumentData, DocumentPrivateSettings, DocumentSettings, DocumentStatus, DocumentTemplatePrivate, GroupType, NationalRegisterNumberOptOut, Parent, RecordAddressAnswer, RecordAnswer, RecordAnswerDecoder, RecordDateAnswer, RecordPriceAnswer, RecordSettings, RecordTextAnswer, RecordType } from '@stamhoofd/structures';
5
5
  import { Sorter } from '@stamhoofd/utility';
6
6
  import { v4 as uuidv4 } from 'uuid';
7
7
 
8
+ import { QueryableModel } from '@stamhoofd/sql';
8
9
  import { render } from '../helpers/Handlebars';
9
10
  import { BalanceItem } from './BalanceItem';
10
11
  import { Document } from './Document';
11
12
  import { Group } from './Group';
12
13
  import { Member, RegistrationWithMember } from './Member';
13
14
  import { Organization } from './Organization';
14
- import { User } from './User';
15
15
  import { Registration } from './Registration';
16
+ import { User } from './User';
16
17
 
17
- export class DocumentTemplate extends Model {
18
+ export class DocumentTemplate extends QueryableModel {
18
19
  static table = 'document_templates';
19
20
 
20
21
  @column({ primary: true, type: 'string', beforeSave(value) {