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>
442 lines
13 KiB
SQL
442 lines
13 KiB
SQL
-- =============================================================================
|
|
-- ERP TRANSPORTISTAS - Schema Transport DDL
|
|
-- =============================================================================
|
|
-- Archivo: 01-transport-schema-ddl.sql
|
|
-- Version: 1.0.0
|
|
-- Fecha: 2026-01-25
|
|
-- Descripcion: Ordenes de transporte, embarques, viajes, paradas, POD
|
|
-- =============================================================================
|
|
|
|
-- =============================================================================
|
|
-- TIPOS ENUMERADOS ADICIONALES
|
|
-- =============================================================================
|
|
|
|
CREATE TYPE transport.estado_orden AS ENUM (
|
|
'BORRADOR',
|
|
'CONFIRMADA',
|
|
'ASIGNADA',
|
|
'EN_PROCESO',
|
|
'COMPLETADA',
|
|
'FACTURADA',
|
|
'CANCELADA'
|
|
);
|
|
|
|
CREATE TYPE transport.tipo_carga AS ENUM (
|
|
'GENERAL',
|
|
'PELIGROSA',
|
|
'REFRIGERADA',
|
|
'SOBREDIMENSIONADA',
|
|
'GRANEL',
|
|
'LIQUIDOS',
|
|
'CONTENEDOR',
|
|
'AUTOMOVILES'
|
|
);
|
|
|
|
CREATE TYPE transport.estado_pod AS ENUM (
|
|
'PENDIENTE',
|
|
'PARCIAL',
|
|
'COMPLETO',
|
|
'RECHAZADO'
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: ordenes_transporte (OT)
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.ordenes_transporte (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Identificación
|
|
codigo VARCHAR(50) NOT NULL,
|
|
referencia_cliente VARCHAR(100),
|
|
|
|
-- Cliente (Shipper)
|
|
shipper_id UUID NOT NULL,
|
|
shipper_nombre VARCHAR(200) NOT NULL,
|
|
|
|
-- Destinatario (Consignee)
|
|
consignee_id UUID NOT NULL,
|
|
consignee_nombre VARCHAR(200) NOT NULL,
|
|
|
|
-- Origen
|
|
origen_direccion TEXT NOT NULL,
|
|
origen_codigo_postal VARCHAR(10),
|
|
origen_ciudad VARCHAR(100),
|
|
origen_estado VARCHAR(100),
|
|
origen_pais VARCHAR(3) DEFAULT 'MEX',
|
|
origen_latitud DECIMAL(10, 7),
|
|
origen_longitud DECIMAL(10, 7),
|
|
origen_contacto VARCHAR(200),
|
|
origen_telefono VARCHAR(30),
|
|
|
|
-- Destino
|
|
destino_direccion TEXT NOT NULL,
|
|
destino_codigo_postal VARCHAR(10),
|
|
destino_ciudad VARCHAR(100),
|
|
destino_estado VARCHAR(100),
|
|
destino_pais VARCHAR(3) DEFAULT 'MEX',
|
|
destino_latitud DECIMAL(10, 7),
|
|
destino_longitud DECIMAL(10, 7),
|
|
destino_contacto VARCHAR(200),
|
|
destino_telefono VARCHAR(30),
|
|
|
|
-- Fechas programadas
|
|
fecha_recoleccion_programada TIMESTAMPTZ,
|
|
fecha_entrega_programada TIMESTAMPTZ,
|
|
ventana_recoleccion_inicio TIME,
|
|
ventana_recoleccion_fin TIME,
|
|
ventana_entrega_inicio TIME,
|
|
ventana_entrega_fin TIME,
|
|
|
|
-- Carga
|
|
tipo_carga transport.tipo_carga DEFAULT 'GENERAL',
|
|
descripcion_carga TEXT,
|
|
peso_kg DECIMAL(12, 2),
|
|
volumen_m3 DECIMAL(12, 4),
|
|
piezas INT,
|
|
pallets INT,
|
|
valor_declarado DECIMAL(15, 2),
|
|
moneda VARCHAR(3) DEFAULT 'MXN',
|
|
|
|
-- Requisitos
|
|
requiere_temperatura BOOLEAN DEFAULT FALSE,
|
|
temperatura_min DECIMAL(5, 2),
|
|
temperatura_max DECIMAL(5, 2),
|
|
requiere_gps BOOLEAN DEFAULT FALSE,
|
|
requiere_escolta BOOLEAN DEFAULT FALSE,
|
|
requiere_cita BOOLEAN DEFAULT FALSE,
|
|
instrucciones_especiales TEXT,
|
|
|
|
-- Servicio y tarifa
|
|
modalidad_servicio transport.modalidad_servicio DEFAULT 'FTL',
|
|
tarifa_id UUID,
|
|
tarifa_base DECIMAL(15, 2),
|
|
recargos DECIMAL(15, 2) DEFAULT 0,
|
|
descuentos DECIMAL(15, 2) DEFAULT 0,
|
|
subtotal DECIMAL(15, 2),
|
|
iva DECIMAL(15, 2),
|
|
total DECIMAL(15, 2),
|
|
|
|
-- Estado
|
|
estado transport.estado_orden DEFAULT 'BORRADOR',
|
|
|
|
-- Asignación
|
|
viaje_id UUID,
|
|
embarque_id UUID,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_by_id UUID,
|
|
deleted_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT uq_ot_tenant_codigo UNIQUE (tenant_id, codigo)
|
|
);
|
|
|
|
CREATE INDEX idx_ot_tenant ON transport.ordenes_transporte(tenant_id);
|
|
CREATE INDEX idx_ot_estado ON transport.ordenes_transporte(tenant_id, estado);
|
|
CREATE INDEX idx_ot_shipper ON transport.ordenes_transporte(tenant_id, shipper_id);
|
|
CREATE INDEX idx_ot_fechas ON transport.ordenes_transporte(tenant_id, fecha_recoleccion_programada);
|
|
CREATE INDEX idx_ot_viaje ON transport.ordenes_transporte(viaje_id) WHERE viaje_id IS NOT NULL;
|
|
|
|
-- =============================================================================
|
|
-- TABLA: embarques (Agrupación de OTs)
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.embarques (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
codigo VARCHAR(50) NOT NULL,
|
|
descripcion VARCHAR(500),
|
|
|
|
-- Cliente principal
|
|
cliente_id UUID NOT NULL,
|
|
|
|
-- Totales consolidados
|
|
total_ots INT DEFAULT 0,
|
|
peso_total_kg DECIMAL(12, 2),
|
|
volumen_total_m3 DECIMAL(12, 4),
|
|
|
|
-- Estado
|
|
estado VARCHAR(20) DEFAULT 'ABIERTO',
|
|
|
|
-- Viaje asignado
|
|
viaje_id UUID,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT uq_embarque_tenant_codigo UNIQUE (tenant_id, codigo)
|
|
);
|
|
|
|
CREATE INDEX idx_embarque_tenant ON transport.embarques(tenant_id);
|
|
CREATE INDEX idx_embarque_cliente ON transport.embarques(tenant_id, cliente_id);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: viajes
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.viajes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Identificación
|
|
codigo VARCHAR(50) NOT NULL,
|
|
|
|
-- Unidad y operador (referencias a fleet schema)
|
|
unidad_id UUID NOT NULL,
|
|
remolque_id UUID,
|
|
operador_id UUID NOT NULL,
|
|
|
|
-- Ruta
|
|
origen_principal VARCHAR(200),
|
|
destino_principal VARCHAR(200),
|
|
distancia_estimada_km DECIMAL(10, 2),
|
|
tiempo_estimado_horas DECIMAL(6, 2),
|
|
|
|
-- Fechas programadas
|
|
fecha_salida_programada TIMESTAMPTZ,
|
|
fecha_llegada_programada TIMESTAMPTZ,
|
|
|
|
-- Fechas reales
|
|
fecha_salida_real TIMESTAMPTZ,
|
|
fecha_llegada_real TIMESTAMPTZ,
|
|
|
|
-- Kilometraje
|
|
km_inicio INT,
|
|
km_fin INT,
|
|
km_recorridos INT GENERATED ALWAYS AS (km_fin - km_inicio) STORED,
|
|
|
|
-- Estado
|
|
estado transport.estado_viaje DEFAULT 'BORRADOR',
|
|
|
|
-- Checklist pre-viaje
|
|
checklist_completado BOOLEAN DEFAULT FALSE,
|
|
checklist_fecha TIMESTAMPTZ,
|
|
checklist_observaciones TEXT,
|
|
|
|
-- Sellos
|
|
sellos_salida JSONB,
|
|
sellos_llegada JSONB,
|
|
|
|
-- Costos
|
|
costo_combustible DECIMAL(15, 2) DEFAULT 0,
|
|
costo_peajes DECIMAL(15, 2) DEFAULT 0,
|
|
costo_viaticos DECIMAL(15, 2) DEFAULT 0,
|
|
costo_otros DECIMAL(15, 2) DEFAULT 0,
|
|
costo_total DECIMAL(15, 2) DEFAULT 0,
|
|
|
|
-- Ingresos
|
|
ingreso_total DECIMAL(15, 2) DEFAULT 0,
|
|
margen DECIMAL(15, 2) GENERATED ALWAYS AS (ingreso_total - costo_total) STORED,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_by_id UUID,
|
|
|
|
CONSTRAINT uq_viaje_tenant_codigo UNIQUE (tenant_id, codigo)
|
|
);
|
|
|
|
CREATE INDEX idx_viaje_tenant ON transport.viajes(tenant_id);
|
|
CREATE INDEX idx_viaje_estado ON transport.viajes(tenant_id, estado);
|
|
CREATE INDEX idx_viaje_unidad ON transport.viajes(tenant_id, unidad_id);
|
|
CREATE INDEX idx_viaje_operador ON transport.viajes(tenant_id, operador_id);
|
|
CREATE INDEX idx_viaje_fechas ON transport.viajes(tenant_id, fecha_salida_programada);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: paradas_viaje (Multi-paradas)
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.paradas_viaje (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
viaje_id UUID NOT NULL REFERENCES transport.viajes(id),
|
|
|
|
-- Secuencia
|
|
secuencia INT NOT NULL,
|
|
|
|
-- Tipo de parada
|
|
tipo VARCHAR(20) NOT NULL, -- 'RECOLECCION', 'ENTREGA', 'ESCALA'
|
|
|
|
-- Ubicación
|
|
direccion TEXT NOT NULL,
|
|
codigo_postal VARCHAR(10),
|
|
ciudad VARCHAR(100),
|
|
estado VARCHAR(100),
|
|
latitud DECIMAL(10, 7),
|
|
longitud DECIMAL(10, 7),
|
|
|
|
-- Contacto
|
|
contacto_nombre VARCHAR(200),
|
|
contacto_telefono VARCHAR(30),
|
|
|
|
-- Programación
|
|
hora_programada_llegada TIMESTAMPTZ,
|
|
hora_programada_salida TIMESTAMPTZ,
|
|
|
|
-- Real
|
|
hora_real_llegada TIMESTAMPTZ,
|
|
hora_real_salida TIMESTAMPTZ,
|
|
|
|
-- OTs asociadas
|
|
ots_ids UUID[],
|
|
|
|
-- Estado
|
|
estado VARCHAR(20) DEFAULT 'PENDIENTE',
|
|
|
|
-- Observaciones
|
|
observaciones TEXT,
|
|
|
|
CONSTRAINT uq_parada_viaje_secuencia UNIQUE (viaje_id, secuencia)
|
|
);
|
|
|
|
CREATE INDEX idx_parada_viaje ON transport.paradas_viaje(viaje_id);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: pod (Proof of Delivery)
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.pod (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
viaje_id UUID NOT NULL REFERENCES transport.viajes(id),
|
|
parada_id UUID REFERENCES transport.paradas_viaje(id),
|
|
ot_id UUID REFERENCES transport.ordenes_transporte(id),
|
|
|
|
-- Estado POD
|
|
estado transport.estado_pod DEFAULT 'PENDIENTE',
|
|
|
|
-- Recepción
|
|
receptor_nombre VARCHAR(200),
|
|
receptor_identificacion VARCHAR(50),
|
|
fecha_recepcion TIMESTAMPTZ,
|
|
|
|
-- Firma digital
|
|
firma_digital TEXT, -- Base64 de la imagen de firma
|
|
|
|
-- Evidencias (URLs o IDs de archivos)
|
|
fotos_entrega JSONB,
|
|
|
|
-- Cantidades
|
|
piezas_entregadas INT,
|
|
piezas_rechazadas INT,
|
|
piezas_danadas INT,
|
|
|
|
-- Observaciones
|
|
observaciones TEXT,
|
|
motivo_rechazo TEXT,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID,
|
|
|
|
CONSTRAINT uq_pod_ot UNIQUE (ot_id)
|
|
);
|
|
|
|
CREATE INDEX idx_pod_viaje ON transport.pod(viaje_id);
|
|
CREATE INDEX idx_pod_estado ON transport.pod(tenant_id, estado);
|
|
|
|
-- =============================================================================
|
|
-- TABLA: incidencias
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE transport.incidencias (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
|
|
|
-- Referencias
|
|
viaje_id UUID REFERENCES transport.viajes(id),
|
|
ot_id UUID REFERENCES transport.ordenes_transporte(id),
|
|
unidad_id UUID,
|
|
operador_id UUID,
|
|
|
|
-- Incidencia
|
|
codigo VARCHAR(50) NOT NULL,
|
|
tipo transport.tipo_incidencia NOT NULL,
|
|
descripcion TEXT NOT NULL,
|
|
|
|
-- Ubicación
|
|
latitud DECIMAL(10, 7),
|
|
longitud DECIMAL(10, 7),
|
|
direccion TEXT,
|
|
|
|
-- Fecha/hora
|
|
fecha_incidencia TIMESTAMPTZ NOT NULL,
|
|
fecha_reporte TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Impacto
|
|
tiempo_perdido_horas DECIMAL(6, 2),
|
|
costo_estimado DECIMAL(15, 2),
|
|
|
|
-- Resolución
|
|
estado VARCHAR(20) DEFAULT 'ABIERTA',
|
|
fecha_resolucion TIMESTAMPTZ,
|
|
resolucion TEXT,
|
|
responsable_id UUID,
|
|
|
|
-- Evidencias
|
|
evidencias JSONB,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by_id UUID NOT NULL,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT uq_incidencia_codigo UNIQUE (tenant_id, codigo)
|
|
);
|
|
|
|
CREATE INDEX idx_incidencia_tenant ON transport.incidencias(tenant_id);
|
|
CREATE INDEX idx_incidencia_viaje ON transport.incidencias(viaje_id) WHERE viaje_id IS NOT NULL;
|
|
CREATE INDEX idx_incidencia_estado ON transport.incidencias(tenant_id, estado);
|
|
|
|
-- =============================================================================
|
|
-- RLS POLICIES
|
|
-- =============================================================================
|
|
|
|
ALTER TABLE transport.ordenes_transporte ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transport.embarques ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transport.viajes ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transport.paradas_viaje ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transport.pod ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transport.incidencias ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation_ot ON transport.ordenes_transporte
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_embarques ON transport.embarques
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_viajes ON transport.viajes
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_paradas ON transport.paradas_viaje
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_pod ON transport.pod
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_incidencias ON transport.incidencias
|
|
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
|
|
-- =============================================================================
|
|
-- COMENTARIOS
|
|
-- =============================================================================
|
|
|
|
COMMENT ON TABLE transport.ordenes_transporte IS 'Ordenes de transporte (OT) - solicitudes de servicio';
|
|
COMMENT ON TABLE transport.embarques IS 'Agrupación de OTs para consolidación';
|
|
COMMENT ON TABLE transport.viajes IS 'Ejecución operativa de transporte';
|
|
COMMENT ON TABLE transport.paradas_viaje IS 'Paradas programadas en un viaje (multi-drop)';
|
|
COMMENT ON TABLE transport.pod IS 'Proof of Delivery - evidencia de entrega';
|
|
COMMENT ON TABLE transport.incidencias IS 'Registro de incidencias durante el transporte';
|
|
|
|
-- =============================================================================
|
|
-- FIN DDL TRANSPORT
|
|
-- =============================================================================
|