import * as repo from './repository.js'; import * as activitiesRepo from '../activities/repository.js'; import * as projectsRepo from '../projects/repository.js'; import { canWriteActivity, recalcFromTasks } from '../activities/service.js'; import { canReadProject } from '../projects/service.js'; import { HttpError } from '../../utils/http.js'; import { requireEditVersion, requireUpdated } from '../../utils/editVersion.js'; import { logAudit } from '../../utils/audit.js'; async function requireActivityAccess(activityId, currentUser) { const activity = await activitiesRepo.findById(activityId); if (!activity) throw new HttpError(404, 'Actividad no encontrada'); const project = await projectsRepo.findById(activity.project_id); if (!(await canWriteActivity(currentUser, project, activity))) { throw new HttpError(403, 'No tienes permisos sobre esta actividad'); } return activity; } export async function listByActivity(activityId, currentUser) { const activity = await activitiesRepo.findById(activityId); if (!activity) throw new HttpError(404, 'Actividad no encontrada'); const project = await projectsRepo.findById(activity.project_id); if (!(await canReadProject(currentUser, project))) throw new HttpError(404, 'Actividad no encontrada'); return repo.findByActivity(activityId); } export async function listTrash(activityId, currentUser) { await requireActivityAccess(activityId, currentUser); return repo.findTrashByActivity(activityId); } export async function createTask(activityId, payload, currentUser) { await requireActivityAccess(activityId, currentUser); if (!payload.name) throw new HttpError(400, 'El nombre es obligatorio'); const task = await repo.create(activityId, payload); await logAudit({ userId: currentUser.id, action: 'create', entityType: 'task', entityId: task.id }); const counts = await repo.countsByActivity(activityId); await recalcFromTasks(activityId, counts.done, counts.total); return task; } export async function updateTask(id, payload, currentUser) { const task = await repo.findById(id); if (!task) throw new HttpError(404, 'Tarea no encontrada'); await requireActivityAccess(task.activity_id, currentUser); if (!payload.name) throw new HttpError(400, 'El nombre es obligatorio'); requireEditVersion(payload.editVersion); const updated = requireUpdated(await repo.update(id, payload)); await logAudit({ userId: currentUser.id, action: 'update', entityType: 'task', entityId: id }); return updated; } export async function setTaskDone(id, isDone, currentUser) { const task = await repo.findById(id); if (!task) throw new HttpError(404, 'Tarea no encontrada'); await requireActivityAccess(task.activity_id, currentUser); const updated = await repo.setDone(id, isDone); await logAudit({ userId: currentUser.id, action: isDone ? 'complete' : 'reopen', entityType: 'task', entityId: id }); const counts = await repo.countsByActivity(task.activity_id); await recalcFromTasks(task.activity_id, counts.done, counts.total); return updated; } // Nuevo orden manual (arrastrar con el mouse): mismo permiso que crear/editar tareas de la actividad. export async function reorderTasks(activityId, ids, currentUser) { await requireActivityAccess(activityId, currentUser); if (!Array.isArray(ids) || ids.length === 0) throw new HttpError(400, 'Falta el orden de tareas'); await repo.reorder(activityId, ids); return repo.findByActivity(activityId); } export async function removeTask(id, currentUser) { const task = await repo.findById(id); if (!task) throw new HttpError(404, 'Tarea no encontrada'); await requireActivityAccess(task.activity_id, currentUser); await repo.softDelete(id, currentUser.id); await logAudit({ userId: currentUser.id, action: 'delete', entityType: 'task', entityId: id }); const counts = await repo.countsByActivity(task.activity_id); await recalcFromTasks(task.activity_id, counts.done, counts.total); } export async function restoreTask(id, currentUser) { const task = await repo.findById(id, { includeDeleted: true }); if (!task || !task.deleted_at) throw new HttpError(404, 'Tarea no encontrada en la papelera'); await requireActivityAccess(task.activity_id, currentUser); await repo.restore(id); await logAudit({ userId: currentUser.id, action: 'restore', entityType: 'task', entityId: id }); const counts = await repo.countsByActivity(task.activity_id); await recalcFromTasks(task.activity_id, counts.done, counts.total); }