invoice integration

This commit is contained in:
Bilal Catic
2019-07-25 02:00:27 +02:00
parent aeffe86313
commit 71e034b6c2
12 changed files with 548 additions and 65 deletions

View File

@@ -23,6 +23,14 @@ class IncidentsReport extends Component {
const { pendingIncidents, incidents } = this.props;
const { dateRange } = this.state;
const membersMap = {};
if (incidents && Array.isArray(incidents)) {
incidents.forEach((incident) => {
membersMap[incident.memberId] = true;
});
}
const memberIds = Object.keys(membersMap) || [];
return (
<Container>
<MainMenu/>
@@ -31,6 +39,7 @@ class IncidentsReport extends Component {
<DateRangePicker buttonLabel="Show report" onDatesUpdate={this.onDatesUpdate} inlineButton />
<br/>
<GenerateFeesInORDButton
memberIds={memberIds}
disabled={pendingIncidents}
dateRange={dateRange}
/>

View File

@@ -13,32 +13,38 @@ const unlockedIncidentLevelsPrices = {
UNLOCKED_0: {
id: 0,
title: 'UNLOCKED_0',
price: parseInt(process.env.UNLOCK_0) || 0
price: parseInt(process.env.UNLOCK_0) || 0,
description: 'First month - warning',
},
UNLOCKED_1: {
id: 1,
title: 'UNLOCKED_1',
price: parseInt(process.env.UNLOCK_1) || 10
price: parseInt(process.env.UNLOCK_1) || 10,
description: 'Second month',
},
UNLOCKED_2: {
id: 2,
title: 'UNLOCKED_2',
price: parseInt(process.env.UNLOCK_2) || 20
price: parseInt(process.env.UNLOCK_2) || 20,
description: 'Third month',
},
UNLOCKED_3: {
id: 3,
title: 'UNLOCKED_3',
price: parseInt(process.env.UNLOCK_3) || 30
price: parseInt(process.env.UNLOCK_3) || 30,
description: 'Fourth month',
},
UNLOCKED_4: {
id: 4,
title: 'UNLOCKED_4',
price: parseInt(process.env.UNLOCK_4) || 40
price: parseInt(process.env.UNLOCK_4) || 40,
description: 'Fifth month',
},
UNLOCKED_5: {
id: 5,
title: 'UNLOCKED_5',
price: parseInt(process.env.UNLOCK_5) || 50
price: parseInt(process.env.UNLOCK_5) || 50,
description: 'Sixth month and onward',
}
};
const csvParserErrors = {
@@ -50,7 +56,10 @@ const csvParserErrors = {
};
const officeRnDAPIErrors = {
FAILED_TO_FETCH_MEMBERS: 'Failed to fetch members',
FAILED_TO_FETCH_BOOKINGS: 'Failed to fetch booking reservations'
FAILED_TO_FETCH_BOOKINGS: 'Failed to fetch booking reservations',
FAILED_TO_FETCH_FEES: 'Failed to fetch existing fees',
FAILED_TO_DELETE_FEES: 'Failed to delete fees in ORD',
FAILED_TO_ADD_FEES: 'Failed to add fees in ORD',
};
const integrationServiceErrors = {
FAILED_TO_SAVE_BOOKINGS: 'Failed to save booking reservations',
@@ -71,6 +80,16 @@ const incidentType = {
BOOKING_CANCELED_LATE: 9,
};
const incidentTypeExplanations = {};
incidentTypeExplanations[incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION] = 'Door left unlocked';
incidentTypeExplanations[incidentType.UNLOCKED_INCIDENT_STANDALONE] = 'Door left unlocked';
incidentTypeExplanations[incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION] = 'Room used before reservation started';
incidentTypeExplanations[incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION] = 'Room used after reservation started';
incidentTypeExplanations[incidentType.UNSCHEDULED_INCIDENT_STANDALONE] = 'Room used without reservation';
incidentTypeExplanations[incidentType.BOOKING_MOVED_TO_ANOTHER_DAY] = 'Reservation moved to another day';
incidentTypeExplanations[incidentType.BOOKING_SHORTENED] = 'Reservation shortened';
incidentTypeExplanations[incidentType.BOOKING_CANCELED_LATE] = 'A reservation canceled after the grace period';
const UI_TIMEZONE = process.env.UI_TIMEZONE || 'America/Los_Angeles';
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
@@ -94,6 +113,7 @@ module.exports = {
unlockedIncidentLevelsPrices,
integrationServiceErrors,
incidentType,
incidentTypeExplanations,
UI_TIMEZONE,
DEFAULT_DATE_FORMAT,
MAX_BACK_TO_BACK_DIFFERENCE,

View File

@@ -2,6 +2,9 @@
const { getMappingsFromDatabase, fetchOffices, fetchResources, saveNewMappingToDatabase } = require('../services/officeRnD/resources');
const { getAllIncidents } = require('../services/integration/reports');
const { getMembersFeesForDateRange } = require('../services/integration/invoiceIntegration');
const { deleteFeesFromORD, addFeesToORD } = require('../services/officeRnD/fees');
const { checkBookingChanges } = require('../services/integration/checkBookingChange');
const getKnownOfficeResourceMappings = (req, res) => {
const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources() ];
@@ -38,7 +41,7 @@ const getAllIncidentsController = (req, res) => {
endDate: req.params.endDate,
};
getAllIncidents(dateRange)
getAllIncidents(dateRange, [])
.then((incidents) => {
res.send(incidents);
})
@@ -55,7 +58,7 @@ const getMemberIncidents = (req, res) => {
endDate: req.params.endDate,
};
getAllIncidents(dateRange, memberId)
getAllIncidents(dateRange, [memberId])
.then((incidents) => {
res.send(incidents);
})
@@ -70,8 +73,39 @@ const addFees = (req, res) => {
const dateRange = req.body && req.body.dateRange ? req.body.dateRange : {startDate: null, endDate: null};
const { startDate, endDate } = dateRange;
if (startDate && endDate){
res.send();
if (startDate && endDate && Array.isArray(memberIds)){
checkBookingChanges()
.then(() => {
deleteFeesFromORD(dateRange, memberIds)
.then(() => {
// TODO: Change this to parallel execution
getMembersFeesForDateRange(dateRange, memberIds)
.then((allFees) => {
addFeesToORD(allFees)
.then((numberOfInsertedFees) => {
res.send({
status: 'ok',
numberOfInsertedFees
});
})
.catch((error) => {
console.log('Error adding fees');
res.status(500).send(error);
})
})
.catch((error) => {
console.log(error);
res.status(500).send(error);
})
})
.catch((error) => {
res.status(500).send(error);
});
})
.catch((error) => {
console.log('Error with updating booking reservations');
res.status(500).send(error);
})
}else{
res.status(400).send('Date range is missing');
}

View File

@@ -1,41 +1,6 @@
'use strict';
require('dotenv').config();
const { fetchAllBookings, bulkWriteReservationsWithChangesTracking } = require('../services/officeRnD/bookings');
const { checkBookingChanges } = require('../services/integration/checkBookingChange');
const { chargeBookingChanges } = require('../services/integration/bookingChangeCharges');
const { bulkWriteChanges } = require('../services/integration/bookingChangeLog');
const checkBookingChanges = () => {
fetchAllBookings()
.then((reservations) => {
bulkWriteReservationsWithChangesTracking(reservations)
.then((changes) => {
bulkWriteChanges(changes)
.then(() => {
chargeBookingChanges(changes)
.then(() => {
process.exit();
})
.catch((error) => {
console.log('Error creating charges ', error);
process.exit();
});
})
.catch((error) => {
console.log('Error bulk write booking reservation change log :', error);
process.exit();
});
})
.catch((error) => {
console.log('Error bulk write booking reservations :', error);
process.exit();
});
})
.catch((error) => {
console.log('Error fetching bookings from ORD ', error);
process.exit();
});
};
checkBookingChanges();
checkBookingChanges().then(() => process.exit()).catch(() => process.exit());

View File

@@ -97,7 +97,7 @@ const chargeBookingChanges = (changes) => {
incidents.push(incident);
}
}else{
const differenceFromCreation = reservationCreationTimestamp.diff(moment.utc(), 'minutes');
const differenceFromCreation = moment.utc().diff(reservationCreationTimestamp, 'minutes');
if (differenceFromCreation > ALLOWED_BOOKING_CANCELLATION_TIME){
const chargeFee = 2 * reservationHourlyRate * oldReservationLength * BOOKING_CHANGE_PERCENTAGE_CHARGE / 100;

View File

@@ -0,0 +1,53 @@
'use strict';
const Op = require('sequelize').Op;
const db = require('../../models/index');
const moment = require('moment-timezone');
const { DEFAULT_DATE_FORMAT, UI_TIMEZONE } = require('../../constants/constants');
const getActiveBookingsForMembersInDateRange = (dateRange, memberIds) => {
const startDate = moment.tz(dateRange.startDate, DEFAULT_DATE_FORMAT, UI_TIMEZONE).startOf('day');
const endDate = moment.tz(dateRange.endDate, DEFAULT_DATE_FORMAT, UI_TIMEZONE).endOf('day');
const attributes = [
'id',
'reservationId',
'memberId',
'officeId',
'resourceId',
'start',
'end',
'timezone',
'canceled',
'hourlyRate'
];
const filters = {
canceled: false,
};
if (startDate && endDate) {
filters.start = {
[Op.gte]: startDate.toISOString()
};
filters.end = {
[Op.lte]: endDate.toISOString(),
};
}
if (memberIds.length > 0){
filters.memberId = {
[Op.in]: memberIds
};
}
return db.bookingReservation.findAll({
attributes,
where: filters,
});
};
module.exports = {
getActiveBookingsForMembersInDateRange,
};

View File

@@ -0,0 +1,44 @@
'use strict';
const { fetchAllBookings, bulkWriteReservationsWithChangesTracking } = require('../officeRnD/bookings');
const { chargeBookingChanges } = require('./bookingChangeCharges');
const { bulkWriteChanges } = require('./bookingChangeLog');
const checkBookingChanges = () => {
return new Promise((resolve, reject) => {
fetchAllBookings()
.then((reservations) => {
bulkWriteReservationsWithChangesTracking(reservations)
.then((changes) => {
bulkWriteChanges(changes)
.then(() => {
chargeBookingChanges(changes)
.then(() => {
resolve(true);
})
.catch((error) => {
console.log('Error creating charges ', error);
reject(error);
});
})
.catch((error) => {
console.log('Error bulk write booking reservation change log :', error);
reject(error);
});
})
.catch((error) => {
console.log('Error bulk write booking reservations :', error);
reject(error);
});
})
.catch((error) => {
console.log('Error fetching bookings from ORD ', error);
reject(error);
});
});
};
module.exports = {
checkBookingChanges
};

View File

@@ -0,0 +1,240 @@
'use strict';
const moment = require('moment-timezone');
const { getAllIncidents } = require('./reports');
const { getActiveBookingsForMembersInDateRange } = require('./bookings');
const { DEFAULT_DATE_FORMAT, UI_TIMEZONE, incidentTypeExplanations, incidentType, unlockedIncidentLevelsPrices } = require('../../constants/constants');
const { getResourceMappings } = require('../officeRnD/resources');
const { fetchAllMembers } = require('../officeRnD/members');
const createFeeFromIncident = (incident) => {
const {
memberId,
officeId,
officeName,
resourceName,
oldResourceName,
newResourceName,
bookingStartRaw,
bookingEndRaw,
oldBookingStartRaw,
oldBookingEndRaw,
newBookingStartRaw,
newBookingEndRaw,
unlockTimestampRaw,
lockTimestampRaw,
incidentLevel,
timeIntervalsToCharge,
incidentPrice,
chargePrice,
totalChargeFee,
incidentTimestampRaw
} = incident;
const incidentTypeNumber = incident.incidentType;
const incidentExplanation = incidentTypeExplanations[incidentTypeNumber];
let price = 0;
let quantity = 0;
let priceExplanation = '';
let bookingTimeExplanation = '';
let incidentTimeExplanation = '';
let additionalIncidentExplanation = '';
let roomExplanation = '';
let dateExplanation = '';
const bookingStartMoment = moment.tz(bookingStartRaw, UI_TIMEZONE);
const bookingEndMoment = moment.tz(bookingEndRaw, UI_TIMEZONE);
const unlockMoment = moment.tz(unlockTimestampRaw, UI_TIMEZONE);
const lockMoment = moment.tz(lockTimestampRaw, UI_TIMEZONE);
const oldBookingStartMoment = moment.tz(oldBookingStartRaw, UI_TIMEZONE);
const oldBookingEndMoment = moment.tz(oldBookingEndRaw, UI_TIMEZONE);
const newBookingStartMoment = moment.tz(newBookingStartRaw, UI_TIMEZONE);
const newBookingEndMoment = moment.tz(newBookingEndRaw, UI_TIMEZONE);
const incidentTimestampMoment = moment.tz(incidentTimestampRaw, UI_TIMEZONE);
switch (incidentTypeNumber){
case incidentType.UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
roomExplanation = resourceName;
dateExplanation = bookingStartMoment.clone().startOf('day').format('MMM DD, YYYY');
bookingTimeExplanation = `${bookingStartMoment.clone().format('HH:mm a')} - ${bookingEndMoment.clone().format('HH:mm a')}`;
incidentTimeExplanation = `UNLOCK : ${unlockMoment.clone().format('HH:mm a')}`;
unlockedIncidentLevelsPrices[incidentLevel].description
additionalIncidentExplanation = unlockedIncidentLevelsPrices[incidentLevel].description;
price = incidentPrice;
quantity = 1;
priceExplanation = `$${price.toFixed(2)}, 1 x $${price.toFixed(2)}`;
break;
case incidentType.UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
roomExplanation = resourceName;
dateExplanation = bookingStartMoment.clone().startOf('day').format('MMM DD, YYYY');
bookingTimeExplanation = `${bookingStartMoment.clone().format('HH:mm a')} - ${bookingEndMoment.clone().format('HH:mm a')}`;
incidentTimeExplanation = `UNLOCK : ${unlockMoment.clone().format('HH:mm a')}`;
price = chargePrice;
quantity = timeIntervalsToCharge;
priceExplanation = `$${totalChargeFee.toFixed(2)}, ${quantity} x $${price.toFixed(2)}`;
break;
case incidentType.UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
roomExplanation = resourceName;
dateExplanation = bookingStartMoment.clone().startOf('day').format('MMM DD, YYYY');
bookingTimeExplanation = `${bookingStartMoment.clone().format('HH:mm a')} - ${bookingEndMoment.clone().format('HH:mm a')}`;
incidentTimeExplanation = `LOCK : ${lockMoment.clone().format('HH:mm a')}`;
price = chargePrice;
quantity = timeIntervalsToCharge;
priceExplanation = `$${totalChargeFee.toFixed(2)}, ${quantity} x $${price.toFixed(2)}`;
break;
case incidentType.UNLOCKED_INCIDENT_STANDALONE:
roomExplanation = resourceName;
dateExplanation = unlockMoment.clone().startOf('day').format('MMM DD, YYYY');
bookingTimeExplanation = `NO RESERVATION`;
incidentTimeExplanation = `UNLOCK : ${unlockMoment.clone().format('HH:mm a')}`;
additionalIncidentExplanation = unlockedIncidentLevelsPrices[incidentLevel].description;
price = incidentPrice;
quantity = 1;
priceExplanation = `$${price.toFixed(2)}, 1 x $${price.toFixed(2)}`;
break;
case incidentType.UNSCHEDULED_INCIDENT_STANDALONE:
roomExplanation = resourceName;
dateExplanation = unlockMoment.clone().startOf('day').format('MMM DD, YYYY');
bookingTimeExplanation = `NO RESERVATION`;
incidentTimeExplanation = `UNLOCK : ${unlockMoment.clone().format('HH:mm a')} LOCK : ${lockMoment.clone().format('HH:mm a')}`;
additionalIncidentExplanation = '';
price = chargePrice;
quantity = timeIntervalsToCharge;
priceExplanation = `$${totalChargeFee.toFixed(2)}, ${quantity} x $${price.toFixed(2)}`;
break;
case incidentType.BOOKING_MOVED_TO_ANOTHER_DAY:
if (oldResourceName !== newResourceName){
roomExplanation = `${oldResourceName} -> ${newResourceName}`;
}else{
roomExplanation = oldResourceName;
}
dateExplanation = `${oldBookingStartMoment.clone().format('MMM DD, YYYY')} -> ${newBookingStartMoment.clone().format('MMM DD, YYYY')}`;
bookingTimeExplanation = `(${oldBookingStartMoment.clone().format('HH:mm a')} - ${oldBookingEndMoment.clone().format('HH:mm a')}) -> (${newBookingStartMoment.clone().format('HH:mm a')} - ${newBookingEndMoment.clone().format('HH:mm a')})`;
incidentTimeExplanation = `MOVED ON : ${incidentTimestampMoment.clone().format(DEFAULT_DATE_FORMAT)}`;
price = totalChargeFee;
quantity = 1;
priceExplanation = `$${totalChargeFee.toFixed(2)}, 1 x $${price.toFixed(2)}`;
break;
case incidentType.BOOKING_SHORTENED:
if (oldResourceName !== newResourceName){
roomExplanation = `${oldResourceName} -> ${newResourceName}`;
}else{
roomExplanation = oldResourceName;
}
dateExplanation = `${oldBookingStartMoment.clone().format('MMM DD, YYYY')}`;
bookingTimeExplanation = `(${oldBookingStartMoment.clone().format('HH:mm a')} - ${oldBookingEndMoment.clone().format('HH:mm a')}) -> (${newBookingStartMoment.clone().format('HH:mm a')} - ${newBookingEndMoment.clone().format('HH:mm a')})`;
incidentTimeExplanation = `SHORTENED ON : ${incidentTimestampMoment.clone().format(DEFAULT_DATE_FORMAT)}`;
price = totalChargeFee;
quantity = 1;
priceExplanation = `$${totalChargeFee.toFixed(2)}, 1 x $${price.toFixed(2)}`;
break;
case incidentType.BOOKING_CANCELED_LATE:
roomExplanation = oldResourceName;
dateExplanation = `${oldBookingStartMoment.clone().format('MMM DD, YYYY')}`;
bookingTimeExplanation = `${oldBookingStartMoment.clone().format('HH:mm a')} - ${oldBookingEndMoment.clone().format('HH:mm a')}`;
incidentTimeExplanation = `CANCELED ON : ${incidentTimestampMoment.clone().format(DEFAULT_DATE_FORMAT)}`;
price = totalChargeFee;
quantity = 1;
priceExplanation = `$${totalChargeFee.toFixed(2)}, 1 x $${price.toFixed(2)}`;
break;
}
const formattedName = `[INCIDENT FEES] ${officeName}, ${roomExplanation}, ${dateExplanation}, ${bookingTimeExplanation}, ${incidentTimeExplanation}, ${incidentExplanation}, ${additionalIncidentExplanation} ${additionalIncidentExplanation !== '' ? ',' : ''} ${priceExplanation}`;
return {
name: formattedName,
price,
quantity,
date: moment.tz(incidentTimestampRaw, UI_TIMEZONE).startOf('day').toISOString(),
member: memberId,
team: null,
office: officeId,
isPersonal: false,
}
};
const createFeeFromBooking = (booking, resourceMappings) => {
const { officeId, resourceId, memberId, start, end, timezone, hourlyRate } = booking;
const { officesMap, resourcesMap } = resourceMappings;
const startMoment = moment.tz(start, DEFAULT_DATE_FORMAT, timezone);
const endMoment = moment.tz(end, DEFAULT_DATE_FORMAT, timezone);
const reservationLength = endMoment.diff(startMoment, 'hours', true);
const officeName = officesMap[officeId].officeName || 'Unknown';
const resourceName = resourcesMap[resourceId].resourceName || 'Unknown';
const totalCost = (hourlyRate*reservationLength).toFixed(2);
const formattedDate = startMoment.clone().startOf('day').format('MMM DD, YYYY');
const formattedStartTime = startMoment.format('HH:mm a');
const formattedEndTime = endMoment.format('HH:mm a');
const formattedName = `[BOOKING FEES] ${officeName}, ${resourceName}, $${totalCost}, ${reservationLength.toFixed(2)} x $${hourlyRate.toFixed(2)}, ${formattedDate} [${formattedStartTime} - ${formattedEndTime}]`;
return {
name: formattedName,
price: hourlyRate,
quantity: reservationLength,
date: startMoment.startOf('day').toISOString(),
member: memberId,
team: null,
office: officeId,
isPersonal: false,
}
};
const getMembersFeesForDateRange = (dateRange, memberIds) => {
return new Promise((resolve, reject) => {
const collectData = [getAllIncidents(dateRange, memberIds), getActiveBookingsForMembersInDateRange(dateRange, memberIds), getResourceMappings(), fetchAllMembers()];
Promise.all(collectData)
.then((result) => {
const allIncidents = result[0];
const allActiveBookings = result[1];
const resourceMappings = result[2];
const membersList = result[3];
const memberIdTeamMappings = {};
membersList.forEach((member) => {
memberIdTeamMappings[member.memberId] = member.teamId;
});
const allFees = [];
allIncidents.forEach((incident) => allFees.push(createFeeFromIncident(incident)));
allActiveBookings.forEach((booking) => allFees.push(createFeeFromBooking(booking, resourceMappings)));
allFees.forEach((fee) => {
fee.team = memberIdTeamMappings[fee.member] || null;
});
resolve(allFees);
})
.catch((error) => {
console.log(error);
reject(error);
});
});
};
module.exports = {
getMembersFeesForDateRange,
};

View File

@@ -10,7 +10,7 @@ const { incidentType, UI_TIMEZONE, DEFAULT_DATE_FORMAT, integrationServiceErrors
const { fetchAllMembers } = require('../officeRnD/members');
const { fetchOffices, fetchResources } = require('../officeRnD/resources');
const getUnlockedIncidents = (startDate, endDate, memberId) => {
const getUnlockedIncidents = (startDate, endDate, memberIds) => {
const attributes = ['id', 'reservationId', 'memberId', 'resourceId', 'bookingStart', 'bookingEnd', 'unlockTimestamp', 'incidentLevel', 'incidentLevelPrice'];
const filters = {};
@@ -41,8 +41,10 @@ const getUnlockedIncidents = (startDate, endDate, memberId) => {
Object.assign(filters, bookingStartOrUnlockTimestamp);
}
if (memberId){
filters.memberId = memberId;
if (memberIds.length > 0){
filters.memberId = {
[Op.in]: memberIds
};
}
return db.unlockedIncident.findAll({
@@ -54,7 +56,7 @@ const getUnlockedIncidents = (startDate, endDate, memberId) => {
});
};
const getUnscheduledIncidents = (startDate, endDate, memberId) => {
const getUnscheduledIncidents = (startDate, endDate, memberIds) => {
const attributes = [
'id',
'reservationId',
@@ -98,8 +100,10 @@ const getUnscheduledIncidents = (startDate, endDate, memberId) => {
}
if (memberId){
filters.memberId = memberId;
if (memberIds.length > 0){
filters.memberId = {
[Op.in]: memberIds
};
}
return db.unscheduledIncident.findAll({
@@ -111,7 +115,7 @@ const getUnscheduledIncidents = (startDate, endDate, memberId) => {
});
};
const getBookingChangeIncidents = (startDate, endDate, memberId) => {
const getBookingChangeIncidents = (startDate, endDate, memberIds) => {
const attributes = [
'id',
'reservationId',
@@ -138,8 +142,10 @@ const getBookingChangeIncidents = (startDate, endDate, memberId) => {
}
}
if (memberId){
filters.memberId = memberId;
if (memberIds.length > 0){
filters.memberId = {
[Op.in]: memberIds
};
}
return db.bookingChangeIncident.findAll({
@@ -160,7 +166,7 @@ const formatTime = (timestamp) => {
}
};
const getAllIncidents = (dateRange, memberId) => {
const getAllIncidents = (dateRange, memberIds) => {
return new Promise ((resolve, reject) => {
let startDate, endDate;
@@ -178,9 +184,9 @@ const getAllIncidents = (dateRange, memberId) => {
fetchAllMembers(),
fetchOffices(),
fetchResources(),
getUnlockedIncidents(startDate, endDate, memberId),
getUnscheduledIncidents(startDate, endDate, memberId),
getBookingChangeIncidents(startDate, endDate, memberId)
getUnlockedIncidents(startDate, endDate, memberIds),
getUnscheduledIncidents(startDate, endDate, memberIds),
getBookingChangeIncidents(startDate, endDate, memberIds)
];
Promise.all(dataFetchJobs)
@@ -210,10 +216,14 @@ const getAllIncidents = (dateRange, memberId) => {
memberId: unlockedIncident.memberId,
memberName: membersMap[unlockedIncident.memberId].name,
resourceName: resourcesMap[unlockedIncident.resourceId].resourceName,
officeId: resourcesMap[unlockedIncident.resourceId].officeId,
officeName: officesMap[resourcesMap[unlockedIncident.resourceId].officeId].officeName,
bookingStart: formatTime(unlockedIncident.bookingStart),
bookingEnd: formatTime(unlockedIncident.bookingEnd),
bookingStartRaw: unlockedIncident.bookingStart,
bookingEndRaw: unlockedIncident.bookingEnd,
unlockTimestamp: formatTime(unlockedIncident.unlockTimestamp),
unlockTimestampRaw: unlockedIncident.unlockTimestamp,
incidentType: incidentTypeNumber,
incidentLevel: unlockedIncident.incidentLevel,
incidentPrice: unlockedIncident.incidentLevelPrice,
@@ -236,11 +246,16 @@ const getAllIncidents = (dateRange, memberId) => {
memberId: unscheduledIncident.memberId,
memberName: membersMap[unscheduledIncident.memberId].name,
resourceName: resourcesMap[unscheduledIncident.resourceId].resourceName,
officeId: resourcesMap[unscheduledIncident.resourceId].officeId,
officeName: officesMap[resourcesMap[unscheduledIncident.resourceId].officeId].officeName,
bookingStart: formatTime(unscheduledIncident.bookingStart),
bookingEnd: formatTime(unscheduledIncident.bookingEnd),
bookingStartRaw: unscheduledIncident.bookingStart,
bookingEndRaw: unscheduledIncident.bookingEnd,
unlockTimestamp: formatTime(unscheduledIncident.unlockTimestamp),
lockTimestamp: formatTime(unscheduledIncident.lockTimestamp),
unlockTimestampRaw: unscheduledIncident.unlockTimestamp,
lockTimestampRaw: unscheduledIncident.lockTimestamp,
incidentType: incidentTypeNumber,
timeIntervalsToCharge: unscheduledIncident.timeIntervalsToCharge,
chargePrice: unscheduledIncident.chargePrice,
@@ -267,21 +282,28 @@ const getAllIncidents = (dateRange, memberId) => {
const newResource = newResourceId ? resourcesMap[newResourceId] : null;
const oldResourceName = oldResource.resourceName;
const newResourceName = newResource ? newResource.resourceName : null;
const officeName = officesMap[oldResource.officeId].officeName;
const officeId = oldResource.officeId;
const officeName = officesMap[officeId].officeName;
allIncidents.push({
incidentId: id,
memberId,
memberName,
oldResourceName,
newResourceName,
officeId,
officeName,
oldBookingStart: formatTime(oldBookingStart),
oldBookingEnd: formatTime(oldBookingEnd),
newBookingStart: formatTime(newBookingStart),
newBookingEnd: formatTime(newBookingEnd),
oldBookingStartRaw: oldBookingStart,
oldBookingEndRaw: oldBookingEnd,
newBookingStartRaw: newBookingStart,
newBookingEndRaw: newBookingEnd,
incidentType,
totalChargeFee: chargeFee,
incidentTimestamp: formatTime(createdAt),
incidentTimestampRaw: createdAt,
});
});
@@ -292,7 +314,5 @@ const getAllIncidents = (dateRange, memberId) => {
};
module.exports = {
getUnlockedIncidents,
getUnscheduledIncidents,
getAllIncidents,
};

View File

@@ -0,0 +1,72 @@
'use strict';
const moment = require('moment-timezone');
const { API } = require('../../helpers/api');
const { officeRnDAPIErrors, DEFAULT_DATE_FORMAT } = require('../../constants/constants');
const deleteFeesFromORD = (dateRange, memberIds) => {
return new Promise((resolve, reject) => {
const startDate = moment.utc(dateRange.startDate, DEFAULT_DATE_FORMAT).startOf('day');
const endDate = moment.utc(dateRange.endDate, DEFAULT_DATE_FORMAT).endOf('day');
API.get('fees')
.then((response) => {
const fetchedFees = response.data ? response.data : [];
const memberIdsMap = {};
memberIds.forEach((memberId) => {
memberIdsMap[memberId] = true;
});
const deleteRequests = [];
const sendDeleteRequestPromise = (feeId) => {
return new Promise((resolve, reject) => {
API.delete(`fees/${feeId}`)
.then(() => resolve(true))
.catch(() => resolve(false));
});
};
fetchedFees.forEach((fee) => {
const { member, date } = fee;
const feeId = fee['_id'];
if (memberIdsMap[member]) {
const isDateInDateRange = startDate.isSameOrBefore(date) && endDate.isSameOrAfter(date);
if (memberIdsMap[member] && isDateInDateRange) {
deleteRequests.push(sendDeleteRequestPromise(feeId));
}
}
});
Promise.all(deleteRequests)
.then(() => {
resolve(true);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
console.log(error);
reject(officeRnDAPIErrors.FAILED_TO_FETCH_FEES);
});
});
};
const addFeesToORD = (allFees) => {
return new Promise((resolve, reject) => {
API.post('/fees', allFees)
.catch((error) => {
console.log('==== ERROR ====');
console.log(error);
});
resolve(allFees.length);
});
};
module.exports = {
deleteFeesFromORD,
addFeesToORD
};

View File

@@ -12,6 +12,7 @@ const fetchAllMembers = () => {
cleanedResult.push({
name: member.name,
memberId: member['_id'],
teamId: member.team,
});
});
cleanedResult.sort((member1, member2) => (member1.name > member2.name) ? 1 : -1 );

View File

@@ -45,6 +45,30 @@ const fetchResources = () => {
});
};
const getResourceMappings = () => {
return new Promise((resolve, reject) => {
const fetchJobs = [fetchOffices(), fetchResources()];
Promise.all(fetchJobs)
.then((mappings) => {
const offices = mappings[0];
const resources = mappings[1];
const officesMap = {};
const resourcesMap = {};
offices.forEach((office) => officesMap[office.officeId] = office);
resources.forEach((resource) => resourcesMap[resource.resourceId] = resource);
resolve({
officesMap,
resourcesMap,
});
})
.catch((error) => reject(error));
});
};
const getMappingsFromDatabase = () => {
return db.officeResourceMapping.findAll();
};
@@ -57,5 +81,6 @@ module.exports = {
getMappingsFromDatabase,
fetchOffices,
fetchResources,
getResourceMappings,
saveNewMappingToDatabase,
};