user(); if (!$user) { return false; } $managerRoles = [ 'App\Models\Organization' => "Organization\\{$activeId}\\organization-manager", 'App\Models\Bank' => "Bank\\{$activeId}\\bank-manager", ]; if (!isset($managerRoles[$activeType])) { return false; } return $user->hasRole($managerRoles[$activeType]); } } /** * Get the human-readable role label (English DB key) for a profile + web user combination. * Returns a string suitable for passing to __() at render time. * Stored in session as 'activeProfileRoleLabel' at switch time. */ if (!function_exists('getProfileRoleLabel')) { function getProfileRoleLabel($profile, ?\App\Models\User $webUser = null): string { if (!$profile) { return ''; } $type = get_class($profile); if ($type === \App\Models\User::class) { return 'User'; } if ($type === \App\Models\Admin::class) { // Look up the Admin role the web user holds for this admin profile if ($webUser) { $prefix = "Admin\\{$profile->id}\\"; $spatieRole = $webUser->roles->first(fn($r) => str_starts_with($r->name, $prefix)); if ($spatieRole) { $roleName = str_replace($prefix, '', $spatieRole->name); $adminRole = \App\Models\AdminRole::where('name', $roleName)->first(); if ($adminRole?->label) { return $adminRole->label; } } } return 'Administrator'; } if ($type === \App\Models\Organization::class) { if ($webUser) { $prefix = "Organization\\{$profile->id}\\"; $spatieRole = $webUser->roles->first(fn($r) => str_starts_with($r->name, $prefix)); if ($spatieRole) { $roleName = str_replace($prefix, '', $spatieRole->name); $orgRole = \App\Models\OrganizationRole::where('name', $roleName)->first(); if ($orgRole?->label) { return $orgRole->label; } } } return 'Organization Manager'; } if ($type === \App\Models\Bank::class) { if ($webUser) { $prefix = "Bank\\{$profile->id}\\"; $spatieRole = $webUser->roles->first(fn($r) => str_starts_with($r->name, $prefix)); if ($spatieRole) { $roleName = str_replace($prefix, '', $spatieRole->name); $orgRole = \App\Models\OrganizationRole::where('name', $roleName)->first(); if ($orgRole?->label) { return $orgRole->label; } } } return 'Bank Manager'; } return ''; } } /** * Build the session key for the active profile's location hierarchy. * Keyed by both profile type and ID so switching profiles never serves stale data. */ if (!function_exists('activeProfileLocationSessionKey')) { function activeProfileLocationSessionKey(): ?string { $profileType = Session::get('activeProfileType'); $profileId = Session::get('activeProfileId'); if (!$profileType || !$profileId) { return null; } return 'profile_location_hierarchy_' . class_basename($profileType) . '_' . $profileId; } } /** * Get the active profile's resolved location hierarchy, using the session as a cache. * Returns [] if no location is set. */ if (!function_exists('getActiveProfileLocationHierarchy')) { function getActiveProfileLocationHierarchy(): array { $key = activeProfileLocationSessionKey(); if (!$key) { return []; } if (Session::has($key)) { return Session::get($key); } try { $activeProfile = getActiveProfile(); if (!$activeProfile) { \Illuminate\Support\Facades\Log::debug('getActiveProfileLocationHierarchy: no active profile'); return []; } if (!$activeProfile->locations) { \Illuminate\Support\Facades\Log::debug('getActiveProfileLocationHierarchy: profile has no locations relation'); return []; } $location = $activeProfile->locations->first(); if (!$location) { \Illuminate\Support\Facades\Log::debug('getActiveProfileLocationHierarchy: no location record found', ['profile_id' => $activeProfile->id]); return []; } // Resolve full hierarchy once to sync missing IDs, then extract only what's needed. // We store a flat array of IDs and names — no model objects — to keep the session small. $hierarchy = $location->getCompleteHierarchy(); $locale = app()->getLocale(); \Illuminate\Support\Facades\Log::debug('getActiveProfileLocationHierarchy: hierarchy resolved', [ 'country_id' => $hierarchy['country']->id ?? null, 'division_id' => $hierarchy['division']->id ?? null, 'city_id' => $hierarchy['city']->id ?? null, 'district_id' => $hierarchy['district']->id ?? null, 'locale' => $locale, ]); $resolveName = function ($model) use ($locale) { if (!$model) return null; if ($model->translations && $model->translations->count() > 0) { $t = $model->translations->where('locale', $locale)->first(); if ($t && $t->name) return $t->name; } return $model->name ?? null; }; $flat = [ 'country_id' => $hierarchy['country']->id ?? null, 'division_id' => $hierarchy['division']->id ?? null, 'city_id' => $hierarchy['city']->id ?? null, 'district_id' => $hierarchy['district']->id ?? null, 'country_name' => $resolveName($hierarchy['country'] ?? null), 'division_name' => $resolveName($hierarchy['division'] ?? null), 'city_name' => $resolveName($hierarchy['city'] ?? null), 'district_name' => $resolveName($hierarchy['district'] ?? null), ]; \Illuminate\Support\Facades\Log::debug('getActiveProfileLocationHierarchy: storing in session', ['key' => $key, 'flat' => $flat]); Session::put($key, $flat); return $flat; } catch (\Exception $e) { \Illuminate\Support\Facades\Log::warning('getActiveProfileLocationHierarchy failed: ' . $e->getMessage()); return []; } } } /** * Invalidate the session-cached location hierarchy for the active profile. * Call this immediately after saving a profile's location. */ if (!function_exists('invalidateActiveProfileLocationCache')) { function invalidateActiveProfileLocationCache(): void { $key = activeProfileLocationSessionKey(); if ($key) { Session::forget($key); } } } /** * Check if the active viewer can see a bank of a given level for a specific context. * * Reads `banks.visibility.{level}.{viewerType}.{visibilityKey}` from the platform config. * Returns true (visible) when the config key is absent — opt-in hiding only. * * @param int $bankLevel The bank's level (0, 1, 2, …) * @param string $visibilityKey One of: profile_visible, search_visible, messenger_visible, payment_visible * @param string|null $viewerType Explicit viewer type override; defaults to active profile type or 'guest' * @return bool */ if (!function_exists('canViewerSeeBankLevel')) { function canViewerSeeBankLevel(?int $bankLevel, string $visibilityKey, ?string $viewerType = null): bool { if ($bankLevel === null) { return true; } if ($viewerType === null) { $viewerType = getActiveProfileType() ?? 'guest'; } $configured = timebank_config("banks.visibility.{$bankLevel}.{$viewerType}.{$visibilityKey}"); // If the config key is absent, default to visible (safe fallback) if ($configured === null) { return true; } return (bool) $configured; } } /** * Check if the authenticated web user has multiple profiles (organizations, banks, admins). * Returns true if user has 2+ profiles total (User + any elevated profiles). * * @return bool */ if (!function_exists('userHasMultipleProfiles')) { function userHasMultipleProfiles(): bool { $user = Auth::guard('web')->user(); if (!$user) { return false; } $profileCount = 0; // User profile counts as 1 $profileCount++; // Add organizations $organizations = $user->organizations() ->where(fn($q) => $q->whereNull('deleted_at')->orWhere('deleted_at', '>', now())) ->count(); $profileCount += $organizations; // Add banks $banks = $user->banksManaged() ->where(fn($q) => $q->whereNull('deleted_at')->orWhere('deleted_at', '>', now())) ->count(); $profileCount += $banks; // Add admins $admins = $user->admins() ->where(fn($q) => $q->whereNull('deleted_at')->orWhere('deleted_at', '>', now())) ->count(); $profileCount += $admins; return $profileCount >= 2; } } /** * Resolve the correct translation key for the "I/we just transferred..." * chat message sent after a payment, based on the SENDER's profile type. * Organizations and Banks are plural entities ("we"); Users are singular ("I"). * * @param mixed $sender The Eloquent model that sent the payment (User/Organization/Bank). * @return string */ if (!function_exists('payChatMessageKey')) { function payChatMessageKey($sender): string { if ($sender instanceof \App\Models\Organization || $sender instanceof \App\Models\Bank) { return 'messages.pay_chat_message_plural'; } return 'messages.pay_chat_message'; } } /** * Check if the system is in maintenance mode. * * @return bool */ if (!function_exists('isMaintenanceMode')) { function isMaintenanceMode() { return \Illuminate\Support\Facades\Cache::remember('system_setting_maintenance_mode', 300, function () { $setting = \Illuminate\Support\Facades\DB::table('system_settings') ->where('key', 'maintenance_mode') ->first(); return $setting ? $setting->value === 'true' : false; }); } }