chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias, dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import * as repo from './repository.js';
|
||||
import { HttpError } from '../../utils/http.js';
|
||||
import { logAudit } from '../../utils/audit.js';
|
||||
|
||||
function toTree(flat) {
|
||||
const byId = new Map(flat.map((d) => [d.id, { ...d, children: [] }]));
|
||||
const roots = [];
|
||||
for (const dept of byId.values()) {
|
||||
if (dept.parent_id && byId.has(dept.parent_id)) {
|
||||
byId.get(dept.parent_id).children.push(dept);
|
||||
} else {
|
||||
roots.push(dept);
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
export async function listTree() {
|
||||
const flat = await repo.findAllFlat();
|
||||
return toTree(flat);
|
||||
}
|
||||
|
||||
export async function createDepartment(payload, actorId) {
|
||||
if (!payload.name) throw new HttpError(400, 'El nombre es obligatorio');
|
||||
const dept = await repo.create(payload);
|
||||
await logAudit({ userId: actorId, action: 'create', entityType: 'department', entityId: dept.id });
|
||||
return dept;
|
||||
}
|
||||
|
||||
export async function updateDepartment(id, payload, actorId) {
|
||||
if (!payload.name) throw new HttpError(400, 'El nombre es obligatorio');
|
||||
if (Number(payload.parentId) === Number(id)) throw new HttpError(400, 'Un departamento no puede ser su propio padre');
|
||||
const dept = await repo.update(id, payload);
|
||||
await logAudit({ userId: actorId, action: 'update', entityType: 'department', entityId: id });
|
||||
return dept;
|
||||
}
|
||||
|
||||
export async function deleteDepartment(id, actorId) {
|
||||
if (await repo.hasChildren(id)) throw new HttpError(400, 'Tiene sub-departamentos; reasignalos antes de eliminar');
|
||||
if (await repo.hasUsers(id)) throw new HttpError(400, 'Tiene usuarios asignados; reasignalos antes de eliminar');
|
||||
await repo.remove(id);
|
||||
await logAudit({ userId: actorId, action: 'delete', entityType: 'department', entityId: id });
|
||||
}
|
||||
Reference in New Issue
Block a user