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.
Te invito a probar Sublime POS 3, un punto de venta evolutivo, gratuito y que funciona en la nube.
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.
Nota 3: he escrito un sistema gratuito y open source en Laravel, que es como este, pero mejorado.
Así se ve el sistema terminado:
De igual forma míralo en YouTube:
Si gustas descargarlo y probarlo en tu entorno local, lo dejo en GitHub: https://github.com/parzibyte/ventas_pdo/archive/master.zip
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);
# Correcto
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ña);
$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();
}
?>
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" placeholder="Escribe el código">
<label for="descripcion">Descripción:</label>
<textarea required id="descripcion" name="descripcion" cols="30" rows="5" class="form-control"></textarea>
<label for="precioVenta">Precio de venta:</label>
<input class="form-control" name="precioVenta" required type="number" id="precioVenta" placeholder="Precio de venta">
<label for="precioCompra">Precio de compra:</label>
<input class="form-control" name="precioCompra" required type="number" id="precioCompra" placeholder="Precio de compra">
<label for="existencia">Existencia:</label>
<input class="form-control" name="existencia" required type="number" id="existencia" placeholder="Cantidad o 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" ?>
<?php
#Salir si alguno de los datos no está presente
if(!isset($_POST["codigo"]) || !isset($_POST["descripcion"]) || !isset($_POST["precioVenta"]) || !isset($_POST["precioCompra"]) || !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, descripcion, precioVenta, precioCompra, existencia) VALUES (?, ?, ?, ?, ?);");
$resultado = $sentencia->execute([$codigo, $descripcion, $precioVenta, $precioCompra, $existencia]);
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.
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-plus"></i></a>
</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?id=" . $producto->id?>"><i class="fa fa-edit"></i></a></td>
<td><a class="btn btn-danger" href="<?php echo "eliminar.php?id=" . $producto->id?>"><i class="fa fa-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.
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" required type="text" id="codigo" placeholder="Escribe el código">
<label for="descripcion">Descripción:</label>
<textarea required id="descripcion" name="descripcion" cols="30" rows="5" class="form-control"><?php echo $producto->descripcion ?></textarea>
<label for="precioVenta">Precio de venta:</label>
<input value="<?php echo $producto->precioVenta ?>" class="form-control" name="precioVenta" required type="number" id="precioVenta" placeholder="Precio de venta">
<label for="precioCompra">Precio de compra:</label>
<input value="<?php echo $producto->precioCompra ?>" class="form-control" name="precioCompra" required type="number" id="precioCompra" placeholder="Precio de compra">
<label for="existencia">Existencia:</label>
<input value="<?php echo $producto->existencia ?>" class="form-control" name="existencia" required type="number" id="existencia" placeholder="Cantidad o existencia">
<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.
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 = ?, precioVenta = ?, existencia = ? WHERE id = ?;");
$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.
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";
?>
Y así terminamos el CRUD de productos en PHP.
Esta fue la parte que más me gustó. Sólo se trabaja con arreglos y sesiones, pero me agradó el resultado.
<?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 correctamente
</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 realizaba la venta
</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="text" id="codigo" placeholder="Escribe el código">
</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?indice=" . $indice?>"><i class="fa fa-trash"></i></a></td>
</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 😉
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["carrito"][$indice]->precioVenta;
}
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.
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.
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_producto, id_venta, cantidad) VALUES (?, ?, ?);");
$sentenciaExistencia = $base_de_datos->prepare("UPDATE productos SET existencia = existencia - ? 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.
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.
Para terminar este grandioso tutorial veamos el registro de ventas. Se compone de dos cosas: listar ventas y poder eliminarlas.
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, ventas.id, GROUP_CONCAT( productos.codigo, '..', productos.descripcion, '..', productos_vendidos.cantidad SEPARATOR '__') AS productos FROM ventas INNER JOIN productos_vendidos ON productos_vendidos.id_venta = ventas.id INNER JOIN productos ON productos.id = productos_vendidos.id_producto GROUP BY ventas.id ORDER BY ventas.id;");
$ventas = $sentencia->fetchAll(PDO::FETCH_OBJ);
?>
<div class="col-xs-12">
<h1>Ventas</h1>
<div>
<a class="btn btn-success" href="./vender.php">Nueva <i class="fa fa-plus"></i></a>
</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</th>
<th>Descripción</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>
<?php foreach(explode("__", $venta->productos) as $productosConcatenados){
$producto = explode("..", $productosConcatenados)
?>
<tr>
<td><?php echo $producto[0] ?></td>
<td><?php echo $producto[1] ?></td>
<td><?php echo $producto[2] ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</td>
<td><?php echo $venta->total ?></td>
<td><a class="btn btn-danger" href="<?php echo "eliminarVenta.php?id=" . $venta->id?>"><i class="fa fa-trash"></i></a></td>
</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.
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";
?>
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>
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.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.
Ver comentarios
hola excelente tus proyectos, que debo saber para aprender como tu. eres un monstruo!
hola tengo un problema para agregar otro formulario en esto:
prepare("INSERT INTO proveedor(empresa, ajente, detalle, direccion, telefono, ruc) VALUES (?, ?, ?, ?, ?, ?);");
$resultado = $sentencia->execute([$empresa, $ajente, $detalle, $direccion, $telefono, $ruc]);
if($resultado === TRUE){
header("Location: ./Proveedores.php");
exit;
}
else echo "Algo salió mal. Por favor verifica que la tabla exista";
?>
Hola. Si necesita ayuda envíeme un mensaje en https://parzibyte.me/#contacto
Ocurrió algo con la base de datos: SQLSTATE[HY000] [1045] Access denied for user ‘root’@’localhost’ (using password: NO)
Hola, que tal, me aparece este mensaje al intentar correr el proyecto, que puedo hacer?
Y crees que me puedas guiar rápidamente para poder lograr el acceso?
Hola. Si tiene alguna solicitud puede enviarme un mensaje en https://parzibyte.me/#contacto
Debe aprender a leer los errores. El error le está diciendo claramente que el acceso para el usuario root sin contraseña ha sido denegado. Tal vez debe especificar la contraseña, o el usuario / contraseña no son correctos
tengo una duda.... ya hice la prueba y todo bien.... pero al momento de hacer la eliminacion el producto tendria que regresar al inventario...... y como se hace eso?
Hola. Se puede hacer modificando el código
Saludos!
para poder agregar un comentario cualquier producto al momento de agregarlo para la venta como puede realizar?
Para solicitudes puede contactarme en https://parzibyte.me/#contacto
Saludos!
esta muy bueno tu proyecto espero me puedas ayudar al eliminar una venta como puedo hacer que me actualice el inventario y en las ventas al agregar el carrito en cada producto se puede poner una caja de texto para escribir alguna anotacion?
Hey, hola estoy implementando tu código en otro proyecto pero recibo este error al agregar productos al carrito: "aviso
: Intentando obtener la propiedad 'total' de no objeto en
C: \ xampp \ htdocs \ sisang \ vistas \ vendedor.php
en linea
141" este es el supuesto error: "$granTotal += $producto->total;" auqnue se supone que "$productos" se añade al array "$_SESSION["carrito"]" en esta parte del codigo: "array_push($_SESSION["carrito"], $producto);" eso es todo, espero me respondas...
Hola, si requiere ayuda puede contactarme en https://parzibyte.me
Saludos :)
hola tengo una pregunta porque me da este error cuando ya tengo todo montado y quiero correr el programa.
Ocurrió algo con la base de datos: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: NO)
Notice: Undefined variable: conexion in C:\xampp8\htdocs\proyectocrud\Main_app\Admin\admin.php on line 4
Fatal error: Uncaught Error: Function name must be a string in C:\xampp8\htdocs\proyectocrud\Main_app\Admin\admin.php:4 Stack trace: #0 {main} thrown in C:\xampp8\htdocs\proyectocrud\Main_app\Admin\admin.php on line 4
Hola. El primer error te dice que no existe la variable "conexion". Puede que no la estés definiendo, que no hayas incluido un archivo, etcétera.
El segundo error dice que el nombre de la función debe ser una cadena. Puede que estés invocando a una función de manera equivocada.
Saludos.
me podrias explicar como configuaralo en mi pc
Se necesita una instalación de PHP y MySQL. Después de eso se coloca el proyecto en htdocs o /var/www/html dependiendo de tu sistema operativo, configuras las credenciales de acceso a la base de datos, importas las tablas y luego accedes a localhost/carpeta_que_tiene_el_sistema o dominio.com/carpeta_que_tiene_el_sistema dependiendo de tu configuración.
Saludos :)
Buenos días, me puedes asesorar para implementar clientes para cada compra. Muchas gracias.
Hola amigo buen día. Claro, envíeme un correo con los requerimientos del sistema: https://parzibyte.me/blog/contacto/
Saludos :)