# 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.