Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c61ccbb65 | ||
![]() |
a9259eb7dc | ||
![]() |
6dc1b081f9 | ||
![]() |
3fea918255 | ||
![]() |
722bf6c378 | ||
![]() |
8cbe77e354 | ||
![]() |
b71f6e8efc | ||
![]() |
1fac5cdfc1 | ||
![]() |
081a34a8f4 | ||
![]() |
53d7e2a048 | ||
![]() |
b8c1f7cef0 | ||
![]() |
b91ef68091 | ||
![]() |
301b2945bc | ||
![]() |
78ae20cdac | ||
![]() |
65c2e19e55 | ||
![]() |
c9e3b542b7 | ||
![]() |
8a3897ad8f | ||
![]() |
0f8e6a7a6a | ||
![]() |
6a7f8897d5 | ||
![]() |
706c9b0743 | ||
![]() |
25709ee380 | ||
![]() |
5d18c95dc4 | ||
![]() |
aaec25f64c | ||
![]() |
9887be35f9 | ||
![]() |
c2afcfcc5a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
/vendor
|
||||
/cache
|
||||
/cache
|
||||
/.vscode
|
@@ -8,6 +8,7 @@
|
||||
{
|
||||
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
||||
"email": "red@hood.su",
|
||||
"homepage": "https://hood.su/mirzaev",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
@@ -20,10 +21,12 @@
|
||||
"bower-asset/bootstrap": "*",
|
||||
"npm-asset/jquery": "^3.5",
|
||||
"bower-asset/jquery": "^3.5",
|
||||
"explosivebit/arangodb": "^2.0",
|
||||
"triagens/arangodb": "^3.6",
|
||||
"moonlandsoft/yii2-phpexcel": "^2.0",
|
||||
"carono/yii2-1c-exchange": "^0.3.1"
|
||||
"moonlandsoft/yii2-phpexcel": ">=2.0",
|
||||
"carono/yii2-1c-exchange": "^0.3.1",
|
||||
"yiisoft/yii2-imagine": "*",
|
||||
"mirzaev/yii2-arangodb": ">=2.1.x-dev",
|
||||
"mirzaev/yii2-arangodb-sessions": ">=1.1.x-dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": ">=4.1",
|
||||
@@ -41,10 +44,7 @@
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"mirzaev\\skillparts\\": "mirzaev/skillparts/system"
|
||||
},
|
||||
"classmap": [
|
||||
"vendor/explosivebit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
@@ -87,18 +87,6 @@
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://asset-packagist.org"
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "explosivebit/arangodb",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pBazsi/yii2-arangodb.git",
|
||||
"reference": "master"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
774
composer.lock
generated
774
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
namespace app\assets;
|
||||
|
||||
use yii\web\AssetBundle;
|
||||
use yii\web\View;
|
||||
|
||||
/**
|
||||
* Main application asset bundle.
|
||||
@@ -25,21 +26,26 @@ class AppAsset extends AssetBundle
|
||||
'css/bootstrap/bootstrap.min.css',
|
||||
'css/main.css',
|
||||
'css/header.css',
|
||||
'css/notification.css',
|
||||
'css/info_panel.css',
|
||||
'css/categories_blocks_panel.css',
|
||||
'css/footer.css'
|
||||
];
|
||||
public $js = [
|
||||
'https://kit.fontawesome.com/d7e922c226.js',
|
||||
'https://code.jquery.com/jquery-3.5.1.min.js',
|
||||
'js/bootstrap/popper.min.js',
|
||||
'js/bootstrap/bootstrap.min.js',
|
||||
'https://cdn.jsdelivr.net/bxslider/4.1.1/jquery.bxslider.min.js',
|
||||
'https://unpkg.com/cookielib/src/cookie.min.js',
|
||||
'js/menu.js',
|
||||
'js/account.js',
|
||||
'js/search.js',
|
||||
'js/notification.js',
|
||||
'js/reinitialization.js'
|
||||
];
|
||||
public $jsOptions = [
|
||||
// 'position' => View::POS_HEAD
|
||||
];
|
||||
public $depends = [
|
||||
'yii\web\YiiAsset',
|
||||
// 'yii\bootstrap\BootstrapAsset'
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
$config = [
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'id' => 'skillparts-console',
|
||||
'basePath' => dirname(__DIR__),
|
||||
'bootstrap' => ['log'],
|
||||
@@ -9,7 +11,6 @@ $config = [
|
||||
'@vendor' => dirname(__DIR__) . '/../../../vendor',
|
||||
'@bower' => '@vendor/bower-asset',
|
||||
'@npm' => '@vendor/npm-asset',
|
||||
'@explosivebit' => '@vendor/explosivebit',
|
||||
'@tests' => '@app/tests',
|
||||
],
|
||||
'components' => [
|
||||
@@ -28,19 +29,9 @@ $config = [
|
||||
],
|
||||
'params' => require __DIR__ . '/params.php',
|
||||
'controllerMap' => [
|
||||
'arangodb-migrate' => 'explosivebit\arangodb\console\controllers\MigrateController',
|
||||
'arangodb-migrate' => 'mirzaev\yii2\arangodb\console\controllers\MigrateController',
|
||||
'fixture' => [
|
||||
'class' => 'yii\faker\FixtureController',
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
if (YII_ENV_DEV) {
|
||||
// configuration adjustments for 'dev' environment
|
||||
$config['bootstrap'][] = 'gii';
|
||||
$config['modules']['gii'] = [
|
||||
'class' => 'yii\gii\Module',
|
||||
];
|
||||
}
|
||||
|
||||
return $config;
|
||||
];
|
@@ -3,7 +3,7 @@
|
||||
use ArangoDBClient\ConnectionOptions;
|
||||
|
||||
return [
|
||||
'class' => '\explosivebit\arangodb\Connection',
|
||||
'class' => '\mirzaev\yii2\arangodb\Connection',
|
||||
'connectionOptions' => [
|
||||
ConnectionOptions::OPTION_DATABASE => '',
|
||||
ConnectionOptions::OPTION_ENDPOINT => 'tcp://127.0.0.1:8529',
|
||||
|
@@ -7,8 +7,7 @@ $config = [
|
||||
'aliases' => [
|
||||
'@vendor' => dirname(__DIR__) . '/../../../vendor',
|
||||
'@bower' => '@vendor/bower-asset',
|
||||
'@npm' => '@vendor/npm-asset',
|
||||
'@explosivebit' => '@vendor/explosivebit',
|
||||
'@npm' => '@vendor/npm-asset'
|
||||
],
|
||||
'components' => [
|
||||
'request' => [
|
||||
@@ -20,16 +19,34 @@ $config = [
|
||||
'class' => 'yii\caching\FileCache',
|
||||
],
|
||||
'user' => [
|
||||
'identityClass' => 'app\models\Account',
|
||||
'identityClass' => 'app\models\Account',
|
||||
'loginUrl' => ['/authentication'],
|
||||
'enableAutoLogin' => true
|
||||
// 'enableAutoLogin' => true,
|
||||
'enableSession' => true
|
||||
],
|
||||
// 'session' => [
|
||||
// 'class' => 'yii\web\Session',
|
||||
// 'cookieParams' => ['lifetime' => 3600 * 24 * 30 * 12],
|
||||
// 'timeout' => 3600 * 24 * 30 * 12,
|
||||
// 'useCookies' => true,
|
||||
// 'authManager' => [
|
||||
// 'class' => 'mirzaev\yii2\arangodb\rbac\DbManager',
|
||||
// ],
|
||||
'session' => [
|
||||
'class' => 'mirzaev\yii2\arangodb\sessions\ArangoDbSession',
|
||||
'document' => 'session',
|
||||
'cookieParams' => [
|
||||
// 'lifetime' => 3600 * 24 * 30 * 12
|
||||
'lifetime' => 3600 * 24 * 3
|
||||
],
|
||||
'writeCallback' => function($session): array {
|
||||
// Инициализация
|
||||
$data = [];
|
||||
|
||||
yii::$app->request->userIP and $data['ip'] = yii::$app->request->userIP;
|
||||
Yii::$app->user->id and $data['account'] = Yii::$app->user->id;
|
||||
|
||||
return $data ?? [];
|
||||
},
|
||||
'timeout' => 3600 * 24 * 3,
|
||||
'useStrictMode' => false,
|
||||
'useCookies' => true
|
||||
],
|
||||
'errorHandler' => [
|
||||
'errorAction' => 'error',
|
||||
],
|
||||
@@ -58,7 +75,9 @@ $config = [
|
||||
'class' => 'yii\rest\UrlRule',
|
||||
'controller' => 'main'
|
||||
],
|
||||
'product/<id:\d+>' => 'product/index'
|
||||
'product/<catn:[^/]+>' => 'product/index',
|
||||
'product/<catn:[^/]+>/<action:(write|edit|delete)>/<target:(title|catn|desc|image)>' => 'product/<action>-<target>',
|
||||
'orders' => 'order/index'
|
||||
],
|
||||
],
|
||||
|
||||
@@ -93,7 +112,7 @@ if (YII_ENV_DEV) {
|
||||
'class' => 'yii\debug\Module',
|
||||
'panels' => [
|
||||
'ArangoDB' => [
|
||||
'class' => 'explosivebit\arangodb\panels\arangodb\ArangoDbPanel'
|
||||
'class' => 'mirzaev\yii2\arangodb\panels\arangodb\ArangoDbPanel'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
@@ -2,11 +2,15 @@
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use app\models\AccountForm;
|
||||
|
||||
use yii;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
use yii\filters\AccessControl;
|
||||
use app\models\AccountForm;
|
||||
|
||||
use Throwable;
|
||||
use Exception;
|
||||
|
||||
class AuthenticationController extends Controller
|
||||
{
|
||||
@@ -31,37 +35,61 @@ class AuthenticationController extends Controller
|
||||
|
||||
public function actionIndex()
|
||||
{
|
||||
if (Yii::$app->request->isAjax) {
|
||||
if (yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
|
||||
// Инициализация
|
||||
$model = new AccountForm(Yii::$app->request->post('AccountForm'));
|
||||
$model = new AccountForm(yii::$app->request->post('AccountForm'));
|
||||
$model->scenario = $model::SCENARIO_AUTHENTICATION;
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
if (!Yii::$app->user->isGuest || $model->authentication()) {
|
||||
if (!yii::$app->user->isGuest || $model->authentication()) {
|
||||
// Аккаунт аутентифицирован
|
||||
|
||||
// Создание сессии
|
||||
// yii::$app->session->open();
|
||||
|
||||
// Инициализация
|
||||
$notifications_button = $this->renderPartial('/notification/button');
|
||||
$notifications_panel_full = true;
|
||||
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
|
||||
|
||||
// Запись ответа
|
||||
$return = [
|
||||
'nav' => (new AccountForm())->deauthenticationGenHtml(),
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
|
||||
'notifications_button',
|
||||
'notifications_panel',
|
||||
'notifications_panel_full'
|
||||
)),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (($cookies = Yii::$app->request->cookies)->has('redirect')) {
|
||||
if (($cookies = yii::$app->request->cookies)->has('redirect')) {
|
||||
// Найдено cookie с переадресацией
|
||||
|
||||
// Запись ответа
|
||||
$return['redirect'] = '/' . $cookies['redirect'];
|
||||
$return['main'] = $this->renderPartial($return['redirect'] . '/index');
|
||||
|
||||
try {
|
||||
if (empty($return['main'] = $this->renderPartial($return['redirect']))) {
|
||||
throw new Exception('Представление найдено, но вернуло пустой результат');
|
||||
}
|
||||
} catch (Throwable $t) {
|
||||
$return['main'] = $this->renderPartial($return['redirect'] . '/index');
|
||||
}
|
||||
|
||||
// Генерация и запись
|
||||
// $controller = 'app\\controllers\\' . ucfirst($cookies['redirect']) . 'Controller';
|
||||
// $action = 'action' . ucfirst($cookies['redirect_action']);
|
||||
// $return['main'] = (new $controller())->$action();
|
||||
|
||||
// Очистка cookie
|
||||
unset(Yii::$app->response->cookies['redirect']);
|
||||
unset(yii::$app->response->cookies['redirect']);
|
||||
} else {
|
||||
// Не найдено cookie с переадресацией
|
||||
|
||||
if (Yii::$app->request->pathInfo === 'authentication' || Yii::$app->request->pathInfo === 'registration') {
|
||||
if (yii::$app->request->pathInfo === 'authentication' || yii::$app->request->pathInfo === 'registration') {
|
||||
// Если клиент на промежуточном URI
|
||||
|
||||
// Запись ответа
|
||||
@@ -74,20 +102,20 @@ class AuthenticationController extends Controller
|
||||
} else {
|
||||
// Аккаунт не аутентифицирован
|
||||
|
||||
Yii::$app->response->statusCode = 400;
|
||||
yii::$app->response->statusCode = 400;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('/account', compact('model')),
|
||||
'main' => $this->renderPartial('/account/index', compact('model')),
|
||||
'redirect' => '/authentication',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!Yii::$app->user->isGuest) {
|
||||
Yii::$app->response->redirect('/');
|
||||
if (!yii::$app->user->isGuest) {
|
||||
yii::$app->response->redirect('/');
|
||||
} else {
|
||||
return $this->render('/account');
|
||||
return $this->render('/account/index');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
61
mirzaev/skillparts/system/controllers/CartController.php
Normal file
61
mirzaev/skillparts/system/controllers/CartController.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
|
||||
use app\models\Order;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CartController extends Controller
|
||||
{
|
||||
/**
|
||||
* Страница: "Корзина"
|
||||
*
|
||||
* @see $this->behaviors Доступ только аутентифицированным
|
||||
*/
|
||||
public function actionIndex(): string|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
|
||||
$account = yii::$app->user;
|
||||
|
||||
// Поиск корзины (текущего заказа)
|
||||
$model = Order::search();
|
||||
|
||||
if (empty($model)) {
|
||||
// Корзина не инициализирована
|
||||
|
||||
// Инициализация
|
||||
$model = new Order();
|
||||
$model->save() or throw new Exception('Не удалось инициализировать заказ');
|
||||
|
||||
// Подключение
|
||||
$model->connect($account);
|
||||
}
|
||||
|
||||
// Инициализация содержимого корзины
|
||||
$supplies = $model->content(10, $page);
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('index', compact('model', 'supplies')),
|
||||
'title' => 'Корзина',
|
||||
'redirect' => '/cart',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('index', compact('model', 'supplies'));
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
use yii\filters\AccessControl;
|
||||
@@ -34,20 +34,26 @@ class DeauthenticationController extends Controller
|
||||
|
||||
public function actionIndex()
|
||||
{
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
// Инициализация
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
// Удаление сессии
|
||||
yii::$app->session['status'] = 'inactive';
|
||||
// yii::$app->session->close();
|
||||
|
||||
// Выход из аккаунта
|
||||
Yii::$app->user->logout();
|
||||
yii::$app->user->logout();
|
||||
|
||||
|
||||
// Инициализация
|
||||
$model = new AccountForm(Yii::$app->request->post('AccountForm') ?? Yii::$app->request->get('AccountForm') ?? null);
|
||||
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm') ?? null);
|
||||
|
||||
// Ответа
|
||||
return [
|
||||
'nav' => $model->authenticationGenHtml($this->renderPartial('/account', compact('model'))),
|
||||
'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
|
||||
'main' => $this->renderPartial('/index'),
|
||||
'redirect' => '/',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,6 @@ namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii\web\Controller;
|
||||
use yii\web\ErrorAction;
|
||||
use yii\web\Response;
|
||||
|
||||
class ErrorController extends Controller
|
||||
{
|
||||
|
@@ -1,53 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
|
||||
use app\models\AccountForm;
|
||||
|
||||
class IdentificationController extends Controller
|
||||
{
|
||||
public function actionIndex()
|
||||
{
|
||||
if (Yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
if (Yii::$app->user->isGuest) {
|
||||
if (yii::$app->user->isGuest) {
|
||||
// Аккаунт не аутентифицирован
|
||||
|
||||
// Инициализация
|
||||
$model = new AccountForm(Yii::$app->request->post('AccountForm') ?? Yii::$app->request->get('AccountForm') ?? null);
|
||||
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm') ?? null);
|
||||
|
||||
// Запись ответа
|
||||
$return = [
|
||||
'nav' => $model->authenticationGenHtml($this->renderPartial('/account', compact('model'))),
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
} else {
|
||||
// Аккаунт аутентифицирован
|
||||
|
||||
// Инициализация
|
||||
$model = Yii::$app->user;
|
||||
$model = yii::$app->user;
|
||||
|
||||
// Инициализация
|
||||
$notifications_button = $this->renderPartial('/notification/button');
|
||||
$notifications_panel_full = true;
|
||||
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
|
||||
|
||||
// Запись ответа
|
||||
$return = [
|
||||
'nav' => (new AccountForm())->deauthenticationGenHtml(),
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
|
||||
'notifications_button',
|
||||
'notifications_panel'
|
||||
)),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
if (($cookies = Yii::$app->request->cookies)->has('redirect')) {
|
||||
if (($cookies = yii::$app->request->cookies)->has('redirect')) {
|
||||
// Найдено cookie с переадресацией
|
||||
|
||||
// Запись ответа
|
||||
$return['redirect'] = '/' . $cookies['redirect'];
|
||||
|
||||
// Очистка cookie
|
||||
unset(Yii::$app->response->cookies['redirect']);
|
||||
unset(yii::$app->response->cookies['redirect']);
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
@@ -22,11 +24,9 @@ class MainController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays homepage.
|
||||
*
|
||||
* @return string
|
||||
* Главная страница
|
||||
*/
|
||||
public function actionIndex()
|
||||
public function actionIndex(): string|array
|
||||
{
|
||||
if (Yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
|
212
mirzaev/skillparts/system/controllers/NotificationController.php
Normal file
212
mirzaev/skillparts/system/controllers/NotificationController.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
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\Notification;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
'access' => [
|
||||
'class' => AccessControl::class,
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => true,
|
||||
'roles' => ['@'],
|
||||
'actions' => ['index']
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Перенести логику в модель
|
||||
*/
|
||||
public function actionIndex()
|
||||
{
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
// Инициализация
|
||||
$model = new Notification(yii::$app->request->post('Notification'));
|
||||
$preload = (bool) (int) yii::$app->request->post('preload');
|
||||
$account = yii::$app->user->identity;
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (yii::$app->request->post('last')) {
|
||||
// Запрос последнего уведомлений (всплывающее окно)
|
||||
|
||||
// Инициализация
|
||||
$limit = 1;
|
||||
} else if (yii::$app->request->post('stream')) {
|
||||
// Запрос последних уведомлений (панель)
|
||||
$limit = 10;
|
||||
}
|
||||
|
||||
if (isset($limit)) {
|
||||
// Обработка для всплывающего окна или панель
|
||||
|
||||
// Подзапрос для проверки статуса уведомления относительно пользователя
|
||||
// Поиск рёбер: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ
|
||||
$let = $model::find()
|
||||
->for(['account', $model::collectionName() . '_edge_account'])
|
||||
->traversal($model::collectionName(), 'OUTBOUND')
|
||||
->in('account_edge_' . $model::collectionName())
|
||||
->where(['account._id == "' . $account->readId() . '" && notification_edge_account._from != account_edge_notification[0]._to'])
|
||||
->select($model::collectionName() . '_edge_account');
|
||||
|
||||
if (yii::$app->request->post('last')) {
|
||||
// Запрос последнего уведомлений (всплывающее окно)
|
||||
|
||||
// Уведомление которое не выводилось на мониторе пользователя
|
||||
$type = 'received';
|
||||
} else if (yii::$app->request->post('stream')) {
|
||||
// Запрос последних уведомлений (панель)
|
||||
|
||||
// Уведомление которое не было прочитано в окне уведомний
|
||||
$type = 'checked';
|
||||
}
|
||||
|
||||
// Генерация
|
||||
$let = $let->createCommand();
|
||||
|
||||
/**
|
||||
* Поиск рёбер: (УВЕДОМЛЕНИЕ)? -> ПОЛЬЗОВАТЕЛЬ
|
||||
*
|
||||
* @param bool $new Активация проверки на то, что уведомление не получено
|
||||
* @param bool $count Посчитать
|
||||
*/
|
||||
$search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int {
|
||||
if ($count) {
|
||||
// Запрошен подсчёт непрочитанных уведомлений
|
||||
|
||||
// Реинициализация
|
||||
$type = 'checked';
|
||||
$limit = null;
|
||||
}
|
||||
|
||||
return $model::searchByEdge(
|
||||
from: 'account',
|
||||
to: 'notification',
|
||||
params: $new ? $let->getBindVars() : [],
|
||||
subquery_where: [
|
||||
[
|
||||
'account._id' => $account->readId()
|
||||
],
|
||||
[
|
||||
[
|
||||
'notification.html' => null
|
||||
],
|
||||
'operator' => '!='
|
||||
],
|
||||
[
|
||||
'account_edge_notification.type' => $type
|
||||
]
|
||||
],
|
||||
let: [
|
||||
'notification_edge_account',
|
||||
'(' . (string) $let . ')'
|
||||
],
|
||||
foreach: [
|
||||
'edge' => 'notification_edge_account'
|
||||
],
|
||||
where: $new ? [
|
||||
'edge != null'
|
||||
] : [],
|
||||
limit: $limit,
|
||||
sort: ['DESC'],
|
||||
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();
|
||||
}
|
||||
|
||||
if (empty($notifications)) {
|
||||
// Уведомления не найдены
|
||||
|
||||
/**
|
||||
* @todo Определить какой код лучше использовать (404 не подходит)
|
||||
*/
|
||||
yii::$app->response->statusCode = 200;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
foreach ($notifications as $notification) {
|
||||
// Перебор найденных уведомлений
|
||||
|
||||
if ($preload) {
|
||||
// Запрошены уведомления для предзагрузки (их ещё не увидели)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано)
|
||||
AccountEdgeNotification::write(yii::$app->user->id, $notification->readId(), $type);
|
||||
}
|
||||
|
||||
if (yii::$app->request->post('last')) {
|
||||
// Запрос последнего уведомлений (всплывающее окно)
|
||||
|
||||
// Реинициализация
|
||||
$notification = $notifications[0];
|
||||
|
||||
$return['popup'] = [
|
||||
'html' => $this->renderPartial('popup', compact('model', 'notification')),
|
||||
'id' => 'popup/' . $notification->readId()
|
||||
];
|
||||
} else if (yii::$app->request->post('stream')) {
|
||||
// Запрос последних уведомлений (панель)
|
||||
|
||||
$return['panel'] = $this->renderPartial('panel', compact('model', 'notifications'));
|
||||
}
|
||||
} else {
|
||||
// Иначе обрабатывается как запрос страницы уведомлений
|
||||
|
||||
$return['main'] = $this->renderPartial('index', compact('model'));
|
||||
$return['redirect'] = '/notification';
|
||||
}
|
||||
|
||||
/**
|
||||
* Конец алгоритма
|
||||
*/
|
||||
end:
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
return $this->render('index', ['model' => new Notification(yii::$app->request->get('Notification'))]);
|
||||
}
|
||||
}
|
338
mirzaev/skillparts/system/controllers/OrderController.php
Normal file
338
mirzaev/skillparts/system/controllers/OrderController.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
|
||||
use app\models\Order;
|
||||
use app\models\AccountEdgeOrder;
|
||||
use Exception;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
'access' => [
|
||||
'class' => AccessControl::class,
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => true,
|
||||
'roles' => ['@'],
|
||||
'actions' => ['index', 'write', 'delete', 'amount-update', 'pay']
|
||||
],
|
||||
[
|
||||
'allow' => false,
|
||||
'roles' => ['?'],
|
||||
'denyCallback' => [$this, 'accessDenied']
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function accessDenied()
|
||||
{
|
||||
// Инициализация
|
||||
$cookies = yii::$app->response->cookies;
|
||||
|
||||
// Запись cookie с редиректом, который выполнится после авторизации
|
||||
$cookies->add(new Cookie([
|
||||
'name' => 'redirect',
|
||||
'value' => yii::$app->request->pathInfo
|
||||
]));
|
||||
|
||||
if (Yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
// Настройка
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
// Генерация ответа
|
||||
Yii::$app->response->content = json_encode([
|
||||
'main' => $this->renderPartial('/orders/index'),
|
||||
'redirect' => '/authentication',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
]);
|
||||
} else if (Yii::$app->request->isGet) {
|
||||
// GET-запрос
|
||||
|
||||
$this->redirect('/authentication');
|
||||
}
|
||||
}
|
||||
|
||||
public function actionIndex()
|
||||
{
|
||||
// Инициализация
|
||||
$orders = Order::search(type: 'all', limit: 10, page: 1, select: '{account_edge_order, order}');
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('/orders/index', compact('orders')),
|
||||
'title' => 'Заказы',
|
||||
'redirect' => '/orders',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('/orders/index', compact('orders'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись
|
||||
*/
|
||||
public function actionWrite(): string|array|null
|
||||
{
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
// Инициализация входных данных
|
||||
$account = yii::$app->user;
|
||||
$supplies = yii::$app->request->post('supplies');
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
// Инициализация возврата по умолчанию
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (is_null($supplies)) {
|
||||
// 501 Not Implemented
|
||||
yii::$app->response->statusCode = 501;
|
||||
}
|
||||
|
||||
if (!is_null($supplies)) {
|
||||
// Переданы поставки для записи
|
||||
|
||||
if (!$model = Order::search($account)) {
|
||||
// Корзина не найдена (текущий заказ)
|
||||
|
||||
// Инициализация
|
||||
$model = new Order();
|
||||
$model->save() or throw new Exception('Не удалось инициализировать заказ');
|
||||
|
||||
// Запись ребра: АККАУНТ -> ЗАКАЗ
|
||||
AccountEdgeOrder::write($account->id, $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
|
||||
}
|
||||
|
||||
// Проверка входных данных
|
||||
if (!is_array($supplies)) {
|
||||
// Неверные входные данные
|
||||
|
||||
// Запись ошибки
|
||||
$model->addError('errors', 'Переменная должна быть массивом');
|
||||
}
|
||||
|
||||
// Если запись не удалась, то вернуть код: 500 Internal Server Error
|
||||
$model->writeSupply($supplies) or yii::$app->response->statusCode = 500;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
yii::$app->response->statusCode = 500;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление
|
||||
*
|
||||
* @todo Разделить логику (для удобства чтения)
|
||||
*/
|
||||
public function actionDelete(): 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();
|
||||
|
||||
if ($targets) {
|
||||
// Удаление выбранных целей (удаление из заказа)
|
||||
|
||||
foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) {
|
||||
// Унификация входных параметров
|
||||
|
||||
foreach ($target as $catn => $amount) {
|
||||
// Перебор целей
|
||||
|
||||
// Удаление
|
||||
$order->deleteSupply([$catn => (int) $amount]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Целью подразумевается сам заказ (удаление заказа)
|
||||
|
||||
// Инициализация
|
||||
$order->stts = 'reserved';
|
||||
|
||||
if ($order->update()) {
|
||||
// Запись в журнал
|
||||
$order->journal('reserved');
|
||||
|
||||
// Поиск
|
||||
$edge = AccountEdgeOrder::searchByVertex($account->id, $order->readId(), 'current');
|
||||
|
||||
if (count($edge) > 1) {
|
||||
// Найден более чем 1 заказ
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$edge = $edge[0];
|
||||
$edge->type = 'reserved';
|
||||
|
||||
// Запись
|
||||
$edge->update();
|
||||
|
||||
// Реинициализация
|
||||
$order = Order::search();
|
||||
|
||||
if (empty($order)) {
|
||||
// Корзина не инициализирована
|
||||
|
||||
// Инициализация
|
||||
$order = new Order();
|
||||
$order->save() or throw new Exception('Не удалось инициализировать заказ');
|
||||
|
||||
// Подключение
|
||||
$order->connect($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация содержимого корзины
|
||||
$supplies = $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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление количества товара
|
||||
*/
|
||||
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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Оплата
|
||||
*/
|
||||
public function actionPay(): string|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$model = Order::search();
|
||||
|
||||
// Поиск ребра
|
||||
$edge = AccountEdgeOrder::searchByVertex(yii::$app->user->id, $model->readId(), 'current');
|
||||
|
||||
if (count($edge) > 1) {
|
||||
// Найден более чем 1 заказ
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$edge = $edge[0];
|
||||
|
||||
// Запись
|
||||
$edge->type = 'accepted';
|
||||
|
||||
// Отправка изменений
|
||||
$edge->update();
|
||||
|
||||
// Инициализация
|
||||
$orders = Order::search(type: 'all', limit: 10, page: 1, select: '{account_edge_order, order}');
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('/orders/index', compact('orders')),
|
||||
'title' => 'Заказы',
|
||||
'redirect' => '/orders',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('/orders/index', compact('orders'));
|
||||
}
|
||||
}
|
@@ -1,52 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
use app\models\Product;
|
||||
use yii\web\HttpException;
|
||||
use yii\web\UploadedFile;
|
||||
|
||||
use app\models\Product;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function behaviors()
|
||||
public function actionIndex(string $catn): array|string|null
|
||||
{
|
||||
return [
|
||||
'access' => [
|
||||
'class' => AccessControl::class,
|
||||
'only' => ['add'],
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => false,
|
||||
'roles' => ['?']
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function actionIndex(int $id)
|
||||
{
|
||||
if ($model = Product::readById($id)) {
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
// Товар найден
|
||||
|
||||
// Инициализация
|
||||
$model = $model->getAttributes();
|
||||
|
||||
if (Yii::$app->request->isAjax) {
|
||||
if (yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('index', compact('model')),
|
||||
'redirect' => '/product/' . $id,
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'redirect' => '/product/' . $catn,
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
@@ -55,4 +37,203 @@ class ProductController extends Controller
|
||||
throw new HttpException(404);
|
||||
}
|
||||
}
|
||||
|
||||
public function actionEditTitle(string $catn): array|string|null
|
||||
{
|
||||
// Инициализация
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (is_null($catn)) {
|
||||
// Не получен артикул
|
||||
|
||||
yii::$app->response->statusCode = 500;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
// Товар найден
|
||||
|
||||
// Инициализация
|
||||
$text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия';
|
||||
|
||||
$model->name = $text;
|
||||
|
||||
if ($model->save()) {
|
||||
// Товар обновлён
|
||||
|
||||
$return['name'] = $text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конец алгоритма
|
||||
*/
|
||||
end:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
return $this->render('index', compact('model'));
|
||||
} else {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
public function actionEditCatn(string $catn): array|string|null
|
||||
{
|
||||
// Инициализация
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (is_null($catn)) {
|
||||
// Не получен артикул
|
||||
|
||||
yii::$app->response->statusCode = 500;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
// Товар найден
|
||||
|
||||
// Инициализация
|
||||
$text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия';
|
||||
|
||||
$model->catn = $text;
|
||||
|
||||
if ($model->save()) {
|
||||
// Товар обновлён
|
||||
|
||||
$return['main'] = $this->renderPartial('index', compact('model'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конец алгоритма
|
||||
*/
|
||||
end:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
return $this->render('index', compact('model'));
|
||||
} else {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
public function actionEditDesc(string $catn): array|string|null
|
||||
{
|
||||
// Инициализация
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (is_null($catn)) {
|
||||
// Не получен артикул
|
||||
|
||||
yii::$app->response->statusCode = 500;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ($product = Product::searchByCatn($catn)) {
|
||||
// Товар найден
|
||||
|
||||
// Инициализация
|
||||
$text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия';
|
||||
|
||||
$product->desc = $text;
|
||||
|
||||
if ($product->save()) {
|
||||
// Товар обновлён
|
||||
|
||||
$return['description'] = $text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конец алгоритма
|
||||
*/
|
||||
end:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
return $this->render('index', compact('model'));
|
||||
} else {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
public function actionWriteImage(string $catn): array|string|null
|
||||
{
|
||||
// Инициализация
|
||||
$return = [
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (is_null($catn)) {
|
||||
// Не получен артикул
|
||||
|
||||
yii::$app->response->statusCode = 500;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
// Товар найден
|
||||
|
||||
// Инициализация
|
||||
$model->file_image = UploadedFile::getInstancesByName('images');
|
||||
$model->scenario = $model::SCENARIO_IMPORT_IMAGE;
|
||||
|
||||
if ($model->importImages() > 0) {
|
||||
// Товар обновлён
|
||||
|
||||
$return['main'] = $this->renderPartial('index', compact('model'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конец алгоритма
|
||||
*/
|
||||
end:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ($model = Product::searchByCatn($catn)) {
|
||||
return $this->render('index', compact('model'));
|
||||
} else {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
use yii\web\Cookie;
|
||||
use yii\web\UploadedFile;
|
||||
|
||||
use app\models\Supply;
|
||||
use app\models\SupplyGroup;
|
||||
use app\models\Search;
|
||||
use app\models\Notification;
|
||||
use app\models\Settings;
|
||||
use app\models\SettingsEdgeSettings;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
@@ -24,13 +28,27 @@ class ProfileController extends Controller
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => true,
|
||||
'roles' => ['@']
|
||||
'roles' => ['@'],
|
||||
'actions' => ['index', 'supplies', 'import', 'monitoring', 'readGroups']
|
||||
],
|
||||
[
|
||||
'allow' => false,
|
||||
'roles' => ['?'],
|
||||
'verbs' => ['POST'],
|
||||
'denyCallback' => [$this, 'accessDenied']
|
||||
],
|
||||
[
|
||||
'allow' => true,
|
||||
'actions' => ['panel', 'panel-notification-write'],
|
||||
'matchCallback' => function ($rule, $action): bool {
|
||||
if (
|
||||
yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -39,67 +57,340 @@ class ProfileController extends Controller
|
||||
|
||||
public function accessDenied()
|
||||
{
|
||||
$cookies = Yii::$app->response->cookies;
|
||||
// Инициализация
|
||||
$cookies = yii::$app->response->cookies;
|
||||
|
||||
// Запись cookie с редиректом, который выполнится после авторизации
|
||||
$cookies->add(new Cookie([
|
||||
'name' => 'redirect',
|
||||
'value' => Yii::$app->request->pathInfo
|
||||
'value' => yii::$app->request->pathInfo
|
||||
]));
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
// Проверить переадресацию на уровне сервера
|
||||
Yii::$app->response->content = json_encode([
|
||||
'main' => $this->renderPartial('/account'),
|
||||
'redirect' => '/authentication',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
]);
|
||||
}
|
||||
|
||||
public function actionIndex()
|
||||
{
|
||||
// Инициализация
|
||||
$model = new Supply(Yii::$app->request->post('Supply') ?? Yii::$app->request->get('Supply'));
|
||||
|
||||
if (Yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
if (Yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
// Настройка
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('index'),
|
||||
'redirect' => '/profile',
|
||||
// Генерация ответа
|
||||
Yii::$app->response->content = json_encode([
|
||||
'main' => $this->renderPartial('/account/index'),
|
||||
'redirect' => '/authentication',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
]);
|
||||
} else if (Yii::$app->request->isGet) {
|
||||
// GET-запрос
|
||||
|
||||
$this->redirect('/authentication');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница с настройками аккаунта
|
||||
*/
|
||||
public function actionIndex(): string|array
|
||||
{
|
||||
// Инициализация
|
||||
$model = yii::$app->user->identity;
|
||||
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
|
||||
$sidebar = $this->renderPartial('sidebar');
|
||||
|
||||
// Обработка настроек аккаунта
|
||||
if ($vars = yii::$app->request->post('Account') ?? yii::$app->request->get('Account')) {
|
||||
// Обнаружены входные параметры
|
||||
|
||||
if (isset($vars['opts'])) {
|
||||
// Переданы параметры
|
||||
|
||||
// Инициализация
|
||||
is_array($model->opts) || $model->opts = [];
|
||||
|
||||
// Запись
|
||||
$model->opts = array_merge($model->opts, $vars['opts']);
|
||||
|
||||
$model->update();
|
||||
} else {
|
||||
/**
|
||||
* @todo Написать обработчик ошибок
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]', limit: null));
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('index', compact(
|
||||
'model',
|
||||
'sidebar',
|
||||
'list',
|
||||
'panel'
|
||||
)),
|
||||
'redirect' => '/profile',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('index', compact('model'));
|
||||
return $this->render('index', compact(
|
||||
'model',
|
||||
'sidebar',
|
||||
'list',
|
||||
'panel'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница панели управления для доверенных пользователей
|
||||
*/
|
||||
public function actionPanel(): string|array
|
||||
{
|
||||
// Инициализация
|
||||
$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'))) {
|
||||
// Обнаружены входные параметры из раздела "Уведомления"
|
||||
|
||||
// Реинициализация с новыми параметрами
|
||||
$model_notifications = new Notification($vars);
|
||||
|
||||
// Запись уведомления и отправка (запись ребра до аккаунта)
|
||||
$model_notifications->write();
|
||||
} else if (!is_null($vars = yii::$app->request->post('Settings') ?? yii::$app->request->get('Settings'))) {
|
||||
// Обнаружены входные параметры из раздела "Настройки"
|
||||
|
||||
if ($to = new Settings($vars)) {
|
||||
// Настройки инициализированы
|
||||
|
||||
// Отправка
|
||||
if ($to->save()) {
|
||||
// Сохранено в базе данных
|
||||
|
||||
// Буфер
|
||||
$from = $model_settings;
|
||||
|
||||
// Реинициализация (для представления)
|
||||
$model_settings = $to;
|
||||
|
||||
if ($from) {
|
||||
// Найдена старая версия настроек
|
||||
|
||||
// Запись ребра: НАСТРОЙКИ (старые) -> НАСТРОЙКИ (новые)
|
||||
SettingsEdgeSettings::write($from->readId(), $to->readId(), 'update');
|
||||
}
|
||||
} else {
|
||||
// Не сохранено в базе данных
|
||||
|
||||
// Запись ошибок
|
||||
$model_settings->addErrors($to->getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Деинициализация
|
||||
unset($vars);
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// AJAX-POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('panel', compact(
|
||||
'model_notifications',
|
||||
'model_settings',
|
||||
'sidebar',
|
||||
'panel'
|
||||
)),
|
||||
'redirect' => '/profile/panel',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('panel', compact(
|
||||
'model_notifications',
|
||||
'model_settings',
|
||||
'sidebar',
|
||||
'panel'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Страница мониторинга
|
||||
*/
|
||||
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;
|
||||
|
||||
$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: $offset = ((int) $page_search_history ?? 0) * $rows_amount
|
||||
);
|
||||
|
||||
// Проверка результатов
|
||||
monitoring_result_check:
|
||||
|
||||
if (count($search_history) === 0 && $offset !== 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: $offset = ((int) $page_search_history ?? 0) * $rows_amount
|
||||
);
|
||||
|
||||
// Повторная проверка
|
||||
goto monitoring_result_check;
|
||||
}
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// AJAX-POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('monitoring', compact(
|
||||
'sidebar',
|
||||
'search_history',
|
||||
'page_search_history',
|
||||
'panel'
|
||||
)),
|
||||
'search' => $page_search_history + 1,
|
||||
'redirect' => '/profile/monitoring',
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('monitoring', compact(
|
||||
'sidebar',
|
||||
'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()
|
||||
{
|
||||
var_dump($_FILES);
|
||||
// Инициализация
|
||||
$model = new Supply(Yii::$app->request->post('Supply') ?? Yii::$app->request->get('Supply'));
|
||||
$model->scenario = $model::SCENARIO_IMPORT;
|
||||
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
|
||||
$model->scenario = $model::SCENARIO_IMPORT_EXCEL;
|
||||
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
|
||||
$sidebar = $this->renderPartial('sidebar');
|
||||
$groups = self::readGroups();
|
||||
|
||||
if (Yii::$app->request->isAjax) {
|
||||
if (yii::$app->request->isPost) {
|
||||
// AJAX-POST-запрос
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
$model->file = UploadedFile::getInstances($model, 'file');
|
||||
$model->file_excel = UploadedFile::getInstances($model, 'file_excel');
|
||||
|
||||
if (!$test = $model->import()) {
|
||||
Yii::$app->response->statusCode = 409;
|
||||
if ($model->importExcel()) {
|
||||
return [
|
||||
'main' => $this->renderPartial('supplies', compact(
|
||||
'model',
|
||||
'groups',
|
||||
'sidebar',
|
||||
'panel'
|
||||
)),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('index', compact('model')),
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
];
|
||||
yii::$app->response->statusCode = 409;
|
||||
}
|
||||
|
||||
return $this->render('index', compact('model'));
|
||||
return $this->render('supplies', compact(
|
||||
'model',
|
||||
'groups',
|
||||
'sidebar',
|
||||
'panel'
|
||||
));
|
||||
}
|
||||
|
||||
public static function readGroups()
|
||||
|
@@ -1,59 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\filters\AccessControl;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
|
||||
use app\models\AccountForm;
|
||||
|
||||
class RegistrationController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function behaviors()
|
||||
{
|
||||
return [
|
||||
'access' => [
|
||||
'class' => AccessControl::class,
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => true,
|
||||
'roles' => ['?'],
|
||||
]
|
||||
],
|
||||
]
|
||||
];
|
||||
}
|
||||
// public function behaviors()
|
||||
// {
|
||||
// return [
|
||||
// 'access' => [
|
||||
// 'class' => AccessControl::class,
|
||||
// 'rules' => [
|
||||
// [
|
||||
// 'allow' => true,
|
||||
// 'roles' => ['?'],
|
||||
// ]
|
||||
// ],
|
||||
// ]
|
||||
// ];
|
||||
// }
|
||||
|
||||
public function actionIndex()
|
||||
{
|
||||
if (Yii::$app->request->isAjax) {
|
||||
// AJAX-POST-запрос
|
||||
// Инициализация
|
||||
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm'));
|
||||
$model->scenario = $model::SCENARIO_REGISTRATION;
|
||||
|
||||
// Инициализация
|
||||
$model = new AccountForm(Yii::$app->request->post('AccountForm'));
|
||||
$model->scenario = $model::SCENARIO_REGISTRATION;
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
Yii::$app->response->format = Response::FORMAT_JSON;
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
if (!Yii::$app->user->isGuest || $model->registration()) {
|
||||
if (!yii::$app->user->isGuest || $model->registration()) {
|
||||
// Данные прошли проверку и аккаунт был создан
|
||||
|
||||
// Аутентификация
|
||||
$model->scenario = $model::SCENARIO_AUTHENTICATION;
|
||||
$model->authentication();
|
||||
|
||||
// Инициализация
|
||||
$notifications_button = $this->renderPartial('/notification/button');
|
||||
$notifications_panel_full = true;
|
||||
$notifications_panel = $this->renderPartial('/notification/panel', compact('notifications_panel_full'));
|
||||
|
||||
// Запись ответа
|
||||
$return = [
|
||||
'nav' => (new AccountForm())->deauthenticationGenHtml(),
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
|
||||
'notifications_button',
|
||||
'notifications_panel'
|
||||
)),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if (($cookies = Yii::$app->response->cookies)->has('redirect')) {
|
||||
if (($cookies = yii::$app->response->cookies)->has('redirect')) {
|
||||
// Найдено cookie с переадресацией
|
||||
|
||||
// Запись ответа
|
||||
@@ -61,11 +68,11 @@ class RegistrationController extends Controller
|
||||
$return['main'] = $this->renderPartial($return['redirect'] . '/index');
|
||||
|
||||
// Очистка cookie
|
||||
unset(Yii::$app->response->cookies['redirect']);
|
||||
unset(yii::$app->response->cookies['redirect']);
|
||||
} else {
|
||||
// Не найдено cookie с переадресацией
|
||||
|
||||
if (Yii::$app->request->pathInfo === 'authentication' || Yii::$app->request->pathInfo === 'registration') {
|
||||
if (yii::$app->request->pathInfo === 'authentication' || yii::$app->request->pathInfo === 'registration') {
|
||||
// Если клиент на промежуточном URI
|
||||
|
||||
// Запись ответа
|
||||
@@ -78,20 +85,20 @@ class RegistrationController extends Controller
|
||||
} else {
|
||||
// Данные не прошли проверку
|
||||
|
||||
Yii::$app->response->statusCode = 400;
|
||||
yii::$app->response->statusCode = 400;
|
||||
|
||||
return [
|
||||
'main' => $this->renderPartial('/account', compact('model')),
|
||||
'main' => $this->renderPartial('/account/index', compact('model')),
|
||||
'redirect' => '/registration',
|
||||
'_csrf' => Yii::$app->request->getCsrfToken()
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!Yii::$app->user->isGuest) {
|
||||
Yii::$app->response->redirect('/');
|
||||
if (!yii::$app->user->isGuest) {
|
||||
yii::$app->response->redirect('/');
|
||||
} else {
|
||||
return $this->render('/account', compact('model'));
|
||||
return $this->render('/account/index', compact('model'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
231
mirzaev/skillparts/system/controllers/SearchController.php
Normal file
231
mirzaev/skillparts/system/controllers/SearchController.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\controllers;
|
||||
|
||||
use yii;
|
||||
use yii\web\Controller;
|
||||
use yii\web\Response;
|
||||
|
||||
use app\models\Product;
|
||||
use app\models\Supply;
|
||||
use app\models\Search;
|
||||
|
||||
class SearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* @todo Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
|
||||
*/
|
||||
public function actionIndex(): array|string
|
||||
{
|
||||
// Инициализация параметров
|
||||
$auth_only = false;
|
||||
|
||||
if ($auth_only && yii::$app->user->isGuest) {
|
||||
// Если активирован режим "Поиск только аутентифицированным" и запрос пришел не от аутентифицированного
|
||||
|
||||
// Запись кода ответа: 401 (необходима авторизация)
|
||||
yii::$app->response->statusCode = 401;
|
||||
|
||||
// Переход к концу обработки
|
||||
goto skip_search;
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$query = yii::$app->request->post('request') ?? yii::$app->request->get('q');
|
||||
|
||||
if (yii::$app->request->post('type') === 'product' || yii::$app->request->get('type') === 'product') {
|
||||
// Поиск по продуктам
|
||||
|
||||
if (yii::$app->request->post('history')) {
|
||||
// Запрошена история
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'panel' => $this->renderPartial('/search/panel', ['history' => true]),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
}
|
||||
|
||||
// Инициализация сессии
|
||||
$session = yii::$app->session;
|
||||
|
||||
// Инициализация ответа
|
||||
$response = null;
|
||||
|
||||
// Инициализация параметров
|
||||
$timer = 0;
|
||||
|
||||
// Период пропуска запросов (в секундах)
|
||||
$period = 5;
|
||||
$keep_connect = false;
|
||||
$sanction = false;
|
||||
$sanction_condition = ($session['last_request'] + $period - time()) < $period;
|
||||
$sanction_time = 2;
|
||||
$query_min = 2;
|
||||
$query_max = 20;
|
||||
|
||||
if (isset($session['last_request'])) {
|
||||
// Данные о времени последнего запроса не найдены
|
||||
|
||||
if ($sanction && $sanction_condition) {
|
||||
// Наказание за повторный запрос при условии задержки
|
||||
|
||||
$session['last_request'] += $sanction_time;
|
||||
}
|
||||
} else {
|
||||
// Это первый запрос
|
||||
|
||||
// Инициализация
|
||||
$session['last_request'] = time();
|
||||
|
||||
// Пропуск проверок
|
||||
goto first_request;
|
||||
}
|
||||
|
||||
// Метка: "Повтор обработки поиска" (для создания непрерывного соединения)
|
||||
keep_connect_wait:
|
||||
|
||||
// Запись времени последнего запроса и вычисление об истечении таймера
|
||||
$timer = $session['last_request'] + $period - time();
|
||||
|
||||
if ($timer > 0) {
|
||||
// Время блокировки не истекло
|
||||
|
||||
// Запись кода ответа: 202 (запрос не обработан)
|
||||
yii::$app->response->statusCode = 202;
|
||||
|
||||
$return = [
|
||||
'timer' => $timer,
|
||||
'panel' => $this->renderPartial('/search/loading'),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
} else {
|
||||
// Запрос
|
||||
|
||||
// Очистка времени последнего запроса
|
||||
unset($session['last_request']);
|
||||
|
||||
// Метка: "Первый запрос"
|
||||
first_request:
|
||||
|
||||
if (strlen($query) < $query_min) {
|
||||
// Выход за ограничения длины с недостатком
|
||||
|
||||
// Пропуск поиска
|
||||
goto skip_query;
|
||||
} else if (strlen($query) > $query_max) {
|
||||
// Выход за ограничения длины с превышением
|
||||
|
||||
// Пропуск поиска
|
||||
goto skip_query;
|
||||
}
|
||||
|
||||
// Запись в историю
|
||||
Search::write($query);
|
||||
|
||||
$limit = yii::$app->request->isPost ? 10 : 20;
|
||||
|
||||
if ($response = Product::searchByPartialCatn($query, $limit, ['catn' => 'catn', '_key' => '_key'])) {
|
||||
// Данные найдены по поиску в полях Каталожного номера
|
||||
|
||||
foreach ($response as &$row) {
|
||||
// Перебор продуктов
|
||||
|
||||
// Поиск поставок привязанных к продуктам
|
||||
$row['supplies'] = Supply::searchByEdge(
|
||||
from: 'product',
|
||||
to: 'supply',
|
||||
edge: 'supply_edge_product',
|
||||
limit: 11,
|
||||
direction: 'OUTBOUND',
|
||||
subquery_where: [
|
||||
['product._key' => $row['_key']],
|
||||
['supply.catn == product.catn'],
|
||||
['supply_edge_product.type' => 'connect']
|
||||
],
|
||||
where: 'supply._id == supply_edge_product[0]._from',
|
||||
select: 'supply_edge_product[0]'
|
||||
);
|
||||
|
||||
if (count($row['supplies']) === 11) {
|
||||
// Если в базе данных хранится много поставок
|
||||
|
||||
// Инициализация
|
||||
$row['overload'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Запись ответа
|
||||
$return = [
|
||||
'panel' => $this->renderPartial('/search/panel', compact('response')),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
|
||||
if ((int) yii::$app->request->post('advanced')) {
|
||||
// Полноценный поиск
|
||||
|
||||
// Запись ответа
|
||||
$return['main'] = $this->renderPartial('/search/index', compact('response'));
|
||||
$return['hide'] = 1;
|
||||
$return['redirect'] = '/search?type=product&q=' . $query;
|
||||
}
|
||||
} else {
|
||||
// Данные не найдены
|
||||
|
||||
// Запись кода ответа: 404 (запрашиваемые данные не найдены)
|
||||
yii::$app->response->statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
// Метка: "Пропуск запроса" (для пропуска самого поиска и возврата ответа)
|
||||
skip_query:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return $return ?? [
|
||||
'panel' => $this->renderPartial('/search/panel'),
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
} else {
|
||||
// GET-запрос
|
||||
|
||||
if (empty($return['main']) && $keep_connect && $timer > 0) {
|
||||
// Режим непрерывного соединения
|
||||
|
||||
// Ожидание
|
||||
sleep($timer);
|
||||
|
||||
// Повтор обработки
|
||||
goto keep_connect_wait;
|
||||
}
|
||||
|
||||
return $this->render('/search/index', compact('response', 'timer'));
|
||||
}
|
||||
}
|
||||
|
||||
// Метка: "Пропуск обработки"
|
||||
skip_search:
|
||||
|
||||
if (yii::$app->request->isPost) {
|
||||
// POST-запрос
|
||||
|
||||
yii::$app->response->format = Response::FORMAT_JSON;
|
||||
|
||||
return [
|
||||
'panel' => $this->renderPartial('/search/panel'),
|
||||
'hide' => (int) $auth_only,
|
||||
'_csrf' => yii::$app->request->getCsrfToken()
|
||||
];
|
||||
} else {
|
||||
// GET-запрос
|
||||
|
||||
return $this->render('/search/index');
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m201219_074926_create_account_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210101_092505_create_product_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210107_163448_create_supply_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210108_014505_create_account_edge_supply_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210108_212826_create_product_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210108_221446_create_product_edge_product_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210108_222132_create_supply_edge_product_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('supply_edge_product', ['Type' => 3]);
|
||||
}
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('supply_edge_product', ['Type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('supply_edge_product');
|
||||
}
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('supply_edge_product');
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210108_222740_create_product_edge_product_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210109_214817_create_supply_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210109_214833_create_supply_edge_supply_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210111_044635_create_supply_edge_supply_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210112_010347_create_product_group_edge_product_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210112_010411_create_supply_group_edge_supply_group_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210112_034135_create_requisite_collection extends Migration
|
||||
{
|
||||
|
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210112_034232_create_supply_edge_requisite_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('supply_edge_requisite', ['Type' => 3]);
|
||||
$this->createCollection('supply_edge_requisite', ['Type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210113_021800_create_purchase_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210113_021905_create_account_edge_purchase_collection extends Migration
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use explosivebit\arangodb\Migration;
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210113_021917_create_purchase_edge_supply_collection extends Migration
|
||||
{
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210206_154140_create_search_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('search', []);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('search');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210206_154210_create_account_edge_search_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('account_edge_search', ['Type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('account_edge_search');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210209_185314_create_notification_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('notification', []);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('notification');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210209_185328_create_account_edge_notification_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('account_edge_notification', ['type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('account_edge_notification');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210223_121142_create_settings_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('settings', []);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('settings');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210223_145042_create_settings_edge_settings_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('settings_edge_settings', ['type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('settings_edge_settings');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210303_192326_create_order_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('order', []);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('order');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210303_192451_create_order_edge_supply_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('order_edge_supply', ['type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('order_edge_supply');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210307_000210_create_account_edge_order_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('account_edge_order', ['type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('account_edge_order');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210314_133722_create_session_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('session', []);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('session');
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use mirzaev\yii2\arangodb\Migration;
|
||||
|
||||
class m210314_133926_create_account_edge_session_collection extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$this->createCollection('account_edge_session', ['type' => 3]);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->dropCollection('account_edge_session');
|
||||
}
|
||||
}
|
@@ -1,23 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use Yii;
|
||||
use yii\web\IdentityInterface;
|
||||
|
||||
use carono\exchange1c\interfaces\PartnerInterface;
|
||||
|
||||
/**
|
||||
* Аккаунт
|
||||
*
|
||||
* Реализует аккаунты пользователей и поставщиков
|
||||
*/
|
||||
class Account extends Document implements IdentityInterface, PartnerInterface
|
||||
{
|
||||
public static function collectionName()
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account';
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'auth',
|
||||
'mail',
|
||||
'pswd',
|
||||
'name',
|
||||
@@ -25,12 +40,42 @@ class Account extends Document implements IdentityInterface, PartnerInterface
|
||||
'sity',
|
||||
'comp',
|
||||
'taxn',
|
||||
'auth'
|
||||
'onec',
|
||||
'opts',
|
||||
'agnt',
|
||||
'type'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'auth' => 'Аутентификационный хеш',
|
||||
'mail' => 'Почта',
|
||||
'pswd' => 'Пароль',
|
||||
'name' => 'Имя',
|
||||
'simc' => 'Номер',
|
||||
'sity' => 'Город',
|
||||
'comp' => 'Компания',
|
||||
'taxn' => 'ИНН',
|
||||
'onec' => 'Данные 1C',
|
||||
'opts' => 'Параметры',
|
||||
'agnt' => 'Агент (поставщик)',
|
||||
'type' => 'Тип аккаунта'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
@@ -42,24 +87,13 @@ class Account extends Document implements IdentityInterface, PartnerInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function attributeLabels()
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'mail' => 'Почта',
|
||||
'pswd' => 'Пароль',
|
||||
'name' => 'Имя',
|
||||
'simc' => 'Номер',
|
||||
'sity' => 'Город',
|
||||
'comp' => 'Компания',
|
||||
'taxn' => 'ИНН',
|
||||
'auth' => 'Аутентификационный хеш'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function beforeSave($data)
|
||||
/**
|
||||
* Перед сохранением
|
||||
*
|
||||
* @todo Подождать обновление от ебаного Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public function beforeSave($data): bool
|
||||
{
|
||||
if (parent::beforeSave($data)) {
|
||||
if ($this->isNewRecord) {
|
||||
@@ -71,70 +105,201 @@ class Account extends Document implements IdentityInterface, PartnerInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getExportFields1c($context = null)
|
||||
/**
|
||||
* Чтение полей для экспорта из 1С
|
||||
*/
|
||||
public function getExportFields1c($context = null): array
|
||||
{
|
||||
return [
|
||||
'Ид' => 'id',
|
||||
'Наименование' => 'username',
|
||||
'ПолноеНаименование' => 'full_name',
|
||||
'Фамилия' => 'surname',
|
||||
'Имя' => 'name',
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение идентификатора
|
||||
*
|
||||
* @see IdentityInterface
|
||||
*/
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->_key;
|
||||
return self::collectionName() . '/' . $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение идентификатора
|
||||
*/
|
||||
public function readId(): string
|
||||
{
|
||||
return self::collectionName() . '/' . $this->getId();
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function getAuthKey()
|
||||
/**
|
||||
* Чтение аутентификационного ключа
|
||||
*/
|
||||
public function getAuthKey(): string
|
||||
{
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
public static function findIdentity($_key)
|
||||
/**
|
||||
* Идентификация
|
||||
*
|
||||
* @todo Подождать обновление от ебаного Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public static function findIdentity($_id): self
|
||||
{
|
||||
return static::findByKey($_key);
|
||||
return static::findById($_id);
|
||||
}
|
||||
|
||||
public static function findIdentityByAccessToken($pass, $type = null)
|
||||
/**
|
||||
* Поиск по ключу
|
||||
*
|
||||
* @todo Подождать обновление от ебаного Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public static function findIdentityByAccessToken($pass, $type = null): self
|
||||
{
|
||||
return static::findOne(['pass' => $pass]);
|
||||
}
|
||||
|
||||
public static function findByMail($mail)
|
||||
/**
|
||||
* Поиск по почте
|
||||
*
|
||||
* @todo Подождать обновление Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public static function findByMail($mail): ?self
|
||||
{
|
||||
return static::findOne(['mail' => $mail]);
|
||||
}
|
||||
|
||||
public static function findByKey($_key)
|
||||
/**
|
||||
* Поиск по идентификатору
|
||||
*
|
||||
* @todo Подождать обновление Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public static function findById($_id): self
|
||||
{
|
||||
return static::findOne(['_key' => $_key]);
|
||||
return static::searchById($_id);
|
||||
}
|
||||
|
||||
public static function validateMail($mail)
|
||||
/**
|
||||
* Проверка почты
|
||||
*/
|
||||
public static function validateMail(string $mail): bool
|
||||
{
|
||||
if (static::findByMail($mail)) {
|
||||
// Почта найдена в базе данных
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function validatePassword($pswd)
|
||||
/**
|
||||
* Проверка пароля
|
||||
*/
|
||||
public function validatePassword(string $pswd): bool
|
||||
{
|
||||
return Yii::$app->security->validatePassword($pswd, $this->pswd);
|
||||
}
|
||||
|
||||
public function validateAuthKey($auth)
|
||||
/**
|
||||
* Проверка аутентификационного ключа
|
||||
*
|
||||
* @todo Подождать обновление Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public function validateAuthKey($auth): bool
|
||||
{
|
||||
return $this->getAuthKey() === $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать параметр
|
||||
*
|
||||
* @param string $name Название параметра
|
||||
* @param mixed $value Значение параметра
|
||||
*/
|
||||
public function writeOption(string $name, mixed $value = null): bool
|
||||
{
|
||||
// Запись
|
||||
$this->opts[$name] = $value;
|
||||
|
||||
// Отправка
|
||||
return $this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить параметр
|
||||
*
|
||||
* @param string $name Название параметра
|
||||
*/
|
||||
public function deleteOption(string $name): bool
|
||||
{
|
||||
// Удаление
|
||||
unset($this->opts[$name]);
|
||||
|
||||
// Отправка
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
13
mirzaev/skillparts/system/models/AccountEdgeNotification.php
Normal file
13
mirzaev/skillparts/system/models/AccountEdgeNotification.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class AccountEdgeNotification extends Edge
|
||||
{
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account_edge_notification';
|
||||
}
|
||||
}
|
13
mirzaev/skillparts/system/models/AccountEdgeOrder.php
Normal file
13
mirzaev/skillparts/system/models/AccountEdgeOrder.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class AccountEdgeOrder extends Edge
|
||||
{
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account_edge_order';
|
||||
}
|
||||
}
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class AccountEdgePurchase extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account_edge_purchase';
|
||||
}
|
||||
|
13
mirzaev/skillparts/system/models/AccountEdgeSearch.php
Normal file
13
mirzaev/skillparts/system/models/AccountEdgeSearch.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class AccountEdgeSearch extends Edge
|
||||
{
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account_edge_search';
|
||||
}
|
||||
}
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class AccountEdgeSupply extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'account_edge_supply';
|
||||
}
|
||||
|
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use Yii;
|
||||
use yii;
|
||||
use yii\base\Model;
|
||||
|
||||
use app\models\Account;
|
||||
|
||||
/**
|
||||
@@ -20,7 +23,7 @@ class AccountForm extends Model
|
||||
|
||||
public $mail;
|
||||
public $pswd;
|
||||
public $auto = true;
|
||||
public $auto = false;
|
||||
|
||||
private $account = false;
|
||||
|
||||
@@ -56,7 +59,7 @@ class AccountForm extends Model
|
||||
|
||||
$account = $this->getAccount();
|
||||
|
||||
if (!$account || !$account->validateMail($this->mail)) {
|
||||
if (!$account || $account->validateMail($this->mail)) {
|
||||
// Проверка не пройдена
|
||||
|
||||
$this->addError($attribute, 'Почта уже привязана');
|
||||
@@ -94,7 +97,7 @@ class AccountForm extends Model
|
||||
// Проверка пройдена
|
||||
|
||||
// Аутентификация
|
||||
return Yii::$app->user->login($this->getAccount(), $this->auto ? 3600 * 24 * 30 : 0);
|
||||
return yii::$app->user->login($this->getAccount(), $this->auto ? 3600 * 24 * 30 : 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -113,7 +116,7 @@ class AccountForm extends Model
|
||||
|
||||
// Запись параметров
|
||||
$this->account->mail = $this->mail;
|
||||
$this->account->pswd = Yii::$app->security->generatePasswordHash($this->pswd);
|
||||
$this->account->pswd = yii::$app->security->generatePasswordHash($this->pswd);
|
||||
|
||||
// Регистрация
|
||||
return $this->account->save();
|
||||
@@ -135,38 +138,4 @@ class AccountForm extends Model
|
||||
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
public function authenticationGenHtml(string $dropdown): string
|
||||
{
|
||||
return <<<HTML
|
||||
<a class="text-dark my-auto mr-2" href="/cart"><i class="fas fa-shopping-cart mx-2"></i></a>
|
||||
<a class="text-dark my-auto mr-2" href="/orders"><i class="fas fa-list mx-2"></i></a>
|
||||
<div class="btn-group">
|
||||
<a class="btn m-0 px-0 text-dark button_clean" title="Личный кабинет" href="/profile" role="button" onclick="return page_profile();">Личный кабинет</a>
|
||||
<button id="profile_button" class="btn pr-0 dropdown-toggle dropdown-toggle-split button_clean" type="button" data-toggle="dropdown" onmouseover="$('#profile_button').dropdown('show')"></button>
|
||||
<div class="dropdown-menu dropdown-menu-long dropdown-menu-right p-3" aria-labelledby="profile_button" onmouseout="$('#profile_button').dropdown('show')">
|
||||
<h5 class="mb-3 text-center">Аутентификация</h5>
|
||||
$dropdown
|
||||
<!-- <a class="dropdown-item-text text-center px-0 py-2" href="#"><small>Восстановление пароля</small></a> -->
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
public function deauthenticationGenHtml(): string
|
||||
{
|
||||
$mail = Yii::$app->user->identity->mail;
|
||||
|
||||
return <<<HTML
|
||||
<a class="text-dark my-auto mr-2" href="/cart"><i class="fas fa-shopping-cart mx-2"></i></a>
|
||||
<a class="text-dark my-auto mr-2" href="/orders"><i class="fas fa-list mx-2"></i></a>
|
||||
<div class="btn-group">
|
||||
<a class="btn m-0 px-0 text-dark button_clean" title="Личный кабинет" href="/profile" role="button" onclick="return page_profile();">Личный кабинет</a>
|
||||
<button id="profile_button" class="btn pr-0 dropdown-toggle dropdown-toggle-split button_clean" type="button" data-toggle="dropdown" onmouseover="$('#profile_button').dropdown('show')"></button>
|
||||
<div class="dropdown-menu dropdown-menu-right py-1" aria-labelledby="profile_button" onmouseout="$('#profile_button').dropdown('show')">
|
||||
<a class="dropdown-item button_white text-dark" onclick="deauthentication()">Выход ($mail)</a>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
@@ -1,62 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use Yii;
|
||||
use explosivebit\arangodb\ActiveRecord;
|
||||
use yii;
|
||||
|
||||
use mirzaev\yii2\arangodb\ActiveRecord;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Документ
|
||||
*/
|
||||
abstract class Document extends ActiveRecord
|
||||
{
|
||||
public function attributes()
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return throw new Exception('Не инициализировано название коллекции');
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'_key',
|
||||
'date',
|
||||
'writer'
|
||||
'jrnl'
|
||||
];
|
||||
}
|
||||
|
||||
public function attributeLabels()
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'Дата',
|
||||
'writer' => 'Аккаунт записавшего'
|
||||
'_key' => 'Ключ',
|
||||
'jrnl' => 'Журнал'
|
||||
];
|
||||
}
|
||||
|
||||
public function rules()
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*
|
||||
* @todo Добавить проверку существования аккаунта
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'writer',
|
||||
'string'
|
||||
// Надо добавить проверку существования аккаунта
|
||||
]
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
public function beforeSave($data)
|
||||
/**
|
||||
* Перед сохранением
|
||||
*
|
||||
* @todo Подождать обновление от ебаного Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public function beforeSave($data): bool
|
||||
{
|
||||
if (parent::beforeSave($data)) {
|
||||
if ($this->isNewRecord) {
|
||||
// Запись в журнал
|
||||
$this->jrnl = array_merge(
|
||||
[[
|
||||
'date' => time(),
|
||||
'account' => yii::$app->user->id,
|
||||
'action' => 'create'
|
||||
]],
|
||||
$this->jrnl ?? []
|
||||
);
|
||||
}
|
||||
|
||||
$this->date = time();
|
||||
$this->writer = $this->writer ?? Yii::$app->user->identity->readId();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function readId(): ?string
|
||||
/**
|
||||
* Журнал
|
||||
*
|
||||
* Записывает данные в журнал
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return int|bool Время записанное в журнале или false, если не удалось записать
|
||||
*/
|
||||
public function journal(string $action = 'update', array ...$data): int|bool
|
||||
{
|
||||
return isset($this->_key) ? static::collectionName() . '/' . $this->_key : null;
|
||||
// Инициализация
|
||||
if (isset($this->jrnl) && is_array($this->jrnl)) {
|
||||
} else {
|
||||
$this->jrnl = [];
|
||||
}
|
||||
|
||||
// Генерация
|
||||
$this->jrnl = array_merge(
|
||||
$this->jrnl,
|
||||
[array_merge(
|
||||
[
|
||||
'date' => $time = time(),
|
||||
'account' => yii::$app->user->id,
|
||||
'action' => $action
|
||||
],
|
||||
...$data
|
||||
)]
|
||||
);
|
||||
|
||||
// Запись и возврат
|
||||
return $this->update() ? $time : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение идентификатора
|
||||
*/
|
||||
public function readId(): ?string
|
||||
{
|
||||
return isset($this->_key) && static::collectionName() ? static::collectionName() . '/' . $this->_key : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск по идентификатору
|
||||
*/
|
||||
public static function searchById(string $_id): ?static
|
||||
{
|
||||
return static::findOne(['_id' => $_id]);
|
||||
}
|
||||
|
||||
public static function readLast(): ?static
|
||||
{
|
||||
return static::find()->orderBy(['DESC'])->one();
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение всех записей
|
||||
*/
|
||||
public static function readAll(): array
|
||||
{
|
||||
return static::find()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение количества записей
|
||||
*/
|
||||
public static function readAmount(): int
|
||||
{
|
||||
return static::find()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка на то, что в свойство передан массив
|
||||
*/
|
||||
public function arrayValidator(string $attribute, array $params = null): bool
|
||||
{
|
||||
if (is_array($this->$attribute)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use explosivebit\arangodb\ActiveRecord;
|
||||
|
||||
/**
|
||||
* Ребро
|
||||
*/
|
||||
abstract class Edge extends Document
|
||||
{
|
||||
public function attributes()
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
@@ -18,19 +24,24 @@ abstract class Edge extends Document
|
||||
);
|
||||
}
|
||||
|
||||
public function attributeLabels()
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'date' => 'От кого',
|
||||
'date' => 'К кому',
|
||||
'type' => 'Тип'
|
||||
'_from' => 'От кого',
|
||||
'_to' => 'К кому',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
@@ -48,13 +59,47 @@ abstract class Edge extends Document
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Перед сохранением
|
||||
*
|
||||
* @todo Подождать обновление от ебаного Yii2 и добавить
|
||||
* проверку типов передаваемых параметров
|
||||
*/
|
||||
public function beforeSave($data): bool
|
||||
{
|
||||
if (parent::beforeSave($data)) {
|
||||
if ($this->isNewRecord) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать (с проверкой на существование)
|
||||
*
|
||||
* Создаст ребро только в том случае, если его аналога не существует
|
||||
*/
|
||||
public static function writeSafe(string $_from, string $_to, string $type = '', array $data = []): ?static
|
||||
{
|
||||
if ($edge = self::searchByVertex($_from, $_to, limit: 1)) {
|
||||
// Найдено в базе данных
|
||||
|
||||
return $edge;
|
||||
}
|
||||
|
||||
return self::write($_from, $_to, $type, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать
|
||||
*/
|
||||
public 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 = isset($this->_key) ? $this : new static;
|
||||
$edge = new static;
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $_from;
|
||||
@@ -62,34 +107,52 @@ abstract class Edge extends Document
|
||||
$edge->type = $type;
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if(is_int($key)) {
|
||||
// Если ключ задан автоматически
|
||||
if (is_int($key)) {
|
||||
// Обычная запись
|
||||
|
||||
$edge->{$value} = true;
|
||||
} else {
|
||||
// Иначе ключ записан вручную
|
||||
// Ассоциативная запись
|
||||
|
||||
$edge->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Запись
|
||||
$edge->save();
|
||||
|
||||
return $edge;
|
||||
return $edge->save() ? $edge : null;
|
||||
}
|
||||
|
||||
public function beforeSave($data)
|
||||
/**
|
||||
* Поиск ребра по его вершинам
|
||||
*/
|
||||
public static function searchByVertex(string $_from, string $_to, string|null $type = null, int $limit = 1): array|null
|
||||
{
|
||||
if (parent::beforeSave($data)) {
|
||||
if ($this->isNewRecord) {
|
||||
}
|
||||
$query = self::find()->where([
|
||||
'_from' => $_from,
|
||||
'_to' => $_to
|
||||
]);
|
||||
|
||||
$this->type = $this->type ?? '';
|
||||
|
||||
return true;
|
||||
if (isset($type)) {
|
||||
$query->where(['type' => $type]);
|
||||
}
|
||||
|
||||
return false;
|
||||
return $query->limit($limit)->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск рёбер
|
||||
*/
|
||||
public static function search(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null
|
||||
{
|
||||
if ($direction === 'OUTBOUND') {
|
||||
$query = self::find()->where(['_from' => $target, 'type' => $type]);
|
||||
} else if ($direction === 'INBOUND') {
|
||||
$query = self::find()->where(['_to' => $target, 'type' => $type]);
|
||||
}
|
||||
|
||||
if ($limit < 2) {
|
||||
return $query->one();
|
||||
} else {
|
||||
return $query->limit($limit)->all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
219
mirzaev/skillparts/system/models/Notification.php
Normal file
219
mirzaev/skillparts/system/models/Notification.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use yii;
|
||||
|
||||
use app\models\traits\SearchByEdge;
|
||||
|
||||
use app\models\Account;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Поиск
|
||||
*
|
||||
* @see Product Поиск по товарам
|
||||
*
|
||||
* @todo Нет смысла каждый раз возвращать кнопку, надо написать проверки
|
||||
*/
|
||||
class Notification extends Document
|
||||
{
|
||||
use SearchByEdge;
|
||||
|
||||
/**
|
||||
* Сценарий для доверенного пользователя с созданием уведомления
|
||||
*/
|
||||
const SCENARIO_TRUSTED_CREATE = 'create';
|
||||
|
||||
/**
|
||||
* Тип уведомления: памятка
|
||||
*/
|
||||
const TYPE_NOTICE = 'notice';
|
||||
|
||||
/**
|
||||
* Тип уведомления: предупреждение
|
||||
*/
|
||||
const TYPE_WARNING = 'warning';
|
||||
|
||||
/**
|
||||
* Цель для отправки уведомления
|
||||
*
|
||||
* Расшифровывается как $target
|
||||
*
|
||||
* @see SCENARIO_TRUSTED_CREATE
|
||||
*/
|
||||
public string|null $account;
|
||||
|
||||
/**
|
||||
* Текст уведомления
|
||||
*/
|
||||
public string $text;
|
||||
|
||||
/**
|
||||
* Разделитель получателей уведомлений
|
||||
*/
|
||||
public static string $delimiter = ',';
|
||||
|
||||
/**
|
||||
* Типы уведомлений
|
||||
*/
|
||||
public array $typs = [
|
||||
'notice' => 'Уведомление',
|
||||
'warning' => 'Предупреждение'
|
||||
];
|
||||
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'notification';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'html',
|
||||
'type'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'html' => 'HTML',
|
||||
'type' => 'Тип',
|
||||
'trgt' => 'Получатели',
|
||||
'text' => 'Текст',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
[
|
||||
[
|
||||
'html',
|
||||
'required'
|
||||
],
|
||||
[
|
||||
'type',
|
||||
'default',
|
||||
'value' => 'notice'
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись
|
||||
*/
|
||||
public function write(): self|array|null
|
||||
{
|
||||
return $this::_write($this->text, $this->html, $this->account, $this->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись
|
||||
*
|
||||
* @param string $html Содержимое уведомления (HTML или текст)
|
||||
* @param bool|string|null $html Содержимое уведомления (HTML или текст)
|
||||
* @param string $account Получатель уведомления
|
||||
* @param string $type Тип уведомления
|
||||
*/
|
||||
public static function _write(string $text, bool|string|null $html = false, string $account = null, string $type = self::TYPE_NOTICE): self|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$model = new self;
|
||||
$account or $account = yii::$app->user->identity->_key ?? throw new Exception('Не удалось инициализировать получателя');
|
||||
|
||||
if ((bool) (int) $html) {
|
||||
// Получен текст в формете HTML-кода
|
||||
|
||||
$model->html = $text ?? null;
|
||||
} else {
|
||||
// Получен необработанный текст
|
||||
|
||||
$text = htmlspecialchars(strip_tags($text ?? null));
|
||||
|
||||
$model->html = <<<HTML
|
||||
<p class="my-2 mx-3">$text</p>
|
||||
HTML;
|
||||
}
|
||||
|
||||
if ($model->save()) {
|
||||
// Уведомление записано
|
||||
|
||||
// Инициализация получателей и создание ребра
|
||||
self::searchReceiverAndConnect($model, $account, $type);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск получателя
|
||||
*
|
||||
* @param self $model Уведомление
|
||||
* @param string $text Необработанный текст
|
||||
* @param string $type Тип уведомления
|
||||
*/
|
||||
protected static function searchReceiverAndConnect(self $model, string $text, string $type = self::TYPE_NOTICE): AccountEdgeNotification|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$return = [];
|
||||
|
||||
// Конвертация
|
||||
$accounts = array_map('trim', explode(self::$delimiter, $text));
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (in_array('@all', $accounts, true)) {
|
||||
// Найден флаг обозначающий отправку всем пользователям
|
||||
|
||||
// Инициализация
|
||||
$return = [];
|
||||
|
||||
foreach (Account::readAll() as $account) {
|
||||
// Перебор всех аккаунтов
|
||||
|
||||
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
|
||||
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
|
||||
}
|
||||
} else if (in_array('@test', $accounts, true)) {
|
||||
// Найден флаг обозначающий тестирование (отправка самому себе)
|
||||
|
||||
$return[] = AccountEdgeNotification::writeSafe($model->readId(), yii::$app->user->id, $type);
|
||||
} else {
|
||||
// Найден идентификатор (подразумевается)
|
||||
|
||||
if ($account = Account::searchById(Account::collectionName() . '/' . $account)) {
|
||||
// Аккаунт найден
|
||||
|
||||
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
|
||||
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return ? $return : null;
|
||||
}
|
||||
}
|
369
mirzaev/skillparts/system/models/Order.php
Normal file
369
mirzaev/skillparts/system/models/Order.php
Normal file
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use yii;
|
||||
use yii\web\User as Account;
|
||||
|
||||
use app\models\traits\SearchByEdge;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Заказ
|
||||
*
|
||||
* @see Account Заказчик
|
||||
* @see Supply Поставки для заказа
|
||||
*/
|
||||
class Order extends Document
|
||||
{
|
||||
use SearchByEdge;
|
||||
|
||||
/**
|
||||
* Поставки для записи
|
||||
*/
|
||||
public array $supplies;
|
||||
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'order';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'stts'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'stts' => 'Статус'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
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 writeSupply(Supply|string|array $supply, Account $trgt = null): int
|
||||
{
|
||||
// Инициализация
|
||||
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика');
|
||||
|
||||
if ($supply instanceof Supply) {
|
||||
// Передана инстанция класса поставки или второй элемент массива не является числом
|
||||
|
||||
// Унификация входных данных
|
||||
$supply = [$supply->catn => 1];
|
||||
}
|
||||
|
||||
if (is_null($this->_key)) {
|
||||
// Корзина не инициализирована
|
||||
|
||||
// Инициализация
|
||||
if (!$this->save()) {
|
||||
// Инициализация заказа не удалась
|
||||
|
||||
throw new Exception('Ошибка при записи заказа в базу данных');
|
||||
}
|
||||
|
||||
// Инициализация ребра: АККАУНТ -> ЗАКАЗ
|
||||
if (!AccountEdgeOrder::write($trgt->readId(), $this->readId(), 'create')) {
|
||||
// Инициализация не удалась
|
||||
|
||||
throw new Exception('Ошибка при записи ребра от аккаунта до заказа в базу данных');
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$amount = 0;
|
||||
|
||||
foreach (is_array($supply) ? $supply : [$supply => 1] as $supply_raw => $amount_raw) {
|
||||
// Перебор поставок
|
||||
|
||||
for ($i = 0; $i < $amount_raw; $i++) {
|
||||
// Создание рёбер соразмерно запросу (добавление нескольких продуктов в корзину)
|
||||
|
||||
// Запись ребра: ЗАКАЗ -> ПОСТАВКА
|
||||
if (!$supply_model = Supply::searchByCatn($supply_raw) or !OrderEdgeSupply::write($this->readId(), $supply_model->readId(), 'write')) {
|
||||
// Поставка не найдена или запись ребра не удалась
|
||||
|
||||
continue;
|
||||
} else {
|
||||
// Ребро создано (товар подключен к заказу)
|
||||
|
||||
// Постинкрементация счётчика добавленных товаров
|
||||
$amount++;
|
||||
|
||||
// Запись в журнал
|
||||
$this->journal('write', ['target' => $supply_model->readId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 deleteSupply(Supply|string|array $supply): int
|
||||
{
|
||||
// Инициализация
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск заказа
|
||||
*/
|
||||
public static function search(Account $account = null, string $type = 'current', int $limit = 1, int $page = 1, string $select = null): self|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$account or $account = yii::$app->user ?? throw new Exception('Не удалось инициализировать пользователя');
|
||||
|
||||
// Генерация сдвига по запрашиваемым данным (пагинация)
|
||||
$offset = $limit * ($page - 1);
|
||||
|
||||
if (strcasecmp($type, 'all') !== 0) {
|
||||
// Если не указан параметр поиска всех заказов
|
||||
|
||||
$where_type = [
|
||||
'account_edge_order.type' => $type
|
||||
];
|
||||
} else {
|
||||
$where_type = [];
|
||||
}
|
||||
|
||||
$return = self::searchByEdge(
|
||||
from: 'account',
|
||||
to: 'order',
|
||||
subquery_where: [
|
||||
[
|
||||
'account._id' => $account->id
|
||||
],
|
||||
$where_type
|
||||
],
|
||||
foreach: ['edge' => 'account_edge_order'],
|
||||
where: 'edge._to == order._id',
|
||||
limit: $limit,
|
||||
offset: $offset,
|
||||
sort: ['DESC'],
|
||||
select: $select,
|
||||
direction: 'INBOUND'
|
||||
);
|
||||
|
||||
return $limit === 1 ? $return[0] ?? null : $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск содержимого заказа
|
||||
*
|
||||
* @todo В будущем возможно заказ не только поставок реализовать
|
||||
*/
|
||||
public function content(int $limit = 1, int $page = 1): Supply|array|null
|
||||
{
|
||||
// Генерация сдвига по запрашиваемым данным (пагинация)
|
||||
$offset = $limit * ($page - 1);
|
||||
|
||||
// Поиск рёбер: ЗАКАЗ -> ПОСТАВКА
|
||||
$supplies = Supply::searchByEdge(
|
||||
from: 'order',
|
||||
to: 'supply',
|
||||
edge: 'order_edge_supply',
|
||||
subquery_where: [
|
||||
[
|
||||
'order._id' => $this->readId()
|
||||
]
|
||||
],
|
||||
foreach: ['edge' => 'order_edge_supply'],
|
||||
where: 'edge._to == supply._id',
|
||||
limit: $limit,
|
||||
offset: $offset,
|
||||
direction: 'INBOUND'
|
||||
);
|
||||
|
||||
// Инициализация реестра дубликатов
|
||||
$registry = [];
|
||||
|
||||
// Подсчёт и перестройка массива для очистки от дубликатов
|
||||
foreach ($supplies as $key => &$supply) {
|
||||
// Перебор поставок
|
||||
|
||||
if (in_array($supply->catn, $registry)) {
|
||||
// Если данная поставка найдена в реестре
|
||||
|
||||
// Удаление
|
||||
unset($supplies[$key]);
|
||||
|
||||
// Пропуск итерации
|
||||
continue;
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$amount = 0;
|
||||
|
||||
// Повторный перебор для поиска дубликатов
|
||||
foreach ($supplies as &$supply4check) {
|
||||
if ($supply == $supply4check) {
|
||||
// Найден дубликат
|
||||
|
||||
// Постинкрементация счётчика
|
||||
$amount++;
|
||||
|
||||
// Запись в реестр
|
||||
$registry[] = $supply4check->catn;
|
||||
}
|
||||
}
|
||||
|
||||
// Запись количества для заказа
|
||||
$supply->amnt = $amount;
|
||||
}
|
||||
|
||||
// Поиск стоимости для каждой поставки
|
||||
foreach ($supplies as $key => &$supply) {
|
||||
// Перебор поставок
|
||||
|
||||
// Чтение стоимости
|
||||
$cost = $supply->readCost();
|
||||
|
||||
if ($cost < 1) {
|
||||
// Если стоимость равна нулю (явная ошибка)
|
||||
|
||||
// Удаление из базы данных
|
||||
$this->deleteSupply($supply->readId());
|
||||
|
||||
// Удаление из списка
|
||||
unset($supplies[$key]);
|
||||
|
||||
// Пропуск итерации
|
||||
continue;
|
||||
}
|
||||
|
||||
// Запись цены
|
||||
$supply->cost = $cost['ЦенаЗаЕдиницу'] . ' ' . $cost['Валюта'];
|
||||
}
|
||||
|
||||
return $supplies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправка уведомления
|
||||
*/
|
||||
public static function notification(string $text, string|null $type = Notification::TYPE_NOTICE): Notification|array|null
|
||||
{
|
||||
// Отправка
|
||||
return Notification::_write($text, type: $type);
|
||||
}
|
||||
}
|
13
mirzaev/skillparts/system/models/OrderEdgeSupply.php
Normal file
13
mirzaev/skillparts/system/models/OrderEdgeSupply.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class OrderEdgeSupply extends Edge
|
||||
{
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'order_edge_supply';
|
||||
}
|
||||
}
|
@@ -1,60 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use yii;
|
||||
use yii\web\UploadedFile;
|
||||
use yii\imagine\Image;
|
||||
|
||||
use app\models\traits\SearchByEdge;
|
||||
|
||||
use moonland\phpexcel\Excel;
|
||||
|
||||
/**
|
||||
* Продукт (в ассортименте магазина)
|
||||
*
|
||||
* Представляет собой лот состоящий из предложений от поставщиков
|
||||
*
|
||||
* @see Supply Поставки для продуктов
|
||||
*/
|
||||
class Product extends Document
|
||||
{
|
||||
const SCENARIO_IMPORT = 'import';
|
||||
use SearchByEdge;
|
||||
|
||||
/**
|
||||
* Сценарий импорта .excel документа
|
||||
*
|
||||
* Использовать для обхода правил при загрузке файла
|
||||
*/
|
||||
const SCENARIO_IMPORT_EXCEL = 'import_excel';
|
||||
|
||||
/**
|
||||
* Сценарий импорта изображений
|
||||
*
|
||||
* Использовать для обхода правил при загрузке файла
|
||||
*/
|
||||
const SCENARIO_IMPORT_IMAGE = 'import_image';
|
||||
|
||||
/**
|
||||
* Сценарий записи товара
|
||||
*/
|
||||
const SCENARIO_WRITE = 'write';
|
||||
|
||||
public $file;
|
||||
public $group;
|
||||
/**
|
||||
* Файл .excel для импорта товаров
|
||||
*/
|
||||
public Excel|string|array|null $file_excel = null;
|
||||
|
||||
/**
|
||||
* Изображение для импорта
|
||||
*/
|
||||
public UploadedFile|string|array|null $file_image = null;
|
||||
|
||||
/**
|
||||
* Группа в которой состоит товар
|
||||
*/
|
||||
public ProductGroup|null $group = null;
|
||||
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'product';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'name',
|
||||
'ocid',
|
||||
'catn',
|
||||
'oemn'
|
||||
// 'data',
|
||||
// 'cost',
|
||||
// 'time'
|
||||
'name',
|
||||
'desc',
|
||||
'ocid',
|
||||
'imgs',
|
||||
'time',
|
||||
'oemn',
|
||||
'cost'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'catn' => 'Каталожный номер (catn)',
|
||||
'name' => 'Название (name)',
|
||||
'desc' => 'Описание (desc)',
|
||||
'ocid' => 'Идентификатор 1C (ocid)',
|
||||
'imgs' => 'Изображения (imgs)',
|
||||
'time' => 'Срок доставки (time)',
|
||||
'oemn' => 'OEM номера (oemn)',
|
||||
'cost' => 'Стоимость (cost)',
|
||||
'file_excel' => 'Документ (file_excel)',
|
||||
'file_image' => 'Изображение (file_image)',
|
||||
'group' => 'Группа (group)'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
[
|
||||
[
|
||||
['name', 'catn'],
|
||||
'catn',
|
||||
'required',
|
||||
'message' => 'Заполните поля: {attribute}',
|
||||
'on' => self::SCENARIO_WRITE,
|
||||
'except' => self::SCENARIO_IMPORT
|
||||
'except' => [self::SCENARIO_IMPORT_EXCEL, self::SCENARIO_IMPORT_IMAGE]
|
||||
],
|
||||
[
|
||||
'file',
|
||||
'catn',
|
||||
'string',
|
||||
'message' => '{attribute} должен быть строкой'
|
||||
],
|
||||
[
|
||||
[
|
||||
'oemn',
|
||||
'imgs'
|
||||
],
|
||||
'arrayValidator',
|
||||
'message' => '{attribute} должен быть массивом.'
|
||||
],
|
||||
[
|
||||
'file_excel',
|
||||
'required',
|
||||
'message' => 'Заполните поля: {attribute}',
|
||||
'on' => self::SCENARIO_IMPORT
|
||||
'on' => self::SCENARIO_IMPORT_EXCEL
|
||||
],
|
||||
['catn', 'string', 'message' => '{attribute} должен быть строкой'],
|
||||
// ['oemn', 'integer'], Нужна своя проверка на массив
|
||||
[
|
||||
'file',
|
||||
'file_excel',
|
||||
'file',
|
||||
'skipOnEmpty' => false,
|
||||
'extensions' => 'xlsx',
|
||||
@@ -63,46 +152,209 @@ class Product extends Document
|
||||
'maxSize' => 1024 * 1024 * 30,
|
||||
'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"',
|
||||
'message' => 'Проблема при чтении документа',
|
||||
'on' => self::SCENARIO_IMPORT
|
||||
'on' => self::SCENARIO_IMPORT_EXCEL
|
||||
],
|
||||
[
|
||||
'file_image',
|
||||
'required',
|
||||
'message' => 'Загрузите изображение',
|
||||
'on' => self::SCENARIO_IMPORT_IMAGE
|
||||
],
|
||||
[
|
||||
'file_image',
|
||||
'file',
|
||||
'skipOnEmpty' => false,
|
||||
'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
|
||||
'checkExtensionByMimeType' => true,
|
||||
'maxFiles' => 10,
|
||||
'maxSize' => 1024 * 1024 * 30,
|
||||
'wrongExtension' => 'Разрешены только изображения в формате: ".jpg", ".jpeg", ".png", ".gif", ".webp"',
|
||||
'message' => 'Проблема при загрузке изображения',
|
||||
'on' => self::SCENARIO_IMPORT_IMAGE
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function attributeLabels(): array
|
||||
/**
|
||||
* Инициализация продукта
|
||||
*
|
||||
* @param string $catn Артикул, каталожный номер
|
||||
*/
|
||||
public static function initEmpty(string $catn): self|array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'name' => 'Название (name)',
|
||||
'ocid' => 'Идентификатор 1C (ocid)',
|
||||
'catn' => 'Каталожный номер (catn)',
|
||||
'oemn' => 'OEM номера (oemn)',
|
||||
// 'data' => 'Данные товара (data)',
|
||||
// 'cost' => 'Цены (cost)',
|
||||
// 'time' => 'Сроки доставки (time)',
|
||||
'file' => 'Документ',
|
||||
'group' => 'Группа'
|
||||
]
|
||||
);
|
||||
$oemn = self::searchOemn($catn);
|
||||
|
||||
if (count($oemn) === 1) {
|
||||
// Передан только один артикул
|
||||
|
||||
if ($model = self::searchByCatn($catn)) {
|
||||
// Продукт уже существует
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
// Запись пустого продукта
|
||||
return self::writeEmpty($catn);
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$models = [];
|
||||
|
||||
foreach ($oemn as $catn) {
|
||||
// Перебор всех найденных артикулов
|
||||
|
||||
if ($model = self::searchByCatn($catn)) {
|
||||
// Продукт уже существует
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Запись
|
||||
if ($model = self::writeEmpty($catn)) {
|
||||
// Записано
|
||||
|
||||
// Запись в массив сохранённых моделей
|
||||
$models[] = $model;
|
||||
}
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
public function import()
|
||||
/**
|
||||
* Запись пустого продукта
|
||||
*/
|
||||
public static function writeEmpty(string $catn): ?self
|
||||
{
|
||||
// Инициализация массива данных
|
||||
// Инициализация
|
||||
$model = new self;
|
||||
|
||||
// Настройки
|
||||
$model->catn = $catn;
|
||||
|
||||
// Запись
|
||||
return $model->save() ? $model : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск OEM номеров
|
||||
*
|
||||
* @param string $oemn Необработанная строка с OEM-номерами
|
||||
* @param string $delimiters Разделители
|
||||
*
|
||||
* @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ
|
||||
*
|
||||
* @return array OEM-номера
|
||||
*/
|
||||
public static function searchOemn(string $oemn, string $delimiters = '\s\+\/,'): array
|
||||
{
|
||||
// Инициализация
|
||||
$catn = [];
|
||||
|
||||
// Конвертация
|
||||
preg_match_all("/[^$delimiters]+/", $oemn, $catn);
|
||||
|
||||
return $catn[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Импорт изображений
|
||||
*
|
||||
* @return int Количество сохранённых изображений
|
||||
*/
|
||||
public function importImages(): int
|
||||
{
|
||||
if ($this->validate()) {
|
||||
// Проверка пройдена
|
||||
|
||||
// Инициализация
|
||||
$amount = 0;
|
||||
|
||||
foreach ($this->file_image as $file) {
|
||||
// Перебор обрабатываемых изображений
|
||||
|
||||
if (!file_exists(YII_PATH_PUBLIC . $catalog = '/img/products/' . $this->_key)) {
|
||||
// Директория для изображений продукта не найдена
|
||||
|
||||
if (!mkdir(YII_PATH_PUBLIC . $catalog, 0775, true)) {
|
||||
// Не удалось записать директорию
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (!file_exists(YII_PATH_PUBLIC . $catalog_h150 = '/img/products/' . $this->_key . '/h150')) {
|
||||
// Директория для обложек изображений продукта не найдена
|
||||
|
||||
if (!mkdir(YII_PATH_PUBLIC . $catalog_h150, 0775, true)) {
|
||||
// Не удалось записать директорию
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// Запись на сервер
|
||||
$file->saveAs(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension . '.original');
|
||||
|
||||
// Конвертация изображения для сохранения полного изображения
|
||||
Image::resize(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension . '.original', 800, 800)
|
||||
->save(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension, ['quality' => 80]);
|
||||
|
||||
// Конвертация изображения для сохранения обложки (150px)
|
||||
Image::resize(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension, 150, 150)
|
||||
->save(YII_PATH_PUBLIC . $catalog_h150 . '/' . $file->baseName . '.' . $file->extension, ['quality' => 80]);
|
||||
|
||||
// Запись в базу данных
|
||||
$this->imgs = array_merge(
|
||||
$this->imgs ?? [],
|
||||
[[
|
||||
'orig' => $catalog . '/' . $file->baseName . '.' . $file->extension,
|
||||
'h150' => $catalog_h150 . '/' . $file->baseName . '.' . $file->extension
|
||||
]]
|
||||
);
|
||||
|
||||
|
||||
$this->scenario = self::SCENARIO_WRITE;
|
||||
|
||||
if ($this->save()) {
|
||||
// Изменения сохранены в базе данных
|
||||
|
||||
// Постинкрементация счётчика
|
||||
$amount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Импорт товаров
|
||||
*
|
||||
* На данный момент обрабатывает только импорт из
|
||||
* файлов с расширением .excel
|
||||
*/
|
||||
public function importExcel(): bool
|
||||
{
|
||||
// Инициализация
|
||||
$data = [];
|
||||
$amount = 0;
|
||||
|
||||
if ($this->validate()) {
|
||||
foreach ($this->file as $file) {
|
||||
foreach ($this->file_excel 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,
|
||||
@@ -110,71 +362,157 @@ 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_excel = '';
|
||||
|
||||
static::afterImportExcel($amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->addError('erros', 'Неизвестная ошибка');
|
||||
|
||||
static::afterImportExcel($amount);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function writeEdgeBetweenGroup(string $from, string $to): bool
|
||||
/**
|
||||
* Поиск по каталожному номеру
|
||||
*
|
||||
* Ищет продукт и возвращает его,
|
||||
* либо выполняет поиск через представление
|
||||
*
|
||||
* @todo Переделать нормально
|
||||
*/
|
||||
public static function searchByCatn(string $catn, int $limit = 1, array $select = []): static|array|null
|
||||
{
|
||||
// Инициализация
|
||||
$edge = new SupplyEdgeSupplyGroup();
|
||||
if ($limit <= 1) {
|
||||
return static::findOne(['catn' => $catn]);
|
||||
}
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $from;
|
||||
$edge->_to = $to;
|
||||
$query = self::find()
|
||||
->where(['catn' => $catn])
|
||||
->limit($limit)
|
||||
->select($select)
|
||||
->createCommand()
|
||||
->execute()
|
||||
->getAll();
|
||||
|
||||
// Запись
|
||||
return $edge->save();
|
||||
}
|
||||
foreach ($query as &$attribute) {
|
||||
// Приведение всех свойств в массив и очистка от лишних данных
|
||||
|
||||
private static function writeEdgeBetweenRequisite(string $from, string $to): bool
|
||||
{
|
||||
// Инициализация
|
||||
$edge = new SupplyEdgeRequisite();
|
||||
$attribute = $attribute->getAll();
|
||||
}
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $from;
|
||||
$edge->_to = $to;
|
||||
|
||||
// Запись
|
||||
return $edge->save();
|
||||
}
|
||||
|
||||
public static function readById(string $_key): ?Product
|
||||
{
|
||||
return self::findOne(['_key' => $_key]);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск по каталожному номеру
|
||||
* Поиск по каталожному номеру (через представления)
|
||||
*
|
||||
* Ищет продукт и возвращает его,
|
||||
* либо выполняет поиск через представление
|
||||
*
|
||||
* @todo Переделать нормально
|
||||
*/
|
||||
public static function readByCatn(string $catn): ?Product
|
||||
public static function searchByPartialCatn(string $catn, int $limit = 1, array $select = []): static|array|null
|
||||
{
|
||||
return self::findOne(['catn' => $catn]);
|
||||
$query = self::find()
|
||||
->for('product')
|
||||
->in('product_search')
|
||||
->search(['catn' => $catn])
|
||||
->limit($limit)
|
||||
->select($select)
|
||||
->createCommand()
|
||||
->execute()
|
||||
->getAll();
|
||||
|
||||
foreach ($query as &$attribute) {
|
||||
// Приведение всех свойств в массив и очистка от лишних данных
|
||||
|
||||
$attribute = $attribute->getAll();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class ProductEdgeProduct extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'product_edge_product';
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class ProductEdgeProductGroup extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'product_edge_product_group';
|
||||
}
|
||||
|
@@ -1,26 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use carono\exchange1c\interfaces\GroupInterface;
|
||||
|
||||
use Zenwalker\CommerceML\Model\Group;
|
||||
|
||||
/**
|
||||
* Группировка продуктов
|
||||
*/
|
||||
class ProductGroup extends Document implements GroupInterface
|
||||
{
|
||||
public static function collectionName()
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'product_group';
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
['name', 'onec_id', 'onec_prnt_id']
|
||||
[
|
||||
'name'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'name' => 'Название (name)'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
@@ -34,34 +64,18 @@ class ProductGroup extends Document implements GroupInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function attributeLabels()
|
||||
/**
|
||||
* Запись члена группы
|
||||
*/
|
||||
public function writeMember(Product $member): ProductEdgeProductGroup
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'name' => 'Название (name)',
|
||||
'onec_id' => 'Название 1C (onec_id)',
|
||||
'onec_prnt_id' => 'Название родителя 1C (onec_prnt_id)',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function writeMember(Document $member, string $group)
|
||||
{
|
||||
if (isset($member->_key)) {
|
||||
return static::writeEdgeBetweenMember($member->collectionName() . '/' . $member->_key, $this->collectionName() . '/' . $group);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ProductEdgeProductGroup::write($member->readId(), $this->readId(), 'member');
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание дерева групп
|
||||
* в параметр передаётся массив всех групп (import.xml > Классификатор > Группы)
|
||||
* $groups[0]->parent - родительская группа
|
||||
* $groups[0]->children - дочерние группы
|
||||
* Запись рёбер групп
|
||||
*
|
||||
* @param Group[] $groups
|
||||
* Создание взаимоотношений между группами по типу древовидной системы
|
||||
*/
|
||||
public static function createTree1c($groups): Document|null
|
||||
{
|
||||
@@ -88,53 +102,43 @@ class ProductGroup extends Document implements GroupInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаём группу по модели группы CommerceML
|
||||
* проверяем все дерево родителей группы, если родителя нет в базе - создаём
|
||||
* Запись группы
|
||||
*
|
||||
* @param Group $group
|
||||
* Создаём группу по модели группы CommerceML
|
||||
* проверяем все дерево родителей группы,
|
||||
* если родителя нет в базе - создаём
|
||||
*/
|
||||
public static function createByML(Group $group): static|array|null
|
||||
{
|
||||
if (!$model = static::readByOnecId($group->id)) {
|
||||
// Группа не найдена
|
||||
// if (!$model = static::readByOnecId($group->id)) {
|
||||
// // Группа не найдена
|
||||
|
||||
// Инициализация
|
||||
$model = new static;
|
||||
// // Инициализация
|
||||
// $model = new static;
|
||||
|
||||
$model->onec_id = $group->id;
|
||||
}
|
||||
// $model->onec_id = $group->id;
|
||||
// }
|
||||
|
||||
$model->name = $group->name;
|
||||
// $model->name = $group->name;
|
||||
|
||||
if ($parent = $group->getParent()) {
|
||||
// Найден родитель
|
||||
// if ($parent = $group->getParent()) {
|
||||
// // Найден родитель
|
||||
|
||||
// Инициализация (рекурсия)
|
||||
$parentModel = static::createByML($parent);
|
||||
// // Инициализация (рекурсия)
|
||||
// $parentModel = static::createByML($parent);
|
||||
|
||||
$model->onec_prnt_id = $parentModel->id;
|
||||
// $model->onec_prnt_id = $parentModel->id;
|
||||
|
||||
unset($parentModel);
|
||||
} else {
|
||||
$model->onec_prnt_id = null;
|
||||
}
|
||||
// unset($parentModel);
|
||||
// } else {
|
||||
// $model->onec_prnt_id = null;
|
||||
// }
|
||||
|
||||
$model->save();
|
||||
// $model->save();
|
||||
|
||||
return $model;
|
||||
}
|
||||
// return $model;
|
||||
|
||||
private static function writeEdgeBetweenMember(string $from, string $to): bool
|
||||
{
|
||||
// Инициализация
|
||||
$edge = new ProductEdgeProductGroup();
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $from;
|
||||
$edge->_to = $to;
|
||||
|
||||
// Запись
|
||||
return $edge->save();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function writeEdgeBetweenGroup(string $from, string $to): bool
|
||||
@@ -150,11 +154,6 @@ class ProductGroup extends Document implements GroupInterface
|
||||
return $edge->save();
|
||||
}
|
||||
|
||||
public static function readAll()
|
||||
{
|
||||
return static::find()->all();
|
||||
}
|
||||
|
||||
public static function readByName(string $name)
|
||||
{
|
||||
return static::findOne(['name' => $name]);
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class ProductGroupEdgeProductGroup extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'product_group_edge_product_group';
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use carono\exchange1c\interfaces\DocumentInterface;
|
||||
@@ -14,7 +16,7 @@ class Purchase extends Document implements DocumentInterface
|
||||
/**
|
||||
* @return DocumentInterface[]
|
||||
*/
|
||||
public static function findDocuments1c(): ?self
|
||||
public static function findDocuments1c(): ?array
|
||||
{
|
||||
return self::find()->andWhere(['status_id' => 2])->all();
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class PurchaseEdgeSupply extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'purchase_edge_supply';
|
||||
}
|
||||
|
@@ -1,15 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class Requisite extends Document
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'requisite';
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
@@ -17,7 +19,7 @@ class Requisite extends Document
|
||||
);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
@@ -31,7 +33,7 @@ class Requisite extends Document
|
||||
);
|
||||
}
|
||||
|
||||
public function attributeLabels()
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
|
109
mirzaev/skillparts/system/models/Search.php
Normal file
109
mirzaev/skillparts/system/models/Search.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use yii;
|
||||
use yii\web\IdentityInterface;
|
||||
|
||||
use app\models\traits\SearchByEdge;
|
||||
|
||||
/**
|
||||
* Поиск
|
||||
*
|
||||
* @see Product Поиск по товарам
|
||||
*/
|
||||
class Search extends Document
|
||||
{
|
||||
use SearchByEdge;
|
||||
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'search';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'text',
|
||||
'ipv4',
|
||||
'head'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'text' => 'Текст',
|
||||
'ipv4' => 'IPv4',
|
||||
'head' => 'Заголовки'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
[
|
||||
[
|
||||
'text',
|
||||
'required'
|
||||
],
|
||||
[
|
||||
'ipv4',
|
||||
'default',
|
||||
'value' => yii::$app->request->userIP
|
||||
],
|
||||
[
|
||||
'head',
|
||||
'default',
|
||||
'value' => yii::$app->request->getHeaders()
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись
|
||||
*
|
||||
* @param string $text Текст запроса
|
||||
* @param Account|null $account Пользователь совершивший запрос
|
||||
*/
|
||||
public static function write(string $text, Account|null $account = null): ?self
|
||||
{
|
||||
// Инициализация
|
||||
$vertex = new self;
|
||||
isset($account) && yii::$app->user->isGuest ?: $account = yii::$app->user->identity;
|
||||
|
||||
// Настройки
|
||||
$vertex->text = $text;
|
||||
|
||||
if ($vertex->save()) {
|
||||
// Поиск записан
|
||||
|
||||
// Запись ребра: АККАУНТ -> ПОИСК
|
||||
return $account && AccountEdgeSearch::writeSafe($account->id, $vertex->readId(), 'request') ? $vertex : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
73
mirzaev/skillparts/system/models/Settings.php
Normal file
73
mirzaev/skillparts/system/models/Settings.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
/**
|
||||
* Настройки
|
||||
*/
|
||||
class Settings extends Document
|
||||
{
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributes(),
|
||||
[
|
||||
'search_period',
|
||||
'search_connect_keep'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'search_period' => 'Поисковый период',
|
||||
'search_connect_keep' => 'Режим удержания'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Правила
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
[
|
||||
[
|
||||
[
|
||||
'search_period'
|
||||
],
|
||||
'integer',
|
||||
'message' => '{attribute} должен хранить цифровое значение'
|
||||
],
|
||||
[
|
||||
[
|
||||
'search_connect_keep'
|
||||
],
|
||||
'string',
|
||||
'message' => '{attribute} должен хранить строковый тип'
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
13
mirzaev/skillparts/system/models/SettingsEdgeSettings.php
Normal file
13
mirzaev/skillparts/system/models/SettingsEdgeSettings.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class SettingsEdgeSettings extends Edge
|
||||
{
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'settings_edge_settings';
|
||||
}
|
||||
}
|
@@ -1,24 +1,52 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
// class Supply extends Product implements OfferInterface
|
||||
use carono\exchange1c\interfaces\ProductInterface;
|
||||
use carono\exchange1c\controllers\ApiController;
|
||||
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Поставка (выгрузка товаров от поставщиков)
|
||||
*
|
||||
* Представляет собой предложения от поставщиков которые добавляются
|
||||
* в универсальные лоты товаров в асспортименте магазина
|
||||
*
|
||||
* @see Product Продукт (туда добавляются поставки)
|
||||
*/
|
||||
class Supply extends Product implements ProductInterface
|
||||
{
|
||||
use Xml2Array;
|
||||
|
||||
/**
|
||||
* Количество
|
||||
*
|
||||
* Используется при выводе в корзине
|
||||
*/
|
||||
public int $amnt = 0;
|
||||
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply';
|
||||
}
|
||||
|
||||
/**
|
||||
* Свойства
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return array_merge(
|
||||
@@ -29,34 +57,50 @@ class Supply extends Product implements ProductInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метки свойств
|
||||
*/
|
||||
public function attributeLabels(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::attributeLabels(),
|
||||
[
|
||||
'onec' => 'Данные 1C'
|
||||
'onec' => 'Данные 1С'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* После сохранения
|
||||
*/
|
||||
public function afterSave($data, $vars): void
|
||||
{
|
||||
// Запись ребра: АККАУНТ -> ПОСТАВКА
|
||||
(new AccountEdgeSupply)->write(Yii::$app->user->identity->readId(), $this->readId(), 'import');
|
||||
if (AccountEdgeSupply::searchByVertex(yii::$app->user->id, $this->readId())) {
|
||||
// Ребро: "АККАУНТ -> ПОСТАВКА" уже существует
|
||||
|
||||
} else {
|
||||
// Ребра не существует
|
||||
|
||||
// Запись ребра: АККАУНТ -> ПОСТАВКА
|
||||
(new AccountEdgeSupply)->write(yii::$app->user->id, $this->readId(), 'import');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись реквизитов из 1С
|
||||
*/
|
||||
public function setRequisite1c($name, $value): mixed
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка группы, где находится продукт
|
||||
* Запись группы из 1С
|
||||
*/
|
||||
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);
|
||||
// }
|
||||
@@ -64,23 +108,166 @@ class Supply extends Product implements ProductInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function createProperties1c($properties): mixed
|
||||
/**
|
||||
* Поиск через связь с аккаунтом
|
||||
*
|
||||
* @param string|null $id Идентификатор пользователя
|
||||
* @param string|array|null $select Запрашиваемые значения
|
||||
*/
|
||||
public static function searchByAccount(string|null $id = null, string|array|null $select = null, int|null $limit = 10): array
|
||||
{
|
||||
return true;
|
||||
isset($id) ?: $id = yii::$app->user->id ?? throw new Exception('Не найден идентификатор');
|
||||
|
||||
return self::searchByEdge(
|
||||
from: 'account',
|
||||
to: 'supply',
|
||||
subquery_where: [
|
||||
[
|
||||
'account._id' => $id
|
||||
]
|
||||
],
|
||||
where: 'supply._id == account_edge_supply[0]._to AND supply.onec["ЗначенияСвойств"] != null',
|
||||
select: $select,
|
||||
limit: $limit
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись данных свойств по UUID 1C
|
||||
*
|
||||
* Ищет записанные свойства из 1C по их идентификатору и добавляет к ним
|
||||
* недостающие данные. Это костыль оставшийся от реляционных баз данных
|
||||
*
|
||||
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
|
||||
*/
|
||||
public static function createProperties1c($properties, Account|null $account = null): void
|
||||
{
|
||||
// Инициализация
|
||||
$account ?? $account = yii::$app->user->identity;
|
||||
$models = self::searchByAccount($account->readId());
|
||||
$properties = self::xml2array($properties->xml);
|
||||
|
||||
$account->on(ApiController::EVENT_AFTER_OFFER_SYNC, self::afterImport());
|
||||
|
||||
foreach ($models as $model) {
|
||||
// Перебор записей
|
||||
|
||||
// Инициализация
|
||||
$changes = false;
|
||||
$transit = $model->onec;
|
||||
|
||||
foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) {
|
||||
// Перебор аттрибутов
|
||||
|
||||
foreach ($properties as $property) {
|
||||
// Перебор свойств
|
||||
|
||||
if (is_array($attribute_value) && is_array($property) && $attribute_value['Ид'] === $property['Ид']) {
|
||||
// Совпадение идентификаторов
|
||||
|
||||
// Объединение данных
|
||||
$transit['ЗначенияСвойств'][$attribute_name] = array_merge($attribute_value, $property, $transit['ЗначенияСвойств'][$attribute_name]);
|
||||
|
||||
// Запись индикатора наличия изменений
|
||||
$changes = true;
|
||||
} else {
|
||||
// Объединение данных
|
||||
$transit['ЗначенияСвойств'][$attribute_name] = $property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes) {
|
||||
// Если указано, что записаны изменения
|
||||
|
||||
// Настройка ($transit нужен из-за особенностей __set())
|
||||
$model->onec = $transit;
|
||||
|
||||
foreach ($model->onec['ЗначенияСвойств'] as $property) {
|
||||
// Перебор всех свойств
|
||||
|
||||
if (is_array($property)) {
|
||||
// if ($property['Ид'] === $account->opts['import_sections_oem']) {
|
||||
// // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
|
||||
|
||||
// Настройка
|
||||
$model->catn = $property['Значение'];
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Запись
|
||||
$model->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись параметров из 1С
|
||||
*/
|
||||
public function setProperty1c($property): mixed
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addImage1c($path, $caption): mixed
|
||||
/**
|
||||
* Запись изображений из 1С
|
||||
*
|
||||
* @todo Добавить параметры в админ-панель
|
||||
* Запретить доступ к изображениям
|
||||
*/
|
||||
public function addImage1c($path, $caption): bool
|
||||
{
|
||||
return true;
|
||||
// Инициализация
|
||||
$i = 0;
|
||||
|
||||
if (!file_exists(YII_PATH_PUBLIC . $catalog = '/img/supplies/' . $this->_key)) {
|
||||
// Директория для изображений продукта не найдена
|
||||
|
||||
if (!mkdir(YII_PATH_PUBLIC . $catalog, 0775, true)) {
|
||||
// не удалось записать директорию
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
foreach ($this->imgs ?? [] as $image) {
|
||||
// Перебор имеющихся изображений
|
||||
|
||||
if ($path === $image['sorc']) {
|
||||
// Изображение уже записано на сервер
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$urn = basename($path);
|
||||
|
||||
// Запись
|
||||
copy($path, $path_local = YII_PATH_PUBLIC . $catalog . '/' . $urn);
|
||||
|
||||
// Запись свойства
|
||||
$this->imgs = array_merge(
|
||||
$this->imgs ?? [],
|
||||
[
|
||||
[
|
||||
'desc' => $caption,
|
||||
'path' => $path_local,
|
||||
'sorc' => $path
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
// Отправка в базу данных
|
||||
return $this->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись ребра (предложения от поставки к продукту)
|
||||
* Запись ребра (предложения от поставок к продуктам) из 1С
|
||||
*
|
||||
* @todo Разобраться зачем нужно возвращать SupplyEdgeProduct
|
||||
* Вернуть создание карточек, но только по условиям (загрузка от админа, например)
|
||||
*/
|
||||
public function getOffer1c($offer): SupplyEdgeProduct
|
||||
{
|
||||
@@ -89,99 +276,173 @@ class Supply extends Product implements ProductInterface
|
||||
|
||||
// Разработчику библеотеки надо дать по жопе
|
||||
return new SupplyEdgeProduct;
|
||||
} else if (!$catn = Product::readByCatn($this->catn)) {
|
||||
// Продукт не найден
|
||||
|
||||
if (!$this->initProduct()) {
|
||||
// Не удалось инициализировать продукт
|
||||
|
||||
// Разработчику библеотеки надо дать по жопе
|
||||
return new SupplyEdgeProduct;
|
||||
}
|
||||
}
|
||||
|
||||
$catn = Product::readByCatn($this->catn);
|
||||
// Создание продукта (временно заблокировано)
|
||||
|
||||
// Запись ребра: ПОСТАВКА -> ПРОДУКТ
|
||||
return (new SupplyEdgeProduct)->write(
|
||||
$this->readId(),
|
||||
$catn->readId(),
|
||||
'sell',
|
||||
[
|
||||
'onec' => self::xml2array($offer->xml)
|
||||
]
|
||||
);
|
||||
// // Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑
|
||||
// $product = Product::initEmpty($this->catn);
|
||||
|
||||
// if (!is_array($product)) {
|
||||
// // Создался только один товар и вернулся в виде модели
|
||||
|
||||
// $product = [$product];
|
||||
// }
|
||||
|
||||
// if (is_array($this->oemn)) {
|
||||
// // Значение OEM было инициализировано
|
||||
|
||||
// foreach ($this->oemn as $oem) {
|
||||
// // Перебор артикулов из массива ОЕМ-номеров
|
||||
|
||||
// // Инициализация и запись
|
||||
// $product[] = Product::initEmpty($oem);
|
||||
// }
|
||||
// }
|
||||
|
||||
// foreach ($product as $product) {
|
||||
// // Перебор всех инициализированных продуктов
|
||||
|
||||
// if ($this->catn !== $product->catn) {
|
||||
// // Каталожные номера не соответствуют друг другу
|
||||
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Код ниже скорее всего устарел
|
||||
|
||||
// if (SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId())) {
|
||||
// // Ребро уже существует
|
||||
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Запись ребра: ПОСТАВКА -> ПРОДУКТ
|
||||
// $return = (new SupplyEdgeProduct)->write(
|
||||
// $this->readId(),
|
||||
// $product->readId(),
|
||||
// 'connect',
|
||||
// [
|
||||
// 'onec' => self::xml2array($offer->xml)
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
|
||||
// Возвращает последнее сохранённое ребро
|
||||
// Надо будет с этим разобраться
|
||||
return $return ?? new SupplyEdgeProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать продукт
|
||||
* Запись продукта из 1С (поставка)
|
||||
*
|
||||
* @see Supply
|
||||
*
|
||||
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
|
||||
* Разобраться и создать возможность загрузки от лица другого аккаунта
|
||||
*/
|
||||
public static function createModel1c($product): self
|
||||
public static function createModel1c($product): ?self
|
||||
{
|
||||
// Инициализация
|
||||
$model = self::readByOnecId($id = (string) $product->Ид) ?? new self;
|
||||
$model = self::searchByOcid($id = (string) $product->Ид) ?? new self;
|
||||
$account ?? $account = yii::$app->user->identity;
|
||||
|
||||
// Настройки
|
||||
// Настройка
|
||||
$model->ocid = $id ?? null;
|
||||
$model->catn = (string) $product->Артикул;
|
||||
$model->oemn = null;
|
||||
$model->desc = (string) $product->Описание;
|
||||
$model->onec = self::xml2array($product->xml);
|
||||
|
||||
// Запись
|
||||
$model->save();
|
||||
if (isset($model->onec['ЗначенияСвойств'])) {
|
||||
// Свойства инициализированы
|
||||
|
||||
return $model;
|
||||
}
|
||||
foreach ($model->onec['ЗначенияСвойств'] as $property) {
|
||||
// Перебор всех свойств
|
||||
|
||||
protected function initProduct(): bool
|
||||
{
|
||||
// Надо не забыть сделать выборку полей и ручное подключение
|
||||
if (is_array($property)) {
|
||||
if (!empty($account->opts['import_sections_oem']) && $property['Ид'] === $account->opts['import_sections_oem']) {
|
||||
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
|
||||
|
||||
if (empty($this->catn)) {
|
||||
// Не передан каталожный номер
|
||||
|
||||
return false;
|
||||
} else if (Product::readByCatn($this->catn)) {
|
||||
// Продукт уже был инициализирован
|
||||
|
||||
return true;
|
||||
// Настройка
|
||||
$model->oemn = array_merge(self::searchOemn($property['Значение']), self::searchOemn((string) $product->Артикул));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
$product = new Product();
|
||||
|
||||
// Настройки
|
||||
$product->catn = $this->catn;
|
||||
|
||||
// Запись
|
||||
return $product->save();
|
||||
if ($model->save()) {
|
||||
// Поставка успешно сохранена
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function setPrice1c($price): void
|
||||
/**
|
||||
* Запись цены из 1С
|
||||
*/
|
||||
public function setPrice1c($price): mixed
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись данных на случай ошибки при экспорте из 1С
|
||||
*/
|
||||
public function setRaw1cData($cml, $object): bool
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function readByOnecId(string $ocid): ?Supply
|
||||
/**
|
||||
* Поиск по идентификатору из 1С
|
||||
*
|
||||
* @param string $ocid Идентификатор из 1С
|
||||
*
|
||||
* @return Supply|null
|
||||
*/
|
||||
public static function searchByOcid(string $ocid): ?Supply
|
||||
{
|
||||
return self::findOne([self::getIdFieldName1c() => $ocid]);
|
||||
return static::findOne([static::getIdFieldName1c() => $ocid]);
|
||||
}
|
||||
|
||||
public function getGroup1c(): SupplyGroup
|
||||
/**
|
||||
* Чтение группы из 1С
|
||||
*/
|
||||
public function getGroup1c(): ?SupplyGroup
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Название поля в котором хранится ID из 1C
|
||||
* Чтение названия поля в котором хранится идентификатор из 1С
|
||||
*/
|
||||
public static function getIdFieldName1c(): string
|
||||
{
|
||||
return 'ocid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск по OEM-номерам
|
||||
*
|
||||
* @todo Реализовать с помощью LIKE
|
||||
*/
|
||||
public static function searchByOemn(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать стоимость
|
||||
*/
|
||||
public function readCost(Product $product = null): array
|
||||
{
|
||||
if (isset($product)) {
|
||||
return SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
|
||||
}
|
||||
|
||||
return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
use app\models\traits\Xml2Array;
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class SupplyEdgeRequisite extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply_edge_requisite';
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class SupplyEdgeSupply extends ProductEdgeProduct
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply_edge_supply';
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class SupplyEdgeSupplyGroup extends ProductEdgeProductGroup
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply_edge_supply_group';
|
||||
}
|
||||
|
@@ -1,37 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
/**
|
||||
* Группировка поставок
|
||||
*/
|
||||
class SupplyGroup extends ProductGroup
|
||||
{
|
||||
public static function collectionName()
|
||||
/**
|
||||
* Имя коллекции
|
||||
*/
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply_group';
|
||||
}
|
||||
|
||||
protected static function writeEdgeBetweenMember(string $from, string $to): bool
|
||||
{
|
||||
// Инициализация
|
||||
$edge = new SupplyEdgeSupplyGroup();
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $from;
|
||||
$edge->_to = $to;
|
||||
|
||||
// Запись
|
||||
return $edge->save();
|
||||
}
|
||||
|
||||
protected static function writeEdgeBetweenGroup(string $from, string $to): bool
|
||||
{
|
||||
// Инициализация
|
||||
$edge = new SupplyGroupEdgeSupplyGroup();
|
||||
|
||||
// Настройка
|
||||
$edge->_from = $from;
|
||||
$edge->_to = $to;
|
||||
|
||||
// Запись
|
||||
return $edge->save();
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models;
|
||||
|
||||
class SupplyGroupEdgeSupplyGroup extends Edge
|
||||
{
|
||||
public static function collectionName()
|
||||
public static function collectionName(): string
|
||||
{
|
||||
return 'supply_group_edge_supply_group';
|
||||
}
|
||||
|
84
mirzaev/skillparts/system/models/traits/SearchByEdge.php
Normal file
84
mirzaev/skillparts/system/models/traits/SearchByEdge.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models\traits;
|
||||
|
||||
use yii;
|
||||
|
||||
use exception;
|
||||
|
||||
trait SearchByEdge
|
||||
{
|
||||
/**
|
||||
* Поиск через связи рёбрами с аккаунтом
|
||||
*
|
||||
* @param string $id Идентификатор пользователя
|
||||
* @param int $limit Количество
|
||||
* @param int $offset Сдвиг
|
||||
* @param string $sort Сортировка
|
||||
*/
|
||||
public static function searchByEdge(
|
||||
string $from,
|
||||
string $to,
|
||||
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|null $let = [],
|
||||
string|array $select = null,
|
||||
callable|null $handle = null,
|
||||
array $params = []
|
||||
): mixed {
|
||||
$subquery = static::find()
|
||||
->params($params)
|
||||
->for([$from, $edge ?? $from . '_edge_' . $to])
|
||||
->traversal($to, $direction)
|
||||
->in($edge ?? $from . '_edge_' . $to)
|
||||
->where($subquery_where);
|
||||
|
||||
$subquery = $subquery->select($edge ?? $from . '_edge_' . $to)
|
||||
->createCommand();
|
||||
|
||||
$query = static::find()
|
||||
->params($subquery->getBindVars())
|
||||
->let($edge ?? $from . '_edge_' . $to, '(' . (string) $subquery . ')')
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->orderBy($sort);
|
||||
|
||||
if (!empty($let)) {
|
||||
// Переданы дополнительные условия фильтрации
|
||||
|
||||
$query->let(...$let);
|
||||
}
|
||||
|
||||
// Запрос
|
||||
$request = $query
|
||||
->foreach($foreach)
|
||||
->where($where)
|
||||
->select($select ?? $to);
|
||||
|
||||
if (isset($handle)) {
|
||||
// Передана функция для постобработки
|
||||
|
||||
return $handle($request);
|
||||
} else if (isset($select)) {
|
||||
$response = $request->createCommand()->execute()->getAll();
|
||||
|
||||
foreach ($response as &$attribute) {
|
||||
// Приведение всех свойств в массив и очистка от лишних данных
|
||||
|
||||
$attribute = $attribute->getAll();
|
||||
}
|
||||
|
||||
return $response;
|
||||
} else {
|
||||
return $request->all();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\models\traits;
|
||||
|
||||
trait Xml2Array {
|
||||
|
@@ -1,13 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
|
||||
use app\models\AccountForm;
|
||||
|
||||
/**
|
||||
* @todo Восстановить сохранение сессии
|
||||
*/
|
||||
?>
|
||||
|
||||
<div class="container d-flex flex-column h-100">
|
||||
<div class="row my-auto">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="mx-auto">
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
@@ -26,7 +32,7 @@ use app\models\AccountForm;
|
||||
$model = $model ?? new AccountForm;
|
||||
?>
|
||||
|
||||
<?= $form->field($model, 'mail', ['enableLabel' => false])->textInput(['autofocus' => true, 'placeholder' => $model->getAttributeLabel('mail')]) ?>
|
||||
<?= $form->field($model, 'mail', ['enableLabel' => false, 'options' => ['class' => 'mb-2']])->textInput(['autofocus' => true, 'placeholder' => $model->getAttributeLabel('mail')]) ?>
|
||||
<?= $form->field($model, 'pswd', ['enableLabel' => false])->passwordInput(['placeholder' => $model->getAttributeLabel('pswd')]) ?>
|
||||
|
||||
<div class="d-flex mb-2 mt-3">
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
|
||||
if (!yii::$app->user->isGuest) {
|
||||
// $popup = yii::$app->controller->renderPartial('/notification/panel');
|
||||
|
||||
// echo <<<HTML
|
||||
// <a id="notification_button" class="text-dark d-flex h-100 mr-2" title="Уведомления" role="button" data-toggle="dropdown" data-offset="-100%p + 100%" onclick="return notification_stream();">
|
||||
// <i class="fas fa-bell my-auto mx-2"></i>
|
||||
// </a>
|
||||
// <div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
|
||||
// $popup
|
||||
// </div>
|
||||
// HTML;
|
||||
}
|
||||
?>
|
||||
<?=$notifications_button?>
|
||||
<?=$notifications_panel?>
|
||||
<a class="text-dark my-auto mr-2" title="Корзина" href="/cart" role="button" onclick="return page_cart();"><i class="fas fa-shopping-cart mx-2"></i></a>
|
||||
<a class="text-dark my-auto mr-2" title="Заказы" href="/orders" role="button" onclick="return page_orders();"><i class="fas fa-list mx-2"></i></a>
|
||||
<div class="btn-group my-auto">
|
||||
<a class="btn m-0 px-0 text-dark button_clean" title="Личный кабинет" href="/profile" role="button" onclick="return page_profile();">Личный кабинет</a>
|
||||
<button id="profile_button" class="btn pr-0 dropdown-toggle dropdown-toggle-split button_clean" type="button" data-toggle="dropdown" onmouseover="$('#profile_button').dropdown('show')"></button>
|
||||
<div id="profile_button_panel" class="dropdown-menu dropdown-menu-right py-1" aria-labelledby="profile_button" onmouseout="$('#profile_button').dropdown('show')">
|
||||
<a class="dropdown-item button_white text-dark" onclick="deauthentication()">Выход (<?= yii::$app->user->identity->mail ?>)</a>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
|
||||
?>
|
||||
<a class="text-dark my-auto mr-2" title="Корзина" href="/cart" role="button" onclick="return page_cart();"><i class="fas fa-shopping-cart mx-2"></i></a>
|
||||
<a class="text-dark my-auto mr-2" title="Заказы" href="/orders" role="button" onclick="return page_orders();"><i class="fas fa-list mx-2"></i></a>
|
||||
<div class="btn-group my-auto">
|
||||
<a class="btn m-0 px-0 text-dark button_clean" title="Личный кабинет" href="/profile" role="button" onclick="return page_profile();">Личный кабинет</a>
|
||||
<button id="profile_button" class="btn pr-0 dropdown-toggle dropdown-toggle-split button_clean" type="button" data-toggle="dropdown" onmouseover="$('#profile_button').dropdown('show')"></button>
|
||||
<div id="profile_button_panel" class="dropdown-menu dropdown-menu-long dropdown-menu-right p-3" aria-labelledby="profile_button" onmouseout="$('#profile_button').dropdown('show')">
|
||||
<h5 class="mb-3 text-center">Аутентификация</h5>
|
||||
<?= yii::$app->controller->renderPartial('/account/index', compact('model')) ?>
|
||||
<!-- <a class="dropdown-item-text text-center px-0 py-2" href="#"><small>Восстановление пароля</small></a> -->
|
||||
</div>
|
||||
</div>
|
83
mirzaev/skillparts/system/views/cart/index.php
Normal file
83
mirzaev/skillparts/system/views/cart/index.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<link href="/css/pages/cart.css" rel="stylesheet">
|
||||
|
||||
<div id="page_cart" class="container mb-auto py-3">
|
||||
<article class="py-3 px-4 rounded">
|
||||
<h4 class="ml-4 mt-2 mb-4"><i class="fas fa-shopping-cart mr-2"></i>Корзина</h4>
|
||||
<div class="col mb-4 list rounded overflow-hidden">
|
||||
<div class="row py-2">
|
||||
<div class="pl-3 mr-1">
|
||||
<input id="checkbox_cart_all" type="checkbox" onchange="return cart_list_checkbox(this);"/>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<span>Артикул</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span>Описание</span>
|
||||
</div>
|
||||
<div class="col-1 ml-auto px-0 text-center">
|
||||
<span>Количество</span>
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<span>Доставка</span>
|
||||
</div>
|
||||
<div class="col-2 mr-3 text-right">
|
||||
<span>Стоимость</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if (isset($supplies) && !empty($supplies)) {
|
||||
foreach ($supplies as $supply) {
|
||||
echo <<<HTML
|
||||
<div class="row py-2 cart_list_target">
|
||||
<div class="pl-3 mr-1">
|
||||
<input id="cart_list_checkbox_$supply->catn" type="checkbox" onchange="return cart_list_checkbox(this);"/>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
$supply->catn
|
||||
</div>
|
||||
<div class="col-4">
|
||||
$supply->desc
|
||||
</div>
|
||||
<div class="col-1 ml-auto">
|
||||
<input id="cart_list_amnt_$supply->catn" class="form-control text-center" type="text" value="$supply->amnt" onchange="return cart_list_amount_update('$supply->catn', this)" aria-invalid="false">
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
$supply->time
|
||||
</div>
|
||||
<div class="col-2 mr-3 text-right">
|
||||
$supply->cost
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<div class="row py-2 cart_list_target">
|
||||
<div class="mx-auto py-2">
|
||||
Корзина пуста
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="row mb-2 mx-0">
|
||||
<select id="cart_list_action" class="form-control mr-3 button_clean w-auto" name="CartListAction">
|
||||
<option value="none" hidden>Действие с выбранными</option>
|
||||
<option value="delete" onclick="return cart_list_delete();">Удалить</option>
|
||||
</select>
|
||||
<a class="mr-3 btn button_red button_clean" title="Очистить корзину" href="/cart" role="button" onclick="return cart_delete();">
|
||||
Очистить
|
||||
</a>
|
||||
<p class="ml-auto mr-3 cart_field_cost">
|
||||
<span id="cart_cost">0</span>
|
||||
руб
|
||||
</p>
|
||||
<a class="btn button_clean button_blue" title="Оформить заказ" href="/pay" role="button" onclick="return cart_pay();">
|
||||
Оформить заказ
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<script src="/js/cart.js" defer></script>
|
@@ -1,45 +1,46 @@
|
||||
<?php
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
declare(strict_types=1);
|
||||
|
||||
$this->title = 'SkillParts';
|
||||
|
||||
?>
|
||||
|
||||
<link href="/css/ticker.css" rel="stylesheet">
|
||||
|
||||
<div id="page_index">
|
||||
<div class="info_panel mb-4">
|
||||
<div id="page_index" class="mb-auto">
|
||||
<section class="info_panel mb-4">
|
||||
<div class="container h-100 d-flex flex-column justify-content-center">
|
||||
<p class="mb-4 gilroy">Проблема с подбором запчастей?</p>
|
||||
<p class="d-flex">
|
||||
<span class="p-2 px-3 button_call_icon"><i class="fas fa-phone-alt text-white"></i></span>
|
||||
<h1 class="mb-4 ml-0 gilroy">Проблема с подбором запчастей?</h1>
|
||||
<p class="ml-0 d-flex">
|
||||
<span class="p-2 px-3 button_call_icon d-flex"><i class="fas fa-phone-alt my-auto text-white"></i></span>
|
||||
<a class="btn text-white button_clean button_blue button_call" href="/call" role="button">Связаться с менеджером</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="h-100 d-flex flex-column justify-content-end">
|
||||
<img class="img-fluid" src="/img/photos/compressed/963K_cutted.webp" alt="Связаться с менеджером">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="h-100 d-flex ticker">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/cummins.png" alt="Cummins">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/iveco.png" alt="Iveco">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/komatsu.png" alt="Komatsu">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/case.png" alt="Case">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/isuzu.png" alt="Isuzu">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/new_holland.svg" alt="New Holland">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/perkins.png" alt="Perkins">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/john_deere.png" alt="John Deere">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/caterpillar.png" alt="Caterpillar">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/shantui.png" alt="Shantui">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/xcmg.png" alt="XCMG">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/kobelco.png" alt="Kobelco">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/shehwa.png" alt="SHEHWA">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/bomag.png" alt="BOMAG">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h100px/compressed/hitachi.png" alt="Hitachi">
|
||||
</div>
|
||||
<section class="h-100 d-flex ticker">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/cummins.png" alt="Cummins">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/iveco.png" alt="Iveco">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/komatsu.png" alt="Komatsu">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/case.png" alt="Case">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/isuzu.png" alt="Isuzu">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/new_holland.png" alt="New Holland">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/perkins.png" alt="Perkins">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/john_deere.png" alt="John Deere">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/caterpillar.png" alt="Caterpillar">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/shantui.png" alt="Shantui">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/xcmg.png" alt="XCMG">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/kobelco.png" alt="Kobelco">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/shehwa.png" alt="SHEHWA">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/bomag.png" alt="BOMAG">
|
||||
<img class="w-auto h-100 mr-3 my-auto" src="/img/logos/h32px/compressed/hitachi.png" alt="Hitachi">
|
||||
</section>
|
||||
|
||||
<div class="container mb-4">
|
||||
<section class="container mb-4">
|
||||
<!-- <div class="row mb-3">
|
||||
<h4 class="col gilroy categories_blocks_panel_title">Сопутствующие товары</h4>
|
||||
</div> -->
|
||||
@@ -80,7 +81,7 @@ $this->title = 'SkillParts';
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script src="/js/ticker.js" defer></script>
|
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
/* @var $this \yii\web\View */
|
||||
/* @var $content string */
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii\helpers\Html;
|
||||
|
||||
use app\assets\AppAsset;
|
||||
|
||||
AppAsset::register($this);
|
||||
@@ -19,6 +19,25 @@ AppAsset::register($this);
|
||||
<meta charset="<?= Yii::$app->charset ?>">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/favicons/apple-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/favicons/apple-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/favicons/apple-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/favicons/apple-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/favicons/apple-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/favicons/apple-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/favicons/apple-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/favicons/apple-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/favicons/android-icon-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/favicons/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png">
|
||||
<link rel="manifest" href="/favicons/manifest.json">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="/favicons/ms-icon-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<?php $this->registerCsrfMetaTags() ?>
|
||||
<title><?= Html::encode($this->title ?? 'SkillParts') ?></title>
|
||||
<?php $this->head() ?>
|
||||
@@ -27,19 +46,23 @@ AppAsset::register($this);
|
||||
<body>
|
||||
<?php $this->beginBody() ?>
|
||||
|
||||
<header class="container pt-2 mt-1 mb-4">
|
||||
<div id="notifications_popup_wrap" class="col-3 m-4"></div>
|
||||
|
||||
<header class="container pt-2 mt-1 mb-2 mb-sm-4">
|
||||
<div class="row h-100">
|
||||
<a class="col-3 h-100 py-2" title="SkillParts" href="/" role="button" onclick="return page_main();">
|
||||
<img class="h-100" src="/img/logos/skillparts.svg" alt="SkillParts">
|
||||
</a>
|
||||
<div class="col-6 px-0 mt-auto d-flex">
|
||||
<div id="logo" class="col-3 col-md-4 h-100">
|
||||
<a class="py-2 h-100 d-inline-flex" title="SkillParts" href="/" role="button" onclick="return page_main();">
|
||||
<img class="h-100" src="/img/logos/skillparts.svg" alt="SkillParts">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col px-0 mt-auto d-flex h-title">
|
||||
<div class="ml-auto p-0 h-divider-title-left"></div>
|
||||
<div class="px-4 d-flex flex-column justify-content-center h-divider-title">
|
||||
<h6 class="text-center text-white my-0"><b>Запчасти для спецтехники</b></h6>
|
||||
</div>
|
||||
<div class="mr-auto p-0 h-divider-title-right"></div>
|
||||
</div>
|
||||
<nav class="col-3 mt-auto d-flex justify-content-end"></nav>
|
||||
<menu class="col-auto col-lg-4 mb-0 d-flex justify-content-end"></menu>
|
||||
</div>
|
||||
<div class="h-divider"></div>
|
||||
</header>
|
||||
@@ -47,11 +70,11 @@ AppAsset::register($this);
|
||||
<aside class="container mb-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-3 d-flex flex-column align-center justify-content-end dropdownMenuButton_column">
|
||||
<button id="dropdownMenuButton" class="btn form-control d-flex align-items-center button_clean catalog_button" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button id="catalog" class="btn form-control d-flex align-items-center button_clean catalog_button" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-bars col-auto text-left p-0 mr-auto h-100 d-flex flex-column justify-content-center"></i>
|
||||
<p class="col-10 m-0 p-0">Каталог товаров</p>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<div class="dropdown-menu" aria-labelledby="catalog">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
@@ -65,15 +88,33 @@ AppAsset::register($this);
|
||||
<label class="mb-0 px-3 px-md-4 py-1" for="catalog_search_panel_button_2">Вторая кнопка</label>
|
||||
<input id="catalog_search_panel_button_3" class="btn btn-sm5 text-white button_clean" type="radio" name="catalog_search_panel_buttons" value="catalog_search_panel_button_3">
|
||||
<label class="mb-0 px-3 px-md-4 py-1" for="catalog_search_panel_button_3">Третья кнопка</label> -->
|
||||
<form class="d-flex catalog_search">
|
||||
<input type="text" class="form-control col-8 col-lg-10 catalog_search_line" id="productNumber" placeholder="Введите номер запчасти, например: 45223503481">
|
||||
<button type="submit" class="col btn button_clean catalog_search_button">ПОИСК</button>
|
||||
<form class="d-flex catalog_search" onsubmit="return false;">
|
||||
<div class="position-relative col-sm-8 col-lg-10 px-0">
|
||||
<input id="search_line" type="text" class="form-control col-12 catalog_search_line button_clean" placeholder="Введите номер запчасти, например: 45223503481" oninput="$('#search_line').dropdown('hide'); product_search(this.value);" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" autocomplete="off">
|
||||
<?php
|
||||
// Сделать системные настройки и по ним работать
|
||||
$search_panel = yii::$app->controller->renderPartial('/search/panel', ['history' => true]);
|
||||
|
||||
// if (!yii::$app->user->isGuest && $search_panel = $search_panel ?? yii::$app->controller->renderPartial('/search/panel', ['history' => true])) {
|
||||
echo <<<HTML
|
||||
<div id="search_line_window" class="dropdown-menu w-100" aria-labelledby="search_line">
|
||||
$search_panel
|
||||
</div>
|
||||
HTML;
|
||||
// } else {
|
||||
// echo <<<HTML
|
||||
// <div id="search_line_window" class="dropdown-menu w-100" style="display: none" aria-labelledby="search_line"></div>
|
||||
// HTML;
|
||||
// }
|
||||
?>
|
||||
</div>
|
||||
<button type="submit" class="col btn button_clean catalog_search_button" onclick="product_search(this.parentElement.getElementsByTagName('input')[0].value, 1)">ПОИСК</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="col p-0">
|
||||
<main class="col p-0 d-flex flex-column justify-content-center">
|
||||
<?= $content ?>
|
||||
</main>
|
||||
|
||||
|
18
mirzaev/skillparts/system/views/notification/button.php
Normal file
18
mirzaev/skillparts/system/views/notification/button.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<a id="notification_button" class="text-dark d-flex h-100 mr-2" title="Уведомления" role="button" data-toggle="dropdown" data-offset="-100%p + 100%" onclick="return notification_stream();">
|
||||
<?php
|
||||
if (empty($notifications_new_amount) || $notifications_new_amount < 1) {
|
||||
// Новые уведомления не найдены
|
||||
|
||||
echo <<<HTML
|
||||
<i class="fas fa-bell my-auto mx-2"></i>
|
||||
HTML;
|
||||
} else {
|
||||
// Новые уведомления найдены
|
||||
|
||||
echo <<<HTML
|
||||
<small class="my-auto ml-2 mr-1"><b>$notifications_new_amount</b></small>
|
||||
<i class="fas fa-bell my-auto mr-2 notification_button_active"></i>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</a>
|
30
mirzaev/skillparts/system/views/notification/content.php
Normal file
30
mirzaev/skillparts/system/views/notification/content.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
if (empty($notifications)) {
|
||||
echo <<<HTML
|
||||
<p class="px-2 py-4 text-center">Уведомлений нет</p>
|
||||
HTML;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($notifications as $notification) {
|
||||
// Перебор уведомлений
|
||||
|
||||
// Инициализация
|
||||
$notification = $notification->getAttributes();
|
||||
|
||||
if ($notification['type'] === 'notice') {
|
||||
// Уведомление
|
||||
|
||||
echo $notification['html'];
|
||||
} else if ($notification['type'] === 'warning') {
|
||||
// Предупреждение
|
||||
|
||||
echo $notification['html'];
|
||||
} else {
|
||||
// Неизвестно
|
||||
|
||||
echo $notification['html'] ?? '<p>ОШИБКА</p>';
|
||||
}
|
||||
}
|
||||
?>
|
45
mirzaev/skillparts/system/views/notification/panel.php
Normal file
45
mirzaev/skillparts/system/views/notification/panel.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
if (empty($notifications)) {
|
||||
// Уведомления не найдены
|
||||
|
||||
$content = <<<HTML
|
||||
<p class="px-2 py-4 text-center">Уведомлений нет</p>
|
||||
HTML;
|
||||
} else {
|
||||
// Уведомления найдены
|
||||
|
||||
// Инициализация
|
||||
$content = '';
|
||||
|
||||
foreach ($notifications as $notification) {
|
||||
// Перебор уведомлений
|
||||
|
||||
// Инициализация
|
||||
$notification = $notification->getAttributes();
|
||||
|
||||
if ($notification['type'] === 'notice') {
|
||||
// Уведомление
|
||||
|
||||
$content .= $notification['html'];
|
||||
} else if ($notification['type'] === 'warning') {
|
||||
// Предупреждение
|
||||
|
||||
$content .= $notification['html'];
|
||||
} else {
|
||||
// Неизвестно
|
||||
|
||||
$content .= $notification['html'] ?? '<p>ОШИБКА</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($notifications_panel_full ?? false) {
|
||||
echo <<<HTML
|
||||
<div id="notification_button_panel" class="dropdown-menu p-2" aria-labelledby="notification_button">
|
||||
$content
|
||||
</div>
|
||||
HTML;
|
||||
} else {
|
||||
echo $content;
|
||||
}
|
||||
?>
|
10
mirzaev/skillparts/system/views/notification/popup.php
Normal file
10
mirzaev/skillparts/system/views/notification/popup.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
$id = 'popup/' . $notification->readId();
|
||||
$html = $notification->html;
|
||||
|
||||
echo <<<HTML
|
||||
<div id="$id" class="mt-3 p-0 notification rounded" onmouseleave="return notification_popup_delete(this);">
|
||||
$html
|
||||
</div>
|
||||
HTML;
|
||||
?>
|
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
if($amount > 0) {
|
||||
echo <<<HTML
|
||||
<p>Импортировано $amount товаров из excel-документа <span>($date)</span></p>
|
||||
HTML;
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<p>Неудачная попытка импорта товаров из excel-документа <span>($date)</span></p>
|
||||
HTML;
|
||||
}
|
||||
|
||||
?>
|
@@ -0,0 +1,2 @@
|
||||
|
||||
<p>Импортированы товары из 1C <span>(<?= $date ?>)</span></p>
|
64
mirzaev/skillparts/system/views/orders/index.php
Normal file
64
mirzaev/skillparts/system/views/orders/index.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<link href="/css/pages/orders.css" rel="stylesheet">
|
||||
|
||||
<div id="page_orders" class="container mb-auto py-3">
|
||||
<article class="py-3 px-4 rounded">
|
||||
<h4 class="ml-4 mt-2 mb-4"><i class="fas fa-list mr-2"></i>Заказы</h4>
|
||||
<div class="col mb-4 list rounded overflow-hidden">
|
||||
<div class="row py-2">
|
||||
<div class="pl-3 mr-1">
|
||||
<input id="checkbox_cart_all" type="checkbox" onchange="return cart_list_checkbox(this);" />
|
||||
</div>
|
||||
<div class="col-2 ml-auto">
|
||||
<span>Статус</span>
|
||||
</div>
|
||||
<div class="col-2 mr-3 text-right">
|
||||
<span>Время</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
if (isset($orders) && !empty($orders)) {
|
||||
foreach ($orders as $order) {
|
||||
// Перебор заказов
|
||||
|
||||
// Инициализация
|
||||
extract($order);
|
||||
|
||||
// Инициализация
|
||||
$date = empty($jrnl) ? '' : date('H:i d.m.Y', end($jrnl)['date']);
|
||||
|
||||
echo <<<HTML
|
||||
<div class="row py-2 cart_list_target">
|
||||
<div class="pl-3 mr-1">
|
||||
<input type="checkbox" onchange="return cart_list_checkbox(this);"/>
|
||||
</div>
|
||||
<div class="col-2 ml-auto">
|
||||
{$account_edge_order[0]['type']}
|
||||
</div>
|
||||
<div class="col-2 mr-3 text-right">
|
||||
$date
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<div class="row py-2 cart_list_target">
|
||||
<div class="mx-auto py-2">
|
||||
Заказов нет
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="row mb-2 mx-0">
|
||||
<select id="cart_list_action" class="form-control mr-3 button_clean w-auto" name="CartListAction">
|
||||
<option value="none" hidden>Действие с выбранными</option>
|
||||
<option value="delete" onclick="return cart_list_delete();">Удалить</option>
|
||||
</select>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<script src="/js/orders.js" defer></script>
|
@@ -1,18 +1,186 @@
|
||||
<?php
|
||||
|
||||
use yii\bootstrap\ActiveForm;
|
||||
use yii\helpers\Html;
|
||||
|
||||
use app\models\Product;
|
||||
|
||||
?>
|
||||
|
||||
<div id="page_product" class="h-100">
|
||||
<div class="container h-100">
|
||||
<div class="row h-100 py-3">
|
||||
<div class="col-12">
|
||||
<div class="block_main h-100 p-3 rounded">
|
||||
<h4 class="ml-4"><?php echo $model['name'] ?></h4>
|
||||
<div class="dropdown-divider"></div>
|
||||
<p class="ml-4">Дата создания: <?php echo date('d.m.Y H:i', $model['date']) ?></p>
|
||||
<pre class="ml-4"><?php var_dump($model) ?></pre>
|
||||
<link href="/css/pages/product.css" rel="stylesheet">
|
||||
|
||||
<div id="page_product" class="container mb-auto">
|
||||
<div class="row py-3">
|
||||
<article class="col-12">
|
||||
<div class="p-3 d-flex flex-column rounded">
|
||||
<div id="product_slider" class="row px-3 profile_panel">
|
||||
<div class="col-1 product_slider_preview p-0 pr-3 mb-3">
|
||||
<?php
|
||||
foreach ($model['imgs'] ?? [null] as $key => $image) {
|
||||
// Перебор изображений
|
||||
|
||||
// Инициализация
|
||||
$h150 = $image['h150'] ?? '/img/covers/h150/product.png';
|
||||
|
||||
// Генерация предпросмотра изображения
|
||||
echo <<<HTML
|
||||
<label class="p-0 mb-2" for="product_slider_image_$key">
|
||||
<img class="img-fluid rounded" src="$h150"/>
|
||||
</label>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<input id="product_slider_image_new" type="file" class="d-none" onchange="return product_panel_images_write('<?= $model['catn'] ?>', this);" multiple />
|
||||
<label class="p-0 mb-2" for="product_slider_image_new" role="button">
|
||||
<img class="img-fluid rounded" src="/img/covers/h150/product_new.png" />
|
||||
</label>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="product_slider_image">
|
||||
<?php
|
||||
// Инициализация
|
||||
$imgs = $model['imgs'] ?? [null];
|
||||
$checked = '';
|
||||
|
||||
foreach ($model['imgs'] ?? [null] as $key => $image) {
|
||||
// Перебор изображений
|
||||
|
||||
// Инициализация
|
||||
$name = $image['name'] ?? 'Без названия';
|
||||
$orig = $image['orig'] ?? '/img/covers/product.png';
|
||||
$covr = $image['covr'] ?? false;
|
||||
|
||||
if ($covr || count($imgs) < 2) {
|
||||
// Если это изображение является обложкой
|
||||
|
||||
// Реинициализация
|
||||
$checked = 'checked';
|
||||
}
|
||||
|
||||
// Генерация изображения
|
||||
echo <<<HTML
|
||||
<input type="radio" id="product_slider_image_$key" name="slider" $checked/>
|
||||
<div class="col p-0">
|
||||
<img class="img-fluid rounded" src="$orig"/>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
// Деинициализация
|
||||
$checked = '';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="col ml-4 d-flex flex-column">
|
||||
<div class="row mb-1">
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<h3 id="title_<?= $model['catn'] ?>" class="my-auto pointer-event" role="button" onclick="return product_panel_title_edit('<?= $model['catn'] ?>', this);"><?= $model['name'] ?? 'Без названия' ?></h3>
|
||||
<?php else : ?>
|
||||
<h3 id="title_<?= $model['catn'] ?>" class="my-auto"><?= $model['name'] ?? 'Без названия' ?></h3>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<h5 id="catn_<?= $model['catn'] ?>" class="ml-auto my-auto d-flex pointer-event" role="button" onclick="return product_panel_catn_edit('<?= $model['catn'] ?>', this, true);">
|
||||
<?= $model['catn'] ?>
|
||||
<!-- <small class="d-flex">
|
||||
<a class="text-dark my-auto ml-3" title="Редактировать" role="button" onclick="return product_panel_edit('<?= $model['catn'] ?>');">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="text-dark my-auto ml-2" title="Удалить" role="button" onclick="return product_panel_delete('<?= $model['catn'] ?>');">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</a>
|
||||
</small> -->
|
||||
</h5>
|
||||
<?php else : ?>
|
||||
<h5 id="catn_<?= $model['catn'] ?>" class="ml-auto my-auto d-flex">
|
||||
<?= $model['catn'] ?>
|
||||
</h5>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="dropdown-divider px-0 mb-3"></div>
|
||||
<div class="row mb-3 h-100 product_panel d-flex flex-column">
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<p id="description_<?= $model['catn'] ?>" class="mt-0 ml-0 text-break pointer-event product_description" role="button" onclick="return product_panel_description_edit('<?= $model['catn'] ?>', this);"><?= $model['desc'] ?? 'Без описания' ?></p>
|
||||
<?php else : ?>
|
||||
<p id="description_<?= $model['catn'] ?>" class="mt-0 ml-0 text-break product_description"><?= $model['name'] ?? 'Без описания' ?></p>
|
||||
<?php endif ?>
|
||||
<p class="mt-0">
|
||||
<?php
|
||||
// foreach ($model['catn'] ?? [] as $catn) {
|
||||
// echo <<<HTML
|
||||
// $catn
|
||||
// HTML;
|
||||
// }
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => 'form_product_cart',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{input}',
|
||||
],
|
||||
'options' => [
|
||||
'onsubmit' => 'return false;',
|
||||
'class' => 'row mt-auto'
|
||||
]
|
||||
]);
|
||||
|
||||
// Просто для теста
|
||||
$model = new Product();
|
||||
?>
|
||||
|
||||
<div class="col-6 px-0 mr-4 btn-group">
|
||||
<?= Html::submitButton('В корзину', ['name' => 'cartWrite', 'onclick' => 'product_cart_write(this.parentElement);', 'class' => 'col-10 btn button_blue button_clean py-2 px-5']) ?>
|
||||
<?= $form->field($model, 'amount', ['options' => ['class' => 'col h-100 m-0 form-group']])->textInput(['value' => '1', 'class' => 'form-control h-100 rounded-0 text-center button_clean']); ?>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mt-0">
|
||||
<strong>Хабаровск: </strong>
|
||||
в наличии
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
Доставим: завтра
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="row mt-3 mx-0">
|
||||
<p class="ml-0">Время для повышения релевантности в поисковиках</p>
|
||||
<time class="ml-auto"></time>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/product.js" defer></script>
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<script src="/js/product_panel.js" defer></script>
|
||||
<?php endif ?>
|
@@ -1,61 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
use app\controllers\ProfileController;
|
||||
use app\models\Product;
|
||||
use app\models\Supply;
|
||||
|
||||
// Инициализация
|
||||
if (
|
||||
!yii::$app->user->isGuest
|
||||
&& yii::$app->user->identity->agnt
|
||||
) {
|
||||
$panel ?? $panel = 'profile_panel_settings_import';
|
||||
} else {
|
||||
$panel ?? $panel = 'profile_panel_settings_account';
|
||||
}
|
||||
|
||||
?>
|
||||
<link href="/css/pages/profile.css" rel="stylesheet">
|
||||
|
||||
<div id="page_profile" class="h-100">
|
||||
<div class="container h-100">
|
||||
<div class="row h-100 py-3">
|
||||
<div class="col-3">
|
||||
<div class="block_sidebar h-100 p-3 rounded">
|
||||
<div class="d-flex">
|
||||
<p>Почта: </p>
|
||||
<p class="ml-auto"><?php echo Yii::$app->user->identity->mail ?></p>
|
||||
<div id="page_profile" class="container mb-auto">
|
||||
<div class="row h-100 py-3">
|
||||
<nav class="col-3">
|
||||
<?= $sidebar ?? yii::$app->controller->renderPartial('/profile/sidebar') ?>
|
||||
</nav>
|
||||
<article class="col-9">
|
||||
<div class="p-4 rounded">
|
||||
<h4 class="ml-4 mb-4"><i class="fas fa-sliders-h my-auto mr-2"></i>Настройки</h4>
|
||||
<div id="profile_panel_settings" class="profile_panel">
|
||||
<div class="profile_panel_menu mb-3">
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_settings_account">Аккаунт</label>
|
||||
<?php
|
||||
if (
|
||||
!yii::$app->user->isGuest
|
||||
&& yii::$app->user->identity->agnt
|
||||
) {
|
||||
echo <<<HTML
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_settings_company">Компания</label>
|
||||
<label class="btn button_white mb-0" for="profile_panel_settings_import">Импорт</label>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="profile_panel_content d-flex">
|
||||
<input type="radio" id="profile_panel_settings_account" name="main_panel" <?= $panel === 'profile_panel_settings_account' ? 'checked' : null ?> />
|
||||
<div class="col">
|
||||
1
|
||||
</div>
|
||||
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& yii::$app->user->identity->agnt
|
||||
) : ?>
|
||||
<input type="radio" id="profile_panel_settings_company" name="main_panel" <?= $panel === 'profile_panel_settings_company' ? 'checked' : null ?> />
|
||||
<div class="col">
|
||||
2
|
||||
</div>
|
||||
|
||||
<input type="radio" id="profile_panel_settings_import" name="main_panel" <?= $panel === 'profile_panel_settings_import' ? 'checked' : null ?> />
|
||||
<div class="col">
|
||||
<h5>Параметры 1C</h5>
|
||||
<div class="dropdown-divider mb-3"></div>
|
||||
<?php $form = ActiveForm::begin([
|
||||
'id' => 'form_profile_settings',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{label}{input}',
|
||||
],
|
||||
'options' => [
|
||||
'onsubmit' => 'return false;'
|
||||
]
|
||||
]);
|
||||
|
||||
// Инициализация
|
||||
$model ?? $model = yii::$app->user->identity;
|
||||
$list or $list = ['Нет данных'];
|
||||
?>
|
||||
|
||||
<?= $form->field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]])
|
||||
->dropDownList($list, [
|
||||
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_import\')',
|
||||
'disabled' => count($list) <= 1
|
||||
])->label('OEM-номера'); ?>
|
||||
|
||||
<small class="d-block mb-1">Выберите поле в котором хранятся <b>ОЕМ-номера</b> и повторите импорт</small>
|
||||
<small class="d-block">Значения взяты из импортированных товаров</small>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<div class="block_main h-100 p-3 rounded">
|
||||
<h4 class="ml-4">Личный кабинет</h4>
|
||||
<div class="dropdown-divider"></div>
|
||||
<p>Не знаю что сюда пока добавить</p>
|
||||
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => '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();
|
||||
|
||||
?>
|
||||
|
||||
<?= $form->field($model, 'group', ['options' => ['class' => "mb-3"]])->dropDownList($groups ?? ['Нет данных']); ?>
|
||||
<?= $form->field($model, 'file', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'supply_import(this.parentElement.parentElement)']) ?>
|
||||
|
||||
<?= $form->errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
|
||||
<p>Всего товаров: <?php echo Product::readAmount() ?></p>
|
||||
<p>Всего поставок: <?php echo Supply::readAmount() ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/profile.js" defer></script>
|
||||
<script src="/js/profile.js" defer></script>
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator'
|
||||
) : ?>
|
||||
<script src="/js/profile_panel.js" defer></script>
|
||||
<?php endif ?>
|
87
mirzaev/skillparts/system/views/profile/monitoring.php
Normal file
87
mirzaev/skillparts/system/views/profile/monitoring.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Инициализация
|
||||
$panel ?? $panel = 'profile_panel_monitoring_input_search_history';
|
||||
|
||||
?>
|
||||
<link href="/css/pages/profile.css" rel="stylesheet">
|
||||
|
||||
<div id="page_profile" class="container mb-auto">
|
||||
<div class="row h-100 py-3">
|
||||
<nav class="col-3">
|
||||
<?= $sidebar ?? Yii::$app->controller->renderPartial('/profile/sidebar') ?>
|
||||
</nav>
|
||||
<article class="col-9">
|
||||
<div class="p-4 rounded">
|
||||
<h4 class="ml-3 mb-4"><i class="fas fa-eye my-auto mr-2"></i>Мониторинг</h4>
|
||||
<div id="profile_panel_monitoring" class="profile_panel">
|
||||
<div class="profile_panel_menu mb-3">
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_monitoring_input_orders_history">Заказы</label>
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_monitoring_input_search_history">Поисковые запросы</label>
|
||||
<label class="btn button_white mb-0" for="profile_panel_monitoring_input_log">Журнал действий</label>
|
||||
</div>
|
||||
<div class="profile_panel_content">
|
||||
<input type="radio" id="profile_panel_monitoring_input_orders_history" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_orders_history' ? 'checked' : null ?> />
|
||||
<div class="col p-3">
|
||||
История заказов в разработке
|
||||
</div>
|
||||
|
||||
<input type="radio" id="profile_panel_monitoring_input_search_history" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_search_history' ? 'checked' : null ?> />
|
||||
<div class="col rounded overflow-hidden">
|
||||
<div class="row header_blue py-2">
|
||||
<div class="col-sm-4 col-md-3">IPv4</div>
|
||||
<div class="col-7 col-sm-3 col-md-4 col-lg-6 pr-0 px-sm-0 px-lg-3">Поисковый запрос</div>
|
||||
<div class="col">Время</div>
|
||||
</div>
|
||||
<?php
|
||||
foreach ($search_history ?? [] as $row) {
|
||||
// Инициализация
|
||||
$time = $row->jrnl;
|
||||
$date = empty($row->jrnl) ? '' : date('H:i d.m.Y', end($time)['date']);
|
||||
|
||||
echo <<<HTML
|
||||
<div class="row py-1">
|
||||
<div class="col-sm-4 col-md-3">$row->ipv4</div>
|
||||
<div class="col-7 col-sm-3 col-md-4 col-lg-6 pr-0 px-sm-0 px-lg-3">$row->text</div>
|
||||
<div class="col">$date</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
<div class="mt-3">
|
||||
<?php
|
||||
$page = ($page_search_history ?? 0) + 1;
|
||||
|
||||
echo <<<HTML
|
||||
<div class="row">
|
||||
<button class="btn button_clean button_blue" onclick="return page_profile_monitoring(-1, 'profile_panel_monitoring_input_search_history');">Назад</button>
|
||||
<p>$page</p>
|
||||
<button class="btn button_clean button_blue" onclick="return page_profile_monitoring(1, 'profile_panel_monitoring_input_search_history');">Вперёд</button>
|
||||
<div class="col-8"></div>
|
||||
</div>
|
||||
HTML;
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="profile_panel_monitoring_input_log" name="main_panel" <?= $panel === 'profile_panel_monitoring_input_log' ? 'checked' : null ?> />
|
||||
<div class="col p-3">
|
||||
Журнал в разработке
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/profile.js" defer></script>
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<script src="/js/profile_panel.js" defer></script>
|
||||
<?php endif ?>
|
145
mirzaev/skillparts/system/views/profile/panel.php
Normal file
145
mirzaev/skillparts/system/views/profile/panel.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
use yii\helpers\Html;
|
||||
|
||||
use app\models\Notification;
|
||||
|
||||
// Инициализация
|
||||
$panel ?? $panel = 'profile_panel_panel_input_notifications';
|
||||
|
||||
?>
|
||||
|
||||
<link href="/css/pages/profile.css" rel="stylesheet">
|
||||
|
||||
<div id="page_profile" class="container mb-auto">
|
||||
<div class="row h-100 py-3">
|
||||
<nav class="col-3">
|
||||
<?= $sidebar ?? yii::$app->controller->renderPartial('/profile/sidebar') ?>
|
||||
</nav>
|
||||
<article class="col-9">
|
||||
<div class="p-4 rounded">
|
||||
<h4 class="ml-4 mb-4"><i class="fas fa-user-shield my-auto mr-2"></i>Панель управления</h4>
|
||||
<div id="profile_panel_panel" class="profile_panel">
|
||||
<div class="profile_panel_menu mb-3">
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_panel_input_notifications">Уведомления</label>
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_panel_input_settings">Настройки</label>
|
||||
</div>
|
||||
<div class="profile_panel_content">
|
||||
<input type="radio" id="profile_panel_panel_input_notifications" name="main_panel" <?= $panel === 'profile_panel_panel_input_notifications' ? 'checked' : null ?>/>
|
||||
<div class="col">
|
||||
<h5>Отправка уведомления</h5>
|
||||
<div class="dropdown-divider mb-3"></div>
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => 'form_profile_panel_notifications',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{label}{input}'
|
||||
],
|
||||
'options' => [
|
||||
'onsubmit' => 'return false;'
|
||||
]
|
||||
]);
|
||||
|
||||
// Значения по умолчанию
|
||||
$model_notifications ?? $model_notifications = new Notification;
|
||||
$model_notifications->account ?? $model_notifications->account = null;
|
||||
$model_notifications->text ?? $model_notifications->text = '';
|
||||
?>
|
||||
|
||||
<?= $form->errorSummary($model_notifications) ?>
|
||||
|
||||
<div class="row">
|
||||
<?= $form->field($model_notifications, 'account', ['options' => ['class' => "mb-1 col-9"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?>
|
||||
<?= $form->field($model_notifications, 'type', ['options' => ['class' => "col pl-0"]])->dropDownList($model_notifications->typs); ?>
|
||||
</div>
|
||||
<small class="d-block mb-1"><b>Множественная отправка:</b> @all или перечислить через запятую</small>
|
||||
<small class="d-block mb-3"><b>Тестирование:</b> @test или оставить поле пустым</small>
|
||||
|
||||
<?= $form->field($model_notifications, 'text', ['options' => ['class' => "mb-3"]])->textarea(); ?>
|
||||
|
||||
<?= Html::submitButton('Отправить', ['name' => 'submitNotification', 'onclick' => 'page_profile_panel_notification_create(this.parentElement);', 'class' => 'flex-grow-1 mr-2 btn button_white button_clean']) ?>
|
||||
<?= Html::submitButton('Отправить как HTML', ['name' => 'submitNotification', 'onclick' => 'page_profile_panel_notification_create(this.parentElement, 1);', 'class' => 'flex-grow-1 mr-2 btn button_white button_clean']) ?>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="profile_panel_panel_input_settings" name="main_panel" <?= $panel === 'profile_panel_panel_input_settings' ? 'checked' : null ?>/>
|
||||
<div class="col">
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => 'form_profile_panel_settings_search_period',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{label}{input}',
|
||||
],
|
||||
'options' => [
|
||||
'onsubmit' => 'return false;',
|
||||
'class' => 'mb-4'
|
||||
]
|
||||
]);
|
||||
?>
|
||||
|
||||
<?= $form->errorSummary($model_settings, ['header' => 'Получены ошибки:']) ?>
|
||||
|
||||
<?= $form->field($model_settings, 'search_period', ['options' => ['class' => "mb-1"]])->textInput(['value' => $model_settings['search_period'], 'onChange' => 'page_profile_panel_settings(this.parentElement.parentElement, \'profile_panel_panel_input_settings\')']); ?>
|
||||
<small class="d-block mb-1">Время которое надо ждать для повторного поиска в секундах</small>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => 'form_profile_panel_settings_search_connect_keep',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{label}{input}',
|
||||
],
|
||||
'options' => [
|
||||
'onsubmit' => 'return false;'
|
||||
]
|
||||
]);
|
||||
|
||||
$list = ['false' => 'Нет', 'true' => 'Да'];
|
||||
|
||||
if (isset($model_settings->search_connect_keep)) {
|
||||
// Найден ранее записанный параметр
|
||||
|
||||
if (isset($list[$model_settings->search_connect_keep])) {
|
||||
// Совпадение параметра с параметрами списка найдено
|
||||
|
||||
// Буфер для сохранения параметра
|
||||
$buffer = $list[$model_settings->search_connect_keep];
|
||||
|
||||
// Удаление параметра
|
||||
unset($list[$model_settings->search_connect_keep]);
|
||||
|
||||
// Сохранение параметра в начале массива
|
||||
$list = array_merge([$model_settings->search_connect_keep => $buffer], $list);
|
||||
} else {
|
||||
// Совпадение параметра с параметрами списка не найдено
|
||||
|
||||
// Сохранение параметра из данных аккаунта в начале массива
|
||||
$list = array_merge([$model_settings->search_connect_keep => $model_settings->search_connect_keep], $list);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?= $form->field($model_settings, 'search_connect_keep', ['options' => ['class' => "mb-1"]])->dropDownList($list, ['onChange' => 'page_profile_panel_settings(this.parentElement.parentElement, \'profile_panel_panel_input_settings\')']); ?>
|
||||
<small class="d-block mb-1">Удерживать открытое соединение до истечения срока блокировки поиска?</small>
|
||||
<small class="d-block mb-1">При малой задержке позволяет снизить время загрузки страницы, но при большой будет казаться, что сайт завис</small>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/profile.js" defer></script>
|
||||
<script src="/js/profile_panel.js" defer></script>
|
125
mirzaev/skillparts/system/views/profile/sidebar.php
Normal file
125
mirzaev/skillparts/system/views/profile/sidebar.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
|
||||
use app\models\Product;
|
||||
use app\models\Supply;
|
||||
use app\models\AccountEdgeSupply;
|
||||
use app\models\SupplyEdgeProduct;
|
||||
|
||||
?>
|
||||
<div class="col py-3 rounded">
|
||||
<div class="row px-3">
|
||||
<p class="ml-0">Почта: </p>
|
||||
<p class="mr-0"><?= yii::$app->user->identity->mail ?></p>
|
||||
</div>
|
||||
<div class="dropdown-divider my-3"></div>
|
||||
<dl class="m-0">
|
||||
<?php
|
||||
if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) {
|
||||
// Пользователь является доверенным
|
||||
|
||||
// Инициализация
|
||||
$targetUrl = '/profile/panel';
|
||||
|
||||
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
|
||||
// Запрошена та же страница от которой послан запрос (текущая)
|
||||
|
||||
echo <<<HTML
|
||||
<dt>
|
||||
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a>
|
||||
</dt>
|
||||
HTML;
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<dt>
|
||||
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return page_profile_panel_settings();"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a>
|
||||
</dt>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<dt>
|
||||
<?php
|
||||
if (yii::$app->user->identity->agnt) {
|
||||
if (
|
||||
'/' . yii::$app->request->pathInfo === $targetUrl = '/profile/supplies'
|
||||
|| '/' . yii::$app->request->pathInfo === $targetUrl = '/profile/import'
|
||||
) {
|
||||
// Запрошена та же страница от которой послан запрос (текущая)
|
||||
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
|
||||
HTML;
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return page_profile_supplies();"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
?>
|
||||
</dt>
|
||||
<dt>
|
||||
<?php
|
||||
// Инициализация
|
||||
$targetUrl = '/profile/monitoring';
|
||||
|
||||
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
|
||||
// Запрошена та же страница от которой послан запрос (текущая)
|
||||
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
|
||||
HTML;
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return page_profile_monitoring();"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</dt>
|
||||
<dt>
|
||||
<?php
|
||||
// Инициализация
|
||||
$targetUrl = '/profile';
|
||||
|
||||
if ('/' . yii::$app->request->pathInfo === $targetUrl) {
|
||||
// Запрошена та же страница от которой послан запрос (текущая)
|
||||
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a>
|
||||
HTML;
|
||||
} else {
|
||||
echo <<<HTML
|
||||
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return page_profile_settings();"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a>
|
||||
HTML;
|
||||
}
|
||||
?>
|
||||
</dt>
|
||||
</dl>
|
||||
<?php if (yii::$app->user->identity->agnt) : ?>
|
||||
<div class="dropdown-divider my-3"></div>
|
||||
<div class="row px-3">
|
||||
<p class="ml-0">Загрузок с аккаунта:</p>
|
||||
<p class="mr-0"><?= AccountEdgeSupply::readAmount() ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="row px-3">
|
||||
<p class="ml-0">Товары:</p>
|
||||
<p class="mr-0"><?= Product::readAmount() ?></p>
|
||||
</div>
|
||||
<div class="row px-3">
|
||||
<p class="ml-0">Поставки:</p>
|
||||
<p class="mr-0"><?= Supply::readAmount() ?></p>
|
||||
</div>
|
||||
<div class="row px-3">
|
||||
<p class="ml-0">Связано поставок:</p>
|
||||
<p class="mr-0"><?= SupplyEdgeProduct::readAmount() ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
73
mirzaev/skillparts/system/views/profile/supplies.php
Normal file
73
mirzaev/skillparts/system/views/profile/supplies.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use yii;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
|
||||
// Инициализация
|
||||
$panel ?? $panel = 'profile_panel_supplies_input_import';
|
||||
|
||||
?>
|
||||
|
||||
<link href="/css/pages/profile.css" rel="stylesheet">
|
||||
|
||||
<div id="page_profile" class="container mb-auto">
|
||||
<div class="row h-100 py-3">
|
||||
<nav class="col-3">
|
||||
<?= $sidebar ?? yii::$app->controller->renderPartial('/profile/sidebar') ?>
|
||||
</nav>
|
||||
<article class="col-9">
|
||||
<div class="h-100 p-4 rounded">
|
||||
<h4 class="ml-4 mb-4"><i class="fas fa-truck my-auto mr-2"></i>Управление поставками</h4>
|
||||
<div id="profile_panel_supplies" class="profile_panel">
|
||||
<div class="profile_panel_menu mb-3">
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_settings">Настройки</label>
|
||||
<label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_import">Импорт</label>
|
||||
</div>
|
||||
<div class="profile_panel_content">
|
||||
|
||||
<input type="radio" id="profile_panel_supplies_input_settings" name="main_panel" <?= $panel === 'profile_panel_supplies_input_settings' ? 'checked' : null ?> />
|
||||
<div class="col">
|
||||
<p>В разработке</p>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="profile_panel_supplies_input_import" name="main_panel" <?= $panel === 'profile_panel_supplies_input_import' ? 'checked' : null ?> />
|
||||
<div class="col">
|
||||
<h5>Импорт из Excel-документа</h5>
|
||||
<div class="dropdown-divider mb-3"></div>
|
||||
<?php
|
||||
$form = ActiveForm::begin([
|
||||
'id' => 'form_product_import_excel',
|
||||
'action' => false,
|
||||
'fieldConfig' => [
|
||||
'template' => '{label}{input}',
|
||||
'options' => ['class' => '']
|
||||
],
|
||||
'options' => [
|
||||
'class' => 'mb-3',
|
||||
'onsubmit' => 'return false;'
|
||||
]
|
||||
]);
|
||||
?>
|
||||
|
||||
<?= $form->field($model, 'file_excel', ['enableLabel' => false])->fileInput(['multiple' => true, 'class' => 'mb-2', 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?>
|
||||
|
||||
<?= $form->errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?>
|
||||
|
||||
<?php ActiveForm::end(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/profile.js" defer></script>
|
||||
<?php if (
|
||||
!yii::$app->user->isGuest
|
||||
&& (yii::$app->user->identity->type === 'administrator'
|
||||
|| yii::$app->user->identity->type === 'moderator')
|
||||
) : ?>
|
||||
<script src="/js/profile_panel.js" defer></script>
|
||||
<?php endif ?>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user