- 03a-gps-devices-ddl.sql: GPS device tracking schema - 09-dispatch-schema-ddl.sql: Dispatch management schema - 10-offline-schema-ddl.sql: Offline operation schema Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
428 lines
14 KiB
SQL
428 lines
14 KiB
SQL
-- =============================================================================
|
|
-- ERP TRANSPORTISTAS - Schema Despacho DDL
|
|
-- =============================================================================
|
|
-- Archivo: 09-dispatch-schema-ddl.sql
|
|
-- Version: 1.0.0
|
|
-- Fecha: 2026-01-28
|
|
-- Descripcion: Tablas para modulo de despacho (dispatch center)
|
|
-- Basado en: erp-mecanicas-diesel MMD-011 Dispatch Center
|
|
-- =============================================================================
|
|
|
|
-- =============================================================================
|
|
-- SCHEMA DESPACHO
|
|
-- =============================================================================
|
|
|
|
CREATE SCHEMA IF NOT EXISTS despacho;
|
|
|
|
-- =============================================================================
|
|
-- TIPOS ENUMERADOS
|
|
-- =============================================================================
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE despacho.estado_unidad AS ENUM (
|
|
'available',
|
|
'assigned',
|
|
'en_route',
|
|
'on_site',
|
|
'returning',
|
|
'offline',
|
|
'maintenance'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE despacho.capacidad_unidad AS ENUM (
|
|
'light',
|
|
'medium',
|
|
'heavy'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE despacho.accion_despacho AS ENUM (
|
|
'created',
|
|
'assigned',
|
|
'reassigned',
|
|
'rejected',
|
|
'escalated',
|
|
'cancelled',
|
|
'acknowledged',
|
|
'completed'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE despacho.canal_notificacion AS ENUM (
|
|
'email',
|
|
'sms',
|
|
'whatsapp',
|
|
'push',
|
|
'call'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- =============================================================================
|
|
-- TABLA: tableros_despacho
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS despacho.tableros_despacho (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
nombre VARCHAR(100) NOT NULL,
|
|
descripcion TEXT,
|
|
|
|
-- Map defaults
|
|
default_zoom INTEGER DEFAULT 12,
|
|
centro_lat DECIMAL(10, 7) DEFAULT 19.4326,
|
|
centro_lng DECIMAL(10, 7) DEFAULT -99.1332,
|
|
|
|
-- Behavior
|
|
intervalo_refresco_segundos INTEGER DEFAULT 30,
|
|
mostrar_unidades_offline BOOLEAN DEFAULT TRUE,
|
|
auto_asignar_habilitado BOOLEAN DEFAULT FALSE,
|
|
max_sugerencias INTEGER DEFAULT 5,
|
|
|
|
-- Filters
|
|
filtros_default JSONB DEFAULT '{}',
|
|
|
|
activo BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_tableros_despacho_tenant ON despacho.tableros_despacho(tenant_id);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: estado_unidades
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS despacho.estado_unidades (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Unit reference (FK a fleet.unidades)
|
|
unidad_id UUID NOT NULL,
|
|
codigo_unidad VARCHAR(50),
|
|
nombre_unidad VARCHAR(100),
|
|
|
|
-- Status
|
|
estado despacho.estado_unidad DEFAULT 'offline',
|
|
|
|
-- Current assignment (FK a transport.viajes)
|
|
viaje_actual_id UUID,
|
|
operador_ids UUID[] DEFAULT '{}',
|
|
|
|
-- Location (cached from GPS)
|
|
ultima_posicion_id UUID,
|
|
ultima_posicion_lat DECIMAL(10, 7),
|
|
ultima_posicion_lng DECIMAL(10, 7),
|
|
ultima_actualizacion_ubicacion TIMESTAMPTZ,
|
|
|
|
-- Timing
|
|
ultimo_cambio_estado TIMESTAMPTZ DEFAULT NOW(),
|
|
disponible_estimado_en TIMESTAMPTZ,
|
|
|
|
-- Capacity and capabilities
|
|
capacidad_unidad despacho.capacidad_unidad DEFAULT 'light',
|
|
puede_remolcar BOOLEAN DEFAULT FALSE,
|
|
peso_max_remolque_kg INTEGER,
|
|
|
|
-- Transport-specific
|
|
es_refrigerada BOOLEAN DEFAULT FALSE,
|
|
capacidad_peso_kg DECIMAL(10, 2),
|
|
|
|
notas TEXT,
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT uq_estado_unidad UNIQUE (tenant_id, unidad_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_estado_unidades_tenant ON despacho.estado_unidades(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_estado_unidades_estado ON despacho.estado_unidades(tenant_id, estado);
|
|
CREATE INDEX IF NOT EXISTS idx_estado_unidades_viaje ON despacho.estado_unidades(viaje_actual_id) WHERE viaje_actual_id IS NOT NULL;
|
|
|
|
-- =============================================================================
|
|
-- TABLA: reglas_despacho
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS despacho.reglas_despacho (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
nombre VARCHAR(100) NOT NULL,
|
|
descripcion TEXT,
|
|
|
|
prioridad INTEGER DEFAULT 0,
|
|
|
|
-- Applicability
|
|
tipo_viaje VARCHAR(50),
|
|
categoria_viaje VARCHAR(50),
|
|
|
|
-- Conditions (JSONB with transport-specific rules)
|
|
condiciones JSONB NOT NULL DEFAULT '{}',
|
|
|
|
-- Action
|
|
auto_asignar BOOLEAN DEFAULT FALSE,
|
|
peso_asignacion INTEGER DEFAULT 100,
|
|
|
|
activo BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reglas_despacho_tenant ON despacho.reglas_despacho(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_reglas_despacho_prioridad ON despacho.reglas_despacho(tenant_id, prioridad DESC);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: reglas_escalamiento
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS despacho.reglas_escalamiento (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
nombre VARCHAR(100) NOT NULL,
|
|
descripcion TEXT,
|
|
|
|
-- Trigger conditions
|
|
disparar_despues_minutos INTEGER NOT NULL,
|
|
disparar_estado VARCHAR(50),
|
|
disparar_prioridad VARCHAR(20),
|
|
|
|
-- Escalation target
|
|
escalar_a_rol VARCHAR(50) NOT NULL,
|
|
escalar_a_usuarios UUID[],
|
|
|
|
-- Notification (default whatsapp for transport)
|
|
canal_notificacion despacho.canal_notificacion DEFAULT 'whatsapp',
|
|
plantilla_notificacion TEXT,
|
|
datos_notificacion JSONB DEFAULT '{}',
|
|
|
|
-- Repeat
|
|
intervalo_repeticion_minutos INTEGER,
|
|
max_escalamientos INTEGER DEFAULT 3,
|
|
|
|
activo BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reglas_escalamiento_tenant ON despacho.reglas_escalamiento(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_reglas_escalamiento_trigger ON despacho.reglas_escalamiento(tenant_id, disparar_despues_minutos);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: log_despacho
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS despacho.log_despacho (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Viaje reference (FK a transport.viajes)
|
|
viaje_id UUID NOT NULL,
|
|
|
|
-- Action
|
|
accion despacho.accion_despacho NOT NULL,
|
|
|
|
-- Unit/Operador changes
|
|
desde_unidad_id UUID,
|
|
hacia_unidad_id UUID,
|
|
desde_operador_id UUID,
|
|
hacia_operador_id UUID,
|
|
|
|
-- Context
|
|
razon TEXT,
|
|
automatizado BOOLEAN DEFAULT FALSE,
|
|
regla_id UUID,
|
|
escalamiento_id UUID,
|
|
|
|
-- Response times
|
|
tiempo_respuesta_segundos INTEGER,
|
|
|
|
-- Actor
|
|
ejecutado_por UUID,
|
|
ejecutado_en TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Extra data
|
|
metadata JSONB DEFAULT '{}'
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_log_despacho_tenant ON despacho.log_despacho(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_log_despacho_viaje ON despacho.log_despacho(tenant_id, viaje_id);
|
|
CREATE INDEX IF NOT EXISTS idx_log_despacho_fecha ON despacho.log_despacho(tenant_id, ejecutado_en DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_log_despacho_accion ON despacho.log_despacho(tenant_id, accion);
|
|
|
|
-- =============================================================================
|
|
-- TABLAS ADICIONALES EN FLEET (certificaciones y turnos)
|
|
-- =============================================================================
|
|
|
|
-- TABLA: certificaciones_operador
|
|
CREATE TABLE IF NOT EXISTS fleet.certificaciones_operador (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Operador reference (FK a fleet.operadores)
|
|
operador_id UUID NOT NULL,
|
|
|
|
-- Certification definition
|
|
codigo_certificacion VARCHAR(50) NOT NULL,
|
|
nombre_certificacion VARCHAR(100) NOT NULL,
|
|
descripcion TEXT,
|
|
|
|
-- Level
|
|
nivel VARCHAR(20) DEFAULT 'basico',
|
|
|
|
-- Certification details
|
|
numero_certificado VARCHAR(100),
|
|
fecha_certificacion DATE,
|
|
vigencia_hasta DATE,
|
|
documento_url TEXT,
|
|
|
|
-- Status
|
|
activa BOOLEAN DEFAULT TRUE,
|
|
verificado_por UUID,
|
|
verificado_en TIMESTAMPTZ,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT uq_operador_certificacion UNIQUE(tenant_id, operador_id, codigo_certificacion)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_certificaciones_operador ON fleet.certificaciones_operador(operador_id);
|
|
CREATE INDEX IF NOT EXISTS idx_certificaciones_tenant ON fleet.certificaciones_operador(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_certificaciones_vencimiento ON fleet.certificaciones_operador(vigencia_hasta) WHERE activa = TRUE;
|
|
|
|
-- TABLA: turnos_operador
|
|
CREATE TABLE IF NOT EXISTS fleet.turnos_operador (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Operador reference (FK a fleet.operadores)
|
|
operador_id UUID NOT NULL,
|
|
|
|
-- Schedule
|
|
fecha_turno DATE NOT NULL,
|
|
tipo_turno VARCHAR(20) NOT NULL,
|
|
hora_inicio TIME NOT NULL,
|
|
hora_fin TIME NOT NULL,
|
|
|
|
-- On-call specifics
|
|
en_guardia BOOLEAN DEFAULT FALSE,
|
|
prioridad_guardia INTEGER DEFAULT 0,
|
|
|
|
-- Assignment
|
|
unidad_asignada_id UUID,
|
|
|
|
-- Status
|
|
hora_inicio_real TIMESTAMPTZ,
|
|
hora_fin_real TIMESTAMPTZ,
|
|
ausente BOOLEAN DEFAULT FALSE,
|
|
motivo_ausencia TEXT,
|
|
|
|
notas TEXT,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_turnos_operador ON fleet.turnos_operador(operador_id, fecha_turno);
|
|
CREATE INDEX IF NOT EXISTS idx_turnos_tenant ON fleet.turnos_operador(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_turnos_fecha ON fleet.turnos_operador(tenant_id, fecha_turno);
|
|
|
|
-- =============================================================================
|
|
-- RLS POLICIES
|
|
-- =============================================================================
|
|
|
|
ALTER TABLE despacho.tableros_despacho ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE despacho.estado_unidades ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE despacho.reglas_despacho ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE despacho.reglas_escalamiento ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE despacho.log_despacho ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE fleet.certificaciones_operador ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE fleet.turnos_operador ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_tableros ON despacho.tableros_despacho
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_estado ON despacho.estado_unidades
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_reglas ON despacho.reglas_despacho
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_escalamiento ON despacho.reglas_escalamiento
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_log ON despacho.log_despacho
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_certificaciones ON fleet.certificaciones_operador
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE POLICY tenant_isolation_turnos ON fleet.turnos_operador
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
EXCEPTION WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- =============================================================================
|
|
-- COMENTARIOS
|
|
-- =============================================================================
|
|
|
|
COMMENT ON SCHEMA despacho IS 'Modulo de despacho para asignacion de viajes a unidades/operadores';
|
|
|
|
COMMENT ON TABLE despacho.tableros_despacho IS 'Configuracion de tableros de despacho con mapa';
|
|
COMMENT ON TABLE despacho.estado_unidades IS 'Estado en tiempo real de unidades para despacho';
|
|
COMMENT ON TABLE despacho.reglas_despacho IS 'Reglas para asignacion automatica de viajes';
|
|
COMMENT ON TABLE despacho.reglas_escalamiento IS 'Reglas para escalar viajes sin respuesta';
|
|
COMMENT ON TABLE despacho.log_despacho IS 'Auditoria de acciones de despacho';
|
|
|
|
COMMENT ON TABLE fleet.certificaciones_operador IS 'Certificaciones y licencias de operadores';
|
|
COMMENT ON TABLE fleet.turnos_operador IS 'Programacion de turnos de operadores';
|
|
|
|
COMMENT ON COLUMN despacho.estado_unidades.viaje_actual_id IS 'FK a transport.viajes - viaje actualmente asignado';
|
|
COMMENT ON COLUMN despacho.estado_unidades.operador_ids IS 'IDs de operadores asignados a la unidad';
|
|
COMMENT ON COLUMN despacho.log_despacho.viaje_id IS 'FK a transport.viajes - viaje al que aplica la accion';
|
|
|
|
-- =============================================================================
|
|
-- FIN DDL DESPACHO
|
|
-- =============================================================================
|