En este post voy a presentar el código fuente y el JAR de un sistema de ventas o punto de venta open source programado en Java, utilizando el framework web Spring Boot con el paradigma MVC.
Para el diseño he utilizado Bootstrap, y para la persistencia de datos, MySQL.
A través de este post te explicaré cómo fue programado este sistema de ventas con Spring Boot, además de mostrarte en dónde está el código fuente y cómo ejecutar el sistema; ya que el mismo es open source y gratuito.
Lo que tiene el sistema es:
Puedes descargar el sistema en la página de releases, o analizar, estudiar y modificar el código fuente en GitHub (recuerda configurar GitHub desktop antes)
Si quieres saber cómo ejecutarlo o quieres ver un vídeo con las características de este software, míralo a continuación:
Este sistema de ventas con Spring Boot utiliza el motor de base de datos MySQL, y Bootstrap 4 como framework CSS para el diseño.
También he cargado los iconos de FontAwesome y un poco de JavaScript para el menú responsivo de Bootstrap 4.
El motor de plantillas usado es Thymeleaf.
Las dependencias o el archivo build.gradle es el siguiente:
El archivo application.properties
es el siguiente:
Solo estoy configurando la manera en la que hibernate hace las migraciones, además de indicar que se usará el dialecto MySQL, la contraseña, usuario y cadena de conexión a la base de datos.
En este caso la base de datos se llama ventas_springboot.
Voy a comenzar explicando los layouts, que fue un tema un poco complicado. Lo que requería hacer era incluir o reutilizar plantillas como el encabezado o el pie, modificando únicamente el contenido.
Al final lo logré utilizando alguna dependencia de por ahí. Así que definí la plantilla maestra:
Tengo 4 partes, el encabezado, el menú de navegación, el contenido principal y el pie de página.
El encabezado queda así:
En el mismo cargo el CSS de los estilos y los iconos, así como el framework de Bootstrap.
Después está el menú de navegación:
El navbar solo tiene enlaces hacia cada apartado del sistema. Luego tenemos al pie (ya sé que en orden, ahora iría el contenido, pero el mismo se define en cada sección que veremos más abajo):
Lo único que se hace en el pie es definir los créditos y un script para que el menú sea responsivo. Ese es todo el JavaScript que verás dentro del sistema.
Ahora que he presentado el layout principal veremos cada apartado.
La gestión de los productos es sencilla. Comenzamos por ver las entidades y repositorios.
Primero definimos la entidad Producto, que tendrá su respectiva tabla en MySQL.
Lo único que defino es un producto (que luego será un producto que se agrega al carrito de ventas o un producto vendido) con algunas validaciones, constructores, getters y setters.
El repositorio de productos es el siguiente:
Estoy extendiendo de CrudRepository
, lo único que hago es definir mi interfaz con la entidad y Spring se encarga del resto. He definido también una “consulta” adicional (findFirstByCodigo
) que buscará el primer producto por código de barras, esto será útil en otros casos que ya veremos.
Cuando el usuario quiere agregar un producto se invoca al siguiente método del controlador:
Simplemente regresamos la vista con un Producto vacío, si quieres saber más sobre esto mira mi tutorial sobre el procesamiento de formularios en Spring Boot.
Para agregar un producto se define el siguiente formulario:
Tiene todo lo necesario para mostrar los errores de validación en caso de que existan. Su action
es el método del controlador:
En la función recibimos el objeto del formulario a través de @ModelAtribute
.
Validamos el formulario y en caso de que haya errores de validación regresamos la vista y detenemos la ejecución del código.
Como segundo paso verificamos que no exista un producto con ese código de barras, en caso de que exista ponemos algunos mensajes flash y regresamos.
En caso de que todo vaya bien, guardamos el producto invocando a productosRepository.save(producto)
y volvemos al formulario, con un mensaje de éxito.
Además de los detalles de los productos tengo 2 botones, o eso parece, ya que en realidad uno es un enlace que lleva a la edición del producto, y otro es el botón submit
de un formulario que eliminará el producto como veremos más adelante.
Para mostrar productos se visita la URL /productos/mostrar que invoca al siguiente método:
Devuelve la vista ver_productos.html
y le pasa una lista de todos los productos, invocando al método findAll
del repositorio de productos.
La vista en cuestión es la siguiente, en donde se dibuja una tabla responsiva de Bootstrap usando th:each propio de Thymeleaf para renderizar arreglos o listas.
Cuando alguien hace click en Editar se va a una URL como la siguiente: /productos/editar/2 en donde el último valor es el id del producto. Lo que se hace es obtener ese producto por id y regresar la view que va a mostrar el formulario, así que el controlador queda así:
A través del método findById
busco el producto usando el id obtenido de @PathVariable. Utilizo orElse
para asegurarme de que me regresa un objeto de tipo Producto
(y no del tipo Optional
) o null
.
La vista es la siguiente:
De igual manera es un formulario que mostrará los errores de validación en caso de que existan. Tengo además un input
de tipo hidden
con el id del producto, aunque bien se podría pasar el id en la ruta.
Cuando se guarda el producto, se llama a este método del controlador:
Ya sé que el parámetro no se debería recuperar por la URL pero lo hago para mostrar la misma vista en caso de que haya errores de validación, y para que la URL no cambie (ya que al procesar la información se usa el método POST pero para mostrar el formulario se usa GET)
En fin, eso no debería tener mayor importancia. Se hace la validación y se comprueba que no se esté usando un código de barras ya existente, aunque se comprueba de igual forma que, si existe un producto con ese código de barras, no sea este mismo que estamos editando.
Para eliminar un producto simplemente se envía un formulario con el id dentro del mismo. El método del controlador es el siguiente:
Invocamos al método deleteById
pasándole el id del producto obtenido al llamar getId
. Después ponemos un mensaje de que la eliminación fue correcta y redirigimos a la lista de productos.
Entonces el controlador de productos queda así:
El controlador tiene todo para manejar este CRUD de productos con Spring
Ahora pasemos a la interfaz que se encarga de agregar productos a un carrito para la venta. Los productos los he almacenado en un ArrayList
de tipo ProductoParaVender
; ya veremos eso más adelante.
Primero veamos la nueva clase de ProductoParaVender
, que extiende de Producto pero agrega otras propiedades como la cantidad (distinta a la existencia, pues se refiere a la cantidad que se vende):
No es una entidad, pues no será guardado en una base de datos, es simplemente un ayudante para la lista de compras.
Ahora veamos dos métodos que nos ayudan a guardar y a obtener el carrito. Son los siguientes y están dentro del controlador:
Se trata de un ArrayList
como lo dije hace un momento, ambos se guardan en la sesión, por eso los métodos necesitan el parámetro de HttpServletRequest
.
Ahora tenemos el método que muestra la vista, pasándole la variable del total del carrito:
Renderiza la vista llamada vender.html
que es la siguiente:
Para mostrar la tabla con los productos lee session.carrito
, pues las vistas de Thymeleaf tienen acceso a la sesión. Fíjate en que también estoy accediendo al iterador para obtener el índice y ponerlo en el formulario que quita un producto de la lista.
Tengo un formulario (que es el único input
) que agrega el producto a partir del código de barras; vamos a reutilizar el método que programamos anteriormente para obtener un producto por código de barras.
El controlador queda así:
Se ve un poco complejo, pero no lo es. Comenzamos obteniendo el producto por código de barras, después se valida que el producto exista por código de barras y que tenga existencia.
Más tarde busca dentro del ArrayList y si ya existe, simplemente le aumenta la cantidad. En caso de que no exista, agrega uno nuevo invocando al método add
.
Es un simple formulario que tiene el índice que ocupa el producto dentro de la lista. El método del controlador que lo maneja es el siguiente:
Se obtiene el carrito y si existe y tiene elementos, se invoca al método remove
del ArrayList, que elimina el elemento usando su índice.
Finalmente llamamos al método guardarCarrito
y hacemos una redirección a vender.
Para cancelar la venta actual solo hay que limpiar el carrito de compras:
Para limpar o eliminar el carrito, guardamos un ArrayList
vacío.
Este método se encarga de terminar la venta, es decir, crear una nueva venta, restar las existencias de los productos vendidos y agregar los nuevos que se venden junto con la venta.
Queda así:
Los comentarios son explicativos. Todavía no hemos visto la clase ProductoVendido
ni Venta
, así como los repositorios, pero los veremos a continuación.
Así es como queda esta interfaz.
Los productos vendidos son una entidad distinta. No pueden tener una relación con los productos, ya que los mismos pueden cambiar de precio en el futuro y si esto cambia, se afectaría al historial de ventas.
En fin, la entidad (esta sí es una entidad pues va a una base de datos) queda así:
En la línea 12, 13 y 14 establecemos la relación que tiene con la entidad Venta
, pues una venta tendrá productos vendidos.
Lleva casi las mismas propiedades que el producto normal. A esta entidad hay que agregarle su respectivo repositorio:
Eso es todo lo que se hace con esta entidad. Ahora veamos la última entidad.
Esto es fácil, pues no tenemos métodos manuales ni cosas por el estilo. Veamos la entidad de Venta
:
Tiene la fecha y hora, un id y un conjunto de productos que son de la clase ProductoVendido
. La fecha y hora se obtiene de la clase Utiles
, la cual se obtiene y formatea como vimos en otro post.
También existe el método getTotal
que recorre todo el conjunto de productos para obtener el total de venta.
Finalmente podemos fijarnos en la anotación @OneToMany
de la línea 13, la cual relaciona la venta con los productos vendidos.
Debemos crear un repositorio de las ventas:
No hacemos nada más que definir la interface
. Finalmente veamos el controlador:
Tiene un único método que muestra todas las ventas. No tenemos que preocuparnos por hacer relaciones manuales o algo así, pues al invocar a findAll
la venta tendrá los productos que se vendieron.
Después de obtener las ventas las enviamos a la vista de ver_ventas.html
que se encargan de renderizarlas y de dibujar las tablas:
La view es muy simple, se encarga de dibujar una tabla que tiene otra tabla dentro.
Si quieres puedes clonar el repositorio, he usado gradle para todo así que puedes usar incluso el bloc de notas para programar.
Lo que tienes que hacer es instalar gradle, MySQL y Java; es decir, todo lo necesario para programar con Spring Boot y MySQL.
Después ejecuta el programa con:
gradlew bootRun
Y crea el jar usando:
gradlew build
También puedes importar el proyecto usando IntelliJ IDEA.
Después ejecuta el jar con:
java -jar nombre-del-jar.jar
En ambos casos (ya sea que estés ejecutando el sistema para programar, o ejecutes el jar) visita http://localhost:8080/productos/mostrar.
No olvides que dejo el código fuente en GitHub.
Más información sobre Spring Boot y Gradle aquí.
Si mi contraseña, usuario o configuración no son acorde a tus requisitos recuerda que siempre puedes crear un archivo llamado application.properties
en el mismo directorio en donde está el jar, así, será tomado en cuenta ese archivo en lugar del mío.
Te invito a ver otros sistemas de ventas que he programado:
Sublime POS 3 – Este está listo para ser usado
Punto de venta con CodeIgniter
En mi blog te he enseñado a usar youtube-dl para descargar vídeos con permiso del…
Siguiendo con los tutoriales que consumen la API de los Bots de Telegram con cURL…
En un post previo te enseñé a enviar un mensaje en nombre de un Bot…
En este artículo te voy a mostrar una guía para imprimir en una impresora térmica…
La impresión de un PDF en cualquier impresora se puede automatizar con un bot de…
Hoy te enseñaré cómo enviar un mensaje a un usuario desde un bot de Telegram…
Esta web usa cookies.
Ver comentarios
Saludos como puedo modificar para que el precio de compra y precio de venta del producto me coja con decimales... De antemano gracias.
Hola. Gracias por sus comentarios. Si tiene alguna consulta con gusto lo atiendo en https://parzibyte.me/#contacto
Saludos!
como puedo usarlo con una base de datos pre cargada desde h2 ?
¿y la DB?
tira el mismo error que el comentario anterior de esta persona
Me imagino que ya exploró el repositorio, se me hace raro que no haya encontrado el siguiente archivo:
https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/src/main/resources/esquema_ventas_springboot.sql
Saludos :)
Hola! me gusto el articulo y descargué el programa junto con lo demás, hasta vi el video de presentación para ver cómo podia compilarlo y hacer que corriera pero no me deja, y las explicaciones que se muestran no son claras. podrias por favor, colaborarme explicando paso por paso literalmente cómo compilarlo y ejecutarlo de manera clara. es que siguiendo los pasos me causa error y no compila ni puede ver la página web del programa. PSDA: soy nueva en la materia y no tengo experiencia, por eso mi problema al compilarlo. espero puedas ayudar!
espero tu respuesta lo más pronto posible! por favor
Hola. En el repositorio del software se encuentra la documentación de cómo compilar ya sea desde la terminal o con un IDE; es igual a todos los proyectos de Spring Boot. Igualmente si tiene dudas específicas puede contactarme en https://parzibyte.me
Saludos :)
No entiendo nada, estoy viejo para estas cosas me quede en las aplicaciones de escritorio...
Donde se supone que ponte el proyecto para hacer uso de el ... En Tomcat ?????
Hola. Lo tiene que colocar en un servidor con Apache (solo apache, no Apache Tomcat) específicamente en la carpeta pública que en Linux es /var/www/html y en Windows, si usa XAMPP, es C:\xampp\htdocs
Hola que tal, al querer correr el programa me arroja el siguiente error:
"Error: no se ha encontrado o cargado la clase principal me.parzibyte.sistemaventasspringboot.Application" el proyecto lo descargue de
"https://github.com/parzibyte/sistema-ventas-spring-boot" tendras idea porque puede dar este error?
Estoy haciendo algo similar pero estoy trabado en poder modificar una factura existente, ya sea las cantidades o agregado de nuevos ítems. Tienes alguna sugerencia o ejemplo hecho?
Gracias y excelente explicación?
No tengo ejemplo, pero se me ocurre que una factura tendría una relación con una tabla de productos. El producto tendría cantidad. Podría crear un formulario dinámico por cada producto, y así tener una factura modificable.
Saludos y gracias por sus comentarios :)
me gusta mucho lo publicado, tendras algo que permita modificar un comorobante bmya creado donde puedan modificarse itens, cabridades y preciis? gracias
Hola, buen día. Lo siento, no cuento con ese ejemplo. Si quiere puede contratarme para hacerlo: https://parzibyte.me/blog/contrataciones-ayuda/
Saludos
Hola otra vez amigo, pudieras poner un pom.xml para los que usamos Maven o listar al menos las dependencias? estuve revisando en github y el pom.xml no tiene las dependencias, saludos
Gracias por tus comentarios. Las dependencias están en el archivo build.gradle, te invito a leer mis otros posts sobre Gradle para que veas cómo trabajo. El archivo es:
https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/build.gradle
No olvides seguirme y compartir :)
Buenos días amigo, muy buen trabajo, me interesa mucho la forma en que trabajas con Thymileaf, saludos