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

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()) {
// Аккаунт аутентифицирован
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel_full = true;
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
// Запись ответа
$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()
];

View File

@ -15,22 +15,6 @@ use Exception;
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 [
'menu' => $this->renderPartial('/account/authentication', compact('model')),
'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
'main' => $this->renderPartial('/index'),
'redirect' => '/',
'_csrf' => yii::$app->request->getCsrfToken()

View File

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

View File

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

View File

@ -4,16 +4,13 @@ declare(strict_types=1);
namespace app\controllers;
use app\models\AccountEdgeNotification;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use app\models\Supply;
use app\models\Order;
use app\models\AccountEdgeOrder;
use app\models\OrderEdgeSupply;
use Exception;
class OrderController extends Controller
@ -102,7 +99,7 @@ class OrderController extends Controller
}
// Если запись не удалась, то вернуть код: 500 Internal Server Error
$model->write($supplies) or yii::$app->response->statusCode = 500;
$model->writeSupply($supplies) or yii::$app->response->statusCode = 500;
}
return $return;
@ -115,6 +112,8 @@ class OrderController extends Controller
/**
* Удаление
*
* @todo Разделить логику (для удобства чтения)
*/
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');
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user;
$order = Order::search();
if ($targets) {
// Удаление выбранных целей (удаление из заказа)
foreach ($targets as $target) {
foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) {
// Унификация входных параметров
// Инициализация
$model = Order::search();
foreach ($target as $catn => $amount) {
// Перебор целей
// Удаление
$model->deleteEdge($target);
$order->deleteSupply([$catn => (int) $amount]);
}
}
} 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->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) {
// POST-запрос
@ -159,14 +187,73 @@ class OrderController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/cart/index', compact('model', 'supplies')),
'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')),
'title' => 'Корзина',
'redirect' => '/cart',
'_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');
if (count($edge) > 1) {
// Найден более чем 1 заказ
return null;
}
// Инициализация
$edge = $edge[0];
// Запись
$edge->type = 'accepted';

View File

@ -85,8 +85,10 @@ class ProfileController extends Controller
{
// Инициализация
$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')) {
// Обнаружены входные параметры
@ -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) {
// POST-запрос
@ -116,39 +118,23 @@ class ProfileController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index', compact('model', 'sidebar', 'supplies')),
'main' => $this->renderPartial('index', compact(
'model',
'sidebar',
'list',
'panel'
)),
'redirect' => '/profile',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('model', 'sidebar', 'supplies'));
}
/**
* Страница поставок
*/
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'));
return $this->render('index', compact(
'model',
'sidebar',
'list',
'panel'
));
}
/**
@ -159,6 +145,8 @@ class ProfileController extends Controller
// Инициализация
$model_notifications = null;
$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'))) {
// Обнаружены входные параметры из раздела "Уведомления"
@ -202,69 +190,53 @@ class ProfileController extends Controller
// Деинициализация
unset($vars);
// Генерация
$sidebar = $this->renderPartial('sidebar');
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
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',
'_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
{
// Инициализация
$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;
if ($page_search_history <= 0) {
// Вышли за границу поиска перед первой страницей
// Инициализация
$page_search_history = 0;
}
// Инициализация количества строк на одной странице
$rows_amount = 10;
// Генерация
$sidebar = $this->renderPartial('sidebar');
$search_history = Search::SearchByEdge(
from: 'account',
to: 'search',
@ -279,6 +251,33 @@ class ProfileController extends Controller
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) {
// AJAX-POST-запрос
@ -288,7 +287,8 @@ class ProfileController extends Controller
'main' => $this->renderPartial('monitoring', compact(
'sidebar',
'search_history',
'page_search_history'
'page_search_history',
'panel'
)),
'search' => $page_search_history + 1,
'redirect' => '/profile/monitoring',
@ -299,18 +299,61 @@ class ProfileController extends Controller
return $this->render('monitoring', compact(
'sidebar',
'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()
{
// Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$model->scenario = $model::SCENARIO_IMPORT;
// Генерация
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups();
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
@ -319,17 +362,27 @@ class ProfileController extends Controller
$model->file = UploadedFile::getInstances($model, 'file');
if (!$test = $model->import()) {
yii::$app->response->statusCode = 409;
}
if ($model->import()) {
return [
'main' => $this->renderPartial('supplies', compact('model', 'sidebar')),
'main' => $this->renderPartial('supplies', compact(
'model',
'groups',
'sidebar',
'panel'
)),
'_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()

View File

@ -48,7 +48,7 @@ class RegistrationController extends Controller
// Запись ответа
$return = [
'menu' => $this->renderPartial('/account/deauthentication'),
'menu' => $this->renderPartial('/account/panel/authenticated'),
'_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();
}
/**
* Генерация списка 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
{
return throw new Exception('Не установлено название коллекции');
return throw new Exception('Не инициализировано название коллекции');
}
/**
@ -30,8 +30,7 @@ abstract class Document extends ActiveRecord
{
return [
'_key',
'date',
'wrtr'
'jrnl'
];
}
@ -42,8 +41,7 @@ abstract class Document extends ActiveRecord
{
return [
'_key' => 'Ключ',
'date' => 'Дата',
'wrtr' => 'Аккаунт записавшего'
'jrnl' => 'Журнал'
];
}
@ -55,22 +53,62 @@ abstract class Document extends ActiveRecord
*/
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',
'string'
'date' => $time = time(),
'account' => yii::$app->user->id,
'action' => $action
],
[
'wrtr',
'default',
'value' => yii::$app->user->id
],
[
'date',
'default',
'value' => time()
]
];
...$data
)]
);
// Запись и возврат
return $this->save() ? $time : false;
}
/**

View File

@ -32,9 +32,8 @@ abstract class Edge extends Document
return array_merge(
parent::attributeLabels(),
[
'date' => 'От кого',
'date' => 'К кому',
'type' => 'Тип'
'_from' => 'От кого',
'_to' => 'К кому',
]
);
}
@ -72,8 +71,6 @@ abstract class Edge extends Document
if ($this->isNewRecord) {
}
$this->type = $this->type ?? '';
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;
@ -121,22 +118,33 @@ abstract class Edge extends Document
}
}
// Запись
return $edge->save() ? $edge : null;
if ($edge->save()) {
// Записано в базу данных
// Запись в журнал
$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) {
return $query->one();
} else {
return $query->limit($limit)->all();
if (isset($type)) {
$query->where(['type' => $type]);
}
return $query->limit($limit)->all();
}
/**

View File

@ -5,10 +5,11 @@ declare(strict_types=1);
namespace app\models;
use yii;
use yii\web\IdentityInterface;
use app\models\traits\SearchByEdge;
use app\models\Account;
use Exception;
/**
@ -25,6 +26,16 @@ class Notification extends Document
*/
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
*/
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 IdentityInterface $trgt Получатель уведомления
* @param string $type Тип уведомления
*
* @todo Создать параметр разделителя для администрации
* @param bool|string $html Содержимое уведомления (HTML или текст)
* @param Account|array|string|null $account Получатель уведомления
*/
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('Не удалось инициализировать содержимое');
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать получателя');
$type ?? $trgt = $this->type ?? throw new Exception('Не удалось инициализировать тип');
$model = new self;
$account ?? $account = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать получателя');
// Инициализация уведомления
if (isset($html) && (bool) (int) $html) {
if ((bool) (int) $html) {
// Получен текст в формете HTML-кода
$this->html = $this->text ?? null;
$model->html = $text ?? null;
} 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>
HTML;
}
if ($this->save()) {
if ($model->save()) {
// Уведомление записано
// Инициализация получателей и создание ребра
if (empty($trgt)) {
if (empty($account)) {
// Получатель не передан
goto test;
} else if (is_string($trgt)) {
} else if (is_string($account)) {
// Передана необработанная строка
// Инициализация
$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 = [];
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;
}
if (in_array('@test', $trgt, true)) {
if (in_array('@test', $account, true)) {
// Найден флаг обозначающий тестирование (отправка самому себе)
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 = [];
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;
} 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(
parent::attributes(),
[]
[
'stts'
]
);
}
@ -52,7 +54,9 @@ class Order extends Document
{
return array_merge(
parent::attributeLabels(),
[]
[
'stts' => 'Статус'
]
);
}
@ -63,21 +67,43 @@ class Order extends Document
{
return array_merge(
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 ]
*
* @param Supply|array $supply Поставка
* @param Account $trgt Заказчик
*
* @return int Количество записанных поставок
*
* @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('Не удалось инициализировать заказчика');
@ -86,7 +112,7 @@ class Order extends Document
// Передана инстанция класса поставки или второй элемент массива не является числом
// Унификация входных данных
$supply = [$supply, 1];
$supply = [$supply->catn => 1];
}
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;
} else {
// Ребро создано (товар подключен к заказу)
// Постинкрементация счётчика добавленных товаров
$amount++;
// Запись в журнал
$this->journal('write', ['target' => $supply_model->readId()]);
}
}
}
// Отправка на сервер базы данных
return $this->save() ? $this : null;
if ($amount === 0) {
// Отправка уведомления
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 {
// Запись ребра: АККАУНТ -> ЗАКАЗ
return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
/**
* Удаление
*/
public function deleteEdge(string|array $catn): ?OrderEdgeSupply
public function deleteSupply(Supply|string|array $supply): int
{
// Запись ребра: ЗАКАЗ -> ПОСТАВКА
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);
// Подзапрос для проверки статуса уведомления относительно пользователя
// Поиск рёбер: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ
$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(
from: 'order',
@ -223,18 +287,10 @@ class Order extends Document
subquery_where: [
[
'order._id' => $this->readId()
],
[
'order_edge_supply.type' => 'write'
]
],
params: $let->getBindVars(),
let: [
'order_edge_supply_deleted',
'(' . (string) $let . ')'
],
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,
offset: $offset,
direction: 'INBOUND'
@ -257,6 +313,7 @@ class Order extends Document
continue;
}
// Инициализация
$amount = 0;
// Повторный перебор для поиска дубликатов
@ -286,9 +343,10 @@ class Order extends Document
if ($cost < 1) {
// Если стоимость равна нулю (явная ошибка)
$this->deleteEdge($supply->readId());
// Удаление из базы данных
$this->deleteSupply($supply->readId());
// Удаление
// Удаление из списка
unset($supplies[$key]);
// Пропуск итерации
@ -301,4 +359,13 @@ class Order extends Document
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;
use moonland\phpexcel\Excel;
use yii;
use app\models\traits\SearchByEdge;
use moonland\phpexcel\Excel;
/**
* Продукт (в ассортименте магазина)
*
@ -20,6 +23,8 @@ class Product extends Document
/**
* Сценарий импорта из .excel документа
*
* Использовать для обхода правил при загрузке файла
*/
const SCENARIO_IMPORT = 'import';
@ -31,7 +36,7 @@ class Product extends Document
/**
* Файл .excel для импорта товаров
*/
public Excel|null $file = null;
public Excel|string|array|null $file = null;
/**
* Группа в которой состоит товар
@ -54,7 +59,6 @@ class Product extends Document
return array_merge(
parent::attributes(),
[
'name',
'desc',
'ocid',
'catn',
@ -74,16 +78,15 @@ class Product extends Document
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название',
'desc' => 'Описание',
'ocid' => 'Идентификатор 1C',
'catn' => 'Каталожный номер',
'imgs' => 'Изображения',
'time' => 'Срок доставки',
'oemn' => 'OEM номера',
'cost' => 'Стоимость',
'file' => 'Документ',
'group' => 'Группа'
'catn' => 'Каталожный номер (catn)',
'desc' => 'Описание (desc)',
'ocid' => 'Идентификатор 1C (ocid)',
'imgs' => 'Изображения (imgs)',
'time' => 'Срок доставки (time)',
'oemn' => 'OEM номера (oemn)',
'cost' => 'Стоимость (cost)',
'file' => 'Документ (file)',
'group' => 'Группа (group)'
]
);
}
@ -97,21 +100,12 @@ class Product extends Document
parent::rules(),
[
[
[
'name',
'catn'
],
'catn',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_WRITE,
'except' => self::SCENARIO_IMPORT
],
[
'file',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT
],
[
'catn',
'string',
@ -125,6 +119,12 @@ class Product extends Document
'arrayValidator',
'message' => '{attribute} должен быть массивом.'
],
[
'file',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT
],
[
'file',
'file',
@ -142,13 +142,13 @@ class Product extends Document
}
/**
* Запись
* Инициализация продукта
*
* @param string $catn Артикул, каталожный номер
*/
public static function initEmpty(string $catn): self|array
{
$oemn = self::convertOemn2Catn($catn);
$oemn = self::searchOemn($catn);
if (count($oemn) === 1) {
// Передан только один артикул
@ -159,7 +159,7 @@ class Product extends Document
return $model;
}
// Запись
// Запись пустого продукта
return self::writeEmpty($catn);
}
@ -188,7 +188,7 @@ class Product extends Document
}
/**
* Запись
* Запись пустого продукта
*/
public static function writeEmpty(string $catn): ?self
{
@ -205,12 +205,14 @@ class Product extends Document
/**
* Поиск OEM номеров
*
* @param string $oemn OEM номера
* @param string $oemn Необработанная строка с OEM-номерами
* @param string $delimiters Разделители
*
* @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 = [];
@ -229,20 +231,22 @@ class Product extends Document
*/
public function import(): bool
{
// Инициализация массива данных
// Инициализация
$data = [];
$amount = 0;
if ($this->validate()) {
foreach ($this->file as $file) {
// Перебор файлов
// Сохранение на диск
if (!file_exists('../assets/import/excel/')) {
mkdir('../assets/import/excel/', 0775, true);
}
$file->saveAs($path = '../assets/import/excel/' . $file->baseName . '.' . $file->extension);
// Инициализация
$dir = '../assets/import/' . date('Y_m_d#H-i', time()) . '/excel/';
// Проверка файла пройдена
// Сохранение на диск
if (!file_exists($dir)) {
mkdir($dir, 0775, true);
}
$file->saveAs($path = $dir . $file->baseName . '.' . $file->extension);
$data[] = Excel::import($path, [
'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->scenario = $product::SCENARIO_WRITE;
if ($product->validate()) {
// Проверка пройдена
// Запись документа
$product->save();
// Постинкрементация счётчика
$amount++;
// Запись группы
$group = static::class . 'Group';
(new $group())->writeMember($product, $this->group);
// $group = static::class . 'Group';
// (new $group())->writeMember($product, $this->group);
} else {
// Проверка не пройдена
foreach ($product->errors as $attribute => $error) {
@ -272,10 +292,21 @@ class Product extends Document
}
}
}
}
}
// Деинициализация
$this->file = '';
static::afterImportExcel($amount);
return true;
}
$this->addError('erros', 'Неизвестная ошибка');
static::afterImportExcel($amount);
return false;
}
@ -310,4 +341,42 @@ class Product extends Document
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 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;
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;
@ -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;

View File

@ -4,13 +4,16 @@ declare(strict_types=1);
namespace app\models;
use Yii;
use yii;
use yii\web\User;
use app\models\Account;
use app\models\Product;
use app\models\SupplyEdgeProduct;
use app\models\traits\Xml2Array;
use carono\exchange1c\interfaces\ProductInterface;
use carono\exchange1c\controllers\ApiController;
use exception;
@ -73,7 +76,7 @@ class Supply extends Product implements ProductInterface
public function afterSave($data, $vars): void
{
if (AccountEdgeSupply::searchByVertex(yii::$app->user->id, $this->readId())) {
// Ребро уже существует
// Ребро: "АККАУНТ -> ПОСТАВКА" уже существует
} else {
// Ребра не существует
@ -97,20 +100,21 @@ class Supply extends Product implements ProductInterface
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 true;
}
/**
* Поиск через связь с аккаунтом
*
* @param string $id Идентификатор пользователя
* @param bool $select Запрашиваемые значения
* @param string|null $id Идентификатор пользователя
* @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('Не найден идентификатор');
@ -124,17 +128,7 @@ class Supply extends Product implements ProductInterface
],
where: 'supply._id == account_edge_supply[0]._to AND supply.onec["ЗначенияСвойств"] != null',
select: $select,
// handle: function ($request) {
// $response = $request->createCommand()->execute()->getAll();
// foreach ($response as &$attribute) {
// // Приведение всех свойств в массив и очистка от лишних данных
// $attribute = $attribute->getAll();
// }
// return $response;
// }
limit: $limit
);
}
@ -146,13 +140,15 @@ class Supply extends Product implements ProductInterface
*
* @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);
// for ($i = 0; $i <= count($models); $i++)
$account->on(ApiController::EVENT_AFTER_OFFER_SYNC, self::afterImport());
foreach ($models as $model) {
// Перебор записей
@ -161,7 +157,7 @@ class Supply extends Product implements ProductInterface
$transit = $model->onec;
foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) {
// Перебор аттрибутовfw
// Перебор аттрибутов
foreach ($properties as $property) {
// Перебор свойств
@ -174,8 +170,6 @@ class Supply extends Product implements ProductInterface
// Запись индикатора наличия изменений
$changes = true;
// break;
} else {
// Объединение данных
$transit['ЗначенияСвойств'][$attribute_name] = $property;
@ -186,19 +180,19 @@ class Supply extends Product implements ProductInterface
if ($changes) {
// Если указано, что записаны изменения
// Настройка ($transit нужен из-за ограничений __set())
// Настройка ($transit нужен из-за особенностей __set())
$model->onec = $transit;
foreach ($model->onec['ЗначенияСвойств'] as $property) {
// Перебор всех свойств
if (is_array($property)) {
if ($property['Ид'] === 'd99622fe-4526-11eb-b7f3-f3e52d0a06a9') {
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
// if ($property['Ид'] === $account->opts['import_sections_oem']) {
// // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
// Настройка
$model->catn = $property['Значение'];
}
// }
}
}
@ -273,6 +267,7 @@ class Supply extends Product implements ProductInterface
* Запись ребра (предложения от поставок к продуктам) из 1С
*
* @todo Разобраться зачем нужно возвращать SupplyEdgeProduct
* Вернуть создание карточек, но только по условиям (загрузка от админа, например)
*/
public function getOffer1c($offer): SupplyEdgeProduct
{
@ -283,53 +278,55 @@ class Supply extends Product implements ProductInterface
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)) {
// Значение OEM было инициализировано
// $product = [$product];
// }
foreach ($this->oemn as $oem) {
// Перебор артикулов из массива ОЕМ-номеров
// if (is_array($this->oemn)) {
// // Значение OEM было инициализировано
// Инициализация и запись
$product[] = Product::initEmpty($oem);
}
}
// foreach ($this->oemn as $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())) {
// // Ребро уже существует
// Запись ребра: ПОСТАВКА -> ПРОДУКТ
$return = (new SupplyEdgeProduct)->write(
$this->readId(),
$product->readId(),
'connect',
[
'onec' => self::xml2array($offer->xml)
]
);
}
// continue;
// }
// // Запись ребра: ПОСТАВКА -> ПРОДУКТ
// $return = (new SupplyEdgeProduct)->write(
// $this->readId(),
// $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['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
* Разобраться и создать возможность загрузки от лица другого аккаунта
*/
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;
@ -359,11 +360,11 @@ class Supply extends Product implements ProductInterface
// Перебор всех свойств
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 номера
// Настройка
$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
*/
public static function searchByOnecId(string $ocid): ?Supply
public static function searchByOcid(string $ocid): ?Supply
{
return static::findOne([static::getIdFieldName1c() => $ocid]);
}

View File

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

View File

@ -5,18 +5,20 @@ declare(strict_types=1);
use yii;
if (!yii::$app->user->isGuest) {
$popup = yii::$app->controller->renderPartial('/notification/panel');
// $popup = yii::$app->controller->renderPartial('/notification/panel');
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();">
<i class="fas fa-bell my-auto mx-2"></i>
</a>
<div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
$popup
</div>
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();">
// <i class="fas fa-bell my-auto mx-2"></i>
// </a>
// <div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
// $popup
// </div>
// 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="/orders" role="button" onclick="return page_orders();"><i class="fas fa-list mx-2"></i></a>
<div class="btn-group my-auto">

View File

@ -11,10 +11,10 @@
<div class="col-2">
<span>Артикул</span>
</div>
<div class="col-3">
<div class="col-4">
<span>Описание</span>
</div>
<div class="col-2 ml-auto text-center">
<div class="col-1 ml-auto px-0 text-center">
<span>Количество</span>
</div>
<div class="col-2 text-right">
@ -30,16 +30,16 @@
echo <<<HTML
<div class="row py-2 cart_list_target">
<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 class="col-2">
$supply->catn
</div>
<div class="col-3">
<div class="col-4">
$supply->desc
</div>
<div class="col-2 ml-auto text-center">
$supply->amnt
<div class="col-1 ml-auto">
<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 class="col-2 text-right">
$supply->time

View File

@ -19,6 +19,25 @@ AppAsset::register($this);
<meta charset="<?= Yii::$app->charset ?>">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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() ?>
<title><?= Html::encode($this->title ?? 'SkillParts') ?></title>
<?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,11 +1,15 @@
<?php
if (empty($notifications)) {
echo <<<HTML
// Уведомления не найдены
$content = <<<HTML
<p class="px-2 py-4 text-center">Уведомлений нет</p>
HTML;
} else {
// Уведомления найдены
return;
}
// Инициализация
$content = '';
foreach ($notifications as $notification) {
// Перебор уведомлений
@ -16,15 +20,26 @@
if ($notification['type'] === 'notice') {
// Уведомление
echo $notification['html'];
$content .= $notification['html'];
} else if ($notification['type'] === 'warning') {
// Предупреждение
echo $notification['html'];
$content .= $notification['html'];
} 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 class="row mt-auto mx-0">
<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>
</article>

View File

@ -5,7 +5,8 @@ declare(strict_types=1);
use yii;
use yii\bootstrap\ActiveForm;
use app\models\Supply;
// Инициализация
$panel ?? $panel = 'profile_panel_settings_import';
?>
<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>
</div>
<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">
1
</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">
2
</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">
<h5>Параметры 1C</h5>
<div class="dropdown-divider mb-3"></div>
@ -51,59 +52,18 @@ use app\models\Supply;
]
]);
/**
* @todo Перенести в модель
*/
// Инициализация
$model ?? $model = yii::$app->user->identity;
$supplies ?? $supplies = Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]');
$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);
}
}
$list or $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">Значения взяты из импортированных товаров</small>
<!-- <small class="d-block mb-3">Значения взяты из импортированных товаров</small> -->
<?php ActiveForm::end(); ?>
</div>

View File

@ -2,6 +2,9 @@
declare(strict_types=1);
// Инициализация
$panel ?? $panel = 'profile_panel_monitoring_input_search_history';
?>
<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>
</div>
<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>
<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="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-7 col-sm-3 col-md-4 col-lg-6 pr-0 px-sm-0 px-lg-3">Поисковый запрос</div>
<div class="col">Время</div>
@ -35,7 +38,7 @@ declare(strict_types=1);
<?php
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
<div class="row py-1">
@ -52,9 +55,9 @@ declare(strict_types=1);
echo <<<HTML
<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>
<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>
HTML;
@ -62,7 +65,7 @@ declare(strict_types=1);
</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>

View File

@ -25,17 +25,17 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile/trusted';
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница
// Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML
<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>
HTML;
} else {
echo <<<HTML
<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>
HTML;
}
@ -43,18 +43,18 @@ use app\models\SupplyEdgeProduct;
?>
<dt>
<?php
// Инициализация
$targetUrl = '/profile/supplies';
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница
if (
'/' . yii::$app->request->pathInfo === '/profile/supplies'
|| '/' . yii::$app->request->pathInfo === '/profile/import'
) {
// Запрошена та же страница от которой послан запрос (текущая)
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;
} else {
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;
}
?>
@ -65,14 +65,14 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile/monitoring';
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница
// Запрошена та же страница от которой послан запрос (текущая)
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;
} else {
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;
}
?>
@ -83,14 +83,14 @@ use app\models\SupplyEdgeProduct;
$targetUrl = '/profile';
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
// Запрошена эта страница
// Запрошена та же страница от которой послан запрос (текущая)
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;
} else {
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;
}
?>

View File

@ -5,10 +5,11 @@ declare(strict_types=1);
use yii;
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">
<div id="page_profile" class="container h-100">
@ -18,7 +19,23 @@ use app\models\Supply;
</nav>
<article class="col-9">
<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
$form = ActiveForm::begin([
'id' => 'form_product_import_excel',
@ -32,19 +49,17 @@ use app\models\Supply;
'onsubmit' => 'return false;'
]
]);
$model = $model ?? new Supply;
$groups = ProfileController::readGroups();
?>
<?= $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, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?>
<?= $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' => 'Исправьте их и попробуйте снова'*/]); ?>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>
</article>
</div>
</div>

View File

@ -7,9 +7,12 @@ use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
use app\models\Notification;
use app\models\Settings;
// Инициализация
$panel ?? $panel = 'profile_panel_trusted_input_notifications';
?>
<link href="/css/pages/profile.css" rel="stylesheet">
<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>
</div>
<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">
<h5>Отправка уведомления</h5>
<div class="dropdown-divider mb-3"></div>
@ -43,15 +46,15 @@ use app\models\Settings;
]);
// Значения по умолчанию
isset($model_notifications) or $model_notifications = new Notification;
isset($model_notifications->trgt) or $model_notifications->trgt = null;
isset($model_notifications->text) or $model_notifications->text = '';
$model_notifications ?? $model_notifications = new Notification;
$model_notifications->account ?? $model_notifications->account = null;
$model_notifications->text ?? $model_notifications->text = '';
?>
<?= $form->errorSummary($model_notifications) ?>
<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); ?>
</div>
<small class="d-block mb-1"><b>Множественная отправка:</b> @all или перечислить через запятую</small>
@ -64,7 +67,8 @@ use app\models\Settings;
<?php ActiveForm::end(); ?>
</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">
<?php
$form = ActiveForm::begin([
@ -82,7 +86,7 @@ use app\models\Settings;
<?= $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>
<?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>

View File

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

View File

@ -24,7 +24,7 @@ if (isset($history) && $history) {
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
<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 {
min-height : 100px;
min-height : 60px;
border : 1px solid #a39bb1;
background-color: #fff;
opacity: 0;
@ -10,6 +15,7 @@
z-index : 100;
bottom : 0;
position: fixed;
width: 370px;
overflow: hidden;
}

View File

@ -18,16 +18,16 @@
}
#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) {
background-color: #f0f5ff;
background-color: #f7f6f9;
}
#page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n + 1) {
background-color: #e7edf9;
}
/* #page_profile [id^=profile_panel_]>.profile_panel_content>div>.header_blue~.row:nth-child(2n + 1) {
background-color: #dbdde3;
} */
/* Экстрабольшие девайсы (большие десктопы, >= 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) {
if (form == undefined) {
form = { '_csrf': yii.getCsrfToken() };
form = {
'_csrf': yii.getCsrfToken()
};
} else {
form = $(form).serialize();
}

View File

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

View File

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

View File

@ -1,10 +1,47 @@
function supply_import(form) {
if (form == undefined) {
function page_profile_supplies(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({
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({
@ -17,15 +54,26 @@ function supply_import(form) {
success: page_profile_response_success,
error: page_profile_response_error
});
return false;
};
function page_profile_settings(form) {
function page_profile_settings(form, panel) {
if (form == undefined) {
form = {
'_csrf': yii.getCsrfToken()
};
if (panel !== undefined) {
form.panel = panel;
}
} else {
form = $(form).serialize();
form = $(form).serializeArray();
form.push({
name: "panel",
value: panel
});
}
$.ajax({
@ -36,79 +84,81 @@ function page_profile_settings(form) {
success: page_profile_response_success,
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);
search = url.searchParams.get('search');
let url = new URL(document.location);
let search = url.searchParams.get('search');
if (search == undefined) {
if (search === null) {
search = 1;
} else {
search = +search + change;
}
// Предобработка
url.searchParams.set('search', search);
$.ajax({
url: '/profile/monitoring',
type: 'post',
dataType: 'json',
data: {
'_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.searchParams.set('search', data.search);
// Запись в историю
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) {
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;
}
error: page_profile_response_error
});
return true;
return false;
};
function page_profile_response_success(data, status) {
if (data.main !== undefined) {
main = document.getElementsByTagName('main')[0];
// Обновление документа
main.innerHTML = data.main;
// Реинициализация
reinitialization(main);
// Активная панель
if (data.panel_selected !== undefined) {
// Активация
document.getElementById(data.panel_selected).click()
}
// Перенаправление
if (data.redirect !== undefined) {
// Перенаправление
history.pushState({}, document.title, data.redirect);
}
if (data._csrf !== undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data._csrf);
}
page_profile_response(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];
// Обновление окна результатов поиска
@ -117,8 +167,21 @@ function page_profile_response_error(data, status) {
// Реинициализация
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);
}
function page_profile_trusted_settings(form) {
function page_profile_trusted_settings(form, panel) {
if (form == undefined) {
form = {
'_csrf': yii.getCsrfToken()
};
if (panel !== undefined) {
form.panel = panel;
}
} else {
form = $(form).serializeArray();
form.push({
name: "panel",
value: panel
});
}
return page_profile_trusted_write(form);