Tabla de contenido
Una vez más aquí. Hoy vengo a dejar un pequeño artículo para un «señor» que me ha pedido una interfaz web para el registro de transacciones exportando a Excel. Sinceramente, no tengo muy claro para qué, pues transportar una hoja de Excel, tampoco es tan complicado. Pero bueno, tras jugar un poco con el código y utilizando la librería XLSX, pues aquí vengo a prestar el el resultado.
Este proyecto web te permite al usuario registrar transacciones (compras y ventas) y exportarlas a un archivo Excel. Utiliza PHP y MySQL para conectarse a una base de datos y almacenar la información que el usuario proporciona.
Un vistazo al código de este registro de transacciones con exportando a Excel

Archivo index.php
Esta es la parte en la que se va a ver todo el «mondongo» de la interfaz. Posiblemente termine separando en diferentes archivos las ventanas modal que utiliza para añadir y editar las transacciones, además de separar también el código PHP que escribe la tabla de productos. Pero por el momento, el código que aquí vamos a ver, funciona:
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Registro en Excel con PHP y MySQL</title> <link rel="icon" href="./img/favicon.ico" type="image/x-icon"> <link rel="stylesheet" type="text/css" href="./css/style.css"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"> <!-- Data Table --> <link rel="stylesheet" href="https://cdn.datatables.net/1.13.7/css/jquery.dataTables.css" /> <!-- Font Awesome --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"> </head> <body> <div class="main"> <div class="header alert alert-dark" role="alert">Transacciones en Excel con PHP y MyQL</div> <div class="product-container"> <div class="product-header mb-2"> <h3>Lista de transacciones</h3> <div class="button-group ml-auto"> <button type="button" class="btn btn-success" onclick="exportToExcel()">Exportar a Excel</button> <button type="button" class="btn btn-dark" data-toggle="modal" data-target="#addProductModal">Añadir transacción</button> </div> </div> <!-- Modal para añadir la transacción --> <div class="modal fade" id="addProductModal" tabindex="-1" aria-labelledby="addProduct" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content mt-5"> <div class="modal-header"> <h5 class="modal-title" id="addProduct">Añadir</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form action="./actions/add-product.php" method="POST"> <div class="form-group"> <label for="Client">Nombre de Cliente*:</label> <input type="text" class="form-control" id="Client" name="people" required> </div> <div class="form-group"> <label for="Nif_Cif">NIF/CIF*:</label> <input type="text" class="form-control" id="Nif_Cif" name="nif_cif" required> </div> <div class="form-group"> <label for="Transaction">Transacción:</label> <select class="form-control" id="Transaction" name="transaction"> <option value="compra">Compra</option> <option value="venta" selected>Venta</option> </select> </div> <hr /> <div class="form-group"> <label for="productName">Nombre de Producto*:</label> <input type="text" class="form-control" id="productName" name="product_name" required> </div> <div class="form-group"> <label for="productCode">Código de Producto*:</label> <input type="text" class="form-control" id="productCode" name="product_code" required> </div> <div class="row"> <div class="col"> <div class="form-group"> <label for="quantity">Cantidad*:</label> <input type="text" class="form-control" id="quantity" name="quantity" required> </div> </div> <div class="col"> <div class="form-group"> <label for="price">Precio*:</label> <input type="text" class="form-control" id="price" name="price" required> </div> </div> </div> <div class="form-group"> <label for="notes">Notas:</label> <textarea class="form-control" id="notes" name="notes" rows="4"></textarea> </div> <div class="aclaration form-group"> <span id="aclaracion">Los campos con * son obligatorios</span> </div> <button type="submit" class="btn btn-primary form-control mt-1 mb-1">Guardar Cambios</button> </form> </div> </div> </div> </div> <!-- Modal para actualizar la transacción --> <div class="modal fade" id="updateProductModal" tabindex="-1" aria-labelledby="updateProduct" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content mt-5"> <div class="modal-header"> <h5 class="modal-title" id="updateProduct">Actualizar</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form action="./actions/update-product.php" method="POST"> <div class="form-group" hidden> <label for="updateProductID">ID de Producto</label> <input type="text" class="form-control" id="updateProductID" name="tbl_product_id"> </div> <div class="form-group"> <label for="updateClient">Nombre de cliente*:</label> <input type="text" class="form-control" id="updateClient" name="people" required> </div> <div class="form-group"> <label for="updateNif_Cif">NIF/CIF*:</label> <input type="text" class="form-control" id="updateNif_Cif" name="nif_cif" required> </div> <div class="form-group"> <label for="updateTransaction">Transacción:</label> <input type="text" class="form-control" id="updateTransaction" name="transaction" readonly> </div> <hr /> <div class="form-group"> <label for="updateProductName">Nombre de Producto*:</label> <input type="text" class="form-control" id="updateProductName" name="product_name" required> </div> <div class="form-group"> <label for="updateProductCode">Código de Producto*:</label> <input type="text" class="form-control" id="updateProductCode" name="product_code" required> </div> <div class="row"> <div class="col"> <div class="form-group"> <label for="updateQuantity">Cantidad*:</label> <input type="text" class="form-control" id="updateQuantity" name="quantity" required> </div> </div> <div class="col"> <div class="form-group"> <label for="updatePrice">Precio (€)*:</label> <input type="text" class="form-control" id="updatePrice" name="price" required> </div> </div> </div> <div class="form-group"> <label for="updateNotes">Notas:</label> <textarea class="form-control" id="updateNotes" name="notes" rows="4"></textarea> </div> <div class="aclaration form-group"> <span id="aclaracion">Los campos con * son obligatorios</span> </div> <button type="submit" class="btn btn-primary form-control mt-1 mb-1">Guardar Cambios</button> </form> </div> </div> </div> </div> <table class="table table-hover product-table"> <thead> <tr> <th scope="col">ID</th> <th scope="col">Producto</th> <th scope="col">Código</th> <th scope="col">Cantidad</th> <th scope="col">Precio</th> <th scope="col">Total</th> <th scope="col">Fecha</th> <th scope="col">Transacción</th> <th scope="col">Cliente</th> <th scope="col">NIF/CIF</th> <th scope="col" style="display: none;">Nota</th> <th scope="col">Acciones</th> </tr> </thead> <tbody> <?php include('./connection/con.php'); $stmt = $conn->prepare("SELECT * FROM tbl_product"); $stmt->execute(); $result = $stmt->fetchAll(); foreach ($result as $row) { $productID = $row['tbl_product_id']; $productName = $row['product_name']; $productCode = $row['product_code']; $quantity = $row['quantity']; $quantityT = $quantity; $quantity = number_format($quantity, 0, '.', '.'); $price = $row['price']; $priceT = $price; // Reemplazamos las , por . en el precio $price = str_replace('.', ',', $price); $date = $row['date']; $timestamp = strtotime($date); // Formatear la fecha según el formato deseado $date = date("H:i, d-m-Y", $timestamp); $transaction = $row['transaction']; $people = $row['people']; $nif_cif = $row['nif_cif']; $notes = $row['notes']; $total = $quantityT * $priceT; // Formatear el total con comas y dos decimales $totalFormatted = number_format($total, 2, ',', '.'); ?> <tr> <th scope="row" id="productID-<?= $productID ?>"><?= $productID ?></th> <td id="productName-<?= $productID ?>"><?= $productName ?></td> <td id="productCode-<?= $productID ?>"><?= $productCode ?></td> <td id="quantity-<?= $productID ?>"><?= $quantity ?></td> <td id="price-<?= $productID ?>"><?= str_replace('€', '', $price) ?> €</td> <td id="total-<?= $productID ?>"><?= $totalFormatted ?></td> <td id="date-<?= $productID ?>"><?= $date ?></td> <td id="transaction-<?= $productID ?>"><?= $transaction ?></td> <td id="people-<?= $productID ?>"><?= $people ?></td> <td id="nif_cif-<?= $productID ?>"><?= $nif_cif ?></td> <td id="notes-<?= $productID ?>" style="display: none;"><?= $notes ?></td> <td> <button type="button" class="btn btn-primary" style="font-size: 12px;" onclick="updateProduct(<?= $productID ?>)"> Editar</button> <button type="button" class="btn btn-danger" style="font-size: 12px;" onclick="deleteProduct(<?= $productID ?>)"> Eliminar</button> <?php if (!empty($notes)) : ?> <button type="button" class="btn btn-info" style="font-size: 12px;" onclick="openNoteModal('<?= $productID ?>', '<?= $notes ?>')"> Ver Nota </button> <?php endif; ?> </td> </tr> <?php } ?> </tbody> </table> </div> </div> <!-- Footer --> <footer class="footer"> <div class="container"> <span class="text-muted">entreunosyceros.net</span> </div> </footer> <!-- Boostrap JS --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js"></script> <!-- Librería SheetJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js"></script> <!-- DataTables JS --> <script src="https://cdn.datatables.net/1.13.7/js/jquery.dataTables.js"></script> <!-- Funciones Jquery --> <script src="./js/script.js"></script> <!-- Javascript Notas --> <script> function openNoteModal(productID, note) { var options = { title: 'Nota de la transacción con ID: ' + productID, width: 400, height: 200, left: (window.innerWidth - 400) / 2, top: (window.innerHeight - 300) / 2, resizable: 'yes', scrollbars: 'no', status: 'no', toolbar: 'no', menubar: 'no' }; var customAlert = window.open('', '', 'width=' + options.width + ', height=' + options.height + ', left=' + options.left + ', top=' + options.top + ', resizable=' + options.resizable + ', scrollbars=' + options.scrollbars + ', status=' + options.status + ', toolbar=' + options.toolbar + ', menubar=' + options.menubar); // html de la nota customAlert.document.write('<html><head><title>' + options.title + '</title></head><body style="background-color: #ffffcc;"><p><b>Nota de la transacción con ID ' + productID + ':</b> ' + note + '</p></body></html>'); } </script> </body> </html>
Funcionalidad de este registro de transacciones con exportando a Excel
La página web tiene dos secciones principales:
- Lista de transacciones: Esta sección muestra una tabla con un listado de todas las transacciones registradas. Cada fila de la tabla muestra la siguiente información:
- ID
- Producto
- Código
- Cantidad
- Precio
- Total
- Fecha
- Transacción
- Cliente
- NIF/CIF
- Acciones (Editar, Eliminar, Ver Nota)
- Añadir transacción: Esta sección te permite agregar una nueva transacción a la base de datos. Es necesario completar la siguiente información:
- Nombre del cliente (obligatorio)
- NIF/CIF (obligatorio)
- Tipo de transacción (compra o venta)
- Nombre del producto (obligatorio)
- Código del producto (obligatorio)
- Cantidad (obligatorio)
- Precio (obligatorio)
- Notas (opcional)
Explicación del código
El fragmento de código proporcionado muestra la estructura HTML de la página web. Este archivo incluye los siguientes elementos:
- HTML: La estructura básica de la página web.
- Head: Contiene información sobre el documento, incluido el título, la codificación de caracteres y enlaces a recursos externos como hojas de estilo.
- Body: Contiene el contenido visible de la página web, incluido el encabezado, el contenedor del producto, el pie de página y el código JavaScript.
- Header: Muestra el título de la página web.
- Contenedor del producto: Esta sección contiene la lista de transacciones y el formulario para agregar una nueva transacción.
- Encabezado del producto: Muestra el título «Lista de transacciones» y dos botones:
- Exportar a Excel: Este botón activa una función JavaScript para exportar los datos de la transacción a un archivo Excel.
- Añadir transacción: Este botón abre una ventana modal donde se puede añadir una nueva transacción.
- Lista de transacciones: Esta sección utiliza un plugin de DataTables para mostrar una tabla dinámica con la lista de transacciones.
- Modal de añadir transacción: Esta es una ventana emergente que aparece al hacer clic en el botón «Añadir transacción». Contiene un formulario para ingresar la información de una nueva transacción.
- Encabezado del producto: Muestra el título «Lista de transacciones» y dos botones:
- Footer: Muestra información.
Archivo con.php

Dentro de este archvio, se incluye una llamada al archivo config.php
, que es el de las credenciales para conectarse a la base de datos MySQL. Este archivo archivo de conexión es crucial para que el proyecto funcione correctamente.
<?php require_once 'config.php'; try { $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { echo "Fallo en la conexión: " . $e->getMessage(); } ?>
Explicación del código:
- Variables de conexión:
$servername
: Almacena el nombre del servidor de la base de datos.$dbname
: Almacena el nombre de la base de datos a la que se desea conectar.$username
: Almacena el nombre de usuario de la base de datos.$password
: Almacena la contraseña del usuario de la base de datos.
- Establecimiento de la conexión:
- Se utiliza la clase
PDO
de PHP para establecer la conexión con la base de datos. - Pasaremos la información de la conexión como argumentos al constructor de la clase
PDO
. - Se configura el modo de error de PDO a
PDO::ERRMODE_EXCEPTION
para que se lance una excepción en caso de error.
- Se utiliza la clase
Uso del archivo de conexión:
El archivo de conexión se incluye en las páginas PHP que necesitan acceder a la base de datos. En este ejemplo, se incluye el archivo config.php
al inicio del script. Luego, se utiliza la variable $conn
para ejecutar la consulta SQL en cualquiera de los archivos php.
Archivo add-product
<?php include("../connection/con.php"); if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Validar que todos los campos requeridos estén presentes y no estén vacíos $requiredFields = ['product_name', 'product_code', 'quantity', 'price', 'transaction', 'people', 'nif_cif']; $missingFields = array_filter($requiredFields, function($field) { return !isset($_POST[$field]) || empty($_POST[$field]); }); if (!empty($missingFields)) { // Mostrar mensaje de error si faltan campos requeridos $errorMessage = "Por favor, complete todos los campos."; redirectToIndex($errorMessage); } // Validar datos numéricos $numericFields = ['quantity', 'price']; $invalidNumericFields = array_filter($numericFields, function($field) { return !is_numeric($_POST[$field]); }); if (!empty($invalidNumericFields)) { // Mostrar mensaje de error si hay campos numéricos no válidos $errorMessage = "La cantidad y el precio deben ser números válidos."; redirectToIndex($errorMessage); } // Procesar los datos insertados try { $productName = $_POST['product_name']; $productCode = $_POST['product_code']; $quantity = $_POST['quantity']; $price = $_POST['price']; // Reemplazamos las , por . en el precio para guardarlo en la base de datos $price = str_replace(',', '.', $price); $date = date("Y-m-d H:i:s"); $transaction = $_POST['transaction']; $people = $_POST['people']; $nif_cif = $_POST['nif_cif']; $notes = isset($_POST['notes']) ? $_POST['notes'] : ''; // Insertar datos en la base de datos $stmt = $conn->prepare("INSERT INTO tbl_product (product_name, product_code, quantity, price, date, transaction, people, nif_cif, notes) VALUES (:product_name, :product_code, :quantity, :price, :date, :transaction, :people, :nif_cif, :notes)"); $stmt->bindParam(":product_name", $productName, PDO::PARAM_STR); $stmt->bindParam(":product_code", $productCode, PDO::PARAM_STR); $stmt->bindParam(":quantity", $quantity, PDO::PARAM_INT); $stmt->bindParam(":price", $price, PDO::PARAM_STR); $stmt->bindParam(":date", $date, PDO::PARAM_STR); $stmt->bindParam(":transaction", $transaction, PDO::PARAM_STR); $stmt->bindParam(":people", $people, PDO::PARAM_STR); $stmt->bindParam(":nif_cif", $nif_cif, PDO::PARAM_STR); $stmt->bindParam(":notes", $notes, PDO::PARAM_STR); $stmt->execute(); // Redirigir con mensaje de éxito $successMessage = "Registro añadido correctamente."; redirectToIndex($successMessage); } catch (PDOException $e) { // Mostrar mensaje de error en caso de excepción $errorMessage = "Error al insertar el registro: " . $e->getMessage(); redirectToIndex($errorMessage); } finally { // Cerrar la conexión a la base de datos $conn = null; } } else { // Si no es una solicitud POST, redirigir con mensaje de error $errorMessage = "Acceso no permitido."; redirectToIndex($errorMessage); } function redirectToIndex($message) { $timestamp = time(); echo "<script> alert('$message'); window.location.href = 'http://localhost/inventario-excel/?timestamp=$timestamp'; </script>"; exit(); } ?>
Inclusión del archivo de conexión:
include("../connection/con.php");
: El código incluye el archivocon.php
ubicado en la carpetaconnection
, el cual contiene la información necesaria para establecer la conexión con la base de datos.
Comprobación del método de solicitud:
if ($_SERVER['REQUEST_METHOD'] === 'POST') { ... }
: Este código verifica si la solicitud que se ha enviado al servidor es de tipo POST, lo cual indica que se pretende insertar un nuevo registro en la base de datos.
Validación de los datos del formulario:
- Campos requeridos:
- Se define un arreglo con los nombres de los campos obligatorios (
product_name
,product_code
,quantity
,price
,transaction
,people
ynif_cif
). - Se utiliza la función
array_filter
para identificar si alguno de estos campos falta o está vacío en la solicitud POST. - Si se detectan campos faltantes, se muestra un mensaje de error y se redirige a la página principal.
- Se define un arreglo con los nombres de los campos obligatorios (
- Datos numéricos:
- Se define un arreglo con los nombres de los campos que deben ser numéricos (
quantity
yprice
). - Utilizaremos la función
array_filter
para identificar si alguno de estos campos no contiene un valor numérico válido. - Si se detectan valores numéricos inválidos, se muestra un mensaje de error y se redirige a la página principal.
- Se define un arreglo con los nombres de los campos que deben ser numéricos (
Procesamiento de los datos insertados:
- Obtención de los datos:
- Obtendremos los valores de los campos del formulario desde la variable
$_POST
. - Se formatea el precio, reemplazando las comas por puntos, para ajustarlo a la forma en que se almacena en la base de datos.
- Se obtiene la fecha y hora actual utilizando la función
date
.
- Obtendremos los valores de los campos del formulario desde la variable
- Inserción en la base de datos:
- Se prepara una consulta SQL para insertar los datos en la tabla
tbl_product
. - Utilizaremos marcadores de posición (
:product_name
,:product_code
, etc.) para evitar la inyección SQL. - Se enlazan los valores de las variables a los marcadores de posición utilizando la función
bindParam
. - Se ejecuta la consulta SQL.
- Se prepara una consulta SQL para insertar los datos en la tabla
- Redirección con mensaje de éxito:
- Si la inserción se realiza correctamente, se muestra un mensaje de éxito y se redirige a la página principal.
Gestión de errores:
- Excepciones:
- Se utiliza un bloque try-catch para atrapar cualquier excepción que pueda ocurrir durante la ejecución de la consulta SQL.
- Si se produce una excepción, se muestra un mensaje de error y se redirige a la página principal.
- Acceso no permitido:
- Si la solicitud no es de tipo POST, se muestra un mensaje de error «Acceso no permitido» y se redirige a la página principal.
Cierre de la conexión a la base de datos:
conn = null
: Se cierra la conexión a la base de datos al finalizar el script.
Función de redirección:
redirectToIndex($message)
: Esta función muestra un mensaje de alerta JavaScript. Después se redirige al usuario a la página principal del registro de transacciones exportando a Excel utilizando un timestamp para evitar el almacenamiento en caché del navegador.
Archivo delete-product.php

<?php // Incluye el archivo de conexión a la base de datos include("../connection/con.php"); // Verifica si se ha proporcionado un ID de producto a eliminar if (isset($_GET['product'])) { // Obtiene el ID del producto de la URL $productID = $_GET['product']; try { // Prepara la consulta para eliminar el producto con el ID especificado $stmt = $conn->prepare("DELETE FROM tbl_product WHERE tbl_product_id = :tbl_product_id"); // Enlaza el parámetro del ID del producto $stmt->bindParam(':tbl_product_id', $productID, PDO::PARAM_INT); // Ejecuta la consulta para eliminar el producto $stmt->execute(); // Redirige de vuelta al index con un mensaje de éxito redirectToIndex("Registro eliminado correctamente!!!"); } catch (PDOException $e) { // En caso de error, muestra un mensaje de error $errorMessage = "Error al eliminar el registro: " . $e->getMessage(); redirectToIndex($errorMessage); } } else { // Si no se proporciona un ID de producto, redirige de vuelta al index con un mensaje de error redirectToIndex("ID de producto no especificado."); } // Función para redirigir de vuelta al index con un mensaje function redirectToIndex($message) { echo "<script> alert('$message'); window.location.href = 'http://localhost/inventario-excel/'; </script>"; exit(); } // Cierra la conexión a la base de datos $conn = null; ?>
Inclusión del archivo de conexión:
include("../connection/con.php");
: Este código incluye el archivocon.php
que contiene la información necesaria para establecer la conexión con la base de datos.
Verificación del ID del producto:
if (isset($_GET['product'])) { ... }
: Este código verifica si se ha pasado un parámetroproduct
en la URL.
Obtención del ID del producto:
$productID = $_GET['product'];
: Obtiene el valor del parámetroproduct
y se asigna a la variable$productID
.
Eliminación del registro:
- Preparación de la consulta:
- Se crea una consulta SQL
DELETE
para eliminar el registro de la tablatbl_product
con el ID especificado. - Utilizaremos un marcador de posición
:tbl_product_id
para evitar la inyección SQL.
- Se crea una consulta SQL
- Enlace del parámetro:
- Se utiliza la función
bindParam
para enlazar el valor de la variable$productID
al marcador de posición:tbl_product_id
.
- Se utiliza la función
- Ejecución de la consulta:
- Se ejecuta la consulta SQL para eliminar el registro.
Redirección con mensaje:
redirectToIndex("Registro eliminado correctamente!!!");
: Se utiliza la funciónredirectToIndex
para redirigir al usuario a la página principal con un mensaje de éxito.
Gestión de errores:
- Bloque try-catch:
- Se utiliza un bloque try-catch para atrapar cualquier excepción que pueda ocurrir durante la ejecución de la consulta SQL.
- Si se produce una excepción, se muestra un mensaje de error y se redirige al usuario a la página principal.
Redirección en caso de error:
redirectToIndex("ID de producto no especificado.");
: Si no se ha especificado un ID de producto en la URL, se redirige al usuario a la página principal con un mensaje de error.
Cierre de la conexión a la base de datos:
$conn = null;
: Se cierra la conexión a la base de datos al finalizar el script.
Función de redirección:
redirectToIndex($message)
: Esta función muestra un mensaje de alerta JavaScript y luego redirige al usuario a la página principal.
El código PHP se encarga de eliminar un registro de la tabla tbl_product
en base al ID del producto proporcionado en la URL. Si la eliminación se realiza correctamente, se muestra un mensaje de éxito y se redirige al usuario a la página principal del registro de transacciones con exportación a Excel. En caso de error, se muestra un mensaje informativo y se redirige al usuario a la página principal.
Archivo update-product.php

<?php // Incluye el archivo de conexión a la base de datos include("../connection/con.php"); // Verifica si la solicitud es de tipo POST if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Verifica si se han proporcionado todos los datos necesarios if (isset($_POST['product_name'], $_POST['product_code'], $_POST['quantity'], $_POST['price'], $_POST['people'], $_POST['nif_cif'])) { // Obtiene los datos del formulario $productID = $_POST['tbl_product_id']; $productName = $_POST['product_name']; $productCode = $_POST['product_code']; $quantity = $_POST['quantity']; $price = $_POST['price']; // Reemplazamos las , por . en el precio para guardarlo en la base de datos $price = str_replace(',', '.', $price); // Elimina el símbolo de moneda del precio $price = preg_replace('/[^0-9,.]/', '', $price); // Reemplaza las comas por puntos en el precio $price = str_replace(',', '.', $price); // Obtiene la fecha actual $date = date("Y-m-d H:i:s"); // Obtiene los datos restantes del formulario $transaction = $_POST['transaction']; $people = $_POST['people']; $nif_cif = $_POST['nif_cif']; $notes = $_POST['notes']; try { // Prepara la consulta para actualizar el registro en la base de datos $stmt = $conn->prepare("UPDATE tbl_product SET product_name = :product_name, product_code = :product_code, quantity = :quantity, price = :price, date = :date, transaction = :transaction, people = :people, nif_cif = :nif_cif, notes = :notes WHERE tbl_product_id = :tbl_product_id"); // Enlaza los parámetros de la consulta $stmt->bindParam(":tbl_product_id", $productID, PDO::PARAM_INT); $stmt->bindParam(":product_name", $productName, PDO::PARAM_STR); $stmt->bindParam(":product_code", $productCode, PDO::PARAM_STR); $stmt->bindParam(":quantity", $quantity, PDO::PARAM_STR); $stmt->bindParam(":price", $price, PDO::PARAM_STR); $stmt->bindParam(":date", $date, PDO::PARAM_STR); $stmt->bindParam(":transaction", $transaction, PDO::PARAM_STR); $stmt->bindParam(":people", $people, PDO::PARAM_STR); $stmt->bindParam(":nif_cif", $nif_cif, PDO::PARAM_STR); $stmt->bindParam(":notes", $notes, PDO::PARAM_STR); // Ejecuta la consulta $stmt->execute(); // Redirige de vuelta al index con un mensaje de éxito redirectToIndex("Registro actualizado correctamente!!!"); } catch (PDOException $e) { // En caso de error, muestra un mensaje de error $errorMessage = "Error al actualizar el registro: " . $e->getMessage(); redirectToIndex($errorMessage); } } else { // Si no se proporcionan todos los datos, muestra un mensaje de error redirectToIndex("Debes rellenar todas las casillas!!!"); } } // Función para redirigir de vuelta al index con un mensaje function redirectToIndex($message) { $timestamp = time(); echo "<script> alert('$message'); window.location.href = 'http://localhost/inventario-excel/?timestamp=$timestamp'; </script>"; exit(); }
Inclusión del archivo de conexión:
include("../connection/con.php");
: Este código incluye el archivocon.php
que contiene la información necesaria para establecer la conexión con la base de datos.
Verificación del método de solicitud:
if ($_SERVER['REQUEST_METHOD'] === 'POST') { ... }
: El código verifica si la solicitud que se ha enviado al servidor es de tipo POST, lo cual indica que se pretende actualizar un registro en la base de datos.
Validación de los datos del formulario:
if (isset($_POST['product_name'], $_POST['product_code'], $_POST['quantity'], $_POST['price'], $_POST['people'], $_POST['nif_cif'])) { ... }
: Este código verifica si se han proporcionado todos los datos necesarios para actualizar el registro.
Obtención de los datos del formulario:
- Se obtienen los valores de los campos del formulario desde la variable
$_POST
. - Se formatea el precio, reemplazando las comas por puntos y eliminando el símbolo de moneda, para ajustarlo a la forma en que se almacena en la base de datos.
- Obtendremos la fecha actual utilizando la función
date
.
Actualización del registro:
- Preparación de la consulta:
- Se crea una consulta SQL
UPDATE
para actualizar el registro en la tablatbl_product
. - Se utilizan marcadores de posición para evitar la inyección SQL.
- Se crea una consulta SQL
- Enlace de los parámetros:
- Se utiliza la función
bindParam
para enlazar los valores de las variables a los marcadores de posición de la consulta.
- Se utiliza la función
- Ejecución de la consulta:
- Ejecutamos la consulta SQL para actualizar el registro.
Redirección con mensaje:
redirectToIndex("Registro actualizado correctamente!!!");
: Se utiliza la funciónredirectToIndex
para redirigir al usuario a la página principal con un mensaje de éxito.
Gestión de errores:
- Bloque try-catch:
- Se utiliza un bloque try-catch para atrapar cualquier excepción que pueda ocurrir durante la ejecución de la consulta SQL.
- En caso de producirse una excepción, se muestra un mensaje de error y se redirige al usuario a la página principal.
Redirección en caso de datos incompletos:
redirectToIndex("Debes rellenar todas las casillas!!!");
: Si no se han proporcionado todos los datos necesarios, se redirige al usuario a la página principal con un mensaje de error.
Función de redirección:
redirectToIndex($message)
: Esta función muestra un mensaje de alerta JavaScript y luego redirige al usuario a la página principal.
Este código PHP se encarga de actualizar un registro en la tabla tbl_product
con los datos proporcionados en el formulario. Si la actualización se realiza correctamente, se muestra un mensaje de éxito y se redirige al usuario a la página principal del registro de transacciones con exportación a Excel. En caso de error o si no se han proporcionado todos los datos necesarios, se muestra un mensaje informativo y se redirige al usuario a la página principal.
Archivo script.js

$(document).ready(function() { $('.product-table').DataTable(); }); function updateProduct(id) { $("#updateProductModal").modal("show"); let updateProductID = $("#productID-" + id).text(); let updateClient = $("#people-" + id).text(); let updateNif_Cif = $("#nif_cif-" + id).text(); let updateTransaction = $("#transaction-" + id).text(); let updateProductName = $("#productName-" + id).text(); let updateProductCode = $("#productCode-" + id).text(); let updateQuantity = $("#quantity-" + id).text(); let updatePrice = $("#price-" + id).text(); let updateNotes = $("#notes-" + id).text(); console.log($("#notes-" + id).text()) // Eliminar el símbolo € del precio updatePrice = updatePrice.replace('€', ''); $("#updateProductID").val(updateProductID); $("#updateClient").val(updateClient); $("#updateNif_Cif").val(updateNif_Cif); $("#updateTransaction").val(updateTransaction); $("#updateProductName").val(updateProductName); $("#updateProductCode").val(updateProductCode); $("#updateQuantity").val(updateQuantity); $("#updatePrice").val(updatePrice); $("#updateNotes").val(updateNotes); } function deleteProduct(id) { if (confirm('Deseas eliminar este producto?')) { window.location.href = "./actions/delete-product.php?product=" + id; } } function exportToExcel() { // Get table data var table = $('.product-table').DataTable(); var data = table.rows().data().toArray(); // Remove the "Action" column from the data var filteredData = data.map(function(row) { return row.slice(0, row.length - 1); }); // Create a worksheet var ws = XLSX.utils.aoa_to_sheet([ ['ID', 'Producto', 'Código', 'Cantidad', 'Precio', 'Total','Fecha', 'Trasacción', 'Cliente', 'NIF/CIF', 'Notas'] ].concat(filteredData)); // Create a workbook var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'entreunosyceros.net'); // Save the workbook as an Excel file XLSX.writeFile(wb, 'inventario-excel.xlsx'); }
Este fragmento de código se compone de tres partes principales:
Inicialización de DataTable:
- Este código utiliza la librería JavaScript jQuery.
- Espera a que el documento esté completamente cargado (
$(document).ready(function() { ... })
). - Luego, selecciona el elemento con la clase
product-table
y le aplica el plugin DataTable. - El plugin DataTable se utiliza para mejorar la funcionalidad de la tabla de lista de productos, agregando características como clasificación, filtrado y paginación.
Funcionalidad de actualización de productos:
- Esta función,
updateProduct
, toma un parámetroid
que representa el identificador único del producto. - Muestra el elemento modal con el ID
updateProductModal
usando la funciónmodal
de jQuery, abriendo una ventana modal para actualizar la información del producto. - Luego, recupera valores de varios elementos HTML con IDs específicos que corresponden a los detalles del producto mostrados en la tabla.
- Los valores recuperados se establecen luego como valores de los campos de formulario correspondientes en el modal de actualización usando selectores jQuery.
Funcionalidad de eliminación y exportación de este registro de transacciones exportando a Excel
- La función
deleteProduct
toma un parámetroid
y utiliza un cuadro de diálogo de confirmación antes de redirigir al usuario al scriptdelete-product.php
, es responsable de eliminar el producto de la base de datos. - La función
exportToExcel
recupera datos de la tablaproduct-table
utilizando el plugin DataTable. - Luego filtra los datos para eliminar la columna «Acción» antes de crear un libro de Excel utilizando la librería XLSX.
- Finalmente, guarda la hoja como un archivo llamado
inventario-excel.xlsx
.
Este fragmento de código demuestra funcionalidades relacionadas con la gestión de una lista de productos mostrada en una tabla. Utiliza librerías JavaScript como jQuery y, potencialmente, DataTable y XLSX para lograr funcionalidades como mejoras en la tabla, interacción con modales, actualizaciones, eliminaciones de productos y exportación de datos en este registro de transacciones exportando a Excel.
Archivo style.css
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500&display=swap'); * { margin: 0; padding: 0; font-family: 'Poppins', sans-serif !important; } .main { display: flex; flex-direction: column; align-items: center; } .header { text-align: center; font-size: 40px; font-weight: bold; padding: 20px; width: 100%; } .alert-dark { color: #e9e9e9 !important; background-color: #60007a !important; } .product-container { position: relative; /* Importante para que los elementos absolutos se posicionen en relación con este contenedor */ display: flex; flex-direction: column; align-items: center; width: 80%; height: auto; /* Permitir que el contenedor crezca según el contenido */ min-height: 680px; /* Establecer una altura mínima si es necesario */ margin: 35px; margin-top: 35px; border-radius: 20px; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; background: #fff; } .product-header { display: flex; align-items: center; width: 95%; height: 50px; margin-top: 30px; } .product-header h3 { font-weight: bold; } .dataTables_wrapper { position: relative; /* Importante para que los elementos absolutos se posicionen en relación con este contenedor */ width: 95% !important; box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; padding: 20px; border-radius: 10px; height: 83%; text-align: center !important; margin-bottom: 50px; /* Añade margen inferior para dejar espacio para la paginación */ } .dataTables_info { position: absolute; /* Cambiado a absolute */ bottom: -45px; /* Mueve la paginación hacia abajo */ left: 10px; /* Ajusta la posición izquierda */ clear: both !important; float: left !important; padding-top: 0.755em; } .dataTables_paginate { position: absolute; /* Cambiado a absolute */ bottom: -45px; /* Mueve la paginación hacia abajo */ right: 0px; /* Ajusta la posición derecha */ } table.dataTable thead>tr>th.sorting, table.dataTable thead>tr>th.sorting_asc, table.dataTable thead>tr>th.sorting_desc, table.dataTable thead>tr>th.sorting_asc_disabled, table.dataTable thead>tr>th.sorting_desc_disabled, table.dataTable thead>tr>td.sorting, table.dataTable thead>tr>td.sorting_asc, table.dataTable thead>tr>td.sorting_desc, table.dataTable thead>tr>td.sorting_asc_disabled, table.dataTable thead>tr>td.sorting_desc_disabled { text-align: center; } td { text-align: center; } .modal-content { margin-top: 100px !important; } label, .modal-title{ font-weight: bold !important; } .aclaration{ font-size: small; text-align: center; } /* Estilos generales */ .product-container { margin-top: 20px; } .product-header { display: flex; align-items: center; justify-content: space-between; } .button-group { display: flex; align-items: center; } .footer { position: fixed; bottom: 0; width: 100%; background-color: #f8f8f8; padding: 10px 0; text-align: center; z-index: -1; box-shadow: rgba(0, 0, 0, 0.35) 15px 5px 15px; }
Importación de fuente:
- Esta línea importa la fuente
Poppins
de Google Fonts, especificando un grosor de 500 (mediana). - El parámetro
display=swap
asegura que la fuente se intercambie rápidamente, lo que puede mejorar la velocidad de renderizado inicial.
Estilos globales:
- Este bloque aplica estilos a todos los elementos (
*
) del documento. - Establece
margin
ypadding
a 0 para eliminar los márgenes y rellenos predeterminados del navegador. - La
font-family
la pondremos comoPoppins
, consans-serif
como alternativa siPoppins
no está disponible. La regla!important
asegura que este estilo anule cualquier estilo conflictivo.
El .main
:
- Esta define estilos para elementos con la clase
main
. - Pondremos la visualización como
flex
, organizando los elementos hijos horizontalmente en una columna. - Establece la
flex-direction
comocolumn
, apilando los elementos verticalmente uno tras otro. - Establece
align-items
comocenter
, centrando horizontalmente los elementos dentro del contenedor.
Clase .header
:
- Esta define estilos para elementos con la clase
header
. - Establece la alineación del texto como
center
. - Se establece el tamaño de la fuente a 40 píxeles para encabezados más grandes.
- Establece el grosor de la fuente como
bold
para una mayor presencia. - Agrega 20 píxeles de relleno en todos los lados para el espaciado.
- Establece el ancho al 100%.
Clase .alert-dark
:
- Esta define estilos para elementos con la clase
alert-dark
. Estos podrían ser cuadros de alerta o elementos destinados a tener una apariencia oscura e informativa. - Pondremos el color del texto a un gris claro (
#e9e9e9
) usando!important
para anular los valores predeterminados. - Establece el color de fondo a un azul oscuro (
#60007a
) usando!important
para una posible anulación.
.product-container
:
- Esta define estilos para elementos con la clase
product-container
, que probablemente representa un contenedor para información de productos. - Establece la
position
comorelative
para permitir el posicionamiento absoluto de los elementos hijos dentro. - Usa propiedades flexbox similares (
display
,flex-direction
,align-items
) como la clase.main
para el diseño. - Se establece el ancho al 80% del visor.
- Establece la altura como
auto
para adaptarse al contenido, con unamin-height
mínima de 680px para la consistencia. - Agrega márgenes de 35px en todos los lados y un margen superior adicional de 35px para un posible espaciado.
La clase .product-header
:
- Define estilos para elementos con la clase
product-header
. - Establece la
display
comoflex
para organizar los elementos hijos horizontalmente. - Centra los elementos verticalmente con
align-items: center
. - Establece el ancho al 95% del contenedor padre.
- Establece la altura a 50px para una altura fija del encabezado.
- Agrega un margen superior de 30px para separar el encabezado del contenido.
- Anida un selector
h3
dentro de.product-header
para aplicar un estilo defont-weight: bold
al título del encabezado.
Estilos específicos de la tabla:
- Se definen estilos específicos para la tabla utilizando clases y selectores específicos de DataTables.
- Se establece la
position
comorelative
para el contenedordataTables_wrapper
para permitir el posicionamiento absoluto de elementos dentro. - Ajustaremos el ancho al 95% y se aplica un
!important
para anular posibles estilos conflictivos. - Añadimos una sombra de caja sutil.
- Se establece un margen inferior de 50px para dejar espacio para la paginación.
- Se reposicionan los elementos de información y paginación (
dataTables_info
ydataTables_paginate
) usandoposition: absolute
y ajustandobottom
,left
yright
. - Se centra el texto de las cabeceras y celdas de la tabla usando
text-align: center
.
Con esto, aun que no está todo el código, creo que le hemos dado un vistazo por encima a las partes más importantes de esta interfaz web para el registro de transacciones exportando a Excel. El código fuente al completo se pueden ver en el repositorio de GitHub en el que he subido el proyecto.
Si quieres ver este registro de transacciones exportando a Excel funcionando, puedes dirigirte a la sección de ejemplos que hay disponible en esta página web.