'use strict'; const db = require('../../models/index'); const moment = require('moment-timezone'); const Op = require('sequelize').Op; const { API } = require('../../helpers/api'); const { officeRnDAPIErrors, MAX_BACK_TO_BACK_DIFFERENCE } = require('../../constants/constants'); const fetchAllBookings = () => { return new Promise((resolve, reject) => { API.get('/bookings') .then((result) => { const cleanedBookingReservations = []; const bookingData = result && result.data ? result.data : []; bookingData.forEach((fullBookingEntry) => { const fees = fullBookingEntry && fullBookingEntry.fees ? fullBookingEntry.fees : []; const firstFee = fees.length > 0 && fees[0].fee ? fees[0].fee : undefined; const hourlyRate = firstFee && firstFee.price ? firstFee.price : 0; cleanedBookingReservations.push({ reservationId: fullBookingEntry['_id'], memberId: fullBookingEntry.member, officeId: fullBookingEntry.office, resourceId: fullBookingEntry.resourceId, start: fullBookingEntry.start.dateTime, end: fullBookingEntry.end.dateTime, timezone: fullBookingEntry.timezone, canceled: fullBookingEntry.canceled || false, hourlyRate, }); }); resolve(cleanedBookingReservations); }) .catch((error) => { console.log(officeRnDAPIErrors.FAILED_TO_FETCH_BOOKINGS); console.log('Details : ', error); reject(officeRnDAPIErrors.FAILED_TO_FETCH_BOOKINGS); }); }); }; const getAllFinishedBookings = () => { const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const filters = { canceled: false, end: { [Op.lt]: moment.utc().toISOString() } }; return db.bookingReservation.findAll({ attributes, where: filters, order: [ ['start', 'ASC'], ] }) }; const getFirstNextBooking = (reservation) => { return new Promise ((resolve, reject) => { const { resourceId, start } = reservation; const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const filters = { canceled: false, start: { [Op.gt]: start }, resourceId, }; const order = [['start', 'ASC']]; db.bookingReservation.findAll({ attributes, where: filters, order, }) .then((reservations) => { if (reservations && reservations[0]){ resolve(reservations[0]); }else{ resolve(undefined); } }) .catch((error) => reject(error)); }); }; const getFirstPreviousBooking = (reservation) => { return new Promise ((resolve, reject) => { const { resourceId, start } = reservation; const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const filters = { canceled: false, end: { [Op.lte]: start }, resourceId, }; const order = [['end', 'DESC']]; db.bookingReservation.findAll({ attributes, where: filters, order, }) .then((reservations) => { if (reservations && reservations[0]){ resolve(reservations[0]); }else{ resolve(undefined); } }) .catch((error) => reject(error)); }); }; const getFirstReservationInBlock = (reservation) => { return new Promise ((resolve, reject) => { const { resourceId, memberId, start } = reservation; const fromTimestamp = moment.utc(start).subtract(MAX_BACK_TO_BACK_DIFFERENCE).toISOString(); const toTimestamp = start; const filters = { resourceId, memberId, end: { [Op.and]: [ {[Op.gte]: fromTimestamp}, {[Op.lte]: toTimestamp} ] } }; db.bookingReservation.findOne({where: filters}) .then((previousReservation) => { if (!previousReservation) { resolve(reservation); } else { resolve(getFirstReservationInBlock(previousReservation)); } }) .catch((error) => reject(error)); }); }; const writeBookingReservation = (bookingReservation) => { const { reservationId, memberId, officeId, resourceId, start, end, timezone, canceled, hourlyRate } = bookingReservation; const bookingReservationForDB = { reservationId, memberId, officeId, resourceId, start, end, timezone, canceled, hourlyRate, }; return db.bookingReservation.findOrCreate({where: {...bookingReservationForDB}, defaults: {...bookingReservationForDB}}); }; const bulkWriteReservationsWithChangesTracking = (reservations) => { return new Promise ((resolve, reject) => { const changes = []; const asyncJobs = []; db.bookingReservation.addHook('beforeUpdate', 'updateHook', (instance) => { const changedKeys = instance.changed(); const previous = instance.previous(); const lookupKeys = ['start', 'end', 'resourceId', 'canceled']; let realChange = false; lookupKeys.forEach((key) => { if ((changedKeys.indexOf(key) !== -1) && (JSON.stringify(previous[key]) !== JSON.stringify(instance[key]))){ realChange = true; } }); instance.setDataValue('hourlyRate', instance.previous('hourlyRate')); if (realChange){ changes.push({ oldReservation: previous, newReservation: instance.get(), }); } }); reservations.forEach((reservation) => { const asyncReservationUpdate = () => { return new Promise((resolve, reject) => { db.bookingReservation.update(reservation, { where: { reservationId: reservation.reservationId, }, returning: true, individualHooks: true, }) .then(([updateCount, updatedInstances]) => { if (updateCount === 0){ const oldReservation = { start: null, end: null, resourceId: null, }; changes.push({ oldReservation, newReservation: reservation, }); resolve(db.bookingReservation.upsert(reservation)); }else{ resolve(); } }) .catch((error) => { console.log('Error updating'); console.log(error); reject(error); }) }); }; asyncJobs.push(asyncReservationUpdate()); }); Promise.all(asyncJobs) .then(() => { db.bookingReservation.removeHook('updateHook'); resolve(changes); }) .catch((error) => reject(error)); }); }; module.exports = { fetchAllBookings, writeBookingReservation, getAllFinishedBookings, getFirstNextBooking, getFirstPreviousBooking, getFirstReservationInBlock, bulkWriteReservationsWithChangesTracking, };