@stamhoofd/structures 2.7.0 → 2.9.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 (197) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/BalanceItem.d.ts +44 -5
  6. package/dist/src/BalanceItem.d.ts.map +1 -1
  7. package/dist/src/BalanceItem.js +108 -12
  8. package/dist/src/BalanceItem.js.map +1 -1
  9. package/dist/src/BalanceItemDetailed.d.ts +25 -25
  10. package/dist/src/BalanceItemDetailed.d.ts.map +1 -1
  11. package/dist/src/BalanceItemDetailed.js +128 -59
  12. package/dist/src/BalanceItemDetailed.js.map +1 -1
  13. package/dist/src/Event.d.ts +4 -0
  14. package/dist/src/Event.d.ts.map +1 -1
  15. package/dist/src/Event.js +7 -0
  16. package/dist/src/Event.js.map +1 -1
  17. package/dist/src/GroupSettings.d.ts +18 -0
  18. package/dist/src/GroupSettings.d.ts.map +1 -1
  19. package/dist/src/GroupSettings.js +64 -11
  20. package/dist/src/GroupSettings.js.map +1 -1
  21. package/dist/src/OrganizationPrivateMetaData.d.ts +3 -1
  22. package/dist/src/OrganizationPrivateMetaData.d.ts.map +1 -1
  23. package/dist/src/OrganizationPrivateMetaData.js +6 -1
  24. package/dist/src/OrganizationPrivateMetaData.js.map +1 -1
  25. package/dist/src/Platform.d.ts +24 -2
  26. package/dist/src/Platform.d.ts.map +1 -1
  27. package/dist/src/Platform.js +67 -3
  28. package/dist/src/Platform.js.map +1 -1
  29. package/dist/src/StockReservation.d.ts +1 -1
  30. package/dist/src/StockReservation.d.ts.map +1 -1
  31. package/dist/src/StockReservation.js +3 -6
  32. package/dist/src/StockReservation.js.map +1 -1
  33. package/dist/src/Version.d.ts +1 -1
  34. package/dist/src/Version.js +1 -1
  35. package/dist/src/addresses/Address.d.ts.map +1 -1
  36. package/dist/src/addresses/Address.js.map +1 -1
  37. package/dist/src/addresses/Premise.d.ts +10 -0
  38. package/dist/src/addresses/Premise.d.ts.map +1 -0
  39. package/dist/src/addresses/Premise.js +28 -0
  40. package/dist/src/addresses/Premise.js.map +1 -0
  41. package/dist/src/email/EditorSmartVariable.d.ts.map +1 -1
  42. package/dist/src/email/EditorSmartVariable.js +44 -53
  43. package/dist/src/email/EditorSmartVariable.js.map +1 -1
  44. package/dist/src/email/EmailTemplate.d.ts +1 -0
  45. package/dist/src/email/EmailTemplate.d.ts.map +1 -1
  46. package/dist/src/email/EmailTemplate.js +23 -15
  47. package/dist/src/email/EmailTemplate.js.map +1 -1
  48. package/dist/src/endpoints/GroupsWithOrganizations.d.ts +8 -0
  49. package/dist/src/endpoints/GroupsWithOrganizations.d.ts.map +1 -0
  50. package/dist/src/endpoints/GroupsWithOrganizations.js +22 -0
  51. package/dist/src/endpoints/GroupsWithOrganizations.js.map +1 -0
  52. package/dist/src/members/MemberDetails.d.ts +8 -0
  53. package/dist/src/members/MemberDetails.d.ts.map +1 -1
  54. package/dist/src/members/MemberDetails.js +40 -2
  55. package/dist/src/members/MemberDetails.js.map +1 -1
  56. package/dist/src/members/OrganizationRecordsConfiguration.d.ts +16 -5
  57. package/dist/src/members/OrganizationRecordsConfiguration.d.ts.map +1 -1
  58. package/dist/src/members/OrganizationRecordsConfiguration.js +26 -1
  59. package/dist/src/members/OrganizationRecordsConfiguration.js.map +1 -1
  60. package/dist/src/members/PaymentGeneral.d.ts +2 -4
  61. package/dist/src/members/PaymentGeneral.d.ts.map +1 -1
  62. package/dist/src/members/PaymentGeneral.js +50 -26
  63. package/dist/src/members/PaymentGeneral.js.map +1 -1
  64. package/dist/src/members/PlatformMember.d.ts +17 -13
  65. package/dist/src/members/PlatformMember.d.ts.map +1 -1
  66. package/dist/src/members/PlatformMember.js +90 -78
  67. package/dist/src/members/PlatformMember.js.map +1 -1
  68. package/dist/src/members/Registration.d.ts +5 -0
  69. package/dist/src/members/Registration.d.ts.map +1 -1
  70. package/dist/src/members/Registration.js +21 -2
  71. package/dist/src/members/Registration.js.map +1 -1
  72. package/dist/src/members/checkout/BalanceItemCartItem.d.ts +3 -3
  73. package/dist/src/members/checkout/BalanceItemCartItem.d.ts.map +1 -1
  74. package/dist/src/members/checkout/BalanceItemCartItem.js +3 -3
  75. package/dist/src/members/checkout/BalanceItemCartItem.js.map +1 -1
  76. package/dist/src/members/checkout/RegisterCart.d.ts +6 -6
  77. package/dist/src/members/checkout/RegisterCart.d.ts.map +1 -1
  78. package/dist/src/members/checkout/RegisterCart.js +42 -21
  79. package/dist/src/members/checkout/RegisterCart.js.map +1 -1
  80. package/dist/src/members/checkout/RegisterCheckout.d.ts +5 -4
  81. package/dist/src/members/checkout/RegisterCheckout.d.ts.map +1 -1
  82. package/dist/src/members/checkout/RegisterCheckout.js +23 -8
  83. package/dist/src/members/checkout/RegisterCheckout.js.map +1 -1
  84. package/dist/src/members/checkout/RegisterItem.d.ts +22 -10
  85. package/dist/src/members/checkout/RegisterItem.d.ts.map +1 -1
  86. package/dist/src/members/checkout/RegisterItem.js +270 -111
  87. package/dist/src/members/checkout/RegisterItem.js.map +1 -1
  88. package/dist/src/members/records/RecordAnswer.d.ts.map +1 -1
  89. package/dist/src/members/records/RecordAnswer.js +2 -1
  90. package/dist/src/members/records/RecordAnswer.js.map +1 -1
  91. package/dist/src/webshops/CartItem.d.ts +2 -2
  92. package/dist/src/webshops/CartItem.d.ts.map +1 -1
  93. package/dist/src/webshops/CartItem.js +1 -1
  94. package/dist/src/webshops/CartItem.js.map +1 -1
  95. package/esm/dist/index.d.ts +2 -1
  96. package/esm/dist/index.d.ts.map +1 -1
  97. package/esm/dist/index.js +2 -1
  98. package/esm/dist/index.js.map +1 -1
  99. package/esm/dist/src/BalanceItem.d.ts +44 -5
  100. package/esm/dist/src/BalanceItem.d.ts.map +1 -1
  101. package/esm/dist/src/BalanceItem.js +104 -11
  102. package/esm/dist/src/BalanceItem.js.map +1 -1
  103. package/esm/dist/src/BalanceItemDetailed.d.ts +25 -25
  104. package/esm/dist/src/BalanceItemDetailed.d.ts.map +1 -1
  105. package/esm/dist/src/BalanceItemDetailed.js +128 -58
  106. package/esm/dist/src/BalanceItemDetailed.js.map +1 -1
  107. package/esm/dist/src/Event.d.ts +4 -0
  108. package/esm/dist/src/Event.d.ts.map +1 -1
  109. package/esm/dist/src/Event.js +7 -0
  110. package/esm/dist/src/Event.js.map +1 -1
  111. package/esm/dist/src/GroupSettings.d.ts +18 -0
  112. package/esm/dist/src/GroupSettings.d.ts.map +1 -1
  113. package/esm/dist/src/GroupSettings.js +64 -11
  114. package/esm/dist/src/GroupSettings.js.map +1 -1
  115. package/esm/dist/src/OrganizationPrivateMetaData.d.ts +3 -1
  116. package/esm/dist/src/OrganizationPrivateMetaData.d.ts.map +1 -1
  117. package/esm/dist/src/OrganizationPrivateMetaData.js +6 -1
  118. package/esm/dist/src/OrganizationPrivateMetaData.js.map +1 -1
  119. package/esm/dist/src/Platform.d.ts +24 -2
  120. package/esm/dist/src/Platform.d.ts.map +1 -1
  121. package/esm/dist/src/Platform.js +66 -3
  122. package/esm/dist/src/Platform.js.map +1 -1
  123. package/esm/dist/src/StockReservation.d.ts +1 -1
  124. package/esm/dist/src/StockReservation.d.ts.map +1 -1
  125. package/esm/dist/src/StockReservation.js +3 -6
  126. package/esm/dist/src/StockReservation.js.map +1 -1
  127. package/esm/dist/src/Version.d.ts +1 -1
  128. package/esm/dist/src/Version.js +1 -1
  129. package/esm/dist/src/addresses/Address.d.ts.map +1 -1
  130. package/esm/dist/src/addresses/Address.js.map +1 -1
  131. package/esm/dist/src/addresses/Premise.d.ts +10 -0
  132. package/esm/dist/src/addresses/Premise.d.ts.map +1 -0
  133. package/esm/dist/src/addresses/Premise.js +24 -0
  134. package/esm/dist/src/addresses/Premise.js.map +1 -0
  135. package/esm/dist/src/email/EditorSmartVariable.d.ts.map +1 -1
  136. package/esm/dist/src/email/EditorSmartVariable.js +46 -55
  137. package/esm/dist/src/email/EditorSmartVariable.js.map +1 -1
  138. package/esm/dist/src/email/EmailTemplate.d.ts +1 -0
  139. package/esm/dist/src/email/EmailTemplate.d.ts.map +1 -1
  140. package/esm/dist/src/email/EmailTemplate.js +23 -15
  141. package/esm/dist/src/email/EmailTemplate.js.map +1 -1
  142. package/esm/dist/src/endpoints/GroupsWithOrganizations.d.ts +8 -0
  143. package/esm/dist/src/endpoints/GroupsWithOrganizations.d.ts.map +1 -0
  144. package/esm/dist/src/endpoints/GroupsWithOrganizations.js +18 -0
  145. package/esm/dist/src/endpoints/GroupsWithOrganizations.js.map +1 -0
  146. package/esm/dist/src/members/MemberDetails.d.ts +8 -0
  147. package/esm/dist/src/members/MemberDetails.d.ts.map +1 -1
  148. package/esm/dist/src/members/MemberDetails.js +40 -2
  149. package/esm/dist/src/members/MemberDetails.js.map +1 -1
  150. package/esm/dist/src/members/OrganizationRecordsConfiguration.d.ts +16 -5
  151. package/esm/dist/src/members/OrganizationRecordsConfiguration.d.ts.map +1 -1
  152. package/esm/dist/src/members/OrganizationRecordsConfiguration.js +27 -2
  153. package/esm/dist/src/members/OrganizationRecordsConfiguration.js.map +1 -1
  154. package/esm/dist/src/members/PaymentGeneral.d.ts +2 -4
  155. package/esm/dist/src/members/PaymentGeneral.d.ts.map +1 -1
  156. package/esm/dist/src/members/PaymentGeneral.js +50 -26
  157. package/esm/dist/src/members/PaymentGeneral.js.map +1 -1
  158. package/esm/dist/src/members/PlatformMember.d.ts +17 -13
  159. package/esm/dist/src/members/PlatformMember.d.ts.map +1 -1
  160. package/esm/dist/src/members/PlatformMember.js +90 -78
  161. package/esm/dist/src/members/PlatformMember.js.map +1 -1
  162. package/esm/dist/src/members/Registration.d.ts +5 -0
  163. package/esm/dist/src/members/Registration.d.ts.map +1 -1
  164. package/esm/dist/src/members/Registration.js +21 -2
  165. package/esm/dist/src/members/Registration.js.map +1 -1
  166. package/esm/dist/src/members/checkout/BalanceItemCartItem.d.ts +3 -3
  167. package/esm/dist/src/members/checkout/BalanceItemCartItem.d.ts.map +1 -1
  168. package/esm/dist/src/members/checkout/BalanceItemCartItem.js +3 -3
  169. package/esm/dist/src/members/checkout/BalanceItemCartItem.js.map +1 -1
  170. package/esm/dist/src/members/checkout/RegisterCart.d.ts +6 -6
  171. package/esm/dist/src/members/checkout/RegisterCart.d.ts.map +1 -1
  172. package/esm/dist/src/members/checkout/RegisterCart.js +43 -22
  173. package/esm/dist/src/members/checkout/RegisterCart.js.map +1 -1
  174. package/esm/dist/src/members/checkout/RegisterCheckout.d.ts +5 -4
  175. package/esm/dist/src/members/checkout/RegisterCheckout.d.ts.map +1 -1
  176. package/esm/dist/src/members/checkout/RegisterCheckout.js +23 -8
  177. package/esm/dist/src/members/checkout/RegisterCheckout.js.map +1 -1
  178. package/esm/dist/src/members/checkout/RegisterItem.d.ts +22 -10
  179. package/esm/dist/src/members/checkout/RegisterItem.d.ts.map +1 -1
  180. package/esm/dist/src/members/checkout/RegisterItem.js +270 -111
  181. package/esm/dist/src/members/checkout/RegisterItem.js.map +1 -1
  182. package/esm/dist/src/members/records/RecordAnswer.d.ts.map +1 -1
  183. package/esm/dist/src/members/records/RecordAnswer.js +2 -1
  184. package/esm/dist/src/members/records/RecordAnswer.js.map +1 -1
  185. package/esm/dist/src/webshops/CartItem.d.ts +2 -2
  186. package/esm/dist/src/webshops/CartItem.d.ts.map +1 -1
  187. package/esm/dist/src/webshops/CartItem.js +1 -1
  188. package/esm/dist/src/webshops/CartItem.js.map +1 -1
  189. package/package.json +3 -3
  190. package/dist/src/members/PaymentDetailed.d.ts +0 -16
  191. package/dist/src/members/PaymentDetailed.d.ts.map +0 -1
  192. package/dist/src/members/PaymentDetailed.js +0 -43
  193. package/dist/src/members/PaymentDetailed.js.map +0 -1
  194. package/esm/dist/src/members/PaymentDetailed.d.ts +0 -16
  195. package/esm/dist/src/members/PaymentDetailed.d.ts.map +0 -1
  196. package/esm/dist/src/members/PaymentDetailed.js +0 -39
  197. package/esm/dist/src/members/PaymentDetailed.js.map +0 -1
@@ -8,6 +8,7 @@ const uuid_1 = require("uuid");
8
8
  const Group_1 = require("../../Group");
9
9
  const GroupSettings_1 = require("../../GroupSettings");
10
10
  const StockReservation_1 = require("../../StockReservation");
11
+ const utility_1 = require("@stamhoofd/utility");
11
12
  class RegisterItemOption extends simple_encoding_1.AutoEncoder {
12
13
  constructor() {
13
14
  super(...arguments);
@@ -28,6 +29,7 @@ class IDRegisterItem extends simple_encoding_1.AutoEncoder {
28
29
  constructor() {
29
30
  super(...arguments);
30
31
  this.options = [];
32
+ this.replaceRegistrationIds = [];
31
33
  }
32
34
  hydrate(context) {
33
35
  return RegisterItem.fromId(this, context);
@@ -52,6 +54,9 @@ tslib_1.__decorate([
52
54
  tslib_1.__decorate([
53
55
  (0, simple_encoding_1.field)({ decoder: new simple_encoding_1.ArrayDecoder(RegisterItemOption) })
54
56
  ], IDRegisterItem.prototype, "options", void 0);
57
+ tslib_1.__decorate([
58
+ (0, simple_encoding_1.field)({ decoder: new simple_encoding_1.ArrayDecoder(simple_encoding_1.StringDecoder) })
59
+ ], IDRegisterItem.prototype, "replaceRegistrationIds", void 0);
55
60
  class RegisterItem {
56
61
  /**
57
62
  * @deprecated
@@ -64,13 +69,33 @@ class RegisterItem {
64
69
  id: registration.id,
65
70
  member,
66
71
  group: registration.group,
72
+ organization,
73
+ groupPrice: registration.groupPrice,
74
+ options: registration.options
75
+ });
76
+ }
77
+ static defaultFor(member, group, organization) {
78
+ if (group.organizationId !== organization.id) {
79
+ throw new Error("Group and organization do not match in RegisterItem.defaultFor");
80
+ }
81
+ const item = new RegisterItem({
82
+ member,
83
+ group,
67
84
  organization
68
85
  });
86
+ return item;
69
87
  }
70
88
  constructor(data) {
71
- var _a, _b, _c, _d, _e;
89
+ var _a, _b, _c, _d, _e, _f, _g;
72
90
  this.options = [];
91
+ /**
92
+ * Price for the new registration
93
+ */
73
94
  this.calculatedPrice = 0;
95
+ /**
96
+ * Refund for the replaced registrations
97
+ */
98
+ this.calculatedRefund = 0;
74
99
  /**
75
100
  * These registrations will be replaced as part of this new registration (moving or updating a registration is possible this way)
76
101
  */
@@ -82,21 +107,59 @@ class RegisterItem {
82
107
  this.id = (_a = data.id) !== null && _a !== void 0 ? _a : (0, uuid_1.v4)();
83
108
  this.member = data.member;
84
109
  this.group = data.group;
85
- this.groupPrice = (_c = (_b = data.groupPrice) !== null && _b !== void 0 ? _b : this.group.settings.prices[0]) !== null && _c !== void 0 ? _c : GroupSettings_1.GroupPrice.create({ name: 'Ongeldig tarief', id: '' });
110
+ if (!data.groupPrice) {
111
+ const prices = this.getFilteredPrices();
112
+ for (const price of prices) {
113
+ const stock = price.getRemainingStock(this);
114
+ if (stock !== 0) {
115
+ this.groupPrice = price;
116
+ break;
117
+ }
118
+ }
119
+ if (!this.groupPrice) {
120
+ // Probably all sold out
121
+ // Select the first one anyway
122
+ this.groupPrice = (_b = prices[0]) !== null && _b !== void 0 ? _b : GroupSettings_1.GroupPrice.create({ name: 'Ongeldig tarief', id: '' });
123
+ }
124
+ }
125
+ else {
126
+ this.groupPrice = data.groupPrice;
127
+ }
86
128
  this.organization = data.organization;
87
- this.options = (_d = data.options) !== null && _d !== void 0 ? _d : [];
88
- this.replaceRegistrations = (_e = data.replaceRegistrations) !== null && _e !== void 0 ? _e : [];
129
+ this.options = (_c = data.options) !== null && _c !== void 0 ? _c : [];
130
+ this.replaceRegistrations = (_d = data.replaceRegistrations) !== null && _d !== void 0 ? _d : [];
131
+ this.cartError = (_e = data.cartError) !== null && _e !== void 0 ? _e : null;
132
+ this.calculatedPrice = (_f = data.calculatedPrice) !== null && _f !== void 0 ? _f : 0;
133
+ this.calculatedRefund = (_g = data.calculatedRefund) !== null && _g !== void 0 ? _g : 0;
89
134
  // Select all defaults
90
135
  for (const optionMenu of this.group.settings.optionMenus) {
91
136
  if (!optionMenu.multipleChoice) {
92
137
  if (this.options.find(o => o.optionMenu.id === optionMenu.id)) {
93
138
  continue;
94
139
  }
95
- this.options.push(RegisterItemOption.create({
96
- option: optionMenu.options[0],
97
- optionMenu: optionMenu,
98
- amount: 1
99
- }));
140
+ let added = false;
141
+ const options = this.getFilteredOptions(optionMenu);
142
+ for (const option of options) {
143
+ const stock = option.getRemainingStock(this);
144
+ if (stock === 0) {
145
+ continue;
146
+ }
147
+ this.options.push(RegisterItemOption.create({
148
+ option,
149
+ optionMenu: optionMenu,
150
+ amount: 1
151
+ }));
152
+ added = true;
153
+ break;
154
+ }
155
+ if (!added && options.length > 0) {
156
+ // Add the first (this one is sold out, but still required for correct error handling)
157
+ this.options.push(RegisterItemOption.create({
158
+ option: options[0],
159
+ optionMenu: optionMenu,
160
+ amount: 1
161
+ }));
162
+ }
100
163
  }
101
164
  }
102
165
  }
@@ -104,40 +167,43 @@ class RegisterItem {
104
167
  return this.member.family.checkout.cart.contains(this);
105
168
  }
106
169
  get showItemView() {
107
- return this.shouldUseWaitingList() || this.replaceRegistrations.length || this.group.settings.prices.length > 1 || this.group.settings.optionMenus.length > 0 || (!this.isInCart && !this.isValid);
170
+ return !!this.replaceRegistrations.length || this.group.settings.prices.length !== 1 || this.group.settings.optionMenus.length > 0 || this.group.type === Group_1.GroupType.WaitingList || this.group.settings.description.length > 2 || this.group.settings.prices[0].price.price > 0 || (!this.isInCart && !this.isValid);
108
171
  }
109
172
  calculatePrice() {
110
173
  this.calculatedPrice = this.groupPrice.price.forMember(this.member);
174
+ this.calculatedRefund = 0;
111
175
  for (const option of this.options) {
112
176
  this.calculatedPrice += option.option.price.forMember(this.member) * option.amount;
113
177
  }
114
178
  for (const registration of this.replaceRegistrations) {
115
- this.calculatedPrice -= registration.price;
179
+ this.calculatedRefund += registration.price;
116
180
  }
117
181
  }
182
+ get totalPrice() {
183
+ return this.calculatedPrice - this.calculatedRefund;
184
+ }
118
185
  get priceBreakown() {
119
186
  let all = [];
120
187
  let replacePrice = 0;
121
188
  for (const registration of this.replaceRegistrations) {
122
189
  replacePrice += registration.price;
123
190
  all.push({
124
- name: 'Terugbetaling ' + registration.group.settings.name,
191
+ name: this.checkout.isAdminFromSameOrganization ? 'Reeds aangerekend voor ' + registration.group.settings.name : 'Terugbetaling ' + registration.group.settings.name,
125
192
  price: -registration.price
126
193
  });
127
194
  }
128
- const subtotal = this.calculatedPrice + replacePrice;
129
195
  all = all.filter(a => a.price !== 0);
130
196
  if (all.length > 0) {
131
197
  all.unshift({
132
198
  name: 'Subtotaal',
133
- price: subtotal
199
+ price: this.calculatedPrice
134
200
  });
135
201
  }
136
202
  return [
137
203
  ...all,
138
204
  {
139
- name: 'Totaal',
140
- price: this.calculatedPrice
205
+ name: this.checkout.isAdminFromSameOrganization ? (this.totalPrice >= 0 ? 'Openstaand bedrag stijgt met' : 'Openstaand bedrag daalt met') : 'Totaal',
206
+ price: this.checkout.isAdminFromSameOrganization ? Math.abs(this.totalPrice) : this.totalPrice
141
207
  }
142
208
  ];
143
209
  }
@@ -149,34 +215,30 @@ class RegisterItem {
149
215
  organization: this.organization,
150
216
  groupPrice: this.groupPrice.clone(),
151
217
  options: this.options.map(o => o.clone()),
152
- replaceRegistrations: this.replaceRegistrations.map(r => r.clone())
218
+ replaceRegistrations: this.replaceRegistrations.map(r => r.clone()),
219
+ cartError: this.cartError,
220
+ calculatedPrice: this.calculatedPrice,
221
+ calculatedRefund: this.calculatedRefund
153
222
  });
154
223
  }
155
224
  copyFrom(item) {
156
225
  this.groupPrice = item.groupPrice.clone();
157
226
  this.options = item.options.map(o => o.clone());
158
227
  this.calculatedPrice = item.calculatedPrice;
228
+ this.calculatedRefund = item.calculatedRefund;
159
229
  }
160
230
  getFilteredPrices() {
161
- return this.group.settings.prices.filter(p => {
162
- if (p.hidden && !this.checkout.isAdminFromSameOrganization) {
163
- return false;
164
- }
165
- return true;
166
- });
231
+ const base = this.group.settings.getFilteredPrices({ admin: this.checkout.isAdminFromSameOrganization });
232
+ if (this.groupPrice && !base.some(b => b.id === this.groupPrice.id)) {
233
+ return [this.groupPrice, ...base];
234
+ }
235
+ return base;
167
236
  }
168
237
  getFilteredOptionMenus() {
169
- return this.group.settings.optionMenus.filter(p => {
170
- return this.getFilteredOptions(p).length > 0;
171
- });
238
+ return this.group.settings.getFilteredOptionMenus({ admin: this.checkout.isAdminFromSameOrganization });
172
239
  }
173
240
  getFilteredOptions(menu) {
174
- return menu.options.filter(p => {
175
- if (p.hidden && !this.checkout.isAdminFromSameOrganization) {
176
- return false;
177
- }
178
- return true;
179
- });
241
+ return menu.getFilteredOptions({ admin: this.checkout.isAdminFromSameOrganization });
180
242
  }
181
243
  convert() {
182
244
  return IDRegisterItem.create({
@@ -185,7 +247,8 @@ class RegisterItem {
185
247
  groupId: this.group.id,
186
248
  organizationId: this.organization.id,
187
249
  groupPrice: this.groupPrice,
188
- options: this.options
250
+ options: this.options,
251
+ replaceRegistrationIds: this.replaceRegistrations.map(r => r.id)
189
252
  });
190
253
  }
191
254
  get memberId() {
@@ -204,21 +267,6 @@ class RegisterItem {
204
267
  get checkout() {
205
268
  return this.family.checkout;
206
269
  }
207
- static defaultFor(member, group, organization) {
208
- if (group.organizationId !== organization.id) {
209
- throw new Error("Group and organization do not match in RegisterItem.defaultFor");
210
- }
211
- const item = new RegisterItem({
212
- member,
213
- group,
214
- organization
215
- });
216
- //if (item.shouldUseWaitingList() && group.waitingList) {
217
- // group = group.waitingList
218
- // item = RegisterItem.defaultFor(member, group, organization);
219
- //}
220
- return item;
221
- }
222
270
  /**
223
271
  * Update self to the newest available data, and throw error if something failed (only after refreshing other ones)
224
272
  */
@@ -238,7 +286,8 @@ class RegisterItem {
238
286
  errors.addError(new simple_errors_1.SimpleError({
239
287
  code: "product_unavailable",
240
288
  message: "Product unavailable",
241
- human: "Eén of meerdere tarieven van " + this.group.settings.name + " zijn niet meer beschikbaar"
289
+ human: "Eén of meerdere tarieven van " + this.group.settings.name + " zijn niet meer beschikbaar",
290
+ meta: { recoverable: true }
242
291
  }));
243
292
  }
244
293
  else {
@@ -255,7 +304,8 @@ class RegisterItem {
255
304
  errors.addError(new simple_errors_1.SimpleError({
256
305
  code: "option_menu_unavailable",
257
306
  message: "Option menu unavailable",
258
- human: "Eén of meerdere keuzemogelijkheden van " + this.group.settings.name + " zijn niet meer beschikbaar"
307
+ human: "Eén of meerdere keuzemogelijkheden van " + this.group.settings.name + " zijn niet meer beschikbaar",
308
+ meta: { recoverable: true }
259
309
  }));
260
310
  continue;
261
311
  }
@@ -269,7 +319,8 @@ class RegisterItem {
269
319
  errors.addError(new simple_errors_1.SimpleError({
270
320
  code: "option_unavailable",
271
321
  message: "Option unavailable",
272
- human: "Eén of meerdere keuzemogelijkheden van " + this.group.settings.name + " zijn niet meer beschikbaar"
322
+ human: "Eén of meerdere keuzemogelijkheden van " + this.group.settings.name + " zijn niet meer beschikbaar",
323
+ meta: { recoverable: true }
273
324
  }));
274
325
  continue;
275
326
  }
@@ -281,13 +332,17 @@ class RegisterItem {
281
332
  errors.addError(new simple_errors_1.SimpleError({
282
333
  code: "missing_menu",
283
334
  message: "Missing menu's " + remainingMenus.filter(m => !m.multipleChoice).map(m => m.name).join(", "),
284
- human: "Er zijn nieuwe keuzemogelijkheden voor " + this.group.settings.name + " waaruit je moet kiezen"
335
+ human: "Er zijn nieuwe keuzemogelijkheden voor " + this.group.settings.name + " waaruit je moet kiezen",
336
+ meta: { recoverable: true }
285
337
  }));
286
338
  }
287
339
  errors.throwIfNotEmpty();
288
340
  }
341
+ willReplace(registrationId) {
342
+ return this.replaceRegistrations.some(rr => rr.id === registrationId);
343
+ }
289
344
  isAlreadyRegistered() {
290
- return !!this.member.member.registrations.find(r => r.groupId === this.group.id && r.registeredAt !== null && r.deactivatedAt === null);
345
+ return !!this.member.member.registrations.find(r => !this.willReplace(r.id) && r.groupId === this.group.id && r.registeredAt !== null && r.deactivatedAt === null);
291
346
  }
292
347
  hasReachedCategoryMaximum() {
293
348
  if (this.group.type !== Group_1.GroupType.Membership) {
@@ -297,7 +352,7 @@ class RegisterItem {
297
352
  for (const parent of parents) {
298
353
  if (parent.settings.maximumRegistrations !== null) {
299
354
  const count = this.member.patchedMember.registrations.filter(r => {
300
- if (r.registeredAt !== null && r.deactivatedAt === null && parent.groupIds.includes(r.groupId)) {
355
+ if (!this.willReplace(r.id) && r.registeredAt !== null && r.deactivatedAt === null && parent.groupIds.includes(r.groupId)) {
301
356
  return true;
302
357
  }
303
358
  return false;
@@ -318,7 +373,7 @@ class RegisterItem {
318
373
  doesMeetRequireGroupIds() {
319
374
  if (this.group.settings.requireGroupIds.length > 0) {
320
375
  const hasGroup = this.member.member.registrations.find(r => {
321
- return r.registeredAt !== null && r.deactivatedAt === null && this.group.settings.requireGroupIds.includes(r.groupId);
376
+ return !this.willReplace(r.id) && r.registeredAt !== null && r.deactivatedAt === null && this.group.settings.requireGroupIds.includes(r.groupId);
322
377
  });
323
378
  if (!hasGroup && !this.checkout.cart.items.find(item => item.member.id === this.member.id && this.group.settings.requireGroupIds.includes(item.group.id))) {
324
379
  return false;
@@ -326,7 +381,7 @@ class RegisterItem {
326
381
  }
327
382
  if (this.group.settings.requireDefaultAgeGroupIds.length > 0) {
328
383
  const hasGroup = this.member.member.registrations.find(r => {
329
- return r.registeredAt !== null && r.deactivatedAt === null && r.group.defaultAgeGroupId && this.group.settings.requireDefaultAgeGroupIds.includes(r.group.defaultAgeGroupId);
384
+ return !this.willReplace(r.id) && r.registeredAt !== null && r.deactivatedAt === null && r.group.defaultAgeGroupId && this.group.settings.requireDefaultAgeGroupIds.includes(r.group.defaultAgeGroupId);
330
385
  });
331
386
  if (!hasGroup && !this.checkout.cart.items.find(item => item.member.id === this.member.id && item.group.defaultAgeGroupId && this.group.settings.requireDefaultAgeGroupIds.includes(item.group.defaultAgeGroupId))) {
332
387
  return false;
@@ -337,7 +392,7 @@ class RegisterItem {
337
392
  doesMeetRequireOrganizationIds() {
338
393
  if (this.group.settings.requireOrganizationIds.length > 0) {
339
394
  const hasGroup = this.member.member.registrations.find(r => {
340
- return r.group.type === Group_1.GroupType.Membership && this.group.settings.requireOrganizationIds.includes(r.organizationId) && r.registeredAt !== null && r.deactivatedAt === null;
395
+ return !this.willReplace(r.id) && r.group.type === Group_1.GroupType.Membership && this.group.settings.requireOrganizationIds.includes(r.organizationId) && r.registeredAt !== null && r.deactivatedAt === null;
341
396
  });
342
397
  if (!hasGroup && !this.checkout.cart.items.find(item => item.member.id === this.member.id && this.group.settings.requireOrganizationIds.includes(item.organization.id))) {
343
398
  return false;
@@ -381,36 +436,57 @@ class RegisterItem {
381
436
  }
382
437
  return descriptions.filter(d => !!d).join("\n");
383
438
  }
384
- shouldUseWaitingList() {
385
- if (this.group.settings.waitingListType === GroupSettings_1.WaitingListType.All) {
386
- return true;
387
- }
388
- const existingMember = this.isExistingMemberOrFamily();
389
- if (this.group.settings.waitingListType === GroupSettings_1.WaitingListType.ExistingMembersFirst && !existingMember) {
439
+ hasReachedGroupMaximum() {
440
+ const available = this.group.settings.getRemainingStock(this);
441
+ if (available !== null && available <= 0) {
390
442
  return true;
391
443
  }
392
- if (this.group.waitingList) {
393
- if (this.hasReachedGroupMaximum()) {
444
+ // If all prices are sold out -> also reached maximum
445
+ const prices = this.getFilteredPrices();
446
+ if (prices.length > 0) {
447
+ let allPricesSoldOut = true;
448
+ for (const price of prices) {
449
+ const remaining = price.getRemainingStock(this);
450
+ if (remaining === null || remaining > 0) {
451
+ allPricesSoldOut = false;
452
+ break;
453
+ }
454
+ }
455
+ if (allPricesSoldOut) {
394
456
  return true;
395
457
  }
396
458
  }
397
- return false;
398
- }
399
- hasReachedGroupMaximum() {
400
- const available = this.group.settings.availableMembers;
401
- if (available !== null) {
402
- const count = this.checkout.cart.items.filter(item => item.group.id === this.group.id && item.member.member.id !== this.member.member.id && !item.waitingList).length;
403
- if (count >= available) {
404
- // Check if we have a reserved spot
405
- const now = new Date();
406
- const reserved = this.member.member.registrations.find(r => r.groupId === this.group.id && r.reservedUntil && r.reservedUntil > now && !r.waitingList && r.registeredAt === null && r.cycle === this.group.cycle);
407
- if (!reserved) {
459
+ // If non-multiple choice option menu's are sold out -> also reached maximum
460
+ const optionMenus = this.getFilteredOptionMenus();
461
+ for (const menu of optionMenus) {
462
+ if (!menu.multipleChoice) {
463
+ let allOptionsSoldOut = true;
464
+ for (const option of menu.options) {
465
+ const remaining = option.getRemainingStock(this);
466
+ if (remaining === null || remaining > 0) {
467
+ allOptionsSoldOut = false;
468
+ break;
469
+ }
470
+ }
471
+ if (allOptionsSoldOut) {
408
472
  return true;
409
473
  }
410
474
  }
411
475
  }
412
476
  return false;
413
477
  }
478
+ get validationErrorForWaitingList() {
479
+ try {
480
+ this.validate({ forWaitingList: true });
481
+ }
482
+ catch (e) {
483
+ if ((0, simple_errors_1.isSimpleError)(e) || (0, simple_errors_1.isSimpleErrors)(e)) {
484
+ return e.getHuman();
485
+ }
486
+ throw e;
487
+ }
488
+ return null;
489
+ }
414
490
  get validationError() {
415
491
  try {
416
492
  this.validate();
@@ -447,14 +523,28 @@ class RegisterItem {
447
523
  }
448
524
  validate(options) {
449
525
  var _a;
450
- this.cartError = null;
451
526
  this.refresh(this.group);
452
527
  const checkout = this.member.family.checkout;
453
528
  const admin = checkout.isAdminFromSameOrganization && !(options === null || options === void 0 ? void 0 : options.warnings);
454
529
  if (this.group.organizationId !== this.organization.id) {
455
530
  throw new Error("Group and organization do not match in RegisterItem.validate");
456
531
  }
457
- if (checkout.asOrganizationId && !admin && !this.group.settings.allowRegistrationsByOrganization) {
532
+ if (this.checkout.singleOrganization && this.checkout.singleOrganization.id !== this.organization.id) {
533
+ throw new simple_errors_1.SimpleError({
534
+ code: "multiple_organizations",
535
+ message: "Cannot add items of multiple organizations to the checkout",
536
+ human: `Reken eerst jouw huidige winkelmandje af. Inschrijvingen voor ${this.group.settings.name} moeten aan een andere organisatie betaald worden en kan je daardoor niet samen afrekenen.`,
537
+ meta: { recoverable: true }
538
+ });
539
+ }
540
+ if ((options === null || options === void 0 ? void 0 : options.forWaitingList) && !this.group.waitingList) {
541
+ throw new simple_errors_1.SimpleError({
542
+ code: "missing_waiting_list",
543
+ message: "No waiting list",
544
+ human: `Je kan niet inschrijven voor de wachtlijst`
545
+ });
546
+ }
547
+ if (checkout.asOrganizationId && !checkout.isAdminFromSameOrganization && !this.group.settings.allowRegistrationsByOrganization) {
458
548
  throw new simple_errors_1.SimpleError({
459
549
  code: "as_organization_disabled",
460
550
  message: "allowRegistrationsByOrganization disabled",
@@ -463,7 +553,7 @@ class RegisterItem {
463
553
  }
464
554
  for (const registration of this.replaceRegistrations) {
465
555
  // todo: check if you are allowed to move
466
- if (registration.member.id !== this.member.id) {
556
+ if (registration.memberId !== this.member.id) {
467
557
  throw new simple_errors_1.SimpleError({
468
558
  code: "invalid_move",
469
559
  message: "Invalid member in replaceRegistration",
@@ -479,6 +569,14 @@ class RegisterItem {
479
569
  field: "replaceRegistrations"
480
570
  });
481
571
  }
572
+ if (!admin) {
573
+ throw new simple_errors_1.SimpleError({
574
+ code: "invalid_move",
575
+ message: "Not allowed to move registrations",
576
+ human: "Enkel beheerders kunnen inschrijvingen verplaatsen.",
577
+ field: "replaceRegistrations"
578
+ });
579
+ }
482
580
  }
483
581
  // Already registered
484
582
  if (this.isAlreadyRegistered()) {
@@ -501,19 +599,21 @@ class RegisterItem {
501
599
  return;
502
600
  }
503
601
  if (!admin) {
504
- if (this.group.notYetOpen) {
505
- throw new simple_errors_1.SimpleError({
506
- code: "not_yet_open",
507
- message: "Not yet open",
508
- human: `De inschrijvingen voor ${this.group.settings.name} zijn nog niet geopend.`
509
- });
510
- }
511
- if (this.group.closed) {
512
- throw new simple_errors_1.SimpleError({
513
- code: "closed",
514
- message: "Closed",
515
- human: `De inschrijvingen voor ${this.group.settings.name} zijn gesloten.`
516
- });
602
+ if (!(options === null || options === void 0 ? void 0 : options.forWaitingList)) {
603
+ if (this.group.notYetOpen) {
604
+ throw new simple_errors_1.SimpleError({
605
+ code: "not_yet_open",
606
+ message: "Not yet open",
607
+ human: `De inschrijvingen voor ${this.group.settings.name} zijn nog niet geopend.`
608
+ });
609
+ }
610
+ if (this.group.closed) {
611
+ throw new simple_errors_1.SimpleError({
612
+ code: "closed",
613
+ message: "Closed",
614
+ human: `De inschrijvingen voor ${this.group.settings.name} zijn gesloten.`
615
+ });
616
+ }
517
617
  }
518
618
  // Check if it fits
519
619
  if (this.member.member.details) {
@@ -566,22 +666,73 @@ class RegisterItem {
566
666
  });
567
667
  }
568
668
  }
569
- if (this.shouldUseWaitingList()) {
570
- throw new simple_errors_1.SimpleError({
571
- code: "waiting_list_required",
572
- message: "Waiting list required",
573
- human: `${this.member.member.firstName} kan momenteel enkel voor de wachtlijst van ${this.group.settings.name} inschrijven.`,
574
- meta: { recoverable: true }
575
- });
669
+ const reachedMaximum = this.hasReachedGroupMaximum();
670
+ if (!(options === null || options === void 0 ? void 0 : options.forWaitingList)) {
671
+ // More detailed error messages
672
+ if (this.group.settings.waitingListType === GroupSettings_1.WaitingListType.All) {
673
+ throw new simple_errors_1.SimpleError({
674
+ code: "waiting_list_required",
675
+ message: "Waiting list required",
676
+ human: `Iedereen moet zich eerst op de wachtlijst inschrijven`,
677
+ meta: { recoverable: true }
678
+ });
679
+ }
680
+ if (this.group.settings.waitingListType === GroupSettings_1.WaitingListType.ExistingMembersFirst && !existingMember) {
681
+ throw new simple_errors_1.SimpleError({
682
+ code: "waiting_list_required",
683
+ message: "Waiting list required",
684
+ human: `Nieuwe leden moeten zich eerst op de wachtlijst inschrijven`,
685
+ meta: { recoverable: true }
686
+ });
687
+ }
688
+ if (this.group.waitingList) {
689
+ if (reachedMaximum) {
690
+ throw new simple_errors_1.SimpleError({
691
+ code: "waiting_list_required",
692
+ message: "Waiting list required",
693
+ human: `De inschrijvingen voor ${this.group.settings.name} zijn volzet. Je kan wel nog inschrijven voor de wachtlijst`,
694
+ meta: { recoverable: true }
695
+ });
696
+ }
697
+ }
576
698
  }
577
- if (this.hasReachedGroupMaximum()) {
699
+ if (reachedMaximum && !this.group.waitingList) {
700
+ // Reached maximum without waiting lists
578
701
  throw new simple_errors_1.SimpleError({
579
702
  code: "maximum_reached",
580
703
  message: "Maximum reached",
581
- human: this.group.waitingList ? `De inschrijvingen voor ${this.group.settings.name} zijn volzet. Je kan wel nog inschrijven voor de wachtlijst.` : `De inschrijvingen voor ${this.group.settings.name} zijn volzet. `,
704
+ human: `De inschrijvingen voor ${this.group.settings.name} zijn volzet`,
582
705
  meta: { recoverable: true }
583
706
  });
584
707
  }
708
+ // Only check individual stock if we haven't reached the maximum - otherwise it won't suggest to use the waiting list
709
+ if (!reachedMaximum) {
710
+ // Check individual stock
711
+ if (this.groupPrice.getRemainingStock(this) === 0) {
712
+ throw new simple_errors_1.SimpleError({
713
+ code: "stock_empty",
714
+ message: "Stock empty",
715
+ human: `Het tarief ${this.groupPrice.name} is uitverkocht`,
716
+ meta: { recoverable: true }
717
+ });
718
+ }
719
+ for (const option of this.options) {
720
+ const remaining = option.option.getRemainingStock(this);
721
+ if (remaining !== null && remaining < option.amount) {
722
+ throw new simple_errors_1.SimpleError({
723
+ code: "stock_empty",
724
+ message: "Stock empty",
725
+ human: remaining === 0 ? `De keuzemogelijkheid ${option.option.name} is uitverkocht` : `Er zijn nog maar ${utility_1.Formatter.pluralText(remaining, 'stuk', 'stuks')} beschikbaar van ${option.option.name}`,
726
+ meta: { recoverable: true }
727
+ });
728
+ }
729
+ }
730
+ }
731
+ if (options === null || options === void 0 ? void 0 : options.forWaitingList) {
732
+ // Also check waiting list itself
733
+ const item = RegisterItem.defaultFor(this.member, this.group.waitingList, this.organization);
734
+ item.validate({ warnings: options === null || options === void 0 ? void 0 : options.warnings });
735
+ }
585
736
  }
586
737
  }
587
738
  static fromId(idRegisterItem, context) {
@@ -597,27 +748,33 @@ class RegisterItem {
597
748
  if (!group) {
598
749
  throw new Error("Group not found: " + idRegisterItem.groupId);
599
750
  }
751
+ const replaceRegistrations = [];
752
+ for (const registrationId of idRegisterItem.replaceRegistrationIds) {
753
+ const registration = member.patchedMember.registrations.find(r => r.id === registrationId);
754
+ if (!registration) {
755
+ throw new Error("Registration not found: " + registrationId);
756
+ }
757
+ replaceRegistrations.push(registration);
758
+ }
600
759
  return new RegisterItem({
601
760
  id: idRegisterItem.id,
602
761
  member,
603
762
  group,
604
763
  organization,
605
764
  groupPrice: idRegisterItem.groupPrice,
606
- options: idRegisterItem.options
765
+ options: idRegisterItem.options,
766
+ replaceRegistrations
607
767
  });
608
768
  }
609
- get paymentConfiguration() {
610
- if (this.calculatedPrice === 0) {
611
- return null;
612
- }
613
- return this.organization.meta.registrationPaymentConfiguration;
614
- }
615
769
  /**
616
770
  * Returns the stock that will be taken (or freed if negative) by all the register items before this item
617
771
  * and with the removed registrations freed up, so this can be negative
618
772
  */
619
773
  getCartPendingStockReservations() {
620
- const deleteRegistrations = this.checkout.cart.deleteRegistrations.filter(r => r.groupId === this.group.id);
774
+ const deleteRegistrations = [
775
+ ...this.checkout.cart.deleteRegistrations.filter(r => r.groupId === this.group.id),
776
+ ...this.replaceRegistrations.filter(r => r.groupId === this.group.id)
777
+ ];
621
778
  const cartIndex = this.checkout.cart.items.findIndex(i => i.id === this.id);
622
779
  const itemsBefore = this.checkout.cart.items.slice(0, cartIndex === -1 ? undefined : cartIndex);
623
780
  return StockReservation_1.StockReservation.removed(itemsBefore.flatMap(i => i.getPendingStockReservations()), // these will be removed
@@ -625,10 +782,10 @@ class RegisterItem {
625
782
  );
626
783
  }
627
784
  /**
628
- * Stock that will be taken by this item
785
+ * Stock that will be taken or removed by this item
629
786
  */
630
787
  getPendingStockReservations() {
631
- return [
788
+ const base = [
632
789
  // Global level stock reservations (stored in each group)
633
790
  StockReservation_1.StockReservation.create({
634
791
  objectId: this.group.id,
@@ -651,6 +808,8 @@ class RegisterItem {
651
808
  ]
652
809
  })
653
810
  ];
811
+ const freed = this.replaceRegistrations.flatMap(r => r.stockReservations);
812
+ return StockReservation_1.StockReservation.removed(base, freed);
654
813
  }
655
814
  }
656
815
  exports.RegisterItem = RegisterItem;