From 97b74203c986bfe44ed8ee4c7287ec1a3e0785fe Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 01/44] 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 95015c4ebb613ed81f152a2c67d171f9713eac8d Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 02/44] 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 03/44] 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 04/44] 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 05/44] 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 06/44] 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 ba03b5b00594cace86a084e12897e05b8669dbe7 Mon Sep 17 00:00:00 2001 From: GaryB93 Date: Thu, 5 Oct 2023 17:05:38 -0500 Subject: [PATCH 07/44] 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 08/44] 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 09/44] 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 10/44] 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 11/44] 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 82d29effc13a1202d8b83ed69d3c11f92a5dec7c Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Sun, 15 Oct 2023 12:06:40 -0400 Subject: [PATCH 12/44] 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 cf9d773e07f5d671e1b147af9a56c80ebaef15a2 Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Wed, 1 Nov 2023 12:58:36 -0400 Subject: [PATCH 13/44] 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 14/44] 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 15/44] 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 16/44] 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 17/44] 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 18/44] 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 19/44] 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 20/44] 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 21/44] 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 22/44] 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 23/44] 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 24/44] 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 25/44] 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 b9e28592e96eb5378ff5e071783f0926e4e46e7d Mon Sep 17 00:00:00 2001 From: leonzh2k Date: Thu, 6 Jul 2023 17:55:26 -0400 Subject: [PATCH 26/44] 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 27/44] 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 28/44] 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 29/44] 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 30/44] 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 31/44] 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 32/44] 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 33/44] 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 34/44] 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 35/44] 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 36/44] 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 37/44] 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 38/44] 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 4eb13e71cbc4271f6e4296ca94faf1bcae844e96 Mon Sep 17 00:00:00 2001 From: robertoooc Date: Thu, 9 Nov 2023 18:12:53 -0800 Subject: [PATCH 39/44] adds logic to prevent server from throwing errors when a time entry is edited and not related to task --- src/controllers/timeEntryController.js | 66 ++++++++++++++++---------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index bea25d0a6..962f30170 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -122,6 +122,8 @@ const timeEntrycontroller = function (TimeEntry) { 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; @@ -134,19 +136,19 @@ const timeEntrycontroller = function (TimeEntry) { // 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 (findTask) { + if (initialIsTangible === true) { + findTask.hoursLogged -= (initialSeconds / 3600); + } + + if (req.body.isTangible === true) { + findTask.hoursLogged += (totalSeconds / 3600); + } - if (req.body.isTangible === true) { - const editedTask = await task.findById(req.body.projectId); - editedTask.hoursLogged += (totalSeconds / 3600); - await editedTask.save(); + await findTask.save(); } } catch (error) { - console.log('Failed to find task by id'); + throw new Error(error); } // Update edit history @@ -203,10 +205,13 @@ 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 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 currentTask = await task.findById(req.body.projectId); + checkTaskOvertime(timeEntry, record, currentTask); + } } catch (err) { await session.abortTransaction(); return res.status(400).send({ error: err.toString() }); @@ -271,20 +276,28 @@ 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) { + // 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 { - const currentTask = await task.findById(req.body.projectId); - currentTask.hoursLogged += (timeentry.totalSeconds / 3600); + currentTask.hoursLogged += timeentry.totalSeconds / 3600; await currentTask.save(); } catch (error) { - throw new Error('Failed to find the task by id'); + throw new Error(error); + } + } + + // 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); } } - // 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); }; const getTimeEntriesForSpecifiedPeriod = function (req, res) { @@ -469,8 +482,11 @@ const timeEntrycontroller = function (TimeEntry) { if (record.isTangible === true) { task.findById(record.projectId) .then((currentTask) => { - currentTask.hoursLogged -= (record.totalSeconds / 3600); - currentTask.save(); + // 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.save(); + } }) .catch((error) => { throw new Error(error); From 8a634b4dfdd9339a89d8dae939925a5ac10abb74 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Fri, 10 Nov 2023 09:31:52 -0800 Subject: [PATCH 40/44] 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 54aeac506b0cba57b56fe7999742e135de4642ec Mon Sep 17 00:00:00 2001 From: Oleksandr Riazantsev Date: Mon, 13 Nov 2023 14:36:42 -0500 Subject: [PATCH 41/44] 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 0fbbe6183f908f873b7c170747997954a42e4504 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Tue, 14 Nov 2023 15:22:25 -0800 Subject: [PATCH 42/44] 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 43/44] 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 7542d5c29e6f8657e59e77af12b7b4ee15391477 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Wed, 15 Nov 2023 20:25:45 -0800 Subject: [PATCH 44/44] 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, });