feat: Add complete DDL for all transport schemas (01-08)
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>
This commit is contained in:
parent
c93e2b1e0e
commit
7a91823784
441
ddl/01-transport-schema-ddl.sql
Normal file
441
ddl/01-transport-schema-ddl.sql
Normal file
@ -0,0 +1,441 @@
|
||||
-- =============================================================================
|
||||
-- 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
|
||||
-- =============================================================================
|
||||
409
ddl/02-fleet-schema-ddl.sql
Normal file
409
ddl/02-fleet-schema-ddl.sql
Normal file
@ -0,0 +1,409 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Fleet DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 02-fleet-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Unidades, remolques, operadores, documentos, licencias
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS ADICIONALES
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE fleet.tipo_licencia AS ENUM (
|
||||
'A', -- Motociclista
|
||||
'B', -- Automovilista particular
|
||||
'C', -- Chofer particular
|
||||
'D', -- Chofer público pasajeros
|
||||
'E', -- Chofer público carga
|
||||
'F' -- Federal (SCT)
|
||||
);
|
||||
|
||||
CREATE TYPE fleet.estado_operador AS ENUM (
|
||||
'ACTIVO',
|
||||
'EN_VIAJE',
|
||||
'DESCANSO',
|
||||
'VACACIONES',
|
||||
'INCAPACIDAD',
|
||||
'SUSPENDIDO',
|
||||
'BAJA'
|
||||
);
|
||||
|
||||
CREATE TYPE fleet.tipo_documento AS ENUM (
|
||||
'LICENCIA',
|
||||
'INE',
|
||||
'CURP',
|
||||
'RFC',
|
||||
'NSS',
|
||||
'TARJETA_CIRCULACION',
|
||||
'POLIZA_SEGURO',
|
||||
'VERIFICACION',
|
||||
'PERMISO_SCT',
|
||||
'CERTIFICADO_FISICO',
|
||||
'ANTIDOPING',
|
||||
'OTRO'
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: unidades (Tractoras y vehículos)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fleet.unidades (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
numero_economico VARCHAR(20) NOT NULL,
|
||||
tipo fleet.tipo_unidad NOT NULL,
|
||||
|
||||
-- Vehículo
|
||||
marca VARCHAR(50),
|
||||
modelo VARCHAR(50),
|
||||
anio INT,
|
||||
color VARCHAR(30),
|
||||
numero_serie VARCHAR(50),
|
||||
numero_motor VARCHAR(50),
|
||||
|
||||
-- Placas
|
||||
placa VARCHAR(15),
|
||||
placa_estado VARCHAR(50),
|
||||
|
||||
-- SCT
|
||||
permiso_sct VARCHAR(50),
|
||||
tipo_permiso_sct VARCHAR(10),
|
||||
configuracion_vehicular VARCHAR(10), -- C2, C3, T3S2, etc.
|
||||
|
||||
-- Capacidades
|
||||
capacidad_peso_kg DECIMAL(10, 2),
|
||||
capacidad_volumen_m3 DECIMAL(10, 4),
|
||||
capacidad_pallets INT,
|
||||
|
||||
-- Combustible
|
||||
tipo_combustible VARCHAR(20), -- DIESEL, GASOLINA, GAS
|
||||
rendimiento_km_litro DECIMAL(6, 2),
|
||||
capacidad_tanque_litros DECIMAL(8, 2),
|
||||
|
||||
-- Odómetro
|
||||
odometro_actual INT DEFAULT 0,
|
||||
odometro_ultimo_servicio INT,
|
||||
|
||||
-- GPS
|
||||
tiene_gps BOOLEAN DEFAULT FALSE,
|
||||
gps_proveedor VARCHAR(50),
|
||||
gps_imei VARCHAR(50),
|
||||
|
||||
-- Estado
|
||||
estado fleet.estado_unidad DEFAULT 'DISPONIBLE',
|
||||
ubicacion_actual_lat DECIMAL(10, 7),
|
||||
ubicacion_actual_lng DECIMAL(10, 7),
|
||||
ultima_actualizacion_ubicacion TIMESTAMPTZ,
|
||||
|
||||
-- Propiedad
|
||||
es_propia BOOLEAN DEFAULT TRUE,
|
||||
propietario_id UUID, -- Si no es propia, referencia al carrier
|
||||
|
||||
-- Costos
|
||||
costo_adquisicion DECIMAL(15, 2),
|
||||
fecha_adquisicion DATE,
|
||||
valor_actual DECIMAL(15, 2),
|
||||
|
||||
-- Fechas importantes
|
||||
fecha_verificacion_proxima DATE,
|
||||
fecha_poliza_vencimiento DATE,
|
||||
fecha_permiso_vencimiento DATE,
|
||||
|
||||
-- Activo
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
fecha_baja DATE,
|
||||
motivo_baja TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_by_id UUID,
|
||||
|
||||
CONSTRAINT uq_unidad_numero UNIQUE (tenant_id, numero_economico),
|
||||
CONSTRAINT uq_unidad_placa UNIQUE (tenant_id, placa)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_unidad_tenant ON fleet.unidades(tenant_id);
|
||||
CREATE INDEX idx_unidad_tipo ON fleet.unidades(tenant_id, tipo);
|
||||
CREATE INDEX idx_unidad_estado ON fleet.unidades(tenant_id, estado);
|
||||
CREATE INDEX idx_unidad_activo ON fleet.unidades(tenant_id, activo) WHERE activo = TRUE;
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: remolques
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fleet.remolques (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
numero_economico VARCHAR(20) NOT NULL,
|
||||
tipo fleet.tipo_unidad NOT NULL, -- CAJA_SECA, PLATAFORMA, etc.
|
||||
|
||||
-- Vehículo
|
||||
marca VARCHAR(50),
|
||||
modelo VARCHAR(50),
|
||||
anio INT,
|
||||
numero_serie VARCHAR(50),
|
||||
|
||||
-- Placas
|
||||
placa VARCHAR(15),
|
||||
placa_estado VARCHAR(50),
|
||||
|
||||
-- Dimensiones
|
||||
largo_metros DECIMAL(6, 2),
|
||||
ancho_metros DECIMAL(6, 2),
|
||||
alto_metros DECIMAL(6, 2),
|
||||
|
||||
-- Capacidades
|
||||
capacidad_peso_kg DECIMAL(10, 2),
|
||||
capacidad_volumen_m3 DECIMAL(10, 4),
|
||||
capacidad_pallets INT,
|
||||
|
||||
-- Refrigeración (si aplica)
|
||||
es_refrigerado BOOLEAN DEFAULT FALSE,
|
||||
marca_refrigeracion VARCHAR(50),
|
||||
modelo_refrigeracion VARCHAR(50),
|
||||
temperatura_min DECIMAL(5, 2),
|
||||
temperatura_max DECIMAL(5, 2),
|
||||
|
||||
-- Estado
|
||||
estado fleet.estado_unidad DEFAULT 'DISPONIBLE',
|
||||
|
||||
-- Propiedad
|
||||
es_propia BOOLEAN DEFAULT TRUE,
|
||||
propietario_id UUID,
|
||||
|
||||
-- Fechas importantes
|
||||
fecha_verificacion_proxima DATE,
|
||||
fecha_poliza_vencimiento DATE,
|
||||
|
||||
-- Activo
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT uq_remolque_numero UNIQUE (tenant_id, numero_economico)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_remolque_tenant ON fleet.remolques(tenant_id);
|
||||
CREATE INDEX idx_remolque_tipo ON fleet.remolques(tenant_id, tipo);
|
||||
CREATE INDEX idx_remolque_estado ON fleet.remolques(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: operadores (Conductores)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fleet.operadores (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
numero_empleado VARCHAR(20) NOT NULL,
|
||||
nombre VARCHAR(100) NOT NULL,
|
||||
apellido_paterno VARCHAR(100) NOT NULL,
|
||||
apellido_materno VARCHAR(100),
|
||||
nombre_completo VARCHAR(300) GENERATED ALWAYS AS (
|
||||
nombre || ' ' || apellido_paterno || COALESCE(' ' || apellido_materno, '')
|
||||
) STORED,
|
||||
|
||||
-- Documentos de identidad
|
||||
curp VARCHAR(18),
|
||||
rfc VARCHAR(13),
|
||||
nss VARCHAR(15), -- Número Seguro Social
|
||||
|
||||
-- Contacto
|
||||
telefono VARCHAR(30),
|
||||
telefono_emergencia VARCHAR(30),
|
||||
email VARCHAR(255),
|
||||
|
||||
-- Dirección
|
||||
direccion TEXT,
|
||||
codigo_postal VARCHAR(10),
|
||||
ciudad VARCHAR(100),
|
||||
estado VARCHAR(100),
|
||||
|
||||
-- Datos de nacimiento
|
||||
fecha_nacimiento DATE,
|
||||
lugar_nacimiento VARCHAR(100),
|
||||
nacionalidad VARCHAR(50) DEFAULT 'Mexicana',
|
||||
|
||||
-- Licencia de conducir
|
||||
tipo_licencia fleet.tipo_licencia,
|
||||
numero_licencia VARCHAR(30),
|
||||
licencia_vigencia DATE,
|
||||
licencia_estado_expedicion VARCHAR(50),
|
||||
|
||||
-- Certificaciones
|
||||
certificado_fisico_vigencia DATE,
|
||||
antidoping_vigencia DATE,
|
||||
capacitacion_materiales_peligrosos BOOLEAN DEFAULT FALSE,
|
||||
capacitacion_mp_vigencia DATE,
|
||||
|
||||
-- Estado
|
||||
estado fleet.estado_operador DEFAULT 'ACTIVO',
|
||||
|
||||
-- Unidad asignada (default)
|
||||
unidad_asignada_id UUID REFERENCES fleet.unidades(id),
|
||||
|
||||
-- Métricas de desempeño
|
||||
calificacion DECIMAL(3, 2) DEFAULT 5.00,
|
||||
total_viajes INT DEFAULT 0,
|
||||
total_km INT DEFAULT 0,
|
||||
incidentes INT DEFAULT 0,
|
||||
|
||||
-- Datos bancarios (para pagos)
|
||||
banco VARCHAR(100),
|
||||
cuenta_bancaria VARCHAR(30),
|
||||
clabe VARCHAR(18),
|
||||
|
||||
-- Salario
|
||||
salario_base DECIMAL(12, 2),
|
||||
tipo_pago VARCHAR(20), -- 'FIJO', 'POR_VIAJE', 'MIXTO'
|
||||
|
||||
-- Fechas
|
||||
fecha_ingreso DATE,
|
||||
fecha_baja DATE,
|
||||
motivo_baja TEXT,
|
||||
|
||||
-- Activo
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_by_id UUID,
|
||||
|
||||
CONSTRAINT uq_operador_numero UNIQUE (tenant_id, numero_empleado),
|
||||
CONSTRAINT uq_operador_curp UNIQUE (tenant_id, curp)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_operador_tenant ON fleet.operadores(tenant_id);
|
||||
CREATE INDEX idx_operador_estado ON fleet.operadores(tenant_id, estado);
|
||||
CREATE INDEX idx_operador_activo ON fleet.operadores(tenant_id, activo) WHERE activo = TRUE;
|
||||
CREATE INDEX idx_operador_licencia ON fleet.operadores(licencia_vigencia);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: documentos_flota
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fleet.documentos_flota (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Referencia polimórfica
|
||||
entidad_tipo VARCHAR(20) NOT NULL, -- 'UNIDAD', 'REMOLQUE', 'OPERADOR'
|
||||
entidad_id UUID NOT NULL,
|
||||
|
||||
-- Documento
|
||||
tipo_documento fleet.tipo_documento NOT NULL,
|
||||
nombre VARCHAR(200) NOT NULL,
|
||||
numero_documento VARCHAR(100),
|
||||
descripcion TEXT,
|
||||
|
||||
-- Vigencia
|
||||
fecha_emision DATE,
|
||||
fecha_vencimiento DATE,
|
||||
dias_alerta_vencimiento INT DEFAULT 30,
|
||||
|
||||
-- Archivo
|
||||
archivo_url TEXT,
|
||||
archivo_nombre VARCHAR(255),
|
||||
archivo_tipo VARCHAR(50),
|
||||
archivo_tamano_bytes BIGINT,
|
||||
|
||||
-- Estado
|
||||
verificado BOOLEAN DEFAULT FALSE,
|
||||
verificado_por UUID,
|
||||
verificado_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Activo
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_documento_entidad ON fleet.documentos_flota(entidad_tipo, entidad_id);
|
||||
CREATE INDEX idx_documento_vencimiento ON fleet.documentos_flota(fecha_vencimiento) WHERE activo = TRUE;
|
||||
CREATE INDEX idx_documento_tipo ON fleet.documentos_flota(tenant_id, tipo_documento);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: asignaciones_unidad_operador
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fleet.asignaciones (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
unidad_id UUID NOT NULL REFERENCES fleet.unidades(id),
|
||||
operador_id UUID NOT NULL REFERENCES fleet.operadores(id),
|
||||
remolque_id UUID REFERENCES fleet.remolques(id),
|
||||
|
||||
-- Vigencia de asignación
|
||||
fecha_inicio TIMESTAMPTZ NOT NULL,
|
||||
fecha_fin TIMESTAMPTZ,
|
||||
|
||||
-- Activa
|
||||
activa BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Motivo
|
||||
motivo VARCHAR(200),
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asignacion_unidad ON fleet.asignaciones(unidad_id, activa);
|
||||
CREATE INDEX idx_asignacion_operador ON fleet.asignaciones(operador_id, activa);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE fleet.unidades ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fleet.remolques ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fleet.operadores ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fleet.documentos_flota ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fleet.asignaciones ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_unidades ON fleet.unidades
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_remolques ON fleet.remolques
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_operadores ON fleet.operadores
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_documentos ON fleet.documentos_flota
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_asignaciones ON fleet.asignaciones
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE fleet.unidades IS 'Unidades motrices (tractoras, camiones, camionetas)';
|
||||
COMMENT ON TABLE fleet.remolques IS 'Remolques, cajas, plataformas, tanques';
|
||||
COMMENT ON TABLE fleet.operadores IS 'Operadores/conductores de la flota';
|
||||
COMMENT ON TABLE fleet.documentos_flota IS 'Documentos de unidades, remolques y operadores';
|
||||
COMMENT ON TABLE fleet.asignaciones IS 'Historial de asignaciones unidad-operador';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL FLEET
|
||||
-- =============================================================================
|
||||
369
ddl/03-tracking-schema-ddl.sql
Normal file
369
ddl/03-tracking-schema-ddl.sql
Normal file
@ -0,0 +1,369 @@
|
||||
-- =============================================================================
|
||||
-- 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
|
||||
-- =============================================================================
|
||||
315
ddl/04-fuel-schema-ddl.sql
Normal file
315
ddl/04-fuel-schema-ddl.sql
Normal file
@ -0,0 +1,315 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Fuel DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 04-fuel-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Combustible, peajes, gastos de viaje, viaticos
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE fuel.tipo_carga_combustible AS ENUM (
|
||||
'VALE',
|
||||
'TARJETA',
|
||||
'EFECTIVO',
|
||||
'FACTURA_DIRECTA'
|
||||
);
|
||||
|
||||
CREATE TYPE fuel.tipo_gasto AS ENUM (
|
||||
'COMBUSTIBLE',
|
||||
'PEAJE',
|
||||
'VIATICO',
|
||||
'HOSPEDAJE',
|
||||
'ALIMENTOS',
|
||||
'ESTACIONAMIENTO',
|
||||
'MULTA',
|
||||
'MANIOBRA',
|
||||
'REPARACION_MENOR',
|
||||
'OTRO'
|
||||
);
|
||||
|
||||
CREATE TYPE fuel.estado_gasto AS ENUM (
|
||||
'PENDIENTE',
|
||||
'APROBADO',
|
||||
'RECHAZADO',
|
||||
'PAGADO'
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: cargas_combustible
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fuel.cargas_combustible (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Unidad y viaje
|
||||
unidad_id UUID NOT NULL,
|
||||
viaje_id UUID,
|
||||
operador_id UUID NOT NULL,
|
||||
|
||||
-- Carga
|
||||
tipo_carga fuel.tipo_carga_combustible NOT NULL,
|
||||
tipo_combustible VARCHAR(20) NOT NULL, -- DIESEL, GASOLINA, GAS
|
||||
litros DECIMAL(10, 3) NOT NULL,
|
||||
precio_litro DECIMAL(10, 4) NOT NULL,
|
||||
total DECIMAL(12, 2) NOT NULL,
|
||||
|
||||
-- Odómetro
|
||||
odometro_carga INT,
|
||||
rendimiento_calculado DECIMAL(6, 2), -- km/litro desde última carga
|
||||
|
||||
-- Ubicación
|
||||
estacion_id UUID,
|
||||
estacion_nombre VARCHAR(200),
|
||||
estacion_direccion TEXT,
|
||||
latitud DECIMAL(10, 7),
|
||||
longitud DECIMAL(10, 7),
|
||||
|
||||
-- Vale/Factura
|
||||
numero_vale VARCHAR(50),
|
||||
numero_factura VARCHAR(50),
|
||||
folio_ticket VARCHAR(50),
|
||||
|
||||
-- Fecha
|
||||
fecha_carga TIMESTAMPTZ NOT NULL,
|
||||
|
||||
-- Aprobación
|
||||
estado fuel.estado_gasto DEFAULT 'PENDIENTE',
|
||||
aprobado_por UUID,
|
||||
aprobado_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Evidencia
|
||||
foto_ticket_url TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_carga_unidad ON fuel.cargas_combustible(unidad_id);
|
||||
CREATE INDEX idx_carga_viaje ON fuel.cargas_combustible(viaje_id);
|
||||
CREATE INDEX idx_carga_fecha ON fuel.cargas_combustible(tenant_id, fecha_carga);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: cruces_peaje
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fuel.cruces_peaje (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Unidad y viaje
|
||||
unidad_id UUID NOT NULL,
|
||||
viaje_id UUID,
|
||||
operador_id UUID,
|
||||
|
||||
-- Peaje
|
||||
caseta_nombre VARCHAR(200) NOT NULL,
|
||||
caseta_codigo VARCHAR(50),
|
||||
carretera VARCHAR(200),
|
||||
|
||||
-- Monto
|
||||
monto DECIMAL(10, 2) NOT NULL,
|
||||
tipo_pago VARCHAR(20), -- EFECTIVO, TAG, PREPAGO
|
||||
|
||||
-- TAG (si aplica)
|
||||
tag_numero VARCHAR(50),
|
||||
|
||||
-- Ubicación
|
||||
latitud DECIMAL(10, 7),
|
||||
longitud DECIMAL(10, 7),
|
||||
|
||||
-- Fecha
|
||||
fecha_cruce TIMESTAMPTZ NOT NULL,
|
||||
|
||||
-- Comprobante
|
||||
numero_ticket VARCHAR(50),
|
||||
foto_ticket_url TEXT,
|
||||
|
||||
-- Estado
|
||||
estado fuel.estado_gasto DEFAULT 'APROBADO',
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_peaje_unidad ON fuel.cruces_peaje(unidad_id);
|
||||
CREATE INDEX idx_peaje_viaje ON fuel.cruces_peaje(viaje_id);
|
||||
CREATE INDEX idx_peaje_fecha ON fuel.cruces_peaje(tenant_id, fecha_cruce);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: gastos_viaje
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fuel.gastos_viaje (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Viaje y operador
|
||||
viaje_id UUID NOT NULL,
|
||||
operador_id UUID NOT NULL,
|
||||
|
||||
-- Gasto
|
||||
tipo_gasto fuel.tipo_gasto NOT NULL,
|
||||
descripcion VARCHAR(500) NOT NULL,
|
||||
monto DECIMAL(12, 2) NOT NULL,
|
||||
|
||||
-- Comprobante
|
||||
tiene_factura BOOLEAN DEFAULT FALSE,
|
||||
numero_factura VARCHAR(50),
|
||||
numero_ticket VARCHAR(50),
|
||||
foto_comprobante_url TEXT,
|
||||
|
||||
-- Ubicación
|
||||
lugar VARCHAR(200),
|
||||
latitud DECIMAL(10, 7),
|
||||
longitud DECIMAL(10, 7),
|
||||
|
||||
-- Fecha
|
||||
fecha_gasto TIMESTAMPTZ NOT NULL,
|
||||
|
||||
-- Estado
|
||||
estado fuel.estado_gasto DEFAULT 'PENDIENTE',
|
||||
aprobado_por UUID,
|
||||
aprobado_fecha TIMESTAMPTZ,
|
||||
motivo_rechazo TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_gasto_viaje ON fuel.gastos_viaje(viaje_id);
|
||||
CREATE INDEX idx_gasto_operador ON fuel.gastos_viaje(operador_id);
|
||||
CREATE INDEX idx_gasto_estado ON fuel.gastos_viaje(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: anticipos_viaticos
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fuel.anticipos_viaticos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Viaje y operador
|
||||
viaje_id UUID NOT NULL,
|
||||
operador_id UUID NOT NULL,
|
||||
|
||||
-- Anticipo
|
||||
monto_solicitado DECIMAL(12, 2) NOT NULL,
|
||||
monto_aprobado DECIMAL(12, 2),
|
||||
monto_comprobado DECIMAL(12, 2) DEFAULT 0,
|
||||
monto_reintegro DECIMAL(12, 2) DEFAULT 0,
|
||||
|
||||
-- Conceptos desglosados
|
||||
combustible_estimado DECIMAL(12, 2),
|
||||
peajes_estimado DECIMAL(12, 2),
|
||||
viaticos_estimado DECIMAL(12, 2),
|
||||
|
||||
-- Estado
|
||||
estado VARCHAR(20) DEFAULT 'SOLICITADO',
|
||||
-- SOLICITADO, APROBADO, ENTREGADO, COMPROBANDO, LIQUIDADO
|
||||
|
||||
-- Fechas
|
||||
fecha_solicitud TIMESTAMPTZ DEFAULT NOW(),
|
||||
fecha_aprobacion TIMESTAMPTZ,
|
||||
fecha_entrega TIMESTAMPTZ,
|
||||
fecha_liquidacion TIMESTAMPTZ,
|
||||
|
||||
-- Aprobaciones
|
||||
aprobado_por UUID,
|
||||
entregado_por UUID,
|
||||
liquidado_por UUID,
|
||||
|
||||
-- Observaciones
|
||||
observaciones TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_anticipo_viaje ON fuel.anticipos_viaticos(viaje_id);
|
||||
CREATE INDEX idx_anticipo_operador ON fuel.anticipos_viaticos(operador_id);
|
||||
CREATE INDEX idx_anticipo_estado ON fuel.anticipos_viaticos(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: control_rendimiento
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE fuel.control_rendimiento (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Unidad
|
||||
unidad_id UUID NOT NULL,
|
||||
|
||||
-- Período
|
||||
fecha_inicio DATE NOT NULL,
|
||||
fecha_fin DATE NOT NULL,
|
||||
|
||||
-- Métricas
|
||||
km_recorridos INT NOT NULL,
|
||||
litros_consumidos DECIMAL(12, 3) NOT NULL,
|
||||
rendimiento_real DECIMAL(6, 2) NOT NULL,
|
||||
rendimiento_esperado DECIMAL(6, 2),
|
||||
variacion_porcentaje DECIMAL(5, 2),
|
||||
|
||||
-- Costos
|
||||
costo_total_combustible DECIMAL(15, 2),
|
||||
costo_por_km DECIMAL(8, 4),
|
||||
|
||||
-- Alertas
|
||||
tiene_anomalia BOOLEAN DEFAULT FALSE,
|
||||
tipo_anomalia VARCHAR(50),
|
||||
descripcion_anomalia TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_rendimiento_unidad ON fuel.control_rendimiento(unidad_id);
|
||||
CREATE INDEX idx_rendimiento_fecha ON fuel.control_rendimiento(tenant_id, fecha_inicio);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE fuel.cargas_combustible ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fuel.cruces_peaje ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fuel.gastos_viaje ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fuel.anticipos_viaticos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE fuel.control_rendimiento ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_cargas ON fuel.cargas_combustible
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_peajes ON fuel.cruces_peaje
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_gastos ON fuel.gastos_viaje
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_anticipos ON fuel.anticipos_viaticos
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_rendimiento ON fuel.control_rendimiento
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE fuel.cargas_combustible IS 'Registro de cargas de combustible';
|
||||
COMMENT ON TABLE fuel.cruces_peaje IS 'Cruces de casetas de peaje';
|
||||
COMMENT ON TABLE fuel.gastos_viaje IS 'Gastos diversos durante el viaje';
|
||||
COMMENT ON TABLE fuel.anticipos_viaticos IS 'Anticipos entregados a operadores';
|
||||
COMMENT ON TABLE fuel.control_rendimiento IS 'Control de rendimiento de combustible por unidad';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL FUEL
|
||||
-- =============================================================================
|
||||
306
ddl/05-maintenance-schema-ddl.sql
Normal file
306
ddl/05-maintenance-schema-ddl.sql
Normal file
@ -0,0 +1,306 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Maintenance DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 05-maintenance-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Mantenimiento preventivo, correctivo, ordenes de trabajo
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE maintenance.tipo_mantenimiento AS ENUM (
|
||||
'PREVENTIVO',
|
||||
'CORRECTIVO',
|
||||
'PREDICTIVO',
|
||||
'EMERGENCIA'
|
||||
);
|
||||
|
||||
CREATE TYPE maintenance.prioridad AS ENUM (
|
||||
'BAJA',
|
||||
'MEDIA',
|
||||
'ALTA',
|
||||
'URGENTE'
|
||||
);
|
||||
|
||||
CREATE TYPE maintenance.estado_orden AS ENUM (
|
||||
'BORRADOR',
|
||||
'PROGRAMADA',
|
||||
'EN_PROCESO',
|
||||
'ESPERANDO_REFACCIONES',
|
||||
'COMPLETADA',
|
||||
'CANCELADA'
|
||||
);
|
||||
|
||||
CREATE TYPE maintenance.tipo_servicio AS ENUM (
|
||||
'CAMBIO_ACEITE',
|
||||
'FRENOS',
|
||||
'LLANTAS',
|
||||
'SUSPENSION',
|
||||
'MOTOR',
|
||||
'TRANSMISION',
|
||||
'ELECTRICO',
|
||||
'CARROCERIA',
|
||||
'REFRIGERACION',
|
||||
'ALINEACION_BALANCEO',
|
||||
'REVISION_GENERAL',
|
||||
'OTRO'
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: planes_mantenimiento
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE maintenance.planes_mantenimiento (
|
||||
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,
|
||||
descripcion TEXT,
|
||||
|
||||
-- Tipo de unidad que aplica
|
||||
aplica_tipo_unidad fleet.tipo_unidad[],
|
||||
aplica_todas_unidades BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Frecuencia
|
||||
frecuencia_km INT,
|
||||
frecuencia_dias INT,
|
||||
frecuencia_horas_motor INT,
|
||||
|
||||
-- Servicios incluidos
|
||||
servicios maintenance.tipo_servicio[] NOT NULL,
|
||||
|
||||
-- Costos estimados
|
||||
costo_estimado_mano_obra DECIMAL(12, 2),
|
||||
costo_estimado_refacciones DECIMAL(12, 2),
|
||||
tiempo_estimado_horas DECIMAL(6, 2),
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_plan_tenant ON maintenance.planes_mantenimiento(tenant_id);
|
||||
CREATE UNIQUE INDEX idx_plan_codigo ON maintenance.planes_mantenimiento(tenant_id, codigo);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: programacion_mantenimiento
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE maintenance.programacion_mantenimiento (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Unidad y plan
|
||||
unidad_id UUID NOT NULL,
|
||||
plan_id UUID REFERENCES maintenance.planes_mantenimiento(id),
|
||||
|
||||
-- Tipo
|
||||
tipo maintenance.tipo_mantenimiento NOT NULL,
|
||||
|
||||
-- Próximo mantenimiento
|
||||
proximo_km INT,
|
||||
proxima_fecha DATE,
|
||||
proximas_horas_motor INT,
|
||||
|
||||
-- Último mantenimiento
|
||||
ultimo_km INT,
|
||||
ultima_fecha DATE,
|
||||
ultima_orden_id UUID,
|
||||
|
||||
-- Estado
|
||||
vencido BOOLEAN DEFAULT FALSE,
|
||||
dias_para_vencer INT,
|
||||
|
||||
-- Auditoría
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_programacion_unidad ON maintenance.programacion_mantenimiento(unidad_id);
|
||||
CREATE INDEX idx_programacion_vencido ON maintenance.programacion_mantenimiento(tenant_id, vencido);
|
||||
CREATE INDEX idx_programacion_proxima ON maintenance.programacion_mantenimiento(proxima_fecha);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: ordenes_trabajo
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE maintenance.ordenes_trabajo (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
numero_orden VARCHAR(50) NOT NULL,
|
||||
|
||||
-- Unidad
|
||||
unidad_id UUID NOT NULL,
|
||||
remolque_id UUID,
|
||||
|
||||
-- Tipo y prioridad
|
||||
tipo maintenance.tipo_mantenimiento NOT NULL,
|
||||
prioridad maintenance.prioridad DEFAULT 'MEDIA',
|
||||
|
||||
-- Diagnóstico inicial
|
||||
descripcion_falla TEXT,
|
||||
reportado_por VARCHAR(200),
|
||||
reportado_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Programación
|
||||
fecha_programada DATE,
|
||||
hora_programada TIME,
|
||||
taller_id UUID,
|
||||
taller_externo_nombre VARCHAR(200),
|
||||
es_taller_externo BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Ejecución
|
||||
fecha_inicio TIMESTAMPTZ,
|
||||
fecha_fin TIMESTAMPTZ,
|
||||
mecanico_responsable VARCHAR(200),
|
||||
|
||||
-- Odómetro
|
||||
odometro_entrada INT,
|
||||
odometro_salida INT,
|
||||
|
||||
-- Diagnóstico final
|
||||
diagnostico_final TEXT,
|
||||
trabajos_realizados TEXT,
|
||||
|
||||
-- Costos
|
||||
costo_mano_obra DECIMAL(12, 2) DEFAULT 0,
|
||||
costo_refacciones DECIMAL(12, 2) DEFAULT 0,
|
||||
costo_otros DECIMAL(12, 2) DEFAULT 0,
|
||||
costo_total DECIMAL(12, 2) DEFAULT 0,
|
||||
|
||||
-- Plan relacionado
|
||||
plan_id UUID REFERENCES maintenance.planes_mantenimiento(id),
|
||||
|
||||
-- Estado
|
||||
estado maintenance.estado_orden DEFAULT 'BORRADOR',
|
||||
|
||||
-- Garantía
|
||||
tiene_garantia BOOLEAN DEFAULT FALSE,
|
||||
garantia_dias INT,
|
||||
garantia_km INT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_by_id UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ot_tenant ON maintenance.ordenes_trabajo(tenant_id);
|
||||
CREATE INDEX idx_ot_unidad ON maintenance.ordenes_trabajo(unidad_id);
|
||||
CREATE INDEX idx_ot_estado ON maintenance.ordenes_trabajo(tenant_id, estado);
|
||||
CREATE INDEX idx_ot_fecha ON maintenance.ordenes_trabajo(fecha_programada);
|
||||
CREATE UNIQUE INDEX idx_ot_numero ON maintenance.ordenes_trabajo(tenant_id, numero_orden);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: lineas_orden_trabajo (Refacciones usadas)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE maintenance.lineas_orden_trabajo (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
orden_id UUID NOT NULL REFERENCES maintenance.ordenes_trabajo(id),
|
||||
|
||||
-- Tipo de línea
|
||||
tipo VARCHAR(20) NOT NULL, -- 'REFACCION', 'MANO_OBRA', 'OTRO'
|
||||
|
||||
-- Refacción (si aplica)
|
||||
producto_id UUID,
|
||||
numero_parte VARCHAR(100),
|
||||
descripcion VARCHAR(500) NOT NULL,
|
||||
|
||||
-- Cantidades
|
||||
cantidad DECIMAL(10, 3) NOT NULL,
|
||||
unidad_medida VARCHAR(20),
|
||||
|
||||
-- Precios
|
||||
precio_unitario DECIMAL(12, 2) NOT NULL,
|
||||
descuento DECIMAL(12, 2) DEFAULT 0,
|
||||
total DECIMAL(12, 2) NOT NULL,
|
||||
|
||||
-- Proveedor
|
||||
proveedor_id UUID,
|
||||
proveedor_nombre VARCHAR(200),
|
||||
|
||||
-- Factura
|
||||
factura_proveedor VARCHAR(50)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_linea_ot ON maintenance.lineas_orden_trabajo(orden_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: checklist_mantenimiento
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE maintenance.checklist_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
orden_id UUID NOT NULL REFERENCES maintenance.ordenes_trabajo(id),
|
||||
|
||||
-- Item
|
||||
categoria VARCHAR(100),
|
||||
descripcion VARCHAR(500) NOT NULL,
|
||||
obligatorio BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Resultado
|
||||
resultado VARCHAR(20), -- 'OK', 'REPARADO', 'PENDIENTE', 'NO_APLICA'
|
||||
observaciones TEXT,
|
||||
|
||||
-- Evidencia
|
||||
foto_url TEXT,
|
||||
|
||||
-- Revisado
|
||||
revisado_por VARCHAR(200),
|
||||
revisado_fecha TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_checklist_orden ON maintenance.checklist_items(orden_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE maintenance.planes_mantenimiento ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE maintenance.programacion_mantenimiento ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE maintenance.ordenes_trabajo ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE maintenance.lineas_orden_trabajo ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE maintenance.checklist_items ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_planes ON maintenance.planes_mantenimiento
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_programacion ON maintenance.programacion_mantenimiento
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_ordenes ON maintenance.ordenes_trabajo
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_lineas ON maintenance.lineas_orden_trabajo
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_checklist ON maintenance.checklist_items
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE maintenance.planes_mantenimiento IS 'Planes de mantenimiento preventivo';
|
||||
COMMENT ON TABLE maintenance.programacion_mantenimiento IS 'Programación de próximos mantenimientos por unidad';
|
||||
COMMENT ON TABLE maintenance.ordenes_trabajo IS 'Órdenes de trabajo de mantenimiento';
|
||||
COMMENT ON TABLE maintenance.lineas_orden_trabajo IS 'Líneas de detalle de órdenes de trabajo';
|
||||
COMMENT ON TABLE maintenance.checklist_items IS 'Items de checklist de mantenimiento';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL MAINTENANCE
|
||||
-- =============================================================================
|
||||
344
ddl/06-carriers-schema-ddl.sql
Normal file
344
ddl/06-carriers-schema-ddl.sql
Normal file
@ -0,0 +1,344 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Carriers DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 06-carriers-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Transportistas subcontratados, documentos, scorecard
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE carriers.estado_carrier AS ENUM (
|
||||
'PROSPECTO',
|
||||
'EN_VALIDACION',
|
||||
'ACTIVO',
|
||||
'SUSPENDIDO',
|
||||
'BAJA'
|
||||
);
|
||||
|
||||
CREATE TYPE carriers.tipo_contrato AS ENUM (
|
||||
'SPOT',
|
||||
'DEDICADO',
|
||||
'PREFERENTE'
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: carriers (Transportistas terceros)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.carriers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
codigo VARCHAR(20) NOT NULL,
|
||||
razon_social VARCHAR(200) NOT NULL,
|
||||
nombre_comercial VARCHAR(200),
|
||||
|
||||
-- Fiscal
|
||||
rfc VARCHAR(13) NOT NULL,
|
||||
regimen_fiscal VARCHAR(100),
|
||||
|
||||
-- SCT
|
||||
permiso_sct VARCHAR(50),
|
||||
tipo_permiso_sct VARCHAR(10),
|
||||
permiso_vigencia DATE,
|
||||
|
||||
-- Contacto
|
||||
contacto_nombre VARCHAR(200),
|
||||
contacto_telefono VARCHAR(30),
|
||||
contacto_email VARCHAR(255),
|
||||
|
||||
-- Dirección
|
||||
direccion TEXT,
|
||||
codigo_postal VARCHAR(10),
|
||||
ciudad VARCHAR(100),
|
||||
estado VARCHAR(100),
|
||||
|
||||
-- Operación
|
||||
cobertura_estados TEXT[], -- Estados donde opera
|
||||
tipos_equipo fleet.tipo_unidad[], -- Tipos de unidad disponibles
|
||||
capacidad_unidades INT,
|
||||
|
||||
-- Tipo de relación
|
||||
tipo_contrato carriers.tipo_contrato DEFAULT 'SPOT',
|
||||
|
||||
-- Términos comerciales
|
||||
dias_pago INT DEFAULT 30,
|
||||
porcentaje_retencion DECIMAL(5, 2) DEFAULT 4.00,
|
||||
|
||||
-- Seguros
|
||||
poliza_seguro_carga VARCHAR(100),
|
||||
poliza_vigencia DATE,
|
||||
suma_asegurada DECIMAL(15, 2),
|
||||
|
||||
-- Evaluación
|
||||
calificacion DECIMAL(3, 2) DEFAULT 0,
|
||||
total_viajes INT DEFAULT 0,
|
||||
viajes_a_tiempo INT DEFAULT 0,
|
||||
incidentes INT DEFAULT 0,
|
||||
|
||||
-- Estado
|
||||
estado carriers.estado_carrier DEFAULT 'PROSPECTO',
|
||||
fecha_alta DATE,
|
||||
fecha_baja DATE,
|
||||
motivo_baja TEXT,
|
||||
|
||||
-- Datos bancarios
|
||||
banco VARCHAR(100),
|
||||
cuenta_bancaria VARCHAR(30),
|
||||
clabe VARCHAR(18),
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_by_id UUID,
|
||||
|
||||
CONSTRAINT uq_carrier_codigo UNIQUE (tenant_id, codigo),
|
||||
CONSTRAINT uq_carrier_rfc UNIQUE (tenant_id, rfc)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_carrier_tenant ON carriers.carriers(tenant_id);
|
||||
CREATE INDEX idx_carrier_estado ON carriers.carriers(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: documentos_carrier
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.documentos_carrier (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carrier_id UUID NOT NULL REFERENCES carriers.carriers(id),
|
||||
|
||||
-- Documento
|
||||
tipo_documento VARCHAR(50) NOT NULL,
|
||||
nombre VARCHAR(200) NOT NULL,
|
||||
descripcion TEXT,
|
||||
|
||||
-- Vigencia
|
||||
fecha_emision DATE,
|
||||
fecha_vencimiento DATE,
|
||||
|
||||
-- Archivo
|
||||
archivo_url TEXT,
|
||||
archivo_nombre VARCHAR(255),
|
||||
|
||||
-- Verificación
|
||||
verificado BOOLEAN DEFAULT FALSE,
|
||||
verificado_por UUID,
|
||||
verificado_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_doc_carrier ON carriers.documentos_carrier(carrier_id);
|
||||
CREATE INDEX idx_doc_vencimiento ON carriers.documentos_carrier(fecha_vencimiento);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: unidades_carrier
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.unidades_carrier (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carrier_id UUID NOT NULL REFERENCES carriers.carriers(id),
|
||||
|
||||
-- Unidad
|
||||
numero_economico VARCHAR(20) NOT NULL,
|
||||
tipo fleet.tipo_unidad NOT NULL,
|
||||
marca VARCHAR(50),
|
||||
modelo VARCHAR(50),
|
||||
anio INT,
|
||||
placa VARCHAR(15),
|
||||
|
||||
-- SCT
|
||||
configuracion_vehicular VARCHAR(10),
|
||||
|
||||
-- Capacidad
|
||||
capacidad_peso_kg DECIMAL(10, 2),
|
||||
capacidad_volumen_m3 DECIMAL(10, 4),
|
||||
|
||||
-- GPS
|
||||
tiene_gps BOOLEAN DEFAULT FALSE,
|
||||
gps_proveedor VARCHAR(50),
|
||||
|
||||
-- Estado
|
||||
activa BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_unidad_carrier ON carriers.unidades_carrier(carrier_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: operadores_carrier
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.operadores_carrier (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carrier_id UUID NOT NULL REFERENCES carriers.carriers(id),
|
||||
|
||||
-- Operador
|
||||
nombre_completo VARCHAR(300) NOT NULL,
|
||||
telefono VARCHAR(30),
|
||||
|
||||
-- Licencia
|
||||
tipo_licencia fleet.tipo_licencia,
|
||||
numero_licencia VARCHAR(30),
|
||||
licencia_vigencia DATE,
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_operador_carrier ON carriers.operadores_carrier(carrier_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: asignaciones_carrier (Viajes asignados a carriers)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.asignaciones_carrier (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Carrier
|
||||
carrier_id UUID NOT NULL REFERENCES carriers.carriers(id),
|
||||
unidad_carrier_id UUID REFERENCES carriers.unidades_carrier(id),
|
||||
operador_carrier_id UUID REFERENCES carriers.operadores_carrier(id),
|
||||
|
||||
-- Viaje/OT
|
||||
viaje_id UUID,
|
||||
ot_id UUID,
|
||||
|
||||
-- Tarifa acordada
|
||||
tarifa_acordada DECIMAL(15, 2) NOT NULL,
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
|
||||
-- Fechas
|
||||
fecha_asignacion TIMESTAMPTZ DEFAULT NOW(),
|
||||
fecha_confirmacion TIMESTAMPTZ,
|
||||
|
||||
-- Estado
|
||||
estado VARCHAR(20) DEFAULT 'PENDIENTE',
|
||||
-- PENDIENTE, CONFIRMADA, EN_PROCESO, COMPLETADA, CANCELADA
|
||||
|
||||
-- Facturación
|
||||
factura_carrier VARCHAR(50),
|
||||
fecha_factura DATE,
|
||||
monto_facturado DECIMAL(15, 2),
|
||||
fecha_pago DATE,
|
||||
|
||||
-- Evaluación del viaje
|
||||
calificacion INT, -- 1-5
|
||||
comentarios TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asignacion_carrier ON carriers.asignaciones_carrier(carrier_id);
|
||||
CREATE INDEX idx_asignacion_viaje ON carriers.asignaciones_carrier(viaje_id);
|
||||
CREATE INDEX idx_asignacion_estado ON carriers.asignaciones_carrier(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: scorecard_carrier
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE carriers.scorecard (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carrier_id UUID NOT NULL REFERENCES carriers.carriers(id),
|
||||
|
||||
-- Período
|
||||
periodo_inicio DATE NOT NULL,
|
||||
periodo_fin DATE NOT NULL,
|
||||
|
||||
-- Métricas de servicio
|
||||
total_viajes INT DEFAULT 0,
|
||||
viajes_a_tiempo INT DEFAULT 0,
|
||||
viajes_retrasados INT DEFAULT 0,
|
||||
viajes_cancelados INT DEFAULT 0,
|
||||
|
||||
-- Porcentajes
|
||||
otif_porcentaje DECIMAL(5, 2), -- On Time In Full
|
||||
puntualidad_porcentaje DECIMAL(5, 2),
|
||||
|
||||
-- Incidentes
|
||||
total_incidentes INT DEFAULT 0,
|
||||
incidentes_graves INT DEFAULT 0,
|
||||
|
||||
-- Calificación
|
||||
calificacion_servicio DECIMAL(3, 2),
|
||||
calificacion_documentacion DECIMAL(3, 2),
|
||||
calificacion_comunicacion DECIMAL(3, 2),
|
||||
calificacion_general DECIMAL(3, 2),
|
||||
|
||||
-- Financiero
|
||||
monto_total_servicios DECIMAL(15, 2),
|
||||
monto_penalizaciones DECIMAL(15, 2),
|
||||
|
||||
-- Auditoría
|
||||
calculado_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_scorecard_carrier ON carriers.scorecard(carrier_id);
|
||||
CREATE INDEX idx_scorecard_periodo ON carriers.scorecard(periodo_inicio, periodo_fin);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE carriers.carriers ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE carriers.documentos_carrier ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE carriers.unidades_carrier ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE carriers.operadores_carrier ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE carriers.asignaciones_carrier ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE carriers.scorecard ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_carriers ON carriers.carriers
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_docs ON carriers.documentos_carrier
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_unidades ON carriers.unidades_carrier
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_operadores ON carriers.operadores_carrier
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_asignaciones ON carriers.asignaciones_carrier
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_scorecard ON carriers.scorecard
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE carriers.carriers IS 'Transportistas subcontratados';
|
||||
COMMENT ON TABLE carriers.documentos_carrier IS 'Documentos de carriers';
|
||||
COMMENT ON TABLE carriers.unidades_carrier IS 'Unidades de carriers';
|
||||
COMMENT ON TABLE carriers.operadores_carrier IS 'Operadores de carriers';
|
||||
COMMENT ON TABLE carriers.asignaciones_carrier IS 'Asignaciones de viajes a carriers';
|
||||
COMMENT ON TABLE carriers.scorecard IS 'Evaluación periódica de carriers';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL CARRIERS
|
||||
-- =============================================================================
|
||||
352
ddl/07-billing-transport-ddl.sql
Normal file
352
ddl/07-billing-transport-ddl.sql
Normal file
@ -0,0 +1,352 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Billing Transport DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 07-billing-transport-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Tarifas, facturacion, recargos especificos de transporte
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE billing.tipo_tarifa AS ENUM (
|
||||
'POR_VIAJE',
|
||||
'POR_KM',
|
||||
'POR_TONELADA',
|
||||
'POR_M3',
|
||||
'POR_PALLET',
|
||||
'POR_HORA',
|
||||
'MIXTA'
|
||||
);
|
||||
|
||||
CREATE TYPE billing.tipo_recargo AS ENUM (
|
||||
'FUEL_SURCHARGE',
|
||||
'DETENTION',
|
||||
'MANIOBRAS',
|
||||
'CUSTODIA',
|
||||
'ESCOLTA',
|
||||
'PERNOCTA',
|
||||
'ESTADIAS',
|
||||
'FALSO_FLETE',
|
||||
'SEGURO_ADICIONAL',
|
||||
'OTRO'
|
||||
);
|
||||
|
||||
CREATE TYPE billing.estado_factura AS ENUM (
|
||||
'BORRADOR',
|
||||
'EMITIDA',
|
||||
'ENVIADA',
|
||||
'PAGADA',
|
||||
'PARCIAL',
|
||||
'VENCIDA',
|
||||
'CANCELADA'
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: lanes (Rutas origen-destino)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.lanes (
|
||||
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,
|
||||
|
||||
-- Origen
|
||||
origen_ciudad VARCHAR(100) NOT NULL,
|
||||
origen_estado VARCHAR(100) NOT NULL,
|
||||
origen_codigo_postal VARCHAR(10),
|
||||
|
||||
-- Destino
|
||||
destino_ciudad VARCHAR(100) NOT NULL,
|
||||
destino_estado VARCHAR(100) NOT NULL,
|
||||
destino_codigo_postal VARCHAR(10),
|
||||
|
||||
-- Distancia
|
||||
distancia_km DECIMAL(10, 2),
|
||||
tiempo_estimado_horas DECIMAL(6, 2),
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_lane_tenant ON billing.lanes(tenant_id);
|
||||
CREATE UNIQUE INDEX idx_lane_codigo ON billing.lanes(tenant_id, codigo);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: tarifas
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.tarifas (
|
||||
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,
|
||||
descripcion TEXT,
|
||||
|
||||
-- Cliente (opcional para tarifas generales)
|
||||
cliente_id UUID,
|
||||
|
||||
-- Lane (opcional)
|
||||
lane_id UUID REFERENCES billing.lanes(id),
|
||||
|
||||
-- Tipo de servicio
|
||||
modalidad_servicio transport.modalidad_servicio,
|
||||
tipo_equipo fleet.tipo_unidad,
|
||||
|
||||
-- Tipo de tarifa
|
||||
tipo_tarifa billing.tipo_tarifa NOT NULL,
|
||||
|
||||
-- Precios
|
||||
tarifa_base DECIMAL(15, 2) NOT NULL,
|
||||
tarifa_km DECIMAL(10, 4),
|
||||
tarifa_tonelada DECIMAL(10, 4),
|
||||
tarifa_m3 DECIMAL(10, 4),
|
||||
tarifa_pallet DECIMAL(10, 4),
|
||||
tarifa_hora DECIMAL(10, 4),
|
||||
|
||||
-- Mínimos
|
||||
minimo_facturar DECIMAL(15, 2),
|
||||
peso_minimo_kg DECIMAL(10, 2),
|
||||
|
||||
-- Moneda
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
|
||||
-- Vigencia
|
||||
fecha_inicio DATE NOT NULL,
|
||||
fecha_fin DATE,
|
||||
|
||||
-- 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_tarifa_tenant ON billing.tarifas(tenant_id);
|
||||
CREATE INDEX idx_tarifa_cliente ON billing.tarifas(cliente_id);
|
||||
CREATE INDEX idx_tarifa_lane ON billing.tarifas(lane_id);
|
||||
CREATE INDEX idx_tarifa_activa ON billing.tarifas(tenant_id, activa, fecha_inicio);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: recargos_catalogo
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.recargos_catalogo (
|
||||
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 billing.tipo_recargo NOT NULL,
|
||||
descripcion TEXT,
|
||||
|
||||
-- Monto
|
||||
es_porcentaje BOOLEAN DEFAULT FALSE,
|
||||
monto DECIMAL(15, 4) NOT NULL, -- Si es_porcentaje=true, es porcentaje; sino monto fijo
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
|
||||
-- Aplicación
|
||||
aplica_automatico BOOLEAN DEFAULT FALSE,
|
||||
condicion_aplicacion TEXT, -- Descripción de cuándo aplica
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_recargo_tenant ON billing.recargos_catalogo(tenant_id);
|
||||
CREATE UNIQUE INDEX idx_recargo_codigo ON billing.recargos_catalogo(tenant_id, codigo);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: facturas_transporte
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.facturas_transporte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Identificación
|
||||
serie VARCHAR(10),
|
||||
folio VARCHAR(20) NOT NULL,
|
||||
uuid_cfdi UUID, -- UUID del CFDI timbrado
|
||||
|
||||
-- Cliente
|
||||
cliente_id UUID NOT NULL,
|
||||
cliente_rfc VARCHAR(13) NOT NULL,
|
||||
cliente_razon_social VARCHAR(200) NOT NULL,
|
||||
cliente_uso_cfdi VARCHAR(10),
|
||||
|
||||
-- Fechas
|
||||
fecha_emision TIMESTAMPTZ NOT NULL,
|
||||
fecha_vencimiento DATE,
|
||||
|
||||
-- Totales
|
||||
subtotal DECIMAL(15, 2) NOT NULL,
|
||||
descuento DECIMAL(15, 2) DEFAULT 0,
|
||||
iva DECIMAL(15, 2) DEFAULT 0,
|
||||
retencion_iva DECIMAL(15, 2) DEFAULT 0,
|
||||
retencion_isr DECIMAL(15, 2) DEFAULT 0,
|
||||
total DECIMAL(15, 2) NOT NULL,
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
tipo_cambio DECIMAL(10, 4) DEFAULT 1,
|
||||
|
||||
-- Pago
|
||||
forma_pago VARCHAR(10),
|
||||
metodo_pago VARCHAR(10),
|
||||
condiciones_pago VARCHAR(200),
|
||||
|
||||
-- Relacionados
|
||||
viaje_ids UUID[],
|
||||
ot_ids UUID[],
|
||||
|
||||
-- CFDI
|
||||
xml_cfdi TEXT,
|
||||
pdf_url TEXT,
|
||||
|
||||
-- Estado
|
||||
estado billing.estado_factura DEFAULT 'BORRADOR',
|
||||
|
||||
-- Pago
|
||||
monto_pagado DECIMAL(15, 2) DEFAULT 0,
|
||||
fecha_pago TIMESTAMPTZ,
|
||||
|
||||
-- Cancelación
|
||||
fecha_cancelacion TIMESTAMPTZ,
|
||||
motivo_cancelacion TEXT,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_factura_tenant ON billing.facturas_transporte(tenant_id);
|
||||
CREATE INDEX idx_factura_cliente ON billing.facturas_transporte(cliente_id);
|
||||
CREATE INDEX idx_factura_estado ON billing.facturas_transporte(tenant_id, estado);
|
||||
CREATE INDEX idx_factura_fecha ON billing.facturas_transporte(fecha_emision);
|
||||
CREATE UNIQUE INDEX idx_factura_folio ON billing.facturas_transporte(tenant_id, serie, folio);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: lineas_factura
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.lineas_factura (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
factura_id UUID NOT NULL REFERENCES billing.facturas_transporte(id),
|
||||
|
||||
-- Secuencia
|
||||
linea INT NOT NULL,
|
||||
|
||||
-- Concepto
|
||||
descripcion TEXT NOT NULL,
|
||||
clave_producto_sat VARCHAR(10), -- Catálogo SAT
|
||||
unidad_sat VARCHAR(10),
|
||||
|
||||
-- Cantidades
|
||||
cantidad DECIMAL(12, 4) NOT NULL,
|
||||
precio_unitario DECIMAL(15, 4) NOT NULL,
|
||||
descuento DECIMAL(15, 2) DEFAULT 0,
|
||||
importe DECIMAL(15, 2) NOT NULL,
|
||||
|
||||
-- Impuestos
|
||||
iva_tasa DECIMAL(5, 2) DEFAULT 16,
|
||||
iva_monto DECIMAL(15, 2),
|
||||
|
||||
-- Referencia
|
||||
viaje_id UUID,
|
||||
ot_id UUID,
|
||||
recargo_id UUID REFERENCES billing.recargos_catalogo(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_linea_factura ON billing.lineas_factura(factura_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: fuel_surcharge (Índice de combustible)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE billing.fuel_surcharge (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Período
|
||||
fecha_inicio DATE NOT NULL,
|
||||
fecha_fin DATE NOT NULL,
|
||||
|
||||
-- Precios de referencia
|
||||
precio_diesel_referencia DECIMAL(10, 4), -- Precio base
|
||||
precio_diesel_actual DECIMAL(10, 4),
|
||||
|
||||
-- Surcharge
|
||||
porcentaje_surcharge DECIMAL(5, 2) NOT NULL,
|
||||
|
||||
-- Estado
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_fuel_surcharge_fecha ON billing.fuel_surcharge(tenant_id, fecha_inicio, fecha_fin);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE billing.lanes ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE billing.tarifas ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE billing.recargos_catalogo ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE billing.facturas_transporte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE billing.lineas_factura ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE billing.fuel_surcharge ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_lanes ON billing.lanes
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_tarifas ON billing.tarifas
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_recargos ON billing.recargos_catalogo
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_facturas ON billing.facturas_transporte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_lineas ON billing.lineas_factura
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_fuel ON billing.fuel_surcharge
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE billing.lanes IS 'Rutas origen-destino para tarifas';
|
||||
COMMENT ON TABLE billing.tarifas IS 'Catálogo de tarifas de transporte';
|
||||
COMMENT ON TABLE billing.recargos_catalogo IS 'Catálogo de recargos aplicables';
|
||||
COMMENT ON TABLE billing.facturas_transporte IS 'Facturas emitidas a clientes';
|
||||
COMMENT ON TABLE billing.lineas_factura IS 'Detalle de líneas de factura';
|
||||
COMMENT ON TABLE billing.fuel_surcharge IS 'Índices de fuel surcharge por período';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL BILLING
|
||||
-- =============================================================================
|
||||
440
ddl/08-compliance-schema-ddl.sql
Normal file
440
ddl/08-compliance-schema-ddl.sql
Normal file
@ -0,0 +1,440 @@
|
||||
-- =============================================================================
|
||||
-- ERP TRANSPORTISTAS - Schema Compliance DDL
|
||||
-- =============================================================================
|
||||
-- Archivo: 08-compliance-schema-ddl.sql
|
||||
-- Version: 1.0.0
|
||||
-- Fecha: 2026-01-25
|
||||
-- Descripcion: Carta Porte CFDI 3.1, HOS, inspecciones, NOM-087/068
|
||||
-- =============================================================================
|
||||
|
||||
-- =============================================================================
|
||||
-- TIPOS ENUMERADOS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE compliance.estado_carta_porte AS ENUM (
|
||||
'BORRADOR',
|
||||
'VALIDADA',
|
||||
'TIMBRADA',
|
||||
'CANCELADA'
|
||||
);
|
||||
|
||||
CREATE TYPE compliance.tipo_cfdi_carta_porte AS ENUM (
|
||||
'INGRESO', -- Servicio de transporte
|
||||
'TRASLADO' -- Traslado propio
|
||||
);
|
||||
|
||||
CREATE TYPE compliance.estado_hos AS ENUM (
|
||||
'DRIVING', -- Conduciendo
|
||||
'ON_DUTY', -- En servicio (no conduciendo)
|
||||
'SLEEPER', -- En litera
|
||||
'OFF_DUTY' -- Fuera de servicio
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: cartas_porte
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.cartas_porte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Viaje relacionado
|
||||
viaje_id UUID NOT NULL,
|
||||
|
||||
-- CFDI
|
||||
tipo_cfdi compliance.tipo_cfdi_carta_porte NOT NULL,
|
||||
version_carta_porte VARCHAR(10) DEFAULT '3.1',
|
||||
|
||||
-- Identificación CFDI
|
||||
serie VARCHAR(10),
|
||||
folio VARCHAR(20),
|
||||
uuid_cfdi UUID,
|
||||
fecha_timbrado TIMESTAMPTZ,
|
||||
|
||||
-- Emisor
|
||||
emisor_rfc VARCHAR(13) NOT NULL,
|
||||
emisor_nombre VARCHAR(200) NOT NULL,
|
||||
emisor_regimen_fiscal VARCHAR(10),
|
||||
|
||||
-- Receptor (para Ingreso) / Propietario (para Traslado)
|
||||
receptor_rfc VARCHAR(13) NOT NULL,
|
||||
receptor_nombre VARCHAR(200) NOT NULL,
|
||||
receptor_uso_cfdi VARCHAR(10),
|
||||
receptor_domicilio_fiscal_cp VARCHAR(10),
|
||||
|
||||
-- Totales CFDI
|
||||
subtotal DECIMAL(15, 2),
|
||||
total DECIMAL(15, 2),
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
|
||||
-- Datos transporte federal
|
||||
transporte_internacional BOOLEAN DEFAULT FALSE,
|
||||
entrada_salida_merc VARCHAR(10), -- 'Entrada' o 'Salida'
|
||||
pais_origen_destino VARCHAR(3),
|
||||
|
||||
-- Datos específicos autotransporte
|
||||
permiso_sct VARCHAR(50),
|
||||
num_permiso_sct VARCHAR(50),
|
||||
config_vehicular VARCHAR(10), -- C2, C3, T3S2, etc.
|
||||
peso_bruto_total DECIMAL(12, 3),
|
||||
unidad_peso VARCHAR(10) DEFAULT 'KGM',
|
||||
num_total_mercancias INT,
|
||||
|
||||
-- Seguro
|
||||
asegura_resp_civil VARCHAR(100),
|
||||
poliza_resp_civil VARCHAR(50),
|
||||
asegura_med_ambiente VARCHAR(100),
|
||||
poliza_med_ambiente VARCHAR(50),
|
||||
asegura_carga VARCHAR(100),
|
||||
poliza_carga VARCHAR(50),
|
||||
prima_seguro DECIMAL(15, 2),
|
||||
|
||||
-- Estado
|
||||
estado compliance.estado_carta_porte DEFAULT 'BORRADOR',
|
||||
|
||||
-- XML y PDF
|
||||
xml_cfdi TEXT,
|
||||
xml_carta_porte TEXT,
|
||||
pdf_url TEXT,
|
||||
qr_url TEXT,
|
||||
|
||||
-- Cancelación
|
||||
fecha_cancelacion TIMESTAMPTZ,
|
||||
motivo_cancelacion TEXT,
|
||||
uuid_sustitucion UUID,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_carta_porte_tenant ON compliance.cartas_porte(tenant_id);
|
||||
CREATE INDEX idx_carta_porte_viaje ON compliance.cartas_porte(viaje_id);
|
||||
CREATE INDEX idx_carta_porte_uuid ON compliance.cartas_porte(uuid_cfdi);
|
||||
CREATE INDEX idx_carta_porte_estado ON compliance.cartas_porte(tenant_id, estado);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: ubicaciones_carta_porte
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.ubicaciones_carta_porte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carta_porte_id UUID NOT NULL REFERENCES compliance.cartas_porte(id),
|
||||
|
||||
-- Tipo
|
||||
tipo_ubicacion VARCHAR(10) NOT NULL, -- 'Origen' o 'Destino'
|
||||
|
||||
-- ID Ubicación (catálogo SAT)
|
||||
id_ubicacion VARCHAR(10),
|
||||
|
||||
-- RFC
|
||||
rfc_remitente_destinatario VARCHAR(13),
|
||||
nombre_remitente_destinatario VARCHAR(200),
|
||||
|
||||
-- Domicilio
|
||||
pais VARCHAR(3) DEFAULT 'MEX',
|
||||
estado VARCHAR(10), -- Clave SAT
|
||||
municipio VARCHAR(10), -- Clave SAT
|
||||
localidad VARCHAR(10),
|
||||
codigo_postal VARCHAR(10) NOT NULL,
|
||||
colonia VARCHAR(10),
|
||||
calle VARCHAR(200),
|
||||
numero_exterior VARCHAR(50),
|
||||
numero_interior VARCHAR(50),
|
||||
referencia VARCHAR(500),
|
||||
|
||||
-- Fechas
|
||||
fecha_hora_salida_llegada TIMESTAMPTZ,
|
||||
|
||||
-- Distancia
|
||||
distancia_recorrida DECIMAL(10, 2), -- Solo para destinos
|
||||
|
||||
-- Secuencia (orden en la ruta)
|
||||
secuencia INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ubicacion_carta ON compliance.ubicaciones_carta_porte(carta_porte_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: mercancias_carta_porte
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.mercancias_carta_porte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carta_porte_id UUID NOT NULL REFERENCES compliance.cartas_porte(id),
|
||||
|
||||
-- Bienes transportados
|
||||
bienes_transp VARCHAR(10) NOT NULL, -- Clave SAT
|
||||
descripcion VARCHAR(1000) NOT NULL,
|
||||
cantidad DECIMAL(14, 3) NOT NULL,
|
||||
clave_unidad VARCHAR(10) NOT NULL, -- Clave SAT
|
||||
unidad VARCHAR(50),
|
||||
|
||||
-- Dimensiones
|
||||
peso_en_kg DECIMAL(14, 3) NOT NULL,
|
||||
largo_cm DECIMAL(10, 2),
|
||||
ancho_cm DECIMAL(10, 2),
|
||||
alto_cm DECIMAL(10, 2),
|
||||
|
||||
-- Valor
|
||||
valor_mercancia DECIMAL(15, 2),
|
||||
moneda VARCHAR(3) DEFAULT 'MXN',
|
||||
|
||||
-- Material peligroso
|
||||
material_peligroso BOOLEAN DEFAULT FALSE,
|
||||
cve_material_peligroso VARCHAR(10),
|
||||
tipo_embalaje VARCHAR(10),
|
||||
descripcion_embalaje VARCHAR(200),
|
||||
|
||||
-- Fracción arancelaria (comercio exterior)
|
||||
fraccion_arancelaria VARCHAR(10),
|
||||
uuid_comercio_ext UUID,
|
||||
|
||||
-- Pedimentos (comercio exterior)
|
||||
pedimentos TEXT[], -- Array de números de pedimento
|
||||
|
||||
-- Guías (paquetería)
|
||||
guias TEXT[], -- Array de números de guía
|
||||
|
||||
-- Secuencia
|
||||
secuencia INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_mercancia_carta ON compliance.mercancias_carta_porte(carta_porte_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: figuras_transporte (Operador, propietario, arrendatario)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.figuras_transporte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carta_porte_id UUID NOT NULL REFERENCES compliance.cartas_porte(id),
|
||||
|
||||
-- Tipo de figura
|
||||
tipo_figura VARCHAR(10) NOT NULL, -- '01'=Operador, '02'=Propietario, '03'=Arrendador
|
||||
|
||||
-- Datos
|
||||
rfc_figura VARCHAR(13),
|
||||
nombre_figura VARCHAR(200),
|
||||
num_licencia VARCHAR(50), -- Solo para operadores
|
||||
|
||||
-- Domicilio (opcional)
|
||||
pais VARCHAR(3),
|
||||
estado VARCHAR(10),
|
||||
codigo_postal VARCHAR(10),
|
||||
calle VARCHAR(200),
|
||||
|
||||
-- Partes transporte (solo para Propietario/Arrendador)
|
||||
partes_transporte JSONB -- Array de {parte_transporte: string}
|
||||
);
|
||||
|
||||
CREATE INDEX idx_figura_carta ON compliance.figuras_transporte(carta_porte_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: autotransporte_carta_porte
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.autotransporte_carta_porte (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
carta_porte_id UUID NOT NULL REFERENCES compliance.cartas_porte(id),
|
||||
|
||||
-- Permiso
|
||||
perm_sct VARCHAR(10) NOT NULL, -- Tipo permiso SAT
|
||||
num_permiso_sct VARCHAR(50) NOT NULL,
|
||||
|
||||
-- Identificación vehicular tractora
|
||||
config_vehicular VARCHAR(10) NOT NULL, -- C2, C3, T3S2, etc.
|
||||
placa_vm VARCHAR(15) NOT NULL,
|
||||
anio_modelo_vm INT,
|
||||
|
||||
-- Remolques (puede haber hasta 2)
|
||||
remolques JSONB -- Array de {sub_tipo_rem, placa}
|
||||
);
|
||||
|
||||
CREATE INDEX idx_autotransporte_carta ON compliance.autotransporte_carta_porte(carta_porte_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: hos_logs (Hours of Service - NOM-087)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.hos_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Operador y viaje
|
||||
operador_id UUID NOT NULL,
|
||||
viaje_id UUID,
|
||||
|
||||
-- Log
|
||||
fecha DATE NOT NULL,
|
||||
hora_inicio TIME NOT NULL,
|
||||
hora_fin TIME,
|
||||
duracion_minutos INT,
|
||||
|
||||
-- Estado
|
||||
estado compliance.estado_hos NOT NULL,
|
||||
|
||||
-- Ubicación
|
||||
latitud DECIMAL(10, 7),
|
||||
longitud DECIMAL(10, 7),
|
||||
ubicacion_descripcion VARCHAR(200),
|
||||
|
||||
-- Odómetro
|
||||
odometro_inicio INT,
|
||||
odometro_fin INT,
|
||||
|
||||
-- Observaciones
|
||||
observaciones TEXT,
|
||||
|
||||
-- Certificado
|
||||
certificado_por_operador BOOLEAN DEFAULT FALSE,
|
||||
certificado_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_hos_operador ON compliance.hos_logs(operador_id);
|
||||
CREATE INDEX idx_hos_fecha ON compliance.hos_logs(tenant_id, fecha);
|
||||
CREATE INDEX idx_hos_viaje ON compliance.hos_logs(viaje_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: hos_resumen_diario
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.hos_resumen_diario (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
operador_id UUID NOT NULL,
|
||||
fecha DATE NOT NULL,
|
||||
|
||||
-- Horas por estado
|
||||
horas_driving DECIMAL(4, 2) DEFAULT 0,
|
||||
horas_on_duty DECIMAL(4, 2) DEFAULT 0,
|
||||
horas_sleeper DECIMAL(4, 2) DEFAULT 0,
|
||||
horas_off_duty DECIMAL(4, 2) DEFAULT 0,
|
||||
horas_totales DECIMAL(4, 2) DEFAULT 0,
|
||||
|
||||
-- Cumplimiento NOM-087
|
||||
horas_conduccion_disponibles DECIMAL(4, 2),
|
||||
horas_servicio_disponibles DECIMAL(4, 2),
|
||||
en_cumplimiento BOOLEAN DEFAULT TRUE,
|
||||
violaciones TEXT[],
|
||||
|
||||
-- Acumulados (ciclo de 7 días)
|
||||
horas_conduccion_ciclo DECIMAL(5, 2),
|
||||
horas_servicio_ciclo DECIMAL(5, 2),
|
||||
|
||||
-- Certificación
|
||||
certificado BOOLEAN DEFAULT FALSE,
|
||||
certificado_fecha TIMESTAMPTZ,
|
||||
|
||||
CONSTRAINT uq_hos_resumen UNIQUE (operador_id, fecha)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_hos_resumen_operador ON compliance.hos_resumen_diario(operador_id);
|
||||
CREATE INDEX idx_hos_resumen_fecha ON compliance.hos_resumen_diario(tenant_id, fecha);
|
||||
|
||||
-- =============================================================================
|
||||
-- TABLA: inspecciones_pre_viaje
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE compliance.inspecciones_pre_viaje (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES public.tenants(id),
|
||||
|
||||
-- Viaje y unidad
|
||||
viaje_id UUID NOT NULL,
|
||||
unidad_id UUID NOT NULL,
|
||||
remolque_id UUID,
|
||||
operador_id UUID NOT NULL,
|
||||
|
||||
-- Fecha
|
||||
fecha_inspeccion TIMESTAMPTZ NOT NULL,
|
||||
|
||||
-- Resultado general
|
||||
aprobada BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Items checklist (JSON con resultados)
|
||||
checklist_items JSONB NOT NULL,
|
||||
-- Ejemplo: [{ "item": "Frenos", "estado": "OK", "observacion": null }, ...]
|
||||
|
||||
-- Defectos encontrados
|
||||
defectos_encontrados TEXT[],
|
||||
defectos_criticos INT DEFAULT 0,
|
||||
defectos_menores INT DEFAULT 0,
|
||||
|
||||
-- Firma
|
||||
firma_operador TEXT, -- Base64
|
||||
firma_fecha TIMESTAMPTZ,
|
||||
|
||||
-- Evidencias
|
||||
fotos JSONB, -- URLs de fotos
|
||||
|
||||
-- Auditoría
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_inspeccion_viaje ON compliance.inspecciones_pre_viaje(viaje_id);
|
||||
CREATE INDEX idx_inspeccion_unidad ON compliance.inspecciones_pre_viaje(unidad_id);
|
||||
CREATE INDEX idx_inspeccion_fecha ON compliance.inspecciones_pre_viaje(tenant_id, fecha_inspeccion);
|
||||
|
||||
-- =============================================================================
|
||||
-- RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE compliance.cartas_porte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.ubicaciones_carta_porte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.mercancias_carta_porte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.figuras_transporte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.autotransporte_carta_porte ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.hos_logs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.hos_resumen_diario ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE compliance.inspecciones_pre_viaje ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY tenant_isolation_cartas ON compliance.cartas_porte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_ubicaciones ON compliance.ubicaciones_carta_porte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_mercancias ON compliance.mercancias_carta_porte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_figuras ON compliance.figuras_transporte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_autotransporte ON compliance.autotransporte_carta_porte
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_hos ON compliance.hos_logs
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_hos_resumen ON compliance.hos_resumen_diario
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_inspecciones ON compliance.inspecciones_pre_viaje
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMENTARIOS
|
||||
-- =============================================================================
|
||||
|
||||
COMMENT ON TABLE compliance.cartas_porte IS 'CFDI con complemento Carta Porte 3.1';
|
||||
COMMENT ON TABLE compliance.ubicaciones_carta_porte IS 'Ubicaciones origen/destino de la carta porte';
|
||||
COMMENT ON TABLE compliance.mercancias_carta_porte IS 'Mercancías transportadas en la carta porte';
|
||||
COMMENT ON TABLE compliance.figuras_transporte IS 'Figuras de transporte (operador, propietario, arrendador)';
|
||||
COMMENT ON TABLE compliance.autotransporte_carta_porte IS 'Datos del autotransporte federal';
|
||||
COMMENT ON TABLE compliance.hos_logs IS 'Registros de horas de servicio (NOM-087)';
|
||||
COMMENT ON TABLE compliance.hos_resumen_diario IS 'Resumen diario de HOS por operador';
|
||||
COMMENT ON TABLE compliance.inspecciones_pre_viaje IS 'Inspecciones pre-viaje de unidades';
|
||||
|
||||
-- =============================================================================
|
||||
-- FIN DDL COMPLIANCE
|
||||
-- =============================================================================
|
||||
Loading…
Reference in New Issue
Block a user