diff --git a/src/controladores/controlador_Clientes.js b/src/controladores/controlador_Clientes.js index 5749568..9e6cd78 100644 --- a/src/controladores/controlador_Clientes.js +++ b/src/controladores/controlador_Clientes.js @@ -205,7 +205,7 @@ controlador.consulta_clientesApps = (req, res) => { // Nombre de la función cam FROM clientes WHERE - client_nombre LIKE ? OR client_rucCed LIKE ? + client_nombre LIKE %?% OR client_rucCed LIKE %?% LIMIT 10; `; diff --git a/src/controladores/controlador_Cont_PagosTransacciones b/src/controladores/controlador_Cont_PagosTransacciones new file mode 100644 index 0000000..52a6939 --- /dev/null +++ b/src/controladores/controlador_Cont_PagosTransacciones @@ -0,0 +1,440 @@ +// controladores/controlador_ContPagosTransacciones.js + +const controladorContPagosTransacciones = { + + /** + * @function crearTransaccion + * @description Crea un nuevo registro en la tabla `cont_pagos_transacciones`. + * @param {Object} req - Objeto de solicitud de Express (req.body). + * @param {Object} res - Objeto de respuesta de Express. + */ + crearTransaccion: (req, res) => { + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para crear transacción de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const { + monto_total_cobro, + client_id, + // id_miembro ya no se espera aquí + origen_transaccion, + documento_cobro_tipo, + numero_documento, + fecha_documento, + referencia_interna, // Puede ser id_visita, id_venta, id_membresia, id_miembro + cuenta_contable_ingreso_codigo, + tipo_movimiento_contable, // Espera 'Entrada' + estado_transaccion, // Valor inicial, ej. 'Pendiente' o 'Conciliado' + registrado_por, + observaciones + } = req.body; + + // Validaciones básicas de campos obligatorios + if (!monto_total_cobro || !client_id || !origen_transaccion || !documento_cobro_tipo || + !numero_documento || !fecha_documento || !cuenta_contable_ingreso_codigo || + !tipo_movimiento_contable || !estado_transaccion) { + return res.status(400).json({ mensaje: 'Faltan campos obligatorios para crear la transacción de pago.' }); + } + + // Validar tipos ENUM (aunque MySQL los valida al insertar) + // Se asume que 'tipo_cobro' se gestiona a través del 'origen_transaccion' y el estado del proceso de pago. + // La tabla no tiene un campo 'tipo_cobro' directo, sino que se infiere de 'estado_transaccion' y el contexto. + + // Si necesitas validar tipo_cobro aquí, agrégalo a req.body y a la validación. + // Ejemplo: if (!['Contado', 'Crédito', 'Mixto'].includes(req.body.tipo_cobro)) {...} + + if (!['Pendiente', 'Conciliado', 'ParcialmentePagado', 'Anulado', 'Devuelto', 'EnMora', 'Rechazado'].includes(estado_transaccion)) { + return res.status(400).json({ mensaje: 'El estado_transaccion proporcionado no es válido.' }); + } + if (tipo_movimiento_contable !== 'Entrada') { // Para cobros, siempre 'Entrada' + return res.status(400).json({ mensaje: 'El tipo_movimiento_contable para un cobro debe ser "Entrada".' }); + } + + // Construir la fecha de documento (si no viene en formato DATETIME correcto) + const fechaDocumentoFormateada = new Date(fecha_documento).toISOString().slice(0, 19).replace('T', ' '); + + const query = ` + INSERT INTO cont_pagos_transacciones ( + monto_total_cobro, client_id, origen_transaccion, + documento_cobro_tipo, numero_documento, fecha_documento, referencia_interna, + cuenta_contable_ingreso_codigo, tipo_movimiento_contable, estado_transaccion, + registrado_por, observaciones + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; + const values = [ + monto_total_cobro, + client_id, + origen_transaccion, + documento_cobro_tipo, + numero_documento, + fechaDocumentoFormateada, + referencia_interna || null, + cuenta_contable_ingreso_codigo, + tipo_movimiento_contable, + estado_transaccion, + registrado_por || null, + observaciones || null + ]; + + connection.query(query, values, (err, result) => { + if (err) { + console.error('Error al crear transacción de pago:', err); + if (err.code === 'ER_NO_REFERENCED_ROW_2' || err.code === 'ER_NO_REFERENCED_ROW') { + return res.status(400).json({ mensaje: 'El cliente referenciado no existe.', error: err.message }); + } + if (err.code === 'ER_DUP_ENTRY') { + return res.status(409).json({ mensaje: `El documento de cobro ${documento_cobro_tipo} con número ${numero_documento} ya existe.`, error: err.message }); + } + return res.status(500).json({ mensaje: 'Error interno del servidor al crear la transacción de pago.', error: err.message }); + } + + if (result.affectedRows === 1) { + res.status(201).json({ + mensaje: 'Transacción de pago creada exitosamente.', + id_transaccion: result.insertId, + datos_creados: { id_transaccion: result.insertId, monto_total_cobro, client_id, origen_transaccion, numero_documento } + }); + } else { + res.status(500).json({ mensaje: 'Error al crear la transacción de pago: no se afectó ninguna fila.' }); + } + }); + }); + }, + + /** + * @function listarTransacciones + * @description Lista todas las transacciones de pagos, con detalles de cliente y cuenta contable. + * Permite filtros opcionales por client_id, estado_transaccion, origen_transaccion, etc. + * @param {Object} req - Objeto de solicitud de Express (req.query para filtros). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarTransacciones: (req, res) => { + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar transacciones de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + let query = ` + SELECT + cpt.*, + cli.client_nombre, + cli.client_rucCed, + cc_ingreso.contCuent_Detalle AS cuenta_ingreso_detalle + FROM + cont_pagos_transacciones cpt + JOIN + clientes cli ON cpt.client_id = cli.client_id + JOIN + cont_cuentas cc_ingreso ON cpt.cuenta_contable_ingreso_codigo = cc_ingreso.contCuent_codigo + WHERE 1=1 + `; + const queryParams = []; + + // Aplicar filtros desde req.query + if (req.query.client_id) { + query += ` AND cpt.client_id = ?`; + queryParams.push(req.query.client_id); + } + // id_miembro ya no es un campo directo para filtrar + if (req.query.estado_transaccion) { + query += ` AND cpt.estado_transaccion = ?`; + queryParams.push(req.query.estado_transaccion); + } + if (req.query.origen_transaccion) { + query += ` AND cpt.origen_transaccion = ?`; + queryParams.push(req.query.origen_transaccion); + } + if (req.query.numero_documento) { + query += ` AND cpt.numero_documento LIKE ?`; + queryParams.push(`%${req.query.numero_documento}%`); + } + // Puedes añadir filtros por rango de fechas (ej. fecha_hora_registro_desde, fecha_hora_registro_hasta) + if (req.query.fecha_desde) { + query += ` AND cpt.fecha_hora_registro >= ?`; + queryParams.push(req.query.fecha_desde + ' 00:00:00'); + } + if (req.query.fecha_hasta) { + query += ` AND cpt.fecha_hora_registro <= ?`; + queryParams.push(req.query.fecha_hasta + ' 23:59:59'); + } + + query += ` ORDER BY cpt.fecha_hora_registro DESC`; + // Implementar paginación si se necesita + + connection.query(query, queryParams, (err, rows) => { + if (err) { + console.error('Error al listar transacciones de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar transacciones de pago.', error: err.message }); + } + res.json(rows); + }); + }); + }, + + /** + * @function obtenerTransaccionPorId + * @description Obtiene una transacción de pago específica por su ID. + * @param {Object} req - Objeto de solicitud de Express (req.params.id_transaccion). + * @param {Object} res - Objeto de respuesta de Express. + */ + obtenerTransaccionPorId: (req, res) => { + const { id_transaccion } = req.params; + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para transacción de pago por ID:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cpt.*, + cli.client_nombre, + cli.client_rucCed, + cc_ingreso.contCuent_Detalle AS cuenta_ingreso_detalle + FROM + cont_pagos_transacciones cpt + JOIN + clientes cli ON cpt.client_id = cli.client_id + JOIN + cont_cuentas cc_ingreso ON cpt.cuenta_contable_ingreso_codigo = cc_ingreso.contCuent_codigo + WHERE cpt.id_transaccion = ? + `; + + connection.query(query, [id_transaccion], (err, rows) => { + if (err) { + console.error('Error al obtener transacción de pago por ID:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener transacción de pago.', error: err.message }); + } + if (rows.length === 0) { + return res.status(404).json({ mensaje: 'Transacción de pago no encontrada.' }); + } + res.json(rows[0]); + }); + }); + }, + + /** + * @function actualizarTransaccion + * @description Actualiza un registro de transacción de pago existente por su ID. + * Se permiten actualizar campos específicos como monto_total_cobro, referencia_interna, estado_transaccion, + * fecha_liquidado_conciliado, registrado_por, observaciones. + * Campos como client_id, origen_transaccion, documento_cobro_tipo, numero_documento, fecha_documento, + * cuenta_contable_ingreso_codigo, tipo_movimiento_contable NO deben actualizarse para mantener la auditoría original. + * @param {Object} req - Objeto de solicitud de Express (req.params.id_transaccion, req.body). + * @param {Object} res - Objeto de respuesta de Express. + */ + actualizarTransaccion: (req, res) => { + const { id_transaccion } = req.params; + const { + monto_total_cobro, + referencia_interna, + estado_transaccion, + fecha_liquidado_conciliado, + registrado_por, + observaciones + } = req.body; + + if (!monto_total_cobro || !estado_transaccion) { + return res.status(400).json({ mensaje: 'Faltan campos obligatorios para actualizar la transacción (monto_total_cobro, estado_transaccion).' }); + } + if (!['Pendiente', 'Conciliado', 'ParcialmentePagado', 'Anulado', 'Devuelto', 'EnMora', 'Rechazado'].includes(estado_transaccion)) { + return res.status(400).json({ mensaje: 'El estado_transaccion proporcionado para la actualización no es válido.' }); + } + + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para actualizar transacción de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + UPDATE cont_pagos_transacciones SET + monto_total_cobro = ?, + referencia_interna = ?, + estado_transaccion = ?, + fecha_liquidado_conciliado = ?, + registrado_por = ?, + observaciones = ?, + fecha_actualizacion = CURRENT_TIMESTAMP() + WHERE id_transaccion = ? + `; + const values = [ + monto_total_cobro, + referencia_interna || null, + estado_transaccion, + fecha_liquidado_conciliado || null, + registrado_por || null, + observaciones || null, + id_transaccion + ]; + + connection.query(query, values, (err, result) => { + if (err) { + console.error('Error al actualizar transacción de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al actualizar transacción de pago.', error: err.message }); + } + + if (result.affectedRows === 0) { + return res.status(404).json({ mensaje: 'Transacción de pago no encontrada para actualizar.' }); + } + res.json({ mensaje: 'Transacción de pago actualizada exitosamente.' }); + }); + }); + }, + + /** + * @function anularTransaccion + * @description Realiza una "eliminación lógica" de una transacción de pago, + * cambiando su `estado_transaccion` a 'Anulado'. + * @param {Object} req - Objeto de solicitud de Express (req.params.id_transaccion, req.body.registrado_por, req.body.observaciones_anulacion). + * @param {Object} res - Objeto de respuesta de Express. + */ + anularTransaccion: (req, res) => { + const { id_transaccion } = req.params; + const { registrado_por, observaciones_anulacion } = req.body; + + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para anular transacción de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + connection.query('SELECT estado_transaccion FROM cont_pagos_transacciones WHERE id_transaccion = ?', [id_transaccion], (err, rows) => { + if (err) { + console.error('Error al verificar estado de transacción para anular:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al verificar estado.', error: err.message }); + } + if (rows.length === 0) { + return res.status(404).json({ mensaje: 'Transacción de pago no encontrada para anular.' }); + } + if (rows[0].estado_transaccion === 'Anulado') { + return res.status(409).json({ mensaje: 'La transacción ya se encuentra anulada.' }); + } + + // Si la transacción ya está conciliada, podría requerir un proceso de "reversión" contable + // más complejo en lugar de una simple anulación. + // if (rows[0].estado_transaccion === 'Conciliado') { + // return res.status(409).json({ mensaje: 'No se puede anular una transacción ya conciliada. Debe realizar una reversión.' }); + // } + + const query = ` + UPDATE cont_pagos_transacciones SET + estado_transaccion = 'Anulado', + observaciones = CONCAT(IFNULL(observaciones, ''), '\nAnulado por: ', ?, ' - Motivo: ', ?, ' - Fecha: ', CURRENT_TIMESTAMP()), + fecha_actualizacion = CURRENT_TIMESTAMP() + WHERE id_transaccion = ? + `; + const values = [ + registrado_por || 'Sistema/API', + observaciones_anulacion || 'Sin motivo especificado', + id_transaccion + ]; + + connection.query(query, values, (err, result) => { + if (err) { + console.error('Error al anular transacción de pago:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al anular transacción de pago.', error: err.message }); + } + + if (result.affectedRows === 0) { + return res.status(404).json({ mensaje: 'Transacción de pago no encontrada para anular.' }); + } + res.json({ mensaje: 'Transacción de pago anulada exitosamente (estado cambiado a "Anulado").' }); + }); + }); + }); + }, + + /** + * @function listarTransaccionesPorCliente + * @description Lista transacciones de pago filtradas por un client_id específico. + * @param {Object} req - Objeto de solicitud de Express (req.params.client_id). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarTransaccionesPorCliente: (req, res) => { + const { client_id } = req.params; + req.getConnection((err, connection) => { + if (err) { + console.error('Error al obtener conexión para listar transacciones por cliente:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cpt.*, + cli.client_nombre, + cli.client_rucCed, + cc_ingreso.contCuent_Detalle AS cuenta_ingreso_detalle + FROM + cont_pagos_transacciones cpt + JOIN + clientes cli ON cpt.client_id = cli.client_id + JOIN + cont_cuentas cc_ingreso ON cpt.cuenta_contable_ingreso_codigo = cc_ingreso.contCuent_codigo + WHERE cpt.client_id = ? + ORDER BY cpt.fecha_hora_registro DESC + `; + + connection.query(query, [client_id], (err, rows) => { + if (err) { + console.error('Error al listar transacciones por cliente:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar transacciones por cliente.', error: err.message }); + } + if (rows.length === 0) { + return res.status(404).json({ mensaje: 'No se encontraron transacciones para el cliente proporcionado.' }); + } + res.json(rows); + }); + }); + }, + + /** + * @function listarTransaccionesPorEstado + * @description Lista transacciones de pago filtradas por un estado_transaccion específico. + * @param {Object} req - Objeto de solicitud de Express (req.params.estado_transaccion). + * @param {Object} res - Objeto de respuesta de Express. + */ + listarTransaccionesPorEstado: (req, res) => { + const { estado_transaccion } = req.params; + const estadosPermitidos = ['Pendiente', 'Conciliado', 'ParcialmentePagado', 'Anulado', 'Devuelto', 'EnMora', 'Rechazado']; + if (!estadosPermitidos.includes(estado_transaccion)) { + return res.status(400).json({ mensaje: `El estado proporcionado '${estado_transaccion}' 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 transacciones por estado:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al obtener conexión', error: err.message }); + } + + const query = ` + SELECT + cpt.*, + cli.client_nombre, + cli.client_rucCed, + cc_ingreso.contCuent_Detalle AS cuenta_ingreso_detalle + FROM + cont_pagos_transacciones cpt + JOIN + clientes cli ON cpt.client_id = cli.client_id + JOIN + cont_cuentas cc_ingreso ON cpt.cuenta_contable_ingreso_codigo = cc_ingreso.contCuent_codigo + WHERE cpt.estado_transaccion = ? + ORDER BY cpt.fecha_hora_registro DESC + `; + + connection.query(query, [estado_transaccion], (err, rows) => { + if (err) { + console.error('Error al listar transacciones por estado:', err); + return res.status(500).json({ mensaje: 'Error interno del servidor al listar transacciones por estado.', error: err.message }); + } + res.json(rows); + }); + }); + } +}; + +module.exports = controladorContPagosTransacciones; \ No newline at end of file diff --git a/src/rutas/rt_Generales.js b/src/rutas/rt_Generales.js index b156eca..4005ade 100644 --- a/src/rutas/rt_Generales.js +++ b/src/rutas/rt_Generales.js @@ -2,6 +2,7 @@ const express = require('express'); const rutas = express.Router(); const controlador_init = require('../controladores/controlador_General'); +const controladorContPagosTransacciones = require('../controladores/controlador_Cont_PagosTransacciones'); //ver listar items /formulario/:varNomCampoDb, controlador clase.funcion rutas.get('/ventasJson/', controlador_init.verVentasJson);//ver productos en modo json/get //rutas.get('/', controlador_init.app_sigma);//Una app para PEDIDOS @@ -25,6 +26,23 @@ rutas.post('/login_app', controlador_init.login_appTK);//login APP / dev rutas.post('/auth-keygen', controlador_init.auth_keygen);//genera TOKENS / dev rutas.post('/auth-token', controlador_init.auth_token);//Valida TOKENS / dev +// --- Rutas RESTful para Transacciones de Pagos (cont_pagos_transacciones) --- +// Crear una nueva transacción de pago +rutas.post('/api/pagos/transacciones', controladorContPagosTransacciones.crearTransaccion); +// Obtener todas las transacciones de pagos (con filtros opcionales) +rutas.get('/api/pagos/transacciones', controladorContPagosTransacciones.listarTransacciones); +// Obtener una transacción de pago por su ID +rutas.get('/api/pagos/transacciones/:id_transaccion', controladorContPagosTransacciones.obtenerTransaccionPorId); +// Actualizar una transacción de pago existente +rutas.put('/api/pagos/transacciones/:id_transaccion', controladorContPagosTransacciones.actualizarTransaccion); +// "Eliminar" lógicamente (cambiar estado a 'Anulado') una transacción de pago +rutas.put('/api/pagos/transacciones/:id_transaccion/anular', controladorContPagosTransacciones.anularTransaccion); +// (Opcional) Obtener transacciones por cliente +rutas.get('/api/pagos/transacciones/por-cliente/:client_id', controladorContPagosTransacciones.listarTransaccionesPorCliente); +// (Opcional) Obtener transacciones por estado +rutas.get('/api/pagos/transacciones/estado/:estado_transaccion', controladorContPagosTransacciones.listarTransaccionesPorEstado); + + /** * @swagger * /operaciones: