Files
stock-manager/README.md

407 lines
13 KiB
Markdown

# 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
```bash
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](http://localhost:3000).
> `db:seed` crea un administrador inicial cuando no existe ningún admin activo. Configurá las variables `ADMIN_*` en `.env` antes 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.ts` y `es.ts` con 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:
```bash
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 `ADMIN` activo, no hace nada.
- Si `ADMIN_BOOTSTRAP_ENABLED=false`, no crea administrador.
- En producción, `ADMIN_PASSWORD` es obligatorio.
- En desarrollo, si no se define contraseña, usa un valor por defecto sólo para facilitar el arranque local.
Ejecutar manualmente:
```bash
bun run db:seed
```
## Desarrollo local
### Prerrequisitos
- Bun 1.3+
- PostgreSQL accesible mediante `DATABASE_URL`
- Docker disponible para tests con Testcontainers
- Git
### Pasos
```bash
# 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.
1. Abrí el repo en VS Code.
2. Elegí **Reopen in Container**.
3. El entorno instala dependencias y puede levantar el servidor de desarrollo.
4. Accedé a [http://localhost:3000](http://localhost:3000).
## Docker / despliegue
Con Docker Compose:
```bash
docker compose -f compose.yaml up -d
```
El `Dockerfile` ejecuta al iniciar:
```bash
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:
```txt
prisma.config.ts
prisma/schema.prisma
prisma/migrations/
```
El cliente Prisma se genera en:
```txt
src/generated/prisma
```
Ese directorio está ignorado por Git. Después de clonar, cambiar schema o instalar dependencias, ejecutá:
```bash
bun run db:generate
```
Validar schema:
```bash
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
```txt
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 rol `ADMIN` para operaciones de gestión.
- Usuarios inactivos no pueden iniciar sesión.
Helpers relevantes:
```txt
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:
```txt
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 `ASSIGNMENT` y `RETURN` se 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.name` es ú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 `ADMIN` puede 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:
```txt
tests/integration/
```
Usan PostgreSQL real mediante Testcontainers. El helper de DB:
1. levanta un contenedor PostgreSQL aislado;
2. setea `DATABASE_URL` antes de importar Prisma/use-cases;
3. aplica migraciones con `prisma migrate deploy`;
4. limpia tablas entre tests.
> Importante: `src/lib/prisma.ts` lee `DATABASE_URL` al importarse. En tests, configurá el entorno antes de importar `@/lib/prisma`, services o use-cases.
### E2E smoke tests
Los tests E2E viven en:
```txt
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á:
```bash
bun run test && bun run test:e2e && bunx tsc --noEmit && bunx prisma validate
```
## Validación antes de subir cambios
Ejecutá al menos:
```bash
bunx tsc --noEmit
bunx prisma validate
```
Para cambios de Prisma:
```bash
bun run db:generate
bunx prisma validate
```
Para cambios de formato/lint:
```bash
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.