diff --git a/src/modules/gestion-flota/entities/index.ts b/src/modules/gestion-flota/entities/index.ts index 55118e7..89c800b 100644 --- a/src/modules/gestion-flota/entities/index.ts +++ b/src/modules/gestion-flota/entities/index.ts @@ -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'; diff --git a/src/modules/gestion-flota/entities/operador.entity.ts b/src/modules/gestion-flota/entities/operador.entity.ts new file mode 100644 index 0000000..c0efe82 --- /dev/null +++ b/src/modules/gestion-flota/entities/operador.entity.ts @@ -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 : ''}`; + } +} diff --git a/src/modules/gestion-flota/entities/unidad.entity.ts b/src/modules/gestion-flota/entities/unidad.entity.ts new file mode 100644 index 0000000..dceac38 --- /dev/null +++ b/src/modules/gestion-flota/entities/unidad.entity.ts @@ -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; +} diff --git a/src/modules/ordenes-transporte/entities/orden-transporte.entity.ts b/src/modules/ordenes-transporte/entities/orden-transporte.entity.ts new file mode 100644 index 0000000..f0f5d96 --- /dev/null +++ b/src/modules/ordenes-transporte/entities/orden-transporte.entity.ts @@ -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; +} diff --git a/src/modules/partners/entities/partner.entity.ts b/src/modules/partners/entities/partner.entity.ts index 3173892..a6d063e 100644 --- a/src/modules/partners/entities/partner.entity.ts +++ b/src/modules/partners/entities/partner.entity.ts @@ -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 }) diff --git a/src/modules/tracking/entities/evento-tracking.entity.ts b/src/modules/tracking/entities/evento-tracking.entity.ts new file mode 100644 index 0000000..f2091b2 --- /dev/null +++ b/src/modules/tracking/entities/evento-tracking.entity.ts @@ -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; + + // 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; + + // Observaciones + @Column({ type: 'text', nullable: true }) + observaciones: string; +} diff --git a/src/modules/tracking/entities/geocerca.entity.ts b/src/modules/tracking/entities/geocerca.entity.ts new file mode 100644 index 0000000..f7c8cac --- /dev/null +++ b/src/modules/tracking/entities/geocerca.entity.ts @@ -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; +} diff --git a/src/modules/tracking/entities/index.ts b/src/modules/tracking/entities/index.ts index 7967be4..771b9ea 100644 --- a/src/modules/tracking/entities/index.ts +++ b/src/modules/tracking/entities/index.ts @@ -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