Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ChrisCore1/inventario_sud/llms.txt

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

Inventario SUD uses Drizzle ORM with a Neon PostgreSQL database. The schema is defined in lib/schema.ts and covers roles, users, inventory items, consumables, loans, audit logs, and password resets. The biometria field on the Usuario table uses the pgvector extension — you must enable it in your Neon project before biometric login will work.

pgvector extension

The biometria column is of type vector(1024), which requires the pgvector extension to be enabled in your PostgreSQL instance. In Neon, you can enable it by running CREATE EXTENSION IF NOT EXISTS vector; in the SQL editor of your project dashboard. Without this extension, the schema migration will fail.
Facial recognition vectors are compared using cosine similarity. The query finds the user whose stored biometric embedding is closest to the one captured at login:
-- Cosine similarity query used for biometric authentication (from app/api/biometria/route.ts)
SELECT id_usuario, email, nombre_usuario, biometria <=> $1::vector AS distance
FROM "Usuario"
WHERE biometria IS NOT NULL
ORDER BY distance ASC
LIMIT 1;
The <=> operator is the pgvector cosine distance operator. A similarity value close to 1.0 means the vectors are nearly identical.

Tables

Rol

Stores the access roles that can be assigned to users (for example, Obispo, Consejero).
export const roles = pgTable("Rol", {
  id_rol: serial("id_rol").primaryKey(),
  nombre_rol: varchar("nombre_rol", { length: 50 }).notNull().unique(),
});
id_rol
serial
required
Auto-incrementing primary key.
nombre_rol
varchar(50)
required
Unique name for the role, such as Obispo or Consejero.

Usuario

Stores system users. Each user is assigned exactly one role and may optionally have a biometric embedding stored for facial recognition login.
export const usuarios = pgTable("Usuario", {
  id_usuario: serial("id_usuario").primaryKey(),
  nombre_usuario: varchar("nombre_usuario", { length: 100 }).notNull(),
  email: varchar("email", { length: 150 }).notNull().unique(),
  password_hash: varchar("password_hash", { length: 255 }).notNull(),
  is_active: boolean("is_active").default(true),
  biometria: vector("biometria"),         // vector(1024) — pgvector
  id_rol: integer("id_rol").notNull().references(() => roles.id_rol),
});
id_usuario
serial
required
Auto-incrementing primary key.
nombre_usuario
varchar(100)
required
Display name of the user.
email
varchar(150)
required
Unique email address used for login and password reset.
password_hash
varchar(255)
required
bcrypt hash of the user’s password.
is_active
boolean
Whether the account is active. Defaults to true. Set to false to disable login without deleting the record.
biometria
vector(1024)
1024-dimensional facial recognition embedding generated by @vladmandic/human. Requires the pgvector extension. Null when biometric login has not been enrolled.
id_rol
integer
required
Foreign key referencing Rol.id_rol.

Categoria

Lookup table for item categories shared by both Articulo and Consumible.
export const categorias = pgTable("Categoria", {
  id_categoria: serial("id_categoria").primaryKey(),
  tipo_categoria: varchar("tipo_categoria", { length: 100 }).notNull().unique(),
});
id_categoria
serial
required
Auto-incrementing primary key.
tipo_categoria
varchar(100)
required
Unique category name, such as Mobiliario or Electrónica.

Ubicacion

Lookup table for physical locations within the facility.
export const ubicaciones = pgTable("Ubicacion", {
  id_ubicacion: serial("id_ubicacion").primaryKey(),
  nombre_ubicacion: varchar("nombre_ubicacion", { length: 100 }).notNull(),
});
id_ubicacion
serial
required
Auto-incrementing primary key.
nombre_ubicacion
varchar(100)
required
Human-readable location name, such as Salón Principal or Almacén.

Articulo

Tracks fixed assets (non-consumable items). Supports soft delete via is_deleted.
export const articulos = pgTable("Articulo", {
  id_articulo: serial("id_articulo").primaryKey(),
  nombre: varchar("nombre", { length: 150 }).notNull(),
  cantidad: integer("cantidad").notNull().default(0),
  estado_fisico: varchar("estado_fisico", { length: 50 }),
  estado_disponibilidad: varchar("estado_disponibilidad", { length: 50 }),
  is_deleted: boolean("is_deleted").default(false),
  id_categoria: integer("id_categoria").notNull().references(() => categorias.id_categoria),
  id_ubicacion: integer("id_ubicacion").notNull().references(() => ubicaciones.id_ubicacion),
});
id_articulo
serial
required
Auto-incrementing primary key.
nombre
varchar(150)
required
Name of the asset.
cantidad
integer
required
Total quantity of this asset. Defaults to 0.
estado_fisico
varchar(50)
Physical condition of the asset. Expected values: Bueno, Regular, Dañado.
estado_disponibilidad
varchar(50)
Availability status. Expected values: Disponible, Prestado, No Disponible, Agotado/Baja.
is_deleted
boolean
Soft-delete flag. When true, the asset is hidden from the UI but retained in the database. Defaults to false.
id_categoria
integer
required
Foreign key referencing Categoria.id_categoria.
id_ubicacion
integer
required
Foreign key referencing Ubicacion.id_ubicacion.

Consumible

Tracks consumable supplies with minimum-stock thresholds.
export const consumibles = pgTable("Consumible", {
  id_consumible: serial("id_consumible").primaryKey(),
  nombre: varchar("nombre", { length: 150 }).notNull(),
  cantidad: integer("cantidad").default(0).notNull(),
  stock_minimo: integer("stock_minimo").default(0).notNull(),
  unidad_medida: varchar("unidad_medida", { length: 20 }).default("uds"),
  is_deleted: boolean("is_deleted").default(false),
  id_categoria: integer("id_categoria").notNull().references(() => categorias.id_categoria),
  id_ubicacion: integer("id_ubicacion").notNull().references(() => ubicaciones.id_ubicacion),
});
id_consumible
serial
required
Auto-incrementing primary key.
nombre
varchar(150)
required
Name of the consumable item.
cantidad
integer
required
Current stock quantity. Defaults to 0.
stock_minimo
integer
required
Minimum stock threshold. The dashboard surfaces a low-stock alert when cantidad falls at or below this value. Defaults to 0.
unidad_medida
varchar(20)
Unit of measure (for example, uds, kg, litros). Defaults to uds.
is_deleted
boolean
Soft-delete flag. Defaults to false.
id_categoria
integer
required
Foreign key referencing Categoria.id_categoria.
id_ubicacion
integer
required
Foreign key referencing Ubicacion.id_ubicacion.

Movimiento_Prestamos

Records equipment loans, linking a user who authorized the loan to a specific asset.
export const movimientosPrestamos = pgTable("Movimiento_Prestamos", {
  id_movimiento: serial("id_movimiento").primaryKey(),
  cantidad_prestada: integer("cantidad_prestada").notNull().default(1),
  autorizado_por: varchar("autorizado_por", { length: 100 }),
  destino: varchar("destino", { length: 150 }),
  fecha_inicio: timestamp("fecha_inicio").notNull(),
  fecha_retorno: timestamp("fecha_retorno"),
  responsable_externo: varchar("responsable_externo", { length: 100 }),
  is_deleted: boolean("is_deleted").default(false),
  id_usuario: integer("id_usuario").notNull().references(() => usuarios.id_usuario),
  id_articulo: integer("id_articulo").notNull().references(() => articulos.id_articulo),
});
id_movimiento
serial
required
Auto-incrementing primary key.
cantidad_prestada
integer
required
Number of units lent. Defaults to 1.
autorizado_por
varchar(100)
Name of the person who authorized the loan.
destino
varchar(150)
Destination or purpose of the loan.
fecha_inicio
timestamp
required
Date and time the loan started.
fecha_retorno
timestamp
Expected return date and time. Null if no return date has been set.
responsable_externo
varchar(100)
Name of the external person responsible for the loaned item.
is_deleted
boolean
Soft-delete flag. Defaults to false.
id_usuario
integer
required
Foreign key referencing Usuario.id_usuario — the internal user who registered the loan.
id_articulo
integer
required
Foreign key referencing Articulo.id_articulo — the asset being lent.

Auditoria_log

Append-only audit trail. Every significant action in the system writes a row here automatically.
export const auditoriaLogs = pgTable("Auditoria_log", {
  id_log: serial("id_log").primaryKey(),
  accion_realizada: varchar("accion_realizada", { length: 255 }).notNull(),
  fecha_hora: timestamp("fecha_hora").defaultNow().notNull(),
  direccion_ip: varchar("direccion_ip", { length: 45 }),
  id_usuario: integer("id_usuario").notNull().references(() => usuarios.id_usuario),
});
id_log
serial
required
Auto-incrementing primary key.
accion_realizada
varchar(255)
required
Human-readable description of the action performed (for example, Creó artículo: Silla plegable).
fecha_hora
timestamp
required
Timestamp of the action. Defaults to now().
direccion_ip
varchar(45)
IP address of the request. Supports both IPv4 and IPv6 addresses.
id_usuario
integer
required
Foreign key referencing Usuario.id_usuario — the user who performed the action.

password_resets

Temporary table for time-limited PIN codes used in the password reset flow. Rows are deleted after a successful reset or when a new PIN is requested for the same email.
export const passwordResets = pgTable("password_resets", {
  id_reset: serial("id_reset").primaryKey(),
  email: varchar("email", { length: 255 }).notNull(),
  pin: varchar("pin", { length: 6 }).notNull(),
  creado_en: timestamp("creado_en").defaultNow().notNull(),
  expira_en: timestamp("expira_en").notNull(),
});
id_reset
serial
required
Auto-incrementing primary key.
email
varchar(255)
required
Email address the reset PIN was sent to.
pin
varchar(6)
required
Six-digit numeric PIN delivered by email. Valid for 10 minutes.
creado_en
timestamp
required
Timestamp when the PIN was created. Defaults to now().
expira_en
timestamp
required
Timestamp after which the PIN is considered expired.

Entity relationships

The diagram below summarizes the foreign key relationships between tables.

Build docs developers (and LLMs) love