diff --git a/src/controladores/controlador_Membresias.js b/src/controladores/controlador_Clientes_Membresias.js similarity index 100% rename from src/controladores/controlador_Membresias.js rename to src/controladores/controlador_Clientes_Membresias.js diff --git a/src/controladores/controlador_Miembros.js b/src/controladores/controlador_Clientes_Miembros.js similarity index 100% rename from src/controladores/controlador_Miembros.js rename to src/controladores/controlador_Clientes_Miembros.js diff --git a/src/controladores/controlador_Clientes_MiembrosVisitas.js b/src/controladores/controlador_Clientes_MiembrosVisitas.js new file mode 100644 index 0000000..84c32fd --- /dev/null +++ b/src/controladores/controlador_Clientes_MiembrosVisitas.js @@ -0,0 +1,342 @@ +// src/controladores/controlador_Clientes_MiembrosVisitas.js + +// NO NECESITARÁS 'moment-timezone' NI CÁLCULOS MANUALES COMPLEJOS +// const moment = require('moment-timezone'); + +// ¡IMPORTANTE: Cambia el nombre de esta constante para que coincida con la importación en rt_clientes.js! +const controlador_Clientes_MiembrosVisitas = { + + /** + * @function registrarVisita + * @description Registra una nueva visita de miembro en la tabla clientes_miembros_visitas. + * @param {Object} req - Objeto de solicitud de Express. + * @param {Object} res - Objeto de respuesta de Express. + */ + registrarVisita: (req, res) => { + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para registrar visita:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const { + id_miembro, + matricula_registrada, + nombre_area_acceso, + estado_acceso, // 'CONCEDIDO' o 'DENEGADO' + motivo_denegacion, + registrado_por, + observaciones + } = req.body; + + // Validación básica de los campos requeridos + if (!id_miembro || !matricula_registrada || !nombre_area_acceso || !estado_acceso) { + return res.status(400).json({ mensaje: 'Faltan campos obligatorios para registrar la visita (id_miembro, matricula_registrada, nombre_area_acceso, estado_acceso).' }); + } + + // Asegúrate de que estado_acceso sea uno de los valores ENUM permitidos + if (!['CONCEDIDO', 'DENEGADO'].includes(estado_acceso.toUpperCase())) { + return res.status(400).json({ mensaje: 'El estado de acceso debe ser "CONCEDIDO" o "DENEGADO".' }); + } + + // --- SIMPLIFICACIÓN: Usar la fecha nativa de JavaScript --- + const now = new Date(); // Esto obtendrá la fecha y hora local del servidor (Quito) + + // Formatear la fecha y hora a 'YYYY-MM-DD HH:mm:ss' para MySQL + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); // getMonth() es 0-indexado + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + + const fecha_hora_visita = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + // --------------------------------------------------- + + const query = ` + INSERT INTO clientes_miembros_visitas + (id_miembro, matricula_registrada, fecha_hora_visita, nombre_area_acceso, estado_acceso, motivo_denegacion, registrado_por, observaciones) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `; + const values = [ + id_miembro, + matricula_registrada, + fecha_hora_visita, // Usa la fecha formateada + nombre_area_acceso, + estado_acceso.toUpperCase(), // Asegura que se guarde en mayúsculas + motivo_denegacion || null, + registrado_por || null, + observaciones || null + ]; + + connection.query(query, values, (err, result) => { + if (err) { + console.error('Error al registrar visita:', err); + if (err.code === 'ER_NO_REFERENCED_ROW_2' || err.code === 'ER_NO_REFERENCED_ROW') { + return res.status(400).json({ mensaje: 'El ID de miembro especificado no existe. Asegúrate de que el miembro esté registrado en el sistema.', error: err.message }); + } + return res.status(500).json({ mensaje: 'Error interno del servidor al registrar la visita', error: err.message }); + } + + if (result.affectedRows === 1) { + res.status(201).json({ + mensaje: 'Visita registrada exitosamente.', + id_visita: result.insertId, + datos_visita: { + id_miembro, + matricula_registrada, + nombre_area_acceso, + estado_acceso, + fecha_hora_visita // Devuelve la fecha formateada + } + }); + } else { + res.status(500).json({ mensaje: 'Error al registrar la visita: no se afectó ninguna fila.' }); + } + }); + }); + }, + + /** + * @function listarVisitas + * @description Lista todos los registros de visitas. + * @param {Object} req - Objeto de solicitud de Express. + * @param {Object} res - Objeto de respuesta de Express. + */ + listarVisitas: (req, res) => { + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar visitas:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cv.id_visita, + cv.id_miembro, + cm.matricula AS matricula_miembro, + cli.client_nombre AS nombre_cliente, + cv.matricula_registrada, + cv.fecha_hora_visita, + cv.nombre_area_acceso, + cv.estado_acceso, + cv.motivo_denegacion, + cv.registrado_por, + cv.observaciones, + cv.fecha_creacion + FROM + clientes_miembros_visitas cv + JOIN + clientes_miembros cm ON cv.id_miembro = cm.id_miembro + JOIN + clientes cli ON cm.client_id = cli.client_id + ORDER BY cv.fecha_hora_visita DESC + `; + + connection.query(query, (err, rows) => { + if (err) { + console.error('Error al listar visitas:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar visitas', error: err.message }); + } + res.json(rows); + }); + }); + }, + + /** + * @function obtenerVisitaPorId + * @description Obtiene una visita específica por su ID. + * @param {Object} req - Objeto de solicitud de Express (req.params.id_visita). + * @param {Object} res - Objeto de respuesta de Express. + */ + obtenerVisitaPorId: (req, res) => { + const { id_visita } = req.params; + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para visita por ID:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cv.id_visita, + cv.id_miembro, + cm.matricula AS matricula_miembro, + cli.client_nombre AS nombre_cliente, + cv.matricula_registrada, + cv.fecha_hora_visita, + cv.nombre_area_acceso, + cv.estado_acceso, + cv.motivo_denegacion, + cv.registrado_por, + cv.observaciones, + cv.fecha_creacion + FROM + clientes_miembros_visitas cv + JOIN + clientes_miembros cm ON cv.id_miembro = cm.id_miembro + JOIN + clientes cli ON cm.client_id = cli.client_id + WHERE cv.id_visita = ? + `; + + connection.query(query, [id_visita], (err, rows) => { + if (err) { + console.error('Error al obtener visita por ID:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener visita', error: err.message }); + } + if (rows.length === 0) { + return res.status(404).json({ mensaje: 'Visita no encontrada' }); + } + res.json(rows[0]); + }); + }); + }, + + /** + * @function listarVisitasPorMiembro + * @description Lista el historial de visitas para un miembro específico por su id_miembro. + * @param {Object} req - Objeto de solicitud de Express (req.params.id_miembro). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarVisitasPorMiembro: (req, res) => { + const { id_miembro } = req.params; + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar visitas por miembro:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cv.id_visita, + cv.matricula_registrada, + cv.fecha_hora_visita, + cv.nombre_area_acceso, + cv.estado_acceso, + cv.motivo_denegacion, + cv.registrado_por, + cv.observaciones + FROM + clientes_miembros_visitas cv + WHERE cv.id_miembro = ? + ORDER BY cv.fecha_hora_visita DESC + `; + + connection.query(query, [id_miembro], (err, rows) => { + if (err) { + console.error('Error al listar visitas por miembro:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar visitas por miembro', error: err.message }); + } + if (rows.length === 0) { + return res.status(404).json({ mensaje: 'No se encontraron visitas para el miembro con el ID proporcionado.' }); + } + res.json(rows); + }); + }); + }, + + /** + * @function listarVisitasPorEstadoAcceso + * @description Lista las visitas filtradas por un estado de acceso específico (CONCEDIDO/DENEGADO). + * @param {Object} req - Objeto de solicitud de Express (req.params.estado_acceso). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarVisitasPorEstadoAcceso: (req, res) => { + const { estado_acceso } = req.params; + // Validar que el estado sea uno de los permitidos por el ENUM + const estadosPermitidos = ['CONCEDIDO', 'DENEGADO']; + if (!estadosPermitidos.includes(estado_acceso.toUpperCase())) { + return res.status(400).json({ mensaje: `El estado de acceso proporcionado '${estado_acceso}' no es válido. Los estados permitidos son: ${estadosPermitidos.join(', ')}.` }); + } + + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar visitas por estado de acceso:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cv.id_visita, + cv.id_miembro, + cm.matricula AS matricula_miembro, + cli.client_nombre AS nombre_cliente, + cv.matricula_registrada, + cv.fecha_hora_visita, + cv.nombre_area_acceso, + cv.estado_acceso, + cv.motivo_denegacion + FROM + clientes_miembros_visitas cv + JOIN + clientes_miembros cm ON cv.id_miembro = cm.id_miembro + JOIN + clientes cli ON cm.client_id = cli.client_id + WHERE cv.estado_acceso = ? + ORDER BY cv.fecha_hora_visita DESC + `; + + connection.query(query, [estado_acceso.toUpperCase()], (err, rows) => { + if (err) { + console.error('Error al listar visitas por estado de acceso:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar visitas por estado de acceso', error: err.message }); + } + res.json(rows); + }); + }); + }, + + /** + * @function listarVisitasPorArea + * @description Lista las visitas filtradas por el nombre del área de acceso. + * @param {Object} req - Objeto de solicitud de Express (req.params.nombre_area). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarVisitasPorArea: (req, res) => { + const { nombre_area } = req.params; + + if (!nombre_area) { + return res.status(400).json({ mensaje: 'El nombre del área es obligatorio.' }); + } + + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar visitas por área:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cv.id_visita, + cv.id_miembro, + cm.matricula AS matricula_miembro, + cli.client_nombre AS nombre_cliente, + cv.matricula_registrada, + cv.fecha_hora_visita, + cv.nombre_area_acceso, + cv.estado_acceso, + cv.motivo_denegacion + FROM + clientes_miembros_visitas cv + JOIN + clientes_miembros cm ON cv.id_miembro = cm.id_miembro + JOIN + clientes cli ON cm.client_id = cli.client_id + WHERE cv.nombre_area_acceso LIKE ? + ORDER BY cv.fecha_hora_visita DESC + `; + + connection.query(query, [`%${nombre_area}%`], (err, rows) => { // Uso de LIKE para búsqueda parcial + if (err) { + console.error('Error al listar visitas por área:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar visitas por área', error: err.message }); + } + res.json(rows); + }); + }); + } + +}; + +module.exports = controlador_Clientes_MiembrosVisitas; \ No newline at end of file diff --git a/src/rutas/rt_clientes.js b/src/rutas/rt_clientes.js index 86bdc22..6a25068 100644 --- a/src/rutas/rt_clientes.js +++ b/src/rutas/rt_clientes.js @@ -2,10 +2,10 @@ const express = require('express'); const rutas = express.Router(); const controladorClientes = require('../controladores/controlador_Clientes'); -// Importa el NUEVO controlador de Membresías -const controladorMembresias = require('../controladores/controlador_Membresias'); -const controladorMiembros = require('../controladores/controlador_Miembros'); +const controladorMembresias = require('../controladores/controlador_Clientes_Membresias'); +const controladorMiembros = require('../controladores/controlador_Clientes_Miembros'); const controladorAreas = require('../controladores/controlador_Areas'); // Controlador para áreas de configuración +const controladorVisitasMiembros = require('../controladores/controlador_Clientes_MiembrosVisitas'); // Controlador para visitas de miembros //indice inical rutas.get('/clientes', controladorClientes.ver);//ver lista de clientes @@ -74,6 +74,28 @@ rutas.get('/api/miembros/estado/:estado', controladorMiembros.listarMiembrosPorE // GET /api/tipos-membresia/:id_tipo_membresia/miembros rutas.get('/api/tipos-membresia/:id_tipo_membresia/miembros', controladorMiembros.listarMiembrosPorTipoMembresia); +// --- Rutas RESTful para VISITAS de Miembros (tabla 'clientes_miembros_visitas') --- +// Estas rutas apuntarán al nuevo controlador_VisitasMiembros +// Registrar una nueva visita de miembro +// POST /api/visitas-miembros +// Datos esperados en el cuerpo: id_miembro, matricula_registrada, nombre_area_acceso, estado_acceso, motivo_denegacion (opcional), registrado_por (opcional), observaciones (opcional) +rutas.post('/api/visitas-miembros', controladorVisitasMiembros.registrarVisita); +// Obtener todas las visitas (puede ser útil para reportes o auditorías, pero cuidado con el volumen) +// GET /api/visitas-miembros +rutas.get('/api/visitas-miembros', controladorVisitasMiembros.listarVisitas); +// Obtener visitas por ID de miembro (para ver el historial de un miembro específico) +// GET /api/miembros/:id_miembro/visitas +rutas.get('/api/miembros/:id_miembro/visitas', controladorVisitasMiembros.listarVisitasPorMiembro); +// Obtener una visita específica por su ID +// GET /api/visitas-miembros/:id_visita +rutas.get('/api/visitas-miembros/:id_visita', controladorVisitasMiembros.obtenerVisitaPorId); +// (Opcional) Obtener visitas por estado de acceso (CONCEDIDO/DENEGADO) +// GET /api/visitas-miembros/estado/:estado_acceso +rutas.get('/api/visitas-miembros/estado/:estado_acceso', controladorVisitasMiembros.listarVisitasPorEstadoAcceso); +// (Opcional) Obtener visitas por área de acceso +// GET /api/visitas-miembros/area/:nombre_area +rutas.get('/api/visitas-miembros/area/:nombre_area', controladorVisitasMiembros.listarVisitasPorArea); + // Rutas para la administración de Áreas de Acceso rutas.get('/api/areas', controladorAreas.listarAreas); rutas.get('/api/areas/:id', controladorAreas.obtenerAreaPorId);