'Habilitado', self::STATUS_DISABLED => 'Deshabilitado', self::STATUS_REMOVED => 'Eliminado', ]; /** * List of names for each status. * @var array */ public static $statusListClass = [ self::STATUS_ENABLED => 'success', self::STATUS_DISABLED => 'warning', self::STATUS_REMOVED => 'danger', ]; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'contact_code', 'name', 'last_name', 'email', 'password', 'profile_photo_path', 'company', 'birth_date', 'hire_date', 'curp', 'nss', 'job_title', 'face_vector', 'rfc', 'nombre_fiscal', 'tipo_persona', 'c_regimen_fiscal', 'domicilio_fiscal', 'c_uso_cfdi', 'is_partner', 'is_employee', 'is_prospect', 'is_customer', 'is_provider', 'is_user', 'status', 'created_by', ]; /** * The attributes that should be hidden for serialization. * * @var array */ protected $hidden = [ 'password', 'remember_token', 'two_factor_recovery_codes', 'two_factor_secret', ]; /** * The accessors to append to the model's array form. * * @var array */ protected $appends = [ 'profile_photo_url', ]; /** * Get the attributes that should be cast. * * @return array */ protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', 'face_vector' => 'array', 'birth_date' => 'date', 'hire_date' => 'date', ]; } /** * Attributes to include in the Audit. * * @var array */ protected $auditInclude = [ 'name', 'email', ]; public function updateProfilePhoto(UploadedFile $image_avatar) { try { // Verificar si el archivo existe if (!file_exists($image_avatar->getRealPath())) throw new \Exception('El archivo no existe en la ruta especificada.'); if (!in_array($image_avatar->getClientOriginalExtension(), ['jpg', 'jpeg', 'png'])) throw new \Exception('El formato del archivo debe ser JPG o PNG.'); // Directorio donde se guardarán los avatares $avatarDisk = self::AVATAR_DISK; $avatarPath = self::PROFILE_PHOTO_DIR; $avatarName = uniqid('avatar_') . '.png'; // Nombre único para el avatar // Crear la instancia de ImageManager $driver = config('image.driver', 'gd'); $manager = new ImageManager($driver); // Crear el directorio si no existe if (!Storage::disk($avatarDisk)->exists($avatarPath)) Storage::disk($avatarDisk)->makeDirectory($avatarPath); // Leer la imagen $image = $manager->read($image_avatar->getRealPath()); // crop the best fitting 5:3 (600x360) ratio and resize to 600x360 pixel $image->cover(self::AVATAR_WIDTH, self::AVATAR_HEIGHT); // Guardar la imagen en el disco de almacenamiento gestionado por Laravel Storage::disk($avatarDisk)->put($avatarPath . $avatarName, $image->toPng(indexed: true)); // Elimina el avatar existente si hay uno $this->deleteProfilePhoto(); // Update the user's profile photo path $this->forceFill([ 'profile_photo_path' => $avatarName, ])->save(); } catch (\Exception $e) { throw new \Exception('Ocurrió un error al actualizar el avatar. ' . $e->getMessage()); } } public function deleteProfilePhoto() { if (!empty($this->profile_photo_path)) { $avatarDisk = self::AVATAR_DISK; Storage::disk($avatarDisk)->delete($this->profile_photo_path); $this->forceFill([ 'profile_photo_path' => null, ])->save(); } } public function getAvatarColor() { // Selecciona un color basado en el id del usuario return self::AVATAR_COLORS[$this->id % count(self::AVATAR_COLORS)]; } public static function getAvatarImage($name, $color, $background, $size) { $avatarDisk = self::AVATAR_DISK; $directory = self::INITIAL_AVATAR_DIR; $initials = self::getInitials($name); $cacheKey = "avatar-{$initials}-{$color}-{$background}-{$size}"; $path = "{$directory}/{$cacheKey}.png"; $storagePath = storage_path("app/public/{$path}"); // Verificar si el avatar ya está en caché if (Storage::disk($avatarDisk)->exists($path)) return response()->file($storagePath); // Crear el avatar $image = self::createAvatarImage($name, $color, $background, $size); // Guardar en el directorio de iniciales Storage::disk($avatarDisk)->put($path, $image->toPng(indexed: true)); // Retornar la imagen directamente return response()->file($storagePath); } private static function createAvatarImage($name, $color, $background, $size) { // Usar la configuración del driver de imagen $driver = config('image.driver', 'gd'); $manager = new ImageManager($driver); $initials = self::getInitials($name); // Crear la imagen con fondo $image = $manager->create($size, $size) ->fill($background); // Escribir texto en la imagen $image->text( $initials, $size / 2, // Centrar horizontalmente $size / 2, // Centrar verticalmente function (FontFactory $font) use ($color, $size) { $font->file(base_path('/modules/Admin/Resources/assets/vendor/fonts/OpenSans/static/OpenSans-Bold.ttf')); $font->size($size * 0.4); $font->color($color); $font->align('center'); $font->valign('middle'); } ); return $image; } public static function getInitials($name) { // Manejar casos de nombres vacíos o nulos if (empty($name)) return 'NA'; // Usar array_map para mayor eficiencia $initials = implode('', array_map(function ($word) { return mb_substr($word, 0, 1); }, explode(' ', $name))); $initials = substr($initials, 0, self::INITIAL_MAX_LENGTH); return strtoupper($initials); } public function getProfilePhotoUrlAttribute() { if ($this->profile_photo_path) return Storage::url(self::PROFILE_PHOTO_DIR . '/' . $this->profile_photo_path); // Generar URL del avatar por iniciales $name = urlencode($this->fullname); $color = ltrim($this->getAvatarColor(), '#'); $background = ltrim(self::AVATAR_BACKGROUND, '#'); $size = (self::AVATAR_WIDTH + self::AVATAR_HEIGHT) / 2; return url("/admin/usuario/avatar?name={$name}&color={$color}&background={$background}&size={$size}"); } public function getFullnameAttribute() { return trim($this->name . ' ' . $this->last_name); } public function getInitialsAttribute() { return self::getInitials($this->fullname); } /** * Envía la notificación de restablecimiento de contraseña. * * @param string $token */ public function sendPasswordResetNotification($token) { // Usar la notificación personalizada $this->notify(new CustomResetPasswordNotification($token)); } /** * Relations */ // User who created this user public function creator() { return $this->belongsTo(self::class, 'created_by'); } // Regimen fiscal public function regimenFiscal() { return $this->belongsTo(RegimenFiscal::class, 'c_regimen_fiscal', 'c_regimen_fiscal'); } // Domicilio fiscal public function domicilioFiscal() { return $this->belongsTo(CodigoPostal::class, 'domicilio_fiscal', 'c_codigo_postal'); } // Uso de CFDI public function usoCfdi() { return $this->belongsTo(UsoCfdi::class, 'c_uso_cfdi', 'c_uso_cfdi'); } /** * Helper methods */ public function isActive() { return $this->status === 1; } public function isPartner() { return $this->is_partner === 1; } public function isEmployee() { return $this->is_employee === 1; } public function isCustomer() { return $this->is_customer === 1; } public function isProvider() { return $this->is_provider === 1; } }