erp-transportistas-database-v2/ddl/08-compliance-schema-ddl.sql
Adrian Flores Cortes 7a91823784 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>
2026-01-25 10:26:20 -06:00

441 lines
14 KiB
SQL

-- =============================================================================
-- 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
-- =============================================================================