Skip to content
Open
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions src/controllers/bmdashboard/bmMaterialsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,55 @@ const bmMaterialsController = function (BuildingMaterial) {
}
};

const bmApplyMaterialBulkAction = async function (req, res) {
const { materialIds, action, notes } = req.body;

if (!Array.isArray(materialIds) || materialIds.length === 0) {
return res.status(400).send('At least one material id is required.');
}

if (!materialIds.every((id) => mongoose.Types.ObjectId.isValid(id))) {
return res.status(400).send('One or more material ids are invalid.');
}

if (!['hold', 'review', 'notes'].includes(action)) {
return res.status(400).send('Invalid bulk action.');
}

const update = {};

if (action === 'hold') {
update.$set = { stockHold: true };
}

if (action === 'review') {
update.$set = { isReviewed: true };
}

if (action === 'notes') {
const trimmedNotes = typeof notes === 'string' ? notes.trim() : '';
if (!trimmedNotes) {
return res.status(400).send('Notes content is required for notes action.');
}
update.$set = { notes: trimmedNotes };
}

try {
const result = await BuildingMaterial.updateMany(
{
_id: { $in: materialIds },
},
update,
);

return res.status(200).send({
result: `Applied '${action}' to ${result.modifiedCount} material records.`,

@AdiDubbs AdiDubbs Aug 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This project uses Mongoose 5.13.23, where updateMany returns nModified rather than modifiedCount. This therefore returns "undefined material records," which is the issue other reviewers have reproduced in the paired frontend.

Please use nModified, or a compatibility fallback if this endpoint needs to support a future Mongoose upgrade.

});
} catch (error) {
return res.status(500).send(error);
}
};

const bmupdatePurchaseStatus = async function (req, res) {
const { purchaseId, status, quantity } = req.body;
try {
Expand Down Expand Up @@ -370,6 +419,7 @@ const bmMaterialsController = function (BuildingMaterial) {
bmMaterialsList,
bmPostMaterialUpdateRecord,
bmPostMaterialUpdateBulk,
bmApplyMaterialBulkAction,
bmPurchaseMaterials,
bmupdatePurchaseStatus,
bmGetMaterialSummaryByProject,
Expand Down
3 changes: 3 additions & 0 deletions src/models/bmdashboard/buildingMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const buildingMaterial = new Schema({
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)
stockHold: { type: Boolean, default: false },
isReviewed: { type: Boolean, default: false },
notes: { type: String, default: '' },
purchaseRecord: [
{
date: { type: Date, default: Date.now() },
Expand Down
2 changes: 2 additions & 0 deletions src/routes/bmdashboard/bmMaterialsRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const routes = function (buildingMaterial) {

materialsRouter.route('/updateMaterialRecordBulk').post(controller.bmPostMaterialUpdateBulk);

materialsRouter.route('/materials/bulk-actions').post(controller.bmApplyMaterialBulkAction);

@AdiDubbs AdiDubbs Aug 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can bulk-modify any materials but has no permission check - any authenticated user can call it. Most controllers here gate with hasPermission(req.body.requestor, ...). I think this might need to be restricted.

@kunchalasireesha kunchalasireesha Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bulk-actions endpoint sits behind the same JWT auth as the rest of /api/bm. None of the existing materials write endpoints (bmPurchaseMaterials, bmPostMaterialUpdateBulk, bmupdatePurchaseStatus) use hasPermission, and there's no materials permission string in the seed yet. Adding a proper manageMaterials permission across the BM controllers is worth doing as its own PR rather than bolting it onto just this endpoint.


materialsRouter.route('/updateMaterialStatus').post(controller.bmupdatePurchaseStatus);

materialsRouter.route('/materials/:projectId').get(controller.bmGetMaterialSummaryByProject);
Expand Down
Loading