407 lines
13 KiB
Markdown
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.
|