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