diff --git a/client/src/components/MemberIncidentsTables/components/SingleIncidentsTable.js b/client/src/components/MemberIncidentsTables/components/SingleIncidentsTable.js
new file mode 100644
index 0000000..5c1d91e
--- /dev/null
+++ b/client/src/components/MemberIncidentsTables/components/SingleIncidentsTable.js
@@ -0,0 +1,119 @@
+import React from 'react';
+import { Loader } from 'semantic-ui-react';
+import ReactTable from 'react-table';
+import 'react-table/react-table.css';
+import { NavLink } from 'react-router-dom';
+
+import {incidentsReportHeaderTitles} from '../../../constants/menuItems';
+import {
+ incidentDescriptions,
+ incidentLevelDescriptions,
+ UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION, UNLOCKED_INCIDENT_STANDALONE, UNSCHEDULED_INCIDENT_AFTER_RESERVATION,
+ UNSCHEDULED_INCIDENT_BEFORE_RESERVATION, UNSCHEDULED_INCIDENT_STANDALONE
+} from '../../../constants/enums';
+
+
+const SingleIncidentsTable = props => {
+ const { loading, title, openMemberSummaryOnMemberClick, showBookingTimes, showDoorLockEntryTimes, hideMemberName } = props;
+ const incidents = props.incidents ? props.incidents : [];
+
+ const columns = [];
+ if (incidents && incidents.length > 0){
+ const incidentHeaders = Object.keys(incidentsReportHeaderTitles);
+
+ incidentHeaders.forEach((header) => {
+ const columnTitle = incidentsReportHeaderTitles[header];
+
+ let showColumn = true;
+ if ((header === 'bookingStart' || header === 'bookingEnd') && !showBookingTimes){
+ showColumn = false;
+ }
+ if ((header === 'unlockTimestamp' || header === 'lockTimestamp') && !showDoorLockEntryTimes){
+ showColumn = false;
+ }
+ if (header === 'memberName' && hideMemberName){
+ showColumn = false;
+ }
+
+ if (columnTitle && showColumn){
+ const columnAlignments = {
+ left: 'left',
+ right: 'right',
+ };
+ let columnContentsAlignment = columnAlignments.left;
+
+ columns.push({
+ Header: incidentsReportHeaderTitles[header],
+ accessor: header,
+ Cell: props => {
+ let cellValue = '';
+ let urlValue = undefined;
+
+ switch (props.column.id) {
+ case 'memberName':
+ const memberId = props.row['_original'].memberId;
+ urlValue = `/practice-summary-report/${memberId}`;
+ cellValue = props.value;
+ break;
+ case 'incidentType':
+ cellValue = incidentDescriptions[props.value];
+ break;
+ case 'incidentLevel':
+ cellValue = incidentLevelDescriptions[props.value];
+ break;
+ case 'feeDescription':
+ const { incidentType, incidentLevel, timeIntervalsToCharge } = props.row['_original'];
+
+ switch (incidentType) {
+ case UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
+ case UNLOCKED_INCIDENT_STANDALONE:
+ cellValue = `${incidentLevelDescriptions[incidentLevel]}`;
+ break;
+ case UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
+ case UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
+ case UNSCHEDULED_INCIDENT_STANDALONE:
+ cellValue = `${timeIntervalsToCharge} x 5 min`;
+ break;
+ default:
+ cellValue = '';
+ break;
+ }
+ break;
+ case 'totalChargeFee':
+ const totalFee = props.value ? props.value : props.row['_original'].incidentPrice;
+ const totalFeeFormatted = parseFloat(totalFee).toFixed(2);
+ cellValue = `$ ${totalFeeFormatted}`;
+ columnContentsAlignment = columnAlignments.right;
+ break;
+ default:
+ cellValue = props.value;
+ }
+
+ if (openMemberSummaryOnMemberClick && urlValue){
+ return {cellValue}
+ }else{
+ return
{cellValue}
+ }
+ }
+ });
+ }
+ });
+ }
+
+ return (
+
+
{title}
+
+ {
+ !loading && incidents &&
+
+ }
+
+ );
+};
+
+export default SingleIncidentsTable;
diff --git a/client/src/components/MemberIncidentsTables/index.js b/client/src/components/MemberIncidentsTables/index.js
new file mode 100644
index 0000000..ed05541
--- /dev/null
+++ b/client/src/components/MemberIncidentsTables/index.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import {Accordion, Label} from 'semantic-ui-react';
+import SingleIncidentsTable from './components/SingleIncidentsTable';
+import {
+ UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION, UNLOCKED_INCIDENT_STANDALONE, UNSCHEDULED_INCIDENT_AFTER_RESERVATION,
+ UNSCHEDULED_INCIDENT_BEFORE_RESERVATION, UNSCHEDULED_INCIDENT_STANDALONE
+} from '../../constants/enums';
+
+export default function MemberIncidentsTables (props) {
+ const { pendingIncidents, incidents, hideMemberName } = props;
+
+ const incidentsRelatedToReservations = [];
+ const standaloneIncidents = [];
+
+ if (Array.isArray(incidents)){
+ incidents.forEach((incident) => {
+ if (incident && incident.incidentType){
+ switch (incident.incidentType) {
+ case UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
+ case UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
+ case UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
+ incidentsRelatedToReservations.push(incident);
+ break;
+ case UNLOCKED_INCIDENT_STANDALONE:
+ case UNSCHEDULED_INCIDENT_STANDALONE:
+ standaloneIncidents.push(incident);
+ break;
+ }
+ }
+ });
+ }
+
+ const incidentsRelatedToReservationsTable = (
+
+ );
+
+ const standaloneIncidentsTable = (
+
+ );
+
+ const accordionPanels = [
+ {
+ key: 'related-door-lock-incidents',
+ title : { content: },
+ content: { content : incidentsRelatedToReservationsTable },
+ },
+ {
+ key: 'standalone-door-lock-incidents',
+ title : { content: },
+ content: { content: standaloneIncidentsTable },
+ },
+ {
+ key: 'reservation-modification-incidents',
+ title: {content: },
+ }
+ ];
+
+ return (
+
+ );
+}
diff --git a/client/src/constants/enums.js b/client/src/constants/enums.js
index a6da2d0..2a3f642 100644
--- a/client/src/constants/enums.js
+++ b/client/src/constants/enums.js
@@ -1,10 +1,16 @@
-export const UNLOCKED_INCIDENT = 2;
-export const UNSCHEDULED_INCIDENT = 3;
+export const UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION = 2;
+export const UNSCHEDULED_INCIDENT_BEFORE_RESERVATION = 3;
+export const UNSCHEDULED_INCIDENT_AFTER_RESERVATION = 4;
+export const UNLOCKED_INCIDENT_STANDALONE = 5;
+export const UNSCHEDULED_INCIDENT_STANDALONE = 6;
export const incidentDescriptions = {};
-incidentDescriptions[UNLOCKED_INCIDENT] = 'User left door unlocked';
-incidentDescriptions[UNSCHEDULED_INCIDENT] = 'Unscheduled use';
+incidentDescriptions[UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION] = 'User left door unlocked';
+incidentDescriptions[UNSCHEDULED_INCIDENT_BEFORE_RESERVATION] = 'Unscheduled use - before';
+incidentDescriptions[UNSCHEDULED_INCIDENT_AFTER_RESERVATION] = 'Unscheduled use - after';
+incidentDescriptions[UNLOCKED_INCIDENT_STANDALONE] = 'User left door unlocked';
+incidentDescriptions[UNSCHEDULED_INCIDENT_STANDALONE] = 'Unscheduled use';
export const incidentLevelDescriptions = {
UNLOCKED_0: 'First month',
@@ -13,5 +19,4 @@ export const incidentLevelDescriptions = {
UNLOCKED_3: 'Fourth month',
UNLOCKED_4: 'Fifth month',
UNLOCKED_5: 'Sixth month',
-
};
diff --git a/client/src/constants/menuItems.js b/client/src/constants/menuItems.js
index 07b6c96..8a83cfc 100644
--- a/client/src/constants/menuItems.js
+++ b/client/src/constants/menuItems.js
@@ -45,6 +45,8 @@ export const incidentsReportHeaderTitles = {
resourceName: 'Room',
bookingStart: 'Reservation Start',
bookingEnd: 'Reservation End',
+ unlockTimestamp: 'Unlock Time',
+ lockTimestamp: 'Lock Time',
memberName: 'Member Name',
incidentType: 'Incident Type',
feeDescription: 'Fee description',
diff --git a/client/src/scenes/IncidentsReport/index.js b/client/src/scenes/IncidentsReport/index.js
index fa464ac..3c4f766 100644
--- a/client/src/scenes/IncidentsReport/index.js
+++ b/client/src/scenes/IncidentsReport/index.js
@@ -4,7 +4,7 @@ import { Container } from 'semantic-ui-react';
import MainMenu from '../../components/MainMenu';
import DateRangePicker from '../../components/DateRangePicker';
-import MemberIncidentsTable from '../../components/MemberIncidentsTable';
+import MemberIncidentsTables from '../../components/MemberIncidentsTables';
import { fetchIncidents } from '../../store/actions';
@@ -24,7 +24,7 @@ class IncidentsReport extends Component {
-
+
);
}
diff --git a/client/src/scenes/PracticeSummaryReport/components/MemberSelector.js b/client/src/scenes/PracticeSummaryReport/components/MemberSelector.js
index 2c439b3..d22ebe7 100644
--- a/client/src/scenes/PracticeSummaryReport/components/MemberSelector.js
+++ b/client/src/scenes/PracticeSummaryReport/components/MemberSelector.js
@@ -22,7 +22,7 @@ class MemberSelector extends Component {
}
render(){
- const { members } = this.props;
+ const { members, defaultMemberId } = this.props;
const dropdownOptions = members && Array.isArray(members) ? members.map(member => ({
key: member.memberId,
@@ -41,6 +41,7 @@ class MemberSelector extends Component {
search
fluid
onChange={this.onMemberSelectionChange.bind(this)}
+ value={defaultMemberId ? defaultMemberId : null}
/>
);
diff --git a/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js b/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
index 6f15622..212d173 100644
--- a/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
+++ b/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
@@ -1,7 +1,11 @@
import React from 'react';
import { Loader, Grid } from 'semantic-ui-react';
-import { UNSCHEDULED_INCIDENT, UNLOCKED_INCIDENT } from '../../../constants/enums';
+import {
+ UNSCHEDULED_INCIDENT_BEFORE_RESERVATION,
+ UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION,
+ UNSCHEDULED_INCIDENT_AFTER_RESERVATION, UNSCHEDULED_INCIDENT_STANDALONE, UNLOCKED_INCIDENT_STANDALONE
+} from '../../../constants/enums';
const MemberSummary = props => {
const { loading } = props;
@@ -12,10 +16,13 @@ const MemberSummary = props => {
incidents.forEach((incident) => {
switch (incident.incidentType) {
- case UNSCHEDULED_INCIDENT:
+ case UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
+ case UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
+ case UNSCHEDULED_INCIDENT_STANDALONE:
totalUnscheduledFees += parseFloat(incident.totalChargeFee);
break;
- case UNLOCKED_INCIDENT:
+ case UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
+ case UNLOCKED_INCIDENT_STANDALONE:
totalUnlockedFees += parseFloat(incident.incidentPrice);
break;
default:
diff --git a/client/src/scenes/PracticeSummaryReport/index.js b/client/src/scenes/PracticeSummaryReport/index.js
index 32ea48e..e9a062b 100644
--- a/client/src/scenes/PracticeSummaryReport/index.js
+++ b/client/src/scenes/PracticeSummaryReport/index.js
@@ -6,7 +6,7 @@ import MainMenu from '../../components/MainMenu';
import DateRangePicker from '../../components/DateRangePicker';
import MemberSelector from './components/MemberSelector';
import MemberSummary from './components/MemberSummary';
-import MemberIncidentsTable from '../../components/MemberIncidentsTable';
+import MemberIncidentsTables from '../../components/MemberIncidentsTables';
import { fetchMemberIncidents } from '../../store/actions';
@@ -40,6 +40,7 @@ class PracticeSummaryReport extends Component {
render () {
const { memberIncidents, loading } = this.props;
+ const { memberId } = this.state;
return (
@@ -49,7 +50,7 @@ class PracticeSummaryReport extends Component {
-
+
@@ -66,11 +67,7 @@ class PracticeSummaryReport extends Component {
-
+
diff --git a/constants/constants.js b/constants/constants.js
index 372674a..8f6f966 100644
--- a/constants/constants.js
+++ b/constants/constants.js
@@ -61,8 +61,11 @@ const integrationServiceErrors = {
const incidentType = {
NOT_AN_INCIDENT: 1,
- UNLOCKED_INCIDENT: 2,
- UNSCHEDULED_INCIDENT: 3,
+ UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION: 2,
+ UNSCHEDULED_INCIDENT_BEFORE_RESERVATION: 3,
+ UNSCHEDULED_INCIDENT_AFTER_RESERVATION: 4,
+ UNLOCKED_INCIDENT_STANDALONE: 5,
+ UNSCHEDULED_INCIDENT_STANDALONE: 6,
};
const UI_TIMEZONE = process.env.UI_TIMEZONE || 'America/Los_Angeles';
diff --git a/migrations/20190705054346-add-door-lock-entry-column-to-the-unlockedIncidents-table.js b/migrations/20190705054346-add-door-lock-entry-column-to-the-unlockedIncidents-table.js
new file mode 100644
index 0000000..d3da894
--- /dev/null
+++ b/migrations/20190705054346-add-door-lock-entry-column-to-the-unlockedIncidents-table.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.addColumn('unlockedIncidents', 'unlockTimestamp', {
+ type: Sequelize.DATE,
+ after: 'bookingEnd'
+ });
+ },
+
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.removeColumn('unlockedIncidents', 'unlockTimestamp');
+ }
+};
diff --git a/migrations/20190705065019-add-door-lock-entry-columns-to-the-unscheduledIncidents-table.js b/migrations/20190705065019-add-door-lock-entry-columns-to-the-unscheduledIncidents-table.js
new file mode 100644
index 0000000..b829082
--- /dev/null
+++ b/migrations/20190705065019-add-door-lock-entry-columns-to-the-unscheduledIncidents-table.js
@@ -0,0 +1,27 @@
+'use strict';
+
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.sequelize.transaction((t) => {
+ return Promise.all([
+ queryInterface.addColumn('unscheduledIncidents', 'unlockTimestamp', {
+ type: Sequelize.DATE,
+ after: 'bookingEnd'
+ }),
+ queryInterface.addColumn('unscheduledIncidents', 'lockTimestamp', {
+ type: Sequelize.DATE,
+ after: 'unlockTimestamp'
+ })
+ ]);
+ });
+ },
+
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.sequelize.transaction((t) => {
+ return Promise.all([
+ queryInterface.removeColumn('unscheduledIncidents', 'lockTimestamp'),
+ queryInterface.removeColumn('unscheduledIncidents', 'unlockTimestamp')
+ ]);
+ });
+ }
+};
diff --git a/models/unlockedIncident.js b/models/unlockedIncident.js
index 89c8c34..d77c126 100644
--- a/models/unlockedIncident.js
+++ b/models/unlockedIncident.js
@@ -21,6 +21,7 @@ module.exports = (sequelize, DataTypes) => {
]
},
incidentLevelPrice: DataTypes.FLOAT,
+ unlockTimestamp: DataTypes.DATE,
}, {});
unlockedIncident.associate = function(models) {
// associations can be defined here
diff --git a/models/unscheduledIncident.js b/models/unscheduledIncident.js
index 1f781b9..c24ce79 100644
--- a/models/unscheduledIncident.js
+++ b/models/unscheduledIncident.js
@@ -17,6 +17,8 @@ module.exports = (sequelize, DataTypes) => {
chargePrice: DataTypes.FLOAT,
timeIntervalsToCharge: DataTypes.INTEGER,
totalChargeFee: DataTypes.FLOAT,
+ unlockTimestamp: DataTypes.DATE,
+ lockTimestamp: DataTypes.DATE,
}, {});
unscheduledIncident.associate = function(models) {
// associations can be defined here
diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js
index fdea6f7..b304797 100644
--- a/services/doorLock/doorLock.js
+++ b/services/doorLock/doorLock.js
@@ -188,8 +188,6 @@ const getUnlockEntryForReservation = (reservation, previousReservation) => {
return new Promise((resolve, reject) => {
const { memberId, resourceId } = reservation;
- const attributes = ['memberName', 'event', 'timestamp', 'resourceId'];
-
const previousReservationEndMoment = previousReservation && previousReservation.end ?
moment.utc(previousReservation.end) : null;
const reservationStartMoment = moment.utc(reservation.start);
@@ -214,7 +212,6 @@ const getUnlockEntryForReservation = (reservation, previousReservation) => {
const order = [['timestamp', 'DESC']];
db.doorLockEvent.findAll({
- attributes,
where: filters,
order,
})
diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js
index 0c70262..59b1e81 100644
--- a/services/integration/doorLockCharges.js
+++ b/services/integration/doorLockCharges.js
@@ -8,11 +8,11 @@ const { getUnlockEntryForReservation, getLockEntryForReservation, getEntriesBetw
const { getAllFinishedBookings, getFirstPreviousBooking, getFirstNextBooking } = require('../officeRnD/bookings');
const getSortedIncidentsForMember = (memberId) => {
- const attributes = ['bookingStart', 'incidentLevel', 'incidentLevelPrice'];
+ const attributes = ['bookingStart', 'incidentLevel', 'incidentLevelPrice', 'unlockTimestamp'];
const filters = {
memberId
};
- const order = [['bookingStart', 'DESC']];
+ const order = [['unlockTimestamp', 'DESC']];
return db.unlockedIncident.findAll({
attributes,
@@ -24,9 +24,8 @@ const getSortedIncidentsForMember = (memberId) => {
const insertUnscheduledIncidents = (incidents) => {
const asyncJobs = [];
incidents.forEach((incident) => {
- const { reservation, lockEntry, chargePrice, timeIntervalsToCharge, totalChargeFee } = incident;
- const { reservationId, memberId, resourceId, start, end } = reservation;
- const { timestamp, event } = lockEntry;
+ const { reservation, unlockTimestamp, lockTimestamp, memberId, resourceId, chargePrice, timeIntervalsToCharge, totalChargeFee } = incident;
+ const { reservationId, start, end } = reservation;
const incidentForDB = {
reservationId,
@@ -34,11 +33,13 @@ const insertUnscheduledIncidents = (incidents) => {
resourceId,
bookingStart: start,
bookingEnd: end,
- doorLockEventTimestamp: timestamp,
- doorLockEventType: event,
+ doorLockEventTimestamp: null,
+ doorLockEventType: null,
chargePrice,
timeIntervalsToCharge,
totalChargeFee,
+ unlockTimestamp,
+ lockTimestamp,
};
asyncJobs.push(db.unscheduledIncident.findOrCreate({
@@ -48,8 +49,8 @@ const insertUnscheduledIncidents = (incidents) => {
resourceId,
bookingStart: start,
bookingEnd: end,
- doorLockEventTimestamp: timestamp,
- doorLockEventType: event
+ unlockTimestamp,
+ lockTimestamp,
},
defaults: {...incidentForDB},
}));
@@ -61,36 +62,50 @@ const insertUnscheduledIncidents = (incidents) => {
const insertUnlockedIncidents = (incidents) => {
const asyncJobs = [];
incidents.forEach((incident) => {
- const { reservationId, memberId, resourceId, bookingStart, bookingEnd } = incident;
+ const { reservation, memberId, resourceId, unlockTimestamp, incidentLevel, incidentLevelPrice } = incident;
+ const { reservationId, start, end} = reservation;
+
+ const incidentForDB = {
+ reservationId,
+ memberId,
+ resourceId,
+ bookingStart: start,
+ bookingEnd: end,
+ unlockTimestamp,
+ incidentLevel,
+ incidentLevelPrice,
+ };
asyncJobs.push(db.unlockedIncident.findOrCreate({
where: {
reservationId,
memberId,
resourceId,
- bookingStart,
- bookingEnd,
+ bookingStart: start,
+ bookingEnd: end,
+ unlockTimestamp,
+ incidentLevel,
},
- defaults: {...incident},
+ defaults: {...incidentForDB},
}));
});
return Promise.all(asyncJobs);
};
-const setUnlockedIncidentsLevel = (incidentReservations) => {
+const setUnlockedIncidentsLevel = (incidents) => {
return new Promise ((resolve, reject) => {
- const sortingFunction = (reservationA, reservationB) => {
- const sortCondition = moment.utc(reservationA.start).isBefore(moment.utc(reservationB.start));
+ const sortingFunction = (incidentA, incidentB) => {
+ const sortCondition = moment.utc(incidentA.unlockTimestamp).isBefore(moment.utc(incidentB.unlockTimestamp));
return sortCondition ? -1 : 1;
};
- incidentReservations.sort(sortingFunction);
+ incidents.sort(sortingFunction);
const membersLastIncident = {};
- incidentReservations.forEach((reservation) => {
- membersLastIncident[reservation.memberId] = {
+ incidents.forEach((incident) => {
+ membersLastIncident[incident.memberId] = {
incidentLevel: null,
incidentTimestamp: null,
};
@@ -108,59 +123,58 @@ const setUnlockedIncidentsLevel = (incidentReservations) => {
if (lastIncident) {
membersLastIncident[lastIncident.memberId] = {
incidentLevel: lastIncident.incidentLevel,
- incidentTimestamp: lastIncident.bookingStart,
+ incidentTimestamp: lastIncident.unlockTimestamp,
}
}
});
const incidentsWithLevel = [];
- incidentReservations.forEach((reservation) => {
- const memberLastIncident = membersLastIncident[reservation.memberId];
+ incidents.forEach((incident) => {
+ const memberLastIncident = membersLastIncident[incident.memberId];
- const incident = {
- reservationId: reservation.reservationId,
- memberId: reservation.memberId,
- resourceId: reservation.resourceId,
- bookingStart: reservation.start,
- bookingEnd: reservation.end,
+ const formattedIncident = {
+ reservation: incident.reservation,
+ memberId: incident.memberId,
+ resourceId: incident.resourceId,
incidentLevel: undefined,
incidentLevelPrice: undefined,
+ unlockTimestamp: incident.unlockTimestamp,
};
if (!memberLastIncident.incidentLevel) {
- incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
+ 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(reservation.start).startOf('month');
+ const currentIncidentTime = moment.utc(incident.unlockTimestamp).startOf('month');
const timeDiff = Math.abs(lastIncidentTime.diff(currentIncidentTime, 'months'));
if (timeDiff >= (parseInt(process.env.UNLOCK_STREAK_REPAIR_AFTER) || 6)){
- incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
+ formattedIncident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
+ formattedIncident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
} else {
const lastIncidentLevelId = unlockedIncidentLevelsPrices[memberLastIncident.incidentLevel].id;
const maxId = 5;
if ((lastIncidentLevelId && (lastIncidentLevelId >= maxId)) || (timeDiff === 0)){
- incident.incidentLevel = memberLastIncident.incidentLevel;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices[incident.incidentLevel].price;
+ formattedIncident.incidentLevel = memberLastIncident.incidentLevel;
+ formattedIncident.incidentLevelPrice = unlockedIncidentLevelsPrices[formattedIncident.incidentLevel].price;
} else {
const nextId = lastIncidentLevelId + 1;
Object.keys(unlockedIncidentLevelsPrices).forEach((key) => {
if (unlockedIncidentLevelsPrices[key].id === nextId){
- incident.incidentLevel = unlockedIncidentLevelsPrices[key].title;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices[key].price
+ formattedIncident.incidentLevel = unlockedIncidentLevelsPrices[key].title;
+ formattedIncident.incidentLevelPrice = unlockedIncidentLevelsPrices[key].price
}
});
}
}
}
- memberLastIncident.incidentLevel = incident.incidentLevel;
- memberLastIncident.incidentTimestamp = incident.bookingStart;
+ memberLastIncident.incidentLevel = formattedIncident.incidentLevel;
+ memberLastIncident.incidentTimestamp = formattedIncident.unlockTimestamp;
- incidentsWithLevel.push(incident);
+ incidentsWithLevel.push(formattedIncident);
});
resolve(incidentsWithLevel);
@@ -318,17 +332,18 @@ const getIncidentData = (reservation) => {
if (!pairUnlockEntry){
pairUnlockEntry = entry;
}else{
- const virtualReservation = {
- reservationId: '',
- start: pairUnlockEntry.timestamp,
- end: pairUnlockEntry.timestamp,
- memberId: pairUnlockEntry.memberId,
- resourceId,
+ const emptyReservation = {
+ reservationId: null,
+ start: null,
+ end: null,
};
incidents.push({
- incidentType: incidentType.UNLOCKED_INCIDENT,
- reservation: virtualReservation,
+ incidentType: incidentType.UNLOCKED_INCIDENT_STANDALONE,
+ reservation: emptyReservation,
+ unlockTimestamp: pairUnlockEntry.timestamp,
+ memberId: pairUnlockEntry.memberId,
+ resourceId,
});
pairLockEntry = null;
@@ -338,12 +353,10 @@ const getIncidentData = (reservation) => {
case doorLockEvents.USER_LOCKED:
if (pairUnlockEntry && !pairLockEntry){
pairLockEntry = entry;
- const virtualReservation = {
- reservationId: '',
- start: pairUnlockEntry.timestamp,
- end: pairLockEntry.timestamp,
- memberId: pairUnlockEntry.memberId,
- resourceId,
+ const emptyReservation = {
+ reservationId: null,
+ start: null,
+ end: null,
};
const unlockMoment = moment.utc(pairUnlockEntry.timestamp);
const lockMoment = moment.utc(pairLockEntry.timestamp);
@@ -352,9 +365,12 @@ const getIncidentData = (reservation) => {
const totalChargeFee = timeIntervalsToCharge * UNSCHEDULED_CHARGE_PRICE;
if (timeIntervalsToCharge > 0){
incidents.push({
- incidentType: incidentType.UNSCHEDULED_INCIDENT,
- reservation: virtualReservation,
- doorLockEntry: pairLockEntry,
+ incidentType: incidentType.UNSCHEDULED_INCIDENT_STANDALONE,
+ reservation: emptyReservation,
+ unlockTimestamp: pairUnlockEntry.timestamp,
+ lockTimestamp: pairLockEntry.timestamp,
+ memberId: pairUnlockEntry.memberId,
+ resourceId,
chargePrice: UNSCHEDULED_CHARGE_PRICE,
timeIntervalsToCharge,
totalChargeFee,
@@ -384,9 +400,12 @@ const getIncidentData = (reservation) => {
// 1. Check if member entered before reservation start time
if (unlockEntry && chargeBefore && !previousReservationIsBackToBack) {
incidents.push({
- incidentType: incidentType.UNSCHEDULED_INCIDENT,
+ incidentType: incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION,
reservation,
- doorLockEntry: unlockEntry,
+ unlockTimestamp: unlockEntry.timestamp,
+ lockTimestamp: null,
+ memberId: reservation.memberId,
+ resourceId: reservation.resourceId,
chargePrice: UNSCHEDULED_CHARGE_PRICE,
timeIntervalsToCharge: timeIntervalsToChargeBefore,
totalChargeFee: totalChargeFeeBefore,
@@ -396,9 +415,12 @@ const getIncidentData = (reservation) => {
// 2. Check if member left after reservation end time
if (lockEntry && chargeAfter && !nextReservationIsBackToBack) {
incidents.push({
- incidentType: incidentType.UNSCHEDULED_INCIDENT,
+ incidentType: incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION,
reservation,
- doorLockEntry: lockEntry,
+ unlockTimestamp: null,
+ lockTimestamp: lockEntry.timestamp,
+ memberId: reservation.memberId,
+ resourceId: reservation.resourceId,
chargePrice: UNSCHEDULED_CHARGE_PRICE,
timeIntervalsToCharge: timeIntervalsToChargeAfter,
totalChargeFee: totalChargeFeeAfter,
@@ -407,10 +429,20 @@ const getIncidentData = (reservation) => {
// 3. Check if member forgot to lock the door
if (!lockEntry && !nextReservationIsBackToBack){
+ const emptyReservation = {
+ reservationId: null,
+ start: null,
+ end: null,
+ };
+
if (unlockEntry){
+
incidents.push({
- incidentType: incidentType.UNLOCKED_INCIDENT,
- reservation,
+ incidentType: incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION,
+ unlockTimestamp: unlockEntry.timestamp,
+ memberId: reservation.memberId,
+ resourceId: unlockEntry.resourceId,
+ reservation: reservation && reservation.dataValues ? reservation.dataValues : emptyReservation,
});
} else {
// No lock entry, no unlock entry and no reservation after this one
@@ -428,8 +460,11 @@ const getIncidentData = (reservation) => {
.then((lastEntry) => {
if (lastEntry && lastEntry.event === doorLockEvents.USER_UNLOCKED){
incidents.push({
- incidentType: incidentType.UNLOCKED_INCIDENT,
- reservation,
+ incidentType: incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION,
+ unlockTimestamp: lastEntry.timestamp,
+ memberId: lastEntry.memberId,
+ resourceId: lastEntry.resourceId,
+ reservation: reservation && reservation.dataValues ? reservation.dataValues : emptyReservation,
});
}
})
@@ -469,18 +504,14 @@ const calculateDoorLockCharges = () => {
console.log('Error checking incident : ', incident.error);
} else if (incident.incidentType) {
switch (incident.incidentType) {
- case incidentType.UNLOCKED_INCIDENT:
- unlockedIncidents.push(incident.reservation);
+ case incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
+ case incidentType.UNLOCKED_INCIDENT_STANDALONE:
+ unlockedIncidents.push(incident);
break;
- case incidentType.UNSCHEDULED_INCIDENT:
- const {reservation, doorLockEntry, chargePrice, timeIntervalsToCharge, totalChargeFee} = incident;
- unscheduledIncidents.push({
- reservation,
- lockEntry: doorLockEntry,
- chargePrice,
- timeIntervalsToCharge,
- totalChargeFee,
- });
+ case incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
+ case incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
+ case incidentType.UNSCHEDULED_INCIDENT_STANDALONE:
+ unscheduledIncidents.push(incident);
break;
}
}
diff --git a/services/integration/reports.js b/services/integration/reports.js
index eee8f68..ed05c6f 100644
--- a/services/integration/reports.js
+++ b/services/integration/reports.js
@@ -11,17 +11,34 @@ const { fetchAllMembers } = require('../officeRnD/members');
const { fetchOffices, fetchResources } = require('../officeRnD/resources');
const getUnlockedIncidents = (startDate, endDate, memberId) => {
- const attributes = ['id', 'memberId', 'resourceId', 'bookingStart', 'bookingEnd', 'incidentLevel', 'incidentLevelPrice'];
+ const attributes = ['id', 'reservationId', 'memberId', 'resourceId', 'bookingStart', 'bookingEnd', 'unlockTimestamp', 'incidentLevel', 'incidentLevelPrice'];
const filters = {};
if (startDate && endDate) {
- filters.bookingStart = {
- [Op.and]: {
- [Op.gte]: startDate.utc().toISOString(),
- [Op.lte]: endDate.utc().toISOString(),
+ const bookingStartCondition = {
+ bookingStart: {
+ [Op.and]: {
+ [Op.gte]: startDate.toISOString(),
+ [Op.lte]: endDate.toISOString(),
+ }
}
- }
+ };
+
+ const unlockTimestampCondition = {
+ unlockTimestamp: {
+ [Op.and]: {
+ [Op.gte]: startDate.toISOString(),
+ [Op.lte]: endDate.toISOString(),
+ }
+ }
+ };
+
+ const bookingStartOrUnlockTimestamp = {
+ [Op.or]: [bookingStartCondition, unlockTimestampCondition]
+ };
+
+ Object.assign(filters, bookingStartOrUnlockTimestamp);
}
if (memberId){
@@ -40,12 +57,13 @@ const getUnlockedIncidents = (startDate, endDate, memberId) => {
const getUnscheduledIncidents = (startDate, endDate, memberId) => {
const attributes = [
'id',
+ 'reservationId',
'memberId',
'resourceId',
'bookingStart',
'bookingEnd',
- 'doorLockEventTimestamp',
- 'doorLockEventType',
+ 'unlockTimestamp',
+ 'lockTimestamp',
'timeIntervalsToCharge',
'chargePrice',
'totalChargeFee'
@@ -54,14 +72,32 @@ const getUnscheduledIncidents = (startDate, endDate, memberId) => {
const filters = {};
if (startDate && endDate) {
- filters.bookingStart = {
- [Op.and]: {
- [Op.gte]: startDate.utc().toISOString(),
- [Op.lte]: endDate.utc().toISOString(),
+ const bookingStartCondition = {
+ bookingStart: {
+ [Op.and]: {
+ [Op.gte]: startDate.toISOString(),
+ [Op.lte]: endDate.toISOString(),
+ }
}
- }
+ };
+
+ const unlockTimestampCondition = {
+ unlockTimestamp: {
+ [Op.and]: {
+ [Op.gte]: startDate.toISOString(),
+ [Op.lte]: endDate.toISOString(),
+ }
+ }
+ };
+
+ const bookingStartOrUnlockTimestamp = {
+ [Op.or]: [bookingStartCondition, unlockTimestampCondition]
+ };
+
+ Object.assign(filters, bookingStartOrUnlockTimestamp);
}
+
if (memberId){
filters.memberId = memberId;
}
@@ -76,7 +112,12 @@ const getUnscheduledIncidents = (startDate, endDate, memberId) => {
};
const formatTime = (timestamp) => {
- return moment.tz(timestamp, UI_TIMEZONE).format('MM/DD/YYYY hh:mm a');
+ const momentObject = moment.tz(timestamp, UI_TIMEZONE);
+ if (momentObject.isValid()){
+ return momentObject.format('MM/DD/YYYY hh:mm a');
+ }else{
+ return null;
+ }
};
const getAllDoorLockIncidents = (dateRange, memberId) => {
@@ -114,6 +155,8 @@ const getAllDoorLockIncidents = (dateRange, memberId) => {
const allIncidents = [];
unlockedIncidents.forEach((unlockedIncident) => {
+ const incidentTypeNumber = unlockedIncident.reservationId ?
+ incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION : incidentType.UNLOCKED_INCIDENT_STANDALONE;
allIncidents.push({
incidentId: unlockedIncident.id,
memberId: unlockedIncident.memberId,
@@ -122,13 +165,24 @@ const getAllDoorLockIncidents = (dateRange, memberId) => {
officeName: officesMap[resourcesMap[unlockedIncident.resourceId].officeId].officeName,
bookingStart: formatTime(unlockedIncident.bookingStart),
bookingEnd: formatTime(unlockedIncident.bookingEnd),
- incidentType: incidentType.UNLOCKED_INCIDENT,
+ unlockTimestamp: formatTime(unlockedIncident.unlockTimestamp),
+ incidentType: incidentTypeNumber,
incidentLevel: unlockedIncident.incidentLevel,
incidentPrice: unlockedIncident.incidentLevelPrice,
});
});
unscheduledIncidents.forEach((unscheduledIncident) => {
+ let incidentTypeNumber;
+ if (unscheduledIncident.reservationId){
+ if (unscheduledIncident.unlockTimestamp && !unscheduledIncident.lockTimestamp){
+ incidentTypeNumber = incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION;
+ }else{
+ incidentTypeNumber = incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION;
+ }
+ }else{
+ incidentTypeNumber = incidentType.UNSCHEDULED_INCIDENT_STANDALONE;
+ }
allIncidents.push({
incidentId: unscheduledIncident.id,
memberId: unscheduledIncident.memberId,
@@ -137,7 +191,9 @@ const getAllDoorLockIncidents = (dateRange, memberId) => {
officeName: officesMap[resourcesMap[unscheduledIncident.resourceId].officeId].officeName,
bookingStart: formatTime(unscheduledIncident.bookingStart),
bookingEnd: formatTime(unscheduledIncident.bookingEnd),
- incidentType: incidentType.UNSCHEDULED_INCIDENT,
+ unlockTimestamp: formatTime(unscheduledIncident.unlockTimestamp),
+ lockTimestamp: formatTime(unscheduledIncident.lockTimestamp),
+ incidentType: incidentTypeNumber,
timeIntervalsToCharge: unscheduledIncident.timeIntervalsToCharge,
chargePrice: unscheduledIncident.chargePrice,
totalChargeFee: unscheduledIncident.totalChargeFee,