From a5b771a99a36542a25000134d3e9ab462351db26 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Fri, 17 May 2024 18:49:48 +0700 Subject: [PATCH] resolved #125, resolved #122, resolved #121, resolved #120, resolved #110, resolved #109, resolved #108, resolved #90, resolved #68 --- mirzaev/ebala/system/controllers/account.php | 10 +- .../system/controllers/administrator.php | 5 + mirzaev/ebala/system/controllers/market.php | 3 + mirzaev/ebala/system/controllers/operator.php | 5 + mirzaev/ebala/system/controllers/payments.php | 12 +- mirzaev/ebala/system/controllers/settings.php | 94 ++++++ mirzaev/ebala/system/controllers/task.php | 91 +++--- mirzaev/ebala/system/controllers/worker.php | 75 ++--- mirzaev/ebala/system/models/payments.php | 82 +---- mirzaev/ebala/system/models/registry.php | 40 ++- mirzaev/ebala/system/models/settings.php | 111 +++++++ mirzaev/ebala/system/models/task.php | 8 +- mirzaev/ebala/system/public/css/main.css | 3 +- .../system/public/css/pages/operators.css | 16 + .../system/public/css/pages/settings.css | 73 +++++ mirzaev/ebala/system/public/css/popup.css | 25 +- mirzaev/ebala/system/public/index.php | 5 +- mirzaev/ebala/system/public/js/core.js | 5 + .../system/public/js/imask-7.1.0-alpha.js | 4 + mirzaev/ebala/system/public/js/loader.js | 7 + mirzaev/ebala/system/public/js/operators.js | 46 ++- mirzaev/ebala/system/public/js/settings.js | 42 +++ mirzaev/ebala/system/public/js/tasks.js | 2 +- mirzaev/ebala/system/public/js/workers.js | 303 ++++++++++++------ .../views/elements/lists/task/actual.html | 7 +- .../views/elements/lists/task/passed.html | 6 +- .../system/views/elements/operators.html | 2 +- .../ebala/system/views/elements/workers.html | 2 +- mirzaev/ebala/system/views/lists/works.html | 27 +- mirzaev/ebala/system/views/menu.html | 4 +- mirzaev/ebala/system/views/pages/markets.html | 1 - .../ebala/system/views/pages/settings.html | 138 ++++++++ mirzaev/ebala/system/views/pages/tasks.html | 2 +- mirzaev/ebala/system/views/templater.php | 12 + 34 files changed, 965 insertions(+), 303 deletions(-) create mode 100755 mirzaev/ebala/system/controllers/settings.php create mode 100755 mirzaev/ebala/system/models/settings.php create mode 100755 mirzaev/ebala/system/public/css/pages/settings.css create mode 100644 mirzaev/ebala/system/public/js/settings.js create mode 100644 mirzaev/ebala/system/views/pages/settings.html diff --git a/mirzaev/ebala/system/controllers/account.php b/mirzaev/ebala/system/controllers/account.php index 2891c3e..d488299 100755 --- a/mirzaev/ebala/system/controllers/account.php +++ b/mirzaev/ebala/system/controllers/account.php @@ -37,7 +37,7 @@ final class account extends core { if ($this->account->status()) { // Авторизован аккаунт - + // Инициализация истории заявок $this->view->history = task::list(before: 'FILTER task.worker == "' . model::worker($this->account->getId())?->id . '"'); @@ -70,7 +70,7 @@ final class account extends core // Авторизован аккаунт администратора или оператора // Инициализация данных аккаунта - $account = model::read('d._key == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, commentary: d.commentary }')->getAll(); + $account = model::read('d._key == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, commentary: d.commentary, transactions: d.transactions }')->getAll(); if (!empty($account)) { // Найдены данные аккаунта @@ -134,6 +134,12 @@ final class account extends core ); else throw new exception('Пароль должен быть длиннее 6 символов'); if ($parameters['commentary'] !== $account->commentary) $account->commentary = $parameters['commentary']; + if ($parameters['transactions'] !== $account->transactions) + $account->transactions = match ($parameters['transactions']) { + 'true' => true, + 'false' => false, + default => false + }; if (_core::update($account)) { // Записаны данные аккаунта diff --git a/mirzaev/ebala/system/controllers/administrator.php b/mirzaev/ebala/system/controllers/administrator.php index fb16e3a..db5b95e 100755 --- a/mirzaev/ebala/system/controllers/administrator.php +++ b/mirzaev/ebala/system/controllers/administrator.php @@ -147,8 +147,13 @@ final class administrator extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: <<session->buffer[$_SERVER['INTERFACE']]['administrators']['page'], + sort: 'x.created DESC, x._key DESC', target: empty($search) ? account::COLLECTION : 'registry_accounts', + return: '{account: x}', binds: empty($search) ? [] : [ 'search' => $search ] diff --git a/mirzaev/ebala/system/controllers/market.php b/mirzaev/ebala/system/controllers/market.php index cdbad44..b529668 100755 --- a/mirzaev/ebala/system/controllers/market.php +++ b/mirzaev/ebala/system/controllers/market.php @@ -180,12 +180,15 @@ final class market extends core FILTER account.type == 'market' && b.deleted != true %s %s + COLLECT x = account, y = market OPTIONS { method: "sorted" } AQL, empty($filters_before) ? null : "FILTER $filters_before", empty($filters_after) ? null : "FILTER $filters_after" ), page: (int) $this->session->buffer[$_SERVER['INTERFACE']]['markets']['page'], + sort: 'x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? account::COLLECTION : 'registry_accounts', + return: '{account: x, market: y}', binds: empty($search) ? [] : [ 'search' => $search ] diff --git a/mirzaev/ebala/system/controllers/operator.php b/mirzaev/ebala/system/controllers/operator.php index cef225d..0b9c1d4 100755 --- a/mirzaev/ebala/system/controllers/operator.php +++ b/mirzaev/ebala/system/controllers/operator.php @@ -147,8 +147,13 @@ final class operator extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: <<session->buffer[$_SERVER['INTERFACE']]['operators']['page'], + sort: 'x.created DESC, x._key DESC', target: empty($search) ? account::COLLECTION : 'registry_accounts', + return: '{account: x}', binds: empty($search) ? [] : [ 'search' => $search ] diff --git a/mirzaev/ebala/system/controllers/payments.php b/mirzaev/ebala/system/controllers/payments.php index 6675c1d..3fbb0c4 100755 --- a/mirzaev/ebala/system/controllers/payments.php +++ b/mirzaev/ebala/system/controllers/payments.php @@ -34,8 +34,8 @@ final class payments extends core public function workers(array $parameters = []): void { try { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + if ($this->account->status() && ($this->account->type === 'administrator' || ($this->account->type === 'operator' && $this->account->transactions))) { + // Авторизован аккаунт администратора или оператора (с доступом к транзакциям) // Инициализация буфера ошибок $this->errors['export'] ??= []; @@ -119,8 +119,8 @@ final class payments extends core public function markets(array $parameters = []): void { try { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + if ($this->account->status() && ($this->account->type === 'administrator' || ($this->account->type === 'operator' && $this->account->transactions))) { + // Авторизован аккаунт администратора или оператора (с доступом к транзакциям) // Инициализация буфера ошибок $this->errors['export'] ??= []; @@ -204,8 +204,8 @@ final class payments extends core public function confirm(array $parameters = []): void { try { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + if ($this->account->status() && ($this->account->type === 'administrator' || ($this->account->type === 'operator' && $this->account->transactions))) { + // Авторизован аккаунт администратора или оператора (с доступом к транзакциям) // Инициализация буфера ошибок $this->errors['confirm'] ??= []; diff --git a/mirzaev/ebala/system/controllers/settings.php b/mirzaev/ebala/system/controllers/settings.php new file mode 100755 index 0000000..5b61988 --- /dev/null +++ b/mirzaev/ebala/system/controllers/settings.php @@ -0,0 +1,94 @@ + + */ +final class settings extends core +{ + /** + * Страница настроек + * + * @param array $parameters Параметры запроса + */ + public function index(array $parameters = []): ?string + { + if ($this->account->status() && $this->account->type === 'administrator') { + // Авторизован аккаунт (администратор) + + // Чтение настроек + $this->view->settings = model::search( + before: 'FILTER setting.category != null' + ); + + // Генерация представления + $main = $this->view->render(DIRECTORY_SEPARATOR . 'pages' . DIRECTORY_SEPARATOR . 'settings.html'); + + // Возврат (успех) + if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', ['main' => $main]); + else if ($_SERVER['REQUEST_METHOD'] === 'POST') return $main; + } + + // Возврат (провал) + return null; + } + + /** + * Записать + * + * Записывает (обновляет) в ArangoDB + * + * @param array $parameters Параметры запроса + * + * @return void + */ + public function write(array $parameters = []): void + { + try { + if ($this->account->status() && $this->account->type === 'administrator') { + // Авторизован аккаунт администратора + + // Инициализация инстанции настройки + $setting = model::read('d._key == "' . $parameters['id'] . '"'); + + if ($setting instanceof _document) { + // Найдена инстанция настройки + + // Запись значения + $setting->value = $parameters['value']; + + if (_core::update($setting)) { + // Записано в ArangoDB + } else throw new exception('Не удалось обновить заявку'); + } else throw new exception('Не найдена заявка'); + } else throw new exception('Вы не авторизованы'); + } catch (exception $e) { + // Запись в реестр ошибок + $this->errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + } +} diff --git a/mirzaev/ebala/system/controllers/task.php b/mirzaev/ebala/system/controllers/task.php index 8f78a04..9719eac 100755 --- a/mirzaev/ebala/system/controllers/task.php +++ b/mirzaev/ebala/system/controllers/task.php @@ -272,8 +272,10 @@ final class task extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['worker']['tasks']['page'], target: empty($search) ? model::COLLECTION : 'registry_tasks', + return: '{task: z, worker: x, market: y}', binds: ['worker' => account::worker($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search]) ); } else if ($_SERVER['INTERFACE'] === 'market') { @@ -288,8 +290,10 @@ final class task extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['market']['tasks']['page'], target: empty($search) ? model::COLLECTION : 'registry_tasks', + return: '{task: z, worker: x, market: y}', binds: ['market' => account::market($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search]) ); } else if ($_SERVER['INTERFACE'] === 'operator') { @@ -304,8 +308,10 @@ final class task extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['operator']['tasks']['page'], target: empty($search) ? model::COLLECTION : 'registry_tasks', + return: '{task: z, worker: x, market: y}', binds: empty($search) ? [] : [ 'search' => $search ] @@ -322,8 +328,11 @@ final class task extends core $search_query, empty($filters) ? null : " && ($filters)" ), + after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['administrator']['tasks']['page'], + sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? model::COLLECTION : 'registry_tasks', + return: '{task: z, worker: x, market: y}', binds: empty($search) ? [] : [ 'search' => $search ] @@ -632,49 +641,51 @@ final class task extends core if (($worker = worker::read('d.id == "' . $parameters['worker'] . '" && d.active == true', amount: 1)) instanceof _document) { // Найден сотрудник (запрашиваемый для записи сотрудник существует в базе данных) - if ($task->worker !== $parameters['worker']) { - // Идентификатор запрашиваемого сотрудника не равен актуальному + if (!$worker->fired) { + if ($task->worker !== $parameters['worker']) { + // Идентификатор запрашиваемого сотрудника не равен актуальному - // Запись сотрудника - $task->worker = $worker->id; + // Запись сотрудника + $task->worker = $worker->id; - // Снятие с публикации - $task->published = false; + // Снятие с публикации + $task->published = false; - if (_core::update($task)) { - // Записано изменение в базу данных + if (_core::update($task)) { + // Записано изменение в базу данных - // Инициализация строки в глобальную переменную шаблонизатора - $this->view->rows = static::preprocessing($this->account, model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"', amount: 1)); + // Инициализация строки в глобальную переменную шаблонизатора + $this->view->rows = static::preprocessing($this->account, model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"', amount: 1)); - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Генерация ответа - echo json_encode( - [ - 'updated' => true, - 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), - 'errors' => self::parse_only_text($this->errors) - ] - ); + // Генерация ответа + echo json_encode( + [ + 'updated' => true, + 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), + 'errors' => self::parse_only_text($this->errors) + ] + ); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Сотрудник уже назначен'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Сотрудник уже назначен'); + } else throw new exception('Нельзя назначить уволенного сотрудника'); } else throw new exception('Не найден сотрудник'); } } else if (!empty($parameters['market'])) { @@ -869,7 +880,9 @@ final class task extends core 'worker' => 'Сотрудник', default => $key }, - 'value' => $value + 'value' => [ + 'id' => $value + ] + account::read("d._key == \"$value\"", amount: 1)?->name ?? [] ]; else if (match ($key) { 'created', 'updated', 'confirmed', 'hided', 'completed', '_key' => true, @@ -1234,7 +1247,7 @@ final class task extends core // Генерация ответа echo json_encode( [ - 'confirmed' => $this->view->rows[0]->task['confirmed'], + 'confirmed' => $this->view->rows[0]->task['confirmed'] ?? null, 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), 'errors' => self::parse_only_text($this->errors) ] @@ -1633,7 +1646,7 @@ final class task extends core // Генерация ответа echo json_encode( [ - 'hided' => $this->view->rows[0]->task['hided'], + 'hided' => $this->view->rows[0]->task['hided'] ?? null, 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), 'errors' => self::parse_only_text($this->errors) ] @@ -1926,7 +1939,8 @@ final class task extends core $this->view->works = static::WORKS; // Проверка на существование записанной в задаче работы в списке существующих работ и запись об этом в глобальную переменную шаблонизатора - foreach ($this->view->works as $work) if ($this->view->task->work === $work) $this->view->exist = true; + foreach ($this->view->works as $work) + if (in_array($work, $this->view->worker->works)) $this->view->exist = true; // Запись заголовков ответа header('Content-Type: application/json'); @@ -1972,7 +1986,8 @@ final class task extends core $this->view->works = static::WORKS; // Проверка на существование записанной в задаче работы в списке существующих работ и запись об этом в глобальную переменную шаблонизатора - foreach ($this->view->works as $work) if ($this->view->worker->work === $work) $this->view->exist = true; + foreach ($this->view->works as $work) + if (in_array($work, $this->view->worker->works)) $this->view->exist = true; // Запись заголовков ответа header('Content-Type: application/json'); diff --git a/mirzaev/ebala/system/controllers/worker.php b/mirzaev/ebala/system/controllers/worker.php index cad1696..eca8f73 100755 --- a/mirzaev/ebala/system/controllers/worker.php +++ b/mirzaev/ebala/system/controllers/worker.php @@ -146,58 +146,43 @@ final class worker extends core ? null : << 5 && LEVENSHTEIN_MATCH(a._key, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.id, TOKENS(@search, 'text_en')[0], 1, true)) || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.first, TOKENS(@search, 'text_ru')[0], 2, true)) || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.second, TOKENS(@search, 'text_ru')[0], 2, true)) || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.last, TOKENS(@search, 'text_ru')[0], 2, true)) - || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b._key, TOKENS(@search, 'text_en')[0], 2, true)) - || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.id, TOKENS(@search, 'text_en')[0], 1, true)) - || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.first, TOKENS(@search, 'text_ru')[0], 2, true)) - || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.second, TOKENS(@search, 'text_ru')[0], 2, true)) - || (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.last, TOKENS(@search, 'text_ru')[0], 2, true)) - || (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(b.address, TOKENS(@search, 'text_ru')[0], 2, true)) - || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.city, TOKENS(@search, 'text_ru')[0], 1, true)) - || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.district, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(a.address, TOKENS(@search, 'text_ru')[0], 2, true)) + || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.city, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.district, TOKENS(@search, 'text_ru')[0], 1, true)) || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.number, TOKENS(@search, 'text_en')[0], 2, true)) - || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.number, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.mail, TOKENS(@search, 'text_en')[0], 2, true)) - || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.mail, TOKENS(@search, 'text_en')[0], 2, true)) - || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.passport, TOKENS(@search, 'text_ru')[0], 1, true)) - || (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(b.department.number, TOKENS(@search, 'text_en')[0], 1, true)) - || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.department.address, TOKENS(@search, 'text_ru')[0], 1, true)) - || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.requisites, TOKENS(@search, 'text_ru')[0], 1, true)) - || (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(b.tax, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.passport, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(a.department.number, TOKENS(@search, 'text_en')[0], 1, true)) + || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.department.address, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.requisites, TOKENS(@search, 'text_ru')[0], 1, true)) + || (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(a.tax, TOKENS(@search, 'text_ru')[0], 1, true)) OPTIONS { collections: ["account", "worker"] } - AQL; - + AQL;; // Инициализация данных для генерации HTML-документа с таблицей $this->view->rows = registry::workers( before: sprintf( @@ -214,12 +199,15 @@ final class worker extends core FILTER account.type == 'worker' && b.deleted != true %s %s + COLLECT x = account, y = worker OPTIONS { method: "sorted" } AQL, empty($filters_before) ? null : "FILTER $filters_before", empty($filters_after) ? null : "FILTER $filters_after" ), page: (int) $this->session->buffer[$_SERVER['INTERFACE']]['workers']['page'], + sort: 'x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? account::COLLECTION : 'registry_accounts', + return: '{account: x, worker: y}', binds: empty($search) ? [] : [ 'search' => $search ] @@ -280,7 +268,7 @@ final class worker extends core if (!empty($parameters['requisites']) && $parameters['worker_requisites'][-1] === '.') $parameters['worker_requisites'] .= '.'; if (!empty($parameters['worker_birth'])) $parameters['worker_birth'] = DateTime::createFromFormat('Y-m-d', $parameters['worker_birth'])->getTimestamp(); if (!empty($parameters['worker_issued'])) $parameters['worker_issued'] = DateTime::createFromFormat('Y-m-d', $parameters['worker_issued'])->getTimestamp(); - if (!empty($parameters['work'])) $parameters['work'] = in_array($parameters['work'], static::WORKS) ? $parameters['work'] : static::WORKS[0]; + if (!empty($parameters['works'])) $parameters['works'] = in_array($parameters['works'], static::WORKS) ? $parameters['works'] : static::WORKS[0]; if (!empty($parameters['worker_hiring'])) $parameters['worker_hiring'] = DateTime::createFromFormat('Y-m-d', $parameters['worker_hiring'])->getTimestamp(); // Создание аккаунта @@ -410,7 +398,7 @@ final class worker extends core // Авторизован аккаунт администратора или оператора // Инициализация данных сотрудника - $worker = model::read('d.id == "' . urldecode($parameters['id']) . '"', return: '{ name: d.name, number: d.number, mail: d.mail, birth: d.birth, passport: d.passport, issued: d.issued, department: d.department, requisites: d.requisites, payment: d.payment, tax: d.tax, city: d.city, district: d.district, address: d.address, worl: d.work, hiring: d.hiring}')->getAll(); + $worker = model::read('d.id == "' . urldecode($parameters['id']) . '"', return: '{ name: d.name, number: d.number, mail: d.mail, birth: d.birth, passport: d.passport, issued: d.issued, department: d.department, requisites: d.requisites, payment: d.payment, tax: d.tax, city: d.city, district: d.district, address: d.address, worl: d.work, hiring: d.hiring, registration: d.registration}')->getAll(); if (!empty($worker)) { // Найдены данные сотрудника @@ -458,8 +446,12 @@ final class worker extends core // Универсализация if (!empty($parameters['birth'])) $parameters['birth'] = DateTime::createFromFormat('Y-m-d', $parameters['birth'])->getTimestamp(); if (!empty($parameters['issued'])) $parameters['issued'] = DateTime::createFromFormat('Y-m-d', $parameters['issued'])->getTimestamp(); - if (!empty($parameters['work'])) $parameters['work'] = in_array($parameters['work'], static::WORKS) ? $parameters['work'] : static::WORKS[0]; if (!empty($parameters['hiring'])) $parameters['hiring'] = DateTime::createFromFormat('Y-m-d', $parameters['hiring'])->getTimestamp(); + if (!empty($buffer = explode(':', $parameters['works']))) { + $parameters['works'] = []; + foreach ($buffer ?? [] as $work) + if (in_array($work, static::WORKS)) array_push($parameters['works'], $work); + } // Инициализация параметров (перезапись переданными значениями) if ($parameters['name_first'] !== $worker->name['first']) $worker->name = ['first' => $parameters['name_first']] + $worker->name; @@ -480,8 +472,9 @@ final class worker extends core if ($parameters['city'] !== $worker->city) $worker->city = $parameters['city']; if ($parameters['district'] !== $worker->district) $worker->district = $parameters['district']; if ($parameters['address'] !== $worker->address) $worker->address = $parameters['address']; - if ($parameters['work'] !== $worker->work) $worker->work = $parameters['work']; + if ($parameters['works'] !== $worker->works) $worker->works = $parameters['works']; if ($parameters['hiring'] !== $worker->hiring) $worker->hiring = $parameters['hiring']; + if ($parameters['registration'] !== $worker->registration) $worker->registration = $parameters['registration']; if (_core::update($worker)) { // Записаны данные сотрудника diff --git a/mirzaev/ebala/system/models/payments.php b/mirzaev/ebala/system/models/payments.php index f828143..2d39904 100755 --- a/mirzaev/ebala/system/models/payments.php +++ b/mirzaev/ebala/system/models/payments.php @@ -651,72 +651,8 @@ final class payments extends core { return match (mb_strtolower($type)) { - 'market', 'магазин' => match (mb_strtolower($city)) { - 'красноярск' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 257.07, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 257.07, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 257.07, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360, - 'loaders', 'loader', 'грузчики', 'грузчик' => 255.645, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305, - default => 0 - }, - 'железногорск', 'сосновоборск', 'тыва' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 263.34, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 263.34, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 263.34, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360, - 'loaders', 'loader', 'грузчики', 'грузчик' => 255.645, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305, - default => 0 - }, - 'хакасия', 'иркутск' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 245.385, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 245.385, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 245.385, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360, - 'loaders', 'loader', 'грузчики', 'грузчик' => 255.645, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305, - default => 0 - }, - default => 0 - }, - 'worker', 'сотрудник' => match (mb_strtolower($city)) { - 'красноярск' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 190.91, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 190.91, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 190.91, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 250, - 'loaders', 'loader', 'грузчики', 'грузчик' => 177.27, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 250, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 250, - default => 0 - }, - 'железногорск', 'сосновоборск', 'тыва' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 190.91, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 190.91, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 190.91, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 250, - 'loaders', 'loader', 'грузчики', 'грузчик' => 177.27, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 250, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 250, - default => 0 - }, - 'хакасия', 'иркутск' => match (mb_strtolower($work)) { - 'cashiers', 'cashier', 'кассиры', 'кассир' => 181.82, - 'displayers', 'displayer', 'выкладчики', 'выкладчик' => 181.82, - 'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 181.82, - 'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 250, - 'loaders', 'loader', 'грузчики', 'грузчик' => 168.18, - 'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 250, - 'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 250, - default => 0 - }, - default => 0 - }, + 'market', 'магазин' => settings::read("d.category == 'market_hour' && d.city == '$city' && d.work == '$work'")?->value ?? 0, + 'worker', 'сотрудник' => settings::read("d.category == 'worker_hour' && d.city == '$city' && d.work == '$work'")?->value ?? 0, default => 0 }; } @@ -730,10 +666,7 @@ final class payments extends core */ public static function bonus(int $rating): int { - return match ($rating) { - 5 => 100, - default => 0 - }; + return settings::read("d.category == 'worker_bonus' && d.rating == $rating")?->value ?? 0; } /** @@ -745,11 +678,8 @@ final class payments extends core */ public static function penalty(int $rating): ?int { - return match ($rating) { - 3 => -100, - 2 => -500, - 1 => null, - default => 0 - }; + $penalty = settings::read("d.category == 'worker_penalty' && d.rating == $rating")?->value ?? 0; + + return $penalty === 1 ? null : $penalty; } } diff --git a/mirzaev/ebala/system/models/registry.php b/mirzaev/ebala/system/models/registry.php index 042f68e..56cab8e 100755 --- a/mirzaev/ebala/system/models/registry.php +++ b/mirzaev/ebala/system/models/registry.php @@ -36,7 +36,9 @@ final class registry extends core * @param ?string $after Injection of AQL-code after search of edges * @param int $amount Amount of workers * @param int $page Offset by amount + * @param string $sort Sort * @param string $target Collection or view name + * @param string $return Data for return * @param array $binds Binds for query * @param array $errors Errors registry * @@ -49,6 +51,7 @@ final class registry extends core int $page = 1, string $sort = 'account.created DESC, account._key DESC', string $target = account::COLLECTION, + string $return = '{account, worker}', array $binds = [], array &$errors = [] ): array { @@ -69,14 +72,15 @@ final class registry extends core %s SORT %s LIMIT %d, %d - RETURN {account, worker} + RETURN %s AQL, $target, $before, $after, $sort, --$page <= 0 ? 0 : $amount * $page, - $amount + $amount, + $return ), $binds); // Exit (success) @@ -104,6 +108,7 @@ final class registry extends core * @param int $amount Amount of markets * @param int $page Offset by amount * @param string $target Collection or view name + * @param string $return Data for return * @param array $binds Binds for query * @param array $errors Errors registry * @@ -116,13 +121,14 @@ final class registry extends core int $page = 1, string $sort = 'account.created DESC, account._key DESC', string $target = account::COLLECTION, + string $return = '{account, market}', array $binds = [], array &$errors = [] ): array { try { if (collection::init(static::$arangodb->session, account::COLLECTION) && collection::init(static::$arangodb->session, market::COLLECTION)) { // Инициализированы коллекции - + // Search the session data in ArangoDB $markets = collection::search(static::$arangodb->session, sprintf( <<session, sprintf( <<session, sprintf( << $e->getLine(), 'stack' => $e->getTrace() ]; -var_dump($errors); } // Exit (fail) diff --git a/mirzaev/ebala/system/models/settings.php b/mirzaev/ebala/system/models/settings.php new file mode 100755 index 0000000..7299e49 --- /dev/null +++ b/mirzaev/ebala/system/models/settings.php @@ -0,0 +1,111 @@ + + */ +final class settings extends core +{ + use instance, status; + + /** + * Collection name in ArangoDB + */ + final public const COLLECTION = 'settings'; + + /** + * Инстанция документа в базе данных + */ + protected readonly _document $document; + + /** + * Read (search) + * + * @param ?string $before Injection of AQL-code before search of edges + * @param int $amount Amount of administrators + * @param int $page Offset by amount + * @param string $target Collection or view name + * @param array $binds Binds for query + * @param array $errors Errors registry + * + * @return array Instances from ArangoDB + */ + public static function search( + ?string $before = '', + int $amount = 1000, + int $page = 1, + string $sort = 'setting.category ASC, setting.city ASC, setting.work ASC, setting.rating DESC, setting.created DESC, setting._key DESC', + string $target = settings::COLLECTION, + array $binds = [], + array &$errors = [] + ): array { + try { + if (collection::init(static::$arangodb->session, settings::COLLECTION)) { + // Инициализирована коллекция + + // Search the session data in ArangoDB + $settings = collection::search(static::$arangodb->session, sprintf( + <<getAll() ?? [] as $category => $data) $buffer[$category] = $data; + $settings = $buffer; + + // Exit (success) + return $settings; + } else throw new exception('Не удалось инициализировать коллекции'); + } catch (exception $e) { + // Write to the errors registry + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + var_dump($errors); + } + + // Exit (fail) + return []; + } +} diff --git a/mirzaev/ebala/system/models/task.php b/mirzaev/ebala/system/models/task.php index b5142fc..3717620 100755 --- a/mirzaev/ebala/system/models/task.php +++ b/mirzaev/ebala/system/models/task.php @@ -118,6 +118,7 @@ final class task extends core * @param int $amount Amount of tasks * @param int $page Offset by amount * @param string $target Collection or view name + * @param string $return Data for return * @param array $binds Binds for query * @param array $errors Errors registry * @@ -133,6 +134,7 @@ final class task extends core int $page = 1, string $sort = 'task.date DESC, task.created DESC, task._key DESC', string $target = self::COLLECTION, + string $return = '{task, worker, market}', array $binds = [], array &$errors = [] ): array { @@ -154,7 +156,7 @@ final class task extends core %s SORT %s LIMIT %d, %d - RETURN {task, worker, market} + RETURN %s AQL, $target, $before, @@ -163,7 +165,8 @@ final class task extends core $after, $sort, --$page <= 0 ? 0 : $amount * $page, - $amount + $amount, + $return ), $binds); // Exit (success) @@ -291,5 +294,4 @@ final class task extends core // Exit (fail) return null; } - } diff --git a/mirzaev/ebala/system/public/css/main.css b/mirzaev/ebala/system/public/css/main.css index f7e9a71..e971e4a 100755 --- a/mirzaev/ebala/system/public/css/main.css +++ b/mirzaev/ebala/system/public/css/main.css @@ -58,7 +58,8 @@ input[type="range"] { button, input[type="submit"], -input[type="range"] { +input[type="range"], +input[type="checkbox"] { cursor: pointer; } diff --git a/mirzaev/ebala/system/public/css/pages/operators.css b/mirzaev/ebala/system/public/css/pages/operators.css index 4563162..14fbb87 100755 --- a/mirzaev/ebala/system/public/css/pages/operators.css +++ b/mirzaev/ebala/system/public/css/pages/operators.css @@ -59,3 +59,19 @@ section#operators.panel.list > div.row > span[data-column="commentary"] { min-width: unset; width: 100%; } + +section#operators.panel.list>div.row[data-row="operator"]:not(:nth-of-type(1)).transactions { + --background: var(--magma); +} + +section#operators.panel.list>div.row[data-row="operator"]:not(:nth-of-type(1)):nth-child(2n + 1).transactions { + --background: var(--magma-above); +} + +section#operators.panel.list>div.row[data-row="operator"]:not(:nth-of-type(1)).transactions>span:is(.important, .interactive:is(:hover, :focus)) { + --background: var(--magma-important); +} + +section#operators.panel.list>div.row[data-row="operator"]:not(:nth-of-type(1)):nth-child(2n + 1).transactions>span:is(.important, .interactive:is(:hover, :focus)) { + --background: var(--magma-important-above); +} diff --git a/mirzaev/ebala/system/public/css/pages/settings.css b/mirzaev/ebala/system/public/css/pages/settings.css new file mode 100755 index 0000000..3d162ff --- /dev/null +++ b/mirzaev/ebala/system/public/css/pages/settings.css @@ -0,0 +1,73 @@ +@charset "UTF-8"; + +main>section#settings.panel { + z-index: 1000; + width: 800px; + position: relative; + margin-bottom: calc(15vh - 45px); + display: flex; + flex-direction: column; + gap: 60px; + border-radius: 3px; +} + +main>section#settings.panel>section.category { + display: flex; + flex-direction: column; + gap: 20px; + padding: 30px 40px; + border-radius: 3px; + background-color: var(--snow); +} + +main>section#settings.panel>section.category>div { + padding-bottom: 6px; + display: flex; + flex-direction: column; +} + +main>section#settings.panel>section.category>div>div { + margin: 0; + margin-left: 30px; + margin-bottom: 6px; + display: inline-flex; + align-items: center; +} + +main>section#settings.panel>section.category>div>div>h2 { + margin: 0; +} + +main>section#settings.panel>section.category>div>div>i { + position: relative; + margin-left: auto; + margin-right: 20px; +} + +main>section#settings.panel>section.category>div>small { + margin-left: 20px; + width: 100%; +} + +main>section#settings.panel>section.category>section.subcategory { + padding: 20px 20px; + display: flex; + flex-direction: column; + gap: 8px; + border-radius: 3px; + background-color: var(--snow-deep); +} + +main>section#settings.panel>section.category>section.subcategory>h3 { + margin: 6px 0px 3px 25px; +} + +main>section#settings.panel>section.category>section.subcategory>label>input { + width: 90px; +} + +main>section#settings.panel>section.category>section.subcategory>p.empty { + text-align: center; + padding: 0 20%; + font-weight: bold; +} diff --git a/mirzaev/ebala/system/public/css/popup.css b/mirzaev/ebala/system/public/css/popup.css index 95f28c5..cded788 100644 --- a/mirzaev/ebala/system/public/css/popup.css +++ b/mirzaev/ebala/system/public/css/popup.css @@ -44,7 +44,7 @@ div#popup>section.calculated { div#popup>section.list { max-width: max(70vw, 1300px); - max-height: max(62vh, 600px); + max-height: max(62vh, 600px); display: flex; flex-direction: column; padding: 30px; @@ -54,7 +54,7 @@ div#popup>section.list { div#popup>section.list.extensive { max-width: unset; - max-height: unset; + max-height: unset; } div#popup>section.list>h3 { @@ -141,6 +141,25 @@ div#popup>section.list>section.main>div.column>:is(div, section).row>label>input text-align: center; } +div#popup>section.list>section.main>div.column> :is(div, select).row:has(>label>select[multiple]) { + height: auto; + max-height: 60px; +} + +div#popup>section.list>section.main>div.column> :is(div, select).row>label>select[multiple] { + height: auto; + padding: 0px; + border-radius: 3px; +} + +div#popup>section.list>section.main>div.column> :is(div, select).row>label>select[multiple]>option { + padding: 5px 10px; +} + +/* div#popup>section.list>section.main>div.column> :is(div, select).row>label>select[multiple]>option[selected] { + background-color: var(--background-above-5); +} */ + div#popup>section.list>section.main>div.column>:is(div, section).row>label> :is(input, button):only-child { width: 100%; } @@ -297,7 +316,7 @@ div#popup>section.list.errors>section.body>dl>dd { margin-left: 20px; } -div#popup > section.list .separator { +div#popup>section.list .separator { border-top: 2px solid var(--separator, var(--cloud)); padding-top: 10px; margin-top: 10px; diff --git a/mirzaev/ebala/system/public/index.php b/mirzaev/ebala/system/public/index.php index 465d095..6a11e40 100755 --- a/mirzaev/ebala/system/public/index.php +++ b/mirzaev/ebala/system/public/index.php @@ -65,8 +65,6 @@ $router->write('/administrators', 'administrator', 'index', 'GET'); $router->write('/administrators', 'administrator', 'index', 'POST'); $router->write('/administrators/read', 'administrator', 'read', 'POST'); $router->write('/administrators/create', 'administrator', 'create', 'POST'); -$router->write('/settings', 'settings', 'index', 'GET'); -$router->write('/settings', 'settings', 'index', 'POST'); $router->write('/$id', 'account', 'index', 'GET'); $router->write('/$id', 'account', 'index', 'POST'); $router->write('/$id/fields', 'account', 'fields', 'POST'); @@ -107,6 +105,9 @@ $router->write('/elements/menu', 'index', 'menu', 'POST'); $router->write('/payments/workers', 'payments', 'workers', 'POST'); $router->write('/payments/markets', 'payments', 'markets', 'POST'); $router->write('/payments/confirm/$type', 'payments', 'confirm', 'POST'); +$router->write('/settings', 'settings', 'index', 'GET'); +$router->write('/settings', 'settings', 'index', 'POST'); +$router->write('/settings/$id/write', 'settings', 'write', 'POST'); // Инициализация ядра $core = new core(namespace: __NAMESPACE__, router: $router, controller: new controller(false), model: new model(false)); diff --git a/mirzaev/ebala/system/public/js/core.js b/mirzaev/ebala/system/public/js/core.js index 0391c76..e208cdf 100644 --- a/mirzaev/ebala/system/public/js/core.js +++ b/mirzaev/ebala/system/public/js/core.js @@ -23,6 +23,11 @@ if (typeof window.core !== "function") { : (this.subdomain === "xn--80aalqawikqchmc" ? "administrator" : "worker"))); + + static numbers(e) { + const c = (e.which) ? e.which : e.keyCode; + return !(c !== 46 && c !== 44 && c > 31 && (c < 48 || c > 57)); + } }; } diff --git a/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js b/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js index 6e48107..272001f 100644 --- a/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js +++ b/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js @@ -3261,3 +3261,7 @@ })); //# sourceMappingURL=imask.js.map +// Вызов события: "инициализировано" +document.dispatchEvent( + new CustomEvent("imask.initialized"), +); diff --git a/mirzaev/ebala/system/public/js/loader.js b/mirzaev/ebala/system/public/js/loader.js index b7f637f..77ee27f 100755 --- a/mirzaev/ebala/system/public/js/loader.js +++ b/mirzaev/ebala/system/public/js/loader.js @@ -176,6 +176,13 @@ if (typeof window.loader !== "function") { }) .then((response) => response.text()) .then((data) => { + // Write path in history + history.pushState(this.storage, "/settings", "/settings"); + + // Write path to the current directory buffer + core.page = 'settings'; + + // Write content in document document.body.getElementsByTagName("main")[0].innerHTML = data; }); } diff --git a/mirzaev/ebala/system/public/js/operators.js b/mirzaev/ebala/system/public/js/operators.js index 409e812..f373eeb 100644 --- a/mirzaev/ebala/system/public/js/operators.js +++ b/mirzaev/ebala/system/public/js/operators.js @@ -111,7 +111,7 @@ if (typeof window.operators !== "function") { } /** - * Обновить администратора (вызов демпфера) + * Обновить оператора (вызов демпфера) * * @param {HTMLElement} row Строка
* @param {HTMLElement} button Кнопка