parent
c5e940fc25
commit
4c6e5cdd1d
|
@ -38,7 +38,7 @@ final class administrator extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["administrators_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0;
|
$value = $_COOKIE["administrators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0;
|
||||||
|
|
||||||
// Инициализировано значение?
|
// Инициализировано значение?
|
||||||
if ($value === null || $value === 0) continue;
|
if ($value === null || $value === 0) continue;
|
||||||
|
@ -86,7 +86,7 @@ final class administrator extends core
|
||||||
// Перебор фильтров статусов (И)
|
// Перебор фильтров статусов (И)
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
||||||
if (empty($value = $_COOKIE["administrators_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0)) continue;
|
if (empty($value = $_COOKIE["administrators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0)) continue;
|
||||||
|
|
||||||
// Конвертация ярлыков
|
// Конвертация ярлыков
|
||||||
$converted = match ($name) {
|
$converted = match ($name) {
|
||||||
|
@ -116,7 +116,7 @@ final class administrator extends core
|
||||||
if (!empty($filters_statuses_merged)) $filters .= empty($filters) ? $filters_statuses_merged : " && ($filters_statuses_merged)";
|
if (!empty($filters_statuses_merged)) $filters .= empty($filters) ? $filters_statuses_merged : " && ($filters_statuses_merged)";
|
||||||
|
|
||||||
// Инициализация строки поиска
|
// Инициализация строки поиска
|
||||||
$search = $_COOKIE["administrators_filter_search"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters']['search'] ?? '';
|
$search = $_COOKIE["administrators_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters']['search'] ?? '';
|
||||||
if (mb_strlen($search) < 3) $search = null;
|
if (mb_strlen($search) < 3) $search = null;
|
||||||
$search_query = empty($search)
|
$search_query = empty($search)
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -30,7 +30,7 @@ final class index extends core
|
||||||
// Перебор фильтров временного промежутка
|
// Перебор фильтров временного промежутка
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
if (empty($value = (int) ($_COOKIE["tasks_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? (($name === 'from') ? time() : strtotime('+1 month'))))) continue;
|
if (empty($value = (int) ($_COOKIE["tasks_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? (($name === 'from') ? time() : strtotime('+1 month'))))) continue;
|
||||||
|
|
||||||
// Генерация значения для аттрибута "value" для HTML-элемента <input>
|
// Генерация значения для аттрибута "value" для HTML-элемента <input>
|
||||||
$this->view->{$name} = (int) $value;
|
$this->view->{$name} = (int) $value;
|
||||||
|
@ -40,7 +40,7 @@ final class index extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["tasks_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? null;
|
$value = $_COOKIE["tasks_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? null;
|
||||||
|
|
||||||
// Найдено значение?
|
// Найдено значение?
|
||||||
if ($value === null) continue;
|
if ($value === null) continue;
|
||||||
|
|
|
@ -44,7 +44,7 @@ final class market extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["markets_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters'][$name] ?? 0;
|
$value = $_COOKIE["markets_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters'][$name] ?? 0;
|
||||||
|
|
||||||
// Инициализировано значение?
|
// Инициализировано значение?
|
||||||
if ($value === null || $value === 0) continue;
|
if ($value === null || $value === 0) continue;
|
||||||
|
@ -94,7 +94,7 @@ final class market extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
||||||
if (empty($value = $_COOKIE["markets_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters'][$name] ?? 0)) continue;
|
if (empty($value = $_COOKIE["markets_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters'][$name] ?? 0)) continue;
|
||||||
|
|
||||||
// Конвертация ярлыков
|
// Конвертация ярлыков
|
||||||
$converted = match ($name) {
|
$converted = match ($name) {
|
||||||
|
@ -135,7 +135,7 @@ final class market extends core
|
||||||
if (!empty($filters_statuses_after_merged)) $filters_after .= empty($filters_after) ? $filters_statuses_after_merged : " && ($filters_statuses_after_merged)";
|
if (!empty($filters_statuses_after_merged)) $filters_after .= empty($filters_after) ? $filters_statuses_after_merged : " && ($filters_statuses_after_merged)";
|
||||||
|
|
||||||
// Инициализация строки поиска
|
// Инициализация строки поиска
|
||||||
$search = $_COOKIE["markets_filter_search"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters']['search'] ?? '';
|
$search = $_COOKIE["markets_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['markets']['filters']['search'] ?? '';
|
||||||
if (mb_strlen($search) < 3) $search = null;
|
if (mb_strlen($search) < 3) $search = null;
|
||||||
$search_query = empty($search)
|
$search_query = empty($search)
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -38,7 +38,7 @@ final class operator extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["operators_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters'][$name] ?? 0;
|
$value = $_COOKIE["operators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters'][$name] ?? 0;
|
||||||
|
|
||||||
// Инициализировано значение?
|
// Инициализировано значение?
|
||||||
if ($value === null || $value === 0) continue;
|
if ($value === null || $value === 0) continue;
|
||||||
|
@ -86,7 +86,7 @@ final class operator extends core
|
||||||
// Перебор фильтров статусов (И)
|
// Перебор фильтров статусов (И)
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
||||||
if (empty($value = $_COOKIE["operators_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters'][$name] ?? 0)) continue;
|
if (empty($value = $_COOKIE["operators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters'][$name] ?? 0)) continue;
|
||||||
|
|
||||||
// Конвертация ярлыков
|
// Конвертация ярлыков
|
||||||
$converted = match ($name) {
|
$converted = match ($name) {
|
||||||
|
@ -116,7 +116,7 @@ final class operator extends core
|
||||||
if (!empty($filters_statuses_merged)) $filters .= empty($filters) ? $filters_statuses_merged : " && ($filters_statuses_merged)";
|
if (!empty($filters_statuses_merged)) $filters .= empty($filters) ? $filters_statuses_merged : " && ($filters_statuses_merged)";
|
||||||
|
|
||||||
// Инициализация строки поиска
|
// Инициализация строки поиска
|
||||||
$search = $_COOKIE["operators_filter_search"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters']['search'] ?? '';
|
$search = $_COOKIE["operators_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['operators']['filters']['search'] ?? '';
|
||||||
if (mb_strlen($search) < 3) $search = null;
|
if (mb_strlen($search) < 3) $search = null;
|
||||||
$search_query = empty($search)
|
$search_query = empty($search)
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\ebala\controllers;
|
||||||
|
|
||||||
|
// Файлы проекта
|
||||||
|
use mirzaev\ebala\controllers\core,
|
||||||
|
mirzaev\ebala\controllers\traits\errors,
|
||||||
|
mirzaev\ebala\models\payments as model;
|
||||||
|
|
||||||
|
// System libraries
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контроллер выплат
|
||||||
|
*
|
||||||
|
* @package mirzaev\ebala\controllers
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class payments extends core
|
||||||
|
{
|
||||||
|
use errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сотрудники
|
||||||
|
*
|
||||||
|
* Расчитать стоимость работы сотрудников за выбранный период и сгенерировать excel-документ
|
||||||
|
*
|
||||||
|
* @param array $parameters Параметры запроса
|
||||||
|
*
|
||||||
|
* @return void В буфер вывода excel-документ
|
||||||
|
*/
|
||||||
|
public function workers(array $parameters = []): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
|
||||||
|
// Авторизован аккаунт администратора или оператора
|
||||||
|
|
||||||
|
// Инициализация буфера ошибок
|
||||||
|
$this->errors['export'] ??= [];
|
||||||
|
|
||||||
|
if (!empty($from = (int) ($_COOKIE["tasks_filter_from"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['from']))) {
|
||||||
|
// Инициализирован параметр: from
|
||||||
|
|
||||||
|
if (!empty($to = (int) ($_COOKIE["tasks_filter_to"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['to']))) {
|
||||||
|
// Инициализирован параметр: to
|
||||||
|
|
||||||
|
// Сброс буфера вывода
|
||||||
|
if (ob_get_level()) {
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация буфера вывода
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
if (model::workers($from, $to, $this->errors['export'])) {
|
||||||
|
// Сгенерирован excel-документ с выплатами (и отправлен в буфер вывода)
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Description: Spreadsheet transfer');
|
||||||
|
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||||
|
header('Content-Disposition: attachment;filename=workers ' . gmdate("d.m.Y", $from) . ' - ' . gmdate("d.m.Y", $to) . '.xlsx');
|
||||||
|
header('Access-Control-Expose-Headers: Content-Disposition');
|
||||||
|
header('Cache-Control: max-age=0');
|
||||||
|
} else throw new exception('Не удалось сгенерировать excel-документ');
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Length: ' . ob_get_length());
|
||||||
|
|
||||||
|
// Отправка и деинициализация буфера вывода
|
||||||
|
ob_end_flush();
|
||||||
|
flush();
|
||||||
|
} else throw new exception('Не инициализирован параметр: to');
|
||||||
|
} else throw new exception('Не инициализирован параметр: from');
|
||||||
|
} else throw new exception('Вы не авторизованы');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в реестр ошибок
|
||||||
|
$this->errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Content-Encoding: none');
|
||||||
|
header('X-Accel-Buffering: no');
|
||||||
|
|
||||||
|
// Инициализация буфера вывода
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Генерация ответа
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
'errors' => self::parse_only_text($this->errors)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Length: ' . ob_get_length());
|
||||||
|
|
||||||
|
// Отправка и деинициализация буфера вывода
|
||||||
|
ob_end_flush();
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Магазины
|
||||||
|
*
|
||||||
|
* Расчитать ... (сверку?) за выбранный период и сгенерировать excel-документ
|
||||||
|
*
|
||||||
|
* @param array $parameters Параметры запроса
|
||||||
|
*
|
||||||
|
* @return void В буфер вывода excel-документ
|
||||||
|
*/
|
||||||
|
public function markets(array $parameters = []): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
|
||||||
|
// Авторизован аккаунт администратора или оператора
|
||||||
|
|
||||||
|
// Инициализация буфера ошибок
|
||||||
|
$this->errors['export'] ??= [];
|
||||||
|
|
||||||
|
if (!empty($from = (int) ($_COOKIE["tasks_filter_from"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['from']))) {
|
||||||
|
// Инициализирован параметр: from
|
||||||
|
|
||||||
|
if (!empty($to = (int) ($_COOKIE["tasks_filter_to"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['to']))) {
|
||||||
|
// Инициализирован параметр: to
|
||||||
|
|
||||||
|
// Сброс буфера вывода
|
||||||
|
if (ob_get_level()) {
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация буфера вывода
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
if (model::markets($from, $to, $this->errors['export'])) {
|
||||||
|
// Сгенерирован excel-документ с выплатами (и отправлен в буфер вывода)
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Description: Spreadsheet transfer');
|
||||||
|
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||||
|
header('Content-Disposition: attachment;filename=markets ' . gmdate("d.m.Y", $from) . ' - ' . gmdate("d.m.Y", $to) . '.xlsx');
|
||||||
|
header('Access-Control-Expose-Headers: Content-Disposition');
|
||||||
|
header('Cache-Control: max-age=0');
|
||||||
|
} else throw new exception('Не удалось сгенерировать excel-документ');
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Length: ' . ob_get_length());
|
||||||
|
|
||||||
|
// Отправка и деинициализация буфера вывода
|
||||||
|
ob_end_flush();
|
||||||
|
flush();
|
||||||
|
} else throw new exception('Не инициализирован параметр: to');
|
||||||
|
} else throw new exception('Не инициализирован параметр: from');
|
||||||
|
} else throw new exception('Вы не авторизованы');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в реестр ошибок
|
||||||
|
$this->errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Content-Encoding: none');
|
||||||
|
header('X-Accel-Buffering: no');
|
||||||
|
|
||||||
|
// Инициализация буфера вывода
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Генерация ответа
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
'errors' => self::parse_only_text($this->errors)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Запись заголовков ответа
|
||||||
|
header('Content-Length: ' . ob_get_length());
|
||||||
|
|
||||||
|
// Отправка и деинициализация буфера вывода
|
||||||
|
ob_end_flush();
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ final class task extends core
|
||||||
$this->errors['tasks'] ??= [];
|
$this->errors['tasks'] ??= [];
|
||||||
|
|
||||||
if (!empty($json = json_decode(file_get_contents('php://input'), true, 4))) {
|
if (!empty($json = json_decode(file_get_contents('php://input'), true, 4))) {
|
||||||
|
// Найден и декодирован json-документ с данными для создания заявок
|
||||||
|
|
||||||
foreach ($json as $work => $tasks) {
|
foreach ($json as $work => $tasks) {
|
||||||
// Перебор категорий (колонок)
|
// Перебор категорий (колонок)
|
||||||
|
@ -145,7 +145,7 @@ final class task extends core
|
||||||
// Перебор фильтров временного промежутка (И)
|
// Перебор фильтров временного промежутка (И)
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
if (empty($value = (int) ($_COOKIE["tasks_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? (($name === 'from') ? time() : strtotime('+1 month'))))) continue;
|
if (empty($value = (int) ($_COOKIE["tasks_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? (($name === 'from') ? time() : strtotime('+1 month'))))) continue;
|
||||||
|
|
||||||
// Генерация AQL-выражения для инъекции в строку запроса
|
// Генерация AQL-выражения для инъекции в строку запроса
|
||||||
if ($name === 'from') $interval .= " && task.date >= $value";
|
if ($name === 'from') $interval .= " && task.date >= $value";
|
||||||
|
@ -162,7 +162,7 @@ final class task extends core
|
||||||
// Перебор фильтров с произвольными значениями (И)
|
// Перебор фильтров с произвольными значениями (И)
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["tasks_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? null;
|
$value = $_COOKIE["tasks_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? null;
|
||||||
|
|
||||||
// Найдено значение?
|
// Найдено значение?
|
||||||
if ($value === null) continue;
|
if ($value === null) continue;
|
||||||
|
@ -182,7 +182,7 @@ final class task extends core
|
||||||
// Перебор фильтров по статусам
|
// Перебор фильтров по статусам
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
||||||
if (empty($value = $_COOKIE["tasks_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? 0)) continue;
|
if (empty($value = $_COOKIE["tasks_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters'][$name] ?? 0)) continue;
|
||||||
|
|
||||||
// Конвертация ярлыков
|
// Конвертация ярлыков
|
||||||
$converted = match ($name) {
|
$converted = match ($name) {
|
||||||
|
@ -238,7 +238,7 @@ final class task extends core
|
||||||
else if (empty($this->session->buffer[$_SERVER['INTERFACE']]['tasks']['page'])) $this->session->write(['tasks' => ['page' => 1]]);
|
else if (empty($this->session->buffer[$_SERVER['INTERFACE']]['tasks']['page'])) $this->session->write(['tasks' => ['page' => 1]]);
|
||||||
|
|
||||||
// Инициализация строки поиска
|
// Инициализация строки поиска
|
||||||
$search = $_COOKIE["tasks_filter_search"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['search'] ?? '';
|
$search = $_COOKIE["tasks_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['tasks']['filters']['search'] ?? '';
|
||||||
if (mb_strlen($search) < 3) $search = null;
|
if (mb_strlen($search) < 3) $search = null;
|
||||||
$search_query = empty($search)
|
$search_query = empty($search)
|
||||||
? null
|
? null
|
||||||
|
@ -407,6 +407,8 @@ final class task extends core
|
||||||
* @param array $rows Строки
|
* @param array $rows Строки
|
||||||
*
|
*
|
||||||
* @return array Обработанные строки
|
* @return array Обработанные строки
|
||||||
|
*
|
||||||
|
* @todo Переделать в model::hours($from, $to);
|
||||||
*/
|
*/
|
||||||
protected static function preprocessing(account $account, array $rows): array
|
protected static function preprocessing(account $account, array $rows): array
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,7 @@ final class worker extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie)
|
// Инициализация значения (приоритет у cookie)
|
||||||
$value = $_COOKIE["workers_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters'][$name] ?? 0;
|
$value = $_COOKIE["workers_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters'][$name] ?? 0;
|
||||||
|
|
||||||
// Инициализировано значение?
|
// Инициализировано значение?
|
||||||
if ($value === null || $value === 0) continue;
|
if ($value === null || $value === 0) continue;
|
||||||
|
@ -94,7 +94,7 @@ final class worker extends core
|
||||||
// Перебор фильтров статусов
|
// Перебор фильтров статусов
|
||||||
|
|
||||||
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
|
||||||
if (empty($value = $_COOKIE["workers_filter_$name"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters'][$name] ?? 0)) continue;
|
if (empty($value = $_COOKIE["workers_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters'][$name] ?? 0)) continue;
|
||||||
|
|
||||||
// Конвертация ярлыков
|
// Конвертация ярлыков
|
||||||
$converted = match ($name) {
|
$converted = match ($name) {
|
||||||
|
@ -135,7 +135,7 @@ final class worker extends core
|
||||||
if (!empty($filters_statuses_after_merged)) $filters_after .= empty($filters_after) ? $filters_statuses_after_merged : " && ($filters_statuses_after_merged)";
|
if (!empty($filters_statuses_after_merged)) $filters_after .= empty($filters_after) ? $filters_statuses_after_merged : " && ($filters_statuses_after_merged)";
|
||||||
|
|
||||||
// Инициализация строки поиска
|
// Инициализация строки поиска
|
||||||
$search = $_COOKIE["workers_filter_search"] ?? $this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters']['search'] ?? '';
|
$search = $_COOKIE["workers_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['workers']['filters']['search'] ?? '';
|
||||||
if (mb_strlen($search) < 3) $search = null;
|
if (mb_strlen($search) < 3) $search = null;
|
||||||
$search_query = empty($search)
|
$search_query = empty($search)
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -132,6 +132,69 @@ class core extends model
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect from ArangoDB
|
||||||
|
*
|
||||||
|
* @param string $filter Выражения для фильтрации на языке AQL
|
||||||
|
* @param string $sort Выражение для сортировки на языке AQL
|
||||||
|
* @param int $amount Количество документов для выборки
|
||||||
|
* @param int $page Страница
|
||||||
|
* @param string $index Параметр по которому будет производиться сборка
|
||||||
|
* @param string $return Выражение описываемое возвращаемые данные на языке AQL
|
||||||
|
* @param array &$errors Реестр ошибок
|
||||||
|
*
|
||||||
|
* @return _document|array|null Массив инстанций документов в базе данных, если найдены
|
||||||
|
*/
|
||||||
|
public static function collect(
|
||||||
|
string $filter = '',
|
||||||
|
string $sort = 'd.created DESC, d._key DESC',
|
||||||
|
int $amount = 1,
|
||||||
|
int $page = 1,
|
||||||
|
string $index = 'd.updated',
|
||||||
|
string $return = 'd',
|
||||||
|
array &$errors = []
|
||||||
|
): _document|array|null {
|
||||||
|
try {
|
||||||
|
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
|
||||||
|
// Инициализирована коллекция
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return collection::search(
|
||||||
|
static::$arangodb->session,
|
||||||
|
sprintf(
|
||||||
|
<<<'AQL'
|
||||||
|
FOR d IN %s
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
LIMIT %d, %d
|
||||||
|
COLLECT index = %s INTO group = %s
|
||||||
|
RETURN { [index]: group }
|
||||||
|
AQL,
|
||||||
|
static::COLLECTION,
|
||||||
|
empty($filter) ? '' : "FILTER $filter",
|
||||||
|
empty($sort) ? '' : "SORT $sort",
|
||||||
|
--$page <= 0 ? 0 : $amount * $page,
|
||||||
|
$amount,
|
||||||
|
$index,
|
||||||
|
$return
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в реестр ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count documents in ArangoDB
|
* Count documents in ArangoDB
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,609 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\ebala\models;
|
||||||
|
|
||||||
|
// Файлы проекта
|
||||||
|
use mirzaev\ebala\models\traits\status;
|
||||||
|
|
||||||
|
// Библиотека для ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Фреймворк для работы с таблицами
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory,
|
||||||
|
PhpOffice\PhpSpreadsheet\Style\Color,
|
||||||
|
PhpOffice\PhpSpreadsheet\Style\Fill,
|
||||||
|
PhpOffice\PhpSpreadsheet\Style\Conditional,
|
||||||
|
PhpOffice\PhpSpreadsheet\Style\Alignment,
|
||||||
|
PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
|
||||||
|
// System libraries
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Модель выплат
|
||||||
|
*
|
||||||
|
* @package mirzaev\ebala\models
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class payments extends core
|
||||||
|
{
|
||||||
|
use status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сотрудники
|
||||||
|
*
|
||||||
|
* Расчитать стоимость работы сотрудников за выбранный период и сгенерировать excel-документ
|
||||||
|
*
|
||||||
|
* @param int $from Начальная дата для выборки заявок (unixtime)
|
||||||
|
* @param int $to Конечная дата для выборки заявок (unixtime)
|
||||||
|
* @param array $errors Errors registry
|
||||||
|
*
|
||||||
|
* @return bool Записан буфер вывода сгенерированный excel-документ?
|
||||||
|
*/
|
||||||
|
public static function workers(int $from, int $to, array &$errors = []): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Чтение заявок
|
||||||
|
$tasks = @task::read("d.date >= $from && d.date <= $to && d.problematic == false && d.completed == true", amount: 999999, return: '{worker: d.worker, market: d.market, date: d.date, work: d.work, start: d.start, end: d.end, commentary: d.commentary, rating: d.rating, review: d.review}', errors: $errors);
|
||||||
|
|
||||||
|
if (is_array($tasks) && count($tasks) > 0) {
|
||||||
|
// Найдены заявки
|
||||||
|
|
||||||
|
// Инициализация таблицы
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
// Конвертация unixtime в читаемую дату
|
||||||
|
$_from = gmdate("d.m.Y", $from);
|
||||||
|
$_to = gmdate("d.m.Y", $to);
|
||||||
|
|
||||||
|
// Запись настроек таблицы
|
||||||
|
$spreadsheet
|
||||||
|
->getProperties()
|
||||||
|
->setCreator('Спецресурс')
|
||||||
|
->setLastModifiedBy('Спецресурс')
|
||||||
|
->setTitle("$_from - $_to")
|
||||||
|
->setSubject("Зарплаты сотрудникам $_from - $_to")
|
||||||
|
->setDescription("Зарплаты сотрудникам за период с $_from по $_to")
|
||||||
|
->setKeywords('зарплата сотрудники');
|
||||||
|
|
||||||
|
// Открытие страницы
|
||||||
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
|
||||||
|
// Запись первой строки (названия колонок)
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->setCellValue('A1', 'Адрес')
|
||||||
|
->setCellValue('B1', 'Дата выплаты')
|
||||||
|
->setCellValue('C1', 'Дата заявки')
|
||||||
|
->setCellValue('D1', 'Магазин')
|
||||||
|
->setCellValue('E1', 'Сотрудник')
|
||||||
|
->setCellValue('F1', 'Работа')
|
||||||
|
->setCellValue('G1', 'Начало')
|
||||||
|
->setCellValue('H1', 'Конец')
|
||||||
|
->setCellValue('I1', 'Часы')
|
||||||
|
->setCellValue('J1', 'Статус')
|
||||||
|
->setCellValue('K1', 'Рейтинг')
|
||||||
|
->setCellValue('L1', 'Отзыв')
|
||||||
|
->setCellValue('M1', 'ФИО')
|
||||||
|
->setCellValue('N1', 'Час')
|
||||||
|
->setCellValue('O1', 'Смена')
|
||||||
|
->setCellValue('P1', 'Штраф')
|
||||||
|
->setCellValue('Q1', 'Премия')
|
||||||
|
->setCellValue('R1', 'Полная оплата')
|
||||||
|
->setCellValue('S1', 'Наличными')
|
||||||
|
->setCellValue('T1', 'Наличные?')
|
||||||
|
->setCellValue('U1', 'Переводом')
|
||||||
|
->setCellValue('V1', 'Реквизиты')
|
||||||
|
->setCellValue('W1', 'Тариф')
|
||||||
|
->setCellValue('X1', 'Без НДС')
|
||||||
|
->setCellValue('Y1', 'Прибыль')
|
||||||
|
->setCellValue('Z1', 'Примечание')
|
||||||
|
->setCellValue('AA1', 'Долг сотрудника')
|
||||||
|
->setCellValue('AB1', 'Кто платит')
|
||||||
|
->setCellValue('AC1', 'Кто платит');
|
||||||
|
|
||||||
|
// Запись цвета верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A1:AC1')
|
||||||
|
->getFill()
|
||||||
|
->setFillType(Fill::FILL_SOLID)
|
||||||
|
->getStartColor()
|
||||||
|
->setARGB('ffffffb9');
|
||||||
|
|
||||||
|
// Запись толщины текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A1:AC1')
|
||||||
|
->getFont()
|
||||||
|
->setBold(true);
|
||||||
|
|
||||||
|
// Запись размера текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A1:AC1')
|
||||||
|
->getFont()
|
||||||
|
->setSize(13);
|
||||||
|
|
||||||
|
// Запись позиции текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A1:AC1')
|
||||||
|
->getAlignment()
|
||||||
|
->setHorizontal(Alignment::HORIZONTAL_CENTER)
|
||||||
|
->setVertical(Alignment::VERTICAL_CENTER);
|
||||||
|
|
||||||
|
// Запись ширины строки верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getRowDimension(1)
|
||||||
|
->setRowHeight(24);
|
||||||
|
|
||||||
|
// Запись ширины колонок
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('A')->setWidth(30);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('B')->setWidth(18);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth(18);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('E')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('F')->setWidth(22);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('G')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('H')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('I')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('J')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('K')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('L')->setWidth(40);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('M')->setWidth(32);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('N')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('O')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('P')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('Q')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('R')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('S')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('T')->setWidth(22);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('U')->setWidth(22);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('V')->setWidth(80);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('W')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('X')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('Y')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('Z')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('AA')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('AB')->setWidth(14);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('AC')->setWidth(14);
|
||||||
|
|
||||||
|
// Инициализация счётчика строк
|
||||||
|
$row = 2;
|
||||||
|
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
// Перебор заявок
|
||||||
|
|
||||||
|
// Инициализация сотрудника
|
||||||
|
$worker = worker::read('d.id == "' . $task->worker . '"');
|
||||||
|
|
||||||
|
if ($worker instanceof _document) {
|
||||||
|
// Найден сотрудник
|
||||||
|
|
||||||
|
// Инициализация магазина
|
||||||
|
$market = market::read('d.id == "' . $task->market . '"');
|
||||||
|
|
||||||
|
if ($market instanceof _document) {
|
||||||
|
// Найден магазин
|
||||||
|
|
||||||
|
// Запись строки
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->setCellValue("A$row", $market->city . ' ' . $market->address)
|
||||||
|
->setCellValue("B$row", '')
|
||||||
|
->setCellValue("C$row", gmdate("d.m.Y", $task->date))
|
||||||
|
->setCellValue("D$row", $market->id)
|
||||||
|
->setCellValue("E$row", $worker->id)
|
||||||
|
->setCellValue("F$row", $task->work)
|
||||||
|
->setCellValue("G$row", $task->start)
|
||||||
|
->setCellValue("H$row", $task->end)
|
||||||
|
->setCellValue("I$row", $hours = task::hours($task->start, $task->end, $errors))
|
||||||
|
->setCellValue("J$row", '')
|
||||||
|
->setCellValue("K$row", $task->rating ?? 'Отсутствует')
|
||||||
|
->setCellValue("L$row", $task->review ?? '')
|
||||||
|
->setCellValue("M$row", $worker->name['second'] . ' ' . $worker->name['first'] . ' ' . $worker->name['last'])
|
||||||
|
->setCellValue("N$row", $hour = static::hour($market->city, $task->work))
|
||||||
|
->setCellValue("O$row", $payment = $hour * $hours)
|
||||||
|
->setCellValue("P$row", ($penalty = static::penalty($task->rating ?? null)) === null ? $payment : $penalty)
|
||||||
|
->setCellValue("Q$row", $bonus = static::bonus($task->rating ?? null))
|
||||||
|
->setCellValue("R$row", $payment + (($penalty = static::penalty($task->rating ?? null)) === null ? $payment : $penalty) + $bonus)
|
||||||
|
->setCellValue("S$row", '')
|
||||||
|
->setCellValue("T$row", $worker->payment) // Наличные?
|
||||||
|
->setCellValue("U$row", '')
|
||||||
|
->setCellValue("V$row", $worker->requisites)
|
||||||
|
->setCellValue("W$row", '')
|
||||||
|
->setCellValue("X$row", '')
|
||||||
|
->setCellValue("Y$row", '')
|
||||||
|
->setCellValue("Z$row", '')
|
||||||
|
->setCellValue("AA$row", '')
|
||||||
|
->setCellValue("AB$row", '')
|
||||||
|
->setCellValue("AC$row", '');
|
||||||
|
|
||||||
|
// Инкрементация счётчика для генерации следующей строки
|
||||||
|
++$row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to output buffer
|
||||||
|
IOFactory::createWriter($spreadsheet, 'Xlsx')->save('php://output');
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new exception('Не найдены заявки');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Write to the errors registry
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Магазины
|
||||||
|
*
|
||||||
|
* Расчитать ... и сгенерировать excel-документ
|
||||||
|
*
|
||||||
|
* @param int $from Начальная дата для выборки заявок (unixtime)
|
||||||
|
* @param int $to Конечная дата для выборки заявок (unixtime)
|
||||||
|
* @param array $errors Errors registry
|
||||||
|
*
|
||||||
|
* @return bool Записан буфер вывода сгенерированный excel-документ?
|
||||||
|
*/
|
||||||
|
public static function markets(int $from, int $to, array &$errors = []): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Чтение заявок
|
||||||
|
$tasks = @task::collect(
|
||||||
|
"d.date >= $from && d.date <= $to && d.problematic == false && d.completed == true",
|
||||||
|
sort: 'd.date DESC',
|
||||||
|
amount: 999999,
|
||||||
|
index: 'd.date',
|
||||||
|
return: '{worker: d.worker, market: d.market, date: d.date, work: d.work, start: d.start, end: d.end, commentary: d.commentary, rating: d.rating, review: d.review}',
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
|
||||||
|
// Универсализация
|
||||||
|
if ($tasks instanceof _document) $tasks = [$tasks];
|
||||||
|
|
||||||
|
// Инициализация буфера объединённых заявок по дате (подразумеваются дни)
|
||||||
|
$merged = [];
|
||||||
|
|
||||||
|
foreach ($tasks as $groups) {
|
||||||
|
// Перебор групп заявок разделённых по датам
|
||||||
|
|
||||||
|
foreach ($groups->getAll() as $date => $_tasks) {
|
||||||
|
// Перебор дат (подразумевается только одна)
|
||||||
|
|
||||||
|
foreach ($_tasks as $task) {
|
||||||
|
// Перебор заявок
|
||||||
|
|
||||||
|
// Первичная инициализация данных в буфере объединённых заявок по дням
|
||||||
|
$merged[$task['market']] ??= [];
|
||||||
|
$merged[$task['market']][$date] ??= [];
|
||||||
|
$merged[$task['market']][$date][$task['work']] ??= ['workers' => 0, 'hours' => 0];
|
||||||
|
|
||||||
|
// Запись в буфер объединённых заявок по дням
|
||||||
|
$merged[$task['market']][$date][$task['work']]['workers']++;
|
||||||
|
$merged[$task['market']][$date][$task['work']]['hours'] += task::hours($task['start'], $task['end'], $errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($merged) > 0) {
|
||||||
|
// Найдены сгенерированные данные
|
||||||
|
|
||||||
|
// Инициализация таблицы
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
// Конвертация unixtime в читаемую дату
|
||||||
|
$_from = gmdate("d.m.Y", $from);
|
||||||
|
$_to = gmdate("d.m.Y", $to);
|
||||||
|
|
||||||
|
// Запись настроек таблицы
|
||||||
|
$spreadsheet
|
||||||
|
->getProperties()
|
||||||
|
->setCreator('Спецресурс')
|
||||||
|
->setLastModifiedBy('Спецресурс')
|
||||||
|
->setTitle("$_from - $_to")
|
||||||
|
->setSubject(" $_from - $_to")
|
||||||
|
->setDescription(" за период с $_from по $_to")
|
||||||
|
->setKeywords('магазины');
|
||||||
|
|
||||||
|
// Открытие страницы
|
||||||
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
|
||||||
|
// Запись первых строк
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->setCellValue('A1', 'К Договору от 0.0.20')
|
||||||
|
->setCellValue('A2', 'К Договору от 0.0.20')
|
||||||
|
->setCellValue('A4', 'Детализация выполненных заказов за период')
|
||||||
|
->setCellValue('A5', "Период: $_from - $_to")
|
||||||
|
->setCellValue('A6', "Заказчик: ")
|
||||||
|
->setCellValue('A8', "Магазин")
|
||||||
|
->setCellValue('B8', "Тип")
|
||||||
|
->setCellValue('C8', "Адрес")
|
||||||
|
->setCellValue('D8', "Дата")
|
||||||
|
->setCellValue('E8', "Работа")
|
||||||
|
->setCellValue('F8', "Сотрудники")
|
||||||
|
->setCellValue('G8', "Часы")
|
||||||
|
->setCellValue('H8', "Тариф")
|
||||||
|
->setCellValue('I8', "Без НДС")
|
||||||
|
->setCellValue('J8', "С НДС");
|
||||||
|
|
||||||
|
// Запись ширины колонок
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('A')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('B')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth(32);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('E')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('F')->setWidth(16);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('G')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('H')->setWidth(12);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('I')->setWidth(18);
|
||||||
|
$spreadsheet->getActiveSheet()->getColumnDimension('J')->setWidth(18);
|
||||||
|
|
||||||
|
// Фиксация верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->freezePane('K9');
|
||||||
|
|
||||||
|
// Объединение ячеек
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A1:J1');
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A2:J2');
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A4:J4');
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A5:J5');
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A6:J6');
|
||||||
|
|
||||||
|
// Запись позиций текстов "к договору"
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A1:J2')
|
||||||
|
->getAlignment()
|
||||||
|
->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
||||||
|
|
||||||
|
// Запись позиций текста заголовка
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A4:J4')
|
||||||
|
->getAlignment()
|
||||||
|
->setHorizontal(Alignment::HORIZONTAL_CENTER)
|
||||||
|
->setVertical(Alignment::VERTICAL_CENTER);
|
||||||
|
|
||||||
|
// Запись позиций текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A8:J8')
|
||||||
|
->getAlignment()
|
||||||
|
->setHorizontal(Alignment::HORIZONTAL_CENTER)
|
||||||
|
->setVertical(Alignment::VERTICAL_CENTER);
|
||||||
|
|
||||||
|
// Запись цвета верхнего колонтинула (левая половина)
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A8:D8')
|
||||||
|
->getFill()
|
||||||
|
->setFillType(Fill::FILL_SOLID)
|
||||||
|
->getStartColor()
|
||||||
|
->setARGB('ffdfe4ec');
|
||||||
|
|
||||||
|
// Запись цвета верхнего колонтинула (правая половина)
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('E8:J8')
|
||||||
|
->getFill()
|
||||||
|
->setFillType(Fill::FILL_SOLID)
|
||||||
|
->getStartColor()
|
||||||
|
->setARGB('ff8093b3');
|
||||||
|
|
||||||
|
// Запись размера текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A8:J8')
|
||||||
|
->getFont()
|
||||||
|
->setSize(12);
|
||||||
|
|
||||||
|
// Запись толщины текста верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle('A8:J8')
|
||||||
|
->getFont()
|
||||||
|
->setBold(true);
|
||||||
|
|
||||||
|
// Запись ширины строки верхнего колонтинула
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getRowDimension(8)
|
||||||
|
->setRowHeight(32);
|
||||||
|
|
||||||
|
// Инициализация счётчика строк
|
||||||
|
$row = 9;
|
||||||
|
|
||||||
|
foreach ($merged as $id => $dates) {
|
||||||
|
// Перебор магазинов
|
||||||
|
|
||||||
|
// Инициализация магазина
|
||||||
|
$market = market::read('d.id == "' . $id . '"');
|
||||||
|
|
||||||
|
if ($market instanceof _document) {
|
||||||
|
// Найден магазин
|
||||||
|
|
||||||
|
// Инициализация буфера объединённых данных магазина
|
||||||
|
$result = [
|
||||||
|
'workers' => 0,
|
||||||
|
'hours' => 0,
|
||||||
|
'hour' => [],
|
||||||
|
'payment' => 0,
|
||||||
|
'vat' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dates as $date => $works) {
|
||||||
|
// Перебор дат заявок
|
||||||
|
|
||||||
|
foreach ($works as $work => $task) {
|
||||||
|
// Перебор заявок
|
||||||
|
|
||||||
|
// Запись строки с заявками по дате
|
||||||
|
$spreadsheet
|
||||||
|
->setActiveSheetIndex(0)
|
||||||
|
->setCellValue("A$row", $id)
|
||||||
|
->setCellValue("B$row", $market->type)
|
||||||
|
->setCellValue("C$row", $market->address)
|
||||||
|
->setCellValue("D$row", gmdate("d.m.Y", $date))
|
||||||
|
->setCellValue("E$row", $work)
|
||||||
|
->setCellValue("F$row", $task['workers'])
|
||||||
|
->setCellValue("G$row", $task['hours'])
|
||||||
|
->setCellValue("H$row", $hour = static::hour($market->city, $work))
|
||||||
|
->setCellValue("I$row", $payment = $hour * $task['hours'])
|
||||||
|
->setCellValue("J$row", $payment);
|
||||||
|
|
||||||
|
// Запись в буфер объединённых данных магазина
|
||||||
|
$result['workers'] += $task['workers'];
|
||||||
|
$result['hours'] += $task['hours'];
|
||||||
|
$result['hour'][] = $hour;
|
||||||
|
$result['payment'] += $payment;
|
||||||
|
$result['vat'] += $payment;
|
||||||
|
|
||||||
|
// Инкрементация счётчика для генерации следующей строки
|
||||||
|
++$row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись строки с общими данными магазина
|
||||||
|
$spreadsheet
|
||||||
|
->setActiveSheetIndex(0)
|
||||||
|
->setCellValue("A$row", "Всего ($id)")
|
||||||
|
->setCellValue("B$row", '')
|
||||||
|
->setCellValue("C$row", '')
|
||||||
|
->setCellValue("D$row", '')
|
||||||
|
->setCellValue("E$row", '')
|
||||||
|
->setCellValue("F$row", $result['workers'])
|
||||||
|
->setCellValue("G$row", $result['hours'])
|
||||||
|
->setCellValue("H$row", array_sum($result['hour']) / count($result['hour']))
|
||||||
|
->setCellValue("I$row", $result['payment'])
|
||||||
|
->setCellValue("J$row", $result['vat']);
|
||||||
|
|
||||||
|
// Запись цвета строки с общими данными магазина
|
||||||
|
$spreadsheet
|
||||||
|
->getActiveSheet()
|
||||||
|
->getStyle("A$row:J$row")
|
||||||
|
->getFill()
|
||||||
|
->setFillType(Fill::FILL_SOLID)
|
||||||
|
->getStartColor()
|
||||||
|
->setARGB('ffdfe4ec');
|
||||||
|
|
||||||
|
++$row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to output buffer
|
||||||
|
IOFactory::createWriter($spreadsheet, 'Xlsx')->save('php://output');
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new exception('Не найдены заявки');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Write to the errors registry
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine tariff
|
||||||
|
*
|
||||||
|
* @param string $city City in which the place of work is located
|
||||||
|
* @param string $work Type of work
|
||||||
|
*
|
||||||
|
* @return int|float Cost of work per hour (rubles)
|
||||||
|
*/
|
||||||
|
public static function hour(string $city, string $work): int|float
|
||||||
|
{
|
||||||
|
return match ($city) {
|
||||||
|
'Красноярск' => match (mb_strtolower($work)) {
|
||||||
|
'cashiers', 'cashier', 'кассиры', 'кассир' => 257.07,
|
||||||
|
'displayers', 'displayer', 'выкладчики', 'выкладчик' => 257.07,
|
||||||
|
'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 257.07,
|
||||||
|
'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360,
|
||||||
|
'loaders', 'loader', 'грузчики', 'грузчик' => 255.645,
|
||||||
|
'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305,
|
||||||
|
'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305,
|
||||||
|
default => 0
|
||||||
|
},
|
||||||
|
'Железногорск', 'Сосновоборск', 'Тыва' => match (mb_strtolower($work)) {
|
||||||
|
'cashiers', 'cashier', 'кассиры', 'кассир' => 263.34,
|
||||||
|
'displayers', 'displayer', 'выкладчики', 'выкладчик' => 263.34,
|
||||||
|
'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 263.34,
|
||||||
|
'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360,
|
||||||
|
'loaders', 'loader', 'грузчики', 'грузчик' => 255.645,
|
||||||
|
'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305,
|
||||||
|
'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305,
|
||||||
|
default => 0
|
||||||
|
},
|
||||||
|
'Хакасия', 'Иркутск' => match (mb_strtolower($work)) {
|
||||||
|
'cashiers', 'cashier', 'кассиры', 'кассир' => 245.385,
|
||||||
|
'displayers', 'displayer', 'выкладчики', 'выкладчик' => 245.385,
|
||||||
|
'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 245.385,
|
||||||
|
'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 360,
|
||||||
|
'loaders', 'loader', 'грузчики', 'грузчик' => 255.645,
|
||||||
|
'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 305,
|
||||||
|
'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 305,
|
||||||
|
default => 0
|
||||||
|
},
|
||||||
|
default => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bonus on task
|
||||||
|
*
|
||||||
|
* @param int $rating Rating of the task from the market
|
||||||
|
*
|
||||||
|
* @return int Bonus (rubles)
|
||||||
|
*/
|
||||||
|
public static function bonus(int $rating): int
|
||||||
|
{
|
||||||
|
return match ($rating) {
|
||||||
|
5 => 100,
|
||||||
|
default => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Penalty on task
|
||||||
|
*
|
||||||
|
* @param int $rating Rating of the task from the market
|
||||||
|
*
|
||||||
|
* @return int|null Penalty (rubles) (null - all payment)
|
||||||
|
*/
|
||||||
|
public static function penalty(int $rating): ?int
|
||||||
|
{
|
||||||
|
return match ($rating) {
|
||||||
|
3 => -100,
|
||||||
|
2 => -500,
|
||||||
|
1 => null,
|
||||||
|
default => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,9 @@ use mirzaev\arangodb\collection,
|
||||||
// Библиотека для ArangoDB
|
// Библиотека для ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
// Встроенные библиотеки
|
// System libraries
|
||||||
use exception;
|
use datetime,
|
||||||
|
exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Модель заданий
|
* Модель заданий
|
||||||
|
@ -176,14 +177,54 @@ final class task extends core
|
||||||
'line' => $e->getLine(),
|
'line' => $e->getLine(),
|
||||||
'stack' => $e->getTrace()
|
'stack' => $e->getTrace()
|
||||||
];
|
];
|
||||||
|
|
||||||
var_dump($errors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit (fail)
|
// Exit (fail)
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Посчитать количество часов работы
|
||||||
|
*
|
||||||
|
* @param string $start Начало работы (H:i)
|
||||||
|
* @param string $end Конец работы (H:i)
|
||||||
|
* @param array $errors Errors registry
|
||||||
|
*
|
||||||
|
* @return ?float Количество часов, если удалось расчитать
|
||||||
|
*/
|
||||||
|
public static function hours(string $start, string $end, array &$errors = []): ?float
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!empty($start = datetime::createFromFormat('H:i', (string) $start)) && $start instanceof datetime
|
||||||
|
&& !empty($end = datetime::createFromFormat('H:i', (string) $end)) && $end instanceof datetime
|
||||||
|
) {
|
||||||
|
// Инициализированы $start и $end
|
||||||
|
|
||||||
|
// Расчёт часов работы
|
||||||
|
$hours = (float) $start->diff($end)->format('%R%H.%i');
|
||||||
|
if ($hours < 0) $hours += 24;
|
||||||
|
if ($hours >= 6.5 && $hours < 9) $hours -= 0.5;
|
||||||
|
else if ($hours >= 9 && $hours < 12.5) $hours -= 1;
|
||||||
|
else if ($hours >= 12.5) $hours -= 1.5;
|
||||||
|
|
||||||
|
// Выход (успех)
|
||||||
|
return $hours;
|
||||||
|
}
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Write to the errors registry
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выход (провал)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate work type label in Russian
|
* Generate work type label in Russian
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,7 +18,7 @@ section.panel.list.medium {
|
||||||
}
|
}
|
||||||
|
|
||||||
section.panel.list > :is(form, search).row.menu {
|
section.panel.list > :is(form, search).row.menu {
|
||||||
margin-bottom: 10px;%s"
|
margin-bottom: 10px;
|
||||||
transition: 0s;
|
transition: 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ section#tasks.panel.list
|
||||||
> span:is(
|
> span:is(
|
||||||
[data-column="worker"],
|
[data-column="worker"],
|
||||||
[data-column="name"],
|
[data-column="name"],
|
||||||
[data-column="task"],
|
[data-column="work"],
|
||||||
[data-column="address"],
|
[data-column="address"],
|
||||||
[data-column="type"],
|
[data-column="type"],
|
||||||
[data-column="tax"],
|
[data-column="tax"],
|
||||||
|
@ -61,14 +61,14 @@ section#tasks.panel.list > div.row > span[data-column="name"] {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#tasks.panel.list > div.row > span[data-column="task"] {
|
section#tasks.panel.list > div.row > span[data-column="work"] {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#tasks.panel.list
|
section#tasks.panel.list
|
||||||
> div.row:not(:nth-of-type(1))
|
> div.row:not(:nth-of-type(1))
|
||||||
> span[data-column="task"] {
|
> span[data-column="work"] {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,8 @@ $router->write('/task/$task/unpublish', 'task', 'unpublish', 'POST');
|
||||||
$router->write('/task/$task/chat', 'task', 'chat', 'POST');
|
$router->write('/task/$task/chat', 'task', 'chat', 'POST');
|
||||||
$router->write('/task/$task/chat/send', 'task', 'message', 'POST');
|
$router->write('/task/$task/chat/send', 'task', 'message', 'POST');
|
||||||
$router->write('/elements/menu', 'index', 'menu', 'POST');
|
$router->write('/elements/menu', 'index', 'menu', 'POST');
|
||||||
|
$router->write('/payments/workers', 'payments', 'workers', 'POST');
|
||||||
|
$router->write('/payments/markets', 'payments', 'markets', 'POST');
|
||||||
|
|
||||||
// Инициализация ядра
|
// Инициализация ядра
|
||||||
$core = new core(namespace: __NAMESPACE__, router: $router, controller: new controller(false), model: new model(false));
|
$core = new core(namespace: __NAMESPACE__, router: $router, controller: new controller(false), model: new model(false));
|
||||||
|
|
|
@ -13,7 +13,7 @@ if (typeof window.buffer !== "function") {
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static write(name, value) {
|
static async write(name, value) {
|
||||||
if (
|
if (
|
||||||
typeof core === "function" && typeof name === "string" &&
|
typeof core === "function" && typeof name === "string" &&
|
||||||
(typeof value === "string" || typeof value === "number")
|
(typeof value === "string" || typeof value === "number")
|
||||||
|
@ -36,7 +36,7 @@ if (typeof window.buffer !== "function") {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Запрос к серверу для записи в сессию (базу данных)
|
// Запрос к серверу для записи в сессию (базу данных)
|
||||||
return fetch("/session/write", {
|
return await fetch("/session/write", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
|
|
@ -577,7 +577,7 @@ if (typeof window.chat !== "function") {
|
||||||
* @param {string} chat Тип чата (market, worker, both)
|
* @param {string} chat Тип чата (market, worker, both)
|
||||||
* @param {bool} scroll Прокрутить до последнего сообщения?
|
* @param {bool} scroll Прокрутить до последнего сообщения?
|
||||||
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
|
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
|
||||||
* @param {string} chat Тип чата (market, worker)
|
* @param {bool} force Принудительное выполнение (используется в damper())
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
|
@ -601,7 +601,7 @@ if (typeof window.chat !== "function") {
|
||||||
* @param {string} chat Тип чата (market, worker, both)
|
* @param {string} chat Тип чата (market, worker, both)
|
||||||
* @param {bool} scroll Прокрутить до последнего сообщения?
|
* @param {bool} scroll Прокрутить до последнего сообщения?
|
||||||
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
|
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
|
||||||
* @param {bool} force Принудительное выполнение (используется в damper()
|
* @param {bool} force Принудительное выполнение (используется в damper())
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,13 +20,13 @@ function damper(func, timeout = 300, force) {
|
||||||
if (typeof force === 'number' && args[force]) {
|
if (typeof force === 'number' && args[force]) {
|
||||||
// Принудительное выполнение (игнорировать таймер)
|
// Принудительное выполнение (игнорировать таймер)
|
||||||
|
|
||||||
func.apply(this, args);
|
return func.apply(this, args);
|
||||||
} else {
|
} else {
|
||||||
// Обычное выполнение
|
// Обычное выполнение
|
||||||
|
|
||||||
// Вызов функции (вход в рекурсию)
|
// Вызов функции (вход в рекурсию)
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
func.apply(this, args);
|
return func.apply(this, args);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
if (typeof window.payments !== "function") {
|
||||||
|
// Not initialized
|
||||||
|
|
||||||
|
// Initialize of the class in global namespace
|
||||||
|
window.payments = class payments {
|
||||||
|
/**
|
||||||
|
* Сотрудники
|
||||||
|
*
|
||||||
|
* Сгенерировать и скачать excel-документ с зарплатами сотрудников за выбранный период (cookies или session storage)
|
||||||
|
*
|
||||||
|
* @return {void} Вызывает функцию скачивания в браузере
|
||||||
|
*/
|
||||||
|
static workers = damper(() => {
|
||||||
|
// Инициализация оболочки фильтров
|
||||||
|
const filters = document.getElementById("filters").children[0];
|
||||||
|
|
||||||
|
tasks
|
||||||
|
.filter("from", new Date(filters.children[0].value) / 1000, null, true)
|
||||||
|
.then(() => {
|
||||||
|
tasks
|
||||||
|
.filter(
|
||||||
|
"to",
|
||||||
|
new Date(filters.children[1].value) / 1000,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
// Запрос к серверу
|
||||||
|
fetch("/payments/workers", { method: "POST" }).then(
|
||||||
|
(response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
// Сервер вернул код успешного выполнения
|
||||||
|
|
||||||
|
response
|
||||||
|
.clone()
|
||||||
|
.json()
|
||||||
|
.then(
|
||||||
|
(data) => {
|
||||||
|
if (this.errors(data.errors)) {
|
||||||
|
// Сгенерированы ошибки
|
||||||
|
} else {
|
||||||
|
// Не сгенерированы ошибки (подразумевается их отсутствие)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Инициализация имени файла
|
||||||
|
const header = response.headers.get(
|
||||||
|
"Content-Disposition",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (header !== null) {
|
||||||
|
// Найден заголовок (подразумевается, что передан файл, а не случайная ошибка)
|
||||||
|
|
||||||
|
// Инициализация названия файла
|
||||||
|
const name = header.split(";")[1].split("=")[1];
|
||||||
|
|
||||||
|
// Чтение полученного файла (подразумевается ошибка при инициализации json)
|
||||||
|
response.blob().then((blob) => {
|
||||||
|
// Инициализация временного элемента для скачивания с именем файла (хак)
|
||||||
|
const element = document.createElement("a");
|
||||||
|
element.href = window.URL.createObjectURL(blob);
|
||||||
|
element.download = name;
|
||||||
|
element.style.setProperty("display", "none");
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
// Скачивание файла
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
// Деинициализация временного элемента
|
||||||
|
element.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Магазины
|
||||||
|
*
|
||||||
|
* Сгенерировать и скачать excel-документ со ... (сверкой?) за выбранный период (cookies или session storage)
|
||||||
|
*
|
||||||
|
* @return {void} Вызывает функцию скачивания в браузере
|
||||||
|
*/
|
||||||
|
static markets = damper(() => {
|
||||||
|
// Инициализация оболочки фильтров
|
||||||
|
const filters = document.getElementById("filters").children[0];
|
||||||
|
|
||||||
|
tasks
|
||||||
|
.filter("from", new Date(filters.children[0].value) / 1000, null, true)
|
||||||
|
.then(() => {
|
||||||
|
tasks
|
||||||
|
.filter(
|
||||||
|
"to",
|
||||||
|
new Date(filters.children[1].value) / 1000,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
// Запрос к серверу
|
||||||
|
fetch("/payments/markets", { method: "POST" }).then(
|
||||||
|
(response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
// Сервер вернул код успешного выполнения
|
||||||
|
|
||||||
|
response
|
||||||
|
.clone()
|
||||||
|
.json()
|
||||||
|
.then(
|
||||||
|
(data) => {
|
||||||
|
if (this.errors(data.errors)) {
|
||||||
|
// Сгенерированы ошибки
|
||||||
|
} else {
|
||||||
|
// Не сгенерированы ошибки (подразумевается их отсутствие)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Инициализация имени файла
|
||||||
|
const header = response.headers.get(
|
||||||
|
"Content-Disposition",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (header !== null) {
|
||||||
|
// Найден заголовок (подразумевается, что передан файл, а не случайная ошибка)
|
||||||
|
|
||||||
|
// Инициализация названия файла
|
||||||
|
const name = header.split(";")[1].split("=")[1];
|
||||||
|
|
||||||
|
// Чтение полученного файла (подразумевается ошибка при инициализации json)
|
||||||
|
response.blob().then((blob) => {
|
||||||
|
// Инициализация временного элемента для скачивания с именем файла (хак)
|
||||||
|
const element = document.createElement("a");
|
||||||
|
element.href = window.URL.createObjectURL(blob);
|
||||||
|
element.download = name;
|
||||||
|
element.style.setProperty("display", "none");
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
// Скачивание файла
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
// Деинициализация временного элемента
|
||||||
|
element.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сгенерировать HTML-элемент со списком ошибок
|
||||||
|
*
|
||||||
|
* @param {object} registry Реестр ошибок
|
||||||
|
* @param {bool} render Отобразить в окне с ошибками?
|
||||||
|
* @param {bool} clean Очистить окно с ошибками перед добавлением?
|
||||||
|
*
|
||||||
|
* @return {bool} Сгенерированы ошибки?
|
||||||
|
*
|
||||||
|
* @TODO Переделать под показ ошибок где-нибудь
|
||||||
|
*/
|
||||||
|
static errors(registry, render = true, clean = true) {
|
||||||
|
// Инициализация ссылки на HTML-элемент с ошибками
|
||||||
|
const wrap = document.body.contains(this.body.errors)
|
||||||
|
? this.body.errors
|
||||||
|
: document.querySelector('[data-errors="true"]');
|
||||||
|
|
||||||
|
if (wrap instanceof HTMLElement && document.body.contains(wrap)) {
|
||||||
|
// Найден HTML-элемент с ошибками
|
||||||
|
|
||||||
|
// Перерасчёт высоты элемента
|
||||||
|
function height() {
|
||||||
|
wrap.classList.remove("hidden");
|
||||||
|
wrap.classList.remove("animation");
|
||||||
|
// Реинициализация переменной с данными о высоте HTML-элемента (16 - это padding-top + padding-bottom у div#popup > section.errors)
|
||||||
|
wrap.style.setProperty("--height", wrap.offsetHeight - 16 + "px");
|
||||||
|
wrap.classList.add("animation");
|
||||||
|
wrap.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация элемента-списка ошибок
|
||||||
|
const list = wrap.getElementsByTagName("dl")[0];
|
||||||
|
|
||||||
|
// Удаление ошибок из прошлой генерации
|
||||||
|
if (clean) list.innerHTML = null;
|
||||||
|
|
||||||
|
for (const error in registry) {
|
||||||
|
// Генерация HTML-элементов с текстами ошибок
|
||||||
|
|
||||||
|
// Инициализация HTML-элемента текста ошибки
|
||||||
|
const samp = document.createElement("samp");
|
||||||
|
|
||||||
|
if (typeof registry[error] === "object") {
|
||||||
|
// Категория ошибок
|
||||||
|
|
||||||
|
// Проверка наличия ошибок
|
||||||
|
if (registry[error].length === 0) continue;
|
||||||
|
|
||||||
|
// Инициализация HTML-элемента-оболочки
|
||||||
|
const wrap = document.createElement("dt");
|
||||||
|
|
||||||
|
// Запись текста категории
|
||||||
|
samp.innerText = error;
|
||||||
|
|
||||||
|
// Запись HTML-элементов в список
|
||||||
|
wrap.appendChild(samp);
|
||||||
|
list.appendChild(wrap);
|
||||||
|
|
||||||
|
// Реинициализация высоты
|
||||||
|
height();
|
||||||
|
|
||||||
|
// Обработка вложенных ошибок (вход в рекурсию)
|
||||||
|
this.errors(registry[error], false);
|
||||||
|
} else {
|
||||||
|
// Текст ошибки (подразумевается)
|
||||||
|
|
||||||
|
// Инициализация HTML-элемента
|
||||||
|
const wrap = document.createElement("dd");
|
||||||
|
|
||||||
|
// Запись текста ошибки
|
||||||
|
samp.innerText = registry[error];
|
||||||
|
|
||||||
|
// Запись HTML-элемента в список
|
||||||
|
wrap.appendChild(samp);
|
||||||
|
list.appendChild(wrap);
|
||||||
|
|
||||||
|
// Реинициализация высоты
|
||||||
|
height();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
// Запрошена отрисовка
|
||||||
|
|
||||||
|
if (list.childElementCount !== 0) {
|
||||||
|
// Найдены ошибки
|
||||||
|
|
||||||
|
// Сброс анимации
|
||||||
|
// УЛОВКА: таким образом не запускается анимация до взаимодействия с элементом (исправлял это в CSS, но не помню как)
|
||||||
|
wrap.classList.add("animation");
|
||||||
|
|
||||||
|
// Отображение
|
||||||
|
wrap.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
// Не найдены ошибки
|
||||||
|
|
||||||
|
// Скрытие
|
||||||
|
wrap.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.childElementCount === 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызов события: "инициализировано"
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent("payments.initialized", {
|
||||||
|
detail: { payments: window.payments },
|
||||||
|
}),
|
||||||
|
);
|
|
@ -48,119 +48,157 @@ if (typeof window.tasks !== "function") {
|
||||||
// Кассиры получены
|
// Кассиры получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < cashiers.childElementCount; i += 2)
|
for (let i = 1; i < cashiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
cashiers.children[i].children[0].children instanceof HTMLCollection
|
cashiers.children[i].children[0].children instanceof HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of cashiers.children[i].children[0].children)
|
for (const element of cashiers.children[i].children[0].children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < cashiers.childElementCount; i += 2)
|
}
|
||||||
if (cashiers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < cashiers.childElementCount; i += 2) {
|
||||||
|
if (cashiers.children[i].children[0] instanceof HTMLElement) {
|
||||||
cashiers.children[i].children[0].setAttribute("disabled", true);
|
cashiers.children[i].children[0].setAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (displayers instanceof HTMLElement) {
|
if (displayers instanceof HTMLElement) {
|
||||||
// Выкладчики получены
|
// Выкладчики получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < displayers.childElementCount; i += 2)
|
for (let i = 1; i < displayers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
displayers.children[i].children[0].children instanceof
|
displayers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of displayers.children[i].children[0].children)
|
for (const element of displayers.children[i].children[0].children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < displayers.childElementCount; i += 2)
|
}
|
||||||
if (displayers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < displayers.childElementCount; i += 2) {
|
||||||
|
if (displayers.children[i].children[0] instanceof HTMLElement) {
|
||||||
displayers.children[i].children[0].setAttribute("disabled", true);
|
displayers.children[i].children[0].setAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gastronomes instanceof HTMLElement) {
|
if (gastronomes instanceof HTMLElement) {
|
||||||
// Гастрономы получены
|
// Гастрономы получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < gastronomes.childElementCount; i += 2)
|
for (let i = 1; i < gastronomes.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
gastronomes.children[i].children[0].children instanceof
|
gastronomes.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of gastronomes.children[i].children[0].children)
|
for (const element of gastronomes.children[i].children[0]
|
||||||
|
.children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < gastronomes.childElementCount; i += 2)
|
}
|
||||||
if (gastronomes.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < gastronomes.childElementCount; i += 2) {
|
||||||
|
if (gastronomes.children[i].children[0] instanceof HTMLElement) {
|
||||||
gastronomes.children[i].children[0].setAttribute("disabled", true);
|
gastronomes.children[i].children[0].setAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (brigadiers instanceof HTMLElement) {
|
if (brigadiers instanceof HTMLElement) {
|
||||||
// Бригадиры получены
|
// Бригадиры получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < brigadiers.childElementCount; i += 2)
|
for (let i = 1; i < brigadiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
brigadiers.children[i].children[0].children instanceof
|
brigadiers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of brigadiers.children[i].children[0].children)
|
for (const element of brigadiers.children[i].children[0].children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < brigadiers.childElementCount; i += 2)
|
}
|
||||||
if (brigadiers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < brigadiers.childElementCount; i += 2) {
|
||||||
|
if (brigadiers.children[i].children[0] instanceof HTMLElement) {
|
||||||
brigadiers.children[i].children[0].setAttribute("disabled", true);
|
brigadiers.children[i].children[0].setAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders instanceof HTMLElement) {
|
if (loaders instanceof HTMLElement) {
|
||||||
// Грузчики получены
|
// Грузчики получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < loaders.childElementCount; i += 2)
|
for (let i = 1; i < loaders.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders.children[i].children[0].children instanceof HTMLCollection
|
loaders.children[i].children[0].children instanceof HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of loaders.children[i].children[0].children)
|
for (const element of loaders.children[i].children[0].children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < loaders.childElementCount; i += 2)
|
}
|
||||||
if (loaders.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < loaders.childElementCount; i += 2) {
|
||||||
|
if (loaders.children[i].children[0] instanceof HTMLElement) {
|
||||||
loaders.children[i].children[0].setAttribute("disabled", true);
|
loaders.children[i].children[0].setAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders_mobile instanceof HTMLElement) {
|
if (loaders_mobile instanceof HTMLElement) {
|
||||||
// Мобильные грузчики получены
|
// Мобильные грузчики получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < loaders_mobile.childElementCount; i += 2)
|
for (let i = 1; i < loaders_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders_mobile.children[i].children[0].children instanceof
|
loaders_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of loaders_mobile.children[i].children[0]
|
for (const element of loaders_mobile.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < loaders_mobile.childElementCount; i += 2)
|
}
|
||||||
if (loaders_mobile.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < loaders_mobile.childElementCount; i += 2) {
|
||||||
|
if (loaders_mobile.children[i].children[0] instanceof HTMLElement) {
|
||||||
loaders_mobile.children[i].children[0].setAttribute(
|
loaders_mobile.children[i].children[0].setAttribute(
|
||||||
"disabled",
|
"disabled",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (universals_mobile instanceof HTMLElement) {
|
if (universals_mobile instanceof HTMLElement) {
|
||||||
// Мобильные универсалы получены
|
// Мобильные универсалы получены
|
||||||
|
|
||||||
// Блокировка полей ввода
|
// Блокировка полей ввода
|
||||||
for (let i = 1; i < universals_mobile.childElementCount; i += 2)
|
for (let i = 1; i < universals_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
universals_mobile.children[i].children[0].children instanceof
|
universals_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of universals_mobile.children[i].children[0]
|
for (const element of universals_mobile.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.setAttribute("disabled", true);
|
element.setAttribute("disabled", true);
|
||||||
for (let i = 2; i < universals_mobile.childElementCount; i += 2)
|
}
|
||||||
if (universals_mobile.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < universals_mobile.childElementCount; i += 2) {
|
||||||
|
if (
|
||||||
|
universals_mobile.children[i].children[0] instanceof HTMLElement
|
||||||
|
) {
|
||||||
universals_mobile.children[i].children[0].setAttribute(
|
universals_mobile.children[i].children[0].setAttribute(
|
||||||
"disabled",
|
"disabled",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Блокировка кнопки
|
// Блокировка кнопки
|
||||||
button.setAttribute("disabled", true);
|
button.setAttribute("disabled", true);
|
||||||
|
@ -209,124 +247,163 @@ if (typeof window.tasks !== "function") {
|
||||||
// Кассиры получены
|
// Кассиры получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < cashiers.childElementCount; i += 2)
|
for (let i = 1; i < cashiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
cashiers.children[i].children[0].children instanceof
|
cashiers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of cashiers.children[i].children[0].children)
|
for (const element of cashiers.children[i].children[0]
|
||||||
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < cashiers.childElementCount; i += 2)
|
}
|
||||||
if (cashiers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < cashiers.childElementCount; i += 2) {
|
||||||
|
if (cashiers.children[i].children[0] instanceof HTMLElement) {
|
||||||
cashiers.children[i].children[0].removeAttribute("disabled");
|
cashiers.children[i].children[0].removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (displayers instanceof HTMLElement) {
|
if (displayers instanceof HTMLElement) {
|
||||||
// Выкладчики получены
|
// Выкладчики получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < displayers.childElementCount; i += 2)
|
for (let i = 1; i < displayers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
displayers.children[i].children[0].children instanceof
|
displayers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of displayers.children[i].children[0]
|
for (const element of displayers.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < displayers.childElementCount; i += 2)
|
}
|
||||||
if (displayers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < displayers.childElementCount; i += 2) {
|
||||||
|
if (displayers.children[i].children[0] instanceof HTMLElement) {
|
||||||
displayers.children[i].children[0].removeAttribute("disabled");
|
displayers.children[i].children[0].removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gastronomes instanceof HTMLElement) {
|
if (gastronomes instanceof HTMLElement) {
|
||||||
// Гастрономы получены
|
// Гастрономы получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < gastronomes.childElementCount; i += 2)
|
for (let i = 1; i < gastronomes.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
gastronomes.children[i].children[0].children instanceof
|
gastronomes.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of gastronomes.children[i].children[0]
|
for (const element of gastronomes.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < gastronomes.childElementCount; i += 2)
|
}
|
||||||
if (gastronomes.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < gastronomes.childElementCount; i += 2) {
|
||||||
|
if (gastronomes.children[i].children[0] instanceof HTMLElement) {
|
||||||
gastronomes.children[i].children[0].removeAttribute("disabled");
|
gastronomes.children[i].children[0].removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (brigadiers instanceof HTMLElement) {
|
if (brigadiers instanceof HTMLElement) {
|
||||||
// Бригадиры получены
|
// Бригадиры получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < brigadiers.childElementCount; i += 2)
|
for (let i = 1; i < brigadiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
brigadiers.children[i].children[0].children instanceof
|
brigadiers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of brigadiers.children[i].children[0]
|
for (const element of brigadiers.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < brigadiers.childElementCount; i += 2)
|
}
|
||||||
if (brigadiers.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < brigadiers.childElementCount; i += 2) {
|
||||||
|
if (brigadiers.children[i].children[0] instanceof HTMLElement) {
|
||||||
brigadiers.children[i].children[0].removeAttribute("disabled");
|
brigadiers.children[i].children[0].removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders instanceof HTMLElement) {
|
if (loaders instanceof HTMLElement) {
|
||||||
// Грузчики получены
|
// Грузчики получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < loaders.childElementCount; i += 2)
|
for (let i = 1; i < loaders.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders.children[i].children[0].children instanceof
|
loaders.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of loaders.children[i].children[0].children)
|
for (const element of loaders.children[i].children[0]
|
||||||
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < loaders.childElementCount; i += 2)
|
}
|
||||||
if (loaders.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < loaders.childElementCount; i += 2) {
|
||||||
|
if (loaders.children[i].children[0] instanceof HTMLElement) {
|
||||||
loaders.children[i].children[0].removeAttribute("disabled");
|
loaders.children[i].children[0].removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders_mobile instanceof HTMLElement) {
|
if (loaders_mobile instanceof HTMLElement) {
|
||||||
// Мобильные грузчики получены
|
// Мобильные грузчики получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < loaders_mobile.childElementCount; i += 2)
|
for (let i = 1; i < loaders_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders_mobile.children[i].children[0].children instanceof
|
loaders_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of loaders_mobile.children[i].children[0]
|
for (const element of loaders_mobile.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < loaders_mobile.childElementCount; i += 2)
|
}
|
||||||
if (loaders_mobile.children[i].children[0] instanceof HTMLElement)
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < loaders_mobile.childElementCount; i += 2) {
|
||||||
|
if (
|
||||||
|
loaders_mobile.children[i].children[0] instanceof HTMLElement
|
||||||
|
) {
|
||||||
loaders_mobile.children[i].children[0].removeAttribute(
|
loaders_mobile.children[i].children[0].removeAttribute(
|
||||||
"disabled"
|
"disabled"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (universals_mobile instanceof HTMLElement) {
|
if (universals_mobile instanceof HTMLElement) {
|
||||||
// Мобильные универсалы получены
|
// Мобильные универсалы получены
|
||||||
|
|
||||||
// Разблокировка полей ввода
|
// Разблокировка полей ввода
|
||||||
for (let i = 1; i < universals_mobile.childElementCount; i += 2)
|
for (let i = 1; i < universals_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
universals_mobile.children[i].children[0].children instanceof
|
universals_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
for (const element of universals_mobile.children[i].children[0]
|
for (const element of universals_mobile.children[i].children[0]
|
||||||
.children)
|
.children) {
|
||||||
element.removeAttribute("disabled");
|
element.removeAttribute("disabled");
|
||||||
for (let i = 2; i < universals_mobile.childElementCount; i += 2)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 2; i < universals_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
universals_mobile.children[i].children[0] instanceof HTMLElement
|
universals_mobile.children[i].children[0] instanceof HTMLElement
|
||||||
)
|
) {
|
||||||
universals_mobile.children[i].children[0].removeAttribute(
|
universals_mobile.children[i].children[0].removeAttribute(
|
||||||
"disabled"
|
"disabled"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Разблокировка кнопки
|
// Разблокировка кнопки
|
||||||
button.removeAttribute("disabled");
|
button.removeAttribute("disabled");
|
||||||
|
@ -348,11 +425,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.cashiers = [];
|
body.cashiers = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < cashiers.childElementCount; i += 2)
|
for (let i = 1; i < cashiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
cashiers.children[i].children[0].children instanceof
|
cashiers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.cashiers.push({
|
body.cashiers.push({
|
||||||
start:
|
start:
|
||||||
cashiers.children[i].children[0].children[0].value ?? null,
|
cashiers.children[i].children[0].children[0].value ?? null,
|
||||||
|
@ -363,6 +440,8 @@ if (typeof window.tasks !== "function") {
|
||||||
commentary: cashiers.children[i + 1].children[0].value ?? null,
|
commentary: cashiers.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (displayers instanceof HTMLElement) {
|
if (displayers instanceof HTMLElement) {
|
||||||
// Выкладчики получены
|
// Выкладчики получены
|
||||||
|
@ -371,11 +450,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.displayers = [];
|
body.displayers = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < displayers.childElementCount; i += 2)
|
for (let i = 1; i < displayers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
displayers.children[i].children[0].children instanceof
|
displayers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.displayers.push({
|
body.displayers.push({
|
||||||
start:
|
start:
|
||||||
displayers.children[i].children[0].children[0].value ?? null,
|
displayers.children[i].children[0].children[0].value ?? null,
|
||||||
|
@ -388,6 +467,8 @@ if (typeof window.tasks !== "function") {
|
||||||
displayers.children[i + 1].children[0].value ?? null,
|
displayers.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gastronomes instanceof HTMLElement) {
|
if (gastronomes instanceof HTMLElement) {
|
||||||
// Гастрономы получены
|
// Гастрономы получены
|
||||||
|
@ -396,11 +477,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.gastronomes = [];
|
body.gastronomes = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < gastronomes.childElementCount; i += 2)
|
for (let i = 1; i < gastronomes.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
gastronomes.children[i].children[0].children instanceof
|
gastronomes.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.gastronomes.push({
|
body.gastronomes.push({
|
||||||
start:
|
start:
|
||||||
gastronomes.children[i].children[0].children[0].value ?? null,
|
gastronomes.children[i].children[0].children[0].value ?? null,
|
||||||
|
@ -413,6 +494,8 @@ if (typeof window.tasks !== "function") {
|
||||||
gastronomes.children[i + 1].children[0].value ?? null,
|
gastronomes.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (brigadiers instanceof HTMLElement) {
|
if (brigadiers instanceof HTMLElement) {
|
||||||
// Бригадиры получены
|
// Бригадиры получены
|
||||||
|
@ -421,11 +504,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.brigadiers = [];
|
body.brigadiers = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < brigadiers.childElementCount; i += 2)
|
for (let i = 1; i < brigadiers.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
brigadiers.children[i].children[0].children instanceof
|
brigadiers.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.brigadiers.push({
|
body.brigadiers.push({
|
||||||
start:
|
start:
|
||||||
brigadiers.children[i].children[0].children[0].value ?? null,
|
brigadiers.children[i].children[0].children[0].value ?? null,
|
||||||
|
@ -438,6 +521,8 @@ if (typeof window.tasks !== "function") {
|
||||||
brigadiers.children[i + 1].children[0].value ?? null,
|
brigadiers.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders instanceof HTMLElement) {
|
if (loaders instanceof HTMLElement) {
|
||||||
// Грузчики получены
|
// Грузчики получены
|
||||||
|
@ -446,10 +531,10 @@ if (typeof window.tasks !== "function") {
|
||||||
body.loaders = [];
|
body.loaders = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < loaders.childElementCount; i += 2)
|
for (let i = 1; i < loaders.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders.children[i].children[0].children instanceof HTMLCollection
|
loaders.children[i].children[0].children instanceof HTMLCollection
|
||||||
)
|
) {
|
||||||
body.loaders.push({
|
body.loaders.push({
|
||||||
start:
|
start:
|
||||||
loaders.children[i].children[0].children[0].value ?? null,
|
loaders.children[i].children[0].children[0].value ?? null,
|
||||||
|
@ -458,6 +543,8 @@ if (typeof window.tasks !== "function") {
|
||||||
commentary: loaders.children[i + 1].children[0].value ?? null,
|
commentary: loaders.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loaders_mobile instanceof HTMLElement) {
|
if (loaders_mobile instanceof HTMLElement) {
|
||||||
// Мобильные грузчики получены
|
// Мобильные грузчики получены
|
||||||
|
@ -466,11 +553,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.loaders_mobile = [];
|
body.loaders_mobile = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < loaders_mobile.childElementCount; i += 2)
|
for (let i = 1; i < loaders_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
loaders_mobile.children[i].children[0].children instanceof
|
loaders_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.loaders_mobile.push({
|
body.loaders_mobile.push({
|
||||||
start:
|
start:
|
||||||
loaders_mobile.children[i].children[0].children[0].value ??
|
loaders_mobile.children[i].children[0].children[0].value ??
|
||||||
|
@ -485,6 +572,8 @@ if (typeof window.tasks !== "function") {
|
||||||
loaders_mobile.children[i + 1].children[0].value ?? null,
|
loaders_mobile.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (universals_mobile instanceof HTMLElement) {
|
if (universals_mobile instanceof HTMLElement) {
|
||||||
// Мобильные универсалы получены
|
// Мобильные универсалы получены
|
||||||
|
@ -493,11 +582,11 @@ if (typeof window.tasks !== "function") {
|
||||||
body.universals_mobile = [];
|
body.universals_mobile = [];
|
||||||
|
|
||||||
// Запись в буфер JSON для отправки
|
// Запись в буфер JSON для отправки
|
||||||
for (let i = 1; i < universals_mobile.childElementCount; i += 2)
|
for (let i = 1; i < universals_mobile.childElementCount; i += 2) {
|
||||||
if (
|
if (
|
||||||
universals_mobile.children[i].children[0].children instanceof
|
universals_mobile.children[i].children[0].children instanceof
|
||||||
HTMLCollection
|
HTMLCollection
|
||||||
)
|
) {
|
||||||
body.universals_mobile.push({
|
body.universals_mobile.push({
|
||||||
start:
|
start:
|
||||||
universals_mobile.children[i].children[0].children[0].value ??
|
universals_mobile.children[i].children[0].children[0].value ??
|
||||||
|
@ -512,6 +601,8 @@ if (typeof window.tasks !== "function") {
|
||||||
universals_mobile.children[i + 1].children[0].value ?? null,
|
universals_mobile.children[i + 1].children[0].value ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Запрос к серверу
|
// Запрос к серверу
|
||||||
return await fetch("/tasks/create", {
|
return await fetch("/tasks/create", {
|
||||||
|
@ -1746,10 +1837,13 @@ if (typeof window.tasks !== "function") {
|
||||||
* @param {string} name Название
|
* @param {string} name Название
|
||||||
* @param {string|number|null} value Значение
|
* @param {string|number|null} value Значение
|
||||||
* @param {HTMLElement|null} button Кнопка
|
* @param {HTMLElement|null} button Кнопка
|
||||||
|
* @param {bool} force Принудительное выполнение (используется в damper())
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
static filter = damper(async (name, value, button) => {
|
static filter = damper(
|
||||||
|
async (name, value, button, force = false) => {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
if (typeof name === "string") {
|
if (typeof name === "string") {
|
||||||
// Получено название
|
// Получено название
|
||||||
|
|
||||||
|
@ -1760,7 +1854,9 @@ if (typeof window.tasks !== "function") {
|
||||||
// Получено значение
|
// Получено значение
|
||||||
|
|
||||||
// Запись нового значения
|
// Запись нового значения
|
||||||
buffer.write(path, value);
|
await buffer.write(path, value);
|
||||||
|
|
||||||
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
// Не получено значение
|
// Не получено значение
|
||||||
|
|
||||||
|
@ -1771,7 +1867,9 @@ if (typeof window.tasks !== "function") {
|
||||||
if (isNaN(value)) value = 0;
|
if (isNaN(value)) value = 0;
|
||||||
|
|
||||||
// Запись нового значения (инвертирование)
|
// Запись нового значения (инвертирование)
|
||||||
buffer.write(path, ++value < 3 ? value : 0);
|
await buffer.write(path, ++value < 3 ? value : 0);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (button instanceof HTMLElement) {
|
if (button instanceof HTMLElement) {
|
||||||
// Получена кнопка
|
// Получена кнопка
|
||||||
|
@ -1786,7 +1884,13 @@ if (typeof window.tasks !== "function") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 300);
|
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
300,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Записать фильтр поиска
|
* Записать фильтр поиска
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
row.worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.last is not empty %} {{
|
row.worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.last is not empty %} {{
|
||||||
row.worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.second is not empty %} {{
|
row.worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.second is not empty %} {{
|
||||||
row.worker.name.second }}{% endif %}</span>
|
row.worker.name.second }}{% endif %}</span>
|
||||||
<span class="unselectable interactive" data-column="task" title="{{ row.task.description }}">{{ row.task.work
|
<span class="unselectable interactive" data-column="work" title="{{ row.task.description }}">{{ row.task.work
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="unselectable interactive" data-column="start">{{
|
<span class="unselectable interactive" data-column="start">{{
|
||||||
row.task.generated.start }}</span>
|
row.task.generated.start }}</span>
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
<button class="grass dense" onclick="tasks.create()">Создать</button>
|
<button class="grass dense" onclick="tasks.create()">Создать</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if account.type == 'administrator' or account.type == 'operator' %}
|
{% if account.type == 'administrator' or account.type == 'operator' %}
|
||||||
<button class="sea" onclick="">Выгрузка</button>
|
<button class="sea" onclick="payments.workers()">Зарплата</button>
|
||||||
|
<button class="sea" onclick="payments.markets()">Сверка</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
@ -89,7 +90,7 @@
|
||||||
<span data-column="date" class="button">Дата</span>
|
<span data-column="date" class="button">Дата</span>
|
||||||
<span data-column="worker" class="button" title="Сотрудник"><i class="icon bold user"></i></span>
|
<span data-column="worker" class="button" title="Сотрудник"><i class="icon bold user"></i></span>
|
||||||
<span data-column="name" class="button">ФИО</span>
|
<span data-column="name" class="button">ФИО</span>
|
||||||
<span data-column="task" class="button">Работа</span>
|
<span data-column="work" class="button">Работа</span>
|
||||||
<span data-column="start" class="button" title="Начало"><i class="icon work alt"></i></span>
|
<span data-column="start" class="button" title="Начало"><i class="icon work alt"></i></span>
|
||||||
<span data-column="end" class="button" title="Окончание"><i class="icon home"></i></span>
|
<span data-column="end" class="button" title="Окончание"><i class="icon home"></i></span>
|
||||||
<span data-column="hours" class="button" title="Время работы"><i class="icon timer"></i></span>
|
<span data-column="hours" class="button" title="Время работы"><i class="icon timer"></i></span>
|
||||||
|
@ -145,4 +146,5 @@
|
||||||
<script type="text/javascript" src="/js/workers.js" defer></script>
|
<script type="text/javascript" src="/js/workers.js" defer></script>
|
||||||
<script type="text/javascript" src="/js/markets.js" defer></script>
|
<script type="text/javascript" src="/js/markets.js" defer></script>
|
||||||
<script type="text/javascript" src="/js/chat.js" defer></script>
|
<script type="text/javascript" src="/js/chat.js" defer></script>
|
||||||
|
<script type="text/javascript" src="/js/payments.js" defer></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Reference in New Issue