forked from mirzaev/site-tordv-calculator
		
	Панель настроек, асинхронные запросы, калькулятор лазерной резки
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./vendor
 | 
				
			||||||
							
								
								
									
										157
									
								
								mirzaev/calculator/system/controllers/accounts_controller.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								mirzaev/calculator/system/controllers/accounts_controller.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\calculator\controllers\core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\accounts_model as accounts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Контроллер пользователей
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\controllers
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class accounts_controller extends core
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Страница профиля
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $params
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function index(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Регистрация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры запроса
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return string|null HTML-документ
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function registration(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['account' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($vars['account'] = accounts::registration(email: $vars['email'] ?? null, password: $vars['password'] ?? null, errors: $vars['errors']['account'])) {
 | 
				
			||||||
 | 
					            // Удалось зарегистрироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), $vars)) {
 | 
				
			||||||
 | 
					                // Удалось аутентифицироваться
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не удалось аутентифицироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись кода ответа
 | 
				
			||||||
 | 
					                http_response_code(401);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Не удалось зарегистрироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись кода ответа
 | 
				
			||||||
 | 
					            http_response_code(401);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Генерация представления
 | 
				
			||||||
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Аутентификация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры запроса
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return string|null HTML-документ
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function authentication(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['account' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), errors: $vars['errors']['account'])) {
 | 
				
			||||||
 | 
					            // Удалось аутентифицироваться
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Не удалось аутентифицироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись кода ответа
 | 
				
			||||||
 | 
					            http_response_code(401);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Генерация представления
 | 
				
			||||||
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Деаутентификация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры запроса
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return string|null HTML-документ
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function deauthentication(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['account' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (accounts::deauthentication(errors: $vars['errors']['account'])) {
 | 
				
			||||||
 | 
					            // Удалось деаутентифицироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Деинициализация аккаунта
 | 
				
			||||||
 | 
					            $vars['account'] = null;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Не удалось деаутентифицироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись кода ответа
 | 
				
			||||||
 | 
					            http_response_code(500);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Генерация представления
 | 
				
			||||||
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Данные аккаунта
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Если информацию запрашивает администратор, то вернётся вся, иначе только разрешённая публично
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры запроса
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return string JSON-документ
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function data(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['account' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($account = accounts::read(['id' => $vars['id']], $vars['errors'])) {
 | 
				
			||||||
 | 
					            // Найдены данные запрашиваемого аккаунта
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация аккаунта
 | 
				
			||||||
 | 
					            $vars['account'] = accounts::account($vars['errors']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($vars['account'] && $vars['account']['permissions']['accounts'] ?? 0 === 1) {
 | 
				
			||||||
 | 
					                // Удалось аутентифицироваться и пройдена проверка авторизации
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не удалось аутентифицироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Удаление запрещённых к публикации полей
 | 
				
			||||||
 | 
					                $account['password'] = $account['hash'] = $account['time'] = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Генерация ответа
 | 
				
			||||||
 | 
					            return json_encode($account ?? '');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Не найдены данные запрашиваемого аккаунта
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись кода ответа
 | 
				
			||||||
 | 
					            http_response_code(404);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace mirzaev\calculator\controllers;
 | 
					namespace mirzaev\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\calculator\controllers\core;
 | 
					use mirzaev\calculator\controllers\core;
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\calculators_model as calculators;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Twig\Loader\FilesystemLoader;
 | 
					use Twig\Loader\FilesystemLoader;
 | 
				
			||||||
use Twig\Environment as view;
 | 
					use Twig\Environment as view;
 | 
				
			||||||
@@ -119,4 +120,71 @@ final class calculator_controller extends core
 | 
				
			|||||||
        // Генерация представления
 | 
					        // Генерация представления
 | 
				
			||||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'laser.html', $vars);
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'laser.html', $vars);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Рассчёт
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0]
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @todo
 | 
				
			||||||
 | 
					     * 1. Отправлять данные в зависимости от разрешения (обычным пользователям только expenses)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function calculate(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['calculators' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация калькуляторов из тела запроса (подразумевается, что там массивы с параметрами)
 | 
				
			||||||
 | 
					        $calculators = json_decode(file_get_contents('php://input'), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация переменных для буфера вывода
 | 
				
			||||||
 | 
					        $machines = $managers = $engineers = $operators = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($calculators as $i => $calculator) {
 | 
				
			||||||
 | 
					            // Перебор калькуляторов
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (['type'] as $parameter) {
 | 
				
			||||||
 | 
					                // Перебор мета-параметров
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация общего параметра
 | 
				
			||||||
 | 
					                extract([$parameter => $calculator[$parameter] ?? null]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация параметра для обработчика калькулятора
 | 
				
			||||||
 | 
					                unset($calculator[$parameter]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация номера калькулятора в его категории
 | 
				
			||||||
 | 
					            $number = count($vars['errors']['calculators'][$type] ?? []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация журнала ошибок для калькулятора
 | 
				
			||||||
 | 
					            $calculator['errors'] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация журнала ошибок для буфера вывода
 | 
				
			||||||
 | 
					            $vars['errors']['calculators'][$type][$number] = &$calculator['errors'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация буфера параметров
 | 
				
			||||||
 | 
					            $parameters = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация параметра типа покупателя (подразумевается, что если не "entity", то "individual")
 | 
				
			||||||
 | 
					            $parameters['company'] = $calculator['buyer'] === 'entity';
 | 
				
			||||||
 | 
					            unset($calculator['buyer']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Перенос остальных параметров в буфер параметров
 | 
				
			||||||
 | 
					            $parameters += $calculator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // var_dump($parameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Расчёт
 | 
				
			||||||
 | 
					            [$machines, $managers, $engineers, $operators] = calculators::$type(...$parameters);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json_encode([
 | 
				
			||||||
 | 
					            'machines' => $machines,
 | 
				
			||||||
 | 
					            'managers' => $managers,
 | 
				
			||||||
 | 
					            'engineers' => $engineers,
 | 
				
			||||||
 | 
					            'operators' => $operators
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace mirzaev\calculator\controllers;
 | 
					namespace mirzaev\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\calculator\views\manager;
 | 
					use mirzaev\calculator\views\manager;
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\core as models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\minimal\controller;
 | 
					use mirzaev\minimal\controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,6 +26,9 @@ class core extends controller
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        parent::__construct();
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация ядра моделей (соединение с базой данных...)
 | 
				
			||||||
 | 
					        new models();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->view = new manager;
 | 
					        $this->view = new manager;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,18 @@ final class errors_controller extends core
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public function error404()
 | 
					    public function error404()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        // Запись кода ответа
 | 
				
			||||||
 | 
					        http_response_code(404);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Генерация представления
 | 
					        // Генерация представления
 | 
				
			||||||
        return 'Не найдено (404)';
 | 
					        return 'Не найдено (404)';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function error500()
 | 
					    public function error500()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        // Запись кода ответа
 | 
				
			||||||
 | 
					        http_response_code(500);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Генерация представления
 | 
					        // Генерация представления
 | 
				
			||||||
        return 'Внутренняя ошибка (500)';
 | 
					        return 'Внутренняя ошибка (500)';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace mirzaev\calculator\controllers;
 | 
					namespace mirzaev\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\calculator\controllers\core;
 | 
					use mirzaev\calculator\controllers\core;
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\accounts_model as accounts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Twig\Loader\FilesystemLoader;
 | 
					use Twig\Loader\FilesystemLoader;
 | 
				
			||||||
use Twig\Environment as view;
 | 
					use Twig\Environment as view;
 | 
				
			||||||
@@ -17,9 +18,15 @@ use Twig\Environment as view;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
final class main_controller extends core
 | 
					final class main_controller extends core
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public function index(array $params = [])
 | 
					    public function index(array $vars = [])
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['account' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Проверка аутентифицированности
 | 
				
			||||||
 | 
					        $vars['account'] = accounts::account($vars['errors']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Генерация представления
 | 
					        // Генерация представления
 | 
				
			||||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html');
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\calculator\controllers\core;
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\accounts_model as accounts;
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\settings_model as settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Контроллер страницы настроек
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\controllers
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class settings_controller extends core
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Настройки (страница)
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * HTML-документ со списком настроек
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function index(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['settings' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация аккаунта
 | 
				
			||||||
 | 
					        $vars['account'] = accounts::account($vars['errors']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Генерация представления
 | 
				
			||||||
 | 
					        return $this->view->render(DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Записать
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function write(array $vars = []): ?bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['settings' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация аккаунта
 | 
				
			||||||
 | 
					        $vars['account'] = accounts::account($vars['errors']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($vars['account'] && $vars['account']['permissions']['settings'] ?? 0 === 1) {
 | 
				
			||||||
 | 
					            // Удалось аутентифицироваться и пройдена проверка авторизации
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($vars['settings'] ?? [] as $name => $value) {
 | 
				
			||||||
 | 
					                // Перебор настроек
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                settings::write($name, $value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Прочитать
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function read(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					        $vars['errors'] = ['settings' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация аккаунта
 | 
				
			||||||
 | 
					        $vars['account'] = accounts::account($vars['errors']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация буфера вывода
 | 
				
			||||||
 | 
					        $settings = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($vars['settings'] ?? [] as $name) {
 | 
				
			||||||
 | 
					            // Перебор настроек
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $settings[] = settings::read($name, $vars['account'] && $vars['account']['permissions']['settings'] ?? 0 === 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json_encode($settings);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										531
									
								
								mirzaev/calculator/system/models/accounts_model.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										531
									
								
								mirzaev/calculator/system/models/accounts_model.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,531 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use pdo;
 | 
				
			||||||
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Модель регистрации, аутентификации и авторизации
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\models
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class accounts_model extends core
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Регистрация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Входной псевдоним
 | 
				
			||||||
 | 
					     * @param string $email Почта
 | 
				
			||||||
 | 
					     * @param string $password Пароль (password)
 | 
				
			||||||
 | 
					     * @param bool $authentication Автоматическая аутентификация в случае успешной регистрации
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array|bool Аккаунт, если удалось аутентифицироваться
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function registration(string $name = null, string $email = null, string $password, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (static::account($errors)) {
 | 
				
			||||||
 | 
					                // Аутентифицирован пользователь
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись ошибки
 | 
				
			||||||
 | 
					                throw new exception('Уже аутентифицирован');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (empty($account = static::read(['name' => $name]) or $account = static::read(['email' => $email]))) {
 | 
				
			||||||
 | 
					                // Не удалось найти аккаунт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (static::write($name, $email, $password, $errors)) {
 | 
				
			||||||
 | 
					                    // Удалось зарегистрироваться
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return $account;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Удалось найти аккаунт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return $account;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Аутентификация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $login Входной псевдоним
 | 
				
			||||||
 | 
					     * @param string $password Пароль (password)
 | 
				
			||||||
 | 
					     * @param bool $remember Функция "Запомнить меня" - увеличенное время хранения cookies
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Аккаунт (если не найден, то пустой массив)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function authentication(string $login, string $password, bool $remember = false, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (static::account($errors)) {
 | 
				
			||||||
 | 
					                // Аутентифицирован пользователь
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись ошибки
 | 
				
			||||||
 | 
					                throw new exception('Уже аутентифицирован');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (empty($account = static::read(['name' => $login]) or $account = static::read(['email' => $login]))) {
 | 
				
			||||||
 | 
					                // Не удалось найти аккаунт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                throw new exception('Не удалось найти аккаунт');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (password_verify($password, $account['password'])) {
 | 
				
			||||||
 | 
					                // Совпадают хеши паролей
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация идентификатора сессии
 | 
				
			||||||
 | 
					                session_id($account['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация названия сессии
 | 
				
			||||||
 | 
					                session_name('id');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация сессии
 | 
				
			||||||
 | 
					                session_start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация времени хранения хеша
 | 
				
			||||||
 | 
					                $time = time() + ($remember ? 604800 : 86400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация хеша
 | 
				
			||||||
 | 
					                $hash = static::hash((int) $account['id'], crypt($account['password'], time() . $account['id']), $time, $errors)['hash'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация cookies
 | 
				
			||||||
 | 
					                setcookie("hash", $hash, $time, path: '/', secure: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return $account;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не совпадают хеши паролей
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                throw new exception('Неправильный пароль');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Аутентификация
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return bool Удалось ли деаутентифицироваться
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function deauthentication(array &$errors = []): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if ($account = static::account($errors)) {
 | 
				
			||||||
 | 
					                // Аутентифицирован пользователь
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация запроса
 | 
				
			||||||
 | 
					                $request = static::$db->prepare("UPDATE `accounts` SET `hash` = null, `time` = 0 WHERE `id` = :id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Параметры запроса
 | 
				
			||||||
 | 
					                $params = [
 | 
				
			||||||
 | 
					                    ":id" => $account['id'],
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Отправка запроса
 | 
				
			||||||
 | 
					                $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ответа
 | 
				
			||||||
 | 
					                $request->fetch(pdo::FETCH_ASSOC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Деинициализация cookies
 | 
				
			||||||
 | 
					                setcookie("id", '', 0, path: '/', secure: true);
 | 
				
			||||||
 | 
					                setcookie("hash", '', 0, path: '/', secure: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не аутентифицирован пользователь
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись ошибки
 | 
				
			||||||
 | 
					                throw new exception('Не аутентифицирован');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Прочитать данные аккаунта, если пользователь аутентифицирован
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Можно использовать как проверку на аутентифицированность
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Аккаунт (если не найден, то пустой массив)
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @todo 1. Сделать в static::read() возможность передачи нескольких параметров и перенести туда непосредственно чтение аккаунта с проверкой хеша
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function account(array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!empty($_COOKIE['id']) && !empty($_COOKIE['hash'])) {
 | 
				
			||||||
 | 
					                // Аутентифицирован аккаунт (найдены cookie и они хранят значения - подразумевается, что не null или пустое)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($_COOKIE['hash'] === static::hash((int) $_COOKIE['id'], errors: $errors)['hash']) {
 | 
				
			||||||
 | 
					                    // Совпадает переданный хеш с тем, что хранится в базе данных
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Не совпадает переданный хеш с тем, что хранится в базе данных
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Генерация ошибки
 | 
				
			||||||
 | 
					                    throw new exception('Вы аутентифицированы с другого устройства (не совпадают хеши аутентификации)');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация запроса
 | 
				
			||||||
 | 
					                $request = static::$db->prepare("SELECT * FROM `accounts` WHERE `id` = :id && `hash` = :hash");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Параметры запроса
 | 
				
			||||||
 | 
					                $params = [
 | 
				
			||||||
 | 
					                    ":id" => $_COOKIE['id'],
 | 
				
			||||||
 | 
					                    ":hash" => $_COOKIE['hash'],
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Отправка запроса
 | 
				
			||||||
 | 
					                $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ответа
 | 
				
			||||||
 | 
					                if (empty($account = $request->fetch(pdo::FETCH_ASSOC))) {
 | 
				
			||||||
 | 
					                    // Не найдена связка идентификатора с хешем
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Генерация ошибки
 | 
				
			||||||
 | 
					                    throw new exception('Не найден пользотватель или время аутентификации истекло');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Чтение разрешений
 | 
				
			||||||
 | 
					                $account['permissions'] = static::permissions((int) $account['id'], $errors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return $account;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Прочитать разрешения аккаунта
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param int $id Идентификатор аккаунта
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Разрешения аккаунта, если найдены
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function permissions(int $id, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Инициализация запроса
 | 
				
			||||||
 | 
					            $request = static::$db->prepare("SELECT * FROM `permissions` WHERE `id` = :id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Параметры запроса
 | 
				
			||||||
 | 
					            $params = [
 | 
				
			||||||
 | 
					                ":id" => $id
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Отправка запроса
 | 
				
			||||||
 | 
					            $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Генерация ответа
 | 
				
			||||||
 | 
					            if (empty($response = $request->fetch(pdo::FETCH_ASSOC))) {
 | 
				
			||||||
 | 
					                // Не найдены разрешения
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ошибки
 | 
				
			||||||
 | 
					                throw new exception('Не найдены разрешения');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Удаление ненужных данных
 | 
				
			||||||
 | 
					            unset($response['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return $response;
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Запись пользователя в базу данных
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string|null $name Имя
 | 
				
			||||||
 | 
					     * @param string|null $email Почта
 | 
				
			||||||
 | 
					     * @param string|null $password Пароль
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Аккаунт (если не найден, то пустой массив)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function write(string|null $name = null, string|null $email = null, string|null $password = null, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Инициализация параметров запроса
 | 
				
			||||||
 | 
					            $params = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isset($name)) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // Проверка параметра
 | 
				
			||||||
 | 
					                    if (iconv_strlen($name) < 3) throw new exception('Длина имени должна быть не менее 3 символов');
 | 
				
			||||||
 | 
					                    if (iconv_strlen($name) > 60) throw new exception('Длина имени должна быть не более 60 символов');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Запись в буфер параметров запроса
 | 
				
			||||||
 | 
					                    $params[':name'] = $name;
 | 
				
			||||||
 | 
					                } catch (exception $e) {
 | 
				
			||||||
 | 
					                    // Запись в журнал ошибок
 | 
				
			||||||
 | 
					                    $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    goto end;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isset($email)) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // Проверка параметра
 | 
				
			||||||
 | 
					                    if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) throw new exception('Не удалось распознать почту');
 | 
				
			||||||
 | 
					                    if (iconv_strlen($email) < 3) throw new exception('Длина почты должна быть не менее 3 символов');
 | 
				
			||||||
 | 
					                    if (iconv_strlen($email) > 60) throw new exception('Длина почты должна быть не более 80 символов');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Запись в буфер параметров запроса
 | 
				
			||||||
 | 
					                    $params[':email'] = $email;
 | 
				
			||||||
 | 
					                } catch (exception $e) {
 | 
				
			||||||
 | 
					                    // Запись в журнал ошибок
 | 
				
			||||||
 | 
					                    $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    goto end;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isset($password)) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // Проверка параметра
 | 
				
			||||||
 | 
					                    if (iconv_strlen($password) < 3) throw new exception('Длина пароля должна быть не менее 3 символов');
 | 
				
			||||||
 | 
					                    if (iconv_strlen($password) > 60) throw new exception('Длина пароля должна быть не более 120 символов');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Запись в буфер параметров запроса
 | 
				
			||||||
 | 
					                    $params[':password'] = password_hash($password, PASSWORD_BCRYPT);
 | 
				
			||||||
 | 
					                } catch (exception $e) {
 | 
				
			||||||
 | 
					                    // Запись в журнал ошибок
 | 
				
			||||||
 | 
					                    $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    goto end;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация запроса
 | 
				
			||||||
 | 
					            $request = static::$db->prepare("INSERT INTO `accounts` (" . (isset($name) ? '`name`' : '') . (isset($name) && isset($email) ? ', ' : '') . (isset($email) ? '`email`' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? '`password`' : '') . ") VALUES (" . (isset($name) ? ':name' : '') . (isset($name) && isset($email) ? ', ' : '') .  (isset($email) ? ':email' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? ':password' : '') . ")");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Отправка запроса
 | 
				
			||||||
 | 
					            $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Генерация ответа
 | 
				
			||||||
 | 
					            $request->fetch(pdo::FETCH_ASSOC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (isset($name)) {
 | 
				
			||||||
 | 
					                    // Передано имя аккаунта
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Чтение аккаунта
 | 
				
			||||||
 | 
					                    $account = static::read(['name' => $name]);
 | 
				
			||||||
 | 
					                } else if (isset($email)) {
 | 
				
			||||||
 | 
					                    // Передана почта аккаунта
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Чтение аккаунта
 | 
				
			||||||
 | 
					                    $account = static::read(['email' => $email]);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Не передано ни имя, ни почта
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    throw new exception('Не переданны данные для полноценной регистрации аккаунта');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация запроса
 | 
				
			||||||
 | 
					                $request = static::$db->prepare("INSERT INTO `permissions` (`id`) VALUES (:id)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация параметров
 | 
				
			||||||
 | 
					                $params = [
 | 
				
			||||||
 | 
					                    ':id' => $account['id']
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Отправка запроса
 | 
				
			||||||
 | 
					                $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ответа
 | 
				
			||||||
 | 
					                $request->fetch(pdo::FETCH_ASSOC);
 | 
				
			||||||
 | 
					            } catch (exception $e) {
 | 
				
			||||||
 | 
					                // Запись в журнал ошибок
 | 
				
			||||||
 | 
					                $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Конец выполнения
 | 
				
			||||||
 | 
					        end:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return isset($account) && $account ? $account : [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Чтение пользователя из базы данных
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $search Поиск ('поле' => 'значение'), работает только с одним полем
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Аккаунт, если найден
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function read(array $search, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Инициализация данных для поиска
 | 
				
			||||||
 | 
					            $field = array_keys($search)[0] ?? null;
 | 
				
			||||||
 | 
					            $value = $search[$field] ?? null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (empty($field)) {
 | 
				
			||||||
 | 
					                // Получено пустое значение поля
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись ошибки
 | 
				
			||||||
 | 
					                throw new exception('Пустое значение поля для поиска');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация запроса
 | 
				
			||||||
 | 
					            $request = static::$db->prepare("SELECT * FROM `accounts` WHERE `$field` = :field LIMIT 1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Параметры запроса
 | 
				
			||||||
 | 
					            $params = [
 | 
				
			||||||
 | 
					                ":field" => $value,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Отправка запроса
 | 
				
			||||||
 | 
					            $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Генерация ответа
 | 
				
			||||||
 | 
					            if ($account = $request->fetch(pdo::FETCH_ASSOC)) {
 | 
				
			||||||
 | 
					                // Найден аккаунт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    if ($permissions = static::permissions((int) $account['id'], $errors)) {
 | 
				
			||||||
 | 
					                        // Найдены разрешения
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Запись в буфер данных аккаунта
 | 
				
			||||||
 | 
					                        $account['permissions'] = $permissions;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // Не найдены разрешения
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        throw new exception('Не удалось найти и прочитать разрешения');
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (exception $e) {
 | 
				
			||||||
 | 
					                    // Запись в журнал ошибок
 | 
				
			||||||
 | 
					                    $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не найден аккаунт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                throw new exception('Не удалось найти аккаунт');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return isset($account) && $account ? $account : [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Запись или чтение хеша из базы данных
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param int $id Идентификатор аккаунта
 | 
				
			||||||
 | 
					     * @param int|null $hash Хеш аутентифиакции
 | 
				
			||||||
 | 
					     * @param string|null $time Время хранения хеша
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array ['hash' => $hash, 'time' => $time]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function hash(int $id, string|null $hash = null, int|null $time = null, array &$errors = []): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (isset($hash, $time)) {
 | 
				
			||||||
 | 
					                // Переданы хеш и его время хранения
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация запроса
 | 
				
			||||||
 | 
					                $request = static::$db->prepare("UPDATE `accounts` SET `hash` = :hash, `time` = :time WHERE `id` = :id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Параметры запроса
 | 
				
			||||||
 | 
					                $params = [
 | 
				
			||||||
 | 
					                    ":id" => $id,
 | 
				
			||||||
 | 
					                    ":hash" => $hash,
 | 
				
			||||||
 | 
					                    ":time" => $time,
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Отправка запроса
 | 
				
			||||||
 | 
					                $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ответа
 | 
				
			||||||
 | 
					                $request->fetch(pdo::FETCH_ASSOC);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Не переданы хеш и его время хранения
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инициализация запроса
 | 
				
			||||||
 | 
					                $request = static::$db->prepare("SELECT `hash`, `time` FROM `accounts` WHERE `id` = :id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Параметры запроса
 | 
				
			||||||
 | 
					                $params = [
 | 
				
			||||||
 | 
					                    ":id" => $id,
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Отправка запроса
 | 
				
			||||||
 | 
					                $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Генерация ответа
 | 
				
			||||||
 | 
					                extract((array) $request->fetch(pdo::FETCH_ASSOC));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!empty($response['time']) && $response['time'] <= time()) {
 | 
				
			||||||
 | 
					                    // Истекло время жизни хеша
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Инициализация запроса
 | 
				
			||||||
 | 
					                    $request = static::$db->prepare("UPDATE `accounts` SET `hash` = :hash, `time` = :time WHERE `id` = :id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Параметры запроса
 | 
				
			||||||
 | 
					                    $params = [
 | 
				
			||||||
 | 
					                        ":id" => $id,
 | 
				
			||||||
 | 
					                        ":hash" => null,
 | 
				
			||||||
 | 
					                        ":time" => null,
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Отправка запроса
 | 
				
			||||||
 | 
					                    $request->execute($params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Генерация ответа
 | 
				
			||||||
 | 
					                    $response = $request->fetch(pdo::FETCH_ASSOC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Генерация ошибки
 | 
				
			||||||
 | 
					                    throw new exception('Время аутентификации истекло');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ['hash' => $hash, 'time' => $time];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										186
									
								
								mirzaev/calculator/system/models/calculators_model.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								mirzaev/calculator/system/models/calculators_model.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,186 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\calculator\models\settings_model as settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Модель калькуляторов
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\models
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class calculators_model extends core
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Рассчет калькулятора лазерной резки
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param bool|int|string|null $company Юридическое лицо? Или физическое лицо?
 | 
				
			||||||
 | 
					     * @param string|null $complexity Сложность ('easy', 'medium', 'hard')
 | 
				
			||||||
 | 
					     * @param int|string|null $width Высота (мм)
 | 
				
			||||||
 | 
					     * @param int|string|null $height Ширина (мм)
 | 
				
			||||||
 | 
					     * @param int|string|null $lenght Длина (мм)
 | 
				
			||||||
 | 
					     * @param int|string|null $amount Количество
 | 
				
			||||||
 | 
					     * @param string|null $metal Тип металла
 | 
				
			||||||
 | 
					     * @param int|string|null $holes Количество отверстий
 | 
				
			||||||
 | 
					     * @param int|string|null $diameter Диаметр отверстий (мм)
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array|bool Аккаунт, если удалось аутентифицироваться
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @todo
 | 
				
			||||||
 | 
					     * 1. Значения по умолчанию брать из настроек в базе данных
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function laser(
 | 
				
			||||||
 | 
					        bool|int|string|null $company = null,
 | 
				
			||||||
 | 
					        string|null $complexity = null,
 | 
				
			||||||
 | 
					        int|string|null $width = null,
 | 
				
			||||||
 | 
					        int|string|null $height = null,
 | 
				
			||||||
 | 
					        int|string|null $length = null,
 | 
				
			||||||
 | 
					        int|string|null $amount = null,
 | 
				
			||||||
 | 
					        string|null $metal = null,
 | 
				
			||||||
 | 
					        int|string|null $holes = null,
 | 
				
			||||||
 | 
					        int|string|null $diameter = null,
 | 
				
			||||||
 | 
					        array &$errors = []
 | 
				
			||||||
 | 
					    ): array {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Инициализация переменных для буфера вывода
 | 
				
			||||||
 | 
					            $machine = $manager = $engineer = $operator = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация значений по умолчанию (см. @todo)
 | 
				
			||||||
 | 
					            $company = (bool) ($company ?? settings::read('default_buyer', $errors) === 'entity' ?? false);
 | 
				
			||||||
 | 
					            $complexity = (string) ($complexity ?? settings::read('default_complexity', $errors) ?? 'medium');
 | 
				
			||||||
 | 
					            $width = (int) ($width ?? settings::read('default_width', $errors) ?? 500);
 | 
				
			||||||
 | 
					            $height = (int) ($height ?? settings::read('default_height', $errors) ?? 500);
 | 
				
			||||||
 | 
					            $length = (int) ($length ?? settings::read('default_length', $errors) ?? 1);
 | 
				
			||||||
 | 
					            $amount = (int) ($amount ?? settings::read('default_amount', $errors) ?? 1);
 | 
				
			||||||
 | 
					            $metal = (string) ($metal ?? settings::read('default_metal', $errors) ?? 'stainless_steel');
 | 
				
			||||||
 | 
					            $holes = (int) ($holes ?? settings::read('default_holes', $errors) ?? 0);
 | 
				
			||||||
 | 
					            $diameter = (int) ($diameter ?? settings::read('default_diameter', $errors) ?? 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Стоисмость киловатта электроэнергии
 | 
				
			||||||
 | 
					            $electricity = settings::read('electricity', $errors) ?? 6.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Потребляемая электроэнергия станком (квт/ч)
 | 
				
			||||||
 | 
					            $power = settings::read('laser_power', $errors) ?? 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 1 мм толщина = 220 мм/с рез у нержавеющей стали
 | 
				
			||||||
 | 
					            $speed = 220;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Вычисление площади
 | 
				
			||||||
 | 
					            $area = $width * $height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Коэффициент сложности
 | 
				
			||||||
 | 
					            $coefficient = ($area <= (settings::read('coefficient_area_less', $errors) ?? 10000) || $area >= (settings::read('coefficient_area_more', $errors) ?? 100000) ? (settings::read('coefficient_area_degree', $errors) ?? 0.2) : 0) + match ($complexity) {
 | 
				
			||||||
 | 
					                'easy' => (settings::read('coefficient_complexity_easy', $errors) ?? 0.8),
 | 
				
			||||||
 | 
					                'medium' => (settings::read('coefficient_complexity_medium', $errors) ?? 1),
 | 
				
			||||||
 | 
					                'hard' => (settings::read('coefficient_complexity_hard', $errors) ?? 1.2),
 | 
				
			||||||
 | 
					                default => (settings::read('coefficient_complexity_medium', $errors) ?? 1)
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Расчет длины реза (мм)
 | 
				
			||||||
 | 
					            eval(settings::read('laser_formula_cutting', $errors) ?? '$cutting = 3.14 * $diameter * $holes + ($width * 2 + $height * 2) * $coefficient;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Скорость реза в час (мм/с)
 | 
				
			||||||
 | 
					            eval(settings::read('laser_formula_speed', $errors) ?? '$speed = 3600 * $speed;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Стоимость реза в час
 | 
				
			||||||
 | 
					            eval(settings::read('laser_formula_hour', $errors) ?? '$hour = $electricity * $power;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Стоимость 1 миллиметра реза
 | 
				
			||||||
 | 
					            eval(settings::read('laser_formula_millimeter', $errors) ?? '$millimeter = $hour / $speed;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Cтанок (стоимость работы)
 | 
				
			||||||
 | 
					            eval(settings::read('laser_formula_machine', $errors) ?? '$machine = $cutting * $millimeter * $amount;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var_dump($company, $complexity, $width, $height, $length, $amount, $metal, $holes, $diameter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($company) {
 | 
				
			||||||
 | 
					                // Юридическое лицо
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('manager_entity_min', $errors) ?? 1;
 | 
				
			||||||
 | 
					                $max = settings::read('manager_entity_max', $errors) ?? 7;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Менеджер (стоимость работы)
 | 
				
			||||||
 | 
					                $manager = (settings::read('manager_entity_hour', $errors) ?? 200) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('engineer_entity_min', $errors) ?? 2;
 | 
				
			||||||
 | 
					                $max = settings::read('engineer_entity_max', $errors) ?? 72;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инженер (стоимость работы)
 | 
				
			||||||
 | 
					                $engineer = (settings::read('engineer_entity_hour', $errors) ?? 400) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('operator_entity_min', $errors) ?? 0.33;
 | 
				
			||||||
 | 
					                $max = settings::read('operator_entity_max', $errors) ?? 16;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Оператор
 | 
				
			||||||
 | 
					                $operator = (settings::read('operator_entity_hour', $errors) ?? 200) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Физическое лицо
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('manager_individual_min', $errors) ?? 1;
 | 
				
			||||||
 | 
					                $max = settings::read('manager_individual_max', $errors) ?? 3;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Менеджер (стоимость работы)
 | 
				
			||||||
 | 
					                $manager = (settings::read('manager_individual_hour', $errors) ?? 200) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('manager_individual_min', $errors) ?? 2;
 | 
				
			||||||
 | 
					                $max = settings::read('manager_individual_max', $errors) ?? 10;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Инженер (стоимость работы)
 | 
				
			||||||
 | 
					                $engineer = (settings::read('engineer_individual_hour', $errors) ?? 300) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $min = settings::read('manager_individual_min', $errors) ?? 0.33;
 | 
				
			||||||
 | 
					                $max = settings::read('manager_individual_max', $errors) ?? 7;
 | 
				
			||||||
 | 
					                $average = ($min + $max) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Оператор (стоимость работы)
 | 
				
			||||||
 | 
					                $operator = (settings::read('operator_individual_hour', $errors) ?? 200) * match ($complexity) {
 | 
				
			||||||
 | 
					                    'easy' => $min,
 | 
				
			||||||
 | 
					                    'medium' => $average,
 | 
				
			||||||
 | 
					                    'hard' => $max,
 | 
				
			||||||
 | 
					                    default => $average
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [[$machine], [$manager], [$engineer], [$operator]];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										139
									
								
								mirzaev/calculator/system/models/core.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								mirzaev/calculator/system/models/core.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\minimal\model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use pdo;
 | 
				
			||||||
 | 
					use pdoexception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Ядро моделей
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\models
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class core extends model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Соединение с базой данных
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected static PDO $db ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(pdo $db = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (isset($db)) {
 | 
				
			||||||
 | 
					            // Получена инстанция соединения с базой данных
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись и инициализация соединения с базой данных
 | 
				
			||||||
 | 
					            $this->__set('db', $db);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Не получена инстанция соединения с базой данных
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация соединения с базой данных по умолчанию
 | 
				
			||||||
 | 
					            $this->__get('db');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Записать свойство
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название
 | 
				
			||||||
 | 
					     * @param mixed $value Значение
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __set(string $name, mixed $value = null): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        match ($name) {
 | 
				
			||||||
 | 
					            'db' => (function () use ($value) {
 | 
				
			||||||
 | 
					                if ($this->__isset('db')) {
 | 
				
			||||||
 | 
					                    // Свойство уже было инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Выброс исключения (неудача)
 | 
				
			||||||
 | 
					                    throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Свойство ещё не было инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if ($value instanceof pdo) {
 | 
				
			||||||
 | 
					                        // Передано подходящее значение
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Запись свойства (успех)
 | 
				
			||||||
 | 
					                        self::$db = $value;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // Передано неподходящее значение
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Выброс исключения (неудача)
 | 
				
			||||||
 | 
					                        throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией PDO', 500);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })(),
 | 
				
			||||||
 | 
					            default => parent::__set($name, $value)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Прочитать свойство
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return mixed Содержимое
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __get(string $name): mixed
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return match ($name) {
 | 
				
			||||||
 | 
					            'db' => (function () {
 | 
				
			||||||
 | 
					                if (!$this->__isset('db')) {
 | 
				
			||||||
 | 
					                    // Свойство не инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Инициализация значения по умолчанию исходя из настроек
 | 
				
			||||||
 | 
					                    $this->__set('db', new pdo(\TYPE . ':dbname=' . \BASE . ';host=' . \HOST, LOGIN, PASSWORD));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return self::$db;
 | 
				
			||||||
 | 
					            })(),
 | 
				
			||||||
 | 
					            default => parent::__get($name)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Проверить свойство на инициализированность
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __isset(string $name): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return match ($name) {
 | 
				
			||||||
 | 
					            default => parent::__isset($name)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Удалить свойство
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __unset(string $name): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        match ($name) {
 | 
				
			||||||
 | 
					            default => parent::__isset($name)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Статический вызов
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название
 | 
				
			||||||
 | 
					     * @param array $arguments Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function __callStatic(string $name, array $arguments): mixed
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        match ($name) {
 | 
				
			||||||
 | 
					            'db' => (new static)->__get('db'),
 | 
				
			||||||
 | 
					            default => throw new exception("Не найдено свойство или функция: $name", 500)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								mirzaev/calculator/system/models/settings_model.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								mirzaev/calculator/system/models/settings_model.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use pdo;
 | 
				
			||||||
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Модель настроек
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\calculator\models
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					final class settings_model extends core
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Прочитать
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $name Название параметра
 | 
				
			||||||
 | 
					     * @param array &$errors Журнал ошибок
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array Аккаунт, если найден
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static function read(string $name, array &$errors = []): int|float|string|array|null
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Инициализация запроса
 | 
				
			||||||
 | 
					            $request = static::$db->prepare("SELECT `$name` FROM `settings` WHERE `id` = 1 ORDER BY `id` DESC LIMIT 1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Отправка запроса
 | 
				
			||||||
 | 
					            $request->execute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Генерация ответа
 | 
				
			||||||
 | 
					            return $request->fetchColumn();
 | 
				
			||||||
 | 
					        } catch (exception $e) {
 | 
				
			||||||
 | 
					            // Запись в журнал ошибок
 | 
				
			||||||
 | 
					            $errors[] = $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,21 @@
 | 
				
			|||||||
#authentication>form {
 | 
					#authentication>:is(form, div) {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication .exit {
 | 
				
			||||||
 | 
					    margin-top: 25px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication p {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication p>span {
 | 
				
			||||||
 | 
					    margin-left: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#authentication>form>input:is([type="text"], [type="password"]) {
 | 
					#authentication>form>input:is([type="text"], [type="password"]) {
 | 
				
			||||||
    margin-bottom: 12px;
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -13,6 +26,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#authentication>form>.submit {
 | 
					#authentication>form>.submit {
 | 
				
			||||||
    margin-top: 6px;
 | 
					    margin-top: 6px;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,3 +42,26 @@
 | 
				
			|||||||
    border: unset;
 | 
					    border: unset;
 | 
				
			||||||
    border-radius: 0 3px 3px 0;
 | 
					    border-radius: 0 3px 3px 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication>form>input[type=submit].registration {
 | 
				
			||||||
 | 
					    padding: 7px 20px;
 | 
				
			||||||
 | 
					    background-color: #86781C;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication>form>input[type=submit].registration:hover {
 | 
				
			||||||
 | 
					    background-color: #9e8d20;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication>form>input[type=submit].registration:is(:active, :focus) {
 | 
				
			||||||
 | 
					    background-color: #776b19;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#authentication>form>ul.errors {
 | 
				
			||||||
 | 
					    margin-top: 18px;
 | 
				
			||||||
 | 
					    margin-bottom: 0px;
 | 
				
			||||||
 | 
					    padding: 10px;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    list-style: none;
 | 
				
			||||||
 | 
					    background-color: #ae8f8f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,26 +32,49 @@
 | 
				
			|||||||
    margin: 8px auto 15px;
 | 
					    margin: 8px auto 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>#result>:last-child {
 | 
					#calculator>#result>div {
 | 
				
			||||||
    width: 30%;
 | 
					    width: 30%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#calculator>#result>nav {
 | 
				
			||||||
 | 
					    margin-right: 30px;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#calculator>#result>nav>a {
 | 
				
			||||||
 | 
					    margin-top: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#calculator>#result>nav>a#calculate {
 | 
				
			||||||
 | 
					    padding: 10px 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>#result,
 | 
					#calculator>#result,
 | 
				
			||||||
#calculator>#result :last-child>p {
 | 
					#calculator>#result>div>p {
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>#result :last-child>p * {
 | 
					#calculator>#result>div>p * {
 | 
				
			||||||
    font-weight: normal;
 | 
					    font-weight: normal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>#result>:last-child,
 | 
					#calculator>#result>div,
 | 
				
			||||||
#calculator>#result :last-child>p>:is(#expenses, #income, #profit) {
 | 
					#calculator>#result>div>p>:is(#expenses, #income, #profit) {
 | 
				
			||||||
    margin-left: auto;
 | 
					    margin-left: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>#result :last-child>p>:last-child {
 | 
					#calculator>#result>nav>a:first-child:not(:only-of-type),
 | 
				
			||||||
 | 
					#calculator>#result>div>p:first-child:not(:only-of-type) {
 | 
				
			||||||
 | 
					    margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#calculator>#result>nav>a:last-child,
 | 
				
			||||||
 | 
					#calculator>#result>div>p:last-child {
 | 
				
			||||||
 | 
					    margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#calculator>#result>div>p>:last-child {
 | 
				
			||||||
    margin-left: 4px;
 | 
					    margin-left: 4px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,58 +82,58 @@
 | 
				
			|||||||
    margin-top: 30px;
 | 
					    margin-top: 30px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting']) {
 | 
					#calculator>.calculator {
 | 
				
			||||||
    padding: 5px 30px 10px 30px;
 | 
					    padding: 5px 30px 10px 30px;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div {
 | 
					#calculator>.calculator>div {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>label {
 | 
					#calculator>.calculator>div>label {
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    margin: auto 15px auto auto;
 | 
					    margin: auto 15px auto auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div:not(:last-child) {
 | 
					#calculator>.calculator>div:not(:last-child) {
 | 
				
			||||||
    margin-bottom: 15px;
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div {
 | 
					#calculator>.calculator>div>div {
 | 
				
			||||||
    width: 60%;
 | 
					    width: 60%;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div {
 | 
					#calculator>.calculator>div>div {
 | 
				
			||||||
    height: 30px;
 | 
					    height: 30px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>:is(input, small, span):not(.measured, :last-child) {
 | 
					#calculator>.calculator>div>div>:is(input, small, span):not(.measured, :last-child) {
 | 
				
			||||||
    margin-right: 5px !important;
 | 
					    margin-right: 5px !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>:is(input, .unit) {
 | 
					#calculator>.calculator>div>div>:is(input, .unit) {
 | 
				
			||||||
    font-size: small;
 | 
					    font-size: small;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input {
 | 
					#calculator>.calculator>div>div>input {
 | 
				
			||||||
    width: 25px;
 | 
					    width: 25px;
 | 
				
			||||||
    padding: 5px 10px;
 | 
					    padding: 5px 10px;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input.measured {
 | 
					#calculator>.calculator>div>div>input.measured {
 | 
				
			||||||
    padding-right: 3px;
 | 
					    padding-right: 3px;
 | 
				
			||||||
    text-align: right;
 | 
					    text-align: right;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>small {
 | 
					#calculator>.calculator>div>div>small {
 | 
				
			||||||
    margin: auto 0;
 | 
					    margin: auto 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input+.unit {
 | 
					#calculator>.calculator>div>div>input+.unit {
 | 
				
			||||||
    margin: auto 0;
 | 
					    margin: auto 0;
 | 
				
			||||||
    padding-top: unset;
 | 
					    padding-top: unset;
 | 
				
			||||||
    padding-bottom: unset;
 | 
					    padding-bottom: unset;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
* {
 | 
					* {
 | 
				
			||||||
    font-family: "open sans";
 | 
					    font-family: "open sans";
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
@@ -69,7 +70,7 @@ header>nav {
 | 
				
			|||||||
    top: 0;
 | 
					    top: 0;
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
    height: 40px;
 | 
					    height: 40px;
 | 
				
			||||||
    padding: 8px calc(18.5% - 20px);
 | 
					    padding: 8px 20%;
 | 
				
			||||||
    position: sticky;
 | 
					    position: sticky;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    pointer-events: all;
 | 
					    pointer-events: all;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								mirzaev/calculator/system/public/img/black.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mirzaev/calculator/system/public/img/black.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 64 KiB  | 
							
								
								
									
										1
									
								
								mirzaev/calculator/system/public/img/white.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mirzaev/calculator/system/public/img/white.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 64 KiB  | 
@@ -8,6 +8,11 @@ use mirzaev\minimal\core;
 | 
				
			|||||||
use mirzaev\minimal\router;
 | 
					use mirzaev\minimal\router;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
 | 
					define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
 | 
				
			||||||
 | 
					define('TYPE', 'mysql');
 | 
				
			||||||
 | 
					define('BASE', 'calculator');
 | 
				
			||||||
 | 
					define('HOST', '127.0.0.1');
 | 
				
			||||||
 | 
					define('LOGIN', 'root');
 | 
				
			||||||
 | 
					define('PASSWORD', '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Автозагрузка
 | 
					// Автозагрузка
 | 
				
			||||||
require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
 | 
					require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
 | 
				
			||||||
@@ -17,13 +22,22 @@ $router = new router;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Запись маршрутов
 | 
					// Запись маршрутов
 | 
				
			||||||
$router->write('/', 'main', 'index');
 | 
					$router->write('/', 'main', 'index');
 | 
				
			||||||
$router->write('/calculator', 'calculator', 'index');
 | 
					$router->write('/account/registration', 'accounts', 'registration', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/buyer', 'calculator', 'buyer');
 | 
					$router->write('/account/authentication', 'accounts', 'authentication', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/complexity', 'calculator', 'complexity');
 | 
					$router->write('/account/deauthentication', 'accounts', 'deauthentication', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/menu', 'calculator', 'menu');
 | 
					$router->write('/account/deauthentication', 'accounts', 'deauthentication', 'GET');
 | 
				
			||||||
$router->write('/calculator/generate/result', 'calculator', 'result');
 | 
					$router->write('/account/data', 'accounts', 'data', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/divider', 'calculator', 'divider');
 | 
					$router->write('/calculator', 'calculator', 'index', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/laser', 'calculator', 'laser');
 | 
					$router->write('/calculator/generate/buyer', 'calculator', 'buyer', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/generate/complexity', 'calculator', 'complexity', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/generate/menu', 'calculator', 'menu', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/generate/result', 'calculator', 'result', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/generate/divider', 'calculator', 'divider', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/generate/laser', 'calculator', 'laser', 'POST');
 | 
				
			||||||
 | 
					$router->write('/calculator/calculate', 'calculator', 'calculate', 'POST');
 | 
				
			||||||
 | 
					$router->write('/settings', 'settings', 'index', 'GET');
 | 
				
			||||||
 | 
					$router->write('/settings/write', 'settings', 'write', 'POST');
 | 
				
			||||||
 | 
					$router->write('/settings/read', 'settings', 'read', 'POST');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Инициализация ядра
 | 
					// Инициализация ядра
 | 
				
			||||||
$core = new Core(namespace: __NAMESPACE__, router: $router);
 | 
					$core = new Core(namespace: __NAMESPACE__, router: $router);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								mirzaev/calculator/system/public/js/auth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								mirzaev/calculator/system/public/js/auth.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function remember_switch(target) {
 | 
				
			||||||
 | 
					    if (target.classList.contains('fa-unlock')) {
 | 
				
			||||||
 | 
					        // Найден "открытый замок"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Перезапись на "закрытый замок"
 | 
				
			||||||
 | 
					        target.classList.remove('fa-unlock');
 | 
				
			||||||
 | 
					        target.classList.add('fa-lock');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Изменение отправляемого значения
 | 
				
			||||||
 | 
					        document.querySelector('input[name=' + target.getAttribute('for') + ']').checked = true;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Не найден "открытый замок", подразумевается, что найден "закрытый замок"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Перезапись на "открытый замок"
 | 
				
			||||||
 | 
					        target.classList.remove('fa-lock');
 | 
				
			||||||
 | 
					        target.classList.add('fa-unlock');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Изменение отправляемого значения
 | 
				
			||||||
 | 
					        document.querySelector('input[name=' + target.getAttribute('for') + ']').checked = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function authentication(form) {
 | 
				
			||||||
 | 
					    // Инициализация адреса отправки формы
 | 
				
			||||||
 | 
					    form.action = '/account/authentication';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function registration(form) {
 | 
				
			||||||
 | 
					    // Инициализация адреса отправки формы
 | 
				
			||||||
 | 
					    form.action = '/account/registration';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,32 +3,132 @@
 | 
				
			|||||||
let calculator = {
 | 
					let calculator = {
 | 
				
			||||||
    index: document.getElementById("calculator"),
 | 
					    index: document.getElementById("calculator"),
 | 
				
			||||||
    calculators: [],
 | 
					    calculators: [],
 | 
				
			||||||
 | 
					    account: [],
 | 
				
			||||||
    settings: {
 | 
					    settings: {
 | 
				
			||||||
        defaults: {
 | 
					        defaults: {
 | 
				
			||||||
            buyer: 'individual',
 | 
					            buyer: 'individual',
 | 
				
			||||||
            complexity: 'hard',
 | 
					            complexity: 'medium',
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    init() {
 | 
					    init() {
 | 
				
			||||||
        // Инициализация калькулятора
 | 
					        // Инициализация калькулятора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
 | 
					        // !!!!!!!!!!!!!!!!! РАЗОБРАТЬСЯ С ПОРЯДКОМ ВЫПОЛНЕНИЯ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.generate.buyer(this.settings.defaults.buyer);
 | 
					        this.generate.buyer(this.settings.defaults.buyer)
 | 
				
			||||||
        setTimeout(() => {
 | 
					            .then(this.generate.complexity(this.settings.defaults.complexity)
 | 
				
			||||||
            this.generate.complexity(this.settings.defaults.complexity);
 | 
					                .then(this.generate.menu()
 | 
				
			||||||
            setTimeout(() => {
 | 
					                    .then(this.authenticate(cookie.read('id'))
 | 
				
			||||||
                this.generate.menu();
 | 
					                        .then(success => {
 | 
				
			||||||
                setTimeout(() => {
 | 
					                            // Запись данных аккаунта
 | 
				
			||||||
                    // this.calculate();
 | 
					                            this.account = success;
 | 
				
			||||||
                    this.generate.result();
 | 
					
 | 
				
			||||||
                }, 100);
 | 
					                            if (this.account !== undefined && typeof this.account === 'object' && this.account.permissions !== undefined) {
 | 
				
			||||||
            }, 100);
 | 
					                                // Найден аккаунт
 | 
				
			||||||
        }, 100);
 | 
					
 | 
				
			||||||
 | 
					                                if (this.account.permissions.calculate == 1) {
 | 
				
			||||||
 | 
					                                    // Разрешено использовать калькулятор
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    this.generate.result();
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        console.log('[КАЛЬКУЛЯТОР] Инициализирован');
 | 
					        console.log('[КАЛЬКУЛЯТОР] Инициализирован');
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    calculate(repeated = false) {
 | 
					    authenticate(id) {
 | 
				
			||||||
 | 
					        // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return fetch('/account/data?id=' + id, {
 | 
				
			||||||
 | 
					            method: "POST",
 | 
				
			||||||
 | 
					            headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					        }).then((response) => {
 | 
				
			||||||
 | 
					            if (response.status === 200) {
 | 
				
			||||||
 | 
					                return response.json().then(
 | 
				
			||||||
 | 
					                    success => {
 | 
				
			||||||
 | 
					                        console.log('[КАЛЬКУЛЯТОР] Загружены данные пользователя: ' + id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return success;
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    error => {
 | 
				
			||||||
 | 
					                        console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить данные пользователя: ' + id);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    calculate() {
 | 
				
			||||||
 | 
					        // Запрос и генерация HTML с данными о рассчете со всех калькуляторов
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация буферов вывода
 | 
				
			||||||
 | 
					        let expenses, income, profit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация буфера запроса
 | 
				
			||||||
 | 
					        let query = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const number in this.calculators) {
 | 
				
			||||||
 | 
					            // Перебор калькуляторов
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация буфера запроса для нового калькулятора
 | 
				
			||||||
 | 
					            query[number] = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация типа калькулятора
 | 
				
			||||||
 | 
					            query[number]['type'] = this.calculators[number].getAttribute('data-calculator');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) {
 | 
				
			||||||
 | 
					                // Перебор полей с параметрами типа заказчика
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (buyer.checked) {
 | 
				
			||||||
 | 
					                    // Найдено выбранное поле
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Запись в буфер запроса
 | 
				
			||||||
 | 
					                    query[number]['buyer'] = buyer.value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const complexity of this.index.querySelectorAll('input[name="complexity"]')) {
 | 
				
			||||||
 | 
					                // Перебор полей с параметрами сложности
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (complexity.checked) {
 | 
				
			||||||
 | 
					                    // Найдено выбранное поле
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Запись в буфер запроса
 | 
				
			||||||
 | 
					                    query[number]['complexity'] = complexity.value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const field of this.calculators[number].querySelectorAll('[data-calculator-parameter]')) {
 | 
				
			||||||
 | 
					                // Перебор полей с параметрами
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись в буфер запроса
 | 
				
			||||||
 | 
					                query[number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Сортировка
 | 
				
			||||||
 | 
					            query[number] = this.sort[query[number]['type']](query[number]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fetch('/calculator/calculate', {
 | 
				
			||||||
 | 
					            method: "POST",
 | 
				
			||||||
 | 
					            headers: { "content-type": "application/json" },
 | 
				
			||||||
 | 
					            body: JSON.stringify(query)
 | 
				
			||||||
 | 
					        }).then((response) => {
 | 
				
			||||||
 | 
					            if (response.status === 200) {
 | 
				
			||||||
 | 
					                return response.json().then(
 | 
				
			||||||
 | 
					                    success => {
 | 
				
			||||||
 | 
					                        console.log('[КАЛЬКУЛЯТОР] Сгенерирован результат');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return success;
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    error => {
 | 
				
			||||||
 | 
					                        console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result = document.getElementById("result");
 | 
					        let result = document.getElementById("result");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (result === null) {
 | 
					        if (result === null) {
 | 
				
			||||||
@@ -36,121 +136,118 @@ let calculator = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // Инициализия
 | 
					            // Инициализия
 | 
				
			||||||
            this.generate.result();
 | 
					            this.generate.result();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (repeated === false) {
 | 
					 | 
				
			||||||
                // Это первое выполнение функции в потенциальном цикле
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Повтор операции
 | 
					 | 
				
			||||||
                this.calculate(true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    generate: {
 | 
					    generate: {
 | 
				
			||||||
        buyer(value = 'individual') {
 | 
					        buyer(value = 'individual') {
 | 
				
			||||||
            // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)
 | 
					            // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const request = new XMLHttpRequest();
 | 
					            return fetch('/calculator/generate/buyer?value=' + value, {
 | 
				
			||||||
 | 
					                method: "POST",
 | 
				
			||||||
 | 
					                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					            }).then((response) => {
 | 
				
			||||||
 | 
					                if (response.status === 200) {
 | 
				
			||||||
 | 
					                    response.text().then(
 | 
				
			||||||
 | 
					                        success => {
 | 
				
			||||||
 | 
					                            calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.open('GET', '/calculator/generate/buyer?value=' + value);
 | 
					                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
            request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					                        error => {
 | 
				
			||||||
 | 
					                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора типа покупателя');
 | 
				
			||||||
            request.addEventListener("readystatechange", () => {
 | 
					                        });
 | 
				
			||||||
                if (request.readyState === 4 && request.status === 200) {
 | 
					 | 
				
			||||||
                    calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            request.send();
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        complexity(value = 'medium') {
 | 
					        complexity(value = 'medium') {
 | 
				
			||||||
            // Запрос и генерация HTML с данными о сложности работы
 | 
					            // Запрос и генерация HTML с данными о сложности работы
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const request = new XMLHttpRequest();
 | 
					            return fetch('/calculator/generate/complexity?value=' + value, {
 | 
				
			||||||
 | 
					                method: "POST",
 | 
				
			||||||
 | 
					                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					            }).then((response) => {
 | 
				
			||||||
 | 
					                if (response.status === 200) {
 | 
				
			||||||
 | 
					                    response.text().then(
 | 
				
			||||||
 | 
					                        success => {
 | 
				
			||||||
 | 
					                            calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.open('GET', '/calculator/generate/complexity?value=' + value);
 | 
					                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
            request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					                        error => {
 | 
				
			||||||
 | 
					                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора сложности');
 | 
				
			||||||
            request.addEventListener("readystatechange", () => {
 | 
					                        });
 | 
				
			||||||
                if (request.readyState === 4 && request.status === 200) {
 | 
					 | 
				
			||||||
                    calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            request.send();
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        menu() {
 | 
					        menu() {
 | 
				
			||||||
            // Запрос и генерация HTML с кнопками добавления калькулятора
 | 
					            // Запрос и генерация HTML с кнопками добавления калькулятора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const request = new XMLHttpRequest();
 | 
					            return fetch('/calculator/generate/menu', {
 | 
				
			||||||
 | 
					                method: "POST",
 | 
				
			||||||
 | 
					                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					            }).then((response) => {
 | 
				
			||||||
 | 
					                if (response.status === 200) {
 | 
				
			||||||
 | 
					                    response.text().then(
 | 
				
			||||||
 | 
					                        success => {
 | 
				
			||||||
 | 
					                            calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.open('GET', '/calculator/generate/menu');
 | 
					                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
            request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					                        error => {
 | 
				
			||||||
 | 
					                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками добавления калькулятора');
 | 
				
			||||||
            request.addEventListener("readystatechange", () => {
 | 
					                        });
 | 
				
			||||||
                if (request.readyState === 4 && request.status === 200) {
 | 
					 | 
				
			||||||
                    calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            request.send();
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        result() {
 | 
					        result() {
 | 
				
			||||||
            // Запрос и генерация HTML с данными о результате калькуляции
 | 
					            // Запрос и генерация HTML с данными о результате калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const request = new XMLHttpRequest();
 | 
					            return fetch('/calculator/generate/result', {
 | 
				
			||||||
 | 
					                method: "POST",
 | 
				
			||||||
 | 
					                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					            }).then((response) => {
 | 
				
			||||||
 | 
					                if (response.status === 200) {
 | 
				
			||||||
 | 
					                    response.text().then(
 | 
				
			||||||
 | 
					                        success => {
 | 
				
			||||||
 | 
					                            calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.open('GET', '/calculator/generate/result');
 | 
					                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
            request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					                        error => {
 | 
				
			||||||
 | 
					                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с данными о результате калькуляции');
 | 
				
			||||||
            request.addEventListener("readystatechange", () => {
 | 
					                        });
 | 
				
			||||||
                if (request.readyState === 4 && request.status === 200) {
 | 
					 | 
				
			||||||
                    calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            request.send();
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        divider(element, position) {
 | 
					        divider(element, position) {
 | 
				
			||||||
            // Запрос и генерация HTML с данными о результате калькуляции
 | 
					            // Запрос и генерация HTML с данными о результате калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const request = new XMLHttpRequest();
 | 
					            return fetch('/calculator/generate/divider', {
 | 
				
			||||||
 | 
					                method: "POST",
 | 
				
			||||||
 | 
					                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					            }).then((response) => {
 | 
				
			||||||
 | 
					                if (response.status === 200) {
 | 
				
			||||||
 | 
					                    response.text().then(
 | 
				
			||||||
 | 
					                        success => {
 | 
				
			||||||
 | 
					                            if (element === undefined || position === undefined) {
 | 
				
			||||||
 | 
					                                // Не задан элемент и позиция добавляемого разделителя
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.open('GET', '/calculator/generate/divider');
 | 
					                                // Запись разделителя в конце калькулятора
 | 
				
			||||||
 | 
					                                calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                // Задан элемент и позиция добавляемого разделителя
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					                                // Запись разделителя по заданным параметрам
 | 
				
			||||||
 | 
					                                element.insertAdjacentHTML(position, success);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            request.addEventListener("readystatechange", () => {
 | 
					                            console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
 | 
				
			||||||
                if (request.readyState === 4 && request.status === 200) {
 | 
					                        },
 | 
				
			||||||
 | 
					                        error => {
 | 
				
			||||||
                    if (element === undefined || position === undefined) {
 | 
					                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить разделитель');
 | 
				
			||||||
                        // Не задан элемент и позиция добавляемого разделителя
 | 
					                        });
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Запись разделителя в конце калькулятора
 | 
					 | 
				
			||||||
                        calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        // Задан элемент и позиция добавляемого разделителя
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Запись разделителя по заданным параметрам
 | 
					 | 
				
			||||||
                        element.insertAdjacentHTML(position, request.responseText);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            request.send();
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        calculators: {
 | 
					        calculators: {
 | 
				
			||||||
            laser() {
 | 
					            laser() {
 | 
				
			||||||
@@ -158,100 +255,133 @@ let calculator = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
 | 
					                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const request = new XMLHttpRequest();
 | 
					                return fetch('/calculator/generate/laser', {
 | 
				
			||||||
 | 
					                    method: "POST",
 | 
				
			||||||
 | 
					                    headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
				
			||||||
 | 
					                }).then((response) => {
 | 
				
			||||||
 | 
					                    if (response.status === 200) {
 | 
				
			||||||
 | 
					                        response.text().then(
 | 
				
			||||||
 | 
					                            success => {
 | 
				
			||||||
 | 
					                                // Поиск последнего калькулятора
 | 
				
			||||||
 | 
					                                let last = calculator.calculators[calculator.calculators.length - 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                request.open('GET', '/calculator/generate/laser');
 | 
					                                if (last !== undefined && last !== null) {
 | 
				
			||||||
 | 
					                                    // Найден калькулятор
 | 
				
			||||||
                request.setRequestHeader('Content-Type', 'application/x-www-form-url');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                request.addEventListener("readystatechange", () => {
 | 
					 | 
				
			||||||
                    if (request.readyState === 4 && request.status === 200) {
 | 
					 | 
				
			||||||
                        // Поиск последнего калькулятора
 | 
					 | 
				
			||||||
                        let last = calculator.calculators[calculator.calculators.length - 1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (last !== undefined && last !== null) {
 | 
					 | 
				
			||||||
                            // Найден калькулятор
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            // Запись калькулятора после последнего калькулятора
 | 
					 | 
				
			||||||
                            last.insertAdjacentHTML('afterend', request.responseText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            setTimeout(() => {
 | 
					 | 
				
			||||||
                                // Инициализация разделителя перед меню
 | 
					 | 
				
			||||||
                                calculator.generate.divider(last, 'afterend');
 | 
					 | 
				
			||||||
                            }, 100);
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            // Не найден калькулятор
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            // Поиск меню
 | 
					 | 
				
			||||||
                            let menu = document.getElementById("menu");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            if (menu !== null) {
 | 
					 | 
				
			||||||
                                // Найдено меню
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                // Инициализация разделителя перед меню
 | 
					 | 
				
			||||||
                                calculator.generate.divider(menu, 'beforebegin');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                setTimeout(() => {
 | 
					 | 
				
			||||||
                                    // Запись калькулятора перед меню
 | 
					 | 
				
			||||||
                                    menu.insertAdjacentHTML('beforebegin', request.responseText);
 | 
					 | 
				
			||||||
                                }, 100);
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                // Не найдено меню
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                // Поиск результатов калькуляции
 | 
					 | 
				
			||||||
                                let result = document.getElementById("result");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                if (result !== null) {
 | 
					 | 
				
			||||||
                                    // Найден элемент с результатами калькуляции
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    // Инициализация разделителя перед меню
 | 
					                                    // Инициализация разделителя перед меню
 | 
				
			||||||
                                    calculator.generate.divider(result, 'beforebegin');
 | 
					                                    calculator.generate.divider(last, 'afterend').then(
 | 
				
			||||||
 | 
					                                        divider => {
 | 
				
			||||||
 | 
					                                            // Запись калькулятора после последнего калькулятора
 | 
				
			||||||
 | 
					                                            last.insertAdjacentHTML('afterend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    setTimeout(() => {
 | 
					                                            // Поиск калькуляторов
 | 
				
			||||||
                                        // Запись калькулятора перед элементом с результатами калькуляции
 | 
					                                            let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
				
			||||||
                                        result.insertAdjacentHTML('beforebegin', request.responseText);
 | 
					
 | 
				
			||||||
                                    }, 100);
 | 
					                                            // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
				
			||||||
 | 
					                                            calculator.calculators.push(calculators[calculators.length - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            // Запись в журнал
 | 
				
			||||||
 | 
					                                            console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    );
 | 
				
			||||||
                                } else {
 | 
					                                } else {
 | 
				
			||||||
                                    // Не найден элемент с результатами калькуляции
 | 
					                                    // Не найден калькулятор
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    // Инициализация разделителя перед меню
 | 
					                                    // Поиск меню
 | 
				
			||||||
                                    calculator.generate.divider();
 | 
					                                    let menu = document.getElementById("menu");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    setTimeout(() => {
 | 
					                                    if (menu !== null) {
 | 
				
			||||||
                                        // Запись калькулятора в конце калькулятора
 | 
					                                        // Найдено меню
 | 
				
			||||||
                                        calculator.index.insertAdjacentHTML('beforeend', request.responseText);
 | 
					
 | 
				
			||||||
                                    }, 100);
 | 
					                                        // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                                        calculator.generate.divider(menu, 'beforebegin').then(
 | 
				
			||||||
 | 
					                                            divider => {
 | 
				
			||||||
 | 
					                                                // Запись калькулятора перед меню
 | 
				
			||||||
 | 
					                                                menu.insertAdjacentHTML('beforebegin', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                // Поиск калькуляторов
 | 
				
			||||||
 | 
					                                                let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
				
			||||||
 | 
					                                                calculator.calculators.push(calculators[calculators.length - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                // Запись в журнал
 | 
				
			||||||
 | 
					                                                console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					                                    } else {
 | 
				
			||||||
 | 
					                                        // Не найдено меню
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        // Поиск результатов калькуляции
 | 
				
			||||||
 | 
					                                        let result = document.getElementById("result");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        if (result !== null) {
 | 
				
			||||||
 | 
					                                            // Найден элемент с результатами калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                                            calculator.generate.divider(result, 'beforebegin').then(
 | 
				
			||||||
 | 
					                                                divider => {
 | 
				
			||||||
 | 
					                                                    // Запись калькулятора перед элементом с результатами калькуляции
 | 
				
			||||||
 | 
					                                                    result.insertAdjacentHTML('beforebegin', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Поиск калькуляторов
 | 
				
			||||||
 | 
					                                                    let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
				
			||||||
 | 
					                                                    calculator.calculators.push(calculators[calculators.length - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Запись в журнал
 | 
				
			||||||
 | 
					                                                    console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                            );
 | 
				
			||||||
 | 
					                                        } else {
 | 
				
			||||||
 | 
					                                            // Не найден элемент с результатами калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                                            calculator.generate.divider().then(
 | 
				
			||||||
 | 
					                                                divider => {
 | 
				
			||||||
 | 
					                                                    // Запись калькулятора в конце калькулятора
 | 
				
			||||||
 | 
					                                                    calculator.index.insertAdjacentHTML('beforeend', success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Поиск калькуляторов
 | 
				
			||||||
 | 
					                                                    let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
				
			||||||
 | 
					                                                    calculator.calculators.push(calculators[calculators.length - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                    // Запись в журнал
 | 
				
			||||||
 | 
					                                                    console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                            );
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            },
 | 
				
			||||||
                        }
 | 
					                            error => {
 | 
				
			||||||
 | 
					                                // Запись в журнал
 | 
				
			||||||
                        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
 | 
					                                console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки');
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
                        setTimeout(() => {
 | 
					 | 
				
			||||||
                            // Поиск только что созданного калькулятора
 | 
					 | 
				
			||||||
                            let laser = document.getElementById('laser');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            if (laser !== null) {
 | 
					 | 
				
			||||||
                                // Найден только что инициализированный (подразумевается) калькулятор лазерной резки
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                // Реинициализация идентификатора
 | 
					 | 
				
			||||||
                                laser.id = 'laser_' + calculator.calculators.length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                // Запись калькулятора в реестр
 | 
					 | 
				
			||||||
                                calculator.calculators.push(laser);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                console.log('[КАЛЬКУЛЯТОР] Загружен калькулятор лазерной резки');
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                // Не найден только что инициализированный (подразумевается) калькулятор лазерной резки
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                console.log('[КАЛЬКУЛЯТОР] Не удалось инициализировать калькулятор лазерной резки');
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }, 100);
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					 | 
				
			||||||
                request.send();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    sort: {
 | 
				
			||||||
 | 
					        laser(parameters) {
 | 
				
			||||||
 | 
					            // Сортировка параметров для отправки на сервер (динамически вызывается функция-обработчик)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                type: 'laser',
 | 
				
			||||||
 | 
					                buyer: parameters['buyer'] ?? null,
 | 
				
			||||||
 | 
					                complexity: parameters['complexity'] ?? null,
 | 
				
			||||||
 | 
					                width: parameters['width'] ?? null,
 | 
				
			||||||
 | 
					                height: parameters['width'] ?? null,
 | 
				
			||||||
 | 
					                length: parameters['length'] ?? null,
 | 
				
			||||||
 | 
					                amount: parameters['amount'] ?? null,
 | 
				
			||||||
 | 
					                metal: parameters['metal'] ?? null,
 | 
				
			||||||
 | 
					                holes: parameters['holes'] ?? null,
 | 
				
			||||||
 | 
					                diameter: parameters['diameter'] ?? null,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								mirzaev/calculator/system/public/js/cookie.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								mirzaev/calculator/system/public/js/cookie.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let cookie = {
 | 
				
			||||||
 | 
					    read(name) {
 | 
				
			||||||
 | 
					        // Поиск по регулярному выражению
 | 
				
			||||||
 | 
					        let matches = document.cookie.match(new RegExp(
 | 
				
			||||||
 | 
					            "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return matches ? decodeURIComponent(matches[1]) : undefined;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    write(name, value, options = {}) {
 | 
				
			||||||
 | 
					        // Инициализация параметров
 | 
				
			||||||
 | 
					        options = {
 | 
				
			||||||
 | 
					            path: '/',
 | 
				
			||||||
 | 
					            ...options
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (options.expires instanceof Date) {
 | 
				
			||||||
 | 
					            // Передана инстанция Date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись параметра истечения срока
 | 
				
			||||||
 | 
					            options.expires = options.expires.toUTCString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация cookie
 | 
				
			||||||
 | 
					        let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let optionKey in options) {
 | 
				
			||||||
 | 
					            // Перебор параметров
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Запись в cookie названия параметра
 | 
				
			||||||
 | 
					            updatedCookie += "; " + optionKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Инициализация значения параметра
 | 
				
			||||||
 | 
					            let optionValue = options[optionKey];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (optionValue !== true) {
 | 
				
			||||||
 | 
					                // Найдено значение параметра
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Запись в cookie значения параметра
 | 
				
			||||||
 | 
					                updatedCookie += "=" + optionValue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Конкатенация нового cookie с остальными
 | 
				
			||||||
 | 
					        document.cookie = updatedCookie;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    delete(name) {
 | 
				
			||||||
 | 
					        // Удаление
 | 
				
			||||||
 | 
					        setCookie(name, "", {
 | 
				
			||||||
 | 
					            'max-age': -1
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,26 +1,39 @@
 | 
				
			|||||||
<link href="/css/auth.css" rel="stylesheet">
 | 
					<link href="/css/auth.css" rel="stylesheet">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section id="authentication">
 | 
					<section id="authentication">
 | 
				
			||||||
 | 
					    {% if account is not empty %}
 | 
				
			||||||
 | 
					    <h3>Аккаунт</h3>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <p><b>Почта:</b> <span>{{ account.email }}</span></p>
 | 
				
			||||||
 | 
					        <a class="exit" type="button" href='/account/deauthentication'>Выход</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% else %}
 | 
				
			||||||
    <h3>Аутентификация</h3>
 | 
					    <h3>Аутентификация</h3>
 | 
				
			||||||
    <form method="post" accept-charset="UTF-8">
 | 
					    <form method="POST" accept-charset="UTF-8">
 | 
				
			||||||
        <input type="hidden" name="action" value="users/login">
 | 
					        <input type="text" name="email" placeholder="Почта">
 | 
				
			||||||
 | 
					        <input type="password" name="password" placeholder="Пароль">
 | 
				
			||||||
        <input id="loginName" type="text" name="loginName" placeholder="Почта"
 | 
					 | 
				
			||||||
            value="{{ craft.app.user.rememberedUsername }}">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <input id="password" type="password" name="password" placeholder="Пароль">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="submit">
 | 
					        <div class="submit">
 | 
				
			||||||
            <label class="button unselectable" for="rememberMe">
 | 
					            <label class="button unselectable fas fa-unlock" for="remember"
 | 
				
			||||||
                <i class="fas fa-unlock"></i>
 | 
					                onclick="return remember_switch(this);"></label>
 | 
				
			||||||
            </label>
 | 
					            <input type="checkbox" name="remember" value="1">
 | 
				
			||||||
            <input type="checkbox" name="rememberMe" value="1">
 | 
					            <input type="submit" value="Войти" onclick="return authentication(this.parentElement.parentElement);">
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <input type="submit" value="Войти">
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <input type="submit" class="registration" value="Зарегистрироваться"
 | 
				
			||||||
 | 
					            onclick="return registration(this.parentElement);">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% if errorMessage is defined %}
 | 
					        {% if errors is not empty %}
 | 
				
			||||||
        <p>{{ errorMessage }}</p>
 | 
					        {% if errors.account is not empty %}
 | 
				
			||||||
 | 
					        <ul class="errors">
 | 
				
			||||||
 | 
					            {% for error in errors.account %}
 | 
				
			||||||
 | 
					            <li>{{ error }}</li>
 | 
				
			||||||
 | 
					            {% endfor %}
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script type="text/javascript" src="/js/auth.js" defer></script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
	<div class="divider"></div>
 | 
						<div class="divider"></div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script type="text/javascript" src="/js/cookie.js" defer></script>
 | 
				
			||||||
<script type="text/javascript" src="/js/calculator.js" defer></script>
 | 
					<script type="text/javascript" src="/js/calculator.js" defer></script>
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
	if (document.readyState === "complete") {
 | 
						if (document.readyState === "complete") {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<section id="buyer">
 | 
					<section id="buyer" class="unselectable">
 | 
				
			||||||
    <input id="individual" type="radio" name="buyer" value="individual" {% if buyer is same as('individual') %}checked{% endif %}>
 | 
					    <input id="individual" type="radio" name="buyer" value="individual" {% if buyer is same as('individual') %}checked{% endif %}>
 | 
				
			||||||
    <label type="button" for="individual">Физическое лицо</label>
 | 
					    <label type="button" for="individual">Физическое лицо</label>
 | 
				
			||||||
    <input id="entity" type="radio" name="buyer" value="entity" {% if buyer is same as('entity') %}checked{% endif %}>
 | 
					    <input id="entity" type="radio" name="buyer" value="entity" {% if buyer is same as('entity') %}checked{% endif %}>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
<h3>Лазерная резка</h3>
 | 
					<h3>Лазерная резка</h3>
 | 
				
			||||||
<section id="laser">
 | 
					<section class="calculator" data-calculator="laser">
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <label>Металл</label>
 | 
					        <label>Металл</label>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <select name="metal">
 | 
					            <select name="metal" data-calculator-parameter="metal">
 | 
				
			||||||
                <option value="steel">Сталь</option>
 | 
					                <option value="steel">Сталь</option>
 | 
				
			||||||
                <option value="stainless_steel" selected>Нержавеющая сталь</option>
 | 
					                <option value="stainless_steel" selected>Нержавеющая сталь</option>
 | 
				
			||||||
                <option value="brass">Латунь</option>
 | 
					                <option value="brass">Латунь</option>
 | 
				
			||||||
@@ -15,30 +15,30 @@
 | 
				
			|||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <label>Размер</label>
 | 
					        <label>Размер</label>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <input type="text" class="measured" title="Длина" value="100">
 | 
					            <input data-calculator-parameter="width" type="text" class="measured" title="Длина" value="100">
 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					            <span class="unit unselectable">мм</span>
 | 
				
			||||||
            <small>x</small>
 | 
					            <small>x</small>
 | 
				
			||||||
            <input type="text" class="measured" title="Ширина" value="100">
 | 
					            <input data-calculator-parameter="height" type="text" class="measured" title="Ширина" value="100">
 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					            <span class="unit unselectable">мм</span>
 | 
				
			||||||
            <small>x</small>
 | 
					            <small>x</small>
 | 
				
			||||||
            <input type="text" class="measured" title="Толщина" value="1">
 | 
					            <input data-calculator-parameter="length" type="text" class="measured" title="Толщина" value="1">
 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					            <span class="unit unselectable">мм</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <label>Отверстия</label>
 | 
					        <label>Отверстия</label>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <input type="text" class="measured" title="Количество" value="1">
 | 
					            <input data-calculator-parameter="holes" type="text" class="measured" title="Количество" value="1">
 | 
				
			||||||
            <span class="unit unselectable">шт</span>
 | 
					            <span class="unit unselectable">шт</span>
 | 
				
			||||||
            <small>x</small>
 | 
					            <small>x</small>
 | 
				
			||||||
            <input type="text" class="measured" title="Диаметр" value="1">
 | 
					            <input data-calculator-parameter="diameter" type="text" class="measured" title="Диаметр" value="1">
 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					            <span class="unit unselectable">мм</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <label>Количество</label>
 | 
					        <label>Количество</label>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <input type="text" class="measured" title="Количество" value="1">
 | 
					            <input data-calculator-parameter="amount" type="text" class="measured" title="Количество" value="1">
 | 
				
			||||||
            <span class="unit unselectable">шт</span>
 | 
					            <span class="unit unselectable">шт</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<section id="menu">
 | 
					<section id="menu" class="unselectable">
 | 
				
			||||||
    <a type="button" onclick="calculator.generate.calculators.laser(); return false;">
 | 
					    <a type="button" onclick="calculator.generate.calculators.laser(); return false;">
 | 
				
			||||||
        <img src="/img/laser.png" title="Добавить лазерную резку">
 | 
					        <img src="/img/laser.png" title="Добавить лазерную резку">
 | 
				
			||||||
        Лазерная резка
 | 
					        Лазерная резка
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<section id="complexity">
 | 
					<section id="complexity" class="unselectable">
 | 
				
			||||||
    <input id="easy" type="radio" name="complexity"  value="easy" {% if complexity is same as('easy') %}checked{% endif %}>
 | 
					    <input id="easy" type="radio" name="complexity"  value="easy" {% if complexity is same as('easy') %}checked{% endif %}>
 | 
				
			||||||
    <label type="button" for="easy">
 | 
					    <label type="button" for="easy">
 | 
				
			||||||
        <img src="/img/easy.png" title="Лёгкий уровень сложности">
 | 
					        <img src="/img/easy.png" title="Лёгкий уровень сложности">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
<div class="divider"></div>
 | 
					<div class="divider unselectable"></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
<section id="result">
 | 
					<section id="result">
 | 
				
			||||||
 | 
					    <nav class="unselectable">
 | 
				
			||||||
 | 
					        <a id="calculate" type="button" onclick="return calculator.calculate();">Рассчитать</a>
 | 
				
			||||||
 | 
					    </nav>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <p>Расходы: <span id="expenses">0</span><span>рублей</span></p>
 | 
					        <p class="unselectable">Расходы: <span id="expenses">0</span><span>рублей</span></p>
 | 
				
			||||||
        <p>Доход: <span id="income">0</span><span>рублей</span></p>
 | 
					        <p class="unselectable">Доход: <span id="income">0</span><span>рублей</span></p>
 | 
				
			||||||
        <p>Прибыль: <span id="profit">0</span><span>рублей</span></p>
 | 
					        <p class="unselectable">Прибыль: <span id="profit">0</span><span>рублей</span></p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,5 +5,10 @@
 | 
				
			|||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
        <a class="link" href="/contacts" title="Связь с администрацией">Контакты</a>
 | 
					        <a class="link" href="/contacts" title="Связь с администрацией">Контакты</a>
 | 
				
			||||||
        <a class="link" href="/journal" title="Записи рассчётов">Журнал</a>
 | 
					        <a class="link" href="/journal" title="Записи рассчётов">Журнал</a>
 | 
				
			||||||
 | 
					        {% if account is not empty %}
 | 
				
			||||||
 | 
					        {% if account.permissions.settings is defined and account.permissions.settings == 1 %}
 | 
				
			||||||
 | 
					        <a class="link" href="/settings" title="Настройки калькуляторов">Настройки</a>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
    </nav>
 | 
					    </nav>
 | 
				
			||||||
</header>
 | 
					</header>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user