diff --git a/ajax/getSupersetGuestToken.php b/ajax/getSupersetGuestToken.php
new file mode 100644
index 0000000..f1dfc1d
--- /dev/null
+++ b/ajax/getSupersetGuestToken.php
@@ -0,0 +1,92 @@
+ 'Unauthorized'));
+ exit;
+}
+
+$company_id = isset($_SESSION['agency_id']) ? (int)$_SESSION['agency_id'] : 1;
+$user_id = (int)$_SESSION['id'];
+
+//$_SESSION['role_superset'] = 'manager';
+
+if (isset($_SESSION['role_superset']) && !empty($_SESSION['role_superset'])) {
+ $user_role = $_SESSION['role_superset'];
+} else {
+ if (!empty($_SESSION['agency']) || !empty($_SESSION['users_admin'])) {
+ $user_role = 'company_head';
+ } elseif (!empty($_SESSION['manager'])) {
+ $user_role = 'manager';
+ } else {
+ $user_role = 'agent';
+ }
+
+ $_SESSION['role_superset'] = $user_role;
+}
+
+$dashboard_uuid = isset($_GET['dashboard_id']) ? $_GET['dashboard_id'] : '';
+
+// Проверка доступа к дашборду
+$dashboard_uuid_esc = mysql_real_escape_string($dashboard_uuid);
+$company_id_esc = $company_id;
+$user_id_esc = $user_id;
+
+
+if ($user_id == $company_id) {
+ $sql_check = "
+ SELECT 1 FROM bi_analytics_dashboard_access
+ WHERE dashboard_uuid = '$dashboard_uuid_esc'
+ AND company_id = $company_id_esc
+ AND is_active = 1
+ LIMIT 1
+ ";
+} else {
+ $role_level = 0;
+ if ($user_role == 'agent') $role_level = 1;
+ elseif ($user_role == 'manager') $role_level = 2;
+ elseif ($user_role == 'company_head') $role_level = 3;
+
+ $sql_check = "
+ SELECT 1 FROM bi_analytics_dashboard_access
+ WHERE dashboard_uuid = '$dashboard_uuid_esc'
+ AND company_id = $company_id_esc
+ AND is_active = 1
+ AND (user_id = $user_id_esc OR user_id = 0)
+ AND role_required <= $role_level
+ LIMIT 1
+ ";
+}
+
+$result_check = mysql_query($sql_check);
+if (!$result_check || mysql_num_rows($result_check) == 0) {
+ http_response_code(403);
+ echo json_encode(array(
+ 'error' => 'Dashboard access denied',
+ 'role_level' => $user_role
+ ));
+ exit;
+}
+
+// Информация о пользователе
+$userInfo = array(
+ 'first_name' => isset($_SESSION['first_name']) ? $_SESSION['first_name'] : 'User',
+ 'last_name' => isset($_SESSION['last_name']) ? $_SESSION['last_name'] : (string)$user_id,
+ 'email' => isset($_SESSION['email']) ? $_SESSION['email'] : null,
+);
+
+$bi = new BiAnalytics();
+$result = $bi->getGuestToken($dashboard_uuid, $company_id, $user_id, $user_role, $userInfo);
+
+if (isset($result['token'])) {
+ header('Content-Type: application/json');
+ echo json_encode(array('token' => $result['token']));
+} else {
+ http_response_code(500);
+ echo json_encode(array('error' => 'Failed to get guest token', 'details' => $result['error']));
+}
+
+file_put_contents($_SERVER['DOCUMENT_ROOT'] . '/log/superset_guest.log',
+ date('Y-m-d H:i:s') . "\n" . json_encode($result) . "\n\n", FILE_APPEND);
\ No newline at end of file
diff --git a/bi-analytics-test.php b/bi-analytics-test.php
new file mode 100644
index 0000000..7fc3492
--- /dev/null
+++ b/bi-analytics-test.php
@@ -0,0 +1,304 @@
+ 1, 'manager' => 2, 'company_head' => 3, 'admin' => 4];
+ $required_order = isset($role_order[$row['role_required']]) ? $role_order[$row['role_required']] : 1;
+ $user_order = isset($role_order[$user_role]) ? $role_order[$user_role] : 1;
+
+ if ($user_order >= $required_order) {
+ $available_dashboards[] = $row;
+ }
+}
+
+if (empty($available_dashboards)) {
+ echo '
+
📊 Нет доступных дашбордов
+
Для вашей компании ещё не настроены дашборды BI-аналитики.
+
';
+ exit;
+}
+
+$default_dashboard = $available_dashboards[0];
+?>
+
+
+
+
+
+
+ BI Аналитика — Тестирование
+
+
+
+
+
+
+
+ 1): ?>
+
+
+
+
+
+
+
+
+
+
+

+ Загрузка дашборда...
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/classes/BiAnalytics.php b/engine/classes/BiAnalytics.php
new file mode 100644
index 0000000..87c9d1b
--- /dev/null
+++ b/engine/classes/BiAnalytics.php
@@ -0,0 +1,184 @@
+supersetDomain = trim('http://10.91.76.110');
+ $this->loginEndpoint = $this->supersetDomain . '/api/v1/security/login';
+ $this->guestTokenEndpoint = $this->supersetDomain . '/api/v1/security/guest_token/';
+ $this->adminUsername = 'admin';
+ $this->adminPassword = '6a6a78543e215c5adce1b828c9a30ef5';
+ }
+
+ public static function getAvailableUserIds($userId, $role)
+ {
+ if ($role === 'company_head') {
+ return null;
+ }
+ if ($role === 'agent') {
+ return array($userId);
+ }
+ if ($role === 'manager') {
+ $subordinates = self::getAllSubordinateIds($userId);
+ $subordinates[] = $userId;
+ return array_unique($subordinates);
+ }
+ return array($userId);
+ }
+
+ private static function getAllSubordinateIds($managerId)
+ {
+ $sql = "SELECT id FROM users WHERE id_manager = " . intval($managerId) . " AND blocked = 0";
+ $result = mysql_query($sql);
+ if (!$result) {
+ return array();
+ }
+ $subIds = array();
+ while ($row = mysql_fetch_assoc($result)) {
+ $subId = (int)$row['id'];
+ $subIds[] = $subId;
+ $subSubIds = self::getAllSubordinateIds($subId);
+ $subIds = array_merge($subIds, $subSubIds);
+ }
+ return $subIds;
+ }
+
+ public static function buildRlsRules($companyId, $userId, $role)
+ {
+ $tables = array(
+ 'cur.clients_base',
+ 'bi.client_kpi_daily',
+ 'logs.client_events',
+ 'cur.requisitions_base',
+ 'bi.requisition_kpi_daily',
+ 'logs.requisition_events'
+ );
+
+ $availableUserIds = self::getAvailableUserIds($userId, $role);
+ $rules = array();
+
+ // Одно правило company_id на все таблицы (без dataset_name — Superset применит ко всем)
+ $rules[] = array(
+ 'clause' => "company_id = {$companyId}"
+ );
+
+ // Если не company_head — добавляем фильтр по who_work
+ if ($availableUserIds !== null) {
+ $userIdsString = implode(',', $availableUserIds);
+ $rules[] = array(
+ 'clause' => "(who_work IN ({$userIdsString}) OR who_work = 0)"
+ );
+ }
+
+ return $rules;
+ }
+
+ public function getGuestToken($dashboardUuid, $companyId, $userId, $userRole, $userInfo = array())
+ {
+ try {
+ $accessToken = $this->getAdminAccessToken();
+ if (!$accessToken) {
+ return array('error' => 'Failed to obtain admin access token');
+ }
+
+ $rlsRules = self::buildRlsRules($companyId, $userId, $userRole);
+
+ $guestTokenData = array(
+ 'resources' => array(
+ array(
+ 'type' => 'dashboard',
+ 'id' => $dashboardUuid
+ )
+ ),
+ 'rls' => $rlsRules,
+ 'ui_config' => array(
+ 'show_sql' => in_array($userRole, array('manager', 'company_head', 'admin', 'agent')),
+ 'url_params' => array(
+ 'filter_company_id' => (string)$companyId
+ )
+ ),
+ 'user' => array(
+ 'username' => 'joywork_user_' . $userId,
+ 'first_name' => isset($userInfo['first_name']) ? $userInfo['first_name'] : 'User',
+ 'last_name' => isset($userInfo['last_name']) ? $userInfo['last_name'] : (string)$userId,
+ 'email' => isset($userInfo['email']) ? $userInfo['email'] : null,
+ ),
+ 'attributes' => array(
+ 'company_id' => $companyId,
+ 'available_user_ids' => self::getAvailableUserIds($userId, $userRole),
+ ),
+ );
+
+ $response = $this->makeRequest($this->guestTokenEndpoint, $guestTokenData, $accessToken);
+ if (isset($response['token'])) {
+ return array('token' => $response['token']);
+ } else {
+ return array('error' => 'Guest token not returned', 'details' => $response);
+ }
+ } catch (Exception $e) {
+ return array('error' => $e->getMessage());
+ }
+ }
+
+ private function getAdminAccessToken()
+ {
+ $loginData = array(
+ 'username' => $this->adminUsername,
+ 'password' => $this->adminPassword,
+ 'provider' => 'db',
+ 'refresh' => true,
+ );
+
+ $response = $this->makeRequest($this->loginEndpoint, $loginData);
+ if (isset($response['access_token'])) {
+ return $response['access_token'];
+ }
+ return null;
+ }
+
+ private function makeRequest($url, $postData, $accessToken = null)
+ {
+ $ch = curl_init();
+ curl_setopt_array($ch, array(
+ CURLOPT_URL => $url,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => json_encode($postData),
+ CURLOPT_HTTPHEADER => array(
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ ),
+ CURLOPT_SSL_VERIFYPEER => false,
+ ));
+
+ if ($accessToken) {
+ $headers = array(
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ 'Authorization: Bearer ' . $accessToken,
+ );
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $responseBody = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ if ($httpCode !== 200) {
+ throw new Exception("API request failed. HTTP code: $httpCode. Response: $responseBody");
+ }
+
+ $decoded = json_decode($responseBody, true);
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ throw new Exception("Invalid JSON response: " . json_last_error_msg());
+ }
+ return $decoded;
+ }
+}
\ No newline at end of file