diff --git a/constants/constants.js b/constants/constants.js index 6291394..aaeff9f 100644 --- a/constants/constants.js +++ b/constants/constants.js @@ -57,6 +57,8 @@ const csvParserErrors = { const officeRnDAPIErrors = { FAILED_TO_FETCH_MEMBERS: 'Failed to fetch members', FAILED_TO_FETCH_BOOKINGS: 'Failed to fetch booking reservations', + FAILED_TO_CREATE_BOOKINGS: 'Failed to create booking reservations', + FAILED_TO_DELETE_BOOKINGS: 'Failed to delete booking reservations', FAILED_TO_FETCH_FEES: 'Failed to fetch existing fees from ORD', FAILED_TO_FETCH_PLANS: 'Failed to fetch plans from ORD', FAILED_TO_DELETE_FEES: 'Failed to delete fees in ORD', diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js index 6ba62e7..08c7fb6 100644 --- a/services/doorLock/doorLock.js +++ b/services/doorLock/doorLock.js @@ -359,7 +359,7 @@ const getLockEntryForReservation = (reservation, nextReservation) => { const getEntriesBetween = (fromTimestamp, toTimestamp, resourceId) => { return new Promise((resolve, reject) => { - if (!fromTimestamp || !toTimestamp || !resourceId){ + if (!resourceId){ resolve([]); }else { const andTimestampFilters = []; diff --git a/services/integration/bookingChangeCharges.js b/services/integration/bookingChangeCharges.js index 597a632..1f6f2ba 100644 --- a/services/integration/bookingChangeCharges.js +++ b/services/integration/bookingChangeCharges.js @@ -146,7 +146,7 @@ const getIncidentsFromChanges = (changes) => { reservationsForAdditionalCheck.push(reservationId); } } else { - console.log('\t\tNo'); + // console.log('\t\tNo'); // Reservation moved to another day // Add cancellation charge const chargeFee = oldReservationLength * reservationHourlyRate * BOOKING_CHANGE_PERCENTAGE_CHARGE / 100; diff --git a/services/integration/checkBookingChange.js b/services/integration/checkBookingChange.js index 9a46a39..ea53ae2 100644 --- a/services/integration/checkBookingChange.js +++ b/services/integration/checkBookingChange.js @@ -49,10 +49,13 @@ const checkBookingChanges = () => { Promise.all(asyncActions) .then((asyncActionResults) => { const changes = asyncActionResults[1]; + // console.log(changes); bulkWriteChanges(changes) .then(() => { getIncidentsFromChanges(changes) .then(({incidents, reservationsForAdditionalCheck}) => { + // console.log('=====INCIDENTS====='); + // console.log(incidents); bulkWriteBookingChangeIncidents(incidents) .then(() => { getReservationsIncidentsForRemoval(reservationsForAdditionalCheck) diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js index 8e5885b..e3cc7e7 100644 --- a/services/integration/doorLockCharges.js +++ b/services/integration/doorLockCharges.js @@ -51,6 +51,7 @@ const insertUnscheduledIncidents = (incidents) => { totalChargeFee, unlockTimestamp, lockTimestamp, + deleted: false }; asyncJobs.push(db.unscheduledIncident.findOrCreate({ @@ -62,7 +63,6 @@ const insertUnscheduledIncidents = (incidents) => { bookingEnd: end, unlockTimestamp, lockTimestamp, - deleted: false }, defaults: {...incidentForDB}, })); @@ -95,6 +95,7 @@ const insertUnlockedIncidents = (incidents) => { unlockTimestamp, incidentLevel, incidentLevelPrice, + deleted: false }; asyncJobs.push(db.unlockedIncident.findOrCreate({ @@ -106,7 +107,6 @@ const insertUnlockedIncidents = (incidents) => { bookingEnd: end, unlockTimestamp, incidentLevel, - deleted: false }, defaults: {...incidentForDB}, })); @@ -177,8 +177,8 @@ const setUnlockedIncidentsLevel = (incidents) => { formattedIncident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title; formattedIncident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price; } else { - const lastIncidentTime = moment.utc(memberLastIncident.incidentTimestamp).startOf('month'); - const currentIncidentTime = moment.utc(incident.unlockTimestamp).startOf('month'); + const lastIncidentTime = moment.tz(memberLastIncident.incidentTimestamp, UI_TIMEZONE).startOf('month'); + const currentIncidentTime = moment.tz(incident.unlockTimestamp, UI_TIMEZONE).startOf('month'); const timeDiff = Math.abs(lastIncidentTime.diff(currentIncidentTime, 'months')); if (timeDiff >= (parseInt(process.env.UNLOCK_STREAK_REPAIR_AFTER) || 6)){ @@ -238,7 +238,7 @@ const analyseReservation = (reservation) => { const previousReservationEnd = moment.utc(previousReservation.end); timeDifferenceFromPreviousReservation = currentReservationStart.diff(previousReservationEnd, 'minutes'); } - const previousReservationIsBackToBack = timeDifferenceFromPreviousReservation < MAX_BACK_TO_BACK_DIFFERENCE; + const previousReservationIsBackToBack = timeDifferenceFromPreviousReservation <= MAX_BACK_TO_BACK_DIFFERENCE; let timeDifferenceBeforeNextReservation = MAX_BACK_TO_BACK_DIFFERENCE + 100; @@ -246,7 +246,7 @@ const analyseReservation = (reservation) => { const nextReservationStart = moment.utc(nextReservation.start); timeDifferenceBeforeNextReservation = nextReservationStart.diff(currentReservationEnd, 'minutes'); } - const nextReservationIsBackToBack = timeDifferenceBeforeNextReservation < MAX_BACK_TO_BACK_DIFFERENCE; + const nextReservationIsBackToBack = timeDifferenceBeforeNextReservation <= MAX_BACK_TO_BACK_DIFFERENCE; let timeDifferenceFromUnlockEntry = 0; if (unlockEntry) { @@ -273,9 +273,10 @@ const analyseReservation = (reservation) => { const totalChargeFeeAfter = timeIntervalsToChargeAfter * UNSCHEDULED_CHARGE_PRICE; const chargeAfter = totalChargeFeeAfter > 0; - // if (reservation.memberId === '5ce785af422bdd00967fb781') { + // const reservationMoment = moment.tz(reservation.start, reservation.timezone); + // if (reservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-11-21 00:00:16+00')) { // console.log('\r\n\r\n==== ANALYSE RESERVATION ==== '); - // console.log('\tStart : ', moment.tz(reservation.start, reservation.timezone).format('DD.MM, HH:mm')); + // console.log('\tStart : ', reservationMoment.format('DD.MM, HH:mm')); // console.log('\tEnd : ', moment.tz(reservation.end, reservation.timezone).format('DD.MM, HH:mm')); // console.log('\t----------------------------------'); // console.log('\tFirst previous reservation : '); @@ -366,140 +367,61 @@ const getIncidentData = (reservation) => { timeIntervalsToChargeAfter, } = result; const incidents = []; - const incidentsAsyncJobs = []; const { resourceId } = currentReservation; - // 0a. Check for unscheduled use between current and previous reservation - const analysePreviousJob = []; - if (previousReservation && !previousReservationIsBackToBack){ - analysePreviousJob.push(analyseReservation(previousReservation)); - }else { - analysePreviousJob.push(undefined); - } + // const reservationMoment = moment.tz(currentReservation.start, currentReservation.timezone); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-11-23 00:00:16+00')) { + // console.log('\r\n\r\n==== ANALYSE RESERVATION [GET INCIDENT DATA] ==== '); + // console.log('\tStart : ', reservationMoment.format('DD.MM, HH:mm')); + // console.log('\tEnd : ', moment.tz(currentReservation.end, currentReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t----------------------------------'); + // console.log('\tFirst previous reservation : is back to back [', previousReservationIsBackToBack ? 'T' : 'F' , ']'); + // if (previousReservation) { + // console.log('\t\tStart : ', moment.tz(previousReservation.start, previousReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t\tEnd : ', moment.tz(previousReservation.end, previousReservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\t\tNO PREVIOUS RESERVATION'); + // } + // + // console.log('\tFirst next reservation : is back to back [', nextReservationIsBackToBack ? 'T' : 'F', ']'); + // if (nextReservation) { + // console.log('\t\tStart : ', moment.tz(nextReservation.start, nextReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t\tEnd : ', moment.tz(nextReservation.end, nextReservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\t\tNO NEXT RESERVATION'); + // } + // console.log('\t----------------------------------'); + // if (unlockEntry) { + // console.log('\tUnlock entry : ', moment.tz(unlockEntry.timestamp, reservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\tUnlock entry : NO UNLOCK ENTRY'); + // } + // if (lockEntry) { + // console.log('\tLock entry : ', moment.tz(lockEntry.timestamp, reservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\tLock entry : NO LOCK ENTRY'); + // } + // } - incidentsAsyncJobs.push( - Promise.all(analysePreviousJob) - .then(([previousReservationResults]) => { - let fromTimestamp; - let toTimestamp; + //********************** - if (previousReservationResults){ - const previousReservationLockEntry = previousReservationResults.lockEntry; - - fromTimestamp = previousReservationLockEntry && previousReservationLockEntry.timestamp ? - previousReservationLockEntry.timestamp : previousReservation.end; - toTimestamp = unlockEntry && unlockEntry.timestamp ? - unlockEntry.timestamp : reservation.start; - }else{ - fromTimestamp = undefined; - toTimestamp = unlockEntry && unlockEntry.timestamp ? - unlockEntry.timestamp : reservation.start; - } - - incidentsAsyncJobs.push( - getEntriesBetween(fromTimestamp, toTimestamp, resourceId) - .then((entriesBetween) => { - incidentsAsyncJobs.push( - new Promise((resolve, reject) => { - let pairUnlockEntry = null; - let pairLockEntry = null; - - entriesBetween.forEach((entry) => { - if (entry && entry.event){ - switch(entry.event){ - case doorLockEvents.USER_UNLOCKED: - if (!pairUnlockEntry){ - pairUnlockEntry = entry; - }else{ - const emptyReservation = { - reservationId: null, - start: null, - end: null, - }; - - incidents.push({ - incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE, - reservation: emptyReservation, - unlockTimestamp: pairUnlockEntry.timestamp, - memberId: pairUnlockEntry.memberId, - resourceId, - }); - - pairLockEntry = null; - pairUnlockEntry = entry; - } - break; - case doorLockEvents.USER_LOCKED: - if (pairUnlockEntry && !pairLockEntry){ - pairLockEntry = entry; - const emptyReservation = { - reservationId: null, - start: null, - end: null, - }; - const unlockMoment = moment.utc(pairUnlockEntry.timestamp); - const lockMoment = moment.utc(pairLockEntry.timestamp); - - if (lockMoment.tz(UI_TIMEZONE).isSame(unlockMoment.tz(UI_TIMEZONE), 'day')){ - const timeDifference = lockMoment.diff(unlockMoment, 'minutes'); - const timeIntervalsToCharge = Math.floor(timeDifference / UNSCHEDULED_TIME_RESOLUTION); - const totalChargeFee = timeIntervalsToCharge * UNSCHEDULED_CHARGE_PRICE; - if (timeIntervalsToCharge > 0){ - incidents.push({ - incidentType: incidentType.UNSCHEDULED_INCIDENT_STANDALONE, - reservation: emptyReservation, - unlockTimestamp: pairUnlockEntry.timestamp, - lockTimestamp: pairLockEntry.timestamp, - memberId: pairUnlockEntry.memberId, - resourceId, - chargePrice: UNSCHEDULED_CHARGE_PRICE, - timeIntervalsToCharge, - totalChargeFee, - }); - } - }else{ - const emptyReservation = { - reservationId: null, - start: null, - end: null, - }; - - incidents.push({ - incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE, - reservation: emptyReservation, - unlockTimestamp: pairUnlockEntry.timestamp, - memberId: pairUnlockEntry.memberId, - resourceId, - }); - } - - pairUnlockEntry = null; - pairLockEntry = null; - }else{ - if (!pairUnlockEntry){ - pairLockEntry = entry; - //Only lock entry, ignore now - } - pairLockEntry = null; - pairUnlockEntry = null; - } - } - } - }); - resolve(null); - })); - }) - .catch((error) => reject(error))); - }) - .catch((error) => reject(error))); + const emptyReservation = { + reservationId: null, + start: null, + end: null, + }; // 1. Check if member entered before reservation start time - // console.log('\r\n\r\nChecking if member entered before reservation start time :'); - // console.log('\tunlockEntry : ', unlockEntry && unlockEntry.timestamp ? unlockEntry.timestamp : 'NO UNLOCK ENTRY'); - // console.log('\tCharge before : ', chargeBefore); - // console.log('\tThere is prev. res : ', previousReservationIsBackToBack); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\r\n\r\nChecking if member entered before reservation start time :'); + // console.log('\tunlockEntry : ', unlockEntry && unlockEntry.timestamp ? unlockEntry.timestamp : 'NO UNLOCK ENTRY'); + // console.log('\tCharge before : ', chargeBefore); + // console.log('\tThere is prev. res : ', previousReservationIsBackToBack); + // } if (unlockEntry && chargeBefore && !previousReservationIsBackToBack) { - // console.log('\tIncident : YES'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : YES'); + // } incidents.push({ incidentType: incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION, reservation, @@ -512,16 +434,22 @@ const getIncidentData = (reservation) => { totalChargeFee: totalChargeFeeBefore, }); }else{ - // console.log('\tIncident : NO'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : NO'); + // } } // 2. Check if member left after reservation end time - // console.log('\r\n\r\nChecking if member left after reservation end time :'); - // console.log('\tlockEntry : ', lockEntry && lockEntry.timestamp ? lockEntry.timestamp : 'NO LOCK ENTRY'); - // console.log('\tCharge after : ', chargeAfter); - // console.log('\tThere is res after : ', nextReservationIsBackToBack); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\r\n\r\nChecking if member left after reservation end time :'); + // console.log('\tlockEntry : ', lockEntry && lockEntry.timestamp ? lockEntry.timestamp : 'NO LOCK ENTRY'); + // console.log('\tCharge after : ', chargeAfter); + // console.log('\tThere is res after : ', nextReservationIsBackToBack); + // } if (lockEntry && chargeAfter && !nextReservationIsBackToBack) { - // console.log('\tIncident : YES'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : YES'); + // } incidents.push({ incidentType: incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION, reservation, @@ -534,24 +462,25 @@ const getIncidentData = (reservation) => { totalChargeFee: totalChargeFeeAfter, }); }else{ - // console.log('\tIncident : NO'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : NO'); + // } } // 3. Check if member forgot to lock the door - // console.log('\r\n\r\nChecking if member left unlocked door :'); - // console.log('\tunlockEntry : ', unlockEntry && unlockEntry.timestamp ? unlockEntry.timestamp : 'NO UNLOCK ENTRY'); - // console.log('\tlockEntry : ', lockEntry && lockEntry.timestamp ? lockEntry.timestamp : 'NO LOCK ENTRY'); - // console.log('\tThere is res before: ', previousReservationIsBackToBack); - // console.log('\tThere is res after : ', nextReservationIsBackToBack); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\r\n\r\nChecking if member left unlocked door :'); + // console.log('\tunlockEntry : ', unlockEntry && unlockEntry.timestamp ? unlockEntry.timestamp : 'NO UNLOCK ENTRY'); + // console.log('\tlockEntry : ', lockEntry && lockEntry.timestamp ? lockEntry.timestamp : 'NO LOCK ENTRY'); + // console.log('\tThere is res before: ', previousReservationIsBackToBack); + // console.log('\tThere is res after : ', nextReservationIsBackToBack); + // } + let forgotToLockAsyncCheck; if (!lockEntry && !nextReservationIsBackToBack){ - const emptyReservation = { - reservationId: null, - start: null, - end: null, - }; - if (unlockEntry){ - // console.log('\tIncident : YES [#1]'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : YES [#1]'); + // } incidents.push({ incidentType: incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION, unlockTimestamp: unlockEntry.timestamp, @@ -570,11 +499,14 @@ const getIncidentData = (reservation) => { if (previousReservationIsBackToBack){ // To ensure that this is last reservation in block (there is previous but no next reservation back to back) // Now, just check if member actually locked before in the reservation time - incidentsAsyncJobs.push( - getLastEntryForReservation(reservation) + + forgotToLockAsyncCheck = getLastEntryForReservation(reservation); + forgotToLockAsyncCheck .then((lastEntry) => { if (lastEntry && lastEntry.event === doorLockEvents.USER_UNLOCKED){ - // console.log('\tIncident : YES [#2]'); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-12-01 00:00:16+00')) { + // console.log('\tIncident : YES [#2]'); + // } incidents.push({ incidentType: incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION, unlockTimestamp: lastEntry.timestamp, @@ -584,16 +516,224 @@ const getIncidentData = (reservation) => { }); } }) - .catch((error) => reject(error))); + .catch((error) => reject(error)); } } } - Promise.all(incidentsAsyncJobs) - .then(() => { - resolve(incidents); + + + // 4. Check for unscheduled use between current and previous reservation + const analysePreviousJob = []; + if (previousReservation && !previousReservationIsBackToBack){ + analysePreviousJob.push(analyseReservation(previousReservation)); + }else { + analysePreviousJob.push(undefined); + } + + Promise.all(analysePreviousJob) + .then(([previousReservationResults]) => { + let fromTimestamp; + let toTimestamp; + + if (previousReservationResults){ + const previousReservationLockEntry = previousReservationResults.lockEntry; + const previousReservationUnlockEntry = previousReservationResults.unlockEntry; + + //Special check for bookings with break between + if (previousReservation && + !previousReservationIsBackToBack && + previousReservation.memberId === currentReservation.memberId && + previousReservationUnlockEntry && !previousReservationLockEntry && + !unlockEntry && lockEntry //current reservation unlock / lock entries + ) { + + const unlockMoment = previousReservation.end ? moment.utc(previousReservation.end) : null; + const lockMoment = currentReservation.start ? moment.utc(currentReservation.start) : null; + + if (unlockMoment && lockMoment){ + const timeDifference = lockMoment.diff(unlockMoment, 'minutes'); + const timeIntervalsToCharge = Math.floor(timeDifference / UNSCHEDULED_TIME_RESOLUTION); + const totalChargeFee = timeIntervalsToCharge * UNSCHEDULED_CHARGE_PRICE; + if (timeIntervalsToCharge > 0) { + incidents.push({ + incidentType: incidentType.UNSCHEDULED_INCIDENT_STANDALONE, + reservation: emptyReservation, + unlockTimestamp: previousReservation.end, + lockTimestamp: currentReservation.start, + memberId: currentReservation.memberId, + resourceId, + chargePrice: UNSCHEDULED_CHARGE_PRICE, + timeIntervalsToCharge, + totalChargeFee, + }); + } + } + } + + fromTimestamp = previousReservationLockEntry && previousReservationLockEntry.timestamp ? + previousReservationLockEntry.timestamp : previousReservation.end; + toTimestamp = unlockEntry && unlockEntry.timestamp ? + unlockEntry.timestamp : reservation.start; + }else{ + fromTimestamp = undefined; + toTimestamp = unlockEntry && unlockEntry.timestamp ? + unlockEntry.timestamp : reservation.start; + } + + // Let's skip this if previous reservation is back-to-back + let getEntriesAsyncCheck; + if (previousReservation && previousReservationIsBackToBack){ + //Skip + }else{ + getEntriesAsyncCheck = getEntriesBetween(fromTimestamp, toTimestamp, resourceId); + getEntriesAsyncCheck + .then((entriesBetween) => { + // const reservationMoment = moment.tz(currentReservation.start, currentReservation.timezone); + // if (currentReservation.memberId === '5ce785af422bdd00967fb781' && reservationMoment.isAfter('2019-11-23 00:00:16+00')) { + // console.log('\r\n\r\n==== ANALYSE RESERVATION [GET INCIDENT DATA] ==== '); + // console.log('\tStart : ', reservationMoment.format('DD.MM, HH:mm')); + // console.log('\tEnd : ', moment.tz(currentReservation.end, currentReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t----------------------------------'); + // console.log('\tFirst previous reservation : is back to back [', previousReservationIsBackToBack ? 'T' : 'F', ']'); + // if (previousReservation) { + // console.log('\t\tStart : ', moment.tz(previousReservation.start, previousReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t\tEnd : ', moment.tz(previousReservation.end, previousReservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\t\tNO PREVIOUS RESERVATION'); + // } + // + // console.log('\tFirst next reservation : is back to back [', nextReservationIsBackToBack ? 'T' : 'F', ']'); + // if (nextReservation) { + // console.log('\t\tStart : ', moment.tz(nextReservation.start, nextReservation.timezone).format('DD.MM, HH:mm')); + // console.log('\t\tEnd : ', moment.tz(nextReservation.end, nextReservation.timezone).format('DD.MM, HH:mm')); + // } else { + // console.log('\t\tNO NEXT RESERVATION'); + // } + // + // console.log('\t--------------------'); + // + // console.log('\tFrom timestamp : ', fromTimestamp ? moment.tz(fromTimestamp, UI_TIMEZONE).format('DD.MM, HH:mm') : '-'); + // console.log('\tTo timestamp : ', toTimestamp ? moment.tz(toTimestamp, UI_TIMEZONE).format('DD.MM, HH:mm') : '-'); + // console.log('\t--------------------'); + // } + + let pairUnlockEntry = null; + let pairLockEntry = null; + + //Inspect all entries and insert detected incidents + entriesBetween.forEach((entry) => { + // console.log('\tEvent : ', entry.event); + // console.log('\tEvent : ', entry.timestamp ? moment.tz(entry.timestamp, UI_TIMEZONE).format('DD.MM, HH:mm') : '-'); + if (entry && entry.event){ + switch(entry.event){ + case doorLockEvents.USER_UNLOCKED: + if (!pairUnlockEntry){ + pairUnlockEntry = entry; + }else{ + incidents.push({ + incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE, + reservation: emptyReservation, + unlockTimestamp: pairUnlockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + }); + + pairLockEntry = null; + pairUnlockEntry = entry; + } + break; + case doorLockEvents.USER_LOCKED: + if (pairUnlockEntry && !pairLockEntry){ + pairLockEntry = entry; + + const unlockMoment = moment.utc(pairUnlockEntry.timestamp); + const lockMoment = moment.utc(pairLockEntry.timestamp); + + if (lockMoment.tz(UI_TIMEZONE).isSame(unlockMoment.tz(UI_TIMEZONE), 'day')){ + const timeDifference = lockMoment.diff(unlockMoment, 'minutes'); + const timeIntervalsToCharge = Math.floor(timeDifference / UNSCHEDULED_TIME_RESOLUTION); + const totalChargeFee = timeIntervalsToCharge * UNSCHEDULED_CHARGE_PRICE; + if (timeIntervalsToCharge > 0){ + incidents.push({ + incidentType: incidentType.UNSCHEDULED_INCIDENT_STANDALONE, + reservation: emptyReservation, + unlockTimestamp: pairUnlockEntry.timestamp, + lockTimestamp: pairLockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + chargePrice: UNSCHEDULED_CHARGE_PRICE, + timeIntervalsToCharge, + totalChargeFee, + }); + } + }else{ + incidents.push({ + incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE, + reservation: emptyReservation, + unlockTimestamp: pairUnlockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + }); + } + + pairUnlockEntry = null; + pairLockEntry = null; + }else{ + if (!pairUnlockEntry){ + pairLockEntry = entry; + //Only lock entry, ignore now + } + pairLockEntry = null; + pairUnlockEntry = null; + } + } + } + }); + + if (pairUnlockEntry){ + incidents.push({ + incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE, + reservation: emptyReservation, + unlockTimestamp: pairUnlockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + }); + + pairLockEntry = null; + pairUnlockEntry = null; + } + + + //Now wait also for "forgotToLockAsyncCheck" to finish + // Promise.all([forgotToLockAsyncCheck]) + // .then(() => { + // resolve(incidents); + // }) + // .catch(error => reject(error)); + }) + .catch((error) => reject(error)) + } + + //Now wait for "forgotToLockAsyncCheck", and possible "getEntriesBetween" to finish + Promise.all([forgotToLockAsyncCheck, getEntriesAsyncCheck]) + .then(() => { + resolve(incidents); + }) + .catch(error => reject(error)); }) - .catch((error) => reject(error)); + .catch(error => reject(error)); + // + // Promise.all(incidentsAsyncJobs) + // .then(() => { + // // console.log('\tDone with Async jobs for reservation '); + // // console.log('\t\tStart : ', reservationMoment.format('DD.MM, HH:mm')); + // // console.log('\t\tEnd : ', moment.tz(currentReservation.end, currentReservation.timezone).format('DD.MM, HH:mm')); + // + // setTimeout(() => {resolve(incidents)}, 10000); + // // resolve(incidents); + // }) + // .catch((error) => reject(error)); }) .catch((error) => reject(error)); diff --git a/services/officeRnD/bookings.js b/services/officeRnD/bookings.js index d7b508e..35b4fa5 100644 --- a/services/officeRnD/bookings.js +++ b/services/officeRnD/bookings.js @@ -5,7 +5,7 @@ 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 { officeRnDAPIErrors, MAX_BACK_TO_BACK_DIFFERENCE, UI_TIMEZONE } = require('../../constants/constants'); const fetchAllBookings = () => { return new Promise((resolve, reject) => { @@ -14,24 +14,119 @@ const fetchAllBookings = () => { 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; + const bookingsToCreate = []; + const bookingIdsToRemove = []; - 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, - }); + bookingData.forEach(fullBookingEntry => { + if (!fullBookingEntry){ + return; + } + const fees = fullBookingEntry.fees ? fullBookingEntry.fees : []; + + if (fees.length > 1){ + // Recurring booking, let's create new booking + const member = fullBookingEntry.member ? fullBookingEntry.member : null; + const office = fullBookingEntry.office ? fullBookingEntry.office : null; + const resourceId = fullBookingEntry.resourceId ? fullBookingEntry.resourceId : null; + const team = fullBookingEntry.team ? fullBookingEntry.team : null; + const organization = fullBookingEntry.organization ? fullBookingEntry.organization : null; + const plan = fullBookingEntry.plan ? fullBookingEntry.plan : null; + const timezone = fullBookingEntry.timezone ? fullBookingEntry.timezone : UI_TIMEZONE; + const source = 'admin'; + + const startMoment = fullBookingEntry && fullBookingEntry.start && fullBookingEntry.start.dateTime ? + moment.utc(fullBookingEntry.start.dateTime) : null; + const endMoment = fullBookingEntry && fullBookingEntry.end && fullBookingEntry.end.dateTime ? + moment.utc(fullBookingEntry.end.dateTime) : null; + + fees.forEach(fee => { + const dateMoment = fee.date ? moment.utc(fee.date) : null; + + if (startMoment && endMoment && dateMoment){ + const yearPart = dateMoment.year(); + const monthPart = dateMoment.month(); + const dayPart = dateMoment.date(); + + const newStartMoment = startMoment.clone().tz(fullBookingEntry.timezone).year(yearPart).month(monthPart).date(dayPart); + const newEndMoment = endMoment.clone().tz(fullBookingEntry.timezone).year(yearPart).month(monthPart).date(dayPart); + + bookingsToCreate.push({ + start: { + dateTime: newStartMoment.toISOString() + }, + end: { + dateTime: newEndMoment.toISOString() + }, + team, + member, + resourceId, + office, + source, + timezone, + organization, + plan + }) + } + }); + + bookingIdsToRemove.push(fullBookingEntry['_id']); + } }); - resolve(cleanedBookingReservations); + + //Here we now have possible bookings to create and then load again "check Booking changes" + + if (bookingIdsToRemove.length > 0){ + //First delete, wait until operation is done, than create bookings (to avoid conflicting date/time) + API.delete('bookings/?silent', { data: bookingIdsToRemove }) + .then(() => { + //Now, insert new bookings + API.post('bookings/?silent', bookingsToCreate) + .then(() => { + //And fetch again all bookings + resolve(fetchAllBookings()); + }) + .catch((error) => { + console.log(officeRnDAPIErrors.FAILED_TO_CREATE_BOOKINGS); + console.log('Details : ', error); + reject(officeRnDAPIErrors.FAILED_TO_CREATE_BOOKINGS); + }); + }) + .catch(error => { + console.log(officeRnDAPIErrors.FAILED_TO_DELETE_BOOKINGS); + console.log('Details : ', error); + reject(officeRnDAPIErrors.FAILED_TO_DELETE_BOOKINGS); + }); + }else{ + 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; + + const startMoment = fullBookingEntry && fullBookingEntry.start && fullBookingEntry.start.dateTime ? + moment.utc(fullBookingEntry.start.dateTime) : null; + const endMoment = fullBookingEntry && fullBookingEntry.end && fullBookingEntry.end.dateTime ? + moment.utc(fullBookingEntry.end.dateTime) : null; + + // console.log('\r\n\r\nStart : ', startMoment.clone().tz(fullBookingEntry.timezone).format('DD.MM. HH:mm'), '[', startMoment.toISOString(),']'); + // console.log('End : ', endMoment.clone().tz(fullBookingEntry.timezone).format('DD.MM. HH:mm'), '[', endMoment.toISOString(), ']'); + // console.log('Fees : '); + + if (startMoment && endMoment){ + cleanedBookingReservations.push({ + reservationId: fullBookingEntry['_id'], + memberId: fullBookingEntry.member, + officeId: fullBookingEntry.office, + resourceId: fullBookingEntry.resourceId, + start: startMoment.toISOString(), + end: endMoment.toISOString(), + timezone: fullBookingEntry.timezone, + canceled: fullBookingEntry.canceled || false, + hourlyRate, + }); + } + }); + resolve(cleanedBookingReservations); + } }) .catch((error) => { console.log(officeRnDAPIErrors.FAILED_TO_FETCH_BOOKINGS);