Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 26b5876513 | |||
| ff6a646cbd | 
@@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "mirzaev/tordv-calculator",
 | 
					  "name": "mirzaev/zkmr-calculator",
 | 
				
			||||||
  "description": "Калькулятор стоимости услуг по обработке металла",
 | 
					  "description": "Калькулятор стоимости услуг по обработке металла",
 | 
				
			||||||
  "type": "site",
 | 
					  "type": "site",
 | 
				
			||||||
  "keywords": [
 | 
					  "keywords": [
 | 
				
			||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
    "calculator"
 | 
					    "calculator"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "readme": "README.md",
 | 
					  "readme": "README.md",
 | 
				
			||||||
    "homepage": "https://git.mirzaev.sexy/mirzaev/tordv-calculator",
 | 
					  "homepage": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator",
 | 
				
			||||||
  "license": "WTFPL",
 | 
					  "license": "WTFPL",
 | 
				
			||||||
  "authors": [
 | 
					  "authors": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -19,8 +19,8 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "support": {
 | 
					  "support": {
 | 
				
			||||||
    "email": "arsen@mirzaev.sexy",
 | 
					    "email": "arsen@mirzaev.sexy",
 | 
				
			||||||
        "docs": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/wiki",
 | 
					    "docs": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator/wiki",
 | 
				
			||||||
        "issues": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/issues"
 | 
					    "issues": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator/issues"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "funding": [
 | 
					  "funding": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -29,14 +29,15 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "require": {
 | 
					  "require": {
 | 
				
			||||||
        "php": "^8.0.0",
 | 
					    "php": "^8.2.0",
 | 
				
			||||||
    "mirzaev/minimal": "^2.0.x-dev",
 | 
					    "mirzaev/minimal": "^2.0.x-dev",
 | 
				
			||||||
    "twig/twig": "^3.3",
 | 
					    "twig/twig": "^3.3",
 | 
				
			||||||
        "phpoffice/phpspreadsheet": "^1.20"
 | 
					    "phpoffice/phpspreadsheet": "^1.20",
 | 
				
			||||||
 | 
					    "ext-mysqli": "*"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "autoload": {
 | 
					  "autoload": {
 | 
				
			||||||
    "psr-4": {
 | 
					    "psr-4": {
 | 
				
			||||||
            "mirzaev\\tordv\\calculator\\": "mirzaev/tordv/calculator/system"
 | 
					      "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",
 | 
					        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
				
			||||||
        "This file is @generated automatically"
 | 
					        "This file is @generated automatically"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "content-hash": "d63372480c8f1c3719fb9e69026eef21",
 | 
					    "content-hash": "c424f9f2f1b7a9cbcfaa900a5da9d892",
 | 
				
			||||||
    "packages": [
 | 
					    "packages": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "ezyang/htmlpurifier",
 | 
					            "name": "ezyang/htmlpurifier",
 | 
				
			||||||
@@ -926,7 +926,8 @@
 | 
				
			|||||||
    "prefer-stable": false,
 | 
					    "prefer-stable": false,
 | 
				
			||||||
    "prefer-lowest": false,
 | 
					    "prefer-lowest": false,
 | 
				
			||||||
    "platform": {
 | 
					    "platform": {
 | 
				
			||||||
        "php": "^8.0.0"
 | 
					        "php": "^8.2.0",
 | 
				
			||||||
 | 
					        "ext-mysqli": "*"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "platform-dev": [],
 | 
					    "platform-dev": [],
 | 
				
			||||||
    "plugin-api-version": "2.3.0"
 | 
					    "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,69 +0,0 @@
 | 
				
			|||||||
<h3>Лазерная резка <span title="Удалить"></span></h3>
 | 
					 | 
				
			||||||
<section class="calculator" data-calculator="laser">
 | 
					 | 
				
			||||||
    <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>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
        <label>Размер</label>
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="width" type="number" class="measured" title="Длина детали"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.width }}" min="{{ calculators.laser.width.min ?? 1 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.width.max ?? 3000 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					 | 
				
			||||||
            <small>x</small>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="height" type="number" class="measured" title="Ширина детали"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.height }}" min="{{ calculators.laser.height.min ?? 1 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.height.max ?? 3000 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					 | 
				
			||||||
            <small>x</small>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="length" type="number" class="measured" title="Толщина детали"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.length }}" min="{{ calculators.laser.length.min ?? 1 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.length.max ?? 20 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
        <label>Отверстия</label>
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="holes" type="number" class="measured" title="Количество отверстий"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.holes }}" min="{{ calculators.laser.holes.min ?? 0 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.holes.max ?? 100 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">шт</span>
 | 
					 | 
				
			||||||
            <small>x</small>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="diameter" type="number" class="measured" title="Диаметр отверстий"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.diameter }}" min="{{ calculators.laser.diameter.min ?? 0 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.diameter.max ?? 100 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">мм</span>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
        <label>Количество</label>
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="amount" type="number" class="measured" title="Количество изделий"
 | 
					 | 
				
			||||||
                value="{{ calculators.laser.amount }}" min="{{ calculators.laser.amount.min ?? 1 }}"
 | 
					 | 
				
			||||||
                max="{{ calculators.laser.amount.max ?? 10000 }}">
 | 
					 | 
				
			||||||
            <span class="unit unselectable">шт</span>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
        <label for="our">Наш металл</label>
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <input data-calculator-parameter="our" type="checkbox" title="Используется наш металл" checked>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
@@ -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);
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class accounts_controller extends core
 | 
					final class accounts_controller extends core
 | 
				
			||||||
@@ -0,0 +1,307 @@
 | 
				
			|||||||
 | 
					<?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 Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function profnastil(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					      $vars['errors'] = ['calculators' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Инициализация данных калькулятора
 | 
				
			||||||
 | 
					      $vars['calculators'] = ['profnastil' => [
 | 
				
			||||||
 | 
					        '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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 3D-сетка 
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * HTML-код с калькулятором 3D-сетки
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function setka(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					      $vars['errors'] = ['calculators' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Инициализация данных калькулятора
 | 
				
			||||||
 | 
					      $vars['calculators'] = ['profnastil' => [
 | 
				
			||||||
 | 
					        '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 . 'setka.html', $vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Евроштакет
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * HTML-код с калькулятором евроштакета
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array $vars Параметры
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function evroshtaket(array $vars = []): ?string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // Инициализация журнала ошибок
 | 
				
			||||||
 | 
					      $vars['errors'] = ['calculators' => []];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Инициализация данных калькулятора
 | 
				
			||||||
 | 
					      $vars['calculators'] = ['profnastil' => [
 | 
				
			||||||
 | 
					        '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 . 'evroshtaket.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);
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class contacts_controller extends core
 | 
					final class contacts_controller extends core
 | 
				
			||||||
@@ -2,17 +2,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\tordv\calculator\controllers;
 | 
					namespace mirzaev\zkmr\calculator\controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\tordv\calculator\views\manager;
 | 
					use mirzaev\zkmr\calculator\views\manager;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\core as models;
 | 
					use mirzaev\zkmr\calculator\models\core as models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\minimal\controller;
 | 
					use mirzaev\minimal\controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Менеджер представлений
 | 
					 * Менеджер представлений
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\controllers
 | 
					 * @package mirzaev\zkmr\calculator\controllers
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class core extends controller
 | 
					class core extends controller
 | 
				
			||||||
@@ -2,9 +2,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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\Loader\FilesystemLoader;
 | 
				
			||||||
use Twig\Environment as view;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class errors_controller extends core
 | 
					final class errors_controller extends core
 | 
				
			||||||
@@ -2,15 +2,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class journal_controller extends core
 | 
					final class journal_controller extends core
 | 
				
			||||||
@@ -2,10 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Twig\Loader\FilesystemLoader;
 | 
					use Twig\Loader\FilesystemLoader;
 | 
				
			||||||
use Twig\Environment as view;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class main_controller extends core
 | 
					final class main_controller extends core
 | 
				
			||||||
@@ -2,16 +2,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\settings_model as settings;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class settings_controller extends core
 | 
					final class settings_controller extends core
 | 
				
			||||||
@@ -2,25 +2,25 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\supplies_model as supplies;
 | 
					use mirzaev\zkmr\calculator\models\supplies_model as supplies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_aluminum_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_aluminum_filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_brass_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_brass_filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_copper_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_copper_filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_stainless_steel_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_stainless_steel_filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_steel_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_steel_filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_galvanized_steel_filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_galvanized_steel_filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Exception;
 | 
					use Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Контроллер поставок
 | 
					 * Контроллер поставок
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\controllers
 | 
					 * @package mirzaev\zkmr\calculator\controllers
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class supplies_controller extends core
 | 
					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);
 | 
					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\zkmr\calculator\models\settings_model as settings;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
					use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\baloons_model as baloons;
 | 
					use mirzaev\zkmr\calculator\models\baloons_model as baloons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use exception;
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Модель калькуляторов
 | 
					 * Модель калькуляторов
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models
 | 
					 * @package mirzaev\zkmr\calculator\models
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class calculators_model extends core
 | 
					final class calculators_model extends core
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\tordv\calculator\models;
 | 
					namespace mirzaev\zkmr\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\minimal\model;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class core extends model
 | 
					class core extends model
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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
 | 
					 * Фильтр импорта цен на алюминий от неизвестного поставщика под номером 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
					 * @package mirzaev\zkmr\calculator\models\filters
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class import_aluminum_filter extends import_filter_noname_1
 | 
					final class import_aluminum_filter extends import_filter_noname_1
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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
 | 
					 * Фильтр импорта цен на латунь от неизвестного поставщика под номером 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
					 * @package mirzaev\zkmr\calculator\models\filters
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class import_brass_filter extends import_filter_noname_1
 | 
					final class import_brass_filter extends import_filter_noname_1
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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
 | 
					 * Фильтр импорта цен на медь от неизвестного поставщика под номером 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
					 * @package mirzaev\zkmr\calculator\models\filters
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class import_copper_filter extends import_filter_noname_1
 | 
					final class import_copper_filter extends import_filter_noname_1
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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\Reader\IReadFilter as filter;
 | 
				
			||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
interface import_filter extends filter
 | 
					interface import_filter extends filter
 | 
				
			||||||
@@ -2,17 +2,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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\zkmr\calculator\models\filters\import_filter as filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
					use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class import_filter_mc implements filter
 | 
					class import_filter_mc implements filter
 | 
				
			||||||
@@ -2,10 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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\zkmr\calculator\models\filters\import_filter as filter;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\metals_model as metals;
 | 
					use mirzaev\zkmr\calculator\models\metals_model as metals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
					use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 | 
				
			||||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 | 
					use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 | 
				
			||||||
@@ -13,7 +13,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Фильтр импорта для документов от неизвестного поставщика под номером 1
 | 
					 * Фильтр импорта для документов от неизвестного поставщика под номером 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
					 * @package mirzaev\zkmr\calculator\models\filters
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class import_filter_noname_1 implements filter
 | 
					class import_filter_noname_1 implements filter
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @todo 1. Доделать 982 до 992 строки
 | 
					 * @todo 1. Доделать 982 до 992 строки
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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
 | 
					 * Фильтр импорта цен на нержавеющую сталь от неизвестного поставщика под номером 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\tordv\calculator\models\filters
 | 
					 * @package mirzaev\zkmr\calculator\models\filters
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @todo 1. Доделать 982 до 992 строки
 | 
					 * @todo 1. Доделать 982 до 992 строки
 | 
				
			||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class import_steel_filter extends import_filter_mc
 | 
					final class import_steel_filter extends import_filter_mc
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\tordv\calculator\models;
 | 
					namespace mirzaev\zkmr\calculator\models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use pdo;
 | 
					use pdo;
 | 
				
			||||||
use exception;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class settings_model extends core
 | 
					final class settings_model extends core
 | 
				
			||||||
@@ -2,10 +2,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					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\zkmr\calculator\models\accounts_model as accounts;
 | 
				
			||||||
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
 | 
					use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use PhpOffice\PhpSpreadsheet\Reader\Xls as reader;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class supplies_model extends core
 | 
					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);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\tordv\calculator;
 | 
					namespace mirzaev\zkmr\calculator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\minimal\core;
 | 
					use mirzaev\minimal\core;
 | 
				
			||||||
use mirzaev\minimal\router;
 | 
					use mirzaev\minimal\router;
 | 
				
			||||||
@@ -10,9 +10,9 @@ use mirzaev\minimal\router;
 | 
				
			|||||||
define('SUPPLIES', realpath('supplies'));
 | 
					define('SUPPLIES', realpath('supplies'));
 | 
				
			||||||
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
 | 
					define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
 | 
				
			||||||
define('TYPE', 'mysql');
 | 
					define('TYPE', 'mysql');
 | 
				
			||||||
define('BASE', 'calculator');
 | 
					define('BASE', 'zkmr-calculator');
 | 
				
			||||||
define('HOST', '127.0.0.1');
 | 
					define('HOST', 'localhost');
 | 
				
			||||||
define('LOGIN', 'root');
 | 
					define('LOGIN', '');
 | 
				
			||||||
define('PASSWORD', '');
 | 
					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/complexity', 'calculator', 'complexity', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/menu', 'calculator', 'menu', 'POST');
 | 
					$router->write('/calculator/generate/menu', 'calculator', 'menu', 'POST');
 | 
				
			||||||
$router->write('/calculator/generate/result', 'calculator', 'result', '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/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('/calculator/calculate', 'calculator', 'calculate', 'POST');
 | 
				
			||||||
$router->write('/settings', 'settings', 'index', 'GET');
 | 
					$router->write('/settings', 'settings', 'index', 'GET');
 | 
				
			||||||
$router->write('/settings/write', 'settings', 'write', 'POST');
 | 
					$router->write('/settings/write', 'settings', 'write', 'POST');
 | 
				
			||||||
							
								
								
									
										588
									
								
								mirzaev/zkmr/calculator/system/public/js/calculator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,588 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const calculator = {
 | 
				
			||||||
 | 
					  index: document.getElementById("calculator"),
 | 
				
			||||||
 | 
					  calculators: [],
 | 
				
			||||||
 | 
					  account: [],
 | 
				
			||||||
 | 
					  settings: {
 | 
				
			||||||
 | 
					    defaults: {
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  init() {
 | 
				
			||||||
 | 
					    // Инициализация калькулятора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.generate.menu()
 | 
				
			||||||
 | 
					      .then(
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					          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("[КАЛЬКУЛЯТОР] Инициализирован");
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  async authenticate(id) {
 | 
				
			||||||
 | 
					    // Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return await 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;
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          () => {
 | 
				
			||||||
 | 
					            console.log(
 | 
				
			||||||
 | 
					              "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить данные пользователя: " +
 | 
				
			||||||
 | 
					                id,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  async calculate() {
 | 
				
			||||||
 | 
					    // Запрос и генерация HTML с данными о рассчете со всех калькуляторов
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Инициализация параметров
 | 
				
			||||||
 | 
					    const cutting = document.getElementById("cutting");
 | 
				
			||||||
 | 
					    const discount = document.getElementById("discount");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Инициализация буфера запроса
 | 
				
			||||||
 | 
					    const 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 await 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(
 | 
				
			||||||
 | 
					                "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат",
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          () => {
 | 
				
			||||||
 | 
					            console.log(
 | 
				
			||||||
 | 
					              "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат",
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  generate: {
 | 
				
			||||||
 | 
					    async menu() {
 | 
				
			||||||
 | 
					      // Запрос и генерация HTML с кнопками добавления калькулятора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return await 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(
 | 
				
			||||||
 | 
					                "[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора",
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            () => {
 | 
				
			||||||
 | 
					              console.log(
 | 
				
			||||||
 | 
					                "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками добавления калькулятора",
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async fence(element, type = 1) {
 | 
				
			||||||
 | 
					      // Генерация полей с выбором типа забора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return await 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) => {
 | 
				
			||||||
 | 
					              // Поиск оболочки списка
 | 
				
			||||||
 | 
					              const wrap = element.querySelectorAll('select[name="fence"]')[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Запись полученного списка в оболочку
 | 
				
			||||||
 | 
					              wrap.parentElement.parentElement.insertAdjacentHTML(
 | 
				
			||||||
 | 
					                "afterend",
 | 
				
			||||||
 | 
					                success,
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Удаление старого списка
 | 
				
			||||||
 | 
					              if (wrap !== undefined) wrap.parentElement.parentElement.remove();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              console.log("[КАЛЬКУЛЯТОР] Загружен список с типами забора");
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            () => {
 | 
				
			||||||
 | 
					              console.log(
 | 
				
			||||||
 | 
					                "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить список с типами забора",
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async divider(element, position) {
 | 
				
			||||||
 | 
					      // Запрос и генерация HTML с данными о результате калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return await 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("[КАЛЬКУЛЯТОР] Загружен разделитель");
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            () => {
 | 
				
			||||||
 | 
					              console.log(
 | 
				
			||||||
 | 
					                "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить разделитель",
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    result(expenses) {
 | 
				
			||||||
 | 
					      // Запрос и генерация HTML с данными о результате калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      async function request() {
 | 
				
			||||||
 | 
					        return await 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(
 | 
				
			||||||
 | 
					                  "[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции",
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              () => {
 | 
				
			||||||
 | 
					                console.log(
 | 
				
			||||||
 | 
					                  "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с данными о результате калькуляции",
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (document.getElementById("result") === null) {
 | 
				
			||||||
 | 
					        // Не найден элемент с данными расчётов
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // Найден элемент с данными расчётов
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (expenses !== undefined) {
 | 
				
			||||||
 | 
					          // Переданы расходы
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Инициализация элемента
 | 
				
			||||||
 | 
					          const 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") {
 | 
				
			||||||
 | 
					        // Передан массив с ошибками и он является массивом
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Инициализация буфера для проверки вложенности массива
 | 
				
			||||||
 | 
					        const first = Object.values(errors)[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (first !== undefined && first.text === undefined) {
 | 
				
			||||||
 | 
					          // Найден массив с ошибками (категория)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Вход в рекурсию
 | 
				
			||||||
 | 
					          amount += this.error(first);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // Не найден массив с ошибками (подразумевается, что это и есть информация об ошибке)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Инициализация элемента-оболочки
 | 
				
			||||||
 | 
					          const list = document.getElementById("errors");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Перезапись данных об ошибках
 | 
				
			||||||
 | 
					          list.innerText = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Проверка на наличие ошибок
 | 
				
			||||||
 | 
					          if (errors.length === 0) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (list !== null) {
 | 
				
			||||||
 | 
					            // Оболочка найдена
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const [, error] of Object.entries(errors)) {
 | 
				
			||||||
 | 
					              // Перебор станков
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Инициализация элемента-заголовка
 | 
				
			||||||
 | 
					              const term = document.createElement("dt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Запись содержимого
 | 
				
			||||||
 | 
					              term.innerText = error.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Инициализация элемента-описания
 | 
				
			||||||
 | 
					              const 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: {
 | 
				
			||||||
 | 
					      async profnastil() {
 | 
				
			||||||
 | 
					        // Запрос и генерация HTML с калькулятором лазерной резки
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function write(target, position, html) {
 | 
				
			||||||
 | 
					          if (
 | 
				
			||||||
 | 
					            target === undefined || position === undefined || html === undefined
 | 
				
			||||||
 | 
					          ) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Запись калькулятора после последнего калькулятора
 | 
				
			||||||
 | 
					          target.insertAdjacentHTML(position, html);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Поиск калькуляторов
 | 
				
			||||||
 | 
					          const calculators = calculator.index.querySelectorAll(
 | 
				
			||||||
 | 
					            "section[data-calculator]",
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Инициализация идентификатора калькулятора
 | 
				
			||||||
 | 
					          const id = calculators.length - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Запись калькулятору его идентификатора
 | 
				
			||||||
 | 
					          calculators[id].id = "profnastil_" + id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
 | 
				
			||||||
 | 
					          calculator.calculators.push(calculators[id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Запись в журнал
 | 
				
			||||||
 | 
					          console.log(
 | 
				
			||||||
 | 
					            "[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки",
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await fetch("/calculator/generate/profnastil", {
 | 
				
			||||||
 | 
					          method: "POST",
 | 
				
			||||||
 | 
					          headers: { "content-type": "application/x-www-form-urlencoded" },
 | 
				
			||||||
 | 
					        }).then((response) => {
 | 
				
			||||||
 | 
					          if (response.status === 200) {
 | 
				
			||||||
 | 
					            response.text().then(
 | 
				
			||||||
 | 
					              (success) => {
 | 
				
			||||||
 | 
					                // Поиск последнего калькулятора
 | 
				
			||||||
 | 
					                const last =
 | 
				
			||||||
 | 
					                  calculator.calculators[calculator.calculators.length - 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (last !== undefined && last !== null) {
 | 
				
			||||||
 | 
					                  // Найден калькулятор
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  // Инициализация разделителя перед калькулятором 
 | 
				
			||||||
 | 
					                  calculator.generate.divider(last, "afterend").then(
 | 
				
			||||||
 | 
					                    () => write(last, "afterend", success),
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                  // Не найден калькулятор
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  calculator.generate.divider(menu, "beforebegin").then(
 | 
				
			||||||
 | 
					                    () => {
 | 
				
			||||||
 | 
					                      // Поиск меню
 | 
				
			||||||
 | 
					                      const menu = document.getElementById("menu");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                      if (menu !== null) {
 | 
				
			||||||
 | 
					                        // Найдено меню
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                        write(menu, "beforebegin", success)
 | 
				
			||||||
 | 
					                      } else {
 | 
				
			||||||
 | 
					                        // Не найдено меню
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Поиск результатов калькуляции
 | 
				
			||||||
 | 
					                        const result = document.getElementById("result");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (result !== null) {
 | 
				
			||||||
 | 
					                          // Найден элемент с результатами калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                          // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                          calculator.generate.divider(result, "beforebegin")
 | 
				
			||||||
 | 
					                            .then((result) =>
 | 
				
			||||||
 | 
					                              write(result, "beforebegin", success)
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                          // Не найден элемент с результатами калькуляции
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                          // Инициализация разделителя перед меню
 | 
				
			||||||
 | 
					                          calculator.generate.divider().then(() =>
 | 
				
			||||||
 | 
					                            write(calculator.index, "beforeend", success)
 | 
				
			||||||
 | 
					                          );
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              () => {
 | 
				
			||||||
 | 
					                // Запись в журнал
 | 
				
			||||||
 | 
					                console.log(
 | 
				
			||||||
 | 
					                  "[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки",
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					  <label>Забор</label>
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <select name="fence" data-calculator-parameter="fence" title="Тип забора">
 | 
				
			||||||
 | 
					      <option value="1">???</option>
 | 
				
			||||||
 | 
					    </select>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -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>
 | 
				
			||||||
							
								
								
									
										14
									
								
								mirzaev/zkmr/calculator/system/views/calculators/menu.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					<section id="menu" class="unselectable">
 | 
				
			||||||
 | 
					    <a type="button" onclick="calculator.generate.calculators.profnastil(); return false;">
 | 
				
			||||||
 | 
					        <img src="" title="Расчитать профнастил">
 | 
				
			||||||
 | 
					       Профнастил 
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a type="button" onclick="calculator.generate.calculators.setka(); return false;">
 | 
				
			||||||
 | 
					        <img src="" title="Расчитать 3D-сетку">
 | 
				
			||||||
 | 
					       3D-сетка 
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a type="button" onclick="calculator.generate.calculators.evroshtaket); return false;">
 | 
				
			||||||
 | 
					        <img src="" title="Расчитать евроштакет">
 | 
				
			||||||
 | 
					       Евроштакет
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					<h3>Забор из профнастила<span title="Удалить"></span></h3>
 | 
				
			||||||
 | 
					<section class="calculator" data-calculator="laser">
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Тип</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Тип монтажа</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="mounting" data-calculator-parameter="mounting" title="Выбор типа монтажа">
 | 
				
			||||||
 | 
					        <option value="ground">В грунт</option>
 | 
				
			||||||
 | 
					        <option value="piles">На сваи/бетон</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Высота</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="height" data-calculator-parameter="height" title="Высота">
 | 
				
			||||||
 | 
					        <option value="1800">1800мм</option>
 | 
				
			||||||
 | 
					        <option value="2000">2000мм</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Длина</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="length" type="number" class="measured" title="Длина детали"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.length.default ?? 1 }}" min="{{ calculators.profnastil.length.min ?? 1 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.length.max ?? 3000 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">м</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Калитки</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="wickets" type="number" class="measured" title="Количество калиток"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.wickets.default ?? 0 }}" min="{{ calculators.profnastil.wickets.min ?? 0 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.wickets.max ?? 100 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">шт</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Ворота</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="gates" type="number" class="measured" title="Количество ворот"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.gates.default ?? 0 }}" min="{{ calculators.profnastil.gates.min ?? 0 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.gates.max ?? 100 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">шт</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
							
								
								
									
										83
									
								
								mirzaev/zkmr/calculator/system/views/calculators/setka.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					<h3>Забор из профнастила<span title="Удалить"></span></h3>
 | 
				
			||||||
 | 
					<section class="calculator" data-calculator="laser">
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Тип</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="type" data-calculator-parameter="type" title="Выбор типа сетки">
 | 
				
			||||||
 | 
					        <option value="light">light</option>
 | 
				
			||||||
 | 
					        <option value="medium">medium</option>
 | 
				
			||||||
 | 
					        <option value="optima">optima</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Цвет сетки</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="fence_color" data-calculator-parameter="fence_color" title="Выбор цвета сетки">
 | 
				
			||||||
 | 
					        <option value="graphite">Графит</option>
 | 
				
			||||||
 | 
					        <option value="green_moss">Зелёный мох</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Тип крепления</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="fastening" data-calculator-parameter="fastening" title="Выбор типа крепления">
 | 
				
			||||||
 | 
					        <option value="brace">Скоба</option>
 | 
				
			||||||
 | 
					        <option value="collar">Хомут</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Тип монтажа</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="mounting" data-calculator-parameter="mounting" title="Выбор типа монтажа">
 | 
				
			||||||
 | 
					        <option value="ground">В грунт</option>
 | 
				
			||||||
 | 
					        <option value="piles">На сваи/бетон</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Высота</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <select name="height" data-calculator-parameter="height" title="Высота">
 | 
				
			||||||
 | 
					        <option value="1800">1800мм</option>
 | 
				
			||||||
 | 
					        <option value="2000">2000мм</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Длина</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="length" type="number" class="measured" title="Длина детали"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.length.default ?? 1 }}" min="{{ calculators.profnastil.length.min ?? 1 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.length.max ?? 3000 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">м</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Калитки</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="wickets" type="number" class="measured" title="Количество калиток"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.wickets.default ?? 0 }}" min="{{ calculators.profnastil.wickets.min ?? 0 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.wickets.max ?? 100 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">шт</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <label>Ворота</label>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <input data-calculator-parameter="gates" type="number" class="measured" title="Количество ворот"
 | 
				
			||||||
 | 
					      value="{{ calculators.profnastil.gates.default ?? 0 }}" min="{{ calculators.profnastil.gates.min ?? 0 }}"
 | 
				
			||||||
 | 
					      max="{{ calculators.profnastil.gates.max ?? 100 }}">
 | 
				
			||||||
 | 
					      <span class="unit unselectable">шт</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\tordv\calculator\views;
 | 
					namespace mirzaev\zkmr\calculator\views;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mirzaev\minimal\controller;
 | 
					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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class manager extends controller
 | 
					final class manager extends controller
 | 
				
			||||||