Fixed bookings and timezone problems
This commit is contained in:
@@ -73,17 +73,25 @@ class DateRangePicker extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { startDate, endDate, error, startDateLabel, endDateLabel } = this.state;
|
const { startDate, endDate, error, startDateLabel, endDateLabel } = this.state;
|
||||||
|
const { inlineButton } = this.props;
|
||||||
|
|
||||||
const buttonLabel = this.props.buttonLabel || 'Save';
|
const buttonLabel = this.props.buttonLabel || 'Save';
|
||||||
|
|
||||||
const startDateValue = startDate.format(defaultDateFormat);
|
const startDateValue = startDate.format(defaultDateFormat);
|
||||||
const endDateValue = endDate.format(defaultDateFormat);
|
const endDateValue = endDate.format(defaultDateFormat);
|
||||||
|
|
||||||
|
const buttonRender = (
|
||||||
|
<Grid.Column width={inlineButton ? 1 : null}>
|
||||||
|
{ inlineButton && <label>{'\u00A0'}</label> }
|
||||||
|
<Form.Button onClick={this.onButtonClick.bind(this)}>{buttonLabel}</Form.Button>
|
||||||
|
</Grid.Column>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<Grid columns="equal">
|
<Grid stackable columns={inlineButton ? null : 'equal'}>
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Column>
|
<Grid.Column width={inlineButton ? 7 : null}>
|
||||||
<label>{startDateLabel}</label>
|
<label>{startDateLabel}</label>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
fluid
|
fluid
|
||||||
@@ -93,7 +101,7 @@ class DateRangePicker extends Component {
|
|||||||
onChange={this.onStartDateChange.bind(this)}
|
onChange={this.onStartDateChange.bind(this)}
|
||||||
/>
|
/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
<Grid.Column>
|
<Grid.Column width={inlineButton ? 6 : null}>
|
||||||
<label>{endDateLabel}</label>
|
<label>{endDateLabel}</label>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
fluid
|
fluid
|
||||||
@@ -103,6 +111,9 @@ class DateRangePicker extends Component {
|
|||||||
onChange={this.onEndDateChange.bind(this)}
|
onChange={this.onEndDateChange.bind(this)}
|
||||||
/>
|
/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
{
|
||||||
|
inlineButton && buttonRender
|
||||||
|
}
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
{ error &&
|
{ error &&
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
@@ -114,11 +125,12 @@ class DateRangePicker extends Component {
|
|||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
}
|
}
|
||||||
<Grid.Row>
|
{
|
||||||
<Grid.Column>
|
!inlineButton &&
|
||||||
<Form.Button onClick={this.onButtonClick.bind(this)}>{buttonLabel}</Form.Button>
|
<Grid.Row>
|
||||||
</Grid.Column>
|
{buttonRender}
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
|
}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ import { mainMenuItems } from '../../constants/menuItems';
|
|||||||
const MainMenu = () =>
|
const MainMenu = () =>
|
||||||
(<Menu>
|
(<Menu>
|
||||||
{
|
{
|
||||||
mainMenuItems.map(mainMenuItem =>
|
mainMenuItems.map(mainMenuItem => {
|
||||||
<Menu.Item key={mainMenuItem.id}
|
if (!mainMenuItem.showInMenu){
|
||||||
as={NavLink}
|
return null;
|
||||||
to={mainMenuItem.url}
|
} else {
|
||||||
exact
|
return (
|
||||||
>
|
<Menu.Item key={mainMenuItem.id}
|
||||||
{mainMenuItem.title}
|
as={NavLink}
|
||||||
</Menu.Item>)
|
to={mainMenuItem.url}
|
||||||
|
exact
|
||||||
|
>
|
||||||
|
{mainMenuItem.title}
|
||||||
|
</Menu.Item>)
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
</Menu>);
|
</Menu>);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { Loader } from 'semantic-ui-react';
|
import { Loader } from 'semantic-ui-react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import 'react-table/react-table.css';
|
import 'react-table/react-table.css';
|
||||||
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import {incidentsReportHeaderTitles} from '../../constants/menuItems';
|
import {incidentsReportHeaderTitles} from '../../constants/menuItems';
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +14,7 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
const MemberIncidentsTable = props => {
|
const MemberIncidentsTable = props => {
|
||||||
const { loading, title } = props;
|
const { loading, title, openMemberSummaryOnMemberClick } = props;
|
||||||
const incidents = props.incidents ? props.incidents : [];
|
const incidents = props.incidents ? props.incidents : [];
|
||||||
|
|
||||||
const columns = [];
|
const columns = [];
|
||||||
@@ -34,9 +35,15 @@ const MemberIncidentsTable = props => {
|
|||||||
Header: incidentsReportHeaderTitles[header],
|
Header: incidentsReportHeaderTitles[header],
|
||||||
accessor: header,
|
accessor: header,
|
||||||
Cell: props => {
|
Cell: props => {
|
||||||
let cellValue;
|
let cellValue = '';
|
||||||
|
let urlValue = undefined;
|
||||||
|
|
||||||
switch (props.column.id) {
|
switch (props.column.id) {
|
||||||
|
case 'memberName':
|
||||||
|
const memberId = props.row['_original'].memberId;
|
||||||
|
urlValue = `/practice-summary-report/${memberId}`;
|
||||||
|
cellValue = props.value;
|
||||||
|
break;
|
||||||
case 'incidentType':
|
case 'incidentType':
|
||||||
cellValue = incidentDescriptions[props.value];
|
cellValue = incidentDescriptions[props.value];
|
||||||
break;
|
break;
|
||||||
@@ -68,7 +75,17 @@ const MemberIncidentsTable = props => {
|
|||||||
cellValue = props.value;
|
cellValue = props.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div style={{ textAlign: columnContentsAlignment }}>{cellValue}</div>
|
if (openMemberSummaryOnMemberClick && urlValue){
|
||||||
|
return <NavLink to={urlValue}>{cellValue}</NavLink>
|
||||||
|
}else{
|
||||||
|
return <div style={{ textAlign: columnContentsAlignment }}>{cellValue}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// return <NavLink to={urlValue}>
|
||||||
|
// <div>{cellValue}</div>
|
||||||
|
// </NavLink>
|
||||||
|
|
||||||
|
// return <div style={{ textAlign: columnContentsAlignment }}><a href={'www.gogole.com'} >{cellValue}</a></div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,24 +6,34 @@ import PracticeSummaryReport from '../scenes/PracticeSummaryReport';
|
|||||||
export const mainMenuItems = [
|
export const mainMenuItems = [
|
||||||
{
|
{
|
||||||
id: 'home',
|
id: 'home',
|
||||||
|
showInMenu: true,
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
url: '/',
|
url: '/',
|
||||||
component: Home,
|
component: Home,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'practiceSummaryReport',
|
id: 'practiceSummaryReport',
|
||||||
|
showInMenu: true,
|
||||||
title: 'Practice Summary Report',
|
title: 'Practice Summary Report',
|
||||||
url: '/practice-summary-report',
|
url: '/practice-summary-report',
|
||||||
component: PracticeSummaryReport,
|
component: PracticeSummaryReport,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'practiceSummaryReportWithMemberId',
|
||||||
|
showInMenu: false,
|
||||||
|
url: '/practice-summary-report/:memberId',
|
||||||
|
component: PracticeSummaryReport,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'report',
|
id: 'report',
|
||||||
|
showInMenu: true,
|
||||||
title: 'Incidents Report',
|
title: 'Incidents Report',
|
||||||
url: '/incidents-report',
|
url: '/incidents-report',
|
||||||
component: IncidentsReport,
|
component: IncidentsReport,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'uploadDLockData',
|
id: 'uploadDLockData',
|
||||||
|
showInMenu: true,
|
||||||
title: 'DLock',
|
title: 'DLock',
|
||||||
url: '/dlock',
|
url: '/dlock',
|
||||||
component: UploadDLockData,
|
component: UploadDLockData,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class IncidentsReport extends Component {
|
|||||||
<hr/>
|
<hr/>
|
||||||
<DateRangePicker buttonLabel="Show report" onDatesUpdate={this.onDatesUpdate.bind(this)} />
|
<DateRangePicker buttonLabel="Show report" onDatesUpdate={this.onDatesUpdate.bind(this)} />
|
||||||
<br/>
|
<br/>
|
||||||
<MemberIncidentsTable loading={pendingIncidents} incidents={incidents} />
|
<MemberIncidentsTable loading={pendingIncidents} incidents={incidents} openMemberSummaryOnMemberClick />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ const MemberSummary = props => {
|
|||||||
let totalUnlockedFees = 0;
|
let totalUnlockedFees = 0;
|
||||||
|
|
||||||
incidents.forEach((incident) => {
|
incidents.forEach((incident) => {
|
||||||
console.log(incident);
|
|
||||||
switch (incident.incidentType) {
|
switch (incident.incidentType) {
|
||||||
case UNSCHEDULED_INCIDENT:
|
case UNSCHEDULED_INCIDENT:
|
||||||
totalUnscheduledFees += parseFloat(incident.totalChargeFee);
|
totalUnscheduledFees += parseFloat(incident.totalChargeFee);
|
||||||
@@ -19,6 +18,8 @@ const MemberSummary = props => {
|
|||||||
case UNLOCKED_INCIDENT:
|
case UNLOCKED_INCIDENT:
|
||||||
totalUnlockedFees += parseFloat(incident.incidentPrice);
|
totalUnlockedFees += parseFloat(incident.incidentPrice);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,5 +66,4 @@ const MemberSummary = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default MemberSummary;
|
export default MemberSummary;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class PracticeSummaryReport extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
dateRange: null,
|
dateRange: null,
|
||||||
memberId: null,
|
memberId: props.match.params.memberId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class PracticeSummaryReport extends Component {
|
|||||||
<MemberSelector onMemberSelect={this.onMemberSelectionUpdate.bind(this)} />
|
<MemberSelector onMemberSelect={this.onMemberSelectionUpdate.bind(this)} />
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<DateRangePicker onDatesUpdate={this.onDateRangeUpdate.bind(this)}/>
|
<DateRangePicker inlineButton onDatesUpdate={this.onDateRangeUpdate.bind(this)}/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
Reference in New Issue
Block a user