@stamhoofd/models 2.121.0 → 2.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) 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/STPackageFactory.js.map +1 -1
  9. package/dist/factories/UserFactory.d.ts.map +1 -1
  10. package/dist/factories/UserFactory.js +2 -2
  11. package/dist/factories/UserFactory.js.map +1 -1
  12. package/dist/helpers/EmailBuilder.d.ts.map +1 -1
  13. package/dist/helpers/EmailBuilder.js +8 -8
  14. package/dist/helpers/EmailBuilder.js.map +1 -1
  15. package/dist/helpers/Handlebars.d.ts.map +1 -1
  16. package/dist/helpers/Handlebars.js +7 -1
  17. package/dist/helpers/Handlebars.js.map +1 -1
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +0 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/migrations/1605262045-import-postcodes.d.ts.map +1 -1
  23. package/dist/migrations/1605262045-import-postcodes.js +58 -24
  24. package/dist/migrations/1605262045-import-postcodes.js.map +1 -1
  25. package/dist/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -1
  26. package/dist/migrations/1605262046-import-postcodes-nl.js +54 -17
  27. package/dist/migrations/1605262046-import-postcodes-nl.js.map +1 -1
  28. package/dist/migrations/1719567881-organization-periodId.sql +2 -0
  29. package/dist/migrations/1719567882-groups-periodId.sql +2 -0
  30. package/dist/migrations/1720080975-convert-charset.d.ts +4 -0
  31. package/dist/migrations/1720080975-convert-charset.d.ts.map +1 -0
  32. package/dist/migrations/1720080975-convert-charset.js +26 -0
  33. package/dist/migrations/1720080975-convert-charset.js.map +1 -0
  34. package/dist/migrations/1720080976-convert-charset-leads.d.ts.map +1 -1
  35. package/dist/migrations/1720080976-convert-charset-leads.js +11 -10
  36. package/dist/migrations/1720080976-convert-charset-leads.js.map +1 -1
  37. package/dist/migrations/1721400546-users-memberId.sql +2 -0
  38. package/dist/migrations/1722269236-group-waitinglist-id.sql +2 -1
  39. package/dist/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
  40. package/dist/migrations/1722525787-depending-balance-item.sql +2 -0
  41. package/dist/migrations/1722963554-registration-group-price-and-options.sql +1 -1
  42. package/dist/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
  43. package/dist/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
  44. package/dist/migrations/1733317910-paying-organization-id-fk.sql +2 -0
  45. package/dist/migrations/1733504881-negative-invoice-id.sql +6 -0
  46. package/dist/migrations/1733994455-balance-item-status-open.d.ts +4 -0
  47. package/dist/migrations/1733994455-balance-item-status-open.d.ts.map +1 -0
  48. package/dist/migrations/1733994455-balance-item-status-open.js +28 -0
  49. package/dist/migrations/1733994455-balance-item-status-open.js.map +1 -0
  50. package/dist/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
  51. package/dist/migrations/1769087809-payments-invoice-id.sql +2 -0
  52. package/dist/migrations/1772033555-balance-item-package-id.sql +2 -0
  53. package/dist/migrations/1778796615-payments-reversing-payment-id.sql +2 -0
  54. package/dist/migrations/1779443446-transfer-fees.sql +3 -0
  55. package/dist/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
  56. package/dist/migrations/1779968328-payments-admin-user-id.sql +5 -0
  57. package/dist/migrations/1779970611-payments-refunded-amount.sql +2 -0
  58. package/dist/migrations/1779972640-balance-items-failed-at.sql +2 -0
  59. package/dist/migrations/1780328285-document-template-locked.sql +2 -0
  60. package/dist/migrations/1780328286-document-locked.sql +2 -0
  61. package/dist/migrations/1780412083-documents-set-locked.d.ts +4 -0
  62. package/dist/migrations/1780412083-documents-set-locked.d.ts.map +1 -0
  63. package/dist/migrations/1780412083-documents-set-locked.js +14 -0
  64. package/dist/migrations/1780412083-documents-set-locked.js.map +1 -0
  65. package/dist/migrations/1780928401-v1-groups-migration-data.d.ts +4 -0
  66. package/dist/migrations/1780928401-v1-groups-migration-data.d.ts.map +1 -0
  67. package/dist/migrations/1780928401-v1-groups-migration-data.js +44 -0
  68. package/dist/migrations/1780928401-v1-groups-migration-data.js.map +1 -0
  69. package/dist/models/BalanceItem.d.ts +7 -2
  70. package/dist/models/BalanceItem.d.ts.map +1 -1
  71. package/dist/models/BalanceItem.js +41 -38
  72. package/dist/models/BalanceItem.js.map +1 -1
  73. package/dist/models/CachedBalance.d.ts +6 -1
  74. package/dist/models/CachedBalance.d.ts.map +1 -1
  75. package/dist/models/CachedBalance.js +3 -2
  76. package/dist/models/CachedBalance.js.map +1 -1
  77. package/dist/models/Document.d.ts +4 -0
  78. package/dist/models/Document.d.ts.map +1 -1
  79. package/dist/models/Document.js +26 -3
  80. package/dist/models/Document.js.map +1 -1
  81. package/dist/models/DocumentTemplate.d.ts +4 -0
  82. package/dist/models/DocumentTemplate.d.ts.map +1 -1
  83. package/dist/models/DocumentTemplate.js +37 -1
  84. package/dist/models/DocumentTemplate.js.map +1 -1
  85. package/dist/models/Email.d.ts.map +1 -1
  86. package/dist/models/Email.js +1 -1
  87. package/dist/models/Email.js.map +1 -1
  88. package/dist/models/EmailVerificationCode.d.ts.map +1 -1
  89. package/dist/models/EmailVerificationCode.js +3 -13
  90. package/dist/models/EmailVerificationCode.js.map +1 -1
  91. package/dist/models/Event.d.ts +2 -1
  92. package/dist/models/Event.d.ts.map +1 -1
  93. package/dist/models/Event.js +3 -0
  94. package/dist/models/Event.js.map +1 -1
  95. package/dist/models/EventNotification.d.ts.map +1 -1
  96. package/dist/models/EventNotification.js +5 -5
  97. package/dist/models/EventNotification.js.map +1 -1
  98. package/dist/models/Invoice.d.ts +1 -0
  99. package/dist/models/Invoice.d.ts.map +1 -1
  100. package/dist/models/Invoice.js +8 -0
  101. package/dist/models/Invoice.js.map +1 -1
  102. package/dist/models/MemberPlatformMembership.d.ts.map +1 -1
  103. package/dist/models/MemberPlatformMembership.js +9 -0
  104. package/dist/models/MemberPlatformMembership.js.map +1 -1
  105. package/dist/models/Order.d.ts.map +1 -1
  106. package/dist/models/Order.js +1 -0
  107. package/dist/models/Order.js.map +1 -1
  108. package/dist/models/Organization.d.ts +23 -25
  109. package/dist/models/Organization.d.ts.map +1 -1
  110. package/dist/models/Organization.js +92 -64
  111. package/dist/models/Organization.js.map +1 -1
  112. package/dist/models/PasswordToken.d.ts +5 -1
  113. package/dist/models/PasswordToken.d.ts.map +1 -1
  114. package/dist/models/PasswordToken.js +18 -17
  115. package/dist/models/PasswordToken.js.map +1 -1
  116. package/dist/models/Payment.d.ts +21 -2
  117. package/dist/models/Payment.d.ts.map +1 -1
  118. package/dist/models/Payment.js +43 -2
  119. package/dist/models/Payment.js.map +1 -1
  120. package/dist/models/Registration.d.ts.map +1 -1
  121. package/dist/models/Registration.js +3 -3
  122. package/dist/models/Registration.js.map +1 -1
  123. package/dist/models/STCredit.d.ts +4 -0
  124. package/dist/models/STCredit.d.ts.map +1 -1
  125. package/dist/models/STCredit.js +28 -0
  126. package/dist/models/STCredit.js.map +1 -1
  127. package/dist/models/STInvoice.d.ts +7 -1
  128. package/dist/models/STInvoice.d.ts.map +1 -1
  129. package/dist/models/STInvoice.js +9 -0
  130. package/dist/models/STInvoice.js.map +1 -1
  131. package/dist/models/STPackage.d.ts +4 -0
  132. package/dist/models/STPackage.d.ts.map +1 -1
  133. package/dist/models/STPackage.js +12 -1
  134. package/dist/models/STPackage.js.map +1 -1
  135. package/dist/models/UsedRegisterCode.d.ts +9 -0
  136. package/dist/models/UsedRegisterCode.d.ts.map +1 -1
  137. package/dist/models/UsedRegisterCode.js +31 -0
  138. package/dist/models/UsedRegisterCode.js.map +1 -1
  139. package/dist/models/_relations.js +25 -0
  140. package/dist/models/_relations.js.map +1 -1
  141. package/dist/models/addresses/City.d.ts +4 -4
  142. package/dist/models/addresses/City.d.ts.map +1 -1
  143. package/dist/models/addresses/City.js +6 -6
  144. package/dist/models/addresses/City.js.map +1 -1
  145. package/dist/models/addresses/PostalCode.d.ts +2 -2
  146. package/dist/models/addresses/PostalCode.d.ts.map +1 -1
  147. package/dist/models/addresses/PostalCode.js +4 -3
  148. package/dist/models/addresses/PostalCode.js.map +1 -1
  149. package/dist/models/addresses/Street.d.ts +3 -3
  150. package/dist/models/addresses/Street.d.ts.map +1 -1
  151. package/dist/models/addresses/Street.js +4 -4
  152. package/dist/models/addresses/Street.js.map +1 -1
  153. package/dist/models/index.d.ts +1 -0
  154. package/dist/models/index.d.ts.map +1 -1
  155. package/dist/models/index.js +1 -0
  156. package/dist/models/index.js.map +1 -1
  157. package/dist/models/v1GroupMigrationData.d.ts +22 -0
  158. package/dist/models/v1GroupMigrationData.d.ts.map +1 -0
  159. package/dist/models/v1GroupMigrationData.js +48 -0
  160. package/dist/models/v1GroupMigrationData.js.map +1 -0
  161. package/package.json +32 -13
  162. package/src/factories/GroupFactory.ts +4 -6
  163. package/src/factories/OrganizationFactory.ts +12 -4
  164. package/src/factories/STPackageFactory.ts +2 -2
  165. package/src/factories/UserFactory.ts +4 -5
  166. package/src/helpers/EmailBuilder.ts +19 -28
  167. package/src/helpers/Handlebars.ts +6 -1
  168. package/src/index.ts +0 -1
  169. package/src/migrations/1605262045-import-postcodes.ts +62 -25
  170. package/src/migrations/1605262046-import-postcodes-nl.ts +58 -17
  171. package/src/migrations/1719567881-organization-periodId.sql +2 -0
  172. package/src/migrations/1719567882-groups-periodId.sql +2 -0
  173. package/src/migrations/1720080975-convert-charset.ts +34 -0
  174. package/src/migrations/1720080976-convert-charset-leads.ts +16 -13
  175. package/src/migrations/1721400546-users-memberId.sql +2 -0
  176. package/src/migrations/1722269236-group-waitinglist-id.sql +2 -1
  177. package/src/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
  178. package/src/migrations/1722525787-depending-balance-item.sql +2 -0
  179. package/src/migrations/1722963554-registration-group-price-and-options.sql +1 -1
  180. package/src/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
  181. package/src/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
  182. package/src/migrations/1733317910-paying-organization-id-fk.sql +2 -0
  183. package/src/migrations/1733504881-negative-invoice-id.sql +6 -0
  184. package/src/migrations/1733994455-balance-item-status-open.ts +30 -0
  185. package/src/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
  186. package/src/migrations/1769087809-payments-invoice-id.sql +2 -0
  187. package/src/migrations/1772033555-balance-item-package-id.sql +2 -0
  188. package/src/migrations/1778796615-payments-reversing-payment-id.sql +2 -0
  189. package/src/migrations/1779443446-transfer-fees.sql +3 -0
  190. package/src/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
  191. package/src/migrations/1779968328-payments-admin-user-id.sql +5 -0
  192. package/src/migrations/1779970611-payments-refunded-amount.sql +2 -0
  193. package/src/migrations/1779972640-balance-items-failed-at.sql +2 -0
  194. package/src/migrations/1780328285-document-template-locked.sql +2 -0
  195. package/src/migrations/1780328286-document-locked.sql +2 -0
  196. package/src/migrations/1780412083-documents-set-locked.ts +18 -0
  197. package/src/migrations/1780928401-v1-groups-migration-data.ts +50 -0
  198. package/src/models/BalanceItem.ts +44 -43
  199. package/src/models/CachedBalance.test.ts +46 -46
  200. package/src/models/CachedBalance.ts +7 -7
  201. package/src/models/Document.ts +34 -13
  202. package/src/models/DocumentTemplate.ts +56 -17
  203. package/src/models/Email.test.ts +3 -3
  204. package/src/models/Email.ts +28 -49
  205. package/src/models/EmailVerificationCode.ts +8 -22
  206. package/src/models/Event.ts +6 -4
  207. package/src/models/EventNotification.ts +6 -6
  208. package/src/models/Invoice.ts +9 -0
  209. package/src/models/MemberPlatformMembership.test.ts +70 -0
  210. package/src/models/MemberPlatformMembership.ts +16 -12
  211. package/src/models/Order.ts +14 -26
  212. package/src/models/Organization.ts +122 -93
  213. package/src/models/PasswordToken.ts +21 -21
  214. package/src/models/Payment.ts +42 -4
  215. package/src/models/Registration.ts +3 -3
  216. package/src/models/STCredit.ts +32 -0
  217. package/src/models/STInvoice.ts +11 -5
  218. package/src/models/STPackage.ts +19 -14
  219. package/src/models/UsedRegisterCode.ts +34 -0
  220. package/src/models/_relations.ts +29 -0
  221. package/src/models/addresses/City.ts +8 -6
  222. package/src/models/addresses/PostalCode.test.ts +1 -0
  223. package/src/models/addresses/PostalCode.ts +5 -3
  224. package/src/models/addresses/Street.ts +6 -4
  225. package/src/models/index.ts +2 -0
  226. package/src/models/v1GroupMigrationData.ts +43 -0
  227. package/dist/helpers/MemberMerger.d.ts +0 -14
  228. package/dist/helpers/MemberMerger.d.ts.map +0 -1
  229. package/dist/helpers/MemberMerger.js +0 -364
  230. package/dist/helpers/MemberMerger.js.map +0 -1
  231. package/dist/migrations/1720080975-convert-charset.sql +0 -85
  232. package/dist/migrations/1723202126-member-number-index.sql +0 -2
  233. package/dist/models/OneTimeToken.d.ts +0 -38
  234. package/dist/models/OneTimeToken.d.ts.map +0 -1
  235. package/dist/models/OneTimeToken.js +0 -125
  236. package/dist/models/OneTimeToken.js.map +0 -1
  237. package/src/helpers/MemberMerger.test.ts +0 -782
  238. package/src/helpers/MemberMerger.ts +0 -577
  239. package/src/migrations/1720080975-convert-charset.sql +0 -85
  240. package/src/migrations/1723202126-member-number-index.sql +0 -2
  241. package/src/models/OneTimeToken.ts +0 -133
@@ -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 `invoices`
2
+ CHANGE `userAgent` `userAgent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL;
@@ -1,3 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `payments`
2
3
  ADD COLUMN `invoiceId` varchar(36) NULL AFTER `customer`,
3
4
  ADD FOREIGN KEY (`invoiceId`) REFERENCES `invoices` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,3 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `balance_items`
2
3
  ADD COLUMN `packageId` varchar(36) NULL AFTER `memberId`,
3
4
  ADD FOREIGN KEY (`packageId`) REFERENCES `stamhoofd_packages` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -1,3 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
1
2
  ALTER TABLE `payments`
2
3
  ADD COLUMN `reversingPaymentId` varchar(36) NULL AFTER `invoiceId`,
3
4
  ADD FOREIGN KEY (`reversingPaymentId`) REFERENCES `payments` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -0,0 +1,3 @@
1
+ ALTER TABLE `payments`
2
+ ADD COLUMN `transferFeeManual` int NOT NULL DEFAULT '0' AFTER `transferFee`,
3
+ ADD COLUMN `transferFeeManualCharged` int NOT NULL DEFAULT '0' AFTER `transferFeeManual`;
@@ -0,0 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
2
+ ALTER TABLE `used_register_codes`
3
+ ADD COLUMN `balanceItemId` varchar(36) NULL AFTER `code`,
4
+ ADD FOREIGN KEY (`balanceItemId`) REFERENCES `balance_items` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -0,0 +1,5 @@
1
+ SET FOREIGN_KEY_CHECKS=0;
2
+ ALTER TABLE `payments`
3
+ ADD COLUMN `adminUserId` varchar(36) NULL AFTER `organizationId`,
4
+ ADD FOREIGN KEY (`adminUserId`) REFERENCES `users` (`id`) ON UPDATE CASCADE ON DELETE SET NULL;
5
+ SET FOREIGN_KEY_CHECKS=1;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `payments`
2
+ ADD COLUMN `refundedAmount` bigint NOT NULL DEFAULT '0' AFTER `price`;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `balance_items`
2
+ ADD COLUMN `failedAt` datetime NULL AFTER `paidAt`;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `document_templates`
2
+ ADD COLUMN `isLocked` tinyint(1) NOT NULL DEFAULT '0' AFTER `publishedAt`;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `documents`
2
+ ADD COLUMN `isLocked` tinyint(1) NOT NULL DEFAULT '0' AFTER `number`;
@@ -0,0 +1,18 @@
1
+ import { Database, Migration } from '@simonbackx/simple-database';
2
+
3
+ export default new Migration(async () => {
4
+ process.stdout.write('\n');
5
+
6
+ if (STAMHOOFD.userMode === 'platform') {
7
+ console.log('Skipped set documents locked for userMode platform.');
8
+ return Promise.resolve();
9
+ }
10
+
11
+ console.log('Start locking document templates.');
12
+ await Database.statement(`update document_templates set isLocked = 1 where status = 'Published';`);
13
+
14
+ console.log('Start locking documents.');
15
+ await Database.statement(`update documents set isLocked = 1 where status = 'Published';`);
16
+
17
+ return Promise.resolve();
18
+ });
@@ -0,0 +1,50 @@
1
+ import { Database, Migration } from '@simonbackx/simple-database';
2
+ export default new Migration(async () => {
3
+ if (STAMHOOFD.environment === 'test') {
4
+ console.log('skipped in tests');
5
+ return;
6
+ }
7
+
8
+ if (STAMHOOFD.userMode === 'platform') {
9
+ console.log('skipped for userMode platform');
10
+ return;
11
+ }
12
+
13
+ console.log('start create v1_groups_migration_data table');
14
+
15
+ // create table to keep track of which combination of group id and cycle has been migrated to a new group after the migration from Stamhoofd v1 to v2
16
+ const groupsQuery = `CREATE TABLE \`v1_groups_migration_data\` (
17
+ \`newGroupId\` varchar(36) NOT NULL,
18
+ \`oldGroupId\` varchar(36) NOT NULL,
19
+ \`oldCycle\` int NOT NULL DEFAULT '0',
20
+ PRIMARY KEY (\`newGroupId\`),
21
+ UNIQUE KEY \`oldKey\` (\`oldGroupId\`,\`oldCycle\`) USING BTREE,
22
+ CONSTRAINT \`v1_groups_migration_data_ibfk_1\` FOREIGN KEY (\`newGroupId\`) REFERENCES \`groups\` (\`id\`) ON DELETE CASCADE ON UPDATE CASCADE
23
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`;
24
+
25
+ await Database.statement(groupsQuery);
26
+ console.log('finished create v1_groups_migration_data table');
27
+
28
+ console.log('start create v1_waiting_list_migration_data table');
29
+ // create table to keep track of which combination of waiting list group id and cycle has been migrated to a new group after the migration from Stamhoofd v1 to v2
30
+ const waitingListsQuery = `CREATE TABLE \`v1_waiting_list_migration_data\` (
31
+ \`newGroupId\` varchar(36) NOT NULL,
32
+ \`oldGroupId\` varchar(36) NOT NULL,
33
+ \`oldCycle\` int NOT NULL DEFAULT '0',
34
+ PRIMARY KEY (\`newGroupId\`),
35
+ UNIQUE KEY \`oldKey\` (\`oldGroupId\`,\`oldCycle\`) USING BTREE,
36
+ CONSTRAINT \`v1_waiting_list_migration_data_ibfk_1\` FOREIGN KEY (\`newGroupId\`) REFERENCES \`groups\` (\`id\`) ON DELETE CASCADE ON UPDATE CASCADE
37
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`;
38
+ await Database.statement(waitingListsQuery);
39
+
40
+ console.log('finished create v1_waiting_list_migration_data table');
41
+ });
42
+
43
+ // CREATE TABLE `v1_groups_migration_data` (
44
+ // `newGroupId` varchar(36) NOT NULL,
45
+ // `oldGroupId` varchar(36) NOT NULL,
46
+ // `oldCycle` int NOT NULL DEFAULT '0',
47
+ // PRIMARY KEY (`newGroupId`),
48
+ // UNIQUE KEY `oldKey` (`oldGroupId`,`oldCycle`) USING BTREE,
49
+ // CONSTRAINT `groups_ibfk_1` FOREIGN KEY (`newGroupId`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
50
+ // ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
@@ -5,7 +5,8 @@ import { Formatter, STMath } from '@stamhoofd/utility';
5
5
  import { v4 as uuidv4 } from 'uuid';
6
6
 
7
7
  import { EnumDecoder, MapDecoder } from '@simonbackx/simple-encoding';
8
- import { QueryableModel } from '@stamhoofd/sql';
8
+ import { SimpleError } from '@simonbackx/simple-errors';
9
+ import { QueryableModel, SQL } from '@stamhoofd/sql';
9
10
  import { Payment } from './Payment.js';
10
11
 
11
12
  /**
@@ -212,6 +213,12 @@ export class BalanceItem extends QueryableModel {
212
213
  @column({ type: 'datetime', nullable: true })
213
214
  paidAt: Date | null = null;
214
215
 
216
+ /**
217
+ * Marking a balance item as 'failed' can have side effects. To prevent executing these side effects multiple times, we store it in here.
218
+ */
219
+ @column({ type: 'datetime', nullable: true })
220
+ failedAt: Date | null = null;
221
+
215
222
  @column({
216
223
  type: 'datetime', beforeSave(old?: any) {
217
224
  if (old !== undefined) {
@@ -550,7 +557,7 @@ export class BalanceItem extends QueryableModel {
550
557
  await Database.update(query, params);
551
558
  }
552
559
 
553
- /**
560
+ /**
554
561
  * Update the outstanding balance of multiple members in one go (or all members)
555
562
  */
556
563
  static async updateInvoiced(balanceItemIds: string[] | 'all') {
@@ -662,55 +669,38 @@ export class BalanceItem extends QueryableModel {
662
669
  }
663
670
 
664
671
  static async balanceItemsForUsersAndMembers(organizationId: string | null, userIds: string[], memberIds: string[]): Promise<BalanceItem[]> {
665
- if (memberIds.length == 0 && userIds.length == 0) {
672
+ if (memberIds.length === 0 && userIds.length === 0) {
666
673
  return [];
667
674
  }
668
675
 
669
- const params: any[] = [];
670
- const where: string[] = [];
671
-
672
- if (memberIds.length) {
673
- if (memberIds.length == 1) {
674
- where.push(`memberId = ?`);
675
- params.push(memberIds[0]);
676
- }
677
- else {
678
- where.push(`memberId IN (?)`);
679
- params.push(memberIds);
680
- }
681
- }
682
-
683
- // Note here, we don't search for memberId IS NULL restriction in MySQL because it slows down the query too much (500ms)
684
- // Better if we do it in code here
685
- if (userIds.length) {
686
- if (userIds.length == 1) {
687
- where.push('userId = ?');
688
- params.push(userIds[0]);
689
- }
690
- else {
691
- where.push('userId IN (?)');
692
- params.push(userIds);
693
- }
694
- }
695
-
696
- const requiredWhere: string[] = [];
676
+ const base = BalanceItem.select()
677
+ .whereNot('status', BalanceItemStatus.Hidden);
697
678
 
698
679
  if (organizationId) {
699
- requiredWhere.push('organizationId = ?');
700
- params.push(organizationId);
680
+ base.andWhere('organizationId', organizationId);
701
681
  }
702
682
 
703
- const query = `SELECT ${BalanceItem.getDefaultSelect()} FROM ${BalanceItem.table} WHERE (${where.join(' OR ')}) ${requiredWhere.length ? (' AND ' + requiredWhere.join(' AND ')) : ''} AND ${BalanceItem.table}.status != ?`;
704
- params.push(BalanceItemStatus.Hidden);
683
+ if (memberIds.length && userIds.length) {
684
+ // Don't include other members than the provided members
685
+ base.andWhere(
686
+ SQL.where('memberId', memberIds)
687
+ // Include null member
688
+ .or(
689
+ // Don't include balances of other members, even when userId matches
690
+ // this allows removing access for a member
691
+ SQL.where('userId', userIds)
692
+ .and('memberId', null),
693
+ ),
694
+ );
695
+ } else if (memberIds.length) {
696
+ base.andWhere('memberId', memberIds);
697
+ } else if (userIds.length) {
698
+ base.andWhere('userId', userIds);
699
+ }
705
700
 
706
- const [rows] = await Database.select(query, params);
707
- const balanceItems = BalanceItem.fromRows(rows, BalanceItem.table);
701
+ base.andWhere('priceOpen', '!=', 0);
708
702
 
709
- // Filter out items of other members
710
- if (memberIds.length) {
711
- return balanceItems.filter(b => !b.memberId || memberIds.includes(b.memberId));
712
- }
713
- return balanceItems;
703
+ return await base.fetch();
714
704
  }
715
705
 
716
706
  static async balanceItemsForOrganization(payingOrganizationId: string, organizationId?: string): Promise<BalanceItem[]> {
@@ -719,9 +709,20 @@ export class BalanceItem extends QueryableModel {
719
709
  .whereNot('status', BalanceItemStatus.Hidden);
720
710
 
721
711
  if (organizationId) {
722
- base.where('organizationId', organizationId)
712
+ base.where('organizationId', organizationId);
723
713
  }
724
714
 
725
715
  return await base.fetch();
726
716
  }
717
+
718
+ override save(...args: Parameters<QueryableModel['save']>): Promise<boolean> {
719
+ if (this.unitPrice % 100 !== 0) {
720
+ throw new SimpleError({
721
+ statusCode: 500,
722
+ code: 'unrounded_balance_item',
723
+ message: 'Balance item unitPrices should be rounded up to 1 cent',
724
+ });
725
+ }
726
+ return super.save(...args);
727
+ }
727
728
  }
@@ -1,5 +1,5 @@
1
- import { BalanceItemStatus } from '../../../../../shared/structures/dist/BalanceItem.js';
2
- import { ReceivableBalanceType } from '../../../../../shared/structures/dist/ReceivableBalance.js';
1
+ import { BalanceItemStatus } from '@stamhoofd/structures/BalanceItem.js';
2
+ import { ReceivableBalanceType } from '@stamhoofd/structures/ReceivableBalance.js';
3
3
  import { MemberFactory } from '../factories/MemberFactory.js';
4
4
  import { OrganizationFactory } from '../factories/OrganizationFactory.js';
5
5
  import { UserFactory } from '../factories/UserFactory.js';
@@ -21,19 +21,19 @@ describe('CachedBalance', () => {
21
21
 
22
22
  test('Balances for members are summed', async () => {
23
23
  const organization = await new OrganizationFactory({}).create();
24
- const member = await new MemberFactory({organization}).create();
24
+ const member = await new MemberFactory({ organization }).create();
25
25
 
26
26
  const balanceA = new BalanceItem();
27
- balanceA.dueAt = null
27
+ balanceA.dueAt = null;
28
28
  balanceA.quantity = 2;
29
29
  balanceA.unitPrice = 1_00;
30
30
  balanceA.memberId = member.id;
31
31
  balanceA.organizationId = organization.id;
32
- balanceA.status = BalanceItemStatus.Due
32
+ balanceA.status = BalanceItemStatus.Due;
33
33
  await balanceA.save();
34
34
 
35
35
  // Update cached balance for user.
36
- await CachedBalance.updateForMembers(organization.id, [member.id])
36
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
37
37
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
38
38
  expect(cached).toHaveLength(1);
39
39
  expect(cached[0].amountOpen).toBe(2_00);
@@ -44,7 +44,7 @@ describe('CachedBalance', () => {
44
44
 
45
45
  test('Balances less than 7 days in the future are summed', async () => {
46
46
  const organization = await new OrganizationFactory({}).create();
47
- const member = await new MemberFactory({organization}).create();
47
+ const member = await new MemberFactory({ organization }).create();
48
48
 
49
49
  const balanceA = new BalanceItem();
50
50
  balanceA.dueAt = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 6); // 6 days later
@@ -52,11 +52,11 @@ describe('CachedBalance', () => {
52
52
  balanceA.unitPrice = 1_00;
53
53
  balanceA.memberId = member.id;
54
54
  balanceA.organizationId = organization.id;
55
- balanceA.status = BalanceItemStatus.Due
55
+ balanceA.status = BalanceItemStatus.Due;
56
56
  await balanceA.save();
57
57
 
58
58
  // Update cached balance for user.
59
- await CachedBalance.updateForMembers(organization.id, [member.id])
59
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
60
60
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
61
61
  expect(cached).toHaveLength(1);
62
62
  expect(cached[0].amountOpen).toBe(1_00);
@@ -67,7 +67,7 @@ describe('CachedBalance', () => {
67
67
 
68
68
  test('Balances more than 7 days in the future are not summed', async () => {
69
69
  const organization = await new OrganizationFactory({}).create();
70
- const member = await new MemberFactory({organization}).create();
70
+ const member = await new MemberFactory({ organization }).create();
71
71
 
72
72
  const balanceA = new BalanceItem();
73
73
  balanceA.dueAt = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 8); // 8 days later
@@ -75,11 +75,11 @@ describe('CachedBalance', () => {
75
75
  balanceA.unitPrice = 1_00;
76
76
  balanceA.memberId = member.id;
77
77
  balanceA.organizationId = organization.id;
78
- balanceA.status = BalanceItemStatus.Due
78
+ balanceA.status = BalanceItemStatus.Due;
79
79
  await balanceA.save();
80
80
 
81
81
  // Update cached balance for user.
82
- await CachedBalance.updateForMembers(organization.id, [member.id])
82
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
83
83
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
84
84
  expect(cached).toHaveLength(1);
85
85
  expect(cached[0].amountOpen).toBe(0);
@@ -90,7 +90,7 @@ describe('CachedBalance', () => {
90
90
 
91
91
  test('Paid balances more than 7 days in the future are summed', async () => {
92
92
  const organization = await new OrganizationFactory({}).create();
93
- const member = await new MemberFactory({organization}).create();
93
+ const member = await new MemberFactory({ organization }).create();
94
94
 
95
95
  const balanceA = new BalanceItem();
96
96
  balanceA.dueAt = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 8); // 8 days later
@@ -98,12 +98,12 @@ describe('CachedBalance', () => {
98
98
  balanceA.unitPrice = 1_00;
99
99
  balanceA.memberId = member.id;
100
100
  balanceA.organizationId = organization.id;
101
- balanceA.status = BalanceItemStatus.Due
101
+ balanceA.status = BalanceItemStatus.Due;
102
102
  balanceA.pricePaid = 2_00;
103
103
  await balanceA.save();
104
104
 
105
105
  // Update cached balance for user.
106
- await CachedBalance.updateForMembers(organization.id, [member.id])
106
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
107
107
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
108
108
  expect(cached).toHaveLength(1);
109
109
  expect(cached[0].amountOpen).toBe(0);
@@ -112,9 +112,9 @@ describe('CachedBalance', () => {
112
112
  expect(cached[0].nextDueAt).toEqual(null);
113
113
  });
114
114
 
115
- test('Paid balances more than 7 days in the future are summed and due at is still set if only partially paid', async () => {
115
+ test('Paid balances more than 7 days in the future are summed and due at is still set if only partially paid', async () => {
116
116
  const organization = await new OrganizationFactory({}).create();
117
- const member = await new MemberFactory({organization}).create();
117
+ const member = await new MemberFactory({ organization }).create();
118
118
 
119
119
  const balanceA = new BalanceItem();
120
120
  balanceA.dueAt = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 8); // 8 days later
@@ -122,12 +122,12 @@ describe('CachedBalance', () => {
122
122
  balanceA.unitPrice = 1_00;
123
123
  balanceA.memberId = member.id;
124
124
  balanceA.organizationId = organization.id;
125
- balanceA.status = BalanceItemStatus.Due
125
+ balanceA.status = BalanceItemStatus.Due;
126
126
  balanceA.pricePaid = 1_00;
127
127
  await balanceA.save();
128
128
 
129
129
  // Update cached balance for user.
130
- await CachedBalance.updateForMembers(organization.id, [member.id])
130
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
131
131
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
132
132
  expect(cached).toHaveLength(1);
133
133
  expect(cached[0].amountOpen).toBe(0);
@@ -138,7 +138,7 @@ describe('CachedBalance', () => {
138
138
 
139
139
  test('Canceled items are not summed', async () => {
140
140
  const organization = await new OrganizationFactory({}).create();
141
- const member = await new MemberFactory({organization}).create();
141
+ const member = await new MemberFactory({ organization }).create();
142
142
 
143
143
  const balanceA = new BalanceItem();
144
144
  balanceA.dueAt = null;
@@ -146,11 +146,11 @@ describe('CachedBalance', () => {
146
146
  balanceA.unitPrice = 1_00;
147
147
  balanceA.memberId = member.id;
148
148
  balanceA.organizationId = organization.id;
149
- balanceA.status = BalanceItemStatus.Canceled
149
+ balanceA.status = BalanceItemStatus.Canceled;
150
150
  await balanceA.save();
151
151
 
152
152
  // Update cached balance for user.
153
- await CachedBalance.updateForMembers(organization.id, [member.id])
153
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
154
154
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
155
155
  expect(cached).toHaveLength(1);
156
156
  expect(cached[0].amountOpen).toBe(0);
@@ -160,7 +160,7 @@ describe('CachedBalance', () => {
160
160
 
161
161
  test('Hidden items are not summed', async () => {
162
162
  const organization = await new OrganizationFactory({}).create();
163
- const member = await new MemberFactory({organization}).create();
163
+ const member = await new MemberFactory({ organization }).create();
164
164
 
165
165
  const balanceA = new BalanceItem();
166
166
  balanceA.dueAt = null;
@@ -168,11 +168,11 @@ describe('CachedBalance', () => {
168
168
  balanceA.unitPrice = 1_00;
169
169
  balanceA.memberId = member.id;
170
170
  balanceA.organizationId = organization.id;
171
- balanceA.status = BalanceItemStatus.Hidden
171
+ balanceA.status = BalanceItemStatus.Hidden;
172
172
  await balanceA.save();
173
173
 
174
174
  // Update cached balance for user.
175
- await CachedBalance.updateForMembers(organization.id, [member.id])
175
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
176
176
  const cached = await CachedBalance.getForObjects([member.id], organization.id, ReceivableBalanceType.member);
177
177
  expect(cached).toHaveLength(1);
178
178
  expect(cached[0].amountOpen).toBe(0);
@@ -183,8 +183,8 @@ describe('CachedBalance', () => {
183
183
  describe('nextDueAt for users with members', () => {
184
184
  test('Two balance items in the future are merged if less than 7 days in the future', async () => {
185
185
  const organization = await new OrganizationFactory({}).create();
186
- const member = await new MemberFactory({organization}).create();
187
- const user = await new UserFactory({organization}).create();
186
+ const member = await new MemberFactory({ organization }).create();
187
+ const user = await new UserFactory({ organization }).create();
188
188
 
189
189
  // Link member with user
190
190
  await Member.users.reverse('members').link(user, [member]);
@@ -195,7 +195,7 @@ describe('CachedBalance', () => {
195
195
  balanceA.unitPrice = 1_00;
196
196
  balanceA.memberId = member.id;
197
197
  balanceA.organizationId = organization.id;
198
- balanceA.status = BalanceItemStatus.Due
198
+ balanceA.status = BalanceItemStatus.Due;
199
199
  await balanceA.save();
200
200
 
201
201
  const balanceB = new BalanceItem();
@@ -204,12 +204,12 @@ describe('CachedBalance', () => {
204
204
  balanceB.unitPrice = 1_00;
205
205
  balanceB.userId = user.id;
206
206
  balanceB.organizationId = organization.id;
207
- balanceB.status = BalanceItemStatus.Due
207
+ balanceB.status = BalanceItemStatus.Due;
208
208
  await balanceB.save();
209
209
 
210
210
  // Update cached balance for user.
211
- await CachedBalance.updateForMembers(organization.id, [member.id])
212
- await CachedBalance.updateForUsers(organization.id, [user.id])
211
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
212
+ await CachedBalance.updateForUsers(organization.id, [user.id]);
213
213
  {
214
214
  const cached = await CachedBalance.getForObjects([user.id], organization.id, ReceivableBalanceType.user);
215
215
  expect(cached).toHaveLength(1);
@@ -231,8 +231,8 @@ describe('CachedBalance', () => {
231
231
 
232
232
  test('[Regression] Two balance items more than 7 days in the future set correct nextDueAt', async () => {
233
233
  const organization = await new OrganizationFactory({}).create();
234
- const member = await new MemberFactory({organization}).create();
235
- const user = await new UserFactory({organization}).create();
234
+ const member = await new MemberFactory({ organization }).create();
235
+ const user = await new UserFactory({ organization }).create();
236
236
 
237
237
  // Link member with user
238
238
  await Member.users.reverse('members').link(user, [member]);
@@ -243,7 +243,7 @@ describe('CachedBalance', () => {
243
243
  balanceA.unitPrice = 1_00;
244
244
  balanceA.memberId = member.id;
245
245
  balanceA.organizationId = organization.id;
246
- balanceA.status = BalanceItemStatus.Due
246
+ balanceA.status = BalanceItemStatus.Due;
247
247
  await balanceA.save();
248
248
 
249
249
  const balanceB = new BalanceItem();
@@ -252,12 +252,12 @@ describe('CachedBalance', () => {
252
252
  balanceB.unitPrice = 1_00;
253
253
  balanceB.userId = user.id;
254
254
  balanceB.organizationId = organization.id;
255
- balanceB.status = BalanceItemStatus.Due
255
+ balanceB.status = BalanceItemStatus.Due;
256
256
  await balanceB.save();
257
257
 
258
258
  // Update cached balance for user.
259
- await CachedBalance.updateForMembers(organization.id, [member.id])
260
- await CachedBalance.updateForUsers(organization.id, [user.id])
259
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
260
+ await CachedBalance.updateForUsers(organization.id, [user.id]);
261
261
  {
262
262
  const cached = await CachedBalance.getForObjects([user.id], organization.id, ReceivableBalanceType.user);
263
263
  expect(cached).toHaveLength(1);
@@ -278,9 +278,9 @@ describe('CachedBalance', () => {
278
278
 
279
279
  // Advance time with one day, balanceA is now less than 7 days in the future and should be included in the cached balance.
280
280
  // nextDueAt should be different and set to balanceB.dueAt
281
- vitest.setSystemTime(new Date(now.getTime() + 1000 * 60 * 60 * 24 * 1))
282
- await CachedBalance.updateForMembers(organization.id, [member.id])
283
- await CachedBalance.updateForUsers(organization.id, [user.id])
281
+ vitest.setSystemTime(new Date(now.getTime() + 1000 * 60 * 60 * 24 * 1));
282
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
283
+ await CachedBalance.updateForUsers(organization.id, [user.id]);
284
284
  const cachedAfter = await CachedBalance.getForObjects([user.id], organization.id, ReceivableBalanceType.user);
285
285
  expect(cachedAfter).toHaveLength(1);
286
286
  expect(cachedAfter[0].amountOpen).toBe(1_00);
@@ -291,8 +291,8 @@ describe('CachedBalance', () => {
291
291
 
292
292
  test('Two balance items more than 7 days in the future that are partially paid are summed correctly', async () => {
293
293
  const organization = await new OrganizationFactory({}).create();
294
- const member = await new MemberFactory({organization}).create();
295
- const user = await new UserFactory({organization}).create();
294
+ const member = await new MemberFactory({ organization }).create();
295
+ const user = await new UserFactory({ organization }).create();
296
296
 
297
297
  // Link member with user
298
298
  await Member.users.reverse('members').link(user, [member]);
@@ -304,7 +304,7 @@ describe('CachedBalance', () => {
304
304
  balanceA.memberId = member.id;
305
305
  balanceA.organizationId = organization.id;
306
306
  balanceA.pricePaid = 50;
307
- balanceA.status = BalanceItemStatus.Due
307
+ balanceA.status = BalanceItemStatus.Due;
308
308
  await balanceA.save();
309
309
 
310
310
  const balanceB = new BalanceItem();
@@ -314,12 +314,12 @@ describe('CachedBalance', () => {
314
314
  balanceB.userId = user.id;
315
315
  balanceB.organizationId = organization.id;
316
316
  balanceB.pricePaid = 50;
317
- balanceB.status = BalanceItemStatus.Due
317
+ balanceB.status = BalanceItemStatus.Due;
318
318
  await balanceB.save();
319
319
 
320
320
  // Update cached balance for user.
321
- await CachedBalance.updateForMembers(organization.id, [member.id])
322
- await CachedBalance.updateForUsers(organization.id, [user.id])
321
+ await CachedBalance.updateForMembers(organization.id, [member.id]);
322
+ await CachedBalance.updateForUsers(organization.id, [user.id]);
323
323
  const cached = await CachedBalance.getForObjects([user.id], organization.id, ReceivableBalanceType.user);
324
324
  expect(cached).toHaveLength(1);
325
325
  expect(cached[0].amountOpen).toBe(0);
@@ -1,11 +1,11 @@
1
1
  import { column } from '@simonbackx/simple-database';
2
- import type { SQLWhere} from '@stamhoofd/sql';
2
+ import type { SQLWhere } from '@stamhoofd/sql';
3
3
  import { QueryableModel, SQL, SQLAlias, SQLMin, SQLSelectAs, SQLSum, SQLWhereSign } from '@stamhoofd/sql';
4
4
  import { BalanceItemStatus, BalanceItem as BalanceItemStruct, ReceivableBalanceType } from '@stamhoofd/structures';
5
+ import { Formatter } from '@stamhoofd/utility';
5
6
  import { v4 as uuidv4 } from 'uuid';
6
7
  import { BalanceItem } from './BalanceItem.js';
7
8
  import { MemberUser } from './MemberUser.js';
8
- import { Formatter } from '@stamhoofd/utility';
9
9
 
10
10
  /**
11
11
  * Keeps track of how much a member/user owes or needs to be reimbursed.
@@ -328,8 +328,7 @@ export class CachedBalance extends QueryableModel {
328
328
 
329
329
  result[1].amountPending += amountPending;
330
330
  result[1].amountPaid += amountPaid;
331
- }
332
- else {
331
+ } else {
333
332
  results.push([objectId, { amountPaid, amountOpen: 0, amountPending, nextDueAt: amountOpen !== 0 ? dueAt : null }]);
334
333
  }
335
334
  }
@@ -407,10 +406,12 @@ export class CachedBalance extends QueryableModel {
407
406
 
408
407
  static async updateForOrganizations(organizationId: string, organizationIds: string[]) {
409
408
  if (organizationIds.length === 0) {
410
- return;
409
+ return [];
411
410
  }
412
411
  const results = await this.fetchForObjects(organizationId, organizationIds, 'payingOrganizationId');
413
412
  await this.setForResults(organizationId, results, ReceivableBalanceType.organization);
413
+
414
+ return results;
414
415
  }
415
416
 
416
417
  static async updateForMembers(organizationId: string, memberIds: string[]) {
@@ -462,8 +463,7 @@ export class CachedBalance extends QueryableModel {
462
463
  if (memberCachedBalance.nextDueAt && (!result[1].nextDueAt || memberCachedBalance.nextDueAt < result[1].nextDueAt)) {
463
464
  result[1].nextDueAt = memberCachedBalance.nextDueAt;
464
465
  }
465
- }
466
- else {
466
+ } else {
467
467
  // Not possible
468
468
  throw new Error('User not found in results');
469
469
  }