Skip to main content

Overview

Trazea uses a PostgreSQL database hosted on Supabase with 17 main tables organized into functional domains. The database implements Row Level Security (RLS) for multi-tenancy and role-based access control, along with triggers for automatic audit logging.

Database Domains

Table Schemas

User Management Domain

usuarios (Users)

Stores user accounts with approval workflow.
ColumnTypeDescription
id_usuariouuidPrimary key (references auth.users)
nombre_completotextFull name
emailtextEmail address
id_roluuidForeign key to roles
aprobadobooleanApproval status (default: false)
aprobado_poruuidAdmin who approved (nullable)
fecha_aprobaciontimestamptzApproval timestamp (nullable)
motivo_rechazotextRejection reason (nullable)
created_attimestamptzAccount creation timestamp
Relationships:
  • id_rolroles.id_rol
  • aprobado_porusuarios.id_usuario
  • One-to-many with usuarios_localizacion
RLS Policies:
  • Users can read their own profile
  • Admins can read all users
  • Only admins can approve/reject users
-- Example: User approval
UPDATE usuarios
SET 
  aprobado = true,
  aprobado_por = 'admin-uuid',
  fecha_aprobacion = NOW()
WHERE id_usuario = 'user-uuid';

roles (Roles)

Defines user roles with granular permissions.
ColumnTypeDescription
id_roluuidPrimary key
nombre_roltextRole name (admin, tecnico, superuser)
permisosjsonbPermissions object
created_attimestamptzCreation timestamp
Permission Structure:
{
  "inventory": {
    "view": true,
    "create": true,
    "edit": true,
    "delete": false
  },
  "requests": {
    "view": true,
    "create": true,
    "approve": false
  },
  "guarantees": {
    "view": true,
    "create": true,
    "resolve": false
  },
  "users": {
    "view": false,
    "manage": false
  }
}
Default Roles:
  • admin: Full permissions
  • superuser: Most permissions except user management
  • tecnico: View and create, limited edit/delete

usuarios_localizacion (User-Location Assignment)

Junction table for multi-location access.
ColumnTypeDescription
id_usuariouuidForeign key to usuarios
id_localizacionuuidForeign key to localizacion
created_attimestamptzAssignment timestamp
Primary Key: (id_usuario, id_localizacion) RLS: Users can only see data for their assigned locations.

admin_notifications (Admin Subscriptions)

Tracks admin notification preferences.
ColumnTypeDescription
id_adminuuidForeign key to usuarios
tipo_notificaciontextNotification type
activobooleanSubscription status

Location Domain

localizacion (Locations)

Physical locations (workshops, warehouses).
ColumnTypeDescription
id_localizacionuuidPrimary key
nombretextLocation name
direcciontextPhysical address
telefonotextContact phone
activobooleanActive status
created_attimestamptzCreation timestamp

Inventory Domain

repuestos (Spare Parts)

Centralized spare parts catalog.
ColumnTypeDescription
id_repuestouuidPrimary key
referenciatextUnique part reference/SKU
nombretextPart name
tipotextPart type/category
marcatextBrand/manufacturer
descripciontextDetailed description
descontinuadobooleanDiscontinued flag
fecha_estimadadateRestock ETA (nullable)
url_imagentextProduct image URL (nullable)
created_attimestamptzCreation timestamp
Indexes:
  • referencia (unique)
  • nombre (text search)
  • tipo
Example:
interface Repuesto {
  id_repuesto: string
  referencia: string        // "MTR-500W-48V"
  nombre: string            // "Motor 500W 48V"
  tipo: string              // "Motor"
  marca: string             // "Bafang"
  descontinuado: boolean    // false
  fecha_estimada: string    // "2026-04-15"
  url_imagen: string        // "https://..."
}

inventario (Inventory)

Stock levels per location.
ColumnTypeDescription
id_inventariouuidPrimary key
id_localizacionuuidForeign key to localizacion
id_repuestouuidForeign key to repuestos
stock_actualintegerCurrent quantity
cantidad_minimaintegerMinimum stock threshold
posiciontextPhysical location in warehouse
veces_contadointegerCount audit frequency
nuevo_hastatimestamptz”New” badge expiry (nullable)
created_attimestamptzCreation timestamp
updated_attimestamptzLast update timestamp
Unique Constraint: (id_localizacion, id_repuesto) Computed Fields (via view vista_repuestos_inventario):
  • estado_stock: “Critico”, “Bajo”, “Normal”, “Alto”
  • alerta_minimo: stock_actual <= cantidad_minima
  • es_nuevo: NOW() < nuevo_hasta
Triggers:
  • log_inventory_changes: Logs all stock modifications
  • check_low_stock: Creates notification when stock is low
-- Example: Low stock alert
CREATE OR REPLACE FUNCTION check_low_stock()
RETURNS TRIGGER AS $$
BEGIN
  IF NEW.stock_actual <= NEW.cantidad_minima THEN
    INSERT INTO notificaciones (titulo, mensaje, tipo, prioridad)
    VALUES (
      'Stock Bajo',
      'El repuesto ' || NEW.id_repuesto || ' está en nivel crítico',
      'alerta_stock',
      'alta'
    );
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

logs_inventario (Inventory Audit Logs)

Complete audit trail for inventory changes.
ColumnTypeDescription
id_loguuidPrimary key
id_inventariouuidForeign key to inventario
id_usuariouuidUser who made change
tipo_operaciontextOperation type
cantidad_anteriorintegerOld quantity
cantidad_nuevaintegerNew quantity
diferenciaintegerChange amount
motivotextReason for change
detallesjsonbAdditional context
fechatimestamptzChange timestamp
Operation Types:
  • AJUSTE_MANUAL - Manual adjustment
  • SOLICITUD_ENTRADA - Request received
  • SOLICITUD_SALIDA - Request dispatched
  • MOVIMIENTO_TECNICO - Technician movement
  • CONTEO_FISICO - Physical count adjustment
  • GARANTIA - Warranty claim

Request Domain

carrito_solicitudes (Request Cart)

Temporary cart for building requests.
ColumnTypeDescription
id_item_carritouuidPrimary key
id_usuariouuidForeign key to usuarios
id_localizacionuuidDestination location
id_repuestouuidForeign key to repuestos
cantidadintegerRequested quantity
created_attimestamptzAdded to cart timestamp
Lifecycle: Items deleted after request creation.

solicitudes (Requests)

Inter-location transfer requests.
ColumnTypeDescription
id_solicituduuidPrimary key
id_localizacion_origenuuidSource location
id_localizacion_destinouuidDestination location
id_usuario_solicitanteuuidUser who created request
estadotextCurrent state
fecha_creaciontimestamptzCreation timestamp
fecha_alistamientotimestamptzPicking completed (nullable)
fecha_despachotimestamptzDispatched (nullable)
fecha_recepciontimestamptzReceived (nullable)
guia_transportetextShipping tracking number (nullable)
observaciones_generalestextGeneral notes (nullable)
States (workflow):
  1. PENDIENTE - Created, awaiting preparation
  2. ALISTAMIENTO - Being picked
  3. DESPACHADO - Shipped
  4. RECIBIDO - Delivered
  5. CANCELADO - Cancelled

detalles_solicitud (Request Line Items)

Individual items in a request.
ColumnTypeDescription
id_detalleuuidPrimary key
id_solicituduuidForeign key to solicitudes
id_repuestouuidForeign key to repuestos
cantidad_solicitadaintegerOriginal request quantity
cantidad_despachadaintegerActually shipped (nullable)
cantidad_recibidaintegerActually received (nullable)
observacionestextItem-specific notes (nullable)
Discrepancy Tracking: Differences between requested, dispatched, and received quantities are logged.

trazabilidad_solicitudes (Request Traceability)

State transition history for requests.
ColumnTypeDescription
id_trazabilidaduuidPrimary key
id_solicituduuidForeign key to solicitudes
estado_anteriortextPrevious state
estado_nuevotextNew state
id_usuariouuidUser who made change
fecha_cambiotimestamptzChange timestamp
comentariotextTransition comment (nullable)
Trigger: Automatically logged on solicitudes.estado change.

Operations Domain

movimientos_tecnicos (Technician Movements)

Daily parts movements for technicians.
ColumnTypeDescription
id_movimientos_tecnicosuuidPrimary key
id_localizacionuuidForeign key to localizacion
id_repuestouuidForeign key to repuestos
id_usuario_responsableuuidUser logging movement
id_tecnico_asignadouuidTechnician assigned part
conceptotextMovement concept
tipotextMovement type (ingreso/salida/venta)
cantidadintegerQuantity moved
numero_ordenintegerWork order number (nullable)
descargadabooleanInventory deducted flag
fechatimestamptzMovement timestamp
created_attimestamptzRecord creation timestamp
updated_attimestamptzLast update timestamp
Movement Concepts:
  • salida - Parts taken by technician
  • ingreso - Parts returned
  • venta - Parts sold
  • garantia - Warranty replacement
  • prestamo - Temporary loan
  • cotizacion - Quote/estimate
  • devolucion - Return
Types:
  • ingreso - Increase stock
  • salida - Decrease stock
  • venta - Decrease stock (sold)

garantias (Warranties)

Warranty claims for defective parts.
ColumnTypeDescription
id_garantiauuidPrimary key
id_repuestouuidForeign key to repuestos
id_localizacionuuidWorkshop location
id_usuario_reportauuidUser who reported
id_tecnico_asociadouuidTechnician involved (nullable)
cantidadintegerNumber of defective units
motivo_fallatextFailure description
kilometrajeintegerVehicle mileage (nullable)
numero_ordentextWork order reference (nullable)
solicitantetextCustomer name (nullable)
estadotextWarranty status
url_evidencia_fototextPhoto evidence URL (nullable)
comentarios_resoluciontextResolution notes (nullable)
fecha_reportetimestamptzReport timestamp
fecha_resoluciontimestamptzResolution timestamp (nullable)
States:
  1. SIN_ENVIAR - Draft
  2. PENDIENTE - Submitted, awaiting review
  3. APROBADA - Approved by supplier
  4. RECHAZADA - Denied by supplier

registro_conteo (Count Sessions)

Physical inventory count sessions.
ColumnTypeDescription
id_conteouuidPrimary key
id_localizacionuuidForeign key to localizacion
id_usuariouuidUser performing count
fecha_conteotimestamptzCount date
total_itemsintegerItems counted
total_diferenciasintegerDiscrepancies found
items_con_pqintegerItems with “pequeños quedan”
observacionestextGeneral notes (nullable)
created_attimestamptzSession creation timestamp
Summary Calculation: Triggered on detalles_conteo insert.

detalles_conteo (Count Line Items)

Individual item counts within a session.
ColumnTypeDescription
id_detalle_conteouuidPrimary key
id_conteouuidForeign key to registro_conteo
id_repuestouuidForeign key to repuestos
cantidad_sistemaintegerSystem quantity
cantidad_csaintegerCounted quantity at location
cantidad_pqinteger”Pequeños quedan” (loose parts)
diferenciaintegerCalculated: (sistema + pq) - csa
observacionestextItem notes (nullable)
Trigger: Updates inventario.stock_actual if difference found.

Scooter Domain

scooter_types (Scooter Models)

Scooter model catalog.
ColumnTypeDescription
id_tipouuidPrimary key
nombretextModel name
potenciatextPower specifications
descripciontextModel description (nullable)

order_follow (Order Tracking)

Scooter order status tracking.
ColumnTypeDescription
id_ordenuuidPrimary key
numero_ordentextOrder number
id_tipo_scooteruuidForeign key to scooter_types
nivelintegerProgress level (1-3)
estadotextOrder status
telefono_contactotextCustomer phone (nullable)
email_contactotextCustomer email (nullable)
link_ordentextExternal order link (nullable)
fecha_ordentimestamptzOrder date
fecha_entrega_estimadadateETA (nullable)
Levels:
  1. Ordered
  2. In transit
  3. Delivered

Database Views

vista_repuestos_inventario

Combined view of spare parts with inventory details.
CREATE VIEW vista_repuestos_inventario AS
SELECT 
  r.*,
  i.stock_actual,
  i.cantidad_minima,
  i.posicion,
  i.veces_contado,
  i.nuevo_hasta,
  l.nombre AS nombre_localizacion,
  CASE 
    WHEN i.stock_actual = 0 THEN 'Sin Stock'
    WHEN i.stock_actual <= i.cantidad_minima THEN 'Bajo'
    WHEN i.stock_actual <= i.cantidad_minima * 2 THEN 'Normal'
    ELSE 'Alto'
  END AS estado_stock,
  i.stock_actual <= i.cantidad_minima AS alerta_minimo,
  NOW() < i.nuevo_hasta AS es_nuevo
FROM repuestos r
LEFT JOIN inventario i ON r.id_repuesto = i.id_repuesto
LEFT JOIN localizacion l ON i.id_localizacion = l.id_localizacion;

Row Level Security (RLS)

Policies Overview

All tables have RLS enabled with policies based on:
  • User authentication state
  • User role
  • Location assignment

Example Policies

Inventory Access

-- Users can only see inventory for their assigned locations
CREATE POLICY "Users can view inventory for assigned locations"
ON inventario FOR SELECT
USING (
  id_localizacion IN (
    SELECT id_localizacion 
    FROM usuarios_localizacion 
    WHERE id_usuario = auth.uid()
  )
);

-- Admins can see all inventory
CREATE POLICY "Admins can view all inventory"
ON inventario FOR SELECT
USING (
  EXISTS (
    SELECT 1 FROM usuarios u
    JOIN roles r ON u.id_rol = r.id_rol
    WHERE u.id_usuario = auth.uid()
    AND r.nombre_rol = 'admin'
  )
);

Request Access

-- Users can view requests for their locations
CREATE POLICY "Users can view requests for assigned locations"
ON solicitudes FOR SELECT
USING (
  id_localizacion_origen IN (
    SELECT id_localizacion FROM usuarios_localizacion 
    WHERE id_usuario = auth.uid()
  )
  OR id_localizacion_destino IN (
    SELECT id_localizacion FROM usuarios_localizacion 
    WHERE id_usuario = auth.uid()
  )
);

Database Diagram

Indexing Strategy

High-Traffic Queries

-- Inventory lookup by location
CREATE INDEX idx_inventario_localizacion 
ON inventario(id_localizacion);

-- Spare parts search
CREATE INDEX idx_repuestos_referencia 
ON repuestos(referencia);

CREATE INDEX idx_repuestos_nombre_gin 
ON repuestos USING gin(to_tsvector('spanish', nombre));

-- Request filtering
CREATE INDEX idx_solicitudes_estado 
ON solicitudes(estado);

CREATE INDEX idx_solicitudes_fecha 
ON solicitudes(fecha_creacion DESC);

-- Movement queries
CREATE INDEX idx_movimientos_tecnico 
ON movimientos_tecnicos(id_tecnico_asignado);

CREATE INDEX idx_movimientos_fecha 
ON movimientos_tecnicos(fecha DESC);

Triggers

Automatic Logging

-- Log inventory changes
CREATE TRIGGER log_inventory_changes
AFTER UPDATE OF stock_actual ON inventario
FOR EACH ROW
EXECUTE FUNCTION log_inventory_change();

-- Track request state transitions
CREATE TRIGGER track_request_state
AFTER UPDATE OF estado ON solicitudes
FOR EACH ROW
EXECUTE FUNCTION log_request_transition();

-- Update count summary
CREATE TRIGGER update_count_summary
AFTER INSERT ON detalles_conteo
FOR EACH ROW
EXECUTE FUNCTION calculate_count_summary();

Migration Strategy

Supabase uses versioned migrations:
supabase/
├── migrations/
   ├── 20260101000000_initial_schema.sql
   ├── 20260115000000_add_warranties.sql
   ├── 20260201000000_add_count_module.sql
   └── ...

Backup & Recovery

Supabase provides:
  • Daily automated backups (retained 7 days)
  • Point-in-time recovery (Pro plan)
  • Manual backups via dashboard
  • Export to SQL for local backups

Performance Considerations

Query Optimization

  1. Use views for complex joins
  2. Index foreign keys for fast lookups
  3. Limit result sets with pagination
  4. Use computed columns instead of runtime calculations
  5. Cache frequently accessed data with TanStack Query

Connection Pooling

Supabase uses PgBouncer for connection pooling:
  • Transaction mode for web connections
  • Session mode for migrations

Architecture Overview

System architecture overview

Security

Authentication and RLS policies

API Reference

Database API usage

Data Migration

Import and export data

Build docs developers (and LLMs) love