diff --git a/src/controladores/controlador_Clientes.js b/src/controladores/controlador_Clientes.js index 7b45303..5749568 100644 --- a/src/controladores/controlador_Clientes.js +++ b/src/controladores/controlador_Clientes.js @@ -173,6 +173,53 @@ controlador.app_pedidos_clientes = (req, res) => { }); }; +/** + * @function consulta_clientesApps + * @description Consulta clientes por nombre, RUC o cédula para el autocomplete en aplicaciones. + * Espera un parámetro de consulta en la URL: /consultaClientesJson?consulta=dato + * Donde 'dato' puede ser parte del nombre, RUC o cédula. + * @param {Object} req - Objeto de solicitud de Express (req.query.consulta). + * @param {Object} res - Objeto de respuesta de Express. + */ +controlador.consulta_clientesApps = (req, res) => { // Nombre de la función cambiado aquí + const { consulta } = req.query; // Obtener el parámetro 'consulta' de la URL + if (!consulta) { + return res.status(400).json({ mensaje: 'El parámetro "consulta" es obligatorio para la búsqueda.' }); + } + + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para consulta de clientes:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const searchTerm = `%${consulta}%`; // Preparar el término de búsqueda para operaciones LIKE + + // Consulta SQL para buscar clientes por 'client_nombre' o 'client_rucCed' + // Limita los resultados a 10 para evitar cargar demasiados datos en el frontend + const query = ` + SELECT + client_id, + client_nombre, + client_rucCed + FROM + clientes + WHERE + client_nombre LIKE ? OR client_rucCed LIKE ? + LIMIT 10; + `; + + // Ejecutar la consulta con el término de búsqueda para ambos campos + connection.query(query, [searchTerm, searchTerm], (err, rows) => { + if (err) { + console.error('Error al consultar clientes en consulta_clientesApps:', err); // Log actualizado + return res.status(500).json({ mensaje: 'Error interno del servidor al consultar clientes', error: err.message }); + } + res.json(rows); // Devolver los resultados en formato JSON + }); + }); +} + //CONSULTA CLIENTE CLOUD C.I-RUC => ruta:/busquedaSRI var data_url0 = 'http://www.ecuadorlegalonline.com/modulo/sri/consulta-ruc/ruc.api.php'; var data_url1 = "https://siax-system.net/app/clientes_cloud.php"; diff --git a/src/controladores/controlador_Miembros.js b/src/controladores/controlador_Miembros.js index 8f59b7c..ff73b1b 100644 --- a/src/controladores/controlador_Miembros.js +++ b/src/controladores/controlador_Miembros.js @@ -116,50 +116,106 @@ const controladorMiembros = { * @param {Object} req - Objeto de solicitud de Express (req.body contiene los datos del miembro). * @param {Object} res - Objeto de respuesta de Express. */ + /** + * @function crearMiembro + * @description Crea un nuevo registro de miembro, generando la matrícula automáticamente + * basándose en el tipo de membresía, un contador de membresías por cliente y el ID del cliente. + * NO MODIFICA TABLAS NI REQUIERE TABLAS ADICIONALES. + * @param {Object} req - Objeto de solicitud de Express (req.body contiene los datos del miembro). + * @param {Object} res - Objeto de respuesta de Express. + */ crearMiembro: (req, res) => { - const { - matricula, client_id, id_tipo_membresia, fecha_inicio, fecha_fin, - observaciones, creado_por // Añadir creado_por si lo envías desde el frontend - } = req.body; - - // Validar campos obligatorios - if (!matricula || !client_id || !id_tipo_membresia || !fecha_inicio || !fecha_fin) { - return res.status(400).json({ mensaje: 'Faltan campos obligatorios para crear el miembro (matricula, client_id, id_tipo_membresia, fecha_inicio, fecha_fin).' }); - } - - req.getConnection((err, connection) => { - if (err) { - console.error('Error al obtener conexión para crear miembro:', err); - return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + const { + client_id, id_tipo_membresia, fecha_inicio, fecha_fin, + observaciones, creado_por + } = req.body; + + // Validar campos obligatorios que vienen del frontend + if (!client_id || !id_tipo_membresia || !fecha_inicio || !fecha_fin) { + return res.status(400).json({ mensaje: 'Faltan campos obligatorios para crear el miembro (client_id, id_tipo_membresia, fecha_inicio, fecha_fin).' }); } - - const query = ` - INSERT INTO clientes_miembros ( - matricula, client_id, id_tipo_membresia, estado, fecha_registro, - fecha_inicio, fecha_fin, observaciones, creado_por - ) VALUES (?, ?, ?, ?, NOW(), ?, ?, ?, ?) - `; - // 'ACTIVO' como estado por defecto en el INSERT - const values = [ - matricula, client_id, id_tipo_membresia, 'ACTIVO', - fecha_inicio, fecha_fin, observaciones, creado_por - ]; - - connection.query(query, values, (err, result) => { - if (err) { - console.error('Error al crear miembro:', err); - if (err.code === 'ER_DUP_ENTRY') { - return res.status(409).json({ mensaje: 'Ya existe un miembro con esa matrícula.', error: err.message }); + + req.getConnection(async (err, connection) => { + if (err) { + console.error('Error al obtener conexión para crear miembro:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); } - // Posibles errores de clave foránea (client_id o id_tipo_membresia no existen) - if (err.code === 'ER_NO_REFERENCED_ROW_2' || err.code === 'ER_NO_REFERENCED_ROW') { - return res.status(400).json({ mensaje: 'El cliente o el tipo de membresía especificado no existe.', error: err.message }); + + try { + // Iniciar una transacción para asegurar la consistencia si la generación requiere múltiples pasos + await connection.promise().beginTransaction(); + + // 1. Obtener el nombre del tipo de membresía para el prefijo (ej. 'MP' de 'Membresia Premium') + const [tipoMembresiaRows] = await connection.promise().query( + 'SELECT nombre_tipo FROM clientes_membresias WHERE id_tipo_membresia = ?', + [id_tipo_membresia] + ); + + if (tipoMembresiaRows.length === 0) { + await connection.promise().rollback(); // Revertir la transacción + return res.status(400).json({ mensaje: 'Tipo de membresía no encontrado.' }); + } + const nombreTipoMembresia = tipoMembresiaRows[0].nombre_tipo; + + // Derivar prefijo: Tomar las iniciales en mayúsculas. Ej: "Membresia Premium" -> "MP" + const prefijoMembresia = nombreTipoMembresia.split(' ') + .map(word => word[0]) + .join('') + .toUpperCase(); + + // 2. Contar el número de membresías previas de este cliente + const [countMembresiasRows] = await connection.promise().query( + 'SELECT COUNT(*) AS total_membresias FROM clientes_miembros WHERE client_id = ?', + [client_id] + ); + const contadorCliente = countMembresiasRows[0].total_membresias + 1; // Sumar 1 para la nueva membresía + + // Formatear contador a 2 dígitos (ej. 1 -> '01', 10 -> '10') + const contadorFormateado = String(contadorCliente).padStart(2, '0'); + + // Formatear client_id a 6 dígitos (ej. 21 -> '000021') + const clienteIdFormateado = String(client_id).padStart(6, '0'); + + // 3. Ensamblar la matrícula final: MP-01-000021 + const matriculaGenerada = `${prefijoMembresia}-${contadorFormateado}-${clienteIdFormateado}`; + + // 4. Insertar el nuevo miembro con la matrícula generada + const query = ` + INSERT INTO clientes_miembros ( + matricula, client_id, id_tipo_membresia, estado, fecha_registro, + fecha_inicio, fecha_fin, observaciones, creado_por + ) VALUES (?, ?, ?, ?, NOW(), ?, ?, ?, ?) + `; + const values = [ + matriculaGenerada, client_id, id_tipo_membresia, 'ACTIVO', + fecha_inicio, fecha_fin, observaciones, creado_por + ]; + + const [result] = await connection.promise().query(query, values); + + await connection.promise().commit(); // Confirmar la transacción + + res.status(201).json({ + mensaje: 'Miembro creado exitosamente', + id_miembro: result.insertId, + matricula: matriculaGenerada // Devolvemos la matrícula generada + }); + + } catch (error) { + await connection.promise().rollback(); // Revertir la transacción en caso de error + console.error('Error en la lógica de creación de miembro:', error); + if (error.code === 'ER_DUP_ENTRY') { + // Esto es posible si el mismo cliente intenta registrar una membresía + // con la misma matrícula generada, lo cual ocurriría si ya tiene una. + // O si hubo un error en la lógica de conteo o formateo. + return res.status(409).json({ mensaje: `Ya existe un miembro con la matrícula generada (${error.sqlMessage}). Esto puede ocurrir si un cliente ya tiene una membresía con este tipo o si hay un problema con la generación del contador.`, error: error.message }); + } + if (error.code === 'ER_NO_REFERENCED_ROW_2' || error.code === 'ER_NO_REFERENCED_ROW') { + return res.status(400).json({ mensaje: 'El cliente o el tipo de membresía especificado no existe. Asegúrate de que los IDs seleccionados sean válidos.', error: error.message }); + } + return res.status(500).json({ mensaje: 'Error interno del servidor al crear miembro', error: error.message }); } - return res.status(500).json({ mensaje: 'Error interno del servidor al crear miembro', error: err.message }); - } - res.status(201).json({ mensaje: 'Miembro creado exitosamente', id_miembro: result.insertId }); }); - }); }, /** diff --git a/src/rutas/rt_clientes.js b/src/rutas/rt_clientes.js index 85dff12..b715a8e 100644 --- a/src/rutas/rt_clientes.js +++ b/src/rutas/rt_clientes.js @@ -5,6 +5,7 @@ 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'); + //indice inical rutas.get('/clientes', controladorClientes.ver);//ver lista de clientes @@ -23,6 +24,13 @@ rutas.get('/eliminarCliente/:client_id', controladorClientes.eliminarCliente); //almacena cliente rutas.post('/addCliente', controladorClientes.guardaCliente);//almacena en bd el nuevo cliente: https://app.factura-e.net/addCliente rutas.get('/addClienteForm', controladorClientes.verFormNclientes);//muesta form para crear cliente +//*** Consultas de clientes(/consultaClientesJson?consulta=dato) donde dato puede ser nombre, ruc, cedula; +rutas.get('/consultaClientesJson', controladorClientes.app_pedidos_clientes);//consulta clientes: /consultaClientesJson?consulta=dato +rutas.get('/consulta_clientesApps', controladorClientes.consulta_clientesApps); +rutas.get('/busquedaSRI/', controladorClientes.buscarCli_sri);//API consulta clientes + +//API CONSULTA CLIENTES: https://NAME_SERVER/api_consultaClientes?id=numero_rucOcedula => https://app.factura-e.net/api_consultaClientes?id=0701637498001 +rutas.get('/api_consultaClientes/', controladorClientes.api_consultaCliente);//API consulta clientes // --- Rutas RESTful para TIPOS de Membresías (tabla 'clientes_membresias') --- // Estas rutas apuntarán al nuevo controlador_Membresias @@ -68,10 +76,5 @@ rutas.get('/api/tipos-membresia/:id_tipo_membresia/miembros', controladorMiembro // Ruta para obtener ciudades rutas.get('/api/ciudades', controladorClientes.obtenerCiudades); -//APP_SIGMA consultas -rutas.get('/consultaClientesJson', controladorClientes.app_pedidos_clientes);//consulta clientes: /consultaClientesJson?consulta=dato -rutas.get('/busquedaSRI/', controladorClientes.buscarCli_sri);//API consulta clientes -//API CONSULTA CLIENTES: https://NAME_SERVER/api_consultaClientes?id=numero_rucOcedula => https://app.factura-e.net/api_consultaClientes?id=0701637498001 -rutas.get('/api_consultaClientes/', controladorClientes.api_consultaCliente);//API consulta clientes module.exports = rutas;