diff --git a/package-lock.json b/package-lock.json index b8971dd1b..c7f8c9ccc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1277,6 +1277,7 @@ "node_modules/@babel/core": { "version": "7.28.5", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3979,6 +3980,7 @@ "node_modules/@redis/client": { "version": "1.6.1", "license": "MIT", + "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -4946,6 +4948,7 @@ "node_modules/@types/node-fetch": { "version": "2.6.13", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "form-data": "^4.0.4" @@ -5080,6 +5083,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5897,6 +5901,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7129,6 +7134,7 @@ "version": "8.57.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7274,6 +7280,7 @@ "version": "2.32.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7325,6 +7332,7 @@ "version": "6.10.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -7353,6 +7361,7 @@ "version": "7.37.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -7384,6 +7393,7 @@ "version": "4.6.2", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -8059,6 +8069,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index cd61bf077..0502c5825 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -219,6 +219,51 @@ 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 (!['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.`, + }); + } catch (error) { + return res.status(500).send(error); + } + }; + const bmupdatePurchaseStatus = async function (req, res) { const { purchaseId, status, quantity } = req.body; try { @@ -370,6 +415,7 @@ const bmMaterialsController = function (BuildingMaterial) { bmMaterialsList, bmPostMaterialUpdateRecord, bmPostMaterialUpdateBulk, + bmApplyMaterialBulkAction, bmPurchaseMaterials, bmupdatePurchaseStatus, bmGetMaterialSummaryByProject, diff --git a/src/models/bmdashboard/buildingMaterial.js b/src/models/bmdashboard/buildingMaterial.js index 1a4301651..469d3bd62 100644 --- a/src/models/bmdashboard/buildingMaterial.js +++ b/src/models/bmdashboard/buildingMaterial.js @@ -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() }, diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index ed96c8a77..1c21d71d5 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -14,6 +14,8 @@ const routes = function (buildingMaterial) { materialsRouter.route('/updateMaterialRecordBulk').post(controller.bmPostMaterialUpdateBulk); + materialsRouter.route('/materials/bulk-actions').post(controller.bmApplyMaterialBulkAction); + materialsRouter.route('/updateMaterialStatus').post(controller.bmupdatePurchaseStatus); materialsRouter.route('/materials/:projectId').get(controller.bmGetMaterialSummaryByProject);