Está en la página 1de 29

Pequeño, muy pequeño sistema

de ventas con PHP y MySQL


Introducción
Tabla de contenido   

1 Introducción

2 Antes de comenzar

3 Actualización

3.1 Probar sistema terminado

3.1.1 Vista previa

3.1.2 Online

3.1.3 Descargar

3.2 Sistema de ventas en PHP

3.3 Base de datos

3.4 Productos

3.4.1 Nuevo

3.4.2 Listar productos

3.4.3 Editar producto

3.4.4 Guardar cambios

3.4.5 Eliminar producto

3.5 Vender

3.5.1 Interfaz para vender

3.5.2 Agregar al carrito de compra

3.5.3 Quitar del carrito

3.5.4 Terminar venta

3.5.5 Cancelar venta

3.6 Ventas

3.6.1 Listar

3.6.2 Eliminar

3.7 Encabezado y pie

3.8 Conclusión
Hace algunos días hice un ejercicio de un sistema de ventas en PHP. Está
escrito en puro PHP, nada de Javascript. Eso sí, para los estilos utilicé una
variante de Bootstrap.

Los archivos no tienen una estructura, pero como lo dije, es un ejemplo.


También escribo esto porque igual y le sirve a alguien más o me sirve a mí
mismo para algunas referencias.

Para la persistencia de datos utiliza MySQL. Guarda productos y ventas. No


maneja permisos de usuarios. Almacenamos el carrito de compras en la
sesión, y bueno, mejor lo explico por partes.

Antes de comenzar
Te invito a probar Sublime POS 3, un punto de venta evolutivo, gratuito y que
funciona en la nube.

Actualización
Ya he hecho la versión 2 de este sistema. No cambian muchas cosas en
cuanto a su uso, sino a su programación. Lo hice con CodeIgniter usando el
patrón MVC. Ver nueva versión aquí.

Nota: si eres principiante, recomiendo ver primero el sistema aquí presente.

Nota 2: mira este sistema pero corriendo sobre Android.


Probar sistema terminado
Vista previa
Así se ve el sistema terminado:

Online
Para darte una pequeña idea, puedes visitar este link para probar el sistema.
De igual forma míralo en YouTube:

Descargar
Si gustas descargarlo y probarlo en tu entorno local, aquí abajo lo dejo:

https://github.com/parzibyte/ventas_pdo/archive/master.zip

Sistema de ventas en PHP


Base de datos
Aquí dejo el esquema que se utilizó. También lleva algunos registros para
tener con qué trabajar al inicio.

DROP
DATABASE
IF
EXISTS
ventas;
CREATE DATABASE IF NOT EXISTS ventas;
USE ventas;
CREATE TABLE productos(
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
codigo VARCHAR(255) NOT NULL,
descripcion VARCHAR(255) NOT NULL,
precioVenta DECIMAL(5, 2) NOT NULL,
precioCompra DECIMAL(5, 2) NOT NULL,
existencia DECIMAL(5, 2) NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;

CREATE TABLE ventas(


id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
fecha DATETIME NOT NULL,
total DECIMAL(7,2),
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;

CREATE TABLE productos_vendidos(


id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
id_producto BIGINT UNSIGNED NOT NULL,
cantidad BIGINT UNSIGNED NOT NULL,
id_venta BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(id_producto) REFERENCES productos(id) ON DELETE CASCADE,
FOREIGN KEY(id_venta) REFERENCES ventas(id) ON DELETE CASCADE
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;
INSERT INTO productos(id, codigo, descripcion, precioVenta, precioCompra, existencia)
VALUES
(1, '1', 'Galletas chokis', 15, 10, 100),
(2, '2', 'Mermelada de fresa', 80, 65, 100),
(3, '3', 'Aceite', 20, 18, 100),
(4, '4', 'Palomitas de maíz', 15, 12, 100),
(5, '5', 'Doritos', 8, 5, 100);

Y para conectarnos utilizamos PDO. El código para lograr esto es el siguiente:

<?php

$contraseña = "1d3ed423r43crt34";
$usuario = "root";
$nombre_base_de_datos = "ventas";
try{
$base_de_datos = new PDO('mysql:host=localhost;dbname=' . $nombre_base_de_datos, $usuario, $contrase
$base_de_datos->query("set names utf8;");
$base_de_datos->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$base_de_datos->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$base_de_datos->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}catch(Exception $e){
echo "Ocurrió algo con la base de datos: " . $e->getMessage();
}
?>
Productos. Nuevo

Es un simple formulario que pide la información. Sólo pide el código de


barras, la descripción, el precio de venta, de compra y la existencia.

<?php
include_once
"encabezado.php
" ?>

<div class="col-xs-12">
<h1>Nuevo producto</h1>
<form method="post" action="nuevo.php">
<label for="codigo">Código de barras:</label>
<input class="form-control" name="codigo" required type="text" id="codigo" placeho

<label for="descripcion">Descripción:</label>
<textarea required id="descripcion" name="descripcion" cols="30" rows="5" class="f

<label for="precioVenta">Precio de venta:</label>


<input class="form-control" name="precioVenta" required type="number" id="precioVe
venta">

<label for="precioCompra">Precio de compra:</label>


<input class="form-control" name="precioCompra" required type="number" id="precioC
compra">

<label for="existencia">Existencia:</label>
<input class="form-control" name="existencia" required type="number" id="existenci
existencia">
<br><br><input class="btn btn-info" type="submit" value="Guardar">
</form>
</div>
<?php include_once "pie.php" ?>

Estamos incluyendo algunos archivos que son el encabezado y el pie. Estos


archivos serán explicados más abajo.

Al enviar el formulario, los datos son recibidos en un archivo llamado


nuevo.php. Este archivo tiene el siguiente código:

<?php include_once "encabezado.php" ?


>

#Salir si alguno de los datos no está presente


if(!isset($_POST["codigo"]) || !isset($_POST["descripcion"]) || !isset($_PO
isset($_POST["existencia"])) exit();

#Si todo va bien, se ejecuta esta parte del código...

include_once "base_de_datos.php";
$codigo = $_POST["codigo"];
$descripcion = $_POST["descripcion"];
$precioVenta = $_POST["precioVenta"];
$precioCompra = $_POST["precioCompra"];
$existencia = $_POST["existencia"];

$sentencia = $base_de_datos->prepare("INSERT INTO productos(codigo, descrip


?);");
$resultado = $sentencia->execute([$codigo, $descripcion, $precioVenta, $pre

if($resultado === TRUE){


header("Location: ./listar.php");
exit;
}

else echo "Algo salió mal. Por favor verifica que la tabla exista";

?>
<?php include_once "pie.php" ?>

Como se ve, no estamos haciendo ninguna validación y estamos insertando


los datos tal y como vienen del formulario. En caso de que todo vaya bien,
redirigimos a listar.php para que dé la ilusión de que el producto fue guardado
ahí mismo y fue añadido a la tabla.
Listar productos
Hablando de listar productos, aquí el código que dibuja la tabla:

<?php
include_once
"encabezado.php
" ?>
<?php
include_once "base_de_datos.php";
$sentencia = $base_de_datos->query("SELECT * FROM productos;");
$productos = $sentencia->fetchAll(PDO::FETCH_OBJ);
?>

<div class="col-xs-12">
<h1>Productos</h1>
<div>
<a class="btn btn-success" href="./formulario.php">Nuevo <i class="fa fa-pl
</div>
<br>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Código</th>
<th>Descripción</th>
<th>Precio de compra</th>
<th>Precio de venta</th>
<th>Existencia</th>
<th>Editar</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
<?php foreach($productos as $producto){ ?>
<tr>
<td><?php echo $producto->id ?></td>
<td><?php echo $producto->codigo ?></td>
<td><?php echo $producto->descripcion ?></td>
<td><?php echo $producto->precioCompra ?></td>
<td><?php echo $producto->precioVenta ?></td>
<td><?php echo $producto->existencia ?></td>
<td><a class="btn btn-warning" href="<?php echo "editar.php?
edit"></i></a></td>
<td><a class="btn btn-danger" href="<?php echo "eliminar.php
trash"></i></a></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<?php include_once "pie.php" ?>

Ahí mismo incluimos los botones que sirven para editar y eliminar el


producto, que no son más que enlaces a otra página. Por cierto, también
pusimos un botón para agregar un nuevo producto; igualmente es un link que
lleva al formulario.

Editar producto
Cuando hacemos click en el botón editar, nos redirige a otro archivo. En él,
leemos el id utilizando $_GET. Luego, hacemos una consulta a la base de
datos en donde el id sea el que leímos.
Si el producto no existe, lo indicamos. En caso de que sí, entonces dibujamos
el mismo formulario pero ahora lo llenamos con el atributo value de la
etiqueta input. Para el textarea es diferente, pues el contenido debe ir entre las
etiquetas, no como atributo.

<?php

if(!isset($_GET["id"])) exit();
$id = $_GET["id"];
include_once "base_de_datos.php";
$sentencia = $base_de_datos->prepare("SELECT * FROM productos WHERE id = ?;");
$sentencia->execute([$id]);
$producto = $sentencia->fetch(PDO::FETCH_OBJ);
if($producto === FALSE){
echo "¡No existe algún producto con ese ID!";
exit();
}

?>
<?php include_once "encabezado.php" ?>
<div class="col-xs-12">
<h1>Editar producto con el ID <?php echo $producto->id; ?></h1>
<form method="post" action="guardarDatosEditados.php">
<input type="hidden" name="id" value="<?php echo $producto->id; ?>">

<label for="codigo">Código de barras:</label>


<input value="<?php echo $producto->codigo ?>" class="form-control" name="codigo" req

<label for="descripcion">Descripción:</label>
<textarea required id="descripcion" name="descripcion" cols="30" rows="5" class="form

<label for="precioVenta">Precio de venta:</label>


<input value="<?php echo $producto->precioVenta ?>" class="form-control" name="precio

<label for="precioCompra">Precio de compra:</label>


<input value="<?php echo $producto->precioCompra ?>" class="form-control" name="preci

<label for="existencia">Existencia:</label>
<input value="<?php echo $producto->existencia ?>" class="form-control" name="existen
<br><br><input class="btn btn-info" type="submit" value="Guardar">
<a class="btn btn-warning" href="./listar.php">Cancelar</a>
</form>
</div>
<?php include_once "pie.php" ?>

Notar por favor que para saber el id que estamos editando (con el que
haremos el where) lo estoy guardando en un input oculto.

Hay dos botones. Uno de ellos envía el formulario; el otro es simplemente un


enlace que lleva a listar.php. El punto de todo esto es proporcionar al usuario
unos botones de navegación.

Guardar cambios
Arriba vimos el formulario para editar. Ahora veamos el archivo en donde
realmente guardamos los cambios. Es este:

<?php
#Salir si alguno de los datos no está presente
if(
!isset($_POST["codigo"]) ||
!isset($_POST["descripcion"]) ||
!isset($_POST["precioCompra"]) ||
!isset($_POST["precioVenta"]) ||
!isset($_POST["existencia"]) ||
!isset($_POST["id"])
) exit();

#Si todo va bien, se ejecuta esta parte del código...

include_once "base_de_datos.php";
$id = $_POST["id"];
$codigo = $_POST["codigo"];
$descripcion = $_POST["descripcion"];
$precioCompra = $_POST["precioCompra"];
$precioVenta = $_POST["precioVenta"];
$existencia = $_POST["existencia"];

$sentencia = $base_de_datos->prepare("UPDATE productos SET codigo = ?, descripcion = ?, precioCompra = ?, p


$resultado = $sentencia->execute([$codigo, $descripcion, $precioCompra, $precioVenta, $existencia, $id]);

if($resultado === TRUE){


header("Location: ./listar.php");
exit;
}
else echo "Algo salió mal. Por favor verifica que la tabla exista, así como el ID del producto";
?>
Muy parecido al de insertar uno nuevo, pero éste edita. Si todo sale bien, nos
lleva a listar.php que se encargará de mostrar los productos, con los cambios
que se hayan hecho.

Eliminar producto
Finalmente veamos el de eliminar. Notar por favor que no pide confirmación,
así que hay que hacerlo con cuidado.
<?php

if(!isset($_GET["id"])) exit();
$id = $_GET["id"];
include_once "base_de_datos.php";
$sentencia = $base_de_datos->prepare("DELETE FROM productos WHERE id = ?;");
$resultado = $sentencia->execute([$id]);
if($resultado === TRUE){
header("Location: ./listar.php");
exit;
}
else echo "Algo salió mal";
?>
Vender
Esta fue la parte que más me gustó. Sólo se trabaja con arreglos y sesiones,
pero me agradó el resultado.

Interfaz para vender


<?php

include_once "encabezado.php";
session_start();
if(!isset($_SESSION["carrito"])) $_SESSION["carrito"] = [];
$granTotal = 0;
?>
<div class="col-xs-12">
<h1>Vender</h1>
<?php
if(isset($_GET["status"])){
if($_GET["status"] === "1"){
?>
<div class="alert alert-success">
<strong>¡Correcto!</strong> Venta realizada correctament
</div>
<?php
}else if($_GET["status"] === "2"){
?>
<div class="alert alert-info">
<strong>Venta cancelada</strong>
</div>
<?php
}else if($_GET["status"] === "3"){
?>
<div class="alert alert-info">
<strong>Ok</strong> Producto quitado de la lista
</div>
<?php
}else if($_GET["status"] === "4"){
?>
<div class="alert alert-warning">
<strong>Error:</strong> El producto que buscas no existe
</div>
<?php
}else if($_GET["status"] === "5"){
?>
<div class="alert alert-danger">
<strong>Error: </strong>El producto está agotado
</div>
<?php
}else{
?>
<div class="alert alert-danger">
<strong>Error:</strong> Algo salió mal mientras se reali
</div>
<?php
}
}
?>
<br>
<form method="post" action="agregarAlCarrito.php">
<label for="codigo">Código de barras:</label>
<input autocomplete="off" autofocus class="form-control" name="codigo" required type=
</form>
<br><br>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Código</th>
<th>Descripción</th>
<th>Precio de venta</th>
<th>Cantidad</th>
<th>Total</th>
<th>Quitar</th>
</tr>
</thead>
<tbody>
<?php foreach($_SESSION["carrito"] as $indice => $producto){
$granTotal += $producto->total;
?>
<tr>
<td><?php echo $producto->id ?></td>
<td><?php echo $producto->codigo ?></td>
<td><?php echo $producto->descripcion ?></td>
<td><?php echo $producto->precioVenta ?></td>
<td><?php echo $producto->cantidad ?></td>
<td><?php echo $producto->total ?></td>
<td><a class="btn btn-danger" href="<?php echo "quitarDelCarrito.php?in
</tr>
<?php } ?>
</tbody>
</table>

<h3>Total: <?php echo $granTotal; ?></h3>


<form action="./terminarVenta.php" method="POST">
<input name="total" type="hidden" value="<?php echo $granTotal;?>">
<button type="submit" class="btn btn-success">Terminar venta</button>
<a href="./cancelarVenta.php" class="btn btn-danger">Cancelar venta</a>
</form>
</div>
<?php include_once "pie.php" ?>

Muestra una tabla, que será de los productos que componen la venta.
También muestra el total, que al inicio es 0. Y hasta abajo 2 botones que son
para terminar de vender o para cancelar la venta. Igualmente hay unos ifs que
muestran una alerta como “Producto inexistente” o cosas de esas.

Notemos que tiene un input, eso es para leer el código de barras. En realidad
es un input dentro de un formulario. Dicho formulario, al enviarse, se va al
archivo que veremos a continuación.
Finalmente, si vemos el encabezado, notaremos que declara el índice carrito
en el arreglo superglobal de $_SESSION. Ahí es en donde colocaremos los

productos 

Agregar al carrito de compra


Repito que esta es la parte que más me gustó. Comenzamos comprobando la
existencia del producto, si no existe, regresamos a vender.php y le pasamos el
status 5 que dirá que ya no hay existencias del producto.

En caso de que no exista el producto (es decir, que no exista, no que su


existencia sea 0) mandamos el status 4.

<?php

if(!isset($_POST["codigo"])) return;
$codigo = $_POST["codigo"];
include_once "base_de_datos.php";
$sentencia = $base_de_datos->prepare("SELECT * FROM productos WHERE codigo = ? LIMIT 1;");
$sentencia->execute([$codigo]);
$producto = $sentencia->fetch(PDO::FETCH_OBJ);
if($producto){
if($producto->existencia < 1){
header("Location: ./vender.php?status=5");
exit;
}
session_start();
$indice = false;
for ($i=0; $i < count($_SESSION["carrito"]); $i++) {
if($_SESSION["carrito"][$i]->codigo === $codigo){
$indice = $i;
break;
}
}
if($indice === FALSE){
$producto->cantidad = 1;
$producto->total = $producto->precioVenta;
array_push($_SESSION["carrito"], $producto);
}else{

$_SESSION["carrito"][$indice]->cantidad++;
$_SESSION["carrito"][$indice]->total = $_SESSION["carrito"][$indice]->cantidad * $_SESSION[
}
header("Location: ./vender.php");
}else header("Location: ./vender.php?status=4");
?>

Pero suponiendo que todo va bien y que el producto existe, iniciamos sesión y
recorremos el arreglo carrito para ver si ya habíamos agregado ese producto
antes. En caso de que lo hayamos agregado, entonces cambiamos solamente
la cantidad.

Es decir, si ya existe “Mermelada de fresa” y vuelven a leer el código que le


pertenece a ese producto, no vamos a mostrar en la tabla ambos productos.
Mejor mostramos uno e indicamos que su cantidad es 2, y así sucesivamente.

Si no existe en el carrito entonces lo agregamos al arreglo y le ponemos su


cantidad que por defecto es 1. También, por defecto, su total será el precio de
venta. No es necesario hacer multiplicaciones.

Luego, en caso de que ya exista, calculamos su total, que es el resultado de


multiplicar su precio de venta por la cantidad en el carrito.

Quitar del carrito

Ahora veamos cómo quitar productos del carrito. Simplemente eliminamos el


elemento que pertenezca al índice que recibimos en $_GET. El código es muy
fácil de entender:
<?php

if(!isset($_GET["indice"])) return;
$indice = $_GET["indice"];

session_start();
array_splice($_SESSION["carrito"], $indice, 1);
header("Location: ./vender.php?status=3");
?>

Ah, y regresamos a vender.php con el status 3 que creo que dice que fue
quitado correctamente.

Terminar venta
Es un simple archivo que insertará en la base de datos los productos
vendidos, así como la venta, su fecha y su total. Queda así:

<?php

if(!isset($_POST["total"])) exit;

session_start();

$total = $_POST["total"];
include_once "base_de_datos.php";

$ahora = date("Y-m-d H:i:s");


$sentencia = $base_de_datos->prepare("INSERT INTO ventas(fecha, total) VALUES (?, ?);");
$sentencia->execute([$ahora, $total]);

$sentencia = $base_de_datos->prepare("SELECT id FROM ventas ORDER BY id DESC LIMIT 1;");


$sentencia->execute();
$resultado = $sentencia->fetch(PDO::FETCH_OBJ);

$idVenta = $resultado === false ? 1 : $resultado->id;


$base_de_datos-
>beginTransaction();
$sentencia = $base_de_datos->prepare("INSERT INTO productos_vendidos(id_produc
cantidad) VALUES (?, ?, ?);");
$sentenciaExistencia = $base_de_datos->prepare("UPDATE productos SET existenci
WHERE id = ?;");
foreach ($_SESSION["carrito"] as $producto) {
$total += $producto->total;
$sentencia->execute([$producto->id, $idVenta, $producto->cantidad]);
$sentenciaExistencia->execute([$producto->cantidad, $producto->id]);
}
$base_de_datos->commit();
unset($_SESSION["carrito"]);
$_SESSION["carrito"] = [];
header("Location: ./vender.php?status=1");
?>

Restamos existencia de productos, tomamos la hora del servidor y


guardamos la venta. Luego, limpiamos el carrito.

Si me preguntan por beginTransaction y commit, es para (aunque ni se nota)


agilizar el proceso. Es como hacer muchos cambios en la base de datos pero
no guardarlos hasta que terminemos. Más información en Bajo rendimiento
en sentencias preparadas con PHP y MySQL.

Por cierto, regresamos a listar con un status igualmente.


Cancelar venta
Para cancelar la venta simplemente vaciamos el arreglo y listo. Regresamos a
listar con un status.

<?php

session_start();

unset($_SESSION["carrito"]);
$_SESSION["carrito"] = [];

header("Location: ./vender.php?status=2");
?>

Y así terminamos de vender.

Ventas
Para terminar este grandioso tutorial veamos el registro de ventas. Se
compone de dos cosas: listar ventas y poder eliminarlas.
Listar
Por favor no me culpen, pero no sé cómo (y si alguien lo sabe, que me
explique) hacer una consulta que traiga dentro un arreglo. Lo que pasa es que
quería algo para mostrar los productos vendidos por venta. Lo único que se
me ocurrió fue un group_concat. En fin, el código queda así:

<?php include_once "encabezado.php" ?>

<?php
include_once "base_de_datos.php";
$sentencia = $base_de_datos->query("SELECT ventas.total, ventas.fecha, v
productos_vendidos.cantidad SEPARATOR '__') AS productos FROM ventas INN
productos.id = productos_vendidos.id_producto GROUP BY ventas.id ORDER B
$ventas = $sentencia->fetchAll(PDO::FETCH_OBJ);
?>

<div class="col-xs-12">
<h1>Ventas</h1>
<div>
<a class="btn btn-success" href="./vender.php">Nue
</div>
<br>
<table class="table table-bordered">
<thead>
<tr>
<th>Número</th>
<th>Fecha</th>
<th>Productos vendidos</th>
<th>Total</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
<?php foreach($ventas as $venta){ ?>
<tr>
<td><?php echo $venta->id ?></td>
<td><?php echo $venta->fecha ?></td>
<td>
<table class="table table-
bordered">
<thead>
<tr>
<th>Código</t
<th>Descripci
<th>Cantidad<
</tr>
</thead>
<tbody>
<?php foreach(explode
$producto = explode("
?>
<tr>
<td><?php ech
<td><?php ech
<td><?php ech
</tr>
<?php } ?>
</tbody>
</table>
</td>
<td><?php echo
$venta->total ?
></td>
<td><a class="btn btn-danger" href="<?php echo "eliminarVent
</tr>
<?php } ?>
</tbody>
</table>
</div>
<?php include_once "pie.php" ?>

De esta manera, lo concatenamos en la consulta pero lo “desconcatenamos”


en PHP. Y queda un lindo resultado.
Eliminar
Para eliminar una venta es como eliminar un producto. Ojo que tampoco pide
confirmación. El código queda así:

<?php

if(!isset($_GET["id"])) exit();
$id = $_GET["id"];
include_once "base_de_datos.php";
$sentencia = $base_de_datos->prepare("DELETE FROM ventas WHERE id = ?;");
$resultado = $sentencia->execute([$id]);
if($resultado === TRUE){
header("Location: ./ventas.php");
exit;
}
else echo "Algo salió mal";
?>

Encabezado y pie
Como lo prometí, aquí el encabezado y pie. Sólo definen el contendor, el menú
de navegación, algunas etiquetas meta y cargan las librerías css.

Encabezado es:

<!DOCTYPE
html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Ventas</title>

<link rel="stylesheet" href="./css/fontawesome-all.min.css">


<link rel="stylesheet" href="./css/2.css">
<link rel="stylesheet" href="./css/estilo.css">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">POS</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="./listar.php">Productos</a></li>
<li><a href="./vender.php">Vender</a></li>
<li><a href="./ventas.php">Ventas</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
Y pie:

</div>

</div>
</body>
</html>

Conclusión
Como lo dije, no tiene estructura. Fue hecho rápidamente, pero funciona y eso
es lo que cuenta. Cuando pienses que algo funciona pero se ve mal o parece
un truco, recuerda que la humanidad hizo que una roca pensara (al inventar
los microprocesadores).
Por cierto, si quieres ver algo parecido a este sistema pero en Laravel, mira
este post.

Estoy disponible para trabajar en tu proyecto o realizar tu tarea pendiente, no


dudes en ponerte en contacto conmigo.
Si el post fue de tu agrado muestra tu apoyo compartiéndolo, suscribiéndote
al blog, siguiéndome o realizando una donación.

Suscribir por correo


Ingresa tu correo y recibirás mis últimas entradas sobre programación, open
source, bases de datos y todo lo relacionado con informática.

También podría gustarte