Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juadariasmar/inventory_project/llms.txt

Use this file to discover all available pages before exploring further.

Inventory System enforces access control through a two-layer model: a coarse role that determines what a user can manage, and a set of fine-grained permission flags that govern which specific features a regular user can access. Roles are stored on the Usuario record in the rol column; permissions are stored as a Postgres array in the permisos column.

Roles

RoleDescription
SUPER_ADMINPlatform-level administrator. Can view and manage all companies and their data. Auto-promoted at login for any email listed in the ADMIN_EMAILS environment variable.
ADMINCompany administrator. Has unrestricted access to every feature within their own company, including user management, product catalogue, sales, reports, and configuration.
USUARIORegular company member. Access to individual features is controlled entirely by the Permiso flags assigned by an admin.
SUPER_ADMIN is a superset of ADMIN. Both roles bypass all Permiso checks. The distinction is that SUPER_ADMIN can also cross company boundaries (e.g. accessing /admin/empresas), while ADMIN is scoped to their own empresaId.

Permissions (Permiso enum)

Permission flags apply only to users with the USUARIO role. ADMIN and SUPER_ADMIN users always pass every permission check.
PermissionWhat it grants
VER_ANALISISAccess to the analytics dashboard — charts, KPIs, and sales summaries.
EXPORTAR_REPORTESDownload inventory and sales data as Excel or PDF reports.
REGISTRAR_MOVIMIENTOSCreate manual stock movement entries (entries and exits outside of sales or purchase orders).
REALIZAR_VENTASProcess sales from the point-of-sale terminal.
AGREGAR_STOCKAdd stock entries (positive adjustments).
DESCONTAR_STOCKDeduct stock (negative adjustments).
AGREGAR_STOCK and DESCONTAR_STOCK were added to support sibling branches of the application. All values must be present in the Prisma enum so that Prisma can hydrate records from the database without errors, even if a given deployment only exposes a subset of them in its UI.

How permission checks work in code

All permission and session helpers live in src/lib/permisos.ts. They are async functions that call obtenerSesion() internally and are safe to use in Server Components, Route Handlers, and Server Actions.

tienePermiso(permiso)

Returns true if the current user is allowed to perform the action associated with permiso. ADMIN and SUPER_ADMIN always return true; USUARIO must have the flag in their permisos array.
import { tienePermiso } from '@/lib/permisos'
import { Permiso } from '@prisma/client'

// Inside a Next.js Route Handler
export async function GET() {
  const permitido = await tienePermiso(Permiso.VER_ANALISIS)
  if (!permitido) {
    return Response.json({ error: 'No autorizado' }, { status: 403 })
  }

  // ... return analytics data
}

validarAccesoEmpresa()

A stricter guard intended for every API route that reads or writes tenant data. It verifies three conditions in sequence and throws an AppError if any fail:
  1. A valid session exists (usuario is not null).
  2. usuario.estado === 'ACTIVO' — suspended or pending users are rejected.
  3. usuario.empresaId is present — the user belongs to a company.
On success it returns { usuarioId, empresaId, rol, usuario } for use in the route handler.
import { validarAccesoEmpresa } from '@/lib/permisos'

export async function POST(request: Request) {
  const { usuarioId, empresaId, rol } = await validarAccesoEmpresa()

  // empresaId is safe to use in Prisma queries here
  const productos = await prisma.producto.findMany({
    where: { empresaId },
  })

  return Response.json(productos)
}

esAdmin() and esSuperAdmin()

Boolean helper functions for branching logic that depends on role level. Both require estado === 'ACTIVO'.
import { esAdmin, esSuperAdmin } from '@/lib/permisos'

if (await esSuperAdmin()) {
  // cross-company operations
} else if (await esAdmin()) {
  // company-scoped admin operations
}

Checking a permission in a full Route Handler

The following example shows a complete pattern for a route that requires both an active company session and a specific permission flag:
// src/app/api/reportes/route.ts
import { NextResponse } from 'next/server'
import { validarAccesoEmpresa, tienePermiso } from '@/lib/permisos'
import { Permiso } from '@prisma/client'
import { AppError } from '@/lib/AppError'

export async function GET() {
  try {
    // 1. Validate session, estado, and empresaId
    const { empresaId } = await validarAccesoEmpresa()

    // 2. Check the specific permission flag
    const permitido = await tienePermiso(Permiso.EXPORTAR_REPORTES)
    if (!permitido) {
      return NextResponse.json({ error: 'No tienes permiso para exportar reportes' }, { status: 403 })
    }

    // 3. Proceed with scoped query
    const datos = await prisma.venta.findMany({ where: { empresaId } })
    return NextResponse.json(datos)
  } catch (error) {
    if (error instanceof AppError) {
      return NextResponse.json({ error: error.message }, { status: error.statusCode })
    }
    return NextResponse.json({ error: 'Error interno' }, { status: 500 })
  }
}

User states

The EstadoUsuario enum governs whether a user’s session is honoured by the permission layer, regardless of their role.

PENDIENTE

The user self-registered without an invitation. They can sign in but validarAccesoEmpresa() throws a 403 and tienePermiso() returns false until an admin promotes them to ACTIVO.

ACTIVO

The account is fully operational. All role and permission checks proceed normally.

SUSPENDIDO

The account has been manually blocked by an admin. Sign-in still succeeds at the Neon Auth layer, but all application-level guards reject requests with 403.
Suspension is enforced at the application layer, not at the Neon Auth layer. A suspended user still holds a valid session cookie. Always call validarAccesoEmpresa() (which checks estado) rather than relying solely on the presence of a session.

Managing user permissions

Permissions are assigned and updated by an ADMIN through the user management interface or directly via the API:
# Update a user's permissions (ADMIN only)
PUT /api/empresa/usuarios/{id}
{
  "permisos": ["VER_ANALISIS", "REALIZAR_VENTAS", "EXPORTAR_REPORTES"]
}
Only values in the Permiso enum are accepted. Unknown strings are silently stripped by UsuariosService.actualizarUsuario() before the database write.

Build docs developers (and LLMs) love