diff --git a/mirzaev/ebala/system/controllers/administrator.php b/mirzaev/ebala/system/controllers/administrator.php
index b528ba1..fb16e3a 100755
--- a/mirzaev/ebala/system/controllers/administrator.php
+++ b/mirzaev/ebala/system/controllers/administrator.php
@@ -38,7 +38,7 @@ final class administrator extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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;
@@ -86,7 +86,7 @@ final class administrator extends core
// Перебор фильтров статусов (И)
// Инициализация значения (приоритет у 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) {
@@ -116,7 +116,7 @@ final class administrator extends core
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;
$search_query = empty($search)
? null
diff --git a/mirzaev/ebala/system/controllers/index.php b/mirzaev/ebala/system/controllers/index.php
index f408266..4030de5 100755
--- a/mirzaev/ebala/system/controllers/index.php
+++ b/mirzaev/ebala/system/controllers/index.php
@@ -30,7 +30,7 @@ final class index extends core
// Перебор фильтров временного промежутка
// Инициализация значения (приоритет у 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-элемента
$this->view->{$name} = (int) $value;
@@ -40,7 +40,7 @@ final class index extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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;
diff --git a/mirzaev/ebala/system/controllers/market.php b/mirzaev/ebala/system/controllers/market.php
index e87ac01..21e335a 100755
--- a/mirzaev/ebala/system/controllers/market.php
+++ b/mirzaev/ebala/system/controllers/market.php
@@ -44,7 +44,7 @@ final class market extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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;
@@ -94,7 +94,7 @@ final class market extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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) {
@@ -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)";
// Инициализация строки поиска
- $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;
$search_query = empty($search)
? null
diff --git a/mirzaev/ebala/system/controllers/operator.php b/mirzaev/ebala/system/controllers/operator.php
index cd15289..cef225d 100755
--- a/mirzaev/ebala/system/controllers/operator.php
+++ b/mirzaev/ebala/system/controllers/operator.php
@@ -38,7 +38,7 @@ final class operator extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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;
@@ -86,7 +86,7 @@ final class operator extends core
// Перебор фильтров статусов (И)
// Инициализация значения (приоритет у 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) {
@@ -116,7 +116,7 @@ final class operator extends core
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;
$search_query = empty($search)
? null
diff --git a/mirzaev/ebala/system/controllers/payments.php b/mirzaev/ebala/system/controllers/payments.php
new file mode 100755
index 0000000..2ddfcc8
--- /dev/null
+++ b/mirzaev/ebala/system/controllers/payments.php
@@ -0,0 +1,195 @@
+
+ */
+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();
+ }
+ }
+
+}
diff --git a/mirzaev/ebala/system/controllers/task.php b/mirzaev/ebala/system/controllers/task.php
index 30f14f2..c2fcb31 100755
--- a/mirzaev/ebala/system/controllers/task.php
+++ b/mirzaev/ebala/system/controllers/task.php
@@ -48,7 +48,7 @@ final class task extends core
$this->errors['tasks'] ??= [];
if (!empty($json = json_decode(file_get_contents('php://input'), true, 4))) {
-
+ // Найден и декодирован json-документ с данными для создания заявок
foreach ($json as $work => $tasks) {
// Перебор категорий (колонок)
@@ -58,7 +58,7 @@ final class task extends core
// Создание заявки
model::create(
- work: model::label($work),
+ work: model::label($work),
market: $this->account->type === 'market' ? account::market($this->account->getId())?->id : null,
start: $task['start'],
end: $task['end'],
@@ -145,7 +145,7 @@ final class task extends core
// Перебор фильтров временного промежутка (И)
// Инициализация значения (приоритет у 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-выражения для инъекции в строку запроса
if ($name === 'from') $interval .= " && task.date >= $value";
@@ -162,7 +162,7 @@ final class task extends core
// Перебор фильтров с произвольными значениями (И)
// Инициализация значения (приоритет у 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;
@@ -182,7 +182,7 @@ final class task extends core
// Перебор фильтров по статусам
// Инициализация значения (приоритет у 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) {
@@ -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]]);
// Инициализация строки поиска
- $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;
$search_query = empty($search)
? null
@@ -407,6 +407,8 @@ final class task extends core
* @param array $rows Строки
*
* @return array Обработанные строки
+ *
+ * @todo Переделать в model::hours($from, $to);
*/
protected static function preprocessing(account $account, array $rows): array
{
diff --git a/mirzaev/ebala/system/controllers/worker.php b/mirzaev/ebala/system/controllers/worker.php
index 88111f1..8a295a1 100755
--- a/mirzaev/ebala/system/controllers/worker.php
+++ b/mirzaev/ebala/system/controllers/worker.php
@@ -44,7 +44,7 @@ final class worker extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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;
@@ -94,7 +94,7 @@ final class worker extends core
// Перебор фильтров статусов
// Инициализация значения (приоритет у 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) {
@@ -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)";
// Инициализация строки поиска
- $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;
$search_query = empty($search)
? null
diff --git a/mirzaev/ebala/system/models/core.php b/mirzaev/ebala/system/models/core.php
index 37a73bf..658cebc 100755
--- a/mirzaev/ebala/system/models/core.php
+++ b/mirzaev/ebala/system/models/core.php
@@ -132,6 +132,69 @@ class core extends model
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
*
diff --git a/mirzaev/ebala/system/models/payments.php b/mirzaev/ebala/system/models/payments.php
new file mode 100755
index 0000000..9c46c72
--- /dev/null
+++ b/mirzaev/ebala/system/models/payments.php
@@ -0,0 +1,609 @@
+
+ */
+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
+ };
+ }
+}
diff --git a/mirzaev/ebala/system/models/task.php b/mirzaev/ebala/system/models/task.php
index 62c041d..b42c676 100755
--- a/mirzaev/ebala/system/models/task.php
+++ b/mirzaev/ebala/system/models/task.php
@@ -14,8 +14,9 @@ use mirzaev\arangodb\collection,
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
-// Встроенные библиотеки
-use exception;
+// System libraries
+use datetime,
+ exception;
/**
* Модель заданий
@@ -176,14 +177,54 @@ final class task extends core
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
-
- var_dump($errors);
}
// Exit (fail)
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
*
diff --git a/mirzaev/ebala/system/public/css/list.css b/mirzaev/ebala/system/public/css/list.css
index b9af32e..828c80a 100755
--- a/mirzaev/ebala/system/public/css/list.css
+++ b/mirzaev/ebala/system/public/css/list.css
@@ -18,7 +18,7 @@ section.panel.list.medium {
}
section.panel.list > :is(form, search).row.menu {
- margin-bottom: 10px;%s"
+ margin-bottom: 10px;
transition: 0s;
}
diff --git a/mirzaev/ebala/system/public/css/pages/tasks.css b/mirzaev/ebala/system/public/css/pages/tasks.css
index 96cdd79..3f2a98e 100755
--- a/mirzaev/ebala/system/public/css/pages/tasks.css
+++ b/mirzaev/ebala/system/public/css/pages/tasks.css
@@ -13,7 +13,7 @@ section#tasks.panel.list
> span:is(
[data-column="worker"],
[data-column="name"],
- [data-column="task"],
+ [data-column="work"],
[data-column="address"],
[data-column="type"],
[data-column="tax"],
@@ -61,14 +61,14 @@ section#tasks.panel.list > div.row > span[data-column="name"] {
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;
width: 100px;
}
section#tasks.panel.list
> div.row:not(:nth-of-type(1))
- > span[data-column="task"] {
+ > span[data-column="work"] {
text-align: right;
}
diff --git a/mirzaev/ebala/system/public/index.php b/mirzaev/ebala/system/public/index.php
index 75cea38..986b4f9 100755
--- a/mirzaev/ebala/system/public/index.php
+++ b/mirzaev/ebala/system/public/index.php
@@ -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/send', 'task', 'message', '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));
diff --git a/mirzaev/ebala/system/public/js/buffer.js b/mirzaev/ebala/system/public/js/buffer.js
index 67abee3..76d3e2e 100644
--- a/mirzaev/ebala/system/public/js/buffer.js
+++ b/mirzaev/ebala/system/public/js/buffer.js
@@ -13,7 +13,7 @@ if (typeof window.buffer !== "function") {
*
* @return {Promise}
*/
- static write(name, value) {
+ static async write(name, value) {
if (
typeof core === "function" && typeof name === "string" &&
(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",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
diff --git a/mirzaev/ebala/system/public/js/chat.js b/mirzaev/ebala/system/public/js/chat.js
index 821be63..9fd1276 100644
--- a/mirzaev/ebala/system/public/js/chat.js
+++ b/mirzaev/ebala/system/public/js/chat.js
@@ -577,7 +577,7 @@ if (typeof window.chat !== "function") {
* @param {string} chat Тип чата (market, worker, both)
* @param {bool} scroll Прокрутить до последнего сообщения?
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
- * @param {string} chat Тип чата (market, worker)
+ * @param {bool} force Принудительное выполнение (используется в damper())
*
* @return {void}
*/
@@ -601,7 +601,7 @@ if (typeof window.chat !== "function") {
* @param {string} chat Тип чата (market, worker, both)
* @param {bool} scroll Прокрутить до последнего сообщения?
* @param {bool} sound Проигрывать звук уведомления о новом сообщении?
- * @param {bool} force Принудительное выполнение (используется в damper()
+ * @param {bool} force Принудительное выполнение (используется в damper())
*
* @return {void}
*/
diff --git a/mirzaev/ebala/system/public/js/damper.js b/mirzaev/ebala/system/public/js/damper.js
index 70facee..4e7ff57 100755
--- a/mirzaev/ebala/system/public/js/damper.js
+++ b/mirzaev/ebala/system/public/js/damper.js
@@ -20,13 +20,13 @@ function damper(func, timeout = 300, force) {
if (typeof force === 'number' && args[force]) {
// Принудительное выполнение (игнорировать таймер)
- func.apply(this, args);
+ return func.apply(this, args);
} else {
// Обычное выполнение
// Вызов функции (вход в рекурсию)
timer = setTimeout(() => {
- func.apply(this, args);
+ return func.apply(this, args);
}, timeout);
}
};
diff --git a/mirzaev/ebala/system/public/js/payments.js b/mirzaev/ebala/system/public/js/payments.js
new file mode 100644
index 0000000..04b9fc3
--- /dev/null
+++ b/mirzaev/ebala/system/public/js/payments.js
@@ -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 },
+ }),
+);
diff --git a/mirzaev/ebala/system/public/js/tasks.js b/mirzaev/ebala/system/public/js/tasks.js
index 1e3e03f..d31cd3b 100755
--- a/mirzaev/ebala/system/public/js/tasks.js
+++ b/mirzaev/ebala/system/public/js/tasks.js
@@ -48,118 +48,156 @@ if (typeof window.tasks !== "function") {
// Кассиры получены
// Блокировка полей ввода
- for (let i = 1; i < cashiers.childElementCount; i += 2)
+ for (let i = 1; i < cashiers.childElementCount; i += 2) {
if (
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);
- 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);
+ }
+ }
}
if (displayers instanceof HTMLElement) {
// Выкладчики получены
// Блокировка полей ввода
- for (let i = 1; i < displayers.childElementCount; i += 2)
+ for (let i = 1; i < displayers.childElementCount; i += 2) {
if (
displayers.children[i].children[0].children instanceof
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);
- 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);
+ }
+ }
}
if (gastronomes instanceof HTMLElement) {
// Гастрономы получены
// Блокировка полей ввода
- for (let i = 1; i < gastronomes.childElementCount; i += 2)
+ for (let i = 1; i < gastronomes.childElementCount; i += 2) {
if (
gastronomes.children[i].children[0].children instanceof
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);
- 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);
+ }
+ }
}
if (brigadiers instanceof HTMLElement) {
// Бригадиры получены
// Блокировка полей ввода
- for (let i = 1; i < brigadiers.childElementCount; i += 2)
+ for (let i = 1; i < brigadiers.childElementCount; i += 2) {
if (
brigadiers.children[i].children[0].children instanceof
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);
- 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);
+ }
+ }
}
if (loaders instanceof HTMLElement) {
// Грузчики получены
// Блокировка полей ввода
- for (let i = 1; i < loaders.childElementCount; i += 2)
+ for (let i = 1; i < loaders.childElementCount; i += 2) {
if (
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);
- 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);
+ }
+ }
}
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 (
loaders_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of loaders_mobile.children[i].children[0]
- .children)
+ .children) {
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(
"disabled",
true
);
+ }
+ }
}
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 (
universals_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of universals_mobile.children[i].children[0]
- .children)
+ .children) {
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(
"disabled",
true
);
+ }
+ }
}
// Блокировка кнопки
@@ -209,123 +247,162 @@ if (typeof window.tasks !== "function") {
// Кассиры получены
// Разблокировка полей ввода
- for (let i = 1; i < cashiers.childElementCount; i += 2)
+ for (let i = 1; i < cashiers.childElementCount; i += 2) {
if (
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.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");
+ }
+ }
}
if (displayers instanceof HTMLElement) {
// Выкладчики получены
// Разблокировка полей ввода
- for (let i = 1; i < displayers.childElementCount; i += 2)
+ for (let i = 1; i < displayers.childElementCount; i += 2) {
if (
displayers.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of displayers.children[i].children[0]
- .children)
+ .children) {
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");
+ }
+ }
}
if (gastronomes instanceof HTMLElement) {
// Гастрономы получены
// Разблокировка полей ввода
- for (let i = 1; i < gastronomes.childElementCount; i += 2)
+ for (let i = 1; i < gastronomes.childElementCount; i += 2) {
if (
gastronomes.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of gastronomes.children[i].children[0]
- .children)
+ .children) {
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");
+ }
+ }
}
if (brigadiers instanceof HTMLElement) {
// Бригадиры получены
// Разблокировка полей ввода
- for (let i = 1; i < brigadiers.childElementCount; i += 2)
+ for (let i = 1; i < brigadiers.childElementCount; i += 2) {
if (
brigadiers.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of brigadiers.children[i].children[0]
- .children)
+ .children) {
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");
+ }
+ }
}
if (loaders instanceof HTMLElement) {
// Грузчики получены
// Разблокировка полей ввода
- for (let i = 1; i < loaders.childElementCount; i += 2)
+ for (let i = 1; i < loaders.childElementCount; i += 2) {
if (
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.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");
+ }
+ }
}
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 (
loaders_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of loaders_mobile.children[i].children[0]
- .children)
+ .children) {
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(
"disabled"
);
+ }
+ }
}
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 (
universals_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
for (const element of universals_mobile.children[i].children[0]
- .children)
+ .children) {
element.removeAttribute("disabled");
- for (let i = 2; i < universals_mobile.childElementCount; i += 2)
+ }
+ }
+ }
+ 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].removeAttribute(
"disabled"
);
+ }
+ }
}
// Разблокировка кнопки
@@ -348,11 +425,11 @@ if (typeof window.tasks !== "function") {
body.cashiers = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < cashiers.childElementCount; i += 2)
+ for (let i = 1; i < cashiers.childElementCount; i += 2) {
if (
cashiers.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.cashiers.push({
start:
cashiers.children[i].children[0].children[0].value ?? null,
@@ -362,6 +439,8 @@ if (typeof window.tasks !== "function") {
1000 ?? null,
commentary: cashiers.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (displayers instanceof HTMLElement) {
@@ -371,11 +450,11 @@ if (typeof window.tasks !== "function") {
body.displayers = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < displayers.childElementCount; i += 2)
+ for (let i = 1; i < displayers.childElementCount; i += 2) {
if (
displayers.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.displayers.push({
start:
displayers.children[i].children[0].children[0].value ?? null,
@@ -387,6 +466,8 @@ if (typeof window.tasks !== "function") {
commentary:
displayers.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (gastronomes instanceof HTMLElement) {
@@ -396,11 +477,11 @@ if (typeof window.tasks !== "function") {
body.gastronomes = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < gastronomes.childElementCount; i += 2)
+ for (let i = 1; i < gastronomes.childElementCount; i += 2) {
if (
gastronomes.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.gastronomes.push({
start:
gastronomes.children[i].children[0].children[0].value ?? null,
@@ -412,6 +493,8 @@ if (typeof window.tasks !== "function") {
commentary:
gastronomes.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (brigadiers instanceof HTMLElement) {
@@ -421,11 +504,11 @@ if (typeof window.tasks !== "function") {
body.brigadiers = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < brigadiers.childElementCount; i += 2)
+ for (let i = 1; i < brigadiers.childElementCount; i += 2) {
if (
brigadiers.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.brigadiers.push({
start:
brigadiers.children[i].children[0].children[0].value ?? null,
@@ -437,6 +520,8 @@ if (typeof window.tasks !== "function") {
commentary:
brigadiers.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (loaders instanceof HTMLElement) {
@@ -446,10 +531,10 @@ if (typeof window.tasks !== "function") {
body.loaders = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < loaders.childElementCount; i += 2)
+ for (let i = 1; i < loaders.childElementCount; i += 2) {
if (
loaders.children[i].children[0].children instanceof HTMLCollection
- )
+ ) {
body.loaders.push({
start:
loaders.children[i].children[0].children[0].value ?? null,
@@ -457,6 +542,8 @@ if (typeof window.tasks !== "function") {
date: loaders.children[i].children[0].children[2].value ?? null,
commentary: loaders.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (loaders_mobile instanceof HTMLElement) {
@@ -466,11 +553,11 @@ if (typeof window.tasks !== "function") {
body.loaders_mobile = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < loaders_mobile.childElementCount; i += 2)
+ for (let i = 1; i < loaders_mobile.childElementCount; i += 2) {
if (
loaders_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.loaders_mobile.push({
start:
loaders_mobile.children[i].children[0].children[0].value ??
@@ -484,6 +571,8 @@ if (typeof window.tasks !== "function") {
commentary:
loaders_mobile.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
if (universals_mobile instanceof HTMLElement) {
@@ -493,11 +582,11 @@ if (typeof window.tasks !== "function") {
body.universals_mobile = [];
// Запись в буфер JSON для отправки
- for (let i = 1; i < universals_mobile.childElementCount; i += 2)
+ for (let i = 1; i < universals_mobile.childElementCount; i += 2) {
if (
universals_mobile.children[i].children[0].children instanceof
HTMLCollection
- )
+ ) {
body.universals_mobile.push({
start:
universals_mobile.children[i].children[0].children[0].value ??
@@ -511,6 +600,8 @@ if (typeof window.tasks !== "function") {
commentary:
universals_mobile.children[i + 1].children[0].value ?? null,
});
+ }
+ }
}
// Запрос к серверу
@@ -1746,47 +1837,60 @@ if (typeof window.tasks !== "function") {
* @param {string} name Название
* @param {string|number|null} value Значение
* @param {HTMLElement|null} button Кнопка
+ * @param {bool} force Принудительное выполнение (используется в damper())
*
* @return {void}
*/
- static filter = damper(async (name, value, button) => {
- if (typeof name === "string") {
- // Получено название
+ static filter = damper(
+ async (name, value, button, force = false) => {
+ return new Promise(async (resolve, reject) => {
+ if (typeof name === "string") {
+ // Получено название
- // Инициализация сериализованного пути к директории
- const path = `tasks_filter_${name}`;
+ // Инициализация сериализованного пути к директории
+ const path = `tasks_filter_${name}`;
- if (typeof value === "string" || typeof value === "number") {
- // Получено значение
+ if (typeof value === "string" || typeof value === "number") {
+ // Получено значение
- // Запись нового значения
- buffer.write(path, value);
- } else {
- // Не получено значение
+ // Запись нового значения
+ await buffer.write(path, value);
- // Чтение текущего значения
- value = +(await buffer.read(path));
+ resolve();
+ } else {
+ // Не получено значение
- // Инициализация значения по умолчанию
- if (isNaN(value)) value = 0;
+ // Чтение текущего значения
+ value = +(await buffer.read(path));
- // Запись нового значения (инвертирование)
- buffer.write(path, ++value < 3 ? value : 0);
+ // Инициализация значения по умолчанию
+ if (isNaN(value)) value = 0;
- if (button instanceof HTMLElement) {
- // Получена кнопка
+ // Запись нового значения (инвертирование)
+ await buffer.write(path, ++value < 3 ? value : 0);
- // Деинициализация классов статуса фильтра
- button.classList.remove("earth", "sand", "river");
+ resolve();
- // Инициализация классов статуса фильтра
- if (value === 1) button.classList.add("sand");
- else if (value === 2) button.classList.add("river");
- else button.classList.add("earth");
+ if (button instanceof HTMLElement) {
+ // Получена кнопка
+
+ // Деинициализация классов статуса фильтра
+ button.classList.remove("earth", "sand", "river");
+
+ // Инициализация классов статуса фильтра
+ if (value === 1) button.classList.add("sand");
+ else if (value === 2) button.classList.add("river");
+ else button.classList.add("earth");
+ }
+ }
}
- }
- }
- }, 300);
+
+ reject();
+ });
+ },
+ 300,
+ 3
+ );
/**
* Записать фильтр поиска
diff --git a/mirzaev/ebala/system/views/elements/tasks.html b/mirzaev/ebala/system/views/elements/tasks.html
index 2070e0d..fda7462 100644
--- a/mirzaev/ebala/system/views/elements/tasks.html
+++ b/mirzaev/ebala/system/views/elements/tasks.html
@@ -17,7 +17,7 @@
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.second }}{% endif %}
- {{ row.task.work
+ {{ row.task.work
}}
{{
row.task.generated.start }}
diff --git a/mirzaev/ebala/system/views/pages/tasks.html b/mirzaev/ebala/system/views/pages/tasks.html
index 4a84ad5..d0ec569 100644
--- a/mirzaev/ebala/system/views/pages/tasks.html
+++ b/mirzaev/ebala/system/views/pages/tasks.html
@@ -21,7 +21,8 @@
{% endif %}
{% if account.type == 'administrator' or account.type == 'operator' %}
-
+
+
{% endif %}
@@ -89,7 +90,7 @@
Дата
ФИО
- Работа
+ Работа
@@ -145,4 +146,5 @@
+
{% endblock %}