Spring Boot Framework

Sistema de ventas con Spring MVC, MySQL y Bootstrap

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.

Sistema de ventas con Spring Boot

Lo que tiene el sistema es:

  • Control de productos. Registrar producto, eliminar, editar
  • Descuento de existencias por cada venta
  • Interfaz para vender, en donde se escanea el código de barras y se agrega al carrito de compras
  • Registro de ventas, en donde se muestra la fecha y hora de la venta, el total y los productos que se compraron
  • Diseño responsivo. Se adapta a teléfonos, tabletas y computadoras portátiles o de escritorio

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)

Presentación en YouTube

Si quieres saber cómo ejecutarlo o quieres ver un vídeo con las características de este software, míralo a continuación:

Características técnicas

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.

Dependencias y configuración del sistema de ventas con Spring Boot

Las dependencias o el archivo build.gradle es el siguiente:

See the gist on github.

El archivo application.properties es el siguiente:

See the gist on github.

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.

Los templates o layouts

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:

See the gist on github.

Tengo 4 partes, el encabezado, el menú de navegación, el contenido principal y el pie de página.

El encabezado queda así:

See the gist on github.

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:

See the gist on github.

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):

See the gist on github.

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.

Productos

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.

See the gist on github.

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:

See the gist on github.

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.

Agregar producto

Cuando el usuario quiere agregar un producto se invoca al siguiente método del controlador:

See the gist on github.

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:

See the gist on github.

Tiene todo lo necesario para mostrar los errores de validación en caso de que existan. Su action es el método del controlador:

See the gist on github.

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.

Mostrar productos

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:

See the gist on github.

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.

See the gist on github.

Editar producto

Formulario para editar producto de POS usando Spring y Bootstrap 4

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í:

See the gist on github.

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:

See the gist on github.

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:

See the gist on github.

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.

Eliminar producto

Para eliminar un producto simplemente se envía un formulario con el id dentro del mismo. El método del controlador es el siguiente:

See the gist on github.

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.

ProductosController dentro del sistema de ventas con Spring Boot

Entonces el controlador de productos queda así:

See the gist on github.

El controlador tiene todo para manejar este CRUD de productos con Spring

Interfaz para vender en el Sistema de ventas con Spring Boot

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.

Interfaz para vender

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):

See the gist on github.

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:

See the gist on github.

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:

See the gist on github.

Renderiza la vista llamada vender.html que es la siguiente:

See the gist on github.

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.

Agregar producto al carrito

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í:

See the gist on github.

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.

Quitar producto de la lista de venta

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:

See the gist on github.

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.

Cancelar venta

Para cancelar la venta actual solo hay que limpiar el carrito de compras:

See the gist on github.

Para limpar o eliminar el carrito, guardamos un ArrayList vacío.

Terminar venta

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í:

See the gist on github.

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.

Productos vendidos en este POS

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í:

See the gist on github.

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:

See the gist on github.

Eso es todo lo que se hace con esta entidad. Ahora veamos la última entidad.

Ventas dentro de sistema de ventas con spring

Listado de ventas con productos, además de la fecha y hora

Esto es fácil, pues no tenemos métodos manuales ni cosas por el estilo. Veamos la entidad de Venta:

See the gist on github.

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:

See the gist on github.

No hacemos nada más que definir la interface. Finalmente veamos el controlador:

See the gist on github.

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:

See the gist on github.

La view es muy simple, se encarga de dibujar una tabla que tiene otra tabla dentro.

Descargar código del sistema de ventas Spring Boot y compilar

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í.

Tu propio application.properties para mi sistema de ventas con Spring Boot

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.

Conclusión

Te invito a ver otros sistemas de ventas que he programado:

Sublime POS 3 – Este está listo para ser usado

Punto de venta con PHP

Punto de venta con CodeIgniter

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

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! 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 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

  • Buenos días amigo, muy buen trabajo, me interesa mucho la forma en que trabajas con Thymileaf, saludos

Entradas recientes

Solución: Unable to extract uploader id con youtube-dl

En mi blog te he enseñado a usar youtube-dl para descargar vídeos con permiso del…

2 días hace

Enviar foto a Telegram usando cURL y Bot

Siguiendo con los tutoriales que consumen la API de los Bots de Telegram con cURL…

2 días hace

cURL y Telegram: enviar mensaje a Bot

En un post previo te enseñé a enviar un mensaje en nombre de un Bot…

2 días hace

Impresora térmica con Telegram usando Bot

En este artículo te voy a mostrar una guía para imprimir en una impresora térmica…

2 días hace

Imprimir PDF con Bot de Telegram

La impresión de un PDF en cualquier impresora se puede automatizar con un bot de…

6 días hace

Enviar mensaje con bot de Telegram usando JavaScript (lado del cliente)

Hoy te enseñaré cómo enviar un mensaje a un usuario desde un bot de Telegram…

1 semana hace

Esta web usa cookies.