@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
@@ -0,0 +1,162 @@
1
+ import { QueueHandler } from '@stamhoofd/queues';
2
+
3
+ import type { OrganizationInvoiceSettings } from '@stamhoofd/structures/OrganizationInvoiceSettings.js';
4
+ import { Formatter } from '@stamhoofd/utility';
5
+ import type { DateTime } from 'luxon';
6
+ import { Invoice } from '../models/Invoice.js';
7
+
8
+ export class InvoiceCounter {
9
+ static numberCache: Map<string, {lastNumber: number, date: Date}> = new Map();
10
+
11
+
12
+ private static getNextResetDate(last: DateTime, resetMonth: number): DateTime {
13
+ const candidate = last.set({ month: resetMonth, day: 1 }).startOf('day');
14
+ return candidate > last ? candidate : candidate.plus({ years: 1 }).startOf('day');
15
+ }
16
+
17
+ static shouldStartNewSeries(settings: OrganizationInvoiceSettings, lastInvoiceDate: Date, invoiceDate: Date) {
18
+ if (settings.resetMonth === null) {
19
+ return false;
20
+ }
21
+
22
+ const last = Formatter.luxon(lastInvoiceDate);
23
+ const current = Formatter.luxon(invoiceDate);
24
+
25
+ return current >= this.getNextResetDate(last, settings.resetMonth)
26
+ }
27
+
28
+ /**
29
+ * XXX-123 -> 123
30
+ * YYY-0001 -> 1
31
+ *
32
+ * if prefix is enabled:
33
+ * 2025001 -> 1
34
+ * STA-2025001 -> 1
35
+ */
36
+ static parseNumber(settings: OrganizationInvoiceSettings, str: string) {
37
+ str = str.replace(/\D+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
38
+ // Remove prefixes and possibly year
39
+ const splitted = str.split('-');
40
+
41
+ let last = splitted[splitted.length - 1];
42
+
43
+ const stripYear = settings.prefixYear
44
+
45
+ if (last.length) {
46
+ if (stripYear) {
47
+ if (last.length <= 4) {
48
+ console.error('Could not parse invoice number from string ' + str + ' (could not trim year prefix)')
49
+ return null;
50
+ }
51
+ last = last.substring(4)
52
+ }
53
+
54
+ const int = parseInt(last);
55
+ if (int !== 0 && !isNaN(int) && isFinite(int)) {
56
+ return int;
57
+ }
58
+ }
59
+
60
+ console.error('Could not parse invoice number from string ' + str)
61
+ return null;
62
+ }
63
+
64
+ static formatNumber(settings: OrganizationInvoiceSettings, int: number, invoicedAt: Date) {
65
+ let str = int.toFixed(0).padStart(settings.padZeroLength, '0')
66
+
67
+ if (settings.prefixYear) {
68
+ const year = Formatter.luxon(invoicedAt).year
69
+ str = year + str;
70
+ }
71
+
72
+ if (settings.fixedPrefix) {
73
+ if (!settings.fixedPrefix.match(/\D$/)) {
74
+ // Need seperation character
75
+ str = settings.fixedPrefix.replace(/-$/, '') + '-' + str;
76
+ } else {
77
+ str = settings.fixedPrefix + str;
78
+ }
79
+ }
80
+
81
+ return str;
82
+ }
83
+
84
+ static async assignNextNumber(invoice: Invoice, settings: OrganizationInvoiceSettings): Promise<void> {
85
+ const organizationId = invoice.organizationId
86
+ return await QueueHandler.schedule('invoice/numbers-' + organizationId, async () => {
87
+ // Invoice date should be inside the queue to ensure it is chronologically generated
88
+ const invoicedAt = new Date();
89
+
90
+ const c = this.numberCache.get(organizationId);
91
+ if (c !== undefined) {
92
+ const lastNumber = c.lastNumber
93
+
94
+ // check date
95
+ if (!this.shouldStartNewSeries(settings, c.date, invoicedAt)) {
96
+ // Set and save.
97
+ // we do this here because it assures we'll not increase the next number if the save fails
98
+ invoice.number = this.formatNumber(settings, lastNumber + 1, invoicedAt);
99
+ invoice.invoicedAt = invoicedAt;
100
+ await invoice.save()
101
+
102
+ // If save succeeds, increase cache:
103
+ this.numberCache.set(organizationId, {
104
+ lastNumber: lastNumber + 1,
105
+ date: new Date(invoicedAt)
106
+ });
107
+ return
108
+ }
109
+ }
110
+
111
+ const lastInvoice = await Invoice.select()
112
+ .where('organizationId', organizationId)
113
+ .where('number', '!=', null)
114
+ .where('invoicedAt', '!=', null)
115
+ .orderBy('invoicedAt', 'DESC')
116
+ .first(false);
117
+
118
+ if (lastInvoice && lastInvoice.number && lastInvoice.invoicedAt) {
119
+ // check date
120
+ if (!this.shouldStartNewSeries(settings, lastInvoice.invoicedAt, invoicedAt)) {
121
+ const lastNumber = this.parseNumber(settings, lastInvoice.number)
122
+
123
+ if (lastNumber) {
124
+ invoice.number = this.formatNumber(settings, lastNumber + 1, invoicedAt);
125
+ invoice.invoicedAt = invoicedAt;
126
+ await invoice.save()
127
+
128
+ this.numberCache.set(organizationId, {
129
+ lastNumber: lastNumber + 1,
130
+ date: new Date(invoicedAt)
131
+ });
132
+ return;
133
+ }
134
+ }
135
+ }
136
+
137
+ // Start new
138
+ invoice.number = this.formatNumber(settings, 1, invoicedAt);
139
+ invoice.invoicedAt = invoicedAt;
140
+ await invoice.save()
141
+
142
+ this.numberCache.set(organizationId, {
143
+ lastNumber: 1,
144
+ date: new Date(invoicedAt)
145
+ });
146
+ return;
147
+ });
148
+ }
149
+
150
+ static async resetNumbers(organizationId: string): Promise<void> {
151
+ // Prevent race conditions: create a queue
152
+ // The queue can only run one at a time for the same webshop (so multiple webshops at the same time are allowed)
153
+ return await QueueHandler.schedule('invoice/numbers-' + organizationId, async () => {
154
+ this.numberCache.delete(organizationId);
155
+ return Promise.resolve();
156
+ });
157
+ }
158
+
159
+ static clearAll() {
160
+ this.numberCache.clear();
161
+ }
162
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  // Helpers
2
2
  export * from './helpers/EmailBuilder.js';
3
- export * from './helpers/MemberMerger.js';
4
3
  export * from './helpers/RateLimiter.js';
5
4
  export * from './helpers/WebshopCounter.js';
6
5
 
@@ -1,7 +1,8 @@
1
- import { column, Migration } from '@simonbackx/simple-database';
1
+ import { column, Database, Migration } from '@simonbackx/simple-database';
2
2
  import { QueryableModel } from '@stamhoofd/sql';
3
3
  import { Country } from '@stamhoofd/types/Country';
4
4
  import { StringCompare } from '@stamhoofd/utility';
5
+ import { v4 as uuidv4 } from 'uuid';
5
6
  import { City } from '../models/addresses/City.js';
6
7
  import { PostalCode } from '../models/addresses/PostalCode.js';
7
8
  import { Province } from '../models/addresses/Province.js';
@@ -33,16 +34,17 @@ export class Gemeente extends QueryableModel {
33
34
  city!: City;
34
35
  }
35
36
 
36
- async function getProvince(name: string, provinces: Province[]): Promise<Province> {
37
+ function getProvince(name: string, provinces: Province[], provinceRows: string[][]): Province {
37
38
  const p = provinces.find(p => StringCompare.typoCount(p.name, name) < 2 && p.country == Country.Belgium);
38
39
  if (p) {
39
40
  return p;
40
41
  }
41
42
  const province = new Province();
43
+ province.id = uuidv4();
42
44
  province.country = Country.Belgium;
43
45
  province.name = name.trim();
44
- await province.save();
45
46
  provinces.push(province);
47
+ provinceRows.push([province.id, province.name, province.country]);
46
48
  return province;
47
49
  }
48
50
 
@@ -54,42 +56,38 @@ export default new Migration(async () => {
54
56
  // Loop all gemeenten
55
57
  const provinces = await Province.all();
56
58
  const gemeenten = await Gemeente.all();
57
- const cities: City[] = [];
59
+ const citiesByNameAndProvince = new Map<string, City>();
60
+ const provinceRows: string[][] = [];
61
+ const cityRows: unknown[][] = [];
62
+ const postalCodeRows: string[][] = [];
63
+ const parentCityUpdates = new Map<string, string>();
58
64
 
59
65
  for (const gemeente of gemeenten) {
60
- const province = await getProvince(gemeente.provincie, provinces);
66
+ const province = getProvince(gemeente.provincie, provinces, provinceRows);
61
67
 
62
68
  // Some cities have the same name
63
- const found = cities.find(c => c.name.trim() == gemeente.gemeente.trim() && c.provinceId == province.id);
69
+ const found = citiesByNameAndProvince.get(cityKey(gemeente.gemeente, province.id));
64
70
 
65
71
  if (!found) {
66
72
  // Create the city
67
73
  const city = new City();
74
+ city.id = uuidv4();
68
75
  city.name = gemeente.gemeente.trim();
69
76
  city.country = Country.Belgium;
70
77
  city.provinceId = province.id;
71
-
72
- await city.save();
78
+ cityRows.push([city.id, city.name, city.provinceId, city.parentCityId, city.country]);
73
79
 
74
80
  // Also add postal code already
75
- const postalCode = new PostalCode();
76
- postalCode.postalCode = gemeente.postcode;
77
- postalCode.cityId = city.id;
78
- postalCode.country = Country.Belgium;
79
- await postalCode.save();
81
+ postalCodeRows.push([uuidv4(), gemeente.postcode, city.id, Country.Belgium]);
80
82
 
81
83
  gemeente.city = city;
82
- cities.push(city);
84
+ citiesByNameAndProvince.set(cityKey(city.name, city.provinceId), city);
83
85
  }
84
86
  else {
85
87
  gemeente.city = found;
86
88
 
87
89
  // Also add postal code already
88
- const postalCode = new PostalCode();
89
- postalCode.postalCode = gemeente.postcode;
90
- postalCode.cityId = found.id;
91
- postalCode.country = Country.Belgium;
92
- await postalCode.save();
90
+ postalCodeRows.push([uuidv4(), gemeente.postcode, found.id, Country.Belgium]);
93
91
  }
94
92
  }
95
93
 
@@ -98,28 +96,67 @@ export default new Migration(async () => {
98
96
  continue;
99
97
  }
100
98
 
101
- const hoofd = cities.find(c => c.name.trim() == gemeente.hoofdgemeente.trim() && c.provinceId == gemeente.city.provinceId);
99
+ const hoofd = citiesByNameAndProvince.get(cityKey(gemeente.hoofdgemeente, gemeente.city.provinceId));
102
100
  if (!hoofd) {
103
101
  // Create the city
104
102
  const city = new City();
103
+ city.id = uuidv4();
105
104
  city.name = gemeente.hoofdgemeente.trim();
106
105
  city.country = Country.Belgium;
107
106
 
108
- const province = await getProvince(gemeente.provincie, provinces);
107
+ const province = getProvince(gemeente.provincie, provinces, provinceRows);
109
108
  city.provinceId = province.id;
110
- await city.save();
109
+ cityRows.push([city.id, city.name, city.provinceId, city.parentCityId, city.country]);
111
110
 
112
- cities.push(city);
111
+ citiesByNameAndProvince.set(cityKey(city.name, city.provinceId), city);
113
112
  // do not create a postal code here, these don't have one
114
113
 
115
114
  // console.log('Assigning ' + gemeente.gemeente + ' to ' + city.name);
116
115
  gemeente.city.parentCityId = city.id;
117
- await gemeente.city.save();
116
+ parentCityUpdates.set(gemeente.city.id, city.id);
118
117
  }
119
118
  else {
120
119
  // console.log('Assigning ' + gemeente.gemeente + ' to ' + hoofd.name);
121
120
  gemeente.city.parentCityId = hoofd.id;
122
- await gemeente.city.save();
121
+ parentCityUpdates.set(gemeente.city.id, hoofd.id);
123
122
  }
124
123
  }
124
+
125
+ await insertRows(Province.table, ['id', 'name', 'country'], provinceRows);
126
+ await insertRows(City.table, ['id', 'name', 'provinceId', 'parentCityId', 'country'], cityRows);
127
+ await insertRows(PostalCode.table, ['id', 'postalCode', 'cityId', 'country'], postalCodeRows);
128
+ await updateParentCityIds([...parentCityUpdates.entries()]);
125
129
  });
130
+ function cityKey(name: string, provinceId: string): string {
131
+ return `${name.trim()}\0${provinceId}`;
132
+ }
133
+
134
+ async function insertRows(table: string, columns: string[], rows: unknown[][]): Promise<void> {
135
+ for (const chunk of chunks(rows, 1000)) {
136
+ if (chunk.length === 0) {
137
+ continue;
138
+ }
139
+ await Database.insert(`INSERT INTO \`${table}\` (${columns.map(column => `\`${column}\``).join(', ')}) VALUES ?`, [chunk]);
140
+ }
141
+ }
142
+
143
+ async function updateParentCityIds(rows: string[][]): Promise<void> {
144
+ for (const chunk of chunks(rows, 1000)) {
145
+ if (chunk.length === 0) {
146
+ continue;
147
+ }
148
+ const cases = chunk.map(() => 'WHEN ? THEN ?').join(' ');
149
+ await Database.statement(`UPDATE \`${City.table}\` SET \`parentCityId\` = CASE \`id\` ${cases} END WHERE \`id\` IN (?)`, [
150
+ ...chunk.flatMap(([cityId, parentCityId]) => [cityId, parentCityId]),
151
+ chunk.map(([cityId]) => cityId),
152
+ ]);
153
+ }
154
+ }
155
+
156
+ function chunks<T>(values: T[], size: number): T[][] {
157
+ const result: T[][] = [];
158
+ for (let index = 0; index < values.length; index += size) {
159
+ result.push(values.slice(index, index + size));
160
+ }
161
+ return result;
162
+ }
@@ -1,39 +1,42 @@
1
- import { Migration } from '@simonbackx/simple-database';
1
+ import { Database, Migration } from '@simonbackx/simple-database';
2
2
  import { Country } from '@stamhoofd/types/Country';
3
3
  import { StringCompare } from '@stamhoofd/utility';
4
4
  import fs from 'fs';
5
5
  import readline from 'readline';
6
+ import { v4 as uuidv4 } from 'uuid';
6
7
 
7
8
  import { City } from '../models/addresses/City.js';
8
9
  import { PostalCode } from '../models/addresses/PostalCode.js';
9
10
  import { Province } from '../models/addresses/Province.js';
10
11
 
11
- async function getProvince(name: string, provinces: Province[]): Promise<Province> {
12
+ function getProvince(name: string, provinces: Province[], provinceRows: string[][]): Province {
12
13
  const p = provinces.find(p => StringCompare.typoCount(p.name, name) == 0 && p.country == Country.Netherlands);
13
14
  if (p) {
14
15
  return p;
15
16
  }
16
17
  const province = new Province();
18
+ province.id = uuidv4();
17
19
  province.country = Country.Netherlands;
18
20
  province.name = name;
19
- await province.save();
20
21
 
21
22
  provinces.push(province);
23
+ provinceRows.push([province.id, province.name, province.country]);
22
24
  return province;
23
25
  }
24
26
 
25
- async function getCity(name: string, provinceId: string, cities: City[]): Promise<City> {
26
- const p = cities.find(p => StringCompare.typoCount(p.name, name) == 0 && p.country == Country.Netherlands);
27
+ function getCity(name: string, provinceId: string, citiesByName: Map<string, City>, cityRows: unknown[][]): City {
28
+ const p = citiesByName.get(normalizeCityName(name));
27
29
  if (p) {
28
30
  return p;
29
31
  }
30
32
  const city = new City();
33
+ city.id = uuidv4();
31
34
  city.name = name;
32
35
  city.country = Country.Netherlands;
33
36
  city.provinceId = provinceId;
34
- await city.save();
37
+ cityRows.push([city.id, city.name, city.provinceId, city.parentCityId, city.country]);
35
38
 
36
- cities.push(city);
39
+ citiesByName.set(normalizeCityName(city.name), city);
37
40
  return city;
38
41
  }
39
42
 
@@ -52,11 +55,15 @@ export default new Migration(async () => {
52
55
  });
53
56
 
54
57
  const allProvinces = await Province.where({ country: Country.Netherlands });
58
+ const provinceRows: string[][] = [];
59
+ const cityRows: unknown[][] = [];
60
+ const postalCodeRows: string[][] = [];
61
+ const parentCityUpdates: string[][] = [];
55
62
 
56
63
  for (const p of provinces) {
57
64
  // console.log(p.name);
58
65
 
59
- const province = await getProvince(p.name.trim(), allProvinces);
66
+ const province = getProvince(p.name.trim(), allProvinces, provinceRows);
60
67
 
61
68
  // const cities = await fs.readFile(p.path, { encoding: "utf-8" });
62
69
 
@@ -64,7 +71,7 @@ export default new Migration(async () => {
64
71
  input: fs.createReadStream(p.path, { encoding: 'utf-8' }),
65
72
  });
66
73
 
67
- const cities: City[] = [];
74
+ const citiesByName = new Map<string, City>();
68
75
 
69
76
  for await (const line of lineReader) {
70
77
  const splitted = line.split('\t');
@@ -77,19 +84,53 @@ export default new Migration(async () => {
77
84
  const plaats = splitted[2].trim();
78
85
 
79
86
  // Check plaats and gemeente
80
- const city = await getCity(plaats, province.id, cities);
81
- const city2 = await getCity(gemeente, province.id, cities); // might be the same as city
87
+ const city = getCity(plaats, province.id, citiesByName, cityRows);
88
+ const city2 = getCity(gemeente, province.id, citiesByName, cityRows); // might be the same as city
82
89
  if (city2.id !== city.id && city.parentCityId === null) {
83
90
  city.parentCityId = city2.id;
84
- await city.save();
91
+ parentCityUpdates.push([city.id, city2.id]);
85
92
  }
86
93
 
87
94
  // Also add postal code already
88
- const postalCode = new PostalCode();
89
- postalCode.postalCode = postcode;
90
- postalCode.cityId = city.id;
91
- postalCode.country = Country.Netherlands;
92
- await postalCode.save();
95
+ postalCodeRows.push([uuidv4(), postcode, city.id, Country.Netherlands]);
93
96
  }
94
97
  }
98
+
99
+ await insertRows(Province.table, ['id', 'name', 'country'], provinceRows);
100
+ await insertRows(City.table, ['id', 'name', 'provinceId', 'parentCityId', 'country'], cityRows);
101
+ await insertRows(PostalCode.table, ['id', 'postalCode', 'cityId', 'country'], postalCodeRows);
102
+ await updateParentCityIds(parentCityUpdates);
95
103
  });
104
+ function normalizeCityName(name: string): string {
105
+ return name.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().replace(/\s+/, ' ').trim();
106
+ }
107
+
108
+ async function insertRows(table: string, columns: string[], rows: unknown[][]): Promise<void> {
109
+ for (const chunk of chunks(rows, 1000)) {
110
+ if (chunk.length === 0) {
111
+ continue;
112
+ }
113
+ await Database.insert(`INSERT INTO \`${table}\` (${columns.map(column => `\`${column}\``).join(', ')}) VALUES ?`, [chunk]);
114
+ }
115
+ }
116
+
117
+ async function updateParentCityIds(rows: string[][]): Promise<void> {
118
+ for (const chunk of chunks(rows, 1000)) {
119
+ if (chunk.length === 0) {
120
+ continue;
121
+ }
122
+ const cases = chunk.map(() => 'WHEN ? THEN ?').join(' ');
123
+ await Database.statement(`UPDATE \`${City.table}\` SET \`parentCityId\` = CASE \`id\` ${cases} END WHERE \`id\` IN (?)`, [
124
+ ...chunk.flatMap(([cityId, parentCityId]) => [cityId, parentCityId]),
125
+ chunk.map(([cityId]) => cityId),
126
+ ]);
127
+ }
128
+ }
129
+
130
+ function chunks<T>(values: T[], size: number): T[][] {
131
+ const result: T[][] = [];
132
+ for (let index = 0; index < values.length; index += size) {
133
+ result.push(values.slice(index, index + size));
134
+ }
135
+ return result;
136
+ }
@@ -1,2 +1,4 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `organizations` ADD COLUMN `periodId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;
2
3
  ALTER TABLE `organizations` ADD FOREIGN KEY (`periodId`) REFERENCES `registration_periods` (`id`) ON UPDATE CASCADE ON DELETE RESTRICT;
4
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,2 +1,4 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `groups` ADD COLUMN `periodId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;
2
3
  ALTER TABLE `groups` ADD FOREIGN KEY (`periodId`) REFERENCES `registration_periods` (`id`) ON UPDATE CASCADE ON DELETE RESTRICT;
4
+ SET FOREIGN_KEY_CHECKS=1;
@@ -0,0 +1,34 @@
1
+ import { Database, Migration } from '@simonbackx/simple-database';
2
+ import { LoggingTools } from '@stamhoofd/utility';
3
+
4
+ export default new Migration(async () => {
5
+ process.stdout.write('\n');
6
+
7
+ if (STAMHOOFD.userMode === 'platform' && (STAMHOOFD.environment === 'production' || STAMHOOFD.environment === 'staging')) {
8
+ console.log('Skipped convert charset for userMode platform in production.');
9
+ return;
10
+ }
11
+
12
+ const tables = ['_members_users', 'balance_item_payments', 'balance_items', 'buckaroo_payments', 'cities', 'document_templates', 'documents', 'email_templates', 'email_verification_codes', 'groups', 'images', 'members', 'migrations', 'mollie_payments', 'mollie_tokens', 'organization_registration_periods', 'organizations', 'password_tokens', 'payconiq_payments', 'payments', 'platform', 'postal_codes', 'provinces', 'register_codes', 'registration_periods', 'registrations', 'stamhoofd_credits', 'stamhoofd_invoices', 'stamhoofd_packages', 'stamhoofd_pending_invoices', 'streets', 'stripe_accounts', 'stripe_checkout_sessions', 'stripe_payment_intents', 'tokens', 'used_register_codes', 'users', 'webshop_discount_codes', 'webshop_orders', 'webshop_tickets', 'webshops'];
13
+
14
+ const progressLogger = LoggingTools.createProgressLogger(tables.length, { tag: 'convert tables charset' });
15
+
16
+ for (const table of tables) {
17
+ await convertTable(table);
18
+ progressLogger.update();
19
+ }
20
+
21
+ console.log('Start set database charset.');
22
+ await Database.statement('ALTER DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;');
23
+ });
24
+
25
+ async function convertTable(name: string): Promise<void> {
26
+ await Database.beginTransaction(async () => {
27
+ await Database.statement('set foreign_key_checks=0;');
28
+
29
+ console.log('Start converting charset of table: ' + name);
30
+ await Database.statement('ALTER TABLE `' + name + '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;');
31
+
32
+ await Database.statement('set foreign_key_checks=1;');
33
+ });
34
+ }
@@ -3,14 +3,14 @@ import { Database, Migration } from '@simonbackx/simple-database';
3
3
  /**
4
4
  * If the charset of the leads table is not converted this results
5
5
  * in foreign keys constraint errors in other migrations.
6
- *
6
+ *
7
7
  * todo: will we keep the leads table after the migration?
8
8
  */
9
9
  export default new Migration(async () => {
10
10
  process.stdout.write('\n');
11
11
 
12
- if (STAMHOOFD.userMode === 'platform') {
13
- console.log('Skipped update leads charset for userMode platform.')
12
+ if (STAMHOOFD.userMode === 'platform' && (STAMHOOFD.environment === 'production' || STAMHOOFD.environment === 'staging')) {
13
+ console.log('Skipped update leads charset for userMode platform in production.');
14
14
  return Promise.resolve();
15
15
  }
16
16
 
@@ -18,15 +18,7 @@ export default new Migration(async () => {
18
18
  console.log('Has leads table: ' + hasTable);
19
19
 
20
20
  if (hasTable) {
21
- const sqlStatement: string = [
22
- 'set foreign_key_checks=0;',
23
- 'ALTER TABLE `leads` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;',
24
- 'ALTER TABLE `leads` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;',
25
- 'set foreign_key_checks=1;'
26
- ].join('');
27
-
28
- console.log('Start updating charset of leads table.');
29
- await Database.statement(sqlStatement);
21
+ await convertTable('leads');
30
22
  }
31
23
 
32
24
  return Promise.resolve();
@@ -40,7 +32,7 @@ async function tableExists(tableName: string): Promise<boolean> {
40
32
  WHERE table_schema = DATABASE()
41
33
  AND table_name = ?
42
34
  `,
43
- [tableName]
35
+ [tableName],
44
36
  );
45
37
 
46
38
  const count = rows[0]['']['count'];
@@ -51,3 +43,14 @@ async function tableExists(tableName: string): Promise<boolean> {
51
43
 
52
44
  return count > 0;
53
45
  }
46
+
47
+ async function convertTable(name: string): Promise<void> {
48
+ await Database.beginTransaction(async () => {
49
+ await Database.statement('set foreign_key_checks=0;');
50
+
51
+ console.log('Start converting charset of table: ' + name);
52
+ await Database.statement('ALTER TABLE `' + name + '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;');
53
+
54
+ await Database.statement('set foreign_key_checks=1;');
55
+ });
56
+ }
@@ -1,3 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `users`
2
3
  ADD COLUMN `memberId` varchar(36) NULL AFTER `id`;
3
4
  ALTER TABLE `users` ADD FOREIGN KEY (`memberId`) REFERENCES `members` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,4 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `groups`
2
3
  ADD COLUMN `waitingListId` varchar(36) NULL AFTER `defaultAgeGroupId`;
3
-
4
4
  ALTER TABLE `groups` ADD FOREIGN KEY (`waitingListId`) REFERENCES `groups` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,2 +1,4 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `balance_items` ADD COLUMN `payingOrganizationId` varchar(36) NULL;
2
3
  ALTER TABLE `balance_items` ADD FOREIGN KEY (`payingOrganizationId`) REFERENCES `organizations` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
4
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,2 +1,4 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `balance_items` ADD COLUMN `dependingBalanceItemId` varchar(36) NULL;
2
3
  ALTER TABLE `balance_items` ADD FOREIGN KEY (`dependingBalanceItemId`) REFERENCES `balance_items` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
4
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,3 +1,3 @@
1
1
  ALTER TABLE `registrations`
2
2
  ADD COLUMN `options` json NOT NULL DEFAULT ('{"value": [], "version": 0}') AFTER `price`,
3
- ADD COLUMN `groupPrice` json NOT NULL DEFAULT ('{"value": {"id": "unknown", "name": "Onbekend", "price": {"price": 0, "reducedPrice": null}}, "version": 0}') AFTER `price`;
3
+ ADD COLUMN `groupPrice` json NOT NULL DEFAULT ('{"value": {"id": "unknown", "name": "Standaardtarief", "price": {"price": 0, "reducedPrice": null}}, "version": 0}') AFTER `price`;
@@ -1,2 +1,4 @@
1
1
 
2
+ SET FOREIGN_KEY_CHECKS=0;
2
3
  ALTER TABLE `payments` ADD FOREIGN KEY (`payingOrganizationId`) REFERENCES `organizations` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
4
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1 +1,3 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `registrations` ADD FOREIGN KEY (`organizationId`) REFERENCES `organizations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE;
3
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1 +1,3 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `registrations` ADD FOREIGN KEY (`payingOrganizationId`) REFERENCES `organizations` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
3
+ SET FOREIGN_KEY_CHECKS=1;
@@ -0,0 +1,6 @@
1
+ ALTER TABLE
2
+ `stamhoofd_invoices`
3
+ ADD
4
+ COLUMN `negativeInvoiceId` varchar(36) NULL,
5
+ ADD
6
+ FOREIGN KEY (`negativeInvoiceId`) REFERENCES `stamhoofd_invoices` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
@@ -0,0 +1,30 @@
1
+ import { Database, Migration } from '@simonbackx/simple-database';
2
+ import { BalanceItemStatus } from '@stamhoofd/structures';
3
+
4
+ export default new Migration(async () => {
5
+ if (STAMHOOFD.environment === 'test') {
6
+ console.log('skipped in tests');
7
+ return;
8
+ }
9
+
10
+ const query = `
11
+ UPDATE
12
+ balance_items
13
+ SET status = ?
14
+ WHERE status IN (?)`;
15
+ await Database.update(query, [
16
+ BalanceItemStatus.Due,
17
+ ['Paid', 'Pending'],
18
+ ]);
19
+
20
+ const q2 = `
21
+ UPDATE
22
+ balance_items
23
+ SET status = ?,
24
+ amount = coalesce(nullif(ROUND(coalesce(pricePaid / nullif(unitPrice, 0), 0)), 0), 1)
25
+ WHERE amount = 0 AND status = ?`;
26
+ await Database.update(q2, [
27
+ BalanceItemStatus.Canceled,
28
+ BalanceItemStatus.Due,
29
+ ]);
30
+ });
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `balance_item_payments`
2
+ CHANGE `price` `price` bigint NOT NULL DEFAULT '0';