feat: eliminar proyecto (logico), papelera para superadmin y orden por creacion
- DELETE /api/projects/🆔 eliminacion logica (deleted_at/deleted_by), mismo permiso que editar el proyecto. Popup de confirmacion antes de aplicarla. - Papelera (GET /api/projects?trash=true, POST /:id/restore, DELETE /:id/permanent) restringida al superadmin; el borrado fisico exige que el proyecto ya este en la papelera y arrastra actividades/comentarios/adjuntos por ON DELETE CASCADE. - Dashboard (KPIs, actividades pendientes/calendario, proyectos por estado) excluye proyectos eliminados logicamente. - Columna "Creado" en el listado de proyectos, ordenado por defecto de mas nuevo a mas antiguo (ListView admite initialSort ademas del orden por clic en cabecera que ya tenia). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,13 @@ export async function canWriteProject(user, project) {
|
||||
return repo.isMember(project.id, user.id);
|
||||
}
|
||||
|
||||
export const listProjects = (filters) => repo.findAll(filters);
|
||||
// Solo el superadmin puede ver la papelera (proyectos eliminados logicamente).
|
||||
export async function listProjects(filters, currentUser) {
|
||||
if (filters.trash && currentUser?.roleCode !== ROLES.SUPERADMIN) {
|
||||
throw new HttpError(403, 'Solo el superadministrador puede ver la papelera');
|
||||
}
|
||||
return repo.findAll(filters);
|
||||
}
|
||||
|
||||
export async function getProject(id) {
|
||||
const project = await repo.findById(id);
|
||||
@@ -48,6 +54,35 @@ export async function updateProject(id, payload, currentUser) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
// Eliminacion logica: cualquiera que pueda editar el proyecto puede "eliminarlo" (deja de verse
|
||||
// en el listado, pero sigue en la BD). Solo el superadmin puede restaurarlo o borrarlo definitivo.
|
||||
export async function deleteProject(id, currentUser) {
|
||||
const project = await repo.findById(id);
|
||||
if (!project) throw new HttpError(404, 'Proyecto no encontrado');
|
||||
if (!(await canWriteProject(currentUser, project))) throw new HttpError(403, 'No tienes permisos sobre este proyecto');
|
||||
await repo.softDelete(id, currentUser.id);
|
||||
await logAudit({ userId: currentUser.id, action: 'delete', entityType: 'project', entityId: id });
|
||||
}
|
||||
|
||||
export async function restoreProject(id, currentUser) {
|
||||
if (currentUser.roleCode !== ROLES.SUPERADMIN) throw new HttpError(403, 'Solo el superadministrador puede restaurar proyectos');
|
||||
const project = await repo.findById(id, { includeDeleted: true });
|
||||
if (!project || !project.deleted_at) throw new HttpError(404, 'Proyecto no encontrado en la papelera');
|
||||
await repo.restore(id);
|
||||
await logAudit({ userId: currentUser.id, action: 'restore', entityType: 'project', entityId: id });
|
||||
}
|
||||
|
||||
// Borrado fisico e irreversible (arrastra actividades/comentarios/adjuntos por ON DELETE CASCADE).
|
||||
// Solo el superadmin, y solo sobre un proyecto que ya paso por eliminacion logica primero.
|
||||
export async function permanentlyDeleteProject(id, currentUser) {
|
||||
if (currentUser.roleCode !== ROLES.SUPERADMIN) throw new HttpError(403, 'Solo el superadministrador puede eliminar proyectos definitivamente');
|
||||
const project = await repo.findById(id, { includeDeleted: true });
|
||||
if (!project) throw new HttpError(404, 'Proyecto no encontrado');
|
||||
if (!project.deleted_at) throw new HttpError(400, 'El proyecto debe eliminarse primero antes de poder borrarlo definitivamente');
|
||||
await repo.hardDelete(id);
|
||||
await logAudit({ userId: currentUser.id, action: 'permanent_delete', entityType: 'project', entityId: id });
|
||||
}
|
||||
|
||||
// Recalcula el avance del proyecto como promedio del avance de sus actividades.
|
||||
export async function recalcProjectProgress(projectId) {
|
||||
const avg = await repo.averageActivityProgress(projectId);
|
||||
|
||||
Reference in New Issue
Block a user