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