278 lines
14 KiB
JavaScript
278 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
const moment = require('moment-timezone');
|
|
const db = require('../../models/index');
|
|
const Op = require('sequelize').Op;
|
|
|
|
const {
|
|
UI_TIMEZONE,
|
|
BOOKING_CHANGE_PERCENTAGE_CHARGE,
|
|
CHARGE_BOOKING_CHANGE_UNDER_TIME,
|
|
ALLOWED_BOOKING_CANCELLATION_TIME,
|
|
incidentType
|
|
} = require('../../constants/constants');
|
|
|
|
const { getBookingChangeLogsForReservationId } = require('./bookingChangeLog');
|
|
|
|
const getShorteningIncidentsForReservationId = (reservationId) => {
|
|
const filter = {
|
|
reservationId,
|
|
incidentType: incidentType.BOOKING_SHORTENED,
|
|
deleted: false,
|
|
};
|
|
|
|
return db.bookingChangeIncident.findAll({where: filter});
|
|
};
|
|
|
|
const getReservationIncidentsAndLogs = (reservationId) => {
|
|
return new Promise((resolve, reject) => {
|
|
const asyncDataFetch = [getShorteningIncidentsForReservationId(reservationId), getBookingChangeLogsForReservationId(reservationId)];
|
|
|
|
Promise.all(asyncDataFetch)
|
|
.then((reservationData) => {
|
|
resolve({
|
|
incidents: reservationData[0],
|
|
changeLogs: reservationData[1],
|
|
});
|
|
})
|
|
.catch((error) => reject(error));
|
|
});
|
|
};
|
|
|
|
const bulkWriteBookingChangeIncidents = (incidents) => {
|
|
//TODO: Check if this complete method can be replaced with
|
|
// return db.bookingChangeIncident.bulkCreate(incidents)
|
|
return new Promise((resolve, reject) => {
|
|
const asyncJobs = [];
|
|
|
|
incidents.forEach((incident) => {
|
|
asyncJobs.push(db.bookingChangeIncident.findOrCreate({where: incident, defaults: incident}));
|
|
});
|
|
|
|
Promise.all(asyncJobs)
|
|
.then(() => resolve())
|
|
.catch((error) => reject(error));
|
|
});
|
|
};
|
|
|
|
const getIncidentsFromChanges = (changes) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (Array.isArray(changes)){
|
|
const incidents = [];
|
|
const reservationsForAdditionalCheck = [];
|
|
|
|
changes.forEach((change) => {
|
|
const { oldReservation, newReservation } = change;
|
|
if (oldReservation && newReservation){
|
|
const oldResourceId = oldReservation.resourceId;
|
|
const oldStart = oldReservation.start ? moment.utc(oldReservation.start) : null;
|
|
const oldEnd = oldReservation.end ? moment.utc(oldReservation.end) : null;
|
|
|
|
const newStart = newReservation.start ? moment.utc(newReservation.start) : null;
|
|
const newEnd = newReservation.end ? moment.utc(newReservation.end) : null;
|
|
const reservationCreationTimestamp = newReservation.createdAt ? moment.utc(newReservation.createdAt) : null;
|
|
|
|
const reservationTimezone = newReservation.timezone ? newReservation.timezone : UI_TIMEZONE;
|
|
const reservationHourlyRate = oldReservation.hourlyRate ? oldReservation.hourlyRate : newReservation.hourlyRate;
|
|
const canceled = newReservation.canceled;
|
|
|
|
if (oldStart && oldEnd && newStart && newEnd && reservationHourlyRate){
|
|
const oldReservationLength = oldEnd.diff(oldStart, 'hours', true);
|
|
const newReservationLength = newEnd.diff(newStart, 'hours', true);
|
|
|
|
const differenceFromNow = oldStart.diff(moment.utc(), 'minutes');
|
|
|
|
// console.log('Change detected : ');
|
|
// console.log('\tOld reservation :');
|
|
// console.log('\t\tResource : ', oldResourceId);
|
|
// console.log('\t\tStart : ', oldStart.format());
|
|
// console.log('\t\tEnd : ', oldEnd.format());
|
|
// console.log('\t\tLength : ', oldReservationLength);
|
|
// console.log('\tNew Reservation :');
|
|
// console.log('\t\tResource : ', newReservation.resourceId);
|
|
// console.log('\t\tStart : ', newStart.format());
|
|
// console.log('\t\tEnd : ', newEnd.format());
|
|
// console.log('\t\tLength : ', newReservationLength);
|
|
// console.log('\t\tCanceled : ', canceled);
|
|
// console.log('\t---------------------------------');
|
|
// console.log('\tDifference : ', differenceFromNow, 'minutes');
|
|
// console.log('');
|
|
// console.log('\tIs booking changed too late ? ', differenceFromNow, ' < ', CHARGE_BOOKING_CHANGE_UNDER_TIME);
|
|
if (differenceFromNow < CHARGE_BOOKING_CHANGE_UNDER_TIME){
|
|
const { reservationId, memberId, resourceId } = newReservation;
|
|
|
|
// console.log('\t\tYes');
|
|
|
|
// console.log('\tIs booking canceled ?');
|
|
if (!canceled) {
|
|
// console.log('\t\tNo');
|
|
// Check if new reservation is on same day
|
|
const sameDay = oldStart.tz(reservationTimezone).isSame(newStart.tz(reservationTimezone), 'day');
|
|
// console.log('\tIs new reservation on the same day ?');
|
|
if (sameDay) {
|
|
// console.log('\t\tYes');
|
|
// Reservation moved in same day
|
|
// Check if member shortened the reservation
|
|
|
|
// console.log('\tIs reservation shortened ? ', newReservationLength, ' < ', oldReservationLength);
|
|
if (newReservationLength < oldReservationLength) {
|
|
// console.log('\t\tYes');
|
|
const differenceInLength = oldReservationLength - newReservationLength;
|
|
const chargeFee = differenceInLength * reservationHourlyRate * BOOKING_CHANGE_PERCENTAGE_CHARGE / 100;
|
|
|
|
// console.log('\t\t\tThis is [shortened] incident ! Charge fee : ', chargeFee);
|
|
const incident = {
|
|
reservationId,
|
|
memberId,
|
|
oldResourceId: oldResourceId || resourceId,
|
|
newResourceId: resourceId,
|
|
oldBookingStart: oldReservation.start,
|
|
oldBookingEnd: oldReservation.end,
|
|
newBookingStart: newReservation.start,
|
|
newBookingEnd: newReservation.end,
|
|
incidentType: incidentType.BOOKING_SHORTENED,
|
|
chargeFee,
|
|
};
|
|
|
|
incidents.push(incident);
|
|
}else{
|
|
reservationsForAdditionalCheck.push(reservationId);
|
|
}
|
|
} else {
|
|
console.log('\t\tNo');
|
|
// Reservation moved to another day
|
|
// Add cancellation charge
|
|
const chargeFee = oldReservationLength * reservationHourlyRate * BOOKING_CHANGE_PERCENTAGE_CHARGE / 100;
|
|
// console.log('\t\t\tThis is incident ! Charge fee : ', chargeFee);
|
|
const incident = {
|
|
reservationId,
|
|
memberId,
|
|
oldResourceId: oldResourceId || resourceId,
|
|
newResourceId: resourceId,
|
|
oldBookingStart: oldReservation.start,
|
|
oldBookingEnd: oldReservation.end,
|
|
newBookingStart: newReservation.start,
|
|
newBookingEnd: newReservation.end,
|
|
incidentType: incidentType.BOOKING_MOVED_TO_ANOTHER_DAY,
|
|
chargeFee,
|
|
};
|
|
|
|
incidents.push(incident);
|
|
}
|
|
}else{
|
|
// console.log('\t\tYes');
|
|
const differenceFromCreation = moment.utc().diff(reservationCreationTimestamp, 'minutes');
|
|
|
|
// console.log('\tIs booking created in past ', ALLOWED_BOOKING_CANCELLATION_TIME, ' minutes ?', differenceFromCreation, ' > ', ALLOWED_BOOKING_CANCELLATION_TIME);
|
|
if (differenceFromCreation > ALLOWED_BOOKING_CANCELLATION_TIME){
|
|
// console.log('\t\tYes');
|
|
const chargeFee = reservationHourlyRate * oldReservationLength * BOOKING_CHANGE_PERCENTAGE_CHARGE / 100;
|
|
|
|
// console.log('\t\t\tThis is [cancellation] incident ! charge fee : ', chargeFee);
|
|
const incident = {
|
|
reservationId,
|
|
memberId,
|
|
oldResourceId: oldResourceId || resourceId,
|
|
newResourceId: null,
|
|
oldBookingStart: oldReservation.start,
|
|
oldBookingEnd: oldReservation.end,
|
|
newBookingStart: null,
|
|
newBookingEnd: null,
|
|
incidentType: incidentType.BOOKING_CANCELED_LATE,
|
|
chargeFee,
|
|
};
|
|
|
|
incidents.push(incident);
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
// console.log('\t\tNo');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
resolve({incidents, reservationsForAdditionalCheck});
|
|
}else{
|
|
reject('Input argument is not an array !');
|
|
}
|
|
});
|
|
};
|
|
|
|
const getReservationsIncidentsForRemoval = (reservations) => {
|
|
return new Promise((resolve, reject) => {
|
|
const asyncChecks = [];
|
|
reservations.forEach((reservationId) => {
|
|
asyncChecks.push(getReservationIncidentsAndLogs(reservationId));
|
|
});
|
|
|
|
Promise.all(asyncChecks)
|
|
.then((possibleRemovals) => {
|
|
const incidentsToRemove = [];
|
|
possibleRemovals.forEach((possibleRemoval) => {
|
|
const { incidents, changeLogs } = possibleRemoval;
|
|
|
|
if (incidents.length > 0){
|
|
const initialReservation = changeLogs && changeLogs[0] ? changeLogs[0] : undefined;
|
|
const reservationLastChange = changeLogs && changeLogs[changeLogs.length-1] ?
|
|
changeLogs[changeLogs.length-1] : undefined;
|
|
|
|
if (initialReservation && reservationLastChange){
|
|
const initialNewStartMoment = moment.utc(initialReservation.newStart);
|
|
const initialNewEndMoment = moment.utc(initialReservation.newEnd);
|
|
const lastNewStartMoment = moment.utc(reservationLastChange.newStart);
|
|
const lastNewEndMoment = moment.utc(reservationLastChange.newEnd);
|
|
|
|
if (initialNewStartMoment && initialNewEndMoment &&
|
|
lastNewStartMoment && lastNewEndMoment){
|
|
|
|
const initialDuration = initialNewEndMoment.diff(initialNewStartMoment, 'hours', true);
|
|
const lastDuration = lastNewEndMoment.diff(lastNewStartMoment, 'hours', true);
|
|
|
|
if (lastDuration > initialDuration){
|
|
incidentsToRemove.push(...incidents);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
resolve(incidentsToRemove);
|
|
})
|
|
.catch((error) => reject(error));
|
|
});
|
|
};
|
|
|
|
const getChargedCanceledReservations = (reservationIds) => {
|
|
const filters = {
|
|
reservationId: {
|
|
[Op.in]: reservationIds,
|
|
},
|
|
incidentType: incidentType.BOOKING_CANCELED_LATE,
|
|
deleted: false,
|
|
};
|
|
|
|
const attributes = ['memberId', 'oldBookingStart', 'oldBookingEnd', 'chargeFee'];
|
|
|
|
return db.bookingChangeIncident.findAll({attributes, where: filters});
|
|
};
|
|
|
|
const deleteBookingChangeIncidents = (incidents) => {
|
|
const asyncActions = [];
|
|
incidents.forEach((incident) => {
|
|
incident.set('deleted', true);
|
|
asyncActions.push(incident.save());
|
|
});
|
|
|
|
return Promise.all(asyncActions);
|
|
};
|
|
|
|
module.exports = {
|
|
getChargedCanceledReservations,
|
|
getIncidentsFromChanges,
|
|
bulkWriteBookingChangeIncidents,
|
|
getShorteningIncidentsForReservationId,
|
|
getReservationsIncidentsForRemoval,
|
|
deleteBookingChangeIncidents,
|
|
};
|