@stamhoofd/models 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 (296) hide show
  1. package/dist/factories/GroupFactory.d.ts.map +1 -1
  2. package/dist/factories/GroupFactory.js +1 -1
  3. package/dist/factories/GroupFactory.js.map +1 -1
  4. package/dist/factories/OrganizationFactory.d.ts +2 -1
  5. package/dist/factories/OrganizationFactory.d.ts.map +1 -1
  6. package/dist/factories/OrganizationFactory.js +9 -1
  7. package/dist/factories/OrganizationFactory.js.map +1 -1
  8. package/dist/factories/RegistrationInvitationFactory.d.ts +15 -0
  9. package/dist/factories/RegistrationInvitationFactory.d.ts.map +1 -0
  10. package/dist/factories/RegistrationInvitationFactory.js +18 -0
  11. package/dist/factories/RegistrationInvitationFactory.js.map +1 -0
  12. package/dist/factories/STPackageFactory.js.map +1 -1
  13. package/dist/factories/UserFactory.d.ts.map +1 -1
  14. package/dist/factories/UserFactory.js +2 -2
  15. package/dist/factories/UserFactory.js.map +1 -1
  16. package/dist/factories/index.d.ts +1 -0
  17. package/dist/factories/index.d.ts.map +1 -1
  18. package/dist/factories/index.js +1 -0
  19. package/dist/factories/index.js.map +1 -1
  20. package/dist/helpers/EmailBuilder.d.ts.map +1 -1
  21. package/dist/helpers/EmailBuilder.js +8 -8
  22. package/dist/helpers/EmailBuilder.js.map +1 -1
  23. package/dist/helpers/Handlebars.d.ts.map +1 -1
  24. package/dist/helpers/Handlebars.js +10 -1
  25. package/dist/helpers/Handlebars.js.map +1 -1
  26. package/dist/helpers/InvoiceCounter.d.ts +24 -0
  27. package/dist/helpers/InvoiceCounter.d.ts.map +1 -0
  28. package/dist/helpers/InvoiceCounter.js +133 -0
  29. package/dist/helpers/InvoiceCounter.js.map +1 -0
  30. package/dist/index.d.ts +0 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +0 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/migrations/1605262045-import-postcodes.d.ts.map +1 -1
  35. package/dist/migrations/1605262045-import-postcodes.js +58 -24
  36. package/dist/migrations/1605262045-import-postcodes.js.map +1 -1
  37. package/dist/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -1
  38. package/dist/migrations/1605262046-import-postcodes-nl.js +54 -17
  39. package/dist/migrations/1605262046-import-postcodes-nl.js.map +1 -1
  40. package/dist/migrations/1719567881-organization-periodId.sql +2 -0
  41. package/dist/migrations/1719567882-groups-periodId.sql +2 -0
  42. package/dist/migrations/1720080975-convert-charset.d.ts +4 -0
  43. package/dist/migrations/1720080975-convert-charset.d.ts.map +1 -0
  44. package/dist/migrations/1720080975-convert-charset.js +26 -0
  45. package/dist/migrations/1720080975-convert-charset.js.map +1 -0
  46. package/dist/migrations/1720080976-convert-charset-leads.d.ts.map +1 -1
  47. package/dist/migrations/1720080976-convert-charset-leads.js +11 -10
  48. package/dist/migrations/1720080976-convert-charset-leads.js.map +1 -1
  49. package/dist/migrations/1721400546-users-memberId.sql +2 -0
  50. package/dist/migrations/1722269236-group-waitinglist-id.sql +2 -1
  51. package/dist/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
  52. package/dist/migrations/1722525787-depending-balance-item.sql +2 -0
  53. package/dist/migrations/1722963554-registration-group-price-and-options.sql +1 -1
  54. package/dist/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
  55. package/dist/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
  56. package/dist/migrations/1733317910-paying-organization-id-fk.sql +2 -0
  57. package/dist/migrations/1733504881-negative-invoice-id.sql +6 -0
  58. package/dist/migrations/1733994455-balance-item-status-open.d.ts +4 -0
  59. package/dist/migrations/1733994455-balance-item-status-open.d.ts.map +1 -0
  60. package/dist/migrations/1733994455-balance-item-status-open.js +28 -0
  61. package/dist/migrations/1733994455-balance-item-status-open.js.map +1 -0
  62. package/dist/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
  63. package/dist/migrations/1763216320-bigint-balance-items.sql +5 -0
  64. package/dist/migrations/1763216320-bigint-orders.sql +2 -0
  65. package/dist/migrations/1763216320-bigint-payments.sql +2 -0
  66. package/dist/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
  67. package/dist/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
  68. package/dist/migrations/1769087809-payments-invoice-id.sql +2 -0
  69. package/dist/migrations/1772033555-balance-item-package-id.sql +2 -0
  70. package/dist/migrations/1776873089-create-registration-invitations-table.sql +13 -0
  71. package/dist/migrations/1778657958-payments-create-mandate.sql +2 -0
  72. package/dist/migrations/1778657959-payments-mandate-id.sql +2 -0
  73. package/dist/migrations/1778796615-payments-reversing-payment-id.sql +5 -0
  74. package/dist/migrations/1778950642-price-invoiced.sql +2 -0
  75. package/dist/migrations/1779443446-transfer-fees.sql +3 -0
  76. package/dist/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
  77. package/dist/migrations/1779968328-payments-admin-user-id.sql +5 -0
  78. package/dist/migrations/1779970611-payments-refunded-amount.sql +2 -0
  79. package/dist/migrations/1779972640-balance-items-failed-at.sql +2 -0
  80. package/dist/migrations/1780328285-document-template-locked.sql +2 -0
  81. package/dist/migrations/1780328286-document-locked.sql +2 -0
  82. package/dist/migrations/1780412083-documents-set-locked.d.ts +4 -0
  83. package/dist/migrations/1780412083-documents-set-locked.d.ts.map +1 -0
  84. package/dist/migrations/1780412083-documents-set-locked.js +14 -0
  85. package/dist/migrations/1780412083-documents-set-locked.js.map +1 -0
  86. package/dist/migrations/1780928401-v1-groups-migration-data.d.ts +4 -0
  87. package/dist/migrations/1780928401-v1-groups-migration-data.d.ts.map +1 -0
  88. package/dist/migrations/1780928401-v1-groups-migration-data.js +44 -0
  89. package/dist/migrations/1780928401-v1-groups-migration-data.js.map +1 -0
  90. package/dist/models/BalanceItem.d.ts +14 -1
  91. package/dist/models/BalanceItem.d.ts.map +1 -1
  92. package/dist/models/BalanceItem.js +91 -41
  93. package/dist/models/BalanceItem.js.map +1 -1
  94. package/dist/models/CachedBalance.d.ts +6 -1
  95. package/dist/models/CachedBalance.d.ts.map +1 -1
  96. package/dist/models/CachedBalance.js +3 -2
  97. package/dist/models/CachedBalance.js.map +1 -1
  98. package/dist/models/Document.d.ts +4 -0
  99. package/dist/models/Document.d.ts.map +1 -1
  100. package/dist/models/Document.js +26 -3
  101. package/dist/models/Document.js.map +1 -1
  102. package/dist/models/DocumentTemplate.d.ts +4 -0
  103. package/dist/models/DocumentTemplate.d.ts.map +1 -1
  104. package/dist/models/DocumentTemplate.js +37 -1
  105. package/dist/models/DocumentTemplate.js.map +1 -1
  106. package/dist/models/Email.d.ts.map +1 -1
  107. package/dist/models/Email.js +1 -1
  108. package/dist/models/Email.js.map +1 -1
  109. package/dist/models/EmailVerificationCode.d.ts.map +1 -1
  110. package/dist/models/EmailVerificationCode.js +3 -13
  111. package/dist/models/EmailVerificationCode.js.map +1 -1
  112. package/dist/models/Event.d.ts +2 -1
  113. package/dist/models/Event.d.ts.map +1 -1
  114. package/dist/models/Event.js +3 -0
  115. package/dist/models/Event.js.map +1 -1
  116. package/dist/models/EventNotification.d.ts.map +1 -1
  117. package/dist/models/EventNotification.js +5 -5
  118. package/dist/models/EventNotification.js.map +1 -1
  119. package/dist/models/Group.d.ts +4 -0
  120. package/dist/models/Group.d.ts.map +1 -1
  121. package/dist/models/Group.js +17 -0
  122. package/dist/models/Group.js.map +1 -1
  123. package/dist/models/Invoice.d.ts +1 -0
  124. package/dist/models/Invoice.d.ts.map +1 -1
  125. package/dist/models/Invoice.js +8 -8
  126. package/dist/models/Invoice.js.map +1 -1
  127. package/dist/models/MemberPlatformMembership.d.ts.map +1 -1
  128. package/dist/models/MemberPlatformMembership.js +9 -0
  129. package/dist/models/MemberPlatformMembership.js.map +1 -1
  130. package/dist/models/MollieToken.d.ts +4 -8
  131. package/dist/models/MollieToken.d.ts.map +1 -1
  132. package/dist/models/MollieToken.js +37 -90
  133. package/dist/models/MollieToken.js.map +1 -1
  134. package/dist/models/Order.d.ts.map +1 -1
  135. package/dist/models/Order.js +1 -0
  136. package/dist/models/Order.js.map +1 -1
  137. package/dist/models/Organization.d.ts +30 -23
  138. package/dist/models/Organization.d.ts.map +1 -1
  139. package/dist/models/Organization.js +113 -61
  140. package/dist/models/Organization.js.map +1 -1
  141. package/dist/models/PasswordToken.d.ts +5 -1
  142. package/dist/models/PasswordToken.d.ts.map +1 -1
  143. package/dist/models/PasswordToken.js +18 -17
  144. package/dist/models/PasswordToken.js.map +1 -1
  145. package/dist/models/Payment.d.ts +35 -3
  146. package/dist/models/Payment.d.ts.map +1 -1
  147. package/dist/models/Payment.js +66 -3
  148. package/dist/models/Payment.js.map +1 -1
  149. package/dist/models/Registration.d.ts +1 -0
  150. package/dist/models/Registration.d.ts.map +1 -1
  151. package/dist/models/Registration.js +4 -3
  152. package/dist/models/Registration.js.map +1 -1
  153. package/dist/models/RegistrationInvitation.d.ts +14 -0
  154. package/dist/models/RegistrationInvitation.d.ts.map +1 -0
  155. package/dist/models/RegistrationInvitation.js +45 -0
  156. package/dist/models/RegistrationInvitation.js.map +1 -0
  157. package/dist/models/STCredit.d.ts +4 -0
  158. package/dist/models/STCredit.d.ts.map +1 -1
  159. package/dist/models/STCredit.js +28 -0
  160. package/dist/models/STCredit.js.map +1 -1
  161. package/dist/models/STInvoice.d.ts +7 -1
  162. package/dist/models/STInvoice.d.ts.map +1 -1
  163. package/dist/models/STInvoice.js +9 -0
  164. package/dist/models/STInvoice.js.map +1 -1
  165. package/dist/models/STPackage.d.ts +4 -0
  166. package/dist/models/STPackage.d.ts.map +1 -1
  167. package/dist/models/STPackage.js +12 -1
  168. package/dist/models/STPackage.js.map +1 -1
  169. package/dist/models/UsedRegisterCode.d.ts +9 -0
  170. package/dist/models/UsedRegisterCode.d.ts.map +1 -1
  171. package/dist/models/UsedRegisterCode.js +31 -0
  172. package/dist/models/UsedRegisterCode.js.map +1 -1
  173. package/dist/models/User.d.ts +1 -1
  174. package/dist/models/User.d.ts.map +1 -1
  175. package/dist/models/User.js +1 -1
  176. package/dist/models/User.js.map +1 -1
  177. package/dist/models/_relations.js +25 -0
  178. package/dist/models/_relations.js.map +1 -1
  179. package/dist/models/addresses/City.d.ts +4 -4
  180. package/dist/models/addresses/City.d.ts.map +1 -1
  181. package/dist/models/addresses/City.js +6 -6
  182. package/dist/models/addresses/City.js.map +1 -1
  183. package/dist/models/addresses/PostalCode.d.ts +2 -2
  184. package/dist/models/addresses/PostalCode.d.ts.map +1 -1
  185. package/dist/models/addresses/PostalCode.js +4 -3
  186. package/dist/models/addresses/PostalCode.js.map +1 -1
  187. package/dist/models/addresses/Street.d.ts +3 -3
  188. package/dist/models/addresses/Street.d.ts.map +1 -1
  189. package/dist/models/addresses/Street.js +4 -4
  190. package/dist/models/addresses/Street.js.map +1 -1
  191. package/dist/models/index.d.ts +2 -0
  192. package/dist/models/index.d.ts.map +1 -1
  193. package/dist/models/index.js +2 -0
  194. package/dist/models/index.js.map +1 -1
  195. package/dist/models/v1GroupMigrationData.d.ts +22 -0
  196. package/dist/models/v1GroupMigrationData.d.ts.map +1 -0
  197. package/dist/models/v1GroupMigrationData.js +48 -0
  198. package/dist/models/v1GroupMigrationData.js.map +1 -0
  199. package/package.json +41 -13
  200. package/src/factories/GroupFactory.ts +4 -6
  201. package/src/factories/OrganizationFactory.ts +12 -4
  202. package/src/factories/RegistrationInvitationFactory.ts +24 -0
  203. package/src/factories/STPackageFactory.ts +2 -2
  204. package/src/factories/UserFactory.ts +4 -5
  205. package/src/factories/index.ts +1 -0
  206. package/src/helpers/EmailBuilder.ts +19 -28
  207. package/src/helpers/Handlebars.ts +10 -1
  208. package/src/helpers/InvoiceCounter.test.ts +220 -0
  209. package/src/helpers/InvoiceCounter.ts +162 -0
  210. package/src/index.ts +0 -1
  211. package/src/migrations/1605262045-import-postcodes.ts +62 -25
  212. package/src/migrations/1605262046-import-postcodes-nl.ts +58 -17
  213. package/src/migrations/1719567881-organization-periodId.sql +2 -0
  214. package/src/migrations/1719567882-groups-periodId.sql +2 -0
  215. package/src/migrations/1720080975-convert-charset.ts +34 -0
  216. package/src/migrations/1720080976-convert-charset-leads.ts +16 -13
  217. package/src/migrations/1721400546-users-memberId.sql +2 -0
  218. package/src/migrations/1722269236-group-waitinglist-id.sql +2 -1
  219. package/src/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
  220. package/src/migrations/1722525787-depending-balance-item.sql +2 -0
  221. package/src/migrations/1722963554-registration-group-price-and-options.sql +1 -1
  222. package/src/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
  223. package/src/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
  224. package/src/migrations/1733317910-paying-organization-id-fk.sql +2 -0
  225. package/src/migrations/1733504881-negative-invoice-id.sql +6 -0
  226. package/src/migrations/1733994455-balance-item-status-open.ts +30 -0
  227. package/src/migrations/1763216320-bigint-balance-item-payments.sql +2 -0
  228. package/src/migrations/1763216320-bigint-balance-items.sql +5 -0
  229. package/src/migrations/1763216320-bigint-orders.sql +2 -0
  230. package/src/migrations/1763216320-bigint-payments.sql +2 -0
  231. package/src/migrations/1763216332-bigint-balance-item-price-total.sql +2 -0
  232. package/src/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
  233. package/src/migrations/1769087809-payments-invoice-id.sql +2 -0
  234. package/src/migrations/1772033555-balance-item-package-id.sql +2 -0
  235. package/src/migrations/1776873089-create-registration-invitations-table.sql +13 -0
  236. package/src/migrations/1778657958-payments-create-mandate.sql +2 -0
  237. package/src/migrations/1778657959-payments-mandate-id.sql +2 -0
  238. package/src/migrations/1778796615-payments-reversing-payment-id.sql +5 -0
  239. package/src/migrations/1778950642-price-invoiced.sql +2 -0
  240. package/src/migrations/1779443446-transfer-fees.sql +3 -0
  241. package/src/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
  242. package/src/migrations/1779968328-payments-admin-user-id.sql +5 -0
  243. package/src/migrations/1779970611-payments-refunded-amount.sql +2 -0
  244. package/src/migrations/1779972640-balance-items-failed-at.sql +2 -0
  245. package/src/migrations/1780328285-document-template-locked.sql +2 -0
  246. package/src/migrations/1780328286-document-locked.sql +2 -0
  247. package/src/migrations/1780412083-documents-set-locked.ts +18 -0
  248. package/src/migrations/1780928401-v1-groups-migration-data.ts +50 -0
  249. package/src/models/BalanceItem.ts +98 -43
  250. package/src/models/CachedBalance.test.ts +46 -46
  251. package/src/models/CachedBalance.ts +7 -7
  252. package/src/models/Document.ts +34 -13
  253. package/src/models/DocumentTemplate.ts +56 -17
  254. package/src/models/Email.test.ts +3 -3
  255. package/src/models/Email.ts +28 -49
  256. package/src/models/EmailVerificationCode.ts +8 -22
  257. package/src/models/Event.ts +6 -4
  258. package/src/models/EventNotification.ts +6 -6
  259. package/src/models/Group.ts +24 -3
  260. package/src/models/Invoice.ts +10 -9
  261. package/src/models/MemberPlatformMembership.test.ts +70 -0
  262. package/src/models/MemberPlatformMembership.ts +16 -12
  263. package/src/models/MollieToken.ts +42 -102
  264. package/src/models/Order.ts +14 -26
  265. package/src/models/Organization.ts +143 -86
  266. package/src/models/PasswordToken.ts +21 -21
  267. package/src/models/Payment.ts +61 -4
  268. package/src/models/Registration.ts +6 -5
  269. package/src/models/RegistrationInvitation.ts +40 -0
  270. package/src/models/STCredit.ts +32 -0
  271. package/src/models/STInvoice.ts +11 -5
  272. package/src/models/STPackage.ts +19 -14
  273. package/src/models/UsedRegisterCode.ts +34 -0
  274. package/src/models/User.ts +6 -7
  275. package/src/models/_relations.ts +29 -0
  276. package/src/models/addresses/City.ts +8 -6
  277. package/src/models/addresses/PostalCode.test.ts +1 -0
  278. package/src/models/addresses/PostalCode.ts +5 -3
  279. package/src/models/addresses/Street.ts +6 -4
  280. package/src/models/index.ts +3 -0
  281. package/src/models/v1GroupMigrationData.ts +43 -0
  282. package/dist/helpers/MemberMerger.d.ts +0 -14
  283. package/dist/helpers/MemberMerger.d.ts.map +0 -1
  284. package/dist/helpers/MemberMerger.js +0 -364
  285. package/dist/helpers/MemberMerger.js.map +0 -1
  286. package/dist/migrations/1720080975-convert-charset.sql +0 -85
  287. package/dist/migrations/1723202126-member-number-index.sql +0 -2
  288. package/dist/models/OneTimeToken.d.ts +0 -38
  289. package/dist/models/OneTimeToken.d.ts.map +0 -1
  290. package/dist/models/OneTimeToken.js +0 -125
  291. package/dist/models/OneTimeToken.js.map +0 -1
  292. package/src/helpers/MemberMerger.test.ts +0 -782
  293. package/src/helpers/MemberMerger.ts +0 -577
  294. package/src/migrations/1720080975-convert-charset.sql +0 -85
  295. package/src/migrations/1723202126-member-number-index.sql +0 -2
  296. package/src/models/OneTimeToken.ts +0 -133
@@ -1,8 +1,7 @@
1
1
  import { column } from '@simonbackx/simple-database';
2
- import type { PartialWithoutMethods, PlainObject } from '@simonbackx/simple-encoding';
2
+ import type { PlainObject } from '@simonbackx/simple-encoding';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { QueryableModel } from '@stamhoofd/sql';
5
- import { MollieOnboarding, MollieProfile, MollieStatus } from '@stamhoofd/structures';
6
5
  import type { IncomingMessage } from 'http';
7
6
  import https from 'https';
8
7
 
@@ -12,9 +11,17 @@ function sleep(ms: number) {
12
11
  return new Promise(resolve => setTimeout(resolve, ms));
13
12
  }
14
13
 
14
+ export class MollieError extends Error {
15
+ body: PlainObject
16
+ }
17
+
15
18
  export class MollieToken extends QueryableModel {
16
19
  static table = 'mollie_tokens';
17
20
 
21
+ get id() {
22
+ return this.organizationId
23
+ }
24
+
18
25
  @column({ primary: true, type: 'string' })
19
26
  organizationId!: string;
20
27
 
@@ -184,7 +191,9 @@ export class MollieToken extends QueryableModel {
184
191
 
185
192
  if (response.statusCode < 200 || response.statusCode >= 300) {
186
193
  console.error(body);
187
- reject(new Error(response.statusCode + ' ' + response.statusMessage));
194
+ const e = new MollieError(response.statusCode + ' ' + response.statusMessage);
195
+ e.body = json;
196
+ reject(e);
188
197
  return;
189
198
  }
190
199
 
@@ -223,23 +232,38 @@ export class MollieToken extends QueryableModel {
223
232
  * Refresh the token itself, without generating a new token. Everyone who had the token has a new token now
224
233
  */
225
234
  async refresh(): Promise<void> {
226
- const data = await MollieToken.request('POST', '/oauth2/tokens', {
227
- grant_type: 'refresh_token',
228
- refresh_token: this.refreshToken,
229
- }, 'urlencoded');
235
+ try {
236
+ const data = await MollieToken.request('POST', '/oauth2/tokens', {
237
+ grant_type: 'refresh_token',
238
+ refresh_token: this.refreshToken,
239
+ }, 'urlencoded');
230
240
 
231
- if (data && data.access_token && data.refresh_token) {
232
- // Delete token if exisitng
233
- this.refreshToken = data.refresh_token;
234
- this.accessToken = data.access_token;
235
- this.expiresOn = new Date(new Date().getTime() + 3600 * 1000 - 60 * 1000);
236
- await this.save();
237
-
238
- // Update shared tokens if this object was fetched directly
239
- MollieToken.knownTokens.set(this.organizationId, this);
240
- return;
241
+ if (data && data.access_token && data.refresh_token) {
242
+ // Delete token if exisitng
243
+ this.refreshToken = data.refresh_token;
244
+ this.accessToken = data.access_token;
245
+ this.expiresOn = new Date(new Date().getTime() + 3600 * 1000 - 60 * 1000);
246
+ await this.save();
247
+
248
+ // Update shared tokens if this object was fetched directly
249
+ MollieToken.knownTokens.set(this.organizationId, this);
250
+ return;
251
+ }
252
+ throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
253
+ } catch (e) {
254
+ if (e instanceof MollieError) {
255
+ if (e.body && typeof e.body === 'object' && 'error' in e.body && e.body.error === 'invalid_grant') {
256
+ // Refresh token not valid.
257
+ console.error('Deleting Mollie Token because invalid refresh token')
258
+ await this.delete()
259
+
260
+ // Still throw
261
+ throw e;
262
+ }
263
+ }
264
+ throw e;
241
265
  }
242
- throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
266
+
243
267
  }
244
268
 
245
269
  /**
@@ -279,97 +303,13 @@ export class MollieToken extends QueryableModel {
279
303
 
280
304
  this.knownTokens.set(organization.id, token);
281
305
 
282
- organization.privateMeta.mollieOnboarding = await token.getOnboardingStatus();
283
- await token.setup(organization);
284
-
285
- await organization.save();
286
306
  return token;
287
307
  }
288
308
  throw new SimpleError({ code: '', message: 'Something went wrong in the response' });
289
309
  }
290
310
 
291
- async getOnboardingStatus() {
292
- try {
293
- const response = await this.authRequest('GET', '/v2/onboarding/me');
294
- return MollieOnboarding.create({
295
- canReceivePayments: !!response.canReceivePayments,
296
- canReceiveSettlements: !!response.canReceiveSettlements,
297
- status: response.status === 'needs-data' ? MollieStatus.NeedsData : (response.status === 'in-review' ? MollieStatus.InReview : (MollieStatus.Completed)),
298
- });
299
- }
300
- catch (e) {
301
- console.error('Error when requesting Mollie onboarding status:');
302
- console.error(e);
303
- return null;
304
- }
305
- }
306
-
307
- async getProfiles(): Promise<MollieProfile[]> {
308
- try {
309
- const response = await this.authRequest('GET', '/v2/profiles?limit=250');
310
- const profiles = response._embedded.profiles as PartialWithoutMethods<MollieProfile>[];
311
- return profiles.map(p => MollieProfile.create(p));
312
- }
313
- catch (e) {
314
- console.error('Failed to parse mollie profiles', e);
315
- return [];
316
- }
317
- }
318
-
319
- async getProfileId(website?: string): Promise<string | null> {
320
- const response = await this.authRequest('GET', '/v2/profiles?limit=250');
321
- const profiles = response._embedded.profiles;
322
-
323
- // Search profile with Stamhoofd as name
324
- if (website) {
325
- for (const profile of profiles) {
326
- if (profile.website.toLowerCase().includes(website)) {
327
- return profile.id;
328
- }
329
- }
330
- }
331
-
332
- // Search profile with Stamhoofd as name
333
- for (const profile of profiles) {
334
- if (profile.name.toLowerCase().includes('stamhoofd')) {
335
- return profile.id;
336
- }
337
- }
338
-
339
- return response._embedded.profiles[0]?.id ?? null;
340
- }
341
-
342
311
  async getOnboardingLink() {
343
312
  const response = await this.authRequest('GET', '/v2/onboarding/me');
344
313
  return response._links.dashboard.href ?? '';
345
314
  }
346
-
347
- /**
348
- * Set initial onboarding values + enable bancontact
349
- */
350
- async setup(organization: Organization) {
351
- // Submit onboarding data
352
-
353
- if (organization.privateMeta.mollieOnboarding && organization.privateMeta.mollieOnboarding.status == MollieStatus.NeedsData) {
354
- await this.authRequest('POST', '/v2/onboarding/me', {
355
- organization: {
356
- name: organization.name,
357
- address: {
358
- streetAndNumber: organization.address.street + ' ' + organization.address.number,
359
- postalCode: organization.address.postalCode,
360
- city: organization.address.city,
361
- country: organization.address.country,
362
- },
363
-
364
- vatRegulation: 'shifted',
365
- },
366
- profile: {
367
- name: organization.name + ' - Stamhoofd',
368
- // url: "https://"+organization.getHost(),
369
- description: $t(`%x1`),
370
- categoryCode: 8398,
371
- },
372
- });
373
- }
374
- }
375
315
  }
@@ -286,8 +286,7 @@ export class Order extends QueryableModel {
286
286
  if (webshop) {
287
287
  await this.setRelation(Order.webshop, webshop).updateTickets();
288
288
  }
289
- }
290
- else {
289
+ } else {
291
290
  this.markUpdated();
292
291
  await this.save();
293
292
  }
@@ -427,8 +426,7 @@ export class Order extends QueryableModel {
427
426
  // Remove any reserved stock
428
427
  const updated = Order.updateTimeSlotStock(timeSlot, this.data, false);
429
428
  changed = changed || updated;
430
- }
431
- else {
429
+ } else {
432
430
  this.data.reservedOrder = false;
433
431
  this.data.reservedPersons = 0;
434
432
  changed = true;
@@ -442,8 +440,7 @@ export class Order extends QueryableModel {
442
440
  if (timeSlot) {
443
441
  const updated = Order.updateTimeSlotStock(timeSlot, this.data, add);
444
442
  changed = changed || updated;
445
- }
446
- else {
443
+ } else {
447
444
  console.error('Missing timeslot ' + s.id + ' in webshop ' + this.webshopId);
448
445
  }
449
446
  }
@@ -456,6 +453,7 @@ export class Order extends QueryableModel {
456
453
  if (previousData !== null) {
457
454
  // Already removed
458
455
  item.reservedSeats = [];
456
+ // eslint-disable-next-line no-useless-assignment
459
457
  changed = true;
460
458
  }
461
459
 
@@ -491,8 +489,7 @@ export class Order extends QueryableModel {
491
489
  code.reserved = false;
492
490
  discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) - 1);
493
491
  changed = true;
494
- }
495
- else if (!code.reserved && add) {
492
+ } else if (!code.reserved && add) {
496
493
  code.reserved = true;
497
494
  discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) + 1);
498
495
  changed = true;
@@ -599,8 +596,7 @@ export class Order extends QueryableModel {
599
596
  ticketMap.set(item.product.id, offset + item.amount);
600
597
  }
601
598
  }
602
- }
603
- else {
599
+ } else {
604
600
  // Create a shared ticket for the whole order
605
601
  const ticket = new Ticket();
606
602
  ticket.orderId = this.id;
@@ -752,8 +748,7 @@ export class Order extends QueryableModel {
752
748
  // Needs to happen before validation, because we can include the tickets in the validation that way
753
749
  if (this.validAt === null) {
754
750
  await this.setRelation(Order.webshop, webshop).markValid(payment, tickets);
755
- }
756
- else {
751
+ } else {
757
752
  this.markUpdated();
758
753
  await this.save();
759
754
 
@@ -764,8 +759,7 @@ export class Order extends QueryableModel {
764
759
  if (this.data.customer.email.length > 0) {
765
760
  if (didCreateTickets) {
766
761
  await this.setRelation(Order.webshop, webshop).sendTickets();
767
- }
768
- else {
762
+ } else {
769
763
  if (payment && payment.method === PaymentMethod.Transfer) {
770
764
  await this.setRelation(Order.webshop, webshop).sendPaidMail();
771
765
  }
@@ -976,40 +970,34 @@ export class Order extends QueryableModel {
976
970
  await this.sendEmailTemplate({
977
971
  type: EmailTemplateType.TicketsConfirmationPOS,
978
972
  });
979
- }
980
- else {
973
+ } else {
981
974
  await this.sendEmailTemplate({
982
975
  type: EmailTemplateType.TicketsConfirmation,
983
976
  });
984
977
  }
985
- }
986
- else {
978
+ } else {
987
979
  if (this.webshop.meta.ticketType === WebshopTicketType.None) {
988
980
  if (payment && payment.method === PaymentMethod.Transfer) {
989
981
  // Also send a copy
990
982
  await this.sendEmailTemplate({
991
983
  type: EmailTemplateType.OrderConfirmationTransfer,
992
984
  });
993
- }
994
- else if (payment && payment.method === PaymentMethod.PointOfSale) {
985
+ } else if (payment && payment.method === PaymentMethod.PointOfSale) {
995
986
  await this.sendEmailTemplate({
996
987
  type: EmailTemplateType.OrderConfirmationPOS,
997
988
  });
998
- }
999
- else {
989
+ } else {
1000
990
  // Also send a copy
1001
991
  await this.sendEmailTemplate({
1002
992
  type: EmailTemplateType.OrderConfirmationOnline,
1003
993
  });
1004
994
  }
1005
- }
1006
- else {
995
+ } else {
1007
996
  if (payment && payment.method === PaymentMethod.Transfer) {
1008
997
  await this.sendEmailTemplate({
1009
998
  type: EmailTemplateType.TicketsConfirmationTransfer,
1010
999
  });
1011
- }
1012
- else {
1000
+ } else {
1013
1001
  console.error('Unexpected missing tickets for order where tickets are expected');
1014
1002
  }
1015
1003
  }
@@ -6,17 +6,21 @@ import { I18n } from '@stamhoofd/backend-i18n/I18n';
6
6
  import type { EmailInterfaceRecipient } from '@stamhoofd/email';
7
7
  import { QueueHandler } from '@stamhoofd/queues';
8
8
  import { QueryableModel, SQL } from '@stamhoofd/sql';
9
- import type { OrganizationEmail, PrivatePaymentConfiguration } from '@stamhoofd/structures';
10
- import { Address, appToUri, DNSRecordStatus, EmailTemplateType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
11
- import { Formatter } from '@stamhoofd/utility';
9
+ import type { AppType, OrganizationEmail, PrivatePaymentConfiguration } from '@stamhoofd/structures';
10
+ import { AccessRight, Address, appToUri, Company, DNSRecordStatus, EmailTemplateType, getAppHost, GroupType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
11
+ import type { PaymentMandate } from '@stamhoofd/structures/PaymentMandate.js';
12
+ import { Country } from '@stamhoofd/types/Country';
13
+ import { Language } from '@stamhoofd/types/Language';
14
+ import { Formatter, Sorter } from '@stamhoofd/utility';
12
15
  import { v4 as uuidv4 } from 'uuid';
13
16
  import { validateDNSRecords } from '../helpers/DNSValidator.js';
14
17
  import { OrganizationServerMetaData } from '../structures/OrganizationServerMetaData.js';
15
18
  import { Group } from './Group.js';
16
19
  import { OrganizationRegistrationPeriod } from './OrganizationRegistrationPeriod.js';
20
+ import { Registration } from './Registration.js';
17
21
  import { StripeAccount } from './StripeAccount.js';
18
- import { Country } from '@stamhoofd/types/Country';
19
- import { Language } from '@stamhoofd/types/Language';
22
+ import { Token } from './Token.js';
23
+ import type { User } from './User.js';
20
24
 
21
25
  export class Organization extends QueryableModel {
22
26
  static table = 'organizations';
@@ -209,53 +213,12 @@ export class Organization extends QueryableModel {
209
213
  return organization;
210
214
  }
211
215
 
212
- /**
213
- * Potentially includes a path
214
- */
215
- getRegistrationHost(i18n?: { language: Language; locale: string }): string {
216
- if (this.registerDomain) {
217
- let d = this.registerDomain;
218
-
219
- if (i18n && i18n.language !== this.i18n.language) {
220
- d += '/' + i18n.language;
221
- }
222
-
223
- return d;
224
- }
225
- return this.getDefaultRegistrationHost(i18n);
216
+ getDashboardHost(i18n?: { language: Language; locale: string }): string {
217
+ return getAppHost('dashboard', this, true, i18n);
226
218
  }
227
219
 
228
- getDefaultRegistrationHost(i18n?: { language: Language; locale: string }): string {
229
- if (!STAMHOOFD.domains.registration) {
230
- return STAMHOOFD.domains.dashboard + '/' + (i18n?.locale ?? this.i18n.locale) + '/' + appToUri('registration') + '/' + this.uri;
231
- }
232
- let defaultDomain = STAMHOOFD.domains.registration[this.address.country] ?? STAMHOOFD.domains.registration[''];
233
-
234
- if (i18n && i18n.language !== this.i18n.language) {
235
- defaultDomain += '/' + i18n.language;
236
- }
237
-
238
- return this.uri + '.' + defaultDomain;
239
- }
240
-
241
- get registerUrl() {
242
- return 'https://' + this.getRegistrationHost();
243
- }
244
-
245
- /**
246
- * @deprecated
247
- * use getRegistrationHost
248
- */
249
- getHost(i18n?: I18n): string {
250
- return this.getRegistrationHost(i18n);
251
- }
252
-
253
- /**
254
- * @deprecated
255
- * Use getDefaultRegistrationHost
256
- */
257
- getDefaultHost(i18n?: I18n): string {
258
- return this.getDefaultRegistrationHost(i18n);
220
+ get registerUrl(): string {
221
+ return 'https://' + getAppHost('registration', this, false);
259
222
  }
260
223
 
261
224
  get marketingDomain(): string {
@@ -297,8 +260,7 @@ export class Organization extends QueryableModel {
297
260
  await created.save();
298
261
  return created;
299
262
  });
300
- }
301
- else {
263
+ } else {
302
264
  oPeriod = oPeriods[0];
303
265
  }
304
266
 
@@ -361,8 +323,7 @@ export class Organization extends QueryableModel {
361
323
 
362
324
  console.log('Did set register domain for ' + this.id + ' to ' + organization.registerDomain);
363
325
  }
364
- }
365
- else {
326
+ } else {
366
327
  // Clear register domain
367
328
  if (organization.registerDomain) {
368
329
  // We need to clear it, to prevent sending e-mails with invalid links
@@ -400,8 +361,7 @@ export class Organization extends QueryableModel {
400
361
  type: EmailTemplateType.OrganizationStableDNS,
401
362
  bcc: true,
402
363
  });
403
- }
404
- else if (!wasActive && this.privateMeta.mailDomainActive && (!didSendDomainSetupMail || didSendWarning) && !organization.serverMeta.isDNSUnstable) {
364
+ } else if (!wasActive && this.privateMeta.mailDomainActive && (!didSendDomainSetupMail || didSendWarning) && !organization.serverMeta.isDNSUnstable) {
405
365
  organization.serverMeta.didSendDomainSetupMail = true;
406
366
  await organization.save();
407
367
 
@@ -409,15 +369,13 @@ export class Organization extends QueryableModel {
409
369
  await this.sendEmailTemplate({
410
370
  type: EmailTemplateType.OrganizationDNSSetupComplete,
411
371
  });
412
- }
413
- else {
372
+ } else {
414
373
  await this.sendEmailTemplate({
415
374
  type: EmailTemplateType.OrganizationValidDNS,
416
375
  });
417
376
  }
418
377
  }
419
- }
420
- else {
378
+ } else {
421
379
  // DNS settings gone broken
422
380
  if (organization.privateMeta.mailDomain) {
423
381
  organization.privateMeta.pendingMailDomain = organization.privateMeta.pendingMailDomain ?? organization.privateMeta.mailDomain;
@@ -442,8 +400,7 @@ export class Organization extends QueryableModel {
442
400
  type: EmailTemplateType.OrganizationUnstableDNS,
443
401
  bcc: true,
444
402
  });
445
- }
446
- else if (!organization.serverMeta.isDNSUnstable && organization.serverMeta.didSendDomainSetupMail && organization.serverMeta.DNSRecordWarningCount == 0) {
403
+ } else if (!organization.serverMeta.isDNSUnstable && organization.serverMeta.didSendDomainSetupMail && organization.serverMeta.DNSRecordWarningCount == 0) {
447
404
  organization.serverMeta.DNSRecordWarningCount += 1;
448
405
  await organization.save();
449
406
 
@@ -494,6 +451,10 @@ export class Organization extends QueryableModel {
494
451
  token: 'mailDomain',
495
452
  value: this.privateMeta.mailDomain ?? this.privateMeta.pendingMailDomain ?? '',
496
453
  }),
454
+ Replacement.create({
455
+ token: 'organizationName',
456
+ value: this.name,
457
+ }),
497
458
  ],
498
459
  unsubscribeType: 'marketing',
499
460
  fromStamhoofd: true,
@@ -541,8 +502,7 @@ export class Organization extends QueryableModel {
541
502
 
542
503
  await client.send(deleteCmd);
543
504
  console.log('Deleted AWS mail idenitiy @' + this.id + ' for ' + this.privateMeta.mailDomain);
544
- }
545
- catch (e) {
505
+ } catch (e) {
546
506
  console.error('Could not delete AWS email identitiy @' + this.id + ' for ' + this.privateMeta.mailDomain);
547
507
  console.error(e);
548
508
  }
@@ -609,8 +569,7 @@ export class Organization extends QueryableModel {
609
569
  // Recreate it immediately
610
570
  exists = false;
611
571
  }
612
- }
613
- catch (e) {
572
+ } catch (e) {
614
573
  console.error(e);
615
574
  }
616
575
 
@@ -854,6 +813,17 @@ export class Organization extends QueryableModel {
854
813
  return admins.filter(a => a.permissions && a.permissions.forOrganization(this)?.hasFullAccess());
855
814
  }
856
815
 
816
+ /**
817
+ * These email addresess are private
818
+ */
819
+ async getFinanceAdmins() {
820
+ const admins = await this.getAdmins();
821
+ const filtered = admins.filter(a => a.permissions && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
822
+
823
+ // Only full access
824
+ return filtered;
825
+ }
826
+
857
827
  /**
858
828
  * These email addresess are private
859
829
  */
@@ -873,32 +843,90 @@ export class Organization extends QueryableModel {
873
843
  return filtered.flatMap(f => f.getEmailTo());
874
844
  }
875
845
 
846
+ adminsToRecipients(admins: User[]) {
847
+ return admins.flatMap((f) => {
848
+ return Recipient.create({
849
+ firstName: f.firstName,
850
+ lastName: f.lastName,
851
+ email: f.email,
852
+ replacements: [],
853
+ });
854
+ });
855
+ }
856
+
876
857
  /**
877
858
  * These email addresess are private
878
859
  */
879
860
  async getAdminRecipients(): Promise<Recipient[]> {
880
- let filtered = await this.getFullAdmins();
861
+ const filtered = await this.getFullAdmins();
862
+ return this.adminsToRecipients(filtered);
863
+ }
881
864
 
882
- if (STAMHOOFD.environment === 'production') {
883
- if (filtered.length > 1) {
884
- // remove stamhoofd email addresses
885
- filtered = filtered.filter(e => !e.email.endsWith('@stamhoofd.be') && !e.email.endsWith('@stamhoofd.nl'));
886
- }
865
+ /**
866
+ * These email addresess are private
867
+ */
868
+ async getFinanceAdminRecipients(): Promise<Recipient[]> {
869
+ const filtered = await this.getFullAdmins();
870
+ return this.adminsToRecipients(filtered);
871
+ }
872
+
873
+ /**
874
+ * These email addresess are private
875
+ */
876
+ async getInvoicingToEmails() {
877
+ // Circular reference fix
878
+ const filtered = await this.getFinanceAdmins();
879
+
880
+ if (filtered.length > 0) {
881
+ return filtered.flatMap(f => f.getEmailTo()).map((recipient) => {
882
+ if (!recipient.name) {
883
+ return recipient.email;
884
+ }
885
+ const cleanedName = Formatter.emailSenderName(recipient.name);
886
+ if (cleanedName.length < 2) {
887
+ return recipient.email;
888
+ }
889
+ return '"' + cleanedName + '" <' + recipient.email + '>';
890
+ }).join(', ');
887
891
  }
888
892
 
889
- return filtered.flatMap((f) => {
890
- return Recipient.create({
891
- firstName: f.firstName,
892
- lastName: f.lastName,
893
- email: f.email,
894
- replacements: [
895
- Replacement.create({
896
- token: 'organizationName',
897
- value: this.name,
898
- }),
899
- ],
900
- });
893
+ return undefined;
894
+ }
895
+
896
+ /**
897
+ * Returns one email for invoices. since in ubl we can only add one address.
898
+ * We choose the oldest user that was active in the last 3 months (otherwise the oldest user if noone was active)
899
+ */
900
+ async getInvoicingToEmail(): Promise<string | undefined> {
901
+ // Circular reference fix
902
+ const admins = await this.getAdmins();
903
+
904
+ const tokens = await Token.select().where('userId', admins.map(a => a.id)).fetch();
905
+
906
+ // Sort by admins that were active in the last 3 months, then creation date
907
+ const cutoffDate = new Date(Date.now() - 1000 * 60 * 60 * 24 * 31 * 3);
908
+ admins.sort((a, b) => {
909
+ const aTokens = tokens.filter(t => t.userId === a.id);
910
+ const bTokens = tokens.filter(t => t.userId === b.id);
911
+ const aActive = !!aTokens.find(t => t.updatedAt > cutoffDate);
912
+ const bActive = !!bTokens.find(t => t.updatedAt > cutoffDate);
913
+ return Sorter.stack(
914
+ Sorter.byBooleanValue(aActive, bActive),
915
+ Sorter.byDateValue(b.createdAt, a.createdAt),
916
+ );
901
917
  });
918
+
919
+ const filtered = admins.filter(a => a.verified && a.permissions && !a.email.endsWith('@stamhoofd.be') && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
920
+
921
+ if (filtered.length > 0) {
922
+ return filtered.map(f => f.email)[0];
923
+ }
924
+ const filtered2 = admins.filter(a => a.verified && a.permissions && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
925
+
926
+ if (filtered2.length > 0) {
927
+ return filtered2.map(f => f.email)[0];
928
+ }
929
+ return undefined;
902
930
  }
903
931
 
904
932
  /**
@@ -913,10 +941,17 @@ export class Organization extends QueryableModel {
913
941
  };
914
942
  }
915
943
 
916
- async getPaymentProviderFor(method: PaymentMethod, config: PrivatePaymentConfiguration): Promise<{
944
+ async getPaymentProviderFor(method: PaymentMethod, mandate: PaymentMandate | null, config: PrivatePaymentConfiguration): Promise<{
917
945
  provider: PaymentProvider | null;
918
946
  stripeAccount: StripeAccount | null;
919
947
  }> {
948
+ if (mandate) {
949
+ return {
950
+ provider: mandate.provider,
951
+ stripeAccount: null,
952
+ };
953
+ }
954
+
920
955
  let stripeAccount = (config.stripeAccountId ? (await StripeAccount.getByID(config.stripeAccountId)) : null) ?? null;
921
956
  if (stripeAccount && stripeAccount.organizationId !== this.id) {
922
957
  console.warn('Stripe account ' + stripeAccount.id + ' is not linked to organization ' + this.id);
@@ -984,7 +1019,7 @@ export class Organization extends QueryableModel {
984
1019
  return 0;
985
1020
  }
986
1021
 
987
- return await this.select()
1022
+ return await Registration.select()
988
1023
  .join(
989
1024
  SQL.join(Group.table)
990
1025
  .where(SQL.column('id'), SQL.parentColumn('groupId')),
@@ -993,8 +1028,30 @@ export class Organization extends QueryableModel {
993
1028
  .where('deactivatedAt', null)
994
1029
  .where('registeredAt', '!=', null)
995
1030
  .where(SQL.column(Group.table, 'deletedAt'), null)
1031
+ .where(SQL.column(Group.table, 'type'), '!=', GroupType.WaitingList)
996
1032
  .count(
997
1033
  SQL.distinct(SQL.column('memberId')),
998
1034
  );
999
1035
  }
1036
+
1037
+ /**
1038
+ * Assures at least one company at all times
1039
+ */
1040
+ get defaultCompanies() {
1041
+ const b = this.meta.companies.length
1042
+ ? this.meta.companies
1043
+ : [
1044
+ Company.create({
1045
+ name: this.name,
1046
+ address: this.address,
1047
+ }),
1048
+ ];
1049
+
1050
+ // Missing address -> use organization address
1051
+ if (!b[0].address) {
1052
+ b[0].address = this.address;
1053
+ }
1054
+
1055
+ return b;
1056
+ }
1000
1057
  }