From 97b74203c986bfe44ed8ee4c7287ec1a3e0785fe Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 001/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 50a75258d..265346d73 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -64,10 +64,10 @@ const userProfileController = function (UserProfile) { res.status(200).send(getData); return; } - + UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', ) .sort({ lastName: 1, From 8000203e08ee6571c305c8df4096ca104dfb6a7f Mon Sep 17 00:00:00 2001 From: Chuehleo <122568562+Chuehleo@users.noreply.github.com> Date: Wed, 19 Jul 2023 22:35:34 -0700 Subject: [PATCH 002/129] Update teamController.js --- src/controllers/teamController.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/controllers/teamController.js b/src/controllers/teamController.js index 205ee4f0e..758b65831 100644 --- a/src/controllers/teamController.js +++ b/src/controllers/teamController.js @@ -18,14 +18,24 @@ const teamcontroller = function (Team) { }; const postTeam = function (req, res) { const team = new Team(); - team.teamName = req.body.teamName; - team.isACtive = req.body.isActive; + team.isActive = req.body.isActive; team.createdDatetime = Date.now(); team.modifiedDatetime = Date.now(); - team.save() - .then(results => res.send(results).status(200)) + // Check if a team with the same name already exists + Team.findOne({ teamName: team.teamName }) + .then((existingTeam) => { + if (existingTeam) { + // If a team with the same name exists, return an error + res.status(400).send({ error: 'A team with this name already exists' }); + } else { + // If no team with the same name exists, save the new team + team.save() + .then(results => res.send(results).status(200)) + .catch(error => res.send(error).status(404)); + } + }) .catch(error => res.send(error).status(404)); }; const deleteTeam = function (req, res) { From 95015c4ebb613ed81f152a2c67d171f9713eac8d Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 003/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 5f36e91a2..8a866401a 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -62,10 +62,10 @@ const userProfileController = function (UserProfile) { res.status(200).send(getData); return; } - + UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', ) .sort({ lastName: 1, From 9600e9dee200bf32bcc52c5757adc53a231a94f2 Mon Sep 17 00:00:00 2001 From: xaanders Date: Thu, 14 Sep 2023 16:40:41 -0400 Subject: [PATCH 004/129] feat: adding location object to user profile schema --- src/models/userProfile.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/models/userProfile.js b/src/models/userProfile.js index fbb59e6e3..faeef3a86 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -7,7 +7,7 @@ const bcrypt = require('bcryptjs'); const SALT_ROUNDS = 10; const nextDay = new Date(); -nextDay.setDate(nextDay.getDate()+1); +nextDay.setDate(nextDay.getDate() + 1); const userProfileSchema = new Schema({ password: { @@ -81,7 +81,16 @@ const userProfileSchema = new Schema({ infringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], - location: { type: String, default: '' }, + location: { + userProvided: { type: String, default: '' }, + coords: { + lat: { type: Number, default: '' }, + lng: { type: Number, default: '' }, + }, + country: { type: String, default: '' }, + city: { type: String, default: '' } + + }, oldInfringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], @@ -153,12 +162,12 @@ const userProfileSchema = new Schema({ isVisible: { type: Boolean, default: false }, weeklySummaryOption: { type: String }, bioPosted: { type: String, default: 'default' }, - isFirstTimelog: { type: Boolean, default: true}, + isFirstTimelog: { type: Boolean, default: true }, infoCollections: [ { - areaName: { type: String }, + areaName: { type: String }, areaContent: { type: String }, - }], + }], }); userProfileSchema.pre('save', function (next) { From 2d1e82c56bce45d24d31eafab3eca2297b03dd2c Mon Sep 17 00:00:00 2001 From: xaanders Date: Sat, 16 Sep 2023 12:02:02 -0400 Subject: [PATCH 005/129] fix: editing email message after new user creating --- src/controllers/profileInitialSetupController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index d5c955d88..c35230dbe 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -59,7 +59,7 @@ function informManagerMessage(user) { Location: - ${user.location} + ${user.location.userProvided}, ${user.location.country}

Please check the details provided by the user. If any errors were made, kindly ask them to correct the information accordingly.

@@ -184,8 +184,8 @@ const profileInitialSetupController = function (ProfileInitialSetupToken, userPr newUser.bioPosted = 'default'; newUser.privacySettings.email = req.body.privacySettings.email newUser.privacySettings.phoneNumber = req.body.privacySettings.phoneNumber - const savedUser = await newUser.save(); + const savedUser = await newUser.save(); emailSender( process.env.MANAGER_EMAIL, 'New User Profile Created', From 9e74615fde0dcc10c98f419361bb69755f917a51 Mon Sep 17 00:00:00 2001 From: xaanders Date: Fri, 22 Sep 2023 19:58:43 -0400 Subject: [PATCH 006/129] feat: adding a initial functionality for adding new people to the map(maplocation controller, maplocation router, maplocation model) --- src/controllers/mapLocationsController.js | 69 +++++++++++++++++++++++ src/models/mapLocation.js | 42 ++++++++++++++ src/routes/mapLocationsRouter.js | 18 ++++++ src/startup/routes.js | 4 ++ 4 files changed, 133 insertions(+) create mode 100644 src/controllers/mapLocationsController.js create mode 100644 src/models/mapLocation.js create mode 100644 src/routes/mapLocationsRouter.js diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js new file mode 100644 index 000000000..83526998d --- /dev/null +++ b/src/controllers/mapLocationsController.js @@ -0,0 +1,69 @@ +const mongoose = require('mongoose'); +const mapLocation = require('../models/mapLocation'); +const { hasPermission } = require('../utilities/permissions'); + +const mapLocationsController = function () { + const getAllLocations = function (req, res) { + console.log('controller:') + console.log(req.body) + + mapLocation.find({}) + .then(results => res.send(results).status(200)) + .catch(error => res.send(error).status(404)); + }; + const deleteLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { + res.status(403).send({ error: 'You are not authorized to delete teams.' }); + return; + } + const { teamId } = req.params; + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send({ error: 'No valid records found' }); + return; + } + const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); + const deleteteam = record.remove(); + + Promise.all([removeteamfromprofile, deleteteam]) + .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) + .catch((errors) => { + res.status(400).send(errors); + }); + }).catch((error) => { + res.status(400).send(error); + }); + }; + const putUserLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + + const { teamId } = req.params; + + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send('No valid records found'); + return; + } + record.teamName = req.body.teamName; + record.isActive = req.body.isActive; + record.createdDatetime = Date.now(); + record.modifiedDatetime = Date.now(); + + record + .save() + .then(results => res.status(201).send(results._id)) + .catch(errors => res.status(400).send(errors)); + }); + }; + + return { + getAllLocations, + deleteLocation, + putUserLocation + }; +}; + +module.exports = mapLocationsController; diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js new file mode 100644 index 000000000..d7b9d82b5 --- /dev/null +++ b/src/models/mapLocation.js @@ -0,0 +1,42 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const mapLocation = new Schema({ + firstName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + lastName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + title: { + type: String, + default: 'Prior to HGN Data Collection', + }, + userProvided: { + type: String, + required: true, + }, + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { + type: String, + required: true, + }, + city: { + type: String, + default: '', + } +}); + +module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js new file mode 100644 index 000000000..e2e780dac --- /dev/null +++ b/src/routes/mapLocationsRouter.js @@ -0,0 +1,18 @@ +const express = require('express'); + +const router = function (mapLocations) { + const controller = require('../controllers/mapLocationsController')(mapLocations); + + const mapRouter = express.Router(); + + mapRouter.route('/mapLocations') + .get(controller.getAllLocations) + .put(controller.putUserLocation); + + mapRouter.route('/mapLocations/:locationId') + .delete(controller.deleteLocation) + + return mapRouter; +}; + +module.exports = router; diff --git a/src/startup/routes.js b/src/startup/routes.js index 6e000002e..ffe02ccc5 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -20,6 +20,7 @@ const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); +const mapLocations = require('../models/mapLocation'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -53,6 +54,8 @@ const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); +const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); + module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -83,4 +86,5 @@ module.exports = function (app) { app.use('/api', reasonRouter); app.use('/api', informationRouter); app.use('/api', mouseoverTextRouter); + app.use('/api', mapLocationRouter); }; From 656509dcd6ab3b6c770876d68a5c70fe7eb0c566 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Mon, 2 Oct 2023 16:13:42 -0400 Subject: [PATCH 007/129] fix: jobTitle model key, feat: adding isActive status, getting all locations, put new location to a collection --- src/controllers/mapLocationsController.js | 72 +++++++++-------------- src/models/mapLocation.js | 38 +++++++----- 2 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 83526998d..6088de489 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,62 +1,44 @@ -const mongoose = require('mongoose'); -const mapLocation = require('../models/mapLocation'); -const { hasPermission } = require('../utilities/permissions'); - -const mapLocationsController = function () { +const mapLocationsController = function (mapLocation) { const getAllLocations = function (req, res) { - console.log('controller:') - console.log(req.body) mapLocation.find({}) - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => + res.send(results).status(200) + ) + .catch(error => + res.send(error).status(404)); }; const deleteLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { - res.status(403).send({ error: 'You are not authorized to delete teams.' }); + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); return; } - const { teamId } = req.params; - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send({ error: 'No valid records found' }); - return; - } - const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); - const deleteteam = record.remove(); - - Promise.all([removeteamfromprofile, deleteteam]) - .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) - .catch((errors) => { - res.status(400).send(errors); - }); - }).catch((error) => { - res.status(400).send(error); - }); }; const putUserLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + const location = new mapLocation(locationData); - const { teamId } = req.params; - - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send('No valid records found'); - return; + try { + const response = await location.save() + if(!response) { + throw new Error('Something went wrong during saving the location...') } - record.teamName = req.body.teamName; - record.isActive = req.body.isActive; - record.createdDatetime = Date.now(); - record.modifiedDatetime = Date.now(); - - record - .save() - .then(results => res.status(201).send(results._id)) - .catch(errors => res.status(400).send(errors)); - }); + res.status(200).send(response); + } catch (err) { + console.log(err.message) + res.status(500).json({message: err.message || 'Something went wrong...'}); + } }; return { diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index d7b9d82b5..65415239f 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -11,32 +11,38 @@ const mapLocation = new Schema({ type: String, default: 'Prior to HGN Data Collection', }, - title: { + jobTitle: { type: String, default: 'Prior to HGN Data Collection', }, - userProvided: { - type: String, - required: true, + isActive: { + type: Boolean, + default: false, }, - coords: { - lat: { + location: { + userProvided: { type: String, required: true, }, - lng: { + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { type: String, required: true, - } - }, - country: { - type: String, - required: true, + }, + city: { + type: String, + default: '', + }, }, - city: { - type: String, - default: '', - } }); module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); From df10cce04fdf0c5aa9734d05eb3808ce2a5c02dd Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Thu, 5 Oct 2023 19:40:00 +0800 Subject: [PATCH 008/129] add new properties to timeEntry modal and update related function --- src/controllers/projectController.js | 2 +- src/controllers/timeEntryController.js | 194 ++++++++++++++++++++++--- src/models/timeentry.js | 6 +- src/routes/timeentryRouter.js | 9 ++ 4 files changed, 188 insertions(+), 23 deletions(-) diff --git a/src/controllers/projectController.js b/src/controllers/projectController.js index 7f10b0f86..4d5626083 100644 --- a/src/controllers/projectController.js +++ b/src/controllers/projectController.js @@ -28,7 +28,7 @@ const projectController = function (Project) { // find if project has any time enteries associated with it - timeentry.find({ projectId: record._id }, '_id') + timeentry.find({ projectId: record._id, entryType: [ 'default', 'project', null ] }, '_id') .then((timeentries) => { if (timeentries.length > 0) { res.status(400).send({ error: 'This project has associated time entries and cannot be deleted. Consider inactivaing it instead.' }); diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index e83a7e31b..4e482b085 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -129,6 +129,7 @@ const timeEntrycontroller = function (TimeEntry) { timeEntry.lastModifiedDateTime = moment().utc().toISOString(); timeEntry.projectId = mongoose.Types.ObjectId(req.body.projectId); timeEntry.dateOfWork = moment(req.body.dateOfWork).format('YYYY-MM-DD'); + timeEntry.entryType = req.body.entryType; // Update the hoursLogged field of related tasks based on before and after timeEntries // initialIsTangible is a bealoon value, req.body.isTangible is a string @@ -224,43 +225,80 @@ const timeEntrycontroller = function (TimeEntry) { } const items = []; records.forEach((element) => { - const timeentry = new TimeEntry(); - timeentry.personId = element.personId; - timeentry.projectId = element.projectId; - timeentry.dateOfWork = element.dateOfWork; - timeentry.timeSpent = moment('1900-01-01 00:00:00') - .add(element.totalSeconds, 'seconds') - .format('HH:mm:ss'); - timeentry.notes = element.notes; - timeentry.isTangible = element.isTangible; - items.push(timeentry); + if (element.entryType == 'default' || element.entryType == undefined) { + const timeentry = new TimeEntry(); + timeentry.personId = element.personId; + timeentry.projectId = element.projectId; + timeentry.dateOfWork = element.dateOfWork; + timeentry.timeSpent = moment('1900-01-01 00:00:00') + .add(element.totalSeconds, 'seconds') + .format('HH:mm:ss'); + timeentry.notes = element.notes; + timeentry.isTangible = element.isTangible; + timeentry.entryType = 'default'; + items.push(timeentry); + } }); return res.json(items).status(200); }); }; const postTimeEntry = async function (req, res) { - if ( - !mongoose.Types.ObjectId.isValid(req.body.personId) - || !mongoose.Types.ObjectId.isValid(req.body.projectId) - || !req.body.dateOfWork + const isInvalid = !req.body.dateOfWork || !moment(req.body.dateOfWork).isValid() || !req.body.timeSpent - || !req.body.isTangible - ) { + || !req.body.isTangible; + + const returnErr = (res) => { res.status(400).send({ error: 'Bad request' }); return; + }; + + switch (req.body.entryType) { + case 'default': + if ( + !mongoose.Types.ObjectId.isValid(req.body.personId) + || !mongoose.Types.ObjectId.isValid(req.body.projectId) + || isInvalid + ) { + returnErr(res); + } + break; + case 'person': + if ( + !mongoose.Types.ObjectId.isValid(req.body.personId) || isInvalid + ) { + returnErr(res); + } + break; + case 'project': + if ( + !mongoose.Types.ObjectId.isValid(req.body.projectId) || isInvalid + ) { + returnErr(res); + } + break; + case 'team': + if ( + !mongoose.Types.ObjectId.isValid(req.body.teamId) || isInvalid + ) { + returnErr(res); + } + break; } + const timeentry = new TimeEntry(); const { dateOfWork, timeSpent } = req.body; timeentry.personId = req.body.personId; timeentry.projectId = req.body.projectId; + timeentry.teamId = req.body.teamId; timeentry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); timeentry.totalSeconds = moment.duration(timeSpent).asSeconds(); timeentry.notes = req.body.notes; timeentry.isTangible = req.body.isTangible; timeentry.createdDateTime = moment().utc().toISOString(); timeentry.lastModifiedDateTime = moment().utc().toISOString(); + timeentry.entryType = req.body.entryType; timeentry .save() @@ -272,7 +310,7 @@ const timeEntrycontroller = function (TimeEntry) { .catch(error => res.status(400).send(error)); // Add this tangbile time entry to related task's hoursLogged - if (timeentry.isTangible === true) { + if ((timeentry.entryType == 'default') && timeentry.isTangible === true) { try { const currentTask = await task.findById(req.body.projectId); currentTask.hoursLogged += (timeentry.totalSeconds / 3600); @@ -282,9 +320,11 @@ const timeEntrycontroller = function (TimeEntry) { } } // checking if logged in hours exceed estimated time after timeentry for a task - const record = await userProfile.findById(timeentry.personId.toString()); - const currentTask = await task.findById(req.body.projectId); - checkTaskOvertime(timeentry, record, currentTask); + if (timeentry.entryType == 'default') { + const record = await userProfile.findById(timeentry.personId.toString()); + const currentTask = await task.findById(req.body.projectId); + checkTaskOvertime(timeentry, record, currentTask); + } }; const getTimeEntriesForSpecifiedPeriod = function (req, res) { @@ -307,6 +347,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.aggregate([ { $match: { + entryType: { $in: [ 'default', null ] }, personId: mongoose.Types.ObjectId(userId), dateOfWork: { $gte: fromdate, $lte: todate }, }, @@ -390,6 +431,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.find( { + entryType: { $in: [ 'default', null, 'person' ] }, personId: { $in: users }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, @@ -434,6 +476,7 @@ const timeEntrycontroller = function (TimeEntry) { const { projectId } = req.params; TimeEntry.find( { + entryType: [ 'default', null ], projectId, dateOfWork: { $gte: fromDate, $lte: todate }, }, @@ -494,6 +537,114 @@ const timeEntrycontroller = function (TimeEntry) { }); }; + const getLostTimeEntriesForUserList = function (req, res) { + const { users, fromDate, toDate } = req.body; + + TimeEntry.find( + { + entryType: 'person', + personId: { $in: users }, + dateOfWork: { $gte: fromDate, $lte: toDate }, + }, + ' -createdDateTime', + ) + .populate('personId') + .sort({ lastModifiedDateTime: -1 }) + .then((results) => { + const data = []; + results.forEach((element) => { + console.log(element); + const record = {}; + + record._id = element._id; + record.notes = element.notes; + record.isTangible = element.isTangible; + record.personId = element.personId; + record.firstName = element.personId + ? element.personId.firstName + : ''; + record.lastName = element.personId + ? element.personId.lastName + : ''; + record.dateOfWork = element.dateOfWork; + record.entryType = element.entryType; + [record.hours, record.minutes] = formatSeconds(element.totalSeconds); + data.push(record); + }); + res.status(200).send(data); + }) + .catch(error => res.status(400).send(error)); + }; + + const getLostTimeEntriesForProjectList = function (req, res) { + const { projects, fromDate, toDate } = req.body; + + TimeEntry.find( + { + entryType: "project", + projectId: { $in: projects }, + dateOfWork: { $gte: fromDate, $lte: toDate }, + }, + ' -createdDateTime', + ) + .populate('projectId') + .sort({ lastModifiedDateTime: -1 }) + .then((results) => { + const data = []; + results.forEach((element) => { + console.log(element); + const record = {}; + record._id = element._id; + record.notes = element.notes; + record.isTangible = element.isTangible; + record.projectId = element.projectId ? element.projectId._id : ''; + record.projectName = element.projectId + ? element.projectId.projectName + : ''; + record.dateOfWork = element.dateOfWork; + record.entryType = element.entryType; + [record.hours, record.minutes] = formatSeconds(element.totalSeconds); + data.push(record); + }); + res.status(200).send(data); + }) + .catch(error => res.status(400).send(error)); + }; + + const getLostTimeEntriesForTeamList = function (req, res) { + const { teams, fromDate, toDate } = req.body; + + TimeEntry.find( + { + entryType: "team", + teamId: { $in: teams }, + dateOfWork: { $gte: fromDate, $lte: toDate }, + }, + ' -createdDateTime', + ) + .populate('teamId') + .sort({ lastModifiedDateTime: -1 }) + .then((results) => { + const data = []; + results.forEach((element) => { + console.log(element); + const record = {}; + record._id = element._id; + record.notes = element.notes; + record.isTangible = element.isTangible; + record.teamId = element.teamId; + record.teamName = element.teamId + ? element.teamId.teamName + : ''; + record.dateOfWork = element.dateOfWork; + record.entryType = element.entryType; + [record.hours, record.minutes] = formatSeconds(element.totalSeconds); + data.push(record); + }); + res.status(200).send(data); + }) + .catch(error => res.status(400).send(error)); + }; return { getAllTimeEnteries, @@ -504,6 +655,9 @@ const timeEntrycontroller = function (TimeEntry) { deleteTimeEntry, getTimeEntriesForSpecifiedProject, checkTaskOvertime, + getLostTimeEntriesForUserList, + getLostTimeEntriesForProjectList, + getLostTimeEntriesForTeamList, }; }; diff --git a/src/models/timeentry.js b/src/models/timeentry.js index aeef5fdc7..83510773f 100644 --- a/src/models/timeentry.js +++ b/src/models/timeentry.js @@ -4,8 +4,10 @@ const { Schema } = mongoose; const TimeEntry = new Schema({ - personId: { type: Schema.Types.ObjectId, required: [true, 'Resource is a required field'], ref: 'userProfile' }, - projectId: { type: Schema.Types.ObjectId, required: [true, 'Project is a required field'], ref: 'project' }, + entryType: { type: String, required: true, default: 'default' }, + personId: { type: Schema.Types.ObjectId, ref: 'userProfile' }, + projectId: { type: Schema.Types.ObjectId, ref: 'project' }, + teamId: { type: Schema.Types.ObjectId, ref: 'team' }, dateOfWork: { type: String, required: true }, totalSeconds: { type: Number }, notes: { type: String }, diff --git a/src/routes/timeentryRouter.js b/src/routes/timeentryRouter.js index b319aa595..7e0a41797 100644 --- a/src/routes/timeentryRouter.js +++ b/src/routes/timeentryRouter.js @@ -19,6 +19,15 @@ const routes = function (TimeEntry) { TimeEntryRouter.route('/TimeEntry/users') .post(controller.getTimeEntriesForUsersList); + TimeEntryRouter.route('/TimeEntry/lostUsers') + .post(controller.getLostTimeEntriesForUserList); + + TimeEntryRouter.route('/TimeEntry/lostProjects') + .post(controller.getLostTimeEntriesForProjectList); + + TimeEntryRouter.route('/TimeEntry/lostTeams') + .post(controller.getLostTimeEntriesForTeamList); + TimeEntryRouter.route('/TimeEntry/projects/:projectId/:fromDate/:toDate') .get(controller.getTimeEntriesForSpecifiedProject); From ba03b5b00594cace86a084e12897e05b8669dbe7 Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Thu, 5 Oct 2023 17:05:38 -0500 Subject: [PATCH 009/129] add material routing, inventoryItemMaterial schema --- package-lock.json | 106 +++++++++--------- .../bmdashboard/bmMaterialsController.js | 40 +++++++ src/models/inventoryItemMaterial.js | 36 ++++++ src/routes/bmdashboard/bmMaterialsRouter.js | 14 +++ src/startup/routes.js | 4 +- 5 files changed, 146 insertions(+), 54 deletions(-) create mode 100644 src/controllers/bmdashboard/bmMaterialsController.js create mode 100644 src/models/inventoryItemMaterial.js create mode 100644 src/routes/bmdashboard/bmMaterialsRouter.js diff --git a/package-lock.json b/package-lock.json index 5c6bd6db5..47eb32afb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1414,7 +1414,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/mime": { @@ -1570,7 +1570,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { "version": "3.1.6", @@ -2631,7 +2631,7 @@ "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "bignumber.js": { "version": "9.0.2", @@ -2727,7 +2727,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "buffer-from": { "version": "1.1.2", @@ -2860,7 +2860,7 @@ "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" }, "clone-deep": { "version": "4.0.1", @@ -2904,12 +2904,12 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { "version": "1.6.2", @@ -2965,7 +2965,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-js": { "version": "3.21.1", @@ -3118,7 +3118,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { "version": "1.4.81", @@ -3134,7 +3134,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "es-abstract": { "version": "1.19.1", @@ -3215,7 +3215,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", @@ -4129,7 +4129,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "event-target-shim": { "version": "5.0.1", @@ -4294,7 +4294,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-text-encoding": { @@ -4424,7 +4424,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-readdir-recursive": { "version": "1.1.0", @@ -4434,7 +4434,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -4812,7 +4812,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { @@ -4824,7 +4824,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4930,7 +4930,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-glob": { "version": "4.0.3", @@ -5032,13 +5032,13 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" }, "js-tokens": { "version": "4.0.0", @@ -5076,7 +5076,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { @@ -5397,7 +5397,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.merge": { "version": "4.6.2", @@ -5574,7 +5574,7 @@ "lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" }, "make-dir": { "version": "2.1.0", @@ -5588,7 +5588,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memory-pager": { "version": "1.5.0", @@ -5599,7 +5599,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-stream": { "version": "2.0.0", @@ -5610,7 +5610,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { "version": "4.0.5", @@ -5767,7 +5767,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "negotiator": { @@ -5845,7 +5845,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.12.0", @@ -6564,7 +6564,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -6594,7 +6594,7 @@ "os-shim": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==", "dev": true }, "p-limit": { @@ -6639,7 +6639,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==" }, "parseurl": { "version": "1.3.3", @@ -6654,7 +6654,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -6670,7 +6670,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "picocolors": { "version": "1.0.0", @@ -6717,7 +6717,7 @@ "pre-commit": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", - "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", + "integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -6728,7 +6728,7 @@ "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -6739,7 +6739,7 @@ "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -6781,7 +6781,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "punycode": { @@ -7219,7 +7219,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "optional": true, "requires": { "memory-pager": "^1.0.2" @@ -7228,7 +7228,7 @@ "spawn-sync": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==", "dev": true, "requires": { "concat-stream": "^1.4.7", @@ -7623,7 +7623,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-final-newline": { @@ -7654,19 +7654,19 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-regex-range": { "version": "5.0.1", @@ -7684,7 +7684,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tsconfig-paths": { "version": "3.14.2", @@ -7802,7 +7802,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "unbox-primitive": { @@ -7843,7 +7843,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.4.1", @@ -7857,17 +7857,17 @@ "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -7890,17 +7890,17 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8018,7 +8018,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { "version": "8.8.1", diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js new file mode 100644 index 000000000..558a0f403 --- /dev/null +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -0,0 +1,40 @@ +const mongoose = require('mongoose'); + +const bmMaterialsController = function (ItemMaterial, ItemType) { + const bmMaterialsList = async function (req, res) { + try { + ItemMaterial.find() + .populate({ + path: 'project', + select: '_id projectName' + }) + .populate({ + path: 'inventoryItemType', + select: '_id name uom totalStock' + }) + .then(results => res.status(200).send(results)) + .catch(error => res.status(500).send(error)) + } catch (err) { + res.json(err); + } + }; + + const bmAddMaterials = async function (req, res) { + console.log(req.body); + // if new material or new measurement, add to inventoryItemType collection first + const { material, requestor } = req.body; + if (material.newMaterial || material.newMeasurement) { + const materials = await ItemMaterial.find().exec(); + console.log(materials); + + } + // then either add item material to project or update existing item material + }; + + return { + bmMaterialsList, + bmAddMaterials + }; +}; + +module.exports = bmMaterialsController; \ No newline at end of file diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js new file mode 100644 index 000000000..0c8aed460 --- /dev/null +++ b/src/models/inventoryItemMaterial.js @@ -0,0 +1,36 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; + +const InventoryItemMaterial = new Schema({ + inventoryItemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'inventoryItemType', required: true }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'project', required: true }, + stockBought: { type: Number, required: true }, // amount bought for project, affects total stock + stockUsed: { type: Number, required: true }, + stockAvailable: { type: Number, required: true }, + stockHeld: { type: Number, required: true }, + stockWasted: { type: Number, required: true }, + usageRecord: [{ // daily log of amount inventory item used at job site + date: { type: Date, required: true, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, + quantityUsed: { type: Number, required: true }, + }], + updateRecord: [{ // incident report affecting quantity/status of inventory item + date: { type: Date, required: true, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, + action: { type: String, required: true }, // ex: Add, Reduce, Hold (updates stock quantities) + cause: { type: String, required: true }, // ex: Used, Lost, Wasted, Transfer (reason for update) + quantity: { type: Number, required: true }, // amount of material affected + description: { type: String, required: true, maxLength: 150 }, + }], + purchaseRecord: [{ + date: { type: Date, required: true, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, + poId: { type: String, required: true }, + sellerId: { type: String, required: true }, + quantity: { type: Number, required: true }, // adds to stockBought + subtotal: { type: Number, required: true }, + tax: { type: Number, required: true }, + shipping: { type: Number, required: true }, + }] +}) +module.exports = mongoose.model('inventoryItemMaterial', InventoryItemMaterial, 'inventoryMaterial'); \ No newline at end of file diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js new file mode 100644 index 000000000..aaded5587 --- /dev/null +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -0,0 +1,14 @@ +const express = require('express'); + +const routes = function (itemMaterial, itemType) { + const materialsRouter = express.Router(); + const controller = require('../../controllers/bmdashboard/bmMaterialsController')(itemMaterial, itemType); + + materialsRouter.route('/materials') + .get(controller.bmMaterialsList) + .post(controller.bmAddMaterials); + + return materialsRouter; +}; + +module.exports = routes; \ No newline at end of file diff --git a/src/startup/routes.js b/src/startup/routes.js index 43cd226bd..a1eed4a7a 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -20,6 +20,7 @@ const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); +const itemMaterial = require('../models/inventoryItemMaterial'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -55,7 +56,7 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); - +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(itemMaterial); module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -88,4 +89,5 @@ module.exports = function (app) { app.use('/api', mouseoverTextRouter); // bm dashboard app.use('/api/bm', bmLoginRouter); + app.use('/api/bm', bmMaterialsRouter); }; From bbc244e5274541a73f198c58937eaa3ba7bc90aa Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Fri, 6 Oct 2023 14:36:14 -0500 Subject: [PATCH 010/129] controller logic for adding materials, update schemas --- .../bmdashboard/bmMaterialsController.js | 58 +++++++++++++++++-- src/models/inventoryItemMaterial.js | 8 ++- src/models/inventoryItemType.js | 9 ++- src/startup/routes.js | 2 +- 4 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 558a0f403..d9d1c7780 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -21,14 +21,62 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { const bmAddMaterials = async function (req, res) { console.log(req.body); - // if new material or new measurement, add to inventoryItemType collection first const { material, requestor } = req.body; + let itemTypeId = material.material; // if new material: material name / else: itemType id + + // if new material or new measurement, add new inventoryItemType if (material.newMaterial || material.newMeasurement) { - const materials = await ItemMaterial.find().exec(); - console.log(materials); - + try { + const itemType = new ItemType({ + type: 'material', + name: material.material, + description: material.description, + uom: material.measurement, + totalStock: material.quantity, + totalAvailable: material.quantity, + projectsUsing: [material.projectId], + imageUrl: '', + link: material.link, + }); + const newItemType = await itemType.save(); + itemTypeId = newItemType._id; + } catch (err) { + res.status(400).send({ error: 'Error saving new material type'}); + } + } + + try { + const invMaterial = await ItemMaterial.find({ id: material.projectId, inventoryItemType: itemTypeId }).exec(); + // if material already exists in project, add to it + // else, create new material for project + if (invMaterial) { + // TODO + console.log(invMaterial); + } else { + const itemMaterial = new ItemMaterial({ + inventoryItemType: itemTypeId, + project: material.projectId, + stockBought: material.quantity, + stockAvailable: material.quantity, + usageRecord: [], + updateRecord: [], + purchaseRecord: [{ + date: material.purchaseDate, + createdBy: req.requestor.requestorId, + poId: material.invoice, + sellerId: material.phone, + quantity: material.quantity, + unitPrice: material.unitPrice, + subTotal: material.quantity * material.unitPrice, + tax: material.taxRate, + shipping: material.shippingFee, + }], + }); + const newItemMaterial = await itemMaterial.save(); + } + } catch (err) { + res.status(400).send({ error: 'Error adding new material to project'}); } - // then either add item material to project or update existing item material }; return { diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index 0c8aed460..09c521e4d 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -5,10 +5,10 @@ const InventoryItemMaterial = new Schema({ inventoryItemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'inventoryItemType', required: true }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'project', required: true }, stockBought: { type: Number, required: true }, // amount bought for project, affects total stock - stockUsed: { type: Number, required: true }, + stockUsed: { type: Number, default: 0 }, stockAvailable: { type: Number, required: true }, - stockHeld: { type: Number, required: true }, - stockWasted: { type: Number, required: true }, + stockHeld: { type: Number, default: 0 }, + stockWasted: { type: Number, default: 0 }, usageRecord: [{ // daily log of amount inventory item used at job site date: { type: Date, required: true, default: Date.now() }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, @@ -28,6 +28,8 @@ const InventoryItemMaterial = new Schema({ poId: { type: String, required: true }, sellerId: { type: String, required: true }, quantity: { type: Number, required: true }, // adds to stockBought + unitPrice: { type: Number, required: true }, + currency: { type: String, required: true }, subtotal: { type: Number, required: true }, tax: { type: Number, required: true }, shipping: { type: Number, required: true }, diff --git a/src/models/inventoryItemType.js b/src/models/inventoryItemType.js index 80e5e0d7b..20a58c018 100644 --- a/src/models/inventoryItemType.js +++ b/src/models/inventoryItemType.js @@ -3,10 +3,15 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const InventoryItemType = new Schema({ + type: { type: String, required: true }, // ie Material, Equipment, Tool name: { type: String, required: true }, - description: { type: String }, + description: { type: String, required: true, maxLength: 150 }, + uom: { type: String, required: true }, // unit of measurement + totalStock: { type: Number, required: true }, // total amount of all stock acquired + totalAvailable: { type: Number, required: true }, + projectsUsing: [ {type: mongoose.SchemaTypes.ObjectId, ref: 'project'} ], imageUrl: { type: String }, - quantifier: { type: String, default: 'each' }, + link: { type: String }, }); module.exports = mongoose.model('inventoryItemType', InventoryItemType, 'inventoryItemType'); diff --git a/src/startup/routes.js b/src/startup/routes.js index a1eed4a7a..66f7772dd 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -56,7 +56,7 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); -const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(itemMaterial); +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(itemMaterial, inventoryItemType); module.exports = function (app) { app.use('/api', forgotPwdRouter); From e55f980bed651b15bb0fad0202113e70a5ca4214 Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Tue, 10 Oct 2023 18:52:52 -0500 Subject: [PATCH 011/129] save new item type before adding material to project --- .../bmdashboard/bmMaterialsController.js | 33 ++++++++++--------- src/models/inventoryItemMaterial.js | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index d9d1c7780..8b53ab35f 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -20,9 +20,8 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { }; const bmAddMaterials = async function (req, res) { - console.log(req.body); const { material, requestor } = req.body; - let itemTypeId = material.material; // if new material: material name / else: itemType id + let itemTypeId = material.material; // either new material or existing itemTypeId // if new material or new measurement, add new inventoryItemType if (material.newMaterial || material.newMeasurement) { @@ -38,20 +37,20 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { imageUrl: '', link: material.link, }); - const newItemType = await itemType.save(); - itemTypeId = newItemType._id; - } catch (err) { - res.status(400).send({ error: 'Error saving new material type'}); + const result = await itemType.save(); + itemTypeId = result._id; + } catch (error) { + res.status(500).send(error); } } try { - const invMaterial = await ItemMaterial.find({ id: material.projectId, inventoryItemType: itemTypeId }).exec(); - // if material already exists in project, add to it - // else, create new material for project + const invMaterial = await ItemMaterial.findOne( + { project: material.projectId, inventoryItemType: itemTypeId }).exec(); + console.log(invMaterial); if (invMaterial) { - // TODO - console.log(invMaterial); + // TODO: update inventoryMaterial with new purchase record + // and updated quantities } else { const itemMaterial = new ItemMaterial({ inventoryItemType: itemTypeId, @@ -62,26 +61,28 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { updateRecord: [], purchaseRecord: [{ date: material.purchaseDate, - createdBy: req.requestor.requestorId, + createdBy: requestor.requestorId, poId: material.invoice, sellerId: material.phone, quantity: material.quantity, unitPrice: material.unitPrice, - subTotal: material.quantity * material.unitPrice, + currency: material.currency, + subtotal: material.quantity, tax: material.taxRate, shipping: material.shippingFee, }], }); const newItemMaterial = await itemMaterial.save(); + console.log(newItemMaterial); } - } catch (err) { - res.status(400).send({ error: 'Error adding new material to project'}); + } catch (error) { + res.status(500).send(error); } }; return { bmMaterialsList, - bmAddMaterials + bmAddMaterials, }; }; diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index 09c521e4d..2ffa1fba2 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -23,7 +23,7 @@ const InventoryItemMaterial = new Schema({ description: { type: String, required: true, maxLength: 150 }, }], purchaseRecord: [{ - date: { type: Date, required: true, default: Date.now() }, + date: { type: String, required: true }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, poId: { type: String, required: true }, sellerId: { type: String, required: true }, From 31d1e39d35cf0867ea786ef7eb2e59e294d63d4b Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Wed, 11 Oct 2023 17:29:28 -0500 Subject: [PATCH 012/129] update method to add inventory types --- .../bmdashboard/bmMaterialsController.js | 31 +++---------------- src/controllers/inventoryController.js | 7 ++++- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 8b53ab35f..708c4d884 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -21,39 +21,16 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { const bmAddMaterials = async function (req, res) { const { material, requestor } = req.body; - let itemTypeId = material.material; // either new material or existing itemTypeId - - // if new material or new measurement, add new inventoryItemType - if (material.newMaterial || material.newMeasurement) { - try { - const itemType = new ItemType({ - type: 'material', - name: material.material, - description: material.description, - uom: material.measurement, - totalStock: material.quantity, - totalAvailable: material.quantity, - projectsUsing: [material.projectId], - imageUrl: '', - link: material.link, - }); - const result = await itemType.save(); - itemTypeId = result._id; - } catch (error) { - res.status(500).send(error); - } - } try { - const invMaterial = await ItemMaterial.findOne( - { project: material.projectId, inventoryItemType: itemTypeId }).exec(); - console.log(invMaterial); + const invMaterial = await ItemMaterial.findOne({ project: material.projectId, inventoryItemType: material.material }).exec(); if (invMaterial) { + console.log('found item material in project'); // TODO: update inventoryMaterial with new purchase record // and updated quantities } else { const itemMaterial = new ItemMaterial({ - inventoryItemType: itemTypeId, + inventoryItemType: material.material, project: material.projectId, stockBought: material.quantity, stockAvailable: material.quantity, @@ -73,7 +50,7 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { }], }); const newItemMaterial = await itemMaterial.save(); - console.log(newItemMaterial); + res.status(201).send(newItemMaterial); } } catch (error) { res.status(500).send(error); diff --git a/src/controllers/inventoryController.js b/src/controllers/inventoryController.js index bc0902aeb..d4c9d5261 100644 --- a/src/controllers/inventoryController.js +++ b/src/controllers/inventoryController.js @@ -548,10 +548,15 @@ const inventoryController = function (Item, ItemType) { } const itemType = new ItemType(); + itemType.type = req.body.type; itemType.name = req.body.name; itemType.description = req.body.description; + itemType.uom = req.body.uom; + itemType.totalStock = req.body.totalStock; + itemType.totalAvailable = req.body.totalAvailable; + itemType.projectsUsing = []; itemType.imageUrl = req.body.imageUrl || req.body.imageURL; - itemType.quantifier = req.body.quantifier; + itemType.link = req.body.link; itemType.save() .then(results => res.status(201).send(results)) From 670d86fbfd149163f4ea5495e3bfc5c4ef68d27f Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Thu, 12 Oct 2023 18:53:50 -0500 Subject: [PATCH 013/129] update inventory material quantities and purchase records when adding --- .../bmdashboard/bmMaterialsController.js | 44 +++++++++++-------- src/models/inventoryItemMaterial.js | 2 +- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 708c4d884..0f1e8a38e 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -20,14 +20,33 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { }; const bmAddMaterials = async function (req, res) { + // add permission check... + const { material, requestor } = req.body; + const purchaseRecord = { + date: material.purchaseDate, + createdBy: requestor.requestorId, + poId: material.invoice, + sellerId: material.phone, + quantity: material.quantity, + unitPrice: material.unitPrice, + currency: material.currency, + subtotal: material.quantity, + tax: material.taxRate, + shipping: material.shippingFee, + }; try { - const invMaterial = await ItemMaterial.findOne({ project: material.projectId, inventoryItemType: material.material }).exec(); - if (invMaterial) { - console.log('found item material in project'); - // TODO: update inventoryMaterial with new purchase record - // and updated quantities + const result = await ItemMaterial.findOneAndUpdate( + { project: material.projectId, inventoryItemType: material.material }, + { + $inc: { stockBought: material.quantity, stockAvailable: material.quantity }, + $push: { purchaseRecord: purchaseRecord }, + }, + { returnDocument: 'after', lean: true }).exec(); + if (result) { + console.log(result); + res.status(201).send(result); } else { const itemMaterial = new ItemMaterial({ inventoryItemType: material.material, @@ -36,18 +55,7 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { stockAvailable: material.quantity, usageRecord: [], updateRecord: [], - purchaseRecord: [{ - date: material.purchaseDate, - createdBy: requestor.requestorId, - poId: material.invoice, - sellerId: material.phone, - quantity: material.quantity, - unitPrice: material.unitPrice, - currency: material.currency, - subtotal: material.quantity, - tax: material.taxRate, - shipping: material.shippingFee, - }], + purchaseRecord: [purchaseRecord], }); const newItemMaterial = await itemMaterial.save(); res.status(201).send(newItemMaterial); @@ -57,7 +65,7 @@ const bmMaterialsController = function (ItemMaterial, ItemType) { } }; - return { + return { bmMaterialsList, bmAddMaterials, }; diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index 2ffa1fba2..09c521e4d 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -23,7 +23,7 @@ const InventoryItemMaterial = new Schema({ description: { type: String, required: true, maxLength: 150 }, }], purchaseRecord: [{ - date: { type: String, required: true }, + date: { type: Date, required: true, default: Date.now() }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, poId: { type: String, required: true }, sellerId: { type: String, required: true }, From bc2655be2bb9b3149d13d9b34fa3a84247276e12 Mon Sep 17 00:00:00 2001 From: Chuehleo <122568562+Chuehleo@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:55:00 -0700 Subject: [PATCH 014/129] Update userProfileController.js --- src/controllers/userProfileController.js | 159 +++++++++++------------ 1 file changed, 75 insertions(+), 84 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 6036b9d0f..a388f10db 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -12,7 +12,7 @@ const Badge = require('../models/badge'); const userProfile = require('../models/userProfile'); const yearMonthDayDateValidator = require('../utilities/yearMonthDayDateValidator'); const cache = require('../utilities/nodeCache')(); -const { hasPermission, hasIndividualPermission, canRequestorUpdateUser } = require('../utilities/permissions'); +const { hasPermission, canRequestorUpdateUser } = require('../utilities/permissions'); const escapeRegex = require('../utilities/escapeRegex'); const config = require('../config'); @@ -35,7 +35,20 @@ async function ValidatePassword(req, res) { return; } // Verify request is authorized by self or adminsitrator - if (!userId === requestor.requestorId && !await hasPermission(requestor.role, 'updatePassword')) { + if (userId !== requestor.requestorId && !await hasPermission(requestor.role, 'updatePassword')) { + console.log('User ID:', userId); + console.log('Requestor ID:', requestor.requestorId); + res.status(403).send({ + error: "You are unauthorized to update this user's password", + }); + return; + } + + + // Check permissions + if (userId === requestor.requestorId || !await hasPermission(requestor.role, 'updatePasswordForOthers')) { + console.log('User ID:', userId); + console.log('Requestor ID:', requestor.requestorId); res.status(403).send({ error: "You are unauthorized to update this user's password", }); @@ -52,16 +65,20 @@ async function ValidatePassword(req, res) { const userProfileController = function (UserProfile) { const getUserProfiles = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'getUserProfiles') && + if ( + !(await hasPermission(req.body.requestor.role, "getUserProfiles")) && !req.body.requestor.permissions?.frontPermissions.includes( "putUserProfilePermissions" ) ) { - if (!await hasIndividualPermission(req.body.requestor.requestorId, 'seeProjectManagement') - && !await hasIndividualPermission(req.body.requestor.requestorId, 'seeProjectManagementTab')) { - res.status(403).send('You are not authorized to view all users'); + res.status(403).send("You are not authorized to view all users"); + return; + } + + if (cache.getCache("allusers")) { + const getData = JSON.parse(cache.getCache("allusers")); + res.status(200).send(getData); return; - } } UserProfile.find( @@ -73,20 +90,13 @@ const userProfileController = function (UserProfile) { }) .then((results) => { if (!results) { - if (cache.getCache("allusers")) { - const getData = JSON.parse(cache.getCache("allusers")); - res.status(200).send(getData); - return; - }else{ - res.status(500).send({ error: "User result was invalid" }); - return; - } + res.status(500).send({ error: "User result was invalid" }); + return; } cache.setCache("allusers", JSON.stringify(results)); res.status(200).send(results); }) .catch((error) => res.status(404).send(error)); - }; const getProjectMembers = async function (req, res) { @@ -245,9 +255,6 @@ const userProfileController = function (UserProfile) { "putUserProfilePermissions" )) ); - - const canEditTeamCode = req.body.requestor.role === "Owner" || - req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); if (!isRequestorAuthorized) { res.status(403).send("You are not authorized to update this user"); @@ -309,12 +316,6 @@ const userProfileController = function (UserProfile) { record.totalIntangibleHrs = req.body.totalIntangibleHrs; record.bioPosted = req.body.bioPosted || "default"; record.isFirstTimelog = req.body.isFirstTimelog; - - if(!canEditTeamCode && record.teamCode !== req.body.teamCode){ - res.status(403).send("You are not authorized to edit team code."); - return; - } - record.teamCode = req.body.teamCode; // find userData in cache @@ -601,17 +602,6 @@ const userProfileController = function (UserProfile) { const { userId } = req.params; const { key, value } = req.body; - if (key === "teamCode") { - const canEditTeamCode = req.body.requestor.role === "Owner" || - req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); - - if(!canEditTeamCode){ - res.status(403).send("You are not authorized to edit team code."); - return; - } - - } - // remove user from cache, it should be loaded next time cache.removeCache(`user-${userId}`); if (!key || value === undefined) return res.status(400).send({ error: 'Missing property or value' }); @@ -636,70 +626,71 @@ const userProfileController = function (UserProfile) { const updatepassword = async function (req, res) { const { userId } = req.params; const { requestor } = req.body; + + console.log('User ID:', userId); + console.log('Requestor ID:', requestor.requestorId); + + // Check if userId is valid. if (!mongoose.Types.ObjectId.isValid(userId)) { - return res.status(400).send({ - error: 'Bad Request', - }); + return res.status(400).send({ + error: 'Bad Request', + }); } // Verify correct params in body if (!req.body.currentpassword || !req.body.newpassword || !req.body.confirmnewpassword) { - return res.status(400).send({ - error: 'One of more required fields are missing', - }); - } - // Verify request is authorized by self or adminsitrator - if (!userId === requestor.requestorId && !await hasPermission(requestor.role, 'updatePassword')) { - return res.status(403).send({ - error: "You are unauthorized to update this user's password", - }); + return res.status(400).send({ + error: 'One or more required fields are missing', + }); } - if (canRequestorUpdateUser(requestor.requestorId, userId)) { - return res.status(403).send({ - error: "You are unauthorized to update this user's password", - }); + // Check if the requestor has the permission to update passwords. + const hasUpdatePasswordPermission = await hasPermission(requestor.role, 'updatePassword'); + + // If the requestor is updating their own password, allow them to proceed. + if (userId === requestor.requestorId) { + console.log('Requestor is updating their own password'); + } + // Else if they're updating someone else's password, they need the 'updatePassword' permission. + else if (!hasUpdatePasswordPermission) { + console.log("Requestor is trying to update someone else's password but lacks the 'updatePassword' permission"); + return res.status(403).send({ + error: "You are unauthorized to update this user's password", + }); } // Verify new and confirm new password are correct - if (req.body.newpassword !== req.body.confirmnewpassword) { - res.status(400).send({ - error: 'New and confirm new passwords are not same', - }); + return res.status(400).send({ + error: 'New and confirm new passwords are not the same', + }); } - // Verify old and new passwords are not same - if (req.body.currentpassword === req.body.newpassword) { - res.status(400).send({ - error: 'Old and new passwords should not be same', - }); - } + // Process the password change + try { + const user = await UserProfile.findById(userId, 'password'); + const passwordMatch = await bcrypt.compare(req.body.currentpassword, user.password); - return UserProfile.findById(userId, 'password') - .then((user) => { - bcrypt - .compare(req.body.currentpassword, user.password) - .then((passwordMatch) => { - if (!passwordMatch) { - return res.status(400).send({ + if (!passwordMatch) { + return res.status(400).send({ error: 'Incorrect current password', - }); - } - - user.set({ - password: req.body.newpassword, - resetPwd: undefined, }); - return user - .save() - .then(() => res.status(200).send({ message: 'updated password' })) - .catch(error => res.status(500).send(error)); - }) - .catch(error => res.status(500).send(error)); - }) - .catch(error => res.status(500).send(error)); - }; + } + + user.set({ + password: req.body.newpassword, + resetPwd: undefined, + }); + + await user.save(); + + return res.status(200).send({ message: 'Updated password successfully' }); + + } catch (error) { + return res.status(500).send(error); + } +}; + const getreportees = async function (req, res) { if (!mongoose.Types.ObjectId.isValid(req.params.userId)) { From 82d29effc13a1202d8b83ed69d3c11f92a5dec7c Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 015/129] feat: userprofiles sending totalTangibleHrs, update user controller, all manually locations response structure --- src/controllers/mapLocationsController.js | 85 ++++++++++++++++++++--- src/controllers/userProfileController.js | 2 +- src/routes/mapLocationsRouter.js | 3 +- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 6088de489..f01666558 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,11 +1,26 @@ -const mapLocationsController = function (mapLocation) { +const userProfile = require('../models/userProfile'); +const cache = require('../utilities/nodeCache')(); + +const mapLocationsController = function (MapLocation) { const getAllLocations = function (req, res) { + const priorText = 'Prior to HGN Data Collection'; + MapLocation.find({}) + .then(results => { + const users = results.map(item => { + return ({ + title: priorText, + firstName: item.firstName !== priorText ? item.firstName : '', + lastName: item.lastName !== priorText ? item.lastName : '', + jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', + location: item.location, + isActive: item.isActive, + _id: item._id + }) + }) + res.send(users).status(200); - mapLocation.find({}) - .then(results => - res.send(results).status(200) - ) - .catch(error => + }) + .catch(error => res.send(error).status(404)); }; const deleteLocation = async function (req, res) { @@ -14,6 +29,11 @@ const mapLocationsController = function (mapLocation) { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationId = req.params.locationId + + MapLocation.findOneAndDelete({ _id: locationId }) + .then(() => res.status(200).send({ message: "The location was successfully removed!" })) + .catch(error => res.status(500).send({ message: error || "Couldn't remove the location" })); }; const putUserLocation = async function (req, res) { @@ -27,24 +47,69 @@ const mapLocationsController = function (mapLocation) { jobTitle: req.body.jobTitle, location: req.body.location, } - const location = new mapLocation(locationData); + const location = new MapLocation(locationData); try { const response = await location.save() - if(!response) { + if (!response) { throw new Error('Something went wrong during saving the location...') } res.status(200).send(response); } catch (err) { console.log(err.message) - res.status(500).json({message: err.message || 'Something went wrong...'}); + res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + const updateUserLocation = async function (req, res) { + console.log(req.body) + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + const userType = req.body.type; + const userId= req.body._id; + const updateData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + _id: req.body._id + } + try { + let response; + if(userType === 'user') { + response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + cache.removeCache('allusers') + cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); + } else { + response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + } + + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + const newData = { + firstName: response.firstName, + lastName: response.lastName, + jobTitle: response.jobTitle, + location: response.location, + _id: response._id, + type: userType + } + + res.status(200).send(newData); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + }; return { getAllLocations, deleteLocation, - putUserLocation + putUserLocation, + updateUserLocation }; }; diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 8a866401a..9e6d83894 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle totalTangibleHrs', ) .sort({ lastName: 1, diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js index e2e780dac..db004ff18 100644 --- a/src/routes/mapLocationsRouter.js +++ b/src/routes/mapLocationsRouter.js @@ -7,7 +7,8 @@ const router = function (mapLocations) { mapRouter.route('/mapLocations') .get(controller.getAllLocations) - .put(controller.putUserLocation); + .put(controller.putUserLocation) + .patch(controller.updateUserLocation); mapRouter.route('/mapLocations/:locationId') .delete(controller.deleteLocation) From bc3d082a9959c527e202841f87840d836e957064 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Thu, 19 Oct 2023 06:43:15 +0800 Subject: [PATCH 016/129] test --- src/controllers/timeEntryController.js | 3 --- src/helpers/dashboardhelper.js | 14 ++++++++++++++ src/helpers/reporthelper.js | 3 +++ src/helpers/taskHelper.js | 6 ++++++ src/helpers/userHelper.js | 3 ++- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 0bd9bb573..094cee9ba 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -553,7 +553,6 @@ const timeEntrycontroller = function (TimeEntry) { .then((results) => { const data = []; results.forEach((element) => { - console.log(element); const record = {}; record._id = element._id; @@ -592,7 +591,6 @@ const timeEntrycontroller = function (TimeEntry) { .then((results) => { const data = []; results.forEach((element) => { - console.log(element); const record = {}; record._id = element._id; record.notes = element.notes; @@ -627,7 +625,6 @@ const timeEntrycontroller = function (TimeEntry) { .then((results) => { const data = []; results.forEach((element) => { - console.log(element); const record = {}; record._id = element._id; record.notes = element.notes; diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 34d464583..1c03c2f88 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -69,6 +69,9 @@ const dashboardhelper = function () { { $lte: ['$$timeentry.dateOfWork', pdtend], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, @@ -162,6 +165,7 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); + const entryTypes = ['default', null]; return myTeam.aggregate([ { $match: { @@ -283,6 +287,9 @@ const dashboardhelper = function () { { $lte: ['$$timeentry.dateOfWork', pdtend], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + } ], }, }, @@ -438,6 +445,7 @@ const dashboardhelper = function () { $gte: pdtStart, $lte: pdtEnd, }, + eentryType: { $in: [ 'default', null ] }, personId: userId, }); @@ -575,6 +583,9 @@ const dashboardhelper = function () { { $lte: ['$$timeentry.dateOfWork', todate], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, @@ -652,6 +663,9 @@ const dashboardhelper = function () { { $lte: ['$$timeentry.dateOfWork', todate], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index 0c2a8104d..c812ef03c 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -68,6 +68,9 @@ const reporthelper = function () { moment(pstEnd).format("YYYY-MM-DD"), ], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index a94aaee94..30bb220e9 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -113,6 +113,9 @@ const taskHelper = function () { { $lte: ['$$timeentry.dateOfWork', pdtend], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, @@ -357,6 +360,9 @@ const taskHelper = function () { { $lte: ['$$timeentry.dateOfWork', pdtend], }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, ], }, }, diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index af416f1a7..e1ca1fb7f 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -565,7 +565,8 @@ const userHelper = function () { $and: [ { $eq: ["$isTangible", true] }, { $gte: ["$dateOfWork", startOfLastWeek] }, - { $lte: ["$dateOfWork", endOfLastWeek] } + { $lte: ["$dateOfWork", endOfLastWeek] }, + { $in: ['$entryType', 'default', null] } ] } } From 55c5e16eacd9071adfa8176e94113637e6efb791 Mon Sep 17 00:00:00 2001 From: Nathan Hoffman Date: Tue, 24 Oct 2023 21:12:17 -0700 Subject: [PATCH 017/129] Fix hasPermission() arguments and add catches. --- src/controllers/rolePresetsController.js | 8 ++++---- src/utilities/permissions.js | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/controllers/rolePresetsController.js b/src/controllers/rolePresetsController.js index 3642627f7..3a7dc18c8 100644 --- a/src/controllers/rolePresetsController.js +++ b/src/controllers/rolePresetsController.js @@ -2,7 +2,7 @@ const { hasPermission } = require('../utilities/permissions'); const rolePresetsController = function (Preset) { const getPresetsByRole = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putRole')) { + if (!await hasPermission(req.body.requestor, 'putRole')) { res.status(403).send('You are not authorized to make changes to roles.'); return; } @@ -14,7 +14,7 @@ const rolePresetsController = function (Preset) { }; const createNewPreset = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putRole')) { + if (!await hasPermission(req.body.requestor, 'putRole')) { res.status(403).send('You are not authorized to make changes to roles.'); return; } @@ -34,7 +34,7 @@ const rolePresetsController = function (Preset) { }; const updatePresetById = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putRole')) { + if (!await hasPermission(req.body.requestor, 'putRole')) { res.status(403).send('You are not authorized to make changes to roles.'); return; } @@ -53,7 +53,7 @@ const rolePresetsController = function (Preset) { }; const deletePresetById = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putRole')) { + if (!await hasPermission(req.body.requestor, 'putRole')) { res.status(403).send('You are not authorized to make changes to roles.'); return; } diff --git a/src/utilities/permissions.js b/src/utilities/permissions.js index 7b5d4a245..ebf35b2a1 100644 --- a/src/utilities/permissions.js +++ b/src/utilities/permissions.js @@ -4,12 +4,14 @@ const UserProfile = require('../models/userProfile'); const hasRolePermission = async (role, action) => Role.findOne({ roleName: role }) .exec() - .then(({ permissions }) => permissions.includes(action)); + .then(({ permissions }) => permissions.includes(action)) + .catch(false); const hasIndividualPermission = async (userId, action) => UserProfile.findById(userId) .select('permissions') .exec() - .then(({ permissions }) => permissions.frontPermissions.includes(action)); + .then(({ permissions }) => permissions.frontPermissions.includes(action)) + .catch(false); const hasPermission = async (requestor, action) => await hasRolePermission(requestor.role, action) || hasIndividualPermission(requestor.requestorId, action); From c58cdd3401d8364653dca9d039dd06e5c36f679e Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Thu, 26 Oct 2023 16:04:14 -0700 Subject: [PATCH 018/129] update record api for material , get bm active projects api --- .../bmdashboard/bmMaterialsController.js | 24 ++++++++++++- .../bmdashboard/bmProjectsController.js | 35 +++++++++++++++++++ src/routes/bmdashboard/bmMaterialsRouter.js | 3 ++ src/routes/bmdashboard/bmProjectsRouter.js | 13 +++++++ src/startup/routes.js | 2 ++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/controllers/bmdashboard/bmProjectsController.js create mode 100644 src/routes/bmdashboard/bmProjectsRouter.js diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index a31ed460e..1fb1fc792 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -42,7 +42,29 @@ const bmMaterialsController = function (ItemMaterial) { res.json(err); } }; - return { bmMaterialsList }; + + const bmPostMaterialUpdateRecord = function (req, res) { + console.log(req.body); + ItemMaterial.update( + { "_id": req.body.material._id }, + { + $push: { + updateRecord: { + date : req.body.date, + createdBy : req.body.requestor.requestorId, + action : req.body.action, + cause : req.body.cause, + quantity : req.body.quantity, + description : req.body.description + } + } + } + ) + .then(results => res.status(200).send(results)) + .catch(error => res.status(500).send(error)) + }; + return { bmMaterialsList, + bmPostMaterialUpdateRecord }; }; module.exports = bmMaterialsController; \ No newline at end of file diff --git a/src/controllers/bmdashboard/bmProjectsController.js b/src/controllers/bmdashboard/bmProjectsController.js new file mode 100644 index 000000000..42ee507eb --- /dev/null +++ b/src/controllers/bmdashboard/bmProjectsController.js @@ -0,0 +1,35 @@ +const mongoose = require('mongoose'); +const UserProfile = require('../../models/userProfile') + +const bmProjectsController = function () { + + //Get current user's Housing/Building projects + const getUserActiveBMProjects = function (req, res) { + try { + const userId = req.body.requestor.requestorId; + UserProfile.findById(userId) + .populate([ + { + path: 'projects', + select: '_id projectName category isActive', + match: { category: 'Housing' }, + } + ]) + .select({ + projects: 1 + }) + .then((results) => { + res.status(200).send(results); + }) + .catch(error => res.status(500).send(error)); + } + + catch (err) { + res.json(err); + } + + }; + return { getUserActiveBMProjects }; +} + +module.exports = bmProjectsController; \ No newline at end of file diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index ab8a67388..0be779cd7 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -7,6 +7,9 @@ const controller = require('../../controllers/bmdashboard/bmMaterialsController' materialsRouter.route('/materials') .get(controller.bmMaterialsList); +materialsRouter.route('/addUpdateMaterialRecord') + .post(controller.bmPostMaterialUpdateRecord); + return materialsRouter; } diff --git a/src/routes/bmdashboard/bmProjectsRouter.js b/src/routes/bmdashboard/bmProjectsRouter.js new file mode 100644 index 000000000..cec4b16e9 --- /dev/null +++ b/src/routes/bmdashboard/bmProjectsRouter.js @@ -0,0 +1,13 @@ +const express = require('express'); + +const routes = function () { +const materialsRouter = express.Router(); +const controller = require('../../controllers/bmdashboard/bmProjectsController')(); + +materialsRouter.route('/getUserActiveBMProjects') + .get(controller.getUserActiveBMProjects); + + return materialsRouter; +} + +module.exports = routes; \ No newline at end of file diff --git a/src/startup/routes.js b/src/startup/routes.js index 2fd7337a6..00d8a3fec 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -61,6 +61,7 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); +const bmProjectsRouter = require('../routes/bmdashboard/bmProjectsRouter')() module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -96,4 +97,5 @@ module.exports = function (app) { // bm dashboard app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); + app.use('/api/bm', bmProjectsRouter); }; From edcdbba50d80d6334f0ae02466185588802d2fd7 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Thu, 26 Oct 2023 16:04:40 -0700 Subject: [PATCH 019/129] update record api for material , get bm active projects api --- .../bmdashboard/bmMaterialsController.js | 52 +- .../bmdashboard/bmProjectsController.js | 18 +- src/controllers/dashBoardController.js | 92 +-- src/controllers/isEmailExistsController.js | 22 +- .../profileInitialSetupController.js | 89 ++- src/controllers/teamController.js | 2 +- src/controllers/userProfileController.js | 106 ++- src/helpers/dashboardhelper.js | 4 +- src/helpers/reporthelper.js | 94 +-- src/helpers/taskHelper.js | 4 +- src/helpers/userHelper.js | 603 +++++++++--------- src/models/badge.js | 2 +- src/models/inventoryItemMaterial.js | 6 +- src/models/inventoryItemType.js | 6 +- src/models/profileInitialSetupToken.js | 4 +- src/models/userProfile.js | 4 +- src/routes/bmdashboard/bmMaterialsRouter.js | 4 +- src/routes/bmdashboard/bmProjectsRouter.js | 4 +- src/routes/isEmailExistsRouter.js | 14 +- src/routes/profileInitialSetupRouter.js | 6 +- src/startup/routes.js | 2 +- 21 files changed, 548 insertions(+), 590 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 1fb1fc792..09fa91ab8 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -1,4 +1,4 @@ -const mongoose = require('mongoose') +const mongoose = require('mongoose'); const bmMaterialsController = function (ItemMaterial) { const bmMaterialsList = async function _matsList(req, res) { @@ -7,37 +7,37 @@ const bmMaterialsController = function (ItemMaterial) { .populate([ { path: 'project', - select: '_id projectName' + select: '_id projectName', }, { path: 'inventoryItemType', - select: '_id name uom totalStock totalAvailable' + select: '_id name uom totalStock totalAvailable', }, { path: 'usageRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } + select: '_id firstName lastName', + }, }, { path: 'updateRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } + select: '_id firstName lastName', + }, }, { path: 'purchaseRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } - } + select: '_id firstName lastName', + }, + }, ]) .exec() .then(results => res.status(200).send(results)) - .catch(error => res.status(500).send(error)) + .catch(error => res.status(500).send(error)); } catch (err) { res.json(err); } @@ -46,25 +46,27 @@ const bmMaterialsController = function (ItemMaterial) { const bmPostMaterialUpdateRecord = function (req, res) { console.log(req.body); ItemMaterial.update( - { "_id": req.body.material._id }, + { _id: req.body.material._id }, { $push: { updateRecord: { - date : req.body.date, - createdBy : req.body.requestor.requestorId, - action : req.body.action, - cause : req.body.cause, - quantity : req.body.quantity, - description : req.body.description - } - } - } + date: req.body.date, + createdBy: req.body.requestor.requestorId, + action: req.body.action, + cause: req.body.cause, + quantity: req.body.quantity, + description: req.body.description, + }, + }, + }, ) .then(results => res.status(200).send(results)) - .catch(error => res.status(500).send(error)) + .catch(error => res.status(500).send(error)); }; - return { bmMaterialsList, - bmPostMaterialUpdateRecord }; + return { + bmMaterialsList, + bmPostMaterialUpdateRecord, +}; }; -module.exports = bmMaterialsController; \ No newline at end of file +module.exports = bmMaterialsController; diff --git a/src/controllers/bmdashboard/bmProjectsController.js b/src/controllers/bmdashboard/bmProjectsController.js index 42ee507eb..3ff0f851a 100644 --- a/src/controllers/bmdashboard/bmProjectsController.js +++ b/src/controllers/bmdashboard/bmProjectsController.js @@ -1,9 +1,8 @@ const mongoose = require('mongoose'); -const UserProfile = require('../../models/userProfile') +const UserProfile = require('../../models/userProfile'); const bmProjectsController = function () { - - //Get current user's Housing/Building projects + // Get current user's Housing/Building projects const getUserActiveBMProjects = function (req, res) { try { const userId = req.body.requestor.requestorId; @@ -13,23 +12,20 @@ const bmProjectsController = function () { path: 'projects', select: '_id projectName category isActive', match: { category: 'Housing' }, - } + }, ]) .select({ - projects: 1 + projects: 1, }) .then((results) => { res.status(200).send(results); }) .catch(error => res.status(500).send(error)); - } - - catch (err) { + } catch (err) { res.json(err); } - }; return { getUserActiveBMProjects }; -} +}; -module.exports = bmProjectsController; \ No newline at end of file +module.exports = bmProjectsController; diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index c9cdbd588..c7f9c3973 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -1,8 +1,8 @@ -const mongoose = require("mongoose"); -const path = require("path"); -const fs = require("fs/promises"); -const dashboardhelper = require("../helpers/dashboardhelper")(); -const emailSender = require("../utilities/emailSender"); +const path = require('path'); +const fs = require('fs/promises'); +const mongoose = require('mongoose'); +const dashboardhelper = require('../helpers/dashboardhelper')(); +const emailSender = require('../utilities/emailSender'); const dashboardcontroller = function () { const dashboarddata = function (req, res) { @@ -20,13 +20,13 @@ const dashboardcontroller = function () { const laborthismonth = dashboardhelper.laborthismonth( userId, req.params.fromDate, - req.params.toDate + req.params.toDate, ); laborthismonth.then((results) => { if (!results || results.length === 0) { const emptyresult = [ { - projectName: "", + projectName: '', timeSpent_hrs: 0, }, ]; @@ -42,7 +42,7 @@ const dashboardcontroller = function () { const laborthisweek = dashboardhelper.laborthisweek( userId, req.params.fromDate, - req.params.toDate + req.params.toDate, ); laborthisweek.then((results) => { res.send(results).status(200); @@ -63,7 +63,7 @@ const dashboardcontroller = function () { }); } }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }; const orgData = function (req, res) { @@ -73,7 +73,7 @@ const dashboardcontroller = function () { .then((results) => { res.status(200).send(results[0]); }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }; const getBugReportEmailBody = function ( @@ -85,7 +85,7 @@ const dashboardcontroller = function () { expected, actual, visual, - severity + severity, ) { const text = `New Bug Report From ${firstName} ${lastName}:

[Feature Name] Bug Title:

@@ -130,32 +130,32 @@ const dashboardcontroller = function () { expected, actual, visual, - severity + severity, ); try { emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', `Bug Rport from ${firstName} ${lastName}`, emailBody, - email + email, ); - res.status(200).send("Success"); + res.status(200).send('Success'); } catch { - res.status(500).send("Failed"); + res.status(500).send('Failed'); } }; const suggestionData = { suggestion: [ - "Identify and remedy poor client and/or user service experiences", - "Identify bright spots and enhance positive service experiences", - "Make fundamental changes to our programs and/or operations", - "Inform the development of new programs/projects", - "Identify where we are less inclusive or equitable across demographic groups", - "Strengthen relationships with the people we serve", + 'Identify and remedy poor client and/or user service experiences', + 'Identify bright spots and enhance positive service experiences', + 'Make fundamental changes to our programs and/or operations', + 'Inform the development of new programs/projects', + 'Identify where we are less inclusive or equitable across demographic groups', + 'Strengthen relationships with the people we serve', "Understand people's needs and how we can help them achieve their goals", - "Other", + 'Other', ], field: [], }; @@ -164,8 +164,8 @@ const dashboardcontroller = function () { let fieldaaray = []; if (suggestionData.field.length) { fieldaaray = suggestionData.field.map( - (item) => `

${item}

-

${args[3][item]}

` + item => `

${item}

+

${args[3][item]}

`, ); } const text = `New Suggestion: @@ -173,7 +173,7 @@ const dashboardcontroller = function () {

${args[0]}

Suggestion:

${args[1]}

- ${fieldaaray.length > 0 ? fieldaaray : ""} + ${fieldaaray.length > 0 ? fieldaaray : ''}

Wants Feedback:

${args[2]}

Thank you,
@@ -184,22 +184,24 @@ const dashboardcontroller = function () { // send suggestion email const sendMakeSuggestion = async (req, res) => { - const { suggestioncate, suggestion, confirm, ...rest } = req.body; + const { + suggestioncate, suggestion, confirm, ...rest +} = req.body; const emailBody = await getsuggestionEmailBody( suggestioncate, suggestion, confirm, - rest + rest, ); try { emailSender( - "onecommunityglobal@gmail.com", - "A new suggestion", - emailBody + 'onecommunityglobal@gmail.com', + 'A new suggestion', + emailBody, ); - res.status(200).send("Success"); + res.status(200).send('Success'); } catch { - res.status(500).send("Failed"); + res.status(500).send('Failed'); } }; @@ -208,40 +210,40 @@ const dashboardcontroller = function () { if (suggestionData) { res.status(200).send(suggestionData); } else { - res.status(404).send("Suggestion data not found."); + res.status(404).send('Suggestion data not found.'); } } catch (error) { - console.error("Error getting suggestion data:", error); - res.status(500).send("Internal Server Error"); + console.error('Error getting suggestion data:', error); + res.status(500).send('Internal Server Error'); } }; const editSuggestionOption = async (req, res) => { try { if (req.body.suggestion) { - if (req.body.action === "add") { + if (req.body.action === 'add') { suggestionData.suggestion.unshift(req.body.newField); } - if (req.body.action === "delete") { + if (req.body.action === 'delete') { suggestionData.suggestion = suggestionData.suggestion.filter( - (item, index) => index + 1 !== +req.body.newField + (item, index) => index + 1 !== +req.body.newField, ); } } else { - if (req.body.action === "add") { + if (req.body.action === 'add') { suggestionData.field.unshift(req.body.newField); } - if (req.body.action === "delete") { + if (req.body.action === 'delete') { suggestionData.field = suggestionData.field.filter( - (item) => item !== req.body.newField + item => item !== req.body.newField, ); } } - res.status(200).send("success"); + res.status(200).send('success'); } catch (error) { - console.error("Error editing suggestion option:", error); - res.status(500).send("Internal Server Error"); + console.error('Error editing suggestion option:', error); + res.status(500).send('Internal Server Error'); } }; diff --git a/src/controllers/isEmailExistsController.js b/src/controllers/isEmailExistsController.js index 2c41efc33..f6009a3c5 100644 --- a/src/controllers/isEmailExistsController.js +++ b/src/controllers/isEmailExistsController.js @@ -1,25 +1,23 @@ const UserProfile = require('../models/userProfile'); const isEmailExistsController = function () { - const isEmailExists = async function (req, res) { - - try { - const userProfile = await UserProfile.findOne({ email: req.params.email }).lean().exec() + try { + const userProfile = await UserProfile.findOne({ email: req.params.email }).lean().exec(); if (userProfile) { - res.status(200).send(`Email, ${userProfile.email}, found.`) + res.status(200).send(`Email, ${userProfile.email}, found.`); } else { - res.status(403).send(`Email, ${req.params.email}, not found.`) + res.status(403).send(`Email, ${req.params.email}, not found.`); } } catch (err) { - console.log(err) + console.log(err); } - } + }; return { - isEmailExists - } -} + isEmailExists, + }; +}; -module.exports = isEmailExistsController +module.exports = isEmailExistsController; diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index 18cf7376c..6914cf48a 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -1,9 +1,9 @@ -const mongoose = require("mongoose"); -const { v4: uuidv4 } = require("uuid"); -const moment = require("moment-timezone"); -const jwt = require("jsonwebtoken"); -const emailSender = require("../utilities/emailSender"); -const config = require("../config"); +const mongoose = require('mongoose'); +const { v4: uuidv4 } = require('uuid'); +const moment = require('moment-timezone'); +const jwt = require('jsonwebtoken'); +const emailSender = require('../utilities/emailSender'); +const config = require('../config'); const cache = require('../utilities/nodeCache')(); // returns the email body that includes the setup link for the recipient. @@ -79,7 +79,7 @@ function informManagerMessage(user) { const profileInitialSetupController = function ( ProfileInitialSetupToken, userProfile, - Project + Project, ) { const { JWT_SECRET } = config; @@ -91,16 +91,16 @@ const profileInitialSetupController = function ( - Generates a link using the token and emails it to the recipient. */ const getSetupToken = async (req, res) => { - let { email, baseUrl,weeklyCommittedHours } = req.body; + let { email, baseUrl, weeklyCommittedHours } = req.body; email = email.toLowerCase(); const token = uuidv4(); - const expiration = moment().tz("America/Los_Angeles").add(1, "week"); + const expiration = moment().tz('America/Los_Angeles').add(1, 'week'); try { const existingEmail = await userProfile.findOne({ - email: email, + email, }); if (existingEmail) { - res.status(400).send("email already in use"); + res.status(400).send('email already in use'); } else { await ProfileInitialSetupToken.findOneAndDelete({ email }); @@ -116,10 +116,10 @@ const profileInitialSetupController = function ( emailSender( email, - "NEEDED: Complete your One Community profile setup", + 'NEEDED: Complete your One Community profile setup', sendLinkMessage(link), null, - null + null, ); res.status(200).send(link); @@ -136,7 +136,7 @@ const profileInitialSetupController = function ( */ const validateSetupToken = async (req, res) => { const { token } = req.body; - const currentMoment = moment.tz("America/Los_Angeles"); + const currentMoment = moment.tz('America/Los_Angeles'); try { const foundToken = await ProfileInitialSetupToken.findOne({ token }); @@ -146,10 +146,10 @@ const profileInitialSetupController = function ( if (expirationMoment.isAfter(currentMoment)) { res.status(200).send(foundToken); } else { - res.status(400).send("Invalid token"); + res.status(400).send('Invalid token'); } } else { - res.status(404).send("Token not found"); + res.status(404).send('Token not found'); } } catch (error) { res.status(500).send(`Error finding token: ${error}`); @@ -167,31 +167,30 @@ const profileInitialSetupController = function ( */ const setUpNewUser = async (req, res) => { const { token } = req.body; - const currentMoment = moment.tz("America/Los_Angeles"); + const currentMoment = moment.tz('America/Los_Angeles'); try { const foundToken = await ProfileInitialSetupToken.findOne({ token }); const existingEmail = await userProfile.findOne({ email: foundToken.email, }); if (existingEmail) { - res.status(400).send("email already in use"); - } else { - if (foundToken) { + res.status(400).send('email already in use'); + } else if (foundToken) { const expirationMoment = moment(foundToken.expiration); if (expirationMoment.isAfter(currentMoment)) { const defaultProject = await Project.findOne({ - projectName: "Orientation and Initial Setup", + projectName: 'Orientation and Initial Setup', }); const newUser = new userProfile(); newUser.password = req.body.password; - newUser.role = "Volunteer"; + newUser.role = 'Volunteer'; newUser.firstName = req.body.firstName; newUser.lastName = req.body.lastName; newUser.jobTitle = req.body.jobTitle; newUser.phoneNumber = req.body.phoneNumber; - newUser.bio = ""; + newUser.bio = ''; newUser.weeklycommittedHours = foundToken.weeklyCommittedHours; newUser.weeklycommittedHoursHistory = [ { @@ -205,32 +204,31 @@ const profileInitialSetupController = function ( newUser.projects = Array.from(new Set([defaultProject])); newUser.createdDate = Date.now(); newUser.email = req.body.email; - newUser.weeklySummaries = [{ summary: "" }]; + newUser.weeklySummaries = [{ summary: '' }]; newUser.weeklySummariesCount = 0; - newUser.weeklySummaryOption = "Required"; - newUser.mediaUrl = ""; + newUser.weeklySummaryOption = 'Required'; + newUser.mediaUrl = ''; newUser.collaborationPreference = req.body.collaborationPreference; - newUser.timeZone = req.body.timeZone || "America/Los_Angeles"; + newUser.timeZone = req.body.timeZone || 'America/Los_Angeles'; newUser.location = req.body.location; newUser.permissions = { frontPermissions: [], - backPermissions: [] - } - newUser.bioPosted = "default"; + backPermissions: [], + }; + newUser.bioPosted = 'default'; newUser.privacySettings.email = req.body.privacySettings.email; - newUser.privacySettings.phoneNumber = - req.body.privacySettings.phoneNumber; - newUser.teamCode = ""; + newUser.privacySettings.phoneNumber = req.body.privacySettings.phoneNumber; + newUser.teamCode = ''; newUser.isFirstTimelog = true; const savedUser = await newUser.save(); emailSender( - process.env.MANAGER_EMAIL || "jae@onecommunityglobal.org", // "jae@onecommunityglobal.org" + process.env.MANAGER_EMAIL || 'jae@onecommunityglobal.org', // "jae@onecommunityglobal.org" `NEW USER REGISTERED: ${savedUser.firstName} ${savedUser.lastName}`, informManagerMessage(savedUser), null, - null + null, ); await ProfileInitialSetupToken.findByIdAndDelete(foundToken._id); @@ -240,14 +238,14 @@ const profileInitialSetupController = function ( permissions: savedUser.permissions, expiryTimestamp: moment().add( config.TOKEN.Lifetime, - config.TOKEN.Units + config.TOKEN.Units, ), }; const token = jwt.sign(jwtPayload, JWT_SECRET); res.send({ token }).status(200); - + const NewUserCache = { permissions: savedUser.permissions, isActive: true, @@ -260,18 +258,15 @@ const profileInitialSetupController = function ( email: savedUser.email, }; - const allUserCache = JSON.parse(cache.getCache("allusers")); + const allUserCache = JSON.parse(cache.getCache('allusers')); allUserCache.push(NewUserCache); - cache.setCache("allusers", JSON.stringify(allUserCache)); - - + cache.setCache('allusers', JSON.stringify(allUserCache)); } else { - res.status(400).send("Token is expired"); + res.status(400).send('Token is expired'); } } else { - res.status(400).send("Invalid token"); + res.status(400).send('Invalid token'); } - } } catch (error) { res.status(500).send(`Error: ${error}`); } @@ -283,17 +278,15 @@ const profileInitialSetupController = function ( - sends the API Key as response */ const getTimeZoneAPIKeyByToken = async (req, res) => { - const token = req.body.token; + const { token } = req.body; const premiumKey = process.env.TIMEZONE_PREMIUM_KEY; const foundToken = await ProfileInitialSetupToken.findOne({ token }); if (foundToken) { res.status(200).send({ userAPIKey: premiumKey }); - return; } else { - res.status(403).send("Unauthorized Request"); - return; + res.status(403).send('Unauthorized Request'); } }; diff --git a/src/controllers/teamController.js b/src/controllers/teamController.js index c82b9e5ce..756f386d5 100644 --- a/src/controllers/teamController.js +++ b/src/controllers/teamController.js @@ -115,7 +115,7 @@ const teamcontroller = function (Team) { users.forEach((element) => { const { userId, operation } = element; // if user's profile is stored in cache, clear it so when you visit their profile page it will be up to date - if(cache.hasCache(`user-${userId}`)) cache.removeCache(`user-${userId}`); + if (cache.hasCache(`user-${userId}`)) cache.removeCache(`user-${userId}`); if (operation === 'Assign') { assignlist.push(userId); diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 6dc571b39..aed8f1788 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -59,27 +59,25 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - "_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate" + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', ) .sort({ lastName: 1, }) .then((results) => { if (!results) { - if (cache.getCache("allusers")) { - const getData = JSON.parse(cache.getCache("allusers")); + if (cache.getCache('allusers')) { + const getData = JSON.parse(cache.getCache('allusers')); res.status(200).send(getData); return; - }else{ - res.status(500).send({ error: "User result was invalid" }); - return; } + res.status(500).send({ error: 'User result was invalid' }); + return; } - cache.setCache("allusers", JSON.stringify(results)); + cache.setCache('allusers', JSON.stringify(results)); res.status(200).send(results); }) - .catch((error) => res.status(404).send(error)); - + .catch(error => res.status(404).send(error)); }; const getProjectMembers = async function (req, res) { @@ -93,14 +91,14 @@ const userProfileController = function (UserProfile) { $in: [req.params.projectId], }, }, - "_id firstName email", + '_id firstName email', (err, profiles) => { if (err) { - res.status(404).send("Error finding user profiles"); + res.status(404).send('Error finding user profiles'); return; } res.json(profiles); - } + }, ); }; @@ -118,15 +116,15 @@ const userProfileController = function (UserProfile) { const userByEmail = await UserProfile.findOne({ email: { $regex: escapeRegex(req.body.email), - $options: "i", + $options: 'i', }, }); if (userByEmail) { res.status(400).send({ error: - "That email address is already in use. Please choose another email address.", - type: "email", + 'That email address is already in use. Please choose another email address.', + type: 'email', }); return; } @@ -145,8 +143,8 @@ const userProfileController = function (UserProfile) { if (userByPhoneNumber) { res.status(400).send({ error: - "That phone number is already in use. Please choose another number.", - type: "phoneNumber", + 'That phone number is already in use. Please choose another number.', + type: 'phoneNumber', }); return; } @@ -160,8 +158,8 @@ const userProfileController = function (UserProfile) { if (userDuplicateName && !req.body.allowsDuplicateName) { res.status(400).send({ error: - "That name is already in use. Please confirm if you want to use this name.", - type: "name", + 'That name is already in use. Please confirm if you want to use this name.', + type: 'name', }); return; } @@ -188,15 +186,15 @@ const userProfileController = function (UserProfile) { up.projects = Array.from(new Set(req.body.projects)); up.createdDate = req.body.createdDate; up.email = req.body.email; - up.weeklySummaries = req.body.weeklySummaries || [{ summary: "" }]; + up.weeklySummaries = req.body.weeklySummaries || [{ summary: '' }]; up.weeklySummariesCount = req.body.weeklySummariesCount || 0; up.weeklySummaryOption = req.body.weeklySummaryOption; - up.mediaUrl = req.body.mediaUrl || ""; - up.collaborationPreference = req.body.collaborationPreference || ""; - up.timeZone = req.body.timeZone || "America/Los_Angeles"; + up.mediaUrl = req.body.mediaUrl || ''; + up.collaborationPreference = req.body.collaborationPreference || ''; + up.timeZone = req.body.timeZone || 'America/Los_Angeles'; up.location = req.body.location; up.permissions = req.body.permissions; - up.bioPosted = req.body.bioPosted || "default"; + up.bioPosted = req.body.bioPosted || 'default'; up.isFirstTimelog = true; up.save() @@ -218,11 +216,11 @@ const userProfileController = function (UserProfile) { lastName: up.lastName, email: up.email, }; - const allUserCache = JSON.parse(cache.getCache("allusers")); + const allUserCache = JSON.parse(cache.getCache('allusers')); allUserCache.push(userCache); - cache.setCache("allusers", JSON.stringify(allUserCache)); + cache.setCache('allusers', JSON.stringify(allUserCache)); }) - .catch((error) => res.status(501).send(error)); + .catch(error => res.status(501).send(error)); }; const putUserProfile = async function (req, res) { @@ -233,12 +231,12 @@ const userProfileController = function (UserProfile) { || req.body.requestor.requestorId === userid ) ); - - const canEditTeamCode = req.body.requestor.role === "Owner" || - req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); + + const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); if (!isRequestorAuthorized) { - res.status(403).send("You are not authorized to update this user"); + res.status(403).send('You are not authorized to update this user'); return; } @@ -250,7 +248,7 @@ const userProfileController = function (UserProfile) { cache.removeCache(`user-${userid}`); UserProfile.findById(userid, async (err, record) => { if (err || !record) { - res.status(404).send("No valid records found"); + res.status(404).send('No valid records found'); return; } // validate userprofile pic @@ -269,8 +267,7 @@ const userProfileController = function (UserProfile) { : []; record.jobTitle = req.body.jobTitle; record.emailPubliclyAccessible = req.body.emailPubliclyAccessible; - record.phoneNumberPubliclyAccessible = - req.body.phoneNumberPubliclyAccessible; + record.phoneNumberPubliclyAccessible = req.body.phoneNumberPubliclyAccessible; record.profilePic = req.body.profilePic; record.firstName = req.body.firstName; @@ -292,24 +289,24 @@ const userProfileController = function (UserProfile) { record.isVisible = req.body.isVisible || false; record.isRehireable = req.body.isRehireable || false; record.totalIntangibleHrs = req.body.totalIntangibleHrs; - record.bioPosted = req.body.bioPosted || "default"; + record.bioPosted = req.body.bioPosted || 'default'; record.isFirstTimelog = req.body.isFirstTimelog; - if(!canEditTeamCode && record.teamCode !== req.body.teamCode){ - res.status(403).send("You are not authorized to edit team code."); + if (!canEditTeamCode && record.teamCode !== req.body.teamCode) { + res.status(403).send('You are not authorized to edit team code.'); return; } record.teamCode = req.body.teamCode; // find userData in cache - const isUserInCache = cache.hasCache("allusers"); + const isUserInCache = cache.hasCache('allusers'); let allUserData; let userData; let userIdx; if (isUserInCache) { - allUserData = JSON.parse(cache.getCache("allusers")); - userIdx = allUserData.findIndex((users) => users._id === userid); + allUserData = JSON.parse(cache.getCache('allusers')); + userIdx = allUserData.findIndex(users => users._id === userid); userData = allUserData[userIdx]; } if (await hasPermission(req.body.requestor, 'putUserProfileImportantInfo')) { @@ -324,11 +321,11 @@ const userProfileController = function (UserProfile) { // If their last update was made today, remove that const lasti = record.weeklycommittedHoursHistory.length - 1; const lastChangeDate = moment( - record.weeklycommittedHoursHistory[lasti].dateChanged + record.weeklycommittedHoursHistory[lasti].dateChanged, ); const now = moment(); - if (lastChangeDate.isSame(now, "day")) { + if (lastChangeDate.isSame(now, 'day')) { record.weeklycommittedHoursHistory.pop(); } @@ -341,8 +338,7 @@ const userProfileController = function (UserProfile) { record.weeklycommittedHoursHistory.push(newEntry); } - record.missedHours = - req.body.role === "Core Team" ? req.body?.missedHours ?? 0 : 0; + record.missedHours = req.body.role === 'Core Team' ? req.body?.missedHours ?? 0 : 0; record.adminLinks = req.body.adminLinks; record.teams = Array.from(new Set(req.body.teams)); record.projects = Array.from(new Set(req.body.projects)); @@ -374,8 +370,7 @@ const userProfileController = function (UserProfile) { record.weeklycommittedHoursHistory.push(newEntry); } // then also change the first committed history (index 0) - record.weeklycommittedHoursHistory[0].dateChanged = - record.createdDate; + record.weeklycommittedHoursHistory[0].dateChanged = record.createdDate; } record.bioPosted = req.body.bioPosted || 'default'; @@ -390,7 +385,7 @@ const userProfileController = function (UserProfile) { userData.endDate = record.endDate.toISOString(); } } else { - record.set("endDate", undefined, { strict: false }); + record.set('endDate', undefined, { strict: false }); } if (isUserInCache) { userData.role = record.role; @@ -412,7 +407,7 @@ const userProfileController = function (UserProfile) { results.infringements, results.firstName, results.lastName, - results.email + results.email, ); res.status(200).json({ _id: record._id, @@ -421,10 +416,10 @@ const userProfileController = function (UserProfile) { // update alluser cache if we have cache if (isUserInCache) { allUserData.splice(userIdx, 1, userData); - cache.setCache("allusers", JSON.stringify(allUserData)); + cache.setCache('allusers', JSON.stringify(allUserData)); } }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }); }; @@ -569,15 +564,14 @@ const userProfileController = function (UserProfile) { const { userId } = req.params; const { key, value } = req.body; - if (key === "teamCode") { - const canEditTeamCode = req.body.requestor.role === "Owner" || - req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); + if (key === 'teamCode') { + const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); - if(!canEditTeamCode){ - res.status(403).send("You are not authorized to edit team code."); + if (!canEditTeamCode) { + res.status(403).send('You are not authorized to edit team code.'); return; } - } // remove user from cache, it should be loaded next time diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 34d464583..fe2007281 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -192,7 +192,7 @@ const dashboardhelper = function () { // leaderboard user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ['Owner', 'Core Team'] }, }, { $and: [ @@ -200,7 +200,7 @@ const dashboardhelper = function () { role: 'Administrator', }, { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ] + ], }, { $and: [ diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index 0c2a8104d..3826aa8ed 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -1,5 +1,5 @@ -const moment = require("moment-timezone"); -const userProfile = require("../models/userProfile"); +const moment = require('moment-timezone'); +const userProfile = require('../models/userProfile'); /** * @@ -8,9 +8,9 @@ const userProfile = require("../models/userProfile"); * @returns The absolute value of the difference in weeks between the two input dates. */ const absoluteDifferenceInWeeks = (dateOfWork, pstEnd) => { - dateOfWork = moment(dateOfWork).endOf("week"); - pstEnd = moment(pstEnd).tz("America/Los_Angeles").endOf("week"); - return Math.abs(dateOfWork.diff(pstEnd, "weeks")); + dateOfWork = moment(dateOfWork).endOf('week'); + pstEnd = moment(pstEnd).tz('America/Los_Angeles').endOf('week'); + return Math.abs(dateOfWork.diff(pstEnd, 'weeks')); }; const reporthelper = function () { @@ -23,14 +23,14 @@ const reporthelper = function () { */ const weeklySummaries = async (startWeekIndex, endWeekIndex) => { const pstStart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(startWeekIndex, "week") + .tz('America/Los_Angeles') + .startOf('week') + .subtract(startWeekIndex, 'week') .toDate(); const pstEnd = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(endWeekIndex, "week") + .tz('America/Los_Angeles') + .endOf('week') + .subtract(endWeekIndex, 'week') .toDate(); const results = await userProfile.aggregate([ @@ -39,33 +39,33 @@ const reporthelper = function () { }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", - as: "timeEntries", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', + as: 'timeEntries', }, }, { - $set: { totalTangibleHrs: { $objectToArray: "$hoursByCategory" } }, + $set: { totalTangibleHrs: { $objectToArray: '$hoursByCategory' } }, }, { $project: { timeEntries: { $filter: { - input: "$timeEntries", - as: "timeEntry", + input: '$timeEntries', + as: 'timeEntry', cond: { $and: [ { $gte: [ - "$$timeEntry.dateOfWork", - moment(pstStart).format("YYYY-MM-DD"), + '$$timeEntry.dateOfWork', + moment(pstStart).format('YYYY-MM-DD'), ], }, { $lte: [ - "$$timeEntry.dateOfWork", - moment(pstEnd).format("YYYY-MM-DD"), + '$$timeEntry.dateOfWork', + moment(pstEnd).format('YYYY-MM-DD'), ], }, ], @@ -86,22 +86,22 @@ const reporthelper = function () { bioPosted: 1, badgeCollection: { $filter: { - input: "$badgeCollection", - as: "badge", + input: '$badgeCollection', + as: 'badge', cond: { $or: [ { $and: [ { $gte: [ - "$$badge.earnedDate", - moment(pstStart).format("YYYY-MM-DD"), + '$$badge.earnedDate', + moment(pstStart).format('YYYY-MM-DD'), ], }, { $lte: [ - "$$badge.earnedDate", - moment(pstEnd).format("YYYY-MM-DD"), + '$$badge.earnedDate', + moment(pstEnd).format('YYYY-MM-DD'), ], }, ], @@ -109,10 +109,10 @@ const reporthelper = function () { { $and: [ { - $gte: ["$$badge.lastModified", pstStart], + $gte: ['$$badge.lastModified', pstStart], }, { - $lte: ["$$badge.lastModified", pstEnd], + $lte: ['$$badge.lastModified', pstEnd], }, ], }, @@ -126,15 +126,15 @@ const reporthelper = function () { role: 1, weeklySummaries: { $filter: { - input: "$weeklySummaries", - as: "ws", + input: '$weeklySummaries', + as: 'ws', cond: { $and: [ { - $gte: ["$$ws.dueDate", pstStart], + $gte: ['$$ws.dueDate', pstStart], }, { - $lte: ["$$ws.dueDate", pstEnd], + $lte: ['$$ws.dueDate', pstEnd], }, ], }, @@ -142,13 +142,13 @@ const reporthelper = function () { }, weeklySummariesCount: 1, isTangible: 1, - totalTangibleHrs: { $sum: "$totalTangibleHrs.v" }, + totalTangibleHrs: { $sum: '$totalTangibleHrs.v' }, daysInTeam: { $dateDiff: { - startDate: "$createdDate", + startDate: '$createdDate', endDate: new Date(), - unit: "day", - timezone: "America/Los_Angeles", + unit: 'day', + timezone: 'America/Los_Angeles', }, }, }, @@ -162,8 +162,8 @@ const reporthelper = function () { result.timeEntries.forEach((entry) => { const index = absoluteDifferenceInWeeks(entry.dateOfWork, pstEnd); if ( - result.totalSeconds[index] === undefined || - result.totalSeconds[index] === null + result.totalSeconds[index] === undefined + || result.totalSeconds[index] === null ) { result.totalSeconds[index] = 0; } @@ -189,16 +189,16 @@ const reporthelper = function () { */ const doesDateBelongToWeek = function (dueDate, weekIndex) { const pstStartOfWeek = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(weekIndex, "week"); + .tz('America/Los_Angeles') + .startOf('week') + .subtract(weekIndex, 'week'); const pstEndOfWeek = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(weekIndex, "week"); + .tz('America/Los_Angeles') + .endOf('week') + .subtract(weekIndex, 'week'); const fromDate = moment(pstStartOfWeek).toDate(); const toDate = moment(pstEndOfWeek).toDate(); - return moment(dueDate).isBetween(fromDate, toDate, undefined, "[]"); + return moment(dueDate).isBetween(fromDate, toDate, undefined, '[]'); }; /** diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index a94aaee94..f59dedcbc 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -43,7 +43,7 @@ const taskHelper = function () { // dashboard tasks user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ['Owner', 'Core Team'] }, }, { $and: [ @@ -51,7 +51,7 @@ const taskHelper = function () { role: 'Administrator', }, { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ] + ], }, { $and: [ diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index af416f1a7..a1fd6265a 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -1,28 +1,28 @@ /* eslint-disable no-continue */ /* eslint-disable no-await-in-loop */ -const mongoose = require("mongoose"); -const moment = require("moment-timezone"); -const _ = require("lodash"); -const userProfile = require("../models/userProfile"); -const timeEntries = require("../models/timeentry"); -const badge = require("../models/badge"); -const myTeam = require("./helperModels/myTeam"); -const dashboardHelper = require("./dashboardhelper")(); -const reportHelper = require("./reporthelper")(); -const emailSender = require("../utilities/emailSender"); -const logger = require("../startup/logger"); -const hasPermission = require("../utilities/permissions"); -const Reason = require("../models/reason"); -const token = require("../models/profileInitialSetupToken") +const mongoose = require('mongoose'); +const moment = require('moment-timezone'); +const _ = require('lodash'); +const userProfile = require('../models/userProfile'); +const timeEntries = require('../models/timeentry'); +const badge = require('../models/badge'); +const myTeam = require('./helperModels/myTeam'); +const dashboardHelper = require('./dashboardhelper')(); +const reportHelper = require('./reporthelper')(); +const emailSender = require('../utilities/emailSender'); +const logger = require('../startup/logger'); +const hasPermission = require('../utilities/permissions'); +const Reason = require('../models/reason'); +const token = require('../models/profileInitialSetupToken'); const userHelper = function () { const getTeamMembers = function (user) { const userId = mongoose.Types.ObjectId(user._id); // var teamid = userdetails.teamId; return myTeam.findById(userId).select({ - "myTeam._id": 0, - "myTeam.role": 0, - "myTeam.fullName": 0, + 'myTeam._id': 0, + 'myTeam.role': 0, + 'myTeam.fullName': 0, _id: 0, }); }; @@ -46,40 +46,39 @@ const userHelper = function () { const getUserName = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - return userProfile.findById(userid, "firstName lastName"); + return userProfile.findById(userid, 'firstName lastName'); }; const validateProfilePic = function (profilePic) { - const picParts = profilePic.split("base64"); + const picParts = profilePic.split('base64'); let result = true; const errors = []; if (picParts.length < 2) { return { result: false, - errors: "Invalid image" + errors: 'Invalid image', }; } // validate size const imageSize = picParts[1].length; - const sizeInBytes = - (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; + const sizeInBytes = (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; if (sizeInBytes > 50) { - errors.push("Image size should not exceed 50KB"); + errors.push('Image size should not exceed 50KB'); result = false; } - const imageType = picParts[0].split("/")[1]; - if (imageType !== "jpeg;" && imageType !== "png;") { - errors.push("Image type shoud be either jpeg or png."); + const imageType = picParts[0].split('/')[1]; + if (imageType !== 'jpeg;' && imageType !== 'png;') { + errors.push('Image type shoud be either jpeg or png.'); result = false; } return { result, - errors + errors, }; }; @@ -87,7 +86,7 @@ const userHelper = function () { firstName, lastName, infringement, - totalInfringements + totalInfringements, ) { const text = `Dear ${firstName} ${lastName},

Oops, it looks like something happened and you’ve managed to get a blue square.

@@ -114,11 +113,11 @@ const userHelper = function () { */ const emailWeeklySummariesForAllUsers = async (weekIndex = 1) => { const currentFormattedDate = moment() - .tz("America/Los_Angeles") + .tz('America/Los_Angeles') .format(); logger.logInfo( - `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}` + `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}`, ); const emails = []; @@ -126,13 +125,11 @@ const userHelper = function () { try { const results = await reportHelper.weeklySummaries(weekIndex, weekIndex); - let emailBody = "

Weekly Summaries for all active users:

"; + let emailBody = '

Weekly Summaries for all active users:

'; - const weeklySummaryNotProvidedMessage = - '
Weekly Summary: Not provided!
'; + const weeklySummaryNotProvidedMessage = '
Weekly Summary: Not provided!
'; - const weeklySummaryNotRequiredMessage = - '
Weekly Summary: Not required for this user
'; + const weeklySummaryNotRequiredMessage = '
Weekly Summary: Not required for this user
'; results.sort((a, b) => `${a.firstName} ${a.lastName}`.localeCompare(`${b.firstName} ${b.lastname}`)); @@ -146,7 +143,7 @@ const userHelper = function () { mediaUrl, weeklySummariesCount, weeklycommittedHours, - weeklySummaryOption + weeklySummaryOption, } = result; if (email !== undefined && email !== null) { @@ -163,14 +160,14 @@ const userHelper = function () { let weeklySummaryMessage = weeklySummaryNotProvidedMessage; const colorStyle = (() => { switch (weeklySummaryOption) { - case "Team": + case 'Team': return 'style="color: magenta;"'; - case "Not Required": + case 'Not Required': return 'style="color: green"'; - case "Required": - return ""; + case 'Required': + return ''; default: - return result.weeklySummaryNotReq ? 'style="color: green"' : ""; + return result.weeklySummaryNotReq ? 'style="color: green"' : ''; } })(); // weeklySummaries array should only have one item if any, hence weeklySummaries[0] needs be used to access it. @@ -181,8 +178,8 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz("America/Los_Angeles") - .format("YYYY-MMM-DD")}): + .tz('America/Los_Angeles') + .format('YYYY-MMM-DD')}):
${summary} @@ -207,15 +204,15 @@ const userHelper = function () {

${weeklySummariesCount === 8 ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` - : `

Total Valid Weekly Summaries: ${weeklySummariesCount || - "No valid submissions yet!"}

` + : `

Total Valid Weekly Summaries: ${weeklySummariesCount + || 'No valid submissions yet!'}

` } ${hoursLogged >= weeklycommittedHours ? `

Hours logged: ${hoursLogged.toFixed(2)} / ${weeklycommittedHours}

` : `

Hours logged: ${hoursLogged.toFixed( - 2 + 2, )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage} @@ -225,10 +222,8 @@ const userHelper = function () { // Necessary because our version of node is outdated // and doesn't have String.prototype.replaceAll let emailString = [...new Set(emails)].toString(); - while (emailString.includes(",")) - emailString = emailString.replace(",", "\n"); - while (emailString.includes("\n")) - emailString = emailString.replace("\n", ", "); + while (emailString.includes(',')) emailString = emailString.replace(',', '\n'); + while (emailString.includes('\n')) emailString = emailString.replace('\n', ', '); emailBody += `\n
@@ -240,10 +235,10 @@ const userHelper = function () { `; emailSender( - "onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com", - "Weekly Summaries for all active users...", + 'onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com', + 'Weekly Summaries for all active users...', emailBody, - null + null, ); } catch (err) { logger.logException(err); @@ -265,15 +260,15 @@ const userHelper = function () { $each: [ { dueDate: moment() - .tz("America/Los_Angeles") - .endOf("week"), - summary: "" - } + .tz('America/Los_Angeles') + .endOf('week'), + summary: '', + }, ], $position: 0, - $slice: 4 - } - } + $slice: 4, + }, + }, }) .catch(error => logger.logException(error)); }; @@ -287,11 +282,11 @@ const userHelper = function () { const assignBlueSquareForTimeNotMet = async () => { try { const currentFormattedDate = moment() - .tz("America/Los_Angeles") + .tz('America/Los_Angeles') .format(); const currentUTCDate = moment - .tz("America/Los_Angeles") - .startOf("day") + .tz('America/Los_Angeles') + .startOf('day') .toISOString(); logger.logInfo( @@ -301,24 +296,24 @@ const userHelper = function () { ); const pdtStartOfLastWeek = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(1, "week"); + .tz('America/Los_Angeles') + .startOf('week') + .subtract(1, 'week'); const pdtEndOfLastWeek = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(1, "week"); + .tz('America/Los_Angeles') + .endOf('week') + .subtract(1, 'week'); const users = await userProfile.find( { isActive: true }, - "_id weeklycommittedHours weeklySummaries missedHours" + '_id weeklycommittedHours weeklySummaries missedHours', ); - //this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be - //targeted as spam - //There's no need to put Promise.all here + // this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be + // targeted as spam + // There's no need to put Promise.all here for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -345,13 +340,12 @@ const userHelper = function () { const results = await dashboardHelper.laborthisweek( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek + pdtEndOfLastWeek, ); const { timeSpent_hrs: timeSpent } = results[0]; - const weeklycommittedHours = - user.weeklycommittedHours + (user.missedHours ?? 0); + const weeklycommittedHours = user.weeklycommittedHours + (user.missedHours ?? 0); const timeNotMet = timeSpent < weeklycommittedHours; let description; @@ -360,19 +354,19 @@ const userHelper = function () { personId, { $inc: { - totalTangibleHrs: timeSpent || 0 + totalTangibleHrs: timeSpent || 0, }, $max: { - personalBestMaxHrs: timeSpent || 0 + personalBestMaxHrs: timeSpent || 0, }, $push: { - savedTangibleHrs: { $each: [timeSpent || 0], $slice: -200 } + savedTangibleHrs: { $each: [timeSpent || 0], $slice: -200 }, }, $set: { - lastWeekTangibleHrs: timeSpent || 0 - } + lastWeekTangibleHrs: timeSpent || 0, + }, }, - { new: true } + { new: true }, ); if ( @@ -384,13 +378,13 @@ const userHelper = function () { hasWeeklySummary = true; } - const cutOffDate = moment().subtract(1, "year"); + const cutOffDate = moment().subtract(1, 'year'); const oldInfringements = []; for (let k = 0; k < updateResult?.infringements.length; k += 1) { if ( - updateResult?.infringements && - moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 + updateResult?.infringements + && moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 ) { oldInfringements.push(updateResult.infringements[k]); } else { @@ -403,72 +397,70 @@ const userHelper = function () { personId, { $push: { - oldInfringements: { $each: oldInfringements, $slice: -10 } - } + oldInfringements: { $each: oldInfringements, $slice: -10 }, + }, }, - { new: true } + { new: true }, ); } if (timeNotMet || !hasWeeklySummary) { if (foundReason) { description = foundReason.reason; - } else { - if (timeNotMet && !hasWeeklySummary) { + } else if (timeNotMet && !hasWeeklySummary) { description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } else if (timeNotMet) { description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } else { description = `System auto-assigned infringement for not submitting a weekly summary for the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } - } const infringement = { date: moment() .utc() - .format("YYYY-MM-DD"), - description + .format('YYYY-MM-DD'), + description, }; const status = await userProfile.findByIdAndUpdate( personId, { $push: { - infringements: infringement - } + infringements: infringement, + }, }, - { new: true } + { new: true }, ); emailSender( status.email, - "New Infringement Assigned", + 'New Infringement Assigned', getInfringementEmailBody( status.firstName, status.lastName, infringement, - status.infringements.length + status.infringements.length, ), null, - "onecommunityglobal@gmail.com" + 'onecommunityglobal@gmail.com', ); const categories = await dashboardHelper.laborThisWeekByCategory( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek + pdtEndOfLastWeek, ); if (Array.isArray(categories) && categories.length > 0) { await userProfile.findOneAndUpdate( { _id: personId, categoryTangibleHrs: { $exists: false } }, - { $set: { categoryTangibleHrs: [] } } + { $set: { categoryTangibleHrs: [] } }, ); } else { continue; @@ -478,29 +470,29 @@ const userHelper = function () { const elem = categories[j]; if (elem._id == null) { - elem._id = "Other"; + elem._id = 'Other'; } const updateResult2 = await userProfile.findOneAndUpdate( - { _id: personId, "categoryTangibleHrs.category": elem._id }, - { $inc: { "categoryTangibleHrs.$.hrs": elem.timeSpent_hrs } }, - { new: true } + { _id: personId, 'categoryTangibleHrs.category': elem._id }, + { $inc: { 'categoryTangibleHrs.$.hrs': elem.timeSpent_hrs } }, + { new: true }, ); if (!updateResult2) { await userProfile.findOneAndUpdate( { _id: personId, - "categoryTangibleHrs.category": { $ne: elem._id } + 'categoryTangibleHrs.category': { $ne: elem._id }, }, { $addToSet: { categoryTangibleHrs: { category: elem._id, - hrs: elem.timeSpent_hrs - } - } - } + hrs: elem.timeSpent_hrs, + }, + }, + }, ); } } @@ -512,12 +504,11 @@ const userHelper = function () { // processWeeklySummaries for nonActive users try { - const inactiveUsers = await userProfile.find({ isActive: false }, "_id"); + const inactiveUsers = await userProfile.find({ isActive: false }, '_id'); for (let i = 0; i < inactiveUsers.length; i += 1) { const user = inactiveUsers[i]; await processWeeklySummariesByUserId(mongoose.Types.ObjectId(user._id), false); - } } catch (err) { logger.logException(err); @@ -527,52 +518,52 @@ const userHelper = function () { const applyMissedHourForCoreTeam = async () => { try { const currentDate = moment() - .tz("America/Los_Angeles") + .tz('America/Los_Angeles') .format(); logger.logInfo( - `Job for applying missed hours for Core Team members starting at ${currentDate}` + `Job for applying missed hours for Core Team members starting at ${currentDate}`, ); const startOfLastWeek = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(1, "week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .subtract(1, 'week') + .format('YYYY-MM-DD'); const endOfLastWeek = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(1, "week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .subtract(1, 'week') + .format('YYYY-MM-DD'); const missedHours = await userProfile.aggregate([ { $match: { - role: "Core Team", - isActive: true - } + role: 'Core Team', + isActive: true, + }, }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', pipeline: [ { $match: { $expr: { $and: [ - { $eq: ["$isTangible", true] }, - { $gte: ["$dateOfWork", startOfLastWeek] }, - { $lte: ["$dateOfWork", endOfLastWeek] } - ] - } - } - } + { $eq: ['$isTangible', true] }, + { $gte: ['$dateOfWork', startOfLastWeek] }, + { $lte: ['$dateOfWork', endOfLastWeek] }, + ], + }, + }, + }, ], - as: "timeEntries" - } + as: 'timeEntries', + }, }, { $project: { @@ -591,31 +582,31 @@ const userHelper = function () { { $sum: { $map: { - input: "$timeEntries", - in: "$$this.totalSeconds" - } - } + input: '$timeEntries', + in: '$$this.totalSeconds', + }, + }, }, - 3600 - ] - } - ] + 3600, + ], + }, + ], }, - 0 - ] - } - } - } + 0, + ], + }, + }, + }, ]); const bulkOps = []; - missedHours.forEach(obj => { + missedHours.forEach((obj) => { bulkOps.push({ updateOne: { filter: { _id: obj._id }, - update: { missedHours: obj.missedHours } - } + update: { missedHours: obj.missedHours }, + }, }); }); @@ -627,16 +618,16 @@ const userHelper = function () { const deleteBlueSquareAfterYear = async () => { const currentFormattedDate = moment() - .tz("America/Los_Angeles") + .tz('America/Los_Angeles') .format(); logger.logInfo( - `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}` + `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}`, ); const cutOffDate = moment() - .subtract(1, "year") - .format("YYYY-MM-DD"); + .subtract(1, 'year') + .format('YYYY-MM-DD'); try { const results = await userProfile.updateMany( @@ -645,11 +636,11 @@ const userHelper = function () { $pull: { infringements: { date: { - $lte: cutOffDate - } - } - } - } + $lte: cutOffDate, + }, + }, + }, + }, ); logger.logInfo(results); @@ -660,17 +651,17 @@ const userHelper = function () { const reActivateUser = async () => { const currentFormattedDate = moment() - .tz("America/Los_Angeles") + .tz('America/Los_Angeles') .format(); logger.logInfo( - `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}` + `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}`, ); try { const users = await userProfile.find( { isActive: false, reactivationDate: { $exists: true } }, - "_id isActive reactivationDate" + '_id isActive reactivationDate', ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -679,18 +670,18 @@ const userHelper = function () { user._id, { $set: { - isActive: true + isActive: true, }, $unset: { - endDate: user.endDate - } + endDate: user.endDate, + }, }, - { new: true } + { new: true }, ); logger.logInfo( `User with id: ${user._id} was re-acticated at ${moment() - .tz("America/Los_Angeles") - .format()}.` + .tz('America/Los_Angeles') + .format()}.`, ); const id = user._id; const person = await userProfile.findById(id); @@ -712,7 +703,6 @@ const userHelper = function () { emailSender('onecommunityglobal@gmail.com', subject, emailBody, null, null); - } } } catch (err) { @@ -722,7 +712,6 @@ const userHelper = function () { const notifyInfringements = function (original, current, firstName, lastName, emailAddress) { - if (!current) return; const newOriginal = original.toObject(); const newCurrent = current.toObject(); @@ -737,43 +726,43 @@ const userHelper = function () { getInfringementEmailBody(firstName, lastName, element, totalInfringements), null, - "onecommunityglobal@gmail.com" + 'onecommunityglobal@gmail.com', ); }); }; const replaceBadge = async function (personId, oldBadgeId, newBadgeId) { userProfile.updateOne( - { _id: personId, "badgeCollection.badge": oldBadgeId }, + { _id: personId, 'badgeCollection.badge': oldBadgeId }, { $set: { - "badgeCollection.$.badge": newBadgeId, - "badgeCollection.$.lastModified": Date.now().toString(), - "badgeCollection.$.count": 1 - } + 'badgeCollection.$.badge': newBadgeId, + 'badgeCollection.$.lastModified': Date.now().toString(), + 'badgeCollection.$.count': 1, + }, }, - err => { + (err) => { if (err) { throw new Error(err); } - } + }, ); }; const increaseBadgeCount = async function (personId, badgeId) { - console.log("Increase Badge Count", personId, badgeId); + console.log('Increase Badge Count', personId, badgeId); userProfile.updateOne( - { _id: personId, "badgeCollection.badge": badgeId }, + { _id: personId, 'badgeCollection.badge': badgeId }, { - $inc: { "badgeCollection.$.count": 1 }, - $set: { "badgeCollection.$.lastModified": Date.now().toString() }, - $push: { "badgeCollection.$.earnedDate": earnedDateBadge() } + $inc: { 'badgeCollection.$.count': 1 }, + $set: { 'badgeCollection.$.lastModified': Date.now().toString() }, + $push: { 'badgeCollection.$.earnedDate': earnedDateBadge() }, }, - err => { + (err) => { if (err) { console.log(err); } - } + }, ); }; @@ -781,7 +770,7 @@ const userHelper = function () { personId, badgeId, count = 1, - featured = false + featured = false, ) { console.log('Adding Badge'); userProfile.findByIdAndUpdate( @@ -793,11 +782,11 @@ const userHelper = function () { }, }, }, - err => { + (err) => { if (err) { throw new Error(err); } - } + }, ); }; @@ -806,14 +795,14 @@ const userHelper = function () { personId, { $pull: { - badgeCollection: { badge: badgeId } - } + badgeCollection: { badge: badgeId }, + }, }, - err => { + (err) => { if (err) { throw new Error(err); } - } + }, ); }; @@ -822,18 +811,18 @@ const userHelper = function () { removeDupBadge(personId, badgeId); } else if (count) { userProfile.updateOne( - { _id: personId, "badgeCollection.badge": badgeId }, + { _id: personId, 'badgeCollection.badge': badgeId }, { $set: { - "badgeCollection.$.count": count, - "badgeCollection.$.lastModified": Date.now().toString() - } + 'badgeCollection.$.count': count, + 'badgeCollection.$.lastModified': Date.now().toString(), + }, }, - err => { + (err) => { if (err) { throw new Error(err); } - } + }, ); } }; @@ -841,7 +830,6 @@ const userHelper = function () { // remove the last badge you earned on this streak(not including 1) const removePrevHrBadge = async function (personId, user, badgeCollection, hrs, weeks) { - // Check each Streak Greater than One to check if it works if (weeks < 3) { return; @@ -851,24 +839,24 @@ const userHelper = function () { .aggregate([ { $match: { - type: "X Hours for X Week Streak", + type: 'X Hours for X Week Streak', weeks: { $gt: 1, $lt: weeks }, - totalHrs: hrs - } + totalHrs: hrs, + }, }, { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: "$weeks", + _id: '$weeks', badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" } - } - } - } + $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, + }, + }, + }, ]) - .then(results => { - results.forEach(streak => { - streak.badges.every(bdge => { + .then((results) => { + results.forEach((streak) => { + streak.badges.every((bdge) => { for (let i = 0; i < badgeCollection.length; i += 1) { if ( @@ -881,7 +869,7 @@ const userHelper = function () { changeBadgeCount( personId, badgeCollection[i].badge._id, - badgeCollection[i].badge.count - 1 + badgeCollection[i].badge.count - 1, ); removed = true; return false; @@ -903,7 +891,6 @@ const userHelper = function () { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if (badgeOfType && badgeOfType.months > badgeCollection[i].badge.months) { - removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -911,23 +898,21 @@ const userHelper = function () { } } await badge - .find({ type: "No Infringement Streak" }) + .find({ type: 'No Infringement Streak' }) .sort({ months: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length) { return; } - results.every(elem => { + results.every((elem) => { // Cannot account for time paused yet if (elem.months <= 12) { - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { - if ( - user.infringements.length === 0 || - Math.abs( + user.infringements.length === 0 + || Math.abs( moment().diff( moment(user.infringements[user.infringements?.length - 1].date), @@ -942,7 +927,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -952,12 +937,10 @@ const userHelper = function () { } } } else if (user?.infringements?.length === 0) { - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { - if ( - user.oldInfringements.length === 0 || - Math.abs( + user.oldInfringements.length === 0 + || Math.abs( moment().diff( moment(user.oldInfringements[user.oldInfringements?.length - 1].date), @@ -973,7 +956,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -992,11 +975,11 @@ const userHelper = function () { const checkMinHoursMultiple = async function ( personId, user, - badgeCollection + badgeCollection, ) { const badgesOfType = badgeCollection .map(obj => obj.badge) - .filter(badge => badge.type === 'Minimum Hours Multiple') + .filter(badge => badge.type === 'Minimum Hours Multiple'); await badge .find({ type: 'Minimum Hours Multiple' }) .sort({ multiple: -1 }) @@ -1009,49 +992,47 @@ const userHelper = function () { const elem = results[i]; // making variable elem accessible for below code if ( - user.lastWeekTangibleHrs / user.weeklycommittedHours >= - elem.multiple + user.lastWeekTangibleHrs / user.weeklycommittedHours + >= elem.multiple ) { const theBadge = badgesOfType.find( - (badge) => badge._id.toString() === elem._id.toString() + badge => badge._id.toString() === elem._id.toString(), ); return theBadge ? increaseBadgeCount( personId, - mongoose.Types.ObjectId(theBadge._id) + mongoose.Types.ObjectId(theBadge._id), ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } } - }) + }); }; // 'Personal Max', const checkPersonalMax = async function (personId, user, badgeCollection) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "Personal Max") { + if (badgeCollection[i].badge?.type === 'Personal Max') { if (badgeOfType) { removeDupBadge(personId, badgeOfType._id); } } } - await badge.findOne({ type: "Personal Max" }).then(results => { + await badge.findOne({ type: 'Personal Max' }).then((results) => { if ( - user.lastWeekTangibleHrs && - user.lastWeekTangibleHrs >= 1 && - user.lastWeekTangibleHrs === user.personalBestMaxHrs + user.lastWeekTangibleHrs + && user.lastWeekTangibleHrs >= 1 + && user.lastWeekTangibleHrs === user.personalBestMaxHrs ) { if (badgeOfType) { changeBadgeCount( personId, mongoose.Types.ObjectId(badgeOfType._id), - user.personalBestMaxHrs + user.personalBestMaxHrs, ); } else { - addBadge(personId, mongoose.Types.ObjectId(results._id), user.personalBestMaxHrs); - } } }); @@ -1071,22 +1052,21 @@ const userHelper = function () { userProfile .aggregate([ { $match: { isActive: true } }, - { $group: { _id: 1, maxHours: { $max: "$lastWeekTangibleHrs" } } }, + { $group: { _id: 1, maxHours: { $max: '$lastWeekTangibleHrs' } } }, ]) .then((userResults) => { if (badgeOfType.length > 1) { removeDupBadge(user._id, badgeOfType[0]._id); - } if ( - user.lastWeekTangibleHrs && - user.lastWeekTangibleHrs >= userResults[0].maxHours + user.lastWeekTangibleHrs + && user.lastWeekTangibleHrs >= userResults[0].maxHours ) { if (badgeOfType.length) { increaseBadgeCount( personId, - mongoose.Types.ObjectId(badgeOfType[0]._id) + mongoose.Types.ObjectId(badgeOfType[0]._id), ); } else { addBadge(personId, mongoose.Types.ObjectId(results._id)); @@ -1102,15 +1082,15 @@ const userHelper = function () { // Handle Increasing the 1 week streak badges const badgesOfType = []; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "X Hours for X Week Streak") { + if (badgeCollection[i].badge?.type === 'X Hours for X Week Streak') { badgesOfType.push(badgeCollection[i].badge); } } await badge - .find({ type: "X Hours for X Week Streak", weeks: 1 }) + .find({ type: 'X Hours for X Week Streak', weeks: 1 }) .sort({ totalHrs: -1 }) - .then(results => { - results.every(elem => { + .then((results) => { + results.every((elem) => { if (elem.totalHrs <= user.lastWeekTangibleHrs) { let theBadge; for (let i = 0; i < badgesOfType.length; i += 1) { @@ -1132,21 +1112,21 @@ const userHelper = function () { // Check each Streak Greater than One to check if it works await badge .aggregate([ - { $match: { type: "X Hours for X Week Streak", weeks: { $gt: 1 } } }, + { $match: { type: 'X Hours for X Week Streak', weeks: { $gt: 1 } } }, { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: "$weeks", + _id: '$weeks', badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" } - } - } - } + $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, + }, + }, + }, ]) - .then(results => { + .then((results) => { let lastHr = -1; - results.forEach(streak => { - streak.badges.every(bdge => { + results.forEach((streak) => { + streak.badges.every((bdge) => { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( @@ -1155,12 +1135,11 @@ const userHelper = function () { && badgeCollection[i].badge?.weeks === bdge.weeks ) { if (badgeOfType && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs) { - removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1185,7 +1164,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(bdge._id) + mongoose.Types.ObjectId(bdge._id), ); removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); @@ -1196,12 +1175,11 @@ const userHelper = function () { user, badgeCollection, bdge.hrs, - bdge.weeks + bdge.weeks, ); } else if (badgeOfType && badgeOfType.totalHrs === bdge.hrs) { increaseBadgeCount(personId, mongoose.Types.ObjectId(badgeOfType._id)); removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); - } return false; } @@ -1222,8 +1200,8 @@ const userHelper = function () { let teamMembers; await getTeamMembers({ - _id: personId - }).then(results => { + _id: personId, + }).then((results) => { if (results) { teamMembers = results.myteam; } else { @@ -1242,13 +1220,11 @@ const userHelper = function () { }); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'Lead a team of X+') { if (badgeOfType && badgeOfType.people <= badgeCollection[i].badge.people) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if (badgeOfType && badgeOfType.people > badgeCollection[i].badge.people) { - removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -1256,9 +1232,9 @@ const userHelper = function () { } } await badge - .find({ type: "Lead a team of X+" }) + .find({ type: 'Lead a team of X+' }) .sort({ people: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length) { return; } @@ -1292,40 +1268,40 @@ const userHelper = function () { const checkTotalHrsInCat = async function (personId, user, badgeCollection) { const hoursByCategory = user.hoursByCategory || {}; const categories = [ - "food", - "energy", - "housing", - "education", - "society", - "economics", - "stewardship" + 'food', + 'energy', + 'housing', + 'education', + 'society', + 'economics', + 'stewardship', ]; const badgesOfType = badgeCollection - .filter(object => object.badge.type === "Total Hrs in Category") + .filter(object => object.badge.type === 'Total Hrs in Category') .map(object => object.badge); - categories.forEach(async category => { + categories.forEach(async (category) => { const categoryHrs = Object.keys(hoursByCategory).find( - elem => elem === category + elem => elem === category, ); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - badgeCollection[i].badge?.type === "Total Hrs in Category" && - badgeCollection[i].badge?.category === category + badgeCollection[i].badge?.type === 'Total Hrs in Category' + && badgeCollection[i].badge?.category === category ) { if ( - badgeOfType && - badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1340,15 +1316,15 @@ const userHelper = function () { await badge.find({ type: 'Total Hrs in Category', category: newCatg }) .sort({ totalHrs: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length || !categoryHrs) { return; } - results.every(elem => { + results.every((elem) => { if ( - hoursByCategory[categoryHrs] >= 100 && - hoursByCategory[categoryHrs] >= elem.totalHrs + hoursByCategory[categoryHrs] >= 100 + && hoursByCategory[categoryHrs] >= elem.totalHrs ) { let theBadge; for (let i = 0; i < badgesOfType.length; i += 1) { @@ -1363,13 +1339,13 @@ const userHelper = function () { } if (badgeOfType) { if ( - badgeOfType._id.toString() !== elem._id.toString() && - badgeOfType.totalHrs < elem.totalHrs + badgeOfType._id.toString() !== elem._id.toString() + && badgeOfType.totalHrs < elem.totalHrs ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -1384,9 +1360,8 @@ const userHelper = function () { }; const awardNewBadges = async () => { - console.log("Awarding"); + console.log('Awarding'); try { - const users = await userProfile.find({ isActive: true }).populate('badgeCollection.badge'); @@ -1419,14 +1394,14 @@ const userHelper = function () { { personId: userId, dateOfWork: { $gte: pdtstart, $lte: pdtend }, - isTangible: true + isTangible: true, }, - "totalSeconds" + 'totalSeconds', ) - .then(results => { + .then((results) => { const totalTangibleWeeklySeconds = results.reduce( (acc, { totalSeconds }) => acc + totalSeconds, - 0 + 0, ); return (totalTangibleWeeklySeconds / 3600).toFixed(2); }); @@ -1436,19 +1411,19 @@ const userHelper = function () { try { const users = await userProfile.find( { isActive: true, endDate: { $exists: true } }, - "_id isActive endDate" + '_id isActive endDate', ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; const { endDate } = user; endDate.setHours(endDate.getHours() + 7); - if (moment().isAfter(moment(endDate).add(1, "days"))) { + if (moment().isAfter(moment(endDate).add(1, 'days'))) { await userProfile.findByIdAndUpdate( user._id, user.set({ - isActive: false + isActive: false, }), - { new: true } + { new: true }, ); const id = user._id; const person = await userProfile.findById(id); @@ -1485,7 +1460,7 @@ const userHelper = function () { } catch (error) { logger.logException(err); } - } + }; return { @@ -1502,7 +1477,7 @@ const userHelper = function () { emailWeeklySummariesForAllUsers, awardNewBadges, getTangibleHoursReportedThisWeekByUserId, - deleteExpiredTokens + deleteExpiredTokens, }; }; diff --git a/src/models/badge.js b/src/models/badge.js index 8b2c754e7..e3e93eaaf 100644 --- a/src/models/badge.js +++ b/src/models/badge.js @@ -19,7 +19,7 @@ const Badge = new Schema({ imageUrl: { type: String }, ranking: { type: Number }, description: { type: String }, - showReport: {type: Boolean}, + showReport: { type: Boolean }, }); module.exports = mongoose.model('badge', Badge, 'badges'); diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index e6153af45..8460ecd6e 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -29,12 +29,12 @@ const InventoryItemMaterial = new Schema({ poId: { type: String, required: true }, sellerId: { type: String, required: true }, quantity: { type: Number, required: true }, // adds to stockBought - unitPrice: {type: Number, required: true}, + unitPrice: { type: Number, required: true }, currency: { type: String, required: true }, subtotal: { type: Number, required: true }, tax: { type: Number, required: true }, shipping: { type: Number, required: true }, - }] -}) + }], +}); module.exports = mongoose.model('inventoryItemMaterial', InventoryItemMaterial, 'inventoryMaterial'); diff --git a/src/models/inventoryItemType.js b/src/models/inventoryItemType.js index 321038a84..b7b3ec46f 100644 --- a/src/models/inventoryItemType.js +++ b/src/models/inventoryItemType.js @@ -9,11 +9,9 @@ const InventoryItemType = new Schema({ // creates an item, tracks total amount i uom: { type: String, required: true }, // unit of measurement totalStock: { type: Number, required: true }, // total amount of all stock acquired totalAvailable: { type: Number, required: true }, - projectsUsing: [ {type: mongoose.SchemaTypes.ObjectId, ref: 'project'} ], + projectsUsing: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'project' }], imageUrl: { type: String }, - link: { type: String} + link: { type: String }, }); module.exports = mongoose.model('inventoryItemType', InventoryItemType, 'inventoryItemType'); - - diff --git a/src/models/profileInitialSetupToken.js b/src/models/profileInitialSetupToken.js index 48413fb77..77b830229 100644 --- a/src/models/profileInitialSetupToken.js +++ b/src/models/profileInitialSetupToken.js @@ -10,7 +10,7 @@ const profileInitialSetupTokenSchema = new mongoose.Schema({ type: String, required: true, }, - weeklyCommittedHours : { + weeklyCommittedHours: { type: Number, required: true, default: 10, @@ -21,4 +21,4 @@ const profileInitialSetupTokenSchema = new mongoose.Schema({ }, }); -module.exports = mongoose.model('profileInitialSetupToken', profileInitialSetupTokenSchema, 'profileInitialSetupToken'); \ No newline at end of file +module.exports = mongoose.model('profileInitialSetupToken', profileInitialSetupTokenSchema, 'profileInitialSetupToken'); diff --git a/src/models/userProfile.js b/src/models/userProfile.js index a58d1d293..1a29ec3e0 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -7,7 +7,7 @@ const bcrypt = require('bcryptjs'); const SALT_ROUNDS = 10; const nextDay = new Date(); -nextDay.setDate(nextDay.getDate()+1); +nextDay.setDate(nextDay.getDate() + 1); const userProfileSchema = new Schema({ password: { @@ -153,7 +153,7 @@ const userProfileSchema = new Schema({ isVisible: { type: Boolean, default: false }, weeklySummaryOption: { type: String }, bioPosted: { type: String, default: 'default' }, - isFirstTimelog: { type: Boolean, default: true}, + isFirstTimelog: { type: Boolean, default: true }, teamCode: { type: String, default: '' }, infoCollections: [ { diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index 0be779cd7..a0f8dfea6 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -11,6 +11,6 @@ materialsRouter.route('/addUpdateMaterialRecord') .post(controller.bmPostMaterialUpdateRecord); return materialsRouter; -} +}; -module.exports = routes; \ No newline at end of file +module.exports = routes; diff --git a/src/routes/bmdashboard/bmProjectsRouter.js b/src/routes/bmdashboard/bmProjectsRouter.js index cec4b16e9..a171fc4cd 100644 --- a/src/routes/bmdashboard/bmProjectsRouter.js +++ b/src/routes/bmdashboard/bmProjectsRouter.js @@ -8,6 +8,6 @@ materialsRouter.route('/getUserActiveBMProjects') .get(controller.getUserActiveBMProjects); return materialsRouter; -} +}; -module.exports = routes; \ No newline at end of file +module.exports = routes; diff --git a/src/routes/isEmailExistsRouter.js b/src/routes/isEmailExistsRouter.js index cfb4e6033..d19b14fe2 100644 --- a/src/routes/isEmailExistsRouter.js +++ b/src/routes/isEmailExistsRouter.js @@ -2,14 +2,14 @@ const express = require('express'); const routes = function () { - const controller = require('../controllers/isEmailExistsController')() + const controller = require('../controllers/isEmailExistsController')(); - const isEmailExistsRouter = express.Router() + const isEmailExistsRouter = express.Router(); - isEmailExistsRouter.route("/is-email-exists/:email") - .get(controller.isEmailExists) + isEmailExistsRouter.route('/is-email-exists/:email') + .get(controller.isEmailExists); - return isEmailExistsRouter -} + return isEmailExistsRouter; +}; -module.exports = routes +module.exports = routes; diff --git a/src/routes/profileInitialSetupRouter.js b/src/routes/profileInitialSetupRouter.js index a23c6a868..2091afcad 100644 --- a/src/routes/profileInitialSetupRouter.js +++ b/src/routes/profileInitialSetupRouter.js @@ -5,9 +5,9 @@ const routes = function (ProfileInitialSetupToken, userProfile, Project) { const controller = require('../controllers/profileInitialSetupController')(ProfileInitialSetupToken, userProfile, Project); ProfileInitialSetup.route('/getInitialSetuptoken') .post(controller.getSetupToken); - ProfileInitialSetup.route('/ProfileInitialSetup').post(controller.setUpNewUser) - ProfileInitialSetup.route('/validateToken').post(controller.validateSetupToken) - ProfileInitialSetup.route('/getTimeZoneAPIKeyByToken').post(controller.getTimeZoneAPIKeyByToken) + ProfileInitialSetup.route('/ProfileInitialSetup').post(controller.setUpNewUser); + ProfileInitialSetup.route('/validateToken').post(controller.validateSetupToken); + ProfileInitialSetup.route('/getTimeZoneAPIKeyByToken').post(controller.getTimeZoneAPIKeyByToken); return ProfileInitialSetup; }; diff --git a/src/startup/routes.js b/src/startup/routes.js index 00d8a3fec..7208535d8 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -61,7 +61,7 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); -const bmProjectsRouter = require('../routes/bmdashboard/bmProjectsRouter')() +const bmProjectsRouter = require('../routes/bmdashboard/bmProjectsRouter')(); module.exports = function (app) { app.use('/api', forgotPwdRouter); From cf9d773e07f5d671e1b147af9a56c80ebaef15a2 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 020/129] fix: moving part of logic from the frontend, bug fixes feat: updating timezone --- src/controllers/mapLocationsController.js | 88 ++++++++++++++--------- src/models/mapLocation.js | 15 ++-- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index f01666558..9fa214080 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,27 +1,32 @@ -const userProfile = require('../models/userProfile'); +const UserProfile = require('../models/userProfile'); const cache = require('../utilities/nodeCache')(); + const mapLocationsController = function (MapLocation) { - const getAllLocations = function (req, res) { - const priorText = 'Prior to HGN Data Collection'; - MapLocation.find({}) - .then(results => { - const users = results.map(item => { - return ({ - title: priorText, - firstName: item.firstName !== priorText ? item.firstName : '', - lastName: item.lastName !== priorText ? item.lastName : '', - jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', - location: item.location, - isActive: item.isActive, - _id: item._id - }) - }) - res.send(users).status(200); - - }) - .catch(error => - res.send(error).status(404)); + const getAllLocations = async function (req, res) { + + try { + const users = []; + const results = await UserProfile.find({}, + '_id firstName lastName isActive location jobTitle totalTangibleHrs hoursByCategory' + ); + + results.forEach((item) => { + if ( + (item.location?.coords.lat && item.location?.coords.lng && item.totalTangibleHrs >= 10) || + (item.location?.coords.lat && item.location?.coords.lng && calculateTotalHours(item.hoursByCategory) >= 10) + ) { + users.push(item); + } + }); + + const m_users = await MapLocation.find({}); + + res.status(200).send({ users, m_users }); + } catch (err) { + res.status(404).send(err); + } + }; const deleteLocation = async function (req, res) { @@ -37,7 +42,7 @@ const mapLocationsController = function (MapLocation) { }; const putUserLocation = async function (req, res) { - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } @@ -61,32 +66,36 @@ const mapLocationsController = function (MapLocation) { } }; const updateUserLocation = async function (req, res) { - console.log(req.body) - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } const userType = req.body.type; - const userId= req.body._id; + const userId = req.body._id; const updateData = { - firstName: req.body.firstName, - lastName: req.body.lastName, - jobTitle: req.body.jobTitle, - location: req.body.location, - _id: req.body._id + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + + if (req.body.timeZone) { + updateData.timeZone = req.body.timeZone } try { let response; - if(userType === 'user') { - response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + if (userType === 'user') { + console.log('updateData----', updateData); + response = await UserProfile.findOneAndUpdate({ _id: userId }, { $set: { ...updateData, jobTitle: [updateData.jobTitle] } }, { new: true }); cache.removeCache('allusers') cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); } else { - response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + response = await MapLocation.findOneAndUpdate({ _id: userId }, { $set: updateData }, { new: true }) } - + if (!response) { throw new Error('Something went wrong during saving the location...') } @@ -98,13 +107,22 @@ const mapLocationsController = function (MapLocation) { _id: response._id, type: userType } - + res.status(200).send(newData); } catch (err) { console.log(err.message) res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + + function calculateTotalHours(hoursByCategory) { + let hours = 0; + Object.keys(hoursByCategory).forEach((x) => { + hours += hoursByCategory[x]; + }); + return hours; + } + return { getAllLocations, deleteLocation, diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index 65415239f..851587dc3 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -3,18 +3,13 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const mapLocation = new Schema({ - firstName: { + title: { type: String, - default: 'Prior to HGN Data Collection', - }, - lastName: { - type: String, - default: 'Prior to HGN Data Collection', - }, - jobTitle: { - type: String, - default: 'Prior to HGN Data Collection', + default: 'Prior to HGN Data Collection' }, + firstName: String, + lastName: String, + jobTitle: String, isActive: { type: Boolean, default: false, From 37942f92fa5b215e3d6cead862b455cfc2384087 Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 021/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 6dc571b39..b9b3bb8a0 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -57,9 +57,15 @@ const userProfileController = function (UserProfile) { return; } + if (cache.getCache('allusers')) { + const getData = JSON.parse(cache.getCache('allusers')); + res.status(200).send(getData); + return; + } + UserProfile.find( {}, - "_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate" + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', ) .sort({ lastName: 1, From f63e1e8c19ebef62acfd09d63510911fe32eec4e Mon Sep 17 00:00:00 2001 From: xaanders Date: Thu, 14 Sep 2023 16:40:41 -0400 Subject: [PATCH 022/129] feat: adding location object to user profile schema --- src/models/userProfile.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/models/userProfile.js b/src/models/userProfile.js index a58d1d293..fc0787e57 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -7,7 +7,7 @@ const bcrypt = require('bcryptjs'); const SALT_ROUNDS = 10; const nextDay = new Date(); -nextDay.setDate(nextDay.getDate()+1); +nextDay.setDate(nextDay.getDate() + 1); const userProfileSchema = new Schema({ password: { @@ -81,7 +81,16 @@ const userProfileSchema = new Schema({ infringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], - location: { type: String, default: '' }, + location: { + userProvided: { type: String, default: '' }, + coords: { + lat: { type: Number, default: '' }, + lng: { type: Number, default: '' }, + }, + country: { type: String, default: '' }, + city: { type: String, default: '' } + + }, oldInfringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], @@ -157,9 +166,9 @@ const userProfileSchema = new Schema({ teamCode: { type: String, default: '' }, infoCollections: [ { - areaName: { type: String }, + areaName: { type: String }, areaContent: { type: String }, - }], + }], }); userProfileSchema.pre('save', function (next) { From 61a4357cc1704455f26e8d4db1a475c06fb240a1 Mon Sep 17 00:00:00 2001 From: xaanders Date: Sat, 16 Sep 2023 12:02:02 -0400 Subject: [PATCH 023/129] fix: editing email message after new user creating --- src/controllers/profileInitialSetupController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index 18cf7376c..b4230f1a4 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -67,7 +67,7 @@ function informManagerMessage(user) { Location: - ${user.location} + ${user.location.userProvided}, ${user.location.country}
From 2e4a2afe95deb0a8cc75dc731baf45836a1a9a3d Mon Sep 17 00:00:00 2001 From: xaanders Date: Fri, 22 Sep 2023 19:58:43 -0400 Subject: [PATCH 024/129] feat: adding a initial functionality for adding new people to the map(maplocation controller, maplocation router, maplocation model) --- src/controllers/mapLocationsController.js | 69 +++++++++++++++++++++++ src/models/mapLocation.js | 42 ++++++++++++++ src/routes/mapLocationsRouter.js | 18 ++++++ src/startup/routes.js | 4 ++ 4 files changed, 133 insertions(+) create mode 100644 src/controllers/mapLocationsController.js create mode 100644 src/models/mapLocation.js create mode 100644 src/routes/mapLocationsRouter.js diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js new file mode 100644 index 000000000..83526998d --- /dev/null +++ b/src/controllers/mapLocationsController.js @@ -0,0 +1,69 @@ +const mongoose = require('mongoose'); +const mapLocation = require('../models/mapLocation'); +const { hasPermission } = require('../utilities/permissions'); + +const mapLocationsController = function () { + const getAllLocations = function (req, res) { + console.log('controller:') + console.log(req.body) + + mapLocation.find({}) + .then(results => res.send(results).status(200)) + .catch(error => res.send(error).status(404)); + }; + const deleteLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { + res.status(403).send({ error: 'You are not authorized to delete teams.' }); + return; + } + const { teamId } = req.params; + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send({ error: 'No valid records found' }); + return; + } + const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); + const deleteteam = record.remove(); + + Promise.all([removeteamfromprofile, deleteteam]) + .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) + .catch((errors) => { + res.status(400).send(errors); + }); + }).catch((error) => { + res.status(400).send(error); + }); + }; + const putUserLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + + const { teamId } = req.params; + + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send('No valid records found'); + return; + } + record.teamName = req.body.teamName; + record.isActive = req.body.isActive; + record.createdDatetime = Date.now(); + record.modifiedDatetime = Date.now(); + + record + .save() + .then(results => res.status(201).send(results._id)) + .catch(errors => res.status(400).send(errors)); + }); + }; + + return { + getAllLocations, + deleteLocation, + putUserLocation + }; +}; + +module.exports = mapLocationsController; diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js new file mode 100644 index 000000000..d7b9d82b5 --- /dev/null +++ b/src/models/mapLocation.js @@ -0,0 +1,42 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const mapLocation = new Schema({ + firstName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + lastName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + title: { + type: String, + default: 'Prior to HGN Data Collection', + }, + userProvided: { + type: String, + required: true, + }, + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { + type: String, + required: true, + }, + city: { + type: String, + default: '', + } +}); + +module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js new file mode 100644 index 000000000..e2e780dac --- /dev/null +++ b/src/routes/mapLocationsRouter.js @@ -0,0 +1,18 @@ +const express = require('express'); + +const router = function (mapLocations) { + const controller = require('../controllers/mapLocationsController')(mapLocations); + + const mapRouter = express.Router(); + + mapRouter.route('/mapLocations') + .get(controller.getAllLocations) + .put(controller.putUserLocation); + + mapRouter.route('/mapLocations/:locationId') + .delete(controller.deleteLocation) + + return mapRouter; +}; + +module.exports = router; diff --git a/src/startup/routes.js b/src/startup/routes.js index eae746e24..0f4c7308c 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -21,6 +21,7 @@ const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); const inventoryItemMaterial = require('../models/inventoryItemMaterial'); +const mapLocations = require('../models/mapLocation'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -56,6 +57,8 @@ const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); +const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); + // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); @@ -90,6 +93,7 @@ module.exports = function (app) { app.use('/api', informationRouter); app.use('/api', mouseoverTextRouter); app.use('/api', isEmailExistsRouter); + app.use('/api', mapLocationRouter); // bm dashboard app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); From fb1841d56ccc85322ba724465a7e3ab4f889d315 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Mon, 2 Oct 2023 16:13:42 -0400 Subject: [PATCH 025/129] fix: jobTitle model key, feat: adding isActive status, getting all locations, put new location to a collection --- src/controllers/mapLocationsController.js | 72 +++++++++-------------- src/models/mapLocation.js | 38 +++++++----- 2 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 83526998d..6088de489 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,62 +1,44 @@ -const mongoose = require('mongoose'); -const mapLocation = require('../models/mapLocation'); -const { hasPermission } = require('../utilities/permissions'); - -const mapLocationsController = function () { +const mapLocationsController = function (mapLocation) { const getAllLocations = function (req, res) { - console.log('controller:') - console.log(req.body) mapLocation.find({}) - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => + res.send(results).status(200) + ) + .catch(error => + res.send(error).status(404)); }; const deleteLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { - res.status(403).send({ error: 'You are not authorized to delete teams.' }); + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); return; } - const { teamId } = req.params; - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send({ error: 'No valid records found' }); - return; - } - const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); - const deleteteam = record.remove(); - - Promise.all([removeteamfromprofile, deleteteam]) - .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) - .catch((errors) => { - res.status(400).send(errors); - }); - }).catch((error) => { - res.status(400).send(error); - }); }; const putUserLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + const location = new mapLocation(locationData); - const { teamId } = req.params; - - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send('No valid records found'); - return; + try { + const response = await location.save() + if(!response) { + throw new Error('Something went wrong during saving the location...') } - record.teamName = req.body.teamName; - record.isActive = req.body.isActive; - record.createdDatetime = Date.now(); - record.modifiedDatetime = Date.now(); - - record - .save() - .then(results => res.status(201).send(results._id)) - .catch(errors => res.status(400).send(errors)); - }); + res.status(200).send(response); + } catch (err) { + console.log(err.message) + res.status(500).json({message: err.message || 'Something went wrong...'}); + } }; return { diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index d7b9d82b5..65415239f 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -11,32 +11,38 @@ const mapLocation = new Schema({ type: String, default: 'Prior to HGN Data Collection', }, - title: { + jobTitle: { type: String, default: 'Prior to HGN Data Collection', }, - userProvided: { - type: String, - required: true, + isActive: { + type: Boolean, + default: false, }, - coords: { - lat: { + location: { + userProvided: { type: String, required: true, }, - lng: { + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { type: String, required: true, - } - }, - country: { - type: String, - required: true, + }, + city: { + type: String, + default: '', + }, }, - city: { - type: String, - default: '', - } }); module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); From f2a6e41f321eb7e16aa9e75fc1d2f51039ab9adc Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 026/129] feat: userprofiles sending totalTangibleHrs, update user controller, all manually locations response structure --- src/controllers/mapLocationsController.js | 85 ++++++++++++++++++++--- src/routes/mapLocationsRouter.js | 3 +- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 6088de489..f01666558 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,11 +1,26 @@ -const mapLocationsController = function (mapLocation) { +const userProfile = require('../models/userProfile'); +const cache = require('../utilities/nodeCache')(); + +const mapLocationsController = function (MapLocation) { const getAllLocations = function (req, res) { + const priorText = 'Prior to HGN Data Collection'; + MapLocation.find({}) + .then(results => { + const users = results.map(item => { + return ({ + title: priorText, + firstName: item.firstName !== priorText ? item.firstName : '', + lastName: item.lastName !== priorText ? item.lastName : '', + jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', + location: item.location, + isActive: item.isActive, + _id: item._id + }) + }) + res.send(users).status(200); - mapLocation.find({}) - .then(results => - res.send(results).status(200) - ) - .catch(error => + }) + .catch(error => res.send(error).status(404)); }; const deleteLocation = async function (req, res) { @@ -14,6 +29,11 @@ const mapLocationsController = function (mapLocation) { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationId = req.params.locationId + + MapLocation.findOneAndDelete({ _id: locationId }) + .then(() => res.status(200).send({ message: "The location was successfully removed!" })) + .catch(error => res.status(500).send({ message: error || "Couldn't remove the location" })); }; const putUserLocation = async function (req, res) { @@ -27,24 +47,69 @@ const mapLocationsController = function (mapLocation) { jobTitle: req.body.jobTitle, location: req.body.location, } - const location = new mapLocation(locationData); + const location = new MapLocation(locationData); try { const response = await location.save() - if(!response) { + if (!response) { throw new Error('Something went wrong during saving the location...') } res.status(200).send(response); } catch (err) { console.log(err.message) - res.status(500).json({message: err.message || 'Something went wrong...'}); + res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + const updateUserLocation = async function (req, res) { + console.log(req.body) + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + const userType = req.body.type; + const userId= req.body._id; + const updateData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + _id: req.body._id + } + try { + let response; + if(userType === 'user') { + response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + cache.removeCache('allusers') + cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); + } else { + response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + } + + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + const newData = { + firstName: response.firstName, + lastName: response.lastName, + jobTitle: response.jobTitle, + location: response.location, + _id: response._id, + type: userType + } + + res.status(200).send(newData); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + }; return { getAllLocations, deleteLocation, - putUserLocation + putUserLocation, + updateUserLocation }; }; diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js index e2e780dac..db004ff18 100644 --- a/src/routes/mapLocationsRouter.js +++ b/src/routes/mapLocationsRouter.js @@ -7,7 +7,8 @@ const router = function (mapLocations) { mapRouter.route('/mapLocations') .get(controller.getAllLocations) - .put(controller.putUserLocation); + .put(controller.putUserLocation) + .patch(controller.updateUserLocation); mapRouter.route('/mapLocations/:locationId') .delete(controller.deleteLocation) From f556211d1ed6d46711eda0b89ef1d603d0ee0731 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 027/129] fix: moving part of logic from the frontend, bug fixes feat: updating timezone --- src/controllers/mapLocationsController.js | 88 ++++++++++++++--------- src/models/mapLocation.js | 15 ++-- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index f01666558..9fa214080 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,27 +1,32 @@ -const userProfile = require('../models/userProfile'); +const UserProfile = require('../models/userProfile'); const cache = require('../utilities/nodeCache')(); + const mapLocationsController = function (MapLocation) { - const getAllLocations = function (req, res) { - const priorText = 'Prior to HGN Data Collection'; - MapLocation.find({}) - .then(results => { - const users = results.map(item => { - return ({ - title: priorText, - firstName: item.firstName !== priorText ? item.firstName : '', - lastName: item.lastName !== priorText ? item.lastName : '', - jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', - location: item.location, - isActive: item.isActive, - _id: item._id - }) - }) - res.send(users).status(200); - - }) - .catch(error => - res.send(error).status(404)); + const getAllLocations = async function (req, res) { + + try { + const users = []; + const results = await UserProfile.find({}, + '_id firstName lastName isActive location jobTitle totalTangibleHrs hoursByCategory' + ); + + results.forEach((item) => { + if ( + (item.location?.coords.lat && item.location?.coords.lng && item.totalTangibleHrs >= 10) || + (item.location?.coords.lat && item.location?.coords.lng && calculateTotalHours(item.hoursByCategory) >= 10) + ) { + users.push(item); + } + }); + + const m_users = await MapLocation.find({}); + + res.status(200).send({ users, m_users }); + } catch (err) { + res.status(404).send(err); + } + }; const deleteLocation = async function (req, res) { @@ -37,7 +42,7 @@ const mapLocationsController = function (MapLocation) { }; const putUserLocation = async function (req, res) { - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } @@ -61,32 +66,36 @@ const mapLocationsController = function (MapLocation) { } }; const updateUserLocation = async function (req, res) { - console.log(req.body) - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } const userType = req.body.type; - const userId= req.body._id; + const userId = req.body._id; const updateData = { - firstName: req.body.firstName, - lastName: req.body.lastName, - jobTitle: req.body.jobTitle, - location: req.body.location, - _id: req.body._id + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + + if (req.body.timeZone) { + updateData.timeZone = req.body.timeZone } try { let response; - if(userType === 'user') { - response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + if (userType === 'user') { + console.log('updateData----', updateData); + response = await UserProfile.findOneAndUpdate({ _id: userId }, { $set: { ...updateData, jobTitle: [updateData.jobTitle] } }, { new: true }); cache.removeCache('allusers') cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); } else { - response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + response = await MapLocation.findOneAndUpdate({ _id: userId }, { $set: updateData }, { new: true }) } - + if (!response) { throw new Error('Something went wrong during saving the location...') } @@ -98,13 +107,22 @@ const mapLocationsController = function (MapLocation) { _id: response._id, type: userType } - + res.status(200).send(newData); } catch (err) { console.log(err.message) res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + + function calculateTotalHours(hoursByCategory) { + let hours = 0; + Object.keys(hoursByCategory).forEach((x) => { + hours += hoursByCategory[x]; + }); + return hours; + } + return { getAllLocations, deleteLocation, diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index 65415239f..851587dc3 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -3,18 +3,13 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const mapLocation = new Schema({ - firstName: { + title: { type: String, - default: 'Prior to HGN Data Collection', - }, - lastName: { - type: String, - default: 'Prior to HGN Data Collection', - }, - jobTitle: { - type: String, - default: 'Prior to HGN Data Collection', + default: 'Prior to HGN Data Collection' }, + firstName: String, + lastName: String, + jobTitle: String, isActive: { type: Boolean, default: false, From 883a00b2107ccf81441b78b6122f54f6dd61c3b0 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Thu, 2 Nov 2023 10:21:10 -0400 Subject: [PATCH 028/129] fix: modifying users before sent --- src/controllers/mapLocationsController.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 9fa214080..c9edd53b8 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -19,10 +19,17 @@ const mapLocationsController = function (MapLocation) { users.push(item); } }); - - const m_users = await MapLocation.find({}); - - res.status(200).send({ users, m_users }); + const modifiedUsers = users.map(item => ({ + location: item.location, + isActive: item.isActive, + jobTitle: item.jobTitle[0], + _id: item._id, + firstName: item.firstName, + lastName: item.lastName + })); + + const mUsers = await MapLocation.find({}); + res.status(200).send({ users: modifiedUsers, mUsers }); } catch (err) { res.status(404).send(err); } @@ -86,7 +93,6 @@ const mapLocationsController = function (MapLocation) { try { let response; if (userType === 'user') { - console.log('updateData----', updateData); response = await UserProfile.findOneAndUpdate({ _id: userId }, { $set: { ...updateData, jobTitle: [updateData.jobTitle] } }, { new: true }); cache.removeCache('allusers') cache.removeCache(`user-${userId}`); From e5380d28843c933ce203902b30da1bde6f8d53a9 Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 029/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index b9b3bb8a0..b0ff9eeee 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', ) .sort({ lastName: 1, From 632ef2007b53650b57d3ff6e06e694d6be8a7879 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 030/129] feat: userprofiles sending totalTangibleHrs, update user controller, all manually locations response structure --- src/controllers/mapLocationsController.js | 29 +++++++++++++++++++++++ src/controllers/userProfileController.js | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index c9edd53b8..97ae88b09 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -129,6 +129,35 @@ const mapLocationsController = function (MapLocation) { return hours; } + try { + let response; + if(userType === 'user') { + response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + cache.removeCache('allusers') + cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); + } else { + response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + } + + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + const newData = { + firstName: response.firstName, + lastName: response.lastName, + jobTitle: response.jobTitle, + location: response.location, + _id: response._id, + type: userType + } + + res.status(200).send(newData); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + }; return { getAllLocations, deleteLocation, diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index b0ff9eeee..d91b08739 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle totalTangibleHrs', ) .sort({ lastName: 1, From f7b7d125361fe3e910b3cf6e45aea1c3a315f558 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 031/129] fix: moving part of logic from the frontend, bug fixes feat: updating timezone --- src/controllers/mapLocationsController.js | 31 +---------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 97ae88b09..29af9e3d6 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -128,36 +128,7 @@ const mapLocationsController = function (MapLocation) { }); return hours; } - - try { - let response; - if(userType === 'user') { - response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); - cache.removeCache('allusers') - cache.removeCache(`user-${userId}`); - cache.setCache(`user-${userId}`, JSON.stringify(response)); - } else { - response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) - } - - if (!response) { - throw new Error('Something went wrong during saving the location...') - } - const newData = { - firstName: response.firstName, - lastName: response.lastName, - jobTitle: response.jobTitle, - location: response.location, - _id: response._id, - type: userType - } - - res.status(200).send(newData); - } catch (err) { - console.log(err.message) - res.status(500).json({ message: err.message || 'Something went wrong...' }); - } - }; + return { getAllLocations, deleteLocation, From e7f23406597cc7ef13df73ea490d32f31d8d0e4b Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Thu, 2 Nov 2023 13:10:56 -0400 Subject: [PATCH 032/129] rebasing and resolving conflicts --- src/controllers/userProfileController.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index d91b08739..6dc571b39 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -57,15 +57,9 @@ const userProfileController = function (UserProfile) { return; } - if (cache.getCache('allusers')) { - const getData = JSON.parse(cache.getCache('allusers')); - res.status(200).send(getData); - return; - } - UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle totalTangibleHrs', + "_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate" ) .sort({ lastName: 1, From 26e5f806ddc67af1d8dafdbe684fe6c4ee080ecf Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Thu, 2 Nov 2023 11:30:39 -0700 Subject: [PATCH 033/129] update material 1 api --- .../bmdashboard/bmMaterialsController.js | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 09fa91ab8..54ec65967 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -44,23 +44,30 @@ const bmMaterialsController = function (ItemMaterial) { }; const bmPostMaterialUpdateRecord = function (req, res) { - console.log(req.body); - ItemMaterial.update( + + let quantityUsed = +req.body.quantityUsed; + let quantityWasted = +req.body.quantityWasted; + let material = req.body.material; + if(req.body.QtyUsedLogUnit=='percent' && quantityWasted>=0) + { + quantityUsed = (+quantityUsed / 100) * material.stockAvailable; + } + if(req.body.QtyWastedLogUnit=='percent' && quantityUsed>=0) + { + quantityWasted = (+quantityWasted / 100) * material.stockAvailable; + } + + ItemMaterial.updateOne( { _id: req.body.material._id }, { - $push: { - updateRecord: { - date: req.body.date, - createdBy: req.body.requestor.requestorId, - action: req.body.action, - cause: req.body.cause, - quantity: req.body.quantity, - description: req.body.description, - }, - }, - }, + $inc: { + 'stockUsed': quantityUsed, + 'stockWasted': quantityWasted, + 'stockAvailable': -(quantityUsed+quantityWasted) + } + } ) - .then(results => res.status(200).send(results)) + .then(results => {res.status(200).send(results)}) .catch(error => res.status(500).send(error)); }; return { From b9e28592e96eb5378ff5e071783f0926e4e46e7d Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 034/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 737b1bc6a..70d774042 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -57,9 +57,15 @@ const userProfileController = function (UserProfile) { return; } + if (cache.getCache('allusers')) { + const getData = JSON.parse(cache.getCache('allusers')); + res.status(200).send(getData); + return; + } + UserProfile.find( {}, - "_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate" + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', ) .sort({ lastName: 1, From c2d332fd937967793017f315f6363ab89fcc89ef Mon Sep 17 00:00:00 2001 From: xaanders Date: Thu, 14 Sep 2023 16:40:41 -0400 Subject: [PATCH 035/129] feat: adding location object to user profile schema --- src/models/userProfile.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/models/userProfile.js b/src/models/userProfile.js index 4739a05e7..a5bb3bfa3 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -81,7 +81,16 @@ const userProfileSchema = new Schema({ infringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], - location: { type: String, default: '' }, + location: { + userProvided: { type: String, default: '' }, + coords: { + lat: { type: Number, default: '' }, + lng: { type: Number, default: '' }, + }, + country: { type: String, default: '' }, + city: { type: String, default: '' } + + }, oldInfringements: [ { date: { type: String, required: true }, description: { type: String, required: true } }, ], @@ -168,9 +177,9 @@ const userProfileSchema = new Schema({ }, infoCollections: [ { - areaName: { type: String }, + areaName: { type: String }, areaContent: { type: String }, - }], + }], }); userProfileSchema.pre('save', function (next) { From 3d5e4546ce199508f2ab7f1989d25ddd8c4bcaf2 Mon Sep 17 00:00:00 2001 From: xaanders Date: Sat, 16 Sep 2023 12:02:02 -0400 Subject: [PATCH 036/129] fix: editing email message after new user creating --- src/controllers/profileInitialSetupController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index 18cf7376c..b4230f1a4 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -67,7 +67,7 @@ function informManagerMessage(user) { Location: - ${user.location} + ${user.location.userProvided}, ${user.location.country}
From 35662d57c7f729203b6b2ecfa17ce35af4e11a58 Mon Sep 17 00:00:00 2001 From: xaanders Date: Fri, 22 Sep 2023 19:58:43 -0400 Subject: [PATCH 037/129] feat: adding a initial functionality for adding new people to the map(maplocation controller, maplocation router, maplocation model) --- src/controllers/mapLocationsController.js | 69 +++++++++++++++++++++++ src/models/mapLocation.js | 42 ++++++++++++++ src/routes/mapLocationsRouter.js | 18 ++++++ src/startup/routes.js | 4 ++ 4 files changed, 133 insertions(+) create mode 100644 src/controllers/mapLocationsController.js create mode 100644 src/models/mapLocation.js create mode 100644 src/routes/mapLocationsRouter.js diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js new file mode 100644 index 000000000..83526998d --- /dev/null +++ b/src/controllers/mapLocationsController.js @@ -0,0 +1,69 @@ +const mongoose = require('mongoose'); +const mapLocation = require('../models/mapLocation'); +const { hasPermission } = require('../utilities/permissions'); + +const mapLocationsController = function () { + const getAllLocations = function (req, res) { + console.log('controller:') + console.log(req.body) + + mapLocation.find({}) + .then(results => res.send(results).status(200)) + .catch(error => res.send(error).status(404)); + }; + const deleteLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { + res.status(403).send({ error: 'You are not authorized to delete teams.' }); + return; + } + const { teamId } = req.params; + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send({ error: 'No valid records found' }); + return; + } + const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); + const deleteteam = record.remove(); + + Promise.all([removeteamfromprofile, deleteteam]) + .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) + .catch((errors) => { + res.status(400).send(errors); + }); + }).catch((error) => { + res.status(400).send(error); + }); + }; + const putUserLocation = async function (req, res) { + if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + + const { teamId } = req.params; + + Team.findById(teamId, (error, record) => { + if (error || record === null) { + res.status(400).send('No valid records found'); + return; + } + record.teamName = req.body.teamName; + record.isActive = req.body.isActive; + record.createdDatetime = Date.now(); + record.modifiedDatetime = Date.now(); + + record + .save() + .then(results => res.status(201).send(results._id)) + .catch(errors => res.status(400).send(errors)); + }); + }; + + return { + getAllLocations, + deleteLocation, + putUserLocation + }; +}; + +module.exports = mapLocationsController; diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js new file mode 100644 index 000000000..d7b9d82b5 --- /dev/null +++ b/src/models/mapLocation.js @@ -0,0 +1,42 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const mapLocation = new Schema({ + firstName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + lastName: { + type: String, + default: 'Prior to HGN Data Collection', + }, + title: { + type: String, + default: 'Prior to HGN Data Collection', + }, + userProvided: { + type: String, + required: true, + }, + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { + type: String, + required: true, + }, + city: { + type: String, + default: '', + } +}); + +module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js new file mode 100644 index 000000000..e2e780dac --- /dev/null +++ b/src/routes/mapLocationsRouter.js @@ -0,0 +1,18 @@ +const express = require('express'); + +const router = function (mapLocations) { + const controller = require('../controllers/mapLocationsController')(mapLocations); + + const mapRouter = express.Router(); + + mapRouter.route('/mapLocations') + .get(controller.getAllLocations) + .put(controller.putUserLocation); + + mapRouter.route('/mapLocations/:locationId') + .delete(controller.deleteLocation) + + return mapRouter; +}; + +module.exports = router; diff --git a/src/startup/routes.js b/src/startup/routes.js index eae746e24..0f4c7308c 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -21,6 +21,7 @@ const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); const inventoryItemMaterial = require('../models/inventoryItemMaterial'); +const mapLocations = require('../models/mapLocation'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -56,6 +57,8 @@ const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); +const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); + // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); @@ -90,6 +93,7 @@ module.exports = function (app) { app.use('/api', informationRouter); app.use('/api', mouseoverTextRouter); app.use('/api', isEmailExistsRouter); + app.use('/api', mapLocationRouter); // bm dashboard app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); From 3e76b43ab0737f4e51d6dd34d8f465c28a02c692 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Mon, 2 Oct 2023 16:13:42 -0400 Subject: [PATCH 038/129] fix: jobTitle model key, feat: adding isActive status, getting all locations, put new location to a collection --- src/controllers/mapLocationsController.js | 72 +++++++++-------------- src/models/mapLocation.js | 38 +++++++----- 2 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 83526998d..6088de489 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,62 +1,44 @@ -const mongoose = require('mongoose'); -const mapLocation = require('../models/mapLocation'); -const { hasPermission } = require('../utilities/permissions'); - -const mapLocationsController = function () { +const mapLocationsController = function (mapLocation) { const getAllLocations = function (req, res) { - console.log('controller:') - console.log(req.body) mapLocation.find({}) - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => + res.send(results).status(200) + ) + .catch(error => + res.send(error).status(404)); }; const deleteLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'deleteTeam')) { - res.status(403).send({ error: 'You are not authorized to delete teams.' }); + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); return; } - const { teamId } = req.params; - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send({ error: 'No valid records found' }); - return; - } - const removeteamfromprofile = userProfile.updateMany({}, { $pull: { teams: record._id } }).exec(); - const deleteteam = record.remove(); - - Promise.all([removeteamfromprofile, deleteteam]) - .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) - .catch((errors) => { - res.status(400).send(errors); - }); - }).catch((error) => { - res.status(400).send(error); - }); }; const putUserLocation = async function (req, res) { - if (!await hasPermission(req.body.requestor.role, 'putTeam')) { + + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + const location = new mapLocation(locationData); - const { teamId } = req.params; - - Team.findById(teamId, (error, record) => { - if (error || record === null) { - res.status(400).send('No valid records found'); - return; + try { + const response = await location.save() + if(!response) { + throw new Error('Something went wrong during saving the location...') } - record.teamName = req.body.teamName; - record.isActive = req.body.isActive; - record.createdDatetime = Date.now(); - record.modifiedDatetime = Date.now(); - - record - .save() - .then(results => res.status(201).send(results._id)) - .catch(errors => res.status(400).send(errors)); - }); + res.status(200).send(response); + } catch (err) { + console.log(err.message) + res.status(500).json({message: err.message || 'Something went wrong...'}); + } }; return { diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index d7b9d82b5..65415239f 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -11,32 +11,38 @@ const mapLocation = new Schema({ type: String, default: 'Prior to HGN Data Collection', }, - title: { + jobTitle: { type: String, default: 'Prior to HGN Data Collection', }, - userProvided: { - type: String, - required: true, + isActive: { + type: Boolean, + default: false, }, - coords: { - lat: { + location: { + userProvided: { type: String, required: true, }, - lng: { + coords: { + lat: { + type: String, + required: true, + }, + lng: { + type: String, + required: true, + } + }, + country: { type: String, required: true, - } - }, - country: { - type: String, - required: true, + }, + city: { + type: String, + default: '', + }, }, - city: { - type: String, - default: '', - } }); module.exports = mongoose.model('MapLocation', mapLocation, 'maplocations'); From f3832a31ce438bb39542cc30f0d2a4f28a5868d4 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 039/129] feat: userprofiles sending totalTangibleHrs, update user controller, all manually locations response structure --- src/controllers/mapLocationsController.js | 85 ++++++++++++++++++++--- src/routes/mapLocationsRouter.js | 3 +- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 6088de489..f01666558 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,11 +1,26 @@ -const mapLocationsController = function (mapLocation) { +const userProfile = require('../models/userProfile'); +const cache = require('../utilities/nodeCache')(); + +const mapLocationsController = function (MapLocation) { const getAllLocations = function (req, res) { + const priorText = 'Prior to HGN Data Collection'; + MapLocation.find({}) + .then(results => { + const users = results.map(item => { + return ({ + title: priorText, + firstName: item.firstName !== priorText ? item.firstName : '', + lastName: item.lastName !== priorText ? item.lastName : '', + jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', + location: item.location, + isActive: item.isActive, + _id: item._id + }) + }) + res.send(users).status(200); - mapLocation.find({}) - .then(results => - res.send(results).status(200) - ) - .catch(error => + }) + .catch(error => res.send(error).status(404)); }; const deleteLocation = async function (req, res) { @@ -14,6 +29,11 @@ const mapLocationsController = function (mapLocation) { res.status(403).send('You are not authorized to make changes in the teams.'); return; } + const locationId = req.params.locationId + + MapLocation.findOneAndDelete({ _id: locationId }) + .then(() => res.status(200).send({ message: "The location was successfully removed!" })) + .catch(error => res.status(500).send({ message: error || "Couldn't remove the location" })); }; const putUserLocation = async function (req, res) { @@ -27,24 +47,69 @@ const mapLocationsController = function (mapLocation) { jobTitle: req.body.jobTitle, location: req.body.location, } - const location = new mapLocation(locationData); + const location = new MapLocation(locationData); try { const response = await location.save() - if(!response) { + if (!response) { throw new Error('Something went wrong during saving the location...') } res.status(200).send(response); } catch (err) { console.log(err.message) - res.status(500).json({message: err.message || 'Something went wrong...'}); + res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + const updateUserLocation = async function (req, res) { + console.log(req.body) + if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + res.status(403).send('You are not authorized to make changes in the teams.'); + return; + } + const userType = req.body.type; + const userId= req.body._id; + const updateData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + _id: req.body._id + } + try { + let response; + if(userType === 'user') { + response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + cache.removeCache('allusers') + cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); + } else { + response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + } + + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + const newData = { + firstName: response.firstName, + lastName: response.lastName, + jobTitle: response.jobTitle, + location: response.location, + _id: response._id, + type: userType + } + + res.status(200).send(newData); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + }; return { getAllLocations, deleteLocation, - putUserLocation + putUserLocation, + updateUserLocation }; }; diff --git a/src/routes/mapLocationsRouter.js b/src/routes/mapLocationsRouter.js index e2e780dac..db004ff18 100644 --- a/src/routes/mapLocationsRouter.js +++ b/src/routes/mapLocationsRouter.js @@ -7,7 +7,8 @@ const router = function (mapLocations) { mapRouter.route('/mapLocations') .get(controller.getAllLocations) - .put(controller.putUserLocation); + .put(controller.putUserLocation) + .patch(controller.updateUserLocation); mapRouter.route('/mapLocations/:locationId') .delete(controller.deleteLocation) From ac823a55717273c4675f55d21529c26f948093f5 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 040/129] fix: moving part of logic from the frontend, bug fixes feat: updating timezone --- src/controllers/mapLocationsController.js | 88 ++++++++++++++--------- src/models/mapLocation.js | 15 ++-- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index f01666558..9fa214080 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -1,27 +1,32 @@ -const userProfile = require('../models/userProfile'); +const UserProfile = require('../models/userProfile'); const cache = require('../utilities/nodeCache')(); + const mapLocationsController = function (MapLocation) { - const getAllLocations = function (req, res) { - const priorText = 'Prior to HGN Data Collection'; - MapLocation.find({}) - .then(results => { - const users = results.map(item => { - return ({ - title: priorText, - firstName: item.firstName !== priorText ? item.firstName : '', - lastName: item.lastName !== priorText ? item.lastName : '', - jobTitle: item.jobTitle !== priorText ? item.jobTitle : '', - location: item.location, - isActive: item.isActive, - _id: item._id - }) - }) - res.send(users).status(200); - - }) - .catch(error => - res.send(error).status(404)); + const getAllLocations = async function (req, res) { + + try { + const users = []; + const results = await UserProfile.find({}, + '_id firstName lastName isActive location jobTitle totalTangibleHrs hoursByCategory' + ); + + results.forEach((item) => { + if ( + (item.location?.coords.lat && item.location?.coords.lng && item.totalTangibleHrs >= 10) || + (item.location?.coords.lat && item.location?.coords.lng && calculateTotalHours(item.hoursByCategory) >= 10) + ) { + users.push(item); + } + }); + + const m_users = await MapLocation.find({}); + + res.status(200).send({ users, m_users }); + } catch (err) { + res.status(404).send(err); + } + }; const deleteLocation = async function (req, res) { @@ -37,7 +42,7 @@ const mapLocationsController = function (MapLocation) { }; const putUserLocation = async function (req, res) { - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } @@ -61,32 +66,36 @@ const mapLocationsController = function (MapLocation) { } }; const updateUserLocation = async function (req, res) { - console.log(req.body) - if (!req.body.requestor.role === 'Administrator' || !req.body.requestor.role === 'Owner') { + if (!req.body.requestor.role === 'Owner') { res.status(403).send('You are not authorized to make changes in the teams.'); return; } const userType = req.body.type; - const userId= req.body._id; + const userId = req.body._id; const updateData = { - firstName: req.body.firstName, - lastName: req.body.lastName, - jobTitle: req.body.jobTitle, - location: req.body.location, - _id: req.body._id + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + + if (req.body.timeZone) { + updateData.timeZone = req.body.timeZone } try { let response; - if(userType === 'user') { - response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + if (userType === 'user') { + console.log('updateData----', updateData); + response = await UserProfile.findOneAndUpdate({ _id: userId }, { $set: { ...updateData, jobTitle: [updateData.jobTitle] } }, { new: true }); cache.removeCache('allusers') cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); } else { - response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + response = await MapLocation.findOneAndUpdate({ _id: userId }, { $set: updateData }, { new: true }) } - + if (!response) { throw new Error('Something went wrong during saving the location...') } @@ -98,13 +107,22 @@ const mapLocationsController = function (MapLocation) { _id: response._id, type: userType } - + res.status(200).send(newData); } catch (err) { console.log(err.message) res.status(500).json({ message: err.message || 'Something went wrong...' }); } }; + + function calculateTotalHours(hoursByCategory) { + let hours = 0; + Object.keys(hoursByCategory).forEach((x) => { + hours += hoursByCategory[x]; + }); + return hours; + } + return { getAllLocations, deleteLocation, diff --git a/src/models/mapLocation.js b/src/models/mapLocation.js index 65415239f..851587dc3 100644 --- a/src/models/mapLocation.js +++ b/src/models/mapLocation.js @@ -3,18 +3,13 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const mapLocation = new Schema({ - firstName: { + title: { type: String, - default: 'Prior to HGN Data Collection', - }, - lastName: { - type: String, - default: 'Prior to HGN Data Collection', - }, - jobTitle: { - type: String, - default: 'Prior to HGN Data Collection', + default: 'Prior to HGN Data Collection' }, + firstName: String, + lastName: String, + jobTitle: String, isActive: { type: Boolean, default: false, From ac4aa7ba8a289b6d54b839ec606e49fe5797e09c Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Thu, 2 Nov 2023 10:21:10 -0400 Subject: [PATCH 041/129] fix: modifying users before sent --- src/controllers/mapLocationsController.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 9fa214080..c9edd53b8 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -19,10 +19,17 @@ const mapLocationsController = function (MapLocation) { users.push(item); } }); - - const m_users = await MapLocation.find({}); - - res.status(200).send({ users, m_users }); + const modifiedUsers = users.map(item => ({ + location: item.location, + isActive: item.isActive, + jobTitle: item.jobTitle[0], + _id: item._id, + firstName: item.firstName, + lastName: item.lastName + })); + + const mUsers = await MapLocation.find({}); + res.status(200).send({ users: modifiedUsers, mUsers }); } catch (err) { res.status(404).send(err); } @@ -86,7 +93,6 @@ const mapLocationsController = function (MapLocation) { try { let response; if (userType === 'user') { - console.log('updateData----', updateData); response = await UserProfile.findOneAndUpdate({ _id: userId }, { $set: { ...updateData, jobTitle: [updateData.jobTitle] } }, { new: true }); cache.removeCache('allusers') cache.removeCache(`user-${userId}`); From 23bb45dfab1a7440cbc540c2ebc736e86f426b51 Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 042/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 70d774042..ae41bec5f 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', ) .sort({ lastName: 1, From 082fd3f132db23f175bcf501b08ad67d1f5efce7 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 043/129] feat: userprofiles sending totalTangibleHrs, update user controller, all manually locations response structure --- src/controllers/mapLocationsController.js | 29 +++++++++++++++++++++++ src/controllers/userProfileController.js | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index c9edd53b8..97ae88b09 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -129,6 +129,35 @@ const mapLocationsController = function (MapLocation) { return hours; } + try { + let response; + if(userType === 'user') { + response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); + cache.removeCache('allusers') + cache.removeCache(`user-${userId}`); + cache.setCache(`user-${userId}`, JSON.stringify(response)); + } else { + response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) + } + + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + const newData = { + firstName: response.firstName, + lastName: response.lastName, + jobTitle: response.jobTitle, + location: response.location, + _id: response._id, + type: userType + } + + res.status(200).send(newData); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + }; return { getAllLocations, deleteLocation, diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index ae41bec5f..1cc7f133c 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle totalTangibleHrs', ) .sort({ lastName: 1, From d6122b319d12231c057404b5458fe0eba1cba16b Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 044/129] fix: moving part of logic from the frontend, bug fixes feat: updating timezone --- src/controllers/mapLocationsController.js | 31 +---------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/controllers/mapLocationsController.js b/src/controllers/mapLocationsController.js index 97ae88b09..29af9e3d6 100644 --- a/src/controllers/mapLocationsController.js +++ b/src/controllers/mapLocationsController.js @@ -128,36 +128,7 @@ const mapLocationsController = function (MapLocation) { }); return hours; } - - try { - let response; - if(userType === 'user') { - response = await userProfile.findOneAndUpdate({ _id: userId }, {$set: {...updateData, jobTitle: [updateData.jobTitle]}}, { new: true }); - cache.removeCache('allusers') - cache.removeCache(`user-${userId}`); - cache.setCache(`user-${userId}`, JSON.stringify(response)); - } else { - response = await MapLocation.findOneAndUpdate({ _id: userId }, {$set: updateData}, { new: true }) - } - - if (!response) { - throw new Error('Something went wrong during saving the location...') - } - const newData = { - firstName: response.firstName, - lastName: response.lastName, - jobTitle: response.jobTitle, - location: response.location, - _id: response._id, - type: userType - } - - res.status(200).send(newData); - } catch (err) { - console.log(err.message) - res.status(500).json({ message: err.message || 'Something went wrong...' }); - } - }; + return { getAllLocations, deleteLocation, From 1effe936b57d19eb32a02015314dd4a25fe20116 Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 045/129] add new fields to send in UserProfileController Add jobTitle and location fields to the userProfile objects that are sent back to the client when requesting all userProfiles. These two bits of info are displayed on the interactive map. --- src/controllers/userProfileController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 1cc7f133c..ae41bec5f 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -65,7 +65,7 @@ const userProfileController = function (UserProfile) { UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle totalTangibleHrs', + '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', ) .sort({ lastName: 1, From b2b2cb0810a2a4cc2358b5c04d99bf523181fd23 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Thu, 2 Nov 2023 13:10:56 -0400 Subject: [PATCH 046/129] rebasing and resolving conflicts --- src/controllers/userProfileController.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index ae41bec5f..737b1bc6a 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -57,15 +57,9 @@ const userProfileController = function (UserProfile) { return; } - if (cache.getCache('allusers')) { - const getData = JSON.parse(cache.getCache('allusers')); - res.status(200).send(getData); - return; - } - UserProfile.find( {}, - '_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate location jobTitle', + "_id firstName lastName role weeklycommittedHours email permissions isActive reactivationDate createdDate endDate" ) .sort({ lastName: 1, From 34a6f5594c82243eb974b7943b2d45c3ee855a39 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Sat, 4 Nov 2023 14:41:07 -0700 Subject: [PATCH 047/129] update material --- .../bmdashboard/bmMaterialsController.js | 89 +++++++++++++++++-- src/routes/bmdashboard/bmMaterialsRouter.js | 5 ++ 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 54ec65967..0e96b5bbb 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -57,22 +57,93 @@ const bmMaterialsController = function (ItemMaterial) { quantityWasted = (+quantityWasted / 100) * material.stockAvailable; } - ItemMaterial.updateOne( - { _id: req.body.material._id }, + if(quantityUsed>material.stockAvailable || quantityWasted>material.stockAvailable || (quantityUsed+quantityWasted)>material.stockAvailable) + { + res.status(500).send('Please check the used and wasted stock values. Either individual values or their sum exceeds the total stock available.') + } + else + { + quantityUsed = Number.parseFloat(quantityUsed.toFixed(2)); + quantityWasted = Number.parseFloat(quantityWasted.toFixed(2)); + let newAvailable = Number.parseFloat(quantityUsed+quantityWasted); + + ItemMaterial.updateOne( + { _id: req.body.material._id }, + { + $inc: { + 'stockUsed': quantityUsed, + 'stockWasted': quantityWasted, + 'stockAvailable': -(newAvailable) + }, + $push: { + updateRecord: { + date: req.body.date, + createdBy: req.body.requestor.requestorId, + quantity: -(quantityUsed+quantityWasted), + }, + } + } + ) + .then(results => {res.status(200).send(results)}) + .catch(error => res.status(500).send({'message':error})); + } + }; + + const bmPostMaterialUpdateBulk = function (req, res) { + const materialUpdates= req.body; + const updateRecordsToBeAdded = materialUpdates.map(payload => { + let quantityUsed = +payload.quantityUsed; + let quantityWasted = +payload.quantityWasted; + let material = payload.material; + if(payload.QtyUsedLogUnit=='percent' && quantityWasted>=0) { - $inc: { + quantityUsed = (+quantityUsed / 100) * material.stockAvailable; + } + if(payload.QtyWastedLogUnit=='percent' && quantityUsed>=0) + { + quantityWasted = (+quantityWasted / 100) * material.stockAvailable; + } + + quantityUsed = Number.parseFloat(quantityUsed.toFixed(2)); + quantityWasted = Number.parseFloat(quantityWasted.toFixed(2)); + let newAvailable = Number.parseFloat(quantityUsed+quantityWasted); + + return ({ + updateId: material._id, + increment: { 'stockUsed': quantityUsed, 'stockWasted': quantityWasted, - 'stockAvailable': -(quantityUsed+quantityWasted) - } - } - ) - .then(results => {res.status(200).send(results)}) - .catch(error => res.status(500).send(error)); + 'stockAvailable': -(newAvailable) + }, + updateValue: { + createdBy: req.body.requestor.requestorId, + quantity: -(quantityUsed+quantityWasted), + date: payload.date, + }}); + + }); + console.log(updateRecordsToBeAdded); + + try { + const updatePromises = updateRecordsToBeAdded.map(updateItem => ItemMaterial.updateOne( + { _id: updateItem.updateId }, + { + $inc: updateItem.increment , + $push: { usageRecord: updateItem.updateValue } }, + ).exec()); + Promise.all(updatePromises) + .then((results) => { + res.status(200).send({ result: `Successfully posted log for ${results.length} Material records.` }); + }) + .catch(error => res.status(500).send(error)); + } catch (err) { + res.json(err); + } }; return { bmMaterialsList, bmPostMaterialUpdateRecord, + bmPostMaterialUpdateBulk }; }; diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index a0f8dfea6..35a887b9b 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -10,6 +10,11 @@ materialsRouter.route('/materials') materialsRouter.route('/addUpdateMaterialRecord') .post(controller.bmPostMaterialUpdateRecord); + materialsRouter.route('/UpdateMaterialRecordBulk') + .post(controller.bmPostMaterialUpdateBulk); + + + return materialsRouter; }; From b568f900b655b8a2654eee9d3ed0ca3f3ea4e137 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 6 Nov 2023 14:40:30 +0800 Subject: [PATCH 048/129] implemented edit and delete functions --- src/controllers/timeEntryController.js | 164 ++++++++++++++--------- src/controllers/userProfileController.js | 11 +- src/routes/timeentryRouter.js | 2 +- 3 files changed, 106 insertions(+), 71 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index d2a4b3df4..aa7eff6ae 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -90,13 +90,16 @@ const timeEntrycontroller = function (TimeEntry) { const session = await mongoose.startSession(); session.startTransaction(); + const type = req.body.entryType; + try { if (!req.params.timeEntryId) { return res.status(400).send({ error: 'ObjectId in request param is not in correct format' }); } - if (!mongoose.Types.ObjectId.isValid(req.params.timeEntryId) || !mongoose.Types.ObjectId.isValid(req.body.projectId)) { - return res.status(400).send({ error: 'ObjectIds are not correctly formed' }); + if (!mongoose.Types.ObjectId.isValid(req.params.timeEntryId) + || ((type === 'default' || type === 'project') && !mongoose.Types.ObjectId.isValid(req.body.projectId))) { + return res.status(400).send({ error: 'ObjectIds are not correctly formed' }); } // Get initial timeEntry by timeEntryId @@ -106,7 +109,8 @@ const timeEntrycontroller = function (TimeEntry) { return res.status(400).send({ error: `No valid records found for ${req.params.timeEntryId}` }); } - if (!(await hasPermission(req.body.requestor, 'editTimeEntry') || timeEntry.personId.toString() === req.body.requestor.requestorId.toString())) { + if (!(await hasPermission(req.body.requestor, 'editTimeEntry') + || ((type === 'default' || type === 'person') && timeEntry.personId.toString() === req.body.requestor.requestorId.toString()))) { return res.status(403).send({ error: 'Unauthorized request' }); } @@ -115,7 +119,7 @@ const timeEntrycontroller = function (TimeEntry) { const totalSeconds = moment.duration(`${hours}:${minutes}`).asSeconds(); - if (timeEntry.isTangible === true && totalSeconds !== timeEntry.totalSeconds) { + if (type === 'default' && timeEntry.isTangible === true && totalSeconds !== timeEntry.totalSeconds) { notifyEditByEmail(timeEntry.personId.toString(), timeEntry, totalSeconds, req.body); } @@ -134,24 +138,27 @@ const timeEntrycontroller = function (TimeEntry) { // Update the hoursLogged field of related tasks based on before and after timeEntries // initialIsTangible is a bealoon value, req.body.isTangible is a string // initialProjectId may be a task id or project id, so do not throw error. - try { - if (initialIsTangible === true) { - const initialTask = await task.findById(initialProjectId); - initialTask.hoursLogged -= (initialSeconds / 3600); - await initialTask.save(); - } + if (type === 'default') { + try { + if (initialIsTangible === true) { + const initialTask = await task.findById(initialProjectId); + initialTask.hoursLogged -= (initialSeconds / 3600); + await initialTask.save(); + } - if (req.body.isTangible === true) { - const editedTask = await task.findById(req.body.projectId); - editedTask.hoursLogged += (totalSeconds / 3600); - await editedTask.save(); + if (req.body.isTangible === true) { + const editedTask = await task.findById(req.body.projectId); + editedTask.hoursLogged += (totalSeconds / 3600); + await editedTask.save(); + } + } catch (error) { + console.log('Failed to find task by id'); } - } catch (error) { - console.log('Failed to find task by id'); } // Update edit history - if (initialSeconds !== totalSeconds + if ((type === 'default' || type === 'person') + && initialSeconds !== totalSeconds && timeEntry.isTangible && req.body.requestor.requestorId === timeEntry.personId.toString() && !await hasPermission(req.body.requestor, 'editTimeEntry') @@ -163,37 +170,39 @@ const timeEntrycontroller = function (TimeEntry) { newSeconds: totalSeconds, }); - // Issue infraction if edit history contains more than 5 edits in the last year - let totalRecentEdits = 0; + if (type === 'default') { + // Issue infraction if edit history contains more than 5 edits in the last year + let totalRecentEdits = 0; - requestor.timeEntryEditHistory.forEach((edit) => { - if (moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365) { - totalRecentEdits += 1; - } - }); - - if (totalRecentEdits >= 5) { - requestor.infringements.push({ - date: moment().tz('America/Los_Angeles'), - description: `${totalRecentEdits} time entry edits in the last calendar year`, + requestor.timeEntryEditHistory.forEach((edit) => { + if (moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365) { + totalRecentEdits += 1; + } }); - emailSender('onecommunityglobal@gmail.com', `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, ` -

- ${requestor.firstName} ${requestor.lastName} (${requestor.email}) was issued a blue square for editing their time entries ${totalRecentEdits} times - within the last calendar year. -

-

- This is the ${totalRecentEdits}th edit within the past 365 days. -

+ if (totalRecentEdits >= 5) { + requestor.infringements.push({ + date: moment().tz('America/Los_Angeles'), + description: `${totalRecentEdits} time entry edits in the last calendar year`, + }); + + emailSender('onecommunityglobal@gmail.com', `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, ` +

+ ${requestor.firstName} ${requestor.lastName} (${requestor.email}) was issued a blue square for editing their time entries ${totalRecentEdits} times + within the last calendar year. +

+

+ This is the ${totalRecentEdits}th edit within the past 365 days. +

`); - const emailInfringement = { - date: moment().tz('America/Los_Angeles').format('MMMM-DD-YY'), - description: `You edited your time entries ${totalRecentEdits} times within the last 365 days, exceeding the limit of 4 times per year you can edit them without penalty.`, - }; + const emailInfringement = { + date: moment().tz('America/Los_Angeles').format('MMMM-DD-YY'), + description: `You edited your time entries ${totalRecentEdits} times within the last 365 days, exceeding the limit of 4 times per year you can edit them without penalty.`, + }; - emailSender(requestor.email, 'You\'ve been issued a blue square for editing your time entry', getInfringementEmailBody(requestor.firstName, requestor.lastName, emailInfringement, requestor.infringements.length)); + emailSender(requestor.email, 'You\'ve been issued a blue square for editing your time entry', getInfringementEmailBody(requestor.firstName, requestor.lastName, emailInfringement, requestor.infringements.length)); + } } await requestor.save(); @@ -204,10 +213,12 @@ const timeEntrycontroller = function (TimeEntry) { res.status(200).send({ message: 'Successfully updated time entry' }); - // checking if logged in hours exceed estimated time after timeentry edit for a task - const record = await userProfile.findById(timeEntry.personId.toString()); - const currentTask = await task.findById(req.body.projectId); - checkTaskOvertime(timeEntry, record, currentTask); + if (type === 'default') { + // checking if logged in hours exceed estimated time after timeentry edit for a task + const record = await userProfile.findById(timeEntry.personId.toString()); + const currentTask = await task.findById(req.body.projectId); + checkTaskOvertime(timeEntry, record, currentTask); + } } catch (err) { await session.abortTransaction(); return res.status(400).send({ error: err.toString() }); @@ -249,9 +260,8 @@ const timeEntrycontroller = function (TimeEntry) { || !req.body.timeSpent || !req.body.isTangible; - const returnErr = (res) => { - res.status(400).send({ error: 'Bad request' }); - return; + const returnErr = (result) => { + result.status(400).send({ error: 'Bad request' }); }; switch (req.body.entryType) { @@ -307,10 +317,12 @@ const timeEntrycontroller = function (TimeEntry) { .status(200) .send({ message: `Time Entry saved with id as ${results._id}` }); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); // Add this tangbile time entry to related task's hoursLogged - if ((timeentry.entryType == 'default') && timeentry.isTangible === true) { + if ((timeentry.entryType === 'default') && timeentry.isTangible === true) { try { const currentTask = await task.findById(req.body.projectId); currentTask.hoursLogged += (timeentry.totalSeconds / 3600); @@ -320,7 +332,7 @@ const timeEntrycontroller = function (TimeEntry) { } } // checking if logged in hours exceed estimated time after timeentry for a task - if (timeentry.entryType == 'default') { + if (timeentry.entryType === 'default') { const record = await userProfile.findById(timeentry.personId.toString()); const currentTask = await task.findById(req.body.projectId); checkTaskOvertime(timeentry, record, currentTask); @@ -347,7 +359,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.aggregate([ { $match: { - entryType: { $in: [ 'default', null ] }, + entryType: { $in: ['default', null] }, personId: mongoose.Types.ObjectId(userId), dateOfWork: { $gte: fromdate, $lte: todate }, }, @@ -423,7 +435,9 @@ const timeEntrycontroller = function (TimeEntry) { }, ]).then((results) => { res.status(200).send(results); - }).catch(error => res.status(400).send(error)); + }).catch((error) => { + res.status(400).send(error); + }); }; const getTimeEntriesForUsersList = function (req, res) { @@ -431,7 +445,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.find( { - entryType: { $in: [ 'default', null, 'person' ] }, + entryType: { $in: ['default', null, 'person'] }, personId: { $in: users }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, @@ -458,7 +472,9 @@ const timeEntrycontroller = function (TimeEntry) { }); res.status(200).send(data); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); }; const getTimeEntriesForSpecifiedProject = function (req, res) { @@ -476,7 +492,7 @@ const timeEntrycontroller = function (TimeEntry) { const { projectId } = req.params; TimeEntry.find( { - entryType: [ 'default', null ], + entryType: ['default', null], projectId, dateOfWork: { $gte: fromDate, $lte: todate }, }, @@ -487,7 +503,9 @@ const timeEntrycontroller = function (TimeEntry) { .then((results) => { res.status(200).send(results); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); }; const deleteTimeEntry = async function (req, res) { @@ -503,6 +521,18 @@ const timeEntrycontroller = function (TimeEntry) { return; } + if (record.entryType === 'project' || record.entryType === 'person' || record.entryType === 'team') { + record + .remove() + .then(() => { + res.status(200).send({ message: 'Successfully deleted' }); + }) + .catch((error) => { + res.status(500).send(error); + }); + return; + } + if ( record.personId.toString() === req.body.requestor.requestorId.toString() @@ -572,7 +602,9 @@ const timeEntrycontroller = function (TimeEntry) { }); res.status(200).send(data); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); }; const getLostTimeEntriesForProjectList = function (req, res) { @@ -580,7 +612,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.find( { - entryType: "project", + entryType: 'project', projectId: { $in: projects }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, @@ -606,7 +638,9 @@ const timeEntrycontroller = function (TimeEntry) { }); res.status(200).send(data); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); }; const getLostTimeEntriesForTeamList = function (req, res) { @@ -614,7 +648,7 @@ const timeEntrycontroller = function (TimeEntry) { TimeEntry.find( { - entryType: "team", + entryType: 'team', teamId: { $in: teams }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, @@ -629,7 +663,7 @@ const timeEntrycontroller = function (TimeEntry) { record._id = element._id; record.notes = element.notes; record.isTangible = element.isTangible; - record.teamId = element.teamId; + record.teamId = element.teamId ? element.teamId._id : ''; record.teamName = element.teamId ? element.teamId.teamName : ''; @@ -640,7 +674,9 @@ const timeEntrycontroller = function (TimeEntry) { }); res.status(200).send(data); }) - .catch(error => res.status(400).send(error)); + .catch((error) => { + res.status(400).send(error); + }); }; return { diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 6dc571b39..440ce765e 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -569,15 +569,14 @@ const userProfileController = function (UserProfile) { const { userId } = req.params; const { key, value } = req.body; - if (key === "teamCode") { - const canEditTeamCode = req.body.requestor.role === "Owner" || - req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); + if (key === 'teamCode') { + const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); - if(!canEditTeamCode){ - res.status(403).send("You are not authorized to edit team code."); + if (!canEditTeamCode) { + res.status(403).send('You are not authorized to edit team code.'); return; } - } // remove user from cache, it should be loaded next time diff --git a/src/routes/timeentryRouter.js b/src/routes/timeentryRouter.js index 7e0a41797..0562f49ed 100644 --- a/src/routes/timeentryRouter.js +++ b/src/routes/timeentryRouter.js @@ -21,7 +21,7 @@ const routes = function (TimeEntry) { TimeEntryRouter.route('/TimeEntry/lostUsers') .post(controller.getLostTimeEntriesForUserList); - + TimeEntryRouter.route('/TimeEntry/lostProjects') .post(controller.getLostTimeEntriesForProjectList); From a7ebfdd40fd5fbedcbcf21e423ab0cb353228d97 Mon Sep 17 00:00:00 2001 From: Chuehleo <122568562+Chuehleo@users.noreply.github.com> Date: Thu, 9 Nov 2023 01:33:16 -0700 Subject: [PATCH 049/129] Update userProfileController.js --- src/controllers/userProfileController.js | 107 ++++++++++++++--------- 1 file changed, 68 insertions(+), 39 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index a39b3c9ff..a388f10db 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -34,7 +34,6 @@ async function ValidatePassword(req, res) { }); return; } - // Verify request is authorized by self or adminsitrator if (userId !== requestor.requestorId && !await hasPermission(requestor.role, 'updatePassword')) { console.log('User ID:', userId); @@ -50,6 +49,11 @@ async function ValidatePassword(req, res) { if (userId === requestor.requestorId || !await hasPermission(requestor.role, 'updatePasswordForOthers')) { console.log('User ID:', userId); console.log('Requestor ID:', requestor.requestorId); + res.status(403).send({ + error: "You are unauthorized to update this user's password", + }); + return; + } // Verify new and confirm new password are correct if (req.body.newpassword !== req.body.confirmnewpassword) { @@ -61,8 +65,19 @@ async function ValidatePassword(req, res) { const userProfileController = function (UserProfile) { const getUserProfiles = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'getUserProfiles')) { - res.status(403).send('You are not authorized to view all users'); + if ( + !(await hasPermission(req.body.requestor.role, "getUserProfiles")) && + !req.body.requestor.permissions?.frontPermissions.includes( + "putUserProfilePermissions" + ) + ) { + res.status(403).send("You are not authorized to view all users"); + return; + } + + if (cache.getCache("allusers")) { + const getData = JSON.parse(cache.getCache("allusers")); + res.status(200).send(getData); return; } @@ -85,8 +100,8 @@ const userProfileController = function (UserProfile) { }; const getProjectMembers = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'getProjectMembers')) { - res.status(403).send('You are not authorized to view all users'); + if (!(await hasPermission(req.body.requestor.role, "getProjectMembers"))) { + res.status(403).send("You are not authorized to view all users"); return; } UserProfile.find( @@ -107,13 +122,16 @@ const userProfileController = function (UserProfile) { }; const postUserProfile = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'postUserProfile')) { - res.status(403).send('You are not authorized to create new users'); + if (!(await hasPermission(req.body.requestor.role, "postUserProfile"))) { + res.status(403).send("You are not authorized to create new users"); return; } - if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { - res.status(403).send('You are not authorized to create new owners'); + if ( + req.body.role === "Owner" && + !(await hasPermission(req.body.requestor.role, "addDeleteEditOwners")) + ) { + res.status(403).send("You are not authorized to create new owners"); return; } @@ -230,10 +248,12 @@ const userProfileController = function (UserProfile) { const putUserProfile = async function (req, res) { const userid = req.params.userId; const isRequestorAuthorized = !!( - canRequestorUpdateUser(req.body.requestor.requestorId, userid) && ( - await hasPermission(req.body.requestor, 'putUserProfile') - || req.body.requestor.requestorId === userid - ) + canRequestorUpdateUser(req.body.requestor.requestorId, userid) && + ((await hasPermission(req.body.requestor.role, "putUserProfile")) || + req.body.requestor.requestorId === userid || + req.body.requestor.permissions?.frontPermissions.includes( + "putUserProfilePermissions" + )) ); if (!isRequestorAuthorized) { @@ -241,8 +261,11 @@ const userProfileController = function (UserProfile) { return; } - if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { - res.status(403).send('You are not authorized to update this user'); + if ( + req.body.role === "Owner" && + !(await hasPermission(req.body.requestor.role, "addDeleteEditOwners")) + ) { + res.status(403).send("You are not authorized to update this user"); return; } @@ -295,12 +318,6 @@ const userProfileController = function (UserProfile) { record.isFirstTimelog = req.body.isFirstTimelog; record.teamCode = req.body.teamCode; - if(!canEditTeamCode && record.teamCode !== req.body.teamCode){ - res.status(403).send("You are not authorized to edit team code."); - return; - } - record.teamCode = req.body.teamCode; - // find userData in cache const isUserInCache = cache.hasCache("allusers"); let allUserData; @@ -311,7 +328,15 @@ const userProfileController = function (UserProfile) { userIdx = allUserData.findIndex((users) => users._id === userid); userData = allUserData[userIdx]; } - if (await hasPermission(req.body.requestor, 'putUserProfileImportantInfo')) { + if ( + (await hasPermission( + req.body.requestor.role, + "putUserProfileImportantInfo" + )) || + req.body.requestor.permissions?.frontPermissions.includes( + "putUserProfilePermissions" + ) + ) { record.role = req.body.role; record.isRehireable = req.body.isRehireable; record.isActive = req.body.isActive; @@ -377,9 +402,16 @@ const userProfileController = function (UserProfile) { record.createdDate; } - record.bioPosted = req.body.bioPosted || 'default'; - - if (await hasPermission(req.body.requestor, 'putUserProfilePermissions')) { + record.bioPosted = req.body.bioPosted || "default"; + if ( + (await hasPermission( + req.body.requestor.role, + "putUserProfilePermissions" + )) || + req.body.requestor.permissions?.frontPermissions.includes( + "putUserProfilePermissions" + ) + ) { record.permissions = req.body.permissions; } @@ -399,7 +431,9 @@ const userProfileController = function (UserProfile) { userData.createdDate = record.createdDate.toISOString(); } } - if (await hasPermission(req.body.requestor, 'infringementAuthorizer')) { + if ( + await hasPermission(req.body.requestor.role, "infringementAuthorizer") + ) { record.infringements = req.body.infringements; } @@ -429,12 +463,12 @@ const userProfileController = function (UserProfile) { const deleteUserProfile = async function (req, res) { const { option, userId } = req.body; - if (!await hasPermission(req.body.requestor, 'deleteUserProfile')) { + if (!await hasPermission(req.body.requestor.role, 'deleteUserProfile')) { res.status(403).send('You are not authorized to delete users'); return; } - if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { + if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor.role, 'addDeleteEditOwners')) { res.status(403).send('You are not authorized to delete this user'); return; } @@ -605,15 +639,9 @@ const userProfileController = function (UserProfile) { // Verify correct params in body if (!req.body.currentpassword || !req.body.newpassword || !req.body.confirmnewpassword) { - return res.status(400).send({ - error: 'One of more required fields are missing', - }); - } - // Verify request is authorized by self or adminsitrator - if (userId !== requestor.requestorId && !await hasPermission(req.body.requestor, 'updatePassword')) { - return res.status(403).send({ - error: "You are unauthorized to update this user's password", - }); + return res.status(400).send({ + error: 'One or more required fields are missing', + }); } // Check if the requestor has the permission to update passwords. @@ -673,10 +701,11 @@ const userProfileController = function (UserProfile) { } const userid = mongoose.Types.ObjectId(req.params.userId); + const { role } = req.body.requestor; let validroles = ['Volunteer', 'Manager', 'Administrator', 'Core Team', 'Owner', 'Mentor']; - if (await hasPermission(req.body.requestor, 'getReporteesLimitRoles')) { + if (await hasPermission(role, 'getReporteesLimitRoles')) { validroles = ['Volunteer', 'Manager']; } @@ -747,7 +776,7 @@ const userProfileController = function (UserProfile) { }); return; } - if (!await hasPermission(req.body.requestor, 'changeUserStatus')) { + if (!await hasPermission(req.body.requestor.role, 'changeUserStatus')) { res.status(403).send('You are not authorized to change user status'); return; } From 92bf9a85e5594ec943c1f5446817f7c070f52d06 Mon Sep 17 00:00:00 2001 From: Chuehleo <122568562+Chuehleo@users.noreply.github.com> Date: Thu, 9 Nov 2023 02:07:31 -0700 Subject: [PATCH 050/129] Update userProfileController.js --- src/controllers/userProfileController.js | 194 +++++++++++------------ 1 file changed, 90 insertions(+), 104 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index a388f10db..5968e3dd6 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -35,20 +35,15 @@ async function ValidatePassword(req, res) { return; } // Verify request is authorized by self or adminsitrator - if (userId !== requestor.requestorId && !await hasPermission(requestor.role, 'updatePassword')) { - console.log('User ID:', userId); - console.log('Requestor ID:', requestor.requestorId); + if (userId !== requestor.requestorId && !await hasPermission(req.body.requestor, 'updatePassword')) { res.status(403).send({ error: "You are unauthorized to update this user's password", }); return; } - - // Check permissions - if (userId === requestor.requestorId || !await hasPermission(requestor.role, 'updatePasswordForOthers')) { - console.log('User ID:', userId); - console.log('Requestor ID:', requestor.requestorId); + // Verify request is authorized by self or adminsitrator + if (userId === requestor.requestorId || !await hasPermission(req.body.requestor, 'updatePassword')) { res.status(403).send({ error: "You are unauthorized to update this user's password", }); @@ -65,19 +60,8 @@ async function ValidatePassword(req, res) { const userProfileController = function (UserProfile) { const getUserProfiles = async function (req, res) { - if ( - !(await hasPermission(req.body.requestor.role, "getUserProfiles")) && - !req.body.requestor.permissions?.frontPermissions.includes( - "putUserProfilePermissions" - ) - ) { - res.status(403).send("You are not authorized to view all users"); - return; - } - - if (cache.getCache("allusers")) { - const getData = JSON.parse(cache.getCache("allusers")); - res.status(200).send(getData); + if (!await hasPermission(req.body.requestor, 'getUserProfiles')) { + res.status(403).send('You are not authorized to view all users'); return; } @@ -90,18 +74,25 @@ const userProfileController = function (UserProfile) { }) .then((results) => { if (!results) { - res.status(500).send({ error: "User result was invalid" }); - return; + if (cache.getCache("allusers")) { + const getData = JSON.parse(cache.getCache("allusers")); + res.status(200).send(getData); + return; + }else{ + res.status(500).send({ error: "User result was invalid" }); + return; + } } cache.setCache("allusers", JSON.stringify(results)); res.status(200).send(results); }) .catch((error) => res.status(404).send(error)); + }; const getProjectMembers = async function (req, res) { - if (!(await hasPermission(req.body.requestor.role, "getProjectMembers"))) { - res.status(403).send("You are not authorized to view all users"); + if (!await hasPermission(req.body.requestor, 'getProjectMembers')) { + res.status(403).send('You are not authorized to view all users'); return; } UserProfile.find( @@ -122,16 +113,13 @@ const userProfileController = function (UserProfile) { }; const postUserProfile = async function (req, res) { - if (!(await hasPermission(req.body.requestor.role, "postUserProfile"))) { - res.status(403).send("You are not authorized to create new users"); + if (!await hasPermission(req.body.requestor, 'postUserProfile')) { + res.status(403).send('You are not authorized to create new users'); return; } - if ( - req.body.role === "Owner" && - !(await hasPermission(req.body.requestor.role, "addDeleteEditOwners")) - ) { - res.status(403).send("You are not authorized to create new owners"); + if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { + res.status(403).send('You are not authorized to create new owners'); return; } @@ -248,24 +236,22 @@ const userProfileController = function (UserProfile) { const putUserProfile = async function (req, res) { const userid = req.params.userId; const isRequestorAuthorized = !!( - canRequestorUpdateUser(req.body.requestor.requestorId, userid) && - ((await hasPermission(req.body.requestor.role, "putUserProfile")) || - req.body.requestor.requestorId === userid || - req.body.requestor.permissions?.frontPermissions.includes( - "putUserProfilePermissions" - )) + canRequestorUpdateUser(req.body.requestor.requestorId, userid) && ( + await hasPermission(req.body.requestor, 'putUserProfile') + || req.body.requestor.requestorId === userid + ) ); + + const canEditTeamCode = req.body.requestor.role === "Owner" || + req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); if (!isRequestorAuthorized) { res.status(403).send("You are not authorized to update this user"); return; } - if ( - req.body.role === "Owner" && - !(await hasPermission(req.body.requestor.role, "addDeleteEditOwners")) - ) { - res.status(403).send("You are not authorized to update this user"); + if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { + res.status(403).send('You are not authorized to update this user'); return; } @@ -318,6 +304,13 @@ const userProfileController = function (UserProfile) { record.isFirstTimelog = req.body.isFirstTimelog; record.teamCode = req.body.teamCode; + if(!canEditTeamCode && record.teamCode !== req.body.teamCode){ + res.status(403).send("You are not authorized to edit team code."); + return; + } + + record.teamCode = req.body.teamCode; + // find userData in cache const isUserInCache = cache.hasCache("allusers"); let allUserData; @@ -328,15 +321,7 @@ const userProfileController = function (UserProfile) { userIdx = allUserData.findIndex((users) => users._id === userid); userData = allUserData[userIdx]; } - if ( - (await hasPermission( - req.body.requestor.role, - "putUserProfileImportantInfo" - )) || - req.body.requestor.permissions?.frontPermissions.includes( - "putUserProfilePermissions" - ) - ) { + if (await hasPermission(req.body.requestor, 'putUserProfileImportantInfo')) { record.role = req.body.role; record.isRehireable = req.body.isRehireable; record.isActive = req.body.isActive; @@ -402,16 +387,9 @@ const userProfileController = function (UserProfile) { record.createdDate; } - record.bioPosted = req.body.bioPosted || "default"; - if ( - (await hasPermission( - req.body.requestor.role, - "putUserProfilePermissions" - )) || - req.body.requestor.permissions?.frontPermissions.includes( - "putUserProfilePermissions" - ) - ) { + record.bioPosted = req.body.bioPosted || 'default'; + + if (await hasPermission(req.body.requestor, 'putUserProfilePermissions')) { record.permissions = req.body.permissions; } @@ -431,9 +409,7 @@ const userProfileController = function (UserProfile) { userData.createdDate = record.createdDate.toISOString(); } } - if ( - await hasPermission(req.body.requestor.role, "infringementAuthorizer") - ) { + if (await hasPermission(req.body.requestor, 'infringementAuthorizer')) { record.infringements = req.body.infringements; } @@ -463,12 +439,12 @@ const userProfileController = function (UserProfile) { const deleteUserProfile = async function (req, res) { const { option, userId } = req.body; - if (!await hasPermission(req.body.requestor.role, 'deleteUserProfile')) { + if (!await hasPermission(req.body.requestor, 'deleteUserProfile')) { res.status(403).send('You are not authorized to delete users'); return; } - if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor.role, 'addDeleteEditOwners')) { + if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { res.status(403).send('You are not authorized to delete this user'); return; } @@ -602,6 +578,17 @@ const userProfileController = function (UserProfile) { const { userId } = req.params; const { key, value } = req.body; + if (key === "teamCode") { + const canEditTeamCode = req.body.requestor.role === "Owner" || + req.body.requestor.permissions?.frontPermissions.includes("editTeamCode"); + + if(!canEditTeamCode){ + res.status(403).send("You are not authorized to edit team code."); + return; + } + + } + // remove user from cache, it should be loaded next time cache.removeCache(`user-${userId}`); if (!key || value === undefined) return res.status(400).send({ error: 'Missing property or value' }); @@ -626,24 +613,18 @@ const userProfileController = function (UserProfile) { const updatepassword = async function (req, res) { const { userId } = req.params; const { requestor } = req.body; - - console.log('User ID:', userId); - console.log('Requestor ID:', requestor.requestorId); - - // Check if userId is valid. if (!mongoose.Types.ObjectId.isValid(userId)) { - return res.status(400).send({ - error: 'Bad Request', - }); + return res.status(400).send({ + error: 'Bad Request', + }); } // Verify correct params in body if (!req.body.currentpassword || !req.body.newpassword || !req.body.confirmnewpassword) { - return res.status(400).send({ - error: 'One or more required fields are missing', - }); + return res.status(400).send({ + error: 'One of more required fields are missing', + }); } - // Check if the requestor has the permission to update passwords. const hasUpdatePasswordPermission = await hasPermission(requestor.role, 'updatePassword'); @@ -666,31 +647,37 @@ const userProfileController = function (UserProfile) { }); } - // Process the password change - try { - const user = await UserProfile.findById(userId, 'password'); - const passwordMatch = await bcrypt.compare(req.body.currentpassword, user.password); + // Verify old and new passwords are not same + if (req.body.currentpassword === req.body.newpassword) { + res.status(400).send({ + error: 'Old and new passwords should not be same', + }); + } - if (!passwordMatch) { - return res.status(400).send({ + return UserProfile.findById(userId, 'password') + .then((user) => { + bcrypt + .compare(req.body.currentpassword, user.password) + .then((passwordMatch) => { + if (!passwordMatch) { + return res.status(400).send({ error: 'Incorrect current password', - }); - } - - user.set({ - password: req.body.newpassword, - resetPwd: undefined, - }); - - await user.save(); - - return res.status(200).send({ message: 'Updated password successfully' }); - - } catch (error) { - return res.status(500).send(error); - } -}; + }); + } + user.set({ + password: req.body.newpassword, + resetPwd: undefined, + }); + return user + .save() + .then(() => res.status(200).send({ message: 'updated password' })) + .catch(error => res.status(500).send(error)); + }) + .catch(error => res.status(500).send(error)); + }) + .catch(error => res.status(500).send(error)); + }; const getreportees = async function (req, res) { if (!mongoose.Types.ObjectId.isValid(req.params.userId)) { @@ -701,11 +688,10 @@ const userProfileController = function (UserProfile) { } const userid = mongoose.Types.ObjectId(req.params.userId); - const { role } = req.body.requestor; let validroles = ['Volunteer', 'Manager', 'Administrator', 'Core Team', 'Owner', 'Mentor']; - if (await hasPermission(role, 'getReporteesLimitRoles')) { + if (await hasPermission(req.body.requestor, 'getReporteesLimitRoles')) { validroles = ['Volunteer', 'Manager']; } @@ -776,7 +762,7 @@ const userProfileController = function (UserProfile) { }); return; } - if (!await hasPermission(req.body.requestor.role, 'changeUserStatus')) { + if (!await hasPermission(req.body.requestor, 'changeUserStatus')) { res.status(403).send('You are not authorized to change user status'); return; } From 8a634b4dfdd9339a89d8dae939925a5ac10abb74 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Fri, 10 Nov 2023 09:31:52 -0800 Subject: [PATCH 051/129] add new inventory models, invtype router and controller --- .../bmdashboard/bmInventoryTypeController.js | 19 +++++++++++++ .../bmdashboard/buildingInventoryType.js | 13 +++++++++ src/models/bmdashboard/buildingMaterial.js | 28 +++++++++++++++++++ .../bmdashboard/bmInventoryTypeRouter.js | 13 +++++++++ 4 files changed, 73 insertions(+) create mode 100644 src/controllers/bmdashboard/bmInventoryTypeController.js create mode 100644 src/models/bmdashboard/buildingInventoryType.js create mode 100644 src/models/bmdashboard/buildingMaterial.js create mode 100644 src/routes/bmdashboard/bmInventoryTypeRouter.js diff --git a/src/controllers/bmdashboard/bmInventoryTypeController.js b/src/controllers/bmdashboard/bmInventoryTypeController.js new file mode 100644 index 000000000..b9ced243e --- /dev/null +++ b/src/controllers/bmdashboard/bmInventoryTypeController.js @@ -0,0 +1,19 @@ +const bmInventoryTypeController = function (InvType) { + const fetchMaterialTypes = async (req, res) => { + try { + InvType + .find() + .exec() + .then(result => res.status(200).send(result)) + .catch(error => res.status(500).send(error)); + } catch (err) { + res.json(err); + } + }; + + return { + fetchMaterialTypes, + }; +}; + +module.exports = bmInventoryTypeController; diff --git a/src/models/bmdashboard/buildingInventoryType.js b/src/models/bmdashboard/buildingInventoryType.js new file mode 100644 index 000000000..71285fa24 --- /dev/null +++ b/src/models/bmdashboard/buildingInventoryType.js @@ -0,0 +1,13 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingInventoryType = new Schema({ + category: String, // Consumable, Material, Tool, Equipment + name: String, + description: String, + unit: String, // unit of measurement + imageUrl: String, +}); + +module.exports = mongoose.model('buildingInventoryType', buildingInventoryType, 'buildingInventoryTypes'); diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js new file mode 100644 index 000000000..714ba4ae7 --- /dev/null +++ b/src/models/bmdashboard/buildingMaterial.js @@ -0,0 +1,28 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingMaterial = new Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project + stockUsed: { type: Number, default: 0 }, // total amount of item used successfully in the project + stockWasted: { type: Number, default: 0 }, // total amount of item wasted/ruined/lost in the project + stockAvailable: { type: Number, default: 0 }, // bought - (used + wasted) + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: Number, + status: { type: String, default: 'Pending' }, // Pending, Rejected, Approved + }], + updateRecord: [{ + _id: false, + date: Date, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: String, // '10 cubic yards' + quantityWasted: Number, + }], +}); + +module.exports = mongoose.model('buildingMaterial', buildingMaterial, 'buildingMaterials'); diff --git a/src/routes/bmdashboard/bmInventoryTypeRouter.js b/src/routes/bmdashboard/bmInventoryTypeRouter.js new file mode 100644 index 000000000..ceae439dc --- /dev/null +++ b/src/routes/bmdashboard/bmInventoryTypeRouter.js @@ -0,0 +1,13 @@ +const express = require('express'); + +const routes = function (invType) { + const inventoryTypeRouter = express.Router(); + const controller = require('../../controllers/bmdashboard/bmInventoryTypeController')(invType); + + inventoryTypeRouter.route('/invtypes/materials') + .get(controller.fetchMaterialTypes); + + return inventoryTypeRouter; +}; + +module.exports = routes; From cff986158da264ab65cc78e960d86b7bb4f3f58f Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Fri, 10 Nov 2023 17:17:54 -0800 Subject: [PATCH 052/129] building inventory --- package-lock.json | 7 +- package.json | 1 + .../bmdashboard/bmMaterialsController.js | 68 +++++++++---------- src/models/buildingInventoryType.js | 10 +++ src/models/buildingMaterial.js | 23 +++++++ src/models/buildingProject.js | 12 ++++ src/models/inventoryItemMaterial.js | 4 +- src/routes/bmdashboard/bmMaterialsRouter.js | 4 +- src/startup/routes.js | 6 +- 9 files changed, 94 insertions(+), 41 deletions(-) create mode 100644 src/models/buildingInventoryType.js create mode 100644 src/models/buildingMaterial.js create mode 100644 src/models/buildingProject.js diff --git a/package-lock.json b/package-lock.json index 3fcc98986..d26330d5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5536,7 +5536,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, "lodash.merge": { "version": "4.6.2", @@ -5806,6 +5806,11 @@ "moment": ">= 2.9.0" } }, + "mongo-round": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mongo-round/-/mongo-round-1.0.0.tgz", + "integrity": "sha512-lwvLJv827Uks+3HnTOt1I/Qr78Avke3du1oMaFqFpTwtRKtOposNOKkfpGXQN4ZGpRN3XAS8fEppIJ4TUj0xQw==" + }, "mongodb": { "version": "3.7.3", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", diff --git a/package.json b/package.json index 1c6b8a5d4..32d23ee6a 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.35", + "mongo-round": "^1.0.0", "mongodb": "^3.7.3", "mongoose": "^5.13.15", "mongoose-validator": "^2.1.0", diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 0e96b5bbb..4529546da 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -7,18 +7,11 @@ const bmMaterialsController = function (ItemMaterial) { .populate([ { path: 'project', - select: '_id projectName', + select: '_id name', }, { - path: 'inventoryItemType', - select: '_id name uom totalStock totalAvailable', - }, - { - path: 'usageRecord', - populate: { - path: 'createdBy', - select: '_id firstName lastName', - }, + path: 'itemType', + select: '_id name unit', }, { path: 'updateRecord', @@ -30,14 +23,17 @@ const bmMaterialsController = function (ItemMaterial) { { path: 'purchaseRecord', populate: { - path: 'createdBy', + path: 'requestedBy', select: '_id firstName lastName', }, }, ]) .exec() .then(results => res.status(200).send(results)) - .catch(error => res.status(500).send(error)); + .catch(error => { + console.log(error) + res.status(500).send(error) + }); } catch (err) { res.json(err); } @@ -63,26 +59,28 @@ const bmMaterialsController = function (ItemMaterial) { } else { - quantityUsed = Number.parseFloat(quantityUsed.toFixed(2)); - quantityWasted = Number.parseFloat(quantityWasted.toFixed(2)); - let newAvailable = Number.parseFloat(quantityUsed+quantityWasted); + quantityUsed = +material.stockUsed + parseFloat(quantityUsed.toFixed(4)); + quantityWasted = +material.stockWasted + parseFloat(quantityWasted.toFixed(4)); + let newAvailable = +material.stockAvailable - parseFloat(quantityUsed+quantityWasted); ItemMaterial.updateOne( { _id: req.body.material._id }, - { - $inc: { + + { + $set: { 'stockUsed': quantityUsed, 'stockWasted': quantityWasted, 'stockAvailable': -(newAvailable) }, - $push: { - updateRecord: { - date: req.body.date, - createdBy: req.body.requestor.requestorId, - quantity: -(quantityUsed+quantityWasted), - }, + $push: { + updateRecord: { + date: req.body.date, + createdBy: req.body.requestor.requestorId, + quantity: -(quantityUsed+quantityWasted), + }, + } } - } + ) .then(results => {res.status(200).send(results)}) .catch(error => res.status(500).send({'message':error})); @@ -104,20 +102,21 @@ const bmMaterialsController = function (ItemMaterial) { quantityWasted = (+quantityWasted / 100) * material.stockAvailable; } - quantityUsed = Number.parseFloat(quantityUsed.toFixed(2)); - quantityWasted = Number.parseFloat(quantityWasted.toFixed(2)); - let newAvailable = Number.parseFloat(quantityUsed+quantityWasted); - + quantityUsed = +material.stockUsed + parseFloat(quantityUsed.toFixed(4)); + quantityWasted = +material.stockWasted + parseFloat(quantityWasted.toFixed(4)); + let newAvailable = +material.stockAvailable - parseFloat(quantityUsed+quantityWasted); + newAvailable = parseFloat(newAvailable.toFixed(4)); + console.log(quantityUsed,quantityWasted,newAvailable); return ({ updateId: material._id, - increment: { + set: { 'stockUsed': quantityUsed, 'stockWasted': quantityWasted, - 'stockAvailable': -(newAvailable) + 'stockAvailable': -newAvailable }, updateValue: { createdBy: req.body.requestor.requestorId, - quantity: -(quantityUsed+quantityWasted), + quantity: -(newAvailable), date: payload.date, }}); @@ -127,9 +126,10 @@ const bmMaterialsController = function (ItemMaterial) { try { const updatePromises = updateRecordsToBeAdded.map(updateItem => ItemMaterial.updateOne( { _id: updateItem.updateId }, - { - $inc: updateItem.increment , - $push: { usageRecord: updateItem.updateValue } }, + { + $set : updateItem.set, + $push: { updateRecord: updateItem.updateValue } + }, ).exec()); Promise.all(updatePromises) .then((results) => { diff --git a/src/models/buildingInventoryType.js b/src/models/buildingInventoryType.js new file mode 100644 index 000000000..3981b3f5a --- /dev/null +++ b/src/models/buildingInventoryType.js @@ -0,0 +1,10 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; +const buildingInventoryType = new Schema({ + category: String, // Consumable, Material, Tool, Equipment + name: String, + description: String, + unit: String, // unit of measurement + imageUrl: String, +}); +module.exports = mongoose.model('buildingInventoryType', buildingInventoryType, 'buildingInventoryTypes'); \ No newline at end of file diff --git a/src/models/buildingMaterial.js b/src/models/buildingMaterial.js new file mode 100644 index 000000000..c1d74b879 --- /dev/null +++ b/src/models/buildingMaterial.js @@ -0,0 +1,23 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; +const buildingMaterial = new Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + stockBought: Number, // total amount of item bought for use in the project + stockUsed: Number, // total amount of item used successfully in the project + stockWasted: Number, // total amount of item wasted/ruined/lost in the project + stockAvailable: Number, // bought - (used + wasted) + purchaseRecord: [{ + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: Number, + status: { type: String, default: 'Pending' }, // Pending, Rejected, Approved + }], + updateRecord: [{ + date: Date, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: String, // '10 cubic yards' + quantityWasted: Number, + }], +}); +module.exports = mongoose.model('buildingMaterial', buildingMaterial, 'buildingMaterials'); diff --git a/src/models/buildingProject.js b/src/models/buildingProject.js new file mode 100644 index 000000000..20d26fc41 --- /dev/null +++ b/src/models/buildingProject.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; +const buildingProject = new Schema({ + isActive: Boolean, + name: String, + template: String, // construction template (ie Earthbag Village) + location: String, // use lat/lng instead? + dateCreated: { type: Date, default: Date.now }, + buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, // BM's id + team: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }], +}); +module.exports = mongoose.model('buildingProject', buildingProject, 'buildingProjects'); \ No newline at end of file diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index 8460ecd6e..3cc7a9966 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -7,9 +7,9 @@ const InventoryItemMaterial = new Schema({ project: { type: mongoose.SchemaTypes.ObjectId, ref: 'project', required: true }, stockBought: { type: Number, required: true }, // amount bought for project, affects total stock stockUsed: { type: Number, required: true }, - stockAvailable: { type: Number, required: true }, + stockAvailable: { type: Number, required: true}, stockHeld: { type: Number, required: true }, - stockWasted: { type: Number, required: true }, + stockWasted: { type: Number, required: true }, usageRecord: [{ // daily log of amount inventory item used at job site date: { type: Date, required: true, default: Date.now() }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index 35a887b9b..d56c682bd 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -1,8 +1,8 @@ const express = require('express'); -const routes = function (itemMaterial) { +const routes = function (buildingMaterial) { const materialsRouter = express.Router(); -const controller = require('../../controllers/bmdashboard/bmMaterialsController')(itemMaterial); +const controller = require('../../controllers/bmdashboard/bmMaterialsController')(buildingMaterial); materialsRouter.route('/materials') .get(controller.bmMaterialsList); diff --git a/src/startup/routes.js b/src/startup/routes.js index 7208535d8..798ae969e 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -21,7 +21,9 @@ const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); -const inventoryItemMaterial = require('../models/inventoryItemMaterial'); +const buildingMaterial = require('../models/buildingMaterial'); +const buildingProject = require('../models/buildingProject'); +const buildingInventoryType = require('../models/buildingInventoryType'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -60,7 +62,7 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); -const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(buildingMaterial); const bmProjectsRouter = require('../routes/bmdashboard/bmProjectsRouter')(); module.exports = function (app) { From 36dd97298dcf2ec8e34801c1ba7b2f7de074f028 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Fri, 10 Nov 2023 19:17:11 -0800 Subject: [PATCH 053/129] building material update --- .../bmdashboard/bmMaterialsController.js | 60 ++++++++++--------- .../bmdashboard/bmProjectsController.js | 31 ---------- .../buildingInventoryType.js | 0 .../{ => bmdashboard}/buildingMaterial.js | 0 src/models/buildingProject.js | 12 ---- src/routes/bmdashboard/bmMaterialsRouter.js | 4 +- src/routes/bmdashboard/bmProjectsRouter.js | 13 ---- src/startup/routes.js | 9 ++- 8 files changed, 38 insertions(+), 91 deletions(-) delete mode 100644 src/controllers/bmdashboard/bmProjectsController.js rename src/models/{ => bmdashboard}/buildingInventoryType.js (100%) rename src/models/{ => bmdashboard}/buildingMaterial.js (100%) delete mode 100644 src/models/buildingProject.js delete mode 100644 src/routes/bmdashboard/bmProjectsRouter.js diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index ebf5798fa..c5403de21 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -37,18 +37,18 @@ const bmMaterialsController = function (ItemMaterial) { }; const bmPostMaterialUpdateRecord = function (req, res) { - + let payload = req.body; let quantityUsed = +req.body.quantityUsed; let quantityWasted = +req.body.quantityWasted; let material = req.body.material; - if(req.body.QtyUsedLogUnit=='percent' && quantityWasted>=0) - { - quantityUsed = (+quantityUsed / 100) * material.stockAvailable; - } - if(req.body.QtyWastedLogUnit=='percent' && quantityUsed>=0) - { - quantityWasted = (+quantityWasted / 100) * material.stockAvailable; - } + if(payload.QtyUsedLogUnit=='percent' && quantityWasted>=0) + { + quantityUsed = +((+quantityUsed / 100) * material.stockAvailable).toFixed(4); + } + if(payload.QtyWastedLogUnit=='percent' && quantityUsed>=0) + { + quantityWasted = +((+quantityWasted / 100) * material.stockAvailable).toFixed(4); + } if(quantityUsed>material.stockAvailable || quantityWasted>material.stockAvailable || (quantityUsed+quantityWasted)>material.stockAvailable) { @@ -56,24 +56,27 @@ const bmMaterialsController = function (ItemMaterial) { } else { - quantityUsed = +material.stockUsed + parseFloat(quantityUsed.toFixed(4)); - quantityWasted = +material.stockWasted + parseFloat(quantityWasted.toFixed(4)); - let newAvailable = +material.stockAvailable - parseFloat(quantityUsed+quantityWasted); - + let newStockUsed = +material.stockUsed + parseFloat(quantityUsed); + let newStockWasted = +material.stockWasted + parseFloat(quantityWasted); + let newAvailable = +material.stockAvailable - parseFloat(quantityUsed) - parseFloat(quantityWasted); + newStockUsed = parseFloat(newStockUsed.toFixed(4)); + newStockWasted = parseFloat(newStockWasted.toFixed(4)); + newAvailable = parseFloat(newAvailable.toFixed(4)); ItemMaterial.updateOne( { _id: req.body.material._id }, { $set: { - 'stockUsed': quantityUsed, - 'stockWasted': quantityWasted, - 'stockAvailable': -(newAvailable) + 'stockUsed': newStockUsed, + 'stockWasted': newStockWasted, + 'stockAvailable': newAvailable }, $push: { updateRecord: { date: req.body.date, createdBy: req.body.requestor.requestorId, - quantity: -(quantityUsed+quantityWasted), + quantityUsed: quantityUsed + ' ' + material.itemType.unit, + quantityWasted: quantityWasted }, } } @@ -92,33 +95,34 @@ const bmMaterialsController = function (ItemMaterial) { let material = payload.material; if(payload.QtyUsedLogUnit=='percent' && quantityWasted>=0) { - quantityUsed = (+quantityUsed / 100) * material.stockAvailable; + quantityUsed = +((+quantityUsed / 100) * material.stockAvailable).toFixed(4); } if(payload.QtyWastedLogUnit=='percent' && quantityUsed>=0) { - quantityWasted = (+quantityWasted / 100) * material.stockAvailable; + quantityWasted = +((+quantityWasted / 100) * material.stockAvailable).toFixed(4); } - quantityUsed = +material.stockUsed + parseFloat(quantityUsed.toFixed(4)); - quantityWasted = +material.stockWasted + parseFloat(quantityWasted.toFixed(4)); - let newAvailable = +material.stockAvailable - parseFloat(quantityUsed+quantityWasted); + let newStockUsed = +material.stockUsed + parseFloat(quantityUsed); + let newStockWasted = +material.stockWasted + parseFloat(quantityWasted); + let newAvailable = +material.stockAvailable - parseFloat(quantityUsed) - parseFloat(quantityWasted); + newStockUsed = parseFloat(newStockUsed.toFixed(4)); + newStockWasted = parseFloat(newStockWasted.toFixed(4)); newAvailable = parseFloat(newAvailable.toFixed(4)); - console.log(quantityUsed,quantityWasted,newAvailable); return ({ updateId: material._id, set: { - 'stockUsed': quantityUsed, - 'stockWasted': quantityWasted, - 'stockAvailable': -newAvailable + 'stockUsed': newStockUsed, + 'stockWasted': newStockWasted, + 'stockAvailable': newAvailable }, updateValue: { createdBy: req.body.requestor.requestorId, - quantity: -(newAvailable), + quantityUsed: quantityUsed + ' ' + material.itemType.unit, + quantityWasted: quantityWasted, date: payload.date, }}); }); - console.log(updateRecordsToBeAdded); try { const updatePromises = updateRecordsToBeAdded.map(updateItem => ItemMaterial.updateOne( diff --git a/src/controllers/bmdashboard/bmProjectsController.js b/src/controllers/bmdashboard/bmProjectsController.js deleted file mode 100644 index 3ff0f851a..000000000 --- a/src/controllers/bmdashboard/bmProjectsController.js +++ /dev/null @@ -1,31 +0,0 @@ -const mongoose = require('mongoose'); -const UserProfile = require('../../models/userProfile'); - -const bmProjectsController = function () { - // Get current user's Housing/Building projects - const getUserActiveBMProjects = function (req, res) { - try { - const userId = req.body.requestor.requestorId; - UserProfile.findById(userId) - .populate([ - { - path: 'projects', - select: '_id projectName category isActive', - match: { category: 'Housing' }, - }, - ]) - .select({ - projects: 1, - }) - .then((results) => { - res.status(200).send(results); - }) - .catch(error => res.status(500).send(error)); - } catch (err) { - res.json(err); - } - }; - return { getUserActiveBMProjects }; -}; - -module.exports = bmProjectsController; diff --git a/src/models/buildingInventoryType.js b/src/models/bmdashboard/buildingInventoryType.js similarity index 100% rename from src/models/buildingInventoryType.js rename to src/models/bmdashboard/buildingInventoryType.js diff --git a/src/models/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js similarity index 100% rename from src/models/buildingMaterial.js rename to src/models/bmdashboard/buildingMaterial.js diff --git a/src/models/buildingProject.js b/src/models/buildingProject.js deleted file mode 100644 index 20d26fc41..000000000 --- a/src/models/buildingProject.js +++ /dev/null @@ -1,12 +0,0 @@ -const mongoose = require('mongoose'); -const { Schema } = mongoose; -const buildingProject = new Schema({ - isActive: Boolean, - name: String, - template: String, // construction template (ie Earthbag Village) - location: String, // use lat/lng instead? - dateCreated: { type: Date, default: Date.now }, - buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, // BM's id - team: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }], -}); -module.exports = mongoose.model('buildingProject', buildingProject, 'buildingProjects'); \ No newline at end of file diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index d56c682bd..a07d82b5c 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -7,10 +7,10 @@ const controller = require('../../controllers/bmdashboard/bmMaterialsController' materialsRouter.route('/materials') .get(controller.bmMaterialsList); -materialsRouter.route('/addUpdateMaterialRecord') +materialsRouter.route('/updateMaterialRecord') .post(controller.bmPostMaterialUpdateRecord); - materialsRouter.route('/UpdateMaterialRecordBulk') + materialsRouter.route('/updateMaterialRecordBulk') .post(controller.bmPostMaterialUpdateBulk); diff --git a/src/routes/bmdashboard/bmProjectsRouter.js b/src/routes/bmdashboard/bmProjectsRouter.js deleted file mode 100644 index a171fc4cd..000000000 --- a/src/routes/bmdashboard/bmProjectsRouter.js +++ /dev/null @@ -1,13 +0,0 @@ -const express = require('express'); - -const routes = function () { -const materialsRouter = express.Router(); -const controller = require('../../controllers/bmdashboard/bmProjectsController')(); - -materialsRouter.route('/getUserActiveBMProjects') - .get(controller.getUserActiveBMProjects); - - return materialsRouter; -}; - -module.exports = routes; diff --git a/src/startup/routes.js b/src/startup/routes.js index 201c777b9..1713d6a67 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -20,9 +20,9 @@ const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); -const buildingMaterial = require('../models/buildingMaterial'); -const buildingProject = require('../models/buildingProject'); -const buildingInventoryType = require('../models/buildingInventoryType'); +const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); +const buildingProject = require('../models/bmdashboard/buildingProject'); +const buildingInventoryType = require('../models/bmdashboard/buildingInventoryType'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -61,7 +61,6 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(buildingMaterial); -const bmProjectsRouter = require('../routes/bmdashboard/bmProjectsRouter')(); const bmProjectRouter = require('../routes/bmdashboard/bmProjectRouter')(buildingProject); module.exports = function (app) { @@ -96,6 +95,6 @@ module.exports = function (app) { app.use('/api', isEmailExistsRouter); // bm dashboard app.use('/api/bm', bmLoginRouter); - app.use('/api/bm', bmProjectsRouter); app.use('/api/bm', bmProjectRouter); + app.use('/api/bm', bmMaterialsRouter); }; From 55dd2e67cdf3b9fb91081ccb175003129c2b91d7 Mon Sep 17 00:00:00 2001 From: Aishwaryak01 Date: Sat, 11 Nov 2023 12:04:05 -0500 Subject: [PATCH 054/129] Create buildingTools.js Created model file for buildingTools.js which consists of schema to hold various attribute information related to tools. --- src/models/bmdashboard/buildingTools.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/models/bmdashboard/buildingTools.js diff --git a/src/models/bmdashboard/buildingTools.js b/src/models/bmdashboard/buildingTools.js new file mode 100644 index 000000000..94468de42 --- /dev/null +++ b/src/models/bmdashboard/buildingTools.js @@ -0,0 +1,19 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingTools = new Schema({ + inventoryItemId: { type: mongoose.SchemaTypes.ObjectId, ref: 'INVENTORY' }, // later ,INVENTORY needs to be changed as per inventory model + title: { type: String, required: true }, + image: { type: String, required: true }, + rentedOnDate: { type: Date, required: true }, + rentDuration: { type: Number, required: true }, // This value should be taken in number of days + total: {type: Number, required: true}, + availableCount:{type: Number, required: true}, + logInStatus:{Boolean}, + condition:{type: String, enum: ['Good', 'Needs Repair', 'Out of Order','Unused'], default: 'Condition'}, + userResponsible:{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + purchaseStatus:{type: String, enum: ['Rented', 'Purchased'], default: 'Status'}, +}); + +module.exports = mongoose.model('buildingTools', buildingTools, 'buildingTools'); \ No newline at end of file From bd537f79f38c980eea9ba1660e6b89aff577ece8 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 13 Nov 2023 10:52:54 +0800 Subject: [PATCH 055/129] solve weekly summaries report page issue --- src/helpers/reporthelper.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index c812ef03c..0c2a8104d 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -68,9 +68,6 @@ const reporthelper = function () { moment(pstEnd).format("YYYY-MM-DD"), ], }, - { - $in: ['$$timeentry.entryType', ['default', null]], - }, ], }, }, From 54aeac506b0cba57b56fe7999742e135de4642ec Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Mon, 13 Nov 2023 14:36:42 -0500 Subject: [PATCH 056/129] merging development --- src/controllers/badgeController.js | 3 + .../bmdashboard/bmMaterialsController.js | 24 +++--- .../bmdashboard/bmProjectController.js | 79 +++++++++++++++++++ src/helpers/userHelper.js | 45 +++++++++-- src/models/bmdashboard/buildingProject.js | 15 ++++ src/models/inventoryItemMaterial.js | 6 +- src/routes/bmdashboard/bmMaterialsRouter.js | 4 +- src/routes/bmdashboard/bmProjectRouter.js | 16 ++++ src/startup/routes.js | 4 + src/utilities/escapeRegex.js | 2 +- 10 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 src/controllers/bmdashboard/bmProjectController.js create mode 100644 src/models/bmdashboard/buildingProject.js create mode 100644 src/routes/bmdashboard/bmProjectRouter.js diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 62bad6399..366c9323e 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -2,6 +2,7 @@ const mongoose = require('mongoose'); const UserProfile = require('../models/userProfile'); const { hasPermission } = require('../utilities/permissions'); const escapeRegex = require('../utilities/escapeRegex'); +const cache = require('../utilities/nodeCache')(); const badgeController = function (Badge) { const getAllBadges = async function (req, res) { @@ -47,6 +48,8 @@ const badgeController = function (Badge) { if (result) { record.badgeCollection = req.body.badgeCollection; + if (cache.hasCache(`user-${userToBeAssigned}`)) cache.removeCache(`user-${userToBeAssigned}`); + record.save() .then(results => res.status(201).send(results._id)) .catch(errors => res.status(500).send(errors)); diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index a31ed460e..fb6003e90 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -1,4 +1,4 @@ -const mongoose = require('mongoose') +const mongoose = require('mongoose'); const bmMaterialsController = function (ItemMaterial) { const bmMaterialsList = async function _matsList(req, res) { @@ -7,37 +7,37 @@ const bmMaterialsController = function (ItemMaterial) { .populate([ { path: 'project', - select: '_id projectName' + select: '_id projectName', }, { path: 'inventoryItemType', - select: '_id name uom totalStock totalAvailable' + select: '_id name uom totalStock totalAvailable', }, { path: 'usageRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } + select: '_id firstName lastName', + }, }, { path: 'updateRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } + select: '_id firstName lastName', + }, }, { path: 'purchaseRecord', populate: { path: 'createdBy', - select: '_id firstName lastName' - } - } + select: '_id firstName lastName', + }, + }, ]) .exec() .then(results => res.status(200).send(results)) - .catch(error => res.status(500).send(error)) + .catch(error => res.status(500).send(error)); } catch (err) { res.json(err); } @@ -45,4 +45,4 @@ const bmMaterialsController = function (ItemMaterial) { return { bmMaterialsList }; }; -module.exports = bmMaterialsController; \ No newline at end of file +module.exports = bmMaterialsController; diff --git a/src/controllers/bmdashboard/bmProjectController.js b/src/controllers/bmdashboard/bmProjectController.js new file mode 100644 index 000000000..929aba4ba --- /dev/null +++ b/src/controllers/bmdashboard/bmProjectController.js @@ -0,0 +1,79 @@ +// TODO: uncomment when executing auth checks +// const jwt = require('jsonwebtoken'); +// const config = require('../../config'); + +const bmMProjectController = function (BuildingProject) { + // TODO: uncomment when executing auth checks + // const { JWT_SECRET } = config; + + const fetchAllProjects = async (req, res) => { + //! Note: for easier testing this route currently returns all projects from the db + // TODO: uncomment the lines below to return only projects where field buildingManager === userid + // const token = req.headers.authorization; + // const { userid } = jwt.verify(token, JWT_SECRET); + try { + const projectData = await BuildingProject + // TODO: uncomment this line to filter by buildingManager field + // .find({ buildingManager: userid }) + .find() + .populate([ + { + path: 'buildingManager', + select: '_id firstName lastName email', + }, + { + path: 'team', + select: '_id firstName lastName email', + }, + ]) + .exec() + .then(result => result) + .catch(error => res.status(500).send(error)); + res.status(200).send(projectData); + } catch (err) { + res.json(err); + } + }; + + // fetches single project by project id + const fetchSingleProject = async (req, res) => { + //! Note: for easier testing this route currently returns the project without an auth check + // TODO: uncomment the lines below to check the user's ability to view the current project + // const token = req.headers.authorization; + // const { userid } = jwt.verify(token, JWT_SECRET); + const { projectId } = req.params; + try { + BuildingProject + .findById(projectId) + .populate([ + { + path: 'buildingManager', + select: '_id firstName lastName email', + }, + { + path: 'team', + select: '_id firstName lastName email', + }, + ]) + .exec() + .then(project => res.status(200).send(project)) + // TODO: uncomment this block to execute the auth check + // authenticate request by comparing userId param with buildingManager id field + // Note: _id has type object and must be converted to string + // .then((project) => { + // if (userid !== project.buildingManager._id.toString()) { + // return res.status(403).send({ + // message: 'You are not authorized to view this record.', + // }); + // } + // return res.status(200).send(project); + // }) + .catch(error => res.status(500).send(error)); + } catch (err) { + res.json(err); + } + }; + return { fetchAllProjects, fetchSingleProject }; +}; + +module.exports = bmMProjectController; diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index af416f1a7..c75dd7b45 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -87,8 +87,18 @@ const userHelper = function () { firstName, lastName, infringement, - totalInfringements + totalInfringements, + timeRemaining ) { + let final_paragraph = ''; + + if (timeRemaining == undefined) { + final_paragraph = '

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

'; + } else { + final_paragraph = `

Life happens and we understand that. Please make up the missed hours this following week though to avoid getting another blue square. So you know what’s needed, the missing/incomplete hours (${timeRemaining} hours) have been added to your current week and this new weekly total can be seen at the top of your dashboard.

+

Reminder also that each blue square is removed from your profile 1 year after it was issued.

`; + } + const text = `Dear ${firstName} ${lastName},

Oops, it looks like something happened and you’ve managed to get a blue square.

Date Assigned: ${infringement.date}

@@ -96,9 +106,10 @@ const userHelper = function () {

Total Infringements: This is your ${moment .localeData() .ordinal(totalInfringements)} blue square of 5.

-

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

+ ${final_paragraph}

Thank you,
One Community

`; + return text; }; @@ -322,6 +333,8 @@ const userHelper = function () { for (let i = 0; i < users.length; i += 1) { const user = users[i]; + const person = await userProfile.findById(user._id); + const foundReason = await Reason.findOne({ date: currentUTCDate, userId: user._id, @@ -356,6 +369,9 @@ const userHelper = function () { const timeNotMet = timeSpent < weeklycommittedHours; let description; + const timeRemaining = weeklycommittedHours - timeSpent; + + const updateResult = await userProfile.findByIdAndUpdate( personId, { @@ -446,15 +462,28 @@ const userHelper = function () { { new: true } ); - emailSender( - status.email, - "New Infringement Assigned", - getInfringementEmailBody( + let emailBody = ''; + if (person.role == 'Core Team' && timeRemaining > 0) { + emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, - status.infringements.length - ), + status.infringements.length, + timeRemaining, + ); + } else { + emailBody = getInfringementEmailBody( + status.firstName, + status.lastName, + infringement, + status.infringements.length, + ); + } + + emailSender( + status.email, + 'New Infringement Assigned', + emailBody, null, "onecommunityglobal@gmail.com" ); diff --git a/src/models/bmdashboard/buildingProject.js b/src/models/bmdashboard/buildingProject.js new file mode 100644 index 000000000..566bc124e --- /dev/null +++ b/src/models/bmdashboard/buildingProject.js @@ -0,0 +1,15 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingProject = new Schema({ + isActive: Boolean, + name: String, + template: String, // construction template (ie Earthbag Village) + location: String, // use lat/lng instead? + dateCreated: { type: Date, default: Date.now }, + buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, // BM's id + team: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }], +}); + +module.exports = mongoose.model('buildingProject', buildingProject, 'buildingProjects'); diff --git a/src/models/inventoryItemMaterial.js b/src/models/inventoryItemMaterial.js index e6153af45..8460ecd6e 100644 --- a/src/models/inventoryItemMaterial.js +++ b/src/models/inventoryItemMaterial.js @@ -29,12 +29,12 @@ const InventoryItemMaterial = new Schema({ poId: { type: String, required: true }, sellerId: { type: String, required: true }, quantity: { type: Number, required: true }, // adds to stockBought - unitPrice: {type: Number, required: true}, + unitPrice: { type: Number, required: true }, currency: { type: String, required: true }, subtotal: { type: Number, required: true }, tax: { type: Number, required: true }, shipping: { type: Number, required: true }, - }] -}) + }], +}); module.exports = mongoose.model('inventoryItemMaterial', InventoryItemMaterial, 'inventoryMaterial'); diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index ab8a67388..1188acf9d 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -8,6 +8,6 @@ materialsRouter.route('/materials') .get(controller.bmMaterialsList); return materialsRouter; -} +}; -module.exports = routes; \ No newline at end of file +module.exports = routes; diff --git a/src/routes/bmdashboard/bmProjectRouter.js b/src/routes/bmdashboard/bmProjectRouter.js new file mode 100644 index 000000000..d60ea9b2b --- /dev/null +++ b/src/routes/bmdashboard/bmProjectRouter.js @@ -0,0 +1,16 @@ +const express = require('express'); + +const routes = function (buildingProject) { + const projectRouter = express.Router(); + const controller = require('../../controllers/bmdashboard/bmProjectController')(buildingProject); + +projectRouter.route('/projects') + .get(controller.fetchAllProjects); + +projectRouter.route('/project/:projectId') + .get(controller.fetchSingleProject); + + return projectRouter; +}; + +module.exports = routes; diff --git a/src/startup/routes.js b/src/startup/routes.js index 0f4c7308c..c7a7393c9 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -22,6 +22,8 @@ const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); const inventoryItemMaterial = require('../models/inventoryItemMaterial'); const mapLocations = require('../models/mapLocation'); +const buildingProject = require('../models/bmdashboard/buildingProject'); + const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -62,6 +64,7 @@ const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial); +const bmProjectRouter = require('../routes/bmdashboard/bmProjectRouter')(buildingProject); module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -97,4 +100,5 @@ module.exports = function (app) { // bm dashboard app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); + app.use('/api/bm', bmProjectRouter); }; diff --git a/src/utilities/escapeRegex.js b/src/utilities/escapeRegex.js index 10fa2e61e..01a65ea50 100644 --- a/src/utilities/escapeRegex.js +++ b/src/utilities/escapeRegex.js @@ -1,6 +1,6 @@ const escapeRegex = function (text) { - return text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&'); + return `^${text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&')}&`; }; module.exports = escapeRegex; From 8009191ffa3dd7333bd0d84b8162e3d231bd8440 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Tue, 14 Nov 2023 15:28:08 +0800 Subject: [PATCH 057/129] reduce redundant code --- src/controllers/projectController.js | 2 +- src/controllers/timeEntryController.js | 27 ++++++++++--------- src/helpers/dashboardhelper.js | 7 +++-- src/helpers/taskHelper.js | 2 +- src/helpers/userHelper.js | 36 +++++++++++++------------- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/controllers/projectController.js b/src/controllers/projectController.js index dc92af354..a88378985 100644 --- a/src/controllers/projectController.js +++ b/src/controllers/projectController.js @@ -28,7 +28,7 @@ const projectController = function (Project) { // find if project has any time entries associated with it - timeentry.find({ projectId: record._id, entryType: [ 'default', 'project', null ] }, '_id') + timeentry.find({ projectId: record._id }, '_id') .then((timeentries) => { if (timeentries.length > 0) { res.status(400).send({ error: 'This project has associated time entries and cannot be deleted. Consider inactivaing it instead.' }); diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index aa7eff6ae..8adc6693d 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -236,7 +236,7 @@ const timeEntrycontroller = function (TimeEntry) { } const items = []; records.forEach((element) => { - if (element.entryType == 'default' || element.entryType == undefined) { + if (element.entryType === 'default' || element.entryType === undefined) { const timeentry = new TimeEntry(); timeentry.personId = element.personId; timeentry.projectId = element.projectId; @@ -265,7 +265,7 @@ const timeEntrycontroller = function (TimeEntry) { }; switch (req.body.entryType) { - case 'default': + default: if ( !mongoose.Types.ObjectId.isValid(req.body.personId) || !mongoose.Types.ObjectId.isValid(req.body.projectId) @@ -321,18 +321,18 @@ const timeEntrycontroller = function (TimeEntry) { res.status(400).send(error); }); - // Add this tangbile time entry to related task's hoursLogged - if ((timeentry.entryType === 'default') && timeentry.isTangible === true) { - try { - const currentTask = await task.findById(req.body.projectId); - currentTask.hoursLogged += (timeentry.totalSeconds / 3600); - await currentTask.save(); - } catch (error) { - throw new Error('Failed to find the task by id'); - } - } - // checking if logged in hours exceed estimated time after timeentry for a task if (timeentry.entryType === 'default') { + // Add this tangbile time entry to related task's hoursLogged + if (timeentry.isTangible === true) { + try { + const currentTask = await task.findById(req.body.projectId); + currentTask.hoursLogged += (timeentry.totalSeconds / 3600); + await currentTask.save(); + } catch (error) { + throw new Error('Failed to find the task by id'); + } + } + // checking if logged in hours exceed estimated time after timeentry for a task const record = await userProfile.findById(timeentry.personId.toString()); const currentTask = await task.findById(req.body.projectId); checkTaskOvertime(timeentry, record, currentTask); @@ -492,7 +492,6 @@ const timeEntrycontroller = function (TimeEntry) { const { projectId } = req.params; TimeEntry.find( { - entryType: ['default', null], projectId, dateOfWork: { $gte: fromDate, $lte: todate }, }, diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 1c03c2f88..041b4e948 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -165,7 +165,6 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - const entryTypes = ['default', null]; return myTeam.aggregate([ { $match: { @@ -196,7 +195,7 @@ const dashboardhelper = function () { // leaderboard user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ['Owner', 'Core Team'] }, }, { $and: [ @@ -289,7 +288,7 @@ const dashboardhelper = function () { }, { $in: ['$$timeentry.entryType', ['default', null]], - } + }, ], }, }, @@ -445,7 +444,7 @@ const dashboardhelper = function () { $gte: pdtStart, $lte: pdtEnd, }, - eentryType: { $in: [ 'default', null ] }, + entryType: { $in: ['default', null] }, personId: userId, }); diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 30bb220e9..b91020039 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -43,7 +43,7 @@ const taskHelper = function () { // dashboard tasks user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ['Owner', 'Core Team'] }, }, { $and: [ diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 5824d5d73..be9e40bbc 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -578,31 +578,31 @@ const userHelper = function () { const missedHours = await userProfile.aggregate([ { $match: { - role: "Core Team", - isActive: true - } + role: 'Core Team', + isActive: true, + }, }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', pipeline: [ { $match: { $expr: { $and: [ - { $eq: ["$isTangible", true] }, - { $gte: ["$dateOfWork", startOfLastWeek] }, - { $lte: ["$dateOfWork", endOfLastWeek] }, - { $in: ['$entryType', 'default', null] } - ] - } - } - } + { $eq: ['$isTangible', true] }, + { $gte: ['$dateOfWork', startOfLastWeek] }, + { $lte: ['$dateOfWork', endOfLastWeek] }, + { $in: ['$entryType', 'default', null] }, + ], + }, + }, + }, ], - as: "timeEntries" - } + as: 'timeEntries', + }, }, { $project: { @@ -621,8 +621,8 @@ const userHelper = function () { { $sum: { $map: { - input: "$timeEntries", - in: "$$this.totalSeconds" + input: '$timeEntries', + in: '$$this.totalSeconds', } } }, From 9036fb629a2a816651ed988d61c7bc6626735c89 Mon Sep 17 00:00:00 2001 From: Eduardo Horta Date: Tue, 14 Nov 2023 13:48:59 -0300 Subject: [PATCH 058/129] Added .nvmrc file. Node v14. --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..518633e16 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +lts/fermium From 0fbbe6183f908f873b7c170747997954a42e4504 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 14 Nov 2023 15:22:25 -0800 Subject: [PATCH 059/129] update to routes, router, controller --- .../bmdashboard/bmMaterialsController.js | 92 ++++++++++--------- src/routes/bmdashboard/bmMaterialsRouter.js | 8 +- src/startup/routes.js | 7 +- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index d8bcfe7df..a2e87eec2 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -1,6 +1,9 @@ -// const mongoose = require('mongoose') +const mongoose = require('mongoose'); -const bmMaterialsController = function (ItemMaterial) { +// use in bmPurchaseMaterials auth check (see below) +// const buildingProject = require('../../models/bmdashboard/buildingProject'); + +const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { const bmMaterialsList = async function _matsList(req, res) { try { ItemMaterial.find() @@ -43,57 +46,58 @@ const bmMaterialsController = function (ItemMaterial) { } }; - const bmAddMaterials = async function (req, res) { - // add permission check... - - const { material, requestor } = req.body; - const purchaseRecord = { - date: material.purchaseDate, - createdBy: requestor.requestorId, - poId: material.invoice, - sellerId: material.phone, - quantity: material.quantity, - unitPrice: material.unitPrice, - currency: material.currency, - subtotal: material.quantity, - tax: material.taxRate, - shipping: material.shippingFee, + const bmPurchaseMaterials = async function (req, res) { + const { + projectId, + matTypeId, + quantity, + requestor: { requestorId }, + } = req.body; + const newPurchaseRecord = { + quantity, + requestedBy: requestorId, }; - try { - const result = await ItemMaterial.findOneAndUpdate( - { project: material.projectId, inventoryItemType: material.material }, - { - $inc: { stockBought: material.quantity, stockAvailable: material.quantity }, - $push: { purchaseRecord }, - }, - { returnDocument: 'after', lean: true }, - ) - .exec(); - if (result) { - console.log(result); - res.status(201).send(result); - } else { - const itemMaterial = new ItemMaterial({ - inventoryItemType: material.material, - project: material.projectId, - stockBought: material.quantity, - stockAvailable: material.quantity, - usageRecord: [], - updateRecord: [], - purchaseRecord: [purchaseRecord], - }); - const newItemMaterial = await itemMaterial.save(); - res.status(201).send(newItemMaterial); + // check if requestor has permission to make purchase request + //! Note: this code is disabled until permissions are added + // TODO: uncomment this code to execute auth check + // const { buildingManager: bmId } = await buildingProject.findById(projectId, 'buildingManager').exec(); + // if (bmId !== requestorId) { + // res.status(403).send({ message: 'You are not authorized to edit this record.' }); + // return; + // } + + // check if the material is already being used in the project + // if no, add a new document to the collection + // if yes, update the existing document + const doc = await BuildingMaterial.findOne({ project: projectId, itemType: matTypeId }); + if (!doc) { + const newDoc = { + itemType: matTypeId, + project: projectId, + purchaseRecord: [newPurchaseRecord], + }; + BuildingMaterial + .create(newDoc) + .then(() => res.status(201).send()) + .catch(error => res.status(500).send(error)); + return; } + BuildingMaterial + .findOneAndUpdate( + { _id: mongoose.Types.ObjectId(doc._id) }, + { $push: { purchaseRecord: newPurchaseRecord } }, + ) + .exec() + .then(() => res.status(201).send()) + .catch(error => res.status(500).send(error)); } catch (error) { res.status(500).send(error); } }; - return { bmMaterialsList, - bmAddMaterials, + bmPurchaseMaterials, }; }; diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index 66dc6c985..da29c35d7 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -1,13 +1,11 @@ const express = require('express'); -const routes = function (itemMaterial, itemType) { +const routes = function (itemMaterial, buildingMaterial) { const materialsRouter = express.Router(); - const controller = require('../../controllers/bmdashboard/bmMaterialsController')(itemMaterial, itemType); - + const controller = require('../../controllers/bmdashboard/bmMaterialsController')(itemMaterial, buildingMaterial); materialsRouter.route('/materials') .get(controller.bmMaterialsList) - .post(controller.bmAddMaterials); - + .post(controller.bmPurchaseMaterials); return materialsRouter; }; diff --git a/src/startup/routes.js b/src/startup/routes.js index 2c8aced29..b1989698a 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -22,7 +22,8 @@ const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); const inventoryItemMaterial = require('../models/inventoryItemMaterial'); const buildingProject = require('../models/bmdashboard/buildingProject'); - +const buildingInventoryType = require('../models/bmdashboard/buildingInventoryType'); +const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -60,8 +61,9 @@ const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverTe // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); -const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial, inventoryItemType); +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial, buildingMaterial); const bmProjectRouter = require('../routes/bmdashboard/bmProjectRouter')(buildingProject); +const bmInventoryTypeRouter = require('../routes/bmdashboard/bmInventoryTypeRouter')(buildingInventoryType); module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -97,4 +99,5 @@ module.exports = function (app) { app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); app.use('/api/bm', bmProjectRouter); + app.use('/api/bm', bmInventoryTypeRouter); }; From cec78db63afed949828395476c4d73f914e6133b Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 15 Nov 2023 17:04:41 -0800 Subject: [PATCH 060/129] add priority, brand fields to controller. update material schema. --- src/controllers/bmdashboard/bmMaterialsController.js | 4 ++++ src/models/bmdashboard/buildingMaterial.js | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index a2e87eec2..a8090ebd9 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -51,10 +51,14 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { projectId, matTypeId, quantity, + priority, + brand, requestor: { requestorId }, } = req.body; const newPurchaseRecord = { quantity, + priority, + brand, requestedBy: requestorId, }; try { diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index 714ba4ae7..4170443e0 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -13,15 +13,17 @@ const buildingMaterial = new Schema({ _id: false, // do not add _id field to subdocument date: { type: Date, default: Date.now() }, requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantity: Number, - status: { type: String, default: 'Pending' }, // Pending, Rejected, Approved + quantity: { type: Number, required: true }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brand: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, }], updateRecord: [{ _id: false, - date: Date, + date: { type: Date, required: true }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: String, // '10 cubic yards' - quantityWasted: Number, + quantityUsed: { type: Number, required: true }, + quantityWasted: { type: Number, required: true }, }], }); From aec7cb8db3697737dbaa971ad65d3c45406ed78c Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 15 Nov 2023 17:21:32 -0800 Subject: [PATCH 061/129] update materials completed --- src/controllers/bmdashboard/bmMaterialsController.js | 4 ++-- src/models/bmdashboard/buildingMaterial.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index c5403de21..502a2014a 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -75,7 +75,7 @@ const bmMaterialsController = function (ItemMaterial) { updateRecord: { date: req.body.date, createdBy: req.body.requestor.requestorId, - quantityUsed: quantityUsed + ' ' + material.itemType.unit, + quantityUsed: quantityUsed, quantityWasted: quantityWasted }, } @@ -117,7 +117,7 @@ const bmMaterialsController = function (ItemMaterial) { }, updateValue: { createdBy: req.body.requestor.requestorId, - quantityUsed: quantityUsed + ' ' + material.itemType.unit, + quantityUsed: quantityUsed, quantityWasted: quantityWasted, date: payload.date, }}); diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index c1d74b879..8e49219f2 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -16,7 +16,7 @@ const buildingMaterial = new Schema({ updateRecord: [{ date: Date, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: String, // '10 cubic yards' + quantityUsed: Number, quantityWasted: Number, }], }); From 89233e9869ff9273613e61a8fea8f6331002618f Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Wed, 15 Nov 2023 21:01:48 -0600 Subject: [PATCH 062/129] add setMapLocation --- .../profileInitialSetupController.js | 27 ++++++++++++++++++- src/routes/profileInitialSetupRouter.js | 6 +++-- src/startup/middleware.js | 2 +- src/startup/routes.js | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index b4230f1a4..21113c0fd 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -79,7 +79,8 @@ function informManagerMessage(user) { const profileInitialSetupController = function ( ProfileInitialSetupToken, userProfile, - Project + Project, + MapLocation ) { const { JWT_SECRET } = config; @@ -297,11 +298,35 @@ const profileInitialSetupController = function ( } }; + + const setMapLocation = async (req,res) => { + + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.location, + } + const location = new MapLocation(locationData); + + try { + const response = await location.save() + if (!response) { + throw new Error('Something went wrong during saving the location...') + } + res.status(200).send(response); + } catch (err) { + console.log(err.message) + res.status(500).json({ message: err.message || 'Something went wrong...' }); + } + } + return { getSetupToken, setUpNewUser, validateSetupToken, getTimeZoneAPIKeyByToken, + setMapLocation }; }; diff --git a/src/routes/profileInitialSetupRouter.js b/src/routes/profileInitialSetupRouter.js index a23c6a868..0e5b050c7 100644 --- a/src/routes/profileInitialSetupRouter.js +++ b/src/routes/profileInitialSetupRouter.js @@ -1,13 +1,15 @@ const express = require('express'); -const routes = function (ProfileInitialSetupToken, userProfile, Project) { +const routes = function (ProfileInitialSetupToken, userProfile, Project , mapLocations) { const ProfileInitialSetup = express.Router(); - const controller = require('../controllers/profileInitialSetupController')(ProfileInitialSetupToken, userProfile, Project); + const controller = require('../controllers/profileInitialSetupController')(ProfileInitialSetupToken, userProfile, Project , mapLocations); ProfileInitialSetup.route('/getInitialSetuptoken') .post(controller.getSetupToken); ProfileInitialSetup.route('/ProfileInitialSetup').post(controller.setUpNewUser) ProfileInitialSetup.route('/validateToken').post(controller.validateSetupToken) ProfileInitialSetup.route('/getTimeZoneAPIKeyByToken').post(controller.getTimeZoneAPIKeyByToken) + ProfileInitialSetup.route('/mapLocationsToken').post(controller.setMapLocation) + return ProfileInitialSetup; }; diff --git a/src/startup/middleware.js b/src/startup/middleware.js index 23ce1d4a6..16916c9f3 100644 --- a/src/startup/middleware.js +++ b/src/startup/middleware.js @@ -22,7 +22,7 @@ module.exports = function (app) { next(); return; } - if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' + if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' || req.originalUrl === '/api/mapLocationsToken' && req.method === 'POST' ) { next(); return; diff --git a/src/startup/routes.js b/src/startup/routes.js index 99e761f75..d4dcb3685 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -45,7 +45,7 @@ const popupBackupRouter = require('../routes/popupEditorBackupRouter')(popupBack const taskNotificationRouter = require('../routes/taskNotificationRouter')(taskNotification); const inventoryRouter = require('../routes/inventoryRouter')(inventoryItem, inventoryItemType); const timeZoneAPIRouter = require('../routes/timeZoneAPIRoutes')(); -const profileInitialSetupRouter = require('../routes/profileInitialSetupRouter')(profileInitialSetuptoken, userProfile, project); +const profileInitialSetupRouter = require('../routes/profileInitialSetupRouter')(profileInitialSetuptoken, userProfile, project , mapLocations); const isEmailExistsRouter = require('../routes/isEmailExistsRouter')(); From 7542d5c29e6f8657e59e77af12b7b4ee15391477 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 15 Nov 2023 20:25:45 -0800 Subject: [PATCH 063/129] update inv type schema --- src/models/bmdashboard/buildingInventoryType.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models/bmdashboard/buildingInventoryType.js b/src/models/bmdashboard/buildingInventoryType.js index 71285fa24..8d2364c64 100644 --- a/src/models/bmdashboard/buildingInventoryType.js +++ b/src/models/bmdashboard/buildingInventoryType.js @@ -3,10 +3,10 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const buildingInventoryType = new Schema({ - category: String, // Consumable, Material, Tool, Equipment - name: String, - description: String, - unit: String, // unit of measurement + category: { type: String, enum: ['Consumable', 'Material', 'Tool', 'Equipment'], required: true }, + name: { type: String, required: true }, + description: { type: String, required: true }, + unit: { type: String, required: true }, // unit of measurement imageUrl: String, }); From 903da0c7f447ae8961ce879eb5cdf2bc067f1c64 Mon Sep 17 00:00:00 2001 From: Aishwaryak01 Date: Thu, 16 Nov 2023 15:09:48 -0500 Subject: [PATCH 064/129] Updated file buildingTool.js Updated file buildingTool.js for changes requested in PR --- .../bmdashboard/{buildingTools.js => buildingTool.js} | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) rename src/models/bmdashboard/{buildingTools.js => buildingTool.js} (64%) diff --git a/src/models/bmdashboard/buildingTools.js b/src/models/bmdashboard/buildingTool.js similarity index 64% rename from src/models/bmdashboard/buildingTools.js rename to src/models/bmdashboard/buildingTool.js index 94468de42..6cec3b5d0 100644 --- a/src/models/bmdashboard/buildingTools.js +++ b/src/models/bmdashboard/buildingTool.js @@ -3,17 +3,14 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const buildingTools = new Schema({ - inventoryItemId: { type: mongoose.SchemaTypes.ObjectId, ref: 'INVENTORY' }, // later ,INVENTORY needs to be changed as per inventory model + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, title: { type: String, required: true }, - image: { type: String, required: true }, rentedOnDate: { type: Date, required: true }, rentDuration: { type: Number, required: true }, // This value should be taken in number of days - total: {type: Number, required: true}, - availableCount:{type: Number, required: true}, logInStatus:{Boolean}, - condition:{type: String, enum: ['Good', 'Needs Repair', 'Out of Order','Unused'], default: 'Condition'}, + condition:{type: String, enum: ['Good', 'Needs Repair', 'Out of Order','Unused'], default: 'Good'}, userResponsible:{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - purchaseStatus:{type: String, enum: ['Rented', 'Purchased'], default: 'Status'}, + purchaseStatus:{type: String, enum: ['Rented', 'Purchased'], default: 'Rented'}, }); module.exports = mongoose.model('buildingTools', buildingTools, 'buildingTools'); \ No newline at end of file From a973d9aa876df747c81b536d289407e459a89f93 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Fri, 17 Nov 2023 16:02:49 -0800 Subject: [PATCH 065/129] added validations before adding to db --- .../bmdashboard/bmMaterialsController.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 502a2014a..671cb0b24 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -89,7 +89,11 @@ const bmMaterialsController = function (ItemMaterial) { const bmPostMaterialUpdateBulk = function (req, res) { const materialUpdates= req.body; - const updateRecordsToBeAdded = materialUpdates.map(payload => { + let errorFlag = false; + const updateRecordsToBeAdded = []; + for(let i=0;i ItemMaterial.updateOne( { _id: updateItem.updateId }, { From a2bb72a83e5ac9946cec68f972ff03469650c229 Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Fri, 17 Nov 2023 20:08:10 -0600 Subject: [PATCH 066/129] update homeCountry,add profile picture --- .../profileInitialSetupController.js | 31 ++++++++++--------- src/routes/profileInitialSetupRouter.js | 1 - src/startup/middleware.js | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index 21113c0fd..88902141d 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -213,6 +213,7 @@ const profileInitialSetupController = function ( newUser.collaborationPreference = req.body.collaborationPreference; newUser.timeZone = req.body.timeZone || "America/Los_Angeles"; newUser.location = req.body.location; + newUser.profilePic = req.body.profilePicture; newUser.permissions = { frontPermissions: [], backPermissions: [] @@ -247,7 +248,19 @@ const profileInitialSetupController = function ( const token = jwt.sign(jwtPayload, JWT_SECRET); + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.homeCountry, + } + res.send({ token }).status(200); + + const mapEntryResult = await setMapLocation(locationData) + if(mapEntryResult.type === "Error"){ + console.log(mapEntryResult.message) + } const NewUserCache = { permissions: savedUser.permissions, @@ -298,26 +311,15 @@ const profileInitialSetupController = function ( } }; - - const setMapLocation = async (req,res) => { + const setMapLocation = async (locationData) => { - const locationData = { - firstName: req.body.firstName, - lastName: req.body.lastName, - jobTitle: req.body.jobTitle, - location: req.body.location, - } const location = new MapLocation(locationData); try { const response = await location.save() - if (!response) { - throw new Error('Something went wrong during saving the location...') - } - res.status(200).send(response); + return response } catch (err) { - console.log(err.message) - res.status(500).json({ message: err.message || 'Something went wrong...' }); + return {type: "Error", message: err.message || 'An error occurred while saving the location'} } } @@ -326,7 +328,6 @@ const profileInitialSetupController = function ( setUpNewUser, validateSetupToken, getTimeZoneAPIKeyByToken, - setMapLocation }; }; diff --git a/src/routes/profileInitialSetupRouter.js b/src/routes/profileInitialSetupRouter.js index 0e5b050c7..9a87aa1a7 100644 --- a/src/routes/profileInitialSetupRouter.js +++ b/src/routes/profileInitialSetupRouter.js @@ -8,7 +8,6 @@ const routes = function (ProfileInitialSetupToken, userProfile, Project , mapLoc ProfileInitialSetup.route('/ProfileInitialSetup').post(controller.setUpNewUser) ProfileInitialSetup.route('/validateToken').post(controller.validateSetupToken) ProfileInitialSetup.route('/getTimeZoneAPIKeyByToken').post(controller.getTimeZoneAPIKeyByToken) - ProfileInitialSetup.route('/mapLocationsToken').post(controller.setMapLocation) return ProfileInitialSetup; diff --git a/src/startup/middleware.js b/src/startup/middleware.js index 16916c9f3..23ce1d4a6 100644 --- a/src/startup/middleware.js +++ b/src/startup/middleware.js @@ -22,7 +22,7 @@ module.exports = function (app) { next(); return; } - if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' || req.originalUrl === '/api/mapLocationsToken' && req.method === 'POST' + if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' ) { next(); return; From a418eaefcde0b3a65d2e4caae0aa7cd02258adcf Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 20 Nov 2023 11:45:18 +0800 Subject: [PATCH 067/129] fix permission issue --- src/controllers/timeEntryController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 8adc6693d..011e466e2 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -110,7 +110,7 @@ const timeEntrycontroller = function (TimeEntry) { } if (!(await hasPermission(req.body.requestor, 'editTimeEntry') - || ((type === 'default' || type === 'person') && timeEntry.personId.toString() === req.body.requestor.requestorId.toString()))) { + || (type === 'default' && timeEntry.personId.toString() === req.body.requestor.requestorId.toString()))) { return res.status(403).send({ error: 'Unauthorized request' }); } From 9338492de79cde0b1d161caa0bdeefb7914d86f6 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Mon, 20 Nov 2023 12:49:17 -0800 Subject: [PATCH 068/129] add team, hours fields to team sub-doc in project schema --- src/models/bmdashboard/buildingProject.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/models/bmdashboard/buildingProject.js b/src/models/bmdashboard/buildingProject.js index 566bc124e..df2b51e96 100644 --- a/src/models/bmdashboard/buildingProject.js +++ b/src/models/bmdashboard/buildingProject.js @@ -8,8 +8,12 @@ const buildingProject = new Schema({ template: String, // construction template (ie Earthbag Village) location: String, // use lat/lng instead? dateCreated: { type: Date, default: Date.now }, - buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, // BM's id - team: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }], + buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + members: [{ + user: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + team: { type: mongoose.SchemaTypes.ObjectId, ref: 'teams' }, + hours: { type: Number, default: 0 }, // tracked via the Member Check-In Page timer + }], }); module.exports = mongoose.model('buildingProject', buildingProject, 'buildingProjects'); From 565522521c7d7fe5bd99ce27b827008fa9538fca Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 22 Nov 2023 17:26:22 -0800 Subject: [PATCH 069/129] build aggregate operator, add sorted fields to return array. add teams array field to buildingProject schema. --- .../bmdashboard/bmProjectController.js | 72 ++++++++++++++----- src/models/bmdashboard/buildingProject.js | 2 +- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/controllers/bmdashboard/bmProjectController.js b/src/controllers/bmdashboard/bmProjectController.js index 929aba4ba..8ca62ea77 100644 --- a/src/controllers/bmdashboard/bmProjectController.js +++ b/src/controllers/bmdashboard/bmProjectController.js @@ -1,3 +1,4 @@ +/* eslint-disable prefer-destructuring */ // TODO: uncomment when executing auth checks // const jwt = require('jsonwebtoken'); // const config = require('../../config'); @@ -12,26 +13,63 @@ const bmMProjectController = function (BuildingProject) { // const token = req.headers.authorization; // const { userid } = jwt.verify(token, JWT_SECRET); try { - const projectData = await BuildingProject - // TODO: uncomment this line to filter by buildingManager field - // .find({ buildingManager: userid }) - .find() - .populate([ - { - path: 'buildingManager', - select: '_id firstName lastName email', + BuildingProject.aggregate([ + { + $match: { isActive: true }, + }, + { + $lookup: { + from: 'userProfiles', + let: { id: '$buildingManager' }, + pipeline: [ + { $match: { $expr: { $eq: ['$_id', '$$id'] } } }, + { $project: { firstName: 1, lastName: 1, email: 1 } }, + ], + as: 'buildingManager', }, - { - path: 'team', - select: '_id firstName lastName email', + }, + { $unwind: '$buildingManager' }, + { + $lookup: { + from: 'buildingMaterials', + let: { id: '$_id' }, + pipeline: [ + { $match: { $expr: { $eq: ['$project', '$$id'] } } }, + { $project: { updateRecord: 0, project: 0 } }, + ], + as: 'materials', }, - ]) - .exec() - .then(result => result) - .catch(error => res.status(500).send(error)); - res.status(200).send(projectData); + }, + { + $project: { + name: 1, + isActive: 1, + template: 1, + location: 1, + dateCreated: 1, + buildingManager: 1, + teams: 1, + members: 1, + materials: 1, + hoursWorked: { $sum: '$members.hours' }, + // cost values can be calculated once a process for purchasing inventory is created + totalMaterialsCost: { $sum: 1500 }, + totalEquipmentCost: { $sum: 3000 }, + }, + }, + ]) + .then((results) => { + results.forEach((proj) => { + proj.mostMaterialWaste = proj.materials.sort((a, b) => b.stockWasted - a.stockWasted)[0]; + proj.leastMaterialAvailable = proj.materials.sort((a, b) => a.stockAvailable - b.stockAvailable)[0]; + proj.mostMaterialBought = proj.materials.sort((a, b) => b.stockBought - a.stockBought)[0]; + proj.teamCount = proj.teams.length; + }); + res.status(200).send(results); + }) + .catch(error => res.status(500).send(error)); } catch (err) { - res.json(err); + res.status(500).send(err); } }; diff --git a/src/models/bmdashboard/buildingProject.js b/src/models/bmdashboard/buildingProject.js index df2b51e96..3ca4bf993 100644 --- a/src/models/bmdashboard/buildingProject.js +++ b/src/models/bmdashboard/buildingProject.js @@ -9,9 +9,9 @@ const buildingProject = new Schema({ location: String, // use lat/lng instead? dateCreated: { type: Date, default: Date.now }, buildingManager: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + teams: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'teams' }], // teams assigned to the project members: [{ user: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - team: { type: mongoose.SchemaTypes.ObjectId, ref: 'teams' }, hours: { type: Number, default: 0 }, // tracked via the Member Check-In Page timer }], }); From 2f35809bccfbc0c20974b3b518d8d7dc4d5a3318 Mon Sep 17 00:00:00 2001 From: Carl Bebli Date: Tue, 28 Nov 2023 09:18:38 +0000 Subject: [PATCH 070/129] added a line break to the email body --- src/controllers/dashBoardController.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index 68e88ae58..b1b62f9ae 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -172,6 +172,8 @@ const dashboardcontroller = function () { args[3].lastName } : +
+
⚹ Suggestion Category:

${args[0]}

⚹ Suggestion: @@ -206,7 +208,8 @@ const dashboardcontroller = function () { emailBody, null, null, - email + email, + null ); res.status(200).send("Success"); } catch { From 83b031f76123cac63bd1e563e08306c80e2f65dc Mon Sep 17 00:00:00 2001 From: Carl Bebli Date: Tue, 28 Nov 2023 09:21:41 +0000 Subject: [PATCH 071/129] added a line break to the email body --- src/controllers/dashBoardController.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index 68e88ae58..b1b62f9ae 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -172,6 +172,8 @@ const dashboardcontroller = function () { args[3].lastName } : +
+
⚹ Suggestion Category:

${args[0]}

⚹ Suggestion: @@ -206,7 +208,8 @@ const dashboardcontroller = function () { emailBody, null, null, - email + email, + null ); res.status(200).send("Success"); } catch { From 50f96f6459ddaebd63f4d66aac2133a7853df1dc Mon Sep 17 00:00:00 2001 From: Carl Bebli Date: Tue, 28 Nov 2023 13:42:03 +0000 Subject: [PATCH 072/129] bluesquare replyto improvement --- src/helpers/userHelper.js | 635 ++++++++++++++++++++++---------------- 1 file changed, 361 insertions(+), 274 deletions(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index c75dd7b45..8bf04eb5f 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -13,7 +13,7 @@ const emailSender = require("../utilities/emailSender"); const logger = require("../startup/logger"); const hasPermission = require("../utilities/permissions"); const Reason = require("../models/reason"); -const token = require("../models/profileInitialSetupToken") +const token = require("../models/profileInitialSetupToken"); const userHelper = function () { const getTeamMembers = function (user) { @@ -57,7 +57,7 @@ const userHelper = function () { if (picParts.length < 2) { return { result: false, - errors: "Invalid image" + errors: "Invalid image", }; } @@ -79,7 +79,7 @@ const userHelper = function () { return { result, - errors + errors, }; }; @@ -90,10 +90,11 @@ const userHelper = function () { totalInfringements, timeRemaining ) { - let final_paragraph = ''; + let final_paragraph = ""; if (timeRemaining == undefined) { - final_paragraph = '

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

'; + final_paragraph = + "

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

"; } else { final_paragraph = `

Life happens and we understand that. Please make up the missed hours this following week though to avoid getting another blue square. So you know what’s needed, the missing/incomplete hours (${timeRemaining} hours) have been added to your current week and this new weekly total can be seen at the top of your dashboard.

Reminder also that each blue square is removed from your profile 1 year after it was issued.

`; @@ -104,8 +105,8 @@ const userHelper = function () {

Date Assigned: ${infringement.date}

Description: ${infringement.description}

Total Infringements: This is your ${moment - .localeData() - .ordinal(totalInfringements)} blue square of 5.

+ .localeData() + .ordinal(totalInfringements)} blue square of 5.

${final_paragraph}

Thank you,
One Community

`; @@ -124,9 +125,7 @@ const userHelper = function () { * @return {void} */ const emailWeeklySummariesForAllUsers = async (weekIndex = 1) => { - const currentFormattedDate = moment() - .tz("America/Los_Angeles") - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}` @@ -145,7 +144,11 @@ const userHelper = function () { const weeklySummaryNotRequiredMessage = '
Weekly Summary: Not required for this user
'; - results.sort((a, b) => `${a.firstName} ${a.lastName}`.localeCompare(`${b.firstName} ${b.lastname}`)); + results.sort((a, b) => + `${a.firstName} ${a.lastName}`.localeCompare( + `${b.firstName} ${b.lastname}` + ) + ); for (let i = 0; i < results.length; i += 1) { const result = results[i]; @@ -157,7 +160,7 @@ const userHelper = function () { mediaUrl, weeklySummariesCount, weeklycommittedHours, - weeklySummaryOption + weeklySummaryOption, } = result; if (email !== undefined && email !== null) { @@ -169,7 +172,9 @@ const userHelper = function () { // hence totalSeconds[0] should be used const hoursLogged = result.totalSeconds[0] / 3600 || 0; - const mediaUrlLink = mediaUrl ? `${mediaUrl}` : 'Not provided!'; + const mediaUrlLink = mediaUrl + ? `${mediaUrl}` + : "Not provided!"; let weeklySummaryMessage = weeklySummaryNotProvidedMessage; const colorStyle = (() => { @@ -192,16 +197,16 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz("America/Los_Angeles") - .format("YYYY-MMM-DD")}): + .tz("America/Los_Angeles") + .format("YYYY-MMM-DD")}):
${summary}
`; } else if ( - weeklySummaryOption === 'Not Required' - || (!weeklySummaryOption && result.weeklySummaryNotReq) + weeklySummaryOption === "Not Required" || + (!weeklySummaryOption && result.weeklySummaryNotReq) ) { weeklySummaryMessage = weeklySummaryNotRequiredMessage; } @@ -213,21 +218,26 @@ const userHelper = function () { Name: ${firstName} ${lastName}

- Media URL: ${mediaUrlLink || 'Not provided!'} + Media URL: ${ + mediaUrlLink || 'Not provided!' + }

- ${weeklySummariesCount === 8 - ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` - : `

Total Valid Weekly Summaries: ${weeklySummariesCount || - "No valid submissions yet!"}

` + ${ + weeklySummariesCount === 8 + ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` + : `

Total Valid Weekly Summaries: ${ + weeklySummariesCount || "No valid submissions yet!" + }

` } - ${hoursLogged >= weeklycommittedHours - - ? `

Hours logged: ${hoursLogged.toFixed(2)} / ${weeklycommittedHours}

` - - : `

Hours logged: ${hoursLogged.toFixed( - 2 - )} / ${weeklycommittedHours}

` + ${ + hoursLogged >= weeklycommittedHours + ? `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` + : `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage}
`; @@ -254,7 +264,9 @@ const userHelper = function () { "onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com", "Weekly Summaries for all active users...", emailBody, - null + null, + null, + emailString ); } catch (err) { logger.logException(err); @@ -275,18 +287,16 @@ const userHelper = function () { weeklySummaries: { $each: [ { - dueDate: moment() - .tz("America/Los_Angeles") - .endOf("week"), - summary: "" - } + dueDate: moment().tz("America/Los_Angeles").endOf("week"), + summary: "", + }, ], $position: 0, - $slice: 4 - } - } + $slice: 4, + }, + }, }) - .catch(error => logger.logException(error)); + .catch((error) => logger.logException(error)); }; /** @@ -297,18 +307,14 @@ const userHelper = function () { */ const assignBlueSquareForTimeNotMet = async () => { try { - const currentFormattedDate = moment() - .tz("America/Los_Angeles") - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); const currentUTCDate = moment .tz("America/Los_Angeles") .startOf("day") .toISOString(); logger.logInfo( - - `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}`, - + `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}` ); const pdtStartOfLastWeek = moment() @@ -321,7 +327,6 @@ const userHelper = function () { .endOf("week") .subtract(1, "week"); - const users = await userProfile.find( { isActive: true }, "_id weeklycommittedHours weeklySummaries missedHours" @@ -344,8 +349,10 @@ const userHelper = function () { let hasWeeklySummary = false; - - if (Array.isArray(user.weeklySummaries) && user.weeklySummaries.length) { + if ( + Array.isArray(user.weeklySummaries) && + user.weeklySummaries.length + ) { const { summary } = user.weeklySummaries[0]; if (summary) { hasWeeklySummary = true; @@ -371,31 +378,28 @@ const userHelper = function () { const timeRemaining = weeklycommittedHours - timeSpent; - const updateResult = await userProfile.findByIdAndUpdate( personId, { $inc: { - totalTangibleHrs: timeSpent || 0 + totalTangibleHrs: timeSpent || 0, }, $max: { - personalBestMaxHrs: timeSpent || 0 + personalBestMaxHrs: timeSpent || 0, }, $push: { - savedTangibleHrs: { $each: [timeSpent || 0], $slice: -200 } + savedTangibleHrs: { $each: [timeSpent || 0], $slice: -200 }, }, $set: { - lastWeekTangibleHrs: timeSpent || 0 - } + lastWeekTangibleHrs: timeSpent || 0, + }, }, { new: true } ); if ( - - updateResult?.weeklySummaryOption === 'Not Required' - || updateResult?.weeklySummaryNotReq - + updateResult?.weeklySummaryOption === "Not Required" || + updateResult?.weeklySummaryNotReq ) { hasWeeklySummary = true; } @@ -419,8 +423,8 @@ const userHelper = function () { personId, { $push: { - oldInfringements: { $each: oldInfringements, $slice: -10 } - } + oldInfringements: { $each: oldInfringements, $slice: -10 }, + }, }, { new: true } ); @@ -431,11 +435,15 @@ const userHelper = function () { description = foundReason.reason; } else { if (timeNotMet && !hasWeeklySummary) { - description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( + description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed( + 2 + )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( "dddd YYYY-MM-DD" )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; } else if (timeNotMet) { - description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( + description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed( + 2 + )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( "dddd YYYY-MM-DD" )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; } else { @@ -446,46 +454,46 @@ const userHelper = function () { } const infringement = { - date: moment() - .utc() - .format("YYYY-MM-DD"), - description + date: moment().utc().format("YYYY-MM-DD"), + description, }; const status = await userProfile.findByIdAndUpdate( personId, { $push: { - infringements: infringement - } + infringements: infringement, + }, }, { new: true } ); - let emailBody = ''; - if (person.role == 'Core Team' && timeRemaining > 0) { + let emailBody = ""; + if (person.role == "Core Team" && timeRemaining > 0) { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, status.infringements.length, - timeRemaining, + timeRemaining ); } else { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, - status.infringements.length, + status.infringements.length ); } emailSender( status.email, - 'New Infringement Assigned', + "New Infringement Assigned", emailBody, null, - "onecommunityglobal@gmail.com" + "onecommunityglobal@gmail.com", + null, + status.email ); const categories = await dashboardHelper.laborThisWeekByCategory( @@ -520,15 +528,15 @@ const userHelper = function () { await userProfile.findOneAndUpdate( { _id: personId, - "categoryTangibleHrs.category": { $ne: elem._id } + "categoryTangibleHrs.category": { $ne: elem._id }, }, { $addToSet: { categoryTangibleHrs: { category: elem._id, - hrs: elem.timeSpent_hrs - } - } + hrs: elem.timeSpent_hrs, + }, + }, } ); } @@ -545,8 +553,10 @@ const userHelper = function () { for (let i = 0; i < inactiveUsers.length; i += 1) { const user = inactiveUsers[i]; - await processWeeklySummariesByUserId(mongoose.Types.ObjectId(user._id), false); - + await processWeeklySummariesByUserId( + mongoose.Types.ObjectId(user._id), + false + ); } } catch (err) { logger.logException(err); @@ -555,9 +565,7 @@ const userHelper = function () { const applyMissedHourForCoreTeam = async () => { try { - const currentDate = moment() - .tz("America/Los_Angeles") - .format(); + const currentDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( `Job for applying missed hours for Core Team members starting at ${currentDate}` @@ -579,8 +587,8 @@ const userHelper = function () { { $match: { role: "Core Team", - isActive: true - } + isActive: true, + }, }, { $lookup: { @@ -594,14 +602,14 @@ const userHelper = function () { $and: [ { $eq: ["$isTangible", true] }, { $gte: ["$dateOfWork", startOfLastWeek] }, - { $lte: ["$dateOfWork", endOfLastWeek] } - ] - } - } - } + { $lte: ["$dateOfWork", endOfLastWeek] }, + ], + }, + }, + }, ], - as: "timeEntries" - } + as: "timeEntries", + }, }, { $project: { @@ -611,9 +619,10 @@ const userHelper = function () { { $subtract: [ { - - $sum: [{ $ifNull: ['$missedHours', 0] }, '$weeklycommittedHours'], - + $sum: [ + { $ifNull: ["$missedHours", 0] }, + "$weeklycommittedHours", + ], }, { $divide: [ @@ -621,30 +630,30 @@ const userHelper = function () { $sum: { $map: { input: "$timeEntries", - in: "$$this.totalSeconds" - } - } + in: "$$this.totalSeconds", + }, + }, }, - 3600 - ] - } - ] + 3600, + ], + }, + ], }, - 0 - ] - } - } - } + 0, + ], + }, + }, + }, ]); const bulkOps = []; - missedHours.forEach(obj => { + missedHours.forEach((obj) => { bulkOps.push({ updateOne: { filter: { _id: obj._id }, - update: { missedHours: obj.missedHours } - } + update: { missedHours: obj.missedHours }, + }, }); }); @@ -655,17 +664,13 @@ const userHelper = function () { }; const deleteBlueSquareAfterYear = async () => { - const currentFormattedDate = moment() - .tz("America/Los_Angeles") - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}` ); - const cutOffDate = moment() - .subtract(1, "year") - .format("YYYY-MM-DD"); + const cutOffDate = moment().subtract(1, "year").format("YYYY-MM-DD"); try { const results = await userProfile.updateMany( @@ -674,10 +679,10 @@ const userHelper = function () { $pull: { infringements: { date: { - $lte: cutOffDate - } - } - } + $lte: cutOffDate, + }, + }, + }, } ); @@ -688,9 +693,7 @@ const userHelper = function () { }; const reActivateUser = async () => { - const currentFormattedDate = moment() - .tz("America/Los_Angeles") - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}` @@ -708,11 +711,11 @@ const userHelper = function () { user._id, { $set: { - isActive: true + isActive: true, }, $unset: { - endDate: user.endDate - } + endDate: user.endDate, + }, }, { new: true } ); @@ -724,8 +727,12 @@ const userHelper = function () { const id = user._id; const person = await userProfile.findById(id); - const endDate = moment(person.endDate).format('YYYY-MM-DD'); - logger.logInfo(`User with id: ${user._id} was re-acticated at ${moment().format()}.`); + const endDate = moment(person.endDate).format("YYYY-MM-DD"); + logger.logInfo( + `User with id: ${ + user._id + } was re-acticated at ${moment().format()}.` + ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been RE-activated in the Highest Good Network`; @@ -739,9 +746,14 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; - - emailSender('onecommunityglobal@gmail.com', subject, emailBody, null, null); - + emailSender( + "onecommunityglobal@gmail.com", + subject, + emailBody, + null, + null, + person.email + ); } } } catch (err) { @@ -749,24 +761,38 @@ const userHelper = function () { } }; - - const notifyInfringements = function (original, current, firstName, lastName, emailAddress) { - + const notifyInfringements = function ( + original, + current, + firstName, + lastName, + emailAddress + ) { if (!current) return; const newOriginal = original.toObject(); const newCurrent = current.toObject(); const totalInfringements = newCurrent.length; let newInfringements = []; - newInfringements = _.differenceWith(newCurrent, newOriginal, (arrVal, othVal) => arrVal._id.equals(othVal._id)); + newInfringements = _.differenceWith( + newCurrent, + newOriginal, + (arrVal, othVal) => arrVal._id.equals(othVal._id) + ); newInfringements.forEach((element) => { emailSender( emailAddress, - 'New Infringement Assigned', - getInfringementEmailBody(firstName, lastName, element, totalInfringements), + "New Infringement Assigned", + getInfringementEmailBody( + firstName, + lastName, + element, + totalInfringements + ), null, - "onecommunityglobal@gmail.com" + "onecommunityglobal@gmail.com", + emailAddress ); }); }; @@ -778,10 +804,10 @@ const userHelper = function () { $set: { "badgeCollection.$.badge": newBadgeId, "badgeCollection.$.lastModified": Date.now().toString(), - "badgeCollection.$.count": 1 - } + "badgeCollection.$.count": 1, + }, }, - err => { + (err) => { if (err) { throw new Error(err); } @@ -796,9 +822,9 @@ const userHelper = function () { { $inc: { "badgeCollection.$.count": 1 }, $set: { "badgeCollection.$.lastModified": Date.now().toString() }, - $push: { "badgeCollection.$.earnedDate": earnedDateBadge() } + $push: { "badgeCollection.$.earnedDate": earnedDateBadge() }, }, - err => { + (err) => { if (err) { console.log(err); } @@ -812,17 +838,21 @@ const userHelper = function () { count = 1, featured = false ) { - console.log('Adding Badge'); + console.log("Adding Badge"); userProfile.findByIdAndUpdate( personId, { $push: { badgeCollection: { - badge: badgeId, count, earnedDate: [earnedDateBadge()], featured, lastModified: Date.now().toString(), + badge: badgeId, + count, + earnedDate: [earnedDateBadge()], + featured, + lastModified: Date.now().toString(), }, }, }, - err => { + (err) => { if (err) { throw new Error(err); } @@ -835,10 +865,10 @@ const userHelper = function () { personId, { $pull: { - badgeCollection: { badge: badgeId } - } + badgeCollection: { badge: badgeId }, + }, }, - err => { + (err) => { if (err) { throw new Error(err); } @@ -855,10 +885,10 @@ const userHelper = function () { { $set: { "badgeCollection.$.count": count, - "badgeCollection.$.lastModified": Date.now().toString() - } + "badgeCollection.$.lastModified": Date.now().toString(), + }, }, - err => { + (err) => { if (err) { throw new Error(err); } @@ -869,8 +899,13 @@ const userHelper = function () { // remove the last badge you earned on this streak(not including 1) - const removePrevHrBadge = async function (personId, user, badgeCollection, hrs, weeks) { - + const removePrevHrBadge = async function ( + personId, + user, + badgeCollection, + hrs, + weeks + ) { // Check each Streak Greater than One to check if it works if (weeks < 3) { return; @@ -882,30 +917,29 @@ const userHelper = function () { $match: { type: "X Hours for X Week Streak", weeks: { $gt: 1, $lt: weeks }, - totalHrs: hrs - } + totalHrs: hrs, + }, }, { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { _id: "$weeks", badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" } - } - } - } + $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, + }, + }, + }, ]) - .then(results => { - results.forEach(streak => { - streak.badges.every(bdge => { + .then((results) => { + results.forEach((streak) => { + streak.badges.every((bdge) => { for (let i = 0; i < badgeCollection.length; i += 1) { if ( - - badgeCollection[i].badge?.type === 'X Hours for X Week Streak' - && badgeCollection[i].badge?.weeks === bdge.weeks - && bdge.hrs === hrs - && !removed - + badgeCollection[i].badge?.type === + "X Hours for X Week Streak" && + badgeCollection[i].badge?.weeks === bdge.weeks && + bdge.hrs === hrs && + !removed ) { changeBadgeCount( personId, @@ -924,15 +958,24 @@ const userHelper = function () { // 'No Infringement Streak', - const checkNoInfringementStreak = async function (personId, user, badgeCollection) { + const checkNoInfringementStreak = async function ( + personId, + user, + badgeCollection + ) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'No Infringement Streak') { - if (badgeOfType && badgeOfType.months <= badgeCollection[i].badge.months) { + if (badgeCollection[i].badge?.type === "No Infringement Streak") { + if ( + badgeOfType && + badgeOfType.months <= badgeCollection[i].badge.months + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; - } else if (badgeOfType && badgeOfType.months > badgeCollection[i].badge.months) { - + } else if ( + badgeOfType && + badgeOfType.months > badgeCollection[i].badge.months + ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -942,28 +985,29 @@ const userHelper = function () { await badge .find({ type: "No Infringement Streak" }) .sort({ months: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length) { return; } - results.every(elem => { + results.every((elem) => { // Cannot account for time paused yet if (elem.months <= 12) { - - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { - + if ( + moment().diff(moment(user.createdDate), "months", true) >= + elem.months + ) { if ( user.infringements.length === 0 || Math.abs( moment().diff( - - moment(user.infringements[user.infringements?.length - 1].date), - 'months', - true, - ), - + moment( + user.infringements[user.infringements?.length - 1].date + ), + "months", + true + ) ) >= elem.months ) { if (badgeOfType) { @@ -981,21 +1025,23 @@ const userHelper = function () { } } } else if (user?.infringements?.length === 0) { - - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { - + if ( + moment().diff(moment(user.createdDate), "months", true) >= + elem.months + ) { if ( user.oldInfringements.length === 0 || Math.abs( moment().diff( - - moment(user.oldInfringements[user.oldInfringements?.length - 1].date), - 'months', - true, - ), - ) - >= elem.months - 12 - + moment( + user.oldInfringements[user.oldInfringements?.length - 1] + .date + ), + "months", + true + ) + ) >= + elem.months - 12 ) { if (badgeOfType) { if (badgeOfType._id.toString() !== elem._id.toString()) { @@ -1024,10 +1070,10 @@ const userHelper = function () { badgeCollection ) { const badgesOfType = badgeCollection - .map(obj => obj.badge) - .filter(badge => badge.type === 'Minimum Hours Multiple') + .map((obj) => obj.badge) + .filter((badge) => badge.type === "Minimum Hours Multiple"); await badge - .find({ type: 'Minimum Hours Multiple' }) + .find({ type: "Minimum Hours Multiple" }) .sort({ multiple: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -1046,13 +1092,13 @@ const userHelper = function () { ); return theBadge ? increaseBadgeCount( - personId, - mongoose.Types.ObjectId(theBadge._id) - ) + personId, + mongoose.Types.ObjectId(theBadge._id) + ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } } - }) + }); }; // 'Personal Max', @@ -1065,7 +1111,7 @@ const userHelper = function () { } } } - await badge.findOne({ type: "Personal Max" }).then(results => { + await badge.findOne({ type: "Personal Max" }).then((results) => { if ( user.lastWeekTangibleHrs && user.lastWeekTangibleHrs >= 1 && @@ -1078,9 +1124,11 @@ const userHelper = function () { user.personalBestMaxHrs ); } else { - - addBadge(personId, mongoose.Types.ObjectId(results._id), user.personalBestMaxHrs); - + addBadge( + personId, + mongoose.Types.ObjectId(results._id), + user.personalBestMaxHrs + ); } } }); @@ -1090,13 +1138,13 @@ const userHelper = function () { const checkMostHrsWeek = async function (personId, user, badgeCollection) { if ( - user.weeklycommittedHours > 0 - && user.lastWeekTangibleHrs > user.weeklycommittedHours + user.weeklycommittedHours > 0 && + user.lastWeekTangibleHrs > user.weeklycommittedHours ) { const badgeOfType = badgeCollection - .filter(object => object.badge.type === 'Most Hrs in Week') - .map(object => object.badge); - await badge.findOne({ type: 'Most Hrs in Week' }).then((results) => { + .filter((object) => object.badge.type === "Most Hrs in Week") + .map((object) => object.badge); + await badge.findOne({ type: "Most Hrs in Week" }).then((results) => { userProfile .aggregate([ { $match: { isActive: true } }, @@ -1105,7 +1153,6 @@ const userHelper = function () { .then((userResults) => { if (badgeOfType.length > 1) { removeDupBadge(user._id, badgeOfType[0]._id); - } if ( @@ -1138,8 +1185,8 @@ const userHelper = function () { await badge .find({ type: "X Hours for X Week Streak", weeks: 1 }) .sort({ totalHrs: -1 }) - .then(results => { - results.every(elem => { + .then((results) => { + results.every((elem) => { if (elem.totalHrs <= user.lastWeekTangibleHrs) { let theBadge; for (let i = 0; i < badgesOfType.length; i += 1) { @@ -1167,24 +1214,26 @@ const userHelper = function () { $group: { _id: "$weeks", badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" } - } - } - } + $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, + }, + }, + }, ]) - .then(results => { + .then((results) => { let lastHr = -1; - results.forEach(streak => { - streak.badges.every(bdge => { + results.forEach((streak) => { + streak.badges.every((bdge) => { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - - badgeCollection[i].badge?.type === 'X Hours for X Week Streak' - && badgeCollection[i].badge?.weeks === bdge.weeks + badgeCollection[i].badge?.type === + "X Hours for X Week Streak" && + badgeCollection[i].badge?.weeks === bdge.weeks ) { - if (badgeOfType && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs) { - + if ( + badgeOfType && + badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( @@ -1217,7 +1266,13 @@ const userHelper = function () { mongoose.Types.ObjectId(bdge._id) ); - removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); + removePrevHrBadge( + personId, + user, + badgeCollection, + bdge.hrs, + bdge.weeks + ); } else if (!badgeOfType) { addBadge(personId, mongoose.Types.ObjectId(bdge._id)); removePrevHrBadge( @@ -1228,9 +1283,17 @@ const userHelper = function () { bdge.weeks ); } else if (badgeOfType && badgeOfType.totalHrs === bdge.hrs) { - increaseBadgeCount(personId, mongoose.Types.ObjectId(badgeOfType._id)); - removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); - + increaseBadgeCount( + personId, + mongoose.Types.ObjectId(badgeOfType._id) + ); + removePrevHrBadge( + personId, + user, + badgeCollection, + bdge.hrs, + bdge.weeks + ); } return false; } @@ -1243,16 +1306,25 @@ const userHelper = function () { // 'Lead a team of X+' - - const checkLeadTeamOfXplus = async function (personId, user, badgeCollection) { - const leaderRoles = ['Mentor', 'Manager', 'Administrator', 'Owner', 'Core Team']; - const approvedRoles = ['Mentor', 'Manager']; + const checkLeadTeamOfXplus = async function ( + personId, + user, + badgeCollection + ) { + const leaderRoles = [ + "Mentor", + "Manager", + "Administrator", + "Owner", + "Core Team", + ]; + const approvedRoles = ["Mentor", "Manager"]; if (!approvedRoles.includes(user.role)) return; let teamMembers; await getTeamMembers({ - _id: personId - }).then(results => { + _id: personId, + }).then((results) => { if (results) { teamMembers = results.myteam; } else { @@ -1271,13 +1343,17 @@ const userHelper = function () { }); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - - if (badgeCollection[i].badge?.type === 'Lead a team of X+') { - if (badgeOfType && badgeOfType.people <= badgeCollection[i].badge.people) { + if (badgeCollection[i].badge?.type === "Lead a team of X+") { + if ( + badgeOfType && + badgeOfType.people <= badgeCollection[i].badge.people + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; - } else if (badgeOfType && badgeOfType.people > badgeCollection[i].badge.people) { - + } else if ( + badgeOfType && + badgeOfType.people > badgeCollection[i].badge.people + ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -1287,7 +1363,7 @@ const userHelper = function () { await badge .find({ type: "Lead a team of X+" }) .sort({ people: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length) { return; } @@ -1295,16 +1371,14 @@ const userHelper = function () { if (teamMembers && teamMembers.length >= badge.people) { if (badgeOfType) { if ( - badgeOfType._id.toString() !== badge._id.toString() - && badgeOfType.people < badge.people - + badgeOfType._id.toString() !== badge._id.toString() && + badgeOfType.people < badge.people ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(badge._id), - + mongoose.Types.ObjectId(badge._id) ); } return false; @@ -1327,19 +1401,18 @@ const userHelper = function () { "education", "society", "economics", - "stewardship" + "stewardship", ]; const badgesOfType = badgeCollection - .filter(object => object.badge.type === "Total Hrs in Category") - .map(object => object.badge); + .filter((object) => object.badge.type === "Total Hrs in Category") + .map((object) => object.badge); - categories.forEach(async category => { + categories.forEach(async (category) => { const categoryHrs = Object.keys(hoursByCategory).find( - elem => elem === category + (elem) => elem === category ); - let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( @@ -1365,16 +1438,16 @@ const userHelper = function () { const newCatg = category.charAt(0).toUpperCase() + category.slice(1); - - await badge.find({ type: 'Total Hrs in Category', category: newCatg }) + await badge + .find({ type: "Total Hrs in Category", category: newCatg }) .sort({ totalHrs: -1 }) - .then(results => { + .then((results) => { if (!Array.isArray(results) || !results.length || !categoryHrs) { return; } - results.every(elem => { + results.every((elem) => { if ( hoursByCategory[categoryHrs] >= 100 && hoursByCategory[categoryHrs] >= elem.totalHrs @@ -1415,9 +1488,9 @@ const userHelper = function () { const awardNewBadges = async () => { console.log("Awarding"); try { - - const users = await userProfile.find({ isActive: true }).populate('badgeCollection.badge'); - + const users = await userProfile + .find({ isActive: true }) + .populate("badgeCollection.badge"); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -1439,20 +1512,25 @@ const userHelper = function () { const getTangibleHoursReportedThisWeekByUserId = function (personId) { const userId = mongoose.Types.ObjectId(personId); - const pdtstart = moment().tz('America/Los_Angeles').startOf('week').format('YYYY-MM-DD'); - const pdtend = moment().tz('America/Los_Angeles').endOf('week').format('YYYY-MM-DD'); - + const pdtstart = moment() + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); + const pdtend = moment() + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return timeEntries .find( { personId: userId, dateOfWork: { $gte: pdtstart, $lte: pdtend }, - isTangible: true + isTangible: true, }, "totalSeconds" ) - .then(results => { + .then((results) => { const totalTangibleWeeklySeconds = results.reduce( (acc, { totalSeconds }) => acc + totalSeconds, 0 @@ -1475,15 +1553,19 @@ const userHelper = function () { await userProfile.findByIdAndUpdate( user._id, user.set({ - isActive: false + isActive: false, }), { new: true } ); const id = user._id; const person = await userProfile.findById(id); - const lastDay = moment(person.endDate).format('YYYY-MM-DD'); - logger.logInfo(`User with id: ${user._id} was de-acticated at ${moment().format()}.`); + const lastDay = moment(person.endDate).format("YYYY-MM-DD"); + logger.logInfo( + `User with id: ${ + user._id + } was de-acticated at ${moment().format()}.` + ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been deactivated in the Highest Good Network`; @@ -1497,8 +1579,14 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; - - emailSender('onecommunityglobal@gmail.com', subject, emailBody, null, null); + emailSender( + "onecommunityglobal@gmail.com", + subject, + emailBody, + null, + null, + person.email + ); } } } catch (err) { @@ -1514,8 +1602,7 @@ const userHelper = function () { } catch (error) { logger.logException(err); } - } - + }; return { getUserName, @@ -1531,7 +1618,7 @@ const userHelper = function () { emailWeeklySummariesForAllUsers, awardNewBadges, getTangibleHoursReportedThisWeekByUserId, - deleteExpiredTokens + deleteExpiredTokens, }; }; From 67b764a89a3fcebb233f9f2cb6d623d69ce9abec Mon Sep 17 00:00:00 2001 From: wang9hu Date: Tue, 28 Nov 2023 17:47:57 -0800 Subject: [PATCH 073/129] fix some response send result before set status code, add empty string for teamCode in the team model, make team controller check team name for duplicates when post new team --- src/controllers/dashBoardController.js | 96 +++++++++++++------------- src/controllers/logincontroller.js | 4 +- src/controllers/teamController.js | 24 ++++--- src/models/team.js | 2 +- 4 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index b1b62f9ae..1cf730a5a 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -1,8 +1,8 @@ -const path = require("path"); -const fs = require("fs/promises"); -const mongoose = require("mongoose"); -const dashboardhelper = require("../helpers/dashboardhelper")(); -const emailSender = require("../utilities/emailSender"); +const path = require('path'); +const fs = require('fs/promises'); +const mongoose = require('mongoose'); +const dashboardhelper = require('../helpers/dashboardhelper')(); +const emailSender = require('../utilities/emailSender'); const dashboardcontroller = function () { const dashboarddata = function (req, res) { @@ -11,7 +11,7 @@ const dashboardcontroller = function () { const snapshot = dashboardhelper.personaldetails(userId); snapshot.then((results) => { - res.send(results).status(200); + res.status(200).send(results); }); }; @@ -20,13 +20,13 @@ const dashboardcontroller = function () { const laborthismonth = dashboardhelper.laborthismonth( userId, req.params.fromDate, - req.params.toDate + req.params.toDate, ); laborthismonth.then((results) => { if (!results || results.length === 0) { const emptyresult = [ { - projectName: "", + projectName: '', timeSpent_hrs: 0, }, ]; @@ -42,10 +42,10 @@ const dashboardcontroller = function () { const laborthisweek = dashboardhelper.laborthisweek( userId, req.params.fromDate, - req.params.toDate + req.params.toDate, ); laborthisweek.then((results) => { - res.send(results).status(200); + res.status(200).send(results); }); }; @@ -63,7 +63,7 @@ const dashboardcontroller = function () { }); } }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }; const orgData = function (req, res) { @@ -73,7 +73,7 @@ const dashboardcontroller = function () { .then((results) => { res.status(200).send(results[0]); }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }; const getBugReportEmailBody = function ( @@ -85,7 +85,7 @@ const dashboardcontroller = function () { expected, actual, visual, - severity + severity, ) { const text = `New Bug Report From ${firstName} ${lastName}:

[Feature Name] Bug Title:

@@ -130,32 +130,32 @@ const dashboardcontroller = function () { expected, actual, visual, - severity + severity, ); try { emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', `Bug Rport from ${firstName} ${lastName}`, emailBody, - email + email, ); - res.status(200).send("Success"); + res.status(200).send('Success'); } catch { - res.status(500).send("Failed"); + res.status(500).send('Failed'); } }; const suggestionData = { suggestion: [ - "Identify and remedy poor client and/or user service experiences", - "Identify bright spots and enhance positive service experiences", - "Make fundamental changes to our programs and/or operations", - "Inform the development of new programs/projects", - "Identify where we are less inclusive or equitable across demographic groups", - "Strengthen relationships with the people we serve", + 'Identify and remedy poor client and/or user service experiences', + 'Identify bright spots and enhance positive service experiences', + 'Make fundamental changes to our programs and/or operations', + 'Inform the development of new programs/projects', + 'Identify where we are less inclusive or equitable across demographic groups', + 'Strengthen relationships with the people we serve', "Understand people's needs and how we can help them achieve their goals", - "Other", + 'Other', ], field: [], }; @@ -164,8 +164,8 @@ const dashboardcontroller = function () { let fieldaaray = []; if (suggestionData.field.length) { fieldaaray = suggestionData.field.map( - (item) => `

${item}

-

${args[3][item]}

` + item => `

${item}

+

${args[3][item]}

`, ); } const text = `New Suggestion From ${args[3].firstName} ${ @@ -178,7 +178,7 @@ const dashboardcontroller = function () {

${args[0]}

⚹ Suggestion:

${args[1]}

- ${fieldaaray.length > 0 ? fieldaaray : ""} + ${fieldaaray.length > 0 ? fieldaaray : ''} ⚹ Name of Suggester:

${args[3].firstName} ${args[3].lastName}

⚹ Email of Suggester: @@ -193,27 +193,29 @@ const dashboardcontroller = function () { // send suggestion email const sendMakeSuggestion = async (req, res) => { - const { suggestioncate, suggestion, confirm, email, ...rest } = req.body; + const { + suggestioncate, suggestion, confirm, email, ...rest +} = req.body; const emailBody = await getsuggestionEmailBody( suggestioncate, suggestion, confirm, rest, - email + email, ); try { emailSender( - "onecommunityglobal@gmail.com", - "A new suggestion", + 'onecommunityglobal@gmail.com', + 'A new suggestion', emailBody, null, null, email, - null + null, ); - res.status(200).send("Success"); + res.status(200).send('Success'); } catch { - res.status(500).send("Failed"); + res.status(500).send('Failed'); } }; @@ -222,40 +224,40 @@ const dashboardcontroller = function () { if (suggestionData) { res.status(200).send(suggestionData); } else { - res.status(404).send("Suggestion data not found."); + res.status(404).send('Suggestion data not found.'); } } catch (error) { - console.error("Error getting suggestion data:", error); - res.status(500).send("Internal Server Error"); + console.error('Error getting suggestion data:', error); + res.status(500).send('Internal Server Error'); } }; const editSuggestionOption = async (req, res) => { try { if (req.body.suggestion) { - if (req.body.action === "add") { + if (req.body.action === 'add') { suggestionData.suggestion.unshift(req.body.newField); } - if (req.body.action === "delete") { + if (req.body.action === 'delete') { suggestionData.suggestion = suggestionData.suggestion.filter( - (item, index) => index + 1 !== +req.body.newField + (item, index) => index + 1 !== +req.body.newField, ); } } else { - if (req.body.action === "add") { + if (req.body.action === 'add') { suggestionData.field.unshift(req.body.newField); } - if (req.body.action === "delete") { + if (req.body.action === 'delete') { suggestionData.field = suggestionData.field.filter( - (item) => item !== req.body.newField + item => item !== req.body.newField, ); } } - res.status(200).send("success"); + res.status(200).send('success'); } catch (error) { - console.error("Error editing suggestion option:", error); - res.status(500).send("Internal Server Error"); + console.error('Error editing suggestion option:', error); + res.status(500).send('Internal Server Error'); } }; diff --git a/src/controllers/logincontroller.js b/src/controllers/logincontroller.js index cdeae9b37..b6da4cf8b 100644 --- a/src/controllers/logincontroller.js +++ b/src/controllers/logincontroller.js @@ -43,7 +43,7 @@ const logincontroller = function () { new: true, userId: user._id, }; - res.send(result).status(200); + res.status(200).send(result); } else if (isPasswordMatch && !isNewUser) { const jwtPayload = { userid: user._id, @@ -57,7 +57,7 @@ const logincontroller = function () { const token = jwt.sign(jwtPayload, JWT_SECRET); - res.send({ token }).status(200); + res.status(200).send({ token }); } else { res.status(403).send({ message: 'Invalid password.', diff --git a/src/controllers/teamController.js b/src/controllers/teamController.js index b204875a5..86733daf4 100644 --- a/src/controllers/teamController.js +++ b/src/controllers/teamController.js @@ -7,32 +7,38 @@ const teamcontroller = function (Team) { const getAllTeams = function (req, res) { Team.find({}) .sort({ teamName: 1 }) - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => res.status(200).send(results)) + .catch(error => res.status(404).send(error)); }; const getTeamById = function (req, res) { const { teamId } = req.params; Team.findById(teamId) - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => res.status(200).send(results)) + .catch(error => res.status(404).send(error)); }; const postTeam = async function (req, res) { if (!await hasPermission(req.body.requestor, 'postTeam')) { res.status(403).send({ error: 'You are not authorized to create teams.' }); return; } + + if (await Team.exists({ teamName: req.body.teamName })) { + res.status(403).send({ error: `Team Name "${req.body.teamName}" already exists` }); + return; + } + const team = new Team(); team.teamName = req.body.teamName; - team.isACtive = req.body.isActive; + team.isACtive = true; team.createdDatetime = Date.now(); team.modifiedDatetime = Date.now(); team .save() - .then(results => res.send(results).status(200)) - .catch(error => res.send(error).status(404)); + .then(results => res.status(200).send(results)) + .catch(error => res.status(404).send(error)); }; const deleteTeam = async function (req, res) { if (!await hasPermission(req.body.requestor, 'deleteTeam')) { @@ -49,7 +55,7 @@ const teamcontroller = function (Team) { const deleteteam = record.remove(); Promise.all([removeteamfromprofile, deleteteam]) - .then(res.status(200).send({ message: ' Team successfully deleted and user profiles updated' })) + .then(res.status(200).send({ message: 'Team successfully deleted and user profiles updated' })) .catch((errors) => { res.status(400).send(errors); }); @@ -87,7 +93,7 @@ const teamcontroller = function (Team) { record .save() - .then(results => res.status(201).send(results._id)) + .then(results => res.status(200).send(results._id)) .catch(errors => res.status(400).send(errors)); }); }; diff --git a/src/models/team.js b/src/models/team.js index 00fbaf8e3..1df50b95a 100644 --- a/src/models/team.js +++ b/src/models/team.js @@ -18,7 +18,7 @@ const team = new Schema({ default: '', validate: { validator(v) { - const teamCoderegex = /^([a-zA-Z]-[a-zA-Z]{3}|[a-zA-Z]{5})$/; + const teamCoderegex = /^([a-zA-Z]-[a-zA-Z]{3}|[a-zA-Z]{5})$|^$/; return teamCoderegex.test(v); }, message: From b3e7dd1e063df76ed893178a7d340116636858c5 Mon Sep 17 00:00:00 2001 From: kaikane lacno <114305309+lacnoskillz@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:49:02 -0600 Subject: [PATCH 074/129] Update pull_request_template.md --- .github/pull_request_template.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c6fee8ab4..4816bd1e2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,9 +17,10 @@ To test this backend PR you need to checkout the #XXX frontend PR. ## How to test: 1. check into current branch 2. do `npm install` and `...` to run this PR locally -3. log as admin user -4. go to dashboard→ Tasks→ task→… -5. verify function “A” (feel free to include screenshot here) +3. Clear site data/cache +4. log as admin user +5. go to dashboard→ Tasks→ task→… +6. verify function “A” (feel free to include screenshot here) ## Screenshots or videos of changes: From d71867ed9574a36efecc0ca073bf55328c166ef0 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Thu, 30 Nov 2023 13:35:10 -0800 Subject: [PATCH 075/129] update material changes --- .../bmdashboard/bmMaterialsController.js | 4 ++-- .../bmdashboard/buildingInventoryType.js | 8 +++---- src/models/bmdashboard/buildingMaterial.js | 22 +++++++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 671cb0b24..b9c6438b4 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -88,7 +88,7 @@ const bmMaterialsController = function (ItemMaterial) { }; const bmPostMaterialUpdateBulk = function (req, res) { - const materialUpdates= req.body; + const materialUpdates= req.body.upadateMaterials; let errorFlag = false; const updateRecordsToBeAdded = []; for(let i=0;i Date: Thu, 30 Nov 2023 16:49:51 -0800 Subject: [PATCH 076/129] resolved conflicts --- src/controllers/dashBoardController.js | 28 -------------------------- 1 file changed, 28 deletions(-) diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index 6739043f3..b8294b93e 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -168,18 +168,6 @@ const dashboardcontroller = function () {

${args[3][item]}

`, ); } -<<<<<<< HEAD - const text = `New Suggestion: -

Suggestion Category:

-

${args[0]}

-

Suggestion:

-

${args[1]}

- ${fieldaaray.length > 0 ? fieldaaray : ''} -

Wants Feedback:

-

${args[2]}

-

Thank you,
- One Community

`; -======= const text = `New Suggestion From ${args[3].firstName} ${ args[3].lastName } @@ -197,33 +185,18 @@ const dashboardcontroller = function () {

${args[2]}

Thank you,
One Community
`; ->>>>>>> development return text; }; // send suggestion email const sendMakeSuggestion = async (req, res) => { -<<<<<<< HEAD - const { - suggestioncate, suggestion, confirm, ...rest -} = req.body; -======= const { suggestioncate, suggestion, confirm, email, ...rest } = req.body; ->>>>>>> development const emailBody = await getsuggestionEmailBody( suggestioncate, suggestion, confirm, rest, -<<<<<<< HEAD - ); - try { - emailSender( - 'onecommunityglobal@gmail.com', - 'A new suggestion', - emailBody, -======= email ); try { @@ -234,7 +207,6 @@ const dashboardcontroller = function () { null, null, email ->>>>>>> development ); res.status(200).send('Success'); } catch { From a9058c03c6166d0227f901cf54b5bca5f31b2b25 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Thu, 30 Nov 2023 18:04:38 -0800 Subject: [PATCH 077/129] resolved conflicts --- src/controllers/dashBoardController.js | 33 +- src/helpers/userHelper.js | 782 ++++++++++++++----------- src/startup/routes.js | 15 +- 3 files changed, 477 insertions(+), 353 deletions(-) diff --git a/src/controllers/dashBoardController.js b/src/controllers/dashBoardController.js index b8294b93e..30bddc871 100644 --- a/src/controllers/dashBoardController.js +++ b/src/controllers/dashBoardController.js @@ -1,8 +1,8 @@ -const path = require("path"); -const fs = require("fs/promises"); -const mongoose = require("mongoose"); -const dashboardhelper = require("../helpers/dashboardhelper")(); -const emailSender = require("../utilities/emailSender"); +const path = require('path'); +const fs = require('fs/promises'); +const mongoose = require('mongoose'); +const dashboardhelper = require('../helpers/dashboardhelper')(); +const emailSender = require('../utilities/emailSender'); const dashboardcontroller = function () { const dashboarddata = function (req, res) { @@ -11,7 +11,7 @@ const dashboardcontroller = function () { const snapshot = dashboardhelper.personaldetails(userId); snapshot.then((results) => { - res.send(results).status(200); + res.status(200).send(results); }); }; @@ -45,7 +45,7 @@ const dashboardcontroller = function () { req.params.toDate, ); laborthisweek.then((results) => { - res.send(results).status(200); + res.status(200).send(results); }); }; @@ -172,11 +172,13 @@ const dashboardcontroller = function () { args[3].lastName }
: +
+
⚹ Suggestion Category:

${args[0]}

⚹ Suggestion:

${args[1]}

- ${fieldaaray.length > 0 ? fieldaaray : ""} + ${fieldaaray.length > 0 ? fieldaaray : ''} ⚹ Name of Suggester:

${args[3].firstName} ${args[3].lastName}

⚹ Email of Suggester: @@ -191,22 +193,25 @@ const dashboardcontroller = function () { // send suggestion email const sendMakeSuggestion = async (req, res) => { - const { suggestioncate, suggestion, confirm, email, ...rest } = req.body; + const { + suggestioncate, suggestion, confirm, email, ...rest +} = req.body; const emailBody = await getsuggestionEmailBody( suggestioncate, suggestion, confirm, rest, - email + email, ); try { emailSender( - "onecommunityglobal@gmail.com", - "A new suggestion", + 'onecommunityglobal@gmail.com', + 'A new suggestion', emailBody, null, null, - email + email, + null ); res.status(200).send('Success'); } catch { @@ -269,4 +274,4 @@ const dashboardcontroller = function () { }; }; -module.exports = dashboardcontroller; +module.exports = dashboardcontroller; \ No newline at end of file diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index ab6dcea9f..f639a4d37 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -1,28 +1,28 @@ /* eslint-disable no-continue */ /* eslint-disable no-await-in-loop */ -const mongoose = require('mongoose'); -const moment = require('moment-timezone'); -const _ = require('lodash'); -const userProfile = require('../models/userProfile'); -const timeEntries = require('../models/timeentry'); -const badge = require('../models/badge'); -const myTeam = require('./helperModels/myTeam'); -const dashboardHelper = require('./dashboardhelper')(); -const reportHelper = require('./reporthelper')(); -const emailSender = require('../utilities/emailSender'); -const logger = require('../startup/logger'); -const hasPermission = require('../utilities/permissions'); -const Reason = require('../models/reason'); -const token = require('../models/profileInitialSetupToken'); +const mongoose = require("mongoose"); +const moment = require("moment-timezone"); +const _ = require("lodash"); +const userProfile = require("../models/userProfile"); +const timeEntries = require("../models/timeentry"); +const badge = require("../models/badge"); +const myTeam = require("./helperModels/myTeam"); +const dashboardHelper = require("./dashboardhelper")(); +const reportHelper = require("./reporthelper")(); +const emailSender = require("../utilities/emailSender"); +const logger = require("../startup/logger"); +const hasPermission = require("../utilities/permissions"); +const Reason = require("../models/reason"); +const token = require("../models/profileInitialSetupToken"); const userHelper = function () { const getTeamMembers = function (user) { const userId = mongoose.Types.ObjectId(user._id); // var teamid = userdetails.teamId; return myTeam.findById(userId).select({ - 'myTeam._id': 0, - 'myTeam.role': 0, - 'myTeam.fullName': 0, + "myTeam._id": 0, + "myTeam.role": 0, + "myTeam.fullName": 0, _id: 0, }); }; @@ -46,33 +46,34 @@ const userHelper = function () { const getUserName = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - return userProfile.findById(userid, 'firstName lastName'); + return userProfile.findById(userid, "firstName lastName"); }; const validateProfilePic = function (profilePic) { - const picParts = profilePic.split('base64'); + const picParts = profilePic.split("base64"); let result = true; const errors = []; if (picParts.length < 2) { return { result: false, - errors: 'Invalid image', + errors: "Invalid image", }; } // validate size const imageSize = picParts[1].length; - const sizeInBytes = (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; + const sizeInBytes = + (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; if (sizeInBytes > 50) { - errors.push('Image size should not exceed 50KB'); + errors.push("Image size should not exceed 50KB"); result = false; } - const imageType = picParts[0].split('/')[1]; - if (imageType !== 'jpeg;' && imageType !== 'png;') { - errors.push('Image type shoud be either jpeg or png.'); + const imageType = picParts[0].split("/")[1]; + if (imageType !== "jpeg;" && imageType !== "png;") { + errors.push("Image type shoud be either jpeg or png."); result = false; } @@ -89,10 +90,11 @@ const userHelper = function () { totalInfringements, timeRemaining ) { - let final_paragraph = ''; + let final_paragraph = ""; if (timeRemaining == undefined) { - final_paragraph = '

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

'; + final_paragraph = + "

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

"; } else { final_paragraph = `

Life happens and we understand that. Please make up the missed hours this following week though to avoid getting another blue square. So you know what’s needed, the missing/incomplete hours (${timeRemaining} hours) have been added to your current week and this new weekly total can be seen at the top of your dashboard.

Reminder also that each blue square is removed from your profile 1 year after it was issued.

`; @@ -103,8 +105,8 @@ const userHelper = function () {

Date Assigned: ${infringement.date}

Description: ${infringement.description}

Total Infringements: This is your ${moment - .localeData() - .ordinal(totalInfringements)} blue square of 5.

+ .localeData() + .ordinal(totalInfringements)}
blue square of 5.

${final_paragraph}

Thank you,
One Community

`; @@ -123,12 +125,10 @@ const userHelper = function () { * @return {void} */ const emailWeeklySummariesForAllUsers = async (weekIndex = 1) => { - const currentFormattedDate = moment() - .tz('America/Los_Angeles') - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( - `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}`, + `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}` ); const emails = []; @@ -136,13 +136,19 @@ const userHelper = function () { try { const results = await reportHelper.weeklySummaries(weekIndex, weekIndex); - let emailBody = '

Weekly Summaries for all active users:

'; + let emailBody = "

Weekly Summaries for all active users:

"; - const weeklySummaryNotProvidedMessage = '
Weekly Summary: Not provided!
'; + const weeklySummaryNotProvidedMessage = + '
Weekly Summary: Not provided!
'; - const weeklySummaryNotRequiredMessage = '
Weekly Summary: Not required for this user
'; + const weeklySummaryNotRequiredMessage = + '
Weekly Summary: Not required for this user
'; - results.sort((a, b) => `${a.firstName} ${a.lastName}`.localeCompare(`${b.firstName} ${b.lastname}`)); + results.sort((a, b) => + `${a.firstName} ${a.lastName}`.localeCompare( + `${b.firstName} ${b.lastname}` + ) + ); for (let i = 0; i < results.length; i += 1) { const result = results[i]; @@ -166,19 +172,21 @@ const userHelper = function () { // hence totalSeconds[0] should be used const hoursLogged = result.totalSeconds[0] / 3600 || 0; - const mediaUrlLink = mediaUrl ? `${mediaUrl}` : 'Not provided!'; + const mediaUrlLink = mediaUrl + ? `${mediaUrl}` + : "Not provided!"; let weeklySummaryMessage = weeklySummaryNotProvidedMessage; const colorStyle = (() => { switch (weeklySummaryOption) { - case 'Team': + case "Team": return 'style="color: magenta;"'; - case 'Not Required': + case "Not Required": return 'style="color: green"'; - case 'Required': - return ''; + case "Required": + return ""; default: - return result.weeklySummaryNotReq ? 'style="color: green"' : ''; + return result.weeklySummaryNotReq ? 'style="color: green"' : ""; } })(); // weeklySummaries array should only have one item if any, hence weeklySummaries[0] needs be used to access it. @@ -189,16 +197,16 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz('America/Los_Angeles') - .format('YYYY-MMM-DD')}): + .tz("America/Los_Angeles") + .format("YYYY-MMM-DD")}):
${summary}
`; } else if ( - weeklySummaryOption === 'Not Required' - || (!weeklySummaryOption && result.weeklySummaryNotReq) + weeklySummaryOption === "Not Required" || + (!weeklySummaryOption && result.weeklySummaryNotReq) ) { weeklySummaryMessage = weeklySummaryNotRequiredMessage; } @@ -210,21 +218,26 @@ const userHelper = function () { Name: ${firstName} ${lastName}

- Media URL: ${mediaUrlLink || 'Not provided!'} + Media URL: ${ + mediaUrlLink || 'Not provided!' + }

- ${weeklySummariesCount === 8 - ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` - : `

Total Valid Weekly Summaries: ${weeklySummariesCount - || 'No valid submissions yet!'}

` + ${ + weeklySummariesCount === 8 + ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` + : `

Total Valid Weekly Summaries: ${ + weeklySummariesCount || "No valid submissions yet!" + }

` } - ${hoursLogged >= weeklycommittedHours - - ? `

Hours logged: ${hoursLogged.toFixed(2)} / ${weeklycommittedHours}

` - - : `

Hours logged: ${hoursLogged.toFixed( - 2, - )} / ${weeklycommittedHours}

` + ${ + hoursLogged >= weeklycommittedHours + ? `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` + : `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage}
`; @@ -233,8 +246,10 @@ const userHelper = function () { // Necessary because our version of node is outdated // and doesn't have String.prototype.replaceAll let emailString = [...new Set(emails)].toString(); - while (emailString.includes(',')) emailString = emailString.replace(',', '\n'); - while (emailString.includes('\n')) emailString = emailString.replace('\n', ', '); + while (emailString.includes(",")) + emailString = emailString.replace(",", "\n"); + while (emailString.includes("\n")) + emailString = emailString.replace("\n", ", "); emailBody += `\n
@@ -246,10 +261,12 @@ const userHelper = function () { `; emailSender( - 'onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com', - 'Weekly Summaries for all active users...', + "onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com", + "Weekly Summaries for all active users...", emailBody, null, + null, + emailString ); } catch (err) { logger.logException(err); @@ -270,10 +287,8 @@ const userHelper = function () { weeklySummaries: { $each: [ { - dueDate: moment() - .tz('America/Los_Angeles') - .endOf('week'), - summary: '', + dueDate: moment().tz("America/Los_Angeles").endOf("week"), + summary: "", }, ], $position: 0, @@ -281,7 +296,7 @@ const userHelper = function () { }, }, }) - .catch(error => logger.logException(error)); + .catch((error) => logger.logException(error)); }; /** @@ -292,39 +307,34 @@ const userHelper = function () { */ const assignBlueSquareForTimeNotMet = async () => { try { - const currentFormattedDate = moment() - .tz('America/Los_Angeles') - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); const currentUTCDate = moment - .tz('America/Los_Angeles') - .startOf('day') + .tz("America/Los_Angeles") + .startOf("day") .toISOString(); logger.logInfo( - - `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}`, - + `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}` ); const pdtStartOfLastWeek = moment() - .tz('America/Los_Angeles') - .startOf('week') - .subtract(1, 'week'); + .tz("America/Los_Angeles") + .startOf("week") + .subtract(1, "week"); const pdtEndOfLastWeek = moment() - .tz('America/Los_Angeles') - .endOf('week') - .subtract(1, 'week'); - + .tz("America/Los_Angeles") + .endOf("week") + .subtract(1, "week"); const users = await userProfile.find( { isActive: true }, - '_id weeklycommittedHours weeklySummaries missedHours', + "_id weeklycommittedHours weeklySummaries missedHours" ); - // this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be - // targeted as spam - // There's no need to put Promise.all here + //this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be + //targeted as spam + //There's no need to put Promise.all here for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -339,8 +349,10 @@ const userHelper = function () { let hasWeeklySummary = false; - - if (Array.isArray(user.weeklySummaries) && user.weeklySummaries.length) { + if ( + Array.isArray(user.weeklySummaries) && + user.weeklySummaries.length + ) { const { summary } = user.weeklySummaries[0]; if (summary) { hasWeeklySummary = true; @@ -353,19 +365,19 @@ const userHelper = function () { const results = await dashboardHelper.laborthisweek( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek, + pdtEndOfLastWeek ); const { timeSpent_hrs: timeSpent } = results[0]; - const weeklycommittedHours = user.weeklycommittedHours + (user.missedHours ?? 0); + const weeklycommittedHours = + user.weeklycommittedHours + (user.missedHours ?? 0); const timeNotMet = timeSpent < weeklycommittedHours; let description; const timeRemaining = weeklycommittedHours - timeSpent; - const updateResult = await userProfile.findByIdAndUpdate( personId, { @@ -382,25 +394,23 @@ const userHelper = function () { lastWeekTangibleHrs: timeSpent || 0, }, }, - { new: true }, + { new: true } ); if ( - - updateResult?.weeklySummaryOption === 'Not Required' - || updateResult?.weeklySummaryNotReq - + updateResult?.weeklySummaryOption === "Not Required" || + updateResult?.weeklySummaryNotReq ) { hasWeeklySummary = true; } - const cutOffDate = moment().subtract(1, 'year'); + const cutOffDate = moment().subtract(1, "year"); const oldInfringements = []; for (let k = 0; k < updateResult?.infringements.length; k += 1) { if ( - updateResult?.infringements - && moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 + updateResult?.infringements && + moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 ) { oldInfringements.push(updateResult.infringements[k]); } else { @@ -416,31 +426,35 @@ const userHelper = function () { oldInfringements: { $each: oldInfringements, $slice: -10 }, }, }, - { new: true }, + { new: true } ); } if (timeNotMet || !hasWeeklySummary) { if (foundReason) { description = foundReason.reason; - } else if (timeNotMet && !hasWeeklySummary) { - description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - 'dddd YYYY-MM-DD', - )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; + } else { + if (timeNotMet && !hasWeeklySummary) { + description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed( + 2 + )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( + "dddd YYYY-MM-DD" + )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; } else if (timeNotMet) { - description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed(2)} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - 'dddd YYYY-MM-DD', - )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; + description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed( + 2 + )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( + "dddd YYYY-MM-DD" + )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; } else { description = `System auto-assigned infringement for not submitting a weekly summary for the week starting ${pdtStartOfLastWeek.format( - 'dddd YYYY-MM-DD', - )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; + "dddd YYYY-MM-DD" + )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; } + } const infringement = { - date: moment() - .utc() - .format('YYYY-MM-DD'), + date: moment().utc().format("YYYY-MM-DD"), description, }; @@ -451,45 +465,47 @@ const userHelper = function () { infringements: infringement, }, }, - { new: true }, + { new: true } ); - let emailBody = ''; - if (person.role == 'Core Team' && timeRemaining > 0) { + let emailBody = ""; + if (person.role == "Core Team" && timeRemaining > 0) { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, status.infringements.length, - timeRemaining, + timeRemaining ); } else { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, - status.infringements.length, + status.infringements.length ); } emailSender( status.email, - 'New Infringement Assigned', + "New Infringement Assigned", emailBody, null, - 'onecommunityglobal@gmail.com', + "onecommunityglobal@gmail.com", + null, + status.email ); const categories = await dashboardHelper.laborThisWeekByCategory( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek, + pdtEndOfLastWeek ); if (Array.isArray(categories) && categories.length > 0) { await userProfile.findOneAndUpdate( { _id: personId, categoryTangibleHrs: { $exists: false } }, - { $set: { categoryTangibleHrs: [] } }, + { $set: { categoryTangibleHrs: [] } } ); } else { continue; @@ -499,20 +515,20 @@ const userHelper = function () { const elem = categories[j]; if (elem._id == null) { - elem._id = 'Other'; + elem._id = "Other"; } const updateResult2 = await userProfile.findOneAndUpdate( - { _id: personId, 'categoryTangibleHrs.category': elem._id }, - { $inc: { 'categoryTangibleHrs.$.hrs': elem.timeSpent_hrs } }, - { new: true }, + { _id: personId, "categoryTangibleHrs.category": elem._id }, + { $inc: { "categoryTangibleHrs.$.hrs": elem.timeSpent_hrs } }, + { new: true } ); if (!updateResult2) { await userProfile.findOneAndUpdate( { _id: personId, - 'categoryTangibleHrs.category': { $ne: elem._id }, + "categoryTangibleHrs.category": { $ne: elem._id }, }, { $addToSet: { @@ -521,7 +537,7 @@ const userHelper = function () { hrs: elem.timeSpent_hrs, }, }, - }, + } ); } } @@ -533,11 +549,14 @@ const userHelper = function () { // processWeeklySummaries for nonActive users try { - const inactiveUsers = await userProfile.find({ isActive: false }, '_id'); + const inactiveUsers = await userProfile.find({ isActive: false }, "_id"); for (let i = 0; i < inactiveUsers.length; i += 1) { const user = inactiveUsers[i]; - await processWeeklySummariesByUserId(mongoose.Types.ObjectId(user._id), false); + await processWeeklySummariesByUserId( + mongoose.Types.ObjectId(user._id), + false + ); } } catch (err) { logger.logException(err); @@ -546,52 +565,50 @@ const userHelper = function () { const applyMissedHourForCoreTeam = async () => { try { - const currentDate = moment() - .tz('America/Los_Angeles') - .format(); + const currentDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( - `Job for applying missed hours for Core Team members starting at ${currentDate}`, + `Job for applying missed hours for Core Team members starting at ${currentDate}` ); const startOfLastWeek = moment() - .tz('America/Los_Angeles') - .startOf('week') - .subtract(1, 'week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .subtract(1, "week") + .format("YYYY-MM-DD"); const endOfLastWeek = moment() - .tz('America/Los_Angeles') - .endOf('week') - .subtract(1, 'week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .subtract(1, "week") + .format("YYYY-MM-DD"); const missedHours = await userProfile.aggregate([ { $match: { - role: 'Core Team', + role: "Core Team", isActive: true, }, }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', + from: "timeEntries", + localField: "_id", + foreignField: "personId", pipeline: [ { $match: { $expr: { $and: [ - { $eq: ['$isTangible', true] }, - { $gte: ['$dateOfWork', startOfLastWeek] }, - { $lte: ['$dateOfWork', endOfLastWeek] }, + { $eq: ["$isTangible", true] }, + { $gte: ["$dateOfWork", startOfLastWeek] }, + { $lte: ["$dateOfWork", endOfLastWeek] }, ], }, }, }, ], - as: 'timeEntries', + as: "timeEntries", }, }, { @@ -602,17 +619,18 @@ const userHelper = function () { { $subtract: [ { - - $sum: [{ $ifNull: ['$missedHours', 0] }, '$weeklycommittedHours'], - + $sum: [ + { $ifNull: ["$missedHours", 0] }, + "$weeklycommittedHours", + ], }, { $divide: [ { $sum: { $map: { - input: '$timeEntries', - in: '$$this.totalSeconds', + input: "$timeEntries", + in: "$$this.totalSeconds", }, }, }, @@ -646,17 +664,13 @@ const userHelper = function () { }; const deleteBlueSquareAfterYear = async () => { - const currentFormattedDate = moment() - .tz('America/Los_Angeles') - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( - `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}`, + `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}` ); - const cutOffDate = moment() - .subtract(1, 'year') - .format('YYYY-MM-DD'); + const cutOffDate = moment().subtract(1, "year").format("YYYY-MM-DD"); try { const results = await userProfile.updateMany( @@ -669,7 +683,7 @@ const userHelper = function () { }, }, }, - }, + } ); logger.logInfo(results); @@ -679,18 +693,16 @@ const userHelper = function () { }; const reActivateUser = async () => { - const currentFormattedDate = moment() - .tz('America/Los_Angeles') - .format(); + const currentFormattedDate = moment().tz("America/Los_Angeles").format(); logger.logInfo( - `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}`, + `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}` ); try { const users = await userProfile.find( { isActive: false, reactivationDate: { $exists: true } }, - '_id isActive reactivationDate', + "_id isActive reactivationDate" ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -705,18 +717,22 @@ const userHelper = function () { endDate: user.endDate, }, }, - { new: true }, + { new: true } ); logger.logInfo( `User with id: ${user._id} was re-acticated at ${moment() - .tz('America/Los_Angeles') - .format()}.`, + .tz("America/Los_Angeles") + .format()}.` ); const id = user._id; const person = await userProfile.findById(id); - const endDate = moment(person.endDate).format('YYYY-MM-DD'); - logger.logInfo(`User with id: ${user._id} was re-acticated at ${moment().format()}.`); + const endDate = moment(person.endDate).format("YYYY-MM-DD"); + logger.logInfo( + `User with id: ${ + user._id + } was re-acticated at ${moment().format()}.` + ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been RE-activated in the Highest Good Network`; @@ -730,8 +746,14 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; - - emailSender('onecommunityglobal@gmail.com', subject, emailBody, null, null); + emailSender( + "onecommunityglobal@gmail.com", + subject, + emailBody, + null, + null, + person.email + ); } } } catch (err) { @@ -739,59 +761,74 @@ const userHelper = function () { } }; - - const notifyInfringements = function (original, current, firstName, lastName, emailAddress) { + const notifyInfringements = function ( + original, + current, + firstName, + lastName, + emailAddress + ) { if (!current) return; const newOriginal = original.toObject(); const newCurrent = current.toObject(); const totalInfringements = newCurrent.length; let newInfringements = []; - newInfringements = _.differenceWith(newCurrent, newOriginal, (arrVal, othVal) => arrVal._id.equals(othVal._id)); + newInfringements = _.differenceWith( + newCurrent, + newOriginal, + (arrVal, othVal) => arrVal._id.equals(othVal._id) + ); newInfringements.forEach((element) => { emailSender( emailAddress, - 'New Infringement Assigned', - getInfringementEmailBody(firstName, lastName, element, totalInfringements), + "New Infringement Assigned", + getInfringementEmailBody( + firstName, + lastName, + element, + totalInfringements + ), null, - 'onecommunityglobal@gmail.com', + "onecommunityglobal@gmail.com", + emailAddress ); }); }; const replaceBadge = async function (personId, oldBadgeId, newBadgeId) { userProfile.updateOne( - { _id: personId, 'badgeCollection.badge': oldBadgeId }, + { _id: personId, "badgeCollection.badge": oldBadgeId }, { $set: { - 'badgeCollection.$.badge': newBadgeId, - 'badgeCollection.$.lastModified': Date.now().toString(), - 'badgeCollection.$.count': 1, + "badgeCollection.$.badge": newBadgeId, + "badgeCollection.$.lastModified": Date.now().toString(), + "badgeCollection.$.count": 1, }, }, (err) => { if (err) { throw new Error(err); } - }, + } ); }; const increaseBadgeCount = async function (personId, badgeId) { - console.log('Increase Badge Count', personId, badgeId); + console.log("Increase Badge Count", personId, badgeId); userProfile.updateOne( - { _id: personId, 'badgeCollection.badge': badgeId }, + { _id: personId, "badgeCollection.badge": badgeId }, { - $inc: { 'badgeCollection.$.count': 1 }, - $set: { 'badgeCollection.$.lastModified': Date.now().toString() }, - $push: { 'badgeCollection.$.earnedDate': earnedDateBadge() }, + $inc: { "badgeCollection.$.count": 1 }, + $set: { "badgeCollection.$.lastModified": Date.now().toString() }, + $push: { "badgeCollection.$.earnedDate": earnedDateBadge() }, }, (err) => { if (err) { console.log(err); } - }, + } ); }; @@ -799,15 +836,19 @@ const userHelper = function () { personId, badgeId, count = 1, - featured = false, + featured = false ) { - console.log('Adding Badge'); + console.log("Adding Badge"); userProfile.findByIdAndUpdate( personId, { $push: { badgeCollection: { - badge: badgeId, count, earnedDate: [earnedDateBadge()], featured, lastModified: Date.now().toString(), + badge: badgeId, + count, + earnedDate: [earnedDateBadge()], + featured, + lastModified: Date.now().toString(), }, }, }, @@ -815,7 +856,7 @@ const userHelper = function () { if (err) { throw new Error(err); } - }, + } ); }; @@ -831,7 +872,7 @@ const userHelper = function () { if (err) { throw new Error(err); } - }, + } ); }; @@ -840,25 +881,31 @@ const userHelper = function () { removeDupBadge(personId, badgeId); } else if (count) { userProfile.updateOne( - { _id: personId, 'badgeCollection.badge': badgeId }, + { _id: personId, "badgeCollection.badge": badgeId }, { $set: { - 'badgeCollection.$.count': count, - 'badgeCollection.$.lastModified': Date.now().toString(), + "badgeCollection.$.count": count, + "badgeCollection.$.lastModified": Date.now().toString(), }, }, (err) => { if (err) { throw new Error(err); } - }, + } ); } }; // remove the last badge you earned on this streak(not including 1) - const removePrevHrBadge = async function (personId, user, badgeCollection, hrs, weeks) { + const removePrevHrBadge = async function ( + personId, + user, + badgeCollection, + hrs, + weeks + ) { // Check each Streak Greater than One to check if it works if (weeks < 3) { return; @@ -868,7 +915,7 @@ const userHelper = function () { .aggregate([ { $match: { - type: 'X Hours for X Week Streak', + type: "X Hours for X Week Streak", weeks: { $gt: 1, $lt: weeks }, totalHrs: hrs, }, @@ -876,9 +923,9 @@ const userHelper = function () { { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: '$weeks', + _id: "$weeks", badges: { - $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, + $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, }, }, }, @@ -888,17 +935,16 @@ const userHelper = function () { streak.badges.every((bdge) => { for (let i = 0; i < badgeCollection.length; i += 1) { if ( - - badgeCollection[i].badge?.type === 'X Hours for X Week Streak' - && badgeCollection[i].badge?.weeks === bdge.weeks - && bdge.hrs === hrs - && !removed - + badgeCollection[i].badge?.type === + "X Hours for X Week Streak" && + badgeCollection[i].badge?.weeks === bdge.weeks && + bdge.hrs === hrs && + !removed ) { changeBadgeCount( personId, badgeCollection[i].badge._id, - badgeCollection[i].badge.count - 1, + badgeCollection[i].badge.count - 1 ); removed = true; return false; @@ -912,14 +958,24 @@ const userHelper = function () { // 'No Infringement Streak', - const checkNoInfringementStreak = async function (personId, user, badgeCollection) { + const checkNoInfringementStreak = async function ( + personId, + user, + badgeCollection + ) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'No Infringement Streak') { - if (badgeOfType && badgeOfType.months <= badgeCollection[i].badge.months) { + if (badgeCollection[i].badge?.type === "No Infringement Streak") { + if ( + badgeOfType && + badgeOfType.months <= badgeCollection[i].badge.months + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; - } else if (badgeOfType && badgeOfType.months > badgeCollection[i].badge.months) { + } else if ( + badgeOfType && + badgeOfType.months > badgeCollection[i].badge.months + ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -927,7 +983,7 @@ const userHelper = function () { } } await badge - .find({ type: 'No Infringement Streak' }) + .find({ type: "No Infringement Streak" }) .sort({ months: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -938,17 +994,20 @@ const userHelper = function () { // Cannot account for time paused yet if (elem.months <= 12) { - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { + if ( + moment().diff(moment(user.createdDate), "months", true) >= + elem.months + ) { if ( - user.infringements.length === 0 - || Math.abs( + user.infringements.length === 0 || + Math.abs( moment().diff( - - moment(user.infringements[user.infringements?.length - 1].date), - 'months', - true, - ), - + moment( + user.infringements[user.infringements?.length - 1].date + ), + "months", + true + ) ) >= elem.months ) { if (badgeOfType) { @@ -956,7 +1015,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id), + mongoose.Types.ObjectId(elem._id) ); } return false; @@ -966,26 +1025,30 @@ const userHelper = function () { } } } else if (user?.infringements?.length === 0) { - if (moment().diff(moment(user.createdDate), 'months', true) >= elem.months) { + if ( + moment().diff(moment(user.createdDate), "months", true) >= + elem.months + ) { if ( - user.oldInfringements.length === 0 - || Math.abs( + user.oldInfringements.length === 0 || + Math.abs( moment().diff( - - moment(user.oldInfringements[user.oldInfringements?.length - 1].date), - 'months', - true, - ), - ) - >= elem.months - 12 - + moment( + user.oldInfringements[user.oldInfringements?.length - 1] + .date + ), + "months", + true + ) + ) >= + elem.months - 12 ) { if (badgeOfType) { if (badgeOfType._id.toString() !== elem._id.toString()) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id), + mongoose.Types.ObjectId(elem._id) ); } return false; @@ -1004,13 +1067,13 @@ const userHelper = function () { const checkMinHoursMultiple = async function ( personId, user, - badgeCollection, + badgeCollection ) { const badgesOfType = badgeCollection - .map(obj => obj.badge) - .filter(badge => badge.type === 'Minimum Hours Multiple'); + .map((obj) => obj.badge) + .filter((badge) => badge.type === "Minimum Hours Multiple"); await badge - .find({ type: 'Minimum Hours Multiple' }) + .find({ type: "Minimum Hours Multiple" }) .sort({ multiple: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -1021,17 +1084,17 @@ const userHelper = function () { const elem = results[i]; // making variable elem accessible for below code if ( - user.lastWeekTangibleHrs / user.weeklycommittedHours - >= elem.multiple + user.lastWeekTangibleHrs / user.weeklycommittedHours >= + elem.multiple ) { const theBadge = badgesOfType.find( - badge => badge._id.toString() === elem._id.toString(), + (badge) => badge._id.toString() === elem._id.toString() ); return theBadge ? increaseBadgeCount( - personId, - mongoose.Types.ObjectId(theBadge._id), - ) + personId, + mongoose.Types.ObjectId(theBadge._id) + ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } } @@ -1042,26 +1105,30 @@ const userHelper = function () { const checkPersonalMax = async function (personId, user, badgeCollection) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'Personal Max') { + if (badgeCollection[i].badge?.type === "Personal Max") { if (badgeOfType) { removeDupBadge(personId, badgeOfType._id); } } } - await badge.findOne({ type: 'Personal Max' }).then((results) => { + await badge.findOne({ type: "Personal Max" }).then((results) => { if ( - user.lastWeekTangibleHrs - && user.lastWeekTangibleHrs >= 1 - && user.lastWeekTangibleHrs === user.personalBestMaxHrs + user.lastWeekTangibleHrs && + user.lastWeekTangibleHrs >= 1 && + user.lastWeekTangibleHrs === user.personalBestMaxHrs ) { if (badgeOfType) { changeBadgeCount( personId, mongoose.Types.ObjectId(badgeOfType._id), - user.personalBestMaxHrs, + user.personalBestMaxHrs ); } else { - addBadge(personId, mongoose.Types.ObjectId(results._id), user.personalBestMaxHrs); + addBadge( + personId, + mongoose.Types.ObjectId(results._id), + user.personalBestMaxHrs + ); } } }); @@ -1071,17 +1138,17 @@ const userHelper = function () { const checkMostHrsWeek = async function (personId, user, badgeCollection) { if ( - user.weeklycommittedHours > 0 - && user.lastWeekTangibleHrs > user.weeklycommittedHours + user.weeklycommittedHours > 0 && + user.lastWeekTangibleHrs > user.weeklycommittedHours ) { const badgeOfType = badgeCollection - .filter(object => object.badge.type === 'Most Hrs in Week') - .map(object => object.badge); - await badge.findOne({ type: 'Most Hrs in Week' }).then((results) => { + .filter((object) => object.badge.type === "Most Hrs in Week") + .map((object) => object.badge); + await badge.findOne({ type: "Most Hrs in Week" }).then((results) => { userProfile .aggregate([ { $match: { isActive: true } }, - { $group: { _id: 1, maxHours: { $max: '$lastWeekTangibleHrs' } } }, + { $group: { _id: 1, maxHours: { $max: "$lastWeekTangibleHrs" } } }, ]) .then((userResults) => { if (badgeOfType.length > 1) { @@ -1089,13 +1156,13 @@ const userHelper = function () { } if ( - user.lastWeekTangibleHrs - && user.lastWeekTangibleHrs >= userResults[0].maxHours + user.lastWeekTangibleHrs && + user.lastWeekTangibleHrs >= userResults[0].maxHours ) { if (badgeOfType.length) { increaseBadgeCount( personId, - mongoose.Types.ObjectId(badgeOfType[0]._id), + mongoose.Types.ObjectId(badgeOfType[0]._id) ); } else { addBadge(personId, mongoose.Types.ObjectId(results._id)); @@ -1111,12 +1178,12 @@ const userHelper = function () { // Handle Increasing the 1 week streak badges const badgesOfType = []; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'X Hours for X Week Streak') { + if (badgeCollection[i].badge?.type === "X Hours for X Week Streak") { badgesOfType.push(badgeCollection[i].badge); } } await badge - .find({ type: 'X Hours for X Week Streak', weeks: 1 }) + .find({ type: "X Hours for X Week Streak", weeks: 1 }) .sort({ totalHrs: -1 }) .then((results) => { results.every((elem) => { @@ -1141,13 +1208,13 @@ const userHelper = function () { // Check each Streak Greater than One to check if it works await badge .aggregate([ - { $match: { type: 'X Hours for X Week Streak', weeks: { $gt: 1 } } }, + { $match: { type: "X Hours for X Week Streak", weeks: { $gt: 1 } } }, { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: '$weeks', + _id: "$weeks", badges: { - $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, + $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, }, }, }, @@ -1159,16 +1226,19 @@ const userHelper = function () { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - - badgeCollection[i].badge?.type === 'X Hours for X Week Streak' - && badgeCollection[i].badge?.weeks === bdge.weeks + badgeCollection[i].badge?.type === + "X Hours for X Week Streak" && + badgeCollection[i].badge?.weeks === bdge.weeks ) { - if (badgeOfType && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs) { + if ( + badgeOfType && + badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType - && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType && + badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1193,10 +1263,16 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(bdge._id), + mongoose.Types.ObjectId(bdge._id) ); - removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); + removePrevHrBadge( + personId, + user, + badgeCollection, + bdge.hrs, + bdge.weeks + ); } else if (!badgeOfType) { addBadge(personId, mongoose.Types.ObjectId(bdge._id)); removePrevHrBadge( @@ -1204,11 +1280,20 @@ const userHelper = function () { user, badgeCollection, bdge.hrs, - bdge.weeks, + bdge.weeks ); } else if (badgeOfType && badgeOfType.totalHrs === bdge.hrs) { - increaseBadgeCount(personId, mongoose.Types.ObjectId(badgeOfType._id)); - removePrevHrBadge(personId, user, badgeCollection, bdge.hrs, bdge.weeks); + increaseBadgeCount( + personId, + mongoose.Types.ObjectId(badgeOfType._id) + ); + removePrevHrBadge( + personId, + user, + badgeCollection, + bdge.hrs, + bdge.weeks + ); } return false; } @@ -1221,10 +1306,19 @@ const userHelper = function () { // 'Lead a team of X+' - - const checkLeadTeamOfXplus = async function (personId, user, badgeCollection) { - const leaderRoles = ['Mentor', 'Manager', 'Administrator', 'Owner', 'Core Team']; - const approvedRoles = ['Mentor', 'Manager']; + const checkLeadTeamOfXplus = async function ( + personId, + user, + badgeCollection + ) { + const leaderRoles = [ + "Mentor", + "Manager", + "Administrator", + "Owner", + "Core Team", + ]; + const approvedRoles = ["Mentor", "Manager"]; if (!approvedRoles.includes(user.role)) return; let teamMembers; @@ -1249,11 +1343,17 @@ const userHelper = function () { }); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === 'Lead a team of X+') { - if (badgeOfType && badgeOfType.people <= badgeCollection[i].badge.people) { + if (badgeCollection[i].badge?.type === "Lead a team of X+") { + if ( + badgeOfType && + badgeOfType.people <= badgeCollection[i].badge.people + ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; - } else if (badgeOfType && badgeOfType.people > badgeCollection[i].badge.people) { + } else if ( + badgeOfType && + badgeOfType.people > badgeCollection[i].badge.people + ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { badgeOfType = badgeCollection[i].badge; @@ -1261,7 +1361,7 @@ const userHelper = function () { } } await badge - .find({ type: 'Lead a team of X+' }) + .find({ type: "Lead a team of X+" }) .sort({ people: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -1271,16 +1371,14 @@ const userHelper = function () { if (teamMembers && teamMembers.length >= badge.people) { if (badgeOfType) { if ( - badgeOfType._id.toString() !== badge._id.toString() - && badgeOfType.people < badge.people - + badgeOfType._id.toString() !== badge._id.toString() && + badgeOfType.people < badge.people ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(badge._id), - + mongoose.Types.ObjectId(badge._id) ); } return false; @@ -1297,40 +1395,39 @@ const userHelper = function () { const checkTotalHrsInCat = async function (personId, user, badgeCollection) { const hoursByCategory = user.hoursByCategory || {}; const categories = [ - 'food', - 'energy', - 'housing', - 'education', - 'society', - 'economics', - 'stewardship', + "food", + "energy", + "housing", + "education", + "society", + "economics", + "stewardship", ]; const badgesOfType = badgeCollection - .filter(object => object.badge.type === 'Total Hrs in Category') - .map(object => object.badge); + .filter((object) => object.badge.type === "Total Hrs in Category") + .map((object) => object.badge); categories.forEach(async (category) => { const categoryHrs = Object.keys(hoursByCategory).find( - elem => elem === category, + (elem) => elem === category ); - let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - badgeCollection[i].badge?.type === 'Total Hrs in Category' - && badgeCollection[i].badge?.category === category + badgeCollection[i].badge?.type === "Total Hrs in Category" && + badgeCollection[i].badge?.category === category ) { if ( - badgeOfType - && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + badgeOfType && + badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType - && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType && + badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1341,8 +1438,8 @@ const userHelper = function () { const newCatg = category.charAt(0).toUpperCase() + category.slice(1); - - await badge.find({ type: 'Total Hrs in Category', category: newCatg }) + await badge + .find({ type: "Total Hrs in Category", category: newCatg }) .sort({ totalHrs: -1 }) .then((results) => { @@ -1352,8 +1449,8 @@ const userHelper = function () { results.every((elem) => { if ( - hoursByCategory[categoryHrs] >= 100 - && hoursByCategory[categoryHrs] >= elem.totalHrs + hoursByCategory[categoryHrs] >= 100 && + hoursByCategory[categoryHrs] >= elem.totalHrs ) { let theBadge; for (let i = 0; i < badgesOfType.length; i += 1) { @@ -1368,13 +1465,13 @@ const userHelper = function () { } if (badgeOfType) { if ( - badgeOfType._id.toString() !== elem._id.toString() - && badgeOfType.totalHrs < elem.totalHrs + badgeOfType._id.toString() !== elem._id.toString() && + badgeOfType.totalHrs < elem.totalHrs ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id), + mongoose.Types.ObjectId(elem._id) ); } return false; @@ -1389,10 +1486,11 @@ const userHelper = function () { }; const awardNewBadges = async () => { - console.log('Awarding'); + console.log("Awarding"); try { - const users = await userProfile.find({ isActive: true }).populate('badgeCollection.badge'); - + const users = await userProfile + .find({ isActive: true }) + .populate("badgeCollection.badge"); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -1414,9 +1512,14 @@ const userHelper = function () { const getTangibleHoursReportedThisWeekByUserId = function (personId) { const userId = mongoose.Types.ObjectId(personId); - const pdtstart = moment().tz('America/Los_Angeles').startOf('week').format('YYYY-MM-DD'); - const pdtend = moment().tz('America/Los_Angeles').endOf('week').format('YYYY-MM-DD'); - + const pdtstart = moment() + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); + const pdtend = moment() + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return timeEntries .find( @@ -1425,12 +1528,12 @@ const userHelper = function () { dateOfWork: { $gte: pdtstart, $lte: pdtend }, isTangible: true, }, - 'totalSeconds', + "totalSeconds" ) .then((results) => { const totalTangibleWeeklySeconds = results.reduce( (acc, { totalSeconds }) => acc + totalSeconds, - 0, + 0 ); return (totalTangibleWeeklySeconds / 3600).toFixed(2); }); @@ -1440,25 +1543,29 @@ const userHelper = function () { try { const users = await userProfile.find( { isActive: true, endDate: { $exists: true } }, - '_id isActive endDate', + "_id isActive endDate" ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; const { endDate } = user; endDate.setHours(endDate.getHours() + 7); - if (moment().isAfter(moment(endDate).add(1, 'days'))) { + if (moment().isAfter(moment(endDate).add(1, "days"))) { await userProfile.findByIdAndUpdate( user._id, user.set({ isActive: false, }), - { new: true }, + { new: true } ); const id = user._id; const person = await userProfile.findById(id); - const lastDay = moment(person.endDate).format('YYYY-MM-DD'); - logger.logInfo(`User with id: ${user._id} was de-acticated at ${moment().format()}.`); + const lastDay = moment(person.endDate).format("YYYY-MM-DD"); + logger.logInfo( + `User with id: ${ + user._id + } was de-acticated at ${moment().format()}.` + ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been deactivated in the Highest Good Network`; @@ -1472,8 +1579,14 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; - - emailSender('onecommunityglobal@gmail.com', subject, emailBody, null, null); + emailSender( + "onecommunityglobal@gmail.com", + subject, + emailBody, + null, + null, + person.email + ); } } } catch (err) { @@ -1491,7 +1604,6 @@ const userHelper = function () { } }; - return { getUserName, getTeamMembers, @@ -1510,4 +1622,4 @@ const userHelper = function () { }; }; -module.exports = userHelper; +module.exports = userHelper; \ No newline at end of file diff --git a/src/startup/routes.js b/src/startup/routes.js index 1713d6a67..8b8cf98f4 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -20,9 +20,11 @@ const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); -const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); +const inventoryItemMaterial = require('../models/inventoryItemMaterial'); +const mapLocations = require('../models/mapLocation'); const buildingProject = require('../models/bmdashboard/buildingProject'); const buildingInventoryType = require('../models/bmdashboard/buildingInventoryType'); +const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); @@ -58,10 +60,13 @@ const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); +const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); + // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); -const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(buildingMaterial); +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial, buildingMaterial); const bmProjectRouter = require('../routes/bmdashboard/bmProjectRouter')(buildingProject); +const bmInventoryTypeRouter = require('../routes/bmdashboard/bmInventoryTypeRouter')(buildingInventoryType); module.exports = function (app) { app.use('/api', forgotPwdRouter); @@ -93,8 +98,10 @@ module.exports = function (app) { app.use('/api', informationRouter); app.use('/api', mouseoverTextRouter); app.use('/api', isEmailExistsRouter); + app.use('/api', mapLocationRouter); // bm dashboard app.use('/api/bm', bmLoginRouter); - app.use('/api/bm', bmProjectRouter); app.use('/api/bm', bmMaterialsRouter); -}; + app.use('/api/bm', bmProjectRouter); + app.use('/api/bm', bmInventoryTypeRouter); +}; \ No newline at end of file From 40e044a474dd2ca6be9a517ac4963ebb0d09acf6 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Fri, 1 Dec 2023 12:17:24 -0800 Subject: [PATCH 078/129] update fetchAllProjects func to populate itemType field --- src/controllers/bmdashboard/bmProjectController.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/controllers/bmdashboard/bmProjectController.js b/src/controllers/bmdashboard/bmProjectController.js index 8ca62ea77..b6b3e4e18 100644 --- a/src/controllers/bmdashboard/bmProjectController.js +++ b/src/controllers/bmdashboard/bmProjectController.js @@ -36,6 +36,17 @@ const bmMProjectController = function (BuildingProject) { pipeline: [ { $match: { $expr: { $eq: ['$project', '$$id'] } } }, { $project: { updateRecord: 0, project: 0 } }, + { + $lookup: { + from: 'buildingInventoryTypes', + localField: 'itemType', + foreignField: '_id', + as: 'itemType', + }, + }, + { + $unwind: '$itemType', + }, ], as: 'materials', }, @@ -63,7 +74,6 @@ const bmMProjectController = function (BuildingProject) { proj.mostMaterialWaste = proj.materials.sort((a, b) => b.stockWasted - a.stockWasted)[0]; proj.leastMaterialAvailable = proj.materials.sort((a, b) => a.stockAvailable - b.stockAvailable)[0]; proj.mostMaterialBought = proj.materials.sort((a, b) => b.stockBought - a.stockBought)[0]; - proj.teamCount = proj.teams.length; }); res.status(200).send(results); }) From f67d2b46f3604ce625dfaeff447d5ae338749310 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Fri, 1 Dec 2023 17:15:06 -0500 Subject: [PATCH 079/129] Cron job - change main locations --- src/cronjobs/userProfileJobs.js | 2 +- src/helpers/userHelper.js | 104 ++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/cronjobs/userProfileJobs.js b/src/cronjobs/userProfileJobs.js index f3903c057..1e4c6392a 100644 --- a/src/cronjobs/userProfileJobs.js +++ b/src/cronjobs/userProfileJobs.js @@ -18,12 +18,12 @@ const userProfileJobs = () => { await userhelper.awardNewBadges(); await userhelper.reActivateUser(); await userhelper.deActivateUser(); + await userhelper.oneTimeLocationUpdate(); }, null, false, 'America/Los_Angeles', ); - allUserProfileJobs.start(); }; module.exports = userProfileJobs; diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 8bf04eb5f..1e349245c 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -105,8 +105,8 @@ const userHelper = function () {

Date Assigned: ${infringement.date}

Description: ${infringement.description}

Total Infringements: This is your ${moment - .localeData() - .ordinal(totalInfringements)} blue square of 5.

+ .localeData() + .ordinal(totalInfringements)} blue square of 5.

${final_paragraph}

Thank you,
One Community

`; @@ -197,8 +197,8 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz("America/Los_Angeles") - .format("YYYY-MMM-DD")}): + .tz("America/Los_Angeles") + .format("YYYY-MMM-DD")}):
${summary} @@ -220,24 +220,24 @@ const userHelper = function () { Media URL: ${ mediaUrlLink || 'Not provided!' - } + }

${ weeklySummariesCount === 8 - ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` - : `

Total Valid Weekly Summaries: ${ + ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` + : `

Total Valid Weekly Summaries: ${ weeklySummariesCount || "No valid submissions yet!" - }

` + }

` } ${ hoursLogged >= weeklycommittedHours - ? `

Hours logged: ${hoursLogged.toFixed( - 2 - )} / ${weeklycommittedHours}

` - : `

Hours logged: ${hoursLogged.toFixed( - 2 - )} / ${weeklycommittedHours}

` + ? `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` + : `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage}
`; @@ -298,6 +298,69 @@ const userHelper = function () { }) .catch((error) => logger.logException(error)); }; + async function wait(ms) { + return new Promise((r) => setTimeout(r, ms)); + } + const oneTimeLocationUpdate = async () => { + const users = await userProfile.find({}, '_id'); + + for (let i = 0; i < users.length; i += 1) { + const user = users[i]; + const person = await userProfile.findById(user._id); + if (!person.location.coords && !person.location.country && !person.location.city && !person.location.userProvided) { + const personLoc = person.location || ''; + let location; + if (personLoc) { + try { + const res = await fetch(`https://api.opencagedata.com/geocode/v1/json?key=d093a5e0eee34aea8043f4e6edb0e9f7&q=${encodeURIComponent(personLoc)}&pretty=1&limit=1`); + if (!res) { + throw new Error(); + } else { + const data = await res.json(); + location = { + userProvided: personLoc || '', + coords: { + lat: data.results[0].geometry.lat || '', + lng: data.results[0].geometry.lng || '', + }, + country: data.results[0].components.country || '', + city: data.results[0].components.city || '', + }; + } + } catch (err) { + console.log(err); + location = { + userProvided: personLoc, + coords: { + lat: 'err', + lng: '', + }, + country: '', + city: '', + } + } + } else { + location = { + userProvided: personLoc || '', + coords: { + lat: '', + lng: '', + }, + country: '', + city: '', + }; + } + await userProfile.findOneAndUpdate({ _id: user._id }, { + $set: { + location, + }, + }); + } + + await wait(2000); + } + }; + /** * This function is called by a cron job to do 3 things to all active users: @@ -936,7 +999,7 @@ const userHelper = function () { for (let i = 0; i < badgeCollection.length; i += 1) { if ( badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && + "X Hours for X Week Streak" && badgeCollection[i].badge?.weeks === bdge.weeks && bdge.hrs === hrs && !removed @@ -1041,7 +1104,7 @@ const userHelper = function () { true ) ) >= - elem.months - 12 + elem.months - 12 ) { if (badgeOfType) { if (badgeOfType._id.toString() !== elem._id.toString()) { @@ -1092,9 +1155,9 @@ const userHelper = function () { ); return theBadge ? increaseBadgeCount( - personId, - mongoose.Types.ObjectId(theBadge._id) - ) + personId, + mongoose.Types.ObjectId(theBadge._id) + ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } } @@ -1227,7 +1290,7 @@ const userHelper = function () { for (let i = 0; i < badgeCollection.length; i += 1) { if ( badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && + "X Hours for X Week Streak" && badgeCollection[i].badge?.weeks === bdge.weeks ) { if ( @@ -1619,6 +1682,7 @@ const userHelper = function () { awardNewBadges, getTangibleHoursReportedThisWeekByUserId, deleteExpiredTokens, + oneTimeLocationUpdate, }; }; From 37dbad95613343a6f7dc0b7c58d29f53521f0a60 Mon Sep 17 00:00:00 2001 From: Nathan Hoffman Date: Fri, 1 Dec 2023 17:12:06 -0800 Subject: [PATCH 080/129] Fix end anchor symbol in escapeRegex --- src/utilities/escapeRegex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/escapeRegex.js b/src/utilities/escapeRegex.js index 01a65ea50..cf7563e26 100644 --- a/src/utilities/escapeRegex.js +++ b/src/utilities/escapeRegex.js @@ -1,6 +1,6 @@ const escapeRegex = function (text) { - return `^${text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&')}&`; + return `^${text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&')}$`; }; module.exports = escapeRegex; From 051cd4bc1bc1b920e52183b8f1b752f3ec1b268d Mon Sep 17 00:00:00 2001 From: One Community Date: Sat, 2 Dec 2023 00:19:36 -0800 Subject: [PATCH 081/129] Revert "Cron job - change main locations" --- src/cronjobs/userProfileJobs.js | 2 +- src/helpers/userHelper.js | 104 ++++++-------------------------- 2 files changed, 21 insertions(+), 85 deletions(-) diff --git a/src/cronjobs/userProfileJobs.js b/src/cronjobs/userProfileJobs.js index 1e4c6392a..f3903c057 100644 --- a/src/cronjobs/userProfileJobs.js +++ b/src/cronjobs/userProfileJobs.js @@ -18,12 +18,12 @@ const userProfileJobs = () => { await userhelper.awardNewBadges(); await userhelper.reActivateUser(); await userhelper.deActivateUser(); - await userhelper.oneTimeLocationUpdate(); }, null, false, 'America/Los_Angeles', ); + allUserProfileJobs.start(); }; module.exports = userProfileJobs; diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 1e349245c..8bf04eb5f 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -105,8 +105,8 @@ const userHelper = function () {

Date Assigned: ${infringement.date}

Description: ${infringement.description}

Total Infringements: This is your ${moment - .localeData() - .ordinal(totalInfringements)} blue square of 5.

+ .localeData() + .ordinal(totalInfringements)} blue square of 5.

${final_paragraph}

Thank you,
One Community

`; @@ -197,8 +197,8 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz("America/Los_Angeles") - .format("YYYY-MMM-DD")}): + .tz("America/Los_Angeles") + .format("YYYY-MMM-DD")}):
${summary} @@ -220,24 +220,24 @@ const userHelper = function () { Media URL: ${ mediaUrlLink || 'Not provided!' - } + }

${ weeklySummariesCount === 8 - ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` - : `

Total Valid Weekly Summaries: ${ + ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` + : `

Total Valid Weekly Summaries: ${ weeklySummariesCount || "No valid submissions yet!" - }

` + }

` } ${ hoursLogged >= weeklycommittedHours - ? `

Hours logged: ${hoursLogged.toFixed( - 2 - )} / ${weeklycommittedHours}

` - : `

Hours logged: ${hoursLogged.toFixed( - 2 - )} / ${weeklycommittedHours}

` + ? `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` + : `

Hours logged: ${hoursLogged.toFixed( + 2 + )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage}
`; @@ -298,69 +298,6 @@ const userHelper = function () { }) .catch((error) => logger.logException(error)); }; - async function wait(ms) { - return new Promise((r) => setTimeout(r, ms)); - } - const oneTimeLocationUpdate = async () => { - const users = await userProfile.find({}, '_id'); - - for (let i = 0; i < users.length; i += 1) { - const user = users[i]; - const person = await userProfile.findById(user._id); - if (!person.location.coords && !person.location.country && !person.location.city && !person.location.userProvided) { - const personLoc = person.location || ''; - let location; - if (personLoc) { - try { - const res = await fetch(`https://api.opencagedata.com/geocode/v1/json?key=d093a5e0eee34aea8043f4e6edb0e9f7&q=${encodeURIComponent(personLoc)}&pretty=1&limit=1`); - if (!res) { - throw new Error(); - } else { - const data = await res.json(); - location = { - userProvided: personLoc || '', - coords: { - lat: data.results[0].geometry.lat || '', - lng: data.results[0].geometry.lng || '', - }, - country: data.results[0].components.country || '', - city: data.results[0].components.city || '', - }; - } - } catch (err) { - console.log(err); - location = { - userProvided: personLoc, - coords: { - lat: 'err', - lng: '', - }, - country: '', - city: '', - } - } - } else { - location = { - userProvided: personLoc || '', - coords: { - lat: '', - lng: '', - }, - country: '', - city: '', - }; - } - await userProfile.findOneAndUpdate({ _id: user._id }, { - $set: { - location, - }, - }); - } - - await wait(2000); - } - }; - /** * This function is called by a cron job to do 3 things to all active users: @@ -999,7 +936,7 @@ const userHelper = function () { for (let i = 0; i < badgeCollection.length; i += 1) { if ( badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && + "X Hours for X Week Streak" && badgeCollection[i].badge?.weeks === bdge.weeks && bdge.hrs === hrs && !removed @@ -1104,7 +1041,7 @@ const userHelper = function () { true ) ) >= - elem.months - 12 + elem.months - 12 ) { if (badgeOfType) { if (badgeOfType._id.toString() !== elem._id.toString()) { @@ -1155,9 +1092,9 @@ const userHelper = function () { ); return theBadge ? increaseBadgeCount( - personId, - mongoose.Types.ObjectId(theBadge._id) - ) + personId, + mongoose.Types.ObjectId(theBadge._id) + ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } } @@ -1290,7 +1227,7 @@ const userHelper = function () { for (let i = 0; i < badgeCollection.length; i += 1) { if ( badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && + "X Hours for X Week Streak" && badgeCollection[i].badge?.weeks === bdge.weeks ) { if ( @@ -1682,7 +1619,6 @@ const userHelper = function () { awardNewBadges, getTangibleHoursReportedThisWeekByUserId, deleteExpiredTokens, - oneTimeLocationUpdate, }; }; From 081c1bba112e3c603d582ef6642dc1dae6b7889b Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 5 Dec 2023 11:38:48 -0800 Subject: [PATCH 082/129] add records subdocuments --- src/models/bmdashboard/buildingTool.js | 38 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/models/bmdashboard/buildingTool.js b/src/models/bmdashboard/buildingTool.js index 6cec3b5d0..95be0c4d5 100644 --- a/src/models/bmdashboard/buildingTool.js +++ b/src/models/bmdashboard/buildingTool.js @@ -2,15 +2,35 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; -const buildingTools = new Schema({ +const buildingTool = new Schema({ itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, - title: { type: String, required: true }, - rentedOnDate: { type: Date, required: true }, - rentDuration: { type: Number, required: true }, // This value should be taken in number of days - logInStatus:{Boolean}, - condition:{type: String, enum: ['Good', 'Needs Repair', 'Out of Order','Unused'], default: 'Good'}, - userResponsible:{ type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - purchaseStatus:{type: String, enum: ['Rented', 'Purchased'], default: 'Rented'}, + code: { type: Number, required: true }, // add function to create code for on-site tool tracking + purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, + // add discriminator based on rental or purchase so these fields are required if tool is rented + rentedOnDate: Date, + rentalDue: Date, + userResponsible: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + purchaseRecord: [{ // track purchase/rental requests + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brand: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], + updateRecord: [{ // track tool condition updates + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, + }], + logRecord: [{ // track tool daily check in/out and use + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + type: { type: String, enum: ['Check In', 'Check Out'] }, // default = opposite of current log status? + }], }); -module.exports = mongoose.model('buildingTools', buildingTools, 'buildingTools'); \ No newline at end of file +module.exports = mongoose.model('buildingTool', buildingTool, 'buildingTools'); From 35b1b6c36220b8af1a83194b7923cf7f301f2a5d Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 6 Dec 2023 08:44:49 -0800 Subject: [PATCH 083/129] add new model template --- src/models/bmdashboard/buildingReusable.js | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/models/bmdashboard/buildingReusable.js diff --git a/src/models/bmdashboard/buildingReusable.js b/src/models/bmdashboard/buildingReusable.js new file mode 100644 index 000000000..1d0503010 --- /dev/null +++ b/src/models/bmdashboard/buildingReusable.js @@ -0,0 +1,29 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingReusable = new Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + stockBought: { type: Number, default: 0 }, + stockDestroyed: { type: Number, default: 0 }, + stockAvailable: { type: Number, default: 0 }, + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: { type: Number, required: true }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brand: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], + updateRecord: [{ + _id: false, + date: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: { type: Number, required: true }, + quantityDestroyed: { type: Number, required: true }, + }], +}); + +module.exports = mongoose.model('buildingReusable', buildingReusable, 'buildingReusables'); From 0976b730a58bb2f6c0a21d13d15e45df3861c672 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 6 Dec 2023 09:43:10 -0800 Subject: [PATCH 084/129] removed mongo-round package --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 32d23ee6a..1c6b8a5d4 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.35", - "mongo-round": "^1.0.0", "mongodb": "^3.7.3", "mongoose": "^5.13.15", "mongoose-validator": "^2.1.0", From 9e5ae9875eb5422b5d6ec260d5617ab314f905a4 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 6 Dec 2023 20:01:06 -0800 Subject: [PATCH 085/129] experimenting with discriminators --- .../bmdashboard/bmMaterialsController.js | 12 ++++- src/models/bmdashboard/buildingMaterial.js | 49 +++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index a8090ebd9..c3dcd8c69 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -47,6 +47,8 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { }; const bmPurchaseMaterials = async function (req, res) { + console.log(BuildingMaterial); + console.log(req.body); const { projectId, matTypeId, @@ -83,7 +85,10 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { }; BuildingMaterial .create(newDoc) - .then(() => res.status(201).send()) + .then((result) => { + console.log('result new: ', result); + res.status(201).send(); + }) .catch(error => res.status(500).send(error)); return; } @@ -93,7 +98,10 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { { $push: { purchaseRecord: newPurchaseRecord } }, ) .exec() - .then(() => res.status(201).send()) + .then((result) => { + console.log('result old: ', result); + res.status(201).send(); + }) .catch(error => res.status(500).send(error)); } catch (error) { res.status(500).send(error); diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index 4170443e0..d9a6be194 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -2,7 +2,14 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; -const buildingMaterial = new Schema({ +const inventoryBaseSchema = new Schema({ + testField1: { type: String, default: 'hello world' }, + testField2: { type: Number, default: 101 }, +}); + +const InvBase = mongoose.model('InvBase', inventoryBaseSchema); + +const buildingMaterial = InvBase.discriminator('buildingMaterial', new Schema({ itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project @@ -25,6 +32,42 @@ const buildingMaterial = new Schema({ quantityUsed: { type: Number, required: true }, quantityWasted: { type: Number, required: true }, }], -}); +})); + +// common fields +const eventDefinition = { time: Date } +// specific fields +const ClickedLinkEventDefinition = {...eventDefinition, url: String} + +// completely separate models and collections on db level +const eventSchema = new mongoose.Schema(eventDefinition, options); +const Event = mongoose.model('Event', eventSchema); + +const ClickedLinkEvent = new mongoose.Schema(ClickedLinkEventDefinition , options); + +// const buildingMaterial = new Schema({ +// itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, +// project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, +// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project +// stockUsed: { type: Number, default: 0 }, // total amount of item used successfully in the project +// stockWasted: { type: Number, default: 0 }, // total amount of item wasted/ruined/lost in the project +// stockAvailable: { type: Number, default: 0 }, // bought - (used + wasted) +// purchaseRecord: [{ +// _id: false, // do not add _id field to subdocument +// date: { type: Date, default: Date.now() }, +// requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantity: { type: Number, required: true }, +// priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, +// brand: String, +// status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, +// }], +// updateRecord: [{ +// _id: false, +// date: { type: Date, required: true }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantityUsed: { type: Number, required: true }, +// quantityWasted: { type: Number, required: true }, +// }], +// }); -module.exports = mongoose.model('buildingMaterial', buildingMaterial, 'buildingMaterials'); +module.exports = buildingMaterial; From d54cc867e1efe980f5bfcc955ddfff34d5414802 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Thu, 7 Dec 2023 16:57:12 -0800 Subject: [PATCH 086/129] add baseInvSchema file, update material and reusable schemas with discriminators --- .../bmdashboard/bmMaterialsController.js | 14 +--- src/models/bmdashboard/baseInvSchema.js | 23 +++++++ src/models/bmdashboard/buildingMaterial.js | 66 +++---------------- src/models/bmdashboard/buildingReusable.js | 22 ++----- 4 files changed, 41 insertions(+), 84 deletions(-) create mode 100644 src/models/bmdashboard/baseInvSchema.js diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index c3dcd8c69..c79cbb56c 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -47,8 +47,6 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { }; const bmPurchaseMaterials = async function (req, res) { - console.log(BuildingMaterial); - console.log(req.body); const { projectId, matTypeId, @@ -60,7 +58,7 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { const newPurchaseRecord = { quantity, priority, - brand, + brandPref: brand, requestedBy: requestorId, }; try { @@ -85,10 +83,7 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { }; BuildingMaterial .create(newDoc) - .then((result) => { - console.log('result new: ', result); - res.status(201).send(); - }) + .then(() => res.status(201).send()) .catch(error => res.status(500).send(error)); return; } @@ -98,10 +93,7 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { { $push: { purchaseRecord: newPurchaseRecord } }, ) .exec() - .then((result) => { - console.log('result old: ', result); - res.status(201).send(); - }) + .then(() => res.status(201).send()) .catch(error => res.status(500).send(error)); } catch (error) { res.status(500).send(error); diff --git a/src/models/bmdashboard/baseInvSchema.js b/src/models/bmdashboard/baseInvSchema.js new file mode 100644 index 000000000..7eff943e7 --- /dev/null +++ b/src/models/bmdashboard/baseInvSchema.js @@ -0,0 +1,23 @@ +const mongoose = require('mongoose'); + +// base schema for all categories of inventory (Consumable, Material, Reusable, Tool, Equipment) +// this schema is extended by the individual schemas for each inventory type +// all documents derived from this schema are saved to the collection 'buildingInventoryItems' + +const baseInvSchema = mongoose.Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: { type: Number, required: true }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brandPref: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], +}); + +const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); + +module.exports = baseInv; diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index d9a6be194..caf61e4c0 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -1,73 +1,23 @@ const mongoose = require('mongoose'); -const { Schema } = mongoose; +const baseInv = require('./baseInvSchema'); -const inventoryBaseSchema = new Schema({ - testField1: { type: String, default: 'hello world' }, - testField2: { type: Number, default: 101 }, -}); +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "buildingMaterial" } -const InvBase = mongoose.model('InvBase', inventoryBaseSchema); - -const buildingMaterial = InvBase.discriminator('buildingMaterial', new Schema({ - itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, - project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, +const buildingMaterial = baseInv.discriminator('buildingMaterial', new mongoose.Schema({ stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project - stockUsed: { type: Number, default: 0 }, // total amount of item used successfully in the project - stockWasted: { type: Number, default: 0 }, // total amount of item wasted/ruined/lost in the project - stockAvailable: { type: Number, default: 0 }, // bought - (used + wasted) - purchaseRecord: [{ - _id: false, // do not add _id field to subdocument - date: { type: Date, default: Date.now() }, - requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantity: { type: Number, required: true }, - priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, - brand: String, - status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, - }], + stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused + stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock + stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) updateRecord: [{ _id: false, date: { type: Date, required: true }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, quantityUsed: { type: Number, required: true }, quantityWasted: { type: Number, required: true }, + test: { type: String, default: 'testing this field' }, }], })); -// common fields -const eventDefinition = { time: Date } -// specific fields -const ClickedLinkEventDefinition = {...eventDefinition, url: String} - -// completely separate models and collections on db level -const eventSchema = new mongoose.Schema(eventDefinition, options); -const Event = mongoose.model('Event', eventSchema); - -const ClickedLinkEvent = new mongoose.Schema(ClickedLinkEventDefinition , options); - -// const buildingMaterial = new Schema({ -// itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, -// project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, -// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project -// stockUsed: { type: Number, default: 0 }, // total amount of item used successfully in the project -// stockWasted: { type: Number, default: 0 }, // total amount of item wasted/ruined/lost in the project -// stockAvailable: { type: Number, default: 0 }, // bought - (used + wasted) -// purchaseRecord: [{ -// _id: false, // do not add _id field to subdocument -// date: { type: Date, default: Date.now() }, -// requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantity: { type: Number, required: true }, -// priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, -// brand: String, -// status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, -// }], -// updateRecord: [{ -// _id: false, -// date: { type: Date, required: true }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantityUsed: { type: Number, required: true }, -// quantityWasted: { type: Number, required: true }, -// }], -// }); - module.exports = buildingMaterial; diff --git a/src/models/bmdashboard/buildingReusable.js b/src/models/bmdashboard/buildingReusable.js index 1d0503010..1f2ceaa48 100644 --- a/src/models/bmdashboard/buildingReusable.js +++ b/src/models/bmdashboard/buildingReusable.js @@ -1,22 +1,14 @@ const mongoose = require('mongoose'); -const { Schema } = mongoose; +const baseInv = require('./baseInvSchema'); -const buildingReusable = new Schema({ - itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, - project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "buildingReusable" } + +const buildingReusable = baseInv.discriminator('buildingReusable', new mongoose.Schema({ stockBought: { type: Number, default: 0 }, stockDestroyed: { type: Number, default: 0 }, stockAvailable: { type: Number, default: 0 }, - purchaseRecord: [{ - _id: false, // do not add _id field to subdocument - date: { type: Date, default: Date.now() }, - requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantity: { type: Number, required: true }, - priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, - brand: String, - status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, - }], updateRecord: [{ _id: false, date: { type: Date, required: true }, @@ -24,6 +16,6 @@ const buildingReusable = new Schema({ quantityUsed: { type: Number, required: true }, quantityDestroyed: { type: Number, required: true }, }], -}); +})); -module.exports = mongoose.model('buildingReusable', buildingReusable, 'buildingReusables'); +module.exports = buildingReusable; From e799a5ed88bdd7bdf710fb6898babb5249ffef2e Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Fri, 8 Dec 2023 12:56:53 -0500 Subject: [PATCH 087/129] Fix: Assigned badges have no earned date - Modified earned date format function in userHelper.js - Added data validation for earned date and badge count mismatch in badgeController.js - Added fillEarnedDateToMatchCount function to resolve earned date and badge count mismatch in badgeController.js - Refactored data validation for duplicate badge id in badgeController.js - Added data validation for badge count should greater than 0 in badgeController.js - Added formatDate function to format date to MMM-DD-YY in badgeController.js --- src/controllers/badgeController.js | 205 ++++++++++++++++++++--------- src/helpers/userHelper.js | 34 +++-- 2 files changed, 167 insertions(+), 72 deletions(-) diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 366c9323e..a438df505 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -3,10 +3,11 @@ const UserProfile = require('../models/userProfile'); const { hasPermission } = require('../utilities/permissions'); const escapeRegex = require('../utilities/escapeRegex'); const cache = require('../utilities/nodeCache')(); +// const userHelper = require('../helpers/userHelper'); const badgeController = function (Badge) { const getAllBadges = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'seeBadges')) { + if (!(await hasPermission(req.body.requestor, 'seeBadges'))) { res.status(403).send('You are not authorized to view all badge data.'); return; } @@ -14,10 +15,11 @@ const badgeController = function (Badge) { Badge.find( {}, 'badgeName type multiple weeks months totalHrs people imageUrl category project ranking description showReport', - ).populate({ - path: 'project', - select: '_id projectName', - }) + ) + .populate({ + path: 'project', + select: '_id projectName', + }) .sort({ ranking: 1, badgeName: 1, @@ -26,8 +28,51 @@ const badgeController = function (Badge) { .catch(error => res.status(404).send(error)); }; + /** + * Updated Date: 12/06/2023 + * Updated By: Shengwei + * Function added: + * - Added data validation for earned date and badge count mismatch. + * - Added fillEarnedDateToMatchCount function to resolve earned date and badge count mismatch. + * - Refactored data validation for duplicate badge id. + * - Added data validation for badge count should greater than 0. + * - Added formatDate function to format date to MMM-DD-YY. + */ + + const formatDate = () => { + const currentDate = new Date(Date.now()); + const monthNames = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; + + const month = monthNames[currentDate.getMonth()]; + const day = currentDate.getDate(); + const year = currentDate.getFullYear().toString().slice(-2); // Get last two digits of the year + + return `${month}-${day}-${year}`; + }; + + const fillEarnedDateToMatchCount = (earnedDate, count) => { + const result = [...earnedDate]; + while (result.length < count) { + result.push(formatDate()); + } + return result; + }; + const assignBadges = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'assignBadges')) { + if (!(await hasPermission(req.body.requestor, 'assignBadges'))) { res.status(403).send('You are not authorized to assign badges.'); return; } @@ -39,63 +84,90 @@ const badgeController = function (Badge) { res.status(400).send('Can not find the user to be assigned.'); return; } - const grouped = req.body.badgeCollection.reduce((groupd, item) => { - const propertyValue = item.badge; - groupd[propertyValue] = (groupd[propertyValue] || 0) + 1; - return groupd; - }, {}); - const result = Object.keys(grouped).every(bdge => grouped[bdge] <= 1); - if (result) { - record.badgeCollection = req.body.badgeCollection; - - if (cache.hasCache(`user-${userToBeAssigned}`)) cache.removeCache(`user-${userToBeAssigned}`); - - record.save() - .then(results => res.status(201).send(results._id)) - .catch(errors => res.status(500).send(errors)); - } else { - res.status(500).send('Duplicate badges sent in.'); + const badgeCounts = {}; + // This line is using the forEach function to group badges in the badgeCollection + // array in the request body. + // Validation: No duplicate badge id; + try { + req.body.badgeCollection.forEach((element) => { + if (badgeCounts[element.badge]) { + res.status(500).send('Duplicate badges sent in.'); + return; + } + badgeCounts[element.badge] = element.count; + // Validation: count should be greater than 0 + if (element.count < 1) { + throw new Error('Badge count should be greater than 0.'); + } + if (element.count !== element.earnedDate.length) { + element.earnedDate = fillEarnedDateToMatchCount( + element.earnedDate, + element.count, + ); + } + }); + } catch (err) { + res.status(500).send('Internal Err: Badge Collection.'); + return; } + record.badgeCollection = req.body.badgeCollection; + + if (cache.hasCache(`user-${userToBeAssigned}`)) { cache.removeCache(`user-${userToBeAssigned}`); } + // Save Updated User Profile + record + .save() + .then(results => res.status(201).send(results._id)) + .catch(errors => res.status(500).send(errors)); }); }; const postBadge = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'createBadges')) { - res.status(403).send({ error: 'You are not authorized to create new badges.' }); + if (!(await hasPermission(req.body.requestor, 'createBadges'))) { + res + .status(403) + .send({ error: 'You are not authorized to create new badges.' }); return; } - Badge.find({ badgeName: { $regex: escapeRegex(req.body.badgeName), $options: 'i' } }) - .then((result) => { - if (result.length > 0) { - res.status(400).send({ error: `Another badge with name ${result[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.` }); - return; - } - const badge = new Badge(); - - badge.badgeName = req.body.badgeName; - badge.category = req.body.category; - badge.type = req.body.type; - badge.multiple = req.body.multiple; - badge.totalHrs = req.body.totalHrs; - badge.weeks = req.body.weeks; - badge.months = req.body.months; - badge.people = req.body.people; - badge.project = req.body.project; - badge.imageUrl = req.body.imageUrl; - badge.ranking = req.body.ranking; - badge.description = req.body.description; - badge.showReport = req.body.showReport; - - badge.save() - .then(results => res.status(201).send(results)) - .catch(errors => res.status(500).send(errors)); - }); + Badge.find({ + badgeName: { $regex: escapeRegex(req.body.badgeName), $options: 'i' }, + }).then((result) => { + if (result.length > 0) { + res + .status(400) + .send({ + error: `Another badge with name ${result[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.`, + }); + return; + } + const badge = new Badge(); + + badge.badgeName = req.body.badgeName; + badge.category = req.body.category; + badge.type = req.body.type; + badge.multiple = req.body.multiple; + badge.totalHrs = req.body.totalHrs; + badge.weeks = req.body.weeks; + badge.months = req.body.months; + badge.people = req.body.people; + badge.project = req.body.project; + badge.imageUrl = req.body.imageUrl; + badge.ranking = req.body.ranking; + badge.description = req.body.description; + badge.showReport = req.body.showReport; + + badge + .save() + .then(results => res.status(201).send(results)) + .catch(errors => res.status(500).send(errors)); + }); }; const deleteBadge = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'deleteBadges')) { - res.status(403).send({ error: 'You are not authorized to delete badges.' }); + if (!(await hasPermission(req.body.requestor, 'deleteBadges'))) { + res + .status(403) + .send({ error: 'You are not authorized to delete badges.' }); return; } const { badgeId } = req.params; @@ -104,19 +176,33 @@ const badgeController = function (Badge) { res.status(400).send({ error: 'No valid records found' }); return; } - const removeBadgeFromProfile = UserProfile.updateMany({}, { $pull: { badgeCollection: { badge: record._id } } }).exec(); + const removeBadgeFromProfile = UserProfile.updateMany( + {}, + { $pull: { badgeCollection: { badge: record._id } } }, + ).exec(); const deleteRecord = record.remove(); Promise.all([removeBadgeFromProfile, deleteRecord]) - .then(res.status(200).send({ message: 'Badge successfully deleted and user profiles updated' })) - .catch((errors) => { res.status(500).send(errors); }); - }) - .catch((error) => { res.status(500).send(error); }); + .then( + res + .status(200) + .send({ + message: 'Badge successfully deleted and user profiles updated', + }), + ) + .catch((errors) => { + res.status(500).send(errors); + }); + }).catch((error) => { + res.status(500).send(error); + }); }; const putBadge = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'updateBadges')) { - res.status(403).send({ error: 'You are not authorized to update badges.' }); + if (!(await hasPermission(req.body.requestor, 'updateBadges'))) { + res + .status(403) + .send({ error: 'You are not authorized to update badges.' }); return; } const { badgeId } = req.params; @@ -129,7 +215,6 @@ const badgeController = function (Badge) { // store onto Azure and return url } - const data = { badgeName: req.body.name || req.body.badgeName, description: req.body.description, diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index f639a4d37..b25328e28 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -27,21 +27,31 @@ const userHelper = function () { }); }; + // Updated By: Shengwei + // Updated Date: 12/08/2023 + // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) const earnedDateBadge = () => { - const today = new Date(); - const yyyy = today.getFullYear(); - // Add 1 beacuse the month start at zero - let mm = today.getMonth() + 1; - let dd = today.getDate(); - - // eslint-disable-next-line no-unused-expressions - mm = mm < 10 ? `0${mm}` : mm; - // eslint-disable-next-line no-unused-expressions - dd = dd < 10 ? `0${dd}` : dd; + const currentDate = new Date(Date.now()); + const monthNames = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; - const formatedDate = `${yyyy}-${mm}-${dd}`; + const month = monthNames[currentDate.getMonth()]; + const day = currentDate.getDate(); + const year = currentDate.getFullYear().toString().slice(-2); // Get last two digits of the year - return formatedDate; + return `${month}-${day}-${year}`; }; const getUserName = async function (userId) { From 07835ede44e255d2e8705608a89ce16a61728fb6 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Fri, 8 Dec 2023 13:11:35 -0500 Subject: [PATCH 088/129] Update: Modified date format function - Update fromatDate in badgeController using moment timezone library - Update earnedDateBadge in userHelper using moment timezone library --- src/controllers/badgeController.js | 22 ++-------------------- src/helpers/userHelper.js | 22 ++-------------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index a438df505..89c47ba79 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -41,28 +41,10 @@ const badgeController = function (Badge) { const formatDate = () => { const currentDate = new Date(Date.now()); - const monthNames = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ]; - - const month = monthNames[currentDate.getMonth()]; - const day = currentDate.getDate(); - const year = currentDate.getFullYear().toString().slice(-2); // Get last two digits of the year - - return `${month}-${day}-${year}`; + return moment(currentDate).tz('America/Los_Angeles').format(); }; + const fillEarnedDateToMatchCount = (earnedDate, count) => { const result = [...earnedDate]; while (result.length < count) { diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index b25328e28..c9d5ca1bf 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -15,6 +15,7 @@ const hasPermission = require("../utilities/permissions"); const Reason = require("../models/reason"); const token = require("../models/profileInitialSetupToken"); + const userHelper = function () { const getTeamMembers = function (user) { const userId = mongoose.Types.ObjectId(user._id); @@ -32,26 +33,7 @@ const userHelper = function () { // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) const earnedDateBadge = () => { const currentDate = new Date(Date.now()); - const monthNames = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ]; - - const month = monthNames[currentDate.getMonth()]; - const day = currentDate.getDate(); - const year = currentDate.getFullYear().toString().slice(-2); // Get last two digits of the year - - return `${month}-${day}-${year}`; + return moment(currentDate).tz('America/Los_Angeles').format(); }; const getUserName = async function (userId) { From 1e08bdbf405f756e40fb36fb93f949bb8ea9b8b8 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Fri, 8 Dec 2023 13:35:59 -0500 Subject: [PATCH 089/129] Fix: Added format MMM-DD-YY for moment format function --- src/controllers/badgeController.js | 4 ++-- src/helpers/userHelper.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 89c47ba79..4c92bd9a5 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -1,9 +1,9 @@ +const moment = require('moment-timezone'); const mongoose = require('mongoose'); const UserProfile = require('../models/userProfile'); const { hasPermission } = require('../utilities/permissions'); const escapeRegex = require('../utilities/escapeRegex'); const cache = require('../utilities/nodeCache')(); -// const userHelper = require('../helpers/userHelper'); const badgeController = function (Badge) { const getAllBadges = async function (req, res) { @@ -41,7 +41,7 @@ const badgeController = function (Badge) { const formatDate = () => { const currentDate = new Date(Date.now()); - return moment(currentDate).tz('America/Los_Angeles').format(); + return moment(currentDate).tz('America/Los_Angeles').format('MMM-DD-YY'); }; diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index c9d5ca1bf..fa0115271 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -33,7 +33,7 @@ const userHelper = function () { // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) const earnedDateBadge = () => { const currentDate = new Date(Date.now()); - return moment(currentDate).tz('America/Los_Angeles').format(); + return moment(currentDate).tz('America/Los_Angeles').format('MMM-DD-YY'); }; const getUserName = async function (userId) { From a63f976b36e4df494356409759803e4bc817e6f8 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Fri, 8 Dec 2023 15:02:00 -0500 Subject: [PATCH 090/129] Added a logger for the function responsible for filling mismatched badge earned dates --- src/controllers/badgeController.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 4c92bd9a5..11ee56ea4 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -4,6 +4,7 @@ const UserProfile = require('../models/userProfile'); const { hasPermission } = require('../utilities/permissions'); const escapeRegex = require('../utilities/escapeRegex'); const cache = require('../utilities/nodeCache')(); +const logger = require('../startup/logger'); const badgeController = function (Badge) { const getAllBadges = async function (req, res) { @@ -44,7 +45,6 @@ const badgeController = function (Badge) { return moment(currentDate).tz('America/Los_Angeles').format('MMM-DD-YY'); }; - const fillEarnedDateToMatchCount = (earnedDate, count) => { const result = [...earnedDate]; while (result.length < count) { @@ -86,6 +86,12 @@ const badgeController = function (Badge) { element.earnedDate, element.count, ); + element.lastModified = Date.now(); + logger.logInfo( + `Badge count and earned dates mismatched found. ${Date.now()} was generated for user ${userToBeAssigned}. Badge record ID ${ + element._id + }; Badge Type ID ${element.badge}`, + ); } }); } catch (err) { @@ -94,7 +100,9 @@ const badgeController = function (Badge) { } record.badgeCollection = req.body.badgeCollection; - if (cache.hasCache(`user-${userToBeAssigned}`)) { cache.removeCache(`user-${userToBeAssigned}`); } + if (cache.hasCache(`user-${userToBeAssigned}`)) { + cache.removeCache(`user-${userToBeAssigned}`); + } // Save Updated User Profile record .save() @@ -115,11 +123,9 @@ const badgeController = function (Badge) { badgeName: { $regex: escapeRegex(req.body.badgeName), $options: 'i' }, }).then((result) => { if (result.length > 0) { - res - .status(400) - .send({ - error: `Another badge with name ${result[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.`, - }); + res.status(400).send({ + error: `Another badge with name ${result[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.`, + }); return; } const badge = new Badge(); @@ -166,11 +172,9 @@ const badgeController = function (Badge) { Promise.all([removeBadgeFromProfile, deleteRecord]) .then( - res - .status(200) - .send({ - message: 'Badge successfully deleted and user profiles updated', - }), + res.status(200).send({ + message: 'Badge successfully deleted and user profiles updated', + }), ) .catch((errors) => { res.status(500).send(errors); From b2608a817416abce1293a6780704ff3543888514 Mon Sep 17 00:00:00 2001 From: Nathan Hoffman Date: Fri, 8 Dec 2023 14:41:38 -0800 Subject: [PATCH 091/129] Update minimum permissions --- src/utilities/createInitialPermissions.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utilities/createInitialPermissions.js b/src/utilities/createInitialPermissions.js index 9cdf7f9e0..86a79b94c 100644 --- a/src/utilities/createInitialPermissions.js +++ b/src/utilities/createInitialPermissions.js @@ -6,6 +6,9 @@ const permissionsRoles = [ { roleName: 'Administrator', permissions: [ + // Reports + 'getWeeklySummaries', + 'getReports', // Doesn't do anything on back-end. // Badges 'seeBadges', 'assignBadges', @@ -65,8 +68,7 @@ const permissionsRoles = [ // General 'getUserProfiles', 'getProjectMembers', - 'getWeeklySummaries', - // 'getReportsPage',? + 'getTimeZoneAPIKey', 'checkLeadTeamOfXplus', ], @@ -97,6 +99,7 @@ const permissionsRoles = [ 'getAllInvType', 'postInvType', 'getWeeklySummaries', + 'getReports', 'getTimeZoneAPIKey', 'checkLeadTeamOfXplus', ], @@ -124,7 +127,6 @@ const permissionsRoles = [ 'putInvType', 'getAllInvType', 'postInvType', - 'getWeeklySummaries', 'getTimeZoneAPIKey', 'checkLeadTeamOfXplus', ], @@ -151,7 +153,6 @@ const permissionsRoles = [ 'putInvType', 'getAllInvType', 'postInvType', - 'getWeeklySummaries', 'getTimeZoneAPIKey', 'checkLeadTeamOfXplus', ], @@ -212,6 +213,7 @@ const permissionsRoles = [ 'getAllInvType', 'postInvType', 'getWeeklySummaries', + 'getReports', 'getTimeZoneAPIKey', 'checkLeadTeamOfXplus', 'editTeamCode', From 778217620b5fb6c11e3522f671e35cdd45d758ef Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Fri, 8 Dec 2023 14:47:04 -0800 Subject: [PATCH 092/129] add single inventory file to hold base and extended schemas --- .../bmdashboard/buildingInventoryItem.js | 94 +++++++++++++++++++ src/startup/routes.js | 2 +- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/models/bmdashboard/buildingInventoryItem.js diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js new file mode 100644 index 000000000..db5c3012c --- /dev/null +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -0,0 +1,94 @@ +const mongoose = require('mongoose'); + +//---------------------- +// BASE INVENTORY SCHEMA +//---------------------- + +// base schema for all categories of inventory (Consumable, Material, Reusable, Tool, Equipment) +// this schema is extended by the individual schemas for each inventory type +// all documents derived from this schema are saved to the collection 'buildingInventoryItems' + +const baseInvSchema = mongoose.Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: { type: Number, required: true }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brandPref: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], +}); + +const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); + +//----------------- +// MATERIALS SCHEMA +//----------------- + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "material" } +// ex: sand, stone, bricks, lumber, insulation + +const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ + stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project + stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused + stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock + stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) + updateRecord: [{ + _id: false, + date: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: { type: Number, required: true }, + quantityWasted: { type: Number, required: true }, + }], +})); + +//----------------- +// REUSABLES SCHEMA +//----------------- + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "reusable" } +// ex: hammers, screwdrivers, mallets, brushes, gloves + +const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ + stockBought: { type: Number, default: 0 }, + stockDestroyed: { type: Number, default: 0 }, + stockAvailable: { type: Number, default: 0 }, + updateRecord: [{ + _id: false, + date: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: { type: Number, required: true }, + quantityDestroyed: { type: Number, required: true }, + }], +})); + +//----------------- +// CONSUMABLES SCHEMA +//----------------- + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "consumable" } +// ex: screws, nails, staples + +//------------- +// TOOLS SCHEMA +//------------- + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "tool" } +// ex: power drills, wheelbarrows, shovels + +// const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ + +// })); + + +module.exports = { + buildingMaterial, + buildingReusable, +}; diff --git a/src/startup/routes.js b/src/startup/routes.js index 2d95ea639..5f77a413d 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -24,7 +24,7 @@ const inventoryItemMaterial = require('../models/inventoryItemMaterial'); const mapLocations = require('../models/mapLocation'); const buildingProject = require('../models/bmdashboard/buildingProject'); const buildingInventoryType = require('../models/bmdashboard/buildingInventoryType'); -const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); +const { buildingMaterial } = require('../models/bmdashboard/buildingInventoryItem'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); From 5dd37d817997961c79b5db65a059d39b771116a4 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sat, 9 Dec 2023 14:55:41 -0800 Subject: [PATCH 093/129] add building tool inv to schema file --- .../bmdashboard/buildingInventoryItem.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index db5c3012c..0d357ae7a 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -83,9 +83,35 @@ const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ // each document derived from this schema includes key field { __t: "tool" } // ex: power drills, wheelbarrows, shovels -// const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ - -// })); +const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ + code: { type: Number, required: true }, // add function to create code for on-site tool tracking + purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, + // add discriminator based on rental or purchase so these fields are required if tool is rented + rentedOnDate: Date, + rentalDue: Date, + userResponsible: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + purchaseRecord: [{ // track purchase/rental requests + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brand: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], + updateRecord: [{ // track tool condition updates + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, + }], + logRecord: [{ // track tool daily check in/out and use + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + type: { type: String, enum: ['Check In', 'Check Out'] }, // default = opposite of current log status? + }], +})); module.exports = { From 6f36edd374fe2e4052a79a36649cfc67b5302394 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Sun, 10 Dec 2023 00:16:26 -0500 Subject: [PATCH 094/129] Update error handling in badge assginment feature --- src/controllers/badgeController.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 11ee56ea4..5dd2113a6 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -73,8 +73,9 @@ const badgeController = function (Badge) { try { req.body.badgeCollection.forEach((element) => { if (badgeCounts[element.badge]) { - res.status(500).send('Duplicate badges sent in.'); - return; + throw new Error('Duplicate badges sent in.'); + // res.status(500).send('Duplicate badges sent in.'); + // return; } badgeCounts[element.badge] = element.count; // Validation: count should be greater than 0 @@ -95,7 +96,7 @@ const badgeController = function (Badge) { } }); } catch (err) { - res.status(500).send('Internal Err: Badge Collection.'); + res.status(500).send(`Internal Error: Badge Collection. ${ err.message}`); return; } record.badgeCollection = req.body.badgeCollection; @@ -107,7 +108,10 @@ const badgeController = function (Badge) { record .save() .then(results => res.status(201).send(results._id)) - .catch(errors => res.status(500).send(errors)); + .catch((err) => { + logger.logException(err); + res.status(500).send('Internal Error: Unable to save the record.'); + }); }); }; From 4a9deec48787963f3993f3a8faeb232280623d15 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sat, 9 Dec 2023 23:53:58 -0800 Subject: [PATCH 095/129] integrate ownerMessage and ownerStandardMessage, simplify route and controller logic --- src/controllers/ownerMessageController.js | 88 +++++++++++------------ src/models/ownerMessage.js | 3 +- src/routes/ownerMessageRouter.js | 11 ++- src/startup/routes.js | 6 +- 4 files changed, 52 insertions(+), 56 deletions(-) diff --git a/src/controllers/ownerMessageController.js b/src/controllers/ownerMessageController.js index b4a00431b..f09a32030 100644 --- a/src/controllers/ownerMessageController.js +++ b/src/controllers/ownerMessageController.js @@ -1,63 +1,61 @@ const ownerMessageController = function (OwnerMessage) { - const postOwnerMessage = function (req, res) { - if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to create messages!'); + const getOwnerMessage = async function (req, res) { + try { + const results = await OwnerMessage.find({}); + if (results.length === 0) { // first time initialization + const ownerMessage = new OwnerMessage(); + await ownerMessage.save(); + res.status(200).send({ ownerMessage }); + } else { + res.status(200).send({ ownerMessage: results[0] }); + } + } catch (error) { + res.status(404).send(error); } - const ownerMessage = new OwnerMessage(); - ownerMessage.message = req.body.newMessage; - ownerMessage.save().then(() => res.status(201).json({ - _serverMessage: 'Message succesfuly created!', - ownerMessage, - })).catch(err => res.status(500).send({ err })); - }; - - const getOwnerMessage = function (req, res) { - OwnerMessage.find() - .then(results => res.status(200).send(results)) - .catch(error => res.status(404).send(error)); }; - const deleteOwnerMessage = function (req, res) { + const updateOwnerMessage = async function (req, res) { if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to delete messages!'); + res.status(403).send('You are not authorized to create messages!'); + } + const { standardMessage, message } = req.body; + try { + const results = await OwnerMessage.find({}); + const ownerMessage = results[0]; + if (standardMessage) { + ownerMessage.standardMessage = standardMessage; + } else { + ownerMessage.message = message; + } + await ownerMessage.save(); + res.status(201).send({ + _serverMessage: 'Update successfully!', + ownerMessage: { standardMessage, message }, + }); + } catch (error) { + res.status(500).send(error); } - OwnerMessage.deleteMany({}) - .then((result) => { - result - .then(res.status(200).send({ _serverMessage: 'Message deleted!' })) - .catch((error) => { - res.status(400).send(error); - }); - }) - .catch((error) => { - res.status(400).send(error); - }); }; - const updateOwnerMessage = function (req, res) { + const deleteOwnerMessage = async function (req, res) { if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to update messages!'); + res.status(403).send('You are not authorized to delete messages!'); + } + try { + const results = await OwnerMessage.find({}); + const ownerMessage = results[0]; + ownerMessage.message = ''; + await ownerMessage.save(); + res.status(200).send({ _serverMessage: 'Delete successfully!', ownerMessage }); + } catch (error) { + res.status(500).send(error); } - const { id } = req.params; - - return OwnerMessage.findById(id, (error, ownerMessage) => { - if (error || ownerMessage === null) { - res.status(400).send('No ownerMessage found'); - return; - } - - ownerMessage.message = req.body.newMessage; - ownerMessage.save() - .then(results => res.status(201).send(results)) - .catch(errors => res.status(400).send(errors)); - }); }; return { - postOwnerMessage, getOwnerMessage, - deleteOwnerMessage, updateOwnerMessage, + deleteOwnerMessage, }; }; diff --git a/src/models/ownerMessage.js b/src/models/ownerMessage.js index be953c3a3..a6314c929 100644 --- a/src/models/ownerMessage.js +++ b/src/models/ownerMessage.js @@ -3,7 +3,8 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const OwnerMessage = new Schema({ - message: { type: String }, + message: { type: String, default: '' }, + standardMessage: { type: String, default: '' }, }); module.exports = mongoose.model('ownerMessage', OwnerMessage, 'ownerMessage'); diff --git a/src/routes/ownerMessageRouter.js b/src/routes/ownerMessageRouter.js index e436deed8..6f5716fe9 100644 --- a/src/routes/ownerMessageRouter.js +++ b/src/routes/ownerMessageRouter.js @@ -5,14 +5,11 @@ const routes = function (ownerMessage) { const OwnerMessageRouter = express.Router(); OwnerMessageRouter.route('/ownerMessage') - .post(controller.postOwnerMessage) - .get(controller.getOwnerMessage) - .delete(controller.deleteOwnerMessage); + .get(controller.getOwnerMessage) + .put(controller.updateOwnerMessage) + .delete(controller.deleteOwnerMessage); - OwnerMessageRouter.route('/ownerMessage/:id') - .put(controller.updateOwnerMessage); - -return OwnerMessageRouter; + return OwnerMessageRouter; }; module.exports = routes; diff --git a/src/startup/routes.js b/src/startup/routes.js index 2d95ea639..e7c91dab0 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -16,7 +16,7 @@ const inventoryItemType = require('../models/inventoryItemType'); const role = require('../models/role'); const rolePreset = require('../models/rolePreset'); const ownerMessage = require('../models/ownerMessage'); -const ownerStandardMessage = require('../models/ownerStandardMessage'); +// const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); @@ -55,7 +55,7 @@ const taskEditSuggestionRouter = require('../routes/taskEditSuggestionRouter')(t const roleRouter = require('../routes/roleRouter')(role); const rolePresetRouter = require('../routes/rolePresetRouter')(rolePreset); const ownerMessageRouter = require('../routes/ownerMessageRouter')(ownerMessage); -const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter')(ownerStandardMessage); +// const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter')(ownerStandardMessage); const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); @@ -92,7 +92,7 @@ module.exports = function (app) { app.use('/api', roleRouter); app.use('/api', rolePresetRouter); app.use('/api', ownerMessageRouter); - app.use('/api', ownerStandardMessageRouter); + // app.use('/api', ownerStandardMessageRouter); app.use('/api', profileInitialSetupRouter); app.use('/api', reasonRouter); app.use('/api', informationRouter); From 1128f8937e0ae2acf8e237a45a947813f87a0abb Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sun, 10 Dec 2023 14:28:16 -0800 Subject: [PATCH 096/129] remove unused files and codes related to ownerStandardMessage --- .../ownerStandardMessageController.js | 64 ------------------- src/models/ownerStandardMessage.js | 9 --- src/routes/ownerStandardMessageRouter.js | 18 ------ src/startup/routes.js | 3 - 4 files changed, 94 deletions(-) delete mode 100644 src/controllers/ownerStandardMessageController.js delete mode 100644 src/models/ownerStandardMessage.js delete mode 100644 src/routes/ownerStandardMessageRouter.js diff --git a/src/controllers/ownerStandardMessageController.js b/src/controllers/ownerStandardMessageController.js deleted file mode 100644 index efd933888..000000000 --- a/src/controllers/ownerStandardMessageController.js +++ /dev/null @@ -1,64 +0,0 @@ -const ownerStandardMessageController = function (OwnerStandardMessage) { - const postOwnerStandardMessage = function (req, res) { - if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to create messages!'); - } - const ownerStandardMessage = new OwnerStandardMessage(); - ownerStandardMessage.message = req.body.newStandardMessage; - ownerStandardMessage.save().then(() => res.status(201).json({ - _serverMessage: 'Message succesfuly created!', - ownerStandardMessage, - })).catch(err => res.status(500).send({ err })); - }; - - const getOwnerStandardMessage = function (req, res) { - OwnerStandardMessage.find() - .then(results => res.status(200).send(results)) - .catch(error => res.status(404).send(error)); - }; - - const deleteOwnerStandardMessage = function (req, res) { - if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to delete messages!'); - } - OwnerStandardMessage.deleteMany({}) - .then((result) => { - result - .then(res.status(200).send({ _serverMessage: 'Standard Message deleted!' })) - .catch((error) => { - res.status(400).send(error); - }); - }) - .catch((error) => { - res.status(400).send(error); - }); - }; - - const updateOwnerStandardMessage = function (req, res) { - if (req.body.requestor.role !== 'Owner') { - res.status(403).send('You are not authorized to update messages!'); - } - const { id } = req.params; - - return OwnerStandardMessage.findById(id, (error, ownerStandardMessage) => { - if (error || ownerStandardMessage === null) { - res.status(400).send('No ownerStandardMessage found'); - return; - } - - ownerStandardMessage.message = req.body.newStandardMessage; - ownerStandardMessage.save() - .then(results => res.status(201).send(results)) - .catch(errors => res.status(400).send(errors)); - }); - }; - - return { - postOwnerStandardMessage, - getOwnerStandardMessage, - deleteOwnerStandardMessage, - updateOwnerStandardMessage, - }; -}; - -module.exports = ownerStandardMessageController; diff --git a/src/models/ownerStandardMessage.js b/src/models/ownerStandardMessage.js deleted file mode 100644 index d344a773f..000000000 --- a/src/models/ownerStandardMessage.js +++ /dev/null @@ -1,9 +0,0 @@ -const mongoose = require('mongoose'); - -const { Schema } = mongoose; - -const OwnerStandardMessage = new Schema({ - message: { type: String }, -}); - -module.exports = mongoose.model('ownerStandardMessage', OwnerStandardMessage, 'ownerStandardMessage'); diff --git a/src/routes/ownerStandardMessageRouter.js b/src/routes/ownerStandardMessageRouter.js deleted file mode 100644 index 08e629c3d..000000000 --- a/src/routes/ownerStandardMessageRouter.js +++ /dev/null @@ -1,18 +0,0 @@ -const express = require('express'); - -const routes = function (ownerStandardMessage) { - const controller = require('../controllers/ownerStandardMessageController')(ownerStandardMessage); - const OwnerStandardMessageRouter = express.Router(); - - OwnerStandardMessageRouter.route('/ownerStandardMessage') - .post(controller.postOwnerStandardMessage) - .get(controller.getOwnerStandardMessage) - .delete(controller.deleteOwnerStandardMessage); - - OwnerStandardMessageRouter.route('/ownerStandardMessage/:id') - .put(controller.updateOwnerStandardMessage); - -return OwnerStandardMessageRouter; -}; - -module.exports = routes; diff --git a/src/startup/routes.js b/src/startup/routes.js index e7c91dab0..ff5d01b2b 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -16,7 +16,6 @@ const inventoryItemType = require('../models/inventoryItemType'); const role = require('../models/role'); const rolePreset = require('../models/rolePreset'); const ownerMessage = require('../models/ownerMessage'); -// const ownerStandardMessage = require('../models/ownerStandardMessage'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); @@ -55,7 +54,6 @@ const taskEditSuggestionRouter = require('../routes/taskEditSuggestionRouter')(t const roleRouter = require('../routes/roleRouter')(role); const rolePresetRouter = require('../routes/rolePresetRouter')(rolePreset); const ownerMessageRouter = require('../routes/ownerMessageRouter')(ownerMessage); -// const ownerStandardMessageRouter = require('../routes/ownerStandardMessageRouter')(ownerStandardMessage); const reasonRouter = require('../routes/reasonRouter')(reason, userProfile); const mouseoverTextRouter = require('../routes/mouseoverTextRouter')(mouseoverText); @@ -92,7 +90,6 @@ module.exports = function (app) { app.use('/api', roleRouter); app.use('/api', rolePresetRouter); app.use('/api', ownerMessageRouter); - // app.use('/api', ownerStandardMessageRouter); app.use('/api', profileInitialSetupRouter); app.use('/api', reasonRouter); app.use('/api', informationRouter); From 4cef847de5eb3711b72dd94838f54dee69325a2b Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sun, 10 Dec 2023 21:23:20 -0800 Subject: [PATCH 097/129] make update standard message to empty message --- src/controllers/ownerMessageController.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/controllers/ownerMessageController.js b/src/controllers/ownerMessageController.js index f09a32030..1b2c30205 100644 --- a/src/controllers/ownerMessageController.js +++ b/src/controllers/ownerMessageController.js @@ -18,16 +18,18 @@ const ownerMessageController = function (OwnerMessage) { if (req.body.requestor.role !== 'Owner') { res.status(403).send('You are not authorized to create messages!'); } - const { standardMessage, message } = req.body; + const { isStandard, newMessage } = req.body; try { const results = await OwnerMessage.find({}); const ownerMessage = results[0]; - if (standardMessage) { - ownerMessage.standardMessage = standardMessage; + if (isStandard) { + ownerMessage.standardMessage = newMessage; + ownerMessage.message = ''; } else { - ownerMessage.message = message; + ownerMessage.message = newMessage; } await ownerMessage.save(); + const { standardMessage, message } = ownerMessage; res.status(201).send({ _serverMessage: 'Update successfully!', ownerMessage: { standardMessage, message }, From acefbbb69d072699f1e556e3b294684770b7617c Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Mon, 11 Dec 2023 18:08:12 -0800 Subject: [PATCH 098/129] Leaderboard api fix --- src/helpers/dashboardhelper.js | 603 +++++++++++++++++++-------------- 1 file changed, 355 insertions(+), 248 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index fe2007281..59b1d7f51 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -3,6 +3,7 @@ const mongoose = require('mongoose'); const userProfile = require('../models/userProfile'); const timeentry = require('../models/timeentry'); const myTeam = require('../helpers/helperModels/myTeam'); +const team = require('../models/team'); const dashboardhelper = function () { const personaldetails = function (userId) { @@ -152,8 +153,13 @@ const dashboardhelper = function () { return output; }; - const getLeaderboard = function (userId) { + const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1}) + .then((res)=>{ return res; }).catch((e)=>{}); + + if(userById==null) return null; + const userRole = userById.role; const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -162,254 +168,355 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - return myTeam.aggregate([ - { - $match: { - _id: userid, - }, - }, - { - $unwind: '$myteam', - }, - { - $project: { - _id: 0, - role: 1, - personId: '$myteam._id', - name: '$myteam.fullName', - }, - }, - { - $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', - }, - }, - { - $match: { - // leaderboard user roles hierarchy - $or: [ - { - role: { $in: ['Owner', 'Core Team'] }, - }, - { - $and: [ - { - role: 'Administrator', - }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ], - }, - { - $and: [ - { - role: { $in: ['Manager', 'Mentor'] }, - }, - { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - }, - }, - ], - }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - role: { - $arrayElemAt: ['$persondata.role', 0], - }, - isVisible: { - $arrayElemAt: ['$persondata.isVisible', 0], - }, - hasSummary: { - $ne: [ - { - $arrayElemAt: [ - { - $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], - }, - 0, - ], - }, - '', - ], - }, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - }, - { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - }, - ], - }, - }, - }, - { - $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeEntryData: { - $filter: { - input: '$timeEntryData', - as: 'timeentry', - cond: { - $and: [ - { - $gte: ['$$timeentry.dateOfWork', pdtstart], - }, - { - $lte: ['$$timeentry.dateOfWork', pdtend], - }, - ], - }, - }, - }, - }, - }, - { - $unwind: { - path: '$timeEntryData', - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - totalSeconds: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.totalSeconds', - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.isTangible', - false, - ], - }, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ['$isTangible', true], - }, - '$totalSeconds', - 0, - ], - }, - intangibletime: { - $cond: [ - { - $eq: ['$isTangible', false], - }, - '$totalSeconds', - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', - isVisible: '$isVisible', - hasSummary: '$hasSummary', - }, - totalSeconds: { - $sum: '$totalSeconds', - }, - tangibletime: { - $sum: '$tangibletime', - }, - intangibletime: { - $sum: '$intangibletime', - }, - }, - }, - { - $project: { - _id: 0, - personId: '$_id.personId', - name: '$_id.name', - role: '$_id.role', - isVisible: '$_id.isVisible', - hasSummary: '$_id.hasSummary', - weeklycommittedHours: '$_id.weeklycommittedHours', - totaltime_hrs: { - $divide: ['$totalSeconds', 3600], - }, - totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], - }, - totalintangibletime_hrs: { - $divide: ['$intangibletime', 3600], - }, - percentagespentintangible: { - $cond: [ - { - $eq: ['$totalSeconds', 0], - }, - 0, - { - $multiply: [ - { - $divide: ['$tangibletime', '$totalSeconds'], - }, - 100, - ], - }, - ], - }, - }, - }, - { - $sort: { - totaltangibletime_hrs: -1, - name: 1, - role: 1, - }, + + let teamMemberIds = [userid] + let teamMembers = []; + if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members + { + + const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) + .then((res)=>{ return res; }).catch((e)=>{}) + + teamsResult.map((_myTeam)=>{ + _myTeam.members.map((teamMember)=> { + if(!teamMember.userId.equals(userid)) + teamMemberIds.push( teamMember.userId ); + } ) + }) + + teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + + } + else { + if(userRole == 'Administrator'){ //All users except Owner and Core Team + const excludedRoles = ['Core Team', 'Owner']; + teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else{ //'Core Team', 'Owner' //All users + teamMembers = await userProfile.find({ isActive:true}, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + + + } + + teamMemberIds = teamMembers.map(member => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, }, - ]); + personId: { $in: teamMemberIds } + }); + + let timeEntryByPerson = {} + timeEntries.map((timeEntry)=>{ + + let personIdStr = timeEntry.personId.toString(); + + if(timeEntryByPerson[personIdStr]==null) + timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } else { + timeEntryByPerson[personIdStr].intangibleSeconds += timeEntry.totalSeconds; + } + + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }) + + let leaderBoardData = []; + teamMembers.map((teamMember)=>{ + let obj = { + personId : teamMember._id, + role : teamMember.role, + name : teamMember.firstName + ' ' + teamMember.lastName, + isVisible : teamMember.isVisible, + hasSummary : teamMember.weeklySummaries?.length > 0 ? teamMember.weeklySummaries[0].summary!='' : false, + weeklycommittedHours : teamMember.weeklycommittedHours, + totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), + totalintangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / 3600) || 0), + totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), + percentagespentintangible : + (timeEntryByPerson[teamMember._id.toString()] && timeEntryByPerson[teamMember._id.toString()]?.totalSeconds !=0 && timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds !=0) ? + (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * 100 + : + 0 + } + leaderBoardData.push(obj); + }) + + let sortedLBData = leaderBoardData.sort((a, b) => { + // Sort by totaltangibletime_hrs in descending order + if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { + return b.totaltangibletime_hrs - a.totaltangibletime_hrs; + } + + // Then sort by name in ascending order + if (a.name !== b.name) { + return a.name.localeCompare(b.name); + } + + // Finally, sort by role in ascending order + return a.role.localeCompare(b.role); + }); + return sortedLBData; + + // return myTeam.aggregate([ + // { + // $match: { + // _id: userid, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // role: 1, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // }, + // }, + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // leaderboard user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: { + // $arrayElemAt: ['$persondata.role', 0], + // }, + // isVisible: { + // $arrayElemAt: ['$persondata.isVisible', 0], + // }, + // hasSummary: { + // $ne: [ + // { + // $arrayElemAt: [ + // { + // $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], + // }, + // 0, + // ], + // }, + // '', + // ], + // }, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // intangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', false], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // isVisible: '$isVisible', + // hasSummary: '$hasSummary', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // intangibletime: { + // $sum: '$intangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // role: '$_id.role', + // isVisible: '$_id.isVisible', + // hasSummary: '$_id.hasSummary', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // totalintangibletime_hrs: { + // $divide: ['$intangibletime', 3600], + // }, + // percentagespentintangible: { + // $cond: [ + // { + // $eq: ['$totalSeconds', 0], + // }, + // 0, + // { + // $multiply: [ + // { + // $divide: ['$tangibletime', '$totalSeconds'], + // }, + // 100, + // ], + // }, + // ], + // }, + // }, + // }, + // { + // $sort: { + // totaltangibletime_hrs: -1, + // name: 1, + // role: 1, + // }, + // }, + // ]); }; /** From 2a1ef65af84d0d89eea2d6fb2fcc83fd0cf98810 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 12 Dec 2023 08:51:56 -0800 Subject: [PATCH 099/129] add tool and equipment discriminators --- .../bmdashboard/buildingInventoryItem.js | 88 +++++++++++++------ 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index 0d357ae7a..845243331 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -1,8 +1,8 @@ const mongoose = require('mongoose'); -//---------------------- -// BASE INVENTORY SCHEMA -//---------------------- +//----------------------- +// BASE INVENTORY SCHEMAS +//----------------------- // base schema for all categories of inventory (Consumable, Material, Reusable, Tool, Equipment) // this schema is extended by the individual schemas for each inventory type @@ -46,6 +46,28 @@ const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ }], })); +//----------------- +// CONSUMABLES SCHEMA +//----------------- + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "consumable" } +// ex: screws, nails, staples + +const buildingConsumable = baseInv.discriminator('consumable', new mongoose.Schema({ + stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project + stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused + stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock + stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) + updateRecord: [{ + _id: false, + date: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: { type: Number, required: true }, + quantityWasted: { type: Number, required: true }, + }], +})); + //----------------- // REUSABLES SCHEMA //----------------- @@ -67,54 +89,66 @@ const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ }], })); -//----------------- -// CONSUMABLES SCHEMA -//----------------- - -// inherits all properties of baseInv schema using discriminator -// each document derived from this schema includes key field { __t: "consumable" } -// ex: screws, nails, staples - //------------- -// TOOLS SCHEMA +// TOOL SCHEMAS //------------- // inherits all properties of baseInv schema using discriminator // each document derived from this schema includes key field { __t: "tool" } -// ex: power drills, wheelbarrows, shovels +// ex: power drills, wheelbarrows, shovels, jackhammers + +// Base Tool Schema: const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ code: { type: Number, required: true }, // add function to create code for on-site tool tracking purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, - // add discriminator based on rental or purchase so these fields are required if tool is rented - rentedOnDate: Date, - rentalDue: Date, - userResponsible: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - purchaseRecord: [{ // track purchase/rental requests - _id: false, // do not add _id field to subdocument - date: { type: Date, default: Date.now() }, - requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, - brand: String, - status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, - }], + imgUrl: String, updateRecord: [{ // track tool condition updates _id: false, date: { type: Date, default: Date.now() }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, }], - logRecord: [{ // track tool daily check in/out and use + logRecord: [{ // track tool daily check in/out and responsible user _id: false, date: { type: Date, default: Date.now() }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - type: { type: String, enum: ['Check In', 'Check Out'] }, // default = opposite of current log status? + type: { type: String, enum: ['Check In', 'Check Out'] }, }], })); +// Rented Tool Schema: +// inherits all properties of buildingTool schema using discriminator +// each document derived from this schema includes key field { __t: "tool_rental" } + +// const buildingToolRental = buildingTool.discriminator('tool_rental', new mongoose.Schema({ +// rentedOnDate: { type: Date, required: true }, +// rentalDueDate: { type: Date, required: true }, +// })); + +//------------------ +// EQUIPMENT SCHEMAS +//------------------ + +// inherits all properties of baseInv schema using discriminator +// each document derived from this schema includes key field { __t: "equipment" } +// items in this category are assumed to be rented +// ex: tractors, excavators, bulldozers + +const buildingEquipment = baseInv.discriminator('equipment', new mongoose.Schema({ + isTracked: { type: Boolean, required: true }, + assetTracker: String, + // add rental record? +})); + +// add purchase varient instead of rental varient? module.exports = { buildingMaterial, + buildingConsumable, buildingReusable, + buildingTool, + // buildingToolRental, + buildingEquipment, }; From ef31db0d43ac6f8c2c6f79b6b9d859502776547a Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 12 Dec 2023 10:12:45 -0800 Subject: [PATCH 100/129] delete redundant files. add basic equipment schema --- src/models/bmdashboard/baseInvSchema.js | 23 ----------- .../bmdashboard/buildingInventoryItem.js | 41 ++++++++++++------- src/models/bmdashboard/buildingReusable.js | 21 ---------- 3 files changed, 26 insertions(+), 59 deletions(-) delete mode 100644 src/models/bmdashboard/baseInvSchema.js delete mode 100644 src/models/bmdashboard/buildingReusable.js diff --git a/src/models/bmdashboard/baseInvSchema.js b/src/models/bmdashboard/baseInvSchema.js deleted file mode 100644 index 7eff943e7..000000000 --- a/src/models/bmdashboard/baseInvSchema.js +++ /dev/null @@ -1,23 +0,0 @@ -const mongoose = require('mongoose'); - -// base schema for all categories of inventory (Consumable, Material, Reusable, Tool, Equipment) -// this schema is extended by the individual schemas for each inventory type -// all documents derived from this schema are saved to the collection 'buildingInventoryItems' - -const baseInvSchema = mongoose.Schema({ - itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, - project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, - purchaseRecord: [{ - _id: false, // do not add _id field to subdocument - date: { type: Date, default: Date.now() }, - requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantity: { type: Number, required: true }, - priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, - brandPref: String, - status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, - }], -}); - -const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); - -module.exports = baseInv; diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index 845243331..ed9c52ebc 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -15,7 +15,7 @@ const baseInvSchema = mongoose.Schema({ _id: false, // do not add _id field to subdocument date: { type: Date, default: Date.now() }, requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantity: { type: Number, required: true }, + quantity: { type: Number, required: true, default: 1 }, // default 1 for tool or equipment purchases priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, brandPref: String, status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, @@ -100,8 +100,11 @@ const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ // Base Tool Schema: const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ - code: { type: Number, required: true }, // add function to create code for on-site tool tracking + code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, + // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) + rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, + rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, imgUrl: String, updateRecord: [{ // track tool condition updates _id: false, @@ -118,15 +121,6 @@ const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ }], })); -// Rented Tool Schema: -// inherits all properties of buildingTool schema using discriminator -// each document derived from this schema includes key field { __t: "tool_rental" } - -// const buildingToolRental = buildingTool.discriminator('tool_rental', new mongoose.Schema({ -// rentedOnDate: { type: Date, required: true }, -// rentalDueDate: { type: Date, required: true }, -// })); - //------------------ // EQUIPMENT SCHEMAS //------------------ @@ -137,9 +131,27 @@ const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ // ex: tractors, excavators, bulldozers const buildingEquipment = baseInv.discriminator('equipment', new mongoose.Schema({ - isTracked: { type: Boolean, required: true }, - assetTracker: String, - // add rental record? + isTracked: { type: Boolean, required: true }, // has asset tracker + assetTracker: { type: String, required: () => this.isTracked }, // required if isTracked = true (syntax?) + code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking + purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, + // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) + rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, + rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, + imgUrl: String, + updateRecord: [{ // track equipment condition updates + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, + }], + logRecord: [{ // track tool daily check in/out and responsible user + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + type: { type: String, enum: ['Check In', 'Check Out'] }, + }], })); // add purchase varient instead of rental varient? @@ -149,6 +161,5 @@ module.exports = { buildingConsumable, buildingReusable, buildingTool, - // buildingToolRental, buildingEquipment, }; diff --git a/src/models/bmdashboard/buildingReusable.js b/src/models/bmdashboard/buildingReusable.js deleted file mode 100644 index 1f2ceaa48..000000000 --- a/src/models/bmdashboard/buildingReusable.js +++ /dev/null @@ -1,21 +0,0 @@ -const mongoose = require('mongoose'); - -const baseInv = require('./baseInvSchema'); - -// inherits all properties of baseInv schema using discriminator -// each document derived from this schema includes key field { __t: "buildingReusable" } - -const buildingReusable = baseInv.discriminator('buildingReusable', new mongoose.Schema({ - stockBought: { type: Number, default: 0 }, - stockDestroyed: { type: Number, default: 0 }, - stockAvailable: { type: Number, default: 0 }, - updateRecord: [{ - _id: false, - date: { type: Date, required: true }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: { type: Number, required: true }, - quantityDestroyed: { type: Number, required: true }, - }], -})); - -module.exports = buildingReusable; From 6cc5807680d68381b3446d49eaca951f51b33ea3 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 12 Dec 2023 12:03:57 -0800 Subject: [PATCH 101/129] experimenting with new schema varient --- .../bmdashboard/buildingInventoryItem.js | 217 ++++++++++++------ 1 file changed, 146 insertions(+), 71 deletions(-) diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index ed9c52ebc..a9559a45d 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -8,9 +8,28 @@ const mongoose = require('mongoose'); // this schema is extended by the individual schemas for each inventory type // all documents derived from this schema are saved to the collection 'buildingInventoryItems' -const baseInvSchema = mongoose.Schema({ +// const baseInvSchema = mongoose.Schema({ +// itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, +// project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, +// purchaseRecord: [{ +// _id: false, // do not add _id field to subdocument +// date: { type: Date, default: Date.now() }, +// requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantity: { type: Number, required: true, default: 1 }, // default 1 for tool or equipment purchases +// priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, +// brandPref: String, +// status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, +// }], +// }); + +// const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); + +const baseSchemaForMaterialReusableConsumable = mongoose.Schema({ itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project + // TODO: can stockAvailable default be a function? + stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) purchaseRecord: [{ _id: false, // do not add _id field to subdocument date: { type: Date, default: Date.now() }, @@ -20,9 +39,41 @@ const baseInvSchema = mongoose.Schema({ brandPref: String, status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, }], + updateRecord: [{ + _id: false, + date: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantityUsed: { type: Number, required: true }, + quantityWasted: { type: Number, required: true }, + }], }); -const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); +const baseInvSmallItems = mongoose.model('buildingInvSmallItems', baseSchemaForMaterialReusableConsumable, 'buildingInventoryItems'); + +const baseSchemaForToolEquipment = mongoose.Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, + purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, + // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) + rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, + rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, + imgUrl: String, + updateRecord: [{ // track tool condition updates + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, + }], + logRecord: [{ // track tool daily check in/out and responsible user + _id: false, + date: { type: Date, default: Date.now() }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + type: { type: String, enum: ['Check In', 'Check Out'] }, + }], +}); + +const baseInvLargeItems = mongoose.model('buildingInvLargeItems', baseSchemaForToolEquipment, 'buildingInventoryItems'); //----------------- // MATERIALS SCHEMA @@ -32,18 +83,23 @@ const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInve // each document derived from this schema includes key field { __t: "material" } // ex: sand, stone, bricks, lumber, insulation -const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ - stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project +// const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ +// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project +// stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused +// stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock +// stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) +// updateRecord: [{ +// _id: false, +// date: { type: Date, required: true }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantityUsed: { type: Number, required: true }, +// quantityWasted: { type: Number, required: true }, +// }], +// })); + +const buildingMaterial = baseInvSmallItems.discriminator('material', new mongoose.Schema({ stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock - stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) - updateRecord: [{ - _id: false, - date: { type: Date, required: true }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: { type: Number, required: true }, - quantityWasted: { type: Number, required: true }, - }], })); //----------------- @@ -54,18 +110,23 @@ const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ // each document derived from this schema includes key field { __t: "consumable" } // ex: screws, nails, staples -const buildingConsumable = baseInv.discriminator('consumable', new mongoose.Schema({ - stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project +// const buildingConsumable = baseInv.discriminator('consumable', new mongoose.Schema({ +// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project +// stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused +// stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock +// stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) +// updateRecord: [{ +// _id: false, +// date: { type: Date, required: true }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantityUsed: { type: Number, required: true }, +// quantityWasted: { type: Number, required: true }, +// }], +// })); + +const buildingConsumable = baseInvSmallItems.discriminator('consumable', new mongoose.Schema({ stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock - stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) - updateRecord: [{ - _id: false, - date: { type: Date, required: true }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: { type: Number, required: true }, - quantityWasted: { type: Number, required: true }, - }], })); //----------------- @@ -76,17 +137,21 @@ const buildingConsumable = baseInv.discriminator('consumable', new mongoose.Sche // each document derived from this schema includes key field { __t: "reusable" } // ex: hammers, screwdrivers, mallets, brushes, gloves -const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ - stockBought: { type: Number, default: 0 }, +// const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ +// stockBought: { type: Number, default: 0 }, +// stockDestroyed: { type: Number, default: 0 }, +// stockAvailable: { type: Number, default: 0 }, +// updateRecord: [{ +// _id: false, +// date: { type: Date, required: true }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// quantityUsed: { type: Number, required: true }, +// quantityDestroyed: { type: Number, required: true }, +// }], +// })); + +const buildingReusable = baseInvSmallItems.discriminator('reusable', new mongoose.Schema({ stockDestroyed: { type: Number, default: 0 }, - stockAvailable: { type: Number, default: 0 }, - updateRecord: [{ - _id: false, - date: { type: Date, required: true }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - quantityUsed: { type: Number, required: true }, - quantityDestroyed: { type: Number, required: true }, - }], })); //------------- @@ -99,28 +164,33 @@ const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ // Base Tool Schema: -const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ +// const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ +// code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking +// purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, +// // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) +// rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, +// rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, +// imgUrl: String, +// updateRecord: [{ // track tool condition updates +// _id: false, +// date: { type: Date, default: Date.now() }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, +// }], +// logRecord: [{ // track tool daily check in/out and responsible user +// _id: false, +// date: { type: Date, default: Date.now() }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// type: { type: String, enum: ['Check In', 'Check Out'] }, +// }], +// })); + +const buildingTool = baseInvLargeItems.discriminator('tool', new mongoose.Schema({ code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking - purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, - // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) - rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, - rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, - imgUrl: String, - updateRecord: [{ // track tool condition updates - _id: false, - date: { type: Date, default: Date.now() }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, - }], - logRecord: [{ // track tool daily check in/out and responsible user - _id: false, - date: { type: Date, default: Date.now() }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - type: { type: String, enum: ['Check In', 'Check Out'] }, - }], })); + //------------------ // EQUIPMENT SCHEMAS //------------------ @@ -130,28 +200,33 @@ const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ // items in this category are assumed to be rented // ex: tractors, excavators, bulldozers -const buildingEquipment = baseInv.discriminator('equipment', new mongoose.Schema({ +// const buildingEquipment = baseInv.discriminator('equipment', new mongoose.Schema({ +// isTracked: { type: Boolean, required: true }, // has asset tracker +// assetTracker: { type: String, required: () => this.isTracked }, // required if isTracked = true (syntax?) +// code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking +// purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, +// // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) +// rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, +// rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, +// imgUrl: String, +// updateRecord: [{ // track equipment condition updates +// _id: false, +// date: { type: Date, default: Date.now() }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, +// }], +// logRecord: [{ // track tool daily check in/out and responsible user +// _id: false, +// date: { type: Date, default: Date.now() }, +// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, +// type: { type: String, enum: ['Check In', 'Check Out'] }, +// }], +// })); + +const buildingEquipment = baseInvLargeItems.discriminator('equipment', new mongoose.Schema({ isTracked: { type: Boolean, required: true }, // has asset tracker assetTracker: { type: String, required: () => this.isTracked }, // required if isTracked = true (syntax?) - code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking - purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, - // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) - rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, - rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, - imgUrl: String, - updateRecord: [{ // track equipment condition updates - _id: false, - date: { type: Date, default: Date.now() }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, - }], - logRecord: [{ // track tool daily check in/out and responsible user - _id: false, - date: { type: Date, default: Date.now() }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, - type: { type: String, enum: ['Check In', 'Check Out'] }, - }], })); // add purchase varient instead of rental varient? From f92d83360f7cca247068b393d576c2cf3baec0dc Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 12 Dec 2023 12:29:45 -0800 Subject: [PATCH 102/129] refactor file --- .../bmdashboard/buildingInventoryItem.js | 184 +++++------------- 1 file changed, 46 insertions(+), 138 deletions(-) diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index a9559a45d..5e8b24916 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -4,27 +4,13 @@ const mongoose = require('mongoose'); // BASE INVENTORY SCHEMAS //----------------------- -// base schema for all categories of inventory (Consumable, Material, Reusable, Tool, Equipment) -// this schema is extended by the individual schemas for each inventory type -// all documents derived from this schema are saved to the collection 'buildingInventoryItems' - -// const baseInvSchema = mongoose.Schema({ -// itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, -// project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, -// purchaseRecord: [{ -// _id: false, // do not add _id field to subdocument -// date: { type: Date, default: Date.now() }, -// requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantity: { type: Number, required: true, default: 1 }, // default 1 for tool or equipment purchases -// priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, -// brandPref: String, -// status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, -// }], -// }); - -// const baseInv = mongoose.model('buildingInventory', baseInvSchema, 'buildingInventoryItems'); - -const baseSchemaForMaterialReusableConsumable = mongoose.Schema({ +// TODO: purchaseRecord subdocs may be changed to purchaseRequests. A new purchaseRecord subdoc may be added to track purchases and costs for the item. + +// SMALL ITEMS BASE +// base schema for Consumable, Material, Reusable +// documents stored in 'buildingInventoryItems' collection + +const smallItemBaseSchema = mongoose.Schema({ itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project @@ -48,16 +34,29 @@ const baseSchemaForMaterialReusableConsumable = mongoose.Schema({ }], }); -const baseInvSmallItems = mongoose.model('buildingInvSmallItems', baseSchemaForMaterialReusableConsumable, 'buildingInventoryItems'); +const smallItemBase = mongoose.model('smallItemBase', smallItemBaseSchema, 'buildingInventoryItems'); + +// LARGE ITEMS BASE +// base schema for Tool, Equipment +// documents stored in 'buildingInventoryItems' collection -const baseSchemaForToolEquipment = mongoose.Schema({ +const largeItemBaseSchema = mongoose.Schema({ itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, - imgUrl: String, + imageUrl: String, + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + makeModelPref: String, + estTimeRequired: { type: Number, required: true }, // estimated time required on site + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], updateRecord: [{ // track tool condition updates _id: false, date: { type: Date, default: Date.now() }, @@ -73,164 +72,73 @@ const baseSchemaForToolEquipment = mongoose.Schema({ }], }); -const baseInvLargeItems = mongoose.model('buildingInvLargeItems', baseSchemaForToolEquipment, 'buildingInventoryItems'); +const largeItemBase = mongoose.model('largeItemBase', largeItemBaseSchema, 'buildingInventoryItems'); //----------------- // MATERIALS SCHEMA //----------------- -// inherits all properties of baseInv schema using discriminator +// inherits all properties of smallItemBaseSchema // each document derived from this schema includes key field { __t: "material" } // ex: sand, stone, bricks, lumber, insulation -// const buildingMaterial = baseInv.discriminator('material', new mongoose.Schema({ -// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project -// stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused -// stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock -// stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) -// updateRecord: [{ -// _id: false, -// date: { type: Date, required: true }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantityUsed: { type: Number, required: true }, -// quantityWasted: { type: Number, required: true }, -// }], -// })); - -const buildingMaterial = baseInvSmallItems.discriminator('material', new mongoose.Schema({ +const buildingMaterial = smallItemBase.discriminator('material', new mongoose.Schema({ stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock })); -//----------------- -// CONSUMABLES SCHEMA -//----------------- +//------------------ +// CONSUMABLE SCHEMA +//------------------ -// inherits all properties of baseInv schema using discriminator +// inherits all properties of smallItemBaseSchema // each document derived from this schema includes key field { __t: "consumable" } // ex: screws, nails, staples -// const buildingConsumable = baseInv.discriminator('consumable', new mongoose.Schema({ -// stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project -// stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused -// stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock -// stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) -// updateRecord: [{ -// _id: false, -// date: { type: Date, required: true }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantityUsed: { type: Number, required: true }, -// quantityWasted: { type: Number, required: true }, -// }], -// })); - -const buildingConsumable = baseInvSmallItems.discriminator('consumable', new mongoose.Schema({ +const buildingConsumable = smallItemBase.discriminator('consumable', new mongoose.Schema({ stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock })); -//----------------- -// REUSABLES SCHEMA -//----------------- +//---------------- +// REUSABLE SCHEMA +//---------------- -// inherits all properties of baseInv schema using discriminator +// inherits all properties of smallItemBaseSchema // each document derived from this schema includes key field { __t: "reusable" } // ex: hammers, screwdrivers, mallets, brushes, gloves -// const buildingReusable = baseInv.discriminator('reusable', new mongoose.Schema({ -// stockBought: { type: Number, default: 0 }, -// stockDestroyed: { type: Number, default: 0 }, -// stockAvailable: { type: Number, default: 0 }, -// updateRecord: [{ -// _id: false, -// date: { type: Date, required: true }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// quantityUsed: { type: Number, required: true }, -// quantityDestroyed: { type: Number, required: true }, -// }], -// })); - -const buildingReusable = baseInvSmallItems.discriminator('reusable', new mongoose.Schema({ +const buildingReusable = smallItemBase.discriminator('reusable', new mongoose.Schema({ stockDestroyed: { type: Number, default: 0 }, })); -//------------- -// TOOL SCHEMAS -//------------- +//------------ +// TOOL SCHEMA +//------------ -// inherits all properties of baseInv schema using discriminator +// inherits all properties of largeItemBaseSchema // each document derived from this schema includes key field { __t: "tool" } // ex: power drills, wheelbarrows, shovels, jackhammers -// Base Tool Schema: - -// const buildingTool = baseInv.discriminator('tool', new mongoose.Schema({ -// code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking -// purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, -// // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) -// rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, -// rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, -// imgUrl: String, -// updateRecord: [{ // track tool condition updates -// _id: false, -// date: { type: Date, default: Date.now() }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, -// }], -// logRecord: [{ // track tool daily check in/out and responsible user -// _id: false, -// date: { type: Date, default: Date.now() }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// type: { type: String, enum: ['Check In', 'Check Out'] }, -// }], -// })); - -const buildingTool = baseInvLargeItems.discriminator('tool', new mongoose.Schema({ +const buildingTool = largeItemBase.discriminator('tool', new mongoose.Schema({ code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking })); -//------------------ -// EQUIPMENT SCHEMAS -//------------------ +//----------------- +// EQUIPMENT SCHEMA +//----------------- -// inherits all properties of baseInv schema using discriminator +// inherits all properties of largeItemBaseSchema // each document derived from this schema includes key field { __t: "equipment" } // items in this category are assumed to be rented // ex: tractors, excavators, bulldozers -// const buildingEquipment = baseInv.discriminator('equipment', new mongoose.Schema({ -// isTracked: { type: Boolean, required: true }, // has asset tracker -// assetTracker: { type: String, required: () => this.isTracked }, // required if isTracked = true (syntax?) -// code: { type: Number, required: true }, // TODO: add function to create simple numeric code for on-site tool tracking -// purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, -// // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) -// rentedOnDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, -// rentalDueDate: { type: Date, required: () => this.purchaseStatus === 'Rental' }, -// imgUrl: String, -// updateRecord: [{ // track equipment condition updates -// _id: false, -// date: { type: Date, default: Date.now() }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// condition: { type: String, enum: ['Good', 'Needs Repair', 'Out of Order'] }, -// }], -// logRecord: [{ // track tool daily check in/out and responsible user -// _id: false, -// date: { type: Date, default: Date.now() }, -// createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// responsibleUser: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, -// type: { type: String, enum: ['Check In', 'Check Out'] }, -// }], -// })); - -const buildingEquipment = baseInvLargeItems.discriminator('equipment', new mongoose.Schema({ +const buildingEquipment = largeItemBase.discriminator('equipment', new mongoose.Schema({ isTracked: { type: Boolean, required: true }, // has asset tracker assetTracker: { type: String, required: () => this.isTracked }, // required if isTracked = true (syntax?) })); -// add purchase varient instead of rental varient? - module.exports = { buildingMaterial, buildingConsumable, From bae46b1c69333910a54d0d914a5faaec6ed31246 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 12 Dec 2023 12:43:52 -0800 Subject: [PATCH 103/129] undo changes to current material routes to avoid breaking things --- .../bmdashboard/bmMaterialsController.js | 2 +- src/models/bmdashboard/buildingMaterial.js | 30 +++++++++++-------- src/startup/routes.js | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 2c79bd9b9..911ca0b55 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -48,7 +48,7 @@ const bmMaterialsController = function (ItemMaterial,BuildingMaterial) { const newPurchaseRecord = { quantity, priority, - brandPref: brand, + brand, requestedBy: requestorId, }; try { diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index caf61e4c0..bc86884ed 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -1,23 +1,29 @@ const mongoose = require('mongoose'); -const baseInv = require('./baseInvSchema'); +const { Schema } = mongoose; -// inherits all properties of baseInv schema using discriminator -// each document derived from this schema includes key field { __t: "buildingMaterial" } - -const buildingMaterial = baseInv.discriminator('buildingMaterial', new mongoose.Schema({ +const buildingMaterial = new Schema({ + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project - stockUsed: { type: Number, default: 0 }, // stock that has been used up and cannot be reused - stockWasted: { type: Number, default: 0 }, // ruined or destroyed stock - stockAvailable: { type: Number, default: 0 }, // available = bought - (used + wasted/destroyed) + stockUsed: { type: Number, default: 0 }, // total amount of item used successfully in the project + stockWasted: { type: Number, default: 0 }, // total amount of item wasted/ruined/lost in the project + stockAvailable: { type: Number, default: 0 }, // bought - (used + wasted) + purchaseRecord: [{ + _id: false, // do not add _id field to subdocument + date: { type: Date, default: Date.now() }, + requestedBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, + quantity: { type: Number, required: true }, + priority: { type: String, enum: ['Low', 'Medium', 'High'], required: true }, + brand: String, + status: { type: String, default: 'Pending', enum: ['Approved', 'Pending', 'Rejected'] }, + }], updateRecord: [{ _id: false, date: { type: Date, required: true }, createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, quantityUsed: { type: Number, required: true }, quantityWasted: { type: Number, required: true }, - test: { type: String, default: 'testing this field' }, }], -})); - -module.exports = buildingMaterial; +}); +module.exports = mongoose.model('buildingMaterial', buildingMaterial, 'buildingMaterials'); diff --git a/src/startup/routes.js b/src/startup/routes.js index 37832c199..ff5d01b2b 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -23,7 +23,7 @@ const inventoryItemMaterial = require('../models/inventoryItemMaterial'); const mapLocations = require('../models/mapLocation'); const buildingProject = require('../models/bmdashboard/buildingProject'); const buildingInventoryType = require('../models/bmdashboard/buildingInventoryType'); -const { buildingMaterial } = require('../models/bmdashboard/buildingInventoryItem'); +const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); const userProfileRouter = require('../routes/userProfileRouter')(userProfile); const badgeRouter = require('../routes/badgeRouter')(badge); From 230cedc459088655359f75cc7a1752be24a4ed40 Mon Sep 17 00:00:00 2001 From: Carl Bebli Date: Fri, 15 Dec 2023 15:41:22 +0000 Subject: [PATCH 104/129] added the replyTo feature --- src/controllers/timeEntryController.js | 366 +++++++++++++++---------- src/helpers/userHelper.js | 4 +- 2 files changed, 228 insertions(+), 142 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 962f30170..9c4d7c5e3 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -1,17 +1,17 @@ -const moment = require('moment-timezone'); -const mongoose = require('mongoose'); -const { getInfringementEmailBody } = require('../helpers/userHelper')(); -const userProfile = require('../models/userProfile'); -const task = require('../models/task'); -const emailSender = require('../utilities/emailSender'); -const { hasPermission } = require('../utilities/permissions'); +const moment = require("moment-timezone"); +const mongoose = require("mongoose"); +const { getInfringementEmailBody } = require("../helpers/userHelper")(); +const userProfile = require("../models/userProfile"); +const task = require("../models/task"); +const emailSender = require("../utilities/emailSender"); +const { hasPermission } = require("../utilities/permissions"); const formatSeconds = function (seconds) { const formattedseconds = parseInt(seconds, 10); const values = `${Math.floor( - moment.duration(formattedseconds, 'seconds').asHours(), - )}:${moment.duration(formattedseconds, 'seconds').minutes()}`; - return values.split(':'); + moment.duration(formattedseconds, "seconds").asHours() + )}:${moment.duration(formattedseconds, "seconds").minutes()}`; + return values.split(":"); }; /** @@ -24,9 +24,20 @@ const formatSeconds = function (seconds) { * @param {*} requestor The userProfile object of the person that modified the time entry * @returns {String} */ -const getEditedTimeEntryEmailBody = (firstName, lastName, email, originalTime, finalTime, requestor) => { - const formattedOriginal = moment.utc(originalTime * 1000).format('HH[ hours ]mm[ minutes]'); - const formattedFinal = moment.utc(finalTime * 1000).format('HH[ hours ]mm[ minutes]'); +const getEditedTimeEntryEmailBody = ( + firstName, + lastName, + email, + originalTime, + finalTime, + requestor +) => { + const formattedOriginal = moment + .utc(originalTime * 1000) + .format("HH[ hours ]mm[ minutes]"); + const formattedFinal = moment + .utc(finalTime * 1000) + .format("HH[ hours ]mm[ minutes]"); return ` A time entry belonging to ${firstName} ${lastName} (${email}) was modified by ${requestor.firstName} ${requestor.lastName} (${requestor.email}). The entry's duration was changed from [${formattedOriginal}] to [${formattedFinal}] @@ -45,18 +56,39 @@ const notifyEditByEmail = async (personId, original, finalTime, final) => { try { const originalTime = original.totalSeconds; const record = await userProfile.findById(personId); - const requestor = (personId !== final.requestor.requestorId) ? await userProfile.findById(final.requestor.requestorId) : record; - const emailBody = getEditedTimeEntryEmailBody(record.firstName, record.lastName, record.email, originalTime, finalTime, requestor); - emailSender('onecommunityglobal@gmail.com', `A Time Entry was Edited for ${record.firstName} ${record.lastName}`, emailBody); + const requestor = + personId !== final.requestor.requestorId + ? await userProfile.findById(final.requestor.requestorId) + : record; + const emailBody = getEditedTimeEntryEmailBody( + record.firstName, + record.lastName, + record.email, + originalTime, + finalTime, + requestor + ); + emailSender( + "onecommunityglobal@gmail.com", + `A Time Entry was Edited for ${record.firstName} ${record.lastName}`, + emailBody + ); } catch (error) { - throw new Error(`Failed to send email notification about the modification of time entry belonging to user with id ${personId}`); + throw new Error( + `Failed to send email notification about the modification of time entry belonging to user with id ${personId}` + ); } }; -const notifyTaskOvertimeEmailBody = async (personId, taskName, estimatedHours, hoursLogged) => { +const notifyTaskOvertimeEmailBody = async ( + personId, + taskName, + estimatedHours, + hoursLogged +) => { try { - const record = await userProfile.findById(personId); - const text = `Dear ${record.firstName}${record.lastName}, + const record = await userProfile.findById(personId); + const text = `Dear ${record.firstName}${record.lastName},

Oops, it looks like you have logged more hours than estimated for a task

Task Name : ${taskName}

Time Estimated : ${estimatedHours}

@@ -64,24 +96,37 @@ const notifyTaskOvertimeEmailBody = async (personId, taskName, estimatedHours, h

Please connect with your manager to explain what happened and submit a new hours estimation for completion.

Thank you,

One Community

`; - emailSender( - record.email, - 'Logged more hours than estimated for a task', - text, - 'onecommunityglobal@gmail.com', - null, + emailSender( + record.email, + "Logged more hours than estimated for a task", + text, + "onecommunityglobal@gmail.com", + null, + record.email, + null ); } catch (error) { - console.log(`Failed to send email notification about the overtime for a task belonging to user with id ${personId}`); + console.log( + `Failed to send email notification about the overtime for a task belonging to user with id ${personId}` + ); } }; const checkTaskOvertime = async (timeentry, record, currentTask) => { try { // send email notification if logged in hours exceeds estiamted hours for a task - if (currentTask.hoursLogged > currentTask.estimatedHours) { notifyTaskOvertimeEmailBody(timeentry.personId.toString(), currentTask.taskName, currentTask.estimatedHours, currentTask.hoursLogged); } + if (currentTask.hoursLogged > currentTask.estimatedHours) { + notifyTaskOvertimeEmailBody( + timeentry.personId.toString(), + currentTask.taskName, + currentTask.estimatedHours, + currentTask.hoursLogged + ); + } } catch (error) { - console.log(`Failed to find task whose logged-in hours are more than estimated hours ${record.email}`); + console.log( + `Failed to find task whose logged-in hours are more than estimated hours ${record.email}` + ); } }; @@ -92,31 +137,58 @@ const timeEntrycontroller = function (TimeEntry) { try { if (!req.params.timeEntryId) { - return res.status(400).send({ error: 'ObjectId in request param is not in correct format' }); + return res + .status(400) + .send({ + error: "ObjectId in request param is not in correct format", + }); } - if (!mongoose.Types.ObjectId.isValid(req.params.timeEntryId) || !mongoose.Types.ObjectId.isValid(req.body.projectId)) { - return res.status(400).send({ error: 'ObjectIds are not correctly formed' }); + if ( + !mongoose.Types.ObjectId.isValid(req.params.timeEntryId) || + !mongoose.Types.ObjectId.isValid(req.body.projectId) + ) { + return res + .status(400) + .send({ error: "ObjectIds are not correctly formed" }); } // Get initial timeEntry by timeEntryId const timeEntry = await TimeEntry.findById(req.params.timeEntryId); if (!timeEntry) { - return res.status(400).send({ error: `No valid records found for ${req.params.timeEntryId}` }); + return res + .status(400) + .send({ + error: `No valid records found for ${req.params.timeEntryId}`, + }); } - if (!(await hasPermission(req.body.requestor, 'editTimeEntry') || timeEntry.personId.toString() === req.body.requestor.requestorId.toString())) { - return res.status(403).send({ error: 'Unauthorized request' }); + if ( + !( + (await hasPermission(req.body.requestor, "editTimeEntry")) || + timeEntry.personId.toString() === + req.body.requestor.requestorId.toString() + ) + ) { + return res.status(403).send({ error: "Unauthorized request" }); } - const hours = req.body.hours ? req.body.hours : '00'; - const minutes = req.body.minutes ? req.body.minutes : '00'; + const hours = req.body.hours ? req.body.hours : "00"; + const minutes = req.body.minutes ? req.body.minutes : "00"; const totalSeconds = moment.duration(`${hours}:${minutes}`).asSeconds(); - if (timeEntry.isTangible === true && totalSeconds !== timeEntry.totalSeconds) { - notifyEditByEmail(timeEntry.personId.toString(), timeEntry, totalSeconds, req.body); + if ( + timeEntry.isTangible === true && + totalSeconds !== timeEntry.totalSeconds + ) { + notifyEditByEmail( + timeEntry.personId.toString(), + timeEntry, + totalSeconds, + req.body + ); } const initialSeconds = timeEntry.totalSeconds; @@ -130,7 +202,7 @@ const timeEntrycontroller = function (TimeEntry) { timeEntry.isTangible = req.body.isTangible; timeEntry.lastModifiedDateTime = moment().utc().toISOString(); timeEntry.projectId = mongoose.Types.ObjectId(req.body.projectId); - timeEntry.dateOfWork = moment(req.body.dateOfWork).format('YYYY-MM-DD'); + timeEntry.dateOfWork = moment(req.body.dateOfWork).format("YYYY-MM-DD"); // Update the hoursLogged field of related tasks based on before and after timeEntries // initialIsTangible is a bealoon value, req.body.isTangible is a string @@ -138,11 +210,11 @@ const timeEntrycontroller = function (TimeEntry) { try { if (findTask) { if (initialIsTangible === true) { - findTask.hoursLogged -= (initialSeconds / 3600); + findTask.hoursLogged -= initialSeconds / 3600; } if (req.body.isTangible === true) { - findTask.hoursLogged += (totalSeconds / 3600); + findTask.hoursLogged += totalSeconds / 3600; } await findTask.save(); @@ -152,14 +224,17 @@ const timeEntrycontroller = function (TimeEntry) { } // Update edit history - if (initialSeconds !== totalSeconds - && timeEntry.isTangible - && req.body.requestor.requestorId === timeEntry.personId.toString() - && !await hasPermission(req.body.requestor, 'editTimeEntry') - ) { - const requestor = await userProfile.findById(req.body.requestor.requestorId); + if ( + initialSeconds !== totalSeconds && + timeEntry.isTangible && + req.body.requestor.requestorId === timeEntry.personId.toString() && + !(await hasPermission(req.body.requestor, "editTimeEntry")) + ) { + const requestor = await userProfile.findById( + req.body.requestor.requestorId + ); requestor.timeEntryEditHistory.push({ - date: moment().tz('America/Los_Angeles').toDate(), + date: moment().tz("America/Los_Angeles").toDate(), initialSeconds, newSeconds: totalSeconds, }); @@ -168,18 +243,23 @@ const timeEntrycontroller = function (TimeEntry) { let totalRecentEdits = 0; requestor.timeEntryEditHistory.forEach((edit) => { - if (moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365) { + if ( + moment().tz("America/Los_Angeles").diff(edit.date, "days") <= 365 + ) { totalRecentEdits += 1; } }); if (totalRecentEdits >= 5) { requestor.infringements.push({ - date: moment().tz('America/Los_Angeles'), + date: moment().tz("America/Los_Angeles"), description: `${totalRecentEdits} time entry edits in the last calendar year`, }); - emailSender('onecommunityglobal@gmail.com', `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, ` + emailSender( + "onecommunityglobal@gmail.com", + `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, + `

${requestor.firstName} ${requestor.lastName} (${requestor.email}) was issued a blue square for editing their time entries ${totalRecentEdits} times within the last calendar year. @@ -187,28 +267,39 @@ const timeEntrycontroller = function (TimeEntry) {

This is the ${totalRecentEdits}th edit within the past 365 days.

- `); + ` + ); const emailInfringement = { - date: moment().tz('America/Los_Angeles').format('MMMM-DD-YY'), + date: moment().tz("America/Los_Angeles").format("MMMM-DD-YY"), description: `You edited your time entries ${totalRecentEdits} times within the last 365 days, exceeding the limit of 4 times per year you can edit them without penalty.`, }; - emailSender(requestor.email, 'You\'ve been issued a blue square for editing your time entry', getInfringementEmailBody(requestor.firstName, requestor.lastName, emailInfringement, requestor.infringements.length)); + emailSender( + requestor.email, + "You've been issued a blue square for editing your time entry", + getInfringementEmailBody( + requestor.firstName, + requestor.lastName, + emailInfringement, + requestor.infringements.length + ) + ); } await requestor.save(); } - await timeEntry.save(); - res.status(200).send({ message: 'Successfully updated time entry' }); + res.status(200).send({ message: "Successfully updated time entry" }); // If the time entry isn't related to a task (i.e. it's a project), then don't check for overtime (Most likely pr team) if (findTask) { // checking if logged in hours exceed estimated time after timeentry edit for a task - const record = await userProfile.findById(timeEntry.personId.toString()); + const record = await userProfile.findById( + timeEntry.personId.toString() + ); const currentTask = await task.findById(req.body.projectId); checkTaskOvertime(timeEntry, record, currentTask); } @@ -233,9 +324,9 @@ const timeEntrycontroller = function (TimeEntry) { timeentry.personId = element.personId; timeentry.projectId = element.projectId; timeentry.dateOfWork = element.dateOfWork; - timeentry.timeSpent = moment('1900-01-01 00:00:00') - .add(element.totalSeconds, 'seconds') - .format('HH:mm:ss'); + timeentry.timeSpent = moment("1900-01-01 00:00:00") + .add(element.totalSeconds, "seconds") + .format("HH:mm:ss"); timeentry.notes = element.notes; timeentry.isTangible = element.isTangible; items.push(timeentry); @@ -246,21 +337,21 @@ const timeEntrycontroller = function (TimeEntry) { const postTimeEntry = async function (req, res) { if ( - !mongoose.Types.ObjectId.isValid(req.body.personId) - || !mongoose.Types.ObjectId.isValid(req.body.projectId) - || !req.body.dateOfWork - || !moment(req.body.dateOfWork).isValid() - || !req.body.timeSpent - || !req.body.isTangible + !mongoose.Types.ObjectId.isValid(req.body.personId) || + !mongoose.Types.ObjectId.isValid(req.body.projectId) || + !req.body.dateOfWork || + !moment(req.body.dateOfWork).isValid() || + !req.body.timeSpent || + !req.body.isTangible ) { - res.status(400).send({ error: 'Bad request' }); + res.status(400).send({ error: "Bad request" }); return; } const timeentry = new TimeEntry(); const { dateOfWork, timeSpent } = req.body; timeentry.personId = req.body.personId; timeentry.projectId = req.body.projectId; - timeentry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); + timeentry.dateOfWork = moment(dateOfWork).format("YYYY-MM-DD"); timeentry.totalSeconds = moment.duration(timeSpent).asSeconds(); timeentry.notes = req.body.notes; timeentry.isTangible = req.body.isTangible; @@ -274,12 +365,14 @@ const timeEntrycontroller = function (TimeEntry) { .status(200) .send({ message: `Time Entry saved with id as ${results._id}` }); }) - .catch(error => res.status(400).send(error)); + .catch((error) => res.status(400).send(error)); - // Get the task related to this time entry, if not found, then it's a project sets to null - const currentTask = await task.findById(req.body.projectId).catch(() => null); + // Get the task related to this time entry, if not found, then it's a project sets to null + const currentTask = await task + .findById(req.body.projectId) + .catch(() => null); - // Add this tangbile time entry to related task's hoursLogged and checks if timeEntry is related to a task + // Add this tangbile time entry to related task's hoursLogged and checks if timeEntry is related to a task if (timeentry.isTangible === true && currentTask) { try { currentTask.hoursLogged += timeentry.totalSeconds / 3600; @@ -292,7 +385,9 @@ const timeEntrycontroller = function (TimeEntry) { // checking if logged in hours exceed estimated time after timeentry for a task, only if the time entry is related to a task (It might not be, if it's a project) if (currentTask) { try { - const record = await userProfile.findById(timeentry.personId.toString()); + const record = await userProfile.findById( + timeentry.personId.toString() + ); checkTaskOvertime(timeentry, record, currentTask); } catch (error) { throw new Error(error); @@ -302,19 +397,23 @@ const timeEntrycontroller = function (TimeEntry) { const getTimeEntriesForSpecifiedPeriod = function (req, res) { if ( - !req.params - || !req.params.fromdate - || !req.params.todate - || !req.params.userId - || !moment(req.params.fromdate).isValid() - || !moment(req.params.toDate).isValid() + !req.params || + !req.params.fromdate || + !req.params.todate || + !req.params.userId || + !moment(req.params.fromdate).isValid() || + !moment(req.params.toDate).isValid() ) { - res.status(400).send({ error: 'Invalid request' }); + res.status(400).send({ error: "Invalid request" }); return; } - const fromdate = moment(req.params.fromdate).tz('America/Los_Angeles').format('YYYY-MM-DD'); - const todate = moment(req.params.todate).tz('America/Los_Angeles').format('YYYY-MM-DD'); + const fromdate = moment(req.params.fromdate) + .tz("America/Los_Angeles") + .format("YYYY-MM-DD"); + const todate = moment(req.params.todate) + .tz("America/Los_Angeles") + .format("YYYY-MM-DD"); const { userId } = req.params; TimeEntry.aggregate([ @@ -326,18 +425,18 @@ const timeEntrycontroller = function (TimeEntry) { }, { $lookup: { - from: 'projects', - localField: 'projectId', - foreignField: '_id', - as: 'project', + from: "projects", + localField: "projectId", + foreignField: "_id", + as: "project", }, }, { $lookup: { - from: 'tasks', - localField: 'projectId', - foreignField: '_id', - as: 'task', + from: "tasks", + localField: "projectId", + foreignField: "_id", + as: "task", }, }, { @@ -349,41 +448,26 @@ const timeEntrycontroller = function (TimeEntry) { projectId: 1, lastModifiedDateTime: 1, projectName: { - $arrayElemAt: [ - '$project.projectName', - 0, - ], + $arrayElemAt: ["$project.projectName", 0], }, taskName: { - $arrayElemAt: [ - '$task.taskName', - 0, - ], + $arrayElemAt: ["$task.taskName", 0], }, category: { - $arrayElemAt: [ - '$project.category', - 0, - ], + $arrayElemAt: ["$project.category", 0], }, classification: { - $arrayElemAt: [ - '$task.classification', - 0, - ], + $arrayElemAt: ["$task.classification", 0], }, dateOfWork: 1, hours: { $floor: { - $divide: ['$totalSeconds', 3600], + $divide: ["$totalSeconds", 3600], }, }, minutes: { $floor: { - $divide: [ - { $mod: ['$totalSeconds', 3600] }, - 60, - ], + $divide: [{ $mod: ["$totalSeconds", 3600] }, 60], }, }, }, @@ -393,9 +477,11 @@ const timeEntrycontroller = function (TimeEntry) { lastModifiedDateTime: -1, }, }, - ]).then((results) => { - res.status(200).send(results); - }).catch(error => res.status(400).send(error)); + ]) + .then((results) => { + res.status(200).send(results); + }) + .catch((error) => res.status(400).send(error)); }; const getTimeEntriesForUsersList = function (req, res) { @@ -406,9 +492,9 @@ const timeEntrycontroller = function (TimeEntry) { personId: { $in: users }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, - ' -createdDateTime', + " -createdDateTime" ) - .populate('projectId') + .populate("projectId") .sort({ lastModifiedDateTime: -1 }) .then((results) => { const data = []; @@ -419,72 +505,73 @@ const timeEntrycontroller = function (TimeEntry) { record.notes = element.notes; record.isTangible = element.isTangible; record.personId = element.personId; - record.projectId = element.projectId ? element.projectId._id : ''; + record.projectId = element.projectId ? element.projectId._id : ""; record.projectName = element.projectId ? element.projectId.projectName - : ''; + : ""; record.dateOfWork = element.dateOfWork; [record.hours, record.minutes] = formatSeconds(element.totalSeconds); data.push(record); }); res.status(200).send(data); }) - .catch(error => res.status(400).send(error)); + .catch((error) => res.status(400).send(error)); }; const getTimeEntriesForSpecifiedProject = function (req, res) { if ( - !req.params - || !req.params.fromDate - || !req.params.toDate - || !req.params.projectId + !req.params || + !req.params.fromDate || + !req.params.toDate || + !req.params.projectId ) { - res.status(400).send({ error: 'Invalid request' }); + res.status(400).send({ error: "Invalid request" }); return; } - const todate = moment(req.params.toDate).format('YYYY-MM-DD'); - const fromDate = moment(req.params.fromDate).format('YYYY-MM-DD'); + const todate = moment(req.params.toDate).format("YYYY-MM-DD"); + const fromDate = moment(req.params.fromDate).format("YYYY-MM-DD"); const { projectId } = req.params; TimeEntry.find( { projectId, dateOfWork: { $gte: fromDate, $lte: todate }, }, - '-createdDateTime -lastModifiedDateTime', + "-createdDateTime -lastModifiedDateTime" ) - .populate('userId') + .populate("userId") .sort({ dateOfWork: -1 }) .then((results) => { res.status(200).send(results); }) - .catch(error => res.status(400).send(error)); + .catch((error) => res.status(400).send(error)); }; const deleteTimeEntry = async function (req, res) { if (!req.params.timeEntryId) { - res.status(400).send({ error: 'Bad request' }); + res.status(400).send({ error: "Bad request" }); return; } TimeEntry.findById(req.params.timeEntryId) .then(async (record) => { if (!record) { - res.status(400).send({ message: 'No valid record found' }); + res.status(400).send({ message: "No valid record found" }); return; } if ( - record.personId.toString() - === req.body.requestor.requestorId.toString() - || await hasPermission(req.body.requestor, 'deleteTimeEntry') + record.personId.toString() === + req.body.requestor.requestorId.toString() || + (await hasPermission(req.body.requestor, "deleteTimeEntry")) ) { // Revert this tangible timeEntry of related task's hoursLogged if (record.isTangible === true) { - task.findById(record.projectId) + task + .findById(record.projectId) .then((currentTask) => { // If the time entry isn't related to a task (i.e. it's a project), then don't revert hours (Most likely pr team) if (currentTask) { - currentTask.hoursLogged -= (record.totalSeconds / 3600); + currentTask.hoursLogged -= record.totalSeconds / 3600; currentTask.save(); } }) @@ -496,13 +583,13 @@ const timeEntrycontroller = function (TimeEntry) { record .remove() .then(() => { - res.status(200).send({ message: 'Successfully deleted' }); + res.status(200).send({ message: "Successfully deleted" }); }) .catch((error) => { res.status(500).send(error); }); } else { - res.status(403).send({ error: 'Unauthorized request' }); + res.status(403).send({ error: "Unauthorized request" }); } }) .catch((error) => { @@ -510,7 +597,6 @@ const timeEntrycontroller = function (TimeEntry) { }); }; - return { getAllTimeEnteries, postTimeEntry, diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 8bf04eb5f..42422a02d 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -492,8 +492,8 @@ const userHelper = function () { emailBody, null, "onecommunityglobal@gmail.com", - null, - status.email + status.email, + null ); const categories = await dashboardHelper.laborThisWeekByCategory( From 775265b4716a32e39624a1cd5516a57675bbfe40 Mon Sep 17 00:00:00 2001 From: Ruike Qiu <114443664+StrawberryCalpico@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:55:06 -0800 Subject: [PATCH 105/129] added sanitizer --- package-lock.json | 127 +++++++++++++++++--------------- package.json | 1 + src/routes/userProfileRouter.js | 71 +++++++++++++----- 3 files changed, 121 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c8f3ee0b..ff66994e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1568,7 +1568,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, "@types/mime": { @@ -1729,7 +1729,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-includes": { "version": "3.1.6", @@ -2768,7 +2768,7 @@ "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, "bignumber.js": { "version": "9.0.2", @@ -2863,7 +2863,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "buffer-from": { "version": "1.1.2", @@ -2995,7 +2995,7 @@ "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" }, "clone-deep": { "version": "4.0.1", @@ -3039,12 +3039,12 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -3100,7 +3100,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "core-js": { "version": "3.21.1", @@ -3253,7 +3253,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { "version": "1.4.81", @@ -3269,7 +3269,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "es-abstract": { "version": "1.19.1", @@ -3350,7 +3350,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", @@ -4264,7 +4264,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "event-target-shim": { "version": "5.0.1", @@ -4409,6 +4409,22 @@ } } }, + "express-validator": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", + "requires": { + "lodash": "^4.17.21", + "validator": "^13.9.0" + }, + "dependencies": { + "validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4429,7 +4445,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "fast-text-encoding": { @@ -4559,7 +4575,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-readdir-recursive": { "version": "1.1.0", @@ -4569,7 +4585,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -4952,7 +4968,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "indent-string": { @@ -4964,7 +4980,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5069,7 +5085,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-glob": { "version": "4.0.3", @@ -5171,13 +5187,13 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "js-tokens": { "version": "4.0.0", @@ -5215,7 +5231,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, "json5": { @@ -5536,7 +5552,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.merge": { "version": "4.6.2", @@ -5713,7 +5729,7 @@ "lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" }, "make-dir": { "version": "2.1.0", @@ -5727,7 +5743,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memory-pager": { "version": "1.5.0", @@ -5738,7 +5754,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "merge-stream": { "version": "2.0.0", @@ -5749,7 +5765,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { "version": "4.0.5", @@ -5806,11 +5822,6 @@ "moment": ">= 2.9.0" } }, - "mongo-round": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mongo-round/-/mongo-round-1.0.0.tgz", - "integrity": "sha512-lwvLJv827Uks+3HnTOt1I/Qr78Avke3du1oMaFqFpTwtRKtOposNOKkfpGXQN4ZGpRN3XAS8fEppIJ4TUj0xQw==" - }, "mongodb": { "version": "3.7.3", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", @@ -5934,7 +5945,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, "negotiator": { @@ -6074,7 +6085,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { "version": "1.12.0", @@ -6793,7 +6804,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" } @@ -6823,7 +6834,7 @@ "os-shim": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", "dev": true }, "p-limit": { @@ -6868,7 +6879,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==" + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" }, "parseurl": { "version": "1.3.3", @@ -6883,7 +6894,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", @@ -6899,7 +6910,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "picocolors": { "version": "1.0.0", @@ -6946,7 +6957,7 @@ "pre-commit": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", - "integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==", + "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -6957,7 +6968,7 @@ "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -6968,7 +6979,7 @@ "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", "dev": true, "requires": { "isexe": "^2.0.0" @@ -7010,7 +7021,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "pstree.remy": { @@ -7488,7 +7499,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", "optional": true, "requires": { "memory-pager": "^1.0.2" @@ -7497,7 +7508,7 @@ "spawn-sync": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==", + "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", "dev": true, "requires": { "concat-stream": "^1.4.7", @@ -7892,7 +7903,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "strip-final-newline": { @@ -7923,19 +7934,19 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-regex-range": { "version": "5.0.1", @@ -7962,7 +7973,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "tsconfig-paths": { "version": "3.14.2", @@ -8080,7 +8091,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, "unbox-primitive": { @@ -8127,7 +8138,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "uri-js": { "version": "4.4.1", @@ -8141,17 +8152,17 @@ "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { "version": "3.4.0", @@ -8174,17 +8185,17 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8302,7 +8313,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "8.8.1", diff --git a/package.json b/package.json index 1c6b8a5d4..e7fbd6307 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "cron": "^1.8.2", "dotenv": "^5.0.1", "express": "^4.17.1", + "express-validator": "^7.0.1", "googleapis": "^100.0.0", "jsonwebtoken": "^9.0.0", "lodash": "^4.17.21", diff --git a/src/routes/userProfileRouter.js b/src/routes/userProfileRouter.js index 9032359a8..8d7807514 100644 --- a/src/routes/userProfileRouter.js +++ b/src/routes/userProfileRouter.js @@ -1,53 +1,84 @@ -const express = require('express'); - +const express = require("express"); +import { body } from "express-validator"; const routes = function (userProfile) { - const controller = require('../controllers/userProfileController')(userProfile); + const controller = require("../controllers/userProfileController")( + userProfile + ); const userProfileRouter = express.Router(); - userProfileRouter.route('/userProfile') + userProfileRouter + .route("/userProfile") .get(controller.getUserProfiles) - .post(controller.postUserProfile); - - userProfileRouter.route('/userProfile/:userId') + .post( + body("firstName").customSanitizer((value) => value.trim()), + body("lastName").customSanitizer((value) => value.trim()), + controller.postUserProfile + ); + + userProfileRouter + .route("/userProfile/:userId") .get(controller.getUserById) - .put(controller.putUserProfile) + .put( + body("firstName").customSanitizer((value) => value.trim()), + body("lastName").customSanitizer((value) => value.trim()), + body("personalLinks").customSanitizer((value) => + value.map((link) => { + if (link.Name.replace(/\s/g, "") || link.Link.replace(/\s/g, "")) { + return { + ...link, + Name: link.Name.trim(), + Link: link.Link.replace(/\s/g, ""), + }; + } + throw new Error("Url not valid"); + }) + ), + controller.putUserProfile + ) .delete(controller.deleteUserProfile) .patch(controller.changeUserStatus); - userProfileRouter.route('/userProfile/name/:name') + userProfileRouter + .route("/userProfile/name/:name") .get(controller.getUserByName); - userProfileRouter.route('/refreshToken/:userId') - .get(controller.refreshToken); + userProfileRouter.route("/refreshToken/:userId").get(controller.refreshToken); - userProfileRouter.route('/userProfile/reportees/:userId') + userProfileRouter + .route("/userProfile/reportees/:userId") .get(controller.getreportees); - userProfileRouter.route('/userProfile/teammembers/:userId') + userProfileRouter + .route("/userProfile/teammembers/:userId") .get(controller.getTeamMembersofUser); - userProfileRouter.route('/userProfile/:userId/property') + userProfileRouter + .route("/userProfile/:userId/property") .patch(controller.updateOneProperty); - userProfileRouter.route('/userProfile/:userId/updatePassword') + userProfileRouter + .route("/userProfile/:userId/updatePassword") .patch(controller.updatepassword); - userProfileRouter.route('/userProfile/:userId/resetPassword') + userProfileRouter + .route("/userProfile/:userId/resetPassword") .patch(controller.resetPassword); - userProfileRouter.route('/userProfile/name/:userId') + userProfileRouter + .route("/userProfile/name/:userId") .get(controller.getUserName); - userProfileRouter.route('/userProfile/project/:projectId') + userProfileRouter + .route("/userProfile/project/:projectId") .get(controller.getProjectMembers); - userProfileRouter.route('/userProfile/socials/facebook') + userProfileRouter + .route("/userProfile/socials/facebook") .get(controller.getAllUsersWithFacebookLink); return userProfileRouter; }; - module.exports = routes; From 5c7cc8cdc7c5fcf7563008e31af9e08443b723c3 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Wed, 20 Dec 2023 12:35:10 -0800 Subject: [PATCH 106/129] Refactor of timeentry controller, wbs controller, task controller and their router --- src/controllers/taskController.js | 58 ++- src/controllers/timeEntryController.js | 472 +++++++++++------------ src/controllers/wbsController.js | 23 ++ src/helpers/helperModels/userProjects.js | 1 + src/models/timeentry.js | 6 + src/routes/taskRouter.js | 4 +- src/routes/timeentryRouter.js | 4 +- src/routes/wbsRouter.js | 3 + 8 files changed, 284 insertions(+), 287 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index 9bcf071de..5b087db4d 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -1,10 +1,10 @@ const mongoose = require('mongoose'); -const wbs = require('../models/wbs'); +const WBS = require('../models/wbs'); +const UserProfile = require('../models/userProfile'); const timeEntryHelper = require('../helpers/timeEntryHelper')(); const taskHelper = require('../helpers/taskHelper')(); const { hasPermission } = require('../utilities/permissions'); const emailSender = require('../utilities/emailSender'); -const userProfile = require('../models/userProfile'); const taskController = function (Task) { const getTasks = (req, res) => { @@ -33,7 +33,7 @@ const taskController = function (Task) { const getWBSId = (req, res) => { const { wbsId } = req.params; - wbs.findById(wbsId) + WBS.findById(wbsId) .then(results => res.status(200).send(results)) .catch(error => res.status(404).send(error)); }; @@ -446,7 +446,7 @@ const taskController = function (Task) { }); const saveTask = _task.save(); - const saveWbs = wbs.findById(wbsId).then((currentwbs) => { + const saveWbs = WBS.findById(wbsId).then((currentwbs) => { currentwbs.modifiedDatetime = Date.now(); return currentwbs.save(); }); @@ -803,31 +803,28 @@ const taskController = function (Task) { res.status(200).send('done'); }; - const getTasksByUserList = async (req, res) => { - const { members } = req.query; - const membersArr = members.split(','); + const getTasksByUserId = async (req, res) => { + const { userId } = req.params; try { - Task.find( - { 'resources.userID': { $in: membersArr } }, - '-resources.profilePic', - ).then((results) => { - wbs - .find({ - _id: { $in: results.map(item => item.wbsId) }, - }) - .then((projectIds) => { - const resultsWithProjectsIds = results.map((item) => { - item.set( - 'projectId', - projectIds?.find( - projectId => projectId._id.toString() === item.wbsId.toString(), - )?.projectId, - { strict: false }, - ); - return item; - }); - res.status(200).send(resultsWithProjectsIds); + Task.find({ + 'resources.userID': mongoose.Types.ObjectId(userId), + }, '-resources.profilePic') + .then((results) => { + WBS.find({ + _id: { $in: results.map(item => item.wbsId) }, + }).then((WBSs) => { + const resultsWithProjectsIds = results.map((item) => { + item.set( + 'projectId', + WBSs?.find( + wbs => wbs._id.toString() === item.wbsId.toString(), + )?.projectId, + { strict: false }, + ); + return item; }); + res.status(200).send(resultsWithProjectsIds); + }); }); } catch (error) { res.status(400).send(error); @@ -845,6 +842,7 @@ const taskController = function (Task) { res.status(200).send(singleUserData); } } catch (error) { + console.log(error); res.status(400).send(error); } }; @@ -872,8 +870,8 @@ const taskController = function (Task) { const getRecipients = async function (myUserId) { const recipients = []; - const user = await userProfile.findById(myUserId); - const membership = await userProfile.find({ role: { $in: ['Administrator', 'Manager', 'Mentor'] } }); + const user = await UserProfile.findById(myUserId); + const membership = await UserProfile.find({ role: { $in: ['Administrator', 'Manager', 'Mentor'] } }); membership.forEach((member) => { if (member.teams.some(team => user.teams.includes(team))) { recipients.push(member.email); @@ -917,7 +915,7 @@ const taskController = function (Task) { updateAllParents, deleteTaskByWBS, moveTask, - getTasksByUserList, + getTasksByUserId, getTasksForTeamsByUser, updateTaskStatus, sendReviewReq, diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 9c4d7c5e3..5edd3a095 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -1,17 +1,18 @@ -const moment = require("moment-timezone"); -const mongoose = require("mongoose"); -const { getInfringementEmailBody } = require("../helpers/userHelper")(); -const userProfile = require("../models/userProfile"); -const task = require("../models/task"); -const emailSender = require("../utilities/emailSender"); -const { hasPermission } = require("../utilities/permissions"); +const moment = require('moment-timezone'); +const mongoose = require('mongoose'); +const { getInfringementEmailBody } = require('../helpers/userHelper')(); +const UserProfile = require('../models/userProfile'); +const Task = require('../models/task'); +const WBS = require('../models/wbs'); +const emailSender = require('../utilities/emailSender'); +const { hasPermission } = require('../utilities/permissions'); const formatSeconds = function (seconds) { const formattedseconds = parseInt(seconds, 10); const values = `${Math.floor( - moment.duration(formattedseconds, "seconds").asHours() - )}:${moment.duration(formattedseconds, "seconds").minutes()}`; - return values.split(":"); + moment.duration(formattedseconds, 'seconds').asHours(), + )}:${moment.duration(formattedseconds, 'seconds').minutes()}`; + return values.split(':'); }; /** @@ -30,14 +31,14 @@ const getEditedTimeEntryEmailBody = ( email, originalTime, finalTime, - requestor + requestor, ) => { const formattedOriginal = moment .utc(originalTime * 1000) - .format("HH[ hours ]mm[ minutes]"); + .format('HH[ hours ]mm[ minutes]'); const formattedFinal = moment .utc(finalTime * 1000) - .format("HH[ hours ]mm[ minutes]"); + .format('HH[ hours ]mm[ minutes]'); return ` A time entry belonging to ${firstName} ${lastName} (${email}) was modified by ${requestor.firstName} ${requestor.lastName} (${requestor.email}). The entry's duration was changed from [${formattedOriginal}] to [${formattedFinal}] @@ -55,10 +56,9 @@ const getEditedTimeEntryEmailBody = ( const notifyEditByEmail = async (personId, original, finalTime, final) => { try { const originalTime = original.totalSeconds; - const record = await userProfile.findById(personId); - const requestor = - personId !== final.requestor.requestorId - ? await userProfile.findById(final.requestor.requestorId) + const record = await UserProfile.findById(personId); + const requestor = personId !== final.requestor.requestorId + ? await UserProfile.findById(final.requestor.requestorId) : record; const emailBody = getEditedTimeEntryEmailBody( record.firstName, @@ -66,16 +66,16 @@ const notifyEditByEmail = async (personId, original, finalTime, final) => { record.email, originalTime, finalTime, - requestor + requestor, ); emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', `A Time Entry was Edited for ${record.firstName} ${record.lastName}`, - emailBody + emailBody, ); } catch (error) { throw new Error( - `Failed to send email notification about the modification of time entry belonging to user with id ${personId}` + `Failed to send email notification about the modification of time entry belonging to user with id ${personId}`, ); } }; @@ -84,10 +84,10 @@ const notifyTaskOvertimeEmailBody = async ( personId, taskName, estimatedHours, - hoursLogged + hoursLogged, ) => { try { - const record = await userProfile.findById(personId); + const record = await UserProfile.findById(personId); const text = `Dear ${record.firstName}${record.lastName},

Oops, it looks like you have logged more hours than estimated for a task

Task Name : ${taskName}

@@ -98,16 +98,16 @@ const notifyTaskOvertimeEmailBody = async (

One Community

`; emailSender( record.email, - "Logged more hours than estimated for a task", + 'Logged more hours than estimated for a task', text, - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', null, record.email, - null + null, ); } catch (error) { console.log( - `Failed to send email notification about the overtime for a task belonging to user with id ${personId}` + `Failed to send email notification about the overtime for a task belonging to user with id ${personId}`, ); } }; @@ -120,122 +120,132 @@ const checkTaskOvertime = async (timeentry, record, currentTask) => { timeentry.personId.toString(), currentTask.taskName, currentTask.estimatedHours, - currentTask.hoursLogged + currentTask.hoursLogged, ); } } catch (error) { console.log( - `Failed to find task whose logged-in hours are more than estimated hours ${record.email}` + `Failed to find task whose logged-in hours are more than estimated hours ${record.email}`, ); } }; +// update timeentry with wbsId and taskId if projectId in the old timeentry is actually a taskId +const updateTaskIdInTimeEntry = async (id, timeEntry) => { + // if id is a taskId, then timeentry should have the parent wbsId and projectId for that task; + // if id is not a taskId, then it is a projectId, timeentry should have both wbsId and taskId to be null; + let taskId = null; + let wbsId = null; + let projectId = id; + const task = await Task.findById(id); + if (task) { + taskId = id; + ({ wbsId } = task); + const wbs = await WBS.findById(wbsId); + ({ projectId } = wbs); + } + Object.assign(timeEntry, { taskId, wbsId, projectId }); +}; + const timeEntrycontroller = function (TimeEntry) { const editTimeEntry = async (req, res) => { + const { timeEntryId } = req.params; + + if (!timeEntryId) { + const error = 'ObjectId in request param is not in correct format'; + return res.status(400).send({ error }); + } + + if (!mongoose.Types.ObjectId.isValid(timeEntryId)) { + const error = 'ObjectIds are not correctly formed'; + return res.status(400).send({ error }); + } + + const { + personId, + hours = '00', + minutes = '00', + notes, + isTangible, + projectId, + wbsId, + taskId, + dateOfWork, + } = req.body; + + const isForAuthUser = personId === req.body.requestor.requestorId; + const isSameDayTimeEntry = moment().tz('America/Los_Angeles').format('YYYY-MM-DD') === dateOfWork; + const canEdit = (await hasPermission(req.body.requestor, 'editTimeEntry')) || (isForAuthUser && isSameDayTimeEntry); + + if (!canEdit) { + const error = 'Unauthorized request'; + return res.status(403).send({ error }); + } + const session = await mongoose.startSession(); session.startTransaction(); try { - if (!req.params.timeEntryId) { - return res - .status(400) - .send({ - error: "ObjectId in request param is not in correct format", - }); - } - - if ( - !mongoose.Types.ObjectId.isValid(req.params.timeEntryId) || - !mongoose.Types.ObjectId.isValid(req.body.projectId) - ) { - return res - .status(400) - .send({ error: "ObjectIds are not correctly formed" }); - } - // Get initial timeEntry by timeEntryId - const timeEntry = await TimeEntry.findById(req.params.timeEntryId); - + const timeEntry = await TimeEntry.findById(timeEntryId); if (!timeEntry) { - return res - .status(400) - .send({ - error: `No valid records found for ${req.params.timeEntryId}`, - }); + const error = `No valid records found for ${timeEntryId}`; + return res.status(400).send({ error }); } - if ( - !( - (await hasPermission(req.body.requestor, "editTimeEntry")) || - timeEntry.personId.toString() === - req.body.requestor.requestorId.toString() - ) - ) { - return res.status(403).send({ error: "Unauthorized request" }); - } - - const hours = req.body.hours ? req.body.hours : "00"; - const minutes = req.body.minutes ? req.body.minutes : "00"; + const totalSeconds = moment.duration({ hours, minutes }).asSeconds(); + const { + totalSeconds: initialTotalSeconds, + projectId: initialProjectId, + wbsId: initialWBSId, + taskId: initialTaskId, + isTangible: initialIsTangible, + } = timeEntry; - const totalSeconds = moment.duration(`${hours}:${minutes}`).asSeconds(); - - if ( - timeEntry.isTangible === true && - totalSeconds !== timeEntry.totalSeconds - ) { + if (initialIsTangible && totalSeconds !== initialTotalSeconds) { notifyEditByEmail( timeEntry.personId.toString(), timeEntry, totalSeconds, - req.body + req.body, ); } - const initialSeconds = timeEntry.totalSeconds; - const initialProjectId = timeEntry.projectId; - const initialIsTangible = timeEntry.isTangible; - // Get the task related to this time entry, if not found, then it's a project and will be null - const findTask = await task.findById(initialProjectId); - - timeEntry.notes = req.body.notes; - timeEntry.totalSeconds = totalSeconds; - timeEntry.isTangible = req.body.isTangible; - timeEntry.lastModifiedDateTime = moment().utc().toISOString(); - timeEntry.projectId = mongoose.Types.ObjectId(req.body.projectId); - timeEntry.dateOfWork = moment(req.body.dateOfWork).format("YYYY-MM-DD"); - - // Update the hoursLogged field of related tasks based on before and after timeEntries - // initialIsTangible is a bealoon value, req.body.isTangible is a string - // initialProjectId may be a task id or project id, so do not throw error. - try { - if (findTask) { - if (initialIsTangible === true) { - findTask.hoursLogged -= initialSeconds / 3600; - } - - if (req.body.isTangible === true) { - findTask.hoursLogged += totalSeconds / 3600; - } + updateTaskIdInTimeEntry(projectId, timeEntry); - await findTask.save(); + if (timeEntry.taskId) { + const findTask = await Task.findById(initialProjectId); + if (initialIsTangible) { + findTask.hoursLogged -= initialTotalSeconds / 3600; } - } catch (error) { - throw new Error(error); + if (req.body.isTangible === true) { + findTask.hoursLogged += totalSeconds / 3600; + } + await findTask.save(); } + timeEntry.notes = notes; + timeEntry.totalSeconds = totalSeconds; + timeEntry.isTangible = isTangible; + timeEntry.lastModifiedDateTime = moment().utc().toISOString(); + timeEntry.projectId = mongoose.Types.ObjectId(projectId); + timeEntry.wbsId = wbsId ? mongoose.Types.ObjectId(wbsId) : null; + timeEntry.taskId = taskId ? mongoose.Types.ObjectId(taskId) : null; + timeEntry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); + // Update edit history if ( - initialSeconds !== totalSeconds && - timeEntry.isTangible && - req.body.requestor.requestorId === timeEntry.personId.toString() && - !(await hasPermission(req.body.requestor, "editTimeEntry")) + initialTotalSeconds !== totalSeconds + && timeEntry.isTangible + && req.body.requestor.requestorId === timeEntry.personId.toString() + && !(await hasPermission(req.body.requestor, 'editTimeEntry')) ) { - const requestor = await userProfile.findById( - req.body.requestor.requestorId + const requestor = await UserProfile.findById( + req.body.requestor.requestorId, ); requestor.timeEntryEditHistory.push({ - date: moment().tz("America/Los_Angeles").toDate(), - initialSeconds, + date: moment().tz('America/Los_Angeles').toDate(), + initialSeconds: initialTotalSeconds, newSeconds: totalSeconds, }); @@ -244,7 +254,7 @@ const timeEntrycontroller = function (TimeEntry) { requestor.timeEntryEditHistory.forEach((edit) => { if ( - moment().tz("America/Los_Angeles").diff(edit.date, "days") <= 365 + moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365 ) { totalRecentEdits += 1; } @@ -252,12 +262,12 @@ const timeEntrycontroller = function (TimeEntry) { if (totalRecentEdits >= 5) { requestor.infringements.push({ - date: moment().tz("America/Los_Angeles"), + date: moment().tz('America/Los_Angeles'), description: `${totalRecentEdits} time entry edits in the last calendar year`, }); emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, `

@@ -267,11 +277,11 @@ const timeEntrycontroller = function (TimeEntry) {

This is the ${totalRecentEdits}th edit within the past 365 days.

- ` + `, ); const emailInfringement = { - date: moment().tz("America/Los_Angeles").format("MMMM-DD-YY"), + date: moment().tz('America/Los_Angeles').format('MMMM-DD-YY'), description: `You edited your time entries ${totalRecentEdits} times within the last 365 days, exceeding the limit of 4 times per year you can edit them without penalty.`, }; @@ -282,8 +292,8 @@ const timeEntrycontroller = function (TimeEntry) { requestor.firstName, requestor.lastName, emailInfringement, - requestor.infringements.length - ) + requestor.infringements.length, + ), ); } @@ -292,15 +302,15 @@ const timeEntrycontroller = function (TimeEntry) { await timeEntry.save(); - res.status(200).send({ message: "Successfully updated time entry" }); + res.status(200).send({ message: 'Successfully updated time entry' }); // If the time entry isn't related to a task (i.e. it's a project), then don't check for overtime (Most likely pr team) if (findTask) { // checking if logged in hours exceed estimated time after timeentry edit for a task - const record = await userProfile.findById( - timeEntry.personId.toString() + const record = await UserProfile.findById( + timeEntry.personId.toString(), ); - const currentTask = await task.findById(req.body.projectId); + const currentTask = await Task.findById(req.body.projectId); checkTaskOvertime(timeEntry, record, currentTask); } } catch (err) { @@ -324,9 +334,9 @@ const timeEntrycontroller = function (TimeEntry) { timeentry.personId = element.personId; timeentry.projectId = element.projectId; timeentry.dateOfWork = element.dateOfWork; - timeentry.timeSpent = moment("1900-01-01 00:00:00") - .add(element.totalSeconds, "seconds") - .format("HH:mm:ss"); + timeentry.timeSpent = moment('1900-01-01 00:00:00') + .add(element.totalSeconds, 'seconds') + .format('HH:mm:ss'); timeentry.notes = element.notes; timeentry.isTangible = element.isTangible; items.push(timeentry); @@ -337,21 +347,23 @@ const timeEntrycontroller = function (TimeEntry) { const postTimeEntry = async function (req, res) { if ( - !mongoose.Types.ObjectId.isValid(req.body.personId) || - !mongoose.Types.ObjectId.isValid(req.body.projectId) || - !req.body.dateOfWork || - !moment(req.body.dateOfWork).isValid() || - !req.body.timeSpent || - !req.body.isTangible + !mongoose.Types.ObjectId.isValid(req.body.personId) + || !mongoose.Types.ObjectId.isValid(req.body.projectId) + || !req.body.dateOfWork + || !moment(req.body.dateOfWork).isValid() + || !req.body.timeSpent + || !req.body.isTangible ) { - res.status(400).send({ error: "Bad request" }); + res.status(400).send({ error: 'Bad request' }); return; } const timeentry = new TimeEntry(); const { dateOfWork, timeSpent } = req.body; timeentry.personId = req.body.personId; timeentry.projectId = req.body.projectId; - timeentry.dateOfWork = moment(dateOfWork).format("YYYY-MM-DD"); + timeentry.wbsId = req.body.wbsId; + timeentry.taskId = req.body.taskId; + timeentry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); timeentry.totalSeconds = moment.duration(timeSpent).asSeconds(); timeentry.notes = req.body.notes; timeentry.isTangible = req.body.isTangible; @@ -365,10 +377,10 @@ const timeEntrycontroller = function (TimeEntry) { .status(200) .send({ message: `Time Entry saved with id as ${results._id}` }); }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); // Get the task related to this time entry, if not found, then it's a project sets to null - const currentTask = await task + const currentTask = await Task .findById(req.body.projectId) .catch(() => null); @@ -385,8 +397,8 @@ const timeEntrycontroller = function (TimeEntry) { // checking if logged in hours exceed estimated time after timeentry for a task, only if the time entry is related to a task (It might not be, if it's a project) if (currentTask) { try { - const record = await userProfile.findById( - timeentry.personId.toString() + const record = await UserProfile.findById( + timeentry.personId.toString(), ); checkTaskOvertime(timeentry, record, currentTask); } catch (error) { @@ -395,93 +407,47 @@ const timeEntrycontroller = function (TimeEntry) { } }; - const getTimeEntriesForSpecifiedPeriod = function (req, res) { + const getTimeEntriesForSpecifiedPeriod = async function (req, res) { if ( - !req.params || - !req.params.fromdate || - !req.params.todate || - !req.params.userId || - !moment(req.params.fromdate).isValid() || - !moment(req.params.toDate).isValid() + !req.params + || !req.params.fromdate + || !req.params.todate + || !req.params.userId + || !moment(req.params.fromdate).isValid() + || !moment(req.params.toDate).isValid() ) { - res.status(400).send({ error: "Invalid request" }); + res.status(400).send({ error: 'Invalid request' }); return; } const fromdate = moment(req.params.fromdate) - .tz("America/Los_Angeles") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .format('YYYY-MM-DD'); const todate = moment(req.params.todate) - .tz("America/Los_Angeles") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .format('YYYY-MM-DD'); const { userId } = req.params; - TimeEntry.aggregate([ - { - $match: { - personId: mongoose.Types.ObjectId(userId), - dateOfWork: { $gte: fromdate, $lte: todate }, - }, - }, - { - $lookup: { - from: "projects", - localField: "projectId", - foreignField: "_id", - as: "project", - }, - }, - { - $lookup: { - from: "tasks", - localField: "projectId", - foreignField: "_id", - as: "task", - }, - }, - { - $project: { - _id: 1, - notes: 1, - isTangible: 1, - personId: 1, - projectId: 1, - lastModifiedDateTime: 1, - projectName: { - $arrayElemAt: ["$project.projectName", 0], - }, - taskName: { - $arrayElemAt: ["$task.taskName", 0], - }, - category: { - $arrayElemAt: ["$project.category", 0], - }, - classification: { - $arrayElemAt: ["$task.classification", 0], - }, - dateOfWork: 1, - hours: { - $floor: { - $divide: ["$totalSeconds", 3600], - }, - }, - minutes: { - $floor: { - $divide: [{ $mod: ["$totalSeconds", 3600] }, 60], - }, - }, - }, - }, - { - $sort: { - lastModifiedDateTime: -1, - }, - }, - ]) - .then((results) => { - res.status(200).send(results); - }) - .catch((error) => res.status(400).send(error)); + try { + const timeEntries = await TimeEntry.find({ + personId: userId, + dateOfWork: { $gte: fromdate, $lte: todate }, + }).sort('-lastModifiedDateTime'); + + const results = await Promise.all(timeEntries.map(async (timeEntry) => { + timeEntry = { ...timeEntry.toObject() }; + const { projectId } = timeEntry; + await updateTaskIdInTimeEntry(projectId, timeEntry); + const hours = Math.floor(timeEntry.totalSeconds / 3600); + const minutes = Math.floor((timeEntry.totalSeconds % 3600) / 60); + Object.assign(timeEntry, { hours, minutes, totalSeconds: undefined }); + return timeEntry; + })); + + res.status(200).send(results); + } catch (err) { + res.status(400).send({ error: err }); + } }; const getTimeEntriesForUsersList = function (req, res) { @@ -492,9 +458,9 @@ const timeEntrycontroller = function (TimeEntry) { personId: { $in: users }, dateOfWork: { $gte: fromDate, $lte: toDate }, }, - " -createdDateTime" + ' -createdDateTime', ) - .populate("projectId") + .populate('projectId') .sort({ lastModifiedDateTime: -1 }) .then((results) => { const data = []; @@ -505,68 +471,68 @@ const timeEntrycontroller = function (TimeEntry) { record.notes = element.notes; record.isTangible = element.isTangible; record.personId = element.personId; - record.projectId = element.projectId ? element.projectId._id : ""; + record.projectId = element.projectId ? element.projectId._id : ''; record.projectName = element.projectId ? element.projectId.projectName - : ""; + : ''; record.dateOfWork = element.dateOfWork; [record.hours, record.minutes] = formatSeconds(element.totalSeconds); data.push(record); }); res.status(200).send(data); }) - .catch((error) => res.status(400).send(error)); + .catch(error => res.status(400).send(error)); }; - const getTimeEntriesForSpecifiedProject = function (req, res) { - if ( - !req.params || - !req.params.fromDate || - !req.params.toDate || - !req.params.projectId - ) { - res.status(400).send({ error: "Invalid request" }); - return; - } - const todate = moment(req.params.toDate).format("YYYY-MM-DD"); - const fromDate = moment(req.params.fromDate).format("YYYY-MM-DD"); - const { projectId } = req.params; - TimeEntry.find( - { - projectId, - dateOfWork: { $gte: fromDate, $lte: todate }, - }, - "-createdDateTime -lastModifiedDateTime" - ) - .populate("userId") - .sort({ dateOfWork: -1 }) - .then((results) => { - res.status(200).send(results); - }) - .catch((error) => res.status(400).send(error)); - }; + // const getTimeEntriesForSpecifiedProject = function (req, res) { + // if ( + // !req.params + // || !req.params.fromDate + // || !req.params.toDate + // || !req.params.projectId + // ) { + // res.status(400).send({ error: 'Invalid request' }); + // return; + // } + // const todate = moment(req.params.toDate).format('YYYY-MM-DD'); + // const fromDate = moment(req.params.fromDate).format('YYYY-MM-DD'); + // const { projectId } = req.params; + // TimeEntry.find( + // { + // projectId, + // dateOfWork: { $gte: fromDate, $lte: todate }, + // }, + // '-createdDateTime -lastModifiedDateTime', + // ) + // .populate('userId') + // .sort({ dateOfWork: -1 }) + // .then((results) => { + // res.status(200).send(results); + // }) + // .catch(error => res.status(400).send(error)); + // }; const deleteTimeEntry = async function (req, res) { if (!req.params.timeEntryId) { - res.status(400).send({ error: "Bad request" }); + res.status(400).send({ error: 'Bad request' }); return; } TimeEntry.findById(req.params.timeEntryId) .then(async (record) => { if (!record) { - res.status(400).send({ message: "No valid record found" }); + res.status(400).send({ message: 'No valid record found' }); return; } if ( - record.personId.toString() === - req.body.requestor.requestorId.toString() || - (await hasPermission(req.body.requestor, "deleteTimeEntry")) + record.personId.toString() + === req.body.requestor.requestorId.toString() + || (await hasPermission(req.body.requestor, 'deleteTimeEntry')) ) { // Revert this tangible timeEntry of related task's hoursLogged if (record.isTangible === true) { - task + Task .findById(record.projectId) .then((currentTask) => { // If the time entry isn't related to a task (i.e. it's a project), then don't revert hours (Most likely pr team) @@ -583,13 +549,13 @@ const timeEntrycontroller = function (TimeEntry) { record .remove() .then(() => { - res.status(200).send({ message: "Successfully deleted" }); + res.status(200).send({ message: 'Successfully deleted' }); }) .catch((error) => { res.status(500).send(error); }); } else { - res.status(403).send({ error: "Unauthorized request" }); + res.status(403).send({ error: 'Unauthorized request' }); } }) .catch((error) => { @@ -604,7 +570,7 @@ const timeEntrycontroller = function (TimeEntry) { getTimeEntriesForUsersList, editTimeEntry, deleteTimeEntry, - getTimeEntriesForSpecifiedProject, + // getTimeEntriesForSpecifiedProject, checkTaskOvertime, }; }; diff --git a/src/controllers/wbsController.js b/src/controllers/wbsController.js index fa7f4427f..04070eaf9 100644 --- a/src/controllers/wbsController.js +++ b/src/controllers/wbsController.js @@ -1,4 +1,7 @@ +const mongoose = require('mongoose'); const { hasPermission } = require('../utilities/permissions'); +const Project = require('../models/project'); +const Task = require('../models/task'); const wbsController = function (WBS) { const getAllWBS = function (req, res) { @@ -68,12 +71,32 @@ const wbsController = function (WBS) { .catch(error => res.status(404).send(error)); }; + const getWBSByUserId = async function (req, res) { + const { userId } = req.params; + try { + const result = await Task.aggregate() + .match({ 'resources.userID': mongoose.Types.ObjectId(userId) }) + .project('wbsId -_id') + .group({ _id: '$wbsId' }) + .lookup({ + from: 'wbs', localField: '_id', foreignField: '_id', as: 'wbs', + }) + .unwind('wbs') + .replaceRoot('wbs'); + + res.status(200).send(result); + } catch (error) { + res.status(404).send(error); + } + }; + return { postWBS, deleteWBS, getAllWBS, getWBS, getWBSById, + getWBSByUserId, }; }; diff --git a/src/helpers/helperModels/userProjects.js b/src/helpers/helperModels/userProjects.js index 108ec345b..e325a0e39 100644 --- a/src/helpers/helperModels/userProjects.js +++ b/src/helpers/helperModels/userProjects.js @@ -5,6 +5,7 @@ const { Schema } = mongoose; const ProjectSchema = new Schema({ projectId: { type: mongoose.SchemaTypes.ObjectId, ref: 'projects' }, projectName: { type: String }, + category: { type: String }, }); diff --git a/src/models/timeentry.js b/src/models/timeentry.js index aeef5fdc7..3876cfcc7 100644 --- a/src/models/timeentry.js +++ b/src/models/timeentry.js @@ -6,6 +6,12 @@ const { Schema } = mongoose; const TimeEntry = new Schema({ personId: { type: Schema.Types.ObjectId, required: [true, 'Resource is a required field'], ref: 'userProfile' }, projectId: { type: Schema.Types.ObjectId, required: [true, 'Project is a required field'], ref: 'project' }, + wbsId: { + type: Schema.Types.ObjectId, required: false, default: null, ref: 'wbs', + }, + taskId: { + type: Schema.Types.ObjectId, required: false, default: null, ref: 'task', + }, dateOfWork: { type: String, required: true }, totalSeconds: { type: Number }, notes: { type: String }, diff --git a/src/routes/taskRouter.js b/src/routes/taskRouter.js index 46e467c9d..e04b499eb 100644 --- a/src/routes/taskRouter.js +++ b/src/routes/taskRouter.js @@ -43,8 +43,8 @@ const routes = function (task, userProfile) { wbsRouter.route('/tasks/moveTasks/:wbsId') .put(controller.moveTask); - wbsRouter.route('/tasks/userProfile') - .get(controller.getTasksByUserList); + wbsRouter.route('/tasks/user/:userId') + .get(controller.getTasksByUserId); wbsRouter.route('/user/:userId/teams/tasks') .get(controller.getTasksForTeamsByUser); diff --git a/src/routes/timeentryRouter.js b/src/routes/timeentryRouter.js index b319aa595..f5a89e351 100644 --- a/src/routes/timeentryRouter.js +++ b/src/routes/timeentryRouter.js @@ -19,8 +19,8 @@ const routes = function (TimeEntry) { TimeEntryRouter.route('/TimeEntry/users') .post(controller.getTimeEntriesForUsersList); - TimeEntryRouter.route('/TimeEntry/projects/:projectId/:fromDate/:toDate') - .get(controller.getTimeEntriesForSpecifiedProject); + // TimeEntryRouter.route('/TimeEntry/projects/:projectId/:fromDate/:toDate') + // .get(controller.getTimeEntriesForSpecifiedProject); return TimeEntryRouter; }; diff --git a/src/routes/wbsRouter.js b/src/routes/wbsRouter.js index b78ada5b9..08bfdc7b5 100644 --- a/src/routes/wbsRouter.js +++ b/src/routes/wbsRouter.js @@ -14,6 +14,9 @@ const routes = function (wbs) { wbsRouter.route('/wbsId/:id') .get(controller.getWBSById); + wbsRouter.route('/wbs/user/:userId') + .get(controller.getWBSByUserId); + wbsRouter.route('/wbs').get(controller.getWBS); return wbsRouter; From 1f2d4bec21da660d2f943f14adb098d5c9aa71c5 Mon Sep 17 00:00:00 2001 From: Ruike Qiu <114443664+StrawberryCalpico@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:16:31 -0800 Subject: [PATCH 107/129] Update for adminLinks --- src/routes/userProfileRouter.js | 78 +++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/routes/userProfileRouter.js b/src/routes/userProfileRouter.js index 8d7807514..ac567ebe8 100644 --- a/src/routes/userProfileRouter.js +++ b/src/routes/userProfileRouter.js @@ -1,81 +1,91 @@ -const express = require("express"); -import { body } from "express-validator"; +import { body } from 'express-validator'; + +const express = require('express'); + const routes = function (userProfile) { - const controller = require("../controllers/userProfileController")( - userProfile + const controller = require('../controllers/userProfileController')( + userProfile, ); const userProfileRouter = express.Router(); userProfileRouter - .route("/userProfile") + .route('/userProfile') .get(controller.getUserProfiles) .post( - body("firstName").customSanitizer((value) => value.trim()), - body("lastName").customSanitizer((value) => value.trim()), - controller.postUserProfile + body('firstName').customSanitizer(value => value.trim()), + body('lastName').customSanitizer(value => value.trim()), + controller.postUserProfile, ); userProfileRouter - .route("/userProfile/:userId") + .route('/userProfile/:userId') .get(controller.getUserById) .put( - body("firstName").customSanitizer((value) => value.trim()), - body("lastName").customSanitizer((value) => value.trim()), - body("personalLinks").customSanitizer((value) => - value.map((link) => { - if (link.Name.replace(/\s/g, "") || link.Link.replace(/\s/g, "")) { - return { - ...link, - Name: link.Name.trim(), - Link: link.Link.replace(/\s/g, ""), - }; - } - throw new Error("Url not valid"); - }) - ), - controller.putUserProfile + body('firstName').customSanitizer(value => value.trim()), + body('lastName').customSanitizer(value => value.trim()), + body('personalLinks').customSanitizer(value => value.map((link) => { + if (link.Name.replace(/\s/g, '') || link.Link.replace(/\s/g, '')) { + return { + ...link, + Name: link.Name.trim(), + Link: link.Link.replace(/\s/g, ''), + }; + } + throw new Error('Url not valid'); + })), + body('adminLinks').customSanitizer(value => value.map((link) => { + if (link.Name.replace(/\s/g, '') || link.Link.replace(/\s/g, '')) { + return { + ...link, + Name: link.Name.trim(), + Link: link.Link.replace(/\s/g, ''), + }; + } + throw new Error('Url not valid'); + })), + controller.putUserProfile, ) .delete(controller.deleteUserProfile) .patch(controller.changeUserStatus); userProfileRouter - .route("/userProfile/name/:name") + .route('/userProfile/name/:name') .get(controller.getUserByName); - userProfileRouter.route("/refreshToken/:userId").get(controller.refreshToken); + userProfileRouter.route('/refreshToken/:userId').get(controller.refreshToken); userProfileRouter - .route("/userProfile/reportees/:userId") + .route('/userProfile/reportees/:userId') .get(controller.getreportees); userProfileRouter - .route("/userProfile/teammembers/:userId") + .route('/userProfile/teammembers/:userId') .get(controller.getTeamMembersofUser); userProfileRouter - .route("/userProfile/:userId/property") + .route('/userProfile/:userId/property') .patch(controller.updateOneProperty); userProfileRouter - .route("/userProfile/:userId/updatePassword") + .route('/userProfile/:userId/updatePassword') .patch(controller.updatepassword); userProfileRouter - .route("/userProfile/:userId/resetPassword") + .route('/userProfile/:userId/resetPassword') .patch(controller.resetPassword); userProfileRouter - .route("/userProfile/name/:userId") + .route('/userProfile/name/:userId') .get(controller.getUserName); userProfileRouter - .route("/userProfile/project/:projectId") + .route('/userProfile/project/:projectId') .get(controller.getProjectMembers); userProfileRouter - .route("/userProfile/socials/facebook") + .route('/userProfile/socials/facebook') .get(controller.getAllUsersWithFacebookLink); return userProfileRouter; From 01e386b2030049c95e73e961aa6af661dba5b2eb Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Wed, 20 Dec 2023 20:59:34 -0800 Subject: [PATCH 108/129] fix issue that logged time reset on leaderboard --- src/controllers/timeEntryController.js | 29 +++++++++++++++++--------- src/helpers/dashboardhelper.js | 29 ++++++++++++++++++++------ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 3516517cc..effe61a3b 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -14,6 +14,13 @@ const formatSeconds = function (seconds) { return values.split(':'); }; +const isGeneralTimeEntry = function (type) { + if (type === undefined || type === 'default') { + return true; + } + return false; +}; + /** * * @param {*} firstName First name of the owner of the time entry that was modified @@ -135,6 +142,7 @@ const timeEntrycontroller = function (TimeEntry) { session.startTransaction(); const type = req.body.entryType; + const isGeneralEntry = isGeneralTimeEntry(type); try { if (!req.params.timeEntryId) { @@ -147,7 +155,7 @@ const timeEntrycontroller = function (TimeEntry) { if ( !mongoose.Types.ObjectId.isValid(req.params.timeEntryId) - || ((type === 'default' || type === 'project') + || ((isGeneralEntry || type === 'project') && !mongoose.Types.ObjectId.isValid(req.body.projectId) )) { return res @@ -169,7 +177,7 @@ const timeEntrycontroller = function (TimeEntry) { if ( !( (await hasPermission(req.body.requestor, 'editTimeEntry')) - || (type === 'default' + || (isGeneralEntry && timeEntry.personId.toString() === req.body.requestor.requestorId.toString() ) @@ -183,7 +191,7 @@ const timeEntrycontroller = function (TimeEntry) { const totalSeconds = moment.duration(`${hours}:${minutes}`).asSeconds(); if ( - type === 'default' + isGeneralEntry && timeEntry.isTangible === true && totalSeconds !== timeEntry.totalSeconds ) { @@ -207,13 +215,13 @@ const timeEntrycontroller = function (TimeEntry) { timeEntry.lastModifiedDateTime = moment().utc().toISOString(); timeEntry.projectId = mongoose.Types.ObjectId(req.body.projectId); timeEntry.dateOfWork = moment(req.body.dateOfWork).format('YYYY-MM-DD'); - timeEntry.entryType = req.body.entryType; + timeEntry.entryType = req.body.entryType === undefined ? 'default' : req.body.entryType; // Update the hoursLogged field of related tasks based on before and after timeEntries // initialIsTangible is a bealoon value, req.body.isTangible is a string // initialProjectId may be a task id or project id, so do not throw error. try { - if (type === 'default' && findTask) { + if (isGeneralEntry && findTask) { if (initialIsTangible === true) { findTask.hoursLogged -= initialSeconds / 3600; } @@ -230,7 +238,7 @@ const timeEntrycontroller = function (TimeEntry) { // Update edit history if ( - (type === 'default' || type === 'person') + (isGeneralEntry || type === 'person') && initialSeconds !== totalSeconds && timeEntry.isTangible && req.body.requestor.requestorId === timeEntry.personId.toString() @@ -245,7 +253,7 @@ const timeEntrycontroller = function (TimeEntry) { newSeconds: totalSeconds, }); - if (type === 'default') { + if (isGeneralEntry) { // Issue infraction if edit history contains more than 5 edits in the last year let totalRecentEdits = 0; @@ -303,7 +311,7 @@ const timeEntrycontroller = function (TimeEntry) { res.status(200).send({ message: 'Successfully updated time entry' }); // If the time entry isn't related to a task (i.e. it's a project), then don't check for overtime (Most likely pr team) - if (type === 'default' && findTask) { + if (isGeneralEntry && findTask) { // checking if logged in hours exceed estimated time after timeentry edit for a task const record = await userProfile.findById( timeEntry.personId.toString(), @@ -328,7 +336,8 @@ const timeEntrycontroller = function (TimeEntry) { } const items = []; records.forEach((element) => { - if (element.entryType === 'default' || element.entryType === undefined) { + const isGeneralEntry = isGeneralTimeEntry(element.entryType); + if (isGeneralEntry) { const timeentry = new TimeEntry(); timeentry.personId = element.personId; timeentry.projectId = element.projectId; @@ -400,7 +409,7 @@ const timeEntrycontroller = function (TimeEntry) { timeentry.isTangible = req.body.isTangible; timeentry.createdDateTime = moment().utc().toISOString(); timeentry.lastModifiedDateTime = moment().utc().toISOString(); - timeentry.entryType = req.body.entryType; + timeentry.entryType = req.body.entryType === undefined ? 'default' : req.body.entryType; timeentry .save() diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index bc56fc5c6..b7ca2f131 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -70,7 +70,11 @@ const dashboardhelper = function () { $lte: ['$$timeentry.dateOfWork', pdtend], }, { - $in: ['$$timeentry.entryType', ['default', null]], + $not: [ + { + $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + }, + ], }, ], }, @@ -155,7 +159,7 @@ const dashboardhelper = function () { return output; }; - const getLeaderboard = function (userId) { + const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); const pdtstart = moment() .tz('America/Los_Angeles') @@ -165,7 +169,7 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - return myTeam.aggregate([ + const output = await myTeam.aggregate([ { $match: { _id: userid, @@ -287,7 +291,11 @@ const dashboardhelper = function () { $lte: ['$$timeentry.dateOfWork', pdtend], }, { - $in: ['$$timeentry.entryType', ['default', null]], + $not: [ + { + $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + }, + ], }, ], }, @@ -416,6 +424,7 @@ const dashboardhelper = function () { }, }, ]); + return output; }; /** @@ -583,7 +592,11 @@ const dashboardhelper = function () { $lte: ['$$timeentry.dateOfWork', todate], }, { - $in: ['$$timeentry.entryType', ['default', null]], + $not: [ + { + $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + }, + ], }, ], }, @@ -663,7 +676,11 @@ const dashboardhelper = function () { $lte: ['$$timeentry.dateOfWork', todate], }, { - $in: ['$$timeentry.entryType', ['default', null]], + $not: [ + { + $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + }, + ], }, ], }, From 62b4ff352f874123aef9726cd044f3c942f90a52 Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Thu, 21 Dec 2023 02:58:10 -0600 Subject: [PATCH 109/129] fixed error --- .../profileInitialSetupController.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index df1f5adfb..12776442e 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -101,6 +101,18 @@ const profileInitialSetupController = function ( ) { const { JWT_SECRET } = config; + const setMapLocation = async (locationData) => { + + const location = new MapLocation(locationData); + + try { + const response = await location.save() + return response + } catch (err) { + return {type: "Error", message: err.message || 'An error occurred while saving the location'} + } + } + /* Function to handle token generation and email process: - Generates a new token and saves it to the database. @@ -319,17 +331,7 @@ const profileInitialSetupController = function ( } }; - const setMapLocation = async (locationData) => { - const location = new MapLocation(locationData); - - try { - const response = await location.save() - return response - } catch (err) { - return {type: "Error", message: err.message || 'An error occurred while saving the location'} - } - } return { getSetupToken, From 9d3dda9b4792a9e62d6a548503217deab4194ce3 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sat, 23 Dec 2023 03:11:18 -0800 Subject: [PATCH 110/129] finish TimeEntryForm, TimeLog, TimeEntry refactor --- src/controllers/timeEntryController.js | 256 ++++++++++--------------- src/models/timeentry.js | 2 +- src/models/userProfile.js | 2 +- 3 files changed, 103 insertions(+), 157 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index bc7763705..6c2269a1d 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -53,9 +53,8 @@ const getEditedTimeEntryEmailBody = ( * @param {*} final Final time entry object * @returns {Void} */ -const notifyEditByEmail = async (personId, original, finalTime, final) => { +const notifyEditByEmail = async (personId, originalTime, finalTime, final) => { try { - const originalTime = original.totalSeconds; const record = await UserProfile.findById(personId); const requestor = personId !== final.requestor.requestorId ? await UserProfile.findById(final.requestor.requestorId) @@ -112,7 +111,7 @@ const notifyTaskOvertimeEmailBody = async ( } }; -const checkTaskOvertime = async (timeentry, record, currentTask) => { +const checkTaskOvertime = async (timeentry, currentUser, currentTask) => { try { // send email notification if logged in hours exceeds estiamted hours for a task if (currentTask.hoursLogged > currentTask.estimatedHours) { @@ -125,7 +124,7 @@ const checkTaskOvertime = async (timeentry, record, currentTask) => { } } catch (error) { console.log( - `Failed to find task whose logged-in hours are more than estimated hours ${record.email}`, + `Failed to find task whose logged-in hours are more than estimated hours for ${currentUser.email}`, ); } }; @@ -163,18 +162,18 @@ const timeEntrycontroller = function (TimeEntry) { const { personId, - hours = '00', - minutes = '00', - notes, - isTangible, - projectId, - wbsId, - taskId, - dateOfWork, + hours: newHours = '00', + minutes: newMinutes = '00', + notes: newNotes, + isTangible: newIsTangible, + projectId: newProjectId, + wbsId: newWbsId, + taskId: newTaskId, + dateOfWork: newDateOfWork, } = req.body; const isForAuthUser = personId === req.body.requestor.requestorId; - const isSameDayTimeEntry = moment().tz('America/Los_Angeles').format('YYYY-MM-DD') === dateOfWork; + const isSameDayTimeEntry = moment().tz('America/Los_Angeles').format('YYYY-MM-DD') === newDateOfWork; const canEdit = (await hasPermission(req.body.requestor, 'editTimeEntry')) || (isForAuthUser && isSameDayTimeEntry); if (!canEdit) { @@ -189,130 +188,98 @@ const timeEntrycontroller = function (TimeEntry) { try { if (!req.params.timeEntryId) { - return res - .status(400) - .send({ - error: 'ObjectId in request param is not in correct format', - }); + const error = 'ObjectId in request param is not in correct format'; + return res.status(400).send({ error }); } - if ( - !mongoose.Types.ObjectId.isValid(req.params.timeEntryId) + if (!mongoose.Types.ObjectId.isValid(req.params.timeEntryId) || ((type === 'default' || type === 'project') && !mongoose.Types.ObjectId.isValid(req.body.projectId) )) { - return res - .status(400) - .send({ error: 'ObjectIds are not correctly formed' }); + const error = 'ObjectIds are not correctly formed'; + return res.status(400).send({ error }); } // Get initial timeEntry by timeEntryId const timeEntry = await TimeEntry.findById(timeEntryId); + if (!timeEntry) { const error = `No valid records found for ${timeEntryId}`; return res.status(400).send({ error }); } - const totalSeconds = moment.duration({ hours, minutes }).asSeconds(); - const { - totalSeconds: initialTotalSeconds, - projectId: initialProjectId, - wbsId: initialWBSId, - taskId: initialTaskId, - isTangible: initialIsTangible, - } = timeEntry; + const newTotalSeconds = moment.duration({ hours: newHours, minutes: newMinutes }).asSeconds(); - if (initialIsTangible && totalSeconds !== initialTotalSeconds) { + if (timeEntry.isTangible && newIsTangible && newTotalSeconds !== timeEntry.totalSeconds) { notifyEditByEmail( timeEntry.personId.toString(), - timeEntry, - totalSeconds, + timeEntry.totalSeconds, + newTotalSeconds, req.body, ); } - updateTaskIdInTimeEntry(projectId, timeEntry); - - if (timeEntry.taskId) { - const findTask = await Task.findById(initialProjectId); - if (initialIsTangible) { - findTask.hoursLogged -= initialTotalSeconds / 3600; + if (newTaskId) { + const timeEntryTask = await Task.findById(newTaskId); + const timeEntryUser = await UserProfile.findById(personId); + if (timeEntry.isTangible) { + timeEntryTask.hoursLogged -= timeEntry.totalSeconds / 3600; } - if (req.body.isTangible === true) { - findTask.hoursLogged += totalSeconds / 3600; + if (newIsTangible) { + timeEntryTask.hoursLogged += newTotalSeconds / 3600; } - await findTask.save(); + checkTaskOvertime(timeEntry, timeEntryUser, timeEntryTask); + await timeEntryTask.save(); } - timeEntry.notes = notes; - timeEntry.totalSeconds = totalSeconds; - timeEntry.isTangible = isTangible; - timeEntry.lastModifiedDateTime = moment().utc().toISOString(); - timeEntry.projectId = mongoose.Types.ObjectId(projectId); - timeEntry.wbsId = wbsId ? mongoose.Types.ObjectId(wbsId) : null; - timeEntry.taskId = taskId ? mongoose.Types.ObjectId(taskId) : null; - timeEntry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); - // Update edit history - if ( - (type === 'default' || type === 'person') - && initialSeconds !== totalSeconds + if ((type === 'default' || type === 'person') + && timeEntry.totalSeconds !== newTotalSeconds && timeEntry.isTangible - && req.body.requestor.requestorId === timeEntry.personId.toString() + && isForAuthUser && !(await hasPermission(req.body.requestor, 'editTimeEntry')) ) { const requestor = await UserProfile.findById( req.body.requestor.requestorId, ); + requestor.timeEntryEditHistory.push({ date: moment().tz('America/Los_Angeles').toDate(), - initialSeconds: initialTotalSeconds, - newSeconds: totalSeconds, + initialSeconds: timeEntry.totalSeconds, + newSeconds: newTotalSeconds, }); if (type === 'default') { // Issue infraction if edit history contains more than 5 edits in the last year let totalRecentEdits = 0; - requestor.timeEntryEditHistory.forEach((edit) => { - if ( - moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365 - ) { - totalRecentEdits += 1; - } - }); requestor.timeEntryEditHistory.forEach((edit) => { if ( - moment().tz('America/Los_Angeles').diff(edit.date, 'days') <= 365 - ) { - totalRecentEdits += 1; - } + moment() + .tz('America/Los_Angeles') + .diff(edit.date, 'days') <= 365 + ) totalRecentEdits += 1; }); - if (totalRecentEdits >= 5) { - requestor.infringements.push({ - date: moment().tz('America/Los_Angeles'), - description: `${totalRecentEdits} time entry edits in the last calendar year`, - }); if (totalRecentEdits >= 5) { requestor.infringements.push({ date: moment().tz('America/Los_Angeles'), description: `${totalRecentEdits} time entry edits in the last calendar year`, }); - emailSender( - "onecommunityglobal@gmail.com", - `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, - ` -

- ${requestor.firstName} ${requestor.lastName} (${requestor.email}) was issued a blue square for editing their time entries ${totalRecentEdits} times - within the last calendar year. -

-

- This is the ${totalRecentEdits}th edit within the past 365 days. -

- ` - ); + emailSender( + 'onecommunityglobal@gmail.com', + `${requestor.firstName} ${requestor.lastName} was issued a blue square for for editing a time entry ${totalRecentEdits} times`, + ` +

+ ${requestor.firstName} ${requestor.lastName} (${requestor.email}) was issued a blue square for editing their time entries ${totalRecentEdits} times + within the last calendar year. +

+

+ This is the ${totalRecentEdits}th edit within the past 365 days. +

+ `, + ); const emailInfringement = { date: moment().tz('America/Los_Angeles').format('MMMM-DD-YY'), @@ -331,31 +298,26 @@ const timeEntrycontroller = function (TimeEntry) { ); } } - await requestor.save(); } + timeEntry.notes = newNotes; + timeEntry.totalSeconds = newTotalSeconds; + timeEntry.isTangible = newIsTangible; + timeEntry.lastModifiedDateTime = moment().utc().toISOString(); + timeEntry.projectId = mongoose.Types.ObjectId(newProjectId); + timeEntry.wbsId = newWbsId ? mongoose.Types.ObjectId(newWbsId) : null; + timeEntry.taskId = newTaskId ? mongoose.Types.ObjectId(newTaskId) : null; + timeEntry.dateOfWork = moment(newDateOfWork).format('YYYY-MM-DD'); await timeEntry.save(); - res.status(200).send({ message: 'Successfully updated time entry' }); - - // If the time entry isn't related to a task (i.e. it's a project), then don't check for overtime (Most likely pr team) - if (type === 'default' && findTask) { - // checking if logged in hours exceed estimated time after timeentry edit for a task - const record = await UserProfile.findById( - timeEntry.personId.toString(), - ); - const currentTask = await Task.findById(req.body.projectId); - checkTaskOvertime(timeEntry, record, currentTask); - } + return res.status(200).send({ message: 'Successfully updated time entry' }); } catch (err) { await session.abortTransaction(); return res.status(400).send({ error: err.toString() }); } finally { session.endSession(); } - - return res.status(200).send(); }; const getAllTimeEnteries = function (req, res) { @@ -369,6 +331,8 @@ const timeEntrycontroller = function (TimeEntry) { const timeentry = new TimeEntry(); timeentry.personId = element.personId; timeentry.projectId = element.projectId; + timeentry.wbsId = element.wbsId; + timeentry.taskId = element.taskId; timeentry.dateOfWork = element.dateOfWork; timeentry.timeSpent = moment('1900-01-01 00:00:00') .add(element.totalSeconds, 'seconds') @@ -386,8 +350,7 @@ const timeEntrycontroller = function (TimeEntry) { const postTimeEntry = async function (req, res) { const isInvalid = !req.body.dateOfWork || !moment(req.body.dateOfWork).isValid() - || !req.body.timeSpent - || !req.body.isTangible; + || !req.body.timeSpent; const returnErr = (result) => { result.status(400).send({ error: 'Bad request' }); @@ -426,57 +389,40 @@ const timeEntrycontroller = function (TimeEntry) { break; } - const timeentry = new TimeEntry(); + const timeEntry = new TimeEntry(); const { dateOfWork, timeSpent } = req.body; - timeentry.personId = req.body.personId; - timeentry.projectId = req.body.projectId; - timeentry.wbsId = req.body.wbsId; - timeentry.taskId = req.body.taskId; - timeentry.teamId = req.body.teamId; - timeentry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); - timeentry.totalSeconds = moment.duration(timeSpent).asSeconds(); - timeentry.notes = req.body.notes; - timeentry.isTangible = req.body.isTangible; - timeentry.createdDateTime = moment().utc().toISOString(); - timeentry.lastModifiedDateTime = moment().utc().toISOString(); - timeentry.entryType = req.body.entryType; - - timeentry - .save() - .then((results) => { - res - .status(200) - .send({ message: `Time Entry saved with id as ${results._id}` }); - }) - .catch((error) => { - res.status(400).send(error); - }); - - if (timeentry.entryType === 'default') { - // Get the task related to this time entry, if not found, then it's a project sets to null - const currentTask = await task - .findById(req.body.projectId) - .catch(() => null); - - // Add this tangbile time entry to related task's hoursLogged and checks if timeEntry is related to a task - if (timeentry.isTangible === true && currentTask) { - try { - currentTask.hoursLogged += timeentry.totalSeconds / 3600; - await currentTask.save(); - } catch (error) { - throw new Error(error); - } + timeEntry.personId = req.body.personId; + timeEntry.projectId = req.body.projectId; + timeEntry.wbsId = req.body.wbsId; + timeEntry.taskId = req.body.taskId; + timeEntry.teamId = req.body.teamId; + timeEntry.dateOfWork = moment(dateOfWork).format('YYYY-MM-DD'); + timeEntry.totalSeconds = moment.duration(timeSpent).asSeconds(); + timeEntry.notes = req.body.notes; + timeEntry.isTangible = req.body.isTangible; + timeEntry.createdDateTime = moment().utc().toISOString(); + timeEntry.lastModifiedDateTime = moment().utc().toISOString(); + timeEntry.entryType = req.body.entryType; + + if (timeEntry.taskId) { + const timeEntryTask = await Task.findById(timeEntry.taskId); + const timeEntryUser = await UserProfile.findById(timeEntry.personId); + if (timeEntry.isTangible) { + timeEntryTask.hoursLogged += timeEntry.totalSeconds / 3600; } + checkTaskOvertime(timeEntry, timeEntryUser, timeEntryTask); + await timeEntryTask.save(); + } - // checking if logged in hours exceed estimated time after timeentry for a task, only if the time entry is related to a task (It might not be, if it's a project) - if (currentTask) { - try { - const record = await UserProfile.findById(timeentry.personId.toString()); - checkTaskOvertime(timeentry, record, currentTask); - } catch (error) { - throw new Error(error); - } - } + try { + return timeEntry + .save() + .then(results => res.status(200).send({ + message: `Time Entry saved with id as ${results._id}`, + })) + .catch(error => res.status(400).send(error)); + } catch (error) { + return res.status(500).send(error); } }; @@ -510,8 +456,8 @@ const timeEntrycontroller = function (TimeEntry) { const results = await Promise.all(timeEntries.map(async (timeEntry) => { timeEntry = { ...timeEntry.toObject() }; - const { projectId } = timeEntry; - await updateTaskIdInTimeEntry(projectId, timeEntry); + const { projectId, taskId } = timeEntry; + if (!taskId) await updateTaskIdInTimeEntry(projectId, timeEntry); // if no taskId, then it might be old time entry data that didn't separate projectId with taskId const hours = Math.floor(timeEntry.totalSeconds / 3600); const minutes = Math.floor((timeEntry.totalSeconds % 3600) / 60); Object.assign(timeEntry, { hours, minutes, totalSeconds: undefined }); @@ -520,7 +466,7 @@ const timeEntrycontroller = function (TimeEntry) { res.status(200).send(results); } catch (error) { - res.status(400).send({ error}); + res.status(400).send({ error }); } }; @@ -773,7 +719,7 @@ const timeEntrycontroller = function (TimeEntry) { getTimeEntriesForUsersList, editTimeEntry, deleteTimeEntry, - // getTimeEntriesForSpecifiedProject, + getTimeEntriesForSpecifiedProject, checkTaskOvertime, getLostTimeEntriesForUserList, getLostTimeEntriesForProjectList, diff --git a/src/models/timeentry.js b/src/models/timeentry.js index 5343e9758..79504cc20 100644 --- a/src/models/timeentry.js +++ b/src/models/timeentry.js @@ -8,7 +8,7 @@ const TimeEntry = new Schema({ personId: { type: Schema.Types.ObjectId, ref: 'userProfile' }, projectId: { type: Schema.Types.ObjectId, ref: 'project' }, wbsId: { type: Schema.Types.ObjectId, ref: 'project' }, - taskId: { type: Schema.Types.ObjectId, ref: 'wbs' }, + taskId: { type: Schema.Types.ObjectId, default: null, ref: 'wbs' }, teamId: { type: Schema.Types.ObjectId, ref: 'task' }, dateOfWork: { type: String, required: true }, totalSeconds: { type: Number }, diff --git a/src/models/userProfile.js b/src/models/userProfile.js index e3f8d4a48..8a8de2e18 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -88,7 +88,7 @@ const userProfileSchema = new Schema({ lng: { type: Number, default: '' }, }, country: { type: String, default: '' }, - city: { type: String, default: '' } + city: { type: String, default: '' }, }, oldInfringements: [ From c1328bbe8fd64e668c5524aa58a8c66e77143e01 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sat, 23 Dec 2023 15:16:53 -0800 Subject: [PATCH 111/129] add task handle for project or task change when editing time entry --- src/controllers/timeEntryController.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 9aef95ddb..c8ef3d464 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -211,7 +211,6 @@ const timeEntrycontroller = function (TimeEntry) { // Get initial timeEntry by timeEntryId const timeEntry = await TimeEntry.findById(timeEntryId); - if (!timeEntry) { const error = `No valid records found for ${timeEntryId}`; return res.status(400).send({ error }); @@ -228,7 +227,8 @@ const timeEntrycontroller = function (TimeEntry) { ); } - if (newTaskId) { + // update task data if project/task is changed + if (newTaskId === timeEntry.taskId) { const timeEntryTask = await Task.findById(newTaskId); const timeEntryUser = await UserProfile.findById(personId); if (timeEntry.isTangible) { @@ -239,6 +239,19 @@ const timeEntrycontroller = function (TimeEntry) { } checkTaskOvertime(timeEntry, timeEntryUser, timeEntryTask); await timeEntryTask.save(); + } else { + const oldTimeEntryTask = await Task.findById(timeEntry.taskId); + const newTimeEntryTask = await Task.findById(newTaskId); + const timeEntryUser = await UserProfile.findById(personId); + if (timeEntry.isTangible) { + oldTimeEntryTask.hoursLogged -= timeEntry.totalSeconds / 3600; + } + if (newIsTangible) { + newTimeEntryTask.hoursLogged += newTotalSeconds / 3600; + } + checkTaskOvertime(timeEntry, timeEntryUser, newTimeEntryTask); + await oldTimeEntryTask.save(); + await newTimeEntryTask.save(); } // Update edit history From 25595a567d1f5a909b26609c18d1ddf05692f394 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Sat, 30 Dec 2023 10:01:56 -0500 Subject: [PATCH 112/129] Fix badge update feature - Add code to update badge earned date when badge count changes --- src/helpers/userHelper.js | 655 +++++++++++++++++++------------------- 1 file changed, 331 insertions(+), 324 deletions(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 1033bab2f..97beafc17 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -1,71 +1,68 @@ /* eslint-disable no-continue */ /* eslint-disable no-await-in-loop */ -const mongoose = require("mongoose"); -const moment = require("moment-timezone"); -const _ = require("lodash"); -const userProfile = require("../models/userProfile"); -const timeEntries = require("../models/timeentry"); -const badge = require("../models/badge"); -const myTeam = require("./helperModels/myTeam"); -const dashboardHelper = require("./dashboardhelper")(); -const reportHelper = require("./reporthelper")(); -const emailSender = require("../utilities/emailSender"); -const logger = require("../startup/logger"); -const hasPermission = require("../utilities/permissions"); -const Reason = require("../models/reason"); -const token = require("../models/profileInitialSetupToken"); +const mongoose = require('mongoose'); +const moment = require('moment-timezone'); +const _ = require('lodash'); +const userProfile = require('../models/userProfile'); +const timeEntries = require('../models/timeentry'); +const badge = require('../models/badge'); +const myTeam = require('./helperModels/myTeam'); +const dashboardHelper = require('./dashboardhelper')(); +const reportHelper = require('./reporthelper')(); +const emailSender = require('../utilities/emailSender'); +const logger = require('../startup/logger'); +const Reason = require('../models/reason'); +const token = require('../models/profileInitialSetupToken'); const userHelper = function () { + // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) + const earnedDateBadge = () => { + const currentDate = new Date(Date.now()); + return moment(currentDate).tz('America/Los_Angeles').format('MMM-DD-YY'); + }; + + const getTeamMembers = function (user) { const userId = mongoose.Types.ObjectId(user._id); // var teamid = userdetails.teamId; return myTeam.findById(userId).select({ - "myTeam._id": 0, - "myTeam.role": 0, - "myTeam.fullName": 0, + 'myTeam._id': 0, + 'myTeam.role': 0, + 'myTeam.fullName': 0, _id: 0, }); }; - // Updated By: Shengwei - // Updated Date: 12/08/2023 - // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) - const earnedDateBadge = () => { - const currentDate = new Date(Date.now()); - return moment(currentDate).tz('America/Los_Angeles').format('MMM-DD-YY'); - }; - const getUserName = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - return userProfile.findById(userid, "firstName lastName"); + return userProfile.findById(userid, 'firstName lastName'); }; const validateProfilePic = function (profilePic) { - const picParts = profilePic.split("base64"); + const picParts = profilePic.split('base64'); let result = true; const errors = []; if (picParts.length < 2) { return { result: false, - errors: "Invalid image", + errors: 'Invalid image', }; } // validate size const imageSize = picParts[1].length; - const sizeInBytes = - (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; + const sizeInBytes = (4 * Math.ceil(imageSize / 3) * 0.5624896334383812) / 1024; if (sizeInBytes > 50) { - errors.push("Image size should not exceed 50KB"); + errors.push('Image size should not exceed 50KB'); result = false; } - const imageType = picParts[0].split("/")[1]; - if (imageType !== "jpeg;" && imageType !== "png;") { - errors.push("Image type shoud be either jpeg or png."); + const imageType = picParts[0].split('/')[1]; + if (imageType !== 'jpeg;' && imageType !== 'png;') { + errors.push('Image type shoud be either jpeg or png.'); result = false; } @@ -80,13 +77,12 @@ const userHelper = function () { lastName, infringement, totalInfringements, - timeRemaining + timeRemaining, ) { - let final_paragraph = ""; + let final_paragraph = ''; if (timeRemaining == undefined) { - final_paragraph = - "

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

"; + final_paragraph = '

Life happens and we understand that. That’s why we allow 5 of them before taking action. This action usually includes removal from our team though, so please let your direct supervisor know what happened and do your best to avoid future blue squares if you are getting close to 5 and wish to avoid termination. Each blue square drops off after a year.

'; } else { final_paragraph = `

Life happens and we understand that. Please make up the missed hours this following week though to avoid getting another blue square. So you know what’s needed, the missing/incomplete hours (${timeRemaining} hours) have been added to your current week and this new weekly total can be seen at the top of your dashboard.

Reminder also that each blue square is removed from your profile 1 year after it was issued.

`; @@ -117,10 +113,10 @@ const userHelper = function () { * @return {void} */ const emailWeeklySummariesForAllUsers = async (weekIndex = 1) => { - const currentFormattedDate = moment().tz("America/Los_Angeles").format(); + const currentFormattedDate = moment().tz('America/Los_Angeles').format(); logger.logInfo( - `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}` + `Job for emailing all users' weekly summaries starting at ${currentFormattedDate}`, ); const emails = []; @@ -128,19 +124,15 @@ const userHelper = function () { try { const results = await reportHelper.weeklySummaries(weekIndex, weekIndex); - let emailBody = "

Weekly Summaries for all active users:

"; + let emailBody = '

Weekly Summaries for all active users:

'; - const weeklySummaryNotProvidedMessage = - '
Weekly Summary: Not provided!
'; + const weeklySummaryNotProvidedMessage = '
Weekly Summary: Not provided!
'; - const weeklySummaryNotRequiredMessage = - '
Weekly Summary: Not required for this user
'; + const weeklySummaryNotRequiredMessage = '
Weekly Summary: Not required for this user
'; - results.sort((a, b) => - `${a.firstName} ${a.lastName}`.localeCompare( - `${b.firstName} ${b.lastname}` - ) - ); + results.sort((a, b) => `${a.firstName} ${a.lastName}`.localeCompare( + `${b.firstName} ${b.lastname}`, + )); for (let i = 0; i < results.length; i += 1) { const result = results[i]; @@ -166,19 +158,19 @@ const userHelper = function () { const mediaUrlLink = mediaUrl ? `${mediaUrl}` - : "Not provided!"; + : 'Not provided!'; let weeklySummaryMessage = weeklySummaryNotProvidedMessage; const colorStyle = (() => { switch (weeklySummaryOption) { - case "Team": + case 'Team': return 'style="color: magenta;"'; - case "Not Required": + case 'Not Required': return 'style="color: green"'; - case "Required": - return ""; + case 'Required': + return ''; default: - return result.weeklySummaryNotReq ? 'style="color: green"' : ""; + return result.weeklySummaryNotReq ? 'style="color: green"' : ''; } })(); // weeklySummaries array should only have one item if any, hence weeklySummaries[0] needs be used to access it. @@ -189,16 +181,16 @@ const userHelper = function () {
Weekly Summary (for the week ending on ${moment(dueDate) - .tz("America/Los_Angeles") - .format("YYYY-MMM-DD")}): + .tz('America/Los_Angeles') + .format('YYYY-MMM-DD')}):
${summary}
`; } else if ( - weeklySummaryOption === "Not Required" || - (!weeklySummaryOption && result.weeklySummaryNotReq) + weeklySummaryOption === 'Not Required' + || (!weeklySummaryOption && result.weeklySummaryNotReq) ) { weeklySummaryMessage = weeklySummaryNotRequiredMessage; } @@ -219,16 +211,16 @@ const userHelper = function () { weeklySummariesCount === 8 ? `

Total Valid Weekly Summaries: ${weeklySummariesCount}

` : `

Total Valid Weekly Summaries: ${ - weeklySummariesCount || "No valid submissions yet!" + weeklySummariesCount || 'No valid submissions yet!' }

` } ${ hoursLogged >= weeklycommittedHours ? `

Hours logged: ${hoursLogged.toFixed( - 2 + 2, )} / ${weeklycommittedHours}

` : `

Hours logged: ${hoursLogged.toFixed( - 2 + 2, )} / ${weeklycommittedHours}

` } ${weeklySummaryMessage} @@ -238,10 +230,8 @@ const userHelper = function () { // Necessary because our version of node is outdated // and doesn't have String.prototype.replaceAll let emailString = [...new Set(emails)].toString(); - while (emailString.includes(",")) - emailString = emailString.replace(",", "\n"); - while (emailString.includes("\n")) - emailString = emailString.replace("\n", ", "); + while (emailString.includes(',')) { emailString = emailString.replace(',', '\n'); } + while (emailString.includes('\n')) { emailString = emailString.replace('\n', ', '); } emailBody += `\n
@@ -253,12 +243,12 @@ const userHelper = function () { `; emailSender( - "onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com", - "Weekly Summaries for all active users...", + 'onecommunityglobal@gmail.com, sangam.pravah@gmail.com, onecommunityhospitality@gmail.com', + 'Weekly Summaries for all active users...', emailBody, null, null, - emailString + emailString, ); } catch (err) { logger.logException(err); @@ -279,8 +269,8 @@ const userHelper = function () { weeklySummaries: { $each: [ { - dueDate: moment().tz("America/Los_Angeles").endOf("week"), - summary: "", + dueDate: moment().tz('America/Los_Angeles').endOf('week'), + summary: '', }, ], $position: 0, @@ -288,7 +278,7 @@ const userHelper = function () { }, }, }) - .catch((error) => logger.logException(error)); + .catch(error => logger.logException(error)); }; /** @@ -299,34 +289,34 @@ const userHelper = function () { */ const assignBlueSquareForTimeNotMet = async () => { try { - const currentFormattedDate = moment().tz("America/Los_Angeles").format(); + const currentFormattedDate = moment().tz('America/Los_Angeles').format(); const currentUTCDate = moment - .tz("America/Los_Angeles") - .startOf("day") + .tz('America/Los_Angeles') + .startOf('day') .toISOString(); logger.logInfo( - `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}` + `Job for assigning blue square for commitment not met starting at ${currentFormattedDate}`, ); const pdtStartOfLastWeek = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(1, "week"); + .tz('America/Los_Angeles') + .startOf('week') + .subtract(1, 'week'); const pdtEndOfLastWeek = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(1, "week"); + .tz('America/Los_Angeles') + .endOf('week') + .subtract(1, 'week'); const users = await userProfile.find( { isActive: true }, - "_id weeklycommittedHours weeklySummaries missedHours" + '_id weeklycommittedHours weeklySummaries missedHours', ); - //this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be - //targeted as spam - //There's no need to put Promise.all here + // this part is supposed to be a for, so it'll be slower when sending emails, so the emails will not be + // targeted as spam + // There's no need to put Promise.all here for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -342,8 +332,8 @@ const userHelper = function () { let hasWeeklySummary = false; if ( - Array.isArray(user.weeklySummaries) && - user.weeklySummaries.length + Array.isArray(user.weeklySummaries) + && user.weeklySummaries.length ) { const { summary } = user.weeklySummaries[0]; if (summary) { @@ -357,13 +347,12 @@ const userHelper = function () { const results = await dashboardHelper.laborthisweek( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek + pdtEndOfLastWeek, ); const { timeSpent_hrs: timeSpent } = results[0]; - const weeklycommittedHours = - user.weeklycommittedHours + (user.missedHours ?? 0); + const weeklycommittedHours = user.weeklycommittedHours + (user.missedHours ?? 0); const timeNotMet = timeSpent < weeklycommittedHours; let description; @@ -386,23 +375,23 @@ const userHelper = function () { lastWeekTangibleHrs: timeSpent || 0, }, }, - { new: true } + { new: true }, ); if ( - updateResult?.weeklySummaryOption === "Not Required" || - updateResult?.weeklySummaryNotReq + updateResult?.weeklySummaryOption === 'Not Required' + || updateResult?.weeklySummaryNotReq ) { hasWeeklySummary = true; } - const cutOffDate = moment().subtract(1, "year"); + const cutOffDate = moment().subtract(1, 'year'); const oldInfringements = []; for (let k = 0; k < updateResult?.infringements.length; k += 1) { if ( - updateResult?.infringements && - moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 + updateResult?.infringements + && moment(updateResult?.infringements[k].date).diff(cutOffDate) >= 0 ) { oldInfringements.push(updateResult.infringements[k]); } else { @@ -418,35 +407,33 @@ const userHelper = function () { oldInfringements: { $each: oldInfringements, $slice: -10 }, }, }, - { new: true } + { new: true }, ); } if (timeNotMet || !hasWeeklySummary) { if (foundReason) { description = foundReason.reason; - } else { - if (timeNotMet && !hasWeeklySummary) { + } else if (timeNotMet && !hasWeeklySummary) { description = `System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. For the hours portion, you logged ${timeSpent.toFixed( - 2 + 2, )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } else if (timeNotMet) { description = `System auto-assigned infringement for not meeting weekly volunteer time commitment. You logged ${timeSpent.toFixed( - 2 + 2, )} hours against committed effort of ${weeklycommittedHours} hours in the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } else { description = `System auto-assigned infringement for not submitting a weekly summary for the week starting ${pdtStartOfLastWeek.format( - "dddd YYYY-MM-DD" - )} and ending ${pdtEndOfLastWeek.format("dddd YYYY-MM-DD")}.`; + 'dddd YYYY-MM-DD', + )} and ending ${pdtEndOfLastWeek.format('dddd YYYY-MM-DD')}.`; } - } const infringement = { - date: moment().utc().format("YYYY-MM-DD"), + date: moment().utc().format('YYYY-MM-DD'), description, }; @@ -457,47 +444,47 @@ const userHelper = function () { infringements: infringement, }, }, - { new: true } + { new: true }, ); - let emailBody = ""; - if (person.role == "Core Team" && timeRemaining > 0) { + let emailBody = ''; + if (person.role === 'Core Team' && timeRemaining > 0) { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, status.infringements.length, - timeRemaining + timeRemaining, ); } else { emailBody = getInfringementEmailBody( status.firstName, status.lastName, infringement, - status.infringements.length + status.infringements.length, ); } emailSender( status.email, - "New Infringement Assigned", + 'New Infringement Assigned', emailBody, null, - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', status.email, - null + null, ); const categories = await dashboardHelper.laborThisWeekByCategory( personId, pdtStartOfLastWeek, - pdtEndOfLastWeek + pdtEndOfLastWeek, ); if (Array.isArray(categories) && categories.length > 0) { await userProfile.findOneAndUpdate( { _id: personId, categoryTangibleHrs: { $exists: false } }, - { $set: { categoryTangibleHrs: [] } } + { $set: { categoryTangibleHrs: [] } }, ); } else { continue; @@ -507,20 +494,20 @@ const userHelper = function () { const elem = categories[j]; if (elem._id == null) { - elem._id = "Other"; + elem._id = 'Other'; } const updateResult2 = await userProfile.findOneAndUpdate( - { _id: personId, "categoryTangibleHrs.category": elem._id }, - { $inc: { "categoryTangibleHrs.$.hrs": elem.timeSpent_hrs } }, - { new: true } + { _id: personId, 'categoryTangibleHrs.category': elem._id }, + { $inc: { 'categoryTangibleHrs.$.hrs': elem.timeSpent_hrs } }, + { new: true }, ); if (!updateResult2) { await userProfile.findOneAndUpdate( { _id: personId, - "categoryTangibleHrs.category": { $ne: elem._id }, + 'categoryTangibleHrs.category': { $ne: elem._id }, }, { $addToSet: { @@ -529,7 +516,7 @@ const userHelper = function () { hrs: elem.timeSpent_hrs, }, }, - } + }, ); } } @@ -541,13 +528,13 @@ const userHelper = function () { // processWeeklySummaries for nonActive users try { - const inactiveUsers = await userProfile.find({ isActive: false }, "_id"); + const inactiveUsers = await userProfile.find({ isActive: false }, '_id'); for (let i = 0; i < inactiveUsers.length; i += 1) { const user = inactiveUsers[i]; await processWeeklySummariesByUserId( mongoose.Types.ObjectId(user._id), - false + false, ); } } catch (err) { @@ -557,28 +544,28 @@ const userHelper = function () { const applyMissedHourForCoreTeam = async () => { try { - const currentDate = moment().tz("America/Los_Angeles").format(); + const currentDate = moment().tz('America/Los_Angeles').format(); logger.logInfo( - `Job for applying missed hours for Core Team members starting at ${currentDate}` + `Job for applying missed hours for Core Team members starting at ${currentDate}`, ); const startOfLastWeek = moment() - .tz("America/Los_Angeles") - .startOf("week") - .subtract(1, "week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .subtract(1, 'week') + .format('YYYY-MM-DD'); const endOfLastWeek = moment() - .tz("America/Los_Angeles") - .endOf("week") - .subtract(1, "week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .subtract(1, 'week') + .format('YYYY-MM-DD'); const missedHours = await userProfile.aggregate([ { $match: { - role: "Core Team", + role: 'Core Team', isActive: true, }, }, @@ -592,16 +579,15 @@ const userHelper = function () { $match: { $expr: { $and: [ - { $eq: ["$isTangible", true] }, - { $gte: ["$dateOfWork", startOfLastWeek] }, - { $lte: ["$dateOfWork", endOfLastWeek] }, - { $in: ["$entryType", 'default', null] }, + { $eq: ['$isTangible', true] }, + { $gte: ['$dateOfWork', startOfLastWeek] }, + { $lte: ['$dateOfWork', endOfLastWeek] }, ], }, }, }, ], - as: "timeEntries", + as: 'timeEntries', }, }, { @@ -613,8 +599,8 @@ const userHelper = function () { $subtract: [ { $sum: [ - { $ifNull: ["$missedHours", 0] }, - "$weeklycommittedHours", + { $ifNull: ['$missedHours', 0] }, + '$weeklycommittedHours', ], }, { @@ -622,8 +608,8 @@ const userHelper = function () { { $sum: { $map: { - input: "$timeEntries", - in: "$$this.totalSeconds", + input: '$timeEntries', + in: '$$this.totalSeconds', }, }, }, @@ -657,13 +643,13 @@ const userHelper = function () { }; const deleteBlueSquareAfterYear = async () => { - const currentFormattedDate = moment().tz("America/Los_Angeles").format(); + const currentFormattedDate = moment().tz('America/Los_Angeles').format(); logger.logInfo( - `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}` + `Job for deleting blue squares older than 1 year starting at ${currentFormattedDate}`, ); - const cutOffDate = moment().subtract(1, "year").format("YYYY-MM-DD"); + const cutOffDate = moment().subtract(1, 'year').format('YYYY-MM-DD'); try { const results = await userProfile.updateMany( @@ -676,7 +662,7 @@ const userHelper = function () { }, }, }, - } + }, ); logger.logInfo(results); @@ -686,16 +672,16 @@ const userHelper = function () { }; const reActivateUser = async () => { - const currentFormattedDate = moment().tz("America/Los_Angeles").format(); + const currentFormattedDate = moment().tz('America/Los_Angeles').format(); logger.logInfo( - `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}` + `Job for activating users based on scheduled re-activation date starting at ${currentFormattedDate}`, ); try { const users = await userProfile.find( { isActive: false, reactivationDate: { $exists: true } }, - "_id isActive reactivationDate" + '_id isActive reactivationDate', ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -710,21 +696,21 @@ const userHelper = function () { endDate: user.endDate, }, }, - { new: true } + { new: true }, ); logger.logInfo( `User with id: ${user._id} was re-acticated at ${moment() - .tz("America/Los_Angeles") - .format()}.` + .tz('America/Los_Angeles') + .format()}.`, ); const id = user._id; const person = await userProfile.findById(id); - const endDate = moment(person.endDate).format("YYYY-MM-DD"); + const endDate = moment(person.endDate).format('YYYY-MM-DD'); logger.logInfo( `User with id: ${ user._id - } was re-acticated at ${moment().format()}.` + } was re-acticated at ${moment().format()}.`, ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been RE-activated in the Highest Good Network`; @@ -740,12 +726,12 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', subject, emailBody, null, null, - person.email + person.email, ); } } @@ -759,7 +745,7 @@ const userHelper = function () { current, firstName, lastName, - emailAddress + emailAddress, ) { if (!current) return; const newOriginal = original.toObject(); @@ -770,58 +756,58 @@ const userHelper = function () { newInfringements = _.differenceWith( newCurrent, newOriginal, - (arrVal, othVal) => arrVal._id.equals(othVal._id) + (arrVal, othVal) => arrVal._id.equals(othVal._id), ); newInfringements.forEach((element) => { emailSender( emailAddress, - "New Infringement Assigned", + 'New Infringement Assigned', getInfringementEmailBody( firstName, lastName, element, - totalInfringements + totalInfringements, ), null, - "onecommunityglobal@gmail.com", - emailAddress + 'onecommunityglobal@gmail.com', + emailAddress, ); }); }; const replaceBadge = async function (personId, oldBadgeId, newBadgeId) { userProfile.updateOne( - { _id: personId, "badgeCollection.badge": oldBadgeId }, + { _id: personId, 'badgeCollection.badge': oldBadgeId }, { $set: { - "badgeCollection.$.badge": newBadgeId, - "badgeCollection.$.lastModified": Date.now().toString(), - "badgeCollection.$.count": 1, + 'badgeCollection.$.badge': newBadgeId, + 'badgeCollection.$.lastModified': Date.now().toString(), + 'badgeCollection.$.count': 1, + 'badgeCollection.$.earnedDate': [earnedDateBadge()], }, }, (err) => { if (err) { throw new Error(err); } - } + }, ); }; const increaseBadgeCount = async function (personId, badgeId) { - console.log("Increase Badge Count", personId, badgeId); userProfile.updateOne( - { _id: personId, "badgeCollection.badge": badgeId }, + { _id: personId, 'badgeCollection.badge': badgeId }, { - $inc: { "badgeCollection.$.count": 1 }, - $set: { "badgeCollection.$.lastModified": Date.now().toString() }, - $push: { "badgeCollection.$.earnedDate": earnedDateBadge() }, + $inc: { 'badgeCollection.$.count': 1 }, + $set: { 'badgeCollection.$.lastModified': Date.now().toString() }, + $push: { 'badgeCollection.$.earnedDate': earnedDateBadge() }, }, (err) => { if (err) { console.log(err); } - } + }, ); }; @@ -829,9 +815,8 @@ const userHelper = function () { personId, badgeId, count = 1, - featured = false + featured = false, ) { - console.log("Adding Badge"); userProfile.findByIdAndUpdate( personId, { @@ -849,7 +834,7 @@ const userHelper = function () { if (err) { throw new Error(err); } - } + }, ); }; @@ -865,27 +850,49 @@ const userHelper = function () { if (err) { throw new Error(err); } - } + }, ); }; - const changeBadgeCount = async function (personId, badgeId, count) { +const changeBadgeCount = async function (personId, badgeId, count) { if (count === 0) { removeDupBadge(personId, badgeId); } else if (count) { + // Process exisiting earned date to match the new count + const userInfo = await userProfile.findById(personId); + let newEarnedDate = []; + const recordToUpdate = userInfo.badgeCollection.find(item => item.badge._id.toString() === badgeId.toString()); + if (!recordToUpdate) { + throw new Error('Badge not found'); + } + const copyOfEarnedDate = recordToUpdate.earnedDate; + if (copyOfEarnedDate.length < count) { + // if the EarnedDate count is less than the new count, add a earned date to the end of the collection + while (copyOfEarnedDate.length < count) { + copyOfEarnedDate.push(earnedDateBadge()); + } + } else { + // if the EarnedDate count is greater than the new count, remove the oldest earned date of the collection until it matches the new count - 1 + while (copyOfEarnedDate.length >= count) { + copyOfEarnedDate.shift(); + } + copyOfEarnedDate.push(earnedDateBadge()); + } + newEarnedDate = [...copyOfEarnedDate]; userProfile.updateOne( - { _id: personId, "badgeCollection.badge": badgeId }, + { _id: personId, 'badgeCollection.badge': badgeId }, { $set: { - "badgeCollection.$.count": count, - "badgeCollection.$.lastModified": Date.now().toString(), + 'badgeCollection.$.count': count, + 'badgeCollection.$.lastModified': Date.now().toString(), + 'badgeCollection.$.earnedDate': newEarnedDate, }, }, (err) => { if (err) { throw new Error(err); } - } + }, ); } }; @@ -897,7 +904,7 @@ const userHelper = function () { user, badgeCollection, hrs, - weeks + weeks, ) { // Check each Streak Greater than One to check if it works if (weeks < 3) { @@ -908,7 +915,7 @@ const userHelper = function () { .aggregate([ { $match: { - type: "X Hours for X Week Streak", + type: 'X Hours for X Week Streak', weeks: { $gt: 1, $lt: weeks }, totalHrs: hrs, }, @@ -916,9 +923,9 @@ const userHelper = function () { { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: "$weeks", + _id: '$weeks', badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, + $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, }, }, }, @@ -928,16 +935,16 @@ const userHelper = function () { streak.badges.every((bdge) => { for (let i = 0; i < badgeCollection.length; i += 1) { if ( - badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && - badgeCollection[i].badge?.weeks === bdge.weeks && - bdge.hrs === hrs && - !removed + badgeCollection[i].badge?.type + === 'X Hours for X Week Streak' + && badgeCollection[i].badge?.weeks === bdge.weeks + && bdge.hrs === hrs + && !removed ) { changeBadgeCount( personId, badgeCollection[i].badge._id, - badgeCollection[i].badge.count - 1 + badgeCollection[i].badge.count - 1, ); removed = true; return false; @@ -950,24 +957,23 @@ const userHelper = function () { }; // 'No Infringement Streak', - const checkNoInfringementStreak = async function ( personId, user, - badgeCollection + badgeCollection, ) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "No Infringement Streak") { + if (badgeCollection[i].badge?.type === 'No Infringement Streak') { if ( - badgeOfType && - badgeOfType.months <= badgeCollection[i].badge.months + badgeOfType + && badgeOfType.months <= badgeCollection[i].badge.months ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.months > badgeCollection[i].badge.months + badgeOfType + && badgeOfType.months > badgeCollection[i].badge.months ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -976,7 +982,7 @@ const userHelper = function () { } } await badge - .find({ type: "No Infringement Streak" }) + .find({ type: 'No Infringement Streak' }) .sort({ months: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -988,19 +994,19 @@ const userHelper = function () { if (elem.months <= 12) { if ( - moment().diff(moment(user.createdDate), "months", true) >= - elem.months + moment().diff(moment(user.createdDate), 'months', true) + >= elem.months ) { if ( - user.infringements.length === 0 || - Math.abs( + user.infringements.length === 0 + || Math.abs( moment().diff( moment( - user.infringements[user.infringements?.length - 1].date + user.infringements[user.infringements?.length - 1].date, ), - "months", - true - ) + 'months', + true, + ), ) >= elem.months ) { if (badgeOfType) { @@ -1008,7 +1014,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -1019,29 +1025,29 @@ const userHelper = function () { } } else if (user?.infringements?.length === 0) { if ( - moment().diff(moment(user.createdDate), "months", true) >= - elem.months + moment().diff(moment(user.createdDate), 'months', true) + >= elem.months ) { if ( - user.oldInfringements.length === 0 || - Math.abs( + user.oldInfringements.length === 0 + || Math.abs( moment().diff( moment( user.oldInfringements[user.oldInfringements?.length - 1] - .date + .date, ), - "months", - true - ) - ) >= - elem.months - 12 + 'months', + true, + ), + ) + >= elem.months - 12 ) { if (badgeOfType) { if (badgeOfType._id.toString() !== elem._id.toString()) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -1060,13 +1066,13 @@ const userHelper = function () { const checkMinHoursMultiple = async function ( personId, user, - badgeCollection + badgeCollection, ) { const badgesOfType = badgeCollection - .map((obj) => obj.badge) - .filter((badge) => badge.type === "Minimum Hours Multiple"); + .map(obj => obj.badge) + .filter(badgeItem => badgeItem.type === 'Minimum Hours Multiple'); await badge - .find({ type: "Minimum Hours Multiple" }) + .find({ type: 'Minimum Hours Multiple' }) .sort({ multiple: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -1077,16 +1083,16 @@ const userHelper = function () { const elem = results[i]; // making variable elem accessible for below code if ( - user.lastWeekTangibleHrs / user.weeklycommittedHours >= - elem.multiple + user.lastWeekTangibleHrs / user.weeklycommittedHours + >= elem.multiple ) { const theBadge = badgesOfType.find( - (badge) => badge._id.toString() === elem._id.toString() + badgeItem => badgeItem._id.toString() === elem._id.toString(), ); return theBadge ? increaseBadgeCount( personId, - mongoose.Types.ObjectId(theBadge._id) + mongoose.Types.ObjectId(theBadge._id), ) : addBadge(personId, mongoose.Types.ObjectId(elem._id)); } @@ -1098,29 +1104,29 @@ const userHelper = function () { const checkPersonalMax = async function (personId, user, badgeCollection) { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "Personal Max") { + if (badgeCollection[i].badge?.type === 'Personal Max') { if (badgeOfType) { removeDupBadge(personId, badgeOfType._id); } } } - await badge.findOne({ type: "Personal Max" }).then((results) => { + await badge.findOne({ type: 'Personal Max' }).then((results) => { if ( - user.lastWeekTangibleHrs && - user.lastWeekTangibleHrs >= 1 && - user.lastWeekTangibleHrs === user.personalBestMaxHrs + user.lastWeekTangibleHrs + && user.lastWeekTangibleHrs >= 1 + && user.lastWeekTangibleHrs === user.personalBestMaxHrs ) { if (badgeOfType) { changeBadgeCount( personId, mongoose.Types.ObjectId(badgeOfType._id), - user.personalBestMaxHrs + user.personalBestMaxHrs, ); } else { addBadge( personId, mongoose.Types.ObjectId(results._id), - user.personalBestMaxHrs + user.personalBestMaxHrs, ); } } @@ -1131,17 +1137,17 @@ const userHelper = function () { const checkMostHrsWeek = async function (personId, user, badgeCollection) { if ( - user.weeklycommittedHours > 0 && - user.lastWeekTangibleHrs > user.weeklycommittedHours + user.weeklycommittedHours > 0 + && user.lastWeekTangibleHrs > user.weeklycommittedHours ) { const badgeOfType = badgeCollection - .filter((object) => object.badge.type === "Most Hrs in Week") - .map((object) => object.badge); - await badge.findOne({ type: "Most Hrs in Week" }).then((results) => { + .filter(object => object.badge.type === 'Most Hrs in Week') + .map(object => object.badge); + await badge.findOne({ type: 'Most Hrs in Week' }).then((results) => { userProfile .aggregate([ { $match: { isActive: true } }, - { $group: { _id: 1, maxHours: { $max: "$lastWeekTangibleHrs" } } }, + { $group: { _id: 1, maxHours: { $max: '$lastWeekTangibleHrs' } } }, ]) .then((userResults) => { if (badgeOfType.length > 1) { @@ -1149,13 +1155,13 @@ const userHelper = function () { } if ( - user.lastWeekTangibleHrs && - user.lastWeekTangibleHrs >= userResults[0].maxHours + user.lastWeekTangibleHrs + && user.lastWeekTangibleHrs >= userResults[0].maxHours ) { if (badgeOfType.length) { increaseBadgeCount( personId, - mongoose.Types.ObjectId(badgeOfType[0]._id) + mongoose.Types.ObjectId(badgeOfType[0]._id), ); } else { addBadge(personId, mongoose.Types.ObjectId(results._id)); @@ -1171,12 +1177,12 @@ const userHelper = function () { // Handle Increasing the 1 week streak badges const badgesOfType = []; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "X Hours for X Week Streak") { + if (badgeCollection[i].badge?.type === 'X Hours for X Week Streak') { badgesOfType.push(badgeCollection[i].badge); } } await badge - .find({ type: "X Hours for X Week Streak", weeks: 1 }) + .find({ type: 'X Hours for X Week Streak', weeks: 1 }) .sort({ totalHrs: -1 }) .then((results) => { results.every((elem) => { @@ -1201,13 +1207,13 @@ const userHelper = function () { // Check each Streak Greater than One to check if it works await badge .aggregate([ - { $match: { type: "X Hours for X Week Streak", weeks: { $gt: 1 } } }, + { $match: { type: 'X Hours for X Week Streak', weeks: { $gt: 1 } } }, { $sort: { weeks: -1, totalHrs: -1 } }, { $group: { - _id: "$weeks", + _id: '$weeks', badges: { - $push: { _id: "$_id", hrs: "$totalHrs", weeks: "$weeks" }, + $push: { _id: '$_id', hrs: '$totalHrs', weeks: '$weeks' }, }, }, }, @@ -1219,19 +1225,19 @@ const userHelper = function () { let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - badgeCollection[i].badge?.type === - "X Hours for X Week Streak" && - badgeCollection[i].badge?.weeks === bdge.weeks + badgeCollection[i].badge?.type + === 'X Hours for X Week Streak' + && badgeCollection[i].badge?.weeks === bdge.weeks ) { if ( - badgeOfType && - badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1256,7 +1262,7 @@ const userHelper = function () { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(bdge._id) + mongoose.Types.ObjectId(bdge._id), ); removePrevHrBadge( @@ -1264,7 +1270,7 @@ const userHelper = function () { user, badgeCollection, bdge.hrs, - bdge.weeks + bdge.weeks, ); } else if (!badgeOfType) { addBadge(personId, mongoose.Types.ObjectId(bdge._id)); @@ -1273,19 +1279,19 @@ const userHelper = function () { user, badgeCollection, bdge.hrs, - bdge.weeks + bdge.weeks, ); } else if (badgeOfType && badgeOfType.totalHrs === bdge.hrs) { increaseBadgeCount( personId, - mongoose.Types.ObjectId(badgeOfType._id) + mongoose.Types.ObjectId(badgeOfType._id), ); removePrevHrBadge( personId, user, badgeCollection, bdge.hrs, - bdge.weeks + bdge.weeks, ); } return false; @@ -1302,16 +1308,16 @@ const userHelper = function () { const checkLeadTeamOfXplus = async function ( personId, user, - badgeCollection + badgeCollection, ) { const leaderRoles = [ - "Mentor", - "Manager", - "Administrator", - "Owner", - "Core Team", + 'Mentor', + 'Manager', + 'Administrator', + 'Owner', + 'Core Team', ]; - const approvedRoles = ["Mentor", "Manager"]; + const approvedRoles = ['Mentor', 'Manager']; if (!approvedRoles.includes(user.role)) return; let teamMembers; @@ -1336,16 +1342,16 @@ const userHelper = function () { }); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { - if (badgeCollection[i].badge?.type === "Lead a team of X+") { + if (badgeCollection[i].badge?.type === 'Lead a team of X+') { if ( - badgeOfType && - badgeOfType.people <= badgeCollection[i].badge.people + badgeOfType + && badgeOfType.people <= badgeCollection[i].badge.people ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.people > badgeCollection[i].badge.people + badgeOfType + && badgeOfType.people > badgeCollection[i].badge.people ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1354,7 +1360,7 @@ const userHelper = function () { } } await badge - .find({ type: "Lead a team of X+" }) + .find({ type: 'Lead a team of X+' }) .sort({ people: -1 }) .then((results) => { if (!Array.isArray(results) || !results.length) { @@ -1364,14 +1370,14 @@ const userHelper = function () { if (teamMembers && teamMembers.length >= badge.people) { if (badgeOfType) { if ( - badgeOfType._id.toString() !== badge._id.toString() && - badgeOfType.people < badge.people + badgeOfType._id.toString() !== badge._id.toString() + && badgeOfType.people < badge.people ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(badge._id) + mongoose.Types.ObjectId(badge._id), ); } return false; @@ -1388,39 +1394,39 @@ const userHelper = function () { const checkTotalHrsInCat = async function (personId, user, badgeCollection) { const hoursByCategory = user.hoursByCategory || {}; const categories = [ - "food", - "energy", - "housing", - "education", - "society", - "economics", - "stewardship", + 'food', + 'energy', + 'housing', + 'education', + 'society', + 'economics', + 'stewardship', ]; const badgesOfType = badgeCollection - .filter((object) => object.badge.type === "Total Hrs in Category") - .map((object) => object.badge); + .filter(object => object.badge.type === 'Total Hrs in Category') + .map(object => object.badge); categories.forEach(async (category) => { const categoryHrs = Object.keys(hoursByCategory).find( - (elem) => elem === category + elem => elem === category, ); let badgeOfType; for (let i = 0; i < badgeCollection.length; i += 1) { if ( - badgeCollection[i].badge?.type === "Total Hrs in Category" && - badgeCollection[i].badge?.category === category + badgeCollection[i].badge?.type === 'Total Hrs in Category' + && badgeCollection[i].badge?.category === category ) { if ( - badgeOfType && - badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs <= badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeOfType._id); badgeOfType = badgeCollection[i].badge; } else if ( - badgeOfType && - badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs + badgeOfType + && badgeOfType.totalHrs > badgeCollection[i].badge.totalHrs ) { removeDupBadge(personId, badgeCollection[i].badge._id); } else if (!badgeOfType) { @@ -1432,7 +1438,7 @@ const userHelper = function () { const newCatg = category.charAt(0).toUpperCase() + category.slice(1); await badge - .find({ type: "Total Hrs in Category", category: newCatg }) + .find({ type: 'Total Hrs in Category', category: newCatg }) .sort({ totalHrs: -1 }) .then((results) => { @@ -1442,8 +1448,8 @@ const userHelper = function () { results.every((elem) => { if ( - hoursByCategory[categoryHrs] >= 100 && - hoursByCategory[categoryHrs] >= elem.totalHrs + hoursByCategory[categoryHrs] >= 100 + && hoursByCategory[categoryHrs] >= elem.totalHrs ) { let theBadge; for (let i = 0; i < badgesOfType.length; i += 1) { @@ -1458,13 +1464,13 @@ const userHelper = function () { } if (badgeOfType) { if ( - badgeOfType._id.toString() !== elem._id.toString() && - badgeOfType.totalHrs < elem.totalHrs + badgeOfType._id.toString() !== elem._id.toString() + && badgeOfType.totalHrs < elem.totalHrs ) { replaceBadge( personId, mongoose.Types.ObjectId(badgeOfType._id), - mongoose.Types.ObjectId(elem._id) + mongoose.Types.ObjectId(elem._id), ); } return false; @@ -1479,11 +1485,11 @@ const userHelper = function () { }; const awardNewBadges = async () => { - console.log("Awarding"); + console.log('Awarding'); try { const users = await userProfile .find({ isActive: true }) - .populate("badgeCollection.badge"); + .populate('badgeCollection.badge'); for (let i = 0; i < users.length; i += 1) { const user = users[i]; @@ -1506,13 +1512,13 @@ const userHelper = function () { const userId = mongoose.Types.ObjectId(personId); const pdtstart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); const pdtend = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); return timeEntries .find( @@ -1521,12 +1527,12 @@ const userHelper = function () { dateOfWork: { $gte: pdtstart, $lte: pdtend }, isTangible: true, }, - "totalSeconds" + 'totalSeconds', ) .then((results) => { const totalTangibleWeeklySeconds = results.reduce( (acc, { totalSeconds }) => acc + totalSeconds, - 0 + 0, ); return (totalTangibleWeeklySeconds / 3600).toFixed(2); }); @@ -1536,28 +1542,28 @@ const userHelper = function () { try { const users = await userProfile.find( { isActive: true, endDate: { $exists: true } }, - "_id isActive endDate" + '_id isActive endDate', ); for (let i = 0; i < users.length; i += 1) { const user = users[i]; const { endDate } = user; endDate.setHours(endDate.getHours() + 7); - if (moment().isAfter(moment(endDate).add(1, "days"))) { + if (moment().isAfter(moment(endDate).add(1, 'days'))) { await userProfile.findByIdAndUpdate( user._id, user.set({ isActive: false, }), - { new: true } + { new: true }, ); const id = user._id; const person = await userProfile.findById(id); - const lastDay = moment(person.endDate).format("YYYY-MM-DD"); + const lastDay = moment(person.endDate).format('YYYY-MM-DD'); logger.logInfo( `User with id: ${ user._id - } was de-acticated at ${moment().format()}.` + } was de-acticated at ${moment().format()}.`, ); const subject = `IMPORTANT:${person.firstName} ${person.lastName} has been deactivated in the Highest Good Network`; @@ -1573,12 +1579,12 @@ const userHelper = function () {

The HGN A.I. (and One Community)

`; emailSender( - "onecommunityglobal@gmail.com", + 'onecommunityglobal@gmail.com', subject, emailBody, null, null, - person.email + person.email, ); } } @@ -1598,6 +1604,7 @@ const userHelper = function () { }; return { + changeBadgeCount, getUserName, getTeamMembers, validateProfilePic, @@ -1615,4 +1622,4 @@ const userHelper = function () { }; }; -module.exports = userHelper; \ No newline at end of file +module.exports = userHelper; From a538fd14b032d998f0f33c1d0f5aaf116ddb38fe Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Sat, 30 Dec 2023 12:41:56 -0500 Subject: [PATCH 113/129] fix bug in changeBadgeCount --- src/helpers/userHelper.js | 78 +++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 97beafc17..4026b1462 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -859,41 +859,45 @@ const changeBadgeCount = async function (personId, badgeId, count) { removeDupBadge(personId, badgeId); } else if (count) { // Process exisiting earned date to match the new count - const userInfo = await userProfile.findById(personId); - let newEarnedDate = []; - const recordToUpdate = userInfo.badgeCollection.find(item => item.badge._id.toString() === badgeId.toString()); - if (!recordToUpdate) { - throw new Error('Badge not found'); - } - const copyOfEarnedDate = recordToUpdate.earnedDate; - if (copyOfEarnedDate.length < count) { - // if the EarnedDate count is less than the new count, add a earned date to the end of the collection - while (copyOfEarnedDate.length < count) { - copyOfEarnedDate.push(earnedDateBadge()); + try { + const userInfo = await userProfile.findById(personId); + let newEarnedDate = []; + const recordToUpdate = userInfo.badgeCollection.find(item => item.badge._id.toString() === badgeId.toString()); + if (!recordToUpdate) { + throw new Error('Badge not found'); } - } else { - // if the EarnedDate count is greater than the new count, remove the oldest earned date of the collection until it matches the new count - 1 - while (copyOfEarnedDate.length >= count) { - copyOfEarnedDate.shift(); + const copyOfEarnedDate = recordToUpdate.earnedDate; + if (copyOfEarnedDate.length < count) { + // if the EarnedDate count is less than the new count, add a earned date to the end of the collection + while (copyOfEarnedDate.length < count) { + copyOfEarnedDate.push(earnedDateBadge()); + } + } else { + // if the EarnedDate count is greater than the new count, remove the oldest earned date of the collection until it matches the new count - 1 + while (copyOfEarnedDate.length >= count) { + copyOfEarnedDate.shift(); + } + copyOfEarnedDate.push(earnedDateBadge()); } - copyOfEarnedDate.push(earnedDateBadge()); - } - newEarnedDate = [...copyOfEarnedDate]; - userProfile.updateOne( - { _id: personId, 'badgeCollection.badge': badgeId }, - { - $set: { - 'badgeCollection.$.count': count, - 'badgeCollection.$.lastModified': Date.now().toString(), - 'badgeCollection.$.earnedDate': newEarnedDate, + newEarnedDate = [...copyOfEarnedDate]; + userProfile.updateOne( + { _id: personId, 'badgeCollection.badge': badgeId }, + { + $set: { + 'badgeCollection.$.count': count, + 'badgeCollection.$.lastModified': Date.now().toString(), + 'badgeCollection.$.earnedDate': newEarnedDate, + }, }, - }, - (err) => { - if (err) { - throw new Error(err); - } - }, - ); + (err) => { + if (err) { + throw new Error(err); + } + }, + ); + } catch (err) { + logger.logException(err); + } } }; @@ -1487,6 +1491,7 @@ const changeBadgeCount = async function (personId, badgeId, count) { const awardNewBadges = async () => { console.log('Awarding'); try { + // This will be used in production to run task on all users const users = await userProfile .find({ isActive: true }) .populate('badgeCollection.badge'); @@ -1503,6 +1508,15 @@ const changeBadgeCount = async function (personId, badgeId, count) { await checkXHrsForXWeeks(personId, user, badgeCollection); await checkNoInfringementStreak(personId, user, badgeCollection); } + + // Testing purpose only + // You can lookup your user id in view profile and get the id from the url + // const user = await userProfile + // .findOne({ _id: '65500b658e0b2922b80d5f9f' }) + // .populate('badgeCollection.badge'); + // for (let badgeItem of user.badgeCollection){ + // await changeBadgeCount('65500b658e0b2922b80d5f9f', badgeItem.badge._id, badgeItem.count + 1); + // } } catch (err) { logger.logException(err); } From af91cadfe4f95f412980e5a6d8302440cc727956 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Sat, 30 Dec 2023 12:42:58 -0500 Subject: [PATCH 114/129] Remove testing code --- src/helpers/userHelper.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 4026b1462..dba1369f3 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -1508,15 +1508,6 @@ const changeBadgeCount = async function (personId, badgeId, count) { await checkXHrsForXWeeks(personId, user, badgeCollection); await checkNoInfringementStreak(personId, user, badgeCollection); } - - // Testing purpose only - // You can lookup your user id in view profile and get the id from the url - // const user = await userProfile - // .findOne({ _id: '65500b658e0b2922b80d5f9f' }) - // .populate('badgeCollection.badge'); - // for (let badgeItem of user.badgeCollection){ - // await changeBadgeCount('65500b658e0b2922b80d5f9f', badgeItem.badge._id, badgeItem.count + 1); - // } } catch (err) { logger.logException(err); } From 04cbd643091cae073f58477efde6a9a6d218e206 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Sat, 30 Dec 2023 21:24:15 -0800 Subject: [PATCH 115/129] fix editTimeEntry --- src/controllers/timeEntryController.js | 43 ++++++++++++++------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index c8ef3d464..1cbab61c4 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -195,15 +195,15 @@ const timeEntrycontroller = function (TimeEntry) { const isGeneralEntry = isGeneralTimeEntry(type); try { - if (!req.params.timeEntryId) { + if (!timeEntryId) { const error = 'ObjectId in request param is not in correct format'; return res.status(400).send({ error }); } if ( - !mongoose.Types.ObjectId.isValid(req.params.timeEntryId) + !mongoose.Types.ObjectId.isValid(timeEntryId) || ((isGeneralEntry || type === 'project') - && !mongoose.Types.ObjectId.isValid(req.body.projectId) + && !mongoose.Types.ObjectId.isValid(newProjectId) )) { const error = 'ObjectIds are not correctly formed'; return res.status(400).send({ error }); @@ -228,30 +228,35 @@ const timeEntrycontroller = function (TimeEntry) { } // update task data if project/task is changed - if (newTaskId === timeEntry.taskId) { + if (newTaskId === timeEntry.taskId && newProjectId === timeEntry.projectId) { + // when project/task is the same const timeEntryTask = await Task.findById(newTaskId); - const timeEntryUser = await UserProfile.findById(personId); - if (timeEntry.isTangible) { - timeEntryTask.hoursLogged -= timeEntry.totalSeconds / 3600; - } - if (newIsTangible) { - timeEntryTask.hoursLogged += newTotalSeconds / 3600; + if (timeEntryTask) { + const timeEntryUser = await UserProfile.findById(personId); + if (timeEntry.isTangible) { + timeEntryTask.hoursLogged -= timeEntry.totalSeconds / 3600; + } + if (newIsTangible) { + timeEntryTask.hoursLogged += newTotalSeconds / 3600; + } + checkTaskOvertime(timeEntry, timeEntryUser, timeEntryTask); + await timeEntryTask.save(); } - checkTaskOvertime(timeEntry, timeEntryUser, timeEntryTask); - await timeEntryTask.save(); } else { + // update oldtTimeEntryTask const oldTimeEntryTask = await Task.findById(timeEntry.taskId); - const newTimeEntryTask = await Task.findById(newTaskId); - const timeEntryUser = await UserProfile.findById(personId); - if (timeEntry.isTangible) { + if (oldTimeEntryTask && timeEntry.isTangible) { oldTimeEntryTask.hoursLogged -= timeEntry.totalSeconds / 3600; + oldTimeEntryTask.save(); } - if (newIsTangible) { + // update newtTimeEntryTask + const newTimeEntryTask = await Task.findById(newTaskId); + if (newTimeEntryTask && newIsTangible) { + const timeEntryUser = await UserProfile.findById(personId); newTimeEntryTask.hoursLogged += newTotalSeconds / 3600; + checkTaskOvertime(timeEntry, timeEntryUser, newTimeEntryTask); + await newTimeEntryTask.save(); } - checkTaskOvertime(timeEntry, timeEntryUser, newTimeEntryTask); - await oldTimeEntryTask.save(); - await newTimeEntryTask.save(); } // Update edit history From 57c0267b2d1a04da276a20d57f3806d70f56336e Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Tue, 2 Jan 2024 15:42:54 -0500 Subject: [PATCH 116/129] Update the scheduler to run every Sunday at midnight --- src/cronjobs/userProfileJobs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cronjobs/userProfileJobs.js b/src/cronjobs/userProfileJobs.js index f3903c057..8ca6ff95c 100644 --- a/src/cronjobs/userProfileJobs.js +++ b/src/cronjobs/userProfileJobs.js @@ -5,7 +5,8 @@ const userhelper = require('../helpers/userHelper')(); const userProfileJobs = () => { const allUserProfileJobs = new CronJob( - '1 0 * * *', // Every day, 1 minute past midnight (PST). + // '* * * * *', // Comment out for testing. Run Every minute. + '0 0 * * 0', // Every Sunday, at midnight. async () => { const SUNDAY = 0; if (moment().tz('America/Los_Angeles').day() === SUNDAY) { From 0d805bc24041186ac9bb95d5b5e22ddd10ba7e9a Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Tue, 2 Jan 2024 15:52:18 -0500 Subject: [PATCH 117/129] Update cron config to run at 1 minute past midnight every Sunday in PST --- src/cronjobs/userProfileJobs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cronjobs/userProfileJobs.js b/src/cronjobs/userProfileJobs.js index 8ca6ff95c..8bc94c0f1 100644 --- a/src/cronjobs/userProfileJobs.js +++ b/src/cronjobs/userProfileJobs.js @@ -6,7 +6,7 @@ const userhelper = require('../helpers/userHelper')(); const userProfileJobs = () => { const allUserProfileJobs = new CronJob( // '* * * * *', // Comment out for testing. Run Every minute. - '0 0 * * 0', // Every Sunday, at midnight. + '1 0 * * 0', // Every Sunday, 1 minute past midnight. async () => { const SUNDAY = 0; if (moment().tz('America/Los_Angeles').day() === SUNDAY) { From bda1712e367d2b7d1b5e64846a4ed13bac3c6682 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Tue, 2 Jan 2024 16:59:35 -0800 Subject: [PATCH 118/129] change request body format for adding and deleting team member, refactor request handler --- src/controllers/teamController.js | 82 +++++++++++-------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/src/controllers/teamController.js b/src/controllers/teamController.js index 5c8cb5cc2..0570afc00 100644 --- a/src/controllers/teamController.js +++ b/src/controllers/teamController.js @@ -115,66 +115,40 @@ const teamcontroller = function (Team) { return; } - if ( - !req.params.teamId - || !mongoose.Types.ObjectId.isValid(req.params.teamId) - || !req.body.users - || req.body.users.length === 0 - ) { - res.status(400).send({ error: 'Invalid request' }); + const { teamId } = req.params; + + if (!teamId || !mongoose.Types.ObjectId.isValid(teamId)) { + res.status(400).send({ error: 'Invalid teamId' }); return; } // verify team exists + const targetTeam = await Team.findById(teamId); - Team.findById(req.params.teamId) - .then((team) => { - if (!team || team.length === 0) { - res.status(400).send({ error: 'Invalid team' }); - return; - } - const { users } = req.body; - const assignlist = []; - const unassignlist = []; - - users.forEach((element) => { - const { userId, operation } = element; - // if user's profile is stored in cache, clear it so when you visit their profile page it will be up to date - if (cache.hasCache(`user-${userId}`)) cache.removeCache(`user-${userId}`); - - if (operation === 'Assign') { - assignlist.push(userId); - } else { - unassignlist.push(userId); - } - }); + if (!targetTeam || targetTeam.length === 0) { + res.status(400).send({ error: 'Invalid team' }); + return; + } - const addTeamToUserProfile = userProfile - .updateMany({ _id: { $in: assignlist } }, { $addToSet: { teams: team._id } }) - .exec(); - const removeTeamFromUserProfile = userProfile - .updateMany({ _id: { $in: unassignlist } }, { $pull: { teams: team._id } }) - .exec(); - const addUserToTeam = Team.updateOne( - { _id: team._id }, - { $addToSet: { members: { $each: assignlist.map(userId => ({ userId })) } } }, - ).exec(); - const removeUserFromTeam = Team.updateOne( - { _id: team._id }, - { $pull: { members: { userId: { $in: unassignlist } } } }, - ).exec(); - - Promise.all([addTeamToUserProfile, removeTeamFromUserProfile, addUserToTeam, removeUserFromTeam]) - .then(() => { - res.status(200).send({ result: 'Done' }); - }) - .catch((error) => { - res.status(500).send({ error }); - }); - }) - .catch((error) => { - res.status(500).send({ error }); - }); + try { + const { userId, operation } = req.body; + + // if user's profile is stored in cache, clear it so when you visit their profile page it will be up to date + if (cache.hasCache(`user-${userId}`)) cache.removeCache(`user-${userId}`); + + + if (operation === 'Assign') { + await Team.findOneAndUpdate({ _id: teamId }, { $addToSet: { members: { userId } } }, { new: true }); + const newMember = await userProfile.findOneAndUpdate({ _id: userId }, { $addToSet: { teams: teamId } }, { new: true }); + res.status(200).send({ newMember }); + } else { + await Team.findOneAndUpdate({ _id: teamId }, { $pull: { members: { userId } } }); + await userProfile.findOneAndUpdate({ _id: userId }, { $pull: { teams: teamId } }, { new: true }); + res.status(200).send({ result: 'Delete Success' }); + } + } catch (error) { + res.status(500).send({ error }); + } }; const getTeamMembership = function (req, res) { From 3dd167170b4d5945821ed64fb9e1e256cca27628 Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Wed, 3 Jan 2024 12:41:46 -0500 Subject: [PATCH 119/129] Update cache removal after badge awarding --- src/helpers/userHelper.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index dba1369f3..5e6b0870b 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -13,7 +13,7 @@ const emailSender = require('../utilities/emailSender'); const logger = require('../startup/logger'); const Reason = require('../models/reason'); const token = require('../models/profileInitialSetupToken'); - +const cache = require('../utilities/nodeCache')(); const userHelper = function () { // Update format to "MMM-DD-YY" from "YYYY-MMM-DD" (Confirmed with Jae) @@ -860,6 +860,7 @@ const changeBadgeCount = async function (personId, badgeId, count) { } else if (count) { // Process exisiting earned date to match the new count try { + console.log('changeBadgeCount'); const userInfo = await userProfile.findById(personId); let newEarnedDate = []; const recordToUpdate = userInfo.badgeCollection.find(item => item.badge._id.toString() === badgeId.toString()); @@ -1507,6 +1508,10 @@ const changeBadgeCount = async function (personId, badgeId, count) { await checkLeadTeamOfXplus(personId, user, badgeCollection); await checkXHrsForXWeeks(personId, user, badgeCollection); await checkNoInfringementStreak(personId, user, badgeCollection); + // remove cache after badge asssignment. + if (cache.hasCache(`user-${_id}`)) { + cache.removeCache(`user-${_id}`); + } } } catch (err) { logger.logException(err); From fdbbd1d76c981b6c8c04f6c32d81134b47bcfd9a Mon Sep 17 00:00:00 2001 From: Shengwei Peng Date: Wed, 3 Jan 2024 15:38:48 -0500 Subject: [PATCH 120/129] Remove log statement from changeBadgeCount --- src/helpers/userHelper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 5e6b0870b..b4b2acb46 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -860,7 +860,6 @@ const changeBadgeCount = async function (personId, badgeId, count) { } else if (count) { // Process exisiting earned date to match the new count try { - console.log('changeBadgeCount'); const userInfo = await userProfile.findById(personId); let newEarnedDate = []; const recordToUpdate = userInfo.badgeCollection.find(item => item.badge._id.toString() === badgeId.toString()); From 8c077b0f71365cab26348f075578d021dfda9d3a Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 14:10:42 -0800 Subject: [PATCH 121/129] Leaderboard api and task api fix --- src/controllers/taskController.js | 2 +- src/helpers/dashboardhelper.js | 11 +- src/helpers/taskHelper.js | 705 ++++++++++++++++++------------ 3 files changed, 430 insertions(+), 288 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index 9bcf071de..8d1559ebc 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -837,7 +837,7 @@ const taskController = function (Task) { const getTasksForTeamsByUser = async (req, res) => { try { const userId = mongoose.Types.ObjectId(req.params.userId); - const teamsData = await taskHelper.getTasksForTeams(userId).exec(); + const teamsData = await taskHelper.getTasksForTeams(userId); if (teamsData.length > 0) { res.status(200).send(teamsData); } else { diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 59b1d7f51..76c0e8554 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -171,19 +171,20 @@ const dashboardhelper = function () { let teamMemberIds = [userid] let teamMembers = []; + if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members { const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) .then((res)=>{ return res; }).catch((e)=>{}) - + teamsResult.map((_myTeam)=>{ _myTeam.members.map((teamMember)=> { if(!teamMember.userId.equals(userid)) teamMemberIds.push( teamMember.userId ); } ) }) - + teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) .then((res)=>{ return res; }).catch((e)=>{}) @@ -202,9 +203,9 @@ const dashboardhelper = function () { .then((res)=>{ return res; }).catch((e)=>{}) } - + } - + teamMemberIds = teamMembers.map(member => member._id); const timeEntries = await timeentry.find({ @@ -231,6 +232,7 @@ const dashboardhelper = function () { timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; }) + let leaderBoardData = []; teamMembers.map((teamMember)=>{ @@ -267,6 +269,7 @@ const dashboardhelper = function () { // Finally, sort by role in ascending order return a.role.localeCompare(b.role); }); + return sortedLBData; // return myTeam.aggregate([ diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index f59dedcbc..2a49439f8 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,9 +1,23 @@ const moment = require('moment-timezone'); const userProfile = require('../models/userProfile'); -const myteam = require('../helpers/helperModels/myTeam'); +const timeentry = require('../models/timeentry'); +const myTeam = require('../helpers/helperModels/myTeam'); +const team = require('../models/team'); +const Task = require('../models/task'); +const TaskNotification = require('../models/taskNotification'); +const Wbs = require('../models/wbs'); +const mongoose = require('mongoose'); const taskHelper = function () { - const getTasksForTeams = function (userId) { + const getTasksForTeams = async function (userId) { + + const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1,firstName:1, lastName:1, role:1, isVisible:1, weeklycommittedHours:1, weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}); + + if(userById==null) return null; + const userRole = userById.role; + const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -12,288 +26,413 @@ const taskHelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - return myteam.aggregate([ - { - $match: { - _id: userId, - }, - }, - { - $unwind: '$myteam', - }, - { - $project: { - _id: 0, - personId: '$myteam._id', - name: '$myteam.fullName', - role: 1, - }, - }, - // have personId, name, role - { - $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', - }, - }, - { - $match: { - // dashboard tasks user roles hierarchy - $or: [ - { - role: { $in: ['Owner', 'Core Team'] }, - }, - { - $and: [ - { - role: 'Administrator', - }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ], - }, - { - $and: [ - { - role: { $in: ['Manager', 'Mentor'] }, - }, - { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - }, - }, - ], - }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - }, - { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - }, - ], - }, - role: 1, - }, - }, - { - $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - timeEntryData: { - $filter: { - input: '$timeEntryData', - as: 'timeentry', - cond: { - $and: [ - { - $gte: ['$$timeentry.dateOfWork', pdtstart], - }, - { - $lte: ['$$timeentry.dateOfWork', pdtend], - }, - ], - }, - }, - }, - role: 1, - }, - }, - { - $unwind: { - path: '$timeEntryData', - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - totalSeconds: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.totalSeconds', - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.isTangible', - false, - ], - }, - role: 1, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ['$isTangible', true], - }, - '$totalSeconds', - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', - }, - totalSeconds: { - $sum: '$totalSeconds', - }, - tangibletime: { - $sum: '$tangibletime', - }, - }, - }, - { - $project: { - _id: 0, - personId: '$_id.personId', - name: '$_id.name', - weeklycommittedHours: '$_id.weeklycommittedHours', - totaltime_hrs: { - $divide: ['$totalSeconds', 3600], - }, - totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], - }, - role: '$_id.role', - }, - }, - { - $lookup: { - from: 'tasks', - localField: 'personId', - foreignField: 'resources.userID', - as: 'tasks', - }, - }, - { - $project: { - tasks: { - resources: { - profilePic: 0, - }, - }, - }, - }, - { - $unwind: { - path: '$tasks', - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: 'wbs', - localField: 'tasks.wbsId', - foreignField: '_id', - as: 'projectId', - }, - }, - { - $addFields: { - 'tasks.projectId': { - $cond: [ - { $ne: ['$projectId', []] }, - { $arrayElemAt: ['$projectId', 0] }, - '$tasks.projectId', - ], - }, - }, - }, - { - $project: { - projectId: 0, - tasks: { - projectId: { - _id: 0, - isActive: 0, - modifiedDatetime: 0, - wbsName: 0, - createdDatetime: 0, - __v: 0, - }, - }, - }, - }, - { - $addFields: { - 'tasks.projectId': '$tasks.projectId.projectId', - }, - }, - { - $lookup: { - from: 'taskNotifications', - localField: 'tasks._id', - foreignField: 'taskId', - as: 'tasks.taskNotifications', - }, - }, - { - $group: { - _id: '$personId', - tasks: { - $push: '$tasks', - }, - data: { - $first: '$$ROOT', - }, - }, - }, - { - $addFields: { - 'data.tasks': { - $filter: { - input: '$tasks', - as: 'task', - cond: { $ne: ['$$task', {}] }, - }, - }, - }, - }, + + let teamMemberIds = [userid] + let teamMembers = []; + + if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members { - $replaceRoot: { - newRoot: '$data', - }, - }, - ]); + + const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) + .then((res)=>{ return res; }).catch((e)=>{}) + + teamsResult.map((_myTeam)=>{ + _myTeam.members.map((teamMember)=> { + if(!teamMember.userId.equals(userid)) + teamMemberIds.push( teamMember.userId ); + } ) + }) + + teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else { + if(userRole == 'Administrator'){ //All users except Owner and Core Team + const excludedRoles = ['Core Team', 'Owner']; + teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else{ //'Core Team', 'Owner' //All users + teamMembers = await userProfile.find({ isActive:true}, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + } + + teamMemberIds = teamMembers.map(member => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, + }, + personId: { $in: teamMemberIds } + }); + + let timeEntryByPerson = {} + timeEntries.map((timeEntry)=>{ + + let personIdStr = timeEntry.personId.toString(); + + if(timeEntryByPerson[personIdStr]==null) + timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }) + + const teamMemberTasks = await Task.find({"resources.userID" : {$in : teamMemberIds }}, { 'resources.profilePic': 0 }) + .populate( { + path: 'wbsId', + select: 'projectId', + }) + const teamMemberTaskIds = teamMemberTasks.map(task => task._id); + const teamMemberTaskNotifications = await TaskNotification.find({"taskId" : {$in : teamMemberTaskIds }}) + + const taskNotificationByTaskNdUser = [] + teamMemberTaskNotifications.map(teamMemberTaskNotification => { + + let taskIdStr = teamMemberTaskNotification.taskId.toString(); + let userIdStr = teamMemberTaskNotification.userId.toString(); + let taskNdUserID = taskIdStr+","+userIdStr; + + if(taskNotificationByTaskNdUser[taskNdUserID]) { + taskNotificationByTaskNdUser[taskNdUserID].push(teamMemberTaskNotification) + } + else{ + taskNotificationByTaskNdUser[taskNdUserID] = [teamMemberTaskNotification] + } + + }) + + const taskByPerson = [] + + teamMemberTasks.map(teamMemberTask => { + + let projId = teamMemberTask.wbsId?.projectId; + let _teamMemberTask = {...teamMemberTask._doc} + _teamMemberTask.projectId = projId; + let taskIdStr = _teamMemberTask._id.toString(); + + teamMemberTask.resources.map(resource => { + + let resourceIdStr = resource.userID.toString(); + let taskNdUserID = taskIdStr+","+resourceIdStr; + _teamMemberTask.taskNotifications = taskNotificationByTaskNdUser[taskNdUserID] || [] + if(taskByPerson[resourceIdStr]) { + taskByPerson[resourceIdStr].push(_teamMemberTask) + } + else{ + taskByPerson[resourceIdStr] = [_teamMemberTask] + } + }) + }) + + + let teamMemberTasksData = []; + teamMembers.map((teamMember)=>{ + let obj = { + personId : teamMember._id, + role : teamMember.role, + name : teamMember.firstName + ' ' + teamMember.lastName, + weeklycommittedHours : teamMember.weeklycommittedHours, + totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), + totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), + tasks : taskByPerson[teamMember._id.toString()] || [] + } + teamMemberTasksData.push(obj); + }) + + + return teamMemberTasksData; + + + // return myteam.aggregate([ + // { + // $match: { + // _id: userId, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // role: 1, + // }, + // }, + // // have personId, name, role + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // dashboard tasks user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // role: 1, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // role: '$_id.role', + // }, + // }, + // { + // $lookup: { + // from: 'tasks', + // localField: 'personId', + // foreignField: 'resources.userID', + // as: 'tasks', + // }, + // }, + // { + // $project: { + // tasks: { + // resources: { + // profilePic: 0, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$tasks', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $lookup: { + // from: 'wbs', + // localField: 'tasks.wbsId', + // foreignField: '_id', + // as: 'projectId', + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': { + // $cond: [ + // { $ne: ['$projectId', []] }, + // { $arrayElemAt: ['$projectId', 0] }, + // '$tasks.projectId', + // ], + // }, + // }, + // }, + // { + // $project: { + // projectId: 0, + // tasks: { + // projectId: { + // _id: 0, + // isActive: 0, + // modifiedDatetime: 0, + // wbsName: 0, + // createdDatetime: 0, + // __v: 0, + // }, + // }, + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': '$tasks.projectId.projectId', + // }, + // }, + // { + // $lookup: { + // from: 'taskNotifications', + // localField: 'tasks._id', + // foreignField: 'taskId', + // as: 'tasks.taskNotifications', + // }, + // }, + // { + // $group: { + // _id: '$personId', + // tasks: { + // $push: '$tasks', + // }, + // data: { + // $first: '$$ROOT', + // }, + // }, + // }, + // { + // $addFields: { + // 'data.tasks': { + // $filter: { + // input: '$tasks', + // as: 'task', + // cond: { $ne: ['$$task', {}] }, + // }, + // }, + // }, + // }, + // { + // $replaceRoot: { + // newRoot: '$data', + // }, + // }, + // ]); }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() From 9e6589608a82300e4d9d4f024df09198b9f3957d Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 16:10:55 -0800 Subject: [PATCH 122/129] merged with dev bracnh --- src/helpers/taskHelper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 19551b0ed..e1e2acb13 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -15,6 +15,7 @@ const taskHelper = function () { const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1,firstName:1, lastName:1, role:1, isVisible:1, weeklycommittedHours:1, weeklySummaries:1}) .then((res)=>{ return res; }).catch((e)=>{}); + if(userById==null) return null; const userRole = userById.role; From d3c2daebe1a1fa8eaafd0556a2bf0b21754fee27 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 16:21:53 -0800 Subject: [PATCH 123/129] api fix --- src/helpers/dashboardhelper.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 0b85cb004..ca534b894 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -176,7 +176,6 @@ const dashboardhelper = function () { .endOf('week') .format('YYYY-MM-DD'); - let teamMemberIds = [userid] let teamMembers = []; @@ -211,7 +210,6 @@ const dashboardhelper = function () { .then((res)=>{ return res; }).catch((e)=>{}) } - } teamMemberIds = teamMembers.map(member => member._id); From 1e8f5e46f27d7e9418b0a0a5445599cf186850a8 Mon Sep 17 00:00:00 2001 From: Vishala09 <65274029+Vishala09@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:49:49 -0800 Subject: [PATCH 124/129] Revert "Vishala LB and Task api fix" --- src/helpers/dashboardhelper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index ca534b894..0b85cb004 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -176,6 +176,7 @@ const dashboardhelper = function () { .endOf('week') .format('YYYY-MM-DD'); + let teamMemberIds = [userid] let teamMembers = []; @@ -210,6 +211,7 @@ const dashboardhelper = function () { .then((res)=>{ return res; }).catch((e)=>{}) } + } teamMemberIds = teamMembers.map(member => member._id); From 9c913c709d714af5f3901262112245f051770916 Mon Sep 17 00:00:00 2001 From: Vishala09 <65274029+Vishala09@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:53:22 -0800 Subject: [PATCH 125/129] Revert "Vishala Leaderboard and Team Member Tasks Api Fix" --- src/controllers/taskController.js | 2 +- src/helpers/dashboardhelper.js | 614 +++++++++++--------------- src/helpers/taskHelper.js | 708 ++++++++++++------------------ 3 files changed, 542 insertions(+), 782 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index 753dbba11..5b087db4d 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -834,7 +834,7 @@ const taskController = function (Task) { const getTasksForTeamsByUser = async (req, res) => { try { const userId = mongoose.Types.ObjectId(req.params.userId); - const teamsData = await taskHelper.getTasksForTeams(userId); + const teamsData = await taskHelper.getTasksForTeams(userId).exec(); if (teamsData.length > 0) { res.status(200).send(teamsData); } else { diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 0b85cb004..b7ca2f131 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -3,7 +3,6 @@ const mongoose = require('mongoose'); const userProfile = require('../models/userProfile'); const timeentry = require('../models/timeentry'); const myTeam = require('../helpers/helperModels/myTeam'); -const team = require('../models/team'); const dashboardhelper = function () { const personaldetails = function (userId) { @@ -162,11 +161,6 @@ const dashboardhelper = function () { const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1}) - .then((res)=>{ return res; }).catch((e)=>{}); - - if(userById==null) return null; - const userRole = userById.role; const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -175,360 +169,262 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - - - let teamMemberIds = [userid] - let teamMembers = []; - - if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members - { - - const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) - .then((res)=>{ return res; }).catch((e)=>{}) - - teamsResult.map((_myTeam)=>{ - _myTeam.members.map((teamMember)=> { - if(!teamMember.userId.equals(userid)) - teamMemberIds.push( teamMember.userId ); - } ) - }) - - teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, - {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - - } - else { - if(userRole == 'Administrator'){ //All users except Owner and Core Team - const excludedRoles = ['Core Team', 'Owner']; - teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, - {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - } - else{ //'Core Team', 'Owner' //All users - teamMembers = await userProfile.find({ isActive:true}, - {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - } - - - } - - teamMemberIds = teamMembers.map(member => member._id); - - const timeEntries = await timeentry.find({ - dateOfWork: { - $gte: pdtstart, - $lte: pdtend, + const output = await myTeam.aggregate([ + { + $match: { + _id: userid, + }, }, - personId: { $in: teamMemberIds } - }); - - let timeEntryByPerson = {} - timeEntries.map((timeEntry)=>{ - - let personIdStr = timeEntry.personId.toString(); - - if(timeEntryByPerson[personIdStr]==null) - timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; - - if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; - } else { - timeEntryByPerson[personIdStr].intangibleSeconds += timeEntry.totalSeconds; - } - - timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; - }) - - - let leaderBoardData = []; - teamMembers.map((teamMember)=>{ - let obj = { - personId : teamMember._id, - role : teamMember.role, - name : teamMember.firstName + ' ' + teamMember.lastName, - isVisible : teamMember.isVisible, - hasSummary : teamMember.weeklySummaries?.length > 0 ? teamMember.weeklySummaries[0].summary!='' : false, - weeklycommittedHours : teamMember.weeklycommittedHours, - totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), - totalintangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / 3600) || 0), - totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), - percentagespentintangible : - (timeEntryByPerson[teamMember._id.toString()] && timeEntryByPerson[teamMember._id.toString()]?.totalSeconds !=0 && timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds !=0) ? - (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * 100 - : - 0 - } - leaderBoardData.push(obj); - }) - - let sortedLBData = leaderBoardData.sort((a, b) => { - // Sort by totaltangibletime_hrs in descending order - if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { - return b.totaltangibletime_hrs - a.totaltangibletime_hrs; - } - - // Then sort by name in ascending order - if (a.name !== b.name) { - return a.name.localeCompare(b.name); - } - - // Finally, sort by role in ascending order - return a.role.localeCompare(b.role); - }); - - return sortedLBData; - - // return myTeam.aggregate([ - // { - // $match: { - // _id: userid, - // }, - // }, - // { - // $unwind: '$myteam', - // }, - // { - // $project: { - // _id: 0, - // role: 1, - // personId: '$myteam._id', - // name: '$myteam.fullName', - // }, - // }, - // { - // $lookup: { - // from: 'userProfiles', - // localField: 'personId', - // foreignField: '_id', - // as: 'persondata', - // }, - // }, - // { - // $match: { - // // leaderboard user roles hierarchy - // $or: [ - // { - // role: { $in: ['Owner', 'Core Team'] }, - // }, - // { - // $and: [ - // { - // role: 'Administrator', - // }, - // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - // ], - // }, - // { - // $and: [ - // { - // role: { $in: ['Manager', 'Mentor'] }, - // }, - // { - // 'persondata.0.role': { - // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - // }, - // }, - // ], - // }, - // { 'persondata.0._id': userId }, - // { 'persondata.0.role': 'Volunteer' }, - // { 'persondata.0.isVisible': true }, - // ], - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // role: { - // $arrayElemAt: ['$persondata.role', 0], - // }, - // isVisible: { - // $arrayElemAt: ['$persondata.isVisible', 0], - // }, - // hasSummary: { - // $ne: [ - // { - // $arrayElemAt: [ - // { - // $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], - // }, - // 0, - // ], - // }, - // '', - // ], - // }, - // weeklycommittedHours: { - // $sum: [ - // { - // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - // }, - // { - // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - // }, - // ], - // }, - // }, - // }, - // { - // $lookup: { - // from: 'timeEntries', - // localField: 'personId', - // foreignField: 'personId', - // as: 'timeEntryData', - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // role: 1, - // isVisible: 1, - // hasSummary: 1, - // weeklycommittedHours: 1, - // timeEntryData: { - // $filter: { - // input: '$timeEntryData', - // as: 'timeentry', - // cond: { - // $and: [ - // { - // $gte: ['$$timeentry.dateOfWork', pdtstart], - // }, - // { - // $lte: ['$$timeentry.dateOfWork', pdtend], - // }, - // ], - // }, - // }, - // }, - // }, - // }, - // { - // $unwind: { - // path: '$timeEntryData', - // preserveNullAndEmptyArrays: true, - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // role: 1, - // isVisible: 1, - // hasSummary: 1, - // weeklycommittedHours: 1, - // totalSeconds: { - // $cond: [ - // { - // $gte: ['$timeEntryData.totalSeconds', 0], - // }, - // '$timeEntryData.totalSeconds', - // 0, - // ], - // }, - // isTangible: { - // $cond: [ - // { - // $gte: ['$timeEntryData.totalSeconds', 0], - // }, - // '$timeEntryData.isTangible', - // false, - // ], - // }, - // }, - // }, - // { - // $addFields: { - // tangibletime: { - // $cond: [ - // { - // $eq: ['$isTangible', true], - // }, - // '$totalSeconds', - // 0, - // ], - // }, - // intangibletime: { - // $cond: [ - // { - // $eq: ['$isTangible', false], - // }, - // '$totalSeconds', - // 0, - // ], - // }, - // }, - // }, - // { - // $group: { - // _id: { - // personId: '$personId', - // weeklycommittedHours: '$weeklycommittedHours', - // name: '$name', - // role: '$role', - // isVisible: '$isVisible', - // hasSummary: '$hasSummary', - // }, - // totalSeconds: { - // $sum: '$totalSeconds', - // }, - // tangibletime: { - // $sum: '$tangibletime', - // }, - // intangibletime: { - // $sum: '$intangibletime', - // }, - // }, - // }, - // { - // $project: { - // _id: 0, - // personId: '$_id.personId', - // name: '$_id.name', - // role: '$_id.role', - // isVisible: '$_id.isVisible', - // hasSummary: '$_id.hasSummary', - // weeklycommittedHours: '$_id.weeklycommittedHours', - // totaltime_hrs: { - // $divide: ['$totalSeconds', 3600], - // }, - // totaltangibletime_hrs: { - // $divide: ['$tangibletime', 3600], - // }, - // totalintangibletime_hrs: { - // $divide: ['$intangibletime', 3600], - // }, - // percentagespentintangible: { - // $cond: [ - // { - // $eq: ['$totalSeconds', 0], - // }, - // 0, - // { - // $multiply: [ - // { - // $divide: ['$tangibletime', '$totalSeconds'], - // }, - // 100, - // ], - // }, - // ], - // }, - // }, - // }, - // { - // $sort: { - // totaltangibletime_hrs: -1, - // name: 1, - // role: 1, - // }, - // }, - // ]); - + { + $unwind: '$myteam', + }, + { + $project: { + _id: 0, + role: 1, + personId: '$myteam._id', + name: '$myteam.fullName', + }, + }, + { + $lookup: { + from: 'userProfiles', + localField: 'personId', + foreignField: '_id', + as: 'persondata', + }, + }, + { + $match: { + // leaderboard user roles hierarchy + $or: [ + { + role: { $in: ['Owner', 'Core Team'] }, + }, + { + $and: [ + { + role: 'Administrator', + }, + { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + ], + }, + { + $and: [ + { + role: { $in: ['Manager', 'Mentor'] }, + }, + { + 'persondata.0.role': { + $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + }, + }, + ], + }, + { 'persondata.0._id': userId }, + { 'persondata.0.role': 'Volunteer' }, + { 'persondata.0.isVisible': true }, + ], + }, + }, + { + $project: { + personId: 1, + name: 1, + role: { + $arrayElemAt: ['$persondata.role', 0], + }, + isVisible: { + $arrayElemAt: ['$persondata.isVisible', 0], + }, + hasSummary: { + $ne: [ + { + $arrayElemAt: [ + { + $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], + }, + 0, + ], + }, + '', + ], + }, + weeklycommittedHours: { + $sum: [ + { + $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + }, + { + $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + }, + ], + }, + }, + }, + { + $lookup: { + from: 'timeEntries', + localField: 'personId', + foreignField: 'personId', + as: 'timeEntryData', + }, + }, + { + $project: { + personId: 1, + name: 1, + role: 1, + isVisible: 1, + hasSummary: 1, + weeklycommittedHours: 1, + timeEntryData: { + $filter: { + input: '$timeEntryData', + as: 'timeentry', + cond: { + $and: [ + { + $gte: ['$$timeentry.dateOfWork', pdtstart], + }, + { + $lte: ['$$timeentry.dateOfWork', pdtend], + }, + { + $not: [ + { + $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + }, + ], + }, + ], + }, + }, + }, + }, + }, + { + $unwind: { + path: '$timeEntryData', + preserveNullAndEmptyArrays: true, + }, + }, + { + $project: { + personId: 1, + name: 1, + role: 1, + isVisible: 1, + hasSummary: 1, + weeklycommittedHours: 1, + totalSeconds: { + $cond: [ + { + $gte: ['$timeEntryData.totalSeconds', 0], + }, + '$timeEntryData.totalSeconds', + 0, + ], + }, + isTangible: { + $cond: [ + { + $gte: ['$timeEntryData.totalSeconds', 0], + }, + '$timeEntryData.isTangible', + false, + ], + }, + }, + }, + { + $addFields: { + tangibletime: { + $cond: [ + { + $eq: ['$isTangible', true], + }, + '$totalSeconds', + 0, + ], + }, + intangibletime: { + $cond: [ + { + $eq: ['$isTangible', false], + }, + '$totalSeconds', + 0, + ], + }, + }, + }, + { + $group: { + _id: { + personId: '$personId', + weeklycommittedHours: '$weeklycommittedHours', + name: '$name', + role: '$role', + isVisible: '$isVisible', + hasSummary: '$hasSummary', + }, + totalSeconds: { + $sum: '$totalSeconds', + }, + tangibletime: { + $sum: '$tangibletime', + }, + intangibletime: { + $sum: '$intangibletime', + }, + }, + }, + { + $project: { + _id: 0, + personId: '$_id.personId', + name: '$_id.name', + role: '$_id.role', + isVisible: '$_id.isVisible', + hasSummary: '$_id.hasSummary', + weeklycommittedHours: '$_id.weeklycommittedHours', + totaltime_hrs: { + $divide: ['$totalSeconds', 3600], + }, + totaltangibletime_hrs: { + $divide: ['$tangibletime', 3600], + }, + totalintangibletime_hrs: { + $divide: ['$intangibletime', 3600], + }, + percentagespentintangible: { + $cond: [ + { + $eq: ['$totalSeconds', 0], + }, + 0, + { + $multiply: [ + { + $divide: ['$tangibletime', '$totalSeconds'], + }, + 100, + ], + }, + ], + }, + }, + }, + { + $sort: { + totaltangibletime_hrs: -1, + name: 1, + role: 1, + }, + }, + ]); + return output; }; /** diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 19551b0ed..cfb1235fd 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,23 +1,9 @@ const moment = require('moment-timezone'); const userProfile = require('../models/userProfile'); -const timeentry = require('../models/timeentry'); -const myTeam = require('../helpers/helperModels/myTeam'); -const team = require('../models/team'); -const Task = require('../models/task'); -const TaskNotification = require('../models/taskNotification'); -const Wbs = require('../models/wbs'); -const mongoose = require('mongoose'); +const myteam = require('../helpers/helperModels/myTeam'); const taskHelper = function () { - const getTasksForTeams = async function (userId) { - - const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1,firstName:1, lastName:1, role:1, isVisible:1, weeklycommittedHours:1, weeklySummaries:1}) - .then((res)=>{ return res; }).catch((e)=>{}); - - if(userById==null) return null; - const userRole = userById.role; - + const getTasksForTeams = function (userId) { const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -26,413 +12,291 @@ const taskHelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - - let teamMemberIds = [userid] - let teamMembers = []; - - if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members + return myteam.aggregate([ { - - const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) - .then((res)=>{ return res; }).catch((e)=>{}) - - teamsResult.map((_myTeam)=>{ - _myTeam.members.map((teamMember)=> { - if(!teamMember.userId.equals(userid)) - teamMemberIds.push( teamMember.userId ); - } ) - }) - - teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, - {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - } - else { - if(userRole == 'Administrator'){ //All users except Owner and Core Team - const excludedRoles = ['Core Team', 'Owner']; - teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, - {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - } - else{ //'Core Team', 'Owner' //All users - teamMembers = await userProfile.find({ isActive:true}, - {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) - .then((res)=>{ return res; }).catch((e)=>{}) - } - } - - teamMemberIds = teamMembers.map(member => member._id); - - const timeEntries = await timeentry.find({ - dateOfWork: { - $gte: pdtstart, - $lte: pdtend, - }, - personId: { $in: teamMemberIds } - }); - - let timeEntryByPerson = {} - timeEntries.map((timeEntry)=>{ - - let personIdStr = timeEntry.personId.toString(); - - if(timeEntryByPerson[personIdStr]==null) - timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; - - if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; - } - timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; - }) - - const teamMemberTasks = await Task.find({"resources.userID" : {$in : teamMemberIds }}, { 'resources.profilePic': 0 }) - .populate( { - path: 'wbsId', - select: 'projectId', - }) - const teamMemberTaskIds = teamMemberTasks.map(task => task._id); - const teamMemberTaskNotifications = await TaskNotification.find({"taskId" : {$in : teamMemberTaskIds }}) - - const taskNotificationByTaskNdUser = [] - teamMemberTaskNotifications.map(teamMemberTaskNotification => { - - let taskIdStr = teamMemberTaskNotification.taskId.toString(); - let userIdStr = teamMemberTaskNotification.userId.toString(); - let taskNdUserID = taskIdStr+","+userIdStr; - - if(taskNotificationByTaskNdUser[taskNdUserID]) { - taskNotificationByTaskNdUser[taskNdUserID].push(teamMemberTaskNotification) - } - else{ - taskNotificationByTaskNdUser[taskNdUserID] = [teamMemberTaskNotification] - } - - }) - - const taskByPerson = [] - - teamMemberTasks.map(teamMemberTask => { - - let projId = teamMemberTask.wbsId?.projectId; - let _teamMemberTask = {...teamMemberTask._doc} - _teamMemberTask.projectId = projId; - let taskIdStr = _teamMemberTask._id.toString(); - - teamMemberTask.resources.map(resource => { - - let resourceIdStr = resource.userID.toString(); - let taskNdUserID = taskIdStr+","+resourceIdStr; - _teamMemberTask.taskNotifications = taskNotificationByTaskNdUser[taskNdUserID] || [] - if(taskByPerson[resourceIdStr]) { - taskByPerson[resourceIdStr].push(_teamMemberTask) - } - else{ - taskByPerson[resourceIdStr] = [_teamMemberTask] - } - }) - }) - - - let teamMemberTasksData = []; - teamMembers.map((teamMember)=>{ - let obj = { - personId : teamMember._id, - role : teamMember.role, - name : teamMember.firstName + ' ' + teamMember.lastName, - weeklycommittedHours : teamMember.weeklycommittedHours, - totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), - totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), - tasks : taskByPerson[teamMember._id.toString()] || [] - } - teamMemberTasksData.push(obj); - }) - - - return teamMemberTasksData; - - - // return myteam.aggregate([ - // { - // $match: { - // _id: userId, - // }, - // }, - // { - // $unwind: '$myteam', - // }, - // { - // $project: { - // _id: 0, - // personId: '$myteam._id', - // name: '$myteam.fullName', - // role: 1, - // }, - // }, - // // have personId, name, role - // { - // $lookup: { - // from: 'userProfiles', - // localField: 'personId', - // foreignField: '_id', - // as: 'persondata', - // }, - // }, - // { - // $match: { - // // dashboard tasks user roles hierarchy - // $or: [ - // { - // role: { $in: ['Owner', 'Core Team'] }, - // }, - // { - // $and: [ - // { - // role: 'Administrator', - // }, - // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - // ], - // }, - // { - // $and: [ - // { - // role: { $in: ['Manager', 'Mentor'] }, - // }, - // { - // 'persondata.0.role': { - // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - // }, - // }, - // ], - // }, - // { 'persondata.0._id': userId }, - // { 'persondata.0.role': 'Volunteer' }, - // { 'persondata.0.isVisible': true }, - // ], - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // weeklycommittedHours: { - // $sum: [ - // { - // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - // }, - // { - // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - // }, - // ], - // }, - // role: 1, - // }, - // }, - // { - // $lookup: { - // from: 'timeEntries', - // localField: 'personId', - // foreignField: 'personId', - // as: 'timeEntryData', - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // weeklycommittedHours: 1, - // timeEntryData: { - // $filter: { - // input: '$timeEntryData', - // as: 'timeentry', - // cond: { - // $and: [ - // { - // $gte: ['$$timeentry.dateOfWork', pdtstart], - // }, - // { - // $lte: ['$$timeentry.dateOfWork', pdtend], - // }, - // ], - // }, - // }, - // }, - // role: 1, - // }, - // }, - // { - // $unwind: { - // path: '$timeEntryData', - // preserveNullAndEmptyArrays: true, - // }, - // }, - // { - // $project: { - // personId: 1, - // name: 1, - // weeklycommittedHours: 1, - // totalSeconds: { - // $cond: [ - // { - // $gte: ['$timeEntryData.totalSeconds', 0], - // }, - // '$timeEntryData.totalSeconds', - // 0, - // ], - // }, - // isTangible: { - // $cond: [ - // { - // $gte: ['$timeEntryData.totalSeconds', 0], - // }, - // '$timeEntryData.isTangible', - // false, - // ], - // }, - // role: 1, - // }, - // }, - // { - // $addFields: { - // tangibletime: { - // $cond: [ - // { - // $eq: ['$isTangible', true], - // }, - // '$totalSeconds', - // 0, - // ], - // }, - // }, - // }, - // { - // $group: { - // _id: { - // personId: '$personId', - // weeklycommittedHours: '$weeklycommittedHours', - // name: '$name', - // role: '$role', - // }, - // totalSeconds: { - // $sum: '$totalSeconds', - // }, - // tangibletime: { - // $sum: '$tangibletime', - // }, - // }, - // }, - // { - // $project: { - // _id: 0, - // personId: '$_id.personId', - // name: '$_id.name', - // weeklycommittedHours: '$_id.weeklycommittedHours', - // totaltime_hrs: { - // $divide: ['$totalSeconds', 3600], - // }, - // totaltangibletime_hrs: { - // $divide: ['$tangibletime', 3600], - // }, - // role: '$_id.role', - // }, - // }, - // { - // $lookup: { - // from: 'tasks', - // localField: 'personId', - // foreignField: 'resources.userID', - // as: 'tasks', - // }, - // }, - // { - // $project: { - // tasks: { - // resources: { - // profilePic: 0, - // }, - // }, - // }, - // }, - // { - // $unwind: { - // path: '$tasks', - // preserveNullAndEmptyArrays: true, - // }, - // }, - // { - // $lookup: { - // from: 'wbs', - // localField: 'tasks.wbsId', - // foreignField: '_id', - // as: 'projectId', - // }, - // }, - // { - // $addFields: { - // 'tasks.projectId': { - // $cond: [ - // { $ne: ['$projectId', []] }, - // { $arrayElemAt: ['$projectId', 0] }, - // '$tasks.projectId', - // ], - // }, - // }, - // }, - // { - // $project: { - // projectId: 0, - // tasks: { - // projectId: { - // _id: 0, - // isActive: 0, - // modifiedDatetime: 0, - // wbsName: 0, - // createdDatetime: 0, - // __v: 0, - // }, - // }, - // }, - // }, - // { - // $addFields: { - // 'tasks.projectId': '$tasks.projectId.projectId', - // }, - // }, - // { - // $lookup: { - // from: 'taskNotifications', - // localField: 'tasks._id', - // foreignField: 'taskId', - // as: 'tasks.taskNotifications', - // }, - // }, - // { - // $group: { - // _id: '$personId', - // tasks: { - // $push: '$tasks', - // }, - // data: { - // $first: '$$ROOT', - // }, - // }, - // }, - // { - // $addFields: { - // 'data.tasks': { - // $filter: { - // input: '$tasks', - // as: 'task', - // cond: { $ne: ['$$task', {}] }, - // }, - // }, - // }, - // }, - // { - // $replaceRoot: { - // newRoot: '$data', - // }, - // }, - // ]); + $match: { + _id: userId, + }, + }, + { + $unwind: '$myteam', + }, + { + $project: { + _id: 0, + personId: '$myteam._id', + name: '$myteam.fullName', + role: 1, + }, + }, + // have personId, name, role + { + $lookup: { + from: 'userProfiles', + localField: 'personId', + foreignField: '_id', + as: 'persondata', + }, + }, + { + $match: { + // dashboard tasks user roles hierarchy + $or: [ + { + role: { $in: ['Owner', 'Core Team'] }, + }, + { + $and: [ + { + role: 'Administrator', + }, + { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + ], + }, + { + $and: [ + { + role: { $in: ['Manager', 'Mentor'] }, + }, + { + 'persondata.0.role': { + $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + }, + }, + ], + }, + { 'persondata.0._id': userId }, + { 'persondata.0.role': 'Volunteer' }, + { 'persondata.0.isVisible': true }, + ], + }, + }, + { + $project: { + personId: 1, + name: 1, + weeklycommittedHours: { + $sum: [ + { + $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + }, + { + $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + }, + ], + }, + role: 1, + }, + }, + { + $lookup: { + from: 'timeEntries', + localField: 'personId', + foreignField: 'personId', + as: 'timeEntryData', + }, + }, + { + $project: { + personId: 1, + name: 1, + weeklycommittedHours: 1, + timeEntryData: { + $filter: { + input: '$timeEntryData', + as: 'timeentry', + cond: { + $and: [ + { + $gte: ['$$timeentry.dateOfWork', pdtstart], + }, + { + $lte: ['$$timeentry.dateOfWork', pdtend], + }, + { + $in: ['$$timeentry.entryType', ['default', null]], + }, + ], + }, + }, + }, + role: 1, + }, + }, + { + $unwind: { + path: '$timeEntryData', + preserveNullAndEmptyArrays: true, + }, + }, + { + $project: { + personId: 1, + name: 1, + weeklycommittedHours: 1, + totalSeconds: { + $cond: [ + { + $gte: ['$timeEntryData.totalSeconds', 0], + }, + '$timeEntryData.totalSeconds', + 0, + ], + }, + isTangible: { + $cond: [ + { + $gte: ['$timeEntryData.totalSeconds', 0], + }, + '$timeEntryData.isTangible', + false, + ], + }, + role: 1, + }, + }, + { + $addFields: { + tangibletime: { + $cond: [ + { + $eq: ['$isTangible', true], + }, + '$totalSeconds', + 0, + ], + }, + }, + }, + { + $group: { + _id: { + personId: '$personId', + weeklycommittedHours: '$weeklycommittedHours', + name: '$name', + role: '$role', + }, + totalSeconds: { + $sum: '$totalSeconds', + }, + tangibletime: { + $sum: '$tangibletime', + }, + }, + }, + { + $project: { + _id: 0, + personId: '$_id.personId', + name: '$_id.name', + weeklycommittedHours: '$_id.weeklycommittedHours', + totaltime_hrs: { + $divide: ['$totalSeconds', 3600], + }, + totaltangibletime_hrs: { + $divide: ['$tangibletime', 3600], + }, + role: '$_id.role', + }, + }, + { + $lookup: { + from: 'tasks', + localField: 'personId', + foreignField: 'resources.userID', + as: 'tasks', + }, + }, + { + $project: { + tasks: { + resources: { + profilePic: 0, + }, + }, + }, + }, + { + $unwind: { + path: '$tasks', + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: 'wbs', + localField: 'tasks.wbsId', + foreignField: '_id', + as: 'projectId', + }, + }, + { + $addFields: { + 'tasks.projectId': { + $cond: [ + { $ne: ['$projectId', []] }, + { $arrayElemAt: ['$projectId', 0] }, + '$tasks.projectId', + ], + }, + }, + }, + { + $project: { + projectId: 0, + tasks: { + projectId: { + _id: 0, + isActive: 0, + modifiedDatetime: 0, + wbsName: 0, + createdDatetime: 0, + __v: 0, + }, + }, + }, + }, + { + $addFields: { + 'tasks.projectId': '$tasks.projectId.projectId', + }, + }, + { + $lookup: { + from: 'taskNotifications', + localField: 'tasks._id', + foreignField: 'taskId', + as: 'tasks.taskNotifications', + }, + }, + { + $group: { + _id: '$personId', + tasks: { + $push: '$tasks', + }, + data: { + $first: '$$ROOT', + }, + }, + }, + { + $addFields: { + 'data.tasks': { + $filter: { + input: '$tasks', + as: 'task', + cond: { $ne: ['$$task', {}] }, + }, + }, + }, + }, + { + $replaceRoot: { + newRoot: '$data', + }, + }, + ]); }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() From e214e9cc7e1c9df313b42c5258c93720f7e07504 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 17:13:02 -0800 Subject: [PATCH 126/129] task api --- src/helpers/taskHelper.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index e1e2acb13..5a17f8b20 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,12 +1,6 @@ const moment = require('moment-timezone'); const userProfile = require('../models/userProfile'); -const timeentry = require('../models/timeentry'); -const myTeam = require('../helpers/helperModels/myTeam'); -const team = require('../models/team'); -const Task = require('../models/task'); -const TaskNotification = require('../models/taskNotification'); -const Wbs = require('../models/wbs'); -const mongoose = require('mongoose'); +const myteam = require('../helpers/helperModels/myTeam'); const taskHelper = function () { const getTasksForTeams = async function (userId) { @@ -15,7 +9,6 @@ const taskHelper = function () { const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1,firstName:1, lastName:1, role:1, isVisible:1, weeklycommittedHours:1, weeklySummaries:1}) .then((res)=>{ return res; }).catch((e)=>{}); - if(userById==null) return null; const userRole = userById.role; From 38ffc5f0e3b49f88e8a14046440bc07935c8ce81 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 17:29:41 -0800 Subject: [PATCH 127/129] leaderboard ,task api fix --- src/controllers/taskController.js | 2 +- src/helpers/dashboardhelper.js | 611 +++++++++++++++----------- src/helpers/taskHelper.js | 699 ++++++++++++++++++------------ 3 files changed, 771 insertions(+), 541 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index 5b087db4d..753dbba11 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -834,7 +834,7 @@ const taskController = function (Task) { const getTasksForTeamsByUser = async (req, res) => { try { const userId = mongoose.Types.ObjectId(req.params.userId); - const teamsData = await taskHelper.getTasksForTeams(userId).exec(); + const teamsData = await taskHelper.getTasksForTeams(userId); if (teamsData.length > 0) { res.status(200).send(teamsData); } else { diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index b7ca2f131..dea392e4f 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -161,6 +161,11 @@ const dashboardhelper = function () { const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1}) + .then((res)=>{ return res; }).catch((e)=>{}); + + if(userById==null) return null; + const userRole = userById.role; const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -169,262 +174,358 @@ const dashboardhelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - const output = await myTeam.aggregate([ - { - $match: { - _id: userid, - }, - }, - { - $unwind: '$myteam', - }, - { - $project: { - _id: 0, - role: 1, - personId: '$myteam._id', - name: '$myteam.fullName', - }, - }, - { - $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', - }, - }, - { - $match: { - // leaderboard user roles hierarchy - $or: [ - { - role: { $in: ['Owner', 'Core Team'] }, - }, - { - $and: [ - { - role: 'Administrator', - }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ], - }, - { - $and: [ - { - role: { $in: ['Manager', 'Mentor'] }, - }, - { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - }, - }, - ], - }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - role: { - $arrayElemAt: ['$persondata.role', 0], - }, - isVisible: { - $arrayElemAt: ['$persondata.isVisible', 0], - }, - hasSummary: { - $ne: [ - { - $arrayElemAt: [ - { - $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], - }, - 0, - ], - }, - '', - ], - }, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - }, - { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - }, - ], - }, - }, - }, - { - $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeEntryData: { - $filter: { - input: '$timeEntryData', - as: 'timeentry', - cond: { - $and: [ - { - $gte: ['$$timeentry.dateOfWork', pdtstart], - }, - { - $lte: ['$$timeentry.dateOfWork', pdtend], - }, - { - $not: [ - { - $in: ['$$timeentry.entryType', ['person', 'team', 'project']], - }, - ], - }, - ], - }, - }, - }, - }, - }, - { - $unwind: { - path: '$timeEntryData', - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - totalSeconds: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.totalSeconds', - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.isTangible', - false, - ], - }, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ['$isTangible', true], - }, - '$totalSeconds', - 0, - ], - }, - intangibletime: { - $cond: [ - { - $eq: ['$isTangible', false], - }, - '$totalSeconds', - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', - isVisible: '$isVisible', - hasSummary: '$hasSummary', - }, - totalSeconds: { - $sum: '$totalSeconds', - }, - tangibletime: { - $sum: '$tangibletime', - }, - intangibletime: { - $sum: '$intangibletime', - }, - }, - }, - { - $project: { - _id: 0, - personId: '$_id.personId', - name: '$_id.name', - role: '$_id.role', - isVisible: '$_id.isVisible', - hasSummary: '$_id.hasSummary', - weeklycommittedHours: '$_id.weeklycommittedHours', - totaltime_hrs: { - $divide: ['$totalSeconds', 3600], - }, - totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], - }, - totalintangibletime_hrs: { - $divide: ['$intangibletime', 3600], - }, - percentagespentintangible: { - $cond: [ - { - $eq: ['$totalSeconds', 0], - }, - 0, - { - $multiply: [ - { - $divide: ['$tangibletime', '$totalSeconds'], - }, - 100, - ], - }, - ], - }, - }, - }, - { - $sort: { - totaltangibletime_hrs: -1, - name: 1, - role: 1, - }, + + let teamMemberIds = [userid] + let teamMembers = []; + + if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members + { + + const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) + .then((res)=>{ return res; }).catch((e)=>{}) + + teamsResult.map((_myTeam)=>{ + _myTeam.members.map((teamMember)=> { + if(!teamMember.userId.equals(userid)) + teamMemberIds.push( teamMember.userId ); + } ) + }) + + teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + + } + else { + if(userRole == 'Administrator'){ //All users except Owner and Core Team + const excludedRoles = ['Core Team', 'Owner']; + teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else{ //'Core Team', 'Owner' //All users + teamMembers = await userProfile.find({ isActive:true}, + {role:1,firstName:1,lastName:1,isVisible:1,weeklycommittedHours:1,weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + + + } + + teamMemberIds = teamMembers.map(member => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, }, - ]); - return output; + personId: { $in: teamMemberIds } + }); + + let timeEntryByPerson = {} + timeEntries.map((timeEntry)=>{ + + let personIdStr = timeEntry.personId.toString(); + + if(timeEntryByPerson[personIdStr]==null) + timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } else { + timeEntryByPerson[personIdStr].intangibleSeconds += timeEntry.totalSeconds; + } + + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }) + + + let leaderBoardData = []; + teamMembers.map((teamMember)=>{ + let obj = { + personId : teamMember._id, + role : teamMember.role, + name : teamMember.firstName + ' ' + teamMember.lastName, + isVisible : teamMember.isVisible, + hasSummary : teamMember.weeklySummaries?.length > 0 ? teamMember.weeklySummaries[0].summary!='' : false, + weeklycommittedHours : teamMember.weeklycommittedHours, + totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), + totalintangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / 3600) || 0), + totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), + percentagespentintangible : + (timeEntryByPerson[teamMember._id.toString()] && timeEntryByPerson[teamMember._id.toString()]?.totalSeconds !=0 && timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds !=0) ? + (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * 100 + : + 0 + } + leaderBoardData.push(obj); + }) + + let sortedLBData = leaderBoardData.sort((a, b) => { + // Sort by totaltangibletime_hrs in descending order + if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { + return b.totaltangibletime_hrs - a.totaltangibletime_hrs; + } + + // Then sort by name in ascending order + if (a.name !== b.name) { + return a.name.localeCompare(b.name); + } + + // Finally, sort by role in ascending order + return a.role.localeCompare(b.role); + }); + + return sortedLBData; + + // return myTeam.aggregate([ + // { + // $match: { + // _id: userid, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // role: 1, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // }, + // }, + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // leaderboard user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: { + // $arrayElemAt: ['$persondata.role', 0], + // }, + // isVisible: { + // $arrayElemAt: ['$persondata.isVisible', 0], + // }, + // hasSummary: { + // $ne: [ + // { + // $arrayElemAt: [ + // { + // $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], + // }, + // 0, + // ], + // }, + // '', + // ], + // }, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // intangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', false], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // isVisible: '$isVisible', + // hasSummary: '$hasSummary', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // intangibletime: { + // $sum: '$intangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // role: '$_id.role', + // isVisible: '$_id.isVisible', + // hasSummary: '$_id.hasSummary', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // totalintangibletime_hrs: { + // $divide: ['$intangibletime', 3600], + // }, + // percentagespentintangible: { + // $cond: [ + // { + // $eq: ['$totalSeconds', 0], + // }, + // 0, + // { + // $multiply: [ + // { + // $divide: ['$tangibletime', '$totalSeconds'], + // }, + // 100, + // ], + // }, + // ], + // }, + // }, + // }, + // { + // $sort: { + // totaltangibletime_hrs: -1, + // name: 1, + // role: 1, + // }, + // }, + // ]); }; /** diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index cfb1235fd..9135f7573 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -3,7 +3,14 @@ const userProfile = require('../models/userProfile'); const myteam = require('../helpers/helperModels/myTeam'); const taskHelper = function () { - const getTasksForTeams = function (userId) { + const getTasksForTeams = async function (userId) { + const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile.findOne({ _id: userid , isActive:true}, {role:1,firstName:1, lastName:1, role:1, isVisible:1, weeklycommittedHours:1, weeklySummaries:1}) + .then((res)=>{ return res; }).catch((e)=>{}); + + if(userById==null) return null; + const userRole = userById.role; + const pdtstart = moment() .tz('America/Los_Angeles') .startOf('week') @@ -12,291 +19,413 @@ const taskHelper = function () { .tz('America/Los_Angeles') .endOf('week') .format('YYYY-MM-DD'); - return myteam.aggregate([ - { - $match: { - _id: userId, - }, - }, - { - $unwind: '$myteam', - }, - { - $project: { - _id: 0, - personId: '$myteam._id', - name: '$myteam.fullName', - role: 1, - }, - }, - // have personId, name, role - { - $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', - }, - }, - { - $match: { - // dashboard tasks user roles hierarchy - $or: [ - { - role: { $in: ['Owner', 'Core Team'] }, - }, - { - $and: [ - { - role: 'Administrator', - }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ], - }, - { - $and: [ - { - role: { $in: ['Manager', 'Mentor'] }, - }, - { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], - }, - }, - ], - }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], - }, - { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], - }, - ], - }, - role: 1, - }, - }, - { - $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - timeEntryData: { - $filter: { - input: '$timeEntryData', - as: 'timeentry', - cond: { - $and: [ - { - $gte: ['$$timeentry.dateOfWork', pdtstart], - }, - { - $lte: ['$$timeentry.dateOfWork', pdtend], - }, - { - $in: ['$$timeentry.entryType', ['default', null]], - }, - ], - }, - }, - }, - role: 1, - }, - }, - { - $unwind: { - path: '$timeEntryData', - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - totalSeconds: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.totalSeconds', - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ['$timeEntryData.totalSeconds', 0], - }, - '$timeEntryData.isTangible', - false, - ], - }, - role: 1, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ['$isTangible', true], - }, - '$totalSeconds', - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', - }, - totalSeconds: { - $sum: '$totalSeconds', - }, - tangibletime: { - $sum: '$tangibletime', - }, - }, - }, - { - $project: { - _id: 0, - personId: '$_id.personId', - name: '$_id.name', - weeklycommittedHours: '$_id.weeklycommittedHours', - totaltime_hrs: { - $divide: ['$totalSeconds', 3600], - }, - totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], - }, - role: '$_id.role', - }, - }, - { - $lookup: { - from: 'tasks', - localField: 'personId', - foreignField: 'resources.userID', - as: 'tasks', - }, - }, - { - $project: { - tasks: { - resources: { - profilePic: 0, - }, - }, - }, - }, - { - $unwind: { - path: '$tasks', - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: 'wbs', - localField: 'tasks.wbsId', - foreignField: '_id', - as: 'projectId', - }, - }, - { - $addFields: { - 'tasks.projectId': { - $cond: [ - { $ne: ['$projectId', []] }, - { $arrayElemAt: ['$projectId', 0] }, - '$tasks.projectId', - ], - }, - }, - }, - { - $project: { - projectId: 0, - tasks: { - projectId: { - _id: 0, - isActive: 0, - modifiedDatetime: 0, - wbsName: 0, - createdDatetime: 0, - __v: 0, - }, - }, - }, - }, - { - $addFields: { - 'tasks.projectId': '$tasks.projectId.projectId', - }, - }, - { - $lookup: { - from: 'taskNotifications', - localField: 'tasks._id', - foreignField: 'taskId', - as: 'tasks.taskNotifications', - }, - }, - { - $group: { - _id: '$personId', - tasks: { - $push: '$tasks', - }, - data: { - $first: '$$ROOT', - }, - }, - }, - { - $addFields: { - 'data.tasks': { - $filter: { - input: '$tasks', - as: 'task', - cond: { $ne: ['$$task', {}] }, - }, - }, - }, - }, + + let teamMemberIds = [userid] + let teamMembers = []; + + if(userRole!='Administrator' && userRole!='Owner' && userRole!='Core Team') //Manager , Mentor , Volunteer ... , Show only team members { - $replaceRoot: { - newRoot: '$data', - }, - }, - ]); + + const teamsResult = await team.find( { "members.userId": { $in: [userid] } }, {members:1} ) + .then((res)=>{ return res; }).catch((e)=>{}) + + teamsResult.map((_myTeam)=>{ + _myTeam.members.map((teamMember)=> { + if(!teamMember.userId.equals(userid)) + teamMemberIds.push( teamMember.userId ); + } ) + }) + + teamMembers = await userProfile.find({ _id: { $in: teamMemberIds } , isActive:true }, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else { + if(userRole == 'Administrator'){ //All users except Owner and Core Team + const excludedRoles = ['Core Team', 'Owner']; + teamMembers = await userProfile.find({ isActive:true , role: { $nin: excludedRoles } }, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + else{ //'Core Team', 'Owner' //All users + teamMembers = await userProfile.find({ isActive:true}, + {role:1,firstName:1,lastName:1,weeklycommittedHours:1}) + .then((res)=>{ return res; }).catch((e)=>{}) + } + } + + teamMemberIds = teamMembers.map(member => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, + }, + personId: { $in: teamMemberIds } + }); + + let timeEntryByPerson = {} + timeEntries.map((timeEntry)=>{ + + let personIdStr = timeEntry.personId.toString(); + + if(timeEntryByPerson[personIdStr]==null) + timeEntryByPerson[personIdStr] = {tangibleSeconds:0,intangibleSeconds:0,totalSeconds:0}; + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }) + + const teamMemberTasks = await Task.find({"resources.userID" : {$in : teamMemberIds }}, { 'resources.profilePic': 0 }) + .populate( { + path: 'wbsId', + select: 'projectId', + }) + const teamMemberTaskIds = teamMemberTasks.map(task => task._id); + const teamMemberTaskNotifications = await TaskNotification.find({"taskId" : {$in : teamMemberTaskIds }}) + + const taskNotificationByTaskNdUser = [] + teamMemberTaskNotifications.map(teamMemberTaskNotification => { + + let taskIdStr = teamMemberTaskNotification.taskId.toString(); + let userIdStr = teamMemberTaskNotification.userId.toString(); + let taskNdUserID = taskIdStr+","+userIdStr; + + if(taskNotificationByTaskNdUser[taskNdUserID]) { + taskNotificationByTaskNdUser[taskNdUserID].push(teamMemberTaskNotification) + } + else{ + taskNotificationByTaskNdUser[taskNdUserID] = [teamMemberTaskNotification] + } + + }) + + const taskByPerson = [] + + teamMemberTasks.map(teamMemberTask => { + + let projId = teamMemberTask.wbsId?.projectId; + let _teamMemberTask = {...teamMemberTask._doc} + _teamMemberTask.projectId = projId; + let taskIdStr = _teamMemberTask._id.toString(); + + teamMemberTask.resources.map(resource => { + + let resourceIdStr = resource.userID.toString(); + let taskNdUserID = taskIdStr+","+resourceIdStr; + _teamMemberTask.taskNotifications = taskNotificationByTaskNdUser[taskNdUserID] || [] + if(taskByPerson[resourceIdStr]) { + taskByPerson[resourceIdStr].push(_teamMemberTask) + } + else{ + taskByPerson[resourceIdStr] = [_teamMemberTask] + } + }) + }) + + + let teamMemberTasksData = []; + teamMembers.map((teamMember)=>{ + let obj = { + personId : teamMember._id, + role : teamMember.role, + name : teamMember.firstName + ' ' + teamMember.lastName, + weeklycommittedHours : teamMember.weeklycommittedHours, + totaltangibletime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), + totaltime_hrs : ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), + tasks : taskByPerson[teamMember._id.toString()] || [] + } + teamMemberTasksData.push(obj); + }) + + + return teamMemberTasksData; + + + // return myteam.aggregate([ + // { + // $match: { + // _id: userId, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // role: 1, + // }, + // }, + // // have personId, name, role + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // dashboard tasks user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // role: 1, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // role: '$_id.role', + // }, + // }, + // { + // $lookup: { + // from: 'tasks', + // localField: 'personId', + // foreignField: 'resources.userID', + // as: 'tasks', + // }, + // }, + // { + // $project: { + // tasks: { + // resources: { + // profilePic: 0, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$tasks', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $lookup: { + // from: 'wbs', + // localField: 'tasks.wbsId', + // foreignField: '_id', + // as: 'projectId', + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': { + // $cond: [ + // { $ne: ['$projectId', []] }, + // { $arrayElemAt: ['$projectId', 0] }, + // '$tasks.projectId', + // ], + // }, + // }, + // }, + // { + // $project: { + // projectId: 0, + // tasks: { + // projectId: { + // _id: 0, + // isActive: 0, + // modifiedDatetime: 0, + // wbsName: 0, + // createdDatetime: 0, + // __v: 0, + // }, + // }, + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': '$tasks.projectId.projectId', + // }, + // }, + // { + // $lookup: { + // from: 'taskNotifications', + // localField: 'tasks._id', + // foreignField: 'taskId', + // as: 'tasks.taskNotifications', + // }, + // }, + // { + // $group: { + // _id: '$personId', + // tasks: { + // $push: '$tasks', + // }, + // data: { + // $first: '$$ROOT', + // }, + // }, + // }, + // { + // $addFields: { + // 'data.tasks': { + // $filter: { + // input: '$tasks', + // as: 'task', + // cond: { $ne: ['$$task', {}] }, + // }, + // }, + // }, + // }, + // { + // $replaceRoot: { + // newRoot: '$data', + // }, + // }, + // ]); }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() From 3784309815e64465d8210ec6e08ec5b6ef1d4256 Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 3 Jan 2024 17:37:23 -0800 Subject: [PATCH 128/129] fix imports --- src/helpers/dashboardhelper.js | 2 ++ src/helpers/taskHelper.js | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index dea392e4f..929b5781b 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -3,6 +3,8 @@ const mongoose = require('mongoose'); const userProfile = require('../models/userProfile'); const timeentry = require('../models/timeentry'); const myTeam = require('../helpers/helperModels/myTeam'); +const team = require('../models/team'); + const dashboardhelper = function () { const personaldetails = function (userId) { diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 9135f7573..8c45df737 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,6 +1,12 @@ const moment = require('moment-timezone'); const userProfile = require('../models/userProfile'); -const myteam = require('../helpers/helperModels/myTeam'); +const timeentry = require('../models/timeentry'); +const myTeam = require('../helpers/helperModels/myTeam'); +const team = require('../models/team'); +const Task = require('../models/task'); +const TaskNotification = require('../models/taskNotification'); +const Wbs = require('../models/wbs'); +const mongoose = require('mongoose'); const taskHelper = function () { const getTasksForTeams = async function (userId) { From 037cc6f1b28407af489643e7a65bd6449001473c Mon Sep 17 00:00:00 2001 From: Carl Bebli Date: Thu, 4 Jan 2024 13:11:23 +0000 Subject: [PATCH 129/129] took out the cc for review requests --- src/controllers/taskController.js | 460 +++++++++++++++++------------- 1 file changed, 263 insertions(+), 197 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index 753dbba11..126d17914 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -1,10 +1,10 @@ -const mongoose = require('mongoose'); -const WBS = require('../models/wbs'); -const UserProfile = require('../models/userProfile'); -const timeEntryHelper = require('../helpers/timeEntryHelper')(); -const taskHelper = require('../helpers/taskHelper')(); -const { hasPermission } = require('../utilities/permissions'); -const emailSender = require('../utilities/emailSender'); +const mongoose = require("mongoose"); +const WBS = require("../models/wbs"); +const UserProfile = require("../models/userProfile"); +const timeEntryHelper = require("../helpers/timeEntryHelper")(); +const taskHelper = require("../helpers/taskHelper")(); +const { hasPermission } = require("../utilities/permissions"); +const emailSender = require("../utilities/emailSender"); const taskController = function (Task) { const getTasks = (req, res) => { @@ -17,7 +17,7 @@ const taskController = function (Task) { const { mother } = req.params; - if (mother !== '0') { + if (mother !== "0") { query = { wbsId: { $in: [req.params.wbsId] }, level: { $in: [level] }, @@ -26,16 +26,16 @@ const taskController = function (Task) { } Task.find(query) - .then(results => res.status(200).send(results)) - .catch(error => res.status(404).send(error)); + .then((results) => res.status(200).send(results)) + .catch((error) => res.status(404).send(error)); }; const getWBSId = (req, res) => { const { wbsId } = req.params; WBS.findById(wbsId) - .then(results => res.status(200).send(results)) - .catch(error => res.status(404).send(error)); + .then((results) => res.status(200).send(results)) + .catch((error) => res.status(404).send(error)); }; const updateSumUp = ( @@ -45,7 +45,7 @@ const taskController = function (Task) { hoursMost, hoursLogged, estimatedHours, - resources, + resources ) => { Task.findById(taskId, (error, task) => { task.hoursBest = hoursBest; @@ -81,10 +81,10 @@ const taskController = function (Task) { }; const calculateSubTasks = (level, tasks) => { - const parentTasks = tasks.filter(task => task.level === level); + const parentTasks = tasks.filter((task) => task.level === level); parentTasks.forEach((task) => { const childTasks = tasks.filter( - taskChild => taskChild.level === level + 1, + (taskChild) => taskChild.level === level + 1 ); let sumHoursBest = 0; let sumHoursWorst = 0; @@ -96,14 +96,19 @@ const taskController = function (Task) { childTasks.forEach((childTask) => { if (childTask.mother.equals(task.taskId)) { hasChild = true; - sumHoursBest = parseFloat(childTask.hoursBest, 10) + parseFloat(sumHoursBest, 10); - sumHoursWorst = parseFloat(childTask.hoursWorst, 10) - + parseFloat(sumHoursWorst, 10); - sumHoursMost = parseFloat(childTask.hoursMost, 10) + parseFloat(sumHoursMost, 10); - sumHoursLogged = parseFloat(childTask.hoursLogged, 10) - + parseFloat(sumHoursLogged, 10); - sumEstimatedHours = parseFloat(childTask.estimatedHours, 10) - + parseFloat(sumEstimatedHours, 10); + sumHoursBest = + parseFloat(childTask.hoursBest, 10) + parseFloat(sumHoursBest, 10); + sumHoursWorst = + parseFloat(childTask.hoursWorst, 10) + + parseFloat(sumHoursWorst, 10); + sumHoursMost = + parseFloat(childTask.hoursMost, 10) + parseFloat(sumHoursMost, 10); + sumHoursLogged = + parseFloat(childTask.hoursLogged, 10) + + parseFloat(sumHoursLogged, 10); + sumEstimatedHours = + parseFloat(childTask.estimatedHours, 10) + + parseFloat(sumEstimatedHours, 10); childTask.resources.forEach((member) => { let isInResource = false; resources.forEach((mem) => { @@ -136,7 +141,7 @@ const taskController = function (Task) { sumHoursMost, sumHoursLogged, sumEstimatedHours, - resources, + resources ); } }); @@ -144,10 +149,10 @@ const taskController = function (Task) { }; const setDatesSubTasks = (level, tasks) => { - const parentTasks = tasks.filter(task => task.level === level); + const parentTasks = tasks.filter((task) => task.level === level); parentTasks.forEach((task) => { const childTasks = tasks.filter( - taskChild => taskChild.level === level + 1, + (taskChild) => taskChild.level === level + 1 ); let minStartedDate = task.startedDatetime; let maxDueDatetime = task.dueDatetime; @@ -178,10 +183,10 @@ const taskController = function (Task) { }; const calculatePriority = (level, tasks) => { - const parentTasks = tasks.filter(task => task.level === level); + const parentTasks = tasks.filter((task) => task.level === level); parentTasks.forEach((task) => { const childTasks = tasks.filter( - taskChild => taskChild.level === level + 1, + (taskChild) => taskChild.level === level + 1 ); let totalNumberPriority = 0; let totalChild = 0; @@ -190,11 +195,11 @@ const taskController = function (Task) { if (childTask.mother.equals(task.taskId)) { hasChild = true; totalChild += 1; - if (childTask.priority === 'Primary') { + if (childTask.priority === "Primary") { totalNumberPriority += 3; - } else if (childTask.priority === 'Secondary') { + } else if (childTask.priority === "Secondary") { totalNumberPriority += 2; - } else if (childTask.priority === 'Tertiary') { + } else if (childTask.priority === "Tertiary") { totalNumberPriority += 1; } } @@ -207,11 +212,11 @@ const taskController = function (Task) { if (mainTask._id.equals(task._id)) { const avg = totalNumberPriority / totalChild; if (avg <= 1.6) { - priority = 'Tertiary'; + priority = "Tertiary"; } else if (avg > 1.6 && avg < 2.5) { - priority = 'Secondary'; + priority = "Secondary"; } else { - priority = 'Primary'; + priority = "Primary"; } } }); @@ -222,10 +227,10 @@ const taskController = function (Task) { }; const setAssigned = (level, tasks) => { - const parentTasks = tasks.filter(task => task.level === level); + const parentTasks = tasks.filter((task) => task.level === level); parentTasks.forEach((task) => { const childTasks = tasks.filter( - taskChild => taskChild.level === level + 1, + (taskChild) => taskChild.level === level + 1 ); let isAssigned = false; let hasChild = false; @@ -259,7 +264,7 @@ const taskController = function (Task) { { wbsId: { $in: [wbsId] } }, ], }).then((tasks) => { - tasks = [...new Set(tasks.map(item => item))]; + tasks = [...new Set(tasks.map((item) => item))]; for (let lv = 3; lv > 0; lv -= 1) { calculateSubTasks(lv, tasks); setDatesSubTasks(lv, tasks); @@ -285,18 +290,20 @@ const taskController = function (Task) { const tasksWithId = tasks.map((task) => { const _id = new mongoose.Types.ObjectId(); const resources = task.resources.map((resource) => { - const [name, userID, profilePic] = resource.split('|'); + const [name, userID, profilePic] = resource.split("|"); return { name, userID, profilePic }; }); return { - ...task, _id, resources, + ...task, + _id, + resources, }; }); // update tasks makes sure its parentIds and mother props are correct assigned, tasksWithId.forEach((task) => { - const taskNumArr = task.num.split('.'); + const taskNumArr = task.num.split("."); switch (task.level) { case 1: // task.num is x, no parentId1 or mother task.parentId1 = null; // no parent so its value is null @@ -305,21 +312,34 @@ const taskController = function (Task) { task.mother = null; break; case 2: // task.num is x.x, only has one level of parent (x) - task.parentId1 = tasksWithId.find(pTask => pTask.num === taskNumArr[0])._id; // task of parentId1 has num prop of x + task.parentId1 = tasksWithId.find( + (pTask) => pTask.num === taskNumArr[0] + )._id; // task of parentId1 has num prop of x task.parentId2 = null; task.parentId3 = null; task.mother = task.parentId1; // parent task num prop is x break; case 3: // task.num is x.x.x, has two levels of parent (parent: x.x and grandparent: x) - task.parentId1 = tasksWithId.find(pTask => pTask.num === taskNumArr[0])._id; // task of parentId1 has num prop of x - task.parentId2 = tasksWithId.find(pTask => pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}`)._id; // task of parentId2 has num prop of x.x + task.parentId1 = tasksWithId.find( + (pTask) => pTask.num === taskNumArr[0] + )._id; // task of parentId1 has num prop of x + task.parentId2 = tasksWithId.find( + (pTask) => pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}` + )._id; // task of parentId2 has num prop of x.x task.parentId3 = null; task.mother = task.parentId2; // parent task num prop is x.x break; case 4: // task.num is x.x.x.x, has three levels of parent (x.x.x, x.x and x) - task.parentId1 = tasksWithId.find(pTask => pTask.num === taskNumArr[0])._id; // x - task.parentId2 = tasksWithId.find(pTask => pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}`)._id; // x.x - task.parentId3 = tasksWithId.find(pTask => pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}.${taskNumArr[2]}`)._id; // x.x.x + task.parentId1 = tasksWithId.find( + (pTask) => pTask.num === taskNumArr[0] + )._id; // x + task.parentId2 = tasksWithId.find( + (pTask) => pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}` + )._id; // x.x + task.parentId3 = tasksWithId.find( + (pTask) => + pTask.num === `${taskNumArr[0]}.${taskNumArr[1]}.${taskNumArr[2]}` + )._id; // x.x.x task.mother = task.parentId3; // parent task num prop is x.x.x break; default: @@ -327,7 +347,9 @@ const taskController = function (Task) { }); // create an array of four empty arrays - const tasksFromSameLevelArr = Array(4).fill(null).map(() => []); + const tasksFromSameLevelArr = Array(4) + .fill(null) + .map(() => []); // sort them out into an array of four arrays based on their levels tasksWithId.forEach((task) => { @@ -358,18 +380,32 @@ const taskController = function (Task) { task.hoursMost += childTask.hoursMost; task.hoursLogged += childTask.hoursLogged; task.estimatedHours += childTask.estimatedHours; - task.startedDatetime = Math.min(task.startedDatetime, childTask.startedDatetime); - task.dueDatetime = Math.max(task.dueDatetime, childTask.dueDatetime); + task.startedDatetime = Math.min( + task.startedDatetime, + childTask.startedDatetime + ); + task.dueDatetime = Math.max( + task.dueDatetime, + childTask.dueDatetime + ); task.childrenQty = (task.childrenQty || 0) + 1; task.isAssigned = task.isAssigned || childTask.isAssigned; - task.resources = childTask.resources.reduce((resources, childTaskMember) => { - if (task.resources.every(member => member.name !== childTaskMember.name)) return [...resources, childTaskMember]; - return resources; - }, [...task.resources]); + task.resources = childTask.resources.reduce( + (resources, childTaskMember) => { + if ( + task.resources.every( + (member) => member.name !== childTaskMember.name + ) + ) + return [...resources, childTaskMember]; + return resources; + }, + [...task.resources] + ); // add priority pts for task.priority - if (childTask.priority === 'Primary') { + if (childTask.priority === "Primary") { priorityPts += 3; - } else if (childTask.priority === 'Secondary') { + } else if (childTask.priority === "Secondary") { priorityPts += 2; } else { priorityPts += 1; @@ -379,11 +415,11 @@ const taskController = function (Task) { }); const averagePts = priorityPts / task.childrenQty; if (averagePts >= 2.5) { - task.priority = 'Primary'; + task.priority = "Primary"; } else if (averagePts >= 1.6) { - task.priority = 'Secondary'; + task.priority = "Secondary"; } else { - task.priority = 'Tertiary'; + task.priority = "Tertiary"; } }); } @@ -392,10 +428,10 @@ const taskController = function (Task) { }; const importTask = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'importTask')) { + if (!(await hasPermission(req.body.requestor, "importTask"))) { res .status(403) - .send({ error: 'You are not authorized to create new Task.' }); + .send({ error: "You are not authorized to create new Task." }); return; } @@ -407,7 +443,10 @@ const taskController = function (Task) { const createdDatetime = Date.now(); const modifiedDatetime = Date.now(); const _task = new Task({ - ...task, wbsId, createdDatetime, modifiedDatetime, + ...task, + wbsId, + createdDatetime, + modifiedDatetime, }); _task @@ -418,20 +457,20 @@ const taskController = function (Task) { }); }); - res.status(201).send('done'); + res.status(201).send("done"); }; const postTask = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'postTask')) { + if (!(await hasPermission(req.body.requestor, "postTask"))) { res .status(403) - .send({ error: 'You are not authorized to create new Task.' }); + .send({ error: "You are not authorized to create new Task." }); return; } if (!req.body.taskName || !req.body.isActive) { res.status(400).send({ - error: 'Task Name, Active status, Task Number are mandatory fields', + error: "Task Name, Active status, Task Number are mandatory fields", }); return; } @@ -442,7 +481,10 @@ const taskController = function (Task) { const modifiedDatetime = Date.now(); const _task = new Task({ - ...task, wbsId, createdDatetime, modifiedDatetime, + ...task, + wbsId, + createdDatetime, + modifiedDatetime, }); const saveTask = _task.save(); @@ -451,22 +493,23 @@ const taskController = function (Task) { return currentwbs.save(); }); - Promise.all([saveTask, saveWbs]).then(results => res.status(201).send(results[0])) + Promise.all([saveTask, saveWbs]) + .then((results) => res.status(201).send(results[0])) .catch((errors) => { res.status(400).send(errors); }); }; const updateNum = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'updateNum')) { + if (!(await hasPermission(req.body.requestor, "updateNum"))) { res .status(403) - .send({ error: 'You are not authorized to create new projects.' }); + .send({ error: "You are not authorized to create new projects." }); return; } if (!req.body.nums) { - res.status(400).send({ error: 'Num is a mandatory fields' }); + res.status(400).send({ error: "Num is a mandatory fields" }); return; } @@ -477,7 +520,7 @@ const taskController = function (Task) { task .save() .then() - .catch(errors => res.status(400).send(errors)); + .catch((errors) => res.status(400).send(errors)); }); // level 2 @@ -487,13 +530,13 @@ const taskController = function (Task) { childTasks1.forEach((childTask1) => { childTask1.num = childTask1.num.replace( childTask1.num.substring(0, elm.num.length), - elm.num, + elm.num ); childTask1 .save() .then(true) - .catch(errors => res.status(400).send(errors)); + .catch((errors) => res.status(400).send(errors)); // level 3 Task.find({ parentId: { $in: [childTask1._id] } }) @@ -502,13 +545,13 @@ const taskController = function (Task) { childTasks2.forEach((childTask2) => { childTask2.num = childTask2.num.replace( childTask2.num.substring(0, childTask1.num.length), - childTask1.num, + childTask1.num ); childTask2 .save() .then(true) - .catch(errors => res.status(400).send(errors)); + .catch((errors) => res.status(400).send(errors)); // level 4 Task.find({ parentId: { $in: [childTask2._id] } }) @@ -518,27 +561,29 @@ const taskController = function (Task) { childTask3.num = childTask3.num.replace( childTask3.num.substring( 0, - childTask2.num.length, + childTask2.num.length ), - childTask2.num, + childTask2.num ); childTask3 .save() .then(true) - .catch(errors => res.status(400).send(errors)); + .catch((errors) => + res.status(400).send(errors) + ); }); } }) - .catch(error => res.status(404).send(error)); + .catch((error) => res.status(404).send(error)); }); } }) - .catch(error => res.status(404).send(error)); + .catch((error) => res.status(404).send(error)); }); } }) - .catch(error => res.status(404).send(error)); + .catch((error) => res.status(404).send(error)); }); res.status(200).send(true); @@ -548,106 +593,113 @@ const taskController = function (Task) { if (!req.body.fromNum || !req.body.toNum) { res .status(400) - .send({ error: 'wbsId, fromNum, toNum are mandatory fields' }); + .send({ error: "wbsId, fromNum, toNum are mandatory fields" }); return; } Task.find({ wbsId: { $in: req.params.wbsId } }).then((tasks) => { - const fromNumArr = req.body.fromNum.replace(/\.0/g, '').split('.'); - const toNumArr = req.body.toNum.replace(/\.0/g, '').split('.'); + const fromNumArr = req.body.fromNum.replace(/\.0/g, "").split("."); + const toNumArr = req.body.toNum.replace(/\.0/g, "").split("."); const changedLvl = fromNumArr.length; const fromLastLvl = parseInt(fromNumArr.pop(), 10); const toLastLvl = parseInt(toNumArr.pop(), 10); - const leadingLvls = fromNumArr.length ? fromNumArr.join('.').concat('.') : ''; // in a format of x, x.x, or x.x.x, also could be '' if move level one tasks + const leadingLvls = fromNumArr.length + ? fromNumArr.join(".").concat(".") + : ""; // in a format of x, x.x, or x.x.x, also could be '' if move level one tasks const changingNums = []; - for (let i = Math.min(fromLastLvl, toLastLvl); i <= Math.max(fromLastLvl, toLastLvl); i += 1) { + for ( + let i = Math.min(fromLastLvl, toLastLvl); + i <= Math.max(fromLastLvl, toLastLvl); + i += 1 + ) { changingNums.push(leadingLvls.concat(`${i}`)); } const changingNumTasks = tasks.filter((task) => { - const taskLeadingNum = task.num.split('.').slice(0, changedLvl).join('.'); + const taskLeadingNum = task.num + .split(".") + .slice(0, changedLvl) + .join("."); return changingNums.includes(taskLeadingNum); }); const queries = []; changingNumTasks.forEach((task) => { - const taskNumArr = task.num.split('.'); + const taskNumArr = task.num.split("."); const taskChanedLvlNum = parseInt(taskNumArr[changedLvl - 1], 10); let newTaskLastLvl; if (fromLastLvl > toLastLvl) { - newTaskLastLvl = taskChanedLvlNum === fromLastLvl ? toLastLvl : taskChanedLvlNum + 1; + newTaskLastLvl = + taskChanedLvlNum === fromLastLvl ? toLastLvl : taskChanedLvlNum + 1; } else { - newTaskLastLvl = taskChanedLvlNum === fromLastLvl ? toLastLvl : taskChanedLvlNum - 1; + newTaskLastLvl = + taskChanedLvlNum === fromLastLvl ? toLastLvl : taskChanedLvlNum - 1; } taskNumArr[changedLvl - 1] = String(newTaskLastLvl); - task.num = taskNumArr.join('.'); + task.num = taskNumArr.join("."); queries.push(task.save()); }); Promise.all(queries) - .then(() => res.status(200).send('Success!')) - .catch(err => res.status(400).send(err)); + .then(() => res.status(200).send("Success!")) + .catch((err) => res.status(400).send(err)); }); }; const deleteTask = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'deleteTask')) { - res - .status(403) - .send({ error: 'You are not authorized to deleteTasks.' }); + if (!(await hasPermission(req.body.requestor, "deleteTask"))) { + res.status(403).send({ error: "You are not authorized to deleteTasks." }); return; } const { taskId } = req.params; const { mother } = req.params; - const removeChildTasks = Task.find( - { - $or: [ - { _id: taskId }, - { parentId1: taskId }, - { parentId2: taskId }, - { parentId3: taskId }, - ], - }, - ) - .then((record) => { - if (!record || record === null || record.length === 0) return res.status(400).send({ error: 'No valid records found' }); - const removeTasks = record.map(rec => rec.remove()); - return removeTasks; + const removeChildTasks = Task.find({ + $or: [ + { _id: taskId }, + { parentId1: taskId }, + { parentId2: taskId }, + { parentId3: taskId }, + ], + }).then((record) => { + if (!record || record === null || record.length === 0) + return res.status(400).send({ error: "No valid records found" }); + const removeTasks = record.map((rec) => rec.remove()); + return removeTasks; }); - const updateMotherChildrenQty = mother !== 'null' - ? Task.findById(mother).then((task) => { - let newQty = 0; - let child = true; - if (task.childrenQty > 0) { - newQty = task.childrenQty - 1; - if (newQty === 0) { - child = false; + const updateMotherChildrenQty = + mother !== "null" + ? Task.findById(mother).then((task) => { + let newQty = 0; + let child = true; + if (task.childrenQty > 0) { + newQty = task.childrenQty - 1; + if (newQty === 0) { + child = false; + } } - } - task.hasChild = child; - task.childrenQty = newQty; - return task.save(); - }) - : Promise.resolve(1); - - Promise - .all([removeChildTasks, updateMotherChildrenQty]) - .then(() => res.status(200).send({ message: 'Task successfully deleted' })) // no need to resetNum(taskId, mother); - .catch(errors => res.status(400).send(errors)); + task.hasChild = child; + task.childrenQty = newQty; + return task.save(); + }) + : Promise.resolve(1); + + Promise.all([removeChildTasks, updateMotherChildrenQty]) + .then(() => + res.status(200).send({ message: "Task successfully deleted" }) + ) // no need to resetNum(taskId, mother); + .catch((errors) => res.status(400).send(errors)); }; const deleteTaskByWBS = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'deleteTask')) { - res - .status(403) - .send({ error: 'You are not authorized to deleteTasks.' }); + if (!(await hasPermission(req.body.requestor, "deleteTask"))) { + res.status(403).send({ error: "You are not authorized to deleteTasks." }); return; } @@ -655,7 +707,7 @@ const taskController = function (Task) { Task.find({ wbsId: { $in: [wbsId] } }, (error, record) => { if (error || !record || record === null || record.length === 0) { - res.status(400).send({ error: 'No valid records found' }); + res.status(400).send({ error: "No valid records found" }); return; } @@ -665,7 +717,9 @@ const taskController = function (Task) { }); Promise.all([...removeTasks]) - .then(() => res.status(200).send({ message: ' Tasks were successfully deleted' })) + .then(() => + res.status(200).send({ message: " Tasks were successfully deleted" }) + ) .catch((errors) => { res.status(400).send(errors); }); @@ -675,8 +729,8 @@ const taskController = function (Task) { }; const updateTask = async (req, res) => { - if (!await hasPermission(req.body.requestor, 'updateTask')) { - res.status(403).send({ error: 'You are not authorized to update Task.' }); + if (!(await hasPermission(req.body.requestor, "updateTask"))) { + res.status(403).send({ error: "You are not authorized to update Task." }); return; } @@ -684,46 +738,46 @@ const taskController = function (Task) { Task.findOneAndUpdate( { _id: mongoose.Types.ObjectId(taskId) }, - { ...req.body, modifiedDatetime: Date.now() }, + { ...req.body, modifiedDatetime: Date.now() } ) .then(() => res.status(201).send()) - .catch(error => res.status(404).send(error)); + .catch((error) => res.status(404).send(error)); }; const swap = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'swapTask')) { + if (!(await hasPermission(req.body.requestor, "swapTask"))) { res .status(403) - .send({ error: 'You are not authorized to create new projects.' }); + .send({ error: "You are not authorized to create new projects." }); return; } if (!req.body.taskId1 || !req.body.taskId2) { res .status(400) - .send({ error: 'taskId1 and taskId2 are mandatory fields' }); + .send({ error: "taskId1 and taskId2 are mandatory fields" }); return; } Task.findById(req.body.taskId1, (error1, task1) => { if (error1 || task1 === null) { - res.status(400).send('No valid records found'); + res.status(400).send("No valid records found"); return; } Task.findById(req.body.taskId2, (error2, task2) => { if (error2 || task2 === null) { - res.status(400).send('No valid records found'); + res.status(400).send("No valid records found"); return; } if (task1.parentId.toString() === task2.parentId.toString()) { - let tmpNum = ''; + let tmpNum = ""; tmpNum = task1.num; task1.num = task2.num; task2.num = tmpNum; } else { - let tmpName = ''; + let tmpName = ""; tmpName = task1.taskName; task1.taskName = task2.taskName; task2.taskName = tmpName; @@ -732,53 +786,62 @@ const taskController = function (Task) { task1 .save() .then() - .catch(errors => res.status(400).send(errors)); + .catch((errors) => res.status(400).send(errors)); task2 .save() .then() - .catch(errors => res.status(400).send(errors)); + .catch((errors) => res.status(400).send(errors)); Task.find({ wbsId: { $in: [task1.wbsId] }, }) - .then(results => res.status(200).send(results)) - .catch(error => res.status(404).send(error)); + .then((results) => res.status(200).send(results)) + .catch((error) => res.status(404).send(error)); }); }); }; const getTaskById = async (req, res) => { try { - const taskId = req.params.id; + const taskId = req.params.id; - // Ensure the task ID is provided - if (!taskId || taskId === 'undefined') { - return res.status(400).send({ error: 'Task ID is missing' }); - } + // Ensure the task ID is provided + if (!taskId || taskId === "undefined") { + return res.status(400).send({ error: "Task ID is missing" }); + } - const task = await Task.findById(taskId, '-__v -createdDatetime -modifiedDatetime'); + const task = await Task.findById( + taskId, + "-__v -createdDatetime -modifiedDatetime" + ); - if (!task) { - return res.status(400).send({ error: 'This is not a valid task' }); - } + if (!task) { + return res.status(400).send({ error: "This is not a valid task" }); + } - const hoursLogged = await timeEntryHelper.getAllHoursLoggedForSpecifiedProject(taskId); - task.set('hoursLogged', hoursLogged, { strict: false }); + const hoursLogged = + await timeEntryHelper.getAllHoursLoggedForSpecifiedProject(taskId); + task.set("hoursLogged", hoursLogged, { strict: false }); - // Fetch the resource names for all resources - const resourceNamesPromises = task.resources.map(resource => taskHelper.getUserProfileFirstAndLastName(resource.userID)); - const resourceNames = await Promise.all(resourceNamesPromises); + // Fetch the resource names for all resources + const resourceNamesPromises = task.resources.map((resource) => + taskHelper.getUserProfileFirstAndLastName(resource.userID) + ); + const resourceNames = await Promise.all(resourceNamesPromises); - // Update the task's resources with the fetched names - task.resources.forEach((resource, index) => { - resource.name = resourceNames[index] !== ' ' ? resourceNames[index] : resource.name; - }); + // Update the task's resources with the fetched names + task.resources.forEach((resource, index) => { + resource.name = + resourceNames[index] !== " " ? resourceNames[index] : resource.name; + }); - res.status(200).send(task); + res.status(200).send(task); } catch (error) { - // Generic error message, you can adjust as needed - res.status(500).send({ error: 'Internal Server Error', details: error.message }); + // Generic error message, you can adjust as needed + res + .status(500) + .send({ error: "Internal Server Error", details: error.message }); } }; @@ -787,39 +850,40 @@ const taskController = function (Task) { try { Task.find({ wbsId: { $in: [wbsId] } }).then((tasks) => { - tasks = tasks.filter(task => task.level === 1); + tasks = tasks.filter((task) => task.level === 1); tasks.forEach((task) => { updateParents(task.wbsId, task.taskId.toString()); }); - res.status(200).send('done'); + res.status(200).send("done"); }); - res.status(200).send('done'); + res.status(200).send("done"); } catch (error) { res.status(400).send(error); } }; const fixTasks = function (req, res) { - res.status(200).send('done'); + res.status(200).send("done"); }; const getTasksByUserId = async (req, res) => { const { userId } = req.params; try { - Task.find({ - 'resources.userID': mongoose.Types.ObjectId(userId), - }, '-resources.profilePic') - .then((results) => { + Task.find( + { + "resources.userID": mongoose.Types.ObjectId(userId), + }, + "-resources.profilePic" + ).then((results) => { WBS.find({ - _id: { $in: results.map(item => item.wbsId) }, + _id: { $in: results.map((item) => item.wbsId) }, }).then((WBSs) => { const resultsWithProjectsIds = results.map((item) => { item.set( - 'projectId', - WBSs?.find( - wbs => wbs._id.toString() === item.wbsId.toString(), - )?.projectId, - { strict: false }, + "projectId", + WBSs?.find((wbs) => wbs._id.toString() === item.wbsId.toString()) + ?.projectId, + { strict: false } ); return item; }); @@ -838,7 +902,9 @@ const taskController = function (Task) { if (teamsData.length > 0) { res.status(200).send(teamsData); } else { - const singleUserData = await taskHelper.getTasksForSingleUser(userId).exec(); + const singleUserData = await taskHelper + .getTasksForSingleUser(userId) + .exec(); res.status(200).send(singleUserData); } } catch (error) { @@ -852,10 +918,10 @@ const taskController = function (Task) { Task.findOneAndUpdate( { _id: mongoose.Types.ObjectId(taskId) }, - { ...req.body, modifiedDatetime: Date.now() }, + { ...req.body, modifiedDatetime: Date.now() } ) .then(() => res.status(201).send()) - .catch(error => res.status(404).send(error)); + .catch((error) => res.status(404).send(error)); }; const getReviewReqEmailBody = function (name, taskName) { @@ -871,9 +937,11 @@ const taskController = function (Task) { const getRecipients = async function (myUserId) { const recipients = []; const user = await UserProfile.findById(myUserId); - const membership = await UserProfile.find({ role: { $in: ['Administrator', 'Manager', 'Mentor'] } }); + const membership = await UserProfile.find({ + role: { $in: ["Administrator", "Manager", "Mentor"] }, + }); membership.forEach((member) => { - if (member.teams.some(team => user.teams.includes(team))) { + if (member.teams.some((team) => user.teams.includes(team))) { recipients.push(member.email); } }); @@ -881,9 +949,7 @@ const taskController = function (Task) { }; const sendReviewReq = async function (req, res) { - const { - myUserId, name, taskName, - } = req.body; + const { myUserId, name, taskName } = req.body; const emailBody = getReviewReqEmailBody(name, taskName); const recipients = await getRecipients(myUserId); @@ -892,12 +958,12 @@ const taskController = function (Task) { recipients, `Review Request from ${name}`, emailBody, - 'highestgoodnetwork@gmail.com', null, + null ); - res.status(200).send('Success'); + res.status(200).send("Success"); } catch (err) { - res.status(500).send('Failed'); + res.status(500).send("Failed"); } };