Files
project.linkedcorp.net/backend/src/modules/activities/repository.js
T
Milton Gonzalez 5e0bc0809e feat: eliminar actividad, bloqueado si tiene tareas pendientes
No existia forma de borrar una actividad. Agregado DELETE
/api/activities/:id con boton "Eliminar" en el listado (popup de
confirmacion). Si tiene tareas, se bloquea con 400 y mensaje claro en
vez de arrastrar todo en cascada sin avisar.

De paso: la logica de borrar adjunto (fila + archivo en disco) se
extrajo a un attachments/service.js reutilizable, usado tanto por el
borrado individual como por el borrado en cascada de una actividad
(evita archivos huerfanos). Comentarios de la actividad tambien se
limpian (sin FK fisica cruzada, entity_type/entity_id es polimorfico).

Probado en dev con curl (bloquea con tarea, permite sin tareas, 404
tras borrar) y Playwright (mensaje de advertencia visible).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 12:30:53 -05:00

109 lines
4.1 KiB
JavaScript

import { pool } from '../../config/db.js';
const LIST_SELECT = `
SELECT a.id, a.project_id, a.code, a.name, a.description, a.responsible_user_id, u.name AS responsible_name,
a.priority_id, pr.name AS priority_name, pr.color AS priority_color,
a.status_id, st.name AS status_name, st.color AS status_color, st.is_closed AS status_is_closed,
a.start_date, a.due_date, a.actual_end_date, a.progress_pct, a.is_milestone,
a.estimated_hours, a.created_by, a.created_at, a.updated_at
FROM activities a
JOIN priorities pr ON pr.id = a.priority_id
JOIN activity_statuses st ON st.id = a.status_id
LEFT JOIN users u ON u.id = a.responsible_user_id
`;
export async function findByProject(projectId) {
const [rows] = await pool.query(`${LIST_SELECT} WHERE a.project_id = ? ORDER BY a.start_date, a.id`, [projectId]);
return rows;
}
export async function findById(id) {
const [rows] = await pool.query(`${LIST_SELECT} WHERE a.id = ?`, [id]);
return rows[0] || null;
}
export async function create(projectId, data) {
const [result] = await pool.query(
`INSERT INTO activities
(project_id, code, name, description, responsible_user_id, priority_id, status_id,
start_date, due_date, is_milestone, estimated_hours, created_by)
VALUES (?, 'PENDING', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
projectId, data.name, data.description || null, data.responsibleUserId || null,
data.priorityId, data.statusId, data.startDate || null, data.dueDate || null,
data.isMilestone ? 1 : 0, data.estimatedHours || null, data.createdBy,
]
);
const id = result.insertId;
const code = `ACT-${String(id).padStart(4, '0')}`;
await pool.query('UPDATE activities SET code = ? WHERE id = ?', [code, id]);
return findById(id);
}
export async function update(id, data) {
await pool.query(
`UPDATE activities SET
name = ?, description = ?, responsible_user_id = ?, priority_id = ?, status_id = ?,
start_date = ?, due_date = ?, actual_end_date = ?, is_milestone = ?, estimated_hours = ?
WHERE id = ?`,
[
data.name, data.description || null, data.responsibleUserId || null, data.priorityId, data.statusId,
data.startDate || null, data.dueDate || null, data.actualEndDate || null,
data.isMilestone ? 1 : 0, data.estimatedHours || null, id,
]
);
return findById(id);
}
export async function updateProgress(id, progressPct) {
await pool.query('UPDATE activities SET progress_pct = ? WHERE id = ?', [progressPct, id]);
}
// Arrastra activity_dependencies (ambos lados) y tasks por ON DELETE CASCADE.
export async function remove(id) {
await pool.query('DELETE FROM activities WHERE id = ?', [id]);
}
export async function countTasks(id) {
const [rows] = await pool.query('SELECT COUNT(*) AS c FROM tasks WHERE activity_id = ?', [id]);
return rows[0].c;
}
export async function listDependencies(id) {
const [rows] = await pool.query(
`SELECT d.depends_on_activity_id AS id, a.code, a.name, a.status_id, st.is_closed AS status_is_closed
FROM activity_dependencies d
JOIN activities a ON a.id = d.depends_on_activity_id
JOIN activity_statuses st ON st.id = a.status_id
WHERE d.activity_id = ?`,
[id]
);
return rows;
}
export async function addDependency(activityId, dependsOnActivityId) {
await pool.query(
'INSERT IGNORE INTO activity_dependencies (activity_id, depends_on_activity_id) VALUES (?, ?)',
[activityId, dependsOnActivityId]
);
return listDependencies(activityId);
}
export async function removeDependency(activityId, dependsOnActivityId) {
await pool.query('DELETE FROM activity_dependencies WHERE activity_id = ? AND depends_on_activity_id = ?', [
activityId, dependsOnActivityId,
]);
return listDependencies(activityId);
}
export async function findDueOrOverdue(userId, { onlyMine = false } = {}) {
const clauses = ["st.is_closed = 0", "a.due_date IS NOT NULL"];
const params = [];
if (onlyMine) { clauses.push('a.responsible_user_id = ?'); params.push(userId); }
const [rows] = await pool.query(
`${LIST_SELECT} WHERE ${clauses.join(' AND ')} ORDER BY a.due_date`,
params
);
return rows;
}