diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js index c50c6eb..fdea6f7 100644 --- a/services/doorLock/doorLock.js +++ b/services/doorLock/doorLock.js @@ -344,8 +344,33 @@ const getLockEntryForReservation = (reservation, nextReservation) => { }); }; -const getEntriesBetween = (reservation, previousReservation) => { +const getEntriesBetween = (fromTimestamp, toTimestamp, resourceId) => { + return new Promise((resolve, reject) => { + if (!fromTimestamp || !toTimestamp || !resourceId){ + resolve([]); + }else { + const andTimestampFilters = []; + if (fromTimestamp){ + andTimestampFilters.push({[Op.gt]: fromTimestamp}); + } + if (toTimestamp){ + andTimestampFilters.push({[Op.lt]: toTimestamp}); + } + + const filters = { + resourceId, + timestamp: { + [Op.and]: andTimestampFilters, + }, + }; + const order = [['timestamp', 'ASC']]; + + db.doorLockEvent.findAll({where: filters, order}) + .then((results) => resolve(results)) + .catch((error) => reject(error)); + } + }); }; const getLastEntryForReservation = (reservation) => { diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js index a6ac770..0c70262 100644 --- a/services/integration/doorLockCharges.js +++ b/services/integration/doorLockCharges.js @@ -180,8 +180,7 @@ const analyseReservation = (reservation) => { .then(([previousReservation, nextReservation]) => { const getRelatedDoorLockEntriesAsync = [ getUnlockEntryForReservation(reservation, previousReservation), - getLockEntryForReservation(reservation, nextReservation), - getEntriesBetween(reservation, previousReservation) + getLockEntryForReservation(reservation, nextReservation) ]; Promise.all(getRelatedDoorLockEntriesAsync) @@ -274,6 +273,113 @@ 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); + } + + 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 virtualReservation = { + reservationId: '', + start: pairUnlockEntry.timestamp, + end: pairUnlockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + }; + + incidents.push({ + incidentType: incidentType.UNLOCKED_INCIDENT, + reservation: virtualReservation, + }); + + pairLockEntry = null; + pairUnlockEntry = entry; + } + break; + case doorLockEvents.USER_LOCKED: + if (pairUnlockEntry && !pairLockEntry){ + pairLockEntry = entry; + const virtualReservation = { + reservationId: '', + start: pairUnlockEntry.timestamp, + end: pairLockEntry.timestamp, + memberId: pairUnlockEntry.memberId, + resourceId, + }; + const unlockMoment = moment.utc(pairUnlockEntry.timestamp); + const lockMoment = moment.utc(pairLockEntry.timestamp); + 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, + reservation: virtualReservation, + doorLockEntry: pairLockEntry, + chargePrice: UNSCHEDULED_CHARGE_PRICE, + timeIntervalsToCharge, + totalChargeFee, + }); + } + + 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))); // 1. Check if member entered before reservation start time if (unlockEntry && chargeBefore && !previousReservationIsBackToBack) { @@ -306,7 +412,6 @@ const getIncidentData = (reservation) => { incidentType: incidentType.UNLOCKED_INCIDENT, reservation, }); - resolve(incidents); } else { // No lock entry, no unlock entry and no reservation after this one // This is either : @@ -318,6 +423,7 @@ 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) .then((lastEntry) => { if (lastEntry && lastEntry.event === doorLockEvents.USER_UNLOCKED){ @@ -326,16 +432,18 @@ const getIncidentData = (reservation) => { reservation, }); } - resolve(incidents); }) - .catch((error) => reject(error)); - } else { - resolve(incidents); + .catch((error) => reject(error))); } } - }else{ - resolve(incidents); } + + Promise.all(incidentsAsyncJobs) + .then(() => { + resolve(incidents); + }) + .catch((error) => reject(error)); + }) .catch((error) => reject(error)); });