@universis/dining
Version:
Universis api for dining
194 lines (164 loc) • 8.08 kB
JavaScript
import {DataObject, EdmMapping, EdmType} from '@themost/data';
import {Args, DataError, DataNotFoundError, TraceUtils} from "@themost/common"
import moment from 'moment';
/**
* @class
* @property {number} id
* @property {number} serialNumber
* @property {boolean} active
* @property {string} cancelReason
* @property {string} notes
* @property {DiningRequestAction|any} action
* @property {AcademicYear|any} academicYear
* @property {AcademicPeriod|any} academicPeriod
* @property {Date} dateCreated
* @property {Date} dateModified
* @property {Date} validFrom
* @property {Date} validThrough
* @property {User|any} student
*/
.entityType('StudentDiningCard')
class StudentDiningCard extends DataObject {
/**
* @constructor
*/
constructor() {
super();
}
/**
* POST StudentDiningCards/:id/Cancel
*/
.param('data', 'Object', false, true)
.action('cancel', 'StudentDiningCard')
async cancel(data) {
if (!data.cancelReason) {
throw new DataError('E_CANCEL_REASON', 'Cancel Reason should be provided');
}
await this.context.executeInTransactionAsync(async () => {
// find StudentDiningCard from id
const studentDiningCard = await this.context.model('StudentDiningCard')
.where('id')
.equal(this.getId())
.select('id', 'active', 'action', 'student', 'academicPeriod', 'academicYear')
.flatten()
.getItem();
// validate existance and permission
if (studentDiningCard == null) {
throw new DataNotFoundError('The specified Student Dining Card cannot be found or is inaccessible');
}
// studentDiningCard already cancelled throw error
if (studentDiningCard.active === false) {
throw new DataError('E_ACTIVE', 'The specified Student Dining Card is already cancelled')
}
//validate DiningRequestAction
if (studentDiningCard.action == null) {
throw new DataError('E_ACTION', 'The Student Dining Card is not linked to a Dining Request Action')
}
// cancel studentDiningCard
studentDiningCard.active = false;
studentDiningCard.cancelReason = data.cancelReason;
studentDiningCard.dateCancelled = new Date();
await this.context.model('StudentDiningCard').save(studentDiningCard);
const action = await this.context.model('DiningRequestAction').where('id').equal(studentDiningCard.action).select('id', 'actionStatus/alternateName as requestStatus').getItem();
//validate DiningRequestAction
if (action == null) {
throw new DataError('E_ACTION', 'The specified DiningRequestAction cannot be found or is inaccessible')
}
if (action.requestStatus !== 'CompletedActionStatus') {
throw new DataError('E_ACTION', 'The specified DiningRequestAction is not in Completed state')
}
action.actionStatus = {
alternateName: "ActiveActionStatus"
}
delete action.requestStatus;
await this.context.model('DiningRequestAction').save(action);
});
}
/**
* POST StudentDiningCards/analytics
*/
.param('data', 'Object', false, true)
.action('analytics', EdmType.CollectionOf('StudentDiningCard'))
static async analytics(context, data) {
Args.notEmpty(data, 'Data');
Args.notEmpty(data.validFrom, 'Valid from');
Args.notEmpty(data.validThrough, 'Valid through');
const validFrom = moment(new Date(data.validFrom)).startOf('day').toDate(); // get only date format
const validThrough = moment(new Date(data.validThrough)).endOf('day').toDate(); // get only date format
const model = context.model('StudentDiningCard');
const queryCards = model.where('date(validThrough)').greaterOrEqual(validFrom).and('date(validFrom)').lowerOrEqual(validThrough);
if (data.department) {
const localDepartment = await context.model('LocalDepartment').where('id').equal(data.department).getItem();
if (localDepartment == null) {
throw new DataNotFoundError('Department not found or is inaccessible.', null, 'LocalDepartment');
}
queryCards.and('student/department').equal(data.department);
}
if (data.location) {
Args.notNumber(data.location, 'Location');
const location = await context.model('DiningPlace').where('id').equal(data.location).getItem();
if (location == null) {
throw new DataNotFoundError('Location not found or is inaccessible.', null, 'DiningPlace');
}
queryCards.and('location').equal(data.location);
}
const cards = await queryCards.getAllItems();
const dates = enumerateDaysBetweenDates(validFrom, validThrough);
let previousActive = 0;
for (let i = 0; i < dates.length; i++) {
const checkDate = dates[i].date;
dates[i].active = cards.filter(x => {
const startDate = moment(new Date(x.validFrom)).startOf('day').toDate();
let endDate = x.dateCancelled || x.validThrough;
endDate = moment(new Date(endDate)).startOf('day').toDate();
return moment(endDate) >= moment(checkDate) && moment(startDate) <= moment(checkDate);
}).length;
// Delta is a commonly used term in mathematics and science to represent the difference or change between two values.
dates[i].delta = dates[i].active - previousActive;
previousActive = dates[i].active;
}
return dates;
}
/**
* POST StudentDiningCards/CancelExpired
*/
.param('data', 'Object', false, true)
.action('cancelExpired', EdmType.CollectionOf('Object'))
static async cancelExpired(context, data) {
const translateService = context.application.getStrategy(function TranslateService() { });
const todayDate = moment(new Date()).startOf('day').toDate();
// if there are find active diningCards that have expired
const cardsToCancel = await context.model('StudentDiningCards')
.where('active')
.equal(true)
.and('validThrough')
.lowerThan(todayDate)
.silent()
.getAllItems();
// if there are, set active to false, set system cancelReason and save
if (Array.isArray(cardsToCancel)) {
if (cardsToCancel.length > 0) {
const cancelReason = data.cancelReason || translateService.translate('StudentDiningCardSystemCancelReasonDiningRequestEventExpired');
cardsToCancel.map((el) => {
el.active = false;
el.cancelReason = cancelReason;
el.dateCancelled = new Date();
return el;
});
await context.model('StudentDiningCard').silent().save(cardsToCancel);
}
TraceUtils.log("Dining: Automatically Canceled " + cardsToCancel.length + " StudentDiningCards");
return { "totalCancelled": cardsToCancel.length };
}
}
}
function enumerateDaysBetweenDates (startDate, endDate){
let firstDate = moment(startDate).format('YYYY-MM-DD');
const dates = [];
while(moment(firstDate) <= moment(endDate)){
dates.push({date:firstDate, active:0});
firstDate = moment(firstDate).add(1, 'days').format("YYYY-MM-DD");
}
return dates;
}
module.exports = StudentDiningCard;