DDL files created: - 01-transport-schema-ddl.sql: OT, embarques, viajes, paradas, POD, incidencias - 02-fleet-schema-ddl.sql: unidades, remolques, operadores, documentos, asignaciones - 03-tracking-schema-ddl.sql: posiciones GPS, eventos, geocercas, alertas, ETA - 04-fuel-schema-ddl.sql: cargas combustible, peajes, gastos, anticipos, rendimiento - 05-maintenance-schema-ddl.sql: planes, programacion, ordenes trabajo, checklist - 06-carriers-schema-ddl.sql: carriers, documentos, unidades, operadores, scorecard - 07-billing-transport-ddl.sql: lanes, tarifas, recargos, facturas, fuel surcharge - 08-compliance-schema-ddl.sql: carta porte CFDI 3.1, HOS NOM-087, inspecciones Features: - All tables with tenant_id and RLS policies - ENUMs for all status and type fields - Proper indexes for common queries - PostGIS for geospatial data - Partitioned tables for high-volume GPS data - CFDI Carta Porte 3.1 compliant structure Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
370 lines
11 KiB
SQL
370 lines
11 KiB
SQL
-- =============================================================================
|
|
-- ERP TRANSPORTISTAS - Schema Tracking DDL
|
|
-- =============================================================================
|
|
-- Archivo: 03-tracking-schema-ddl.sql
|
|
-- Version: 1.0.0
|
|
-- Fecha: 2026-01-25
|
|
-- Descripcion: Eventos GPS, geocercas, alertas, posiciones
|
|
-- =============================================================================
|
|
|
|
-- =============================================================================
|
|
-- TIPOS ENUMERADOS ADICIONALES
|
|
-- =============================================================================
|
|
|
|
CREATE TYPE tracking.tipo_geocerca AS ENUM (
|
|
'CLIENTE',
|
|
'PROVEEDOR',
|
|
'PATIO',
|
|
'ZONA_RIESGO',
|
|
'CASETA',
|
|
'GASOLINERA',
|
|
'PUNTO_CONTROL',
|
|
'OTRO'
|
|
);
|
|
|
|
CREATE TYPE tracking.severidad_alerta AS ENUM (
|
|
'INFO',
|
|
'WARNING',
|
|
'CRITICAL'
|
|
);
|
|
|
|
CREATE TYPE tracking.tipo_alerta AS ENUM (
|
|
'ENTRADA_GEOCERCA',
|
|
'SALIDA_GEOCERCA',
|
|
'EXCESO_VELOCIDAD',
|
|
'PARADA_PROLONGADA',
|
|
'DESVIO_RUTA',
|
|
'TEMPERATURA_FUERA_RANGO',
|
|
'BATERIA_BAJA',
|
|
'SIN_SENAL',
|
|
'BOTON_PANICO',
|
|
'APERTURA_PUERTA',
|
|
'CONSUMO_ANOMALO'
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: posiciones_gps
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.posiciones_gps (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Unidad
|
|
unidad_id UUID NOT NULL,
|
|
|
|
-- Posición
|
|
latitud DECIMAL(10, 7) NOT NULL,
|
|
longitud DECIMAL(10, 7) NOT NULL,
|
|
altitud DECIMAL(8, 2),
|
|
|
|
-- Velocidad y dirección
|
|
velocidad_kmh DECIMAL(6, 2),
|
|
rumbo INT, -- 0-360 grados
|
|
|
|
-- Timestamp
|
|
timestamp_gps TIMESTAMPTZ NOT NULL,
|
|
timestamp_servidor TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Datos adicionales GPS
|
|
hdop DECIMAL(4, 2), -- Dilución de precisión horizontal
|
|
satelites INT,
|
|
|
|
-- Estado del vehículo
|
|
motor_encendido BOOLEAN,
|
|
odometro INT,
|
|
|
|
-- Proveedor
|
|
proveedor_gps VARCHAR(50),
|
|
imei VARCHAR(50),
|
|
|
|
-- Viaje asociado (si está en viaje)
|
|
viaje_id UUID,
|
|
|
|
-- Partición por fecha
|
|
fecha_particion DATE NOT NULL DEFAULT CURRENT_DATE
|
|
) PARTITION BY RANGE (fecha_particion);
|
|
|
|
-- Crear particiones mensuales (ejemplo para 2026)
|
|
CREATE TABLE tracking.posiciones_gps_2026_01 PARTITION OF tracking.posiciones_gps
|
|
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
|
|
|
|
CREATE TABLE tracking.posiciones_gps_2026_02 PARTITION OF tracking.posiciones_gps
|
|
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
|
|
|
|
CREATE TABLE tracking.posiciones_gps_2026_03 PARTITION OF tracking.posiciones_gps
|
|
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
|
|
|
|
CREATE INDEX idx_posicion_unidad_fecha ON tracking.posiciones_gps(unidad_id, timestamp_gps);
|
|
CREATE INDEX idx_posicion_viaje ON tracking.posiciones_gps(viaje_id) WHERE viaje_id IS NOT NULL;
|
|
CREATE INDEX idx_posicion_geo ON tracking.posiciones_gps USING GIST (
|
|
ST_SetSRID(ST_MakePoint(longitud, latitud), 4326)
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: eventos_tracking
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.eventos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Viaje
|
|
viaje_id UUID NOT NULL,
|
|
|
|
-- Tipo y fuente
|
|
tipo_evento tracking.tipo_evento NOT NULL,
|
|
fuente tracking.fuente_evento NOT NULL,
|
|
|
|
-- Ubicación
|
|
latitud DECIMAL(10, 7),
|
|
longitud DECIMAL(10, 7),
|
|
direccion TEXT,
|
|
|
|
-- Timestamp
|
|
timestamp_evento TIMESTAMPTZ NOT NULL,
|
|
timestamp_registro TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Datos específicos del evento
|
|
datos JSONB,
|
|
|
|
-- Parada asociada (si aplica)
|
|
parada_id UUID,
|
|
|
|
-- Usuario/Operador que generó
|
|
generado_por_id UUID,
|
|
generado_por_tipo VARCHAR(20), -- 'OPERADOR', 'SISTEMA', 'USUARIO'
|
|
|
|
-- Evidencias
|
|
evidencias JSONB,
|
|
|
|
-- Observaciones
|
|
observaciones TEXT
|
|
);
|
|
|
|
CREATE INDEX idx_evento_viaje ON tracking.eventos(viaje_id);
|
|
CREATE INDEX idx_evento_tipo ON tracking.eventos(tenant_id, tipo_evento);
|
|
CREATE INDEX idx_evento_fecha ON tracking.eventos(timestamp_evento);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: geocercas
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.geocercas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Identificación
|
|
codigo VARCHAR(50) NOT NULL,
|
|
nombre VARCHAR(200) NOT NULL,
|
|
tipo tracking.tipo_geocerca NOT NULL,
|
|
|
|
-- Geometría (polígono o círculo)
|
|
es_circular BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Para geocerca circular
|
|
centro_latitud DECIMAL(10, 7),
|
|
centro_longitud DECIMAL(10, 7),
|
|
radio_metros DECIMAL(10, 2),
|
|
|
|
-- Para geocerca poligonal (GeoJSON)
|
|
poligono GEOMETRY(POLYGON, 4326),
|
|
|
|
-- Asociación
|
|
cliente_id UUID, -- Si tipo = CLIENTE
|
|
direccion TEXT,
|
|
|
|
-- Alertas
|
|
alerta_entrada BOOLEAN DEFAULT TRUE,
|
|
alerta_salida BOOLEAN DEFAULT TRUE,
|
|
tiempo_permanencia_minutos INT, -- Alerta si permanece más de X minutos
|
|
|
|
-- Configuración
|
|
color VARCHAR(7) DEFAULT '#FF0000',
|
|
activa BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_geocerca_tenant ON tracking.geocercas(tenant_id);
|
|
CREATE INDEX idx_geocerca_tipo ON tracking.geocercas(tenant_id, tipo);
|
|
CREATE INDEX idx_geocerca_geo ON tracking.geocercas USING GIST (poligono);
|
|
CREATE UNIQUE INDEX idx_geocerca_codigo ON tracking.geocercas(tenant_id, codigo);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: alertas
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.alertas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Tipo y severidad
|
|
tipo tracking.tipo_alerta NOT NULL,
|
|
severidad tracking.severidad_alerta NOT NULL,
|
|
|
|
-- Referencias
|
|
unidad_id UUID,
|
|
viaje_id UUID,
|
|
geocerca_id UUID REFERENCES tracking.geocercas(id),
|
|
operador_id UUID,
|
|
|
|
-- Ubicación
|
|
latitud DECIMAL(10, 7),
|
|
longitud DECIMAL(10, 7),
|
|
|
|
-- Descripción
|
|
titulo VARCHAR(200) NOT NULL,
|
|
mensaje TEXT NOT NULL,
|
|
|
|
-- Datos específicos
|
|
datos JSONB,
|
|
|
|
-- Timestamp
|
|
timestamp_alerta TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Estado
|
|
leida BOOLEAN DEFAULT FALSE,
|
|
leida_por UUID,
|
|
leida_fecha TIMESTAMPTZ,
|
|
|
|
atendida BOOLEAN DEFAULT FALSE,
|
|
atendida_por UUID,
|
|
atendida_fecha TIMESTAMPTZ,
|
|
resolucion TEXT,
|
|
|
|
-- Notificaciones enviadas
|
|
notificaciones_enviadas JSONB
|
|
);
|
|
|
|
CREATE INDEX idx_alerta_tenant ON tracking.alertas(tenant_id);
|
|
CREATE INDEX idx_alerta_unidad ON tracking.alertas(unidad_id);
|
|
CREATE INDEX idx_alerta_viaje ON tracking.alertas(viaje_id);
|
|
CREATE INDEX idx_alerta_no_atendida ON tracking.alertas(tenant_id, atendida) WHERE atendida = FALSE;
|
|
CREATE INDEX idx_alerta_fecha ON tracking.alertas(timestamp_alerta);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: reglas_alerta
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.reglas_alerta (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Identificación
|
|
nombre VARCHAR(200) NOT NULL,
|
|
descripcion TEXT,
|
|
|
|
-- Tipo de alerta que genera
|
|
tipo_alerta tracking.tipo_alerta NOT NULL,
|
|
severidad tracking.severidad_alerta DEFAULT 'WARNING',
|
|
|
|
-- Condiciones (JSON con configuración)
|
|
condiciones JSONB NOT NULL,
|
|
-- Ejemplo: { "velocidad_max": 100, "tiempo_parada_max_min": 30, "temp_min": -18, "temp_max": -15 }
|
|
|
|
-- Aplicabilidad
|
|
aplica_todas_unidades BOOLEAN DEFAULT TRUE,
|
|
unidades_ids UUID[],
|
|
aplica_todos_viajes BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Notificaciones
|
|
notificar_email BOOLEAN DEFAULT TRUE,
|
|
notificar_sms BOOLEAN DEFAULT FALSE,
|
|
notificar_push BOOLEAN DEFAULT TRUE,
|
|
destinatarios JSONB, -- Array de {tipo, id, email, telefono}
|
|
|
|
-- Estado
|
|
activa BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_regla_tenant ON tracking.reglas_alerta(tenant_id);
|
|
CREATE INDEX idx_regla_tipo ON tracking.reglas_alerta(tipo_alerta) WHERE activa = TRUE;
|
|
|
|
-- =============================================================================
|
|
-- TABLA: eta_calculado
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE tracking.eta_calculado (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
viaje_id UUID NOT NULL,
|
|
parada_id UUID,
|
|
|
|
-- ETA
|
|
eta_original TIMESTAMPTZ,
|
|
eta_actual TIMESTAMPTZ NOT NULL,
|
|
eta_anterior TIMESTAMPTZ,
|
|
|
|
-- Cálculo
|
|
distancia_restante_km DECIMAL(10, 2),
|
|
tiempo_restante_minutos INT,
|
|
|
|
-- Factores
|
|
factor_trafico DECIMAL(3, 2) DEFAULT 1.00,
|
|
factor_clima DECIMAL(3, 2) DEFAULT 1.00,
|
|
|
|
-- Estado
|
|
estado VARCHAR(20), -- 'EN_TIEMPO', 'ADELANTADO', 'RETRASADO'
|
|
minutos_diferencia INT,
|
|
|
|
-- Timestamp
|
|
calculado_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_eta_viaje ON tracking.eta_calculado(viaje_id);
|
|
CREATE INDEX idx_eta_fecha ON tracking.eta_calculado(calculado_at);
|
|
|
|
-- =============================================================================
|
|
-- RLS POLICIES
|
|
-- =============================================================================
|
|
|
|
ALTER TABLE tracking.posiciones_gps ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE tracking.eventos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE tracking.geocercas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE tracking.alertas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE tracking.reglas_alerta ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE tracking.eta_calculado ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation_posiciones ON tracking.posiciones_gps
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_eventos ON tracking.eventos
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_geocercas ON tracking.geocercas
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_alertas ON tracking.alertas
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_reglas ON tracking.reglas_alerta
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_eta ON tracking.eta_calculado
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
-- =============================================================================
|
|
-- COMENTARIOS
|
|
-- =============================================================================
|
|
|
|
COMMENT ON TABLE tracking.posiciones_gps IS 'Posiciones GPS de unidades (particionada por fecha)';
|
|
COMMENT ON TABLE tracking.eventos IS 'Eventos de tracking durante viajes';
|
|
COMMENT ON TABLE tracking.geocercas IS 'Geocercas/zonas de interés';
|
|
COMMENT ON TABLE tracking.alertas IS 'Alertas generadas por el sistema de tracking';
|
|
COMMENT ON TABLE tracking.reglas_alerta IS 'Reglas de configuración para generar alertas';
|
|
COMMENT ON TABLE tracking.eta_calculado IS 'Historial de cálculos de ETA';
|
|
|
|
-- =============================================================================
|
|
-- FIN DDL TRACKING
|
|
-- =============================================================================
|