feat: Add transport-specific entities and adapt modules

Adapted modules (Phase 1):
- partners: Added TipoPartnerTransporte, UbicacionFrecuente, RequisitoSeguridad
- partners: Added codigoSct, permisoSct, ubicacionesFrecuentes, requisitosSeguridad

New entities (Phase 2-3):
- gestion-flota: Unidad, Operador with TipoUnidad, EstadoUnidad enums
- ordenes-transporte: OrdenTransporte with EstadoOrden, TipoCarga, ModalidadServicio
- tracking: EventoTracking, Geocerca with TipoEvento, FuenteEvento, TipoGeocerca

All entities include:
- Multi-tenancy (tenant_id)
- Full audit columns (created_at/by, updated_at/by)
- Proper indexes for common queries

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-01-25 10:26:03 -06:00
parent 95c6b58449
commit 3cc989cf30
8 changed files with 929 additions and 4 deletions

View File

@ -1,3 +1,13 @@
/**
* Gestion Flota Entities
* Schema: fleet
*/
// Entities de Flota
export * from './unidad.entity';
export * from './operador.entity';
// Entities heredadas de products (para refacciones)
export { ProductCategory } from './product-category.entity';
export { Product } from './product.entity';
export { ProductPrice } from './product-price.entity';

View File

@ -0,0 +1,203 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { Unidad } from './unidad.entity';
/**
* Tipo de Licencia
*/
export enum TipoLicencia {
A = 'A', // Motociclista
B = 'B', // Automovilista particular
C = 'C', // Chofer particular
D = 'D', // Chofer público pasajeros
E = 'E', // Chofer público carga
F = 'F', // Federal (SCT)
}
/**
* Estado del Operador
*/
export enum EstadoOperador {
ACTIVO = 'ACTIVO',
EN_VIAJE = 'EN_VIAJE',
DESCANSO = 'DESCANSO',
VACACIONES = 'VACACIONES',
INCAPACIDAD = 'INCAPACIDAD',
SUSPENDIDO = 'SUSPENDIDO',
BAJA = 'BAJA',
}
@Entity({ schema: 'fleet', name: 'operadores' })
@Index('idx_operador_tenant', ['tenantId'])
@Index('idx_operador_estado', ['tenantId', 'estado'])
export class Operador {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// Identificación
@Column({ name: 'numero_empleado', type: 'varchar', length: 20 })
numeroEmpleado: string;
@Column({ type: 'varchar', length: 100 })
nombre: string;
@Column({ name: 'apellido_paterno', type: 'varchar', length: 100 })
apellidoPaterno: string;
@Column({ name: 'apellido_materno', type: 'varchar', length: 100, nullable: true })
apellidoMaterno: string;
// Documentos de identidad
@Column({ type: 'varchar', length: 18, nullable: true })
curp: string;
@Column({ type: 'varchar', length: 13, nullable: true })
rfc: string;
@Column({ type: 'varchar', length: 15, nullable: true })
nss: string;
// Contacto
@Column({ type: 'varchar', length: 30, nullable: true })
telefono: string;
@Column({ name: 'telefono_emergencia', type: 'varchar', length: 30, nullable: true })
telefonoEmergencia: string;
@Column({ type: 'varchar', length: 255, nullable: true })
email: string;
// Dirección
@Column({ type: 'text', nullable: true })
direccion: string;
@Column({ name: 'codigo_postal', type: 'varchar', length: 10, nullable: true })
codigoPostal: string;
@Column({ type: 'varchar', length: 100, nullable: true })
ciudad: string;
@Column({ type: 'varchar', length: 100, nullable: true })
estadoResidencia: string;
// Datos de nacimiento
@Column({ name: 'fecha_nacimiento', type: 'date', nullable: true })
fechaNacimiento: Date;
@Column({ name: 'lugar_nacimiento', type: 'varchar', length: 100, nullable: true })
lugarNacimiento: string;
@Column({ type: 'varchar', length: 50, default: 'Mexicana' })
nacionalidad: string;
// Licencia de conducir
@Column({ name: 'tipo_licencia', type: 'enum', enum: TipoLicencia, nullable: true })
tipoLicencia: TipoLicencia;
@Column({ name: 'numero_licencia', type: 'varchar', length: 30, nullable: true })
numeroLicencia: string;
@Column({ name: 'licencia_vigencia', type: 'date', nullable: true })
licenciaVigencia: Date;
@Column({ name: 'licencia_estado_expedicion', type: 'varchar', length: 50, nullable: true })
licenciaEstadoExpedicion: string;
// Certificaciones
@Column({ name: 'certificado_fisico_vigencia', type: 'date', nullable: true })
certificadoFisicoVigencia: Date;
@Column({ name: 'antidoping_vigencia', type: 'date', nullable: true })
antidopingVigencia: Date;
@Column({ name: 'capacitacion_materiales_peligrosos', type: 'boolean', default: false })
capacitacionMaterialesPeligrosos: boolean;
@Column({ name: 'capacitacion_mp_vigencia', type: 'date', nullable: true })
capacitacionMpVigencia: Date;
// Estado
@Column({ type: 'enum', enum: EstadoOperador, default: EstadoOperador.ACTIVO })
estado: EstadoOperador;
// Unidad asignada
@Column({ name: 'unidad_asignada_id', type: 'uuid', nullable: true })
unidadAsignadaId: string;
@ManyToOne(() => Unidad)
@JoinColumn({ name: 'unidad_asignada_id' })
unidadAsignada: Unidad;
// Métricas de desempeño
@Column({ type: 'decimal', precision: 3, scale: 2, default: 5.00 })
calificacion: number;
@Column({ name: 'total_viajes', type: 'int', default: 0 })
totalViajes: number;
@Column({ name: 'total_km', type: 'int', default: 0 })
totalKm: number;
@Column({ type: 'int', default: 0 })
incidentes: number;
// Datos bancarios
@Column({ type: 'varchar', length: 100, nullable: true })
banco: string;
@Column({ name: 'cuenta_bancaria', type: 'varchar', length: 30, nullable: true })
cuentaBancaria: string;
@Column({ type: 'varchar', length: 18, nullable: true })
clabe: string;
// Salario
@Column({ name: 'salario_base', type: 'decimal', precision: 12, scale: 2, nullable: true })
salarioBase: number;
@Column({ name: 'tipo_pago', type: 'varchar', length: 20, nullable: true })
tipoPago: string;
// Fechas
@Column({ name: 'fecha_ingreso', type: 'date', nullable: true })
fechaIngreso: Date;
@Column({ name: 'fecha_baja', type: 'date', nullable: true })
fechaBaja: Date;
@Column({ name: 'motivo_baja', type: 'text', nullable: true })
motivoBaja: string;
// Activo
@Column({ type: 'boolean', default: true })
activo: boolean;
// Auditoría
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@Column({ name: 'created_by_id', type: 'uuid' })
createdById: string;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
@Column({ name: 'updated_by_id', type: 'uuid', nullable: true })
updatedById: string;
// Helper para nombre completo
get nombreCompleto(): string {
return `${this.nombre} ${this.apellidoPaterno}${this.apellidoMaterno ? ' ' + this.apellidoMaterno : ''}`;
}
}

View File

@ -0,0 +1,191 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
OneToMany,
} from 'typeorm';
/**
* Tipo de Unidad
*/
export enum TipoUnidad {
TRACTORA = 'TRACTORA',
REMOLQUE = 'REMOLQUE',
CAJA_SECA = 'CAJA_SECA',
CAJA_REFRIGERADA = 'CAJA_REFRIGERADA',
PLATAFORMA = 'PLATAFORMA',
TANQUE = 'TANQUE',
PORTACONTENEDOR = 'PORTACONTENEDOR',
TORTON = 'TORTON',
RABON = 'RABON',
CAMIONETA = 'CAMIONETA',
}
/**
* Estado de Unidad
*/
export enum EstadoUnidad {
DISPONIBLE = 'DISPONIBLE',
EN_VIAJE = 'EN_VIAJE',
EN_TALLER = 'EN_TALLER',
BLOQUEADA = 'BLOQUEADA',
BAJA = 'BAJA',
}
@Entity({ schema: 'fleet', name: 'unidades' })
@Index('idx_unidad_tenant', ['tenantId'])
@Index('idx_unidad_tipo', ['tenantId', 'tipo'])
@Index('idx_unidad_estado', ['tenantId', 'estado'])
export class Unidad {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// Identificación
@Column({ name: 'numero_economico', type: 'varchar', length: 20 })
numeroEconomico: string;
@Column({ type: 'enum', enum: TipoUnidad })
tipo: TipoUnidad;
// Vehículo
@Column({ type: 'varchar', length: 50, nullable: true })
marca: string;
@Column({ type: 'varchar', length: 50, nullable: true })
modelo: string;
@Column({ type: 'int', nullable: true })
anio: number;
@Column({ type: 'varchar', length: 30, nullable: true })
color: string;
@Column({ name: 'numero_serie', type: 'varchar', length: 50, nullable: true })
numeroSerie: string;
@Column({ name: 'numero_motor', type: 'varchar', length: 50, nullable: true })
numeroMotor: string;
// Placas
@Column({ type: 'varchar', length: 15, nullable: true })
placa: string;
@Column({ name: 'placa_estado', type: 'varchar', length: 50, nullable: true })
placaEstado: string;
// SCT
@Column({ name: 'permiso_sct', type: 'varchar', length: 50, nullable: true })
permisoSct: string;
@Column({ name: 'tipo_permiso_sct', type: 'varchar', length: 10, nullable: true })
tipoPermisoSct: string;
@Column({ name: 'configuracion_vehicular', type: 'varchar', length: 10, nullable: true })
configuracionVehicular: string;
// Capacidades
@Column({ name: 'capacidad_peso_kg', type: 'decimal', precision: 10, scale: 2, nullable: true })
capacidadPesoKg: number;
@Column({ name: 'capacidad_volumen_m3', type: 'decimal', precision: 10, scale: 4, nullable: true })
capacidadVolumenM3: number;
@Column({ name: 'capacidad_pallets', type: 'int', nullable: true })
capacidadPallets: number;
// Combustible
@Column({ name: 'tipo_combustible', type: 'varchar', length: 20, nullable: true })
tipoCombustible: string;
@Column({ name: 'rendimiento_km_litro', type: 'decimal', precision: 6, scale: 2, nullable: true })
rendimientoKmLitro: number;
@Column({ name: 'capacidad_tanque_litros', type: 'decimal', precision: 8, scale: 2, nullable: true })
capacidadTanqueLitros: number;
// Odómetro
@Column({ name: 'odometro_actual', type: 'int', default: 0 })
odometroActual: number;
@Column({ name: 'odometro_ultimo_servicio', type: 'int', nullable: true })
odometroUltimoServicio: number;
// GPS
@Column({ name: 'tiene_gps', type: 'boolean', default: false })
tieneGps: boolean;
@Column({ name: 'gps_proveedor', type: 'varchar', length: 50, nullable: true })
gpsProveedor: string;
@Column({ name: 'gps_imei', type: 'varchar', length: 50, nullable: true })
gpsImei: string;
// Estado
@Column({ type: 'enum', enum: EstadoUnidad, default: EstadoUnidad.DISPONIBLE })
estado: EstadoUnidad;
@Column({ name: 'ubicacion_actual_lat', type: 'decimal', precision: 10, scale: 7, nullable: true })
ubicacionActualLat: number;
@Column({ name: 'ubicacion_actual_lng', type: 'decimal', precision: 10, scale: 7, nullable: true })
ubicacionActualLng: number;
@Column({ name: 'ultima_actualizacion_ubicacion', type: 'timestamptz', nullable: true })
ultimaActualizacionUbicacion: Date;
// Propiedad
@Column({ name: 'es_propia', type: 'boolean', default: true })
esPropia: boolean;
@Column({ name: 'propietario_id', type: 'uuid', nullable: true })
propietarioId: string;
// Costos
@Column({ name: 'costo_adquisicion', type: 'decimal', precision: 15, scale: 2, nullable: true })
costoAdquisicion: number;
@Column({ name: 'fecha_adquisicion', type: 'date', nullable: true })
fechaAdquisicion: Date;
@Column({ name: 'valor_actual', type: 'decimal', precision: 15, scale: 2, nullable: true })
valorActual: number;
// Fechas importantes
@Column({ name: 'fecha_verificacion_proxima', type: 'date', nullable: true })
fechaVerificacionProxima: Date;
@Column({ name: 'fecha_poliza_vencimiento', type: 'date', nullable: true })
fechaPolizaVencimiento: Date;
@Column({ name: 'fecha_permiso_vencimiento', type: 'date', nullable: true })
fechaPermisoVencimiento: Date;
// Activo
@Column({ type: 'boolean', default: true })
activo: boolean;
@Column({ name: 'fecha_baja', type: 'date', nullable: true })
fechaBaja: Date;
@Column({ name: 'motivo_baja', type: 'text', nullable: true })
motivoBaja: string;
// Auditoría
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@Column({ name: 'created_by_id', type: 'uuid' })
createdById: string;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
@Column({ name: 'updated_by_id', type: 'uuid', nullable: true })
updatedById: string;
}

View File

@ -0,0 +1,231 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
ManyToOne,
JoinColumn,
} from 'typeorm';
/**
* Estado de la Orden de Transporte
*/
export enum EstadoOrden {
BORRADOR = 'BORRADOR',
CONFIRMADA = 'CONFIRMADA',
ASIGNADA = 'ASIGNADA',
EN_PROCESO = 'EN_PROCESO',
COMPLETADA = 'COMPLETADA',
FACTURADA = 'FACTURADA',
CANCELADA = 'CANCELADA',
}
/**
* Tipo de Carga
*/
export enum TipoCarga {
GENERAL = 'GENERAL',
PELIGROSA = 'PELIGROSA',
REFRIGERADA = 'REFRIGERADA',
SOBREDIMENSIONADA = 'SOBREDIMENSIONADA',
GRANEL = 'GRANEL',
LIQUIDOS = 'LIQUIDOS',
CONTENEDOR = 'CONTENEDOR',
AUTOMOVILES = 'AUTOMOVILES',
}
/**
* Modalidad de Servicio
*/
export enum ModalidadServicio {
FTL = 'FTL', // Full Truck Load
LTL = 'LTL', // Less Than Truck Load
DEDICADO = 'DEDICADO',
EXPRESS = 'EXPRESS',
CONSOLIDADO = 'CONSOLIDADO',
}
@Entity({ schema: 'transport', name: 'ordenes_transporte' })
@Index('idx_ot_tenant', ['tenantId'])
@Index('idx_ot_estado', ['tenantId', 'estado'])
@Index('idx_ot_shipper', ['tenantId', 'shipperId'])
export class OrdenTransporte {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// Identificación
@Column({ type: 'varchar', length: 50 })
codigo: string;
@Column({ name: 'referencia_cliente', type: 'varchar', length: 100, nullable: true })
referenciaCliente: string;
// Cliente (Shipper)
@Column({ name: 'shipper_id', type: 'uuid' })
shipperId: string;
@Column({ name: 'shipper_nombre', type: 'varchar', length: 200 })
shipperNombre: string;
// Destinatario (Consignee)
@Column({ name: 'consignee_id', type: 'uuid' })
consigneeId: string;
@Column({ name: 'consignee_nombre', type: 'varchar', length: 200 })
consigneeNombre: string;
// Origen
@Column({ name: 'origen_direccion', type: 'text' })
origenDireccion: string;
@Column({ name: 'origen_codigo_postal', type: 'varchar', length: 10, nullable: true })
origenCodigoPostal: string;
@Column({ name: 'origen_ciudad', type: 'varchar', length: 100, nullable: true })
origenCiudad: string;
@Column({ name: 'origen_estado', type: 'varchar', length: 100, nullable: true })
origenEstado: string;
@Column({ name: 'origen_latitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
origenLatitud: number;
@Column({ name: 'origen_longitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
origenLongitud: number;
@Column({ name: 'origen_contacto', type: 'varchar', length: 200, nullable: true })
origenContacto: string;
@Column({ name: 'origen_telefono', type: 'varchar', length: 30, nullable: true })
origenTelefono: string;
// Destino
@Column({ name: 'destino_direccion', type: 'text' })
destinoDireccion: string;
@Column({ name: 'destino_codigo_postal', type: 'varchar', length: 10, nullable: true })
destinoCodigoPostal: string;
@Column({ name: 'destino_ciudad', type: 'varchar', length: 100, nullable: true })
destinoCiudad: string;
@Column({ name: 'destino_estado', type: 'varchar', length: 100, nullable: true })
destinoEstado: string;
@Column({ name: 'destino_latitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
destinoLatitud: number;
@Column({ name: 'destino_longitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
destinoLongitud: number;
@Column({ name: 'destino_contacto', type: 'varchar', length: 200, nullable: true })
destinoContacto: string;
@Column({ name: 'destino_telefono', type: 'varchar', length: 30, nullable: true })
destinoTelefono: string;
// Fechas programadas
@Column({ name: 'fecha_recoleccion_programada', type: 'timestamptz', nullable: true })
fechaRecoleccionProgramada: Date;
@Column({ name: 'fecha_entrega_programada', type: 'timestamptz', nullable: true })
fechaEntregaProgramada: Date;
// Carga
@Column({ name: 'tipo_carga', type: 'enum', enum: TipoCarga, default: TipoCarga.GENERAL })
tipoCarga: TipoCarga;
@Column({ name: 'descripcion_carga', type: 'text', nullable: true })
descripcionCarga: string;
@Column({ name: 'peso_kg', type: 'decimal', precision: 12, scale: 2, nullable: true })
pesoKg: number;
@Column({ name: 'volumen_m3', type: 'decimal', precision: 12, scale: 4, nullable: true })
volumenM3: number;
@Column({ type: 'int', nullable: true })
piezas: number;
@Column({ type: 'int', nullable: true })
pallets: number;
@Column({ name: 'valor_declarado', type: 'decimal', precision: 15, scale: 2, nullable: true })
valorDeclarado: number;
// Requisitos
@Column({ name: 'requiere_temperatura', type: 'boolean', default: false })
requiereTemperatura: boolean;
@Column({ name: 'temperatura_min', type: 'decimal', precision: 5, scale: 2, nullable: true })
temperaturaMin: number;
@Column({ name: 'temperatura_max', type: 'decimal', precision: 5, scale: 2, nullable: true })
temperaturaMax: number;
@Column({ name: 'requiere_gps', type: 'boolean', default: false })
requiereGps: boolean;
@Column({ name: 'requiere_escolta', type: 'boolean', default: false })
requiereEscolta: boolean;
@Column({ name: 'instrucciones_especiales', type: 'text', nullable: true })
instruccionesEspeciales: string;
// Servicio y tarifa
@Column({ name: 'modalidad_servicio', type: 'enum', enum: ModalidadServicio, default: ModalidadServicio.FTL })
modalidadServicio: ModalidadServicio;
@Column({ name: 'tarifa_id', type: 'uuid', nullable: true })
tarifaId: string;
@Column({ name: 'tarifa_base', type: 'decimal', precision: 15, scale: 2, nullable: true })
tarifaBase: number;
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
recargos: number;
@Column({ type: 'decimal', precision: 15, scale: 2, default: 0 })
descuentos: number;
@Column({ type: 'decimal', precision: 15, scale: 2, nullable: true })
subtotal: number;
@Column({ type: 'decimal', precision: 15, scale: 2, nullable: true })
iva: number;
@Column({ type: 'decimal', precision: 15, scale: 2, nullable: true })
total: number;
// Estado
@Column({ type: 'enum', enum: EstadoOrden, default: EstadoOrden.BORRADOR })
estado: EstadoOrden;
// Asignación
@Column({ name: 'viaje_id', type: 'uuid', nullable: true })
viajeId: string;
@Column({ name: 'embarque_id', type: 'uuid', nullable: true })
embarqueId: string;
// Auditoría
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@Column({ name: 'created_by_id', type: 'uuid' })
createdById: string;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
@Column({ name: 'updated_by_id', type: 'uuid', nullable: true })
updatedById: string;
@Column({ name: 'deleted_at', type: 'timestamptz', nullable: true })
deletedAt: Date;
}

View File

@ -11,6 +11,46 @@ import {
JoinColumn,
} from 'typeorm';
/**
* Tipo de Partner en contexto de Transporte
* - SHIPPER: Embarcador/Cliente que envía mercancía
* - CONSIGNEE: Destinatario/Receptor de mercancía
* - CARRIER: Transportista tercero subcontratado
* - BROKER: Intermediario/Agente de carga
* - BOTH: Cliente que es tanto shipper como consignee
*/
export enum TipoPartnerTransporte {
SHIPPER = 'shipper',
CONSIGNEE = 'consignee',
CARRIER = 'carrier',
BROKER = 'broker',
BOTH = 'both',
}
/**
* Ubicación Frecuente para Shippers/Consignees
*/
export interface UbicacionFrecuente {
nombre: string;
direccion: string;
codigoPostal: string;
latitud?: number;
longitud?: number;
horarioRecepcion?: string;
contacto?: string;
telefono?: string;
instrucciones?: string;
}
/**
* Requisitos de Seguridad del Cliente
*/
export interface RequisitoSeguridad {
tipo: 'GPS' | 'SELLO' | 'ESCOLTA' | 'CUSTODIA' | 'CANDADO_SATELITAL' | 'OTRO';
obligatorio: boolean;
descripcion?: string;
}
@Entity({ name: 'partners', schema: 'partners' })
export class Partner {
@PrimaryGeneratedColumn('uuid')
@ -31,11 +71,65 @@ export class Partner {
@Column({ name: 'legal_name', type: 'varchar', length: 200, nullable: true })
legalName: string;
// Tipo de partner
// Tipo de partner genérico (compatibilidad)
@Index()
@Column({ name: 'partner_type', type: 'varchar', length: 20, default: 'customer' })
partnerType: 'customer' | 'supplier' | 'both';
// === CAMPOS ESPECIFICOS DE TRANSPORTE ===
// Tipo de partner en contexto transporte
@Index()
@Column({
name: 'tipo_partner_transporte',
type: 'enum',
enum: TipoPartnerTransporte,
nullable: true
})
tipoPartnerTransporte: TipoPartnerTransporte;
// Código SCT (para carriers/transportistas)
@Column({ name: 'codigo_sct', type: 'varchar', length: 20, nullable: true })
codigoSct: string;
// Número de permiso SCT
@Column({ name: 'permiso_sct', type: 'varchar', length: 50, nullable: true })
permisoSct: string;
// Tipo de permiso SCT (TPAF01, TPAF02, etc.)
@Column({ name: 'tipo_permiso_sct', type: 'varchar', length: 10, nullable: true })
tipoPermisoSct: string;
// Ubicaciones frecuentes (origenes/destinos comunes)
@Column({ name: 'ubicaciones_frecuentes', type: 'jsonb', nullable: true })
ubicacionesFrecuentes: UbicacionFrecuente[];
// Requisitos de seguridad del cliente
@Column({ name: 'requisitos_seguridad', type: 'jsonb', nullable: true })
requisitosSeguridad: RequisitoSeguridad[];
// Horario de operación
@Column({ name: 'horario_operacion', type: 'varchar', length: 100, nullable: true })
horarioOperacion: string;
// Requiere cita para entrega
@Column({ name: 'requiere_cita', type: 'boolean', default: false })
requiereCita: boolean;
// Tiempo promedio de carga/descarga (minutos)
@Column({ name: 'tiempo_carga_descarga', type: 'int', nullable: true })
tiempoCargaDescarga: number;
// SLA de entrega (horas)
@Column({ name: 'sla_entrega_horas', type: 'int', nullable: true })
slaEntregaHoras: number;
// Prioridad del cliente (1-5)
@Column({ name: 'prioridad_cliente', type: 'int', default: 3 })
prioridadCliente: number;
// === FIN CAMPOS TRANSPORTE ===
// Fiscal
@Index()
@Column({ name: 'tax_id', type: 'varchar', length: 20, nullable: true })

View File

@ -0,0 +1,99 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
Index,
} from 'typeorm';
/**
* Tipo de Evento de Tracking
*/
export enum TipoEvento {
SALIDA = 'SALIDA',
ARRIBO_ORIGEN = 'ARRIBO_ORIGEN',
INICIO_CARGA = 'INICIO_CARGA',
FIN_CARGA = 'FIN_CARGA',
ARRIBO_DESTINO = 'ARRIBO_DESTINO',
INICIO_DESCARGA = 'INICIO_DESCARGA',
FIN_DESCARGA = 'FIN_DESCARGA',
ENTREGA_POD = 'ENTREGA_POD',
DESVIO = 'DESVIO',
PARADA = 'PARADA',
INCIDENTE = 'INCIDENTE',
GPS_POSICION = 'GPS_POSICION',
}
/**
* Fuente del Evento
*/
export enum FuenteEvento {
GPS = 'GPS',
APP_OPERADOR = 'APP_OPERADOR',
SISTEMA = 'SISTEMA',
MANUAL = 'MANUAL',
GEOCERCA = 'GEOCERCA',
}
@Entity({ schema: 'tracking', name: 'eventos' })
@Index('idx_evento_viaje', ['viajeId'])
@Index('idx_evento_tipo', ['tenantId', 'tipoEvento'])
@Index('idx_evento_fecha', ['timestampEvento'])
export class EventoTracking {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// Viaje
@Column({ name: 'viaje_id', type: 'uuid' })
viajeId: string;
// Tipo y fuente
@Column({ name: 'tipo_evento', type: 'enum', enum: TipoEvento })
tipoEvento: TipoEvento;
@Column({ type: 'enum', enum: FuenteEvento })
fuente: FuenteEvento;
// Ubicación
@Column({ type: 'decimal', precision: 10, scale: 7, nullable: true })
latitud: number;
@Column({ type: 'decimal', precision: 10, scale: 7, nullable: true })
longitud: number;
@Column({ type: 'text', nullable: true })
direccion: string;
// Timestamp
@Column({ name: 'timestamp_evento', type: 'timestamptz' })
timestampEvento: Date;
@CreateDateColumn({ name: 'timestamp_registro', type: 'timestamptz' })
timestampRegistro: Date;
// Datos específicos del evento
@Column({ type: 'jsonb', nullable: true })
datos: Record<string, any>;
// Parada asociada (si aplica)
@Column({ name: 'parada_id', type: 'uuid', nullable: true })
paradaId: string;
// Usuario/Operador que generó
@Column({ name: 'generado_por_id', type: 'uuid', nullable: true })
generadoPorId: string;
@Column({ name: 'generado_por_tipo', type: 'varchar', length: 20, nullable: true })
generadoPorTipo: string;
// Evidencias
@Column({ type: 'jsonb', nullable: true })
evidencias: Record<string, any>;
// Observaciones
@Column({ type: 'text', nullable: true })
observaciones: string;
}

View File

@ -0,0 +1,95 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
} from 'typeorm';
/**
* Tipo de Geocerca
*/
export enum TipoGeocerca {
CLIENTE = 'CLIENTE',
PROVEEDOR = 'PROVEEDOR',
PATIO = 'PATIO',
ZONA_RIESGO = 'ZONA_RIESGO',
CASETA = 'CASETA',
GASOLINERA = 'GASOLINERA',
PUNTO_CONTROL = 'PUNTO_CONTROL',
OTRO = 'OTRO',
}
@Entity({ schema: 'tracking', name: 'geocercas' })
@Index('idx_geocerca_tenant', ['tenantId'])
@Index('idx_geocerca_tipo', ['tenantId', 'tipo'])
export class Geocerca {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// Identificación
@Column({ type: 'varchar', length: 50 })
codigo: string;
@Column({ type: 'varchar', length: 200 })
nombre: string;
@Column({ type: 'enum', enum: TipoGeocerca })
tipo: TipoGeocerca;
// Geometría
@Column({ name: 'es_circular', type: 'boolean', default: false })
esCircular: boolean;
// Para geocerca circular
@Column({ name: 'centro_latitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
centroLatitud: number;
@Column({ name: 'centro_longitud', type: 'decimal', precision: 10, scale: 7, nullable: true })
centroLongitud: number;
@Column({ name: 'radio_metros', type: 'decimal', precision: 10, scale: 2, nullable: true })
radioMetros: number;
// Para geocerca poligonal (GeoJSON como string)
@Column({ type: 'text', nullable: true })
poligono: string;
// Asociación
@Column({ name: 'cliente_id', type: 'uuid', nullable: true })
clienteId: string;
@Column({ type: 'text', nullable: true })
direccion: string;
// Alertas
@Column({ name: 'alerta_entrada', type: 'boolean', default: true })
alertaEntrada: boolean;
@Column({ name: 'alerta_salida', type: 'boolean', default: true })
alertaSalida: boolean;
@Column({ name: 'tiempo_permanencia_minutos', type: 'int', nullable: true })
tiempoPermanenciaMinutos: number;
// Configuración
@Column({ type: 'varchar', length: 7, default: '#FF0000' })
color: string;
@Column({ type: 'boolean', default: true })
activa: boolean;
// Auditoría
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@Column({ name: 'created_by_id', type: 'uuid' })
createdById: string;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
}

View File

@ -1,8 +1,10 @@
/**
* Tracking Entities
* Schema: tracking
*/
// TODO: Implement entities
// - evento-tracking.entity.ts
// - geocerca.entity.ts
export * from './evento-tracking.entity';
export * from './geocerca.entity';
// TODO: Implement remaining entities
// - alerta.entity.ts
// - posicion-gps.entity.ts