feat: Add new DDL schemas for GPS, dispatch and offline modules
- 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>
This commit is contained in:
parent
7a91823784
commit
8de39831dc
255
ddl/03a-gps-devices-ddl.sql
Normal file
255
ddl/03a-gps-devices-ddl.sql
Normal file
@ -0,0 +1,255 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - GPS Devices DDL (Extension)
|
||||
-- =============================================================================
|
||||
-- Archivo: 03a-gps-devices-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-28
|
||||
-- Descripcion: Tablas adicionales para modulo GPS (dispositivos, eventos geocerca, segmentos)
|
||||
-- Basado en: erp-mecanicas-diesel MMD-014 GPS Integration
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS ADICIONALES
|
||||
-- =============================================================================
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE tracking.plataforma_gps AS ENUM (
|
||||
'traccar',
|
||||
'wialon',
|
||||
'samsara',
|
||||
'geotab',
|
||||
'manual'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE tracking.tipo_unidad_gps AS ENUM (
|
||||
'tractora',
|
||||
'remolque',
|
||||
'caja',
|
||||
'equipo',
|
||||
'operador'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE tracking.tipo_evento_geocerca AS ENUM (
|
||||
'entrada',
|
||||
'salida',
|
||||
'permanencia'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE tracking.tipo_segmento AS ENUM (
|
||||
'hacia_destino',
|
||||
'en_destino',
|
||||
'retorno',
|
||||
'entre_paradas',
|
||||
'otro'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: dispositivos_gps
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tracking.dispositivos_gps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Link to fleet unit (fleet.unidades)
|
||||
unidad_id UUID NOT NULL,
|
||||
tipo_unidad tracking.tipo_unidad_gps DEFAULT 'tractora',
|
||||
|
||||
-- External platform identification
|
||||
external_device_id VARCHAR(100) NOT NULL,
|
||||
plataforma tracking.plataforma_gps DEFAULT 'traccar',
|
||||
|
||||
-- Device identifiers
|
||||
imei VARCHAR(20),
|
||||
numero_serie VARCHAR(50),
|
||||
telefono VARCHAR(20),
|
||||
modelo VARCHAR(50),
|
||||
fabricante VARCHAR(50),
|
||||
|
||||
-- Status
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
ultima_posicion_at TIMESTAMPTZ,
|
||||
ultima_posicion_lat DECIMAL(10, 7),
|
||||
ultima_posicion_lng DECIMAL(10, 7),
|
||||
|
||||
-- Configuration
|
||||
intervalo_posicion_segundos INTEGER DEFAULT 30,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID,
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT uq_dispositivo_external UNIQUE (tenant_id, plataforma, external_device_id),
|
||||
CONSTRAINT uq_dispositivo_unidad UNIQUE (tenant_id, unidad_id) WHERE activo = TRUE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dispositivos_gps_tenant ON tracking.dispositivos_gps(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dispositivos_gps_unidad ON tracking.dispositivos_gps(unidad_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dispositivos_gps_external ON tracking.dispositivos_gps(external_device_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dispositivos_gps_plataforma ON tracking.dispositivos_gps(plataforma);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: eventos_geocerca
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tracking.eventos_geocerca (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- References
|
||||
geocerca_id UUID NOT NULL REFERENCES tracking.geocercas(id),
|
||||
dispositivo_id UUID NOT NULL REFERENCES tracking.dispositivos_gps(id),
|
||||
unidad_id UUID NOT NULL,
|
||||
|
||||
-- Event type
|
||||
tipo_evento tracking.tipo_evento_geocerca NOT NULL,
|
||||
|
||||
-- Position that triggered the event
|
||||
posicion_id UUID,
|
||||
latitud DECIMAL(10, 7) NOT NULL,
|
||||
longitud DECIMAL(10, 7) NOT NULL,
|
||||
|
||||
-- Timestamps
|
||||
tiempo_evento TIMESTAMPTZ NOT NULL,
|
||||
procesado_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
-- Link to viaje (if applicable)
|
||||
viaje_id UUID,
|
||||
|
||||
-- Metadata
|
||||
metadata JSONB DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_tenant ON tracking.eventos_geocerca(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_geocerca ON tracking.eventos_geocerca(geocerca_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_dispositivo ON tracking.eventos_geocerca(dispositivo_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_unidad ON tracking.eventos_geocerca(unidad_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_tiempo ON tracking.eventos_geocerca(tiempo_evento);
|
||||
CREATE INDEX IF NOT EXISTS idx_eventos_geocerca_viaje ON tracking.eventos_geocerca(viaje_id) WHERE viaje_id IS NOT NULL;
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: segmentos_ruta
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tracking.segmentos_ruta (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Link to viaje
|
||||
viaje_id UUID,
|
||||
unidad_id UUID NOT NULL,
|
||||
dispositivo_id UUID REFERENCES tracking.dispositivos_gps(id),
|
||||
|
||||
-- Start/end positions
|
||||
posicion_inicio_id UUID,
|
||||
posicion_fin_id UUID,
|
||||
|
||||
-- Coordinates (denormalized)
|
||||
lat_inicio DECIMAL(10, 7) NOT NULL,
|
||||
lng_inicio DECIMAL(10, 7) NOT NULL,
|
||||
lat_fin DECIMAL(10, 7) NOT NULL,
|
||||
lng_fin DECIMAL(10, 7) NOT NULL,
|
||||
|
||||
-- Distances
|
||||
distancia_km DECIMAL(10, 3) NOT NULL,
|
||||
distancia_cruda_km DECIMAL(10, 3),
|
||||
|
||||
-- Times
|
||||
tiempo_inicio TIMESTAMPTZ NOT NULL,
|
||||
tiempo_fin TIMESTAMPTZ NOT NULL,
|
||||
duracion_minutos DECIMAL(8, 2),
|
||||
|
||||
-- Segment type
|
||||
tipo_segmento tracking.tipo_segmento DEFAULT 'otro',
|
||||
|
||||
-- Validation
|
||||
es_valido BOOLEAN DEFAULT TRUE,
|
||||
notas_validacion TEXT,
|
||||
|
||||
-- Encoded polyline for visualization
|
||||
polyline_encoded TEXT,
|
||||
|
||||
-- Metadata
|
||||
metadata JSONB DEFAULT '{}',
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
calculado_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_segmentos_ruta_tenant ON tracking.segmentos_ruta(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_segmentos_ruta_viaje ON tracking.segmentos_ruta(viaje_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_segmentos_ruta_unidad ON tracking.segmentos_ruta(unidad_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_segmentos_ruta_tipo ON tracking.segmentos_ruta(tipo_segmento);
|
||||
CREATE INDEX IF NOT EXISTS idx_segmentos_ruta_tiempo ON tracking.segmentos_ruta(tiempo_inicio);
|
||||
|
||||
-- =============================================================================
|
||||
-- ALTER posiciones_gps para agregar dispositivo_id
|
||||
-- =============================================================================
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE tracking.posiciones_gps ADD COLUMN IF NOT EXISTS dispositivo_id UUID REFERENCES tracking.dispositivos_gps(id);
|
||||
EXCEPTION WHEN others THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE tracking.posiciones_gps ADD COLUMN IF NOT EXISTS es_valido BOOLEAN DEFAULT TRUE;
|
||||
EXCEPTION WHEN others THEN null;
|
||||
END $$;
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE tracking.dispositivos_gps ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tracking.eventos_geocerca ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tracking.segmentos_ruta ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE POLICY tenant_isolation_dispositivos ON tracking.dispositivos_gps
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE POLICY tenant_isolation_eventos_geo ON tracking.eventos_geocerca
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE POLICY tenant_isolation_segmentos ON tracking.segmentos_ruta
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
EXCEPTION WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE tracking.dispositivos_gps IS 'Dispositivos GPS vinculados a unidades de flota';
|
||||
COMMENT ON TABLE tracking.eventos_geocerca IS 'Eventos de entrada/salida de geocercas';
|
||||
COMMENT ON TABLE tracking.segmentos_ruta IS 'Segmentos de ruta para calculo de distancias facturables';
|
||||
|
||||
COMMENT ON COLUMN tracking.dispositivos_gps.plataforma IS 'Proveedor GPS: traccar, wialon, samsara, geotab, manual';
|
||||
COMMENT ON COLUMN tracking.dispositivos_gps.tipo_unidad IS 'Tipo de unidad: tractora, remolque, caja, equipo, operador';
|
||||
COMMENT ON COLUMN tracking.segmentos_ruta.tipo_segmento IS 'Tipo: hacia_destino, en_destino, retorno, entre_paradas, otro';
|
||||
COMMENT ON COLUMN tracking.segmentos_ruta.polyline_encoded IS 'Polyline codificada en formato Google para visualizacion en mapas';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL GPS DEVICES
|
||||
-- =============================================================================
|
||||
427
ddl/09-dispatch-schema-ddl.sql
Normal file
427
ddl/09-dispatch-schema-ddl.sql
Normal file
@ -0,0 +1,427 @@
|
||||
-- =============================================================================
|
||||
-- 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
|
||||
-- =============================================================================
|
||||
358
ddl/10-offline-schema-ddl.sql
Normal file
358
ddl/10-offline-schema-ddl.sql
Normal file
@ -0,0 +1,358 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Offline DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 10-offline-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-28
|
||||
-- Descripcion: Cola de operaciones offline para sincronizacion
|
||||
-- Basado en: OfflineQueue.entity.ts (TASK-007)
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- SCHEMA OFFLINE
|
||||
-- =============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS offline;
|
||||
COMMENT ON SCHEMA offline IS 'Sistema de sincronizacion offline para operaciones en campo';
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
-- Tipos de operacion que pueden ser encoladas offline
|
||||
CREATE TYPE offline.tipo_operacion_offline AS ENUM (
|
||||
-- GPS Operations
|
||||
'GPS_POSICION',
|
||||
'GPS_EVENTO',
|
||||
|
||||
-- Dispatch Operations
|
||||
'VIAJE_ESTADO',
|
||||
'VIAJE_EVENTO',
|
||||
'CHECKIN',
|
||||
'CHECKOUT',
|
||||
|
||||
-- POD Operations
|
||||
'POD_FOTO',
|
||||
'POD_FIRMA',
|
||||
'POD_DOCUMENTO',
|
||||
|
||||
-- Checklist Operations
|
||||
'CHECKLIST_ITEM',
|
||||
'CHECKLIST_COMPLETADO',
|
||||
|
||||
-- Generic
|
||||
'CUSTOM'
|
||||
);
|
||||
|
||||
COMMENT ON TYPE offline.tipo_operacion_offline IS 'Tipos de operaciones que pueden encolarse offline';
|
||||
|
||||
-- Estados de sincronizacion para operaciones encoladas
|
||||
CREATE TYPE offline.estado_sincronizacion AS ENUM (
|
||||
'PENDIENTE', -- Esperando sincronizacion
|
||||
'EN_PROCESO', -- Sincronizacion en progreso
|
||||
'COMPLETADO', -- Sincronizado exitosamente
|
||||
'ERROR', -- Error durante sincronizacion
|
||||
'CONFLICTO', -- Conflicto de datos detectado
|
||||
'DESCARTADO' -- Operacion descartada
|
||||
);
|
||||
|
||||
COMMENT ON TYPE offline.estado_sincronizacion IS 'Estados del proceso de sincronizacion';
|
||||
|
||||
-- Niveles de prioridad para cola de sincronizacion
|
||||
CREATE TYPE offline.prioridad_sync AS ENUM (
|
||||
'1', -- CRITICA: Posiciones GPS, eventos de seguridad
|
||||
'2', -- ALTA: POD, cambios de estado
|
||||
'3', -- NORMAL: Items de checklist, notas
|
||||
'4' -- BAJA: Fotos, documentos
|
||||
);
|
||||
|
||||
COMMENT ON TYPE offline.prioridad_sync IS 'Prioridades de sincronizacion (1=critica, 4=baja)';
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: offline_queue
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE offline.offline_queue (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Referencias opcionales
|
||||
dispositivo_id UUID, -- Dispositivo que genero la operacion
|
||||
usuario_id UUID, -- Usuario que genero la operacion
|
||||
unidad_id UUID, -- Unidad relacionada
|
||||
viaje_id UUID, -- Viaje relacionado
|
||||
|
||||
-- Detalles de la operacion
|
||||
tipo_operacion offline.tipo_operacion_offline NOT NULL,
|
||||
estado offline.estado_sincronizacion DEFAULT 'PENDIENTE',
|
||||
prioridad offline.prioridad_sync DEFAULT '3',
|
||||
|
||||
-- Payload - datos a sincronizar (JSONB)
|
||||
payload JSONB NOT NULL,
|
||||
|
||||
-- Metadata de la operacion
|
||||
endpoint_destino VARCHAR(255) NOT NULL, -- URL/endpoint destino
|
||||
metodo_http VARCHAR(10) DEFAULT 'POST', -- GET, POST, PUT, PATCH, DELETE
|
||||
|
||||
-- Timestamps offline
|
||||
creado_offline_en TIMESTAMPTZ NOT NULL, -- Timestamp cuando se creo offline
|
||||
cliente_id VARCHAR(100), -- UUID generado en cliente para deduplicacion
|
||||
|
||||
-- Tracking de sincronizacion
|
||||
intentos_sync INT DEFAULT 0, -- Contador de intentos
|
||||
max_intentos INT DEFAULT 5, -- Maximo de intentos permitidos
|
||||
ultimo_intento_en TIMESTAMPTZ, -- Timestamp del ultimo intento
|
||||
sincronizado_en TIMESTAMPTZ, -- Timestamp cuando se sincronizo exitosamente
|
||||
|
||||
-- Manejo de errores
|
||||
ultimo_error TEXT, -- Ultimo mensaje de error
|
||||
historial_errores JSONB DEFAULT '[]', -- Array de {timestamp, error}
|
||||
|
||||
-- Resolucion de conflictos
|
||||
version_servidor INT, -- Version del servidor para conflictos
|
||||
resolucion_conflicto VARCHAR(50), -- 'CLIENT_WINS', 'SERVER_WINS', 'MERGE', 'MANUAL'
|
||||
|
||||
-- Optimizacion de ancho de banda
|
||||
tamano_bytes INT, -- Tamano del payload en bytes
|
||||
comprimido BOOLEAN DEFAULT FALSE, -- Si el payload esta comprimido
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- INDICES
|
||||
-- =============================================================================
|
||||
|
||||
-- Indice principal por tenant
|
||||
CREATE INDEX idx_offline_queue_tenant
|
||||
ON offline.offline_queue(tenant_id);
|
||||
|
||||
-- Indice por dispositivo para sincronizacion
|
||||
CREATE INDEX idx_offline_queue_dispositivo
|
||||
ON offline.offline_queue(dispositivo_id)
|
||||
WHERE dispositivo_id IS NOT NULL;
|
||||
|
||||
-- Indice por estado para procesamiento de cola
|
||||
CREATE INDEX idx_offline_queue_estado
|
||||
ON offline.offline_queue(estado);
|
||||
|
||||
-- Indice por prioridad y timestamp para ordenamiento de cola FIFO con prioridad
|
||||
CREATE INDEX idx_offline_queue_prioridad
|
||||
ON offline.offline_queue(prioridad, creado_offline_en);
|
||||
|
||||
-- Indice compuesto para operaciones pendientes por tenant
|
||||
CREATE INDEX idx_offline_queue_pendientes
|
||||
ON offline.offline_queue(tenant_id, estado, prioridad)
|
||||
WHERE estado IN ('PENDIENTE', 'EN_PROCESO', 'ERROR');
|
||||
|
||||
-- Indice para buscar por cliente_id (deduplicacion)
|
||||
CREATE UNIQUE INDEX idx_offline_queue_cliente_id
|
||||
ON offline.offline_queue(tenant_id, cliente_id)
|
||||
WHERE cliente_id IS NOT NULL;
|
||||
|
||||
-- Indice por viaje para consultas relacionadas
|
||||
CREATE INDEX idx_offline_queue_viaje
|
||||
ON offline.offline_queue(viaje_id)
|
||||
WHERE viaje_id IS NOT NULL;
|
||||
|
||||
-- Indice por unidad para consultas relacionadas
|
||||
CREATE INDEX idx_offline_queue_unidad
|
||||
ON offline.offline_queue(unidad_id)
|
||||
WHERE unidad_id IS NOT NULL;
|
||||
|
||||
-- Indice para limpieza de registros antiguos sincronizados
|
||||
CREATE INDEX idx_offline_queue_sincronizado
|
||||
ON offline.offline_queue(sincronizado_en)
|
||||
WHERE estado = 'COMPLETADO';
|
||||
|
||||
-- =============================================================================
|
||||
-- TRIGGER: Actualizar updated_at automaticamente
|
||||
-- =============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION offline.update_offline_queue_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_offline_queue_updated_at
|
||||
BEFORE UPDATE ON offline.offline_queue
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION offline.update_offline_queue_timestamp();
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE offline.offline_queue ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_offline_queue ON offline.offline_queue
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE offline.offline_queue IS 'Cola de operaciones pendientes de sincronizacion desde dispositivos offline';
|
||||
|
||||
COMMENT ON COLUMN offline.offline_queue.tipo_operacion IS 'Tipo de operacion: GPS, POD, Checklist, etc.';
|
||||
COMMENT ON COLUMN offline.offline_queue.estado IS 'Estado actual de la sincronizacion';
|
||||
COMMENT ON COLUMN offline.offline_queue.prioridad IS 'Prioridad de sincronizacion (1=critica, 4=baja)';
|
||||
COMMENT ON COLUMN offline.offline_queue.payload IS 'Datos de la operacion en formato JSON';
|
||||
COMMENT ON COLUMN offline.offline_queue.endpoint_destino IS 'URL o endpoint API destino para la sincronizacion';
|
||||
COMMENT ON COLUMN offline.offline_queue.creado_offline_en IS 'Timestamp original cuando la operacion fue creada offline';
|
||||
COMMENT ON COLUMN offline.offline_queue.cliente_id IS 'UUID generado en el cliente para deduplicacion de operaciones';
|
||||
COMMENT ON COLUMN offline.offline_queue.intentos_sync IS 'Numero de intentos de sincronizacion realizados';
|
||||
COMMENT ON COLUMN offline.offline_queue.historial_errores IS 'Historial de errores como array JSON [{timestamp, error}]';
|
||||
COMMENT ON COLUMN offline.offline_queue.resolucion_conflicto IS 'Estrategia de resolucion: CLIENT_WINS, SERVER_WINS, MERGE, MANUAL';
|
||||
COMMENT ON COLUMN offline.offline_queue.comprimido IS 'Indica si el payload esta comprimido (gzip)';
|
||||
|
||||
-- =============================================================================
|
||||
-- FUNCIONES AUXILIARES
|
||||
-- =============================================================================
|
||||
|
||||
-- Funcion para obtener operaciones pendientes ordenadas por prioridad
|
||||
CREATE OR REPLACE FUNCTION offline.get_pending_operations(
|
||||
p_tenant_id UUID,
|
||||
p_limit INT DEFAULT 100
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
tipo_operacion offline.tipo_operacion_offline,
|
||||
prioridad offline.prioridad_sync,
|
||||
payload JSONB,
|
||||
endpoint_destino VARCHAR(255),
|
||||
metodo_http VARCHAR(10),
|
||||
creado_offline_en TIMESTAMPTZ,
|
||||
intentos_sync INT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
oq.id,
|
||||
oq.tipo_operacion,
|
||||
oq.prioridad,
|
||||
oq.payload,
|
||||
oq.endpoint_destino,
|
||||
oq.metodo_http,
|
||||
oq.creado_offline_en,
|
||||
oq.intentos_sync
|
||||
FROM offline.offline_queue oq
|
||||
WHERE oq.tenant_id = p_tenant_id
|
||||
AND oq.estado IN ('PENDIENTE', 'ERROR')
|
||||
AND oq.intentos_sync < oq.max_intentos
|
||||
ORDER BY oq.prioridad ASC, oq.creado_offline_en ASC
|
||||
LIMIT p_limit;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Funcion para marcar operacion como en proceso
|
||||
CREATE OR REPLACE FUNCTION offline.start_sync_operation(
|
||||
p_operation_id UUID
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_updated INT;
|
||||
BEGIN
|
||||
UPDATE offline.offline_queue
|
||||
SET
|
||||
estado = 'EN_PROCESO',
|
||||
ultimo_intento_en = NOW(),
|
||||
intentos_sync = intentos_sync + 1
|
||||
WHERE id = p_operation_id
|
||||
AND estado IN ('PENDIENTE', 'ERROR');
|
||||
|
||||
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
||||
RETURN v_updated > 0;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Funcion para completar operacion exitosamente
|
||||
CREATE OR REPLACE FUNCTION offline.complete_sync_operation(
|
||||
p_operation_id UUID
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_updated INT;
|
||||
BEGIN
|
||||
UPDATE offline.offline_queue
|
||||
SET
|
||||
estado = 'COMPLETADO',
|
||||
sincronizado_en = NOW()
|
||||
WHERE id = p_operation_id
|
||||
AND estado = 'EN_PROCESO';
|
||||
|
||||
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
||||
RETURN v_updated > 0;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Funcion para registrar error en operacion
|
||||
CREATE OR REPLACE FUNCTION offline.fail_sync_operation(
|
||||
p_operation_id UUID,
|
||||
p_error_message TEXT
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_updated INT;
|
||||
v_historial JSONB;
|
||||
BEGIN
|
||||
-- Obtener historial actual
|
||||
SELECT historial_errores INTO v_historial
|
||||
FROM offline.offline_queue
|
||||
WHERE id = p_operation_id;
|
||||
|
||||
-- Agregar nuevo error al historial
|
||||
v_historial = v_historial || jsonb_build_object(
|
||||
'timestamp', NOW()::TEXT,
|
||||
'error', p_error_message
|
||||
);
|
||||
|
||||
UPDATE offline.offline_queue
|
||||
SET
|
||||
estado = 'ERROR',
|
||||
ultimo_error = p_error_message,
|
||||
historial_errores = v_historial
|
||||
WHERE id = p_operation_id
|
||||
AND estado = 'EN_PROCESO';
|
||||
|
||||
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
||||
RETURN v_updated > 0;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Funcion para limpiar operaciones antiguas completadas
|
||||
CREATE OR REPLACE FUNCTION offline.cleanup_old_operations(
|
||||
p_days_to_keep INT DEFAULT 30
|
||||
)
|
||||
RETURNS INT AS $$
|
||||
DECLARE
|
||||
v_deleted INT;
|
||||
BEGIN
|
||||
DELETE FROM offline.offline_queue
|
||||
WHERE estado = 'COMPLETADO'
|
||||
AND sincronizado_en < NOW() - (p_days_to_keep || ' days')::INTERVAL;
|
||||
|
||||
GET DIAGNOSTICS v_deleted = ROW_COUNT;
|
||||
RETURN v_deleted;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- =============================================================================
|
||||
-- GRANTS
|
||||
-- =============================================================================
|
||||
|
||||
-- Permisos para funciones
|
||||
GRANT EXECUTE ON FUNCTION offline.get_pending_operations(UUID, INT) TO PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION offline.start_sync_operation(UUID) TO PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION offline.complete_sync_operation(UUID) TO PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION offline.fail_sync_operation(UUID, TEXT) TO PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION offline.cleanup_old_operations(INT) TO PUBLIC;
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL OFFLINE
|
||||
-- =============================================================================
|
||||
Loading…
Reference in New Issue
Block a user