diff --git a/src/controllers/bmdashboard/bmInventoryTypeController.js b/src/controllers/bmdashboard/bmInventoryTypeController.js index cd9b8cde6..7416c8aef 100644 --- a/src/controllers/bmdashboard/bmInventoryTypeController.js +++ b/src/controllers/bmdashboard/bmInventoryTypeController.js @@ -1,15 +1,5 @@ -const fs = require('fs'); -const fsPromises = require('fs/promises'); -const path = require('path'); const mongoose = require('mongoose'); -const filename = 'BuildingUnits.json'; -const currentFilePath = __filename; -const rootPath = path.resolve(path.dirname(currentFilePath), '../../../'); // Go up three levels to the root -const filepath = path.join(rootPath, filename); -const { readFile } = fs; -const { writeFile } = fs; - function bmInventoryTypeController( InvType, MatType, @@ -17,6 +7,7 @@ function bmInventoryTypeController( ReusType, ToolType, EquipType, + BuildingUnit, invTypeHistory, ) { async function fetchMaterialTypes(req, res) { @@ -78,23 +69,10 @@ function bmInventoryTypeController( const fetchInvUnitsFromJson = async (req, res) => { try { - // console.log(__dirname,filepath) - readFile(filepath, 'utf8', (err, data) => { - if (err) { - console.error('Error reading file:', err); - res.status(500).send(err); - } - - try { - const jsonData = JSON.parse(data); - res.status(200).send(jsonData); - } catch (parseError) { - console.error('Error parsing JSON:', parseError); - res.status(500).send(parseError); - } - }); + const units = await BuildingUnit.find().exec(); + res.status(200).send(units); } catch (err) { - res.json(err); + res.status(500).json({ error: err.message }); } }; @@ -119,31 +97,19 @@ function bmInventoryTypeController( createdBy: requestorId, }; MatType.create(newDoc) - .then((results) => { + .then(async (results) => { res.status(201).send(results); if (req.body.customUnit) { try { - // Add new unit to json file : src\controllers\bmdashboard\BuildingUnits.json - const newItem = { unit: req.body.customUnit, category: 'Material' }; - const newItemString = JSON.stringify(newItem, null, 2); - readFile(filepath, 'utf8', (err, data) => { - if (err) { - console.error('Error reading file:', err); - return; - } - // Remove the last array bracket and comma - const updatedContent = data.trim().replace(/\s*]$/, ''); - - // Add a comma and newline if the file is not empty - const separator = updatedContent !== '' ? ',\n' : ''; - const updatedFileContent = `${updatedContent}${separator}${newItemString}\n]`; - - writeFile(filepath, updatedFileContent, 'utf8', (error) => { - if (error) { - console.error('Error writing to file:', error); - } - }); + const exists = await BuildingUnit.findOne({ + unit: { $regex: new RegExp(`^${req.body.customUnit}$`, 'i') }, }); + if (!exists) { + await BuildingUnit.create({ + unit: req.body.customUnit, + category: 'Material', + }); + } } catch (e) { console.log(e); } @@ -276,6 +242,74 @@ function bmInventoryTypeController( } } + async function addReusableType(req, res) { + const { + name, + description, + invoice, + purchaseRental, + fromDate, + toDate, + condition, + phoneNumber, + quantity, + currency, + unitPrice, + shippingFee, + taxes, + totalPriceWithShipping, + images, + link, + requestor: { requestorId }, + } = req.body; + + try { + ReusType.find({ name }) + .then((result) => { + if (result.length) { + res.status(409).send('Oops!! Reusable already exists!'); + } else { + const newDoc = { + category: 'Reusable', + name, + description, + invoice, + purchaseRental, + fromDate, + toDate, + condition, + phoneNumber, + quantity, + currency, + unitPrice, + shippingFee, + taxes, + totalPriceWithShipping, + images, + link, + createdBy: requestorId, + }; + ReusType.create(newDoc) + .then((results) => { + res.status(201).send(results); + }) + .catch((error) => { + if (error._message.includes('validation failed')) { + res.status(400).send(error.errors.unit.message); + } else { + res.status(500).send(error); + } + }); + } + }) + .catch((error) => { + res.status(500).send(error); + }); + } catch (error) { + res.status(500).send(error); + } + } + async function fetchInventoryByType(req, res) { const { type } = req.params; let SelectedType = InvType; @@ -361,41 +395,6 @@ function bmInventoryTypeController( } } - async function addReusableType(req, res) { - const { - name, - description, - requestor: { requestorId }, - } = req.body; - try { - ReusType.find({ name }) - .then((result) => { - if (result.length) { - res.status(409).send(); - } else { - const newDoc = { - category: 'Reusable', - name, - description, - createdBy: requestorId, - }; - ReusType.create(newDoc) - .then(() => res.status(201).send()) - .catch((error) => { - if (error._message.includes('validation failed')) { - res.status(400).send(error); - } else { - res.status(500).send(error); - } - }); - } - }) - .catch((error) => res.status(500).send(error)); - } catch (error) { - res.status(500).send(error); - } - } - const fetchSingleInventoryType = async (req, res) => { const { invtypeId } = req.params; try { @@ -503,60 +502,53 @@ function bmInventoryTypeController( } }; - const addInvUnit = async (req, res) => { - // NOTE: category is default to be Material as no other item types need units - const { unit, category = 'Material' } = req.body; - if (typeof unit !== 'string' || unit.length === 0) { - res.status(400).json('Invalid unit'); - return; - } - + const addInventoryUnit = async (req, res) => { try { - // read JSON file and parse it into an array - const unitsJSON = await fsPromises.readFile(filepath, { encoding: 'utf8' }); - const unitsArray = JSON.parse(unitsJSON); + const { unit, category } = req.body; - // append new unit into array - unitsArray.push({ unit, category }); + if (!unit) { + return res.status(400).json({ error: 'Unit is required' }); + } - // save updated array into JSON file and rend it back - await fsPromises.writeFile(filepath, JSON.stringify(unitsArray, null, ' ')); + const exists = await BuildingUnit.findOne({ + unit: { $regex: new RegExp(`^${unit}$`, 'i') }, + }); + if (exists) { + return res.status(409).json({ error: 'Unit already exists' }); + } - res.status(201).send(unitsArray); - } catch (err) { - res.status(500).send(err); - console.error(err); + const newUnit = await BuildingUnit.create({ unit, category: category || 'Material' }); + res.status(201).json(newUnit); + } catch (error) { + res.status(500).json({ error: error.message }); } }; - const deleteInvUnit = async (req, res) => { - const { unit } = req.body; - if (typeof unit !== 'string' || unit.length === 0) { - res.status(400).json('Invalid unit'); - return; - } - + const deleteInventoryUnit = async (req, res) => { try { - // read JSON file and parse it into an array - const unitsJSON = await fsPromises.readFile(filepath, { encoding: 'utf8' }); - const unitsArray = JSON.parse(unitsJSON); - - // if unit does not exist, send err response - const index = unitsArray.findIndex((unitObject) => unitObject.unit === unit); - if (index === -1) { - res.status(400).json('Unit does not exist'); - return; + const { unitName } = req.params; + + if (!unitName) { + return res.status(400).json({ error: 'Unit identifier is required' }); } - // otherwise, remove unit - const filteredUnits = unitsArray.filter((unitObject) => unitObject.unit !== unit); + let deleted; + if (unitName.match(/^[0-9a-fA-F]{24}$/)) { + deleted = await BuildingUnit.findByIdAndDelete(unitName); + } else { + const decodedUnitName = decodeURIComponent(unitName); + deleted = await BuildingUnit.findOneAndDelete({ + unit: { $regex: new RegExp(`^${decodedUnitName}$`, 'i') }, + }); + } - // save updated array into JSON file and rend it back - await fsPromises.writeFile(filepath, JSON.stringify(filteredUnits, null, ' ')); - res.status(200).send(filteredUnits); - } catch (err) { - res.status(500).send(err); - console.error(err); + if (!deleted) { + return res.status(404).json({ error: 'Unit not found' }); + } + + res.status(200).json({ message: 'Unit deleted successfully' }); + } catch (error) { + res.status(500).json({ error: error.message }); } }; @@ -743,6 +735,7 @@ function bmInventoryTypeController( res.status(500).json({ message: 'Failed to fetch inventory history' }); } }; + return { fetchMaterialTypes, fetchConsumableTypes, @@ -758,8 +751,8 @@ function bmInventoryTypeController( updateNameAndUnit, fetchInvUnitsFromJson, fetchInventoryByType, - addInvUnit, - deleteInvUnit, + addInventoryUnit, + deleteInventoryUnit, updateSingleInvType, deleteSingleInvType, fetchInvTypeHistory, diff --git a/src/helpers/__tests__/checkPersonalMax.spec.js b/src/helpers/__tests__/checkPersonalMax.spec.js index 81c74d871..83bc6c388 100644 --- a/src/helpers/__tests__/checkPersonalMax.spec.js +++ b/src/helpers/__tests__/checkPersonalMax.spec.js @@ -34,7 +34,7 @@ jest.mock('../../utilities/nodeCache', () => () => ({ jest.mock('../../startup/logger', () => ({ logException: jest.fn(), })); -jest.mock('puppeteer', () => ({})); +jest.mock('../../utilities/playwrightUtil', () => jest.fn()); jest.mock('sharp', () => ({})); /* ======================= diff --git a/src/models/bmdashboard/buildingUnit.js b/src/models/bmdashboard/buildingUnit.js new file mode 100644 index 000000000..33ff82bd1 --- /dev/null +++ b/src/models/bmdashboard/buildingUnit.js @@ -0,0 +1,10 @@ +const mongoose = require('mongoose'); + +const { Schema } = mongoose; + +const buildingUnitSchema = new Schema({ + unit: { type: String, required: true, unique: true }, + category: { type: String, default: 'Material' }, +}); + +module.exports = mongoose.model('buildingUnit', buildingUnitSchema, 'buildingUnits'); diff --git a/src/routes/bmdashboard/bmInventoryTypeRouter.js b/src/routes/bmdashboard/bmInventoryTypeRouter.js index 2c57438bb..2b98a33cb 100644 --- a/src/routes/bmdashboard/bmInventoryTypeRouter.js +++ b/src/routes/bmdashboard/bmInventoryTypeRouter.js @@ -7,6 +7,7 @@ const routes = function ( reusType, toolType, equipType, + buildingUnit, invTypeHistory, ) { const inventoryTypeRouter = express.Router(); @@ -17,6 +18,7 @@ const routes = function ( reusType, toolType, equipType, + buildingUnit, invTypeHistory, ); @@ -31,7 +33,7 @@ const routes = function ( inventoryTypeRouter.route('/tools').post(controller.addToolType); - inventoryTypeRouter.route('/invtypes/reusables').post(controller.addReusableType); + inventoryTypeRouter.route('/invtypes/reusable').post(controller.addReusableType); inventoryTypeRouter.route('/invtypes/tools').get(controller.fetchToolTypes); @@ -58,8 +60,9 @@ const routes = function ( inventoryTypeRouter .route('/inventoryUnits') .get(controller.fetchInvUnitsFromJson) - .post(controller.addInvUnit) - .delete(controller.deleteInvUnit); + .post(controller.addInventoryUnit); + + inventoryTypeRouter.route('/inventoryUnits/:unitName').delete(controller.deleteInventoryUnit); return inventoryTypeRouter; }; diff --git a/src/startup/routes.js b/src/startup/routes.js index 8aa323ff7..bc1625076 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -88,6 +88,7 @@ const { buildingTool, buildingEquipment, } = require('../models/bmdashboard/buildingInventoryItem'); +const buildingUnit = require('../models/bmdashboard/buildingUnit'); const dashboardMetrics = require('../models/bmdashboard/dashboardMetrics'); const bmTimeLog = require('../models/bmdashboard/buildingTimeLogger'); @@ -225,6 +226,7 @@ const bmInventoryTypeRouter = require('../routes/bmdashboard/bmInventoryTypeRout reusableType, toolType, equipmentType, + buildingUnit, invTypeHistory, );