Sistema minimalista de Punto de Venta (POS) con gestión de inventario en tiempo real, construido con Flask (Python) y MySQL.
- ✅ Interfaz moderna y responsive
- ✅ Listado de productos con búsqueda en tiempo real
- ✅ Carrito de compras dinámico
- ✅ 3 métodos de pago (efectivo, tarjeta, transferencia)
- ✅ Transacciones ACID con lock pesimista (SELECT FOR UPDATE)
- ✅ Stock actualizado automáticamente
- ✅ Recibo de venta generado al finalizar
- ✅ Indicador de estado de conexión a BD
- ✅ Validación exhaustiva de datos
- ✅ Manejo seguro de errores
- Python 3.8 o superior
- MySQL 5.7+ o MariaDB 10.3+
- pip (gestor de paquetes de Python)
cd D:\expo\Mini_POSWindows (PowerShell):
python -m venv .venv
.\.venv\Scripts\Activate.ps1Windows (CMD):
python -m venv .venv
.venv\Scripts\activate.batLinux/Mac:
python3 -m venv .venv
source .venv/bin/activatepip install -r backend/requirements.txtOpción A: Usando el script Python (recomendado)
python init_db.pyOpción B: Manualmente con MySQL
mysql -u root < database/schema.sqlOpción C: Desde cliente MySQL
-- En tu cliente MySQL:
mysql> SOURCE database/schema.sql;Nota: Si tu MySQL tiene contraseña, ajusta
backend/config.py:'mysql+pymysql://root:tu_contraseña@localhost:3306/pos_inventario'
Opción A: Script automático (recomendado)
python run.pyOpción B: Desde terminal
cd backend
python app.pyAbre tu navegador y ve a:
http://localhost:5000
-
Listar productos:
- Deberías ver 10 productos en el catálogo
- Busca por nombre (ej: "Coca", "Arroz")
-
Agregar al carrito:
- Haz clic en un producto o en el botón
+ - Verifica que el badge del carrito se actualiza
- Haz clic en un producto o en el botón
-
Modificar cantidad:
- Usa los botones
−y+en el carrito - Verifica que el total se calcula correctamente
- Usa los botones
-
Finalizar venta:
- Selecciona método de pago
- Haz clic en "Finalizar Venta"
- Verifica que aparece el recibo
- Comprueba que el stock se actualizó
Listar productos:
curl http://localhost:5000/api/productosCrear una venta:
curl -X POST http://localhost:5000/api/ventas \
-H "Content-Type: application/json" \
-d '{
"metodo_pago": "efectivo",
"items": [
{"producto_id": 1, "cantidad": 2, "precio_unitario": 15.50},
{"producto_id": 3, "cantidad": 1, "precio_unitario": 22.50}
]
}'Verificar stock (producto ID 1):
curl http://localhost:5000/api/productos/1Mini_POS/
├── backend/
│ ├── app.py # Aplicación Flask principal
│ ├── config.py # Configuración (BD, claves, etc.)
│ ├── models.py # Modelos SQLAlchemy (Producto, Venta, etc.)
│ ├── routes.py # Endpoints API (/api/productos, /api/ventas)
│ └── requirements.txt # Dependencias Python
├── frontend/
│ └── index.html # Interfaz única en HTML/CSS/JS
├── database/
│ └── schema.sql # Script de creación de BD y datos iniciales
├── .env.example # Variables de entorno (ejemplo)
├── init_db.py # Script para inicializar la BD
├── run.py # Script para ejecutar la app
├── .gitignore # Archivos ignorados por git
└── README.md # Este archivo
Copia .env.example a .env:
cp .env.example .envEdita .env con tus valores:
FLASK_ENV=development
SECRET_KEY=tu-clave-secreta-super-segura
DATABASE_URL=mysql+pymysql://root:contraseña@localhost:3306/pos_inventario- ✅ Validación exhaustiva de entrada (tipos, rangos, campos requeridos)
- ✅ Transacciones ACID con lock pesimista (SELECT FOR UPDATE)
- ✅ Prevención de race conditions en stock
- ✅ Manejo seguro de decimales (Decimal en lugar de float)
- ✅ CORS configurado (ajusta orígenes permitidos en producción)
- ✅ Logging de transacciones importantes
- ✅ Manejo centralizado de errores
- Flask ya está corriendo en otro terminal
- Detén todos los servidores Flask
- O cambia el puerto en
app.py:app.run(port=5001)
- Verifica que MySQL está corriendo
# Windows: verifica en Servicios # Linux: sudo systemctl status mysql
- Verifica credenciales en
backend/config.py - Verifica que la BD
pos_inventarioexiste:SHOW DATABASES;
- Activa el entorno virtual
- Instala dependencias:
pip install -r backend/requirements.txt
- La tabla
INSERTse ejecuta cada vez que creas tablas - Usa
INSERT ... ON DUPLICATE KEY UPDATEo elimina/crea la BD:DROP DATABASE pos_inventario;
Retorna lista de todos los productos
Response (200):
[
{
"id": 1,
"nombre": "Coca-Cola 355ml",
"precio": 15.50,
"stock_actual": 98
}
]Retorna un producto específico
Response (200):
{
"id": 1,
"nombre": "Coca-Cola 355ml",
"precio": 15.50,
"stock_actual": 98
}Crea una nueva venta (con transacción ACID)
Request Body:
{
"metodo_pago": "efectivo",
"items": [
{
"producto_id": 1,
"cantidad": 2,
"precio_unitario": 15.50
}
]
}Response (201):
{
"id": 1,
"metodo_pago": "efectivo",
"total": 31.00,
"created_at": "2026-05-17T20:30:45.123456",
"detalles": [
{
"id": 1,
"venta_id": 1,
"producto_id": 1,
"cantidad": 2,
"precio_unitario": 15.50,
"subtotal": 31.00
}
]
}- Autenticación de usuarios (JWT o sesiones)
- Historial de ventas y reportes
- Sistema de categorías de productos
- Devoluciones y descuentos
- Backup automático de BD
- Tests automáticos de integración
- Deployment en servidor (AWS, Heroku, DigitalOcean)
- Mobile app (React Native, Flutter)
- Las transacciones usan
SELECT ... FOR UPDATEpara evitar race conditions - Los precios se calculan del
producto.precioen tiempo de venta (no se usa el del request) - El frontend está en HTML5 puro sin frameworks para máxima compatibilidad
- Las validaciones se hacen en backend (no confiar en frontend)
Proyecto de demostración educativa. Úsalo como referencia.
Si tienes problemas:
- Verifica que MySQL está corriendo
- Revisa los logs en la terminal de Flask
- Comprueba que
database/schema.sqlse ejecutó correctamente - Verifica las credenciales en
backend/config.py
Versión: 1.0.0
Última actualización: Mayo 2026