@universis/dining
Version:
Universis api for dining
276 lines (242 loc) • 13.2 kB
JavaScript
import { DataError } from "@themost/common";
import { DataObject, EdmMapping } from "@themost/data";
.entityType('DiningRequestEvent')
class DiningRequestEvent extends DataObject {
/**
* @constructor
*/
constructor() {
super();
}
/**
* POST DiningRequestEvents/:id/Remove
*/
//remove dining request event
.param('data', 'Object', false, true)
.action('remove', 'DiningRequestEvent')
async remove(data) {
/**
* @type {import('@themost/data').DataModel}
*/
const model = this.getModel();
const item = await model.where('id')
.equal(this.getId())
.getItem();
const translateService = this.context.application.getStrategy(function TranslateService() { });
if (item == null) {
throw new DataError('E_DINING_REQUEST_EVENT', translateService.translate('The specified DiningRequestEvent cannot be found or is inaccessible.'));
}
const hasActions = await this.context.model('DiningRequestAction')
.where('diningRequestEvent').equal(item.id).count();
if (hasActions) {
throw new DataError('E_DINING_REQUESTS_EXISTS', translateService.translate('Cannot remove dining request event. Dining request event is related to requests.'), null, 'DiningRequestEvent');
}
return model.silent().remove(item);
}
/**
* POST DiningRequestEvents/:id/Extend
*/
/**
* @returns {DiningRequestEvent}
*/
//extend dining request event
.param("data", "Object", false, true)
.action('extend', 'DiningRequestEvent')
async extend(data) {
const model = this.getModel();
const previous = await model.where('id')
.equal(this.getId())
.getItem();
const target = data.event;
const translateService = this.context.application.getStrategy(function TranslateService() { });
if (previous == null) {
throw new DataError('E_DINING_REQUEST_EVENT', translateService.translate('The specified DiningRequestEvent cannot be found or is inaccessible.'));
}
if (previous.eventStatus && previous.eventStatus.alternateName !== 'EventOpened' && target.eventStatus && target.eventStatus.alternateName !== 'EventOpened') {
throw new DataError('E_EVENT_STATUS', translateService.translate('The DiningRequestEvent can only be extended when it is open.'));
}
if ((target.validThrough && target.validFrom)
&& target.validThrough < target.validFrom) {
throw new DataError('E_DATES', translateService.translate('ValidThrough date cannot be earlier than validFrom date.'));
}
const today = new Date().toISOString();
if (previous.validThrough && previous.validThrough <= today) {
throw new DataError('E_DATES', translateService.translate('The DiningRequestEvent can only be extended if it has not already expired.'));
}
if (previous.eventStatus && previous.eventStatus.alternateName === 'EventOpened') {
const validThroughChange = previous.validThrough && previous.validThrough.toISOString() !== target.validThrough.toISOString();
const endDateChange = previous.endDate && previous.endDate.toISOString() !== target.endDate.toISOString();
if (validThroughChange || endDateChange) {
const actionItem = {
object: previous.id,
currentValidThrough: validThroughChange === true ? previous.validThrough : null,
validThrough: validThroughChange === true ? target.validThrough : null,
currentEndDate: endDateChange === true ? previous.endDate : null,
endDate: endDateChange === true ? target.endDate : null,
status: previous.eventStatus,
description: validThroughChange === true ? (endDateChange === true ? translateService.translate('END_DATE_VALID_THROUGH_DATE') : translateService.translate('VALID_THROUGH_DATE')) : translateService.translate('END_DATE')
};
const action = await this.context.model('UpdateDiningRequestEventAction').save(actionItem);
if (action) {
return await this.context.model('DiningRequestEvent').save(target).then(async res => {
if (res) {
const response = res;
let sseUser;
sseUser = this.context.user || await this.context.model('User');
// check if there are active cards to extend
const cards = await this.context.model('StudentDiningCards')
.where('student/studentStatus/alternateName').equal('active')
.or('student/studentStatus/alternateName').equal('declared')
.prepare()
.and('active').equal(true)
.and('action/diningRequestEvent/id').equal(previous.id)
.and('action/actionStatus/alternateName').equal('CompletedActionStatus')
.count();
if (validThroughChange === true && cards) {
Promise.all([
action,
this.handleStudentDiningCards(res, action)
]).then(([actionResult]) => {
this.sendSse(sseUser, this.context, actionResult, null);
return;
}).catch(err => {
this.sendSse(sseUser, this.context, action, err);
});
} else {
action.totalCards = action.totalCardsAffected = 0;
action.actionResult = translateService.translate('The process was completed successfully.');
action.actionStatus = { alternateName: 'CompletedActionStatus' };
await this.context.model('UpdateDiningRequestEventAction').save(action);
this.sendSse(sseUser, this.context, action, null);
}
return response;
} else {
//set action status fail
action.actionResult = translateService.translate('Error occured while trying to extend DiningRequestEvent.')
action.actionStatus = { alternateName: 'FailedActionStatus' };
await this.context.model('UpdateDiningRequestEventAction').save(action);
return false;
}
})
}
} else {
return false;
}
}
}
sendSse(user, context, updateEventAction, error) {
// try to find ServerSentEventService
const sse = context.getApplication().getService(function ServerSentEventService() {});
// only if it exists - it is not required
if (sse != null) {
if (user != null && updateEventAction != null) {
// create an event
const event = {
entitySet: "UpdateDiningRequestEventActions",
entityType: "UpdateDiningRequestEventAction",
target: {
id: updateEventAction.id,
object: updateEventAction.object.id || updateEventAction.object,
actionResult: updateEventAction.actionResult
},
status: {
success: true,
},
dateCreated: new Date()
}
if (error) {
Object.assign(event, {
status: {
success: false
},
error: {
message: error.code || error.message
}
})
}
// and send it to the user that is performing the approval (dining scope)
sse.emit(user.name, user.authenticationScope || "dining", event);
}
}
}
/**
* @returns Promise<ValidationResult[]>
*/
async handleStudentDiningCards(event, action) {
const translateService = this.context.application.getStrategy(function TranslateService() { });
const model = this.getModel();
const eventNext = await model.where('id')
.equal(event.id)
.getItem();
if (!eventNext) {
action.actionResult = translateService.translate('The specified DiningRequestEvent cannot be found or is inaccessible.')
action.actionStatus = { alternateName: 'FailedActionStatus' };
await this.context.model('UpdateDiningRequestEventAction').save(action);
throw new DataError('E_DINING_REQUEST_EVENT', translateService.translate('The specified DiningRequestEvent cannot be found or is inaccessible.'));
}
/**
* Try to update valid through date of active student dining cards
* Do not throw errors, but catch the procedure errors
*/
let actionItem = action;
let cardResults = [];
const diningCardsToChange = await this.context.model('StudentDiningCards')
.where('student/studentStatus/alternateName').equal('active')
.or('student/studentStatus/alternateName').equal('declared')
.prepare()
.and('active').equal(true)
.and('action/diningRequestEvent/id').equal(eventNext.id)
.and('action/actionStatus/alternateName').equal('CompletedActionStatus')
.expand({
name: 'student',
options: {
$expand: 'studentStatus'
}
})
.getItems();
// extend all active diningCards
// let index = 0; // debugging purposes
for (const item of diningCardsToChange) {
try {
// update only validThrough date
// property student is required
// check if studentStatus allows card to be updated (active and declared actionStatus)
if (item && item.student && item.student.studentStatus && item.student.studentStatus.alternateName
&& (item.student.studentStatus.alternateName === 'active' || item.student.studentStatus.alternateName === 'declared')){
const itemNext = {
id: item.id,
validThrough: eventNext.validThrough,
student: item.student
};
await this.context.model('StudentDiningCards').save(itemNext);
}
} catch (err) {
// if err catch on cardResults
const res = item && item.action && item.action.requestNumber
cardResults.push(res)
}
// index ++; // debugging purposes
}
// return result
try {
actionItem.totalCards = diningCardsToChange.length;
actionItem.totalCardsAffected = diningCardsToChange.length - cardResults.length;
if (cardResults && cardResults.length === 0) {
//set action status completed
actionItem.actionStatus = { alternateName: 'CompletedActionStatus' };
actionItem.actionResult = translateService.translate('The process was completed successfully.');
} else {
//set action status fail
actionItem.actionStatus = { alternateName: 'FailedActionStatus' };
actionItem.actionResult = translateService.translate('Error occured while trying to update student dining cards') + '<br>' + cardResults.join("<br>");
}
const res = await this.context.model('UpdateDiningRequestEventActions').save(actionItem);
} catch (err) {
let sseUser;
sseUser = this.context.user || await this.context.model('User');
this.sendSse(sseUser, this.context, action, null);
throw new DataError('E_UPDATE_ACTION', 'Error occured while trying to update the UpdateDiningRequestEventAction.');
}
}
}
module.exports = DiningRequestEvent;