parent
8c1736f4fd
commit
5b10141217
|
@ -8,6 +8,7 @@ namespace mirzaev\ebala\controllers;
|
|||
use mirzaev\ebala\controllers\core,
|
||||
mirzaev\ebala\controllers\traits\errors,
|
||||
mirzaev\ebala\models\account as model,
|
||||
mirzaev\ebala\models\task,
|
||||
mirzaev\ebala\models\registry,
|
||||
mirzaev\ebala\models\core as _core;
|
||||
|
||||
|
@ -27,6 +28,37 @@ final class account extends core
|
|||
{
|
||||
use errors;
|
||||
|
||||
/**
|
||||
* Страница аккаунта
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*/
|
||||
public function index(array $parameters = []): ?string
|
||||
{
|
||||
if ($this->account->status()) {
|
||||
// Авторизован аккаунт
|
||||
|
||||
// Инициализация истории заявок
|
||||
$this->view->history = task::list(before: 'FILTER task.worker == "' . model::worker($this->account->getId())?->id . '"');
|
||||
|
||||
// Инициализация баланса счёта
|
||||
// В будущем сделать перебор по всем связанным с аккаунтам сотрудникам и магазинам (сейчас только 1 сотрудник может быть)
|
||||
$this->view->balance = 0;
|
||||
foreach (task::list(before: 'FILTER task.worker == "' . model::worker($this->account->getId())?->id . '" && task.result.processed == false') as $task) $this->view->balance += $task->task['result']['hours'] * $task->task['result']['hour'] + $task->task['result']['penalty'] + $task->task['result']['bonus'];
|
||||
|
||||
// Генерация представления
|
||||
$main = $this->view->render(DIRECTORY_SEPARATOR . 'pages' . DIRECTORY_SEPARATOR . 'account.html');
|
||||
} else $main = $this->authorization();
|
||||
|
||||
// Возврат (успех)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', ['main' => $main]);
|
||||
else if ($_SERVER['REQUEST_METHOD'] === 'POST') return $main;
|
||||
|
||||
// Возврат (провал)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Прочитать данные
|
||||
*
|
||||
|
|
|
@ -388,6 +388,166 @@ final class market extends core
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Заблокировать сотрудника в магазине
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*/
|
||||
public function ban(array $parameters = []): ?string
|
||||
{
|
||||
if ($this->account->status() && $this->account->type === 'market') {
|
||||
// Авторизован аккаунт магазина
|
||||
|
||||
// Инициализация данных магазина
|
||||
$market = account::market($this->account->getId());
|
||||
|
||||
if ($market instanceof _document) {
|
||||
// Найден магазин
|
||||
|
||||
// Блокировка сотрудника
|
||||
if (!in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = $market->bans + [$parameters['worker']];
|
||||
|
||||
if (_core::update($market)) {
|
||||
// Записаны данные магазина
|
||||
|
||||
// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
|
||||
$this->view->page = null;
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
// Инициализация буфера вывода
|
||||
ob_start();
|
||||
|
||||
// Инициализация буфера ответа
|
||||
$return = [
|
||||
'banned' => true,
|
||||
'errors' => self::parse_only_text($this->errors)
|
||||
];
|
||||
|
||||
// Генерация ответа
|
||||
echo json_encode($return);
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
|
||||
// Отправка и деинициализация буфера вывода
|
||||
ob_end_flush();
|
||||
flush();
|
||||
} else throw new exception('Не удалось записать изменения в базу данных');
|
||||
} else throw new exception('Не удалось найти магазин');
|
||||
}
|
||||
|
||||
// Возврат (провал)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Разблокировать сотрудника в магазине
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*/
|
||||
public function unban(array $parameters = []): ?string
|
||||
{
|
||||
if ($this->account->status() && $this->account->type === 'market') {
|
||||
// Авторизован аккаунт магазина
|
||||
|
||||
// Инициализация данных магазина
|
||||
$market = account::market($this->account->getId());
|
||||
|
||||
if ($market instanceof _document) {
|
||||
// Найден магазин
|
||||
|
||||
// Разблокировка сотрудника
|
||||
if (in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = array_diff($market->bans ??= [], [$parameters['worker']]);
|
||||
|
||||
if (_core::update($market)) {
|
||||
// Записаны данные магазина
|
||||
|
||||
// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
|
||||
$this->view->page = null;
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
// Инициализация буфера вывода
|
||||
ob_start();
|
||||
|
||||
// Инициализация буфера ответа
|
||||
$return = [
|
||||
'unbanned' => true,
|
||||
'errors' => self::parse_only_text($this->errors)
|
||||
];
|
||||
|
||||
// Генерация ответа
|
||||
echo json_encode($return);
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
|
||||
// Отправка и деинициализация буфера вывода
|
||||
ob_end_flush();
|
||||
flush();
|
||||
} else throw new exception('Не удалось записать изменения в базу данных');
|
||||
} else throw new exception('Не удалось найти магазин');
|
||||
}
|
||||
|
||||
// Возврат (провал)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить наличие сотрудника в реестре заблокированных
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*/
|
||||
public function banned(array $parameters = []): ?string
|
||||
{
|
||||
if ($this->account->status() && $this->account->type === 'market') {
|
||||
// Авторизован аккаунт магазина
|
||||
|
||||
// Инициализация данных магазина
|
||||
$market = account::market($this->account->getId());
|
||||
|
||||
if ($market instanceof _document) {
|
||||
// Найден магазин
|
||||
|
||||
// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
|
||||
$this->view->page = null;
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
// Инициализация буфера вывода
|
||||
ob_start();
|
||||
|
||||
// Инициализация буфера ответа
|
||||
$return = [
|
||||
'banned' => in_array($parameters['worker'], $market->bans ?? [], true),
|
||||
'errors' => self::parse_only_text($this->errors)
|
||||
];
|
||||
|
||||
// Генерация ответа
|
||||
echo json_encode($return);
|
||||
|
||||
// Запись заголовков ответа
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
|
||||
// Отправка и деинициализация буфера вывода
|
||||
ob_end_flush();
|
||||
flush();
|
||||
} else throw new exception('Не удалось найти магазин');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить данные
|
||||
*
|
||||
|
@ -416,6 +576,7 @@ final class market extends core
|
|||
if ($parameters['city'] !== $market->city) $market->city = $parameters['city'];
|
||||
if ($parameters['district'] !== $market->district) $market->district = $parameters['district'];
|
||||
if ($parameters['address'] !== $market->address) $market->address = $parameters['address'];
|
||||
if (!in_array($parameters['ban'], $market->bans, true)) $market->bans[] = $parameters['ban'];
|
||||
|
||||
if (_core::update($market)) {
|
||||
// Записаны данные магазина
|
||||
|
|
|
@ -76,7 +76,7 @@ final class payments extends core
|
|||
} else throw new exception('Вы не авторизованы');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$this->errors[] = [
|
||||
$this->errors['export'][] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
|
@ -110,7 +110,7 @@ final class payments extends core
|
|||
/**
|
||||
* Магазины
|
||||
*
|
||||
* Расчитать ... (сверку?) за выбранный период и сгенерировать excel-документ
|
||||
* Расчитать прибыль с магазинов за выбранный период и сгенерировать excel-документ
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*
|
||||
|
@ -161,7 +161,7 @@ final class payments extends core
|
|||
} else throw new exception('Вы не авторизованы');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$this->errors[] = [
|
||||
$this->errors['export'][] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
|
@ -192,4 +192,74 @@ final class payments extends core
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подтвердить
|
||||
*
|
||||
* Подтвердить выполнение операций с документом (магазины или сотрудники)
|
||||
*
|
||||
* @param array $parameters Параметры запроса
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function confirm(array $parameters = []): void
|
||||
{
|
||||
try {
|
||||
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
|
||||
// Авторизован аккаунт администратора или оператора
|
||||
|
||||
// Инициализация буфера ошибок
|
||||
$this->errors['confirm'] ??= [];
|
||||
|
||||
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
|
||||
|
||||
// Запуск процедуры подтверждения
|
||||
model::confirm(
|
||||
$from,
|
||||
$to,
|
||||
match ($parameters['type']) {
|
||||
'workers' => 'workers',
|
||||
'markets' => 'markets',
|
||||
default => throw new exception('Для подтверждения обработки документа необходимо передать его тип: workers, markets')
|
||||
},
|
||||
$this->errors['confirm']
|
||||
);
|
||||
} else throw new exception('Не инициализирован параметр: to');
|
||||
} else throw new exception('Не инициализирован параметр: from');
|
||||
} else throw new exception('Вы не авторизованы');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$this->errors['confirm'][] = [
|
||||
'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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -558,15 +558,15 @@ final class task extends core
|
|||
$end = $date->setTime((int) $end->format('H'), (int) $end->format('i'))->format('U');
|
||||
|
||||
// Заявка уже начата?
|
||||
if (time() - $start > 0)
|
||||
if (time() - $start > 0 && time() - $end < 0)
|
||||
throw new exception('Запрещено редактировать начатую заявку');
|
||||
|
||||
// Заявка уже прошла?
|
||||
if (time() - $end > 0)
|
||||
else if (time() - $end > 0 && $task->completed !== true)
|
||||
throw new exception('Запрещено редактировать прошедшую заявку');
|
||||
|
||||
// Заявка уже завершена?
|
||||
if ($task->completed === true)
|
||||
else if ($task->completed === true)
|
||||
throw new exception('Запрещено редактировать завершённую заявку');
|
||||
|
||||
// Прошло более 30 минут после создания заявки? (1800 секунд = 30 минут)
|
||||
|
@ -861,7 +861,7 @@ final class task extends core
|
|||
// Перевод ключей на русский язык
|
||||
foreach ($this->view->task as $key => $value)
|
||||
if ($key === 'updates')
|
||||
foreach ($value as $key => $value) $buffer['updates'][$key] = [
|
||||
foreach ($value ?? [] as $key => $value) $buffer['updates'][$key] = [
|
||||
'label' => match ($key) {
|
||||
'operator' => 'Оператор',
|
||||
'market' => 'Магазин',
|
||||
|
@ -1473,31 +1473,41 @@ final class task extends core
|
|||
// Иниализация сотрудника
|
||||
$worker = worker::read('d.id == "' . $task->worker . '"');
|
||||
|
||||
// Инициализация магазина
|
||||
$market = market::read('d.id == "' . $task->market . '"');
|
||||
if ($worker instanceof _document) {
|
||||
// Найден сотрудник
|
||||
|
||||
// Подсчёт часов работы
|
||||
$hours = model::hours($task->start, $task->end, $this->errors);
|
||||
// Инициализация магазина
|
||||
$market = market::read('d.id == "' . $task->market . '"');
|
||||
|
||||
// Инициализация цены работы за 1 час
|
||||
$hour = payments::hour($market->city, $task->work);
|
||||
if ($market instanceof _document) {
|
||||
// Найден магазин
|
||||
|
||||
// Подсчёт оплаты за работу
|
||||
$payment = $hour * $hours;
|
||||
// Подсчёт часов работы
|
||||
$hours = model::hours($task->start, $task->end, $this->errors);
|
||||
|
||||
// Инициализация штрафа
|
||||
$penalty = payments::penalty($task->rating ?? null);
|
||||
// Инициализация цены работы сотрудника за 1 час
|
||||
$hour = payments::hour('worker', $market->city, $task->work);
|
||||
|
||||
// Инициализация премии
|
||||
$bonus = payments::bonus($task->rating ?? null);
|
||||
// Подсчёт оплаты за работу
|
||||
$payment = $hour * $hours;
|
||||
|
||||
// Инициализация транзакции к оплате сотруднику
|
||||
model::transaction(
|
||||
$task->getId(),
|
||||
$worker->getId(),
|
||||
$payment - ($penalty === null ? $payment : -$penalty) + $bonus,
|
||||
$this->errors
|
||||
);
|
||||
// Инициализация штрафа
|
||||
$penalty = payments::penalty($task->rating ?? null);
|
||||
if ($penalty === null) $penalty = -$payment;
|
||||
|
||||
// Инициализация премии
|
||||
$bonus = payments::bonus($task->rating ?? null);
|
||||
|
||||
// Запись результатов
|
||||
$task->result = [
|
||||
'hours' => $hours,
|
||||
'hour' => $hour,
|
||||
'penalty' => $penalty,
|
||||
'bonus' => $bonus,
|
||||
'processed' => false
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Запись в реcстре последних обновивших
|
||||
|
@ -1709,15 +1719,15 @@ final class task extends core
|
|||
$end = $date->setTime((int) $end->format('H'), (int) $end->format('i'))->format('U');
|
||||
|
||||
// Заявка уже начата?
|
||||
if (time() - $start > 0)
|
||||
if (time() - $start > 0 && time() - $end < 0)
|
||||
throw new exception('Запрещено удалять начатую заявку');
|
||||
|
||||
// Заявка уже прошла?
|
||||
if (time() - $end > 0)
|
||||
else if (time() - $end > 0 && $task->completed !== true)
|
||||
throw new exception('Запрещено удалять прошедшую заявку');
|
||||
|
||||
// Заявка уже завершена?
|
||||
if ($task->completed === true)
|
||||
else if ($task->completed === true)
|
||||
throw new exception('Запрещено удалять завершённую заявку');
|
||||
|
||||
// Прошло более 30 минут после создания заявки? (1800 секунд = 30 минут)
|
||||
|
@ -2092,15 +2102,15 @@ final class task extends core
|
|||
$end = $date->setTime((int) $end->format('H'), (int) $end->format('i'))->format('U');
|
||||
|
||||
// Заявка уже начата?
|
||||
if (time() - $start > 0)
|
||||
if (time() - $start > 0 && time() - $end < 0)
|
||||
throw new exception('Запрещено редактировать тип работы начатой заявки');
|
||||
|
||||
// Заявка уже прошла?
|
||||
if (time() - $end > 0)
|
||||
else if (time() - $end > 0 && $task->completed !== true)
|
||||
throw new exception('Запрещено редактировать тип работы прошедшей заявки');
|
||||
|
||||
// Заявка уже завершена?
|
||||
if ($task->completed === true)
|
||||
else if ($task->completed === true)
|
||||
throw new exception('Запрещено редактировать тип работы завершённой заявки');
|
||||
|
||||
// Прошло более 30 минут после создания заявки? (1800 секунд = 30 минут)
|
||||
|
@ -2348,15 +2358,15 @@ final class task extends core
|
|||
$end = $date->setTime((int) $end->format('H'), (int) $end->format('i'))->format('U');
|
||||
|
||||
// Заявка уже начата?
|
||||
if (time() - $start > 0)
|
||||
if (time() - $start > 0 && time() - $end < 0)
|
||||
throw new exception('Запрещено редактировать дату и время начатой заявки');
|
||||
|
||||
// Заявка уже прошла?
|
||||
if (time() - $end > 0)
|
||||
else if (time() - $end > 0 && $task->completed !== true)
|
||||
throw new exception('Запрещено редактировать дату и время прошедшей заявки');
|
||||
|
||||
// Заявка уже завершена?
|
||||
if ($task->completed === true)
|
||||
else if ($task->completed === true)
|
||||
throw new exception('Запрещено редактировать дату и время завершённой заявки');
|
||||
|
||||
// Прошло более 30 минут после создания заявки? (1800 секунд = 30 минут)
|
||||
|
|
|
@ -146,41 +146,55 @@ final class worker extends core
|
|||
? null
|
||||
: <<<AQL
|
||||
SEARCH
|
||||
a.commentary IN TOKENS(@search, 'text_ru')
|
||||
|| a.address IN TOKENS(@search, 'text_ru')
|
||||
|| a.passport IN TOKENS(@search, 'text_ru')
|
||||
|| a.department.address IN TOKENS(@search, 'text_ru')
|
||||
|| a.requisites IN TOKENS(@search, 'text_ru')
|
||||
b.commentary IN TOKENS(@search, 'text_ru')
|
||||
|| b.address IN TOKENS(@search, 'text_ru')
|
||||
|| b.passport IN TOKENS(@search, 'text_ru')
|
||||
|| b.department.address IN TOKENS(@search, 'text_ru')
|
||||
|| b.requisites IN TOKENS(@search, 'text_ru')
|
||||
|| STARTS_WITH(a._key, @search)
|
||||
|| STARTS_WITH(a.id, @search)
|
||||
|| STARTS_WITH(a.name.first, @search)
|
||||
|| STARTS_WITH(a.name.second, @search)
|
||||
|| STARTS_WITH(a.name.last, @search)
|
||||
|| STARTS_WITH(a.address, @search)
|
||||
|| STARTS_WITH(a.city, @search)
|
||||
|| STARTS_WITH(a.district, @search)
|
||||
|| STARTS_WITH(b._key, @search)
|
||||
|| STARTS_WITH(b.id, @search)
|
||||
|| STARTS_WITH(b.name.first, @search)
|
||||
|| STARTS_WITH(b.name.second, @search)
|
||||
|| STARTS_WITH(b.name.last, @search)
|
||||
|| STARTS_WITH(b.address, @search)
|
||||
|| STARTS_WITH(b.city, @search)
|
||||
|| STARTS_WITH(b.district, @search)
|
||||
|| STARTS_WITH(a.number, @search)
|
||||
|| STARTS_WITH(b.number, @search)
|
||||
|| STARTS_WITH(a.mail, @search)
|
||||
|| STARTS_WITH(a.passport, @search)
|
||||
|| STARTS_WITH(a.department.number, @search)
|
||||
|| STARTS_WITH(a.department.address, @search)
|
||||
|| STARTS_WITH(a.requisites, @search)
|
||||
|| STARTS_WITH(a.tax, @search)
|
||||
|| STARTS_WITH(b.mail, @search)
|
||||
|| STARTS_WITH(b.passport, @search)
|
||||
|| STARTS_WITH(b.department.number, @search)
|
||||
|| STARTS_WITH(b.department.address, @search)
|
||||
|| STARTS_WITH(b.requisites, @search)
|
||||
|| STARTS_WITH(b.tax, @search)
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a._key, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.id, TOKENS(@search, 'text_en')[0], 1, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.first, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.second, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(a.name.last, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(a.address, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.city, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.district, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b._key, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.id, TOKENS(@search, 'text_en')[0], 1, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.first, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.second, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 3 && LEVENSHTEIN_MATCH(b.name.last, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(b.address, TOKENS(@search, 'text_ru')[0], 2, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.city, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.district, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.number, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.number, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.mail, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.passport, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(a.department.number, TOKENS(@search, 'text_en')[0], 1, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a.department.address, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(a.requisites, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(a.tax, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.mail, TOKENS(@search, 'text_en')[0], 2, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.passport, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(b.department.number, TOKENS(@search, 'text_en')[0], 1, true))
|
||||
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(b.department.address, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(b.requisites, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
|| (LENGTH(@search) > 7 && LEVENSHTEIN_MATCH(b.tax, TOKENS(@search, 'text_ru')[0], 1, true))
|
||||
OPTIONS { collections: ["account", "worker"] }
|
||||
AQL;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ final class payments extends core
|
|||
{
|
||||
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);
|
||||
$tasks = @task::read("d.date >= $from && d.date <= $to && d.problematic == false && d.completed == true && d.result.processed != 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) {
|
||||
// Найдены заявки
|
||||
|
@ -208,9 +208,9 @@ final class payments extends core
|
|||
->setCellValue("M$row", $worker->name['second'] . ' ' . $worker->name['first'] . ' ' . $worker->name['last'])
|
||||
->setCellValue("N$row", $hour = static::hour('worker', $market->city, $task->work))
|
||||
->setCellValue("O$row", $payment = $hour * $hours)
|
||||
->setCellValue("P$row", ($penalty = static::penalty($task->rating ?? null)) === null ? $payment : $penalty)
|
||||
->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("R$row", $payment + ($penalty === null ? -$payment : $penalty) + $bonus)
|
||||
->setCellValue("S$row", '')
|
||||
->setCellValue("T$row", $worker->payment) // Наличные?
|
||||
->setCellValue("U$row", '')
|
||||
|
@ -253,7 +253,7 @@ final class payments extends core
|
|||
/**
|
||||
* Магазины
|
||||
*
|
||||
* Расчитать ... и сгенерировать excel-документ
|
||||
* Расчитать прибыль с магазинов и сгенерировать excel-документ
|
||||
*
|
||||
* @param int $from Начальная дата для выборки заявок (unixtime)
|
||||
* @param int $to Конечная дата для выборки заявок (unixtime)
|
||||
|
@ -488,7 +488,15 @@ final class payments extends core
|
|||
// Инкрементация счётчика для генерации следующей строки
|
||||
++$row;
|
||||
}
|
||||
}
|
||||
} // Чтение заявок
|
||||
$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
|
||||
);
|
||||
|
||||
// Запись строки с общими данными магазина
|
||||
$spreadsheet
|
||||
|
@ -575,6 +583,60 @@ final class payments extends core
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Подтвердить обработку
|
||||
*
|
||||
* Отметить в базе данных то, что выбранные заявки были обработаны
|
||||
*
|
||||
* @param int $from Начальная дата для выборки заявок (unixtime)
|
||||
* @param int $to Конечная дата для выборки заявок (unixtime)
|
||||
* @param string $type Тип документа для подтверждения (workers, markets)
|
||||
* @param array $errors Errors registry
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function confirm(int $from, int $to, string $type, array &$errors = []): bool
|
||||
{
|
||||
try {
|
||||
|
||||
// Чтение заявок
|
||||
$tasks = @task::read("d.date >= $from && d.date <= $to && d.problematic == false && d.completed == true && d.result.processed != true", amount: 999999, errors: $errors);
|
||||
|
||||
if (is_array($tasks) && count($tasks) > 0) {
|
||||
// Найдены заявки
|
||||
|
||||
if ($type === 'workers') {
|
||||
// Подтверждена обработка зарплат сотрудников за выбранный период
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
// Перебор заявок
|
||||
|
||||
// Подтверждение того, что заявка обработана (выплачены деньги сотруднику)
|
||||
$task->result = ['processed' => true] + ($task->result ?? []);
|
||||
|
||||
// Запись обновления в базу данных
|
||||
core::update($task);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -259,7 +259,7 @@ final class task extends core
|
|||
public static function transaction(
|
||||
string $task,
|
||||
string $worker,
|
||||
int $amount = 0,
|
||||
int|float $amount = 0,
|
||||
array &$errors = []
|
||||
): ?string {
|
||||
try {
|
||||
|
|
|
@ -44,6 +44,12 @@ div#popup>section.list>div.row.endless {
|
|||
height: auto;
|
||||
}
|
||||
|
||||
section.panel.list> :is(form, search).row.menu>label>div:has(>button) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
section.panel.list> :is(form, search).row.menu>label>button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
|
|
@ -49,6 +49,9 @@ $router->write('/workers/create', 'worker', 'create', 'POST');
|
|||
$router->write('/market/$market/read', 'task', 'market', 'POST');
|
||||
$router->write('/market/$id/fields', 'market', 'fields', 'POST');
|
||||
$router->write('/market/$id/update', 'market', 'update', 'POST');
|
||||
$router->write('/market/ban/$worker', 'market', 'ban', 'POST');
|
||||
$router->write('/market/unban/$worker', 'market', 'unban', 'POST');
|
||||
$router->write('/market/banned/$worker', 'market', 'banned', 'POST');
|
||||
$router->write('/markets', 'market', 'index', 'GET');
|
||||
$router->write('/markets', 'market', 'index', 'POST');
|
||||
$router->write('/markets/read', 'market', 'read', 'POST');
|
||||
|
@ -103,6 +106,7 @@ $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');
|
||||
$router->write('/payments/confirm/$type', 'payments', 'confirm', 'POST');
|
||||
|
||||
// Инициализация ядра
|
||||
$core = new core(namespace: __NAMESPACE__, router: $router, controller: new controller(false), model: new model(false));
|
||||
|
|
|
@ -185,10 +185,7 @@ if (typeof window.loader !== "function") {
|
|||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static async account() {
|
||||
// Initialization of the account identifier
|
||||
account = Cookies.get(`account_id`) ?? "account";
|
||||
|
||||
static async account(account = Cookies.get(`account_id`) ?? "account") {
|
||||
return await fetch(`/${account}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
@ -198,10 +195,10 @@ if (typeof window.loader !== "function") {
|
|||
.then((response) => response.text())
|
||||
.then((data) => {
|
||||
// Write path in history
|
||||
history.pushState(this.storage, "/account", "/account");
|
||||
history.pushState(this.storage, `/${account}`, `/${account}`);
|
||||
|
||||
// Write path to the current directory buffer
|
||||
core.page = 'account';
|
||||
core.page = account;
|
||||
|
||||
// Write content in document
|
||||
document.body.getElementsByTagName("main")[0].innerHTML = data;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -52,8 +52,8 @@ if (typeof window.payments !== "function") {
|
|||
|
||||
if (header !== null) {
|
||||
// Найден заголовок (подразумевается, что передан файл, а не случайная ошибка)
|
||||
|
||||
// Инициализация названия файла
|
||||
|
||||
// Инициализация названия файла
|
||||
const name = header.split(";")[1].split("=")[1];
|
||||
|
||||
// Чтение полученного файла (подразумевается ошибка при инициализации json)
|
||||
|
@ -129,7 +129,7 @@ if (typeof window.payments !== "function") {
|
|||
if (header !== null) {
|
||||
// Найден заголовок (подразумевается, что передан файл, а не случайная ошибка)
|
||||
|
||||
// Инициализация названия файла
|
||||
// Инициализация названия файла
|
||||
const name = header.split(";")[1].split("=")[1];
|
||||
|
||||
// Чтение полученного файла (подразумевается ошибка при инициализации json)
|
||||
|
@ -157,6 +157,53 @@ if (typeof window.payments !== "function") {
|
|||
});
|
||||
}, 200);
|
||||
|
||||
/**
|
||||
* Подтвердить обработку документа
|
||||
*
|
||||
* @param {string} type Тип обработанного документа (workers, markets)
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static confirm = damper((type) => {
|
||||
// Инициализация оболочки фильтров
|
||||
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/confirm/${type}`, { method: "POST" }).then(
|
||||
(response) => {
|
||||
if (response.ok) {
|
||||
// Сервер вернул код успешного выполнения
|
||||
|
||||
response
|
||||
.clone()
|
||||
.json()
|
||||
.then(
|
||||
(data) => {
|
||||
if (this.errors(data.errors)) {
|
||||
// Сгенерированы ошибки
|
||||
} else {
|
||||
// Не сгенерированы ошибки (подразумевается их отсутствие)
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}, 200);
|
||||
|
||||
/**
|
||||
* Сгенерировать HTML-элемент со списком ошибок
|
||||
*
|
||||
|
|
|
@ -2210,34 +2210,6 @@ if (typeof window.tasks !== "function") {
|
|||
});
|
||||
});
|
||||
|
||||
// Инициализация оболочки для кнопок
|
||||
const buttons_worker = document.createElement("div");
|
||||
buttons_worker.classList.add("row", "buttons", "divided", "merged");
|
||||
|
||||
// Инициализация кнопки подтверждения
|
||||
const ban = document.createElement("button");
|
||||
if (true) {
|
||||
// Забанен сотрудник
|
||||
|
||||
ban.classList.add("earth", "stretched");
|
||||
ban.innerText = "Разблокировать";
|
||||
ban.setAttribute('title', 'Сотрудник снова сможет записываться на ваши заявки');
|
||||
ban.setAttribute(
|
||||
"onclick",
|
||||
`tasks.unban(this, this.parentElement.previousElementSibling.previousElementSibling.children[0], this.parentElement.previousElementSibling, document.getElementById('${task}'))`
|
||||
);
|
||||
} else {
|
||||
// Не забанен сотрудник
|
||||
|
||||
ban.classList.add("clay", "stretched");
|
||||
ban.innerText = "Заблокировать";
|
||||
ban.setAttribute('title', 'Сотрудник не сможет записываться на ваши заявки');
|
||||
ban.setAttribute(
|
||||
"onclick",
|
||||
`tasks.ban(this, this.parentElement.previousElementSibling.previousElementSibling.children[0], this.parentElement.previousElementSibling, document.getElementById('${task}'))`
|
||||
);
|
||||
}
|
||||
|
||||
// Инициализация оболочки для кнопок
|
||||
const buttons = document.createElement("div");
|
||||
buttons.classList.add("row", "buttons", "merged");
|
||||
|
@ -2325,8 +2297,61 @@ if (typeof window.tasks !== "function") {
|
|||
if (data.completed !== true) {
|
||||
// Зявка не завершена
|
||||
|
||||
buttons_worker.appendChild(ban);
|
||||
column.appendChild(buttons_worker);
|
||||
if (typeof window.markets === "function") {
|
||||
// Инициализирована программа-обработчик магазинов (подразумевается)
|
||||
|
||||
// Инициализация оболочки для кнопок
|
||||
const buttons_worker = document.createElement("div");
|
||||
buttons_worker.classList.add(
|
||||
"row",
|
||||
"buttons",
|
||||
"divided",
|
||||
"merged"
|
||||
);
|
||||
|
||||
// Инициализация кнопки подтверждения
|
||||
const ban = document.createElement("button");
|
||||
|
||||
// Инициализация идентификатора сотрудника
|
||||
const worker = row.querySelector(
|
||||
'[data-column="worker"]'
|
||||
).innerText;
|
||||
|
||||
markets.banned(worker).then((banned) => {
|
||||
if (banned) {
|
||||
// Забанен сотрудник
|
||||
|
||||
ban.classList.add("earth", "stretched");
|
||||
ban.innerText = "Разблокировать";
|
||||
ban.setAttribute(
|
||||
"title",
|
||||
"Сотрудник снова сможет записываться на заявки"
|
||||
);
|
||||
ban.setAttribute(
|
||||
"onclick",
|
||||
`markets.unban(${worker}, this)`
|
||||
);
|
||||
} else {
|
||||
// Не забанен сотрудник
|
||||
|
||||
ban.classList.add("clay", "stretched");
|
||||
ban.innerText = "Заблокировать";
|
||||
ban.setAttribute(
|
||||
"title",
|
||||
"Сотрудник не сможет записываться на заявки"
|
||||
);
|
||||
ban.setAttribute(
|
||||
"onclick",
|
||||
`markets.ban(${worker}, this)`
|
||||
);
|
||||
}
|
||||
|
||||
buttons_worker.appendChild(ban);
|
||||
column.insertBefore(buttons_worker, buttons);
|
||||
|
||||
top(this.body.errors);
|
||||
});
|
||||
}
|
||||
|
||||
buttons.appendChild(problem);
|
||||
buttons.appendChild(complete);
|
||||
|
@ -2607,7 +2632,7 @@ if (typeof window.tasks !== "function") {
|
|||
this.works(task).then((html) => (work.innerHTML = html));
|
||||
work.setAttribute("title", "Тип работы");
|
||||
work.setAttribute(
|
||||
'onchange',
|
||||
"onchange",
|
||||
`tasks.work(document.getElementById('${task}'), this)`
|
||||
);
|
||||
|
||||
|
@ -2948,12 +2973,11 @@ if (typeof window.tasks !== "function") {
|
|||
core.interface === "administrator"
|
||||
) {
|
||||
// Оператор или администратор
|
||||
|
||||
|
||||
// Активация виртуальной кнопки "подтвердить"
|
||||
confirm.click();
|
||||
} else {
|
||||
// Магазин или сотрудник
|
||||
|
||||
}
|
||||
|
||||
// Возвращение статуса блокировки закрытия окна
|
||||
|
|
|
@ -31,8 +31,11 @@
|
|||
</li> -->
|
||||
{% endif %}
|
||||
<li id="account">
|
||||
<button class="transparent" onclick="loader.profile()" title="Настройки профиля">{{ account.name.first }} {{
|
||||
account.name.second }}</button>
|
||||
<button class="transparent" onclick="loader.account({{ account.getKey() }})">{%
|
||||
if account.name.first is not empty %}{{
|
||||
account.name.first|slice(0, 1)|upper }}.{% endif %}{% if account.name.last is not empty %} {{
|
||||
account.name.last|slice(0, 1)|upper }}.{% endif %}{% if account.name.second is not empty %} {{
|
||||
account.name.second }}{% endif %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
{% extends('index.html') %}
|
||||
|
||||
{% block css %}
|
||||
<link type="text/css" rel="stylesheet" data-reinitializer-once="true" href="/css/list.css">
|
||||
<link type="text/css" rel="stylesheet" data-reinitializer-once="true" href="/css/pages/account.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<section id="account" class="panel small list">
|
||||
<section id="information">
|
||||
<!-- <h1>{% if account.name.first is not empty %}{{ account.name.first }}{% endif %}{% if account.name.second is not
|
||||
empty
|
||||
%} {{ account.name.second }}{% endif %}{% if account.name.last is not empty %} {{ account.name.last }}{% endif %}
|
||||
</h1> -->
|
||||
<h2>{{ account.getKey() }}</h2>
|
||||
</section>
|
||||
<section id="balance">
|
||||
<p><b>Баланс:</b> <span>{{ balance ?? 0 }}</span></p>
|
||||
</section>
|
||||
<section id="history">
|
||||
{% for entry in history %}
|
||||
<p class="{% if entry.task.result.processed %}processed{% endif %}"><b>{{ entry.task._key }}</b><span>{{
|
||||
entry.task.work }}</span><span>{{ entry.task.result.hours ?? 0 }} часов</span><span>{{ entry.task.result.hour ??
|
||||
0 * entry.task.result.hours ?? 0 }}р.</span><span>{{ entry.task.result.penalty ?? 0 }}р (штраф)</span><span>{{
|
||||
entry.task.result.bonus ?? 0 }}р. (бонус)</span><span>{{ entry.task.result.hour ?? 0 * entry.task.result.hours
|
||||
?? 0 + entry.task.result.penalty ?? 0 + entry.task.result.bonus ?? 0 }}р. (итого)</span></p>
|
||||
|
||||
{% endfor %}
|
||||
</section>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript" data-reinitializer-once="true" src="/js/imask-7.1.0-alpha.js" defer></script>
|
||||
<script type="text/javascript" src="/js/account.js" defer></script>
|
||||
{% endblock %}
|
|
@ -21,8 +21,14 @@
|
|||
<button class="grass dense" onclick="tasks.create()">Создать</button>
|
||||
{% endif %}
|
||||
{% if account.type == 'administrator' or account.type == 'operator' %}
|
||||
<button class="sea" onclick="payments.workers()">Сотрудники</button>
|
||||
<button class="sea" onclick="payments.markets()">Магазины</button>
|
||||
<div>
|
||||
<button class="sea merged right" onclick="payments.workers()">Сотрудники</button>
|
||||
<button class="river merged left" onclick="payments.confirm('workers')">Подтвердить</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="sea merged right" onclick="payments.markets()">Магазины</button>
|
||||
<button class="river merged left" onclick="payments.confirm('markets')">Подтвердить</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</label>
|
||||
</form>
|
||||
|
|
Reference in New Issue