Cambios de unificacion en ingresos de visitas.

This commit is contained in:
Pablinux
2025-06-08 02:29:21 -05:00
parent ba83c866e6
commit 8afb1d09cb
2 changed files with 379 additions and 148 deletions

View File

@@ -1,69 +1,70 @@
// 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.
* @function crearVisita
* @description Registra una nueva visita en la tabla `clientes_miembros_visitas`.
* Maneja tanto visitas de miembros como de clientes ocasionales.
* @param {Object} req - Objeto de solicitud de Express (req.body).
* @param {Object} res - Objeto de respuesta de Express.
*/
registrarVisita: (req, res) => {
crearVisita: (req, res) => {
req.getConnection((err, connection) => {
if (err) {
console.error('Error al obtener conexión para registrar visita:', err);
console.error('Error al obtener conexión para crear visita:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message });
}
const {
id_miembro,
matricula_registrada,
client_id, // Obligatorio, siempre debe estar
es_miembro, // Booleano: true (1) o false (0)
id_miembro, // Opcional, solo si es_miembro es true
nombre_area_acceso,
estado_acceso, // 'CONCEDIDO' o 'DENEGADO'
estado_acceso,
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).' });
// Validaciones básicas de campos obligatorios
if (client_id === undefined || client_id === null || !nombre_area_acceso || !estado_acceso || es_miembro === undefined || es_miembro === null) {
return res.status(400).json({ mensaje: 'Faltan campos obligatorios (client_id, nombre_area_acceso, estado_acceso, es_miembro).' });
}
// 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)
// Validación de coherencia entre `es_miembro` y `id_miembro` (gestionada por código)
if (es_miembro && (id_miembro === undefined || id_miembro === null)) {
return res.status(400).json({ mensaje: 'Si es_miembro es TRUE, id_miembro es obligatorio.' });
}
if (!es_miembro && (id_miembro !== undefined && id_miembro !== null)) {
return res.status(400).json({ mensaje: 'Si es_miembro es FALSE, id_miembro no debe ser proporcionado.' });
}
// Formatear la fecha y hora a 'YYYY-MM-DD HH:mm:ss' para MySQL
// Usar la fecha nativa de JavaScript, asumiendo que el servidor está en UTC-5 (Quito)
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // getMonth() es 0-indexado
const month = String(now.getMonth() + 1).padStart(2, '0');
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 (?, ?, ?, ?, ?, ?, ?, ?)
(client_id, es_miembro, id_miembro, 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
client_id,
es_miembro, // Se insertará como 1 (TRUE) o 0 (FALSE) en MySQL
es_miembro ? id_miembro : null, // Inserta id_miembro si es miembro, de lo contrario NULL
fecha_hora_visita,
nombre_area_acceso,
estado_acceso.toUpperCase(), // Asegura que se guarde en mayúsculas
estado_acceso.toUpperCase(),
motivo_denegacion || null,
registrado_por || null,
observaciones || null
@@ -71,11 +72,14 @@ const controlador_Clientes_MiembrosVisitas = {
connection.query(query, values, (err, result) => {
if (err) {
console.error('Error al registrar visita:', err);
console.error('Error al crear 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 });
let msg = 'El ID de cliente o ID de miembro especificado no existe.';
if (es_miembro) msg += ' Asegúrese de que el miembro y su cliente asociado existan.';
else msg += ' Asegúrese de que el cliente exista.';
return res.status(400).json({ mensaje: msg, error: err.message });
}
return res.status(500).json({ mensaje: 'Error interno del servidor al registrar la visita', error: err.message });
return res.status(500).json({ mensaje: 'Error interno del servidor al crear la visita', error: err.message });
}
if (result.affectedRows === 1) {
@@ -83,15 +87,16 @@ const controlador_Clientes_MiembrosVisitas = {
mensaje: 'Visita registrada exitosamente.',
id_visita: result.insertId,
datos_visita: {
id_miembro,
matricula_registrada,
client_id,
es_miembro,
id_miembro: es_miembro ? id_miembro : null,
nombre_area_acceso,
estado_acceso,
fecha_hora_visita // Devuelve la fecha formateada
fecha_hora_visita
}
});
} else {
res.status(500).json({ mensaje: 'Error al registrar la visita: no se afectó ninguna fila.' });
res.status(500).json({ mensaje: 'Error al crear la visita: no se afectó ninguna fila.' });
}
});
});
@@ -99,8 +104,9 @@ const controlador_Clientes_MiembrosVisitas = {
/**
* @function listarVisitas
* @description Lista todos los registros de visitas.
* @param {Object} req - Objeto de solicitud de Express.
* @description Lista todas las visitas, con información detallada de cliente y miembro si aplica.
* Soporta filtros opcionales por client_id, id_miembro, estado_acceso, nombre_area_acceso, y rango de fechas.
* @param {Object} req - Objeto de solicitud de Express (req.query para filtros).
* @param {Object} res - Objeto de respuesta de Express.
*/
listarVisitas: (req, res) => {
@@ -110,30 +116,81 @@ const controlador_Clientes_MiembrosVisitas = {
return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message });
}
const query = `
let query = `
SELECT
cv.id_visita,
cv.id_miembro,
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.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
ctm.nombre_tipo AS nombre_tipo_membresia,
cmv.fecha_creacion,
cmv.fecha_actualizacion
FROM
clientes_miembros_visitas cv
clientes_miembros_visitas cmv
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
clientes cli ON cmv.client_id = cli.client_id
LEFT JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE 1=1
`;
const queryParams = [];
connection.query(query, (err, rows) => {
// Aplicar filtros desde req.query
if (req.query.client_id) {
query += ` AND cmv.client_id = ?`;
queryParams.push(req.query.client_id);
}
if (req.query.id_miembro) {
query += ` AND cmv.id_miembro = ?`;
queryParams.push(req.query.id_miembro);
}
if (req.query.estado_acceso) {
const estado = req.query.estado_acceso.toUpperCase();
if (['CONCEDIDO', 'DENEGADO'].includes(estado)) {
query += ` AND cmv.estado_acceso = ?`;
queryParams.push(estado);
} else {
return res.status(400).json({ mensaje: 'Estado de acceso inválido para el filtro.' });
}
}
if (req.query.nombre_area) {
query += ` AND cmv.nombre_area_acceso LIKE ?`;
queryParams.push(`%${req.query.nombre_area}%`);
}
if (req.query.fecha_desde) {
query += ` AND cmv.fecha_hora_visita >= ?`;
queryParams.push(req.query.fecha_desde + ' 00:00:00'); // Desde el inicio del día
}
if (req.query.fecha_hasta) {
query += ` AND cmv.fecha_hora_visita <= ?`;
queryParams.push(req.query.fecha_hasta + ' 23:59:59'); // Hasta el final del día
}
// Filtro para `es_miembro` (true/false)
if (req.query.es_miembro !== undefined && req.query.es_miembro !== null) {
const esMiembroBool = req.query.es_miembro === 'true' || req.query.es_miembro === '1';
query += ` AND cmv.es_miembro = ?`;
queryParams.push(esMiembroBool);
}
query += ` ORDER BY cmv.fecha_hora_visita DESC`;
// Implementar paginación si se necesita
// if (req.query.limit && req.query.offset) {
// query += ` LIMIT ? OFFSET ?`;
// queryParams.push(parseInt(req.query.limit), parseInt(req.query.offset));
// }
connection.query(query, queryParams, (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 });
@@ -159,25 +216,31 @@ const controlador_Clientes_MiembrosVisitas = {
const query = `
SELECT
cv.id_visita,
cv.id_miembro,
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.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
ctm.nombre_tipo AS nombre_tipo_membresia,
cmv.fecha_creacion,
cmv.fecha_actualizacion
FROM
clientes_miembros_visitas cv
clientes_miembros_visitas cmv
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 = ?
clientes cli ON cmv.client_id = cli.client_id
LEFT JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE cmv.id_visita = ?
`;
connection.query(query, [id_visita], (err, rows) => {
@@ -193,9 +256,164 @@ const controlador_Clientes_MiembrosVisitas = {
});
},
/**
* @function actualizarVisita
* @description Actualiza un registro de visita existente por su ID en `clientes_miembros_visitas`.
* Solo permite actualizar campos como área, estado, motivo, registrado_por, observaciones.
* NO se permite actualizar `client_id`, `es_miembro`, `id_miembro` o `fecha_hora_visita`.
* @param {Object} req - Objeto de solicitud de Express (req.params.id_visita, req.body).
* @param {Object} res - Objeto de respuesta de Express.
*/
actualizarVisita: (req, res) => {
const { id_visita } = req.params;
const {
nombre_area_acceso,
estado_acceso,
motivo_denegacion,
registrado_por,
observaciones
} = req.body;
if (!nombre_area_acceso || !estado_acceso) {
return res.status(400).json({ mensaje: 'Faltan campos obligatorios para actualizar la visita (nombre_area_acceso, estado_acceso).' });
}
if (!['CONCEDIDO', 'DENEGADO'].includes(estado_acceso.toUpperCase())) {
return res.status(400).json({ mensaje: 'El estado de acceso debe ser "CONCEDIDO" o "DENEGADO".' });
}
req.getConnection((err, connection) => {
if (err) {
console.error('Error al obtener conexión para actualizar visita:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message });
}
const query = `
UPDATE clientes_miembros_visitas SET
nombre_area_acceso = ?,
estado_acceso = ?,
motivo_denegacion = ?,
registrado_por = ?,
observaciones = ?,
fecha_actualizacion = CURRENT_TIMESTAMP()
WHERE id_visita = ?
`;
const values = [
nombre_area_acceso,
estado_acceso.toUpperCase(),
motivo_denegacion || null,
registrado_por || null,
observaciones || null,
id_visita
];
connection.query(query, values, (err, result) => {
if (err) {
console.error('Error al actualizar visita:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al actualizar visita', error: err.message });
}
if (result.affectedRows === 0) {
return res.status(404).json({ mensaje: 'Visita no encontrada para actualizar' });
}
res.json({ mensaje: 'Visita actualizada exitosamente' });
});
});
},
/**
* @function eliminarVisita
* @description Elimina físicamente una visita de la tabla `clientes_miembros_visitas`.
* NOTA: Para propósitos de auditoría, a menudo se prefiere una "eliminación lógica"
* (por ejemplo, actualizando un campo `estado_visita` a 'ANULADA').
* @param {Object} req - Objeto de solicitud de Express (req.params.id_visita).
* @param {Object} res - Objeto de respuesta de Express.
*/
eliminarVisita: (req, res) => {
const { id_visita } = req.params;
req.getConnection((err, connection) => {
if (err) {
console.error('Error al obtener conexión para eliminar visita:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message });
}
const query = `DELETE FROM clientes_miembros_visitas WHERE id_visita = ?`;
connection.query(query, [id_visita], (err, result) => {
if (err) {
console.error('Error al eliminar visita:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al eliminar visita', error: err.message });
}
if (result.affectedRows === 0) {
return res.status(404).json({ mensaje: 'Visita no encontrada para eliminar' });
}
res.json({ mensaje: 'Visita eliminada exitosamente.' });
});
});
},
/**
* @function listarVisitasPorCliente
* @description Lista visitas filtradas por un `client_id` específico.
* Incluye tanto visitas de miembros como de clientes ocasionales asociados a ese cliente.
* @param {Object} req - Objeto de solicitud de Express (req.params.client_id).
* @param {Object} res - Objeto de respuesta de Express.
*/
listarVisitasPorCliente: (req, res) => {
const { client_id } = req.params;
req.getConnection((err, connection) => {
if (err) {
console.error('Error al obtener conexión para listar visitas por cliente:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message });
}
const query = `
SELECT
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.id_miembro,
cm.matricula AS matricula_miembro,
ctm.nombre_tipo AS nombre_tipo_membresia,
cmv.fecha_creacion,
cmv.fecha_actualizacion
FROM
clientes_miembros_visitas cmv
JOIN
clientes cli ON cmv.client_id = cli.client_id
LEFT JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE cmv.client_id = ?
ORDER BY cmv.fecha_hora_visita DESC
`;
connection.query(query, [client_id], (err, rows) => {
if (err) {
console.error('Error al listar visitas por cliente:', err);
return res.status(500).json({ mensaje: 'Error interno del servidor al listar visitas por cliente', error: err.message });
}
if (rows.length === 0) {
return res.status(404).json({ mensaje: 'No se encontraron visitas para el cliente proporcionado.' });
}
res.json(rows);
});
});
},
/**
* @function listarVisitasPorMiembro
* @description Lista el historial de visitas para un miembro específico por su id_miembro.
* @description Lista visitas filtradas por un `id_miembro` específico.
* Solo devolverá visitas donde `es_miembro` sea TRUE.
* @param {Object} req - Objeto de solicitud de Express (req.params.id_miembro).
* @param {Object} res - Objeto de respuesta de Express.
*/
@@ -209,18 +427,32 @@ const controlador_Clientes_MiembrosVisitas = {
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
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.id_miembro,
cm.matricula AS matricula_miembro,
ctm.nombre_tipo AS nombre_tipo_membresia,
cmv.fecha_creacion,
cmv.fecha_actualizacion
FROM
clientes_miembros_visitas cv
WHERE cv.id_miembro = ?
ORDER BY cv.fecha_hora_visita DESC
clientes_miembros_visitas cmv
JOIN
clientes cli ON cmv.client_id = cli.client_id
JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro AND cmv.es_miembro = TRUE
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE cmv.id_miembro = ?
ORDER BY cmv.fecha_hora_visita DESC
`;
connection.query(query, [id_miembro], (err, rows) => {
@@ -229,7 +461,7 @@ const controlador_Clientes_MiembrosVisitas = {
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.' });
return res.status(404).json({ mensaje: 'No se encontraron visitas para el miembro proporcionado.' });
}
res.json(rows);
});
@@ -238,13 +470,12 @@ const controlador_Clientes_MiembrosVisitas = {
/**
* @function listarVisitasPorEstadoAcceso
* @description Lista las visitas filtradas por un estado de acceso específico (CONCEDIDO/DENEGADO).
* @description Lista 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(', ')}.` });
@@ -258,23 +489,30 @@ const controlador_Clientes_MiembrosVisitas = {
const query = `
SELECT
cv.id_visita,
cv.id_miembro,
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.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
ctm.nombre_tipo AS nombre_tipo_membresia
FROM
clientes_miembros_visitas cv
clientes_miembros_visitas cmv
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
clientes cli ON cmv.client_id = cli.client_id
LEFT JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE cmv.estado_acceso = ?
ORDER BY cmv.fecha_hora_visita DESC
`;
connection.query(query, [estado_acceso.toUpperCase()], (err, rows) => {
@@ -289,13 +527,12 @@ const controlador_Clientes_MiembrosVisitas = {
/**
* @function listarVisitasPorArea
* @description Lista las visitas filtradas por el nombre del área de acceso.
* @description Lista 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.' });
}
@@ -308,26 +545,33 @@ const controlador_Clientes_MiembrosVisitas = {
const query = `
SELECT
cv.id_visita,
cv.id_miembro,
cmv.id_visita,
cmv.fecha_hora_visita,
cmv.nombre_area_acceso,
cmv.estado_acceso,
cmv.motivo_denegacion,
cmv.registrado_por,
cmv.observaciones,
cmv.client_id,
cli.client_nombre,
cli.client_rucCed,
cmv.es_miembro,
cmv.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
ctm.nombre_tipo AS nombre_tipo_membresia
FROM
clientes_miembros_visitas cv
clientes_miembros_visitas cmv
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
clientes cli ON cmv.client_id = cli.client_id
LEFT JOIN
clientes_miembros cm ON cmv.id_miembro = cm.id_miembro
LEFT JOIN
clientes_membresias ctm ON cm.id_tipo_membresia = ctm.id_tipo_membresia
WHERE cmv.nombre_area_acceso LIKE ?
ORDER BY cmv.fecha_hora_visita DESC
`;
connection.query(query, [`%${nombre_area}%`], (err, rows) => { // Uso de LIKE para búsqueda parcial
connection.query(query, [`%${nombre_area}%`], (err, rows) => {
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 });
@@ -336,7 +580,6 @@ const controlador_Clientes_MiembrosVisitas = {
});
});
}
};
module.exports = controlador_Clientes_MiembrosVisitas;

View File

@@ -5,7 +5,7 @@ const controladorClientes = require('../controladores/controlador_Clientes');
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
const controladorVisitas = require('../controladores/controlador_Clientes_MiembrosVisitas'); // Controlador para visitas de miembros
//indice inical
rutas.get('/clientes', controladorClientes.ver);//ver lista de clientes
@@ -44,57 +44,45 @@ rutas.delete('/api/tipos-membresia/:id', controladorMembresias.eliminarTipoMembr
// --- Rutas RESTful para MIEMBROS (tabla 'clientes_miembros') ---
// Estas rutas apuntarán al nuevo controlador_Miembros
// Obtener todos los miembros
// GET /api/miembros
rutas.get('/api/miembros', controladorMiembros.listarMiembros);
// Obtener un miembro por su ID
// GET /api/miembros/:id_miembro
rutas.get('/api/miembros/:id_miembro', controladorMiembros.obtenerMiembroPorId);
// Crear un nuevo miembro
// POST /api/miembros
// Datos esperados en el cuerpo de la solicitud: matricula, client_id, id_tipo_membresia, fecha_inicio, fecha_fin, etc.
rutas.post('/api/miembros', controladorMiembros.crearMiembro);
// Actualizar un miembro existente por su ID
// PUT /api/miembros/:id_miembro
// Datos esperados en el cuerpo de la solicitud para actualizar: estado, fechas, etc.
rutas.put('/api/miembros/:id_miembro', controladorMiembros.actualizarMiembro);
// Eliminar (lógicamente/cambiar estado a CANCELADO/SUSPENDIDO) un miembro por su ID
// DELETE /api/miembros/:id_miembro
// NOTA: Dada la importancia del historial, es más común cambiar el estado a 'CANCELADO' o 'SUSPENDIDO'
// en lugar de una eliminación física. El controlador debería manejar esto.
rutas.delete('/api/miembros/:id_miembro', controladorMiembros.eliminarMiembro);
// --- Rutas Adicionales Comunes para Miembros (Opcional) ---
// Obtener miembros por client_id (útil para ver todas las membresías de un cliente)
// GET /api/clientes/:client_id/miembros
rutas.get('/api/clientes/:client_id/miembros', controladorMiembros.listarMiembrosPorCliente);
// Obtener miembros por estado (ej. 'ACTIVO', 'VENCIDO')
// GET /api/miembros/estado/:estado
rutas.get('/api/miembros/estado/:estado', controladorMiembros.listarMiembrosPorEstado);
// Obtener miembros por tipo de membresía
// 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);
/// --- Rutas RESTful para VISITAS UNIFICADAS (tabla 'clientes_visitas') ---
// Estas rutas apuntarán al nuevo controlador_ClientesVisitas
// Registrar una nueva visita (para miembro o ocasional)
rutas.post('/api/visitas', controladorVisitas.crearVisita); // Renombrado a 'crearVisita'
// Obtener todas las visitas (con posibilidad de filtros, cuidado con volumen)
rutas.get('/api/visitas', controladorVisitas.listarVisitas);
// 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.get('/api/visitas/:id_visita', controladorVisitas.obtenerVisitaPorId);
// Actualizar una visita existente (ej. corregir observaciones, cambiar estado si se permite)
rutas.put('/api/visitas/:id_visita', controladorVisitas.actualizarVisita);
// Eliminar lógicamente una visita (ej. marcar como 'ANULADA' si tuvieras ese estado)
rutas.delete('/api/visitas/:id_visita', controladorVisitas.eliminarVisita); // Implementación para una eliminación lógica
// Rutas de consulta específicas para visitas
rutas.get('/api/visitas/por-cliente/:client_id', controladorVisitas.listarVisitasPorCliente);
rutas.get('/api/visitas/por-miembro/:id_miembro', controladorVisitas.listarVisitasPorMiembro);
rutas.get('/api/visitas/estado/:estado_acceso', controladorVisitas.listarVisitasPorEstadoAcceso);
rutas.get('/api/visitas/area/:nombre_area', controladorVisitas.listarVisitasPorArea);
// Rutas para la administración de Áreas de Acceso
rutas.get('/api/areas', controladorAreas.listarAreas);