Compare commits
No commits in common. "stable" and "stable" have entirely different histories.
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "mirzaev/zkmr-calculator",
|
"name": "mirzaev/tordv-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/zkmr-calculator",
|
"homepage": "https://git.mirzaev.sexy/mirzaev/tordv-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/zkmr-calculator/wiki",
|
"docs": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/wiki",
|
||||||
"issues": "https://git.mirzaev.sexy/mirzaev/zkmr-calculator/issues"
|
"issues": "https://git.mirzaev.sexy/mirzaev/tordv-calculator/issues"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -29,15 +29,14 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2.0",
|
"php": "^8.0.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\\zkmr\\calculator\\": "mirzaev/zkmr/calculator/system"
|
"mirzaev\\tordv\\calculator\\": "mirzaev/tordv/calculator/system"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "c424f9f2f1b7a9cbcfaa900a5da9d892",
|
"content-hash": "d63372480c8f1c3719fb9e69026eef21",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "ezyang/htmlpurifier",
|
"name": "ezyang/htmlpurifier",
|
||||||
|
@ -926,8 +926,7 @@
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.2.0",
|
"php": "^8.0.0"
|
||||||
"ext-mysqli": "*"
|
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.3.0"
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер пользователей
|
* Контроллер пользователей
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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,266 @@
|
||||||
|
<?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']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер контактов
|
* Контроллер контактов
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\views\manager;
|
use mirzaev\tordv\calculator\views\manager;
|
||||||
use mirzaev\zkmr\calculator\models\core as models;
|
use mirzaev\tordv\calculator\models\core as models;
|
||||||
|
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Менеджер представлений
|
* Менеджер представлений
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\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\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер журналов
|
* Контроллер журналов
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\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\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
use mirzaev\zkmr\calculator\models\settings_model as settings;
|
use mirzaev\tordv\calculator\models\settings_model as settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер настроек
|
* Контроллер настроек
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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\zkmr\calculator\controllers;
|
namespace mirzaev\tordv\calculator\controllers;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\controllers\core;
|
use mirzaev\tordv\calculator\controllers\core;
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
use mirzaev\zkmr\calculator\models\supplies_model as supplies;
|
use mirzaev\tordv\calculator\models\supplies_model as supplies;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_aluminum_filter;
|
use mirzaev\tordv\calculator\models\filters\import_aluminum_filter;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_brass_filter;
|
use mirzaev\tordv\calculator\models\filters\import_brass_filter;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_copper_filter;
|
use mirzaev\tordv\calculator\models\filters\import_copper_filter;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_stainless_steel_filter;
|
use mirzaev\tordv\calculator\models\filters\import_stainless_steel_filter;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_steel_filter;
|
use mirzaev\tordv\calculator\models\filters\import_steel_filter;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_galvanized_steel_filter;
|
use mirzaev\tordv\calculator\models\filters\import_galvanized_steel_filter;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер поставок
|
* Контроллер поставок
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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
|
|
@ -0,0 +1,621 @@
|
||||||
|
<?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];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\zkmr\calculator\models;
|
namespace mirzaev\tordv\calculator\models;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\settings_model as settings;
|
use mirzaev\tordv\calculator\models\settings_model as settings;
|
||||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
|
use mirzaev\tordv\calculator\models\metals_model as metals;
|
||||||
use mirzaev\zkmr\calculator\models\baloons_model as baloons;
|
use mirzaev\tordv\calculator\models\baloons_model as baloons;
|
||||||
|
|
||||||
use exception;
|
use exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Модель калькуляторов
|
* Модель калькуляторов
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models
|
* @package mirzaev\tordv\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\zkmr\calculator\models;
|
namespace mirzaev\tordv\calculator\models;
|
||||||
|
|
||||||
use mirzaev\minimal\model;
|
use mirzaev\minimal\model;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use exception;
|
||||||
/**
|
/**
|
||||||
* Ядро моделей
|
* Ядро моделей
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
|
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на алюминий от неизвестного поставщика под номером 1
|
* Фильтр импорта цен на алюминий от неизвестного поставщика под номером 1
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
|
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на латунь от неизвестного поставщика под номером 1
|
* Фильтр импорта цен на латунь от неизвестного поставщика под номером 1
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
|
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на медь от неизвестного поставщика под номером 1
|
* Фильтр импорта цен на медь от неизвестного поставщика под номером 1
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\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\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
|
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
|
||||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
|
use mirzaev\tordv\calculator\models\metals_model as metals;
|
||||||
|
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта для документов от "МЕТАЛЛСЕРВИС"
|
* Фильтр импорта для документов от "МЕТАЛЛСЕРВИС"
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
|
use mirzaev\tordv\calculator\models\filters\import_filter as filter;
|
||||||
use mirzaev\zkmr\calculator\models\metals_model as metals;
|
use mirzaev\tordv\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\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
|
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на оцинкованную сталь от "МЕТАЛЛСЕРВИС"
|
* Фильтр импорта цен на оцинкованную сталь от "МЕТАЛЛСЕРВИС"
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_noname_1;
|
use mirzaev\tordv\calculator\models\filters\import_filter_noname_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на нержавеющую сталь от неизвестного поставщика под номером 1
|
* Фильтр импорта цен на нержавеющую сталь от неизвестного поставщика под номером 1
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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\zkmr\calculator\models\filters;
|
namespace mirzaev\tordv\calculator\models\filters;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter_mc;
|
use mirzaev\tordv\calculator\models\filters\import_filter_mc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Фильтр импорта цен на чёрную сталь от "МЕТАЛЛСЕРВИС"
|
* Фильтр импорта цен на чёрную сталь от "МЕТАЛЛСЕРВИС"
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models\filters
|
* @package mirzaev\tordv\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
|
|
@ -0,0 +1,232 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\zkmr\calculator\models;
|
namespace mirzaev\tordv\calculator\models;
|
||||||
|
|
||||||
use pdo;
|
use pdo;
|
||||||
use exception;
|
use exception;
|
||||||
|
@ -10,7 +10,7 @@ use exception;
|
||||||
/**
|
/**
|
||||||
* Модель настроек
|
* Модель настроек
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\models
|
* @package mirzaev\tordv\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\zkmr\calculator\models;
|
namespace mirzaev\tordv\calculator\models;
|
||||||
|
|
||||||
use mirzaev\zkmr\calculator\models\accounts_model as accounts;
|
use mirzaev\tordv\calculator\models\accounts_model as accounts;
|
||||||
use mirzaev\zkmr\calculator\models\filters\import_filter as filter;
|
use mirzaev\tordv\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\zkmr\calculator\models
|
* @package mirzaev\tordv\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\zkmr\calculator;
|
namespace mirzaev\tordv\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', 'zkmr-calculator');
|
define('BASE', 'calculator');
|
||||||
define('HOST', 'localhost');
|
define('HOST', '127.0.0.1');
|
||||||
define('LOGIN', '');
|
define('LOGIN', 'root');
|
||||||
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/fence', 'calculator', 'fence', 'POST');
|
$router->write('/calculator/generate/mark', 'calculator', 'mark', 'POST');
|
||||||
$router->write('/calculator/generate/divider', 'calculator', 'divider', 'POST');
|
$router->write('/calculator/generate/divider', 'calculator', 'divider', 'POST');
|
||||||
$router->write('/calculator/generate/profnastil', 'calculator', 'profnastil', 'POST');
|
$router->write('/calculator/generate/laser', 'calculator', 'laser', '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');
|
|
@ -0,0 +1,577 @@
|
||||||
|
'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('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,69 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,18 @@
|
||||||
|
<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,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\zkmr\calculator\views;
|
namespace mirzaev\tordv\calculator\views;
|
||||||
|
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use Twig\Environment as view;
|
||||||
/**
|
/**
|
||||||
* Менеджер представлений
|
* Менеджер представлений
|
||||||
*
|
*
|
||||||
* @package mirzaev\zkmr\calculator\controllers
|
* @package mirzaev\tordv\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
|
|
@ -1,307 +0,0 @@
|
||||||
<?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']
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,621 +0,0 @@
|
||||||
<?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];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,588 +0,0 @@
|
||||||
"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(
|
|
||||||
"[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
<div>
|
|
||||||
<label>Забор</label>
|
|
||||||
<div>
|
|
||||||
<select name="fence" data-calculator-parameter="fence" title="Тип забора">
|
|
||||||
<option value="1">???</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div>
|
|
||||||
<label>Забор</label>
|
|
||||||
<div>
|
|
||||||
<select name="fence" data-calculator-parameter="fence" title="Тип забора">
|
|
||||||
<option value="1">Сплошной</option>
|
|
||||||
<option value="2">Секционный</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,97 +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="Выбор типа профнастила">
|
|
||||||
<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>
|
|
|
@ -1,83 +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="Выбор типа сетки">
|
|
||||||
<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>
|
|