Co-authored-by: Asis Ferrer <aferrer@aferrer.dev> Co-committed-by: Asis Ferrer <aferrer@aferrer.dev>
Stock Manager Home
Sistema de gestión de inventario, activos serializados, asignaciones y movimientos construido con Next.js, Prisma, PostgreSQL, NextAuth y Bun.
Quick start
bun install
cp .env.example .env
bun run db:generate
bun run db:migrate
bun run db:seed
bun run dev
Abrí la aplicación en http://localhost:3000.
db:seedcrea un administrador inicial cuando no existe ningún admin activo. Configurá las variablesADMIN_*en.envantes de usarlo en entornos compartidos o productivos.
Qué hace la aplicación
Stock Manager permite gestionar:
- Ítems genéricos: productos gestionados por cantidad de stock.
- Activos serializados: equipos individuales con número de serie único.
- Categorías: clasificación de ítems y activos.
- Personas: personas o departamentos que reciben asignaciones. Cada persona puede tener un usuario del sistema vinculado o existir sin credenciales.
- Asignaciones: entrega y devolución de ítems o activos.
- Movimientos: historial auditable de entradas, salidas, asignaciones, devoluciones y ajustes.
- Usuarios del sistema: gestión unificada con personas, roles, estado activo y reseteo de contraseña.
- Importación CSV: flujo legacy de importación masiva, mantenido estructuralmente pero pendiente de rediseño.
Stack técnico
| Área | Tecnología |
|---|---|
| Framework | Next.js 16 App Router |
| Runtime/package manager | Bun |
| UI | React 19, Tailwind CSS, Radix UI/Shadcn-style components |
| Formularios | React Hook Form |
| Validación | Zod |
| Autenticación | NextAuth v5 |
| ORM | Prisma 7 |
| Base de datos | PostgreSQL |
| Formato/lint | Biome |
| Deploy | Docker / Docker Compose |
Internacionalización (i18n)
La aplicación soporta inglés (en) y español (es) en todas las superficies de usuario. La selección de idioma se persiste mediante una cookie stock-manager-locale validada en servidor y se aplica con un cambio de idioma por página sin rutas prefijadas.
Superficies localizadas:
- Login y navbar compartida con selector de idioma compacto.
- Shell común: sidebar, navegación, search, paginación, botón submit, página de acceso denegado.
- Inventario: categorías, ítems, activos, personas, asignaciones, movimientos.
- Personas: gestión unificada de personas y usuarios del sistema.
La arquitectura i18n sigue un patrón consistente:
- Diccionarios tipados en
src/i18n/dictionaries/en.tsyes.tscon paridad de claves obligatoria. - Resolución server-side: las páginas obtienen
getI18n()y pasan props acotadas a componentes cliente. - Schemas localizados: builders (
buildCreateXSchema(copy)) que aceptan copia de diccionario e inyectan mensajes de validación localizados. - Actions localizadas: resuelven locale en servidor, construyen schemas con copia localizada y mapean errores de use-case mediante message mappers.
- Datos de usuario vs UI: nombres, emails, seriales y valores de enumeraciones canónicas nunca se traducen; solo se localizan las etiquetas de presentación.
La importación CSV queda fuera del alcance actual de i18n por su rediseño previsto.
Configuración de entorno
Copiá el ejemplo y completá los valores reales:
cp .env.example .env
Variables principales:
| Grupo | Variables |
|---|---|
| Base de datos | DATABASE_URL, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_HOST, POSTGRES_PORT |
| Auth | AUTH_SECRET, AUTH_TRUST_HOST, DOMAIN, NODE_ENV, DEMO_MODE |
| Bootstrap admin | ADMIN_BOOTSTRAP_ENABLED, ADMIN_EMAIL, ADMIN_NAME, ADMIN_PASSWORD |
Bootstrap admin
El seed ejecuta prisma/seed.ts, que llama a prisma/bootstrap-admin.ts.
Comportamiento:
- Si ya existe un usuario
ADMINactivo, no hace nada. - Si
ADMIN_BOOTSTRAP_ENABLED=false, no crea administrador. - En producción,
ADMIN_PASSWORDes obligatorio. - En desarrollo, si no se define contraseña, usa un valor por defecto sólo para facilitar el arranque local.
Ejecutar manualmente:
bun run db:seed
Desarrollo local
Prerrequisitos
- Bun 1.3+
- PostgreSQL accesible mediante
DATABASE_URL - Docker disponible para tests con Testcontainers
- Git
Pasos
# 1. Instalar dependencias
bun install
# 2. Configurar entorno
cp .env.example .env
# 3. Generar cliente Prisma
bun run db:generate
# 4. Aplicar migraciones en desarrollo
bun run db:migrate
# 5. Crear admin inicial, si corresponde
bun run db:seed
# 6. Levantar Next
bun run dev
Desarrollo con DevContainer
El proyecto incluye configuración para desarrollo en contenedor.
- Abrí el repo en VS Code.
- Elegí Reopen in Container.
- El entorno instala dependencias y puede levantar el servidor de desarrollo.
- Accedé a http://localhost:3000.
Docker / despliegue
Con Docker Compose:
docker compose -f compose.yaml up -d
El Dockerfile ejecuta al iniciar:
bun run db:deploy && bun run db:seed && bun run start
Esto aplica migraciones pendientes, ejecuta el bootstrap admin si corresponde y luego inicia Next.
Scripts disponibles
| Script | Descripción |
|---|---|
bun run dev |
Inicia Next en desarrollo con Turbopack |
bun run build |
Construye la aplicación para producción |
bun run start |
Inicia la build de producción |
bun run lint |
Ejecuta Biome lint con escritura de fixes |
bun run format |
Formatea con Biome |
bun run check |
Ejecuta Biome check con escritura de fixes |
bun run test |
Ejecuta toda la suite Vitest: unit + integración |
bun run test:unit |
Ejecuta unit tests rápidos |
bun run test:integration |
Genera Prisma y ejecuta integration tests con PostgreSQL Testcontainers |
bun run test:e2e |
Genera Prisma y ejecuta Playwright E2E smoke con DB aislada |
bun run test:coverage |
Ejecuta Vitest con coverage V8 |
bun run db:push |
Sincroniza el schema sin crear migraciones |
bun run db:migrate |
Crea/aplica migraciones en desarrollo |
bun run db:migrate:reset |
Resetea la base y reaplica migraciones |
bun run db:deploy |
Aplica migraciones en entornos de deploy |
bun run db:generate |
Genera el cliente Prisma en src/generated/prisma |
bun run db:seed |
Ejecuta el seed/bootstrap admin |
bun run db:studio |
Abre Prisma Studio |
Prisma
El proyecto usa Prisma 7 con configuración en:
prisma.config.ts
prisma/schema.prisma
prisma/migrations/
El cliente Prisma se genera en:
src/generated/prisma
Ese directorio está ignorado por Git. Después de clonar, cambiar schema o instalar dependencias, ejecutá:
bun run db:generate
Validar schema:
bunx prisma validate
Arquitectura del código
La aplicación separa responsabilidades por capa:
| Capa | Ruta | Responsabilidad |
|---|---|---|
| UI / routes | src/app |
Páginas, layouts y componentes por ruta |
| Server Actions | src/actions |
Boundary de servidor: auth, Zod, llamada a use-case, revalidación |
| Use-cases | src/use-cases |
Reglas de negocio, coordinación multi-entidad y transacciones |
| Services | src/services |
Acceso a datos/repositories Prisma; muchos aceptan tx opcional |
| Schemas | src/schemas |
Validación Zod y tipos de formularios/actions |
| Types | src/types |
Tipos compartidos y aliases de Prisma |
| Lib | src/lib |
Infraestructura común: auth, prisma, paginate, security, constants, utils |
Regla práctica
- Las Actions deben ser finas.
- Las reglas de negocio viven en use-cases.
- Los services no orquestan flujos: leen/escriben datos y aceptan transacciones cuando participan en una operación mayor.
Estructura principal
src/
├── actions/ # Server Actions finas
├── app/ # Next.js App Router
│ ├── (auth)/ # Login
│ ├── (dashboard)/ # Dashboard, inventario, asignaciones, importación, people
│ ├── api/ # API routes
│ └── forbidden/ # Página de acceso denegado
├── components/ # Componentes compartidos y UI
├── generated/ # Cliente Prisma generado, ignorado por Git
├── hooks/ # Hooks React
├── lib/ # Infraestructura y utilidades
├── schemas/ # Schemas Zod
├── services/ # Repositories Prisma / read models
├── styles/ # Estilos globales
├── types/ # Tipos compartidos
└── use-cases/ # Casos de uso transaccionales
prisma/
├── bootstrap-admin.ts # Crea/activa admin inicial si corresponde
├── migrations/ # Migraciones Prisma
├── schema.prisma # Modelo de datos
└── seed.ts # Entry point de seed
Autenticación y autorización
- Login con NextAuth credentials.
- Passwords hasheadas con
bcryptjs. - Roles soportados:
ADMIN,MANAGER,STAFF,VIEWER. /people/*requiere rolADMINpara operaciones de gestión.- Usuarios inactivos no pueden iniciar sesión.
Helpers relevantes:
src/services/auth.service.ts
src/lib/auth.ts
src/proxy.ts
Modelo de datos
Entidades principales:
| Entidad | Descripción |
|---|---|
User |
Usuarios del sistema, roles y estados de ciclo de vida |
Person |
Personas del organigrama; pueden vincularse a un User |
Category |
Categorías de inventario |
Item |
Ítems genéricos con stock |
Asset |
Activos serializados |
Assignment |
Asignaciones y devoluciones |
Movement |
Historial auditable de movimientos |
Ver el schema completo en:
prisma/schema.prisma
Flujos importantes
Asignaciones
- Crear asignación decrementa stock de forma transaccional.
- Devolver asignación incrementa stock si aplica y libera activo.
- Movimientos
ASSIGNMENTyRETURNse crean dentro del use-case.
Activos
- Crear activo disponible incrementa stock.
- Crear activo asignado crea asignación y movimiento asociado.
- Cambios de estado generan movimientos adecuados (
IN,OUT,ASSIGNMENT,RETURN,ADJUSTMENT).
Ítems
Item.namees único.- Crear item con stock inicial genera movimiento
IN. - El borrado es soft delete y se bloquea si hay stock o assets asociados.
Usuarios
- Sólo
ADMINpuede gestionar personas y usuarios. - No se puede quitar el propio acceso admin.
- No se puede dejar el sistema sin admin activo.
- La protección de último admin usa transacción serializable con retry de conflictos Prisma
P2034.
Transición de arquitectura
La aplicación está migrando de un modelo separado de usuarios y destinatarios hacia una gestión unificada de personas:
| Antes | Después |
|---|---|
Recipient |
Person |
Gestión en /admin/users |
Gestión en /people |
| Usuarios y personas desvinculados | Persona puede vincularse a un User opcional |
Estado actual:
- El schema Prisma y la migración inicial reflejan el nuevo modelo.
- Las rutas y componentes de UI ya fueron migrados a
/people.
Testing
El proyecto tiene una base inicial de tests en tres niveles:
| Nivel | Comando | Cobertura |
|---|---|---|
| Unit | bun run test:unit |
Schemas Zod, helpers de seguridad y helpers de roles auth |
| Integración | bun run test:integration |
Use-cases principales contra PostgreSQL real con Testcontainers |
| E2E smoke | bun run test:e2e |
Login, dashboard, admin users, inventory items y assignments con Playwright |
Integration tests
Los tests de integración viven en:
tests/integration/
Usan PostgreSQL real mediante Testcontainers. El helper de DB:
- levanta un contenedor PostgreSQL aislado;
- setea
DATABASE_URLantes de importar Prisma/use-cases; - aplica migraciones con
prisma migrate deploy; - limpia tablas entre tests.
Importante:
src/lib/prisma.tsleeDATABASE_URLal importarse. En tests, configurá el entorno antes de importar@/lib/prisma, services o use-cases.
E2E smoke tests
Los tests E2E viven en:
tests/e2e/
Playwright levanta una app real contra una DB Testcontainers aislada y crea un admin determinístico para el smoke test.
El server E2E usa next dev --webpack. Next 16 puede usar Turbopack por defecto y durante la configuración inicial emitió un panic compilando /assignments; para E2E automatizado se fuerza Webpack por estabilidad.
Secuencia completa recomendada
Antes de subir cambios grandes, ejecutá:
bun run test && bun run test:e2e && bunx tsc --noEmit && bunx prisma validate
Validación antes de subir cambios
Ejecutá al menos:
bunx tsc --noEmit
bunx prisma validate
Para cambios de Prisma:
bun run db:generate
bunx prisma validate
Para cambios de formato/lint:
bun run check
Estado conocido
- La importación CSV actual es legacy y se mantiene por compatibilidad; está previsto rediseñarla.
- El cliente Prisma generado no se versiona; debe generarse antes de build/deploy.