Доработки под доп. листы

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2022-02-24 12:58:42 +10:00
parent 4c6331b5da
commit b1d5a6c37b
7 changed files with 639 additions and 217 deletions

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace mirzaev\calculator\controllers; namespace mirzaev\calculator\controllers;
use Exception;
use mirzaev\calculator\controllers\core; use mirzaev\calculator\controllers\core;
use mirzaev\calculator\models\calculators_model as calculators; use mirzaev\calculator\models\calculators_model as calculators;
use mirzaev\calculator\models\settings_model as settings; use mirzaev\calculator\models\settings_model as settings;
@ -93,6 +94,12 @@ final class calculator_controller extends core
*/ */
public function result(array $vars = []): ?string 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); return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'result.html', $vars);
} }
@ -118,7 +125,7 @@ final class calculator_controller extends core
if (empty($vars['marks'])) $vars['marks'] = ['Не найдено']; if (empty($vars['marks'])) $vars['marks'] = ['Не найдено'];
// Генерация представления // Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR. 'metals' . DIRECTORY_SEPARATOR . 'mark.html', $vars); return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'metals' . DIRECTORY_SEPARATOR . 'mark.html', $vars);
} }
/** /**
@ -167,17 +174,13 @@ final class calculator_controller extends core
} }
/** /**
* Рассчёт * Расчёт
* *
* Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0] * Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0]
* *
* @param array $vars Параметры * @param array $vars Параметры
* *
* @todo * @todo
* 1. Отправлять данные в зависимости от разрешения (обычным пользователям только expenses)
* 2. Переписать журнал ошибок и написать вывод ошибок куда-нибудь
* 3. Вывод ошибок в представления
* 4. Проверка на то, что существуют поставки для характеристик вписываемых в калькулятор (в режиме прямой трансляции)
* 5. Убрать передачу цены работы (оставить только время работы в часах и цену за работу в час) * 5. Убрать передачу цены работы (оставить только время работы в часах и цену за работу в час)
*/ */
public function calculate(array $vars = []): ?string public function calculate(array $vars = []): ?string
@ -185,56 +188,79 @@ final class calculator_controller extends core
// Инициализация журнала ошибок // Инициализация журнала ошибок
$vars['errors'] = ['calculators' => []]; $vars['errors'] = ['calculators' => []];
// Инициализация калькуляторов из тела запроса (подразумевается, что там массивы с параметрами) try {
$calculators = json_decode(file_get_contents('php://input'), true); // Инициализация параметров из тела запроса (подразумевается, что там массивы с параметрами)
$vars['input'] = json_decode(file_get_contents('php://input'), true);
// Инициализация переменных для буфера вывода $calculators = $vars['input']['calculators'];
$machines = $managers = $engineers = $operators = 0; $discount = $vars['input']['discount'];
$cutting = $vars['input']['cutting'];
foreach ($calculators as $i => $calculator) { // Инициализация переменных для буфера вывода
// Перебор калькуляторов $machines = $managers = $engineers = $operators = $handymans = $other = 0;
foreach (['calculator'] as &$parameter) { if (count($calculators) > 0) {
// Перебор мета-параметров // Найдены калькуляторы
// Инициализация общего параметра foreach ($calculators as $i => $calculator) {
$type = $calculator[$parameter]; // Перебор калькуляторов
// Инициализация параметра для обработчика калькулятора foreach (['calculator'] as &$parameter) {
unset($calculator[$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) {
// Инициализация номера калькулятора в его категории // Запись в журнал ошибок
$number = count($vars['errors']['calculators'][$type] ?? []); $vars['errors']['calculators'][] = [
'text' => $e->getMessage(),
// Инициализация журнала ошибок для калькулятора 'file' => $e->getFile(),
$calculator['errors'] = []; 'line' => $e->getLine(),
'stack' => $e->getTrace()
// Инициализация журнала ошибок для буфера вывода ];
$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);
} }
return json_encode([ return json_encode([
'machines' => $machines, 'machines' => $machines,
'managers' => $managers, 'managers' => $managers,
'engineers' => $engineers, 'engineers' => $engineers,
'operators' => $operators, 'operators' => $operators,
'handymans' => $handymans, 'handymans' => $handymans,
'other' => $other, 'other' => $other + ['discount' => $discount],
'errors' => $calculator['errors'] 'errors' => $vars['errors']
]); ]);
} }
} }

View File

@ -0,0 +1,163 @@
<?php
declare(strict_types=1);
namespace mirzaev\calculator\models;
use exception;
use pdo;
/**
* Модель баллонов
*
* @package mirzaev\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;
}
}

View File

@ -6,6 +6,7 @@ namespace mirzaev\calculator\models;
use mirzaev\calculator\models\settings_model as settings; use mirzaev\calculator\models\settings_model as settings;
use mirzaev\calculator\models\metals_model as metals; use mirzaev\calculator\models\metals_model as metals;
use mirzaev\calculator\models\baloons_model as baloons;
use exception; use exception;
@ -22,18 +23,18 @@ final class calculators_model extends core
* Расчёт стоимости переработки за 1 тонну * Расчёт стоимости переработки за 1 тонну
* *
* @param string $complexity Сложность * @param string $complexity Сложность
* @param float $lenght Толщина * @param float $length Толщина
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return float Стоимость переработки за 1 тонну (руб) * @return float Стоимость переработки за 1 тонну (руб)
*/ */
public static function reprocessing(string $complexity, float $lenght, array &$errors = []): ?float public static function reprocessing(string $complexity, float $length, array &$errors = []): ?float
{ {
try { try {
return (float) match (true) { return (float) match (true) {
$lenght > 0 && $lenght < 4 => settings::read('reprocessing_' . $complexity . '_1_3', $errors), $length > 0 && $length < 4 => settings::read('reprocessing_' . $complexity . '_1_3', $errors),
$lenght > 3 && $lenght < 7 => settings::read('reprocessing_' . $complexity . '_4_6', $errors), $length > 3 && $length < 7 => settings::read('reprocessing_' . $complexity . '_4_6', $errors),
$lenght > 6 && $lenght < 11 => settings::read('reprocessing_' . $complexity . '_7_10', $errors), $length > 6 && $length < 11 => settings::read('reprocessing_' . $complexity . '_7_10', $errors),
default => settings::read('reprocessing_' . $complexity . '_10', $errors), default => settings::read('reprocessing_' . $complexity . '_10', $errors),
} }
?? 0.0; ?? 0.0;
@ -52,7 +53,7 @@ final class calculators_model extends core
* *
* @param string $complexity Сложность детали (easy, medium, hard) * @param string $complexity Сложность детали (easy, medium, hard)
* @param int|null $area Площадь детали * @param int|null $area Площадь детали
* @param float|null $lenght Толщина детали * @param float|null $length Толщина детали
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return float Коэффициент * @return float Коэффициент
@ -60,7 +61,7 @@ final class calculators_model extends core
* @todo * @todo
* 1. Коэффициент исходя из типа газа (баллоны) * 1. Коэффициент исходя из типа газа (баллоны)
*/ */
public static function coefficient(string $complexity, ?int $area = null, ?float $lenght = null, array &$errors = []): float|false public static function coefficient(string $complexity, ?int $area = null, ?float $length = null, array &$errors = []): float|false
{ {
try { try {
// Коэффициент полученный исходя из сложности детали // Коэффициент полученный исходя из сложности детали
@ -78,15 +79,15 @@ final class calculators_model extends core
} }
if ( if (
isset($lenght) isset($length)
&& ($lenght <= (settings::read('coefficient_lenght_less', $errors) ?? throw new exception("Не найдено: coefficient_lenght_less")) && ($length <= (settings::read('coefficient_length_less', $errors) ?? throw new exception("Не найдено: coefficient_length_less"))
|| $lenght >= (settings::read('coefficient_lenght_more', $errors) ?? throw new exception("Не найдено: coefficient_lenght_more"))) || $length >= (settings::read('coefficient_length_more', $errors) ?? throw new exception("Не найдено: coefficient_length_more")))
) { ) {
// Толщина детали не более и не менее заданных в базе данных размеров // Толщина детали не более и не менее заданных в базе данных размеров
// Прибавление коэффициента исходя из толщины детали // Прибавление коэффициента исходя из толщины детали
// $coefficient += settings::read('coefficient_lenght_degree', $errors) ?? throw new exception("Не найдено: coefficient_lenght_degree"); // $coefficient += settings::read('coefficient_length_degree', $errors) ?? throw new exception("Не найдено: coefficient_length_degree");
// $coefficient -= settings::read('coefficient_lenght_degree', $errors) ?? throw new exception("Не найдено: coefficient_lenght_degree"); // $coefficient -= settings::read('coefficient_length_degree', $errors) ?? throw new exception("Не найдено: coefficient_length_degree");
} }
return (float) $coefficient; return (float) $coefficient;
@ -167,18 +168,18 @@ final class calculators_model extends core
* @param bool|null $out Наш металл? * @param bool|null $out Наш металл?
* @param int|string|null $holes Количество отверстий * @param int|string|null $holes Количество отверстий
* @param int|string|null $diameter Диаметр отверстий (мм) * @param int|string|null $diameter Диаметр отверстий (мм)
* @param string|null $discount Скидка менеджера * @param float|null $cutting Длина реза (мм)
* @param array &$errors Журнал ошибок * @param array &$errors Журнал ошибок
* *
* @return array|bool Аккаунт, если удалось аутентифицироваться * @return array|bool Аккаунт, если удалось аутентифицироваться
* *
* @todo * @todo
* 18. 1200 баллон кислород расход 1 баллон на 3 листа 4ки
* 22. Выводится результат даже если не аутентифицирован (проблема с куки) * 22. Выводится результат даже если не аутентифицирован (проблема с куки)
* 29. Амортизация станка * 39. Переделать массивы в объекты там, где позволяет случай
* 30. Аренда помещения * 40. Перенести лазерный станок в отдельную таблицу в базе данных со всеми его данными
* 31. Расходники * 43. Удаление калькуляторов
* 37. Проверка на то, что загружен минимум 1 калькулятор * @ 44. от 750 +1 лист от 3
* @ 45. от 0.5 до 3 от 620 +1
*/ */
public static function laser( public static function laser(
bool|int|string|null $company = null, bool|int|string|null $company = null,
@ -192,7 +193,7 @@ final class calculators_model extends core
?bool $our = null, ?bool $our = null,
int|string|null $holes = null, int|string|null $holes = null,
float|string|null $diameter = null, float|string|null $diameter = null,
float|string|null $discount = null, float|null $cutting = null,
array &$errors = [] array &$errors = []
): array { ): array {
// Инициализация журнала ошибок // Инициализация журнала ошибок
@ -217,7 +218,7 @@ final class calculators_model extends core
$our = (bool) $our ?? true; $our = (bool) $our ?? true;
$holes = (int) $holes ?? throw new exception('Не передан параметр holes'); $holes = (int) $holes ?? throw new exception('Не передан параметр holes');
$diameter = (float) $diameter ?? throw new exception('Не передан параметр diameter'); $diameter = (float) $diameter ?? throw new exception('Не передан параметр diameter');
$discount = (float) $discount ?? throw new exception('Не передан параметр discount'); $cutting = (float) $cutting ?? throw new exception('Не передан параметр time');
if ($width <= 0 || $height <= 0 || $length <= 0) { if ($width <= 0 || $height <= 0 || $length <= 0) {
// Неподходящие для выполнения значения // Неподходящие для выполнения значения
@ -225,9 +226,11 @@ final class calculators_model extends core
throw new exception('Передан нулевой размер одной из сторон заготовки'); throw new exception('Передан нулевой размер одной из сторон заготовки');
} }
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Инициализация инстанции баллонов
// $gas = 'oxygen'; $baloons = new baloons;
$gas = 'air';
// Вычисление используемого газа
$baloons->gas($length, $errors) ?? throw new exception('Не удалось вычислить тип газа для резки');
// Инициализация станка для буфера вывода // Инициализация станка для буфера вывода
$machine = [ $machine = [
@ -236,25 +239,24 @@ final class calculators_model extends core
// Инициализация буфера остальных вычислений для буфера вывода // Инициализация буфера остальных вычислений для буфера вывода
$other = [ $other = [
'additive' => (float) settings::read('additive', $errors) ?? throw new exception('Не найдено: additive'), 'additive' => (float) settings::read('additive', $errors) ?? throw new exception('Не найдено: additive')
'discount' => $discount
]; ];
// Инициализация прибавок к наценке // // Инициализация прибавок к наценке
$increase_300_3000 = (float) settings::read('additive_increase_300_3000', $errors) ?? throw new exception('Не найдено: additive_increase_300_3000'); // $increase_300_3000 = (float) settings::read('additive_increase_300_3000', $errors) ?? throw new exception('Не найдено: additive_increase_300_3000');
if ($amount >= 300) { // if ($amount >= 300) {
// Количество заказанных деталей равно или более чем 300 шт. // // Количество заказанных деталей равно или более чем 300 шт.
for ($i = 0; $i <= $amount; ++$i) { // for ($i = 0; $i <= $amount; ++$i) {
// Перебор по количеству изготавливаемых деталей // // Перебор по количеству изготавливаемых деталей
// Количество заказанных деталей равно или более чем 300 шт. и равно или менее чем 3000 шт. // // Количество заказанных деталей равно или более чем 300 шт. и равно или менее чем 3000 шт.
if ($amount <= 3000) $other['additive'] += $increase_300_3000; // if ($amount <= 3000) $other['additive'] += $increase_300_3000;
} // }
} // }
$other['additive'] = 1.07; // $other['additive'] = 1.07;
// Площадь // Площадь
$area = $width * $height; $area = $width * $height;
@ -283,7 +285,7 @@ final class calculators_model extends core
// Инициализация характеристик металла // Инициализация характеристик металла
$metal = [ $metal = [
'cut' => metals::cut($type, $gas, $length, $errors), 'cut' => metals::cut($type, $baloons->gas, $length, $errors),
'weight' => metals::kg($type, $errors) * $length, 'weight' => metals::kg($type, $errors) * $length,
'cost' => $list['ton'] / 1000 // Цена за килограмм 'cost' => $list['ton'] / 1000 // Цена за килограмм
]; ];
@ -294,19 +296,24 @@ final class calculators_model extends core
throw new exception('Ошибка при вычислении характеристик металла'); throw new exception('Ошибка при вычислении характеристик металла');
} }
if ($holes == 0 ?? $diameter == 0) { if ($cutting === 0.0) {
// Не переданы данные об отверстиях // Не передана длина реза
// Длина реза (мм) if ($holes == 0 ?? $diameter == 0) {
$cutting = ($width * 2 + $height * 2) * self::coefficient($complexity, $area, $length, $errors); // Не переданы данные об отверстиях
} else {
// Переданы данные об отверстиях
// Длина реза (мм) // Длина реза (мм)
$cutting = (3.1416 * $diameter * $holes + $width * 2 + $height * 2) * self::coefficient($complexity, $area, $length, $errors); $cutting = ($width * 2 + $height * 2) * self::coefficient($complexity, $area, $length, $errors);
} else {
// Переданы данные об отверстиях
// Длина реза (мм)
$cutting = (3.1416 * $diameter * $holes + $width * 2 + $height * 2) * self::coefficient($complexity, $area, $length, $errors);
}
} }
$metal['cut'] = 65; // Запись в буфер вывода
$other['cutting'] = $cutting;
// Время реза одной детали (c) // Время реза одной детали (c)
$time = $cutting / $metal['cut']; $time = $cutting / $metal['cut'];
@ -323,6 +330,9 @@ final class calculators_model extends core
// Стоимость переработки (руб) // Стоимость переработки (руб)
$other['reprocessing'] = self::reprocessing($complexity, $length, $errors) / 1000 * $weight; $other['reprocessing'] = self::reprocessing($complexity, $length, $errors) / 1000 * $weight;
// Стоимость аренды помещения
$other['rent'] = $time * $amount / 60 / 60 * (int) settings::read('rent', $errors) ?? throw new exception('Не найдено: rent');
// (Наш металл) Стоимость металла (руб) // (Наш металл) Стоимость металла (руб)
if ($our) $machine['metal'] = $weight * $metal['cost']; if ($our) $machine['metal'] = $weight * $metal['cost'];
@ -338,6 +348,41 @@ final class calculators_model extends core
// Вычисление количества листов // Вычисление количества листов
for ($lists = 0; ++$lists * $list['volume'] < $volume * $amount;); for ($lists = 0; ++$lists * $list['volume'] < $volume * $amount;);
// Условия для прибавления листа (костыль)
if (($length > 3 && ($width >= 750 || $height >= 750))
|| ($length >= 0.5 && $length <= 3 && ($width >= 620 || $height >= 620))
) ++$list;
// Инициализация данных линзы
$lense = [
'cost' => (int) settings::read('laser_lense_cost', $errors) ?? throw new exception('Не найдено: laser_lense_cost'),
'length' => (int) settings::read('laser_lense_flow', $errors) ?? throw new exception('Не найдено: laser_lense_flow')
];
// Вычисление количества используемых линз
$lenses = $cutting / $lense['length'];
// Запись данных о линзах в буфер вывода (стоимость)
$machine['lenses'] = $lenses * $lense['cost'];
// Вычисление количества использованных баллонов
$baloons->amount($type, $cutting * $amount, $length, errors: $errors) ?? throw new exception('Не удалось вычислить количество используемых баллонов');
// Вычисление стоимости баллонов
$baloons->cost($type, errors: $errors) ?? throw new exception('Не удалось вычислить стоимость баллонов');
// Запись данных о баллонах в буфер вывода
$other['baloons'] = [
'amount' => $baloons->amount,
'cost' => $baloons->cost
];
// Запись данных о баллонах в буфер вывода
$other['baloons'] = [
'amount' => $baloons->amount,
'cost' => $baloons->cost
];
// Вычисление времени работы разнорабочих (ч) // Вычисление времени работы разнорабочих (ч)
$work = $lists * $load / 60; $work = $lists * $load / 60;
@ -375,7 +420,7 @@ final class calculators_model extends core
[ [
'time' => [ 'time' => [
'design' => $operator['time'], 'design' => $operator['time'],
'machine' => ($time * $amount / 60 / 60) + $work 'machine' => $time * $amount / 60 / 60 + $work
], ],
'hour' => $operator['hour'] 'hour' => $operator['hour']
] ]
@ -407,6 +452,8 @@ final class calculators_model extends core
]; ];
} }
$machine['depreciation'] = $time * $amount / 60 / 60 * (int) settings::read('laser_depreciation', $errors) ?? throw new exception('Не найдено: laser_depreciation');
// Инициализация станков // Инициализация станков
$machines = [ $machines = [
$machine $machine

View File

@ -116,3 +116,56 @@
#calculator>.calculator>div>div>input[type="range"] { #calculator>.calculator>div>div>input[type="range"] {
min-width: 50%; min-width: 50%;
} }
#calculator>.calculator>div>div>input[type="range"] {
min-width: 50%;
}
#calculator>#result {
display: flex;
flex-direction: column;
}
#calculator>#result>a {
margin: unset;
}
#calculator>#result>#errors {
margin-bottom: unset;
padding: 15px 25px;
background-color: #acacac;
}
#calculator>#result>#errors:empty {
margin: unset;
padding: unset;
background-color: unset;
}
#calculator>#result>#errors>dt {
margin-bottom: 8px;
font-weight: bold;
}
#calculator>#result>#errors>dd {
margin-left: 20px;
}
#calculator>#result>div[type="row"]:first-of-type {
margin-top: 10px;
}
#calculator>#result>div[type="row"]:last-of-type {
margin-bottom: 20px;
}
#calculator>#result>div[type="row"] {
padding: 0 10px;
display: flex;
flex-direction: column;
}
#calculator>#result>div[type="row"]>label {
font-weight: bold;
}

View File

@ -71,17 +71,25 @@ let calculator = {
calculate() { calculate() {
// Запрос и генерация HTML с данными о рассчете со всех калькуляторов // Запрос и генерация HTML с данными о рассчете со всех калькуляторов
// Инициализация параметров
let cutting = document.getElementById('cutting');
let discount = document.getElementById('discount');
// Инициализация буфера запроса // Инициализация буфера запроса
let query = {}; let query = {
calculators: {},
cutting: +cutting.value ?? 0,
discount: +discount.value ?? 0
};
for (const number in this.calculators) { for (const number in this.calculators) {
// Перебор калькуляторов // Перебор калькуляторов
// Инициализация буфера запроса для нового калькулятора // Инициализация буфера запроса для нового калькулятора
query[number] = {}; query['calculators'][number] = {};
// Инициализация типа калькулятора // Инициализация типа калькулятора
query[number]['calculator'] = this.calculators[number].getAttribute('data-calculator'); query['calculators'][number]['calculator'] = this.calculators[number].getAttribute('data-calculator');
for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) { for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) {
// Перебор полей с параметрами типа заказчика // Перебор полей с параметрами типа заказчика
@ -90,7 +98,7 @@ let calculator = {
// Найдено выбранное поле // Найдено выбранное поле
// Запись в буфер запроса // Запись в буфер запроса
query[number]['buyer'] = buyer.value; query['calculators'][number]['buyer'] = buyer.value;
} }
} }
@ -101,7 +109,7 @@ let calculator = {
// Найдено выбранное поле // Найдено выбранное поле
// Запись в буфер запроса // Запись в буфер запроса
query[number]['complexity'] = complexity.value; query['calculators'][number]['complexity'] = complexity.value;
} }
} }
@ -112,22 +120,19 @@ let calculator = {
// Флажок // Флажок
// Запись в буфер запроса // Запись в буфер запроса
query[number][field.getAttribute('data-calculator-parameter')] = field.checked; query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.checked;
} else if (field.getAttribute('type') === 'text' || field.getAttribute('type') === 'number' || field.getAttribute('type') === 'range') { } else if (field.getAttribute('type') === 'text' || field.getAttribute('type') === 'number' || field.getAttribute('type') === 'range') {
// Текстовое, цифровое поле или ползунок // Текстовое, цифровое поле или ползунок
// Запись в буфер запроса // Запись в буфер запроса
query[number][field.getAttribute('data-calculator-parameter')] = field.value; query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value;
} else { } else {
// Элемент с тегом <select> (подразумевается) // Элемент с тегом <select> (подразумевается)
// Запись в буфер запроса // Запись в буфер запроса
query[number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text; query['calculators'][number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text;
} }
} }
// Сортировка
query[number] = query[number];
} }
return fetch('/calculator/calculate', { return fetch('/calculator/calculate', {
@ -141,54 +146,100 @@ let calculator = {
// Инициализация буфера расходов // Инициализация буфера расходов
let expenses = 0; let expenses = 0;
for (const [, machine] of Object.entries(success.machines)) { // Инициализация буфера с данными расчёта
// Перебор станков let result;
// Прибавление данных к буферу расходов if (this.generate.error(success.errors) > 0) {
expenses += (machine.electricity + (machine.metal ?? 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 + ' рублей';
} }
for (const [, manager] of Object.entries(success.managers)) { if (this.generate.result(result)) {
// Перебор менеджеров
// Прибавление данных к буферу расходов
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.reprocessing;
// Добавление наценки (коэффициент)
expenses *= success.other.additive;
// Добавление скидки менеджера
expenses -= expenses * (success.other.discount / 100)
// Округление
expenses = expenses.toFixed(2);
if (this.generate.result(expenses + ' рублей')) {
console.log(`[КАЛЬКУЛЯТОР] Сгенерирован результат: ${expenses} рублей`); console.log(`[КАЛЬКУЛЯТОР] Сгенерирован результат: ${expenses} рублей`);
} else { } else {
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат'); console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
@ -261,54 +312,6 @@ let calculator = {
} }
}); });
}, },
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) {
// Не найден элемент с данными расчётов
// Генерация элемента с данными расчётов
}
if (expenses !== undefined) {
// Переданы расходы
// Инициализация элемента
let element = document.getElementById('calculate');
if (element == null) {
// Не найден элемент с результатом расчёта
return false;
}
// Запись расходов в элемент (подразумевается кнопка отправки на расчёт)
element.innerText = expenses;
return true;
}
return request();
},
mark(element, type = '') { mark(element, type = '') {
// Запрос и генерация HTML с полем выбора марки металла // Запрос и генерация HTML с полем выбора марки металла
@ -373,6 +376,116 @@ let calculator = {
} }
}); });
}, },
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: { calculators: {
laser() { laser() {
// Запрос и генерация HTML с калькулятором лазерной резки // Запрос и генерация HTML с калькулятором лазерной резки

View File

@ -1,4 +1,4 @@
<h3>Лазерная резка</h3> <h3>Лазерная резка <span title="Удалить"></span></h3>
<section class="calculator" data-calculator="laser"> <section class="calculator" data-calculator="laser">
<div> <div>
<label>Тип</label> <label>Тип</label>
@ -66,27 +66,4 @@
<input data-calculator-parameter="our" type="checkbox" title="Используется наш металл" checked> <input data-calculator-parameter="our" type="checkbox" title="Используется наш металл" checked>
</div> </div>
</div> </div>
<div>
<label>Скидка ({{ calculators.laser.discount.default ?? 0 }}%)</label>
<div>
<input data-calculator-parameter="discount" type="range" title="Скидка менеджера"
value="{{ calculators.laser.discount.default ?? 0 }}" min="{{ calculators.laser.discount.min ?? 0 }}"
max="{{ calculators.laser.discount.max ?? 5 }}" step="{{ calculators.laser.discount.step ?? 1 }}" oninput="this.parentElement.parentElement.children[0].innerText = 'Скидка (' + this.value + '%)';">
<!-- <datalist id="discount_level">
<option value="0" label="{{ calculators.laser.discount.min ?? 0 }}%">
<option value="10">
<option value="20">
<option value="30">
<option value="40">
<option value="50" label="{{ ((calculators.laser.discount.max ?? 5) + (calculators.laser.discount.min ?? 0)) / 2 }}%">
<option value="60">
<option value="70">
<option value="80">
<option value="90">
<option value="100" label="{{ calculators.laser.discount.max ?? 5 }}%">
</datalist> -->
</div>
</div>
</section> </section>

View File

@ -1,3 +1,46 @@
<section id="result"> <section id="result">
<div type="row">
<label>Длина реза 1 детали ({{ cutting.default ?? 0 }}мм)</label>
<input id="cutting" type="range" title="Длина реза 1 детали "
value="{{ cutting.default ?? 0 }}" min="{{ cutting.min ?? 0 }}" max="{{ cutting.max ?? 10000 }}"
step="{{ cutting.step ?? 1 }}"
oninput="this.parentElement.children[0].innerText = 'Длина реза 1 детали (' + this.value + 'мм)';" disabled>
<!-- <datalist id="cutting_level">
<option value="0" label="{{ cutting.min ?? 0 }}%">
<option value="10">
<option value="20">
<option value="30">
<option value="40">
<option value="50" label="{{ ((cutting.max ?? 10000) + (cutting.min ?? 0)) / 2 }}%">
<option value="60">
<option value="70">
<option value="80">
<option value="90">
<option value="100" label="{{ cutting.max ?? 100005 }}%">
</datalist> -->
</div>
<div type="row">
<label>Скидка ({{ discount.default ?? 0 }}%)</label>
<input id="discount" type="range" title="Скидка менеджера"
value="{{ discount.default ?? 0 }}" min="{{ discount.min ?? 0 }}" max="{{ discount.max ?? 5 }}"
step="{{ discount.step ?? 1 }}"
oninput="this.parentElement.children[0].innerText = 'Скидка (' + this.value + '%)';">
<!-- <datalist id="discount_level">
<option value="0" label="{{ calculators.discount.min ?? 0 }}%">
<option value="10">
<option value="20">
<option value="30">
<option value="40">
<option value="50" label="{{ ((calculators.discount.max ?? 5) + (discount.min ?? 0)) / 2 }}%">
<option value="60">
<option value="70">
<option value="80">
<option value="90">
<option value="100" label="{{ discount.max ?? 5 }}%">
</datalist> -->
</div>
<a id="calculate" class="unselectable" type="button" onclick="return calculator.calculate();">0 рублей</a> <a id="calculate" class="unselectable" type="button" onclick="return calculator.calculate();">0 рублей</a>
<dl id="errors"></dl>
</section> </section>