API REST profesional para gestión de inventario y punto de venta, pensada para pequeños y medianos negocios (PyMEs) como tiendas de abarrotes, ferreterías y misceláneas.
Construida con ASP.NET Core 8, Entity Framework Core (SQL Server), JWT con roles y arquitectura en capas.
Una tienda de abarrotes o ferretería típica en México lleva su inventario en libretas o en Excel. Eso provoca tres problemas concretos:
- Ventas de productos que ya no hay — el vendedor no sabe el stock real al momento de cobrar.
- Quiebres de stock silenciosos — nadie se entera de que se acabó el producto estrella hasta que un cliente lo pide.
- Cero visibilidad del negocio — el dueño no sabe cuánto vale su inventario, qué productos se venden más, ni cuánto vendió la semana pasada.
StockFlow resuelve los tres:
- Cada venta valida y descuenta stock automáticamente dentro de una transacción de base de datos: o se registra el ticket completo, o no se descuenta nada.
- El endpoint de alertas lista los productos en o por debajo de su stock mínimo, con el faltante sugerido para resurtir.
- Los reportes responden las preguntas del dueño: ventas por rango de fechas, top 10 de productos más vendidos y valor total del inventario (a costo y a precio de venta, con utilidad potencial).
| Módulo | Endpoints | Acceso |
|---|---|---|
| Autenticación | POST /api/auth/login |
Público |
| Productos | CRUD completo en /api/productos |
Lectura: ambos roles · Escritura: Admin |
| Categorías | CRUD completo en /api/categorias |
Lectura: ambos roles · Escritura: Admin |
| Proveedores | CRUD en /api/proveedores |
Lectura: ambos roles · Escritura: Admin |
| Ventas | POST /api/ventas (descuenta stock), consultas |
Registrar: Admin y Vendedor |
| Entradas de mercancía | POST /api/entradas (aumenta stock), consultas |
Registrar: Admin |
| Alertas | GET /api/alertas/stock-bajo |
Ambos roles |
| Reportes | GET /api/reportes/ventas, /top-productos, /valor-inventario |
Ambos roles |
Usuarios de demostración (creados por los datos semilla):
| Usuario | Contraseña | Rol |
|---|---|---|
admin |
Admin123! |
Admin |
vendedor |
Vendedor123! |
Vendedor |
erDiagram
Categoria ||--o{ Producto : "contiene"
Producto ||--o{ DetalleVenta : "se vende en"
Producto ||--o{ DetalleEntrada : "se recibe en"
Venta ||--|{ DetalleVenta : "incluye"
EntradaMercancia ||--|{ DetalleEntrada : "incluye"
Usuario ||--o{ Venta : "registra"
Usuario ||--o{ EntradaMercancia : "registra"
Proveedor ||--o{ EntradaMercancia : "surte"
Categoria {
int Id PK
string Nombre UK
string Descripcion
}
Producto {
int Id PK
string Nombre
string Sku UK
int CategoriaId FK
decimal PrecioCompra
decimal PrecioVenta
int StockActual
int StockMinimo
bool Activo
datetime FechaCreacion
}
Venta {
int Id PK
string Folio UK
int UsuarioId FK
datetime Fecha
decimal Total
}
DetalleVenta {
int Id PK
int VentaId FK
int ProductoId FK
int Cantidad
decimal PrecioUnitario
decimal Subtotal
}
Proveedor {
int Id PK
string Nombre
string Rfc
string Telefono
bool Activo
}
EntradaMercancia {
int Id PK
int ProveedorId FK
int UsuarioId FK
datetime Fecha
decimal Total
string Observaciones
}
DetalleEntrada {
int Id PK
int EntradaId FK
int ProductoId FK
int Cantidad
decimal CostoUnitario
decimal Subtotal
}
Usuario {
int Id PK
string NombreUsuario UK
string NombreCompleto
string PasswordHash
string Rol
bool Activo
}
Toda la documentación de endpoints está en español, con los usuarios demo visibles en la descripción y botón Authorize para el token JWT:
- SDK de .NET 8
- SQL Server: LocalDB (incluido con Visual Studio), SQL Server Express, o una instancia completa
-
Clonar el repositorio
git clone https://github.com/JDeat-S/StockFlow-API.git cd StockFlow-API -
Verificar la cadena de conexión en
src/StockFlow.Api/appsettings.json. Por defecto usa LocalDB:"ConnectionStrings": { "StockFlow": "Server=(localdb)\\MSSQLLocalDB;Database=StockFlowDb;Trusted_Connection=True;TrustServerCertificate=True" }
Si usas SQL Server Express, cambia el servidor a
.\SQLEXPRESS. Si usas una instancia con usuario y contraseña:Server=localhost;Database=StockFlowDb;User Id=sa;Password=TU_PASSWORD;TrustServerCertificate=True. -
Ejecutar la API
dotnet run --project src/StockFlow.Api
Al arrancar, la API aplica las migraciones automáticamente y, si la base está vacía, carga los datos semilla (36 productos de abarrotes y ferretería, ventas y una entrada de ejemplo). No se necesita ningún paso manual de base de datos.
-
Abrir Swagger en la URL que indique la consola (ej.
http://localhost:5188/swagger). -
Autenticarse: ejecutar
POST /api/auth/loginconadmin / Admin123!, copiar eltokende la respuesta, hacer clic en Authorize y pegarlo.
dotnet test20 pruebas unitarias (xUnit + EF Core In-Memory) cubren la lógica de negocio: validación y descuento de stock, atomicidad de la venta, folios consecutivos, alertas y reportes.
src/StockFlow.Api/
├── Controllers/ # Capa HTTP: rutas, códigos de estado, autorización por rol
├── Services/ # Lógica de negocio (transacciones, validaciones de dominio)
├── Repositories/ # Acceso a datos: repositorio genérico + repos específicos
├── DTOs/ # Contratos de entrada/salida, separados de las entidades
├── Validators/ # Reglas FluentValidation por cada DTO de escritura
├── Middleware/ # Manejador global de errores con respuestas JSON consistentes
├── Models/ # Entidades de EF Core
├── Data/ # DbContext, migraciones y datos semilla
└── Helpers/ # Constantes de roles y excepciones de dominio
tests/StockFlow.Tests/ # Pruebas unitarias de los servicios
Todas las respuestas (éxito y error, incluidos 401/403 del middleware de autenticación) usan la misma envoltura:
{
"exito": false,
"mensaje": "Stock insuficiente para \"Refresco Coca-Cola 600 ml\" (SKU BEB-001): se solicitaron 9999 y solo hay 103.",
"datos": null,
"errores": null
}-
Transacción explícita en ventas y entradas.
VentaService.CrearAsyncenvuelve la validación de stock, el descuento y el alta del ticket enBeginTransactionAsync. Si cualquier renglón falla (producto inexistente, inactivo o sin stock), no se persiste nada. Una prueba unitaria (CrearVenta_UnRenglonSinStock_NoPersisteNada) verifica esa atomicidad. -
El precio se copia al detalle al momento de la operación.
DetalleVenta.PrecioUnitarioyDetalleEntrada.CostoUnitarioguardan el precio vigente al vender/comprar. Cambiar el precio de un producto después no altera tickets históricos ni reportes pasados. -
El cliente nunca manda el precio. En
POST /api/ventassolo se envían producto y cantidad; el precio se toma del catálogo en el servidor. Esto evita que un cliente malicioso registre ventas con precios alterados. -
El stock no se edita directamente.
PUT /api/productos/{id}no aceptastockActual: el stock solo cambia mediante ventas (resta) y entradas de mercancía (suma). Así todo movimiento de inventario queda auditado. -
Borrado lógico de productos con historial. Eliminar un producto con ventas o entradas lo desactiva (
Activo = false) en lugar de borrarlo, preservando la integridad referencial de tickets y reportes. Solo los productos sin movimientos se eliminan físicamente. -
Folio legible por humanos (
V-2026-00042) con consecutivo anual, separado delIdtécnico — es lo que el dueño escribe en un ticket o busca cuando hay una aclaración. -
Renglones duplicados se consolidan. Si el mismo producto llega dos veces en una venta, las cantidades se suman antes de validar stock; así no se puede burlar la validación dividiendo la cantidad.
-
Roles como constantes, no tabla. Con dos roles fijos (
Admin,Vendedor), una tabla de roles sería sobre-ingeniería. Se usaPasswordHasherde ASP.NET Identity para el hash de contraseñas (PBKDF2) sin arrastrar todo el framework de Identity. El registro de ventas está permitido al rol Vendedor porque es la operación diaria del punto de venta; el resto de las escrituras (catálogo, entradas) requiere Admin. -
Capas en carpetas dentro de un solo proyecto. Controllers → Services → Repositories demuestran la separación de responsabilidades sin la fricción de 4 proyectos; quien clona el repo lo compila y entiende en minutos. Los reportes (solo lectura, con agregaciones) consultan EF Core directamente con
AsNoTrackingy proyecciones — un patrón tipo CQRS-lite documentado enReporteService. -
Mensajes de error accionables. El error de stock insuficiente indica producto, SKU, cantidad solicitada y stock disponible. El login falla con el mismo mensaje para usuario inexistente y contraseña incorrecta (no se revela cuál falló).
-
Migraciones y seed automáticos al arrancar. Para un proyecto demo, eliminar pasos manuales de instalación vale más que el control fino de despliegue; en producción se ejecutarían las migraciones en el pipeline de CI/CD.
-
⚠️ Nota de seguridad: la clave JWT vive enappsettings.jsonsolo por tratarse de un proyecto de demostración. En producción debe ir en variables de entorno o un gestor de secretos (Azure Key Vault, AWS Secrets Manager), igual que la cadena de conexión.
| Tecnología | Uso |
|---|---|
| ASP.NET Core 8 Web API | Framework principal |
| Entity Framework Core 8 + SQL Server | ORM, Code First con migraciones |
| JWT Bearer | Autenticación con roles |
| FluentValidation | Validación declarativa de DTOs |
| Swashbuckle (Swagger) | Documentación OpenAPI en español |
| xUnit + EF Core In-Memory | Pruebas unitarias |
