Merge branch 'add-door-lock-charges-screen' into 'master'

Add door lock charges screen

See merge request saburly/psihologija!17
This commit was merged in pull request #17.
This commit is contained in:
Bilal Catic
2019-06-25 10:57:28 +00:00
4 changed files with 33 additions and 173 deletions

View File

@@ -27,7 +27,13 @@ const uploadDoorLockData = (req, res) => {
parserResults.forEach((parserResult) => { parserResults.forEach((parserResult) => {
parsedData.push(...parserResult.parsedData); parsedData.push(...parserResult.parsedData);
parserErrors.push(...parserResult.errors); parserErrors.push(...parserResult.errors);
unknownMembers.push(...parserResult.unknownMembers);
parserResult.unknownMembers.forEach((newUnknownMember) => {
// Check if member is already labeled as unknown in different file
if (!unknownMembers.find((unknownMember) => unknownMember.details === newUnknownMember.details)){
unknownMembers.push(newUnknownMember);
}
});
}); });
const asyncWriteJobs = []; const asyncWriteJobs = [];

View File

@@ -3,10 +3,11 @@
const db = require('../../models'); const db = require('../../models');
const fs = require('fs'); const fs = require('fs');
const csv = require('csv-parser'); const csv = require('csv-parser');
const moment = require('moment/moment'); const moment = require('moment-timezone');
const Op = require('sequelize').Op; const Op = require('sequelize').Op;
const { const {
UI_TIMEZONE,
USER_ENTRY_EVENT, USER_ENTRY_EVENT,
ENABLE_PASSAGE_MODE, ENABLE_PASSAGE_MODE,
DISABLE_PASSAGE_MODE, DISABLE_PASSAGE_MODE,
@@ -38,7 +39,7 @@ const parseDoorLockDataFile = (file) => {
return new Promise ((resolve, reject) => { return new Promise ((resolve, reject) => {
const results = []; const results = [];
const errors = []; const errors = [];
const unknownMembers = []; const unknownMembersToReport = [];
let isValidFile = true; let isValidFile = true;
const prefetchDataJobs = [getMappingsFromDatabase(), fetchAllMembers()]; const prefetchDataJobs = [getMappingsFromDatabase(), fetchAllMembers()];
@@ -48,6 +49,11 @@ const parseDoorLockDataFile = (file) => {
const mappings = result[0]; const mappings = result[0];
const allMembers = result[1]; const allMembers = result[1];
const membersMap = {};
const unknownMembersMap = {};
allMembers.forEach((member) => membersMap[member.name] = member);
const mappingFromFileName = extractMappingFromFileName(file.name); const mappingFromFileName = extractMappingFromFileName(file.name);
const mappingObject = checkIfMappingExsists(mappingFromFileName, mappings); const mappingObject = checkIfMappingExsists(mappingFromFileName, mappings);
if (!mappingObject){ if (!mappingObject){
@@ -104,13 +110,15 @@ const parseDoorLockDataFile = (file) => {
const secondEntry = results[i+1]; const secondEntry = results[i+1];
if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){ if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){
const memberObject = allMembers.find(member => member.name === firstEntry.name); const memberObject = membersMap[firstEntry.name];
if (!memberObject){ if (!memberObject){
//Check if member is already labeled as unknown //Check if member is already labeled as unknown
const unknownMember = unknownMembers.find((member) => member.details === firstEntry.name); const unknownMember = unknownMembersMap[firstEntry.name];
if (!unknownMember){ if (!unknownMember){
unknownMembers.push({ unknownMembersMap[firstEntry.name] = firstEntry.name;
unknownMembersToReport.push({
error: csvParserErrors.UNKNOWN_MEMBER, error: csvParserErrors.UNKNOWN_MEMBER,
details: firstEntry.name, details: firstEntry.name,
file: file.name, file: file.name,
@@ -123,7 +131,7 @@ const parseDoorLockDataFile = (file) => {
doorLockEvents.USER_UNLOCKED : doorLockEvents.USER_LOCKED; doorLockEvents.USER_UNLOCKED : doorLockEvents.USER_LOCKED;
const dateTimeString = `${firstEntry.date} ${firstEntry.time}`; const dateTimeString = `${firstEntry.date} ${firstEntry.time}`;
const timestamp = moment.utc(dateTimeString, 'MM/DD/YY HH:mm:ss A').toISOString(); const timestamp = moment.tz(dateTimeString, 'MM/DD/YY HH:mm:ss A', UI_TIMEZONE).tz('UTC').toISOString();
//Verify that member is registered in OfficeRnD system //Verify that member is registered in OfficeRnD system
if (memberObject){ if (memberObject){
@@ -159,7 +167,7 @@ const parseDoorLockDataFile = (file) => {
} }
resolve({ resolve({
parsedData, parsedData,
unknownMembers, unknownMembers: unknownMembersToReport,
errors errors
}); });
}); });

View File

@@ -21,160 +21,6 @@ const getSortedIncidentsForMember = (memberId) => {
}) })
}; };
const createUnlockedIncident = (reservation) => {
return new Promise((resolve, reject) => {
const { reservationId, memberId, resourceId, start, end } = reservation;
getLastIncidentForMember(memberId)
.then(incidents => {
const lastIncident = incidents && incidents[0] ? incidents[0] : undefined;
const incident = {
reservationId,
memberId,
resourceId,
bookingStart: start,
bookingEnd: end,
incidentLevel: null,
incidentLevelPrice: null,
};
console.log('=> UNLOCKED INCIDENT');
console.log('\tMember : ', memberId);
console.log('\tStart : ', start);
console.log('\tEnd : ', end);
console.log('\tMore details : ');
/*
if (lastIncident){
const lastIncidentLevel = lastIncident.incidentLevel;
const lastIncidentBeginningOfTheMonth = moment(lastIncident.bookingStart).startOf('month');
const beginningOfTheMonth = moment.utc().startOf('month');
const timePassedFromLastIncident = Math.abs(beginningOfTheMonth.diff(lastIncidentBeginningOfTheMonth, 'months'));
if (timePassedFromLastIncident >= 6){
console.log('\t\t-> This is first incident for this member in last 6 months');
incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
} else {
console.log('\t\t-> This member had incident(s) in past 6 months !!!');
incident.incidentLevel = lastIncidentLevel;
incident.incidentLevelPrice = unlockedIncidentLevelsPrices[lastIncidentLevel].price;
}
console.log('\t\tLast incident details : ');
console.log('\t\tStart : ', lastIncident.bookingStart);
console.log('\t\tCalculated diff : ', timePassedFromLastIncident);
console.log('\t\t------------------');
console.log('\tNew incident level : ', incident.incidentLevel);
} else {
console.log('\t\tThis is first incident for this member, EVER !');
incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
}
*/
db.unlockedIncident.findOrCreate({
where: {
reservationId,
memberId,
resourceId,
bookingStart: start,
bookingEnd: end,
},
defaults: {
...incident
}
})
.then(()=>resolve())
.catch((error)=>reject(error));
})
.catch((error) => {
reject(error);
});
});
};
const createUnscheduledUseIncident = (reservation, doorLockEntry) => {
return new Promise((resolve, reject) => {
const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5;
const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5;
const reservationEndTime = moment(reservation.end);
const lockedTime = moment(doorLockEntry.timestamp);
const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes'));
const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution);
const totalChargeFee = timeIntervalsToCharge * chargePrice;
if (timeIntervalsToCharge > 0){
const incident = {
reservationId: reservation.reservationId,
memberId: reservation.memberId,
resourceId: reservation.resourceId,
bookingStart: reservation.start,
bookingEnd: reservation.end,
doorLockEventTimestamp: doorLockEntry.timestamp,
doorLockEventType: doorLockEntry.event,
chargePrice,
timeIntervalsToCharge,
totalChargeFee,
};
db.unscheduledIncident.findOrCreate({where: {...incident}, defaults: {...incident}})
.then(()=>resolve())
.catch((error)=>reject(error));
}else{
resolve();
}
});
};
const createDoorLockIncident = (reservation, doorLockEntry) => {
return new Promise((resolve, reject) => {
if (!doorLockEntry){
// Check if there is unlock entry for this reservation
getUnlockEntryForReservation(reservation)
.then((unlockEntry) => {
if (!unlockEntry){
// check if there is back-to-back booking before current one
getFirstPreviousBooking(reservation)
.then((previousReservation) => {
if (previousReservation){
const previousReservationEnd = moment(previousReservation.end);
const currentReservationStart = moment(reservation.start);
const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes'));
const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;
if (timeDifference <= maxBackToBackDifference) {
createUnlockedIncident(reservation)
.then(() => resolve())
.catch((error) => reject(error));
}else{
resolve();
}
}else{
resolve();
}
})
.catch((error)=>reject(error));
}else {
createUnlockedIncident(reservation)
.then(()=>resolve())
.catch((error)=>reject(error));
}
})
.catch((error) => {
reject(error);
});
}else{
createUnscheduledUseIncident(reservation, doorLockEntry)
.then(()=>resolve())
.catch((error) => reject(error));
}
});
};
const insertUnscheduledIncidents = (incidents) => { const insertUnscheduledIncidents = (incidents) => {
const asyncJobs = []; const asyncJobs = [];
incidents.forEach((incident) => { incidents.forEach((incident) => {
@@ -333,8 +179,8 @@ const getIncidentData = (reservation) => {
if (nextReservation){ if (nextReservation){
// Check if next reservations is immediately after (back to back reservation) // Check if next reservations is immediately after (back to back reservation)
// If yes, then there is no need to check door lock entries related to this booking // If yes, then there is no need to check door lock entries related to this booking
const firstReservationEnd = moment(reservation.end); const firstReservationEnd = moment.utc(reservation.end);
const secondReservationStart = moment(nextReservation.start); const secondReservationStart = moment.utc(nextReservation.start);
const timeDifference = Math.abs(secondReservationStart.diff(firstReservationEnd, 'minutes')); const timeDifference = Math.abs(secondReservationStart.diff(firstReservationEnd, 'minutes'));
const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0; const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;
@@ -355,8 +201,8 @@ const getIncidentData = (reservation) => {
const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5 const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5
const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5; const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5;
const reservationEndTime = moment(reservation.end); const reservationEndTime = moment.utc(reservation.end);
const lockedTime = moment(lockEntry.timestamp); const lockedTime = moment.utc(lockEntry.timestamp);
const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes')); const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes'));
const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution); const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution);
@@ -391,8 +237,8 @@ const getIncidentData = (reservation) => {
getFirstPreviousBooking(reservation) getFirstPreviousBooking(reservation)
.then((previousReservation) => { .then((previousReservation) => {
if (previousReservation){ if (previousReservation){
const previousReservationEnd = moment(previousReservation.end); const previousReservationEnd = moment.utc(previousReservation.end);
const currentReservationStart = moment(reservation.start); const currentReservationStart = moment.utc(reservation.start);
const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes')); const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes'));
const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0; const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;

View File

@@ -41,7 +41,7 @@ const getAllFinishedBookings = () => {
const filters = { const filters = {
canceled: false, canceled: false,
end: { end: {
[Op.lt]: moment().toISOString() [Op.lt]: moment.utc().toISOString()
} }
}; };
@@ -56,8 +56,8 @@ const getAllFinishedBookings = () => {
const getFirstNextBooking = (reservation) => { const getFirstNextBooking = (reservation) => {
return new Promise ((resolve, reject) => { return new Promise ((resolve, reject) => {
const {resourceId, start, timezone} = reservation; const { resourceId, start } = reservation;
const endOfTheDay = moment.tz(start, timezone).endOf('Day').toISOString(); const endOfTheDay = moment.utc(start).endOf('Day').toISOString();
const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone'];
const filters = { const filters = {
@@ -90,8 +90,8 @@ const getFirstNextBooking = (reservation) => {
const getFirstPreviousBooking = (reservation) => { const getFirstPreviousBooking = (reservation) => {
return new Promise ((resolve, reject) => { return new Promise ((resolve, reject) => {
const {resourceId, start, timezone} = reservation; const { resourceId, start } = reservation;
const startOfTheDay = moment.tz(start, timezone).startOf('Day').toISOString(); const startOfTheDay = moment.utc(start).startOf('Day').toISOString();
const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone'];
const filters = { const filters = {