La página principal para la confección de la factura se encuentra en el archivo 'facturacion.php'. Inmediatamente ingresamos a la página se muestra una interfaz visual similar a:
Se muestra el número de factura a emitir, la fecha de emisión de la factura, el control select para buscar el cliente a quien se le facturará. En la parte inferior disponemos de dos botones, uno para agregar productos y otro para finalizar la factura.
facturacion.php<!doctype html> <html> <head> <title>Facturación</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="css/bootstrap.min.css"> <script src="js/jquery.min.js"></script> <script src="js/popper.min.js"></script> <script src="js/bootstrap.min.js"></script> </head> <body> <?php require("conexion.php"); $con = retornarConexion(); $consulta = mysqli_query($con, "insert into facturas() values ()") or die(mysqli_error($con)); $codigofactura = mysqli_insert_id($con); ?> <div class="container"> <div class="row mt-4"> <div class="col-md"> <div class="form-group row"> <label for="CodigoFactura" class="col-lg-3 col-form-label">Número de factura:</label> <div class="col-lg-3"> <input type="text" disabled class="form-control" id="CodigoFactura" value="<?php echo $codigofactura; ?>"> </div> </div> <div class="form-group row"> <label for="Fecha" class="col-lg-3 col-form-label">Fecha de emisión:</label> <div class="col-lg-3"> <input type="date" class="form-control" id="Fecha"> </div> </div> <div class="form-group row"> <label for="CodigoCliente" class="col-lg-3 col-form-label">Cliente:</label> <div class="col-lg-3"> <select class="form-control" id="CodigoCliente"> <?php $consulta = mysqli_query($con, "select codigo, nombre from clientes") or die(mysqli_error($con)); $clientes = mysqli_fetch_all($consulta, MYSQLI_ASSOC); echo "<option value='0'>Seleccionar Cliente</option>"; foreach ($clientes as $cli) { echo "<option value='" . $cli['codigo'] . "'>" . $cli['nombre'] . "</option>"; } ?> </select> </div> </div> </div> </div> <div class="row mt-4"> <div class="col-md"> <table class="table table-striped"> <thead> <tr> <th>Código de Artículo</th> <th>Descripción</th> <th class="text-right">Cantidad</th> <th class="text-right">Precio Unitario</th> <th class="text-right">Total</th> <th class="text-right"></th> </tr> </thead> <tbody id="DetalleFactura"> </tbody> </table> <button type="button" id="btnAgregarProducto" class="btn btn-success">Agregar Producto</button> <button type="button" id="btnTerminarFactura" class="btn btn-success">Terminar Factura</button> </div> </div> </div> <!-- ModalProducto(Agregar) --> <div class="modal fade" id="ModalProducto" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <label>Producto:</label> <select class="form-control" id="CodigoProducto"> <?php $consulta = mysqli_query($con, "select codigo, descripcion, precio from productos") or die(mysqli_error($con)); $productos = mysqli_fetch_all($consulta, MYSQLI_ASSOC); foreach ($productos as $pro) { echo "<option value='" . $pro['codigo'] . "'>" . $pro['descripcion'] . ' ($' . $pro['precio'] . ")</option>"; } ?> </select> </div> <div class="form-row"> <div class="form-group col-md-12"> <label>Cantidad:</label> <input type="number" id="Cantidad" class="form-control" placeholder="" min="1"> </div> </div> </div> <div class="modal-footer"> <button type="button" id="btnConfirmarAgregarProducto" class="btn btn-success">Agregar a la factura</button> <button type="button" data-dismiss="modal" class="btn btn-success">Cancelar</button> </div> </div> </div> </div> <!-- ModalFinFactura --> <div class="modal fade" id="ModalFinFactura" tabindex="-1" role="dialog"> <div class="modal-dialog" style="max-width: 600px" role="document"> <div class="modal-content"> <div class="modal-header"> <h1>Acciones</h1> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-footer"> <button type="button" id="btnConfirmarFactura" class="btn btn-success">Confirmar Factura</button> <button type="button" id="btnConfirmarImprimirFactura" class="btn btn-success">Confirmar e Imprimir Factura</button> <button type="button" id="btnConfirmarDescartarFactura" class="btn btn-success">Descartar la Factura</button> </div> </div> </div> </div> <!-- ModalConfirmarBorrar --> <div class="modal fade" id="ModalConfirmarBorrar" tabindex="-1" role="dialog"> <div class="modal-dialog" style="max-width: 600px" role="document"> <div class="modal-content"> <div class="modal-header"> <h1>¿Realmente quiere borrarlo?</h1> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-footer"> <button type="button" id="btnConfirmarBorrado" class="btn btn-success">Confirmar</button> <button type="button" data-dismiss="modal" class="btn btn-success">Cancelar</button> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { var producto; var cliente; document.getElementById('Fecha').valueAsDate = new Date(); //Boton que muestra el diálogo de agregar producto $('#btnAgregarProducto').click(function() { LimpiarFormulario(); $("#Cantidad").val("1"); $("#ModalProducto").modal(); }); //Boton que agrega el producto al detalle $('#btnConfirmarAgregarProducto').click(function() { RecolectarDatosFormulario(); $("#ModalProducto").modal('hide'); if ($("#Cantidad").val() == "") { //Controlamos que no esté vacío la cantidad de productos alert('no puede estar vacío la cantidad de productos.'); return; } EnviarInformacionProducto("agregar"); }); //Boton terminar factura $('#btnTerminarFactura').click(function() { $("#ModalFinFactura").modal(); }); //Boton confirmar factura $('#btnConfirmarFactura').click(function() { if ($('#CodigoCliente').val() == 0) { alert('Debe seleccionar un cliente'); return; } RecolectarDatosCliente(); EnviarInformacionFactura("confirmarfactura"); }); //Boton que descarta la factura generada borrando tanto en la tabla de facturas como detallefactura $('#btnConfirmarDescartarFactura').click(function() { RecolectarDatosCliente(); EnviarInformacionFactura("confirmardescartarfactura"); }); //Boton confirmar factura y ademas genera pdf $('#btnConfirmarImprimirFactura').click(function() { if ($('#CodigoCliente').val() == 0) { alert('Debe seleccionar un cliente'); return; } RecolectarDatosCliente(); EnviarInformacionFacturaImprimir("confirmarfactura"); }); function RecolectarDatosFormulario() { producto = { codigoproducto: $('#CodigoProducto').val(), cantidad: $('#Cantidad').val() }; } function RecolectarDatosCliente() { cliente = { codigocliente: $('#CodigoCliente').val(), fecha: $('#Fecha').val() }; } //Funciones AJAX para enviar y recuperar datos del servidor //******************************************************* function EnviarInformacionProducto(accion) { $.ajax({ type: 'POST', url: 'procesar.php?accion=' + accion + '&codigofactura=' + <?php echo $codigofactura ?>, data: producto, success: function(msg) { RecuperarDetalle(); }, error: function() { alert("Hay un error .."); } }); } function EnviarInformacionFactura(accion) { $.ajax({ type: 'POST', url: 'procesar.php?accion=' + accion + '&codigofactura=' + <?php echo $codigofactura ?>, data: cliente, success: function(msg) { window.location = 'index.php'; }, error: function() { alert("Hay un error .."); } }); } function EnviarInformacionFacturaImprimir(accion) { $.ajax({ type: 'POST', url: 'procesar.php?accion=' + accion + '&codigofactura=' + <?php echo $codigofactura ?>, data: cliente, success: function(msg) { window.open('pdffactura.php?' + '&codigofactura=' + <?php echo $codigofactura ?>, '_blank'); window.location = 'index.php'; }, error: function() { alert("Hay un error .."); } }); } function LimpiarFormulario() { $('#Cantidad').val(''); } }); //Se ejecuta cuando se presiona un boton de borrar un item del detalle var cod; function borrarItem(coddetalle) { cod = coddetalle; $("#ModalConfirmarBorrar").modal(); } $('#btnConfirmarBorrado').click(function() { $("#ModalConfirmarBorrar").modal('hide'); $.ajax({ type: 'POST', url: 'borrarproductodetalle.php?codigo=' + cod, success: function(msg) { RecuperarDetalle(); }, error: function() { alert("Hay un error .."); } }); }); function RecuperarDetalle() { $.ajax({ type: 'GET', url: 'recuperardetalle.php?codigofactura=' + <?php echo $codigofactura ?>, success: function(datos) { document.getElementById('DetalleFactura').innerHTML = datos; }, error: function() { alert("Hay un error .."); } }); } </script> </body> </html>
Cuando se carga la página se genera en forma automática el número de factura:
<?php require("conexion.php"); $con = retornarConexion(); $consulta = mysqli_query($con, "insert into facturas() values ()") or die(mysqli_error($con)); $codigofactura = mysqli_insert_id($con); ?>
Ejecutamos un comando SQL insert sin indicar valores, luego se genera y carga solo el campo 'codigo', dejando para después la actualización de los campos 'fecha' y 'codigocliente'.
Mostramos el código de factura en un control input desactivo, con el objetivo que el usuario no lo pueda modificar:
<input type="text" disabled class="form-control" id="CodigoFactura" value="<?php echo $codigofactura; ?>">
Mediante un control HTML 'select' mostramos todos los clientes para que el usuario lo pueda seleccionar:
<div class="form-group row"> <label for="CodigoCliente" class="col-lg-3 col-form-label">Cliente:</label> <div class="col-lg-3"> <select class="form-control" id="CodigoCliente"> <?php $consulta = mysqli_query($con, "select codigo, nombre from clientes") or die(mysqli_error($con)); $clientes = mysqli_fetch_all($consulta, MYSQLI_ASSOC); echo "<option value='0'>Seleccionar Cliente</option>"; foreach ($clientes as $cli) { echo "<option value='" . $cli['codigo'] . "'>" . $cli['nombre'] . "</option>"; } ?> </select> </div> </div>
Cuando se presiona el botón 'Agregar producto' se activa el diálogo donde debemos seleccionar el producto y la cantidad del mismo:
El evento del botón es:
$('#btnAgregarProducto').click(function() { LimpiarFormulario(); $("#Cantidad").val("1"); $("#ModalProducto").modal(); });
Llamamos a la funcón de 'LimpiarFormulario' para borrar datos de selecciones previas, fijamos la cantidad de productos en uno y hacemos visible el diálogo.
Cuando presionamos el botón del diálogo 'Agregar a la factura' se dispara el evento:
$('#btnConfirmarAgregarProducto').click(function() { RecolectarDatosFormulario(); $("#ModalProducto").modal('hide'); if ($("#Cantidad").val() == "") { //Controlamos que no esté vacío la cantidad de productos alert('no puede estar vacío la cantidad de productos.'); return; } EnviarInformacionProducto("agregar"); });
Recuperamos los datos del formulario, ocultamos el diálogo, controlamos que haya cargado un valor en la cantidad de productos y llamamos finalmente a la función 'EnviarInformaciónProducto' pasando el parámetro de "agregar".
La función de EnviarInformacionProducto se comunica mediante AJAX al servidor para que se agregue el producto seleccionado:
function EnviarInformacionProducto(accion) { $.ajax({ type: 'POST', url: 'procesar.php?accion=' + accion + '&codigofactura=' + <?php echo $codigofactura ?>, data: producto, success: function(msg) { RecuperarDetalle(); }, error: function() { alert("Hay un error .."); } }); }
Si pasamos la acción 'agregar' luego el archivo 'procesar.php' se ejecuta:
<?php header('Content-Type: application/json'); require("conexion.php"); $conexion = retornarConexion(); switch ($_GET['accion']) { case 'agregar': //Recuperamos el precio del producto $respuesta = mysqli_query($conexion, "select precio from productos where codigo=".$_POST['codigoproducto']); $reg=mysqli_fetch_array($respuesta); $respuesta = mysqli_query($conexion, "insert into detallefactura(codigofactura,codigoproducto,cantidad,precio) values ($_GET[codigofactura],$_POST[codigoproducto],$_POST[cantidad],$reg[precio])"); echo json_encode($respuesta); break; case 'confirmarfactura': $respuesta = mysqli_query($conexion, "update facturas set fecha='$_POST[fecha]', codigocliente=$_POST[codigocliente] where codigo=$_GET[codigofactura]"); echo json_encode($respuesta); break; case 'confirmardescartarfactura': $respuesta = mysqli_query($conexion, "delete from facturas where codigo=$_GET[codigofactura]"); $respuesta = mysqli_query($conexion, "delete from detallefactura where codigofactura=$_GET[codigofactura]"); echo json_encode($respuesta); } ?>
Inmediatamente el servidor nos informa que el producto fue agregado se ejectua la función 'RecuperarDetalle':
function RecuperarDetalle() { $.ajax({ type: 'GET', url: 'recuperardetalle.php?codigofactura=' + <?php echo $codigofactura ?>, success: function(datos) { document.getElementById('DetalleFactura').innerHTML = datos; }, error: function() { alert("Hay un error .."); } }); }
La función 'RecuperarDetalle' ejecuta el algoritmo contenido en el archivo 'recuperardetalle.php':
<?php require("conexion.php"); $conexion = retornarConexion(); $datos = mysqli_query($conexion, "select pro.codigo as codigo, descripcion, round(deta.precio,2) as precio, cantidad, round(deta.precio*cantidad,2) as preciototal, deta.codigo as coddetalle from detallefactura as deta join productos as pro on pro.codigo=deta.codigoproducto where codigofactura=$_GET[codigofactura]") or die(mysqli_error($conexion)); $resultado = mysqli_fetch_all($datos, MYSQLI_ASSOC); $pago=0; foreach ($resultado as $fila) { echo "<tr>"; echo "<td>$fila[codigo]</td>"; echo "<td>$fila[descripcion]</td>"; echo "<td class=\"text-right\">$fila[cantidad]</td>"; echo "<td class=\"text-right\">$fila[precio]</td>"; echo "<td class=\"text-right\">$fila[preciototal]</td>"; echo '<td class="text-right"><a class="btn btn-primary" onclick="borrarItem('.$fila['coddetalle'].')" role="button" href="#" id="'.$fila['coddetalle'].'">Borra?</a></td>'; echo "</tr>"; $pago=$pago+$fila['preciototal']; } echo "<tr>"; echo "<td></td>"; echo "<td></td>"; echo "<td></td>"; echo "<td class=\"text-right\"><strong>Importe total</strong></td>"; echo "<td class=\"text-right\"><strong>".number_format(round($pago,2),2,'.','')."</strong></td>"; echo "<td></td>"; echo "</tr>"; ?>
La función recuperar detalle modifica la tabla HTML donde se muestran todos los productos seleccionados hasta ese momento:
success: function(datos) { document.getElementById('DetalleFactura').innerHTML = datos; },
En pantalla podemos ver todos los productos seleccionados y sus cantidades:
En cualquier momento podemos eliminar un producto de la factura presionando el botón 'Borra?', en dicha situación se dispara la función:
//Se ejecuta cuando se presiona un boton de borrar un item del detalle var cod; function borrarItem(coddetalle) { cod = coddetalle; $("#ModalConfirmarBorrar").modal(); }
Se nos muestra un diálogo para confirma su eliminación:
En caso de confirma su eliminación se ejecuta la función:
$('#btnConfirmarBorrado').click(function() { $("#ModalConfirmarBorrar").modal('hide'); $.ajax({ type: 'POST', url: 'borrarproductodetalle.php?codigo=' + cod, success: function(msg) { RecuperarDetalle(); }, error: function() { alert("Hay un error .."); } }); });
Donde se informa al servidor el código del detalle de factura a eliminar, dicha acción la ejecuta la página 'borrarproductodetalle.php':
<?php header('Content-Type: application/json'); require("conexion.php"); $conexion = retornarConexion(); $respuesta = mysqli_query($conexion, "delete from detallefactura where codigo=".$_GET['codigo']); echo json_encode($respuesta); ?>
Cuando se presiona el botón 'Terminar factura' se muestra un diálogo que nos permite:
Confirmar factura:
$('#btnConfirmarFactura').click(function() { if ($('#CodigoCliente').val() == 0) { alert('Debe seleccionar un cliente'); return; } RecolectarDatosCliente(); EnviarInformacionFactura("confirmarfactura"); });
Donde se comunica con el servidor mediante AJAX para dejar firme la factura generada:
case 'confirmarfactura': $respuesta = mysqli_query($conexion, "update facturas set fecha='$_POST[fecha]', codigocliente=$_POST[codigocliente] where codigo=$_GET[codigofactura]"); echo json_encode($respuesta); break;
Confirmar e imprimir factura (similar al primer botón, con la salvedad que se agrega la llamada a la generación del PDF de la factura):
$('#btnConfirmarImprimirFactura').click(function() { if ($('#CodigoCliente').val() == 0) { alert('Debe seleccionar un cliente'); return; } RecolectarDatosCliente(); EnviarInformacionFacturaImprimir("confirmarfactura"); });
En la función 'EnviarInformacionFacturaImprimir' se llama a la página que genera el PDF:
function EnviarInformacionFacturaImprimir(accion) { $.ajax({ type: 'POST', url: 'procesar.php?accion=' + accion + '&codigofactura=' + , data: cliente, success: function(msg) { window.open('pdffactura.php?' + '&codigofactura=' + , '_blank'); window.location = 'index.php'; }, error: function() { alert("Hay un error .."); } }); }
Por último el botón de 'Descartar la factura' dispara el evento:
$('#btnConfirmarDescartarFactura').click(function() { RecolectarDatosCliente(); EnviarInformacionFactura("confirmardescartarfactura"); });
Se comunica con el servidor para que borre la factura actual y todos los productos añadidos hasta el momento:
case 'confirmardescartarfactura': $respuesta = mysqli_query($conexion, "delete from facturas where codigo=$_GET[codigofactura]"); $respuesta = mysqli_query($conexion, "delete from detallefactura where codigofactura=$_GET[codigofactura]"); echo json_encode($respuesta);