8 Commits
1.0.0 ... 1.8.0

30 changed files with 7305 additions and 5899 deletions

View File

@@ -1,10 +1,7 @@
# Ebala # Ebala
### Site for providing outsourced employees to retail stores Site-registry of tasks for outsourced employees
My customer uses this development to process thousands of applications throughout Krasnoyarsk and neighboring cities.<br/> From this project i earned **700 000** Russian rubles</br>
**9 000 000** Russian rubles pass through the site every **week** 🤟. *As long as commits appear in the repository, this means that i continue paid development*</br>
</br>
For this project I received **600 000** Russian rubles. At the current exchange rate this is **$6000**.<br/> I am selling this site to **capitalist scum** for a lot of money, but you, friend, can use my code **for free** ✌️
If I had not been lazy and submitted the order a few months ago, it would have been **$8000** 😥 (super sad).
I did not sign any documents prohibiting the dissemination of information. You can use the code under the **WTFPL** license 🤝 (read ./LICENSE)

View File

@@ -163,10 +163,14 @@ final class account extends core
'errors' => self::parse_only_text($this->errors) 'errors' => self::parse_only_text($this->errors)
]; ];
if ($password) $return['clipboard'] = <<<TEXT if ($password) $return['clipboard'] = match ($account->type) {
Идентификатор: {$account->getKey()} 'worker' => 'Номер: ' . model::worker($account->getId())?->number,
Пароль: {$parameters['password']} 'market' => 'Идентификатор: ' . model::market($account->getId())?->id,
TEXT; 'operator' => "Идентификатор: {$account->getKey()}",
'administrator' => "Идентификатор: {$account->getKey()}",
default => "Идентификатор: {$account->getKey()}"
}
. "\nПароль: {$parameters['password']}";
// Генерация ответа // Генерация ответа
echo json_encode($return); echo json_encode($return);

View File

@@ -150,7 +150,7 @@ class core extends controller
$buffer = []; $buffer = [];
// Инициализация данных магазина для аккаунта для генерации представления // Инициализация данных магазина для аккаунта для генерации представления
foreach ($this->view->accounts as $vendor) $buffer[] = ['account' => $vendor, 'market' => account::market($vendor->getId(), errors: $this->errors['account'])]; foreach ($this->view->accounts as $account) $buffer[] = ['account' => $account, 'market' => account::market($account->getId(), errors: $this->errors['account'])];
// Запись в глобальную переменную из буфера // Запись в глобальную переменную из буфера
$this->view->accounts = $buffer; $this->view->accounts = $buffer;

View File

@@ -144,6 +144,7 @@ final class market extends core
a.commentary IN TOKENS(@search, 'text_ru') a.commentary IN TOKENS(@search, 'text_ru')
|| a.address IN TOKENS(@search, 'text_ru') || a.address IN TOKENS(@search, 'text_ru')
|| STARTS_WITH(a._key, @search) || STARTS_WITH(a._key, @search)
|| STARTS_WITH(a.id, @search)
|| STARTS_WITH(a.name.first, @search) || STARTS_WITH(a.name.first, @search)
|| STARTS_WITH(a.name.second, @search) || STARTS_WITH(a.name.second, @search)
|| STARTS_WITH(a.name.last, @search) || STARTS_WITH(a.name.last, @search)
@@ -152,6 +153,7 @@ final class market extends core
|| STARTS_WITH(a.number, @search) || STARTS_WITH(a.number, @search)
|| STARTS_WITH(a.mail, @search) || STARTS_WITH(a.mail, @search)
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a._key, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a._key, TOKENS(@search, 'text_en')[0], 2, true))
|| (LENGTH(@search) > 2 && 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.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.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) > 3 && LEVENSHTEIN_MATCH(a.name.last, TOKENS(@search, 'text_ru')[0], 2, true))
@@ -236,6 +238,7 @@ final class market extends core
else if (!empty($parameters['account_number']) && strlen($parameters['account_number']) < 11) throw new exception('Несоответствие формату SIM-номера аккаунта представителя'); else if (!empty($parameters['account_number']) && strlen($parameters['account_number']) < 11) throw new exception('Несоответствие формату SIM-номера аккаунта представителя');
else if (!empty($parameters['market_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['market_mail']) === 0) throw new exception('Несоответствие формату почты представителя'); else if (!empty($parameters['market_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['market_mail']) === 0) throw new exception('Несоответствие формату почты представителя');
else if (!empty($parameters['account_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['account_mail']) === 0) throw new exception('Несоответствие формату почты аккаунта представителя'); else if (!empty($parameters['account_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['account_mail']) === 0) throw new exception('Несоответствие формату почты аккаунта представителя');
else if (!empty($parameters['market_id']) && model::read('d.id == "' . $parameters['market_id'] . '"', errors: $this->errors['account']) instanceof _document) throw new exception('Уже существует магазин с данным идентификатором');
// Универсализация // Универсализация
/* $parameters['market_number'] = (int) $parameters['market_number']; */ /* $parameters['market_number'] = (int) $parameters['market_number']; */
@@ -274,8 +277,8 @@ final class market extends core
]; ];
} }
// Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных) // Инициализация идентификатора магазина
$_key = preg_replace('/.+\//', '', $account ?? ''); $id = empty($parameters['market_id']) ? model::id() : $parameters['market_id'];
// Запись заголовков ответа // Запись заголовков ответа
header('Content-Type: application/json'); header('Content-Type: application/json');
@@ -289,7 +292,7 @@ final class market extends core
echo json_encode( echo json_encode(
[ [
'clipboard' => empty($this->errors['account']) ? <<<TEXT 'clipboard' => empty($this->errors['account']) ? <<<TEXT
Идентификатор: $_key Идентификатор: $id
Пароль: {$parameters['account_password']} Пароль: {$parameters['account_password']}
TEXT : '', TEXT : '',
'errors' => self::parse_only_text($this->errors['account']) 'errors' => self::parse_only_text($this->errors['account'])
@@ -304,29 +307,35 @@ final class market extends core
flush(); flush();
try { try {
// Создание магазина if (isset($account)) {
$market = model::create( // Инициализирован аккаунт
data: [
'name' => [ // Создание магазина
'first' => $parameters['market_name_first'], $market = model::create(
'second' => $parameters['market_name_second'], data: [
'last' => $parameters['market_name_last'] 'id' => (string) $id,
'name' => [
'first' => $parameters['market_name_first'],
'second' => $parameters['market_name_second'],
'last' => $parameters['market_name_last']
],
'number' => $parameters['market_number'] === 0 ? '' : $parameters['market_number'],
'mail' => $parameters['market_mail'],
'type' => $parameters['market_type'],
'city' => $parameters['market_city'],
'district' => $parameters['market_district'],
'address' => $parameters['market_address'],
], ],
'number' => $parameters['market_number'] === 0 ? '' : $parameters['market_number'], errors: $this->errors['account']
'mail' => $parameters['market_mail'], );
'type' => $parameters['market_type'],
'city' => $parameters['market_city'],
'district' => $parameters['market_district'],
'address' => $parameters['market_address'],
],
errors: $this->errors['account']
);
// Проверка существования созданного магазина // Проверка существования созданного магазина
if (empty($market)) throw new exception('Не удалось создать магазин'); if (empty($market)) throw new exception('Не удалось создать магазин');
// Создание ребра: account -> market // Создание ребра: account -> market
account::connect($account, $market, 'market', $this->errors['account']); account::connect($account, $market, 'market', $this->errors['account']);
}
throw new exception('Не инициализирован аккаунт');
} catch (exception $e) { } catch (exception $e) {
// Write to the errors registry // Write to the errors registry
$this->errors['account'][] = [ $this->errors['account'][] = [
@@ -350,7 +359,7 @@ final class market extends core
// Авторизован аккаунт администратора или оператора // Авторизован аккаунт администратора или оператора
// Инициализация данных магазина // Инициализация данных магазина
$market = model::read('d._key == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, type: d.type, city: d.city, district: d.district, address: d.address}')->getAll(); $market = model::read('d.id == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, type: d.type, city: d.city, district: d.district, address: d.address}')->getAll();
if (!empty($market)) { if (!empty($market)) {
// Найдены данные магазина // Найдены данные магазина
@@ -390,12 +399,12 @@ final class market extends core
// Авторизован аккаунт администратора или оператора // Авторизован аккаунт администратора или оператора
// Инициализация данных магазина // Инициализация данных магазина
$market = model::read('d._key == "' . $parameters['id'] . '"'); $market = model::read('d.id == "' . $parameters['id'] . '"');
if (!empty($market)) { if (!empty($market)) {
// Найден магазин // Найден магазин
// Инициализация параметров (перезапись переданными значениями) // Инициализация параметров (перезапись переданными значениями)
if ($parameters['name_first'] !== $market->name['first']) $market->name = ['first' => $parameters['name_first']] + $market->name; if ($parameters['name_first'] !== $market->name['first']) $market->name = ['first' => $parameters['name_first']] + $market->name;
if ($parameters['name_second'] !== $market->name['second']) $market->name = ['second' => $parameters['name_second']] + $market->name; if ($parameters['name_second'] !== $market->name['second']) $market->name = ['second' => $parameters['name_second']] + $market->name;
if ($parameters['name_last'] !== $market->name['last']) $market->name = ['last' => $parameters['name_last']] + $market->name; if ($parameters['name_last'] !== $market->name['last']) $market->name = ['last' => $parameters['name_last']] + $market->name;
@@ -472,7 +481,7 @@ final class market extends core
// Авторизован аккаунт оператора или магазина // Авторизован аккаунт оператора или магазина
// Инициализация данных магазинов // Инициализация данных магазинов
$this->view->markets = model::read(filter: 'd.active == true', amount: 10000, return: '{ _key: d._key, name: d.name }'); $this->view->markets = model::read(filter: 'd.active == true', amount: 10000, return: '{ id: d.id, name: d.name }');
// Универсализация // Универсализация
if ($this->view->markets instanceof _document) $this->view->markets = [$this->view->markets]; if ($this->view->markets instanceof _document) $this->view->markets = [$this->view->markets];

View File

@@ -8,6 +8,7 @@ namespace mirzaev\ebala\controllers;
use mirzaev\ebala\controllers\core, use mirzaev\ebala\controllers\core,
mirzaev\ebala\controllers\traits\errors, mirzaev\ebala\controllers\traits\errors,
mirzaev\ebala\models\account, mirzaev\ebala\models\account,
mirzaev\ebala\models\worker,
mirzaev\ebala\models\market; mirzaev\ebala\models\market;
// Библиотека для ArangoDB // Библиотека для ArangoDB
@@ -60,7 +61,7 @@ final class session extends core
// Проверка параметров на соответствование требованиям // Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Номер не может быть пустым'); if ($length === 0) throw new exception('Номер не может быть пустым');
if ($length !== 11) throw new exception('Номер должен иметь 11 цифр'); if ($length !== 11) throw new exception('Номер должен иметь 11 цифр');
if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['worker'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches)); if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['worker'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches ?? []));
if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') { if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') {
// Запрошено запоминание // Запрошено запоминание
@@ -76,11 +77,11 @@ final class session extends core
} }
// Поиск аккаунта // Поиск аккаунта
$account = account::read('d.number == "' . $parameters['worker'] . '"', amount: 1); $worker = worker::read('d.number == "' . $parameters['worker'] . '"', amount: 1);
// Генерация ответа по запрашиваемым параметрам // Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) { foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($account), 'exist' => $buffer['exist'] = isset($worker),
'account' => (function () use ($parameters, $remember, &$buffer) { 'account' => (function () use ($parameters, $remember, &$buffer) {
// Запись в буфер сессии // Запись в буфер сессии
if ($remember) $this->session->write(['entry' => ['number' => $parameters['worker']]], $this->errors); if ($remember) $this->session->write(['entry' => ['number' => $parameters['worker']]], $this->errors);
@@ -162,7 +163,7 @@ final class session extends core
// Проверка параметров на соответствование требованиям // Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Идентификатор аккаунта не может быть пустым'); if ($length === 0) throw new exception('Идентификатор аккаунта не может быть пустым');
if ($length > 12) throw new exception('Идентификатор аккаунта должен иметь не более 12 цифр'); if ($length > 12) throw new exception('Идентификатор аккаунта должен иметь не более 12 цифр');
if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['administrator'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches)); if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['administrator'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches ?? []));
if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') { if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') {
// Запрошено запоминание // Запрошено запоминание
@@ -268,7 +269,7 @@ final class session extends core
// Проверка параметров на соответствование требованиям // Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Идентификатор аккаунта не может быть пустым'); if ($length === 0) throw new exception('Идентификатор аккаунта не может быть пустым');
if ($length > 12) throw new exception('Идентификатор аккаунта должен иметь не более 12 цифр'); if ($length > 12) throw new exception('Идентификатор аккаунта должен иметь не более 12 цифр');
if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['operator'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches)); if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['operator'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches ?? []));
if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') { if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') {
// Запрошено запоминание // Запрошено запоминание
@@ -364,13 +365,13 @@ final class session extends core
// Проверка параметров на соответствование требованиям // Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Идентификатор аккаунта аккаунта не может быть пустым'); if ($length === 0) throw new exception('Идентификатор аккаунта аккаунта не может быть пустым');
if ($length > 40) throw new exception('Идентификатор аккаунта аккаунта должен иметь не более 40 символов'); if ($length > 40) throw new exception('Идентификатор аккаунта аккаунта должен иметь не более 40 символов');
if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['market'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches)); if (preg_match_all('/[^\d\(\)\-\s\r\n\t\0]+/u', $parameters['market'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches ?? []));
if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') { if ($remember = isset($parameters['remember']) && $parameters['remember'] === '1') {
// Запрошено запоминание // Запрошено запоминание
// Запись в cookie // Запись в cookie
setcookie('entry__key', $parameters['market'], [ setcookie('entry_id', $parameters['market'], [
'expires' => strtotime('+1 day'), 'expires' => strtotime('+1 day'),
'path' => '/', 'path' => '/',
'secure' => true, 'secure' => true,
@@ -379,15 +380,15 @@ final class session extends core
]); ]);
} }
// Поиск аккаунта // Поиск магазина
$account = account::read('d._key == "' . $parameters['market'] . '"', amount: 1); $market = market::read('d.id == "' . $parameters['market'] . '"', amount: 1);
// Генерация ответа по запрашиваемым параметрам // Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) { foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($account), 'exist' => $buffer['exist'] = isset($market),
'account' => (function () use ($parameters, $remember, &$buffer) { 'account' => (function () use ($parameters, $remember, &$buffer) {
// Запись в буфер сессии // Запись в буфер сессии
if ($remember) $this->session->write(['entry' => ['_key' => $parameters['market']]], $this->errors); if ($remember) $this->session->write(['entry' => ['id' => $parameters['market']]], $this->errors);
// Поиск аккаунта и запись в буфер вывода // Поиск аккаунта и запись в буфер вывода
$buffer['account'] = (new account($this->session, 'market', $this->errors))?->instance() instanceof _document; $buffer['account'] = (new account($this->session, 'market', $this->errors))?->instance() instanceof _document;
@@ -429,7 +430,7 @@ final class session extends core
// Запись в буфер сессии // Запись в буфер сессии
if (!in_array('account', $return, true) && ($remember ?? false)) if (!in_array('account', $return, true) && ($remember ?? false))
$this->session->write(['entry' => ['_key' => $parameters['market']]]); $this->session->write(['entry' => ['id' => $parameters['market']]]);
} }
/** /**
@@ -456,7 +457,7 @@ final class session extends core
// Проверка параметров на соответствование требованиям // Проверка параметров на соответствование требованиям
if ($length > 300) throw new exception('Пароль не может быть длиннее 300 символов'); if ($length > 300) throw new exception('Пароль не может быть длиннее 300 символов');
if (preg_match_all('/[^\w\s\r\n\t\0]+/u', $parameters['password'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches)); if (preg_match_all('/[^\w\-\_\d\s]+/u', $parameters['password'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches ?? []));
// Инициализация значения по умолчанию для типа аккаунта // Инициализация значения по умолчанию для типа аккаунта
$parameters['type'] ??= 'worker'; $parameters['type'] ??= 'worker';

View File

@@ -34,7 +34,7 @@ final class task extends core
/** /**
* Создать * Создать
* *
* @param array $parameters Параметры запроса * @param array $parameters Параметры запроса (json в php://input)
* *
* @return void В буфер вывода JSON-документ с запрашиваемыми параметрами * @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
*/ */
@@ -47,33 +47,50 @@ final class task extends core
// Инициализация буфера ошибок // Инициализация буфера ошибок
$this->errors['tasks'] ??= []; $this->errors['tasks'] ??= [];
// Создание строк if (!empty($json = json_decode(file_get_contents('php://input'), true, 4))) {
for ($i = 0, $parameters['cashiers'] = (int) $parameters['cashiers']; $i < $parameters['cashiers']; ++$i) model::create(work: 'Кассир', market: $this->account->type === 'market' ? account::market($this->account->getId())?->getKey() : null, start: $parameters['start'], end: $parameters['end'], date: $parameters['date'], errors: $this->errors['tasks']);
for ($i = 0, $parameters['displayers'] = (int) $parameters['displayers']; $i < $parameters['displayers']; ++$i) model::create(work: 'Выкладчик', market: $this->account->type === 'market' ? account::market($this->account->getId())?->getKey() : null, start: $parameters['start'], end: $parameters['end'], date: $parameters['date'], errors: $this->errors['tasks']);
for ($i = 0, $parameters['loaders'] = (int) $parameters['loaders']; $i < $parameters['loaders']; ++$i) model::create(work: 'Грузчик', market: $this->account->type === 'market' ? account::market($this->account->getId())?->getKey() : null, start: $parameters['start'], end: $parameters['end'], date: $parameters['date'], errors: $this->errors['tasks']);
for ($i = 0, $parameters['gastronomes'] = (int) $parameters['gastronomes']; $i < $parameters['gastronomes']; ++$i) model::create(work: 'Гастроном', market: $this->account->type === 'market' ? account::market($this->account->getId())?->getKey() : null, start: $parameters['start'], end: $parameters['end'], date: $parameters['date'], errors: $this->errors['tasks']);
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода foreach ($json as $work => $tasks) {
ob_start(); // Перебор категорий (колонок)
// Генерация ответа foreach ($tasks as $task) {
echo json_encode( // Перебор заявок
[
'errors' => self::parse_only_text($this->errors)
]
);
// Запись заголовков ответа // Создание заявки
header('Content-Length: ' . ob_get_length()); model::create(
work: model::label($work),
market: $this->account->type === 'market' ? account::market($this->account->getId())?->id : null,
start: $task['start'],
end: $task['end'],
date: $task['date'],
commentary: $task['commentary'],
errors: $this->errors['tasks']
);
}
}
// Отправка и деинициализация буфера вывода // Запись заголовков ответа
ob_end_flush(); header('Content-Type: application/json');
flush(); 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();
} else throw new exception('Не удалось инициализировать JSON-документ с данными заявок');
} else throw new exception('Вы не авторизованы'); } else throw new exception('Вы не авторизованы');
} catch (exception $e) { } catch (exception $e) {
// Запись в реестр ошибок // Запись в реестр ошибок
@@ -205,11 +222,11 @@ final class task extends core
// Инициализация данных для генерации HTML-документа с таблицей // Инициализация данных для генерации HTML-документа с таблицей
if ($_SERVER['INTERFACE'] === 'worker') if ($_SERVER['INTERFACE'] === 'worker')
$this->view->rows = model::list(before: 'FILTER task.worker == "' . account::worker($this->account->getId())?->getKey() . '"' . " && ($filters)"); $this->view->rows = model::list(before: 'FILTER task.worker == "' . account::worker($this->account->getId())?->id . '"' . " && ($filters)");
else if ($_SERVER['INTERFACE'] === 'operator') else if ($_SERVER['INTERFACE'] === 'operator')
$this->view->rows = model::list(before: "FILTER ($filters)"); $this->view->rows = model::list(before: "FILTER ($filters)");
else if ($_SERVER['INTERFACE'] === 'market') else if ($_SERVER['INTERFACE'] === 'market')
$this->view->rows = model::list(before: 'FILTER task.market == "' . account::market($this->account->getId())?->getKey() . '"' . " && ($filters)"); $this->view->rows = model::list(before: 'FILTER task.market == "' . account::market($this->account->getId())?->id . '"' . " && ($filters)");
else if ($_SERVER['INTERFACE'] === 'administrator') else if ($_SERVER['INTERFACE'] === 'administrator')
$this->view->rows = model::list(before: "FILTER ($filters)"); $this->view->rows = model::list(before: "FILTER ($filters)");
else $this->view->rows = []; else $this->view->rows = [];
@@ -234,10 +251,12 @@ final class task extends core
|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task._key, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task._key, TOKENS(@search, 'text_en')[0], 2, true))
|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task.worker, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task.worker, TOKENS(@search, 'text_en')[0], 2, true))
|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task.market, TOKENS(@search, 'text_en')[0], 2, true)) || (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(task.market, TOKENS(@search, 'text_en')[0], 2, true))
AQL; AQL;
// Инициализация данных для генерации HTML-документа с таблицей // Инициализация данных для генерации HTML-документа с таблицей
if ($_SERVER['INTERFACE'] === 'worker') if ($_SERVER['INTERFACE'] === 'worker') {
// Сотрудник
$this->view->rows = model::list( $this->view->rows = model::list(
before: sprintf( before: sprintf(
<<<AQL <<<AQL
@@ -249,9 +268,11 @@ AQL;
), ),
page: (int) $this->session->buffer['worker']['tasks']['page'], page: (int) $this->session->buffer['worker']['tasks']['page'],
target: empty($search) ? model::COLLECTION : 'registry_tasks', target: empty($search) ? model::COLLECTION : 'registry_tasks',
binds: ['worker' => account::worker($this->account->getId())?->getKey()] + (empty($search) ? [] : ['search' => $search]) binds: ['worker' => account::worker($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search])
); );
else if ($_SERVER['INTERFACE'] === 'market') } else if ($_SERVER['INTERFACE'] === 'market') {
// Магазин
$this->view->rows = model::list( $this->view->rows = model::list(
before: sprintf( before: sprintf(
<<<AQL <<<AQL
@@ -263,9 +284,11 @@ AQL;
), ),
page: (int) $this->session->buffer['market']['tasks']['page'], page: (int) $this->session->buffer['market']['tasks']['page'],
target: empty($search) ? model::COLLECTION : 'registry_tasks', target: empty($search) ? model::COLLECTION : 'registry_tasks',
binds: ['market' => account::market($this->account->getId())?->getKey()] + (empty($search) ? [] : ['search' => $search]) binds: ['market' => account::market($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search])
); );
else if ($_SERVER['INTERFACE'] === 'operator') } else if ($_SERVER['INTERFACE'] === 'operator') {
// Оператор
$this->view->rows = model::list( $this->view->rows = model::list(
before: sprintf( before: sprintf(
<<<AQL <<<AQL
@@ -281,7 +304,9 @@ AQL;
'search' => $search 'search' => $search
] ]
); );
else if ($_SERVER['INTERFACE'] === 'administrator') } else if ($_SERVER['INTERFACE'] === 'administrator') {
// Администратор
$this->view->rows = model::list( $this->view->rows = model::list(
before: sprintf( before: sprintf(
<<<AQL <<<AQL
@@ -297,9 +322,9 @@ AQL;
'search' => $search 'search' => $search
] ]
); );
else $this->view->rows = []; } else $this->view->rows = [];
// Запись в cookie (только таким методом можно записать "hostonly: true") // Запись в cookie
setcookie( setcookie(
'tasks_page', 'tasks_page',
(string) $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['page'], (string) $this->session->buffer[$_SERVER['INTERFACE']]['tasks']['page'],
@@ -435,7 +460,8 @@ AQL;
$link->task['start'] instanceof datetime $link->task['start'] instanceof datetime
&& $link->task['end'] instanceof datetime && $link->task['end'] instanceof datetime
) { ) {
$generated['hours'] = (float) $link->task['end']->diff($link->task['start'])->format('%H.%i'); $generated['hours'] = (float) $link->task['start']->diff($link->task['end'])->format('%R%H.%i');
if ($generated['hours'] < 0) $generated['hours'] += 24;
if ($generated['hours'] >= 6.5 && $generated['hours'] < 9) $generated['hours'] -= 0.5; if ($generated['hours'] >= 6.5 && $generated['hours'] < 9) $generated['hours'] -= 0.5;
else if ($generated['hours'] >= 9 && $generated['hours'] < 12.5) $generated['hours'] -= 1; else if ($generated['hours'] >= 9 && $generated['hours'] < 12.5) $generated['hours'] -= 1;
else if ($generated['hours'] >= 12.5) $generated['hours'] -= 1.5; else if ($generated['hours'] >= 12.5) $generated['hours'] -= 1.5;
@@ -503,7 +529,7 @@ AQL;
// Найдена заявка // Найдена заявка
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для редактирования этой заявки'); throw new exception('Вы не авторизованы для редактирования этой заявки');
// Заявка подтверждена? // Заявка подтверждена?
@@ -557,14 +583,14 @@ AQL;
} else { } else {
// Записать нового сотрудника // Записать нового сотрудника
if (($worker = worker::read('d._key == "' . $parameters['worker'] . '" && d.active == true', amount: 1)) instanceof _document) { if (($worker = worker::read('d.id == "' . $parameters['worker'] . '" && d.active == true', amount: 1)) instanceof _document) {
// Найден сотрудник (запрашиваемый для записи сотрудник существует в базе данных) // Найден сотрудник (запрашиваемый для записи сотрудник существует в базе данных)
if ($task->worker !== $parameters['worker']) { if ($task->worker !== $parameters['worker']) {
// Идентификатор запрашиваемого сотрудника не равен актуальному // Идентификатор запрашиваемого сотрудника не равен актуальному
// Запись сотрудника // Запись сотрудника
$task->worker = $worker->getKey(); $task->worker = $worker->id;
// Снятие с публикации // Снятие с публикации
$task->published = false; $task->published = false;
@@ -653,14 +679,14 @@ AQL;
} else { } else {
// Записать новый магазин // Записать новый магазин
if (($market = market::read('d._key == "' . $parameters['market'] . '" && d.active == true', amount: 1)) instanceof _document) { if (($market = market::read('d.id == "' . $parameters['market'] . '" && d.active == true', amount: 1)) instanceof _document) {
// Найден магазин (запрашиваемый для записи магазин существует в базе данных) // Найден магазин (запрашиваемый для записи магазин существует в базе данных)
if ($task->market !== $parameters['market']) { if ($task->market !== $parameters['market']) {
// Идентификатор запрашиваемого сотрудника не равен актуальному // Идентификатор запрашиваемого сотрудника не равен актуальному
// Запись магазина // Запись магазина
$task->market = $market->getKey(); $task->market = $market->id;
if (_core::update($task)) { if (_core::update($task)) {
// Записано изменение в базу данных // Записано изменение в базу данных
@@ -757,7 +783,7 @@ AQL;
)->getAll(); )->getAll();
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $this->view->task['market'] !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $this->view->task['market'] !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для чтения этой заявки'); throw new exception('Вы не авторизованы для чтения этой заявки');
// Удаление данных из выдачи // Удаление данных из выдачи
@@ -892,9 +918,9 @@ AQL;
// Инициализация данных сотрудника // Инициализация данных сотрудника
$this->view->worker = worker::read( $this->view->worker = worker::read(
'd._key == "' . $parameters['worker'] . '"', 'd.id == "' . $parameters['worker'] . '"',
return: $this->account->type === 'market' return: $this->account->type === 'market'
? '{_key: d._key, created: d.created, updated: d.updated, name: d.name, number: d.number, mail: d.mail, birth: d.birth, rating: d.rating}' ? '{id: d.id, created: d.created, updated: d.updated, name: d.name, number: d.number, mail: d.mail, birth: d.birth, rating: d.rating}'
: 'd' : 'd'
)->getAll(); )->getAll();
@@ -1011,7 +1037,7 @@ AQL;
// Авторизован аккаунт администратора или оператора // Авторизован аккаунт администратора или оператора
// Инициализация данных // Инициализация данных
$this->view->market = market::read('d._key == "' . $parameters['market'] . '"')?->getAll(); $this->view->market = market::read('d.id == "' . $parameters['market'] . '"')?->getAll();
if (!empty($this->view->market)) { if (!empty($this->view->market)) {
// Найдены данные магазина // Найдены данные магазина
@@ -1210,7 +1236,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для заявления о проблеме с этой заявкой'); throw new exception('Вы не авторизованы для заявления о проблеме с этой заявкой');
// Инициализация даты // Инициализация даты
@@ -1329,7 +1355,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для завершения этой заявки'); throw new exception('Вы не авторизованы для завершения этой заявки');
// Заявка завершена? // Заявка завершена?
@@ -1552,7 +1578,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для редактирования типа работы этой заявки'); throw new exception('Вы не авторизованы для редактирования типа работы этой заявки');
// Заявка подтверждена? // Заявка подтверждена?
@@ -1665,7 +1691,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для чтения параметров этой заявки'); throw new exception('Вы не авторизованы для чтения параметров этой заявки');
if ($task instanceof _document) { if ($task instanceof _document) {
@@ -1749,7 +1775,7 @@ AQL;
// Найдена заявка // Найдена заявка
// Инициализация списка работ // Инициализация списка работ
$this->view->works = ['Кассир', 'Выкладчик', рузчик', астроном']; $this->view->works = ['Кассир', 'Выкладчик', 'Гастроном', 'Бригадир', 'Грузчик', 'Мобильный грузчик', 'Мобильный универсал'];
// Проверка на существование записанной в задаче работы в списке существующих работ и запись об этом в глобальную переменную шаблонизатора // Проверка на существование записанной в задаче работы в списке существующих работ и запись об этом в глобальную переменную шаблонизатора
foreach ($this->view->works as $work) if ($this->view->task->work === $work) $this->view->exist = true; foreach ($this->view->works as $work) if ($this->view->task->work === $work) $this->view->exist = true;
@@ -1828,7 +1854,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для редактирования типа работы этой заявки'); throw new exception('Вы не авторизованы для редактирования типа работы этой заявки');
// Заявка подтверждена? // Заявка подтверждена?
@@ -1947,7 +1973,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для редактирования описания этой заявки'); throw new exception('Вы не авторизованы для редактирования описания этой заявки');
// Заявка подтверждена? // Заявка подтверждена?
@@ -2056,7 +2082,7 @@ AQL;
$task = model::read('d._key == "' . $parameters['task'] . '"'); $task = model::read('d._key == "' . $parameters['task'] . '"');
// Заявка не принадлежит запросившему магазину? // Заявка не принадлежит запросившему магазину?
if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->getKey()) if ($this->account->type === 'market' and $task->market !== account::market($this->account->getId())?->id)
throw new exception('Вы не авторизованы для редактирования даты и времени этой заявки'); throw new exception('Вы не авторизованы для редактирования даты и времени этой заявки');
// Заявка подтверждена? // Заявка подтверждена?
@@ -2447,7 +2473,7 @@ AQL;
if ( if (
$this->account->type === 'operator' $this->account->type === 'operator'
|| $this->account->type === 'administrator' || $this->account->type === 'administrator'
|| ($this->account->type === 'worker' && $task->worker === account::worker($this->account->getId())?->getKey()) || ($this->account->type === 'worker' && $task->worker === account::worker($this->account->getId())?->id)
) { ) {
// Авторизован аккаунт (если сотрудник, то назначен на эту заявку) // Авторизован аккаунт (если сотрудник, то назначен на эту заявку)
@@ -2513,7 +2539,7 @@ AQL;
if ( if (
$this->account->type === 'operator' $this->account->type === 'operator'
|| $this->account->type === 'administrator' || $this->account->type === 'administrator'
|| ($this->account->type === 'market' && $task->market === account::market($this->account->getId())?->getKey()) || ($this->account->type === 'market' && $task->market === account::market($this->account->getId())?->id)
) { ) {
// Авторизован аккаунт (если магазин, то назначен на эту заявку) // Авторизован аккаунт (если магазин, то назначен на эту заявку)
@@ -2664,7 +2690,7 @@ AQL;
if ( if (
$this->account->type === 'operator' $this->account->type === 'operator'
|| $this->account->type === 'administrator' || $this->account->type === 'administrator'
|| ($this->account->type === 'worker' && $task->worker === account::worker($this->account->getId())?->getKey()) || ($this->account->type === 'worker' && $task->worker === account::worker($this->account->getId())?->id)
) { ) {
// Авторизован аккаунт (если сотрудник, то назначен на эту заявку) // Авторизован аккаунт (если сотрудник, то назначен на эту заявку)
@@ -2678,7 +2704,7 @@ AQL;
'from' => [ 'from' => [
'_key' => $this->account->getKey(), '_key' => $this->account->getKey(),
'type' => $this->account->type 'type' => $this->account->type
], ] + ($this->account->type === 'worker' ? ['id' => $task->worker] : []),
'type' => $parameters['type'] ?? 'message', 'type' => $parameters['type'] ?? 'message',
'text' => $parameters['text'], 'text' => $parameters['text'],
'readed' => [$this->account->getKey() => 0], 'readed' => [$this->account->getKey() => 0],
@@ -2736,7 +2762,7 @@ AQL;
if ( if (
$this->account->type === 'operator' $this->account->type === 'operator'
|| $this->account->type === 'administrator' || $this->account->type === 'administrator'
|| ($this->account->type === 'market' && $task->market === account::market($this->account->getId())?->getKey()) || ($this->account->type === 'market' && $task->market === account::market($this->account->getId())?->id)
) { ) {
// Авторизован аккаунт (если магазин, то назначен на эту заявку) // Авторизован аккаунт (если магазин, то назначен на эту заявку)
@@ -2750,7 +2776,7 @@ AQL;
'from' => [ 'from' => [
'_key' => $this->account->getKey(), '_key' => $this->account->getKey(),
'type' => $this->account->type 'type' => $this->account->type
], ] + ($this->account->type === 'market' ? ['id' => $task->market] : []),
'type' => $parameters['type'] ?? 'message', 'type' => $parameters['type'] ?? 'message',
'text' => $parameters['text'], 'text' => $parameters['text'],
'readed' => [$this->account->getKey() => 0], 'readed' => [$this->account->getKey() => 0],

View File

@@ -39,7 +39,7 @@ final class worker extends core
// Авторизация // Авторизация
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт оператора или администратора // Авторизован аккаунт оператора или администратора
foreach (['active', 'inactive', 'fined', 'decent', 'hided', 'fired'] as $name) { foreach (['active', 'inactive', 'fined', 'decent', 'hided', 'fired'] as $name) {
// Перебор фильтров статусов // Перебор фильтров статусов
@@ -147,6 +147,7 @@ final class worker extends core
|| a.department.address IN TOKENS(@search, 'text_ru') || a.department.address IN TOKENS(@search, 'text_ru')
|| a.requisites IN TOKENS(@search, 'text_ru') || a.requisites IN TOKENS(@search, 'text_ru')
|| STARTS_WITH(a._key, @search) || STARTS_WITH(a._key, @search)
|| STARTS_WITH(a.id, @search)
|| STARTS_WITH(a.name.first, @search) || STARTS_WITH(a.name.first, @search)
|| STARTS_WITH(a.name.second, @search) || STARTS_WITH(a.name.second, @search)
|| STARTS_WITH(a.name.last, @search) || STARTS_WITH(a.name.last, @search)
@@ -161,6 +162,7 @@ final class worker extends core
|| STARTS_WITH(a.requisites, @search) || STARTS_WITH(a.requisites, @search)
|| STARTS_WITH(a.tax, @search) || STARTS_WITH(a.tax, @search)
|| (LENGTH(@search) > 5 && LEVENSHTEIN_MATCH(a._key, TOKENS(@search, 'text_en')[0], 2, true)) || (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.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.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) > 3 && LEVENSHTEIN_MATCH(a.name.last, TOKENS(@search, 'text_ru')[0], 2, true))
@@ -251,6 +253,7 @@ final class worker extends core
else if (!empty($parameters['account_number']) && strlen($parameters['account_number']) < 11) throw new exception('Несоответствие формату SIM-номера аккаунта сотрудника'); else if (!empty($parameters['account_number']) && strlen($parameters['account_number']) < 11) throw new exception('Несоответствие формату SIM-номера аккаунта сотрудника');
else if (!empty($parameters['worker_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['worker_mail']) === 0) throw new exception('Несоответствие формату почты сотрудника'); else if (!empty($parameters['worker_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['worker_mail']) === 0) throw new exception('Несоответствие формату почты сотрудника');
else if (!empty($parameters['account_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['account_mail']) === 0) throw new exception('Несоответствие формату почты аккаунта сотрудника'); else if (!empty($parameters['account_mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['account_mail']) === 0) throw new exception('Несоответствие формату почты аккаунта сотрудника');
else if (!empty($parameters['worker_id']) && model::read('d.id == "' . $parameters['worker_id'] . '"', errors: $this->errors['account']) instanceof _document) throw new exception('Уже существует сотрудник с данным идентификатором');
// Универсализация // Универсализация
/* $parameters['worker_number'] = (int) $parameters['worker_number']; */ /* $parameters['worker_number'] = (int) $parameters['worker_number']; */
@@ -269,7 +272,7 @@ final class worker extends core
'second' => $parameters['account_name_second'], 'second' => $parameters['account_name_second'],
'last' => $parameters['account_name_last'] 'last' => $parameters['account_name_last']
], ],
'number' => $parameters['account_number'] === 0 ? '' : $parameters['account_number'], 'number' => $parameters['account_number'],
'mail' => $parameters['account_mail'], 'mail' => $parameters['account_mail'],
'password' => sodium_crypto_pwhash_str( 'password' => sodium_crypto_pwhash_str(
$parameters['account_password'], $parameters['account_password'],
@@ -293,8 +296,8 @@ final class worker extends core
]; ];
} }
// Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных) // Инициализация идентификатора сотрудника
$_key = preg_replace('/.+\//', '', $account ?? ''); $id = empty($parameters['worker_id']) ? model::id() : $parameters['worker_id'];
// Запись заголовков ответа // Запись заголовков ответа
header('Content-Type: application/json'); header('Content-Type: application/json');
@@ -308,7 +311,7 @@ final class worker extends core
echo json_encode( echo json_encode(
[ [
'clipboard' => empty($this->errors['account']) ? <<<TEXT 'clipboard' => empty($this->errors['account']) ? <<<TEXT
Идентификатор: $_key Номер: {$parameters['account_number']}
Пароль: {$parameters['account_password']} Пароль: {$parameters['account_password']}
TEXT : '', TEXT : '',
'errors' => self::parse_only_text($this->errors['account']) 'errors' => self::parse_only_text($this->errors['account'])
@@ -323,40 +326,46 @@ final class worker extends core
flush(); flush();
try { try {
// Создание сотрудника if (isset($account)) {
$worker = model::create( // Инициализирован аккаунт
data: [
'name' => [
'first' => $parameters['worker_name_first'],
'second' => $parameters['worker_name_second'],
'last' => $parameters['worker_name_last']
],
'number' => $parameters['worker_number'] === 0 ? '' : $parameters['worker_number'],
'mail' => $parameters['worker_mail'],
'birth' => $parameters['worker_birth'],
'passport' => $parameters['worker_passport'],
'issued' => $parameters['worker_issued'],
'department' => [
'number' => $parameters['worker_department_number'],
'address' => $parameters['worker_department_address']
],
'requisites' => $parameters['worker_requisites'],
'payment' => $parameters['worker_payment'],
'tax' => $parameters['worker_tax'],
'city' => $parameters['worker_city'],
'district' => $parameters['worker_district'],
'address' => $parameters['worker_address'],
'hiring' => $parameters['worker_hiring'],
'rating' => 3
],
errors: $this->errors['account']
);
// Проверка существования созданного сотрудника // Создание сотрудника
if (empty($worker)) throw new exception('Не удалось создать сотрудника'); $worker = model::create(
data: [
'id' => (string) $id,
'name' => [
'first' => $parameters['worker_name_first'],
'second' => $parameters['worker_name_second'],
'last' => $parameters['worker_name_last']
],
'number' => $parameters['worker_number'],
'mail' => $parameters['worker_mail'],
'birth' => $parameters['worker_birth'],
'passport' => $parameters['worker_passport'],
'issued' => $parameters['worker_issued'],
'department' => [
'number' => $parameters['worker_department_number'],
'address' => $parameters['worker_department_address']
],
'requisites' => $parameters['worker_requisites'],
'payment' => $parameters['worker_payment'],
'tax' => $parameters['worker_tax'],
'city' => $parameters['worker_city'],
'district' => $parameters['worker_district'],
'address' => $parameters['worker_address'],
'hiring' => $parameters['worker_hiring'],
'rating' => 3
],
errors: $this->errors['account']
);
// Создание ребра: account -> worker // Проверка существования созданного сотрудника
account::connect($account, $worker, 'worker', $this->errors['account']); if (empty($worker)) throw new exception('Не удалось создать сотрудника');
// Создание ребра: account -> worker
account::connect($account, $worker, 'worker', $this->errors['account']);
}
throw new exception('Не инициализирован аккаунт');
} catch (exception $e) { } catch (exception $e) {
// Write to the errors registry // Write to the errors registry
$this->errors['account'][] = [ $this->errors['account'][] = [
@@ -380,7 +389,7 @@ final class worker extends core
// Авторизован аккаунт администратора или оператора // Авторизован аккаунт администратора или оператора
// Инициализация данных сотрудника // Инициализация данных сотрудника
$worker = model::read('d._key == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, birth: d.birth, passport: d.passport, issued: d.issued, department: d.department, requisites: d.requisites, payment: d.payment, tax: d.tax, city: d.city, district: d.district, address: d.address, hiring: d.hiring}')->getAll(); $worker = model::read('d.id == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, birth: d.birth, passport: d.passport, issued: d.issued, department: d.department, requisites: d.requisites, payment: d.payment, tax: d.tax, city: d.city, district: d.district, address: d.address, hiring: d.hiring}')->getAll();
if (!empty($worker)) { if (!empty($worker)) {
// Найдены данные сотрудника // Найдены данные сотрудника
@@ -420,11 +429,11 @@ final class worker extends core
// Авторизован аккаунт администратора или оператора // Авторизован аккаунт администратора или оператора
// Инициализация данных сотрудника // Инициализация данных сотрудника
$worker = model::read('d._key == "' . $parameters['id'] . '"'); $worker = model::read('d.id == "' . $parameters['id'] . '"');
if (!empty($worker)) { if (!empty($worker)) {
// Найден сотрудник // Найден сотрудник
// Универсализация // Универсализация
if (!empty($parameters['birth'])) $parameters['birth'] = DateTime::createFromFormat('Y-m-d', $parameters['birth'])->getTimestamp(); if (!empty($parameters['birth'])) $parameters['birth'] = DateTime::createFromFormat('Y-m-d', $parameters['birth'])->getTimestamp();
if (!empty($parameters['issued'])) $parameters['issued'] = DateTime::createFromFormat('Y-m-d', $parameters['issued'])->getTimestamp(); if (!empty($parameters['issued'])) $parameters['issued'] = DateTime::createFromFormat('Y-m-d', $parameters['issued'])->getTimestamp();
@@ -515,7 +524,7 @@ final class worker extends core
// Авторизован аккаунт оператора или магазина // Авторизован аккаунт оператора или магазина
// Инициализация данных сотрудников // Инициализация данных сотрудников
$this->view->workers = model::read(filter: 'd.active == true', amount: 10000, return: '{ _key: d._key, name: d.name }'); $this->view->workers = model::read(filter: 'd.active == true', amount: 10000, return: '{ id: d.id, name: d.name }');
// Универсализация // Универсализация
if ($this->view->workers instanceof _document) $this->view->workers = [$this->view->workers]; if ($this->view->workers instanceof _document) $this->view->workers = [$this->view->workers];

View File

@@ -129,13 +129,13 @@ final class account extends core
throw new exception('Неизвестная ошибка на этапе проверки пароля'); throw new exception('Неизвестная ошибка на этапе проверки пароля');
} }
} else throw new exception('Не найден пароль в буфере сессии'); } else throw new exception('Не найден пароль в буфере сессии');
} else if (!empty($session->buffer['market']['entry']['_key'])) { } else if (!empty($session->buffer['market']['entry']['id'])) {
// Найден идентификатор магазина в буфере сессии // Найден идентификатор магазина в буфере сессии
if (!empty($session->buffer['market']['entry']['password'])) { if (!empty($session->buffer['market']['entry']['password'])) {
// Найден пароль в буфере сессии // Найден пароль в буфере сессии
if (($account = self::read('d._key == "' . $session->buffer['market']['entry']['_key'] . '" && d.type == "market"', amount: 1)) instanceof _document) { if (($account = market::account(market::read('d.id == "' . $session->buffer['market']['entry']['id'] . '"', amount: 1)?->getId()) ?? null) instanceof _document) {
// Найден аккаунт (игнорируются ошибки) // Найден аккаунт (игнорируются ошибки)
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['market']['entry']['password'])) { if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['market']['entry']['password'])) {
@@ -286,7 +286,7 @@ final class account extends core
RETURN e RETURN e
) )
FILTER d._id == e[0]._to && d.active == true FILTER d._id == e[0]._to && d.active == true
SORT d.created DESC, d._key DESC SORT d.created DESC, d.id DESC
LIMIT 1 LIMIT 1
RETURN d RETURN d
AQL, AQL,
@@ -316,7 +316,7 @@ final class account extends core
* *
* Ищет связь аккаунта с сотрудником, если не находит, то создаёт её * Ищет связь аккаунта с сотрудником, если не находит, то создаёт её
* *
* @param string $worker Идентификатор инстанции документа аккаунта в базе данных * @param string $account Идентификатор инстанции документа аккаунта в базе данных
* @param string $target Идентификатор инстанции документа цели в базе данны (подразумевается сотрудник или магазин) * @param string $target Идентификатор инстанции документа цели в базе данны (подразумевается сотрудник или магазин)
* @param string $type Тип подключения (worker|market) * @param string $type Тип подключения (worker|market)
* @param array &$errors Реестр ошибок * @param array &$errors Реестр ошибок
@@ -342,11 +342,11 @@ final class account extends core
LIMIT 1 LIMIT 1
RETURN d RETURN d
AQL, AQL,
self::COLLECTION . '_edge_' . worker::COLLECTION, self::COLLECTION . "_edge_$type",
$account, $account,
$target $target
)) instanceof _document )) instanceof _document
|| $id = document::write(static::$arangodb->session, self::COLLECTION . "_edge_$type", [ || document::write(static::$arangodb->session, self::COLLECTION . "_edge_$type", [
'_from' => $account, '_from' => $account,
'_to' => $target '_to' => $target
]) ])
@@ -381,7 +381,7 @@ final class account extends core
{ {
try { try {
if (collection::init(static::$arangodb->session, self::COLLECTION)) if (collection::init(static::$arangodb->session, self::COLLECTION))
if ($id = document::write(static::$arangodb->session, self::COLLECTION, $data + ['active' => true])) return $id; if ($id = (string) document::write(static::$arangodb->session, self::COLLECTION, $data + ['active' => true])) return $id;
else throw new exception('Не удалось создать аккаунт'); else throw new exception('Не удалось создать аккаунт');
else throw new exception('Не удалось инициализировать коллекцию'); else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) { } catch (exception $e) {

View File

@@ -132,6 +132,82 @@ class core extends model
return null; return null;
} }
/**
* Count documents in ArangoDB
*
* @param ?string $collection Коллекция для подсчёта
* @param array &$errors Реестр ошибок
*
* @return int|null Количество документов в базе данных, если найдены
*/
public static function count(?string $collection = null, array &$errors = []): int|null {
try {
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
// Инициализирована коллекция
// Exit (success)
return collection::search(
static::$arangodb->session,
sprintf(
<<<'AQL'
RETURN LENGTH(%s)
AQL,
$collection ?? static::COLLECTION
)
);
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Generate identifier
*
* @param array &$errors Реестр ошибок
*
* @return int Идентиикатор (свободный)
*/
public static function id(array &$errors = []): int
{
try {
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
// Инициализирована коллекция
// Exit (success)
return collection::search(
static::$arangodb->session,
sprintf(
<<<'AQL'
RETURN MAX((FOR d in %s RETURN +d.id))
AQL,
$collection ?? static::COLLECTION
)
) + 1;
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return 0;
}
/** /**
* Delete from ArangoDB * Delete from ArangoDB
* *

View File

@@ -40,7 +40,7 @@ final class task extends core
/** /**
* Create task in ArangoDB * Create task in ArangoDB
* *
* @param ?string $date * @param string|int|null $date
* @param ?string $worker * @param ?string $worker
* @param ?string $work * @param ?string $work
* @param ?string $start * @param ?string $start
@@ -51,12 +51,13 @@ final class task extends core
* @param bool $hided * @param bool $hided
* @param bool $problematic * @param bool $problematic
* @param bool $completed * @param bool $completed
* @param ?string $commentary
* @param array $errors * @param array $errors
* *
* @return ?string Identificator of instance of ArangoDB * @return ?string Identificator of instance of ArangoDB
*/ */
public static function create( public static function create(
?string $date = null, string|int|null $date = null,
?string $worker = null, ?string $worker = null,
?string $work = null, ?string $work = null,
?string $start = null, ?string $start = null,
@@ -67,6 +68,7 @@ final class task extends core
bool $hided = false, bool $hided = false,
bool $problematic = false, bool $problematic = false,
bool $completed = false, bool $completed = false,
?string $commentary = null,
array &$errors = [] array &$errors = []
): ?string { ): ?string {
try { try {
@@ -90,6 +92,7 @@ final class task extends core
'hided' => $hided, 'hided' => $hided,
'problematic' => $problematic, 'problematic' => $problematic,
'completed' => $completed, 'completed' => $completed,
'commentary' => $commentary,
]); ]);
} else throw new exception('Не удалось инициализировать коллекции'); } else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) { } catch (exception $e) {
@@ -145,8 +148,8 @@ final class task extends core
<<<AQL <<<AQL
FOR task IN %s FOR task IN %s
%s %s
LET worker = (FOR worker in %s FILTER worker._key LIKE task.worker SORT worker.created DESC, worker._key DESC LIMIT 1 RETURN worker)[0] LET worker = (FOR worker in %s FILTER worker.id != null && worker.id LIKE task.worker SORT worker.created DESC, worker.id DESC LIMIT 1 RETURN worker)[0]
LET market = (FOR market in %s FILTER market._key LIKE task.market SORT market.created DESC, market._key DESC LIMIT 1 RETURN market)[0] LET market = (FOR market in %s FILTER market.id != null && market.id LIKE task.market SORT market.created DESC, market.id DESC LIMIT 1 RETURN market)[0]
%s %s
SORT %s SORT %s
LIMIT %d, %d LIMIT %d, %d
@@ -180,4 +183,25 @@ final class task extends core
// Exit (fail) // Exit (fail)
return []; return [];
} }
/**
* Generate work type label in Russian
*
* @param string $work Type of work
*
* @return string
*/
public static function label(string $work): string
{
return match (mb_strtolower($work)) {
'cashiers', 'cashier', 'кассиры', 'кассир' => 'Кассир',
'displayers', 'displayer', 'выкладчики', 'выкладчик' => 'Выкладчик',
'gastronomes', 'gastronome', 'гастрономы', 'гастроном' => 'Гастроном',
'brigadiers', 'brigadier', 'бригадиры', 'бригадир' => 'Бригадир',
'loaders', 'loader', 'грузчики', 'грузчик' => 'Грузчик',
'loaders_mobile', 'loader_mobile', 'мобильные грузчики', 'мобильный грузчик' => 'Мобильный грузчик',
'universals_mobile', 'universal_mobile', 'мобильные универсалы', 'мобильный универсал' => 'Мобильный универсал',
default => $work
};
}
} }

View File

@@ -302,12 +302,14 @@ label * {
} }
textarea { textarea {
--padding-x: 12px;
--padding-y: 8px;
width: 100%; width: 100%;
min-width: calc(100% - 24px); min-width: calc(100% - 24px);
min-height: 120px; min-height: 120px;
max-width: calc(100% - 24px); max-width: calc(100% - 24px);
max-height: 300px; max-height: 300px;
padding: 8px 12px; padding: var(--padding-y, 8px) var(--padding-x, 12px);
font-size: smaller; font-size: smaller;
overflow: hidden; overflow: hidden;
border-radius: 3px; border-radius: 3px;

View File

@@ -32,12 +32,6 @@ section#administrators.panel.list > div.row > span[data-column="account"] {
text-align: center; text-align: center;
} }
section#administrators.panel.list
> div.row:nth-of-type(1)
> span[data-column="account"] {
margin-top: 6px;
}
section#administrators.panel.list > div.row > span[data-column="name"] { section#administrators.panel.list > div.row > span[data-column="name"] {
min-width: 130px; min-width: 130px;
width: 130px; width: 130px;

View File

@@ -30,18 +30,22 @@ section#markets.panel.list
} }
section#markets.panel.list > div.row > span:is([data-column="account"], [data-column="market"]) { section#markets.panel.list > div.row > span:is([data-column="account"], [data-column="market"]) {
min-width: 102px;
width: 102px;
font-weight: bold; font-weight: bold;
}
section#markets.panel.list > div.row:nth-of-type(1) > span[data-column="account"] {
text-align: center; text-align: center;
} }
section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="account"] { section#markets.panel.list > div.row > span[data-column="account"] {
margin-top: 6px; min-width: 102px;
width: 102px;
text-align: center;
} }
section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="market"] { section#markets.panel.list > div.row > span[data-column="market"] {
margin-top: 0px; min-width: 67px;
width: 67px;
} }
section#markets.panel.list > div.row > span[data-column="name"] { section#markets.panel.list > div.row > span[data-column="name"] {

View File

@@ -32,10 +32,6 @@ section#operators.panel.list > div.row > span[data-column="account"] {
text-align: center; text-align: center;
} }
section#operators.panel.list > div.row:nth-of-type(1) > span[data-column="account"] {
margin-top: 6px;
}
section#operators.panel.list > div.row > span[data-column="name"] { section#operators.panel.list > div.row > span[data-column="name"] {
min-width: 130px; min-width: 130px;
width: 130px; width: 130px;

View File

@@ -34,14 +34,26 @@ section#tasks.panel.list > div.row > span[data-column="date"] {
font-weight: bold; font-weight: bold;
} }
section#tasks.panel.list > div.row > span:is([data-column="worker"], [data-column="market"]) { section#tasks.panel.list > div.row > span:is([data-column="market"], [data-column="worker"]) {
min-width: 102px;
width: 102px;
font-weight: bold; font-weight: bold;
} }
section#tasks.panel.list > div.row:nth-of-type(1) > span[data-column="market"] {
text-align: center;
}
section#tasks.panel.list > div.row:nth-of-type(1) > span[data-column="worker"] { section#tasks.panel.list > div.row:nth-of-type(1) > span[data-column="worker"] {
margin-top: 8px; margin-top: 8px;
}
section#tasks.panel.list > div.row > span[data-column="market"] {
min-width: 67px;
width: 67px;
text-align: right;
}
section#tasks.panel.list > div.row > span[data-column="worker"] {
min-width: 67px;
width: 67px;
} }
section#tasks.panel.list > div.row > span[data-column="name"] { section#tasks.panel.list > div.row > span[data-column="name"] {

View File

@@ -32,18 +32,26 @@ section#workers.panel.list
} }
section#workers.panel.list > div.row > span:is([data-column="account"], [data-column="worker"]) { section#workers.panel.list > div.row > span:is([data-column="account"], [data-column="worker"]) {
min-width: 102px;
width: 102px;
font-weight: bold; font-weight: bold;
text-align: center;
} }
section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="account"] { section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="account"] {
margin-top: 6px; text-align: center;
} }
section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="worker"] { section#workers.panel.list > div.row:nth-of-type(1) > span[data-column="worker"] {
margin-top: 8px; margin-top: 8px;
}
section#workers.panel.list > div.row > span[data-column="account"] {
min-width: 102px;
width: 102px;
text-align: center;
}
section#workers.panel.list > div.row > span[data-column="worker"] {
min-width: 67px;
width: 67px;
} }
section#workers.panel.list > div.row > span[data-column="name"] { section#workers.panel.list > div.row > span[data-column="name"] {

View File

@@ -44,9 +44,12 @@ div#popup>section.calculated {
} }
div#popup>section.list { div#popup>section.list {
max-width: max(70vw, 1300px);
max-height: max(62vh, 600px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 30px; padding: 30px;
overflow-y: scroll;
border-radius: 3px; border-radius: 3px;
} }
@@ -65,17 +68,30 @@ div#popup>section.list h4 {
} }
div#popup>section.list>section.main { div#popup>section.list>section.main {
--gap: 15px;
display: flex; display: flex;
gap: 15px; flex-flow: row wrap;
justify-content: space-between;
gap: var(--gap, 15px);
} }
div#popup>section.list>section.main>div.column { div#popup>section.list>section.main>div.column {
flex-grow: 1;
display: flex; display: flex;
flex-grow: 1;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
} }
div#popup>section.list>section.main.flow>div.column:not(:only-child) {
width: 300px;
}
div#popup>section.list>section.main>div.column:not(:only-child)[data-column="buttons"]:last-of-type {
margin-left: auto;
justify-content: end;
}
div#popup>section.list>section.main>div.column:only-child { div#popup>section.list>section.main>div.column:only-child {
width: 100%; width: 100%;
} }
@@ -140,7 +156,21 @@ div#popup>section.list>section.main>div.column> :is(div, select).row.buttons {
div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons, .stretchable, .endless), div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons, .stretchable, .endless),
div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons, .stretchable, .endless)>button { div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons, .stretchable, .endless)>button {
height: 29px; --height: 29px;
height: var(--height, 29px);
}
div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons .endless).stretchable,
div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons, .endless).stretchable>button {
--height: 29px;
height: max(var(--height, 29px), fit-content);
}
div#popup>section.list>section.main>div.column> :is(div, select).row:not(.buttons .endless).stretchable>textarea {
/* min-height: calc(var(--height, 29px) - var(--padding-y, 8ox) * 2); */
min-height: 1rem;
max-height: 3rem;
height: 1rem;
} }
div#popup>section.list>section.main>div.column>:is(div, section).row:not(.merged)+:is(div, section).row.merged { div#popup>section.list>section.main>div.column>:is(div, section).row:not(.merged)+:is(div, section).row.merged {

View File

@@ -119,6 +119,7 @@ if (typeof window.markets !== "function") {
* @param {HTMLElement} market_name_last Отчество представителя магазина <input> * @param {HTMLElement} market_name_last Отчество представителя магазина <input>
* @param {HTMLElement} market_number SIM-номер представителя магазина <input> * @param {HTMLElement} market_number SIM-номер представителя магазина <input>
* @param {HTMLElement} market_mail Почта представителя магазина <input> * @param {HTMLElement} market_mail Почта представителя магазина <input>
* @param {HTMLElement} market_id Идентификатор магазина <input>
* @param {HTMLElement} market_type Тип магазина <select> * @param {HTMLElement} market_type Тип магазина <select>
* @param {HTMLElement} market_city Город магазина <select> * @param {HTMLElement} market_city Город магазина <select>
* @param {HTMLElement} market_district Регион магазина <input> * @param {HTMLElement} market_district Регион магазина <input>
@@ -140,6 +141,7 @@ if (typeof window.markets !== "function") {
market_name_last, market_name_last,
market_number, market_number,
market_mail, market_mail,
market_id,
market_type, market_type,
market_city, market_city,
market_district, market_district,
@@ -158,6 +160,7 @@ if (typeof window.markets !== "function") {
market_name_last.setAttribute("readonly", true); market_name_last.setAttribute("readonly", true);
market_number.setAttribute("readonly", true); market_number.setAttribute("readonly", true);
market_mail.setAttribute("readonly", true); market_mail.setAttribute("readonly", true);
market_id.setAttribute("readonly", true);
market_type.setAttribute("readonly", true); market_type.setAttribute("readonly", true);
market_city.setAttribute("readonly", true); market_city.setAttribute("readonly", true);
market_district.setAttribute("readonly", true); market_district.setAttribute("readonly", true);
@@ -181,6 +184,7 @@ if (typeof window.markets !== "function") {
market_name_last, market_name_last,
market_number, market_number,
market_mail, market_mail,
market_id,
market_type, market_type,
market_city, market_city,
market_district, market_district,
@@ -204,6 +208,7 @@ if (typeof window.markets !== "function") {
* @param {HTMLElement} market_name_last Отчество представителя магазина <input> * @param {HTMLElement} market_name_last Отчество представителя магазина <input>
* @param {HTMLElement} market_number SIM-номер представителя магазина <input> * @param {HTMLElement} market_number SIM-номер представителя магазина <input>
* @param {HTMLElement} market_mail Почта представителя магазина <input> * @param {HTMLElement} market_mail Почта представителя магазина <input>
* @param {HTMLElement} market_id Идентификатор магазина <input>
* @param {HTMLElement} market_type Тип магазина <select> * @param {HTMLElement} market_type Тип магазина <select>
* @param {HTMLElement} market_city Город магазина <select> * @param {HTMLElement} market_city Город магазина <select>
* @param {HTMLElement} market_district Регион магазина <input> * @param {HTMLElement} market_district Регион магазина <input>
@@ -226,6 +231,7 @@ if (typeof window.markets !== "function") {
market_name_last, market_name_last,
market_number, market_number,
market_mail, market_mail,
market_id,
market_type, market_type,
market_city, market_city,
market_district, market_district,
@@ -246,6 +252,7 @@ if (typeof window.markets !== "function") {
market_name_last.removeAttribute("readonly"); market_name_last.removeAttribute("readonly");
market_number.removeAttribute("readonly"); market_number.removeAttribute("readonly");
market_mail.removeAttribute("readonly"); market_mail.removeAttribute("readonly");
market_id.removeAttribute("readonly");
market_type.removeAttribute("readonly"); market_type.removeAttribute("readonly");
market_city.removeAttribute("readonly"); market_city.removeAttribute("readonly");
market_district.removeAttribute("readonly"); market_district.removeAttribute("readonly");
@@ -275,7 +282,7 @@ if (typeof window.markets !== "function") {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
}, },
body: body:
`market_name_first=${market_name_first.value}&market_name_second=${market_name_second.value}&market_name_last=${market_name_last.value}&market_number=${market_number.mask.unmaskedValue}&market_mail=${market_mail.value}&market_type=${market_type.value}&market_city=${market_city.value}&market_district=${market_district.value}&market_address=${market_address.value}&account_name_first=${account_name_first.value}&account_name_second=${account_name_second.value}&account_name_last=${account_name_last.value}&account_number=${account_number.unmaskedValue}&account_mail=${account_mail.value}&account_password=${account_password.value}&account_commentary=${account_commentary.value}`, `market_name_first=${market_name_first.value}&market_name_second=${market_name_second.value}&market_name_last=${market_name_last.value}&market_number=${market_number.mask.unmaskedValue}&market_mail=${market_mail.value}&market_id=${market_id.value}&market_type=${market_type.value}&market_city=${market_city.value}&market_district=${market_district.value}&market_address=${market_address.value}&account_name_first=${account_name_first.value}&account_name_second=${account_name_second.value}&account_name_last=${account_name_last.value}&account_number=${account_number.mask.unmaskedValue}&account_mail=${account_mail.value}&account_password=${account_password.value}&account_commentary=${account_commentary.value}`,
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
@@ -513,6 +520,29 @@ if (typeof window.markets !== "function") {
market_mail_input.setAttribute("title", "Почта представителя магазина"); market_mail_input.setAttribute("title", "Почта представителя магазина");
market_mail_input.setAttribute("placeholder", "name@server"); market_mail_input.setAttribute("placeholder", "name@server");
// Инициализация строки
const market_id = document.createElement("div");
market_id.classList.add("row", "merged");
// Инициализация оболочки для строки
const market_id_label = document.createElement("label");
market_id_label.setAttribute("id", "market_id");
// Инициализация заголовка для поля ввода
const market_id_title = document.createElement("b");
market_id_title.classList.add("separated", "right", "unselectable");
market_id_title.innerText = "Идентификатор:";
// Инициализация поля ввода
const market_id_input = document.createElement("input");
market_id_input.classList.add("large");
market_id_input.setAttribute("name", "id");
market_id_input.setAttribute("type", "text");
market_id_input.setAttribute("minlength", "1");
market_id_input.setAttribute("maxlength", "3");
market_id_input.setAttribute("title", "Идентификатор магазина");
market_id_input.setAttribute("placeholder", "000");
// Инициализация строки // Инициализация строки
const market_type = document.createElement("div"); const market_type = document.createElement("div");
market_type.classList.add("row", "divided", "merged"); market_type.classList.add("row", "divided", "merged");
@@ -958,6 +988,11 @@ if (typeof window.markets !== "function") {
market_mail.appendChild(market_mail_label); market_mail.appendChild(market_mail_label);
market.appendChild(market_mail); market.appendChild(market_mail);
market_id_label.appendChild(market_id_title);
market_id_label.appendChild(market_id_input);
market_id.appendChild(market_id_label);
market.appendChild(market_id);
market_type_label.appendChild(market_type_title); market_type_label.appendChild(market_type_title);
market_type_select.appendChild(market_type_option_1); market_type_select.appendChild(market_type_option_1);
market_type_select.appendChild(market_type_option_2); market_type_select.appendChild(market_type_option_2);
@@ -1159,7 +1194,7 @@ if (typeof window.markets !== "function") {
this.body.wrap.remove(); this.body.wrap.remove();
// Удаление статуса активной строки // Удаление статуса активной строки
row.removeAttribute("data-selected"); // row.removeAttribute("data-selected"); // Это окно создания строки, её ещё не существует
// Деинициализация быстрых действий по кнопкам // Деинициализация быстрых действий по кнопкам
document.removeEventListener("keydown", this.buttons); document.removeEventListener("keydown", this.buttons);

File diff suppressed because it is too large Load Diff

View File

@@ -119,6 +119,7 @@ if (typeof window.workers !== "function") {
* @param {HTMLElement} worker_name_last Отчество сотрудника <input> * @param {HTMLElement} worker_name_last Отчество сотрудника <input>
* @param {HTMLElement} worker_number SIM-номер сотрудника <input> * @param {HTMLElement} worker_number SIM-номер сотрудника <input>
* @param {HTMLElement} worker_mail Почта сотрудника <input> * @param {HTMLElement} worker_mail Почта сотрудника <input>
* @param {HTMLElement} worker_id Идентификатор сотрудника <input>
* @param {HTMLElement} worker_birth Дата рождения сотрудника <input> * @param {HTMLElement} worker_birth Дата рождения сотрудника <input>
* @param {HTMLElement} worker_passport Номер паспорта сотрудника <input> * @param {HTMLElement} worker_passport Номер паспорта сотрудника <input>
* @param {HTMLElement} worker_issued Дата выдачи паспорта сотрудника <input> * @param {HTMLElement} worker_issued Дата выдачи паспорта сотрудника <input>
@@ -148,6 +149,7 @@ if (typeof window.workers !== "function") {
worker_name_last, worker_name_last,
worker_number, worker_number,
worker_mail, worker_mail,
worker_id,
worker_birth, worker_birth,
worker_passport, worker_passport,
worker_issued, worker_issued,
@@ -174,6 +176,7 @@ if (typeof window.workers !== "function") {
worker_name_last.setAttribute("readonly", true); worker_name_last.setAttribute("readonly", true);
worker_number.setAttribute("readonly", true); worker_number.setAttribute("readonly", true);
worker_mail.setAttribute("readonly", true); worker_mail.setAttribute("readonly", true);
worker_id.setAttribute("readonly", true);
worker_birth.setAttribute("readonly", true); worker_birth.setAttribute("readonly", true);
worker_passport.setAttribute("readonly", true); worker_passport.setAttribute("readonly", true);
worker_issued.setAttribute("readonly", true); worker_issued.setAttribute("readonly", true);
@@ -205,6 +208,7 @@ if (typeof window.workers !== "function") {
worker_name_last, worker_name_last,
worker_number, worker_number,
worker_mail, worker_mail,
worker_id,
worker_birth, worker_birth,
worker_passport, worker_passport,
worker_issued, worker_issued,
@@ -236,6 +240,7 @@ if (typeof window.workers !== "function") {
* @param {HTMLElement} worker_name_last Отчество сотрудника <input> * @param {HTMLElement} worker_name_last Отчество сотрудника <input>
* @param {HTMLElement} worker_number SIM-номер сотрудника <input> * @param {HTMLElement} worker_number SIM-номер сотрудника <input>
* @param {HTMLElement} worker_mail Почта сотрудника <input> * @param {HTMLElement} worker_mail Почта сотрудника <input>
* @param {HTMLElement} worker_id Идентификатор сотрудника <input>
* @param {HTMLElement} worker_birth Дата рождения сотрудника <input> * @param {HTMLElement} worker_birth Дата рождения сотрудника <input>
* @param {HTMLElement} worker_passport Номер паспорта сотрудника <input> * @param {HTMLElement} worker_passport Номер паспорта сотрудника <input>
* @param {HTMLElement} worker_issued Дата выдачи паспорта сотрудника <input> * @param {HTMLElement} worker_issued Дата выдачи паспорта сотрудника <input>
@@ -266,6 +271,7 @@ if (typeof window.workers !== "function") {
worker_name_last, worker_name_last,
worker_number, worker_number,
worker_mail, worker_mail,
worker_id,
worker_birth, worker_birth,
worker_passport, worker_passport,
worker_issued, worker_issued,
@@ -294,6 +300,7 @@ if (typeof window.workers !== "function") {
worker_name_last.removeAttribute("readonly"); worker_name_last.removeAttribute("readonly");
worker_number.removeAttribute("readonly"); worker_number.removeAttribute("readonly");
worker_mail.removeAttribute("readonly"); worker_mail.removeAttribute("readonly");
worker_id.removeAttribute("readonly");
worker_birth.removeAttribute("readonly"); worker_birth.removeAttribute("readonly");
worker_passport.removeAttribute("readonly"); worker_passport.removeAttribute("readonly");
worker_issued.removeAttribute("readonly"); worker_issued.removeAttribute("readonly");
@@ -331,7 +338,7 @@ if (typeof window.workers !== "function") {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
}, },
body: body:
`worker_name_first=${worker_name_first.value}&worker_name_second=${worker_name_second.value}&worker_name_last=${worker_name_last.value}&worker_number=${worker_number.mask.unmaskedValue}&worker_mail=${worker_mail.value}&worker_birth=${worker_birth.value}&worker_birth=${worker_birth.value}&worker_passport=${worker_passport.value}&worker_issued=${worker_issued.value}&worker_department_number=${worker_department_number.value}&worker_department_address=${worker_department_address.value}&worker_requisites=${worker_requisites.value}&worker_payment=${worker_payment.value}&worker_tax=${worker_tax.value}&worker_city=${worker_city.value}&worker_district=${worker_district.value}&worker_address=${worker_address.value}&worker_hiring=${worker_hiring.value}&account_name_first=${account_name_first.value}&account_name_second=${account_name_second.value}&account_name_last=${account_name_last.value}&account_number=${account_number.mask.unmaskedValue}&account_mail=${account_mail.value}&account_password=${account_password.value}&account_commentary=${account_commentary.value}`, `worker_name_first=${worker_name_first.value}&worker_name_second=${worker_name_second.value}&worker_name_last=${worker_name_last.value}&worker_number=${worker_number.mask.unmaskedValue}&worker_mail=${worker_mail.value}&worker_id=${worker_id.value}&worker_birth=${worker_birth.value}&worker_birth=${worker_birth.value}&worker_passport=${worker_passport.value}&worker_issued=${worker_issued.value}&worker_department_number=${worker_department_number.value}&worker_department_address=${worker_department_address.value}&worker_requisites=${worker_requisites.value}&worker_payment=${worker_payment.value}&worker_tax=${worker_tax.value}&worker_city=${worker_city.value}&worker_district=${worker_district.value}&worker_address=${worker_address.value}&worker_hiring=${worker_hiring.value}&account_name_first=${account_name_first.value}&account_name_second=${account_name_second.value}&account_name_last=${account_name_last.value}&account_number=${account_number.mask.unmaskedValue}&account_mail=${account_mail.value}&account_password=${account_password.value}&account_commentary=${account_commentary.value}`,
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
@@ -561,6 +568,29 @@ if (typeof window.workers !== "function") {
worker_mail_input.setAttribute("title", "Почта сотрудника"); worker_mail_input.setAttribute("title", "Почта сотрудника");
worker_mail_input.setAttribute("placeholder", "name@server"); worker_mail_input.setAttribute("placeholder", "name@server");
// Инициализация строки
const worker_id = document.createElement("div");
worker_id.classList.add("row", "merged");
// Инициализация оболочки для строки
const worker_id_label = document.createElement("label");
worker_id_label.setAttribute("id", "worker_id");
// Инициализация заголовка для поля ввода
const worker_id_title = document.createElement("b");
worker_id_title.classList.add("separated", "right", "unselectable");
worker_id_title.innerText = "Идентификатор:";
// Инициализация поля ввода
const worker_id_input = document.createElement("input");
worker_id_input.classList.add("large");
worker_id_input.setAttribute("name", "id");
worker_id_input.setAttribute("type", "text");
worker_id_input.setAttribute("minlength", "1");
worker_id_input.setAttribute("maxlength", "6");
worker_id_input.setAttribute("title", "Идентификатор сотрудника");
worker_id_input.setAttribute("placeholder", "000000");
// Инициализация строки // Инициализация строки
const worker_birth = document.createElement("div"); const worker_birth = document.createElement("div");
worker_birth.classList.add("row", "divided"); worker_birth.classList.add("row", "divided");
@@ -1205,6 +1235,11 @@ if (typeof window.workers !== "function") {
worker_mail.appendChild(worker_mail_label); worker_mail.appendChild(worker_mail_label);
worker.appendChild(worker_mail); worker.appendChild(worker_mail);
worker_id_label.appendChild(worker_id_title);
worker_id_label.appendChild(worker_id_input);
worker_id.appendChild(worker_id_label);
worker.appendChild(worker_id);
worker_birth_label.appendChild(worker_birth_title); worker_birth_label.appendChild(worker_birth_title);
worker_birth_label.appendChild(worker_birth_input); worker_birth_label.appendChild(worker_birth_input);
worker_birth.appendChild(worker_birth_label); worker_birth.appendChild(worker_birth_label);

View File

@@ -1,7 +1,7 @@
<section class="row merged chat cloud rounded" data-chat-element="messages"> <section class="row merged chat cloud rounded" data-chat-element="messages">
{% for message in messages %} {% for message in messages %}
<div class="message {% if message.type != 'message' %}{{ message.type }}{% endif %}"> <div class="message {% if message.type != 'message' %}{{ message.type }}{% endif %}">
<h3 class="coal"><b>{{ message.from._key }}</b> <span class="unselectable">{{ message.from.type|account_type_to_russian }}</span> <span class="date unselectable"><b>{{ message.date|date('H:i d.m.Y') }}</b></span></h3> <h3 class="coal"><b>{{ message.from.id ?? message.from._key }}</b> <span class="unselectable">{{ message.from.type|account_type_to_russian }}</span> <span class="date unselectable"><b>{{ message.date|date('H:i d.m.Y') }}</b></span></h3>
<p>{{ message.text }}</p> <p>{{ message.text }}</p>
</div> </div>
{% endfor %} {% endfor %}

View File

@@ -1,5 +1,5 @@
<!-- MARKET #{{ market.id.value }} --> <!-- MARKET #{{ market.id.value }} -->
{% for key, data in market | filter((data, key) => key != '_key') -%} {% for key, data in market | filter((data, key) => key != 'id') -%}
{% if key == 'created' or key == 'updated' %} {% if key == 'created' or key == 'updated' %}
<span id="{{ market.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' : <span id="{{ market.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' :
data.value|date('Y.m.d H:i:s') }}</span> data.value|date('Y.m.d H:i:s') }}</span>
@@ -7,10 +7,10 @@
<span id="{{ market.id.value }}_number"><b>{{ data.label }}:</b><a href="tel:{{ data.value }}" title="Позвонить">{{ <span id="{{ market.id.value }}_number"><b>{{ data.label }}:</b><a href="tel:{{ data.value }}" title="Позвонить">{{
data.value }}</a></span> data.value }}</a></span>
{% elseif key == 'mail' %} {% elseif key == 'mail' %}
<span id="{{ worker.id.value }}_number"><b>{{ data.label }}:</b><a href="mailto:{{ data.value }}" title="Написать">{{ <span id="{{ market.id.value }}_number"><b>{{ data.label }}:</b><a href="mailto:{{ data.value }}" title="Написать">{{
data.value }}</a></span> data.value }}</a></span>
{% elseif key == 'name' %} {% elseif key == 'name' %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value.first is not empty %}{{ data.value.first }}{% endif %}{% if data.value.second is not empty %} {{ data.value.second }}{% endif %}{% if data.value.last is not empty %} {{ data.value.last }}{% endif %}</span> <span id="{{ market.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value.first is not empty %}{{ data.value.first }}{% endif %}{% if data.value.second is not empty %} {{ data.value.second }}{% endif %}{% if data.value.last is not empty %} {{ data.value.last }}{% endif %}</span>
{% else %} {% else %}
<span id="{{ market.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif data.value is same as(false) %}Нет{% elseif data.value is empty %}{% else %}{{ data.value }}{% endif %}</span> <span id="{{ market.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif data.value is same as(false) %}Нет{% elseif data.value is empty %}{% else %}{{ data.value }}{% endif %}</span>
{% endif %} {% endif %}

View File

@@ -2,7 +2,7 @@
{% for key, data in task | filter((data, key) => key != '_key') -%} {% for key, data in task | filter((data, key) => key != '_key') -%}
{% if (key == 'created' or key == 'updated' or key == 'start' or key == 'end') %} {% if (key == 'created' or key == 'updated' or key == 'start' or key == 'end') %}
<span id="{{ task.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' : <span id="{{ task.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' :
data.value|date('d.m.Y H:i', ) }}</span> data.value|date('d.m.Y H:i') }}</span>
{% elseif key == 'confirmed' or key == 'hided' %} {% elseif key == 'confirmed' or key == 'hided' %}
<span id="{{ task.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif <span id="{{ task.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif
data.value is same as(false) or data.value is empty %}Нет{% else %}{{ data.value }}{% endif %}</span> data.value is same as(false) or data.value is empty %}Нет{% else %}{{ data.value }}{% endif %}</span>

View File

@@ -1,21 +1,21 @@
<!-- WORKER #{{ worker.id.value }} --> <!-- WORKER #{{ worker.id.value }} -->
{% for key, data in worker | filter((data, key) => key != '_key') -%} {% for key, data in worker | filter((data, key) => key != 'id') -%}
{% if key == 'created' or key == 'updated' %} {% if key == 'created' or key == 'updated' %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' : <span id="{{ worker.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value is empty ? 'Никогда' :
data.value|date('Y.m.d H:i:s') }}</span> data.value|date('Y.m.d H:i:s') }}</span>
{% elseif key == 'hiring' or key == 'birth' or key == 'issued' %} {% elseif key == 'hiring' or key == 'birth' or key == 'issued' %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value|date('Y.m.d') }}</span> <span id="{{ worker.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ data.value|date('Y.m.d') }}</span>
{% elseif key == 'number' %} {% elseif key == 'number' %}
<span id="{{ worker._key.value }}_number"><b>{{ data.label }}:</b><a href="tel:{{ data.value }}" title="Позвонить">{{ <span id="{{ worker.id.value }}_number"><b>{{ data.label }}:</b><a href="tel:{{ data.value }}" title="Позвонить">{{
data.value }}</a></span> data.value }}</a></span>
{% elseif key == 'mail' %} {% elseif key == 'mail' %}
<span id="{{ worker.id.value }}_number"><b>{{ data.label }}:</b><a href="mailto:{{ data.value }}" title="Написать">{{ <span id="{{ worker.id.value }}_number"><b>{{ data.label }}:</b><a href="mailto:{{ data.value }}" title="Написать">{{
data.value }}</a></span> data.value }}</a></span>
{% elseif key == 'name' %} {% elseif key == 'name' %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value.first is not empty %}{{ data.value.first }}{% endif %}{% if data.value.second is not empty %} {{ data.value.second }}{% endif %}{% if data.value.last is not empty %} {{ data.value.last }}{% endif %}</span> <span id="{{ worker.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value.first is not empty %}{{ data.value.first }}{% endif %}{% if data.value.second is not empty %} {{ data.value.second }}{% endif %}{% if data.value.last is not empty %} {{ data.value.last }}{% endif %}</span>
{% elseif key == 'department' %} {% elseif key == 'department' %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ (data.value.number ~ ' ' ~ data.value.address)|trim }}</span> <span id="{{ worker.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{{ (data.value.number ~ ' ' ~ data.value.address)|trim }}</span>
{% else %} {% else %}
<span id="{{ worker._key.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif data.value is same as(false) %}Нет{% elseif data.value is empty %}{% else %}{{ data.value }}{% endif %}</span> <span id="{{ worker.id.value }}_{{ key }}"><b>{{ data.label }}:</b>{% if data.value is same as(true) %}Да{% elseif data.value is same as(false) %}Нет{% elseif data.value is empty %}{% else %}{{ data.value }}{% endif %}</span>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@@ -2,7 +2,7 @@
{% for row in rows %} {% for row in rows %}
<div id="{{ row.account._key }}" class="row{% if row.account.active is same as(true) %} active{% else %} hided{% endif %}" data-row="market"> <div id="{{ row.account._key }}" class="row{% if row.account.active is same as(true) %} active{% else %} hided{% endif %}" data-row="market">
<span class="unselectable interactive" data-column="account" title="Аккаунт" onclick="markets.account.update(this.parentElement)">{{ row.account._key }}</span> <span class="unselectable interactive" data-column="account" title="Аккаунт" onclick="markets.account.update(this.parentElement)">{{ row.account._key }}</span>
<span class="unselectable interactive" data-column="market" title="Магазин" onclick="markets.update(this.parentElement)">{{ row.market._key }}</span> <span class="unselectable interactive" data-column="market" title="Магазин" onclick="markets.update(this.parentElement)">{{ row.market.id }}</span>
<span class="unselectable interactive" data-column="name" title="{% if row.market.name.first is not empty %}{{ row.market.name.first }}{% endif %}{% if row.market.name.second is not empty %} {{ row.market.name.second }}{% endif %}{% if row.market.name.last is not empty %} {{ row.market.name.last }}{% endif %}">{% if row.market.name.first is not empty %}{{ row.market.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.market.name.last is not empty %} {{ row.market.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.market.name.second is not empty %} {{ row.market.name.second }}{% endif %}</span> <span class="unselectable interactive" data-column="name" title="{% if row.market.name.first is not empty %}{{ row.market.name.first }}{% endif %}{% if row.market.name.second is not empty %} {{ row.market.name.second }}{% endif %}{% if row.market.name.last is not empty %} {{ row.market.name.last }}{% endif %}">{% if row.market.name.first is not empty %}{{ row.market.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.market.name.last is not empty %} {{ row.market.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.market.name.second is not empty %} {{ row.market.name.second }}{% endif %}</span>
<span class="unselectable interactive" data-column="number"><a href="tel:{{ row.market.number }}" title="Позвонить">{{ row.market.number|storaged_number_to_readable }}</a></span> <span class="unselectable interactive" data-column="number"><a href="tel:{{ row.market.number }}" title="Позвонить">{{ row.market.number|storaged_number_to_readable }}</a></span>
<span class="unselectable interactive" data-column="mail"><a href="mailto:{{ row.market.mail }}" title="Написать письмо">{{ row.market.mail }}</a></span> <span class="unselectable interactive" data-column="mail"><a href="mailto:{{ row.market.mail }}" title="Написать письмо">{{ row.market.mail }}</a></span>

View File

@@ -10,7 +10,7 @@
}}</span> }}</span>
<span class="unselectable interactive" data-column="worker" title="{{ row.worker.id }}" {% if account.type !='worker' <span class="unselectable interactive" data-column="worker" title="{{ row.worker.id }}" {% if account.type !='worker'
%}onclick="tasks.worker.popup(this.parentElement)" {% endif %}>{{ %}onclick="tasks.worker.popup(this.parentElement)" {% endif %}>{{
row.worker._key }}</span> row.worker.id }}</span>
<span class="unselectable interactive" data-column="name" <span class="unselectable interactive" data-column="name"
title="{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}">{% title="{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}">{%
if row.worker.name.first is not empty %}{{ if row.worker.name.first is not empty %}{{
@@ -27,7 +27,7 @@
row.task.generated.hours }}</span> row.task.generated.hours }}</span>
<span class="unselectable interactive" data-column="market" {% if account.type !='market' and account.type !='worker' <span class="unselectable interactive" data-column="market" {% if account.type !='market' and account.type !='worker'
%}onclick="tasks.market.popup(this.parentElement)" {% endif %}>{{ %}onclick="tasks.market.popup(this.parentElement)" {% endif %}>{{
row.market._key }}</span> row.market.id }}</span>
<span class="unselectable interactive" data-column="address" <span class="unselectable interactive" data-column="address"
title="{% if row.market.city is not null %}{{ row.market.city }}, {% endif %}{{ row.market.address }}">{% if title="{% if row.market.city is not null %}{{ row.market.city }}, {% endif %}{{ row.market.address }}">{% if
row.market.city is not null %}{{ row.market.city }}, {% endif row.market.city is not null %}{{ row.market.city }}, {% endif

View File

@@ -2,37 +2,46 @@
{% for row in rows %} {% for row in rows %}
<div id="{{ row.account._key }}" <div id="{{ row.account._key }}"
class="row{% if row.account.active is same as(true) %} active{% else %} hided{% endif %}" data-row="worker"> class="row{% if row.account.active is same as(true) %} active{% else %} hided{% endif %}" data-row="worker">
<span class="unselectable interactive" data-column="account" title="Настройки аккаунта" onclick="workers.account.update(this.parentElement)">{{ <span class="unselectable interactive" data-column="account" title="Настройки аккаунта"
onclick="workers.account.update(this.parentElement)">{{
row.account._key }}</span> row.account._key }}</span>
<span class="unselectable interactive" data-column="worker" title="Настройки сотрудника" onclick="workers.update(this.parentElement)">{{ <span class="unselectable interactive" data-column="worker" title="Настройки сотрудника"
row.worker._key }}</span> onclick="workers.update(this.parentElement)">{{
row.worker.id }}</span>
<span class="unselectable interactive" data-column="name" <span class="unselectable interactive" data-column="name"
title="{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}{% if row.worker.birth is not empty %} {{ row.worker.birth|date('d.m.Y') }}{% endif %}" onclick="navigator.clipboard.writeText('{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}{% if row.worker.birth is not empty %} {{ row.worker.birth|date('d.m.Y') }}{% endif %}')">{% title="{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}{% if row.worker.birth is not empty %} {{ row.worker.birth|date('d.m.Y') }}{% endif %}"
onclick="navigator.clipboard.writeText('{% if row.worker.name.first is not empty %}{{ row.worker.name.first }}{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.second }}{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.last }}{% endif %}{% if row.worker.birth is not empty %} {{ row.worker.birth|date('d.m.Y') }}{% endif %}')">{%
if row.worker.name.first is not empty %}{{ if row.worker.name.first is not empty %}{{
row.worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.last is not empty %} {{ row.worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.last is not empty %} {{
row.worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.second is not empty %} {{ row.worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if row.worker.name.second is not empty %} {{
row.worker.name.second }}{% endif %}</span> row.worker.name.second }}{% endif %}</span>
<span class="unselectable interactive" data-column="number"><a href="tel:{{ row.worker.number }}" <span class="unselectable interactive" data-column="number"><a href="tel:{{ row.worker.number }}" title="Позвонить">{{
title="Позвонить">{{ row.worker.number|storaged_number_to_readable }}</a></span> row.worker.number|storaged_number_to_readable }}</a></span>
<span class="unselectable interactive" data-column="mail"><a href="mailto:{{ row.worker.mail }}" <span class="unselectable interactive" data-column="mail"><a href="mailto:{{ row.worker.mail }}" title="Написать">{{
title="Написать">{{ row.worker.mail }}</a></span> row.worker.mail }}</a></span>
<span class="unselectable interactive" data-column="address" <span class="unselectable interactive" data-column="address"
title="{{ (row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address)|trim }}" title="{{ (row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address)|trim }}"
onclick="navigator.clipboard.writeText('{{ row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address }}')">{% if row.worker.city is not empty and row.worker.district is not onclick="navigator.clipboard.writeText('{{ row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address }}')">{%
if row.worker.city is not empty and row.worker.district is not
empty and row.worker.district is not null %}{{ row.worker.city|slice(0,4) ~ '. ' ~ row.worker.district|slice(0,3) ~ empty and row.worker.district is not null %}{{ row.worker.city|slice(0,4) ~ '. ' ~ row.worker.district|slice(0,3) ~
'. ' ~ row.worker.address }}{% else %}{{ row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address }}{% '. ' ~ row.worker.address }}{% else %}{{ row.worker.city ~ ' ' ~ row.worker.district ~ ' ' ~ row.worker.address }}{%
endif %}</span> endif %}</span>
<span class="unselectable interactive" data-column="passport" <span class="unselectable interactive" data-column="passport"
title="{{ (row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~ row.worker.department.address)|trim(', ') }}" title="{{ (row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~ row.worker.department.address)|trim(', ') }}"
onclick="navigator.clipboard.writeText('{{ (row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~ row.worker.department.address)|trim(', ') }}')">{{ (row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~ onclick="navigator.clipboard.writeText('{{ (row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~ row.worker.department.address)|trim(', ') }}')">{{
(row.worker.passport ~ ', ' ~ row.worker.issued|date('d.m.Y') ~ ', ' ~ row.worker.department.number ~ ', ' ~
row.worker.department.address)|trim(', ') }}</span> row.worker.department.address)|trim(', ') }}</span>
<span class="unselectable interactive" data-column="tax" onclick="navigator.clipboard.writeText('{{ row.worker.tax }}')">{{ row.worker.tax }}</span> <span class="unselectable interactive" data-column="tax"
onclick="navigator.clipboard.writeText('{{ row.worker.tax }}')">{{ row.worker.tax }}</span>
<span class="unselectable interactive" data-column="requisites" <span class="unselectable interactive" data-column="requisites"
title="{% if row.worker.requisites is not empty and row.worker.payment is not empty %}{{ row.worker.requisites }} ({{ row.worker.payment }}){% else %}{{ row.worker.payment }}{% endif %}" title="{% if row.worker.requisites is not empty and row.worker.payment is not empty %}{{ row.worker.requisites }} ({{ row.worker.payment }}){% else %}{{ row.worker.requisites }} {{ row.worker.payment }}{% endif %}"
onclick="navigator.clipboard.writeText('{{ row.worker.requisites|storaged_requisites_to_card }}')">{% if onclick="navigator.clipboard.writeText('{{ row.worker.requisites|storaged_requisites_to_card }}')">
row.worker.requisites is not empty and row.worker.payment is not empty %}{{ {% if row.worker.requisites is not empty and row.worker.payment is not empty %}
row.worker.requisites|storaged_requisites_preview }} ({{ {{ row.worker.requisites|storaged_requisites_preview }} ({{ row.worker.payment }})
row.worker.payment }}){% else %}{{ row.worker.payment }}{% endif %}</span> {% else %}
{{ row.worker.requisites }} {{ row.worker.payment }}
{% endif %}
</span>
<span class="unselectable interactive" data-column="commentary" title="{{ row.account.commentary }}" <span class="unselectable interactive" data-column="commentary" title="{{ row.account.commentary }}"
onclick="navigator.clipboard.writeText('{{ row.account.commentary }}')">{{ row.account.commentary }}</span> onclick="navigator.clipboard.writeText('{{ row.account.commentary }}')">{{ row.account.commentary }}</span>
</div> </div>

View File

@@ -1,5 +1,5 @@
{% for market in markets %} {% for market in markets %}
<option value="{{ market.getKey() }}">{{ market.getKey() }}{% if market.name.first is not empty %} {{ <option value="{{ market.id }}">{{ market.id }}{% if market.name.first is not empty %} {{
market.name.first|slice(0, 1)|upper }}.{% endif %}{% if market.name.last is not empty %} {{ market.name.last|slice(0, market.name.first|slice(0, 1)|upper }}.{% endif %}{% if market.name.last is not empty %} {{ market.name.last|slice(0,
1)|upper }}.{% endif %}{% if market.name.second is not empty %} {{ market.name.second }}{% endif %}</option> 1)|upper }}.{% endif %}{% if market.name.second is not empty %} {{ market.name.second }}{% endif %}</option>
{% endfor %} {% endfor %}

View File

@@ -1,5 +1,5 @@
{% for worker in workers %} {% for worker in workers %}
<option value="{{ worker.getKey() }}">{{ worker.getKey() }}{% if worker.name.first is not empty %} {{ <option value="{{ worker.id }}">{{ worker.id }}{% if worker.name.first is not empty %} {{
worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if worker.name.last is not empty %} {{ worker.name.first|slice(0, 1)|upper }}.{% endif %}{% if worker.name.last is not empty %} {{
worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if worker.name.second is not empty %} {{ worker.name.last|slice(0, 1)|upper }}.{% endif %}{% if worker.name.second is not empty %} {{
worker.name.second }}{% endif %}</option> worker.name.second }}{% endif %}</option>

View File

@@ -25,8 +25,8 @@
class="icon arrow right"></i></button> class="icon arrow right"></i></button>
<datalist id="markets"> <datalist id="markets">
{% for account in accounts %} {% for account in accounts %}
<option value="{{ account.account.getKey() }}">{{ account.account.getKey()}} {{ <option value="{{ account.market }}">{{ account.market }} {{
account.account.name.first }} {{ account.account.name.second }}</option> account.market.name.first }} {{ account.market.name.second }}</option>
{% endfor %} {% endfor %}
</datalist> </datalist>
</label> </label>