Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions src/controllers/plannedCostController.js
Original file line number Diff line number Diff line change
@@ -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;
40 changes: 40 additions & 0 deletions src/models/plannedCost.js
Original file line number Diff line number Diff line change
@@ -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');
25 changes: 25 additions & 0 deletions src/routes/plannedCostRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const express = require('express');

const routes = function (Cost, Project) {
const controller = require('../controllers/plannedCostController')(Cost, Project);
const CostRouter = express.Router();

// Include /projects prefix in the router paths
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);

CostRouter
.route('/projects/:projectId/planned-costs')
.get(controller.getAllPlannedCostsForProject)
.post(controller.createOrUpdatePlannedCost);

CostRouter
.route('/projects/:projectId/planned-costs/:category')
.delete(controller.deletePlannedCost);

return CostRouter;
};

module.exports = routes;
14 changes: 14 additions & 0 deletions src/startup/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ const templateRouter = require('../routes/templateRouter');
const emailTemplateRouter = require('../routes/emailTemplateRouter');

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 projectCostRouter = require('../routes/bmdashboard/projectCostRouter')(projectCost);

Expand Down Expand Up @@ -479,6 +486,10 @@ module.exports = function (app) {
app.use('/api', toolUtilizationRouter);
// lb dashboard


app.use('/api', toolAvailabilityRouter);
app.use('/api', projectCostTrackingRouter);

app.use('/api/bm', bmIssueRouter);
app.use('/api/bm', bmDashboardRouter);
app.use('/api/bm', bmActualVsPlannedCostRouter);
Expand Down Expand Up @@ -511,6 +522,9 @@ module.exports = function (app) {

app.use('/api/lb', biddingRouter);
app.use('/api', registrationRouter);
app.use('/api', projectMaterialRouter);
app.use('/api', plannedCostRouter(plannedCost, project));


// summary dashboard
app.use('/api/suppliers', supplierPerformanceRouter);
Expand Down
Loading