@universis/accommodation 1.0.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 (134) hide show
  1. package/.gitlab-ci.yml +23 -0
  2. package/LICENSE +163 -0
  3. package/README.md +3 -0
  4. package/dist/AccommodationSchemaLoader.js +35 -0
  5. package/dist/AccommodationSchemaLoader.js.map +1 -0
  6. package/dist/AccommodationService.js +68 -0
  7. package/dist/AccommodationService.js.map +1 -0
  8. package/dist/DepartmentReplacer.js +54 -0
  9. package/dist/DepartmentReplacer.js.map +1 -0
  10. package/dist/StudentReplacer.js +75 -0
  11. package/dist/StudentReplacer.js.map +1 -0
  12. package/dist/config/models/Accommodation.json +96 -0
  13. package/dist/config/models/AccommodationCardEffectiveStatusType.json +107 -0
  14. package/dist/config/models/AccommodationCardEffectiveStatusTypeLocale.json +85 -0
  15. package/dist/config/models/AccommodationChildFinancialAttribute.json +46 -0
  16. package/dist/config/models/AccommodationGuardianFinancialAttribute.json +46 -0
  17. package/dist/config/models/AccommodationInscriptionModeTag.json +100 -0
  18. package/dist/config/models/AccommodationRequestAction.json +500 -0
  19. package/dist/config/models/AccommodationRequestActionMessage.json +46 -0
  20. package/dist/config/models/AccommodationRequestActionReview.json +31 -0
  21. package/dist/config/models/AccommodationRequestAttachmentConfiguration.json +72 -0
  22. package/dist/config/models/AccommodationRequestCategory.json +50 -0
  23. package/dist/config/models/AccommodationRequestEffectiveStatusType.json +212 -0
  24. package/dist/config/models/AccommodationRequestEffectiveStatusTypeLocale.json +85 -0
  25. package/dist/config/models/AccommodationRequestEvent.json +117 -0
  26. package/dist/config/models/AccommodationRequestFinancialAttribute.json +53 -0
  27. package/dist/config/models/AccommodationRoom.json +151 -0
  28. package/dist/config/models/AccommodationSiblingFinancialAttribute.json +57 -0
  29. package/dist/config/models/CancelAccommodationCardAction.json +32 -0
  30. package/dist/config/models/GuardianType.json +78 -0
  31. package/dist/config/models/Review.json +50 -0
  32. package/dist/config/models/RoomType.json +320 -0
  33. package/dist/config/models/RoomTypeLocale.json +86 -0
  34. package/dist/config/models/StudentAccommodationCard.json +158 -0
  35. package/dist/config/models/TestAction.json +50 -0
  36. package/dist/config/scope.access.js +148 -0
  37. package/dist/config/scope.access.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +3 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/listeners/AddDefaultAttachmentConfiguration.js +44 -0
  42. package/dist/listeners/AddDefaultAttachmentConfiguration.js.map +1 -0
  43. package/dist/listeners/DefaultAttachmentConfigurations.json +277 -0
  44. package/dist/listeners/DefaultAttachmentTypes.json +523 -0
  45. package/dist/listeners/GenerateRequestNumberListener.js +87 -0
  46. package/dist/listeners/GenerateRequestNumberListener.js.map +1 -0
  47. package/dist/listeners/OnActionCompleteListener.js +96 -0
  48. package/dist/listeners/OnActionCompleteListener.js.map +1 -0
  49. package/dist/listeners/OnCancelAccommodationCardListener.js +87 -0
  50. package/dist/listeners/OnCancelAccommodationCardListener.js.map +1 -0
  51. package/dist/listeners/OnReactivateAccommodationCardListener.js +87 -0
  52. package/dist/listeners/OnReactivateAccommodationCardListener.js.map +1 -0
  53. package/dist/listeners/SendEmailAfterActionStatusChangeListener.js +98 -0
  54. package/dist/listeners/SendEmailAfterActionStatusChangeListener.js.map +1 -0
  55. package/dist/listeners/SendEmailAfterCardStatusChangeListener.js +139 -0
  56. package/dist/listeners/SendEmailAfterCardStatusChangeListener.js.map +1 -0
  57. package/dist/listeners/StudentAccommodationCardSaveListener.js +90 -0
  58. package/dist/listeners/StudentAccommodationCardSaveListener.js.map +1 -0
  59. package/dist/listeners/UpdateStudentAccommodationCardListener.js +70 -0
  60. package/dist/listeners/UpdateStudentAccommodationCardListener.js.map +1 -0
  61. package/dist/listeners/ValidateActionListener.js +348 -0
  62. package/dist/listeners/ValidateActionListener.js.map +1 -0
  63. package/dist/listeners/data-localization.js +209 -0
  64. package/dist/listeners/data-localization.js.map +1 -0
  65. package/dist/listeners/lookup-cache-listener.js +90 -0
  66. package/dist/listeners/lookup-cache-listener.js.map +1 -0
  67. package/dist/models/Accommodation.js +53 -0
  68. package/dist/models/Accommodation.js.map +1 -0
  69. package/dist/models/AccommodationRequestAction.js +214 -0
  70. package/dist/models/AccommodationRequestAction.js.map +1 -0
  71. package/dist/models/AccommodationRequestEvent.js +277 -0
  72. package/dist/models/AccommodationRequestEvent.js.map +1 -0
  73. package/dist/models/EnableAttachmentModel.js +210 -0
  74. package/dist/models/EnableAttachmentModel.js.map +1 -0
  75. package/dist/models/StudentAccommodationCard.js +93 -0
  76. package/dist/models/StudentAccommodationCard.js.map +1 -0
  77. package/dist/models/TestAction.d.ts +5 -0
  78. package/dist/models/TestAction.js +5 -0
  79. package/dist/models/TestAction.js.map +1 -0
  80. package/dist/templates/mail-after-dining-request-status-change/html.ejs +27 -0
  81. package/package.json +57 -0
  82. package/src/AccommodationSchemaLoader.js +35 -0
  83. package/src/AccommodationService.js +67 -0
  84. package/src/DepartmentReplacer.js +53 -0
  85. package/src/StudentReplacer.js +74 -0
  86. package/src/config/models/Accommodation.json +96 -0
  87. package/src/config/models/AccommodationCardEffectiveStatusType.json +107 -0
  88. package/src/config/models/AccommodationCardEffectiveStatusTypeLocale.json +85 -0
  89. package/src/config/models/AccommodationChildFinancialAttribute.json +46 -0
  90. package/src/config/models/AccommodationGuardianFinancialAttribute.json +46 -0
  91. package/src/config/models/AccommodationInscriptionModeTag.json +100 -0
  92. package/src/config/models/AccommodationRequestAction.json +500 -0
  93. package/src/config/models/AccommodationRequestActionMessage.json +46 -0
  94. package/src/config/models/AccommodationRequestActionReview.json +31 -0
  95. package/src/config/models/AccommodationRequestAttachmentConfiguration.json +72 -0
  96. package/src/config/models/AccommodationRequestCategory.json +50 -0
  97. package/src/config/models/AccommodationRequestEffectiveStatusType.json +212 -0
  98. package/src/config/models/AccommodationRequestEffectiveStatusTypeLocale.json +85 -0
  99. package/src/config/models/AccommodationRequestEvent.json +117 -0
  100. package/src/config/models/AccommodationRequestFinancialAttribute.json +53 -0
  101. package/src/config/models/AccommodationRoom.json +151 -0
  102. package/src/config/models/AccommodationSiblingFinancialAttribute.json +57 -0
  103. package/src/config/models/CancelAccommodationCardAction.json +32 -0
  104. package/src/config/models/GuardianType.json +78 -0
  105. package/src/config/models/Review.json +50 -0
  106. package/src/config/models/RoomType.json +320 -0
  107. package/src/config/models/RoomTypeLocale.json +86 -0
  108. package/src/config/models/StudentAccommodationCard.json +158 -0
  109. package/src/config/models/TestAction.json +50 -0
  110. package/src/config/scope.access.js +148 -0
  111. package/src/index.d.ts +2 -0
  112. package/src/index.js +2 -0
  113. package/src/listeners/AddDefaultAttachmentConfiguration.js +43 -0
  114. package/src/listeners/DefaultAttachmentConfigurations.json +277 -0
  115. package/src/listeners/DefaultAttachmentTypes.json +523 -0
  116. package/src/listeners/GenerateRequestNumberListener.js +86 -0
  117. package/src/listeners/OnActionCompleteListener.js +95 -0
  118. package/src/listeners/OnCancelAccommodationCardListener.js +86 -0
  119. package/src/listeners/OnReactivateAccommodationCardListener.js +86 -0
  120. package/src/listeners/SendEmailAfterActionStatusChangeListener.js +97 -0
  121. package/src/listeners/SendEmailAfterCardStatusChangeListener.js +138 -0
  122. package/src/listeners/StudentAccommodationCardSaveListener.js +89 -0
  123. package/src/listeners/UpdateStudentAccommodationCardListener.js +69 -0
  124. package/src/listeners/ValidateActionListener.js +347 -0
  125. package/src/listeners/data-localization.js +214 -0
  126. package/src/listeners/lookup-cache-listener.js +89 -0
  127. package/src/models/Accommodation.js +52 -0
  128. package/src/models/AccommodationRequestAction.js +195 -0
  129. package/src/models/AccommodationRequestEvent.js +276 -0
  130. package/src/models/EnableAttachmentModel.js +209 -0
  131. package/src/models/StudentAccommodationCard.js +92 -0
  132. package/src/models/TestAction.d.ts +5 -0
  133. package/src/models/TestAction.js +10 -0
  134. package/src/templates/mail-after-dining-request-status-change/html.ejs +27 -0
@@ -0,0 +1,347 @@
1
+ import {DataError} from "@themost/common";
2
+ import { HttpForbiddenError } from '@themost/common/errors';
3
+ import { DataObjectState, DataModel } from "@themost/data";
4
+ // eslint-disable-next-line no-unused-vars
5
+ async function beforeSaveAsync(event) {
6
+
7
+ let runAllRules = false;
8
+ const user = event.model.context.user;
9
+ const target = event.model.convert(event.target);
10
+ const context = event.model.context;
11
+
12
+ let studentId = target.student;
13
+ let action;
14
+ let eventHasConsent;
15
+
16
+ if (!Object.prototype.hasOwnProperty.call(target,'student')) {
17
+ const actionID = event.target || event.target.id;
18
+ action = await context.model('AccommodationRequestAction')
19
+ .where('id')
20
+ .equal(actionID)
21
+ .select('student','accommodationRequestEvent/hasConsent as hasConsent','studentConsent')
22
+ .getItem();
23
+ studentId = action.student;
24
+ }
25
+ if (!studentId) {
26
+ throw new DataError('E_STUDENT', 'The student cannot be empty at this context.');
27
+ }
28
+
29
+ const student = event.model.context.model('Student').convert(studentId);
30
+
31
+ const isMe = await student.is(':me');
32
+ if (isMe) {
33
+ const isActive = await student.is(':active');
34
+ if (!isActive) {
35
+ throw new HttpForbiddenError();
36
+ }
37
+
38
+ const accommodationRequestEvent = target.accommodationRequestEvent || target.accommodationRequestEvent.id;
39
+ eventHasConsent = await context.model('AccommodationRequestEvent')
40
+ .where('id')
41
+ .equal(accommodationRequestEvent)
42
+ .select('hasConsent')
43
+ .silent()
44
+ .value();
45
+ }
46
+
47
+ if (event.state === 2) {
48
+ if (isMe === true) {
49
+ //check studentConsent
50
+ if (event.target && eventHasConsent === true && Object.prototype.hasOwnProperty.call(target, 'studentConsent') === false) {
51
+ const translateService = context.application.getStrategy(function TranslateService() { });
52
+ throw new DataError('E_STUDENT_CONSENT', translateService.translate('The application cannot be saved without consent about personal data.'), null, 'AccommodationRequestAction', 'studentConsent');
53
+ } else if (event.target && eventHasConsent === true && Object.prototype.hasOwnProperty.call(target, 'studentConsent') === true && target.studentConsent === false) {
54
+ const translateService = context.application.getStrategy(function TranslateService() { });
55
+ throw new DataError('E_STUDENT_CONSENT', translateService.translate('The application cannot be saved without consent about personal data.'), null, 'AccommodationRequestAction', 'studentConsent');
56
+ }
57
+ //check category
58
+ if (target && (Object.prototype.hasOwnProperty.call(target, 'category') && (target.category == null))) {
59
+ throw new HttpForbiddenError(
60
+ context.__(
61
+ 'The accommodation request creation failed because no request category has been set.'
62
+ )
63
+ );
64
+ }
65
+
66
+ }
67
+
68
+ //UPDATE
69
+ //Load current status
70
+ const actionStatus = await event.model.where('id').equal(event.target.id).select('actionStatus/alternateName').value();
71
+
72
+ //Action is not submitted yet. Run again all rules
73
+ if (actionStatus === 'PotentialActionStatus') {
74
+ runAllRules = true;
75
+ }
76
+ else if (actionStatus === 'ActiveActionStatus' || actionStatus === 'CompletedActionStatus'){
77
+ //accommodation request already submitted. Do not allow to update any property
78
+ removeAllInformationExceptStatus(event.target);
79
+ }
80
+ else if (actionStatus === 'CancelledActionStatus' && event.target.actionStatus && event.target.actionStatus.alternateName !== actionStatus && event.target.actionStatus.alternateName != 'ActiveActionStatus') {
81
+ //accommodation request already cancelled.
82
+ throw new DataError('E_VALUE', 'Cannot update cancelled action.', null, 'AccommodationRequestAction', 'actionStatus');
83
+ }
84
+ // allow agent to change request's status when target request status = 'ActiveActionStatus' example: to reconsider a cancelled request
85
+ else if (actionStatus === 'CancelledActionStatus' && event.target.actionStatus && event.target.actionStatus.alternateName !== actionStatus && event.target.actionStatus.alternateName === 'ActiveActionStatus') {
86
+ let model = event.target;
87
+ // set loggedIn user as request's agent
88
+ if (event.previous.agent != user.id) {
89
+ model.agent = user.id;
90
+ }
91
+ }
92
+ }
93
+
94
+ else if (event.state === 1) {
95
+ // check studentConsent
96
+ if (isMe === true && eventHasConsent === true) {
97
+ if (Object.prototype.hasOwnProperty.call(target, 'studentConsent') === false) {
98
+ const translateService = context.application.getStrategy(function TranslateService() { });
99
+ throw new DataError('E_STUDENT_CONSENT', translateService.translate('The application cannot be saved without consent about personal data.'), null, 'AccommodationRequestAction', 'studentConsent');
100
+ } else if (eventHasConsent === true && target.studentConsent === false) {
101
+ const translateService = context.application.getStrategy(function TranslateService() { });
102
+ throw new DataError('E_STUDENT_CONSENT', translateService.translate('The application cannot be saved without consent about personal data.'), null, 'AccommodationRequestAction', 'studentConsent');
103
+ }
104
+ }
105
+
106
+ if (target && (Object.prototype.hasOwnProperty.call(target, 'category') === false || (target.category == null))) {
107
+ throw new HttpForbiddenError(
108
+ context.__(
109
+ 'The accommodation request creation failed because no request category has been set.'
110
+ )
111
+ );
112
+ }
113
+
114
+ runAllRules = true;
115
+ }
116
+
117
+
118
+
119
+ if (runAllRules){
120
+ // set nullable fields to null when they don't need to have a value
121
+ let model = event.target;
122
+
123
+ if (Object.prototype.hasOwnProperty.call(model, 'militaryActive')) {
124
+ if (model.militaryActive == true) {
125
+ applyRulesForPrerequisites(model);
126
+ return;
127
+ }
128
+ }
129
+
130
+ if (Object.prototype.hasOwnProperty.call(model, 'militarySchool')) {
131
+ if (model.militarySchool == true) {
132
+ applyRulesForPrerequisites(model);
133
+ return;
134
+ }
135
+ }
136
+
137
+ if (Object.prototype.hasOwnProperty.call(model, 'studentHouseResident')) {
138
+ if (model.studentHouseResident == true) {
139
+ applyRulesForPrerequisites(model);
140
+ return;
141
+ }
142
+ }
143
+
144
+ if (Object.prototype.hasOwnProperty.call(model, 'sameDegreeHolder')) {
145
+ if (model.sameDegreeHolder == true) {
146
+ applyRulesForPrerequisites(model);
147
+ return;
148
+ }
149
+ }
150
+
151
+ if (Object.prototype.hasOwnProperty.call(model, 'foreignScholarStudent')) {
152
+ if (model.foreignScholarStudent == true) {
153
+ applyRulesForPrerequisites(model);
154
+ return;
155
+ }
156
+ }
157
+
158
+
159
+ if (Object.prototype.hasOwnProperty.call(model, 'foreignStudentOrChildOfGreekExpatriate')) {
160
+ if (model.foreignStudentOrChildOfGreekExpatriate == true) {
161
+ applyRulesForCyprusStudentOrForeignStudentOrChildOfGreekExpatriate(model);
162
+ return;
163
+ }
164
+ }
165
+
166
+ if (Object.prototype.hasOwnProperty.call(model, 'studentReceivesUnemploymentBenefit')) {
167
+ if (model.studentReceivesUnemploymentBenefit == true){
168
+ model.institutionOrClubLocationSameAsStudentPermanentResidence = null;
169
+ model.studentSubmitsTaxReportInGreece = null;
170
+ nullifyFamilyInformation(model);
171
+ nullifyGuardianFinancialInformation(model);
172
+ nullifySpouseFinancialInformation(model);
173
+ nullifyStudentFinancialInformation(model);
174
+ return;
175
+ }
176
+ }
177
+
178
+ if (Object.prototype.hasOwnProperty.call(model, 'maritalStatus')) {
179
+ if (model.maritalStatus && model.maritalStatus.alternateName == 'single') {
180
+ model.noOfChildren = null;
181
+ nullifySpouseFinancialInformation(model);
182
+ if (model.guardianType.alternateName == 'nobody') {
183
+ model.institutionOrClubLocationSameAsGuardianPermanentResidence = null;
184
+ model.guardianNoOfChildren = null;
185
+ model.numberOfStudyingSiblings = null;
186
+ nullifyGuardianFinancialInformation(model);
187
+ }
188
+ }
189
+ else {
190
+ model.guardianType = null;
191
+ model.institutionOrClubLocationSameAsGuardianPermanentResidence = null;
192
+ model.guardianNoOfChildren = null;
193
+ model.numberOfStudyingSiblings = null;
194
+ nullifyGuardianFinancialInformation(model);
195
+ if (model.maritalStatus && model.maritalStatus.alternateName != 'married') {
196
+ nullifySpouseFinancialInformation(model);
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+
203
+ }
204
+
205
+ function applyRulesForPrerequisites(model) {
206
+ //nullify all other properties except foreignScholarStudent, inscriptionModeTag, lessThan25yrs and allInformationSubmittedIsTrueConfirmation
207
+ nullifyPersonalInformation(model);
208
+ nullifyFamilyInformation(model);
209
+ nullifyGuardianFinancialInformation(model);
210
+ nullifySpouseFinancialInformation(model);
211
+ nullifyStudentFinancialInformation(model);
212
+ }
213
+
214
+ function applyRulesForCyprusStudentOrForeignStudentOrChildOfGreekExpatriate(model) {
215
+ model.studentAfm = null;
216
+ }
217
+
218
+ function nullifyPersonalInformation(model){
219
+ model.foreignStudentOrChildOfGreekExpatriate = null;
220
+ model.studentDisabledPerson = null;
221
+ model.studentOrphan = null;
222
+ model.studentReceivesUnemploymentBenefit = null;
223
+ model.studentSubmitsTaxReportInGreece = null;
224
+ model.institutionOrClubLocationSameAsStudentPermanentResidence = null;
225
+ }
226
+
227
+ function nullifyFamilyInformation(model){
228
+ model.noOfChildren = null;
229
+ model.maritalStatus = null;
230
+ model.numberOfStudyingSiblings = null;
231
+ model.guardianNoOfChildren = null;
232
+ model.guardianType = null;
233
+ model.institutionOrClubLocationSameAsGuadianPermanentResidence = null;
234
+ }
235
+
236
+ function nullifyGuardianFinancialInformation(model){
237
+ model.studentGuardianTotalIncome = null;
238
+ model.studentGuardianAfm = null;
239
+ model.studentGuardianReceivesUnemploymentBenefit = null;
240
+ model.studentGuardianSubmitsTaxReportInGreece = null;
241
+ }
242
+
243
+ function nullifySpouseFinancialInformation(model){
244
+ model.studentSpouseTotalIncome = null;
245
+ model.studentSpouseAfm = null;
246
+ model.studentSpouseReceivesUnemploymentBenefit = null;
247
+ }
248
+
249
+ function nullifyStudentFinancialInformation(model){
250
+ model.studentTotalIncome = null;
251
+ model.studentAfm = null;
252
+ }
253
+
254
+ function removeAllInformationExceptStatus(model){
255
+ delete model.militaryActive;
256
+ delete model.militarySchool;
257
+ delete model.studentHouseResident;
258
+ delete model.sameDegreeHolder;
259
+ delete model.foreignScholarStudent;
260
+ delete model.lessThan25yrs;
261
+ delete model.institutionOrClubLocationSameAsStudentPermanentResidence;
262
+ delete model.studentDisabledPerson;
263
+ delete model.studentOrphan;
264
+ delete model.numberOfStudyingSiblings ;
265
+ delete model.maritalStatus;
266
+ delete model.noOfChildren;
267
+ delete model.guardianType;
268
+ delete model.institutionOrClubLocationSameAsGuadianPermanentResidence;
269
+ delete model.guardianNoOfChildren;
270
+ delete model.studentReceivesUnemploymentBenefit;
271
+ delete model.studentSubmitsTaxReportInGreece;
272
+ delete model.studentGuardianSubmitsTaxReportInGreece;
273
+ delete model.studentAfm;
274
+ delete model.studentTotalIncome;
275
+ delete model.studentSpouseReceivesUnemploymentBenefit;
276
+ delete model.studentSpouseAfm;
277
+ delete model.studentSpouseTotalIncome;
278
+ delete model.studentGuardianReceivesUnemploymentBenefit ;
279
+ delete model.foreignStudentOrChildOfGreekExpatriate;
280
+ delete model.studentGuardianAfm;
281
+ delete model.studentGuardianTotalIncome;
282
+ delete model.allInformationSubmittedIsTrueConfirmation;
283
+ }
284
+
285
+ async function afterSaveAsync(event) {
286
+ // on insert
287
+
288
+ }
289
+
290
+ async function beforeRemoveAsync(event) {
291
+
292
+ }
293
+
294
+ async function afterRemoveAsync(event) {
295
+ const context = event.model.context;
296
+ // try to find if a preference exists for that event
297
+
298
+ }
299
+
300
+ /**
301
+ * @param {DataEventArgs} event
302
+ * @param {Function} callback
303
+ */
304
+ export function beforeSave(event, callback) {
305
+ return beforeSaveAsync(event).then(() => {
306
+ return callback();
307
+ }).catch((err) => {
308
+ return callback(err);
309
+ });
310
+ }
311
+
312
+ /**
313
+ * @param {DataEventArgs} event
314
+ * @param {Function} callback
315
+ */
316
+ export function afterSave(event, callback) {
317
+ return afterSaveAsync(event).then(() => {
318
+ return callback();
319
+ }).catch((err) => {
320
+ return callback(err);
321
+ });
322
+ }
323
+
324
+ /**
325
+ * @param {DataEventArgs} event
326
+ * @param {Function} callback
327
+ */
328
+ export function afterRemove(event, callback) {
329
+ return afterRemoveAsync(event).then(() => {
330
+ return callback();
331
+ }).catch((err) => {
332
+ return callback(err);
333
+ });
334
+ }
335
+
336
+
337
+ /**
338
+ * @param {DataEventArgs} event
339
+ * @param {Function} callback
340
+ */
341
+ export function beforeRemove(event, callback) {
342
+ return beforeRemoveAsync(event).then(() => {
343
+ return callback();
344
+ }).catch((err) => {
345
+ return callback(err);
346
+ });
347
+ }
@@ -0,0 +1,214 @@
1
+ import {Args} from '@themost/common';
2
+ import {ElotConverter} from '@universis/elot-converter';
3
+
4
+ /**
5
+ * @name DataContext#locale
6
+ * @description Gets or sets the current context locale
7
+ * @type {string}
8
+ */
9
+ /**
10
+ * Returns true if a data model has a localization enabled
11
+ * @param {DataModel} model
12
+ * @returns {DataField}
13
+ */
14
+ function hasLocales(model) {
15
+ Args.notNull(model, 'model');
16
+ const attribute = model.getAttribute('locales');
17
+ // if target model does not have a `locales` attribute
18
+ if (attribute == null) {
19
+ return null;
20
+ }
21
+ // if attribute belongs to another model (and it's being inherited by this model)
22
+ if (attribute.model !== model.name) {
23
+ return null;
24
+ }
25
+ return attribute;
26
+ }
27
+
28
+ function getElotAttributes(context) {
29
+ // do not require elotAttributes configuration
30
+ // make it completely optional
31
+ return context.getConfiguration().getSourceAt('settings/i18n/elotAttributes') || ['familyName', 'givenName', 'motherName', 'fatherName'];
32
+ }
33
+
34
+ function getElotLocale() {
35
+ return {
36
+ requiresDefault: 'el',
37
+ isInLocale: 'en'
38
+ }
39
+ }
40
+ /**
41
+ * @param {DataEventArgs} event
42
+ * @returns {Promise<void>}
43
+ */
44
+ async function afterSaveAsync(event) {
45
+ const context = event.model.context;
46
+ const attribute = hasLocales(event.model);
47
+ if (attribute == null) {
48
+ // do nothing
49
+ return;
50
+ }
51
+ if (Object.prototype.hasOwnProperty.call(event.target, 'locales') === false) {
52
+ // do nothing and exit because locales property is missing
53
+ return;
54
+ }
55
+ // get localized model
56
+ const childModel = context.model(attribute.type);
57
+ // throw error for invalid model
58
+ if (childModel == null) {
59
+ throw new Error('Invalid configuration. Localization model cannot be found or is not inaccessible yet');
60
+ }
61
+ if (Array.isArray(event.target.locales) === false) {
62
+ throw new Error('Invalid property type. Property locales must be an array');
63
+ }
64
+ for (let i = 0; i < event.target.locales.length; i++) {
65
+ const itemLocale = event.target.locales[i];
66
+ if (childModel.sealed === false) {
67
+ // delete primary key if any
68
+ delete itemLocale[childModel.primaryKey];
69
+ }
70
+ if (itemLocale.object == null) {
71
+ // set object key
72
+ Object.defineProperty(itemLocale, 'object', {
73
+ configurable: true,
74
+ enumerable: true,
75
+ writable: true,
76
+ value: event.target[event.model.primaryKey]
77
+ });
78
+ }
79
+ }
80
+ if (childModel.sealed === true) {
81
+ // if child model (locale model) is sealed add only other language (not default)
82
+ // get default application locale
83
+ let defaultLocale = context.getConfiguration().getSourceAt('settings/i18n/defaultLocale');
84
+ // throw error if default locale is not defined
85
+ if (defaultLocale == null) {
86
+ throw new Error('Invalid application configuration. The default locale is missing.');
87
+ }
88
+ event.target.locales = event.target.locales.filter((item) => {
89
+ return item.inLanguage !== defaultLocale;
90
+ });
91
+ }
92
+ // important note: get silent mode of parent model
93
+ const silentMode = event.model.isSilent();
94
+ // apply silent mode and save items
95
+ if (event.target.locales.length > 0) {
96
+ await childModel.silent(silentMode).save(event.target.locales);
97
+ }
98
+ }
99
+ /**
100
+ * @param {DataEventArgs} event
101
+ * @returns {Promise<void>}
102
+ */
103
+ async function beforeSaveAsync(event) {
104
+ const context = event.model.context;
105
+ const attribute = hasLocales(event.model);
106
+ if (attribute == null) {
107
+ // do nothing
108
+ return;
109
+ }
110
+ // get localized model
111
+ const childModel = context.model(attribute.type);
112
+ // ensure locales property
113
+ event.target.locales = event.target.locales || [];
114
+ // get default application locale
115
+ let defaultLocale = context.getConfiguration().getSourceAt('settings/i18n/defaultLocale');
116
+ // throw error if default locale is not defined
117
+ if (defaultLocale == null) {
118
+ throw new Error('Invalid application configuration. The default locale is missing.');
119
+ }
120
+ let findLocale = event.target.locales.find((item) => {
121
+ return item.inLanguage === defaultLocale;
122
+ });
123
+ let add = false;
124
+ if (findLocale == null) {
125
+ add = true;
126
+ findLocale = {};
127
+ }
128
+ // enumerate properties and update object
129
+ childModel.attributes.forEach((attribute) => {
130
+ if (!attribute.primary && attribute.type !== event.model.name) {
131
+ if (Object.prototype.hasOwnProperty.call(event.target, attribute.name)) {
132
+ Object.defineProperty(findLocale, attribute.name, {
133
+ configurable: true,
134
+ enumerable: true,
135
+ writable: true,
136
+ value: event.target[attribute.name]
137
+ });
138
+ }
139
+ }
140
+ });
141
+ // set language
142
+ findLocale.inLanguage = defaultLocale;
143
+ if (add) {
144
+ event.target.locales.push(findLocale);
145
+ }
146
+ if (defaultLocale === getElotLocale().requiresDefault) {
147
+ // try to find locale that is in language of elot locale
148
+ let elotLocale = event.target.locales.find(locale => locale.inLanguage === getElotLocale().isInLocale);
149
+ let addElotLocale = false;
150
+ if (!elotLocale) {
151
+ elotLocale = {};
152
+ addElotLocale = true;
153
+ }
154
+ // get elot attributes
155
+ const elotAttributes = getElotAttributes(context);
156
+ // if they are configured, validate them
157
+ if (!(Array.isArray(elotAttributes) && elotAttributes.length)) {
158
+ throw new Error('Invalid configuration. Elot attributes must be a (not-empty) array, e.g. ["familyName", "givenName"]');
159
+ }
160
+ // get previous state
161
+ // it can be undefined for various reasons (e.g. insert state, missing or misplaced previous-state-listener)
162
+ const previous = event.previous;
163
+ // enumerate elot attributes
164
+ for (const attribute of elotAttributes) {
165
+ // if target has the property
166
+ if (Object.prototype.hasOwnProperty.call(event.target, attribute)) {
167
+ // it's not null and has been changed
168
+ if (event.target[attribute] != null && event.target[attribute] != (previous && previous[attribute])) {
169
+ // convert and assign to elot locale
170
+ elotLocale[attribute] = ElotConverter.convert(event.target[attribute]);
171
+ }
172
+ }
173
+ }
174
+ if (addElotLocale) {
175
+ elotLocale.inLanguage = getElotLocale().isInLocale;
176
+ // and finally push the contructed locale
177
+ event.target.locales.push(elotLocale);
178
+ }
179
+ }
180
+ }
181
+ // noinspection JSUnusedGlobalSymbols
182
+ /**
183
+ *
184
+ * @param {DataEventArgs} event
185
+ * @param {*} callback
186
+ * @returns {any}
187
+ */
188
+ function beforeSave(event, callback) {
189
+ return beforeSaveAsync(event).then(() => {
190
+ return callback();
191
+ }).catch((err) => {
192
+ return callback(err);
193
+ });
194
+ }
195
+ // noinspection JSUnusedGlobalSymbols
196
+ /**
197
+ *
198
+ * @param {DataEventArgs} event
199
+ * @param {*} callback
200
+ * @returns {any}
201
+ */
202
+ function afterSave(event, callback) {
203
+ return afterSaveAsync(event).then(() => {
204
+ return callback();
205
+ }).catch((err) => {
206
+ return callback(err);
207
+ });
208
+ }
209
+
210
+ export {
211
+ hasLocales,
212
+ beforeSave,
213
+ afterSave
214
+ }
@@ -0,0 +1,89 @@
1
+ import {TextUtils, TraceUtils} from '@themost/common';
2
+ import {DataCacheStrategy} from "@themost/data";
3
+
4
+ /**
5
+ * @param {DataEventArgs} event
6
+ * @param {Function} callback
7
+ */
8
+ export function beforeExecute(event, callback) {
9
+ try {
10
+ const context = event.model.context;
11
+ if (event.query) {
12
+ if (event.query.$select) {
13
+ //create hash
14
+ const hash = event.emitter? event.emitter.toMD5() : TextUtils.toMD5(event.query);
15
+ const key = '/' + event.model.name + '/?query=' + hash + '&lang=' + event.model.context.locale;
16
+ TraceUtils.debug('Getting data from cache [' + key + ']');
17
+ return context.getConfiguration().getStrategy(DataCacheStrategy).get(key).then((result) => {
18
+ if (typeof result !== 'undefined') {
19
+ //delete expandables
20
+ if (event.emitter) {
21
+ delete event.emitter.$expand;
22
+ }
23
+ event.cached = true;
24
+ event.result = result;
25
+ return callback();
26
+ }
27
+ else {
28
+ return callback();
29
+ }
30
+ }).catch((err)=> {
31
+ return callback(err);
32
+ });
33
+ }
34
+ }
35
+ callback();
36
+ }
37
+ catch (e) {
38
+ callback(e)
39
+ }
40
+ }
41
+
42
+ export function afterExecute(event, callback) {
43
+ try {
44
+ const context = event.model.context;
45
+ if (event.query) {
46
+ if (event.query.$select) {
47
+ if (typeof event.result !== 'undefined' && !event.cached) {
48
+ //create hash
49
+ const hash = event.emitter? event.emitter.toMD5() : TextUtils.toMD5(event.query);
50
+ const key = '/' + event.model.name + '/?query=' + hash + '&lang=' + event.model.context.locale;
51
+ TraceUtils.debug('Setting data to cache [' + key + ']');
52
+ context.getConfiguration().getStrategy(DataCacheStrategy).add(key, event.result);
53
+ return callback();
54
+ }
55
+ }
56
+ }
57
+ callback();
58
+ }
59
+ catch (e) {
60
+ callback(e)
61
+ }
62
+ }
63
+
64
+ function cleanupCache(event, callback) {
65
+ const { name, caching, context } = event.model;
66
+ if (caching === 'none') {
67
+ return callback();
68
+ }
69
+ const service = context.getConfiguration().getStrategy(DataCacheStrategy);
70
+ if (service) {
71
+ return service.remove(`/${name}/*`).then(() => {
72
+ return callback();
73
+ }).catch((err) => {
74
+ TraceUtils.warn('An error occurred while cleaning up cache items');
75
+ TraceUtils.warn(err);
76
+ return callback();
77
+ });
78
+ }
79
+ return callback();
80
+ }
81
+
82
+
83
+ export function afterSave(event, callback) {
84
+ return cleanupCache(event, callback);
85
+ }
86
+
87
+ export function afterRemove(event, callback) {
88
+ return cleanupCache(event, callback);
89
+ }
@@ -0,0 +1,52 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import { DataError } from "@themost/common";
3
+ import { DataObject, EdmMapping } from "@themost/data";
4
+
5
+
6
+ @EdmMapping.entityType('Accommodation')
7
+ class AccommodationRequestEvent extends DataObject {
8
+ /**
9
+ * @constructor
10
+ */
11
+ constructor() {
12
+ super();
13
+ }
14
+
15
+ /**
16
+ * POST AccommodationRequestEvents/:id/Remove
17
+ */
18
+
19
+ //remove accommodation request event
20
+ @EdmMapping.param('data', 'Object', false, true)
21
+ @EdmMapping.action('remove', 'Accommodation')
22
+ async remove(data) {
23
+ /**
24
+ * @type {import('@themost/data').DataModel}
25
+ */
26
+
27
+ const model = this.getModel();
28
+
29
+ const item = await model.where('id')
30
+ .equal(this.getId())
31
+ .getItem();
32
+ const translateService = this.context.application.getStrategy(function TranslateService() { });
33
+
34
+ if (item == null) {
35
+ throw new DataError('E_ACCOMMODATION', translateService.translate('The specified Accommodation cannot be found or is inaccessible.'));
36
+ }
37
+
38
+ const hasRooms = await this.context.model('AccommodationRooms')
39
+ .where('containedInPlace').equal(item.id).count();
40
+
41
+ if (hasRooms) {
42
+ throw new DataError('E_ACCOMMODATION_ROOM', translateService.translate('Cannot remove accommodation. Accommodation is related to rooms.'), null, 'Accommodation');
43
+ }
44
+
45
+ return model.silent().remove(item);
46
+ }
47
+
48
+
49
+
50
+ }
51
+
52
+ module.exports = AccommodationRequestEvent;