Inventory System is a multi-tenant SaaS application where every company operates in complete isolation. The root tenant entity is theDocumentation 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.
Empresa model. Every other model in the schema holds an empresaId foreign key, and every Prisma query that reads or writes business data filters on that key — making cross-company data leaks structurally impossible in normal operation.
The Empresa model
Empresa is the anchor of the entire data graph. It has a minimal footprint by design: only an auto-generated id, a human-readable nombre, and a creation timestamp. All complexity lives in the models that reference it.
Tenant isolation
Isolation is enforced in every query by extractingempresaId from the authenticated session and passing it as a where clause condition. The session helper validarAccesoEmpresa() in src/lib/permisos.ts provides the verified empresaId to every route handler:
empresaId is always sourced from the server-side session and never from a request body or query parameter, a user cannot escalate access to another company’s data by manipulating the request.
Tenant provisioning
Self-registration (new tenant)
When a user signs up without an invitation, a brand-newEmpresa is provisioned for them automatically. This happens in two places, both following the same logic:
- Webhook (
POST /api/webhooks/neon,user.createdevent) — the primary path; triggered by Neon Auth as soon as the user completes registration. - Fallback in
obtenerSesion()— runs on the first authenticated page request if the webhook has not yet arrived.
rol: 'ADMIN' and estado: 'ACTIVO' so they can immediately configure their company without waiting for an approval step.
Invitation-based joining (existing tenant)
Users who receive an invitation viaPOST /api/invitaciones join an existing Empresa rather than creating a new one. When POST /api/invitaciones/aceptar is called with a valid token, the new Usuario record is created with the empresaId stored in the Invitacion row:
ACTIVO immediately — no admin approval is needed because the invitation itself is the explicit approval act.
Company configuration
EachEmpresa has a one-to-one ConfiguracionEmpresa record that stores localisation and branding settings:
| Field | Default | Description |
|---|---|---|
moneda | "COP" | ISO 4217 currency code used in prices and reports |
simboloMoneda | "$" | Symbol rendered in the UI next to monetary values |
impuestos | 0.19 | Default tax rate (19 %) applied to sales |
logoUrl | null | URL of the company logo, uploaded by the admin |
direccion | null | Physical address, printed on invoices and quotes |
telefono | null | Contact phone number |
email | null | Contact email shown on documents |
nombrePersonalizado | null | Display name override (defaults to Empresa.nombre) |
Configuration endpoints
Company member management
Onboarding flow
Newly provisioned tenants are directed to an onboarding wizard at/onboarding. Once the wizard is completed, the frontend calls the onboarding endpoint to persist the flag:
onboardingCompletado = true on the Usuario record. Application logic can read this flag to decide whether to show the onboarding wizard on subsequent visits.
Data model ownership
Every model listed below holds a requiredempresaId column that references Empresa. Deleting an Empresa cascades to all of them (Prisma onDelete: Cascade).
| Model | What it represents |
|---|---|
Usuario | Company members and their roles/permissions |
Categoria | Product categories unique to the company |
Producto | Product catalogue with stock levels and pricing |
Movimiento | Individual stock movements (entries and exits) |
Venta | Sales transactions processed at the terminal |
Cotizacion | Customer quotes that can be converted to sales |
OrdenCompra | Purchase orders sent to suppliers |
Proveedor | Supplier directory |
Cliente | Customer directory |
Auditoria | Immutable audit log of all actions |
Notificacion | In-app notifications (low stock, events, etc.) |
HistorialPrecio | Price change log per product |
SUPER_ADMIN cross-company access
Users with theSUPER_ADMIN role are not bound to a single empresaId. They can browse and manage all tenants through the platform admin panel at /admin/empresas.
The following API endpoints are reserved for SUPER_ADMIN:
SUPER_ADMIN access is granted automatically to emails listed in the ADMIN_EMAILS environment variable. No database migration or manual role update is needed — the promotion happens on login inside obtenerSesion(). See Authentication for details.