Hoy voy a presentar un sistema que acabo de hacer con Laravel. Se trata de un sistema de ventas, punto de venta, PDV o como le llames, que sirve para llevar el seguimiento de los productos que se venden, el registro de ventas, etcétera.
Es un sistema totalmente gratuito y además open source; lo que quiere decir que puedes usarlo sin costo, y modificarlo a tus necesidades o personalizarlo. Entre sus opciones encontramos:
Ahora veamos cómo está hecho, en dónde obtenerlo, etcétera.
Versión de PHP:
PHP 7.4.30 (cli) (built: Jun 7 2022 16:24:55) ( ZTS Visual C++ 2017 x64 )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
Laravel utiliza Blade para las plantillas, así que he utilizado su sistema de layouts y herencia. Defino la plantilla maestra que se basa en un starter template de Bootstrap 4:
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="{{env("APP_NAME")}}">
<meta name="author" content="Parzibyte">
<title>@yield("titulo") - {{env("APP_NAME")}}</title>
<link href="{{url("/css/bootstrap.min.css")}}" rel="stylesheet">
<link href="{{url("/css/all.min.css")}}" rel="stylesheet">
<style>
body {
padding-top: 70px;
/*Para la barra inferior fija*/ padding-bottom: 70px;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" target="_blank" href="//parzibyte.me/blog">{{env("APP_NAME")}}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
id="botonMenu" aria-label="Mostrar u ocultar menú">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav mr-auto">
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">
Registro
</a>
</li>
@else
<li class="nav-item">
<a class="nav-link" href="{{route("home")}}">Inicio <i class="fa fa-home"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route("productos.index")}}">Productos <i class="fa fa-box"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route("vender.index")}}">Vender <i class="fa fa-cart-plus"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route("ventas.index")}}">Ventas <i class="fa fa-list"></i></a>
</li>
@endguest
</ul>
<ul class="navbar-nav ml-auto">
@auth
<li class="nav-item">
<a href="{{route("logout")}}" class="nav-link">
Salir ({{ Auth::user()->name }})
</a>
</li>
@endauth
<li class="nav-item">
<a class="nav-link" href="https://parzibyte.me/blog/contrataciones-ayuda/">Soporte <i
class="fa fa-hands-helping"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route("acerca_de")}}">Acerca de <i class="fa fa-info"></i></a>
</li>
</ul>
</div>
</nav>
<script type="text/javascript">
// Tomado de https://parzibyte.me/blog/2019/06/26/menu-responsivo-bootstrap-4-sin-dependencias/
document.addEventListener("DOMContentLoaded", () => {
const menu = document.querySelector("#menu"),
botonMenu = document.querySelector("#botonMenu");
if (menu) {
botonMenu.addEventListener("click", () => menu.classList.toggle("show"));
}
});
</script>
<main class="container-fluid">
@yield("contenido")
</main>
<footer class="px-2 py-2 fixed-bottom bg-dark">
<span class="text-muted">Punto de venta en Laravel
<i class="fa fa-code text-white"></i>
con
<i class="fa fa-heart" style="color: #ff2b56;"></i>
por
<a class="text-white" href="//parzibyte.me/blog">Parzibyte</a>
|
<a target="_blank" class="text-white" href="//github.com/parzibyte/sistema_ventas_laravel">
<i class="fab fa-github"></i>
</a>
</span>
</footer>
</body>
</html>
De este modo, ahora solo tengo que sobrescribir el contenido. De la plantilla puedes ver que muestro un menú de navegación y que oculto algunos elementos en caso de que el usuario no esté logueado.
Tenemos varias rutas configuradas para vender, ver ventas, imprimir tickets, ver productos, etcétera. Todas ellas están protegidas con el middleware auth
, por lo que no se permite acceder a ellas si no se ha iniciado sesión.
El archivo de web.php se ve así:
<?php
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return redirect()->route("home");
});
Route::get("/acerca-de", function () {
return view("misc.acerca_de");
})->name("acerca_de");
Auth::routes([
"reset" => false,// no pueden olvidar contraseña
]);
Route::get('/home', 'HomeController@index')->name('home');
// Permitir logout con petición get
Route::get("/logout", function () {
Auth::logout();
return redirect()->route("home");
})->name("logout");
Route::middleware("auth")
->group(function () {
Route::resource("productos", "ProductosController");
Route::get("/ventas/ticket", "VentasController@ticket")->name("ventas.ticket");
Route::resource("ventas", "VentasController");
Route::get("/vender", "VenderController@index")->name("vender.index");
Route::post("/productoDeVenta", "VenderController@agregarProductoVenta")->name("agregarProductoVenta");
Route::delete("/productoDeVenta", "VenderController@quitarProductoDeVenta")->name("quitarProductoDeVenta");
Route::post("/cancelarVenta", "VenderController@cancelarVenta")->name("cancelarVenta");
Route::post("/terminarVenta", "VenderController@terminarVenta")->name("terminarVenta");
});
En ella se registran los controladores que son resources, además de otras rutas como el login, el registro, agregar producto al carrito de compras, etcétera.
He usado la autenticación por defecto de Laravel, así que para usar el sistema de ventas hay que iniciar sesión:
De igual modo se provee una pantalla para registrarse, que se puede deshabilitar al gusto.
Al iniciar sesión se dirige al índice o pantalla de bienvenida. En el mismo se muestran accesos directos a los apartados del sistema:
El código del inicio es el siguiente:
@extends('maestra')
@section("titulo", "Inicio")
@section('contenido')
<div class="col-12 text-center">
<h1>Bienvenido, {{Auth::user()->name}}</h1>
</div>
<div class="card-columns">
<a href="{{route("productos.index")}}" style="color: black;">
<div class="card text-center">
<img src="{{url("/img/order.png")}}" class="card-img-top">
<div class="card-body">
<h1 class="card-title">Productos</h1>
</div>
</div>
</a>
<a style="color: black;" target="_blank" href="https://parzibyte.me/blog/contrataciones-ayuda/">
<div class="card text-center">
<img src="{{url("/img/gamer.png")}}" class="card-img-top">
<div class="card-body">
<h1 class="card-title">Soporte</h1>
</div>
</div>
</a>
<a style="color: black;" href="{{route("vender.index")}}">
<div class="card text-center">
<img src="{{url("/img/sales.png")}}" class="card-img-top">
<div class="card-body">
<h1 class="card-title">Vender</h1>
</div>
</div>
</a>
<a style="color: black;" href="{{route("acerca_de")}}">
<div class="card text-center">
<img src="{{url("/img/about.png")}}" class="card-img-top">
<div class="card-body">
<h1 class="card-title">Acerca de</h1>
</div>
</div>
</a>
<a style="color: black;" href="{{route("ventas.index")}}">
<div class="card text-center">
<img src="{{url("/img/coupon.png")}}" class="card-img-top">
<div class="card-body">
<h1 class="card-title">Ventas</h1>
</div>
</div>
</a>
</div>
@endsection
Utilizo tarjetas de Bootstrap. Y a partir de eso se puede navegar a los siguientes apartados.
Tenemos la gestión de productos con Laravel en donde se registra la existencia, el precio de compra, el precio de venta, el código de barras y la descripción del artículo. La definición del modelo y sus fillables se ve así:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Producto extends Model
{
protected $fillable = ["codigo_barras", "descripcion", "precio_compra", "precio_venta", "existencia",
];
}
El formulario de registro de producto se ve así:
@extends("maestra")
@section("titulo", "Agregar producto")
@section("contenido")
<div class="row">
<div class="col-12">
<form method="POST" action="{{route("productos.store")}}">
@csrf
<div class="form-group">
<label class="label">Código de barras</label>
<input required autocomplete="off" name="codigo_barras" class="form-control"
type="text" placeholder="Código de barras">
</div>
<div class="form-group">
<label class="label">Descripción</label>
<input required autocomplete="off" name="descripcion" class="form-control"
type="text" placeholder="Descripción">
</div>
<div class="form-group">
<label class="label">Precio de compra</label>
<input required autocomplete="off" name="precio_compra" class="form-control"
type="decimal(9,2)" placeholder="Precio de compra">
</div>
<div class="form-group">
<label class="label">Precio de venta</label>
<input required autocomplete="off" name="precio_venta" class="form-control"
type="decimal(9,2)" placeholder="Precio de venta">
</div>
<div class="form-group">
<label class="label">Existencia</label>
<input required autocomplete="off" name="existencia" class="form-control"
type="decimal(9,2)" placeholder="Existencia">
</div>
@include("notificacion")
<button class="btn btn-success">Guardar</button>
<a class="btn btn-primary" href="{{route("productos.index")}}">Volver al listado</a>
</form>
</div>
</div>
@endsection
Es un simple formulario que llevará al siguiente controlador o controller de productos, que a su vez es un resource:
<?php
namespace App\Http\Controllers;
use App\Producto;
use Illuminate\Http\Request;
class ProductosController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/ public function index()
{
return view("productos.productos_index", ["productos" => Producto::all()]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/ public function create()
{
return view("productos.productos_create");
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/ public function store(Request $request)
{
$producto = new Producto($request->input());
$producto->saveOrFail();
return redirect()->route("productos.index")->with("mensaje", "Producto guardado");
}
/**
* Display the specified resource.
*
* @param \App\Producto $producto
* @return \Illuminate\Http\Response
*/ public function show(Producto $producto)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Producto $producto
* @return \Illuminate\Http\Response
*/ public function edit(Producto $producto)
{
return view("productos.productos_edit", ["producto" => $producto,
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Producto $producto
* @return \Illuminate\Http\Response
*/ public function update(Request $request, Producto $producto)
{
$producto->fill($request->input());
$producto->saveOrFail();
return redirect()->route("productos.index")->with("mensaje", "Producto actualizado");
}
/**
* Remove the specified resource from storage.
*
* @param \App\Producto $producto
* @return \Illuminate\Http\Response
*/ public function destroy(Producto $producto)
{
$producto->delete();
return redirect()->route("productos.index")->with("mensaje", "Producto eliminado");
}
}
El formulario de editar es muy parecido al de insertar. Para mostrar los productos se utiliza una tabla responsiva de Bootstrap además de usar @foreach
de Blade:
@extends("maestra")
@section("titulo", "Productos")
@section("contenido")
<div class="row">
<div class="col-12">
<h1>Productos <i class="fa fa-box"></i></h1>
<a href="{{route("productos.create")}}" class="btn btn-success mb-2">Agregar</a>
@include("notificacion")
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Código de barras</th>
<th>Descripción</th>
<th>Precio de compra</th>
<th>Precio de venta</th>
<th>Utilidad</th>
<th>Existencia</th>
<th>Editar</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
@foreach($productos as $producto)
<tr>
<td>{{$producto->codigo_barras}}</td>
<td>{{$producto->descripcion}}</td>
<td>{{$producto->precio_compra}}</td>
<td>{{$producto->precio_venta}}</td>
<td>{{$producto->precio_venta - $producto->precio_compra}}</td>
<td>{{$producto->existencia}}</td>
<td>
<a class="btn btn-warning" href="{{route("productos.edit",[$producto])}}">
<i class="fa fa-edit"></i>
</a>
</td>
<td>
<form action="{{route("productos.destroy", [$producto])}}" method="post">
@method("delete")
@csrf
<button type="submit" class="btn btn-danger">
<i class="fa fa-trash"></i>
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
b
Con el siguiente resultado:
Recuerda que el código completo lo puedes ver en GitHub; al final dejaré el enlace. A partir de aquí no explicaré a fondo el código repetitivo, pues ya lo hice con los productos. Veamos las cosas que son más importantes.
En la interfaz tenemos un campo que sirve para escanear el código de barras, ya sea que se use un lector o que se escriba el código y se presione la tecla Enter.
Cuando hay productos, se muestra la lista de los mismos así como algunos botones para cancelar la venta, terminar la venta o quitar un producto de la lista de venta.
Además de eso, se muestra el total de la venta formateado como moneda.
El carrito de compras o lista de productos para la venta se guarda en la sesión como un arreglo, de este modo no se pierde aunque el usuario refresque la página o navegue a otro lugar; permitiendo así un mejor control.
He implementado un simple carrito de compras con un arreglo, las funciones que lo manejan son:
<?php
private function obtenerProductos()
{
$productos = session("productos");
if (!$productos) {
$productos = [];
}
return $productos;
}
private function vaciarProductos()
{
$this->guardarProductos(null);
}
private function guardarProductos($productos)
{
session(["productos" => $productos,
]);
}
public function cancelarVenta()
{
$this->vaciarProductos();
return redirect()
->route("vender.index")
->with("mensaje", "Venta cancelada");
}
public function quitarProductoDeVenta(Request $request)
{
$indice = $request->post("indice");
$productos = $this->obtenerProductos();
array_splice($productos, $indice, 1);
$this->guardarProductos($productos);
return redirect()
->route("vender.index");
}
public function agregarProductoVenta(Request $request)
{
$codigo = $request->post("codigo");
$producto = Producto::where("codigo_barras", "=", $codigo)->first();
if (!$producto) {
return redirect()
->route("vender.index")
->with("mensaje", "Producto no encontrado");
}
$this->agregarProductoACarrito($producto);
return redirect()
->route("vender.index");
}
private function agregarProductoACarrito($producto)
{
if ($producto->existencia <= 0) {
return redirect()->route("vender.index")
->with([
"mensaje" => "No hay existencias del producto",
"tipo" => "danger"
]);
}
$productos = $this->obtenerProductos();
$posibleIndice = $this->buscarIndiceDeProducto($producto->codigo_barras, $productos);
// Es decir, producto no fue encontrado
if ($posibleIndice === -1) {
$producto->cantidad = 1;
array_push($productos, $producto);
} else {
if ($productos[$posibleIndice]->cantidad + 1 > $producto->existencia) {
return redirect()->route("vender.index")
->with([
"mensaje" => "No se pueden agregar más productos de este tipo, se quedarían sin existencia",
"tipo" => "danger"
]);
}
$productos[$posibleIndice]->cantidad++;
}
$this->guardarProductos($productos);
}
private function buscarIndiceDeProducto(string $codigo, array &$productos)
{
foreach ($productos as $indice => $producto) {
if ($producto->codigo_barras === $codigo) {
return $indice;
}
}
return -1;
}
Maneja varias opciones. Por ejemplo, cuando se agrega un producto se envía el código de barras y se busca un producto por ese código; después se hacen dos verificaciones:
Por poner un ejemplo, si se quiere vender más de lo que existe, se muestra una advertencia:
Además, si el producto ya existe, no se agrega, sino que se aumenta su cantidad.
Al terminar la venta se involucran varias cosas. Comenzando con que tenemos un modelo distinto llamado ProductoVendido
que se encarga de encapsular cómo se comporta un producto vendido.
Lo he separado de este modo porque el precio de venta no siempre será el mismo; es decir, hoy puedes vender un producto a 100 pesos que mañana costará 112 por ejemplo; entonces se debe guardar el precio de venta en el que se vendió.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ProductoVendido extends Model
{
protected $table = "productos_vendidos";
protected $fillable = ["id_venta", "descripcion", "codigo_barras", "precio", "cantidad"];
}
Además de eso, el producto lleva una relación con la tabla Ventas, mismas que tienen una fecha de creación (los timestamps) y un id:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Venta extends Model
{
public function productos()
{
return $this->hasMany("App\ProductoVendido", "id_venta");
}
}
Aquí hay un punto interesante, pues tenemos una relación entre productos y ventas, usando las relaciones que Laravel provee. Finalmente, para terminar la venta se hace lo siguiente:
<?php
public function terminarVenta()
{
// Crear una venta
$venta = new Venta();
$venta->saveOrFail();
$idVenta = $venta->id;
$productos = $this->obtenerProductos();
// Recorrer carrito de compras
foreach ($productos as $producto) {
// El producto que se vende...
$productoVendido = new ProductoVendido();
$productoVendido->fill([
"id_venta" => $idVenta,
"descripcion" => $producto->descripcion,
"codigo_barras" => $producto->codigo_barras,
"precio" => $producto->precio_venta,
"cantidad" => $producto->cantidad,
]);
// Lo guardamos
$productoVendido->saveOrFail();
// Y restamos la existencia del original
$productoActualizado = Producto::find($producto->id);
$productoActualizado->existencia -= $productoVendido->cantidad;
$productoActualizado->saveOrFail();
}
$this->vaciarProductos();
return redirect()
->route("vender.index")
->with("mensaje", "Venta terminada");
}
Y así es como se guardan los productos vendidos junto con la venta y la fecha de creación. Más tarde vamos a usar esto para ver el reporte de ventas, el detalle de una venta y para imprimir tickets.
El reporte de ventas muestra el total de la venta, la fecha de venta y botones para ver los detalles, imprimir el ticket o eliminar la venta. La consulta para listar las ventas con el total es:
<?php
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/public function index()
{
$ventasConTotales = Venta::join("productos_vendidos", "productos_vendidos.id_venta", "=", "ventas.id")
->select("ventas.*", DB::raw("sum(productos_vendidos.cantidad * productos_vendidos.precio) as total"))
->groupBy("ventas.id", "ventas.created_at", "ventas.updated_at")
->get();
return view("ventas.ventas_index", ["ventas" => $ventasConTotales,]);
}
Es un poco compleja y me llevó algo de tiempo, sobre todo porque quise hacerla con el Query builder en lugar de hacer la consulta raw. Esas ventas son mostradas en una tabla, así:
Cuando se muestra el detalle de la venta utilizamos el método show
del controlador, que muestra una venta. Ahí, gracias a la relación que definimos anteriormente podemos obtener la tabla de productos y dibujar el detalle como el total:
Por cierto, la vista se ve así:
@extends("maestra")
@section("titulo", "Detalle de venta ")
@section("contenido")
<div class="row">
<div class="col-12">
<h1>Detalle de venta #{{$venta->id}}</h1>
@include("notificacion")
<a class="btn btn-info" href="{{route("ventas.index")}}">
<i class="fa fa-arrow-left"></i> Volver
</a>
<a class="btn btn-success" href="{{route("ventas.ticket", ["id" => $venta->id])}}">
<i class="fa fa-print"></i> Ticket
</a>
<h2>Productos</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>Descripción</th>
<th>Código de barras</th>
<th>Precio</th>
<th>Cantidad</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
@foreach($venta->productos as $producto)
<tr>
<td>{{$producto->descripcion}}</td>
<td>{{$producto->codigo_barras}}</td>
<td>${{number_format($producto->precio)}}</td>
<td>{{$producto->cantidad}}</td>
<td>${{number_format($producto->cantidad * $producto->precio)}}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="3"></td>
<td><strong>Total</strong></td>
<td>${{number_format($total, 2)}}</td>
</tr>
</tfoot>
</table>
</div>
</div>
@endsection
Eso es lo que se encarga de dibujar la tabla, colocar los enlaces, etcétera.
Nota: si quieres saber cómo configurar tu impresora mira el post sobre cómo imprimir un ticket en PHP.
Veamos cómo es que se imprime el ticket en una impresora térmica. Lo que hay que hacer es instalar la librería de mike42; me basé en mi tutorial para imprimir en impresora térmica con Laravel.
La función del controlador es la siguiente:
<?php
public function ticket(Request $request)
{
$venta = Venta::findOrFail($request->get("id"));
$nombreImpresora = env("NOMBRE_IMPRESORA");
$connector = new WindowsPrintConnector($nombreImpresora);
$impresora = new Printer($connector);
$impresora->setJustification(Printer::JUSTIFY_CENTER);
$impresora->setEmphasis(true);
$impresora->text("Ticket de venta\n");
$impresora->text($venta->created_at . "\n");
$impresora->text("https://parzibyte.me/blog\n");
$impresora->setEmphasis(false);
$impresora->text("\n===============================\n");
$total = 0;
foreach ($venta->productos as $producto) {
$subtotal = $producto->cantidad * $producto->precio;
$total += $subtotal;
$impresora->setJustification(Printer::JUSTIFY_LEFT);
$impresora->text(sprintf("%.2fx%s\n", $producto->cantidad, $producto->descripcion));
$impresora->setJustification(Printer::JUSTIFY_RIGHT);
$impresora->text('$' . number_format($subtotal, 2) . "\n");
}
$impresora->setJustification(Printer::JUSTIFY_CENTER);
$impresora->text("\n===============================\n");
$impresora->setJustification(Printer::JUSTIFY_RIGHT);
$impresora->setEmphasis(true);
$impresora->text("Total: $" . number_format($total, 2) . "\n");
$impresora->setJustification(Printer::JUSTIFY_CENTER);
$impresora->setTextSize(1, 1);
$impresora->text("Gracias por su compra\n");
$impresora->text("https://parzibyte.me/blog");
$impresora->feed(5);
$impresora->close();
return redirect()->back()->with("mensaje", "Ticket impreso");
}
Comenzamos obteniendo la venta por ID, misma variable que es pasada a través de la URL. El nombre de la impresora está en el archivo .env, así se puede configurar de manera sencilla.
Imprimimos el encabezado, la fecha de la venta y luego recorremos los productos de la venta (previamente registrados en la base de datos). Por cada uno dibujamos la cantidad, la descripción y el subtotal.
Al pie del ticket imprimimos el total y un mensaje de agradecimiento. Más tarde se redirecciona a la página de donde se venía y si todo está bien, el ticket se debería ver parecido al de la siguiente imagen:
Obviamente el ticket cambiará dependiendo de la lista de productos, y es totalmente dinámico.
Quiero mostrar que este sistema se adapta a cualquier tamaño de pantalla. Por ejemplo, en un teléfono móvil se ve así:
Por lo tanto puedes usarlo en una red de área local o subirlo a un hosting; puedes visitarlo desde cualquier lugar.
El código fuente del programa está en mi repositorio de GitHub. Si quieres descargar el código haz click en Clone or download > Download ZIP dentro del repositorio. Para usarlo configura el archivo env, instala las dependencias con composer y realiza las migraciones pertinentes.
Los pasos son:
intl
en caso de no estar instalada.composer install
.env
copiando .env.example
y colocando las credenciales para la base de datosphp artisan migrate
php artisan key:generate
En algunos casos el sistema da el siguiente error:
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
In PackageManifest.php line 131:
Undefined index: name
Para arreglarlo hice un composer self-update
, luego un composer update laravel/framework
y finalmente un composer install
Ya que parece que es un bug que ocurría en el framework.
He creado un vídeo en YouTube para mostrar cómo instalar este sistema, en caso de que se te complique.
Puedes ver un vídeo de demostración del sistema:
Para una clase que estoy llevando actualmente en la universidad tenía que realizar un CRUD con Laravel, pero tenía ganas (desde hace mucho) de hacer un punto de venta con Laravel y agregarle algunas cosas como la impresión de tickets.
Si te gusta el framework, te invito a leer más sobre Laravel en mi blog. También te invito a ver mis otros proyectos, algunos de ellos open source.
Además, he realizado varios puntos de venta. Entre ellos:
Nota: he agregado más módulos a este sistema, entre ellos el módulo de clientes y el módulo de usuarios.
El día de hoy te mostraré cómo crear un servidor HTTP (servidor web) en Android…
En este post te voy a enseñar a designar una carpeta para imprimir todos los…
En este artículo te voy a enseñar la guía para imprimir en una impresora térmica…
Hoy te voy a mostrar un ejemplo de programación para agregar un módulo de tasa…
Los usuarios del plugin para impresoras térmicas pueden contratar licencias, y en ocasiones me han…
Hoy voy a enseñarte cómo imprimir el € en una impresora térmica. Vamos a ver…
Esta web usa cookies.
Ver comentarios
M e podrían pasar la base de datos
Claro, está en el repositorio
Hola amigo gran proyecto pero me da un error al momento de instalarlo, me dice:
-laravel/passport require php 7.2 ,Your version 8.0.2 does satisfy that requirem,
Me podes ayudar?:( ,Muchas gracias!!
Ahí mismo le está diciendo el error. "laravel/passport require php 7.2 ,Your version 8.0.2 does satisfy that requirem..." es decir, laravel/passport necesita la versión 7.2 y usted tiene la 8.0.2. Ahí recomiendo actualizar la dependencia a una versión que soporte PHP 8.0.2 y ya como última opción cambiar de PHP 8 a 7
BUenas noches amigo, estuve revisando el codigo y al momento de eliminar una venta ya realizada el stock deberia de regresar al stock inicial, ejemplo si tengo un producto con 10 en stock al vender 5 el stock queda en 5 pero al eliminar la venta el stock deberia de regresar al 10 si es que no me equivoco
Hola, buenas noches. Entiendo, gracias por revisar. Recuerde que el programa es open source, espero su pull request para mejorar la parte que me menciona.
Saludos!
Buenas noches, al subirlo a un Hosting no imprime, sale error con la clase WindowsPrintConnector, me podrías orientar como se soluciona cuando lo subes a un CPanel por favor.
Hola. Claro, si tiene dudas puede contactarme en https://parzibyte.me/#contacto
¿Puedo instalar el proyecto en Laravel 8?
Buenas noches, excelente el sistema. Tendrás el código de la apk? me interesa sobre todo ver como consume la API. Gracias
Por ahí anda en GitHub
Buenas tardes amigo como esta.?
Querìa instalar dicho sistema para probarlo.
pero me genera el siguiente error.
In PackageManifest.php line 131:
Undefined index: name
Script @php artisan package:discover --ansi handling the post-autoload-dump event returned with error code 1
alguna soluciòn
Hola. Con gusto atiendo su duda en https://parzibyte.me#contacto
Saludos!
amigo excelente trabajo una pregunta algun tutorial de como instalar este sistema en un hosting con cpanel? tu respuesta sera muy agradecida, me gustaria instalar este sistema pero online
Hola, por el momento no cuento con un tutorial. Igualmente si desea una asesoría personalizada puede contactarme en https://parzibyte.me#contacto
Saludos :)
Se que las ultimas versiones son para 64bit, pero necesito que funcione en 32bit por un tema de portabilidad. Ya he intentado actualizar PHP a 7.4 pero no logro hacerlo fucionar con 32bit esta disponible hasta la ver. 7.3.2 busque tutoriales pero lo logro hacerlo funcionar. Agradeceria que me des una ayuda con esto por que parece muy interesante el sistema. Muchas gracias.
Claro, puede contactarme en https://parzibyte.me/#contacto
Saludos
Estoy utilizando la ultima version para windows en 32bit (por necesidad) y el SQL es 7.3.2 ya he probado otras proyectos con laravel y migra todo perfectamente. Por eso consulto.
Hola. Mi versión es: mysql Ver 15.1 Distrib 10.4.11-MariaDB, for Win64 (AMD64),
He probado el proyecto y funciona de maravilla
Saludos