227 lines
8.5 KiB
JavaScript
227 lines
8.5 KiB
JavaScript
"use strict";
|
|
/**
|
|
* ConceptoController - Controller de conceptos de obra
|
|
*
|
|
* Endpoints REST para gestión del catálogo de conceptos.
|
|
*
|
|
* @module Budgets
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createConceptoController = createConceptoController;
|
|
const express_1 = require("express");
|
|
const concepto_service_1 = require("../services/concepto.service");
|
|
const auth_middleware_1 = require("../../auth/middleware/auth.middleware");
|
|
const auth_service_1 = require("../../auth/services/auth.service");
|
|
const concepto_entity_1 = require("../entities/concepto.entity");
|
|
const user_entity_1 = require("../../core/entities/user.entity");
|
|
const tenant_entity_1 = require("../../core/entities/tenant.entity");
|
|
const refresh_token_entity_1 = require("../../auth/entities/refresh-token.entity");
|
|
/**
|
|
* Crear router de conceptos
|
|
*/
|
|
function createConceptoController(dataSource) {
|
|
const router = (0, express_1.Router)();
|
|
// Repositorios
|
|
const conceptoRepository = dataSource.getRepository(concepto_entity_1.Concepto);
|
|
const userRepository = dataSource.getRepository(user_entity_1.User);
|
|
const tenantRepository = dataSource.getRepository(tenant_entity_1.Tenant);
|
|
const refreshTokenRepository = dataSource.getRepository(refresh_token_entity_1.RefreshToken);
|
|
// Servicios
|
|
const conceptoService = new concepto_service_1.ConceptoService(conceptoRepository);
|
|
const authService = new auth_service_1.AuthService(userRepository, tenantRepository, refreshTokenRepository);
|
|
const authMiddleware = new auth_middleware_1.AuthMiddleware(authService, dataSource);
|
|
// Helper para crear contexto de servicio
|
|
const getContext = (req) => {
|
|
if (!req.tenantId) {
|
|
throw new Error('Tenant ID is required');
|
|
}
|
|
return {
|
|
tenantId: req.tenantId,
|
|
userId: req.user?.sub,
|
|
};
|
|
};
|
|
/**
|
|
* GET /conceptos
|
|
* Listar conceptos raíz
|
|
*/
|
|
router.get('/', authMiddleware.authenticate, async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const page = parseInt(req.query.page) || 1;
|
|
const limit = Math.min(parseInt(req.query.limit) || 50, 100);
|
|
const result = await conceptoService.findRootConceptos(getContext(req), page, limit);
|
|
res.status(200).json({
|
|
success: true,
|
|
data: result.data,
|
|
pagination: result.meta,
|
|
});
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* GET /conceptos/search
|
|
* Buscar conceptos por código o nombre
|
|
*/
|
|
router.get('/search', authMiddleware.authenticate, async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const term = req.query.q;
|
|
if (!term || term.length < 2) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Search term must be at least 2 characters' });
|
|
return;
|
|
}
|
|
const limit = Math.min(parseInt(req.query.limit) || 20, 50);
|
|
const conceptos = await conceptoService.search(getContext(req), term, limit);
|
|
res.status(200).json({ success: true, data: conceptos });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* GET /conceptos/tree
|
|
* Obtener árbol de conceptos
|
|
*/
|
|
router.get('/tree', authMiddleware.authenticate, async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const rootId = req.query.rootId;
|
|
const tree = await conceptoService.getConceptoTree(getContext(req), rootId);
|
|
res.status(200).json({ success: true, data: tree });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* GET /conceptos/:id
|
|
* Obtener concepto por ID
|
|
*/
|
|
router.get('/:id', authMiddleware.authenticate, async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const concepto = await conceptoService.findById(getContext(req), req.params.id);
|
|
if (!concepto) {
|
|
res.status(404).json({ error: 'Not Found', message: 'Concept not found' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, data: concepto });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* GET /conceptos/:id/children
|
|
* Obtener hijos de un concepto
|
|
*/
|
|
router.get('/:id/children', authMiddleware.authenticate, async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const children = await conceptoService.findChildren(getContext(req), req.params.id);
|
|
res.status(200).json({ success: true, data: children });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* POST /conceptos
|
|
* Crear concepto
|
|
*/
|
|
router.post('/', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer', 'director'), async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const dto = req.body;
|
|
if (!dto.code || !dto.name) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'code and name are required' });
|
|
return;
|
|
}
|
|
// Verificar código único
|
|
const exists = await conceptoService.codeExists(getContext(req), dto.code);
|
|
if (exists) {
|
|
res.status(409).json({ error: 'Conflict', message: 'Concept code already exists' });
|
|
return;
|
|
}
|
|
const concepto = await conceptoService.createConcepto(getContext(req), dto);
|
|
res.status(201).json({ success: true, data: concepto });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* PATCH /conceptos/:id
|
|
* Actualizar concepto
|
|
*/
|
|
router.patch('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer', 'director'), async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const dto = req.body;
|
|
const concepto = await conceptoService.update(getContext(req), req.params.id, dto);
|
|
if (!concepto) {
|
|
res.status(404).json({ error: 'Not Found', message: 'Concept not found' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, data: concepto });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
/**
|
|
* DELETE /conceptos/:id
|
|
* Eliminar concepto (soft delete)
|
|
*/
|
|
router.delete('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'director'), async (req, res, next) => {
|
|
try {
|
|
const tenantId = req.tenantId;
|
|
if (!tenantId) {
|
|
res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
|
|
return;
|
|
}
|
|
const deleted = await conceptoService.softDelete(getContext(req), req.params.id);
|
|
if (!deleted) {
|
|
res.status(404).json({ error: 'Not Found', message: 'Concept not found' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, message: 'Concept deleted' });
|
|
}
|
|
catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
return router;
|
|
}
|
|
exports.default = createConceptoController;
|
|
//# sourceMappingURL=concepto.controller.js.map
|