Skip to content

Commit be86987

Browse files
Merge pull request #2171 from OneCommunityGlobal/development
Backend Release to Main [2.98]
2 parents 86edb70 + 8b09961 commit be86987

24 files changed

Lines changed: 1014 additions & 184 deletions

package-lock.json

Lines changed: 0 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,4 @@
140140
"/src/**/*"
141141
]
142142
}
143-
}
143+
}

src/controllers/badgeController.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const UserProfile = require('../models/userProfile');
33
const helper = require('../utilities/permissions');
44
const escapeRegex = require('../utilities/escapeRegex');
55
const cacheClosure = require('../utilities/nodeCache');
6-
// const userHelper = require('../helpers/userHelper')();
6+
const userHelper = require('../helpers/userHelper')();
77

88
const badgeController = function (Badge) {
99
/**
@@ -13,10 +13,10 @@ const badgeController = function (Badge) {
1313
*/
1414
const cache = cacheClosure();
1515

16-
// const awardBadgesTest = async function (req, res) {
17-
// await userHelper.awardNewBadges();
18-
// res.status(200).send('Badges awarded');
19-
// };
16+
const awardNewBadges = async function (req, res) {
17+
await userHelper.awardNewBadges();
18+
res.status(200).send('Badges awarded');
19+
};
2020

2121
const getAllBadges = async function (req, res) {
2222
// console.log(req.body.requestor); // Retain logging from development branch for debugging
@@ -341,7 +341,7 @@ const badgeController = function (Badge) {
341341
};
342342

343343
return {
344-
// awardBadgesTest,
344+
awardNewBadges,
345345
getAllBadges,
346346
assignBadges,
347347
postBadge,

src/controllers/bmdashboard/__tests__/bmInventoryTypeController.test.js

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ const mockEquipType = {
3333
exec: jest.fn(),
3434
};
3535

36+
const mockInvTypeHistory = {
37+
find: jest.fn(),
38+
create: jest.fn(),
39+
exec: jest.fn(),
40+
};
3641
const mockInvType = {
3742
find: jest.fn(),
3843
findById: jest.fn(),
@@ -64,6 +69,7 @@ describe('Building Materials Inventory Controller', () => {
6469
mockReusType,
6570
mockToolType,
6671
mockEquipType,
72+
mockInvTypeHistory,
6773
);
6874

6975
req = {
@@ -173,36 +179,4 @@ describe('Building Materials Inventory Controller', () => {
173179
expect(res.send).toHaveBeenCalledWith(mockTools);
174180
});
175181
});
176-
177-
describe('updateNameAndUnit', () => {
178-
it('should update name and unit successfully', async () => {
179-
req.params = { invtypeId: 'inv123' };
180-
req.body = { name: 'Updated Name', unit: 'Updated Unit' };
181-
182-
const updatedDoc = { ...req.body, _id: 'inv123' };
183-
mockInvType.findByIdAndUpdate.mockResolvedValue(updatedDoc);
184-
185-
await controller.updateNameAndUnit(req, res);
186-
187-
expect(mockInvType.findByIdAndUpdate).toHaveBeenCalledWith(
188-
'inv123',
189-
{ name: 'Updated Name', unit: 'Updated Unit' },
190-
{ new: true, runValidators: true },
191-
);
192-
expect(res.status).toHaveBeenCalledWith(200);
193-
expect(res.json).toHaveBeenCalledWith(updatedDoc);
194-
});
195-
196-
it('should handle non-existent inventory type', async () => {
197-
req.params = { invtypeId: 'nonexistent' };
198-
req.body = { name: 'Updated Name' };
199-
200-
mockInvType.findByIdAndUpdate.mockResolvedValue(null);
201-
202-
await controller.updateNameAndUnit(req, res);
203-
204-
expect(res.status).toHaveBeenCalledWith(404);
205-
expect(res.json).toHaveBeenCalledWith({ error: 'invType Material not found check Id' });
206-
});
207-
});
208182
});

src/controllers/bmdashboard/bmInventoryTypeController.js

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const fs = require('fs');
22
const fsPromises = require('fs/promises');
33
const path = require('path');
4+
const mongoose = require('mongoose');
45

56
const filename = 'BuildingUnits.json';
67
const currentFilePath = __filename;
@@ -9,7 +10,15 @@ const filepath = path.join(rootPath, filename);
910
const { readFile } = fs;
1011
const { writeFile } = fs;
1112

12-
function bmInventoryTypeController(InvType, MatType, ConsType, ReusType, ToolType, EquipType) {
13+
function bmInventoryTypeController(
14+
InvType,
15+
MatType,
16+
ConsType,
17+
ReusType,
18+
ToolType,
19+
EquipType,
20+
invTypeHistory,
21+
) {
1322
async function fetchMaterialTypes(req, res) {
1423
try {
1524
MatType.find()
@@ -398,22 +407,98 @@ function bmInventoryTypeController(InvType, MatType, ConsType, ReusType, ToolTyp
398407
};
399408

400409
const updateNameAndUnit = async (req, res) => {
401-
const { invtypeId } = req.params;
402-
const { name, unit } = req.body;
403-
404410
try {
405-
const updatedInvType = await InvType.findByIdAndUpdate(
406-
invtypeId,
407-
{ name, unit },
408-
{ new: true, runValidators: true },
409-
);
410-
411-
if (!updatedInvType) {
412-
return res.status(404).json({ error: 'invType Material not found check Id' });
411+
const { invtypeId } = req.params;
412+
const {
413+
name,
414+
unit,
415+
type: rawType,
416+
requestor: { requestorId },
417+
} = req.body;
418+
const historyDocs = [];
419+
const updateData = {};
420+
// Selection of Collection depending on Type
421+
const allowedTypes = ['Material', 'Consumable'];
422+
const itemTtype = allowedTypes.includes(rawType) ? rawType : 'Inventory';
423+
424+
// Validate invtypeId
425+
if (!mongoose.Types.ObjectId.isValid(invtypeId)) {
426+
return res.status(400).json({ message: 'Invalid inventory type ID' });
427+
}
428+
// Sanitize name
429+
const safeName = String(name).trim();
430+
if (!safeName) {
431+
return res.status(400).json({ message: 'Invalid inventory name' });
432+
}
433+
// Extract and sanitize
434+
const safeUnit = String(unit).trim();
435+
if (!safeUnit || safeUnit.length > 50) {
436+
return res.status(400).json({ message: 'Invalid unit value' });
437+
}
438+
439+
let CollectionName = InvType;
440+
if (itemTtype === 'Material') {
441+
CollectionName = MatType;
442+
} else if (itemTtype === 'Consumable') {
443+
CollectionName = ConsType;
444+
}
445+
446+
// Fetch existing document
447+
const invType = await CollectionName.findById(invtypeId);
448+
if (!invType) {
449+
return res.status(404).send('Inventory type not found check Id');
450+
}
451+
452+
// Perform query using sanitized values
453+
const existingInvType = await CollectionName.findOne({
454+
name: safeName,
455+
_id: { $ne: mongoose.Types.ObjectId(invtypeId) },
456+
});
457+
458+
if (existingInvType) {
459+
return res.status(409).json({
460+
message: 'Inventory type name already exists',
461+
});
462+
}
463+
464+
// Track name change
465+
if (safeName && safeName !== invType.name) {
466+
historyDocs.push({
467+
invtypeId,
468+
field: 'name',
469+
oldValue: invType.name,
470+
newValue: safeName,
471+
editedBy: requestorId,
472+
});
473+
updateData.name = safeName;
474+
}
475+
476+
// Track unit change
477+
if (safeUnit && safeUnit !== invType.unit) {
478+
historyDocs.push({
479+
invtypeId,
480+
field: 'unit',
481+
oldValue: invType.unit,
482+
newValue: safeUnit,
483+
editedBy: requestorId,
484+
});
485+
updateData.unit = safeUnit;
486+
}
487+
488+
// Save history (if any)
489+
if (historyDocs.length > 0) {
490+
await invTypeHistory.insertMany(historyDocs);
413491
}
414492

493+
// Update main document
494+
const updatedInvType = await CollectionName.findByIdAndUpdate(invtypeId, updateData, {
495+
new: true,
496+
runValidators: true,
497+
});
498+
415499
res.status(200).json(updatedInvType);
416500
} catch (error) {
501+
console.error(error);
417502
res.status(500).send(error);
418503
}
419504
};
@@ -638,6 +723,26 @@ function bmInventoryTypeController(InvType, MatType, ConsType, ReusType, ToolTyp
638723
}
639724
};
640725

726+
const fetchInvTypeHistory = async (req, res) => {
727+
try {
728+
const { invtypeId } = req.params;
729+
const safeInvTypeId = new mongoose.Types.ObjectId(invtypeId);
730+
if (!mongoose.Types.ObjectId.isValid(invtypeId)) {
731+
return res.status(400).json({ message: 'Invalid inventory type id' });
732+
}
733+
734+
const history = await invTypeHistory
735+
.find({ invtypeId: safeInvTypeId })
736+
.populate('editedBy', '_id firstName lastName email')
737+
.sort({ editedAt: -1 })
738+
.lean();
739+
740+
res.status(200).json(history);
741+
} catch (error) {
742+
console.error('Fetch history error:', error);
743+
res.status(500).json({ message: 'Failed to fetch inventory history' });
744+
}
745+
};
641746
return {
642747
fetchMaterialTypes,
643748
fetchConsumableTypes,
@@ -657,6 +762,7 @@ function bmInventoryTypeController(InvType, MatType, ConsType, ReusType, ToolTyp
657762
deleteInvUnit,
658763
updateSingleInvType,
659764
deleteSingleInvType,
765+
fetchInvTypeHistory,
660766
};
661767
}
662768

0 commit comments

Comments
 (0)