From 233acb853744e37f887b57c1317b2ab0e6965040 Mon Sep 17 00:00:00 2001 From: saikrishnaraoyadagiri Date: Wed, 3 Sep 2025 17:57:44 -0400 Subject: [PATCH 1/2] feat: add donut chart route and controller for planned cost breakdown --- src/controllers/plannedCostController.js | 220 +++++++++++++++++++++++ src/models/plannedCost.js | 40 +++++ src/routes/plannedCostRouter.js | 25 +++ src/startup/routes.js | 19 +- 4 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 src/controllers/plannedCostController.js create mode 100644 src/models/plannedCost.js create mode 100644 src/routes/plannedCostRouter.js diff --git a/src/controllers/plannedCostController.js b/src/controllers/plannedCostController.js new file mode 100644 index 000000000..260abf69d --- /dev/null +++ b/src/controllers/plannedCostController.js @@ -0,0 +1,220 @@ +const mongoose = require('mongoose'); + +const plannedCostController = (PlannedCost, Project) => { + const getPlannedCostBreakdown = async (req, res) => { + console.log('[Controller] GET /planned-cost-breakdown hit, projectId:', req.params.projectId); + + try { + const { projectId } = req.params; + console.log('[Controller] Requested projectId:', projectId, 'Type:', typeof projectId); + + if (!mongoose.Types.ObjectId.isValid(projectId)) { + return res.status(400).send({ error: 'Invalid project ID' }); + } + + const project = await Project.findById(projectId); + if (!project) return res.status(404).send({ error: 'Project not found' }); + console.log('[Controller] Found project:', project.projectName); + + console.log('[Controller] Querying PlannedCost with projectId:', projectId); + console.log('[Controller] PlannedCost model collection name:', PlannedCost.collection.name); + + // Try to find planned costs with both ObjectId and string versions of projectId + let plannedCosts = await PlannedCost.find({ projectId }); + console.log('[Controller] Found planned costs with ObjectId query:', plannedCosts); + + // If no results, try with string version + if (plannedCosts.length === 0) { + console.log('[Controller] No results with ObjectId, trying string query...'); + plannedCosts = await PlannedCost.find({ projectId: projectId.toString() }); + console.log('[Controller] Found planned costs with string query:', plannedCosts); + } + + // If still no results, try with both ObjectId and string in the same query + if (plannedCosts.length === 0) { + console.log('[Controller] No results with string, trying OR query...'); + plannedCosts = await PlannedCost.find({ + $or: [{ projectId }, { projectId: projectId.toString() }], + }); + console.log('[Controller] Found planned costs with OR query:', plannedCosts); + } + + // If still no results, try a more direct approach + if (plannedCosts.length === 0) { + console.log('[Controller] No results with any query, trying direct collection access...'); + const { db } = PlannedCost; + const collection = db.collection('plannedCosts'); + plannedCosts = await collection.find({ projectId: projectId.toString() }).toArray(); + console.log('[Controller] Found planned costs with direct collection query:', plannedCosts); + } + + const total = plannedCosts.reduce((sum, c) => sum + c.plannedCost, 0); + console.log('[Controller] Total calculated:', total); + + const breakdown = {}; + plannedCosts.forEach((c) => { + console.log('[Controller] Processing category:', c.category, 'cost:', c.plannedCost); + console.log( + '[Controller] Category type:', + typeof c.category, + 'Cost type:', + typeof c.plannedCost, + ); + + // Sum up costs for the same category instead of overwriting + if (breakdown[c.category]) { + breakdown[c.category] += c.plannedCost; + console.log( + '[Controller] Added to existing category. New total for', + c.category, + ':', + breakdown[c.category], + ); + } else { + breakdown[c.category] = c.plannedCost; + console.log('[Controller] New category added:', c.category, '=', c.plannedCost); + } + }); + console.log('[Controller] Final breakdown:', breakdown); + + // Also try to query with different approaches to debug + console.log('[Controller] Trying alternative query...'); + const allPlannedCosts = await PlannedCost.find({}); + console.log('[Controller] All planned costs in DB:', allPlannedCosts.length); + if (allPlannedCosts.length > 0) { + console.log( + '[Controller] Sample planned cost document:', + JSON.stringify(allPlannedCosts[0], null, 2), + ); + } + + res.status(200).send({ projectId, projectName: project.projectName, total, breakdown }); + } catch (err) { + console.error(err); + res.status(500).send({ error: 'Internal server error' }); + } + }; + + const getAllPlannedCostsForProject = async (req, res) => { + console.log('[Controller] GET /planned-costs hit, projectId:', req.params.projectId); + + try { + const { projectId } = req.params; + + if (!mongoose.Types.ObjectId.isValid(projectId)) { + return res.status(400).send({ error: 'Invalid project ID' }); + } + + const project = await Project.findById(projectId); + if (!project) return res.status(404).send({ error: 'Project not found' }); + + // Try to find planned costs with both ObjectId and string versions of projectId + let plannedCosts = await PlannedCost.find({ projectId }); + if (plannedCosts.length === 0) { + plannedCosts = await PlannedCost.find({ projectId: projectId.toString() }); + } + if (plannedCosts.length === 0) { + plannedCosts = await PlannedCost.find({ + $or: [{ projectId }, { projectId: projectId.toString() }], + }); + } + + // Group by category and sum up costs + const groupedCosts = {}; + plannedCosts.forEach((cost) => { + if (groupedCosts[cost.category]) { + groupedCosts[cost.category] += cost.plannedCost; + } else { + groupedCosts[cost.category] = cost.plannedCost; + } + }); + + res.status(200).send({ + projectId, + projectName: project.projectName, + plannedCosts, + groupedCosts, + }); + } catch (err) { + console.error(err); + res.status(500).send({ error: 'Internal server error' }); + } + }; + + const createOrUpdatePlannedCost = async (req, res) => { + console.log('[Controller] POST /planned-costs hit, projectId:', req.params.projectId); + + try { + const { projectId } = req.params; + const { category, plannedCost } = req.body; + + if (!mongoose.Types.ObjectId.isValid(projectId)) { + return res.status(400).send({ error: 'Invalid project ID' }); + } + + if (!category || !plannedCost) { + return res.status(400).send({ error: 'Category and plannedCost are required' }); + } + + const validCategories = ['Plumbing', 'Electrical', 'Structural', 'Mechanical']; + if (!validCategories.includes(category)) { + return res.status(400).send({ error: 'Invalid category' }); + } + + const project = await Project.findById(projectId); + if (!project) return res.status(404).send({ error: 'Project not found' }); + + const result = await PlannedCost.findOneAndUpdate( + { projectId, category }, + { plannedCost }, + { upsert: true, new: true }, + ); + + res.status(200).send({ message: 'Planned cost updated successfully', data: result }); + } catch (err) { + console.error(err); + res.status(500).send({ error: 'Internal server error' }); + } + }; + + const deletePlannedCost = async (req, res) => { + console.log( + '[Controller] DELETE /planned-costs hit, projectId:', + req.params.projectId, + 'category:', + req.params.category, + ); + + try { + const { projectId, category } = req.params; + + if (!mongoose.Types.ObjectId.isValid(projectId)) { + return res.status(400).send({ error: 'Invalid project ID' }); + } + + const validCategories = ['Plumbing', 'Electrical', 'Structural', 'Mechanical']; + if (!validCategories.includes(category)) { + return res.status(400).send({ error: 'Invalid category' }); + } + + const result = await PlannedCost.findOneAndDelete({ projectId, category }); + if (!result) { + return res.status(404).send({ error: 'Planned cost not found' }); + } + + res.status(200).send({ message: 'Planned cost deleted successfully', data: result }); + } catch (err) { + console.error(err); + res.status(500).send({ error: 'Internal server error' }); + } + }; + + return { + getPlannedCostBreakdown, + getAllPlannedCostsForProject, + createOrUpdatePlannedCost, + deletePlannedCost, + }; +}; + +module.exports = plannedCostController; diff --git a/src/models/plannedCost.js b/src/models/plannedCost.js new file mode 100644 index 000000000..9d8f8ecb1 --- /dev/null +++ b/src/models/plannedCost.js @@ -0,0 +1,40 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const plannedCostSchema = new Schema({ + projectId: { + type: Schema.Types.ObjectId, + ref: 'project', + required: true, + }, + category: { + type: String, + enum: ['Plumbing', 'Electrical', 'Structural', 'Mechanical'], + required: true, + }, + plannedCost: { + type: Number, + required: true, + min: 0, + }, + createdDatetime: { + type: Date, + default: Date.now, + }, + modifiedDatetime: { + type: Date, + default: Date.now, + }, +}); + +// Compound index to ensure unique combination of projectId and category +plannedCostSchema.index({ projectId: 1, category: 1 }, { unique: true }); + +// Update modifiedDatetime on save +plannedCostSchema.pre('save', function (next) { + this.modifiedDatetime = new Date(); + next(); +}); + +module.exports = mongoose.model('plannedCost', plannedCostSchema, 'plannedCosts'); diff --git a/src/routes/plannedCostRouter.js b/src/routes/plannedCostRouter.js new file mode 100644 index 000000000..4db4142a5 --- /dev/null +++ b/src/routes/plannedCostRouter.js @@ -0,0 +1,25 @@ +const express = require('express'); + +const routes = function (PlannedCost, Project) { + const controller = require('../controllers/plannedCostController')(PlannedCost, Project); + const plannedCostRouter = express.Router(); + + // Include /projects prefix in the router paths + plannedCostRouter.route('/projects/:projectId/planned-cost-breakdown').get((req, res, next) => { + console.log('[Router] Route hit for planned-cost-breakdown, projectId:', req.params.projectId); + next(); + }, controller.getPlannedCostBreakdown); + + plannedCostRouter + .route('/projects/:projectId/planned-costs') + .get(controller.getAllPlannedCostsForProject) + .post(controller.createOrUpdatePlannedCost); + + plannedCostRouter + .route('/projects/:projectId/planned-costs/:category') + .delete(controller.deletePlannedCost); + + return plannedCostRouter; +}; + +module.exports = routes; diff --git a/src/startup/routes.js b/src/startup/routes.js index b03ef75db..56697258e 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -215,8 +215,6 @@ const weeklySummaryEmailAssignmentRouter = require('../routes/WeeklySummaryEmail userProfile, ); - - // Automations const appAccessRouter = require('../routes/automation/appAccessRouter'); const dropboxRouter = require('../routes/automation/dropboxRouter'); @@ -226,7 +224,6 @@ const slackRouter = require('../routes/automation/slackRouter'); //lbdashboard_bidoverview - const bidPropertyRouter = require('../routes/lbdashboard/bidPropertyRouter')(bidoverview_Listing); const userBidRouter = require('../routes/lbdashboard/userBidNotificationRouter')( bidoverview_Bid, @@ -235,7 +232,6 @@ const userBidRouter = require('../routes/lbdashboard/userBidNotificationRouter') bidoverview_Notification, ); - //commnunity portal const cpNoShowRouter = require('../routes/CommunityPortal/NoshowVizRouter')(); @@ -246,10 +242,18 @@ const registrationRouter = require('../routes/registrationRouter')(registration) const templateRouter = require('../routes/templateRouter'); const projectMaterialRouter = require('../routes/projectMaterialroutes'); +console.log('Loading plannedCost model...'); +const plannedCost = require('../models/plannedCost'); +console.log('PlannedCost model loaded:', plannedCost ? 'success' : 'failed'); + +console.log('Loading plannedCostRouter...'); +const plannedCostRouter = require('../routes/plannedCostRouter'); +console.log('PlannedCostRouter loaded:', plannedCostRouter ? 'success' : 'failed'); const tagRouter = require('../routes/tagRouter')(tag); module.exports = function (app) { + console.log('Routes function called - registering planned cost routes...'); app.use('/api', forgotPwdRouter); app.use('/api', loginRouter); app.use('/api', forcePwdRouter); @@ -334,7 +338,6 @@ module.exports = function (app) { app.use('/api', toolAvailabilityRouter); // lb dashboard - app.use('/api/bm', bmIssueRouter); app.use('/api/bm', bmDashboardRouter); app.use('/api/bm', bmActualVsPlannedCostRouter); @@ -356,9 +359,9 @@ module.exports = function (app) { app.use('/api', registrationRouter); app.use('/api', projectMaterialRouter); - app.use('/api/bm', bmRentalChart); - app.use('/api/lb', lbWishlistsRouter); - app.use('/api', projectMaterialRouter); + console.log('Registering planned cost router...'); + app.use('/api', plannedCostRouter(plannedCost, project)); + console.log('Planned cost router registered successfully'); app.use('/api/bm', bmRentalChart); app.use('/api/lb', lbWishlistsRouter); }; From d68dffca89880896a9e7092b0979b4c6e714085c Mon Sep 17 00:00:00 2001 From: saikrishnaraoyadagiri Date: Fri, 14 Nov 2025 18:49:08 -0500 Subject: [PATCH 2/2] fix db issue --- src/routes/plannedCostRouter.js | 14 +++++++------- src/startup/db.js | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/routes/plannedCostRouter.js b/src/routes/plannedCostRouter.js index 4db4142a5..1b2c97791 100644 --- a/src/routes/plannedCostRouter.js +++ b/src/routes/plannedCostRouter.js @@ -1,25 +1,25 @@ const express = require('express'); -const routes = function (PlannedCost, Project) { - const controller = require('../controllers/plannedCostController')(PlannedCost, Project); - const plannedCostRouter = express.Router(); +const routes = function (Cost, Project) { + const controller = require('../controllers/plannedCostController')(Cost, Project); + const CostRouter = express.Router(); // Include /projects prefix in the router paths - plannedCostRouter.route('/projects/:projectId/planned-cost-breakdown').get((req, res, next) => { + CostRouter.route('/projects/:projectId/planned-cost-breakdown').get((req, res, next) => { console.log('[Router] Route hit for planned-cost-breakdown, projectId:', req.params.projectId); next(); }, controller.getPlannedCostBreakdown); - plannedCostRouter + CostRouter .route('/projects/:projectId/planned-costs') .get(controller.getAllPlannedCostsForProject) .post(controller.createOrUpdatePlannedCost); - plannedCostRouter + CostRouter .route('/projects/:projectId/planned-costs/:category') .delete(controller.deletePlannedCost); - return plannedCostRouter; + return CostRouter; }; module.exports = routes; diff --git a/src/startup/db.js b/src/startup/db.js index 4dae8a6b9..6179284bb 100644 --- a/src/startup/db.js +++ b/src/startup/db.js @@ -36,7 +36,7 @@ const afterConnect = async () => { }; module.exports = function () { - const uri = `mongodb://${process.env.user}:${encodeURIComponent(process.env.password)}@${process.env.cluster}/${process.env.dbName}?ssl=true&replicaSet=${process.env.replicaSetName}&authSource=admin&retryWrites=false`; + const uri = `mongodb+srv://${process.env.user}:${encodeURIComponent(process.env.password)}@${process.env.cluster}/${process.env.dbName}?retryWrites=true&w=majority&appName=${process.env.appName}`; mongoose .connect(uri, { @@ -46,4 +46,4 @@ module.exports = function () { }) .then(afterConnect) .catch((err) => logger.logException(err)); -}; \ No newline at end of file +};