Перенос от mirzaev/tordv-calculator
@@ -1,42 +1,43 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "mirzaev/tordv-calculator",
 | 
			
		||||
    "description": "Калькулятор стоимости услуг по обработке металла",
 | 
			
		||||
    "type": "site",
 | 
			
		||||
    "keywords": [
 | 
			
		||||
        "site",
 | 
			
		||||
        "calculator"
 | 
			
		||||
    ],
 | 
			
		||||
    "readme": "README.md",
 | 
			
		||||
    "homepage": "https://git.mirzaev.sexy/mirzaev/tordv-calculator",
 | 
			
		||||
    "license": "WTFPL",
 | 
			
		||||
    "authors": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Arsen Mirzaev Tatyano-Muradovich",
 | 
			
		||||
            "email": "arsen@mirzaev.sexy",
 | 
			
		||||
            "homepage": "https://mirzaev.sexy",
 | 
			
		||||
            "role": "Programmer"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "support": {
 | 
			
		||||
        "email": "arsen@mirzaev.sexy",
 | 
			
		||||
        "docs": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/wiki",
 | 
			
		||||
        "issues": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/issues"
 | 
			
		||||
    },
 | 
			
		||||
    "funding": [
 | 
			
		||||
        {
 | 
			
		||||
            "type": "funding",
 | 
			
		||||
            "url": "https://fund.mirzaev.sexy"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "require": {
 | 
			
		||||
        "php": "^8.0.0",
 | 
			
		||||
        "mirzaev/minimal": "^2.0.x-dev",
 | 
			
		||||
        "twig/twig": "^3.3",
 | 
			
		||||
        "phpoffice/phpspreadsheet": "^1.20"
 | 
			
		||||
    },
 | 
			
		||||
    "autoload": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
            "mirzaev\\tordv\\calculator\\": "mirzaev/tordv/calculator/system"
 | 
			
		||||
        }
 | 
			
		||||
  "name": "mirzaev/zkmr-calculator",
 | 
			
		||||
  "description": "Калькулятор стоимости услуг по обработке металла",
 | 
			
		||||
  "type": "site",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "site",
 | 
			
		||||
    "calculator"
 | 
			
		||||
  ],
 | 
			
		||||
  "readme": "README.md",
 | 
			
		||||
  "homepage": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator",
 | 
			
		||||
  "license": "WTFPL",
 | 
			
		||||
  "authors": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Arsen Mirzaev Tatyano-Muradovich",
 | 
			
		||||
      "email": "arsen@mirzaev.sexy",
 | 
			
		||||
      "homepage": "https://mirzaev.sexy",
 | 
			
		||||
      "role": "Programmer"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "support": {
 | 
			
		||||
    "email": "arsen@mirzaev.sexy",
 | 
			
		||||
    "docs": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator/wiki",
 | 
			
		||||
    "issues": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator/issues"
 | 
			
		||||
  },
 | 
			
		||||
  "funding": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "funding",
 | 
			
		||||
      "url": "https://fund.mirzaev.sexy"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "require": {
 | 
			
		||||
    "php": "^8.2.0",
 | 
			
		||||
    "mirzaev/minimal": "^2.0.x-dev",
 | 
			
		||||
    "twig/twig": "^3.3",
 | 
			
		||||
    "phpoffice/phpspreadsheet": "^1.20",
 | 
			
		||||
    "ext-mysqli": "*"
 | 
			
		||||
  },
 | 
			
		||||
  "autoload": {
 | 
			
		||||
    "psr-4": {
 | 
			
		||||
      "mirzaev\\zkmr\\calculator\\": "mirzaev/zkmr/calculator/system"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -4,7 +4,7 @@
 | 
			
		||||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
			
		||||
        "This file is @generated automatically"
 | 
			
		||||
    ],
 | 
			
		||||
    "content-hash": "d63372480c8f1c3719fb9e69026eef21",
 | 
			
		||||
    "content-hash": "c424f9f2f1b7a9cbcfaa900a5da9d892",
 | 
			
		||||
    "packages": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "ezyang/htmlpurifier",
 | 
			
		||||
@@ -926,7 +926,8 @@
 | 
			
		||||
    "prefer-stable": false,
 | 
			
		||||
    "prefer-lowest": false,
 | 
			
		||||
    "platform": {
 | 
			
		||||
        "php": "^8.0.0"
 | 
			
		||||
        "php": "^8.2.0",
 | 
			
		||||
        "ext-mysqli": "*"
 | 
			
		||||
    },
 | 
			
		||||
    "platform-dev": [],
 | 
			
		||||
    "plugin-api-version": "2.3.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,266 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\calculators_model as calculators;
 | 
			
		||||
use mirzaev\tordv\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
			
		||||
 | 
			
		||||
use Twig\Loader\FilesystemLoader;
 | 
			
		||||
use Twig\Environment as view;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер основной страницы
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class calculator_controller extends core
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Калькулятор
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с калькулятором
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function index(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculator' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "тип пользователя"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками: "физическое лицо" и "юридическое лицо"
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function buyer(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация параметров
 | 
			
		||||
        $vars['buyer'] = $vars['value'] ?? 'individual';
 | 
			
		||||
 | 
			
		||||
        // Удаление параметров
 | 
			
		||||
        unset($vars['value']);
 | 
			
		||||
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'buyer.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "сложность"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками: "легко", "средне" и "сложно"
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function complexity(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация параметров
 | 
			
		||||
        $vars['complexity'] = $vars['value'] ?? 'medium';
 | 
			
		||||
 | 
			
		||||
        // Удаление параметров
 | 
			
		||||
        unset($vars['value']);
 | 
			
		||||
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'complexity.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "меню"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками добавления калькуляторов
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function menu(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'menu.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "результат"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с данными результата калькуляции
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function result(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация журнала ошибок
 | 
			
		||||
        $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
        // Инициализация данных калькулятора
 | 
			
		||||
        $vars['discount'] = settings::read('discount', $vars['errors']['calculators']);
 | 
			
		||||
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'result.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "марка"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код со списком марок металла
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     *
 | 
			
		||||
     * @todo 1. Если металл свой, то ничего не генерировать
 | 
			
		||||
     */
 | 
			
		||||
    public function mark(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация журнала ошибок
 | 
			
		||||
        $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
        // Инициализация списка марок
 | 
			
		||||
        $vars['marks'] = metals::marks(empty($vars['type']) ? settings::read("default_type") : $vars['type'], $vars['errors'], $vars['errors']['calculators']);
 | 
			
		||||
 | 
			
		||||
        // Инициализация значения по умолчанию
 | 
			
		||||
        if (empty($vars['marks'])) $vars['marks'] = ['Не найдено'];
 | 
			
		||||
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'metals' . DIRECTORY_SEPARATOR . 'mark.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "разделитель"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с разделителем элементов
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function divider(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'divider.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Лазерная резка
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с калькулятором лазерной резки
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     *
 | 
			
		||||
     * @todo 1. Заголовок калькулятора должен находиться внутри элемента калькулятора
 | 
			
		||||
     * 2. Ограничение значений полей в зависимости от выбранной марки
 | 
			
		||||
     */
 | 
			
		||||
    public function laser(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация журнала ошибок
 | 
			
		||||
        $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
        // Инициализация данных калькулятора
 | 
			
		||||
        $vars['calculators'] = ['laser' => [
 | 
			
		||||
            'company' => settings::read('default_buyer', $vars['errors']['calculators']),
 | 
			
		||||
            'complexity' => settings::read('default_complexity', $vars['errors']['calculators']),
 | 
			
		||||
            'width' => (int) round((float) settings::read('default_width', $vars['errors']['calculators'])),
 | 
			
		||||
            'height' => (int) round((float) settings::read('default_height', $vars['errors']['calculators'])),
 | 
			
		||||
            'length' => (int) round((float) settings::read('default_length', $vars['errors']['calculators'])),
 | 
			
		||||
            'amount' => (int) round((float) settings::read('default_amount', $vars['errors']['calculators'])),
 | 
			
		||||
            'type' => settings::read('default_type', $vars['errors']['calculators']),
 | 
			
		||||
            'holes' => (int) round((float) settings::read('default_holes', $vars['errors']['calculators'])),
 | 
			
		||||
            'diameter' => (float) settings::read('default_diameter', $vars['errors']['calculators'])
 | 
			
		||||
        ]];
 | 
			
		||||
 | 
			
		||||
        // Генерация представления
 | 
			
		||||
        return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'laser.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Расчёт
 | 
			
		||||
     *
 | 
			
		||||
     * Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0]
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     *
 | 
			
		||||
     * @todo
 | 
			
		||||
     * 5. Убрать передачу цены работы (оставить только время работы в часах и цену за работу в час)
 | 
			
		||||
     */
 | 
			
		||||
    public function calculate(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        // Инициализация журнала ошибок
 | 
			
		||||
        $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация параметров из тела запроса (подразумевается, что там массивы с параметрами)
 | 
			
		||||
            $vars['input'] = json_decode(file_get_contents('php://input'), true);
 | 
			
		||||
 | 
			
		||||
            $calculators = $vars['input']['calculators'];
 | 
			
		||||
            $discount = $vars['input']['discount'];
 | 
			
		||||
            $cutting = $vars['input']['cutting'];
 | 
			
		||||
 | 
			
		||||
            // Инициализация переменных для буфера вывода
 | 
			
		||||
            $machines = $managers = $engineers = $operators = $handymans = $other = 0;
 | 
			
		||||
 | 
			
		||||
            if (count($calculators) > 0) {
 | 
			
		||||
                // Найдены калькуляторы
 | 
			
		||||
 | 
			
		||||
                foreach ($calculators as $i => $calculator) {
 | 
			
		||||
                    // Перебор калькуляторов
 | 
			
		||||
 | 
			
		||||
                    foreach (['calculator'] as &$parameter) {
 | 
			
		||||
                        // Перебор мета-параметров
 | 
			
		||||
 | 
			
		||||
                        // Инициализация общего параметра
 | 
			
		||||
                        $type =  $calculator[$parameter];
 | 
			
		||||
 | 
			
		||||
                        // Инициализация параметра для обработчика калькулятора
 | 
			
		||||
                        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;
 | 
			
		||||
 | 
			
		||||
                    // Расчёт
 | 
			
		||||
                    [$machines, $managers, $engineers, $operators, $handymans, $other] = calculators::$type(...$parameters + ['cutting' => $cutting]);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // Не найдены калькуляторы
 | 
			
		||||
 | 
			
		||||
                throw new exception('Не найдены калькуляторы');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $vars['errors']['calculators'][] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return json_encode([
 | 
			
		||||
            'machines' => $machines,
 | 
			
		||||
            'managers' => $managers,
 | 
			
		||||
            'engineers' => $engineers,
 | 
			
		||||
            'operators' => $operators,
 | 
			
		||||
            'handymans' => $handymans,
 | 
			
		||||
            'other' => $other + ['discount' => $discount],
 | 
			
		||||
            'errors' => $vars['errors']
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,621 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
 | 
			
		||||
use pdo;
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Модель регистрации, аутентификации и авторизации
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Проверить разрешение
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $permission Разрешение
 | 
			
		||||
     * @param int|null $id Идентификатор аккаунта
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool|null Статус разрешения, если оно записано
 | 
			
		||||
     */
 | 
			
		||||
    public static function access(string $permission, int|null $id = null, array &$errors = []): ?bool
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация аккаунта
 | 
			
		||||
            $account = isset($id) ? self::read(['id' => $id], $errors) : self::account($errors);
 | 
			
		||||
 | 
			
		||||
            return isset($account['permissions'][$permission]) ? (bool) $account['permissions'][$permission] : null;
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Запись пользователя в базу данных
 | 
			
		||||
     *
 | 
			
		||||
     * @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[] = [
 | 
			
		||||
                        'text' => $e->getMessage(),
 | 
			
		||||
                        'file' => $e->getFile(),
 | 
			
		||||
                        'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                    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[] = [
 | 
			
		||||
                        'text' => $e->getMessage(),
 | 
			
		||||
                        'file' => $e->getFile(),
 | 
			
		||||
                        'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                    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[] = [
 | 
			
		||||
                        'text' => $e->getMessage(),
 | 
			
		||||
                        'file' => $e->getFile(),
 | 
			
		||||
                        'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                    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[] = [
 | 
			
		||||
                    'text' => $e->getMessage(),
 | 
			
		||||
                    'file' => $e->getFile(),
 | 
			
		||||
                    'line' => $e->getLine(),
 | 
			
		||||
                    'stack' => $e->getTrace()
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Конец выполнения
 | 
			
		||||
        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[] = [
 | 
			
		||||
                        'text' => $e->getMessage(),
 | 
			
		||||
                        'file' => $e->getFile(),
 | 
			
		||||
                        'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // Не найден аккаунт
 | 
			
		||||
 | 
			
		||||
                throw new exception('Не удалось найти аккаунт');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ['hash' => $hash, 'time' => $time];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,163 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
 | 
			
		||||
use exception;
 | 
			
		||||
use pdo;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Модель баллонов
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 *
 | 
			
		||||
 * @todo
 | 
			
		||||
 * 1. Если длина реза баллона зависит от типа металла (учитывается) то перенести это в класс металлов
 | 
			
		||||
 */
 | 
			
		||||
final class baloons_model extends core
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Используемый газ
 | 
			
		||||
     */
 | 
			
		||||
    public string $gas;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Количество баллонов
 | 
			
		||||
     */
 | 
			
		||||
    public float $amount = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Цена всех баллонов
 | 
			
		||||
     */
 | 
			
		||||
    public float $cost;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Вычисление используемого газа
 | 
			
		||||
     *
 | 
			
		||||
     * @param float $length Толщина металла
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return string|null Название газа
 | 
			
		||||
     */
 | 
			
		||||
    public function gas(float $length, array &$errors = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            return $this->gas = match (true) {
 | 
			
		||||
                $length >= 4 => 'oxygen',
 | 
			
		||||
                default => 'air'
 | 
			
		||||
            };
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Вычисление количества используемых баллонов
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $metal Тип металла
 | 
			
		||||
     * @param float $cutting Длина реза
 | 
			
		||||
     * @param float $length Толщина листа
 | 
			
		||||
     * @param string|null $gas Используемый газ
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return int|null Количество баллонов
 | 
			
		||||
     *
 | 
			
		||||
     * @todo
 | 
			
		||||
     * 1. Добавить к баллонам уточнение чтобы считало не по листам а по объёму а лучше по длине реза
 | 
			
		||||
     * 2. Определение длины реза по типу металла
 | 
			
		||||
     */
 | 
			
		||||
    public function amount(string $metal, float $cutting, float $length, ?string $gas = null, array &$errors = []): ?float
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация входных параметров
 | 
			
		||||
            $gas ?? $gas = &$this->gas;
 | 
			
		||||
 | 
			
		||||
            // Инициализация запроса
 | 
			
		||||
            $request = static::$db->prepare("SELECT `length` FROM `baloons` WHERE `gas` = :gas LIMIT 30");
 | 
			
		||||
 | 
			
		||||
            // Отправка запроса
 | 
			
		||||
            $request->execute([
 | 
			
		||||
                ':gas' => $gas
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            // Генерация ответа
 | 
			
		||||
            $response = $request->fetch(pdo::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
            // Проверка на полученные значения
 | 
			
		||||
            if (!is_array($response)) return null;
 | 
			
		||||
 | 
			
		||||
            // Вычисление длины реза на которое хватит баллона
 | 
			
		||||
            $flow = $response['length'] / $length;
 | 
			
		||||
 | 
			
		||||
            // Вычисление количества баллонов (округление к большему)
 | 
			
		||||
            return $this->amount = $cutting / $flow;
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Вычисление стоимости баллонов
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $metal Тип металла
 | 
			
		||||
     * @param int|null $gas Используемый газ
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return float|null Стоимость баллонов
 | 
			
		||||
     */
 | 
			
		||||
    public function cost(string $metal, ?int $amount = null, ?string $gas = null, array &$errors = []): ?float
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация входных параметров
 | 
			
		||||
            $amount ?? $amount = &$this->amount;
 | 
			
		||||
            $gas ?? $gas = &$this->gas;
 | 
			
		||||
 | 
			
		||||
            // Инициализация запроса
 | 
			
		||||
            $request = static::$db->prepare("SELECT `cost` FROM `baloons` WHERE `gas` = :gas LIMIT 30");
 | 
			
		||||
 | 
			
		||||
            // Отправка запроса
 | 
			
		||||
            $request->execute([
 | 
			
		||||
                ':gas' => $gas
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            // Генерация ответа
 | 
			
		||||
            $response = $request->fetch(pdo::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
            // Проверка на полученные значения
 | 
			
		||||
            if (!is_array($response)) return null;
 | 
			
		||||
 | 
			
		||||
            // Инициализация стоимости всех баллонов
 | 
			
		||||
            return $this->cost = $response['cost'] * $amount;
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,232 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
 | 
			
		||||
use pdo;
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Модель металла
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class metals_model extends core
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Расчёт скорость реза металла
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $metal Металл
 | 
			
		||||
     * @param string $gas Газ
 | 
			
		||||
     * @param float $lenght Толщина
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return float|int Скорость реза (мм/с)
 | 
			
		||||
     */
 | 
			
		||||
    public static function cut(string $metal, string $gas, float $lenght, array &$errors = []): float|int
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            return (float) settings::read("cut_speed_${metal}_${gas}_$lenght", $errors) ?? throw new exception('Не удалось определить скорость реза металла');
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Определение веса
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $type Тип
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return float|null Вес (кг)
 | 
			
		||||
     */
 | 
			
		||||
    public static function kg(string $type = 'stainless_steel', array &$errors = []): ?float
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            return (float) match ($type) {
 | 
			
		||||
                'steel' => 8,
 | 
			
		||||
                'galvanized_steel' => 8,
 | 
			
		||||
                'stainless_steel' => 8.7,
 | 
			
		||||
                'brass' => 8.7,
 | 
			
		||||
                'copper' => 9,
 | 
			
		||||
                'aluminum' => 3,
 | 
			
		||||
                default => throw new exception('Не удалось определить тип металла')
 | 
			
		||||
            };
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Запись в базу данных
 | 
			
		||||
     *
 | 
			
		||||
     * @param int $supply Идентификатор записи поставки в базе данных
 | 
			
		||||
     * @param string $type Тип
 | 
			
		||||
     * @param string $mark Марка
 | 
			
		||||
     * @param float $width Ширина (мм)
 | 
			
		||||
     * @param float $height Высота (мм)
 | 
			
		||||
     * @param float $length Толщина (мм)
 | 
			
		||||
     * @param int $piece Цена за лист (руб)
 | 
			
		||||
     * @param int $ton Цена за тонну (руб)
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool Статус записи
 | 
			
		||||
     */
 | 
			
		||||
    public static function write(int $supply, string $type, string $mark, float $width, float $height, float $length, int $piece, int $ton, array &$errors = []): bool
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if ($account = accounts::account($errors)) {
 | 
			
		||||
                // Инициализирован аккаунт
 | 
			
		||||
 | 
			
		||||
                // Инициализация запроса
 | 
			
		||||
                $request = static::$db->prepare("INSERT INTO `metals` (`supply`, `type`, `mark`, `width`, `height`, `length`, `piece`, `ton`, `account`) VALUES (:supply, :type, :mark, :width, :height, :length, :piece, :ton, :account)");
 | 
			
		||||
 | 
			
		||||
                // Инициализация параметров
 | 
			
		||||
                $params = [
 | 
			
		||||
                    ':supply' => $supply,
 | 
			
		||||
                    ':type' => $type,
 | 
			
		||||
                    ':mark' => $mark,
 | 
			
		||||
                    ':width' => $width,
 | 
			
		||||
                    ':height' => $height,
 | 
			
		||||
                    ':length' => $length,
 | 
			
		||||
                    ':piece' => $piece,
 | 
			
		||||
                    ':ton' => $ton,
 | 
			
		||||
                    ':account' => $account['id'],
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                // Отправка запроса
 | 
			
		||||
                $request->execute($params);
 | 
			
		||||
 | 
			
		||||
                // Получение ответа
 | 
			
		||||
                return $request->fetch(pdo::FETCH_ASSOC) !== false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Чтение из базы данных
 | 
			
		||||
     *
 | 
			
		||||
     * Очищает от дубликатов
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $type Тип
 | 
			
		||||
     * @param string $mark Марка
 | 
			
		||||
     * @param float $length Толщина
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return float|null Цена за 1 килограмм (руб)
 | 
			
		||||
     */
 | 
			
		||||
    public static function read(string $type, string $mark, float $length, array &$errors = []): ?array
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация запроса
 | 
			
		||||
            $request = static::$db->prepare("SELECT `width`, `height`, `piece`, `ton` FROM `metals` WHERE `type` = :type && `mark` = :mark && `length` = :length LIMIT 30");
 | 
			
		||||
 | 
			
		||||
            // Отправка запроса
 | 
			
		||||
            $request->execute([
 | 
			
		||||
                ':type' => $type,
 | 
			
		||||
                ':mark' => $mark,
 | 
			
		||||
                ':length' => $length
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            // Генерация ответа
 | 
			
		||||
            $response = $request->fetchAll(pdo::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
            // Проверка на полученные значения
 | 
			
		||||
            if (!is_array($response)) return false;
 | 
			
		||||
 | 
			
		||||
            if (count($response) === 1) return $response[0];
 | 
			
		||||
            else if (count($response) > 1) {
 | 
			
		||||
                // Найдено более чем одно значение
 | 
			
		||||
 | 
			
		||||
                // Инициализация буфера вывода
 | 
			
		||||
                $buffer = $response[0];
 | 
			
		||||
 | 
			
		||||
                foreach ($response as $metal) {
 | 
			
		||||
                    // Перебор полученных значений металлов
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер металла с самой большей площадью листа
 | 
			
		||||
                    if (($metal['width'] * $metal['height']) > ($buffer['width'] * $buffer['height'])) $buffer = $metal;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $metal;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Поиск и чтение марок металла
 | 
			
		||||
     *
 | 
			
		||||
     * Очищает от дубликатов
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $type Тип
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return float|null Цена за 1 килограмм (руб)
 | 
			
		||||
     */
 | 
			
		||||
    public static function marks(string $type = '*', array &$errors = []): ?array
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            // Инициализация запроса
 | 
			
		||||
            $request = static::$db->prepare("SELECT DISTINCT `mark` FROM `metals` WHERE `type` = :type");
 | 
			
		||||
 | 
			
		||||
            // Отправка запроса
 | 
			
		||||
            $request->execute([':type' => $type]);
 | 
			
		||||
 | 
			
		||||
            // Генерация ответа
 | 
			
		||||
            $response = $request->fetchAll(pdo::FETCH_COLUMN);
 | 
			
		||||
 | 
			
		||||
            return $response === false ? null : (array) $response;
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
            // Запись в журнал ошибок
 | 
			
		||||
            $errors[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,577 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
let calculator = {
 | 
			
		||||
    index: document.getElementById("calculator"),
 | 
			
		||||
    calculators: [],
 | 
			
		||||
    account: [],
 | 
			
		||||
    settings: {
 | 
			
		||||
        defaults: {
 | 
			
		||||
            buyer: 'individual',
 | 
			
		||||
            complexity: 'medium',
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    init() {
 | 
			
		||||
        // Инициализация калькулятора
 | 
			
		||||
 | 
			
		||||
        this.generate.buyer(this.settings.defaults.buyer)
 | 
			
		||||
            .then(
 | 
			
		||||
                success => {
 | 
			
		||||
                    this.generate.complexity(this.settings.defaults.complexity)
 | 
			
		||||
                        .then(
 | 
			
		||||
                            success => {
 | 
			
		||||
                                this.generate.menu()
 | 
			
		||||
                                    .then(
 | 
			
		||||
                                        success => {
 | 
			
		||||
                                            this.authenticate(cookie.read('id'))
 | 
			
		||||
                                                .then(
 | 
			
		||||
                                                    success => {
 | 
			
		||||
                                                        // Запись данных аккаунта
 | 
			
		||||
                                                        this.account = success;
 | 
			
		||||
 | 
			
		||||
                                                        if (this.account !== undefined && typeof this.account === 'object' && this.account.permissions !== undefined) {
 | 
			
		||||
                                                            // Найден аккаунт
 | 
			
		||||
 | 
			
		||||
                                                            if (this.account.permissions.calculate == 1) {
 | 
			
		||||
                                                                // Разрешено использовать калькулятор
 | 
			
		||||
 | 
			
		||||
                                                                this.generate.result();
 | 
			
		||||
                                                            }
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                );
 | 
			
		||||
                                        }
 | 
			
		||||
                                    );
 | 
			
		||||
                            }
 | 
			
		||||
                        );
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        console.log('[КАЛЬКУЛЯТОР] Инициализирован');
 | 
			
		||||
    },
 | 
			
		||||
    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 cutting = document.getElementById('cutting');
 | 
			
		||||
        let discount = document.getElementById('discount');
 | 
			
		||||
 | 
			
		||||
        // Инициализация буфера запроса
 | 
			
		||||
        let query = {
 | 
			
		||||
            calculators: {},
 | 
			
		||||
            cutting: +cutting.value ?? 0,
 | 
			
		||||
            discount: +discount.value ?? 0
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for (const number in this.calculators) {
 | 
			
		||||
            // Перебор калькуляторов
 | 
			
		||||
 | 
			
		||||
            // Инициализация буфера запроса для нового калькулятора
 | 
			
		||||
            query['calculators'][number] = {};
 | 
			
		||||
 | 
			
		||||
            // Инициализация типа калькулятора
 | 
			
		||||
            query['calculators'][number]['calculator'] = this.calculators[number].getAttribute('data-calculator');
 | 
			
		||||
 | 
			
		||||
            for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) {
 | 
			
		||||
                // Перебор полей с параметрами типа заказчика
 | 
			
		||||
 | 
			
		||||
                if (buyer.checked) {
 | 
			
		||||
                    // Найдено выбранное поле
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер запроса
 | 
			
		||||
                    query['calculators'][number]['buyer'] = buyer.value;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (const complexity of this.index.querySelectorAll('input[name="complexity"]')) {
 | 
			
		||||
                // Перебор полей с параметрами сложности
 | 
			
		||||
 | 
			
		||||
                if (complexity.checked) {
 | 
			
		||||
                    // Найдено выбранное поле
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер запроса
 | 
			
		||||
                    query['calculators'][number]['complexity'] = complexity.value;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (const field of this.calculators[number].querySelectorAll('[data-calculator-parameter]')) {
 | 
			
		||||
                // Перебор полей с параметрами
 | 
			
		||||
 | 
			
		||||
                if (field.getAttribute('type') === 'checkbox') {
 | 
			
		||||
                    // Флажок
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер запроса
 | 
			
		||||
                    query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.checked;
 | 
			
		||||
                } else if (field.getAttribute('type') === 'text' || field.getAttribute('type') === 'number' || field.getAttribute('type') === 'range') {
 | 
			
		||||
                    // Текстовое, цифровое поле или ползунок
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер запроса
 | 
			
		||||
                    query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Элемент с тегом <select> (подразумевается)
 | 
			
		||||
 | 
			
		||||
                    // Запись в буфер запроса
 | 
			
		||||
                    query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return fetch('/calculator/calculate', {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: { "content-type": "application/json" },
 | 
			
		||||
            body: JSON.stringify(query)
 | 
			
		||||
        }).then((response) => {
 | 
			
		||||
            if (response.status === 200) {
 | 
			
		||||
                response.json().then(
 | 
			
		||||
                    success => {
 | 
			
		||||
                        // Инициализация буфера расходов
 | 
			
		||||
                        let expenses = 0;
 | 
			
		||||
 | 
			
		||||
                        // Инициализация буфера с данными расчёта
 | 
			
		||||
                        let result;
 | 
			
		||||
 | 
			
		||||
                        if (this.generate.error(success.errors) > 0) {
 | 
			
		||||
                            // Найдены ошибки
 | 
			
		||||
 | 
			
		||||
                            // Генерация текста ответа
 | 
			
		||||
                            result = 'Ошибка';
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Не найдены ошибки
 | 
			
		||||
 | 
			
		||||
                            if (success.other.cutting !== undefined) {
 | 
			
		||||
                                // Получены данные времени работы
 | 
			
		||||
 | 
			
		||||
                                // Запись полученных данных
 | 
			
		||||
                                cutting.value = success.other.cutting;
 | 
			
		||||
                                cutting.parentElement.children[0].innerText = 'Длина реза 1 детали (' + cutting.value + 'мм)';
 | 
			
		||||
 | 
			
		||||
                                // Разблокировка параметра
 | 
			
		||||
                                cutting.removeAttribute('disabled');
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if (success.other.discount !== undefined) {
 | 
			
		||||
                                // Получены данные скидки
 | 
			
		||||
 | 
			
		||||
                                // Запись полученных данных
 | 
			
		||||
                                discount.value = success.other.discount;
 | 
			
		||||
                                discount.parentElement.children[0].innerText = 'Скидка (' + discount.value + '%)';
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (const [, machine] of Object.entries(success.machines)) {
 | 
			
		||||
                                // Перебор станков
 | 
			
		||||
 | 
			
		||||
                                // Прибавление данных станка к буферу расходов
 | 
			
		||||
                                expenses += (machine.electricity + (machine.metal ?? 0));
 | 
			
		||||
 | 
			
		||||
                                // Прибавление амортизации к буферу вывода
 | 
			
		||||
                                expenses += machine.reprocessing ?? 0;
 | 
			
		||||
 | 
			
		||||
                                // Прибавление линз к буферу вывода
 | 
			
		||||
                                expenses += machine.lenses ?? 0;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (const [, manager] of Object.entries(success.managers)) {
 | 
			
		||||
                                // Перебор менеджеров
 | 
			
		||||
 | 
			
		||||
                                // Прибавление данных менеджера к буферу расходов
 | 
			
		||||
                                expenses += manager.time * manager.hour;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (const [, engineer] of Object.entries(success.engineers)) {
 | 
			
		||||
                                // Перебор инженеров
 | 
			
		||||
 | 
			
		||||
                                // Прибавление данных инженера к буферу расходов
 | 
			
		||||
                                expenses += engineer.time * engineer.hour;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (const [, operator] of Object.entries(success.operators)) {
 | 
			
		||||
                                // Перебор операторов
 | 
			
		||||
 | 
			
		||||
                                // Прибавление данных оператора к буферу расходов
 | 
			
		||||
                                expenses += (operator.time.design + operator.time.machine) * operator.hour;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (const [, handyman] of Object.entries(success.handymans)) {
 | 
			
		||||
                                // Перебор разнорабочих
 | 
			
		||||
 | 
			
		||||
                                // Прибавление данных к буферу расходов
 | 
			
		||||
                                expenses += handyman.time * handyman.hour;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Прибавление аренды к буферу расходов
 | 
			
		||||
                            expenses += success.other.rent ?? 0;
 | 
			
		||||
 | 
			
		||||
                            // Прибавление переработки к буферу расходов
 | 
			
		||||
                            expenses += success.other.reprocessing ?? 0;
 | 
			
		||||
 | 
			
		||||
                            // Прибавление баллонов к буферу расходов
 | 
			
		||||
                            expenses += success.other.baloons.cost ?? 0;
 | 
			
		||||
 | 
			
		||||
                            // Вычисление наценки (коэффициент)
 | 
			
		||||
                            expenses *= success.other.additive ?? 1;
 | 
			
		||||
 | 
			
		||||
                            // Вычитание скидки менеджера
 | 
			
		||||
                            expenses -= expenses * ((discount.value ?? 100) / 100);
 | 
			
		||||
 | 
			
		||||
                            // Округление
 | 
			
		||||
                            expenses = expenses.toFixed(2);
 | 
			
		||||
 | 
			
		||||
                            // Генерация текста ответа
 | 
			
		||||
                            result = expenses + ' рублей';
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (this.generate.result(result)) {
 | 
			
		||||
                            console.log(`[КАЛЬКУЛЯТОР] Сгенерирован результат: ${expenses} рублей`);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    error => {
 | 
			
		||||
                        console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    generate: {
 | 
			
		||||
        buyer(value = 'individual') {
 | 
			
		||||
            // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
 | 
			
		||||
                        },
 | 
			
		||||
                        error => {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора типа покупателя');
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        complexity(value = 'medium') {
 | 
			
		||||
            // Запрос и генерация HTML с данными о сложности работы
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
 | 
			
		||||
                        },
 | 
			
		||||
                        error => {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора сложности');
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        menu() {
 | 
			
		||||
            // Запрос и генерация HTML с кнопками добавления калькулятора
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
 | 
			
		||||
                        },
 | 
			
		||||
                        error => {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками добавления калькулятора');
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        mark(element, type = '') {
 | 
			
		||||
            // Запрос и генерация HTML с полем выбора марки металла
 | 
			
		||||
 | 
			
		||||
            return fetch('/calculator/generate/mark?type=' + type, {
 | 
			
		||||
                method: "POST",
 | 
			
		||||
                headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
			
		||||
            }).then((response) => {
 | 
			
		||||
                if (response.status === 200) {
 | 
			
		||||
                    response.text().then(
 | 
			
		||||
                        success => {
 | 
			
		||||
                            // Поиск устаревшего списка с марками
 | 
			
		||||
                            let old = element.querySelectorAll('select[name="mark"]')[0];
 | 
			
		||||
 | 
			
		||||
                            if (old !== undefined) {
 | 
			
		||||
                                // Найден список с марками
 | 
			
		||||
 | 
			
		||||
                                // Деинициализация
 | 
			
		||||
                                old.parentElement.parentElement.remove();
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Инициализация оболочки в которую необходимо записать список
 | 
			
		||||
                            let wrap = element.querySelectorAll('select[name="type"]')[0].parentElement.parentElement
 | 
			
		||||
 | 
			
		||||
                            // Запись полученного списка в оболочку
 | 
			
		||||
                            wrap.insertAdjacentHTML('afterend', success);
 | 
			
		||||
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] Загружен список с марками металла');
 | 
			
		||||
                        },
 | 
			
		||||
                        error => {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить список с марками металла');
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        divider(element, position) {
 | 
			
		||||
            // Запрос и генерация HTML с данными о результате калькуляции
 | 
			
		||||
 | 
			
		||||
            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) {
 | 
			
		||||
                                // Не задан элемент и позиция добавляемого разделителя
 | 
			
		||||
 | 
			
		||||
                                // Запись разделителя в конце калькулятора
 | 
			
		||||
                                calculator.index.insertAdjacentHTML('beforeend', success);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                // Задан элемент и позиция добавляемого разделителя
 | 
			
		||||
 | 
			
		||||
                                // Запись разделителя по заданным параметрам
 | 
			
		||||
                                element.insertAdjacentHTML(position, success);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
 | 
			
		||||
                        },
 | 
			
		||||
                        error => {
 | 
			
		||||
                            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить разделитель');
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        result(expenses) {
 | 
			
		||||
            // Запрос и генерация HTML с данными о результате калькуляции
 | 
			
		||||
 | 
			
		||||
            function request() {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                                console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
 | 
			
		||||
                            },
 | 
			
		||||
                            error => {
 | 
			
		||||
                                console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с данными о результате калькуляции');
 | 
			
		||||
                            });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (document.getElementById("result") === null) {
 | 
			
		||||
                // Не найден элемент с данными расчётов
 | 
			
		||||
            } else {
 | 
			
		||||
                // Найден элемент с данными расчётов
 | 
			
		||||
 | 
			
		||||
                if (expenses !== undefined) {
 | 
			
		||||
                    // Переданы расходы
 | 
			
		||||
 | 
			
		||||
                    // Инициализация элемента
 | 
			
		||||
                    let element = document.getElementById('calculate');
 | 
			
		||||
 | 
			
		||||
                    if (element == null) {
 | 
			
		||||
                        // Не найден элемент с результатом расчёта
 | 
			
		||||
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Запись расходов в элемент (подразумевается кнопка отправки на расчёт)
 | 
			
		||||
                    element.innerText = expenses;
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return request();
 | 
			
		||||
        },
 | 
			
		||||
        error(errors = []) {
 | 
			
		||||
            // Генерация ошибки
 | 
			
		||||
 | 
			
		||||
            // Инициализация количества обработанных ошибок
 | 
			
		||||
            let amount = 0;
 | 
			
		||||
 | 
			
		||||
            if (typeof errors === 'object') {
 | 
			
		||||
                // Передан массив с ошибками и он является массивом
 | 
			
		||||
 | 
			
		||||
                // Инициализация буфера для проверки вложенности массива
 | 
			
		||||
                let first = Object.values(errors)[0];
 | 
			
		||||
 | 
			
		||||
                if (first !== undefined && first.text === undefined) {
 | 
			
		||||
                    // Найден массив с ошибками (категория)
 | 
			
		||||
 | 
			
		||||
                    // Вход в рекурсию
 | 
			
		||||
                    amount += this.error(first);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Не найден массив с ошибками (подразумевается, что это и есть информация об ошибке)
 | 
			
		||||
 | 
			
		||||
                    // Инициализация элемента-оболочки
 | 
			
		||||
                    let list = document.getElementById('errors');
 | 
			
		||||
 | 
			
		||||
                    // Перезапись данных об ошибках
 | 
			
		||||
                    list.innerText = '';
 | 
			
		||||
 | 
			
		||||
                    // Проверка на наличие ошибок
 | 
			
		||||
                    if (errors.length === 0) return false;
 | 
			
		||||
 | 
			
		||||
                    if (list !== null) {
 | 
			
		||||
                        // Оболочка найдена
 | 
			
		||||
 | 
			
		||||
                        for (const [, error] of Object.entries(errors)) {
 | 
			
		||||
                            // Перебор станков
 | 
			
		||||
 | 
			
		||||
                            // Инициализация элемента-заголовка
 | 
			
		||||
                            let term = document.createElement('dt');
 | 
			
		||||
 | 
			
		||||
                            // Запись содержимого
 | 
			
		||||
                            term.innerText = error.text;
 | 
			
		||||
 | 
			
		||||
                            // Инициализация элемента-описания
 | 
			
		||||
                            let definition = document.createElement('dd');
 | 
			
		||||
 | 
			
		||||
                            // Запись содержимого
 | 
			
		||||
                            definition.innerText = error.file + ' в строке ' + error.line;
 | 
			
		||||
 | 
			
		||||
                            // input.setAttribute('id', element.id);
 | 
			
		||||
 | 
			
		||||
                            // Запись в список
 | 
			
		||||
                            list.insertAdjacentElement('beforeend', term);
 | 
			
		||||
                            list.insertAdjacentElement('beforeend', definition);
 | 
			
		||||
 | 
			
		||||
                            // Добавление к счётчику обработанных ошибок
 | 
			
		||||
                            ++amount;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return amount;
 | 
			
		||||
        },
 | 
			
		||||
        calculators: {
 | 
			
		||||
            laser() {
 | 
			
		||||
                // Запрос и генерация HTML с калькулятором лазерной резки
 | 
			
		||||
 | 
			
		||||
                function write(target, position, html) {
 | 
			
		||||
                    if (target === undefined || position === undefined || html === undefined) return false;
 | 
			
		||||
 | 
			
		||||
                    // Запись калькулятора после последнего калькулятора
 | 
			
		||||
                    target.insertAdjacentHTML(position, html);
 | 
			
		||||
 | 
			
		||||
                    // Поиск калькуляторов
 | 
			
		||||
                    let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
			
		||||
 | 
			
		||||
                    // Инициализация идентификатора калькулятора
 | 
			
		||||
                    let id = calculators.length - 1;
 | 
			
		||||
 | 
			
		||||
                    // Запись калькулятору его идентификатора
 | 
			
		||||
                    calculators[id].id = 'laser_' + id;
 | 
			
		||||
 | 
			
		||||
                    // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
			
		||||
                    calculator.calculators.push(calculators[id]);
 | 
			
		||||
 | 
			
		||||
                    // Запись в журнал
 | 
			
		||||
                    console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
			
		||||
 | 
			
		||||
                    // Инициализация поля с маркой металла
 | 
			
		||||
                    calculator.generate.mark(calculators[id]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                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];
 | 
			
		||||
 | 
			
		||||
                                if (last !== undefined && last !== null) {
 | 
			
		||||
                                    // Найден калькулятор
 | 
			
		||||
 | 
			
		||||
                                    // Инициализация разделителя перед меню
 | 
			
		||||
                                    calculator.generate.divider(last, 'afterend').then(divider => write(last, 'afterend', success));
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    // Не найден калькулятор
 | 
			
		||||
 | 
			
		||||
                                    calculator.generate.divider(menu, 'beforebegin').then(
 | 
			
		||||
                                        first => {
 | 
			
		||||
                                            // Поиск меню
 | 
			
		||||
                                            let menu = document.getElementById("menu");
 | 
			
		||||
 | 
			
		||||
                                            if (menu !== null) {
 | 
			
		||||
                                                // Найдено меню
 | 
			
		||||
 | 
			
		||||
                                                // Инициализация разделителя перед меню
 | 
			
		||||
                                                calculator.generate.divider(menu, 'beforebegin').then(divider => write(menu, 'beforebegin', success));
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                // Не найдено меню
 | 
			
		||||
 | 
			
		||||
                                                // Поиск результатов калькуляции
 | 
			
		||||
                                                let result = document.getElementById("result");
 | 
			
		||||
 | 
			
		||||
                                                if (result !== null) {
 | 
			
		||||
                                                    // Найден элемент с результатами калькуляции
 | 
			
		||||
 | 
			
		||||
                                                    // Инициализация разделителя перед меню
 | 
			
		||||
                                                    calculator.generate.divider(result, 'beforebegin').then(result => write(result, 'beforebegin', success));
 | 
			
		||||
                                                } else {
 | 
			
		||||
                                                    // Не найден элемент с результатами калькуляции
 | 
			
		||||
 | 
			
		||||
                                                    // Инициализация разделителя перед меню
 | 
			
		||||
                                                    calculator.generate.divider().then(result => write(calculator.index, 'beforeend', success));
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            error => {
 | 
			
		||||
                                // Запись в журнал
 | 
			
		||||
                                console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки');
 | 
			
		||||
                            });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<div>
 | 
			
		||||
    <label>Марка</label>
 | 
			
		||||
    <div>
 | 
			
		||||
        <select name="mark" data-calculator-parameter="mark"  title="Марка стали">
 | 
			
		||||
            {% for mark in marks %}
 | 
			
		||||
            <option value="{{ mark }}">{{ mark }}</option>
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
        </select>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
<section id="menu" class="unselectable">
 | 
			
		||||
    <a type="button" onclick="calculator.generate.calculators.laser(); return false;">
 | 
			
		||||
        <img src="/img/laser.png" title="Добавить лазерную резку">
 | 
			
		||||
        Лазерная резка
 | 
			
		||||
    </a>
 | 
			
		||||
    <a type="button" onclick="calculator.generate.calculators.plasma(); return false;">
 | 
			
		||||
        <img src="/img/plasma.png" title="Добавить плазменную резку">
 | 
			
		||||
        Плазменная резка
 | 
			
		||||
    </a>
 | 
			
		||||
    <a type="button" onclick="calculator.generate.calculators.bending(); return false;">
 | 
			
		||||
        <img src="/img/bending.png" title="Добавить гибку металла">
 | 
			
		||||
        Гибка металла
 | 
			
		||||
    </a>
 | 
			
		||||
    <a type="button" onclick="calculator.generate.calculators.painting(); return false;">
 | 
			
		||||
        <img src="/img/painting.png" title="Добавить порошковую покраску">
 | 
			
		||||
        Порошковая покраска
 | 
			
		||||
    </a>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -2,16 +2,16 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер пользователей
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class accounts_controller extends core
 | 
			
		||||
@@ -0,0 +1,258 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\calculators_model as calculators;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\fences_model as fences;
 | 
			
		||||
 | 
			
		||||
use Twig\Loader\FilesystemLoader;
 | 
			
		||||
use Twig\Environment as view;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер основной страницы
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class calculator_controller extends core
 | 
			
		||||
{
 | 
			
		||||
  /**
 | 
			
		||||
   * Калькулятор
 | 
			
		||||
   *
 | 
			
		||||
   * HTML-код с калькулятором
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $vars Параметры
 | 
			
		||||
   */
 | 
			
		||||
  public function index(array $vars = []): ?string
 | 
			
		||||
  {
 | 
			
		||||
    // Генерация представления
 | 
			
		||||
    return $this->view->render(DIRECTORY_SEPARATOR . 'calculator' . DIRECTORY_SEPARATOR . 'index.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "тип пользователя"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками: "физическое лицо" и "юридическое лицо"
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function buyer(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация параметров
 | 
			
		||||
      $vars['buyer'] = $vars['value'] ?? 'individual';
 | 
			
		||||
 | 
			
		||||
      // Удаление параметров
 | 
			
		||||
      unset($vars['value']);
 | 
			
		||||
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'buyer.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "сложность"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками: "легко", "средне" и "сложно"
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function complexity(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация параметров
 | 
			
		||||
      $vars['complexity'] = $vars['value'] ?? 'medium';
 | 
			
		||||
 | 
			
		||||
      // Удаление параметров
 | 
			
		||||
      unset($vars['value']);
 | 
			
		||||
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'complexity.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "меню"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с кнопками добавления калькуляторов
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function menu(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'menu.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "результат"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с данными результата калькуляции
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function result(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация журнала ошибок
 | 
			
		||||
      $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
      // Инициализация данных калькулятора
 | 
			
		||||
      $vars['discount'] = settings::read('discount', $vars['errors']['calculators']);
 | 
			
		||||
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'result.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "тип забора"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код со списком типов забора
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function fence(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация журнала ошибок
 | 
			
		||||
      $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'fences' . DIRECTORY_SEPARATOR . $vars['type'] . '.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Модуль: "разделитель"
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с разделителем элементов
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     */
 | 
			
		||||
    public function divider(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'divider.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Лазерная резка
 | 
			
		||||
     *
 | 
			
		||||
     * HTML-код с калькулятором лазерной резки
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     *
 | 
			
		||||
     * @todo 1. Заголовок калькулятора должен находиться внутри элемента калькулятора
 | 
			
		||||
     * 2. Ограничение значений полей в зависимости от выбранной марки
 | 
			
		||||
     */
 | 
			
		||||
    public function profnastil(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация журнала ошибок
 | 
			
		||||
      $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
      // Инициализация данных калькулятора
 | 
			
		||||
      $vars['calculators'] = ['profnastil' => [
 | 
			
		||||
        'company' => settings::read('default_buyer', $vars['errors']['calculators']),
 | 
			
		||||
        'complexity' => settings::read('default_complexity', $vars['errors']['calculators']),
 | 
			
		||||
        'width' => (int) round((float) settings::read('default_width', $vars['errors']['calculators'])),
 | 
			
		||||
        'height' => (int) round((float) settings::read('default_height', $vars['errors']['calculators'])),
 | 
			
		||||
        'length' => (int) round((float) settings::read('default_length', $vars['errors']['calculators'])),
 | 
			
		||||
        'amount' => (int) round((float) settings::read('default_amount', $vars['errors']['calculators'])),
 | 
			
		||||
        'type' => settings::read('default_type', $vars['errors']['calculators']),
 | 
			
		||||
        'holes' => (int) round((float) settings::read('default_holes', $vars['errors']['calculators'])),
 | 
			
		||||
        'diameter' => (float) settings::read('default_diameter', $vars['errors']['calculators'])
 | 
			
		||||
        ]];
 | 
			
		||||
 | 
			
		||||
      // Генерация представления
 | 
			
		||||
      return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'profnastil.html', $vars);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Расчёт
 | 
			
		||||
     *
 | 
			
		||||
     * Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0]
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $vars Параметры
 | 
			
		||||
     *
 | 
			
		||||
     * @todo
 | 
			
		||||
     * 5. Убрать передачу цены работы (оставить только время работы в часах и цену за работу в час)
 | 
			
		||||
     */
 | 
			
		||||
    public function calculate(array $vars = []): ?string
 | 
			
		||||
    {
 | 
			
		||||
      // Инициализация журнала ошибок
 | 
			
		||||
      $vars['errors'] = ['calculators' => []];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        // Инициализация параметров из тела запроса (подразумевается, что там массивы с параметрами)
 | 
			
		||||
        $vars['input'] = json_decode(file_get_contents('php://input'), true);
 | 
			
		||||
 | 
			
		||||
        $calculators = $vars['input']['calculators'];
 | 
			
		||||
        $discount = $vars['input']['discount'];
 | 
			
		||||
        $cutting = $vars['input']['cutting'];
 | 
			
		||||
 | 
			
		||||
        // Инициализация переменных для буфера вывода
 | 
			
		||||
        $machines = $managers = $engineers = $operators = $handymans = $other = 0;
 | 
			
		||||
 | 
			
		||||
        if (count($calculators) > 0) {
 | 
			
		||||
          // Найдены калькуляторы
 | 
			
		||||
 | 
			
		||||
          foreach ($calculators as $i => $calculator) {
 | 
			
		||||
            // Перебор калькуляторов
 | 
			
		||||
 | 
			
		||||
            foreach (['calculator'] as &$parameter) {
 | 
			
		||||
              // Перебор мета-параметров
 | 
			
		||||
 | 
			
		||||
              // Инициализация общего параметра
 | 
			
		||||
              $type =  $calculator[$parameter];
 | 
			
		||||
 | 
			
		||||
              // Инициализация параметра для обработчика калькулятора
 | 
			
		||||
              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;
 | 
			
		||||
 | 
			
		||||
                    // Расчёт
 | 
			
		||||
                    [$machines, $managers, $engineers, $operators, $handymans, $other] = calculators::$type(...$parameters + ['cutting' => $cutting]);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
              // Не найдены калькуляторы
 | 
			
		||||
 | 
			
		||||
              throw new exception('Не найдены калькуляторы');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
          // Запись в журнал ошибок
 | 
			
		||||
          $vars['errors']['calculators'][] = [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return json_encode([
 | 
			
		||||
          'machines' => $machines,
 | 
			
		||||
          'managers' => $managers,
 | 
			
		||||
          'engineers' => $engineers,
 | 
			
		||||
          'operators' => $operators,
 | 
			
		||||
          'handymans' => $handymans,
 | 
			
		||||
          'other' => $other + ['discount' => $discount],
 | 
			
		||||
          'errors' => $vars['errors']
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,15 +2,15 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер контактов
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class contacts_controller extends core
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\views\manager;
 | 
			
		||||
use mirzaev\tordv\calculator\models\core as models;
 | 
			
		||||
use mirzaev\zkmr\calculator\views\manager;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\core as models;
 | 
			
		||||
 | 
			
		||||
use mirzaev\minimal\controller;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Менеджер представлений
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
class core extends controller
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
 | 
			
		||||
use Twig\Loader\FilesystemLoader;
 | 
			
		||||
use Twig\Environment as view;
 | 
			
		||||
@@ -12,7 +12,7 @@ use Twig\Environment as view;
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер основной страницы
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class errors_controller extends core
 | 
			
		||||
@@ -2,15 +2,15 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер журналов
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class journal_controller extends core
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
 | 
			
		||||
use Twig\Loader\FilesystemLoader;
 | 
			
		||||
use Twig\Environment as view;
 | 
			
		||||
@@ -13,7 +13,7 @@ use Twig\Environment as view;
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер основной страницы
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class main_controller extends core
 | 
			
		||||
@@ -2,16 +2,16 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\tordv\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\settings_model as settings;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер настроек
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class settings_controller extends core
 | 
			
		||||
@@ -2,25 +2,25 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\controllers;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\controllers;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\controllers\core;
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\tordv\calculator\models\supplies_model as supplies;
 | 
			
		||||
use mirzaev\zkmr\calculator\controllers\core;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\supplies_model as supplies;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_aluminum_filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_brass_filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_copper_filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_stainless_steel_filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_steel_filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_galvanized_steel_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_aluminum_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_brass_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_copper_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_stainless_steel_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_steel_filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_galvanized_steel_filter;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Контроллер поставок
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class supplies_controller extends core
 | 
			
		||||
							
								
								
									
										621
									
								
								mirzaev/zkmr/calculator/system/models/accounts_model.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,621 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models;
 | 
			
		||||
 | 
			
		||||
use pdo;
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Модель регистрации, аутентификации и авторизации
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\zkmr\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 Аккаунт, если удалось аутентифицироваться
 | 
			
		||||
   */
 | 
			
		||||
  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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Проверить разрешение
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $permission Разрешение
 | 
			
		||||
     * @param int|null $id Идентификатор аккаунта
 | 
			
		||||
     * @param array &$errors Журнал ошибок
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool|null Статус разрешения, если оно записано
 | 
			
		||||
     */
 | 
			
		||||
    public static function access(string $permission, int|null $id = null, array &$errors = []): ?bool
 | 
			
		||||
    {
 | 
			
		||||
      try {
 | 
			
		||||
        // Инициализация аккаунта
 | 
			
		||||
        $account = isset($id) ? self::read(['id' => $id], $errors) : self::account($errors);
 | 
			
		||||
 | 
			
		||||
        return isset($account['permissions'][$permission]) ? (bool) $account['permissions'][$permission] : null;
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
          // Запись в журнал ошибок
 | 
			
		||||
          $errors[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Запись пользователя в базу данных
 | 
			
		||||
     *
 | 
			
		||||
     * @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[] = [
 | 
			
		||||
                    'text' => $e->getMessage(),
 | 
			
		||||
                    'file' => $e->getFile(),
 | 
			
		||||
                    'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                  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[] = [
 | 
			
		||||
                    'text' => $e->getMessage(),
 | 
			
		||||
                    'file' => $e->getFile(),
 | 
			
		||||
                    'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                  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[] = [
 | 
			
		||||
                    'text' => $e->getMessage(),
 | 
			
		||||
                    'file' => $e->getFile(),
 | 
			
		||||
                    'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                  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[] = [
 | 
			
		||||
                'text' => $e->getMessage(),
 | 
			
		||||
                'file' => $e->getFile(),
 | 
			
		||||
                'line' => $e->getLine(),
 | 
			
		||||
                'stack' => $e->getTrace()
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
          // Запись в журнал ошибок
 | 
			
		||||
          $errors[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Конец выполнения
 | 
			
		||||
        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[] = [
 | 
			
		||||
                    'text' => $e->getMessage(),
 | 
			
		||||
                    'file' => $e->getFile(),
 | 
			
		||||
                    'line' => $e->getLine()
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
              // Не найден аккаунт
 | 
			
		||||
 | 
			
		||||
              throw new exception('Не удалось найти аккаунт');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (exception $e) {
 | 
			
		||||
          // Запись в журнал ошибок
 | 
			
		||||
          $errors[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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[]= [
 | 
			
		||||
            'text' => $e->getMessage(),
 | 
			
		||||
            'file' => $e->getFile(),
 | 
			
		||||
            'line' => $e->getLine(),
 | 
			
		||||
            'stack' => $e->getTrace()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ['hash' => $hash, 'time' => $time];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,18 +2,18 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
			
		||||
use mirzaev\tordv\calculator\models\baloons_model as baloons;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\settings_model as settings;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\baloons_model as baloons;
 | 
			
		||||
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Модель калькуляторов
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class calculators_model extends core
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models;
 | 
			
		||||
 | 
			
		||||
use mirzaev\minimal\model;
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ use exception;
 | 
			
		||||
/**
 | 
			
		||||
 * Ядро моделей
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
class core extends model
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на алюминий от неизвестного поставщика под номером 1
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class import_aluminum_filter extends import_filter_noname_1
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на латунь от неизвестного поставщика под номером 1
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class import_brass_filter extends import_filter_noname_1
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на медь от неизвестного поставщика под номером 1
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class import_copper_filter extends import_filter_noname_1
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter as filter;
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
			
		||||
@@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
			
		||||
/**
 | 
			
		||||
 * Интерфейс фильтра импорта
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
interface import_filter extends filter
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
			
		||||
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта для документов от "МЕТАЛЛСЕРВИС"
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
class import_filter_mc implements filter
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
 | 
			
		||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
			
		||||
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 | 
			
		||||
@@ -13,7 +13,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта для документов от неизвестного поставщика под номером 1
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
class import_filter_noname_1 implements filter
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на оцинкованную сталь от "МЕТАЛЛСЕРВИС"
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 *
 | 
			
		||||
 * @todo 1. Доделать 982 до 992 строки
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на нержавеющую сталь от неизвестного поставщика под номером 1
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 *
 | 
			
		||||
 * @todo 1. Доделать 982 до 992 строки
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models\filters;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models\filters;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter_mc;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter_mc;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Фильтр импорта цен на чёрную сталь от "МЕТАЛЛСЕРВИС"
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models\filters
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class import_steel_filter extends import_filter_mc
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models;
 | 
			
		||||
 | 
			
		||||
use pdo;
 | 
			
		||||
use exception;
 | 
			
		||||
@@ -10,7 +10,7 @@ use exception;
 | 
			
		||||
/**
 | 
			
		||||
 * Модель настроек
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class settings_model extends core
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\models;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\models;
 | 
			
		||||
 | 
			
		||||
use mirzaev\tordv\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
 | 
			
		||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
 | 
			
		||||
 | 
			
		||||
use PhpOffice\PhpSpreadsheet\Reader\Xls as reader;
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ use exception;
 | 
			
		||||
/**
 | 
			
		||||
 * Модель поставки
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\models
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\models
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class supplies_model extends core
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB  | 
| 
		 Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB  | 
| 
		 Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB  | 
| 
		 Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB  | 
| 
		 Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB  | 
| 
		 Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB  | 
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator;
 | 
			
		||||
namespace mirzaev\zkmr\calculator;
 | 
			
		||||
 | 
			
		||||
use mirzaev\minimal\core;
 | 
			
		||||
use mirzaev\minimal\router;
 | 
			
		||||
@@ -10,9 +10,9 @@ use mirzaev\minimal\router;
 | 
			
		||||
define('SUPPLIES', realpath('supplies'));
 | 
			
		||||
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
 | 
			
		||||
define('TYPE', 'mysql');
 | 
			
		||||
define('BASE', 'calculator');
 | 
			
		||||
define('HOST', '127.0.0.1');
 | 
			
		||||
define('LOGIN', 'root');
 | 
			
		||||
define('BASE', 'zkmr-calculator');
 | 
			
		||||
define('HOST', 'localhost');
 | 
			
		||||
define('LOGIN', '');
 | 
			
		||||
define('PASSWORD', '');
 | 
			
		||||
 | 
			
		||||
// Автозагрузка
 | 
			
		||||
@@ -33,9 +33,9 @@ $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/mark', 'calculator', 'mark', 'POST');
 | 
			
		||||
$router->write('/calculator/generate/fence', 'calculator', 'fence', 'POST');
 | 
			
		||||
$router->write('/calculator/generate/divider', 'calculator', 'divider', 'POST');
 | 
			
		||||
$router->write('/calculator/generate/laser', 'calculator', 'laser', 'POST');
 | 
			
		||||
$router->write('/calculator/generate/profnastil', 'calculator', 'profnastil', 'POST');
 | 
			
		||||
$router->write('/calculator/calculate', 'calculator', 'calculate', 'POST');
 | 
			
		||||
$router->write('/settings', 'settings', 'index', 'GET');
 | 
			
		||||
$router->write('/settings/write', 'settings', 'write', 'POST');
 | 
			
		||||
							
								
								
									
										568
									
								
								mirzaev/zkmr/calculator/system/public/js/calculator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,568 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
let calculator = {
 | 
			
		||||
  index: document.getElementById("calculator"),
 | 
			
		||||
  calculators: [],
 | 
			
		||||
  account: [],
 | 
			
		||||
  settings: {
 | 
			
		||||
    defaults: {
 | 
			
		||||
      buyer: 'individual',
 | 
			
		||||
      complexity: 'medium',
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  init() {
 | 
			
		||||
    // Инициализация калькулятора
 | 
			
		||||
 | 
			
		||||
    this.generate.buyer(this.settings.defaults.buyer)
 | 
			
		||||
      .then(
 | 
			
		||||
        success => {
 | 
			
		||||
          this.generate.complexity(this.settings.defaults.complexity)
 | 
			
		||||
            .then(
 | 
			
		||||
              success => {
 | 
			
		||||
                this.generate.menu()
 | 
			
		||||
                  .then(
 | 
			
		||||
                    success => {
 | 
			
		||||
                      this.authenticate(cookie.read('id'))
 | 
			
		||||
                        .then(
 | 
			
		||||
                          success => {
 | 
			
		||||
                            // Запись данных аккаунта
 | 
			
		||||
                            this.account = success;
 | 
			
		||||
 | 
			
		||||
                            if (this.account !== undefined && typeof this.account === 'object' && this.account.permissions !== undefined) {
 | 
			
		||||
                              // Найден аккаунт
 | 
			
		||||
 | 
			
		||||
                              if (this.account.permissions.calculate == 1) {
 | 
			
		||||
                                // Разрешено использовать калькулятор
 | 
			
		||||
 | 
			
		||||
                                this.generate.result();
 | 
			
		||||
                              }
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                  );
 | 
			
		||||
              }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    console.log('[КАЛЬКУЛЯТОР] Инициализирован');
 | 
			
		||||
  },
 | 
			
		||||
  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 cutting = document.getElementById('cutting');
 | 
			
		||||
    let discount = document.getElementById('discount');
 | 
			
		||||
 | 
			
		||||
    // Инициализация буфера запроса
 | 
			
		||||
    let query = {
 | 
			
		||||
      calculators: {},
 | 
			
		||||
      cutting: +cutting.value ?? 0,
 | 
			
		||||
      discount: +discount.value ?? 0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (const number in this.calculators) {
 | 
			
		||||
      // Перебор калькуляторов
 | 
			
		||||
 | 
			
		||||
      // Инициализация буфера запроса для нового калькулятора
 | 
			
		||||
      query['calculators'][number] = {};
 | 
			
		||||
 | 
			
		||||
      // Инициализация типа калькулятора
 | 
			
		||||
      query['calculators'][number]['calculator'] = this.calculators[number].getAttribute('data-calculator');
 | 
			
		||||
 | 
			
		||||
      for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) {
 | 
			
		||||
        // Перебор полей с параметрами типа заказчика
 | 
			
		||||
 | 
			
		||||
        if (buyer.checked) {
 | 
			
		||||
          // Найдено выбранное поле
 | 
			
		||||
 | 
			
		||||
          // Запись в буфер запроса
 | 
			
		||||
          query['calculators'][number]['buyer'] = buyer.value;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (const complexity of this.index.querySelectorAll('input[name="complexity"]')) {
 | 
			
		||||
        // Перебор полей с параметрами сложности
 | 
			
		||||
 | 
			
		||||
        if (complexity.checked) {
 | 
			
		||||
          // Найдено выбранное поле
 | 
			
		||||
 | 
			
		||||
          // Запись в буфер запроса
 | 
			
		||||
          query['calculators'][number]['complexity'] = complexity.value;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (const field of this.calculators[number].querySelectorAll('[data-calculator-parameter]')) {
 | 
			
		||||
        // Перебор полей с параметрами
 | 
			
		||||
 | 
			
		||||
        if (field.getAttribute('type') === 'checkbox') {
 | 
			
		||||
          // Флажок
 | 
			
		||||
 | 
			
		||||
          // Запись в буфер запроса
 | 
			
		||||
          query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.checked;
 | 
			
		||||
        } else if (field.getAttribute('type') === 'text' || field.getAttribute('type') === 'number' || field.getAttribute('type') === 'range') {
 | 
			
		||||
          // Текстовое, цифровое поле или ползунок
 | 
			
		||||
 | 
			
		||||
          // Запись в буфер запроса
 | 
			
		||||
          query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value;
 | 
			
		||||
        } else {
 | 
			
		||||
          // Элемент с тегом <select> (подразумевается)
 | 
			
		||||
 | 
			
		||||
          // Запись в буфер запроса
 | 
			
		||||
          query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return fetch('/calculator/calculate', {
 | 
			
		||||
      method: "POST",
 | 
			
		||||
      headers: { "content-type": "application/json" },
 | 
			
		||||
      body: JSON.stringify(query)
 | 
			
		||||
    }).then((response) => {
 | 
			
		||||
      if (response.status === 200) {
 | 
			
		||||
        response.json().then(
 | 
			
		||||
          success => {
 | 
			
		||||
            // Инициализация буфера расходов
 | 
			
		||||
            let expenses = 0;
 | 
			
		||||
 | 
			
		||||
            // Инициализация буфера с данными расчёта
 | 
			
		||||
            let result;
 | 
			
		||||
 | 
			
		||||
            if (this.generate.error(success.errors) > 0) {
 | 
			
		||||
              // Найдены ошибки
 | 
			
		||||
 | 
			
		||||
              // Генерация текста ответа
 | 
			
		||||
              result = 'Ошибка';
 | 
			
		||||
            } else {
 | 
			
		||||
              // Не найдены ошибки
 | 
			
		||||
 | 
			
		||||
              if (success.other.cutting !== undefined) {
 | 
			
		||||
                // Получены данные времени работы
 | 
			
		||||
 | 
			
		||||
                // Запись полученных данных
 | 
			
		||||
                cutting.value = success.other.cutting;
 | 
			
		||||
                cutting.parentElement.children[0].innerText = 'Длина реза 1 детали (' + cutting.value + 'мм)';
 | 
			
		||||
 | 
			
		||||
                // Разблокировка параметра
 | 
			
		||||
                cutting.removeAttribute('disabled');
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              if (success.other.discount !== undefined) {
 | 
			
		||||
                // Получены данные скидки
 | 
			
		||||
 | 
			
		||||
                // Запись полученных данных
 | 
			
		||||
                discount.value = success.other.discount;
 | 
			
		||||
                discount.parentElement.children[0].innerText = 'Скидка (' + discount.value + '%)';
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              for (const [, machine] of Object.entries(success.machines)) {
 | 
			
		||||
                // Перебор станков
 | 
			
		||||
 | 
			
		||||
                // Прибавление данных станка к буферу расходов
 | 
			
		||||
                expenses += (machine.electricity + (machine.metal ?? 0));
 | 
			
		||||
 | 
			
		||||
                // Прибавление амортизации к буферу вывода
 | 
			
		||||
                expenses += machine.reprocessing ?? 0;
 | 
			
		||||
 | 
			
		||||
                // Прибавление линз к буферу вывода
 | 
			
		||||
                expenses += machine.lenses ?? 0;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              for (const [, manager] of Object.entries(success.managers)) {
 | 
			
		||||
                // Перебор менеджеров
 | 
			
		||||
 | 
			
		||||
                // Прибавление данных менеджера к буферу расходов
 | 
			
		||||
                expenses += manager.time * manager.hour;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              for (const [, engineer] of Object.entries(success.engineers)) {
 | 
			
		||||
                // Перебор инженеров
 | 
			
		||||
 | 
			
		||||
                // Прибавление данных инженера к буферу расходов
 | 
			
		||||
                expenses += engineer.time * engineer.hour;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              for (const [, operator] of Object.entries(success.operators)) {
 | 
			
		||||
                // Перебор операторов
 | 
			
		||||
 | 
			
		||||
                // Прибавление данных оператора к буферу расходов
 | 
			
		||||
                expenses += (operator.time.design + operator.time.machine) * operator.hour;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              for (const [, handyman] of Object.entries(success.handymans)) {
 | 
			
		||||
                // Перебор разнорабочих
 | 
			
		||||
 | 
			
		||||
                // Прибавление данных к буферу расходов
 | 
			
		||||
                expenses += handyman.time * handyman.hour;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              // Прибавление аренды к буферу расходов
 | 
			
		||||
              expenses += success.other.rent ?? 0;
 | 
			
		||||
 | 
			
		||||
              // Прибавление переработки к буферу расходов
 | 
			
		||||
              expenses += success.other.reprocessing ?? 0;
 | 
			
		||||
 | 
			
		||||
              // Прибавление баллонов к буферу расходов
 | 
			
		||||
              expenses += success.other.baloons.cost ?? 0;
 | 
			
		||||
 | 
			
		||||
              // Вычисление наценки (коэффициент)
 | 
			
		||||
              expenses *= success.other.additive ?? 1;
 | 
			
		||||
 | 
			
		||||
              // Вычитание скидки менеджера
 | 
			
		||||
              expenses -= expenses * ((discount.value ?? 100) / 100);
 | 
			
		||||
 | 
			
		||||
              // Округление
 | 
			
		||||
              expenses = expenses.toFixed(2);
 | 
			
		||||
 | 
			
		||||
              // Генерация текста ответа
 | 
			
		||||
              result = expenses + ' рублей';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.generate.result(result)) {
 | 
			
		||||
              console.log(`[КАЛЬКУЛЯТОР] Сгенерирован результат: ${expenses} рублей`);
 | 
			
		||||
            } else {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          error => {
 | 
			
		||||
            console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
 | 
			
		||||
          });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  generate: {
 | 
			
		||||
    buyer(value = 'individual') {
 | 
			
		||||
      // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
 | 
			
		||||
            },
 | 
			
		||||
            error => {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора типа покупателя');
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    complexity(value = 'medium') {
 | 
			
		||||
      // Запрос и генерация HTML с данными о сложности работы
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
 | 
			
		||||
            },
 | 
			
		||||
            error => {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора сложности');
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    menu() {
 | 
			
		||||
      // Запрос и генерация HTML с кнопками добавления калькулятора
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
 | 
			
		||||
            },
 | 
			
		||||
            error => {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками добавления калькулятора');
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    fence(element, type = 1) {
 | 
			
		||||
      // Генерация полей с выбором типа забора 
 | 
			
		||||
 | 
			
		||||
      return fetch('/calculator/generate/fence?type=' + type, {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        headers: { "content-type": "application/x-www-form-urlencoded" }
 | 
			
		||||
      }).then((response) => {
 | 
			
		||||
        if (response.status === 200) {
 | 
			
		||||
          response.text().then(
 | 
			
		||||
            success => {
 | 
			
		||||
              // Поиск оболочки списка
 | 
			
		||||
              let wrap = element.querySelectorAll('select[name="fence"]')[0];
 | 
			
		||||
 | 
			
		||||
              // Запись полученного списка в оболочку
 | 
			
		||||
              wrap.parentElement.parentElement.insertAdjacentHTML('afterend', success);
 | 
			
		||||
 | 
			
		||||
              // Удаление старого списка
 | 
			
		||||
              if (wrap !== undefined) wrap.parentElement.parentElement.remove();
 | 
			
		||||
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] Загружен список с типами забора');
 | 
			
		||||
            },
 | 
			
		||||
            error => {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить список с типами забора');
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    divider(element, position) {
 | 
			
		||||
      // Запрос и генерация HTML с данными о результате калькуляции
 | 
			
		||||
 | 
			
		||||
      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) {
 | 
			
		||||
                // Не задан элемент и позиция добавляемого разделителя
 | 
			
		||||
 | 
			
		||||
                // Запись разделителя в конце калькулятора
 | 
			
		||||
                calculator.index.insertAdjacentHTML('beforeend', success);
 | 
			
		||||
              } else {
 | 
			
		||||
                // Задан элемент и позиция добавляемого разделителя
 | 
			
		||||
 | 
			
		||||
                // Запись разделителя по заданным параметрам
 | 
			
		||||
                element.insertAdjacentHTML(position, success);
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
 | 
			
		||||
            },
 | 
			
		||||
            error => {
 | 
			
		||||
              console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить разделитель');
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    result(expenses) {
 | 
			
		||||
      // Запрос и генерация HTML с данными о результате калькуляции
 | 
			
		||||
 | 
			
		||||
      function request() {
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
                console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
 | 
			
		||||
              },
 | 
			
		||||
              error => {
 | 
			
		||||
                console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с данными о результате калькуляции');
 | 
			
		||||
              });
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (document.getElementById("result") === null) {
 | 
			
		||||
        // Не найден элемент с данными расчётов
 | 
			
		||||
      } else {
 | 
			
		||||
        // Найден элемент с данными расчётов
 | 
			
		||||
 | 
			
		||||
        if (expenses !== undefined) {
 | 
			
		||||
          // Переданы расходы
 | 
			
		||||
 | 
			
		||||
          // Инициализация элемента
 | 
			
		||||
          let element = document.getElementById('calculate');
 | 
			
		||||
 | 
			
		||||
          if (element == null) {
 | 
			
		||||
            // Не найден элемент с результатом расчёта
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Запись расходов в элемент (подразумевается кнопка отправки на расчёт)
 | 
			
		||||
          element.innerText = expenses;
 | 
			
		||||
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return request();
 | 
			
		||||
    },
 | 
			
		||||
    error(errors = []) {
 | 
			
		||||
      // Генерация ошибки
 | 
			
		||||
 | 
			
		||||
      // Инициализация количества обработанных ошибок
 | 
			
		||||
      let amount = 0;
 | 
			
		||||
 | 
			
		||||
      if (typeof errors === 'object') {
 | 
			
		||||
        // Передан массив с ошибками и он является массивом
 | 
			
		||||
 | 
			
		||||
        // Инициализация буфера для проверки вложенности массива
 | 
			
		||||
        let first = Object.values(errors)[0];
 | 
			
		||||
 | 
			
		||||
        if (first !== undefined && first.text === undefined) {
 | 
			
		||||
          // Найден массив с ошибками (категория)
 | 
			
		||||
 | 
			
		||||
          // Вход в рекурсию
 | 
			
		||||
          amount += this.error(first);
 | 
			
		||||
        } else {
 | 
			
		||||
          // Не найден массив с ошибками (подразумевается, что это и есть информация об ошибке)
 | 
			
		||||
 | 
			
		||||
          // Инициализация элемента-оболочки
 | 
			
		||||
          let list = document.getElementById('errors');
 | 
			
		||||
 | 
			
		||||
          // Перезапись данных об ошибках
 | 
			
		||||
          list.innerText = '';
 | 
			
		||||
 | 
			
		||||
          // Проверка на наличие ошибок
 | 
			
		||||
          if (errors.length === 0) return false;
 | 
			
		||||
 | 
			
		||||
          if (list !== null) {
 | 
			
		||||
            // Оболочка найдена
 | 
			
		||||
 | 
			
		||||
            for (const [, error] of Object.entries(errors)) {
 | 
			
		||||
              // Перебор станков
 | 
			
		||||
 | 
			
		||||
              // Инициализация элемента-заголовка
 | 
			
		||||
              let term = document.createElement('dt');
 | 
			
		||||
 | 
			
		||||
              // Запись содержимого
 | 
			
		||||
              term.innerText = error.text;
 | 
			
		||||
 | 
			
		||||
              // Инициализация элемента-описания
 | 
			
		||||
              let definition = document.createElement('dd');
 | 
			
		||||
 | 
			
		||||
              // Запись содержимого
 | 
			
		||||
              definition.innerText = error.file + ' в строке ' + error.line;
 | 
			
		||||
 | 
			
		||||
              // input.setAttribute('id', element.id);
 | 
			
		||||
 | 
			
		||||
              // Запись в список
 | 
			
		||||
              list.insertAdjacentElement('beforeend', term);
 | 
			
		||||
              list.insertAdjacentElement('beforeend', definition);
 | 
			
		||||
 | 
			
		||||
              // Добавление к счётчику обработанных ошибок
 | 
			
		||||
              ++amount;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return amount;
 | 
			
		||||
    },
 | 
			
		||||
    calculators: {
 | 
			
		||||
      profnastil() {
 | 
			
		||||
        // Запрос и генерация HTML с калькулятором лазерной резки
 | 
			
		||||
 | 
			
		||||
        function write(target, position, html) {
 | 
			
		||||
          if (target === undefined || position === undefined || html === undefined) return false;
 | 
			
		||||
 | 
			
		||||
          // Запись калькулятора после последнего калькулятора
 | 
			
		||||
          target.insertAdjacentHTML(position, html);
 | 
			
		||||
 | 
			
		||||
          // Поиск калькуляторов
 | 
			
		||||
          let calculators = calculator.index.querySelectorAll('section[data-calculator]');
 | 
			
		||||
 | 
			
		||||
          // Инициализация идентификатора калькулятора
 | 
			
		||||
          let id = calculators.length - 1;
 | 
			
		||||
 | 
			
		||||
          // Запись калькулятору его идентификатора
 | 
			
		||||
          calculators[id].id = 'profnastil_' + id;
 | 
			
		||||
 | 
			
		||||
          // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
			
		||||
          calculator.calculators.push(calculators[id]);
 | 
			
		||||
 | 
			
		||||
          // Запись в журнал
 | 
			
		||||
          console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return fetch('/calculator/generate/profnastil', {
 | 
			
		||||
          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];
 | 
			
		||||
 | 
			
		||||
                if (last !== undefined && last !== null) {
 | 
			
		||||
                  // Найден калькулятор
 | 
			
		||||
 | 
			
		||||
                  // Инициализация разделителя перед меню
 | 
			
		||||
                  calculator.generate.divider(last, 'afterend').then(divider => write(last, 'afterend', success));
 | 
			
		||||
                } else {
 | 
			
		||||
                  // Не найден калькулятор
 | 
			
		||||
 | 
			
		||||
                  calculator.generate.divider(menu, 'beforebegin').then(
 | 
			
		||||
                    first => {
 | 
			
		||||
                      // Поиск меню
 | 
			
		||||
                      let menu = document.getElementById("menu");
 | 
			
		||||
 | 
			
		||||
                      if (menu !== null) {
 | 
			
		||||
                        // Найдено меню
 | 
			
		||||
 | 
			
		||||
                        // Инициализация разделителя перед меню
 | 
			
		||||
                        calculator.generate.divider(menu, 'beforebegin').then(divider => write(menu, 'beforebegin', success));
 | 
			
		||||
                      } else {
 | 
			
		||||
                        // Не найдено меню
 | 
			
		||||
 | 
			
		||||
                        // Поиск результатов калькуляции
 | 
			
		||||
                        let result = document.getElementById("result");
 | 
			
		||||
 | 
			
		||||
                        if (result !== null) {
 | 
			
		||||
                          // Найден элемент с результатами калькуляции
 | 
			
		||||
 | 
			
		||||
                          // Инициализация разделителя перед меню
 | 
			
		||||
                          calculator.generate.divider(result, 'beforebegin').then(result => write(result, 'beforebegin', success));
 | 
			
		||||
                        } else {
 | 
			
		||||
                          // Не найден элемент с результатами калькуляции
 | 
			
		||||
 | 
			
		||||
                          // Инициализация разделителя перед меню
 | 
			
		||||
                          calculator.generate.divider().then(result => write(calculator.index, 'beforeend', success));
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                  );
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
              error => {
 | 
			
		||||
                // Запись в журнал
 | 
			
		||||
                console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки');
 | 
			
		||||
              });
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<div>
 | 
			
		||||
  <label>Забор</label>
 | 
			
		||||
  <div>
 | 
			
		||||
    <select name="fence" data-calculator-parameter="fence" title="Тип забора">
 | 
			
		||||
      <option value="1">Сплошной</option>
 | 
			
		||||
      <option value="2">Секционный</option>
 | 
			
		||||
    </select>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
<div>
 | 
			
		||||
  <label>Забор</label>
 | 
			
		||||
  <div>
 | 
			
		||||
    <select name="fence" data-calculator-parameter="fence" title="Тип забора">
 | 
			
		||||
      <option value="1">???</option>
 | 
			
		||||
    </select>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -16,18 +16,18 @@
 | 
			
		||||
 | 
			
		||||
			calculator.init();
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Документ не загружен
 | 
			
		||||
  } else {
 | 
			
		||||
    // Документ не загружен
 | 
			
		||||
 | 
			
		||||
		// Обработчик события загрузки документа
 | 
			
		||||
		document.addEventListener('DOMContentLoaded', function () {
 | 
			
		||||
			// Обработчик события инициализации
 | 
			
		||||
    // Обработчик события загрузки документа
 | 
			
		||||
    document.addEventListener('DOMContentLoaded', function () {
 | 
			
		||||
      // Обработчик события инициализации
 | 
			
		||||
 | 
			
		||||
			if (calculator !== undefined) {
 | 
			
		||||
				// Калькулятор инициализирован
 | 
			
		||||
      if (calculator !== undefined) {
 | 
			
		||||
        // Калькулятор инициализирован
 | 
			
		||||
 | 
			
		||||
				calculator.init();
 | 
			
		||||
			}
 | 
			
		||||
        calculator.init();
 | 
			
		||||
      }
 | 
			
		||||
		}, false);
 | 
			
		||||
	};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
<section id="menu" class="unselectable">
 | 
			
		||||
    <a type="button" onclick="calculator.generate.calculators.profnastil(); return false;">
 | 
			
		||||
        <img src="/img/laser.png" title="Расчитать профнастил">
 | 
			
		||||
       Профнастил 
 | 
			
		||||
    </a>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -1,16 +1,46 @@
 | 
			
		||||
<h3>Лазерная резка <span title="Удалить"></span></h3>
 | 
			
		||||
<section class="calculator" data-calculator="laser">
 | 
			
		||||
    <div>
 | 
			
		||||
     <div>
 | 
			
		||||
        <label>Тип</label>
 | 
			
		||||
        <div>
 | 
			
		||||
            <select name="type" data-calculator-parameter="type" title="Тип стали"
 | 
			
		||||
                onchange="calculator.generate.mark(this.parentElement.parentElement.parentElement, this.value)">
 | 
			
		||||
                <option value="steel">Сталь</option>
 | 
			
		||||
                <option value="galvanized_steel">Оцинкованная сталь</option>
 | 
			
		||||
                <option value="stainless_steel">Нержавеющая сталь</option>
 | 
			
		||||
                <option value="brass">Латунь</option>
 | 
			
		||||
                <option value="copper">Медь</option>
 | 
			
		||||
                <option value="aluminum">Алюминий</option>
 | 
			
		||||
            <select name="type" data-calculator-parameter="type" title="Тип профнастила">
 | 
			
		||||
                <option value="c8">C8</option>
 | 
			
		||||
                <option value="c10">C10</option>
 | 
			
		||||
                <option value="hc21">HC21</option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
   <div>
 | 
			
		||||
        <label>Цвет каркаса</label>
 | 
			
		||||
        <div>
 | 
			
		||||
          <select name="base_color" data-calculator-parameter="base_color" title="Цвет">
 | 
			
		||||
                <option value="chocolate">Шоколад</option>
 | 
			
		||||
                <option value="graphite">Графит</option>
 | 
			
		||||
                <option value="green_moss">Зелёный мох</option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
   <div>
 | 
			
		||||
        <label>Каркас</label>
 | 
			
		||||
        <div>
 | 
			
		||||
          <select name="base" data-calculator-parameter="base" title="Каркас" onchange="calculator.generate.fence(this.parentElement.parentElement.parentElement, this.value)">
 | 
			
		||||
                <option value="1">Профильная труба</option>
 | 
			
		||||
                <option value="2">П-образный профиль</option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {% include 'calculators/elements/fences/1.html' %}
 | 
			
		||||
 | 
			
		||||
   <div>
 | 
			
		||||
        <label>Цвет забора</label>
 | 
			
		||||
        <div>
 | 
			
		||||
          <select name="fence_color" data-calculator-parameter="fence_color" title="Цвет">
 | 
			
		||||
                <option value="chocolate">Шоколад</option>
 | 
			
		||||
                <option value="graphite">Графит</option>
 | 
			
		||||
                <option value="green_moss">Зелёный мох</option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\tordv\calculator\views;
 | 
			
		||||
namespace mirzaev\zkmr\calculator\views;
 | 
			
		||||
 | 
			
		||||
use mirzaev\minimal\controller;
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +12,7 @@ use Twig\Environment as view;
 | 
			
		||||
/**
 | 
			
		||||
 * Менеджер представлений
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\tordv\calculator\controllers
 | 
			
		||||
 * @package mirzaev\zkmr\calculator\controllers
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class manager extends controller
 | 
			
		||||
							
								
								
									
										16
									
								
								psalm.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,16 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<psalm
 | 
			
		||||
    errorLevel="7"
 | 
			
		||||
    resolveFromConfigFile="true"
 | 
			
		||||
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
    xmlns="https://getpsalm.org/schema/config"
 | 
			
		||||
    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
 | 
			
		||||
    findUnusedBaselineEntry="true"
 | 
			
		||||
>
 | 
			
		||||
    <projectFiles>
 | 
			
		||||
        <directory name="mirzaev/zkmr/calculator/system" />
 | 
			
		||||
        <ignoreFiles>
 | 
			
		||||
            <directory name="vendor" />
 | 
			
		||||
        </ignoreFiles>
 | 
			
		||||
    </projectFiles>
 | 
			
		||||
</psalm>
 | 
			
		||||