diff --git a/mirzaev/skillparts/system/controllers/AuthenticationController.php b/mirzaev/skillparts/system/controllers/AuthenticationController.php index 9ee8ed9..bd85bce 100644 --- a/mirzaev/skillparts/system/controllers/AuthenticationController.php +++ b/mirzaev/skillparts/system/controllers/AuthenticationController.php @@ -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() ]; diff --git a/mirzaev/skillparts/system/controllers/CartController.php b/mirzaev/skillparts/system/controllers/CartController.php index 9e86b7e..68d8b7c 100644 --- a/mirzaev/skillparts/system/controllers/CartController.php +++ b/mirzaev/skillparts/system/controllers/CartController.php @@ -15,22 +15,6 @@ use Exception; class CartController extends Controller { - public function behaviors() - { - return [ - 'access' => [ - 'class' => AccessControl::class, - 'only' => ['index'], - 'rules' => [ - [ - 'allow' => true, - 'roles' => ['@'] - ] - ] - ] - ]; - } - /** * Страница: "Корзина" * diff --git a/mirzaev/skillparts/system/controllers/DeauthenticationController.php b/mirzaev/skillparts/system/controllers/DeauthenticationController.php index a96bb42..13f42d6 100644 --- a/mirzaev/skillparts/system/controllers/DeauthenticationController.php +++ b/mirzaev/skillparts/system/controllers/DeauthenticationController.php @@ -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() diff --git a/mirzaev/skillparts/system/controllers/IdentificationController.php b/mirzaev/skillparts/system/controllers/IdentificationController.php index 5da2f4a..fa27578 100644 --- a/mirzaev/skillparts/system/controllers/IdentificationController.php +++ b/mirzaev/skillparts/system/controllers/IdentificationController.php @@ -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() ]; } diff --git a/mirzaev/skillparts/system/controllers/NotificationController.php b/mirzaev/skillparts/system/controllers/NotificationController.php index f12e68f..1a5d05e 100644 --- a/mirzaev/skillparts/system/controllers/NotificationController.php +++ b/mirzaev/skillparts/system/controllers/NotificationController.php @@ -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; } diff --git a/mirzaev/skillparts/system/controllers/OrderController.php b/mirzaev/skillparts/system/controllers/OrderController.php index 5ef5fdc..76616eb 100644 --- a/mirzaev/skillparts/system/controllers/OrderController.php +++ b/mirzaev/skillparts/system/controllers/OrderController.php @@ -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'; - // Поиск - $edge = AccountEdgeOrder::searchByVertex($account->id, $model->readId(), 'current'); + if ($order->update()) { + // Запись в журнал + $order->journal('reserved'); - // Запись - $edge->type = 'reserved'; + // Поиск + $edge = AccountEdgeOrder::searchByVertex($account->id, $order->readId(), 'current'); - // Отправка - $edge->update(); + 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'; diff --git a/mirzaev/skillparts/system/controllers/ProfileController.php b/mirzaev/skillparts/system/controllers/ProfileController.php index 5760303..ff6e6e1 100644 --- a/mirzaev/skillparts/system/controllers/ProfileController.php +++ b/mirzaev/skillparts/system/controllers/ProfileController.php @@ -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', + 'groups', + 'sidebar', + 'panel' + )), + '_csrf' => yii::$app->request->getCsrfToken() + ]; } - return [ - 'main' => $this->renderPartial('supplies', compact('model', 'sidebar')), - '_csrf' => yii::$app->request->getCsrfToken() - ]; + yii::$app->response->statusCode = 409; } - return $this->render('supplies', compact('model', 'sidebar')); + return $this->render('supplies', compact( + 'model', + 'groups', + 'sidebar', + 'panel' + )); } public static function readGroups() diff --git a/mirzaev/skillparts/system/controllers/RegistrationController.php b/mirzaev/skillparts/system/controllers/RegistrationController.php index f37bea1..4997020 100644 --- a/mirzaev/skillparts/system/controllers/RegistrationController.php +++ b/mirzaev/skillparts/system/controllers/RegistrationController.php @@ -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() ]; diff --git a/mirzaev/skillparts/system/migrations/arangodb/m210314_133722_create_session_collection.php b/mirzaev/skillparts/system/migrations/arangodb/m210314_133722_create_session_collection.php new file mode 100644 index 0000000..6e7461b --- /dev/null +++ b/mirzaev/skillparts/system/migrations/arangodb/m210314_133722_create_session_collection.php @@ -0,0 +1,16 @@ +createCollection('session', []); + } + + public function down() + { + $this->dropCollection('session'); + } +} diff --git a/mirzaev/skillparts/system/migrations/arangodb/m210314_133926_create_account_edge_session_collection.php b/mirzaev/skillparts/system/migrations/arangodb/m210314_133926_create_account_edge_session_collection.php new file mode 100644 index 0000000..cd8d6e1 --- /dev/null +++ b/mirzaev/skillparts/system/migrations/arangodb/m210314_133926_create_account_edge_session_collection.php @@ -0,0 +1,16 @@ +createCollection('account_edge_session', ['type' => 3]); + } + + public function down() + { + $this->dropCollection('account_edge_session'); + } +} diff --git a/mirzaev/skillparts/system/models/Account.php b/mirzaev/skillparts/system/models/Account.php index 52e6287..cf9c98e 100644 --- a/mirzaev/skillparts/system/models/Account.php +++ b/mirzaev/skillparts/system/models/Account.php @@ -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; + } } diff --git a/mirzaev/skillparts/system/models/Document.php b/mirzaev/skillparts/system/models/Document.php index 435ffe1..8d0c1e1 100644 --- a/mirzaev/skillparts/system/models/Document.php +++ b/mirzaev/skillparts/system/models/Document.php @@ -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 [ - [ - 'wrtr', - 'string' - ], - [ - 'wrtr', - 'default', - 'value' => yii::$app->user->id - ], - [ - 'date', - 'default', - 'value' => time() - ] - ]; + 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( + [ + 'date' => $time = time(), + 'account' => yii::$app->user->id, + 'action' => $action + ], + ...$data + )] + ); + + // Запись и возврат + return $this->save() ? $time : false; } /** diff --git a/mirzaev/skillparts/system/models/Edge.php b/mirzaev/skillparts/system/models/Edge.php index eda7fe2..fa10d2a 100644 --- a/mirzaev/skillparts/system/models/Edge.php +++ b/mirzaev/skillparts/system/models/Edge.php @@ -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(); } /** diff --git a/mirzaev/skillparts/system/models/Notification.php b/mirzaev/skillparts/system/models/Notification.php index d0e9380..fb9c7fd 100644 --- a/mirzaev/skillparts/system/models/Notification.php +++ b/mirzaev/skillparts/system/models/Notification.php @@ -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 = <<$text

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; } } diff --git a/mirzaev/skillparts/system/models/Order.php b/mirzaev/skillparts/system/models/Order.php index 45f166e..30f20c6 100644 --- a/mirzaev/skillparts/system/models/Order.php +++ b/mirzaev/skillparts/system/models/Order.php @@ -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); + } } diff --git a/mirzaev/skillparts/system/models/Product.php b/mirzaev/skillparts/system/models/Product.php index 8ba8c43..7d52fa7 100644 --- a/mirzaev/skillparts/system/models/Product.php +++ b/mirzaev/skillparts/system/models/Product.php @@ -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,32 +254,59 @@ class Product extends Document ]); } - foreach ($data[0] as $doc) { - // Перебор полученных документов - // Сохранение в базе данных - $product = new static($doc); + foreach ($data as $data) { + // Перебор конвертированных файлов - if ($product->validate()) { - // Проверка пройдена + if (count($data) < 1) { + // Не найдены строки с товарами - // Запись документа - $product->save(); - - // Запись группы - $group = static::class . 'Group'; - (new $group())->writeMember($product, $this->group); + $this->addError('erros', 'Не удалось найти данные товаров'); } else { - // Проверка не пройдена - foreach ($product->errors as $attribute => $error) { - $this->addError($attribute, $error); + // Перебор найденных товаров + + 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); + } else { + // Проверка не пройдена + foreach ($product->errors as $attribute => $error) { + $this->addError($attribute, $error); + } + } } } } + // Деинициализация + $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(); + } } diff --git a/mirzaev/skillparts/system/models/Search.php b/mirzaev/skillparts/system/models/Search.php index c4b8f43..0e8ac4a 100644 --- a/mirzaev/skillparts/system/models/Search.php +++ b/mirzaev/skillparts/system/models/Search.php @@ -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; diff --git a/mirzaev/skillparts/system/models/Supply.php b/mirzaev/skillparts/system/models/Supply.php index 09b9fb0..4e74276 100644 --- a/mirzaev/skillparts/system/models/Supply.php +++ b/mirzaev/skillparts/system/models/Supply.php @@ -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]); } diff --git a/mirzaev/skillparts/system/models/traits/SearchByEdge.php b/mirzaev/skillparts/system/models/traits/SearchByEdge.php index 3d39f89..6d51a34 100644 --- a/mirzaev/skillparts/system/models/traits/SearchByEdge.php +++ b/mirzaev/skillparts/system/models/traits/SearchByEdge.php @@ -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]) diff --git a/mirzaev/skillparts/system/views/account/deauthentication.php b/mirzaev/skillparts/system/views/account/panel/authenticated.php similarity index 65% rename from mirzaev/skillparts/system/views/account/deauthentication.php rename to mirzaev/skillparts/system/views/account/panel/authenticated.php index f11d2fa..dd42b96 100644 --- a/mirzaev/skillparts/system/views/account/deauthentication.php +++ b/mirzaev/skillparts/system/views/account/panel/authenticated.php @@ -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; + // echo << + // + // + // + // HTML; } ?> + +
diff --git a/mirzaev/skillparts/system/views/account/authentication.php b/mirzaev/skillparts/system/views/account/panel/deauthenticated.php similarity index 100% rename from mirzaev/skillparts/system/views/account/authentication.php rename to mirzaev/skillparts/system/views/account/panel/deauthenticated.php diff --git a/mirzaev/skillparts/system/views/cart/index.php b/mirzaev/skillparts/system/views/cart/index.php index 848957f..fc40f28 100644 --- a/mirzaev/skillparts/system/views/cart/index.php +++ b/mirzaev/skillparts/system/views/cart/index.php @@ -11,10 +11,10 @@
Артикул
-
+
Описание
-
+
Количество
@@ -30,16 +30,16 @@ echo <<
- +
$supply->catn
-
+
$supply->desc
-
- $supply->amnt +
+
$supply->time diff --git a/mirzaev/skillparts/system/views/layouts/main.php b/mirzaev/skillparts/system/views/layouts/main.php index 8b9675b..12bbabb 100644 --- a/mirzaev/skillparts/system/views/layouts/main.php +++ b/mirzaev/skillparts/system/views/layouts/main.php @@ -19,6 +19,25 @@ AppAsset::register($this); + + + + + + + + + + + + + + + + + + + registerCsrfMetaTags() ?> <?= Html::encode($this->title ?? 'SkillParts') ?> head() ?> diff --git a/mirzaev/skillparts/system/views/notification/button.php b/mirzaev/skillparts/system/views/notification/button.php new file mode 100644 index 0000000..800ceaa --- /dev/null +++ b/mirzaev/skillparts/system/views/notification/button.php @@ -0,0 +1,18 @@ + + + HTML; + } else { + // Новые уведомления найдены + + echo <<$notifications_new_amount + + HTML; + } + ?> + \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/notification/content.php b/mirzaev/skillparts/system/views/notification/content.php new file mode 100644 index 0000000..149f4bf --- /dev/null +++ b/mirzaev/skillparts/system/views/notification/content.php @@ -0,0 +1,30 @@ +Уведомлений нет

+ 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'] ?? '

ОШИБКА

'; + } + } +?> \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/notification/panel.php b/mirzaev/skillparts/system/views/notification/panel.php index 149f4bf..00502b6 100644 --- a/mirzaev/skillparts/system/views/notification/panel.php +++ b/mirzaev/skillparts/system/views/notification/panel.php @@ -1,30 +1,45 @@ Уведомлений нет

- HTML; + // Уведомления не найдены - return; - } - - foreach($notifications as $notification) { - // Перебор уведомлений + $content = <<Уведомлений нет

+ HTML; + } else { + // Уведомления найдены // Инициализация - $notification = $notification->getAttributes(); + $content = ''; - if ($notification['type'] === 'notice') { - // Уведомление + foreach ($notifications as $notification) { + // Перебор уведомлений - echo $notification['html']; - } else if ($notification['type'] === 'warning') { - // Предупреждение + // Инициализация + $notification = $notification->getAttributes(); - echo $notification['html']; - } else { - // Неизвестно + if ($notification['type'] === 'notice') { + // Уведомление - echo $notification['html'] ?? '

ОШИБКА

'; + $content .= $notification['html']; + } else if ($notification['type'] === 'warning') { + // Предупреждение + + $content .= $notification['html']; + } else { + // Неизвестно + + $content .= $notification['html'] ?? '

ОШИБКА

'; + } } } + + if($notifications_panel_full ?? false) { + echo << + $content +
+ HTML; + } else { + echo $content; + } ?> \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/notification/system/afterImportExcel.php b/mirzaev/skillparts/system/views/notification/system/afterImportExcel.php new file mode 100644 index 0000000..352eec4 --- /dev/null +++ b/mirzaev/skillparts/system/views/notification/system/afterImportExcel.php @@ -0,0 +1,13 @@ + 0) { + echo <<Импортировано $amount товаров из excel-документа ($date)

+ HTML; +} else { + echo <<Неудачная попытка импорта товаров из excel-документа ($date)

+ HTML; +} + +?> \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/notification/system/afterImportOnec.php b/mirzaev/skillparts/system/views/notification/system/afterImportOnec.php new file mode 100644 index 0000000..ec4f15f --- /dev/null +++ b/mirzaev/skillparts/system/views/notification/system/afterImportOnec.php @@ -0,0 +1,2 @@ + +

Импортированы товары из 1C ()

\ No newline at end of file diff --git a/mirzaev/skillparts/system/views/product/index.php b/mirzaev/skillparts/system/views/product/index.php index 5e10402..79368ab 100644 --- a/mirzaev/skillparts/system/views/product/index.php +++ b/mirzaev/skillparts/system/views/product/index.php @@ -120,7 +120,7 @@ use app\models\Product;

Время для повышения релевантности в поисковиках

- +
diff --git a/mirzaev/skillparts/system/views/profile/index.php b/mirzaev/skillparts/system/views/profile/index.php index ebe5edc..65b221e 100644 --- a/mirzaev/skillparts/system/views/profile/index.php +++ b/mirzaev/skillparts/system/views/profile/index.php @@ -5,7 +5,8 @@ declare(strict_types=1); use yii; use yii\bootstrap\ActiveForm; -use app\models\Supply; +// Инициализация +$panel ?? $panel = 'profile_panel_settings_import'; ?> @@ -25,85 +26,44 @@ use app\models\Supply;
- + />
1
- + />
2
- + />
Параметры 1C
'form_profile_settings', - 'action' => false, - 'fieldConfig' => [ - 'template' => '{label}{input}', - ], - 'options' => [ - 'onsubmit' => 'return false;' - ] - ]); + $form = ActiveForm::begin([ + 'id' => 'form_profile_settings', + 'action' => false, + 'fieldConfig' => [ + 'template' => '{label}{input}', + ], + 'options' => [ + 'onsubmit' => 'return false;' + ] + ]); - /** - * @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); - } - } + // Инициализация + $model ?? $model = yii::$app->user->identity; + $list or $list = ['Нет данных']; ?> - field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]])->dropDownList($list ?? ['Нет данных'], ['onChange' => 'page_profile_settings(this.parentElement.parentElement)'])->label('OEM-номера'); ?> + 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-номера'); ?> Выберите поле в котором хранятся ОЕМ-номера и повторите импорт Значения взяты из импортированных товаров -
diff --git a/mirzaev/skillparts/system/views/profile/monitoring.php b/mirzaev/skillparts/system/views/profile/monitoring.php index 202be05..5604eef 100644 --- a/mirzaev/skillparts/system/views/profile/monitoring.php +++ b/mirzaev/skillparts/system/views/profile/monitoring.php @@ -2,6 +2,9 @@ declare(strict_types=1); +// Инициализация +$panel ?? $panel = 'profile_panel_monitoring_input_search_history'; + ?> @@ -20,14 +23,14 @@ declare(strict_types=1);
- + />
История заказов в разработке
- + />
-
+
IPv4
Поисковый запрос
Время
@@ -35,7 +38,7 @@ declare(strict_types=1); date); + $date = empty($row->jrnl) ? '' : date('H:i d.m.Y', end($row->jrnl)['date']); echo << @@ -52,9 +55,9 @@ declare(strict_types=1); echo << - +

$page

- +
HTML; @@ -62,7 +65,7 @@ declare(strict_types=1);
- + />
Журнал в разработке
diff --git a/mirzaev/skillparts/system/views/profile/sidebar.php b/mirzaev/skillparts/system/views/profile/sidebar.php index 7b4d353..c3d2533 100644 --- a/mirzaev/skillparts/system/views/profile/sidebar.php +++ b/mirzaev/skillparts/system/views/profile/sidebar.php @@ -25,17 +25,17 @@ use app\models\SupplyEdgeProduct; $targetUrl = '/profile/trusted'; if ('/' . yii::$app->request->pathInfo === $targetUrl) { - // Запрошена эта страница + // Запрошена та же страница от которой послан запрос (текущая) echo << - Панель управления + Панель управления HTML; } else { echo << - Панель управления + Панель управления HTML; } @@ -43,56 +43,56 @@ use app\models\SupplyEdgeProduct; ?>
request->pathInfo === '/profile/supplies' + || '/' . yii::$app->request->pathInfo === '/profile/import' + ) { + // Запрошена та же страница от которой послан запрос (текущая) - if ('/' . yii::$app->request->pathInfo === $targetUrl) { - // Запрошена эта страница - - echo <<Поставки + echo <<Поставки HTML; - } else { - echo <<Поставки + } else { + echo <<Поставки HTML; - } + } ?>
request->pathInfo === $targetUrl) { - // Запрошена эта страница + if ('/' . yii::$app->request->pathInfo === $targetUrl) { + // Запрошена та же страница от которой послан запрос (текущая) - echo <<Мониторинг + echo <<Мониторинг HTML; - } else { - echo <<Мониторинг + } else { + echo <<Мониторинг HTML; - } + } ?>
request->pathInfo === $targetUrl) { - // Запрошена эта страница + if ('/' . yii::$app->request->pathInfo === $targetUrl) { + // Запрошена та же страница от которой послан запрос (текущая) - echo <<Настройки + echo <<Настройки HTML; - } else { - echo <<Настройки + } else { + echo <<Настройки HTML; - } + } ?>
diff --git a/mirzaev/skillparts/system/views/profile/supplies.php b/mirzaev/skillparts/system/views/profile/supplies.php index 5b8a5f8..bea8d75 100644 --- a/mirzaev/skillparts/system/views/profile/supplies.php +++ b/mirzaev/skillparts/system/views/profile/supplies.php @@ -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'; ?> +
@@ -18,33 +19,47 @@ use app\models\Supply;
-

Управление поставками

- 'form_product_import_excel', - 'action' => false, - 'fieldConfig' => [ - 'template' => '{label}{input}', - 'options' => ['class' => ''] - ], - 'options' => [ - 'class' => 'mb-3', - 'onsubmit' => 'return false;' - ] - ]); +

Управление поставками

+
+
+ + +
+
- $model = $model ?? new Supply; - $groups = ProfileController::readGroups(); + /> +
+

В разработке

+
- ?> + /> +
+
Импорт из Excel-документа
+ + 'form_product_import_excel', + 'action' => false, + 'fieldConfig' => [ + 'template' => '{label}{input}', + 'options' => ['class' => ''] + ], + 'options' => [ + 'class' => 'mb-3', + 'onsubmit' => 'return false;' + ] + ]); + ?> - field($model, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?> - field($model, 'file', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'supply_import(this.parentElement.parentElement)']) ?> + field($model, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?> + 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\')']) ?> - errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?> + errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?> - -
+ +
+
+
diff --git a/mirzaev/skillparts/system/views/profile/trusted.php b/mirzaev/skillparts/system/views/profile/trusted.php index fb805f7..9ddb1c4 100644 --- a/mirzaev/skillparts/system/views/profile/trusted.php +++ b/mirzaev/skillparts/system/views/profile/trusted.php @@ -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'; ?> +
@@ -26,7 +29,7 @@ use app\models\Settings;
- + />
Отправка уведомления
@@ -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 = ''; ?> errorSummary($model_notifications) ?>
- field($model_notifications, 'trgt', ['options' => ['class' => "mb-1 col-9"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?> + field($model_notifications, 'account', ['options' => ['class' => "mb-1 col-9"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?> field($model_notifications, 'type', ['options' => ['class' => "col pl-0"]])->dropDownList($model_notifications->typs); ?>
Множественная отправка: @all или перечислить через запятую @@ -64,7 +67,8 @@ use app\models\Settings;
- + + />
errorSummary($model_settings, ['header' => 'Получены ошибки:']) ?> - field($model_settings, 'search_period', ['options' => ['class' => "mb-1"]])->textInput(['value' => $model_settings['search_period'], 'onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement)']); ?> + 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\')']); ?> Время которое надо ждать для повторного поиска в секундах @@ -124,7 +128,7 @@ use app\models\Settings; } ?> - field($model_settings, 'search_connect_keep', ['options' => ['class' => "mb-1"]])->dropDownList($list, ['onChange' => 'page_profile_trusted_settings(this.parentElement.parentElement)']); ?> + 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\')']); ?> Удерживать открытое соединение до истечения срока блокировки поиска? При малой задержке позволяет снизить время загрузки страницы, но при большой будет казаться, что сайт завис diff --git a/mirzaev/skillparts/system/views/search/index.php b/mirzaev/skillparts/system/views/search/index.php index 2005eb9..be322fd 100644 --- a/mirzaev/skillparts/system/views/search/index.php +++ b/mirzaev/skillparts/system/views/search/index.php @@ -63,8 +63,8 @@ HTML; } else { $button_cart = << - + + HTML; } diff --git a/mirzaev/skillparts/system/views/search/panel.php b/mirzaev/skillparts/system/views/search/panel.php index 067d8cf..cd1c58a 100644 --- a/mirzaev/skillparts/system/views/search/panel.php +++ b/mirzaev/skillparts/system/views/search/panel.php @@ -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 <<$row->text$date diff --git a/mirzaev/skillparts/system/web/css/notification.css b/mirzaev/skillparts/system/web/css/notification.css index 1f08f2b..95f7439 100644 --- a/mirzaev/skillparts/system/web/css/notification.css +++ b/mirzaev/skillparts/system/web/css/notification.css @@ -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; } diff --git a/mirzaev/skillparts/system/web/css/pages/profile.css b/mirzaev/skillparts/system/web/css/pages/profile.css index 375225b..5a4c0e0 100644 --- a/mirzaev/skillparts/system/web/css/pages/profile.css +++ b/mirzaev/skillparts/system/web/css/pages/profile.css @@ -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) */ diff --git a/mirzaev/skillparts/system/web/favicon.ico b/mirzaev/skillparts/system/web/favicon.ico index 580ed73..4a35d39 100644 Binary files a/mirzaev/skillparts/system/web/favicon.ico and b/mirzaev/skillparts/system/web/favicon.ico differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-144x144.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-144x144.png new file mode 100644 index 0000000..88921ab Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-144x144.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-192x192.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-192x192.png new file mode 100644 index 0000000..c7fb267 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-192x192.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-36x36.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-36x36.png new file mode 100644 index 0000000..d5845ec Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-36x36.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-48x48.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-48x48.png new file mode 100644 index 0000000..c7cd0d5 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-48x48.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-72x72.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-72x72.png new file mode 100644 index 0000000..45ffc17 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-72x72.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/android-icon-96x96.png b/mirzaev/skillparts/system/web/img/favicons/android-icon-96x96.png new file mode 100644 index 0000000..72c29a0 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/android-icon-96x96.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-114x114.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-114x114.png new file mode 100644 index 0000000..bfab262 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-114x114.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-120x120.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-120x120.png new file mode 100644 index 0000000..46bd87b Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-120x120.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-144x144.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-144x144.png new file mode 100644 index 0000000..88921ab Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-144x144.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-152x152.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-152x152.png new file mode 100644 index 0000000..18588dd Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-152x152.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-180x180.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-180x180.png new file mode 100644 index 0000000..5c3156e Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-180x180.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-57x57.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-57x57.png new file mode 100644 index 0000000..a766f16 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-57x57.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-60x60.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-60x60.png new file mode 100644 index 0000000..9a0a061 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-60x60.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-72x72.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-72x72.png new file mode 100644 index 0000000..45ffc17 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-72x72.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-76x76.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-76x76.png new file mode 100644 index 0000000..af9a5ba Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-76x76.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon-precomposed.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon-precomposed.png new file mode 100644 index 0000000..dfbf7db Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon-precomposed.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/apple-icon.png b/mirzaev/skillparts/system/web/img/favicons/apple-icon.png new file mode 100644 index 0000000..dfbf7db Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/apple-icon.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/browserconfig.xml b/mirzaev/skillparts/system/web/img/favicons/browserconfig.xml new file mode 100644 index 0000000..c554148 --- /dev/null +++ b/mirzaev/skillparts/system/web/img/favicons/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/mirzaev/skillparts/system/web/img/favicons/favicon-16x16.png b/mirzaev/skillparts/system/web/img/favicons/favicon-16x16.png new file mode 100644 index 0000000..b14ab78 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/favicon-16x16.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/favicon-32x32.png b/mirzaev/skillparts/system/web/img/favicons/favicon-32x32.png new file mode 100644 index 0000000..b63cd6b Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/favicon-32x32.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/favicon-96x96.png b/mirzaev/skillparts/system/web/img/favicons/favicon-96x96.png new file mode 100644 index 0000000..72c29a0 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/favicon-96x96.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/favicon.ico b/mirzaev/skillparts/system/web/img/favicons/favicon.ico new file mode 100644 index 0000000..cb5e221 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/favicon.ico differ diff --git a/mirzaev/skillparts/system/web/img/favicons/manifest.json b/mirzaev/skillparts/system/web/img/favicons/manifest.json new file mode 100644 index 0000000..013d4a6 --- /dev/null +++ b/mirzaev/skillparts/system/web/img/favicons/manifest.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/mirzaev/skillparts/system/web/img/favicons/ms-icon-144x144.png b/mirzaev/skillparts/system/web/img/favicons/ms-icon-144x144.png new file mode 100644 index 0000000..88921ab Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/ms-icon-144x144.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/ms-icon-150x150.png b/mirzaev/skillparts/system/web/img/favicons/ms-icon-150x150.png new file mode 100644 index 0000000..5e570ff Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/ms-icon-150x150.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/ms-icon-310x310.png b/mirzaev/skillparts/system/web/img/favicons/ms-icon-310x310.png new file mode 100644 index 0000000..ed1ba9f Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/ms-icon-310x310.png differ diff --git a/mirzaev/skillparts/system/web/img/favicons/ms-icon-70x70.png b/mirzaev/skillparts/system/web/img/favicons/ms-icon-70x70.png new file mode 100644 index 0000000..010bb5b Binary files /dev/null and b/mirzaev/skillparts/system/web/img/favicons/ms-icon-70x70.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.png b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.png new file mode 100644 index 0000000..5967fe9 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.svg b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.svg new file mode 100644 index 0000000..319c7e0 --- /dev/null +++ b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.png b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.png new file mode 100644 index 0000000..8fdc0d8 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.svg b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.svg new file mode 100644 index 0000000..6679d9a --- /dev/null +++ b/mirzaev/skillparts/system/web/img/logos/compressed/skillparts_small.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mirzaev/skillparts/system/web/img/logos/faviconRaw.png b/mirzaev/skillparts/system/web/img/logos/faviconRaw.png new file mode 100644 index 0000000..95babca Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/faviconRaw.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/faviconRaw2.png b/mirzaev/skillparts/system/web/img/logos/faviconRaw2.png new file mode 100644 index 0000000..e043dd4 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/faviconRaw2.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/skillparts.png b/mirzaev/skillparts/system/web/img/logos/skillparts.png index 5f1ca7e..181c1f2 100644 Binary files a/mirzaev/skillparts/system/web/img/logos/skillparts.png and b/mirzaev/skillparts/system/web/img/logos/skillparts.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/skillparts.png-autosave.kra b/mirzaev/skillparts/system/web/img/logos/skillparts.png-autosave.kra new file mode 100644 index 0000000..c5d83d0 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/skillparts.png-autosave.kra differ diff --git a/mirzaev/skillparts/system/web/img/logos/skillparts.svg b/mirzaev/skillparts/system/web/img/logos/skillparts.svg index 98830fd..499fee3 100644 --- a/mirzaev/skillparts/system/web/img/logos/skillparts.svg +++ b/mirzaev/skillparts/system/web/img/logos/skillparts.svg @@ -5,9 +5,9 @@ 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="889.92pt" + width="888.48pt" height="231.12pt" - viewBox="0 0 889.92 231.12"> + viewBox="0 0 888.48 231.12"> - + diff --git a/mirzaev/skillparts/system/web/img/logos/skillparts_small.png b/mirzaev/skillparts/system/web/img/logos/skillparts_small.png new file mode 100644 index 0000000..8f0f997 Binary files /dev/null and b/mirzaev/skillparts/system/web/img/logos/skillparts_small.png differ diff --git a/mirzaev/skillparts/system/web/img/logos/skillparts_small.svg b/mirzaev/skillparts/system/web/img/logos/skillparts_small.svg new file mode 100644 index 0000000..e0a2144 --- /dev/null +++ b/mirzaev/skillparts/system/web/img/logos/skillparts_small.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/mirzaev/skillparts/system/web/js/account.js b/mirzaev/skillparts/system/web/js/account.js index e8f06bb..8d21e4f 100644 --- a/mirzaev/skillparts/system/web/js/account.js +++ b/mirzaev/skillparts/system/web/js/account.js @@ -128,7 +128,9 @@ function deauthentication() { function registration(form) { if (form == undefined) { - form = { '_csrf': yii.getCsrfToken() }; + form = { + '_csrf': yii.getCsrfToken() + }; } else { form = $(form).serialize(); } diff --git a/mirzaev/skillparts/system/web/js/cart.js b/mirzaev/skillparts/system/web/js/cart.js index a74b9ee..e58bc62 100644 --- a/mirzaev/skillparts/system/web/js/cart.js +++ b/mirzaev/skillparts/system/web/js/cart.js @@ -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 elements = document.getElementsByClassName('cart_list_target'); - let reg = /^\w+_([^_]*)$/; - let targets = []; + let targets = {}; - for ($i = 0; $i < elements.length; $i++) { - let checkbox = elements[$i].getElementsByTagName('input')[0]; + if (target !== undefined) { + // Обработка входных параметров - if (checkbox.checked === true) { - targets.push(reg.exec(checkbox.id)[1]); + targets[target] = amount; + } else { + // Обработка выбранных элементов в списке + + // Инициализация + let elements = document.getElementsByClassName('cart_list_target'); + let reg = /^\w+_([^_]*)$/; + + for ($i = 0; $i < elements.length; $i++) { + // Перебор всех выбранных элементов + + // Инициализация + let checkbox = elements[$i].getElementsByTagName('input')[0]; + + if (checkbox.checked === true) { + 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); } \ No newline at end of file diff --git a/mirzaev/skillparts/system/web/js/notification.js b/mirzaev/skillparts/system/web/js/notification.js index b552b6d..713caa2 100644 --- a/mirzaev/skillparts/system/web/js/notification.js +++ b/mirzaev/skillparts/system/web/js/notification.js @@ -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) { - // Обновление документа - $('meta[name=csrf-token]').prop("content", data.responseJSON._csrf); + + // Перенаправление + if (data.redirect !== undefined) { + // Перенаправление + history.pushState({}, document.title, data.redirect); }; -}; \ No newline at end of file + + // CSRF-токен + if (data._csrf !== undefined) { + // Обновление документа + $('meta[name=csrf-token]').prop("content", data._csrf); + }; +} + diff --git a/mirzaev/skillparts/system/web/js/profile.js b/mirzaev/skillparts/system/web/js/profile.js index 24267e3..2975953 100644 --- a/mirzaev/skillparts/system/web/js/profile.js +++ b/mirzaev/skillparts/system/web/js/profile.js @@ -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({ @@ -14,18 +51,29 @@ function supply_import(form) { data: form, processData: false, contentType: false, - success: page_profile_response_success, + 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({ @@ -33,82 +81,84 @@ function page_profile_settings(form) { type: 'post', dataType: 'json', data: form, - success: page_profile_response_success, + 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); } -}; \ No newline at end of file +} + +// Сокрытие первого элемента