Большое исправление ВСЕГО

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2021-03-16 02:50:45 +10:00
parent 1fac5cdfc1
commit b71f6e8efc
82 changed files with 1476 additions and 687 deletions

View File

@ -47,9 +47,18 @@ class AuthenticationController extends Controller
if (!Yii::$app->user->isGuest || $model->authentication()) { if (!Yii::$app->user->isGuest || $model->authentication()) {
// Аккаунт аутентифицирован // Аккаунт аутентифицирован
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel_full = true;
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
// Запись ответа // Запись ответа
$return = [ $return = [
'menu' => $this->renderPartial('/account/deauthentication'), 'menu' => $this->renderPartial('/account/panel/authenticated', compact(
'notifications_button',
'notifications_panel',
'notifications_panel_full'
)),
'_csrf' => Yii::$app->request->getCsrfToken() '_csrf' => Yii::$app->request->getCsrfToken()
]; ];

View File

@ -15,22 +15,6 @@ use Exception;
class CartController extends Controller class CartController extends Controller
{ {
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['index'],
'rules' => [
[
'allow' => true,
'roles' => ['@']
]
]
]
];
}
/** /**
* Страница: "Корзина" * Страница: "Корзина"
* *

View File

@ -44,7 +44,7 @@ class DeauthenticationController extends Controller
// Ответа // Ответа
return [ return [
'menu' => $this->renderPartial('/account/authentication', compact('model')), 'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
'main' => $this->renderPartial('/index'), 'main' => $this->renderPartial('/index'),
'redirect' => '/', 'redirect' => '/',
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()

View File

@ -27,7 +27,7 @@ class IdentificationController extends Controller
// Запись ответа // Запись ответа
$return = [ $return = [
'menu' => $this->renderPartial('/account/authentication', compact('model')), 'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} else { } else {
@ -36,9 +36,17 @@ class IdentificationController extends Controller
// Инициализация // Инициализация
$model = yii::$app->user; $model = yii::$app->user;
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel_full = true;
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
// Запись ответа // Запись ответа
$return = [ $return = [
'menu' => $this->renderPartial('/account/deauthentication'), 'menu' => $this->renderPartial('/account/panel/authenticated', compact(
'notifications_button',
'notifications_panel'
)),
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} }

View File

@ -40,6 +40,7 @@ class NotificationController extends Controller
// Инициализация // Инициализация
$model = new Notification(yii::$app->request->post('Notification')); $model = new Notification(yii::$app->request->post('Notification'));
$preload = (bool) (int) yii::$app->request->post('preload');
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
@ -50,11 +51,11 @@ class NotificationController extends Controller
if (yii::$app->request->post('last')) { if (yii::$app->request->post('last')) {
// Запрос последнего уведомлений (всплывающее окно) // Запрос последнего уведомлений (всплывающее окно)
// Инициализация
$limit = 1; $limit = 1;
} else if (yii::$app->request->post('stream')) { } else if (yii::$app->request->post('stream')) {
// Запрос последних уведомлений (панель) // Запрос последних уведомлений (панель)
$limit = 10;
$limit = 5;
} }
if (isset($limit)) { if (isset($limit)) {
@ -87,13 +88,22 @@ class NotificationController extends Controller
/** /**
* Поиск рёбер: (УВЕДОМЛЕНИЕ)? -> ПОЛЬЗОВАТЕЛЬ * Поиск рёбер: (УВЕДОМЛЕНИЕ)? -> ПОЛЬЗОВАТЕЛЬ
* *
* @param bool $check Активация проверки на то, что уведомление не получено * @param bool $new Активация проверки на то, что уведомление не получено
* @param bool $count Посчитать
*/ */
$search = function (bool $check = false) use ($model, $type, $let, $limit): array { $search = function (bool $new = false, bool $count = false) use ($model, $type, $let, $limit): array|int {
if ($count) {
// Запрошен подсчёт непрочитанных уведомлений
// Реинициализация
$type = 'checked';
$limit = null;
}
return $model::searchByEdge( return $model::searchByEdge(
from: 'account', from: 'account',
to: 'notification', to: 'notification',
params: $check ? $let->getBindVars() : [], params: $new ? $let->getBindVars() : [],
subquery_where: [ subquery_where: [
[ [
'account._id' => yii::$app->user->id 'account._id' => yii::$app->user->id
@ -112,20 +122,28 @@ class NotificationController extends Controller
'notification_edge_account', 'notification_edge_account',
'(' . (string) $let . ')' '(' . (string) $let . ')'
], ],
where: $check ? [ where: $new ? [
'account_edge_notification[0]._to' => null 'account_edge_notification[0]._to' => null
] : [], ] : [],
limit: $limit, limit: $limit,
sort: ['DESC'], sort: ['DESC'],
direction: 'INBOUND' direction: 'INBOUND',
handle: $count ? function ($request) {
// Посчитать рёбра (информация о количестве непрочитанных уведомлений)
return $request->count();
}: null
); );
}; };
// Поиск непрочитанных уведомлений пользователя // Поиск новых (непрочитанных) уведомлений пользователя
// Если заккоментировать, то вместо только новых отправит все последние уведомления
$notifications = $search(true); $notifications = $search(true);
// Подсчёт новых (непрочитанных) уведомлений
$return['button'] = $this->renderPartial('button', ['notifications_new_amount' => $search(new: true, count: true)]);
if (!yii::$app->request->post('last') && empty($notifications)) { if (!yii::$app->request->post('last') && empty($notifications)) {
// Уведомления не найдены и запрошены НЕ всплывающие уведомления // Запрошены НЕ ВСПЛЫВАЮЩИЕ уведомления и новые уведомления не найдены
// Поиск уведомлений пользователя // Поиск уведомлений пользователя
$notifications = $search(); $notifications = $search();
@ -134,7 +152,10 @@ class NotificationController extends Controller
if (empty($notifications)) { if (empty($notifications)) {
// Уведомления не найдены // Уведомления не найдены
yii::$app->response->statusCode = 404; /**
* @todo Определить какой код лучше использовать (404 не подходит)
*/
yii::$app->response->statusCode = 200;
goto end; goto end;
} }
@ -142,6 +163,12 @@ class NotificationController extends Controller
foreach ($notifications as $notification) { foreach ($notifications as $notification) {
// Перебор найденных уведомлений // Перебор найденных уведомлений
if ($preload) {
// Запрошены уведомления для предзагрузки (их ещё не увидели)
break;
}
// Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано) // Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано)
AccountEdgeNotification::write(yii::$app->user->id, $notification->readId(), $type); AccountEdgeNotification::write(yii::$app->user->id, $notification->readId(), $type);
} }
@ -168,7 +195,11 @@ class NotificationController extends Controller
$return['redirect'] = '/notification'; $return['redirect'] = '/notification';
} }
/**
* Конец алгоритма
*/
end: end:
return $return; return $return;
} }

View File

@ -4,16 +4,13 @@ declare(strict_types=1);
namespace app\controllers; namespace app\controllers;
use app\models\AccountEdgeNotification;
use yii; use yii;
use yii\filters\AccessControl; use yii\filters\AccessControl;
use yii\web\Controller; use yii\web\Controller;
use yii\web\Response; use yii\web\Response;
use app\models\Supply;
use app\models\Order; use app\models\Order;
use app\models\AccountEdgeOrder; use app\models\AccountEdgeOrder;
use app\models\OrderEdgeSupply;
use Exception; use Exception;
class OrderController extends Controller class OrderController extends Controller
@ -102,7 +99,7 @@ class OrderController extends Controller
} }
// Если запись не удалась, то вернуть код: 500 Internal Server Error // Если запись не удалась, то вернуть код: 500 Internal Server Error
$model->write($supplies) or yii::$app->response->statusCode = 500; $model->writeSupply($supplies) or yii::$app->response->statusCode = 500;
} }
return $return; return $return;
@ -115,6 +112,8 @@ class OrderController extends Controller
/** /**
* Удаление * Удаление
*
* @todo Разделить логику (для удобства чтения)
*/ */
public function actionDelete(): string|array|null public function actionDelete(): string|array|null
{ {
@ -122,36 +121,65 @@ class OrderController extends Controller
$targets = yii::$app->request->post('targets') ?? yii::$app->request->get('targets'); $targets = yii::$app->request->post('targets') ?? yii::$app->request->get('targets');
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1; $page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user; $account = yii::$app->user;
$order = Order::search();
if ($targets) { if ($targets) {
// Удаление выбранных целей (удаление из заказа) // Удаление выбранных целей (удаление из заказа)
foreach ($targets as $target) { foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) {
// Унификация входных параметров
// Инициализация foreach ($target as $catn => $amount) {
$model = Order::search(); // Перебор целей
// Удаление // Удаление
$model->deleteEdge($target); $order->deleteSupply([$catn => (int) $amount]);
}
} }
} else { } else {
// Целью подразумевается сам заказ (удаление заказа) // Целью подразумевается сам заказ (удаление заказа)
// Инициализация корзины (текущего заказа) // Инициализация
$model = Order::search(); $order->stts = 'reserved';
if ($order->update()) {
// Запись в журнал
$order->journal('reserved');
// Поиск // Поиск
$edge = AccountEdgeOrder::searchByVertex($account->id, $model->readId(), 'current'); $edge = AccountEdgeOrder::searchByVertex($account->id, $order->readId(), 'current');
// Запись if (count($edge) > 1) {
// Найден более чем 1 заказ
return null;
}
// Инициализация
$edge = $edge[0];
$edge->type = 'reserved'; $edge->type = 'reserved';
// Отправка // Запись
$edge->update(); $edge->update();
// Реинициализация
$order = Order::search();
if (empty($order)) {
// Корзина не инициализирована
// Инициализация
$order = new Order();
$order->save() or throw new Exception('Не удалось инициализировать заказ');
// Подключение
$order->connect($account);
}
}
} }
// Инициализация содержимого корзины // Инициализация содержимого корзины
$supplies = $model->content(10, $page); $supplies = $order->content(10, $page);
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// POST-запрос // POST-запрос
@ -159,14 +187,73 @@ class OrderController extends Controller
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
return [ return [
'main' => $this->renderPartial('/cart/index', compact('model', 'supplies')), 'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')),
'title' => 'Корзина', 'title' => 'Корзина',
'redirect' => '/cart', 'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} }
return $this->render('/cart/index', compact('model', 'supplies')); return $this->render('/cart/index', compact('order', 'supplies'));
}
/**
* Обновление количества товара
*/
public function actionAmountUpdate(): string|array|null
{
// Инициализация
$targets = yii::$app->request->post('targets') ?? yii::$app->request->get('targets');
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user;
$order = Order::search();
$supplies = $order->content(10, $page);
foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) {
// Унификация входных параметров
foreach ($target as $catn => $amount) {
// Перебор целей (переданных объектов в корзине)
foreach ($supplies as $supply) {
// Перебор объектов в корзине
if ($supply->catn === $catn) {
// Цель найдена
if ($supply->amnt > $amount) {
// Запрошено уменьшение количества
// Удаление
$order->deleteSupply([$catn => $supply->amnt - $amount]);
} else if ($supply->amnt < $amount) {
// Запрошено увеличение количества
// Запись
$order->writeSupply([$supply->catn => $amount - $supply->amnt]);
}
}
}
}
}
// Ренициализация
$supplies = $order->content(10, $page);
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('/cart/index', compact('order', 'supplies'));
} }
/** /**
@ -180,6 +267,15 @@ class OrderController extends Controller
// Поиск ребра // Поиск ребра
$edge = AccountEdgeOrder::searchByVertex(yii::$app->user->id, $model->readId(), 'current'); $edge = AccountEdgeOrder::searchByVertex(yii::$app->user->id, $model->readId(), 'current');
if (count($edge) > 1) {
// Найден более чем 1 заказ
return null;
}
// Инициализация
$edge = $edge[0];
// Запись // Запись
$edge->type = 'accepted'; $edge->type = 'accepted';

View File

@ -85,8 +85,10 @@ class ProfileController extends Controller
{ {
// Инициализация // Инициализация
$model = yii::$app->user->identity; $model = yii::$app->user->identity;
$supplies = Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]'); $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar');
// Обработка настроек аккаунта
if ($vars = yii::$app->request->post('Account') ?? yii::$app->request->get('Account')) { if ($vars = yii::$app->request->post('Account') ?? yii::$app->request->get('Account')) {
// Обнаружены входные параметры // Обнаружены входные параметры
@ -107,8 +109,8 @@ class ProfileController extends Controller
} }
} }
// Генерация // Инициализация
$sidebar = $this->renderPartial('sidebar'); $list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]', limit: null));
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// POST-запрос // POST-запрос
@ -116,39 +118,23 @@ class ProfileController extends Controller
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
return [ return [
'main' => $this->renderPartial('index', compact('model', 'sidebar', 'supplies')), 'main' => $this->renderPartial('index', compact(
'model',
'sidebar',
'list',
'panel'
)),
'redirect' => '/profile', 'redirect' => '/profile',
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} }
return $this->render('index', compact('model', 'sidebar', 'supplies')); return $this->render('index', compact(
} 'model',
'sidebar',
/** 'list',
* Страница поставок 'panel'
*/ ));
public function actionSupplies(): string|array
{
// Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
// Генерация
$sidebar = $this->renderPartial('sidebar');
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('supplies', compact('model', 'sidebar')),
'redirect' => '/profile/supplies',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('supplies', compact('model', 'sidebar'));
} }
/** /**
@ -159,6 +145,8 @@ class ProfileController extends Controller
// Инициализация // Инициализация
$model_notifications = null; $model_notifications = null;
$model_settings = Settings::readLast(); $model_settings = Settings::readLast();
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar');
if (!is_null($vars = yii::$app->request->post('Notification') ?? yii::$app->request->get('Notification'))) { if (!is_null($vars = yii::$app->request->post('Notification') ?? yii::$app->request->get('Notification'))) {
// Обнаружены входные параметры из раздела "Уведомления" // Обнаружены входные параметры из раздела "Уведомления"
@ -202,69 +190,53 @@ class ProfileController extends Controller
// Деинициализация // Деинициализация
unset($vars); unset($vars);
// Генерация
$sidebar = $this->renderPartial('sidebar');
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// AJAX-POST-запрос // AJAX-POST-запрос
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
return [ return [
'main' => $this->renderPartial('trusted', compact('model_notifications', 'model_settings', 'sidebar')), 'main' => $this->renderPartial('trusted', compact(
'model_notifications',
'model_settings',
'sidebar',
'panel'
)),
'redirect' => '/profile/trusted', 'redirect' => '/profile/trusted',
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} }
return $this->render('trusted', compact('model_notifications', 'model_settings', 'sidebar')); return $this->render('trusted', compact(
'model_notifications',
'model_settings',
'sidebar',
'panel'
));
} }
/** /**
* Страницка панели управления для доверенных пользователей * Страница мониторинга
*
* @todo Перенести в уведомления
*/
// public function actionTrustedNotificationWrite(): string|array
// {
// // Инициализация
// $model = new Notification(yii::$app->request->post('Notification') ?? yii::$app->request->get('Notification'));
// $model->write();
// if (yii::$app->request->isPost) {
// // POST-запрос
// yii::$app->response->format = Response::FORMAT_JSON;
// return [
// 'main' => $this->renderPartial('trusted', compact('model', 'sidebar')),
// 'redirect' => '/profile/trusted',
// '_csrf' => yii::$app->request->getCsrfToken()
// ];
// }
// return $this->render('trusted', compact('model', 'sidebar'));
// }
/**
* Страницка панели управления для доверенных пользователей
*/ */
public function actionMonitoring(): string|array public function actionMonitoring(): string|array
{ {
// Инициализация
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar');
// Инициализация номера страницы // Инициализация номера страницы
$page_search_history = (yii::$app->request->post('search') ?? yii::$app->request->get('search')) - 1; $page_search_history = (yii::$app->request->post('search') ?? yii::$app->request->get('search')) - 1;
if ($page_search_history <= 0) { if ($page_search_history <= 0) {
// Вышли за границу поиска перед первой страницей
// Инициализация
$page_search_history = 0; $page_search_history = 0;
} }
// Инициализация количества строк на одной странице // Инициализация количества строк на одной странице
$rows_amount = 10; $rows_amount = 10;
// Генерация
$sidebar = $this->renderPartial('sidebar');
$search_history = Search::SearchByEdge( $search_history = Search::SearchByEdge(
from: 'account', from: 'account',
to: 'search', to: 'search',
@ -279,6 +251,33 @@ class ProfileController extends Controller
offset: ((int) $page_search_history ?? 0) * $rows_amount offset: ((int) $page_search_history ?? 0) * $rows_amount
); );
// Проверка результатов
monitoring_result_check:
if (count($search_history) === 0) {
// Вышли за границу поиска после последней страницы
// Реинициализация (вычитание для идентичного конечного результата)
--$page_search_history;
$search_history = Search::SearchByEdge(
from: 'account',
to: 'search',
subquery_where: [
[
'account._id' => yii::$app->user->id
]
],
foreach: ['edge' => 'account_edge_search'],
where: 'edge._to == search._id',
limit: $rows_amount,
offset: ((int) $page_search_history ?? 0) * $rows_amount
);
// Повторная проверка
goto monitoring_result_check;
}
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// AJAX-POST-запрос // AJAX-POST-запрос
@ -288,7 +287,8 @@ class ProfileController extends Controller
'main' => $this->renderPartial('monitoring', compact( 'main' => $this->renderPartial('monitoring', compact(
'sidebar', 'sidebar',
'search_history', 'search_history',
'page_search_history' 'page_search_history',
'panel'
)), )),
'search' => $page_search_history + 1, 'search' => $page_search_history + 1,
'redirect' => '/profile/monitoring', 'redirect' => '/profile/monitoring',
@ -299,18 +299,61 @@ class ProfileController extends Controller
return $this->render('monitoring', compact( return $this->render('monitoring', compact(
'sidebar', 'sidebar',
'search_history', 'search_history',
'page_search_history' 'page_search_history',
'panel'
)); ));
} }
/**
* Страница поставок
*/
public function actionSupplies(): string|array
{
// Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups();
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('supplies', compact(
'model',
'groups',
'sidebar',
'panel'
)),
'redirect' => '/profile/supplies',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('supplies', compact(
'model',
'groups',
'sidebar',
'panel'
));
}
/**
* Импорт поставок
*
* На данный момент только из Excel-таблицы
*/
public function actionImport() public function actionImport()
{ {
// Инициализация // Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); $model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$model->scenario = $model::SCENARIO_IMPORT; $model->scenario = $model::SCENARIO_IMPORT;
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
// Генерация
$sidebar = $this->renderPartial('sidebar'); $sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups();
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// AJAX-POST-запрос // AJAX-POST-запрос
@ -319,17 +362,27 @@ class ProfileController extends Controller
$model->file = UploadedFile::getInstances($model, 'file'); $model->file = UploadedFile::getInstances($model, 'file');
if (!$test = $model->import()) { if ($model->import()) {
yii::$app->response->statusCode = 409;
}
return [ return [
'main' => $this->renderPartial('supplies', compact('model', 'sidebar')), 'main' => $this->renderPartial('supplies', compact(
'model',
'groups',
'sidebar',
'panel'
)),
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];
} }
return $this->render('supplies', compact('model', 'sidebar')); yii::$app->response->statusCode = 409;
}
return $this->render('supplies', compact(
'model',
'groups',
'sidebar',
'panel'
));
} }
public static function readGroups() public static function readGroups()

View File

@ -48,7 +48,7 @@ class RegistrationController extends Controller
// Запись ответа // Запись ответа
$return = [ $return = [
'menu' => $this->renderPartial('/account/deauthentication'), 'menu' => $this->renderPartial('/account/panel/authenticated'),
'_csrf' => yii::$app->request->getCsrfToken() '_csrf' => yii::$app->request->getCsrfToken()
]; ];

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210314_133722_create_session_collection extends Migration
{
public function up()
{
$this->createCollection('session', []);
}
public function down()
{
$this->dropCollection('session');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210314_133926_create_account_edge_session_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_session', ['type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_session');
}
}

View File

@ -242,4 +242,62 @@ class Account extends Document implements IdentityInterface, PartnerInterface
// Отправка // Отправка
return $this->save(); return $this->save();
} }
/**
* Генерация списка OEM-номеров
*
* Актуальное (выбранное, активное) значение записывается первым
*
* @param array $supplies Необработанный список поставок
*/
public function genListOem(array $supplies): array
{
// Инициализация
$list = [];
// Перебор свойств поставок
foreach ($supplies as $supply) {
// Инициализация
$id = $supply['ЗначенияСвойства']['Ид'];
if (in_array($id, $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Генерация
!isset($supply['ЗначенияСвойства']['Наименование']) or $list[$id] = $supply['ЗначенияСвойства']['Наименование'];
}
// Инициализация текущего значения параметра в начале массива
if (isset($this->opts['import_sections_oem'])) {
// Параметр 'import_sections_oem' найден в настройках аккаунта
if (isset($list[$this->opts['import_sections_oem']])) {
// Найдено совпадение сохранённого параметра с полученным списком из поставок
// Буфер для сохранения параметра
$buffer = $list[$this->opts['import_sections_oem']];
// Удаление параметра
unset($list[$this->opts['import_sections_oem']]);
// Сохранение параметра в начале массива
$list = array_merge([$this->opts['import_sections_oem'] => $buffer], $list);
} else {
// Совпадение не найдено
// Сохранение параметра из данных аккаунта в начале массива
$list = array_merge([$this->opts['import_sections_oem'] => $this->opts['import_sections_oem']], $list);
}
} else {
// Параметр 'import_sections_oem' не найден в настройках аккаунта
// Сохранение параметра из данных аккаунта в начале массива
$list = array_merge(['Выберите'], $list);
}
return $list;
}
} }

View File

@ -20,7 +20,7 @@ abstract class Document extends ActiveRecord
*/ */
public static function collectionName(): string public static function collectionName(): string
{ {
return throw new Exception('Не установлено название коллекции'); return throw new Exception('Не инициализировано название коллекции');
} }
/** /**
@ -30,8 +30,7 @@ abstract class Document extends ActiveRecord
{ {
return [ return [
'_key', '_key',
'date', 'jrnl'
'wrtr'
]; ];
} }
@ -42,8 +41,7 @@ abstract class Document extends ActiveRecord
{ {
return [ return [
'_key' => 'Ключ', '_key' => 'Ключ',
'date' => 'Дата', 'jrnl' => 'Журнал'
'wrtr' => 'Аккаунт записавшего'
]; ];
} }
@ -55,22 +53,62 @@ abstract class Document extends ActiveRecord
*/ */
public function rules(): array public function rules(): array
{ {
return [ return [];
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
// Запись в журнал
$this->jrnl = [[
'date' => time(),
'account' => yii::$app->user->id,
'action' => 'create'
]];
}
return true;
}
return false;
}
/**
* Журнал
*
* Записывает данные в журнал
*
* @param string $action
*
* @return int|bool Время записанное в журнале или false, если не удалось записать
*/
public function journal(string $action = 'update', array ...$data): int|bool
{
// Инициализация
is_array($this->jrnl) or $this->jrnl = [];
// Генерация
$this->jrnl = array_merge(
$this->jrnl,
[array_merge(
[ [
'wrtr', 'date' => $time = time(),
'string' 'account' => yii::$app->user->id,
'action' => $action
], ],
[ ...$data
'wrtr', )]
'default', );
'value' => yii::$app->user->id
], // Запись и возврат
[ return $this->save() ? $time : false;
'date',
'default',
'value' => time()
]
];
} }
/** /**

View File

@ -32,9 +32,8 @@ abstract class Edge extends Document
return array_merge( return array_merge(
parent::attributeLabels(), parent::attributeLabels(),
[ [
'date' => 'От кого', '_from' => 'От кого',
'date' => 'К кому', '_to' => 'К кому',
'type' => 'Тип'
] ]
); );
} }
@ -72,8 +71,6 @@ abstract class Edge extends Document
if ($this->isNewRecord) { if ($this->isNewRecord) {
} }
$this->type = $this->type ?? '';
return true; return true;
} }
@ -99,7 +96,7 @@ abstract class Edge extends Document
/** /**
* Записать * Записать
*/ */
public static function write(string $_from, string $_to, string $type = '', array $data = []): ?static public static function write(string $_from, string $_to, string $type, array $data = []): ?static
{ {
// Инициализация // Инициализация
$edge = new static; $edge = new static;
@ -121,22 +118,33 @@ abstract class Edge extends Document
} }
} }
// Запись if ($edge->save()) {
return $edge->save() ? $edge : null; // Записано в базу данных
// Запись в журнал
$edge->journal('create');
return $edge;
}
return null;
} }
/** /**
* Поиск ребра по его вершинам * Поиск ребра по его вершинам
*/ */
public static function searchByVertex(string $_from, string $_to, string $type = '', int $limit = 1): static|array|null public static function searchByVertex(string $_from, string $_to, string|null $type = null, int $limit = 1): array|null
{ {
$query = self::find()->where(['_from' => $_from, '_to' => $_to, 'type' => $type]); $query = self::find()->where([
'_from' => $_from,
'_to' => $_to
]);
if ($limit < 2) { if (isset($type)) {
return $query->one(); $query->where(['type' => $type]);
} else {
return $query->limit($limit)->all();
} }
return $query->limit($limit)->all();
} }
/** /**

View File

@ -5,10 +5,11 @@ declare(strict_types=1);
namespace app\models; namespace app\models;
use yii; use yii;
use yii\web\IdentityInterface;
use app\models\traits\SearchByEdge; use app\models\traits\SearchByEdge;
use app\models\Account;
use Exception; use Exception;
/** /**
@ -25,6 +26,16 @@ class Notification extends Document
*/ */
const SCENARIO_TRUSTED_CREATE = 'create'; const SCENARIO_TRUSTED_CREATE = 'create';
/**
* Тип уведомления: памятка
*/
const TYPE_NOTICE = 'notice';
/**
* Тип уведомления: предупреждение
*/
const TYPE_WARNING = 'warning';
/** /**
* Цель для отправки уведомления * Цель для отправки уведомления
* *
@ -32,7 +43,7 @@ class Notification extends Document
* *
* @see SCENARIO_TRUSTED_CREATE * @see SCENARIO_TRUSTED_CREATE
*/ */
public IdentityInterface|string|array|null $trgt; public Account|string|array|null $account;
/** /**
* Текст уведомления * Текст уведомления
@ -109,108 +120,116 @@ class Notification extends Document
/** /**
* Запись * Запись
* *
* @param string $html HTML уведомления * @param bool|string $html Содержимое уведомления (HTML или текст)
* @param IdentityInterface $trgt Получатель уведомления * @param Account|array|string|null $account Получатель уведомления
* @param string $type Тип уведомления
*
* @todo Создать параметр разделителя для администрации
*/ */
public function write(string $html = null, IdentityInterface|array|string $trgt = null, string $type = 'notice'): self|array|null public function write(Account|array|string|null $account = null): self|array|null
{
return $this::_write($this->text, $this->html, $account, $this->type);
}
/**
* Запись
*
* @param string $html Содержимое уведомления (HTML или текст)
* @param bool|string|null $html Содержимое уведомления (HTML или текст)
* @param Account|array|string|null $account Получатель уведомления
* @param string $type Тип уведомления
*/
public static function _write(string $text, bool|string|null $html = false, Account|array|string|null $account = null, string $type = self::TYPE_NOTICE): self|array|null
{ {
// Инициализация // Инициализация
$html ?? $html = $this->html ?? throw new Exception('Не удалось инициализировать содержимое'); $model = new self;
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать получателя'); $account ?? $account = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать получателя');
$type ?? $trgt = $this->type ?? throw new Exception('Не удалось инициализировать тип');
// Инициализация уведомления if ((bool) (int) $html) {
if (isset($html) && (bool) (int) $html) {
// Получен текст в формете HTML-кода // Получен текст в формете HTML-кода
$this->html = $this->text ?? null; $model->html = $text ?? null;
} else { } else {
// Получен необработанный текст // Получен необработанный текст
$text = htmlspecialchars(strip_tags($this->text ?? null)); $text = htmlspecialchars(strip_tags($text ?? null));
$this->html = <<<HTML $model->html = <<<HTML
<p class="my-2 mx-3">$text</p> <p class="my-2 mx-3">$text</p>
HTML; HTML;
} }
if ($this->save()) { if ($model->save()) {
// Уведомление записано // Уведомление записано
// Инициализация получателей и создание ребра // Инициализация получателей и создание ребра
if (empty($trgt)) { if (empty($account)) {
// Получатель не передан // Получатель не передан
goto test; goto test;
} else if (is_string($trgt)) { } else if (is_string($account)) {
// Передана необработанная строка // Передана необработанная строка
// Инициализация // Инициализация
$delimiter = ','; $delimiter = ',';
// Конвертация // Конвертация
$trgt = array_map('trim', explode($delimiter, $trgt)); $account = array_map('trim', explode($delimiter, $account));
if (in_array('@all', $trgt, true)) { if (in_array('@all', $account, true)) {
// Найден флаг обозначающий отправку всем пользователям // Найден флаг обозначающий отправку всем пользователям
// Инициализация // Инициализация
$return = []; $return = [];
foreach (Account::readAll() as $target) { foreach (Account::readAll() as $account) {
// Перебор всех аккаунтов // Перебор всех аккаунтов
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($this->readId(), $target->readId(), $type) ? $this : null; $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type) ? $model : null;
} }
return $return ? $return : null; return $return ? $return : null;
} }
if (in_array('@test', $trgt, true)) { if (in_array('@test', $account, true)) {
// Найден флаг обозначающий тестирование (отправка самому себе) // Найден флаг обозначающий тестирование (отправка самому себе)
test: test:
return AccountEdgeNotification::writeSafe($this->readId(), yii::$app->user->id, $type) ? $this : null; return AccountEdgeNotification::writeSafe($model->readId(), yii::$app->user->id, $type) ? $model : null;
} }
} }
if (is_array($trgt)) { if (is_array($account)) {
// Несколько получателей // Несколько получателей
// Инициализация // Инициализация
$return = []; $return = [];
foreach ($trgt as $target) { foreach ($account as $account) {
// Перебор получателей // Перебор получателей
if ($target instanceof Account) { if ($account instanceof Account) {
// Один получатель // Один получатель
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
return AccountEdgeNotification::writeSafe($this->readId(), $target->readId(), $type) ? $this : null; return AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type) ? $model : null;
} }
if ($target = Account::searchById(Account::collectionName() . '/' . $target)) { if ($account = Account::searchById(Account::collectionName() . '/' . $account)) {
// Аккаунт найден // Аккаунт найден
echo ($target->readId()) . "\n"; echo ($account->readId()) . "\n";
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($this->readId(), $target->readId(), $type) ? $this : null; $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type) ? $model : null;
} }
} }
return $return ? $return : null; return $return ? $return : null;
} else if ($trgt instanceof Account) { } else if ($account instanceof Account) {
// Один получатель // Один получатель
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
return AccountEdgeNotification::writeSafe($this->readId(), $trgt->readId(), $type) ? $this : null; return AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type) ? $model : null;
} }
} }

View File

@ -41,7 +41,9 @@ class Order extends Document
{ {
return array_merge( return array_merge(
parent::attributes(), parent::attributes(),
[] [
'stts'
]
); );
} }
@ -52,7 +54,9 @@ class Order extends Document
{ {
return array_merge( return array_merge(
parent::attributeLabels(), parent::attributeLabels(),
[] [
'stts' => 'Статус'
]
); );
} }
@ -63,21 +67,43 @@ class Order extends Document
{ {
return array_merge( return array_merge(
parent::rules(), parent::rules(),
[] [
[
'stts',
'string',
'message' => '{attribute} должен быть строкой'
],
[
'stts',
'default',
'value' => 'preparing'
]
]
); );
} }
/** /**
* Запись * Подключение к аккаунту
*/
public function connect(Account $account): ?AccountEdgeOrder
{
// Запись ребра: АККАУНТ -> ЗАКАЗ
return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
/**
* Запись товара
* *
* $supply = [ Supply $supply, int $amount = 1 ] * $supply = [ Supply $supply, int $amount = 1 ]
* *
* @param Supply|array $supply Поставка * @param Supply|array $supply Поставка
* @param Account $trgt Заказчик * @param Account $trgt Заказчик
* *
* @return int Количество записанных поставок
*
* @todo Создать параметр разделителя для администрации * @todo Создать параметр разделителя для администрации
*/ */
public function write(Supply|array $supply, Account $trgt = null): self|null public function writeSupply(Supply|string|array $supply, Account $trgt = null): int
{ {
// Инициализация // Инициализация
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика'); $trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика');
@ -86,7 +112,7 @@ class Order extends Document
// Передана инстанция класса поставки или второй элемент массива не является числом // Передана инстанция класса поставки или второй элемент массива не является числом
// Унификация входных данных // Унификация входных данных
$supply = [$supply, 1]; $supply = [$supply->catn => 1];
} }
if (is_null($this->_key)) { if (is_null($this->_key)) {
@ -107,40 +133,98 @@ class Order extends Document
} }
} }
foreach (is_array($supply[0]) ? $supply : [$supply] as $supply_raw) { // Инициализация
$amount = 0;
foreach (is_array($supply) ? $supply : [$supply => 1] as $supply_raw => $amount_raw) {
// Перебор поставок // Перебор поставок
for ($i = 0; $i < $supply_raw[1]; $i++) { for ($i = 0; $i < $amount_raw; $i++) {
// Создание рёбер соразмерно запросу (добавление нескольких продуктов в корзину) // Создание рёбер соразмерно запросу (добавление нескольких продуктов в корзину)
// Запись ребра: ЗАКАЗ -> ПОСТАВКА // Запись ребра: ЗАКАЗ -> ПОСТАВКА
if (!$supply = Supply::searchByCatn($supply_raw[0]) or !OrderEdgeSupply::write($this->readId(), $supply->readId(), 'write')) { if (!$supply_model = Supply::searchByCatn($supply_raw) or !OrderEdgeSupply::write($this->readId(), $supply_model->readId(), 'write')) {
// Поставка не найдена или запись ребра не удалась // Поставка не найдена или запись ребра не удалась
var_dump('ПИЗДА');
continue; continue;
} else {
// Ребро создано (товар подключен к заказу)
// Постинкрементация счётчика добавленных товаров
$amount++;
// Запись в журнал
$this->journal('write', ['target' => $supply_model->readId()]);
} }
} }
} }
// Отправка на сервер базы данных if ($amount === 0) {
return $this->save() ? $this : null; // Отправка уведомления
self::notification('Неудачная попытка добавить товар в корзину');
} else if ($amount === 1) {
// Отправка уведомления
self::notification('Товар ' . $supply_model->catn . ' добавлен в корзину');
} else {
// Отправка уведомления
self::notification('Добавлено ' . $amount . ' товаров в корзину');
}
return $amount;
} }
/** /**
* Подключение к аккаунту * Удаление поставки
*
* @param Supply|string|array $supply Товары
*
* @return int Количество удалённых рёбер
*/ */
public function connect(Account $account): ?AccountEdgeOrder { public function deleteSupply(Supply|string|array $supply): int
// Запись ребра: АККАУНТ -> ЗАКАЗ
return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
/**
* Удаление
*/
public function deleteEdge(string|array $catn): ?OrderEdgeSupply
{ {
// Запись ребра: ЗАКАЗ -> ПОСТАВКА // Инициализация
return OrderEdgeSupply::write($this->readId(), Supply::searchByCatn($catn)->readId(), 'delete'); $amount = 0;
if ($supply instanceof Supply) {
// Передана инстанция класса поставки или второй элемент массива не является числом
// Унификация входных данных
$supply = [$supply->catn => 1];
}
foreach (is_array($supply) ? $supply : [$supply => 1] as $catn => $amount_raw) {
// Перебор товаров
if ($supply = Supply::searchByCatn($catn)) {
foreach (OrderEdgeSupply::searchByVertex($this->readId(), $supply->readId(), limit: $amount_raw) as $edge) {
// Перебор рёбер до продукта (если товаров в заказе несколько)
// Удаление
$edge->delete();
// Запись в журнал
$this->journal('delete', ['target' => $supply->readId()]);
// Постинкрементация счётчика удалённых рёбер
$amount++;
}
}
}
if ($amount === 0) {
// Отправка уведомления
self::notification('Неудачная попытка удалить товар из корзины');
} else if ($amount === 1) {
// Отправка уведомления
self::notification('Товар ' . $supply->catn . ' удалён из корзины');
} else {
// Отправка уведомления
self::notification('Удалено ' . $amount . ' товаров из корзины');
}
return $amount;
} }
/** /**
@ -195,26 +279,6 @@ class Order extends Document
// Генерация сдвига по запрашиваемым данным (пагинация) // Генерация сдвига по запрашиваемым данным (пагинация)
$offset = $limit * ($page - 1); $offset = $limit * ($page - 1);
// Подзапрос для проверки статуса уведомления относительно пользователя
// Поиск рёбер: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ
$let = Supply::find()
->for(['order', 'order_edge_supply_deleted'])
->traversal('supply', 'INBOUND')
->in('order_edge_supply')
->where([
[
'order._id' => $this->readId()
],
[
'order_edge_supply_deleted.type' => 'delete'
]
])
->select('order_edge_supply_deleted');
// Генерация
$let = $let->createCommand();
// Поиск рёбер: ЗАКАЗ -> ПОСТАВКА // Поиск рёбер: ЗАКАЗ -> ПОСТАВКА
$supplies = Supply::searchByEdge( $supplies = Supply::searchByEdge(
from: 'order', from: 'order',
@ -223,18 +287,10 @@ class Order extends Document
subquery_where: [ subquery_where: [
[ [
'order._id' => $this->readId() 'order._id' => $this->readId()
],
[
'order_edge_supply.type' => 'write'
] ]
], ],
params: $let->getBindVars(),
let: [
'order_edge_supply_deleted',
'(' . (string) $let . ')'
],
foreach: ['edge' => 'order_edge_supply'], foreach: ['edge' => 'order_edge_supply'],
where: 'edge._to == supply._id && edge._to != order_edge_supply_deleted[0]._to', where: 'edge._to == supply._id',
limit: $limit, limit: $limit,
offset: $offset, offset: $offset,
direction: 'INBOUND' direction: 'INBOUND'
@ -257,6 +313,7 @@ class Order extends Document
continue; continue;
} }
// Инициализация
$amount = 0; $amount = 0;
// Повторный перебор для поиска дубликатов // Повторный перебор для поиска дубликатов
@ -286,9 +343,10 @@ class Order extends Document
if ($cost < 1) { if ($cost < 1) {
// Если стоимость равна нулю (явная ошибка) // Если стоимость равна нулю (явная ошибка)
$this->deleteEdge($supply->readId()); // Удаление из базы данных
$this->deleteSupply($supply->readId());
// Удаление // Удаление из списка
unset($supplies[$key]); unset($supplies[$key]);
// Пропуск итерации // Пропуск итерации
@ -301,4 +359,13 @@ class Order extends Document
return $supplies; return $supplies;
} }
/**
* Отправка уведомления
*/
public static function notification(string $text, string|null $type = Notification::TYPE_NOTICE): Notification|array|null
{
// Отправка
return Notification::_write($text, type: $type);
}
} }

View File

@ -4,9 +4,12 @@ declare(strict_types=1);
namespace app\models; namespace app\models;
use moonland\phpexcel\Excel; use yii;
use app\models\traits\SearchByEdge; use app\models\traits\SearchByEdge;
use moonland\phpexcel\Excel;
/** /**
* Продукт (в ассортименте магазина) * Продукт (в ассортименте магазина)
* *
@ -20,6 +23,8 @@ class Product extends Document
/** /**
* Сценарий импорта из .excel документа * Сценарий импорта из .excel документа
*
* Использовать для обхода правил при загрузке файла
*/ */
const SCENARIO_IMPORT = 'import'; const SCENARIO_IMPORT = 'import';
@ -31,7 +36,7 @@ class Product extends Document
/** /**
* Файл .excel для импорта товаров * Файл .excel для импорта товаров
*/ */
public Excel|null $file = null; public Excel|string|array|null $file = null;
/** /**
* Группа в которой состоит товар * Группа в которой состоит товар
@ -54,7 +59,6 @@ class Product extends Document
return array_merge( return array_merge(
parent::attributes(), parent::attributes(),
[ [
'name',
'desc', 'desc',
'ocid', 'ocid',
'catn', 'catn',
@ -74,16 +78,15 @@ class Product extends Document
return array_merge( return array_merge(
parent::attributeLabels(), parent::attributeLabels(),
[ [
'name' => 'Название', 'catn' => 'Каталожный номер (catn)',
'desc' => 'Описание', 'desc' => 'Описание (desc)',
'ocid' => 'Идентификатор 1C', 'ocid' => 'Идентификатор 1C (ocid)',
'catn' => 'Каталожный номер', 'imgs' => 'Изображения (imgs)',
'imgs' => 'Изображения', 'time' => 'Срок доставки (time)',
'time' => 'Срок доставки', 'oemn' => 'OEM номера (oemn)',
'oemn' => 'OEM номера', 'cost' => 'Стоимость (cost)',
'cost' => 'Стоимость', 'file' => 'Документ (file)',
'file' => 'Документ', 'group' => 'Группа (group)'
'group' => 'Группа'
] ]
); );
} }
@ -97,21 +100,12 @@ class Product extends Document
parent::rules(), parent::rules(),
[ [
[ [
[ 'catn',
'name',
'catn'
],
'required', 'required',
'message' => 'Заполните поля: {attribute}', 'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_WRITE, 'on' => self::SCENARIO_WRITE,
'except' => self::SCENARIO_IMPORT 'except' => self::SCENARIO_IMPORT
], ],
[
'file',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT
],
[ [
'catn', 'catn',
'string', 'string',
@ -125,6 +119,12 @@ class Product extends Document
'arrayValidator', 'arrayValidator',
'message' => '{attribute} должен быть массивом.' 'message' => '{attribute} должен быть массивом.'
], ],
[
'file',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT
],
[ [
'file', 'file',
'file', 'file',
@ -142,13 +142,13 @@ class Product extends Document
} }
/** /**
* Запись * Инициализация продукта
* *
* @param string $catn Артикул, каталожный номер * @param string $catn Артикул, каталожный номер
*/ */
public static function initEmpty(string $catn): self|array public static function initEmpty(string $catn): self|array
{ {
$oemn = self::convertOemn2Catn($catn); $oemn = self::searchOemn($catn);
if (count($oemn) === 1) { if (count($oemn) === 1) {
// Передан только один артикул // Передан только один артикул
@ -159,7 +159,7 @@ class Product extends Document
return $model; return $model;
} }
// Запись // Запись пустого продукта
return self::writeEmpty($catn); return self::writeEmpty($catn);
} }
@ -188,7 +188,7 @@ class Product extends Document
} }
/** /**
* Запись * Запись пустого продукта
*/ */
public static function writeEmpty(string $catn): ?self public static function writeEmpty(string $catn): ?self
{ {
@ -205,12 +205,14 @@ class Product extends Document
/** /**
* Поиск OEM номеров * Поиск OEM номеров
* *
* @param string $oemn OEM номера * @param string $oemn Необработанная строка с OEM-номерами
* @param string $delimiters Разделители * @param string $delimiters Разделители
* *
* @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ * @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ
*
* @return array OEM-номера
*/ */
public static function convertOemn2Catn(string $oemn, string $delimiters = '\s\+\/,'): array public static function searchOemn(string $oemn, string $delimiters = '\s\+\/,'): array
{ {
// Инициализация // Инициализация
$catn = []; $catn = [];
@ -229,20 +231,22 @@ class Product extends Document
*/ */
public function import(): bool public function import(): bool
{ {
// Инициализация массива данных // Инициализация
$data = []; $data = [];
$amount = 0;
if ($this->validate()) { if ($this->validate()) {
foreach ($this->file as $file) { foreach ($this->file as $file) {
// Перебор файлов // Перебор файлов
// Сохранение на диск // Инициализация
if (!file_exists('../assets/import/excel/')) { $dir = '../assets/import/' . date('Y_m_d#H-i', time()) . '/excel/';
mkdir('../assets/import/excel/', 0775, true);
}
$file->saveAs($path = '../assets/import/excel/' . $file->baseName . '.' . $file->extension);
// Проверка файла пройдена // Сохранение на диск
if (!file_exists($dir)) {
mkdir($dir, 0775, true);
}
$file->saveAs($path = $dir . $file->baseName . '.' . $file->extension);
$data[] = Excel::import($path, [ $data[] = Excel::import($path, [
'setFirstRecordAsKeys' => true, 'setFirstRecordAsKeys' => true,
@ -250,21 +254,37 @@ class Product extends Document
]); ]);
} }
foreach ($data[0] as $doc) {
foreach ($data as $data) {
// Перебор конвертированных файлов
if (count($data) < 1) {
// Не найдены строки с товарами
$this->addError('erros', 'Не удалось найти данные товаров');
} else {
// Перебор найденных товаров
foreach ($data as $doc) {
// Перебор полученных документов // Перебор полученных документов
// Сохранение в базе данных // Сохранение в базе данных
$product = new static($doc); $product = new static($doc);
$product->scenario = $product::SCENARIO_WRITE;
if ($product->validate()) { if ($product->validate()) {
// Проверка пройдена // Проверка пройдена
// Запись документа // Запись документа
$product->save(); $product->save();
// Постинкрементация счётчика
$amount++;
// Запись группы // Запись группы
$group = static::class . 'Group'; // $group = static::class . 'Group';
(new $group())->writeMember($product, $this->group); // (new $group())->writeMember($product, $this->group);
} else { } else {
// Проверка не пройдена // Проверка не пройдена
foreach ($product->errors as $attribute => $error) { foreach ($product->errors as $attribute => $error) {
@ -272,10 +292,21 @@ class Product extends Document
} }
} }
} }
}
}
// Деинициализация
$this->file = '';
static::afterImportExcel($amount);
return true; return true;
} }
$this->addError('erros', 'Неизвестная ошибка');
static::afterImportExcel($amount);
return false; return false;
} }
@ -310,4 +341,42 @@ class Product extends Document
return $query; return $query;
} }
/**
* Вызывается после загрузки поставок из excel-документа
*
* @param int $amount Количество
*/
public static function afterImportExcel(int $amount = 0): bool
{
// Инициализация
$model = new Notification;
$date = date('H:i d.m.Y', time());
// Настройка
$model->text = yii::$app->controller->renderPartial('/notification/system/afterImportExcel', compact('amount', 'date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
/**
* Вызывается после загрузки поставок из 1С
*
* @param int $amount Количество
*/
public static function afterImportOnec(): bool
{
// Инициализация
$model = new Notification;
$date = date('H:i d.m.Y', time());
// Настройка
$model->text = yii::$app->controller->renderPartial('/notification/system/afterImportOnec', compact('amount', 'date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
} }

View File

@ -86,13 +86,13 @@ class Search extends Document
* Запись * Запись
* *
* @param string $text Текст запроса * @param string $text Текст запроса
* @param IdentityInterface|null $user Пользователь совершивший запрос * @param Account|null $account Пользователь совершивший запрос
*/ */
public static function write(string $text, IdentityInterface $user = null): ?self public static function write(string $text, Account|null $account = null): ?self
{ {
// Инициализация // Инициализация
$vertex = new self; $vertex = new self;
isset($user) && yii::$app->user->isGuest ?: $user = yii::$app->user->identity; isset($account) && yii::$app->user->isGuest ?: $account = yii::$app->user->identity;
// Настройки // Настройки
$vertex->text = $text; $vertex->text = $text;
@ -101,7 +101,7 @@ class Search extends Document
// Поиск записан // Поиск записан
// Запись ребра: АККАУНТ -> ПОИСК // Запись ребра: АККАУНТ -> ПОИСК
return $user && AccountEdgeSearch::writeSafe($user->id, $vertex->readId(), 'request') ? $vertex : null; return $account && AccountEdgeSearch::writeSafe($account->id, $vertex->readId(), 'request') ? $vertex : null;
} }
return null; return null;

View File

@ -4,13 +4,16 @@ declare(strict_types=1);
namespace app\models; namespace app\models;
use Yii; use yii;
use yii\web\User;
use app\models\Account;
use app\models\Product; use app\models\Product;
use app\models\SupplyEdgeProduct; use app\models\SupplyEdgeProduct;
use app\models\traits\Xml2Array; use app\models\traits\Xml2Array;
use carono\exchange1c\interfaces\ProductInterface; use carono\exchange1c\interfaces\ProductInterface;
use carono\exchange1c\controllers\ApiController;
use exception; use exception;
@ -73,7 +76,7 @@ class Supply extends Product implements ProductInterface
public function afterSave($data, $vars): void public function afterSave($data, $vars): void
{ {
if (AccountEdgeSupply::searchByVertex(yii::$app->user->id, $this->readId())) { if (AccountEdgeSupply::searchByVertex(yii::$app->user->id, $this->readId())) {
// Ребро уже существует // Ребро: "АККАУНТ -> ПОСТАВКА" уже существует
} else { } else {
// Ребра не существует // Ребра не существует
@ -97,20 +100,21 @@ class Supply extends Product implements ProductInterface
public function setGroup1c($group): mixed public function setGroup1c($group): mixed
{ {
// Чтение группы // Чтение группы
// if ($group = SupplyGroup::readByOnecId($group->id)) { // if ($group = SupplyGroup::readByOcid($group->id)) {
// // Запись ребра: ПОСТАВКА => ГРУППА ПОСТАВОК // // Запись ребра: ПОСТАВКА => ГРУППА ПОСТАВОК
// return static::writeEdgeBetweenGroup(static::collectionName() . '/' . $this->_key, $group->collectionName() . '/' . $group->_key); // return static::writeEdgeBetweenGroup(static::collectionName() . '/' . $this->_key, $group->collectionName() . '/' . $group->_key);
// } // }
return true; return true;
} }
/** /**
* Поиск через связь с аккаунтом * Поиск через связь с аккаунтом
* *
* @param string $id Идентификатор пользователя * @param string|null $id Идентификатор пользователя
* @param bool $select Запрашиваемые значения * @param string|array|null $select Запрашиваемые значения
*/ */
public static function searchByAccount(string $id = null, string|array $select = []): array public static function searchByAccount(string|null $id = null, string|array|null $select = null, int|null $limit = 10): array
{ {
isset($id) ?: $id = yii::$app->user->id ?? throw new Exception('Не найден идентификатор'); isset($id) ?: $id = yii::$app->user->id ?? throw new Exception('Не найден идентификатор');
@ -124,17 +128,7 @@ class Supply extends Product implements ProductInterface
], ],
where: 'supply._id == account_edge_supply[0]._to AND supply.onec["ЗначенияСвойств"] != null', where: 'supply._id == account_edge_supply[0]._to AND supply.onec["ЗначенияСвойств"] != null',
select: $select, select: $select,
// handle: function ($request) { limit: $limit
// $response = $request->createCommand()->execute()->getAll();
// foreach ($response as &$attribute) {
// // Приведение всех свойств в массив и очистка от лишних данных
// $attribute = $attribute->getAll();
// }
// return $response;
// }
); );
} }
@ -146,13 +140,15 @@ class Supply extends Product implements ProductInterface
* *
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать * @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
*/ */
public static function createProperties1c($properties): void public static function createProperties1c($properties, Account|null $account = null): void
{ {
// Инициализация // Инициализация
$models = self::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]'); $account ?? $account = yii::$app->user->identity;
$models = self::searchByAccount($account->readId());
$properties = self::xml2array($properties->xml); $properties = self::xml2array($properties->xml);
// for ($i = 0; $i <= count($models); $i++) $account->on(ApiController::EVENT_AFTER_OFFER_SYNC, self::afterImport());
foreach ($models as $model) { foreach ($models as $model) {
// Перебор записей // Перебор записей
@ -161,7 +157,7 @@ class Supply extends Product implements ProductInterface
$transit = $model->onec; $transit = $model->onec;
foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) { foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) {
// Перебор аттрибутовfw // Перебор аттрибутов
foreach ($properties as $property) { foreach ($properties as $property) {
// Перебор свойств // Перебор свойств
@ -174,8 +170,6 @@ class Supply extends Product implements ProductInterface
// Запись индикатора наличия изменений // Запись индикатора наличия изменений
$changes = true; $changes = true;
// break;
} else { } else {
// Объединение данных // Объединение данных
$transit['ЗначенияСвойств'][$attribute_name] = $property; $transit['ЗначенияСвойств'][$attribute_name] = $property;
@ -186,19 +180,19 @@ class Supply extends Product implements ProductInterface
if ($changes) { if ($changes) {
// Если указано, что записаны изменения // Если указано, что записаны изменения
// Настройка ($transit нужен из-за ограничений __set()) // Настройка ($transit нужен из-за особенностей __set())
$model->onec = $transit; $model->onec = $transit;
foreach ($model->onec['ЗначенияСвойств'] as $property) { foreach ($model->onec['ЗначенияСвойств'] as $property) {
// Перебор всех свойств // Перебор всех свойств
if (is_array($property)) { if (is_array($property)) {
if ($property['Ид'] === 'd99622fe-4526-11eb-b7f3-f3e52d0a06a9') { // if ($property['Ид'] === $account->opts['import_sections_oem']) {
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера // // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
// Настройка // Настройка
$model->catn = $property['Значение']; $model->catn = $property['Значение'];
} // }
} }
} }
@ -273,6 +267,7 @@ class Supply extends Product implements ProductInterface
* Запись ребра (предложения от поставок к продуктам) из 1С * Запись ребра (предложения от поставок к продуктам) из 1С
* *
* @todo Разобраться зачем нужно возвращать SupplyEdgeProduct * @todo Разобраться зачем нужно возвращать SupplyEdgeProduct
* Вернуть создание карточек, но только по условиям (загрузка от админа, например)
*/ */
public function getOffer1c($offer): SupplyEdgeProduct public function getOffer1c($offer): SupplyEdgeProduct
{ {
@ -283,53 +278,55 @@ class Supply extends Product implements ProductInterface
return new SupplyEdgeProduct; return new SupplyEdgeProduct;
} }
// Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑ // Создание продукта (временно заблокировано)
$product = Product::initEmpty($this->catn);
if (!is_array($product)) { // // Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑
// Создался только один товар и вернулся в виде модели // $product = Product::initEmpty($this->catn);
$product = [$product]; // if (!is_array($product)) {
} // // Создался только один товар и вернулся в виде модели
if (is_array($this->oemn)) { // $product = [$product];
// Значение OEM было инициализировано // }
foreach ($this->oemn as $oem) { // if (is_array($this->oemn)) {
// Перебор артикулов из массива ОЕМ-номеров // // Значение OEM было инициализировано
// Инициализация и запись // foreach ($this->oemn as $oem) {
$product[] = Product::initEmpty($oem); // // Перебор артикулов из массива ОЕМ-номеров
}
}
foreach ($product as $product) { // // Инициализация и запись
// Перебор всех инициализированных продуктов // $product[] = Product::initEmpty($oem);
// }
// }
if ($this->catn !== $product->catn) { // foreach ($product as $product) {
// Каталожные номера не соответствуют друг другу // // Перебор всех инициализированных продуктов
continue; // if ($this->catn !== $product->catn) {
} // // Каталожные номера не соответствуют друг другу
// Код ниже скорее всего устарел // continue;
// }
if (SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId())) { // // Код ниже скорее всего устарел
// Ребро уже существует
continue; // if (SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId())) {
} // // Ребро уже существует
// Запись ребра: ПОСТАВКА -> ПРОДУКТ // continue;
$return = (new SupplyEdgeProduct)->write( // }
$this->readId(),
$product->readId(), // // Запись ребра: ПОСТАВКА -> ПРОДУКТ
'connect', // $return = (new SupplyEdgeProduct)->write(
[ // $this->readId(),
'onec' => self::xml2array($offer->xml) // $product->readId(),
] // 'connect',
); // [
} // 'onec' => self::xml2array($offer->xml)
// ]
// );
// }
// Возвращает последнее сохранённое ребро // Возвращает последнее сохранённое ребро
// Надо будет с этим разобраться // Надо будет с этим разобраться
@ -337,14 +334,18 @@ class Supply extends Product implements ProductInterface
} }
/** /**
* Запись продукта из 1С * Запись продукта из 1С (поставка)
*
* @see Supply
* *
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать * @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
* Разобраться и создать возможность загрузки от лица другого аккаунта
*/ */
public static function createModel1c($product): ?self public static function createModel1c($product): ?self
{ {
// Инициализация // Инициализация
$model = self::searchByOnecId($id = (string) $product->Ид) ?? new self; $model = self::searchByOcid($id = (string) $product->Ид) ?? new self;
$account ?? $account = yii::$app->user->identity;
// Настройка // Настройка
$model->ocid = $id ?? null; $model->ocid = $id ?? null;
@ -359,11 +360,11 @@ class Supply extends Product implements ProductInterface
// Перебор всех свойств // Перебор всех свойств
if (is_array($property)) { if (is_array($property)) {
if ($property['Ид'] === 'd99622fe-4526-11eb-b7f3-f3e52d0a06a9') { if (!empty($account->opts['import_sections_oem']) && $property['Ид'] === $account->opts['import_sections_oem']) {
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
// Настройка // Настройка
$model->oemn = array_merge(self::convertOemn2Catn($property['Значение']), self::convertOemn2Catn((string) $product->Артикул)); $model->oemn = array_merge(self::searchOemn($property['Значение']), self::searchOemn((string) $product->Артикул));
} }
} }
} }
@ -402,7 +403,7 @@ class Supply extends Product implements ProductInterface
* *
* @return Supply|null * @return Supply|null
*/ */
public static function searchByOnecId(string $ocid): ?Supply public static function searchByOcid(string $ocid): ?Supply
{ {
return static::findOne([static::getIdFieldName1c() => $ocid]); return static::findOne([static::getIdFieldName1c() => $ocid]);
} }

View File

@ -21,19 +21,19 @@ trait SearchByEdge
public static function searchByEdge( public static function searchByEdge(
string $from, string $from,
string $to, string $to,
string $edge = null, string|null $edge = null,
int $limit = 10, int|null $limit = 10,
int $offset = 0, int|null $offset = 0,
array $sort = ['ASC'], array $sort = ['ASC'],
string|array $subquery_where = [], string|array $subquery_where = [],
array $foreach = [], array $foreach = [],
string|array $where = [], string|array $where = [],
string $direction = 'ANY', string $direction = 'ANY',
array $let = [], array|null $let = [],
string|array $select = null, string|array $select = null,
callable $handle = null, callable|null $handle = null,
array $params = [] array $params = []
): ?array { ): mixed {
$subquery = static::find() $subquery = static::find()
->params($params) ->params($params)
->for([$from, $edge ?? $from . '_edge_' . $to]) ->for([$from, $edge ?? $from . '_edge_' . $to])

View File

@ -5,18 +5,20 @@ declare(strict_types=1);
use yii; use yii;
if (!yii::$app->user->isGuest) { if (!yii::$app->user->isGuest) {
$popup = yii::$app->controller->renderPartial('/notification/panel'); // $popup = yii::$app->controller->renderPartial('/notification/panel');
echo <<<HTML // echo <<<HTML
<a id="notification_button" class="text-dark d-flex h-100 mr-2" title="Уведомления" role="button" data-toggle="dropdown" data-offset="-100%p + 100%" onclick="return notification_stream();"> // <a id="notification_button" class="text-dark d-flex h-100 mr-2" title="Уведомления" role="button" data-toggle="dropdown" data-offset="-100%p + 100%" onclick="return notification_stream();">
<i class="fas fa-bell my-auto mx-2"></i> // <i class="fas fa-bell my-auto mx-2"></i>
</a> // </a>
<div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button"> // <div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
$popup // $popup
</div> // </div>
HTML; // HTML;
} }
?> ?>
<?=$notifications_button?>
<?=$notifications_panel?>
<a class="text-dark my-auto mr-2" title="Корзина" href="/cart" role="button" onclick="return page_cart();"><i class="fas fa-shopping-cart mx-2"></i></a> <a class="text-dark my-auto mr-2" title="Корзина" href="/cart" role="button" onclick="return page_cart();"><i class="fas fa-shopping-cart mx-2"></i></a>
<a class="text-dark my-auto mr-2" title="Заказы" href="/orders" role="button" onclick="return page_orders();"><i class="fas fa-list mx-2"></i></a> <a class="text-dark my-auto mr-2" title="Заказы" href="/orders" role="button" onclick="return page_orders();"><i class="fas fa-list mx-2"></i></a>
<div class="btn-group my-auto"> <div class="btn-group my-auto">

View File

@ -11,10 +11,10 @@
<div class="col-2"> <div class="col-2">
<span>Артикул</span> <span>Артикул</span>
</div> </div>
<div class="col-3"> <div class="col-4">
<span>Описание</span> <span>Описание</span>
</div> </div>
<div class="col-2 ml-auto text-center"> <div class="col-1 ml-auto px-0 text-center">
<span>Количество</span> <span>Количество</span>
</div> </div>
<div class="col-2 text-right"> <div class="col-2 text-right">
@ -30,16 +30,16 @@
echo <<<HTML echo <<<HTML
<div class="row py-2 cart_list_target"> <div class="row py-2 cart_list_target">
<div class="pl-3 mr-1"> <div class="pl-3 mr-1">
<input id="checkbox_cart_$supply->catn" type="checkbox" onchange="return cart_list_checkbox(this);"/> <input id="cart_list_checkbox_$supply->catn" type="checkbox" onchange="return cart_list_checkbox(this);"/>
</div> </div>
<div class="col-2"> <div class="col-2">
$supply->catn $supply->catn
</div> </div>
<div class="col-3"> <div class="col-4">
$supply->desc $supply->desc
</div> </div>
<div class="col-2 ml-auto text-center"> <div class="col-1 ml-auto">
$supply->amnt <input id="cart_list_amnt_$supply->catn" class="form-control text-center" type="text" value="$supply->amnt" onchange="return cart_list_amount_update('$supply->catn', this)" aria-invalid="false">
</div> </div>
<div class="col-2 text-right"> <div class="col-2 text-right">
$supply->time $supply->time

View File

@ -19,6 +19,25 @@ AppAsset::register($this);
<meta charset="<?= Yii::$app->charset ?>"> <meta charset="<?= Yii::$app->charset ?>">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="57x57" href="/favicons/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/favicons/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/favicons/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/favicons/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/favicons/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/favicons/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/favicons/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/favicons/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/favicons/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicons/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png">
<link rel="manifest" href="/favicons/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/favicons/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<?php $this->registerCsrfMetaTags() ?> <?php $this->registerCsrfMetaTags() ?>
<title><?= Html::encode($this->title ?? 'SkillParts') ?></title> <title><?= Html::encode($this->title ?? 'SkillParts') ?></title>
<?php $this->head() ?> <?php $this->head() ?>

View File

@ -0,0 +1,18 @@
<a id="notification_button" class="text-dark d-flex h-100 mr-2" title="Уведомления" role="button" data-toggle="dropdown" data-offset="-100%p + 100%" onclick="return notification_stream();">
<?
if (empty($notifications_new_amount) || $notifications_new_amount < 1) {
// Новые уведомления не найдены
echo <<<HTML
<i class="fas fa-bell my-auto mx-2"></i>
HTML;
} else {
// Новые уведомления найдены
echo <<<HTML
<small class="my-auto ml-2 mr-1"><b>$notifications_new_amount</b></small>
<i class="fas fa-bell my-auto mr-2 notification_button_active"></i>
HTML;
}
?>
</a>

View File

@ -0,0 +1,30 @@
<?php
if (empty($notifications)) {
echo <<<HTML
<p class="px-2 py-4 text-center">Уведомлений нет</p>
HTML;
return;
}
foreach($notifications as $notification) {
// Перебор уведомлений
// Инициализация
$notification = $notification->getAttributes();
if ($notification['type'] === 'notice') {
// Уведомление
echo $notification['html'];
} else if ($notification['type'] === 'warning') {
// Предупреждение
echo $notification['html'];
} else {
// Неизвестно
echo $notification['html'] ?? '<p>ОШИБКА</p>';
}
}
?>

View File

@ -1,13 +1,17 @@
<?php <?php
if (empty($notifications)) { if (empty($notifications)) {
echo <<<HTML // Уведомления не найдены
$content = <<<HTML
<p class="px-2 py-4 text-center">Уведомлений нет</p> <p class="px-2 py-4 text-center">Уведомлений нет</p>
HTML; HTML;
} else {
// Уведомления найдены
return; // Инициализация
} $content = '';
foreach($notifications as $notification) { foreach ($notifications as $notification) {
// Перебор уведомлений // Перебор уведомлений
// Инициализация // Инициализация
@ -16,15 +20,26 @@
if ($notification['type'] === 'notice') { if ($notification['type'] === 'notice') {
// Уведомление // Уведомление
echo $notification['html']; $content .= $notification['html'];
} else if ($notification['type'] === 'warning') { } else if ($notification['type'] === 'warning') {
// Предупреждение // Предупреждение
echo $notification['html']; $content .= $notification['html'];
} else { } else {
// Неизвестно // Неизвестно
echo $notification['html'] ?? '<p>ОШИБКА</p>'; $content .= $notification['html'] ?? '<p>ОШИБКА</p>';
} }
} }
}
if($notifications_panel_full ?? false) {
echo <<<HTML
<div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
$content
</div>
HTML;
} else {
echo $content;
}
?> ?>

View File

@ -0,0 +1,13 @@
<?php
if($amount > 0) {
echo <<<HTML
<p>Импортировано $amount товаров из excel-документа <span>($date)</span></p>
HTML;
} else {
echo <<<HTML
<p>Неудачная попытка импорта товаров из excel-документа <span>($date)</span></p>
HTML;
}
?>

View File

@ -0,0 +1,2 @@
<p>Импортированы товары из 1C <span>(<?= $date ?>)</span></p>

View File

@ -120,7 +120,7 @@ use app\models\Product;
</div> </div>
<div class="row mt-auto mx-0"> <div class="row mt-auto mx-0">
<p class="ml-0">Время для повышения релевантности в поисковиках</p> <p class="ml-0">Время для повышения релевантности в поисковиках</p>
<time class="ml-auto"><?= date('d.m.Y', $model['date']) ?></time> <time class="ml-auto"><?= empty($row->jrnl) ? '' : date('H:i d.m.Y', end($row->jrnl)['date']); ?></time>
</div> </div>
</div> </div>
</article> </article>

View File

@ -5,7 +5,8 @@ declare(strict_types=1);
use yii; use yii;
use yii\bootstrap\ActiveForm; use yii\bootstrap\ActiveForm;
use app\models\Supply; // Инициализация
$panel ?? $panel = 'profile_panel_settings_import';
?> ?>
<link href="/css/pages/profile.css" rel="stylesheet"> <link href="/css/pages/profile.css" rel="stylesheet">
@ -25,17 +26,17 @@ use app\models\Supply;
<label class="btn button_white mb-0" for="profile_panel_settings_import">Импорт</label> <label class="btn button_white mb-0" for="profile_panel_settings_import">Импорт</label>
</div> </div>
<div class="profile_panel_content d-flex"> <div class="profile_panel_content d-flex">
<input type="radio" id="profile_panel_settings_account" name="main_panel" /> <input type="radio" id="profile_panel_settings_account" name="main_panel" <?= $panel === 'profile_panel_settings_account' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
1 1
</div> </div>
<input type="radio" id="profile_panel_settings_company" name="main_panel" /> <input type="radio" id="profile_panel_settings_company" name="main_panel" <?= $panel === 'profile_panel_settings_company' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
2 2
</div> </div>
<input type="radio" id="profile_panel_settings_import" name="main_panel" checked /> <input type="radio" id="profile_panel_settings_import" name="main_panel" <?= $panel === 'profile_panel_settings_import' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
<h5>Параметры 1C</h5> <h5>Параметры 1C</h5>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-3"></div>
@ -51,59 +52,18 @@ use app\models\Supply;
] ]
]); ]);
/**
* @todo Перенести в модель
*/
// Инициализация // Инициализация
$model ?? $model = yii::$app->user->identity; $model ?? $model = yii::$app->user->identity;
$supplies ?? $supplies = Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]'); $list or $list = ['Нет данных'];
$list = [];
// Перебор свойств поставок
foreach ($supplies as $supply) {
// Инициализация
$id = $supply['ЗначенияСвойства']['Ид'];
if (in_array($id, $list, true)) {
// Если встретился дубликат (вызывается очень часто)
continue;
}
// Генерация
!isset($supply['ЗначенияСвойства']['Наименование']) or $list[$id] = $supply['ЗначенияСвойства']['Наименование'];
}
// Инициализация текущего значения параметра в начале массива
if (isset($model->opts['import_sections_oem'])) {
// Параметр 'import_sections_oem' найден в настройках аккаунта
if (isset($list[$model->opts['import_sections_oem']])) {
// Найдено совпадение сохранённого параметра с полученным списком из поставок
// Буфер для сохранения параметра
$buffer = $list[$model->opts['import_sections_oem']];
// Удаление параметра
unset($list[$model->opts['import_sections_oem']]);
// Сохранение параметра в начале массива
$list = array_merge([$model->opts['import_sections_oem'] => $buffer], $list);
} else {
// Совпадение не найдено
// Сохранение параметра из данных аккаунта в начале массива
$list = array_merge([$model->opts['import_sections_oem'] => $model->opts['import_sections_oem']], $list);
}
}
?> ?>
<?= $form->field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]])->dropDownList($list ?? ['Нет данных'], ['onChange' => 'page_profile_settings(this.parentElement.parentElement)'])->label('OEM-номера'); ?> <?= $form->field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]])
->dropDownList($list, [
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_import\')',
'disabled' => count($list) <= 1
])->label('OEM-номера'); ?>
<small class="d-block mb-1">Выберите поле в котором хранятся <b>ОЕМ-номера</b> и повторите импорт</small> <small class="d-block mb-1">Выберите поле в котором хранятся <b>ОЕМ-номера</b> и повторите импорт</small>
<small class="d-block">Значения взяты из импортированных товаров</small> <small class="d-block">Значения взяты из импортированных товаров</small>
<!-- <small class="d-block mb-3">Значения взяты из импортированных товаров</small> -->
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
</div> </div>

View File

@ -2,6 +2,9 @@
declare(strict_types=1); declare(strict_types=1);
// Инициализация
$panel ?? $panel = 'profile_panel_monitoring_input_search_history';
?> ?>
<link href="/css/pages/profile.css" rel="stylesheet"> <link href="/css/pages/profile.css" rel="stylesheet">
@ -20,14 +23,14 @@ declare(strict_types=1);
<label class="btn button_white mb-0" for="profile_panel_monitoring_input_log">Журнал действий</label> <label class="btn button_white mb-0" for="profile_panel_monitoring_input_log">Журнал действий</label>
</div> </div>
<div class="profile_panel_content"> <div class="profile_panel_content">
<input type="radio" id="profile_panel_monitoring_input_orders_history" name="main_panel" /> <input type="radio" id="profile_panel_monitoring_input_orders_history" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_orders_history' ? 'checked' : null ?>/>
<div class="col p-3"> <div class="col p-3">
История заказов в разработке История заказов в разработке
</div> </div>
<input type="radio" id="profile_panel_monitoring_input_search_history" name="main_panel" checked /> <input type="radio" id="profile_panel_monitoring_input_search_history" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_search_history' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
<div class="row header_blue py-2 text-white"> <div class="row header_blue py-2">
<div class="col-sm-4 col-md-3">IPv4</div> <div class="col-sm-4 col-md-3">IPv4</div>
<div class="col-7 col-sm-3 col-md-4 col-lg-6 pr-0 px-sm-0 px-lg-3">Поисковый запрос</div> <div class="col-7 col-sm-3 col-md-4 col-lg-6 pr-0 px-sm-0 px-lg-3">Поисковый запрос</div>
<div class="col">Время</div> <div class="col">Время</div>
@ -35,7 +38,7 @@ declare(strict_types=1);
<?php <?php
foreach ($search_history as $row) { foreach ($search_history as $row) {
// Инициализация // Инициализация
$date = date('H:i d.m.Y', $row->date); $date = empty($row->jrnl) ? '' : date('H:i d.m.Y', end($row->jrnl)['date']);
echo <<<HTML echo <<<HTML
<div class="row py-1"> <div class="row py-1">
@ -52,9 +55,9 @@ declare(strict_types=1);
echo <<<HTML echo <<<HTML
<div class="row"> <div class="row">
<button class="btn button_clean button_blue" onclick="return page_profile_monitoring(-1);">Назад</button> <button class="btn button_clean button_blue" onclick="return page_profile_monitoring(-1, 'profile_panel_monitoring_input_search_history');">Назад</button>
<p>$page</p> <p>$page</p>
<button class="btn button_clean button_blue" onclick="return page_profile_monitoring(1);">Вперёд</button> <button class="btn button_clean button_blue" onclick="return page_profile_monitoring(1, 'profile_panel_monitoring_input_search_history');">Вперёд</button>
<div class="col-8"></div> <div class="col-8"></div>
</div> </div>
HTML; HTML;
@ -62,7 +65,7 @@ declare(strict_types=1);
</div> </div>
</div> </div>
<input type="radio" id="profile_panel_monitoring_input_log" name="main_panel" /> <input type="radio" id="profile_panel_monitoring_input_log" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_log' ? 'checked' : null ?>/>
<div class="col p-3"> <div class="col p-3">
Журнал в разработке Журнал в разработке
</div> </div>

View File

@ -25,17 +25,17 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile/trusted'; $targetUrl = '/profile/trusted';
if ('/' . yii::$app->request->pathInfo === $targetUrl) { if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<dt> <dt>
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a> <a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a>
</dt> </dt>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
<dt> <dt>
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a> <a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return page_profile_trusted_settings();"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a>
</dt> </dt>
HTML; HTML;
} }
@ -43,18 +43,18 @@ use app\models\SupplyEdgeProduct;
?> ?>
<dt> <dt>
<?php <?php
// Инициализация if (
$targetUrl = '/profile/supplies'; '/' . yii::$app->request->pathInfo === '/profile/supplies'
|| '/' . yii::$app->request->pathInfo === '/profile/import'
if ('/' . yii::$app->request->pathInfo === $targetUrl) { ) {
// Запрошена эта страница // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a> <a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a> <a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return page_profile_supplies();"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
HTML; HTML;
} }
?> ?>
@ -65,14 +65,14 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile/monitoring'; $targetUrl = '/profile/monitoring';
if ('/' . yii::$app->request->pathInfo === $targetUrl) { if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a> <a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a> <a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return page_profile_monitoring();"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
HTML; HTML;
} }
?> ?>
@ -83,14 +83,14 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile'; $targetUrl = '/profile';
if ('/' . yii::$app->request->pathInfo === $targetUrl) { if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a> <a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" href="$targetUrl"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a> <a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return page_profile_settings();"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a>
HTML; HTML;
} }
?> ?>

View File

@ -5,10 +5,11 @@ declare(strict_types=1);
use yii; use yii;
use yii\bootstrap\ActiveForm; use yii\bootstrap\ActiveForm;
use app\controllers\ProfileController; // Инициализация
use app\models\Supply; $panel ?? $panel = 'profile_panel_supplies_input_import';
?> ?>
<link href="/css/pages/profile.css" rel="stylesheet"> <link href="/css/pages/profile.css" rel="stylesheet">
<div id="page_profile" class="container h-100"> <div id="page_profile" class="container h-100">
@ -18,7 +19,23 @@ use app\models\Supply;
</nav> </nav>
<article class="col-9"> <article class="col-9">
<div class="h-100 p-4 rounded"> <div class="h-100 p-4 rounded">
<h4 class="ml-4 mb-4">Управление поставками</h4> <h4 class="ml-4 mb-4"><i class="fas fa-truck my-auto mr-2"></i>Управление поставками</h4>
<div id="profile_panel_supplies" class="profile_panel">
<div class="profile_panel_menu mb-3">
<label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_settings">Настройки</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_import">Импорт</label>
</div>
<div class="profile_panel_content">
<input type="radio" id="profile_panel_supplies_input_settings" name="main_panel" <?= $panel === 'profile_panel_supplies_input_settings' ? 'checked' : null ?> />
<div class="col">
<p>В разработке</p>
</div>
<input type="radio" id="profile_panel_supplies_input_import" name="main_panel" <?= $panel === 'profile_panel_supplies_input_import' ? 'checked' : null ?> />
<div class="col">
<h5>Импорт из Excel-документа</h5>
<div class="dropdown-divider mb-3"></div>
<?php <?php
$form = ActiveForm::begin([ $form = ActiveForm::begin([
'id' => 'form_product_import_excel', 'id' => 'form_product_import_excel',
@ -32,19 +49,17 @@ use app\models\Supply;
'onsubmit' => 'return false;' 'onsubmit' => 'return false;'
] ]
]); ]);
$model = $model ?? new Supply;
$groups = ProfileController::readGroups();
?> ?>
<?= $form->field($model, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?> <? //$form->field($model, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?>
<?= $form->field($model, 'file', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'supply_import(this.parentElement.parentElement)']) ?> <?= $form->field($model, 'file', ['enableLabel' => false])->fileInput(['multiple' => true, 'class' => 'mb-2', 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?>
<?= $form->errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?> <?= $form->errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
</div> </div>
</div>
</div>
</article> </article>
</div> </div>
</div> </div>

View File

@ -7,9 +7,12 @@ use yii\bootstrap\ActiveForm;
use yii\helpers\Html; use yii\helpers\Html;
use app\models\Notification; use app\models\Notification;
use app\models\Settings;
// Инициализация
$panel ?? $panel = 'profile_panel_trusted_input_notifications';
?> ?>
<link href="/css/pages/profile.css" rel="stylesheet"> <link href="/css/pages/profile.css" rel="stylesheet">
<div id="page_profile" class="container h-100"> <div id="page_profile" class="container h-100">
@ -26,7 +29,7 @@ use app\models\Settings;
<label class="btn button_white mb-0 mr-2" for="profile_panel_trusted_input_settings">Настройки</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_trusted_input_settings">Настройки</label>
</div> </div>
<div class="profile_panel_content"> <div class="profile_panel_content">
<input type="radio" id="profile_panel_trusted_input_notifications" name="main_panel" /> <input type="radio" id="profile_panel_trusted_input_notifications" name="main_panel" <?= $panel === 'profile_panel_trusted_input_notifications' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
<h5>Отправка уведомления</h5> <h5>Отправка уведомления</h5>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-3"></div>
@ -43,15 +46,15 @@ use app\models\Settings;
]); ]);
// Значения по умолчанию // Значения по умолчанию
isset($model_notifications) or $model_notifications = new Notification; $model_notifications ?? $model_notifications = new Notification;
isset($model_notifications->trgt) or $model_notifications->trgt = null; $model_notifications->account ?? $model_notifications->account = null;
isset($model_notifications->text) or $model_notifications->text = ''; $model_notifications->text ?? $model_notifications->text = '';
?> ?>
<?= $form->errorSummary($model_notifications) ?> <?= $form->errorSummary($model_notifications) ?>
<div class="row"> <div class="row">
<?= $form->field($model_notifications, 'trgt', ['options' => ['class' => "mb-1 col-9"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?> <?= $form->field($model_notifications, 'account', ['options' => ['class' => "mb-1 col-9"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?>
<?= $form->field($model_notifications, 'type', ['options' => ['class' => "col pl-0"]])->dropDownList($model_notifications->typs); ?> <?= $form->field($model_notifications, 'type', ['options' => ['class' => "col pl-0"]])->dropDownList($model_notifications->typs); ?>
</div> </div>
<small class="d-block mb-1"><b>Множественная отправка:</b> @all или перечислить через запятую</small> <small class="d-block mb-1"><b>Множественная отправка:</b> @all или перечислить через запятую</small>
@ -64,7 +67,8 @@ use app\models\Settings;
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
</div> </div>
<input type="radio" id="profile_panel_trusted_input_settings" name="main_panel" checked />
<input type="radio" id="profile_panel_trusted_input_settings" name="main_panel" <?= $panel === 'profile_panel_trusted_input_settings' ? 'checked' : null ?>/>
<div class="col"> <div class="col">
<?php <?php
$form = ActiveForm::begin([ $form = ActiveForm::begin([
@ -82,7 +86,7 @@ use app\models\Settings;
<?= $form->errorSummary($model_settings, ['header' => 'Получены ошибки:']) ?> <?= $form->errorSummary($model_settings, ['header' => 'Получены ошибки:']) ?>
<?= $form->field($model_settings, 'search_period', ['options' => ['class' => "mb-1"]])->textInput(['value' => $model_settings['search_period'], 'onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement)']); ?> <?= $form->field($model_settings, 'search_period', ['options' => ['class' => "mb-1"]])->textInput(['value' => $model_settings['search_period'], 'onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement, \'profile_panel_trusted_input_settings\')']); ?>
<small class="d-block mb-1">Время которое надо ждать для повторного поиска в секундах</small> <small class="d-block mb-1">Время которое надо ждать для повторного поиска в секундах</small>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
@ -124,7 +128,7 @@ use app\models\Settings;
} }
?> ?>
<?= $form->field($model_settings, 'search_connect_keep', ['options' => ['class' => "mb-1"]])->dropDownList($list, ['onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement)']); ?> <?= $form->field($model_settings, 'search_connect_keep', ['options' => ['class' => "mb-1"]])->dropDownList($list, ['onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement, \'profile_panel_trusted_input_settings\')']); ?>
<small class="d-block mb-1">Удерживать открытое соединение до истечения срока блокировки поиска?</small> <small class="d-block mb-1">Удерживать открытое соединение до истечения срока блокировки поиска?</small>
<small class="d-block mb-1">При малой задержке позволяет снизить время загрузки страницы, но при большой будет казаться, что сайт завис</small> <small class="d-block mb-1">При малой задержке позволяет снизить время загрузки страницы, но при большой будет казаться, что сайт завис</small>

View File

@ -63,8 +63,8 @@
HTML; HTML;
} else { } else {
$button_cart = <<<HTML $button_cart = <<<HTML
<a class="col-1 h-100 text-dark d-flex" title="Добавить $catn в корзину" href="/cart" role="button" onclick="return cart_write('$catn');"> <a class="col-1 h-100 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" href="/cart" role="button" onclick="return cart_write('$catn');">
<i class="fas fa-cart-arrow-down my-auto"></i> <i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
</a> </a>
HTML; HTML;
} }

View File

@ -24,7 +24,7 @@ if (isset($history) && $history) {
foreach ($rows as $row) { foreach ($rows as $row) {
// Инициализация // Инициализация
$date = date('H:i d.m.Y', $row->date); $date = empty($row->jrnl) ? '' : date('H:i d.m.Y', end($row->jrnl)['date']);
echo <<<HTML echo <<<HTML
<a class="dropdown-item d-flex button_white text-dark" href="/search?type=product&q=$row->text">$row->text<span class="ml-auto">$date</span></a> <a class="dropdown-item d-flex button_white text-dark" href="/search?type=product&q=$row->text">$row->text<span class="ml-auto">$date</span></a>

View File

@ -1,5 +1,10 @@
#notification_button .notification_button_active {
/* color: #123EAB; */
color: #0010ff;
}
#notifications_popup_wrap>.notification { #notifications_popup_wrap>.notification {
min-height : 100px; min-height : 60px;
border : 1px solid #a39bb1; border : 1px solid #a39bb1;
background-color: #fff; background-color: #fff;
opacity: 0; opacity: 0;
@ -10,6 +15,7 @@
z-index : 100; z-index : 100;
bottom : 0; bottom : 0;
position: fixed; position: fixed;
width: 370px;
overflow: hidden; overflow: hidden;
} }

View File

@ -18,16 +18,16 @@
} }
#page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue { #page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue {
background-color: #123EAB; background-color: #dbdde3;
} }
#page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n) { #page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n) {
background-color: #f0f5ff; background-color: #f7f6f9;
} }
#page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n + 1) { /* #page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n + 1) {
background-color: #e7edf9; background-color: #dbdde3;
} } */
/* Экстрабольшие девайсы (большие десктопы, >= 1200px) */ /* Экстрабольшие девайсы (большие десктопы, >= 1200px) */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="454.1" height="308.2" viewBox="0 0 340.6 231.1">
<defs/>
<path fill="#123eab" fill-rule="evenodd" stroke="#123eab" stroke-linecap="square" stroke-linejoin="bevel" d="M85.3.7L81 27.4c23.2-.5 59.3-.5 74.5-3.6L278.6.8z"/>
<path fill="#123eab" fill-rule="evenodd" d="M79.2 0l-4.3 28C60.5 28 52 35.8 48.2 39a37.1 37.1 0 00-10.8 23.7L.7 75.6c-1.2-5.8.7-15 2.9-20.2C5.8 50.2 50.2 6.1 54 3.6 57.8 1.1 69.1 0 79.2 0zM.7 80.6l36.7-13v52L.7 157z"/>
<path fill="#123eab" fill-rule="evenodd" stroke="#123eab" stroke-linecap="square" stroke-linejoin="bevel" d="M256.5 230.4l4.3-26.6c-23.2.4-59.3.4-74.5 3.6l-123.1 23z"/>
<path fill="#123eab" fill-rule="evenodd" d="M262.6 231.1l4.3-28c14.4 0 22.8-7.7 26.7-10.9a37.1 37.1 0 0010.8-23.7l36.7-13c1.2 5.8-.7 15-2.9 20.2-2.2 5.2-46.6 49.3-50.4 51.8-3.8 2.5-15.1 3.6-25.2 3.6zM341.3 150.5l-37 13v-51.9l37-37.4v76.3zM121.8 173.4c15 0 26-3 32.9-8.8 7-5.8 10.4-14.4 10.3-25.6 0-9.4-2-16.6-6.2-21.6s-10.8-8.4-20-10.4l-13.6-3c-3.3-.7-5.6-1.5-7-2.5-1.3-1-2-2.4-2-4.2 0-1.8.6-3.1 2-4.1 1.3-1 3.8-1.6 7.5-1.6A94.9 94.9 0 01159 98l-1.6-28.6a77 77 0 00-13.7-3.5 128 128 0 00-21.2-1.8c-14.4 0-25 2.9-31.9 8.6-6.9 5.8-10.3 14.1-10.2 25-.1 9.3 2 16.4 6.1 21.5 4.2 5.1 11 8.7 20.5 10.8l13.9 3.2c3.2.7 5.4 1.6 6.6 2.5 1.2 1 1.8 2.3 1.7 3.8a5 5 0 01-2.1 4.4c-1.5 1.1-4.1 1.7-7.8 1.7A157.1 157.1 0 0180 139l4.3 28.7a136.3 136.3 0 0037.4 5.6m54-2.4l34 .1v-31.3h14.6c13.4 0 23.3-3.1 29.8-9.5 6.6-6.3 9.8-15.2 9.8-26.8 0-11.6-3.2-20.5-9.8-26.9-6.5-6.3-16.4-9.6-29.8-9.7l-48.6-.1V171m34-58V93.7h11.7c3 0 5 1 6.5 2.6 1.4 1.7 2 4.1 2 7.1s-.6 5.4-2 7c-1.4 1.8-3.6 2.7-6.5 2.7h-11.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Krita: https://krita.org -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:krita="http://krita.org/namespaces/svg/krita"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
width="340.56pt"
height="231.12pt"
viewBox="0 0 340.56 231.12">
<defs/>
<path id="shape0" transform="matrix(0.999999989543185 0 0 0.999999989543185 81.0246195015223 0.719998959050944)" fill="#123eab" fill-rule="evenodd" stroke="#123eab" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" d="M4.32 0L0 26.64C23.145 26.1781 59.2915 26.1648 74.4954 23.04C140.067 10.7681 181.107 3.0881 197.615 3.55271e-15Z"/><path id="shape1" transform="matrix(0.999999989543185 0 0 0.999999989543185 0.334586894527806 -1.03342032290581e-06)" fill="#123eab" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" d="M78.8654 0L74.5454 28.08C60.1454 28.08 51.7335 35.7357 47.9054 38.88C43.5545 42.4538 36.6254 54.48 37.1054 62.64L0.385411 75.6C-0.845066 69.8105 1.07494 60.6905 3.26541 55.44C5.45589 50.1895 49.8804 6.08814 53.6654 3.6C57.4504 1.11186 68.7854 1.52185e-05 78.8654 0Z"/><path id="shape2" transform="matrix(0.999999989543185 0 0 0.999999989543185 0.719995894172135 67.6799995238226)" fill="#123eab" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" d="M0 12.96L36.72 0L36.72 51.84L1.33227e-15 89.28Z"/><path id="shape01" transform="matrix(-0.999999989543185 0 0 -0.999999989543185 260.782486704629 230.400008058693)" fill="#123eab" fill-rule="evenodd" stroke="#123eab" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" d="M4.32 1.10134e-13L0 26.64C23.145 26.1781 59.2915 26.1648 74.4954 23.04C140.067 10.7681 181.107 3.0881 197.615 0Z"/><path id="shape11" transform="matrix(-0.999999989543185 0 0 -0.999999989543185 341.472519311624 231.120008051163)" fill="#123eab" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" d="M78.8654 0L74.5454 28.08C60.1454 28.08 51.7335 35.7357 47.9054 38.88C43.5545 42.4538 36.6254 54.48 37.1054 62.64L0.385411 75.6C-0.845066 69.8105 1.07494 60.6905 3.26541 55.44C5.45589 50.1895 49.8804 6.08814 53.6654 3.6C57.4504 1.11186 68.7854 1.52185e-05 78.8654 0Z"/><path id="shape21" transform="matrix(-0.999999989543185 0 0 -0.999999989543185 341.280008422616 163.440007493919)" fill="#123eab" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" d="M0 12.96C7.344 10.368 19.6483 6.048 36.9129 0L36.9129 51.84C19.6483 69.312 7.344 81.792 1.33227e-15 89.28C0 53.664 0 28.224 0 12.96Z"/><path id="shape011" transform="matrix(1.00000002467949 0.0028125000793221 2.44921276809008e-16 1.00000002467949 80.079921156527 63.9591790897417)" fill="#123eab" fill-rule="evenodd" stroke="#000000" stroke-opacity="0" stroke-width="0" stroke-linecap="square" stroke-linejoin="bevel" d="M41.7111 109.348C56.6777 109.304 67.6397 106.339 74.5969 100.45C81.5542 94.5616 84.9923 86.0102 84.9114 74.796C84.9734 65.4367 82.9134 58.2574 78.7311 53.2579C74.5489 48.2584 67.8722 44.8054 58.7011 42.8988L45.1412 39.9177C41.8305 39.219 39.4991 38.3806 38.1468 37.4025C36.7946 36.4243 36.1418 35.0269 36.1884 33.2104C36.1138 31.4745 36.7479 30.1021 38.0909 29.093C39.4338 28.0838 41.9331 27.5684 45.5889 27.5467C52.0921 27.6398 58.3799 28.3478 64.4523 29.6705C70.5248 30.9933 75.3588 32.372 78.9546 33.8066L77.3164 5.212C74.301 4.017 69.7211 2.87 63.5766 1.768C57.4322 0.667 50.3918 0.078 42.4554 0C28.0536 0.05 17.4145 2.966 10.538 8.7489C3.66143 14.5318 0.24811 22.8843 0.298 33.8066C0.20803 43.0292 2.24951 50.1837 6.42249 55.2701C10.5954 60.3565 17.4396 63.9338 26.9552 66.0019L40.814 69.132C44.0345 69.8493 46.2479 70.6877 47.454 71.6472C48.6602 72.6068 49.2322 73.8551 49.17 75.3922C49.2291 77.2771 48.5141 78.7614 47.025 79.8451C45.536 80.9288 42.9185 81.4816 39.1727 81.5033C32.0185 81.4039 24.8375 80.6711 17.6296 79.3048C10.4217 77.9385 4.5452 76.5349 0 75.0941L4.31892 103.838C8.06539 105.11 13.3107 106.326 20.0547 107.486C26.7987 108.647 34.0175 109.267 41.7111 109.348M95.725 106.815L129.679 106.815L129.679 75.5247L144.294 75.5247C157.676 75.4382 167.631 72.2238 174.159 65.8817C180.688 59.5395 183.94 50.5886 183.915 39.029C183.94 27.4695 180.688 18.5186 174.159 12.1764C167.631 5.834 157.676 2.62 144.294 2.533L95.725 2.533L95.725 106.815M129.679 48.7233L129.679 29.3347L141.46 29.3347C144.359 29.3658 146.503 30.2358 147.892 31.9447C149.281 33.6536 149.971 36.0151 149.961 39.029C149.971 42.0429 149.281 44.4044 147.892 46.1133C146.503 47.8222 144.359 48.6922 141.46 48.7233L129.679 48.7233"/>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -128,7 +128,9 @@ function deauthentication() {
function registration(form) { function registration(form) {
if (form == undefined) { if (form == undefined) {
form = { '_csrf': yii.getCsrfToken() }; form = {
'_csrf': yii.getCsrfToken()
};
} else { } else {
form = $(form).serialize(); form = $(form).serialize();
} }

View File

@ -1,22 +1,29 @@
/**
* Записать в корзину (создать корзину, если не существует)
*/
function cart_write(catn, amount = 1) { function cart_write(catn, amount = 1) {
// Инициализация
let data = {};
data[catn] = amount;
$.ajax({ $.ajax({
url: '/order/write', url: '/order/write',
type: 'post', type: 'post',
dataType: 'json', dataType: 'json',
data: { data: {
'_csrf': yii.getCsrfToken(), '_csrf': yii.getCsrfToken(),
'supplies': [ 'supplies': data
catn,
amount
]
}, },
success: cart_success, success: cart_response_success,
error: cart_error error: cart_response_error
}); });
return false; return false;
} }
/**
* Удалить корзину
*/
function cart_delete() { function cart_delete() {
$.ajax({ $.ajax({
url: '/order/delete', url: '/order/delete',
@ -25,13 +32,16 @@ function cart_delete() {
data: { data: {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}, },
success: cart_success, success: cart_response_success,
error: cart_error error: cart_response_error
}); });
return false; return false;
} }
/**
* Сформировать заказ
*/
function cart_pay() { function cart_pay() {
$.ajax({ $.ajax({
url: '/order/pay', url: '/order/pay',
@ -40,13 +50,16 @@ function cart_pay() {
data: { data: {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}, },
success: cart_success, success: cart_response_success,
error: cart_error error: cart_response_error
}); });
return false; return false;
} }
/**
* Управление чекбоксами
*/
function cart_list_checkbox(target) { function cart_list_checkbox(target) {
// Инициализация // Инициализация
@ -57,12 +70,16 @@ function cart_list_checkbox(target) {
if (catn === 'all') { if (catn === 'all') {
if (target.checked === true) { if (target.checked === true) {
for ($i = 0; $i < elements.length; $i++) { for ($i = 0; $i < elements.length; $i++) {
// Перебор всех выбранных элементов
elements[$i].getElementsByTagName('input')[0].checked = true; elements[$i].getElementsByTagName('input')[0].checked = true;
} }
target.checked = true; target.checked = true;
} else { } else {
for ($i = 0; $i < elements.length; $i++) { for ($i = 0; $i < elements.length; $i++) {
// Перебор всех выбранных элементов
elements[$i].getElementsByTagName('input')[0].checked = false; elements[$i].getElementsByTagName('input')[0].checked = false;
} }
@ -73,17 +90,33 @@ function cart_list_checkbox(target) {
} }
} }
function cart_list_delete(dropdown) { /**
* Удалить из корзины
*/
function cart_list_delete(target, amount = 0) {
// Инициализация
let targets = {};
if (target !== undefined) {
// Обработка входных параметров
targets[target] = amount;
} else {
// Обработка выбранных элементов в списке
// Инициализация // Инициализация
let elements = document.getElementsByClassName('cart_list_target'); let elements = document.getElementsByClassName('cart_list_target');
let reg = /^\w+_([^_]*)$/; let reg = /^\w+_([^_]*)$/;
let targets = [];
for ($i = 0; $i < elements.length; $i++) { for ($i = 0; $i < elements.length; $i++) {
// Перебор всех выбранных элементов
// Инициализация
let checkbox = elements[$i].getElementsByTagName('input')[0]; let checkbox = elements[$i].getElementsByTagName('input')[0];
if (checkbox.checked === true) { if (checkbox.checked === true) {
targets.push(reg.exec(checkbox.id)[1]); targets[reg.exec(checkbox.id)[1]] = 0;
}
} }
} }
@ -95,27 +128,62 @@ function cart_list_delete(dropdown) {
'_csrf': yii.getCsrfToken(), '_csrf': yii.getCsrfToken(),
'targets': targets 'targets': targets
}, },
success: cart_success, success: cart_response_success,
error: cart_error error: cart_response_error
}); });
// Реинициализация
document.getElementById('cart_list_action').value = 'none'; document.getElementById('cart_list_action').value = 'none';
// Подсчитывание стоимости // Пересчитывание стоимости
cart_cost_calculate(); cart_cost_calculate();
return false; return false;
} }
/**
* Изменить количество товара в корзине
*/
function cart_list_amount_update(target, input) {
if (target !== undefined && input !== undefined) {
// Обработка входных параметров
// Инициализация
let targets = {};
targets[target] = input.value;
$.ajax({
url: '/order/amount-update',
type: 'post',
dataType: 'json',
data: {
'_csrf': yii.getCsrfToken(),
'targets': targets
},
success: cart_response_success,
error: cart_response_error
});
}
// Пересчитывание стоимости
cart_cost_calculate();
return false;
}
/**
* Подсчёт стоимости
*/
function cart_cost_calculate() { function cart_cost_calculate() {
let elements = document.getElementsByClassName('cart_list_target'); let elements = document.getElementsByClassName('cart_list_target');
let reg = /^([0-9]*)\s*\w*/; let reg = /^([0-9]*)\s*\w*/;
let costs = 0; let costs = 0;
for ($i = 0; $i < elements.length; $i++) { for ($i = 0; $i < elements.length; $i++) {
let cost = elements[$i].getElementsByTagName('div')[5]; let cost = elements[$i].getElementsByTagName('div')[5].innerText;
let amount = elements[$i].getElementsByTagName('div')[3].children[0].value;
costs += +reg.exec(cost.innerText)[1]; costs += +reg.exec(cost)[1] * amount;
} }
document.getElementById('cart_cost').innerHTML = costs; document.getElementById('cart_cost').innerHTML = costs;
@ -123,8 +191,8 @@ function cart_cost_calculate() {
cart_cost_calculate(); cart_cost_calculate();
function cart_success(data, status) { function cart_response(data, status) {
// Обработка ответов от удавшихся запросов // Обработка ответов
// Основной блок // Основной блок
if (data.main !== undefined) { if (data.main !== undefined) {
@ -144,23 +212,17 @@ function cart_success(data, status) {
} }
} }
function cart_error(data, status) { function cart_response_success(data, status) {
// Обработка ответов от удавшихся запросов
cart_response(data, status);
}
function cart_response_error(data, status) {
// Обработка ответов от неудавшихся запросов // Обработка ответов от неудавшихся запросов
// Основной блок // Инициализвация
if (data.responseJSON.main !== undefined) { data = data.responseJSON;
main = document.getElementsByTagName('main')[0];
// Обновление окна результатов поиска cart_response(data, status);
main.innerHTML = data.main;
// Реинициализация
reinitialization(main);
}
// CSRF-токен
if (data.responseJSON._csrf !== undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf);
}
} }

View File

@ -17,9 +17,6 @@ function notification_last() {
// Проверка уведомлений // Проверка уведомлений
setInterval(notification_last, 5000); setInterval(notification_last, 5000);
// Поиск уведомлений
notification_stream();
function notification_popup_create(html, id) { function notification_popup_create(html, id) {
// Инициализация // Инициализация
let wrap = document.getElementById('notifications_popup_wrap'); let wrap = document.getElementById('notifications_popup_wrap');
@ -40,7 +37,7 @@ function notification_popup_create(html, id) {
function notification_popup_delete(notification) { function notification_popup_delete(notification) {
// Эффект затухания (статус того, что уведомление прочитано) // Эффект затухания (статус того, что уведомление прочитано)
notification.style.opacity = 0.4; notification.style.opacity = 0.75;
notification.style.borderColor = '#c0b7d0'; notification.style.borderColor = '#c0b7d0';
setTimeout(() => { setTimeout(() => {
@ -56,14 +53,15 @@ function notification_popup_delete(notification) {
return false; return false;
}; };
function notification_stream() { function notification_stream(preload = 0) {
$.ajax({ $.ajax({
url: '/notification', url: '/notification',
type: 'post', type: 'post',
dataType: 'json', dataType: 'json',
data: { data: {
'_csrf': yii.getCsrfToken(), '_csrf': yii.getCsrfToken(),
'stream': 1 'stream': 1,
'preload': preload
}, },
success: notification_response_success, success: notification_response_success,
error: notification_response_error error: notification_response_error
@ -72,16 +70,26 @@ function notification_stream() {
return false; return false;
}; };
function notification_response_success(data, status) { // Предзагрузка уведомлений
if (data.main !== undefined) { notification_stream(1);
main = document.getElementsByTagName('main')[0];
// Обновление документа function notification_response_success(data, status) {
main.innerHTML = data.main; // Кнопка уведомлений
if (data.button !== undefined) {
// Инициализация
button_old = document.getElementById('notification_button');
// Запись
button_old.insertAdjacentHTML("beforebegin", data.button);
// Запись
button_old.remove();
// Реинициализация // Реинициализация
reinitialization(main); $('#notification_button').dropdown().init();
}; }
// Панель уведомлений
if (data.panel !== undefined) { if (data.panel !== undefined) {
// Инициализация // Инициализация
panel = document.getElementById('notification_button_panel'); panel = document.getElementById('notification_button_panel');
@ -91,36 +99,50 @@ function notification_response_success(data, status) {
// Реинициализация // Реинициализация
reinitialization(panel); reinitialization(panel);
}; }
// Всплывающее окно
if (data.popup !== undefined && data.popup['html'] !== undefined && data.popup['id'] !== undefined) { if (data.popup !== undefined && data.popup['html'] !== undefined && data.popup['id'] !== undefined) {
// Инициализация // Инициализация
popup = document.getElementById('notification_popup'); popup = document.getElementById('notification_popup');
// Генерация // Генерация
notification_popup_create(data.popup['html'], data.popup['id']); notification_popup_create(data.popup['html'], data.popup['id']);
}; }
if (data.redirect !== undefined) {
// Перенаправление notification_response(data, status);
history.pushState({}, document.title, data.redirect); }
};
if (data._csrf !== undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data._csrf);
};
};
function notification_response_error(data, status) { function notification_response_error(data, status) {
if (data.responseJSON.main !== undefined) { // Инициализация
data = data.responseJSON;
notification_response(data, status);
}
function notification_response(data, status) {
// Основной блок
if (data.main !== undefined) {
// Инициализация
main = document.getElementsByTagName('main')[0]; main = document.getElementsByTagName('main')[0];
// Обновление окна результатов поиска // Обновление документа
main.innerHTML = data.main; main.innerHTML = data.main;
// Реинициализация // Реинициализация
reinitialization(main); reinitialization(main);
}; };
if (data.responseJSON._csrf !== undefined) {
// Обновление документа // Перенаправление
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf); if (data.redirect !== undefined) {
// Перенаправление
history.pushState({}, document.title, data.redirect);
}; };
};
// CSRF-токен
if (data._csrf !== undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data._csrf);
};
}

View File

@ -1,10 +1,47 @@
function supply_import(form) { function page_profile_supplies(form, panel) {
if (form == undefined) { if (form === undefined) {
form = { form = {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}; };
if (panel !== undefined) {
form.panel = panel;
}
} else { } else {
form = new FormData(form); form = new FormData(form);
if (panel !== undefined) {
form.append('panel', panel);
}
}
$.ajax({
url: '/profile/supplies',
type: 'post',
dataType: 'json',
data: form,
success: page_profile_response_success,
error: page_profile_response_error
});
return false;
}
function page_profile_supplies_import_excel(form, panel) {
if (form === undefined) {
form = {
'_csrf': yii.getCsrfToken()
};
if (panel !== undefined) {
form.panel = panel;
}
} else {
form = new FormData(form);
if (panel !== undefined) {
form.append('panel', panel);
}
} }
$.ajax({ $.ajax({
@ -17,15 +54,26 @@ function supply_import(form) {
success: page_profile_response_success, success: page_profile_response_success,
error: page_profile_response_error error: page_profile_response_error
}); });
return false;
}; };
function page_profile_settings(form) { function page_profile_settings(form, panel) {
if (form == undefined) { if (form == undefined) {
form = { form = {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}; };
if (panel !== undefined) {
form.panel = panel;
}
} else { } else {
form = $(form).serialize(); form = $(form).serializeArray();
form.push({
name: "panel",
value: panel
});
} }
$.ajax({ $.ajax({
@ -36,79 +84,81 @@ function page_profile_settings(form) {
success: page_profile_response_success, success: page_profile_response_success,
error: page_profile_response_error error: page_profile_response_error
}); });
// Реинициализация
initDropdownOem();
return false;
} }
function page_profile_monitoring(change = 1) { function page_profile_monitoring(change = 1, panel) {
// Инициализация // Инициализация
url = new URL(document.location); let url = new URL(document.location);
search = url.searchParams.get('search'); let search = url.searchParams.get('search');
if (search == undefined) { if (search === null) {
search = 1; search = 1;
} else {
search = +search + change;
} }
// Предобработка
url.searchParams.set('search', search);
$.ajax({ $.ajax({
url: '/profile/monitoring', url: '/profile/monitoring',
type: 'post', type: 'post',
dataType: 'json', dataType: 'json',
data: { data: {
'_csrf': yii.getCsrfToken(), '_csrf': yii.getCsrfToken(),
'search': +search + change 'search': search,
'panel': panel
}, },
success: function (data) { success: function (data, status) {
page_profile_response_success(data, status);
// Ренициализация
let url = new URL(document.location);
// Запись параметра в URL // Запись параметра в URL
url.searchParams.set('search', data.search); url.searchParams.set('search', data.search);
// Запись в историю
history.pushState('', document.title, url.toString()); history.pushState('', document.title, url.toString());
if (data.main !== undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.main;
}
if (data._csrf !== undefined) {
$('meta[name=csrf-token]').prop("content", data._csrf);
}
return false;
}, },
error: function (data) { error: page_profile_response_error
if (data.responseJSON.main !== undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.responseJSON.main;
}
if (data.responseJSON._csrf !== undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf);
}
return true;
}
}); });
return true; return false;
}; };
function page_profile_response_success(data, status) { function page_profile_response_success(data, status) {
if (data.main !== undefined) { // Активная панель
main = document.getElementsByTagName('main')[0]; if (data.panel_selected !== undefined) {
// Активация
// Обновление документа document.getElementById(data.panel_selected).click()
main.innerHTML = data.main;
// Реинициализация
reinitialization(main);
} }
// Перенаправление
if (data.redirect !== undefined) { if (data.redirect !== undefined) {
// Перенаправление // Перенаправление
history.pushState({}, document.title, data.redirect); history.pushState({}, document.title, data.redirect);
} }
if (data._csrf !== undefined) {
// Обновление документа page_profile_response(data, status);
$('meta[name=csrf-token]').prop("content", data._csrf);
}
}; };
function page_profile_response_error(data, status) { function page_profile_response_error(data, status) {
if (data.responseJSON.main !== undefined) { // Инициализация
data = data.responseJSON;
page_profile_response(data, status);
};
function page_profile_response(data, status) {
// Основной блок
if (data.main !== undefined) {
// Инициализация
main = document.getElementsByTagName('main')[0]; main = document.getElementsByTagName('main')[0];
// Обновление окна результатов поиска // Обновление окна результатов поиска
@ -117,8 +167,21 @@ function page_profile_response_error(data, status) {
// Реинициализация // Реинициализация
reinitialization(main); reinitialization(main);
} }
if (data.responseJSON._csrf !== undefined) {
// CSRF-токен
if (data._csrf !== undefined) {
// Обновление документа // Обновление документа
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf); $('meta[name=csrf-token]').prop("content", data._csrf);
} }
}; }
// Сокрытие первого элемента <option> в списке
function initDropdownOem() {
let dropdown = document.getElementById('account-opts-import_sections_oem');
if (dropdown !== null && dropdown.length > 1) {
dropdown.children[0].hidden = true;
}
}
initDropdownOem();

View File

@ -16,13 +16,22 @@ function page_profile_trusted_notification_create(form, html = 0) {
return page_profile_trusted_write(form); return page_profile_trusted_write(form);
} }
function page_profile_trusted_settings(form) { function page_profile_trusted_settings(form, panel) {
if (form == undefined) { if (form == undefined) {
form = { form = {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}; };
if (panel !== undefined) {
form.panel = panel;
}
} else { } else {
form = $(form).serializeArray(); form = $(form).serializeArray();
form.push({
name: "panel",
value: panel
});
} }
return page_profile_trusted_write(form); return page_profile_trusted_write(form);