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
109 changes: 96 additions & 13 deletions src/controllers/department.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import prisma from '../config/prismaClient.js';
* @route GET /api/departments
* @method GET
* @access Private (Admin or Manager)
*/ export const getAllDepartments = async (req, res, next) => {
*/
export const getAllDepartments = async (req, res, next) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = 10;
Expand Down Expand Up @@ -239,33 +240,54 @@ export const updateDepartment = async (req, res, next) => {
});

if (!department) {
return res.status(404).json({ message: 'Department not found' });
return res.status(404).json({
success: false,
message: 'Department not found',
});
}

// Permission check (using the corrected version above)
// Permission check
if (role === 'MANAGER') {
if (department.managerId !== userId) {
if (!department.managerId || department.managerId !== userId) {
return res.status(403).json({
success: false,
message: 'You can only update departments you manage',
});
}
} else if (role === 'ADMIN' || role === 'OWNER') {
const userInOrg = await prisma.user.findFirst({
where: {
id: userId,
organizationId: department.organizationId,
role: { in: ['ADMIN', 'OWNER'] },
departmentId: department.id, // Fixed this line
deletedAt: null,
},
select: {
id: true,
role: true,
organizationId: true,
departmentId: true,
firstName: true,
lastName: true,
email: true,
},
});

if (!userInOrg) {
return res.status(403).json({
message: 'You do not have permission in this organization',
success: false,
message: 'User does not belong to this organization',
});
}

if (userInOrg.role !== 'ADMIN' && userInOrg.role !== 'OWNER') {
return res.status(403).json({
success: false,
message: 'You do not have the required role to perform this action',
});
}
} else {
return res.status(403).json({
success: false,
message: 'You do not have permission to update departments',
});
}
Expand All @@ -283,6 +305,7 @@ export const updateDepartment = async (req, res, next) => {

if (existingDept) {
return res.status(409).json({
success: false,
message: 'Department name already exists in this organization',
});
}
Expand All @@ -305,8 +328,8 @@ export const updateDepartment = async (req, res, next) => {
);
}

// Handle user additions
if (addUsers && addUsers.length > 0) {
// Handle user additions only if addUsers exists and has items
if (addUsers?.length > 0) {
// Verify users exist and belong to same organization
const existingUsers = await prisma.user.findMany({
where: {
Expand All @@ -318,6 +341,7 @@ export const updateDepartment = async (req, res, next) => {

if (existingUsers.length !== addUsers.length) {
return res.status(400).json({
success: false,
message: 'Some users not found or not in the same organization',
});
}
Expand All @@ -330,11 +354,12 @@ export const updateDepartment = async (req, res, next) => {
);
}

// Handle user removals
if (removeUsers && removeUsers.length > 0) {
// Handle user removals only if removeUsers exists and has items
if (removeUsers?.length > 0) {
// Don't allow removing the manager
if (removeUsers.includes(department.managerId)) {
return res.status(400).json({
success: false,
message: 'Cannot remove department manager this way',
});
}
Expand All @@ -350,8 +375,15 @@ export const updateDepartment = async (req, res, next) => {
);
}

// Execute all operations in a transaction
await prisma.$transaction(transaction);
// Only execute transaction if there are operations to perform
if (transaction.length > 0) {
await prisma.$transaction(transaction);
} else {
return res.status(400).json({
success: false,
message: 'No valid fields provided for update',
});
}

// Fetch updated department with all relations
const updatedDepartment = await prisma.department.findUnique({
Expand Down Expand Up @@ -389,3 +421,54 @@ export const updateDepartment = async (req, res, next) => {
next(error);
}
};

/**
* @desc Soft delete a department
* @route DELETE /api/department/:id
* @access Private (Owner or Admin only)
*/
export const softDeleteDepartment = async (req, res, next) => {
try {
const { id } = req.params;

// 1. First check if department exists and isn't deleted
const department = await prisma.department.findUnique({
where: { id },
select: {
id: true,
deletedAt: true,
managerId: true, // Important for maintaining referential integrity
},
});

if (!department) {
return res.status(404).json({
success: false,
message: 'Department not found',
});
}

if (department.deletedAt) {
return res.status(400).json({
success: false,
message: 'Department is already deleted',
});
}

// 2. Perform all operations in a transaction
await prisma.$transaction([
// Finally: Soft delete the department itself
prisma.department.update({
where: { id },
data: { deletedAt: new Date() },
}),
]);

return res.status(200).json({
success: true,
message: 'Department soft deleted successfully',
});
} catch (error) {
next(error);
}
};
12 changes: 12 additions & 0 deletions src/middlewares/verifyOwnerOrAdmin.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const verifyOwnerOrAdmin = (req, res, next) => {
const { role } = req.user;

if (role === 'OWNER' || role === 'ADMIN') {
return next();
}

return res.status(403).json({
success: false,
message: 'Only organization owners or admins can perform this action',
});
};
9 changes: 9 additions & 0 deletions src/routes/department.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createDepartment,
getAllDepartments,
getDepartmentById,
softDeleteDepartment,
updateDepartment,
} from '../controllers/department.controller.js';
import { verifyManagerPermission } from '../middlewares/verifyManagerPermission.middleware.js';
Expand All @@ -11,6 +12,7 @@ import {
validateCreateDepartment,
validateUpdateDepartment,
} from '../validations/department.validation.js';
import { verifyOwnerOrAdmin } from '../middlewares/verifyOwnerOrAdmin.middleware.js';

const router = express.Router();

Expand Down Expand Up @@ -44,4 +46,11 @@ router.put(
validateUpdateDepartment,
updateDepartment,
);

router.delete(
'/api/department/:id',
verifyAccessToken,
verifyOwnerOrAdmin,
softDeleteDepartment,
);
export default router;