From df10cce04fdf0c5aa9734d05eb3808ce2a5c02dd Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Thu, 5 Oct 2023 19:40:00 +0800 Subject: [PATCH 1/6] 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 bc3d082a9959c527e202841f87840d836e957064 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Thu, 19 Oct 2023 06:43:15 +0800 Subject: [PATCH 2/6] 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 b568f900b655b8a2654eee9d3ed0ca3f3ea4e137 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 6 Nov 2023 14:40:30 +0800 Subject: [PATCH 3/6] 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 bd537f79f38c980eea9ba1660e6b89aff577ece8 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 13 Nov 2023 10:52:54 +0800 Subject: [PATCH 4/6] 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 8009191ffa3dd7333bd0d84b8162e3d231bd8440 Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Tue, 14 Nov 2023 15:28:08 +0800 Subject: [PATCH 5/6] 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 a418eaefcde0b3a65d2e4caae0aa7cd02258adcf Mon Sep 17 00:00:00 2001 From: AriaYu927 Date: Mon, 20 Nov 2023 11:45:18 +0800 Subject: [PATCH 6/6] 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' }); }