7 Commits
0.1.0 ... 0.5.0

120 changed files with 4315 additions and 1091 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,2 @@
vendor/
cache/
/vendor
/cache

View File

@@ -12,16 +12,18 @@
}
],
"require": {
"php": ">=7.4.0",
"php": "^8.0.0",
"twbs/bootstrap": ">=4.5",
"yiisoft/yii2": ">=2.0.14",
"yiisoft/yii2": "2.*",
"yiisoft/yii2-bootstrap": ">=2.0.0",
"yiisoft/yii2-swiftmailer": ">=2.0.0",
"bower-asset/bootstrap": "*",
"npm-asset/jquery": "^3.5",
"bower-asset/jquery": "^3.5",
"pbazsi/yii2-arangodb": "2.0",
"triagens/arangodb": "^3.6"
"explosivebit/arangodb": "^2.0",
"triagens/arangodb": "^3.6",
"moonlandsoft/yii2-phpexcel": "^2.0",
"carono/yii2-1c-exchange": "^0.3.1"
},
"require-dev": {
"codeception/codeception": ">=4.1",
@@ -39,7 +41,10 @@
"autoload": {
"psr-4": {
"mirzaev\\skillparts\\": "mirzaev/skillparts/system"
}
},
"classmap": [
"vendor/explosivebit"
]
},
"autoload-dev": {
"psr-4": {
@@ -86,14 +91,14 @@
{
"type": "package",
"package": {
"name": "pbazsi/yii2-arangodb",
"version": "2.0",
"source": {
"type" : "git",
"url" : "https://github.com/pBazsi/yii2-arangodb.git",
"reference" : "master"
}
"name": "explosivebit/arangodb",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/pBazsi/yii2-arangodb.git",
"reference": "master"
}
}
}
}
]
}

1542
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
/import

View File

@@ -27,16 +27,18 @@ class AppAsset extends AssetBundle
'css/header.css',
'css/info_panel.css',
'css/categories_blocks_panel.css',
'css/ticker.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',
'js/ticker.js',
'https://kit.fontawesome.com/d7e922c226.js'
'https://unpkg.com/cookielib/src/cookie.min.js',
'js/menu.js',
'js/account.js',
'js/reinitialization.js'
];
public $depends = [
'yii\web\YiiAsset',

View File

@@ -1,17 +1,16 @@
<?php
$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/db.php';
$config = [
'id' => 'basic-console',
'id' => 'skillparts-console',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'controllerNamespace' => 'app\commands',
'aliases' => [
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@tests' => '@app/tests',
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@explosivebit' => '@vendor/explosivebit',
'@tests' => '@app/tests',
],
'components' => [
'cache' => [
@@ -25,16 +24,15 @@ $config = [
],
],
],
'db' => $db,
'arangodb' => require __DIR__ . '/db.php'
],
'params' => $params,
/*
'params' => require __DIR__ . '/params.php',
'controllerMap' => [
'fixture' => [ // Fixture generation command line.
'arangodb-migrate' => 'explosivebit\arangodb\console\controllers\MigrateController',
'fixture' => [
'class' => 'yii\faker\FixtureController',
],
],
*/
]
];
if (YII_ENV_DEV) {

View File

@@ -1,14 +1,14 @@
<?php
return [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=;dbname=',
'username' => '',
'password' => '',
'charset' => 'utf8',
use ArangoDBClient\ConnectionOptions;
// Schema cache options (for production environment)
//'enableSchemaCache' => true,
//'schemaCacheDuration' => 60,
//'schemaCache' => 'cache',
return [
'class' => '\explosivebit\arangodb\Connection',
'connectionOptions' => [
ConnectionOptions::OPTION_DATABASE => '',
ConnectionOptions::OPTION_ENDPOINT => 'tcp://127.0.0.1:8529',
ConnectionOptions::OPTION_AUTH_TYPE => 'Basic',
ConnectionOptions::OPTION_AUTH_USER => '',
ConnectionOptions::OPTION_AUTH_PASSWD => '',
],
];

View File

@@ -1,12 +1,10 @@
<?php
$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/test_db.php';
/**
* Application configuration shared by all test types
*/
return [
'id' => 'basic-tests',
'id' => 'skillparts-tests',
'basePath' => dirname(__DIR__),
'aliases' => [
'@bower' => '@vendor/bower-asset',
@@ -14,7 +12,7 @@ return [
],
'language' => 'en-US',
'components' => [
'db' => $db,
'db' => require __DIR__ . '/test_db.php',
'mailer' => [
'useFileTransport' => true,
],
@@ -25,7 +23,7 @@ return [
'showScriptName' => true,
],
'user' => [
'identityClass' => 'app\models\User',
'identityClass' => 'app\models\Account',
],
'request' => [
'cookieValidationKey' => 'test',
@@ -38,5 +36,5 @@ return [
*/
],
],
'params' => $params,
'params' => require __DIR__ . '/params.php',
];

View File

@@ -1,33 +1,37 @@
<?php
use ArangoDBClient\ConnectionOptions;
$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/db.php';
$config = [
'id' => 'basic',
'id' => 'skillparts',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'aliases' => [
'@vendor' => '../../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@explosivebit' => '@vendor/explosivebit',
],
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => '',
'baseUrl' => ''
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
// 'cache' => [
// 'class' => 'yii\caching\FileCache',
// ],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'identityClass' => 'app\models\Account',
'loginUrl' => ['/authentication'],
'enableAutoLogin' => true
],
// 'session' => [
// 'class' => 'yii\web\Session',
// 'cookieParams' => ['lifetime' => 3600 * 24 * 30 * 12],
// 'timeout' => 3600 * 24 * 30 * 12,
// 'useCookies' => true,
// ],
'errorHandler' => [
'errorAction' => 'site/error',
'errorAction' => 'error',
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
@@ -45,42 +49,54 @@ $config = [
],
],
],
'db' => $db,
'arangodb' => [
'class' => '\explosivebit\arangodb\Connection',
'connectionOptions' => [
ConnectionOptions::OPTION_DATABASE => '',
ConnectionOptions::OPTION_ENDPOINT => '',
ConnectionOptions::OPTION_AUTH_TYPE => 'Basic',
ConnectionOptions::OPTION_AUTH_USER => '',
ConnectionOptions::OPTION_AUTH_PASSWD => '',
],
],
'arangodb' => require __DIR__ . '/db.php',
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [],
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'main'
],
'product/<id:\d+>' => 'product/index'
],
],
],
'params' => $params,
'modules' => [
'exchange' => [
'class' => 'carono\exchange1c\ExchangeModule',
'groupClass' => 'app\models\SupplyGroup',
'productClass' => 'app\models\Supply',
'offerClass' => 'app\models\SupplyEdgeProduct',
'partnerClass' => 'app\models\Account',
'documentClass' => 'app\models\Purchase',
'auth' => function ($mail, $pswd) {
// Необходимо уничтожить AccountForm
// return (new \app\models\AccountForm())->authentication($mail, $pswd);
if ($user = \app\models\Account::findByMail($mail)) {
if ($user->validatePassword($pswd)) {
return $user;
}
}
return false;
}
]
],
'params' => require __DIR__ . '/params.php',
];
if (YII_ENV_DEV) {
// configuration adjustments for 'dev' environment
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = [
'class' => 'yii\debug\Module',
// uncomment the following to add your IP if you are not connecting from localhost.
//'allowedIPs' => ['127.0.0.1', '::1'],
];
$config['bootstrap'][] = 'gii';
$config['modules']['gii'] = [
'class' => 'yii\gii\Module',
// uncomment the following to add your IP if you are not connecting from localhost.
//'allowedIPs' => ['127.0.0.1', '::1'],
'panels' => [
'ArangoDB' => [
'class' => 'explosivebit\arangodb\panels\arangodb\ArangoDbPanel'
]
]
];
}
return $config;
return $config;

View File

@@ -0,0 +1,93 @@
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\AccessControl;
use app\models\AccountForm;
class AuthenticationController extends Controller
{
/**
* {@inheritdoc}
*/
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'));
$model->scenario = $model::SCENARIO_AUTHENTICATION;
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest || $model->authentication()) {
// Аккаунт аутентифицирован
// Запись ответа
$return = [
'nav' => (new AccountForm())->deauthenticationGenHtml(),
'_csrf' => Yii::$app->request->getCsrfToken()
];
if (($cookies = Yii::$app->request->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
$return['main'] = $this->renderPartial($return['redirect'] . '/index');
// Очистка cookie
unset(Yii::$app->response->cookies['redirect']);
} else {
// Не найдено cookie с переадресацией
if (Yii::$app->request->pathInfo === 'authentication' || Yii::$app->request->pathInfo === 'registration') {
// Если клиент на промежуточном URI
// Запись ответа
$return['redirect'] = '/';
$return['main'] = $this->renderPartial('/index');
}
}
return $return;
} else {
// Аккаунт не аутентифицирован
Yii::$app->response->statusCode = 400;
return [
'main' => $this->renderPartial('/account', compact('model')),
'redirect' => '/authentication',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
}
if (!Yii::$app->user->isGuest) {
Yii::$app->response->redirect('/');
} else {
return $this->render('/account');
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use app\models\AccountForm;
class DeauthenticationController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
]
],
],
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'index' => ['post'],
],
]
];
}
public function actionIndex()
{
Yii::$app->response->format = Response::FORMAT_JSON;
// Выход из аккаунта
Yii::$app->user->logout();
// Инициализация
$model = new AccountForm(Yii::$app->request->post('AccountForm') ?? Yii::$app->request->get('AccountForm') ?? null);
// Ответа
return [
'nav' => $model->authenticationGenHtml($this->renderPartial('/account', compact('model'))),
'main' => $this->renderPartial('/index'),
'redirect' => '/',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\ErrorAction;
use yii\web\Response;
class ErrorController extends Controller
{
public function actionIndex()
{
$exception = Yii::$app->errorHandler->exception;
if ($exception !== null) {
// Исключение не выброшено
// Запись кода ошибки
$statusCode = $exception->statusCode;
// Запись названия ошибки
$name = match ($exception->statusCode) {
404 => '404 (Не найдено)',
default => $exception->getName()
};
// Запись сообщения об ошибке
$message = match ($exception->statusCode) {
404 => 'Страница не найдена',
default => $exception->getMessage()
};
return $this->render('/error', compact('exception', 'statusCode', 'name', 'message'));
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace app\controllers;
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-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
if (Yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
// Инициализация
$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()
];
} else {
// Аккаунт аутентифицирован
// Инициализация
$model = Yii::$app->user;
// Запись ответа
$return = [
'nav' => (new AccountForm())->deauthenticationGenHtml(),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
if (($cookies = Yii::$app->request->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
// Очистка cookie
unset(Yii::$app->response->cookies['redirect']);
}
return $return;
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
class MainController extends Controller
{
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Displays homepage.
*
* @return string
*/
public function actionIndex()
{
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/index'),
'redirect' => '/',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
return $this->render('/index');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use app\models\Product;
use yii\web\HttpException;
class ProductController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['add'],
'rules' => [
[
'allow' => false,
'roles' => ['?']
]
]
]
];
}
public function actionIndex(int $id)
{
if ($model = Product::readById($id)) {
// Товар найден
// Инициализация
$model = $model->getAttributes();
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index', compact('model')),
'redirect' => '/product/' . $id,
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('model'));
} else {
throw new HttpException(404);
}
}
}

View File

@@ -0,0 +1,119 @@
<?php
namespace app\controllers;
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;
class ProfileController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@']
],
[
'allow' => false,
'roles' => ['?'],
'verbs' => ['POST'],
'denyCallback' => [$this, 'accessDenied']
]
]
]
];
}
public function accessDenied()
{
$cookies = Yii::$app->response->cookies;
$cookies->add(new Cookie([
'name' => 'redirect',
'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-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index'),
'redirect' => '/profile',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('model'));
}
public function actionImport()
{
// Инициализация
$model = new Supply(Yii::$app->request->post('Supply') ?? Yii::$app->request->get('Supply'));
$model->scenario = $model::SCENARIO_IMPORT;
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
$model->file = UploadedFile::getInstances($model, 'file');
if (!$test = $model->import()) {
Yii::$app->response->statusCode = 409;
}
return [
'main' => $this->renderPartial('index', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('model'));
}
public static function readGroups()
{
// Инициализация
$groups = [];
foreach (SupplyGroup::readAll() as $group) {
// Перебор всех групп
// Генерация [КЛЮЧ => ИМЯ]
$groups[$group->_key] = $group->name;
}
return $groups;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace app\controllers;
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 actionIndex()
{
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
// Инициализация
$model = new AccountForm(Yii::$app->request->post('AccountForm'));
$model->scenario = $model::SCENARIO_REGISTRATION;
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest || $model->registration()) {
// Данные прошли проверку и аккаунт был создан
// Аутентификация
$model->scenario = $model::SCENARIO_AUTHENTICATION;
$model->authentication();
// Запись ответа
$return = [
'nav' => (new AccountForm())->deauthenticationGenHtml(),
'_csrf' => Yii::$app->request->getCsrfToken()
];
if (($cookies = Yii::$app->response->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
$return['main'] = $this->renderPartial($return['redirect'] . '/index');
// Очистка cookie
unset(Yii::$app->response->cookies['redirect']);
} else {
// Не найдено cookie с переадресацией
if (Yii::$app->request->pathInfo === 'authentication' || Yii::$app->request->pathInfo === 'registration') {
// Если клиент на промежуточном URI
// Запись ответа
$return['redirect'] = '/';
$return['main'] = $this->renderPartial('/index');
}
}
return $return;
} else {
// Данные не прошли проверку
Yii::$app->response->statusCode = 400;
return [
'main' => $this->renderPartial('/account', compact('model')),
'redirect' => '/registration',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
}
if (!Yii::$app->user->isGuest) {
Yii::$app->response->redirect('/');
} else {
return $this->render('/account', compact('model'));
}
}
}

View File

@@ -1,128 +0,0 @@
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
class SiteController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Displays homepage.
*
* @return string
*/
public function actionIndex()
{
return $this->render('index');
}
/**
* Login action.
*
* @return Response|string
*/
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
}
$model->password = '';
return $this->render('login', [
'model' => $model,
]);
}
/**
* Logout action.
*
* @return Response
*/
public function actionLogout()
{
Yii::$app->user->logout();
return $this->goHome();
}
/**
* Displays contact page.
*
* @return Response|string
*/
public function actionContact()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
}
return $this->render('contact', [
'model' => $model,
]);
}
/**
* Displays about page.
*
* @return string
*/
public function actionAbout()
{
return $this->render('about');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m201219_074926_create_account_collection extends Migration
{
public function up()
{
$this->createCollection('account');
}
public function down()
{
$this->dropCollection('account');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210101_092505_create_product_collection extends Migration
{
public function up()
{
$this->createCollection('product');
}
public function down()
{
$this->dropCollection('product');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210107_163448_create_supply_collection extends Migration
{
public function up()
{
$this->createCollection('supply');
}
public function down()
{
$this->dropCollection('supply');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210108_014505_create_account_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_supply');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210108_212826_create_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_group');
}
public function down()
{
$this->dropCollection('product_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210108_221446_create_product_edge_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_edge_product_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_edge_product_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210108_222132_create_supply_edge_product_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_product', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_product');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210108_222740_create_product_edge_product_collection extends Migration
{
public function up()
{
$this->createCollection('product_edge_product', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_edge_product');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210109_214817_create_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_group');
}
public function down()
{
$this->dropCollection('supply_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210109_214833_create_supply_edge_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_supply_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_supply_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210111_044635_create_supply_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_supply');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210112_010347_create_product_group_edge_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_group_edge_product_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_group_edge_product_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210112_010411_create_supply_group_edge_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_group_edge_supply_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_group_edge_supply_group');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210112_034135_create_requisite_collection extends Migration
{
public function up()
{
$this->createCollection('requisite');
}
public function down()
{
$this->dropCollection('requisite');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210112_034232_create_supply_edge_requisite_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_requisite', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_requisite');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210113_021800_create_purchase_collection extends Migration
{
public function up()
{
$this->createCollection('purchase');
}
public function down()
{
$this->dropCollection('purchase');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210113_021905_create_account_edge_purchase_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_purchase', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_purchase');
}
}

View File

@@ -0,0 +1,16 @@
<?php
use explosivebit\arangodb\Migration;
class m210113_021917_create_purchase_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('purchase_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('purchase_edge_supply');
}
}

View File

@@ -0,0 +1,140 @@
<?php
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()
{
return 'account';
}
public function attributes()
{
return array_merge(
parent::attributes(),
[
'mail',
'pswd',
'name',
'simc',
'sity',
'comp',
'taxn',
'auth'
]
);
}
public function rules()
{
return array_merge(
parent::rules(),
[
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
['mail', 'email'],
[['mail', 'comp', 'simc'], 'unique', 'message' => '2']
]
);
}
public function attributeLabels()
{
return array_merge(
parent::attributeLabels(),
[
'mail' => 'Почта',
'pswd' => 'Пароль',
'name' => 'Имя',
'simc' => 'Номер',
'sity' => 'Город',
'comp' => 'Компания',
'taxn' => 'ИНН',
'auth' => 'Аутентификационный хеш'
]
);
}
public function beforeSave($data)
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
$this->auth = Yii::$app->security->generateRandomString();
}
return true;
}
return false;
}
public function getExportFields1c($context = null)
{
return [
'Ид' => 'id',
'Наименование' => 'username',
'ПолноеНаименование' => 'full_name',
'Фамилия' => 'surname',
'Имя' => 'name',
];
}
public function getId(): string
{
return $this->_key;
}
public function readId(): string
{
return self::collectionName() . '/' . $this->getId();
}
public function getAuthKey()
{
return $this->auth;
}
public static function findIdentity($_key)
{
return static::findByKey($_key);
}
public static function findIdentityByAccessToken($pass, $type = null)
{
return static::findOne(['pass' => $pass]);
}
public static function findByMail($mail)
{
return static::findOne(['mail' => $mail]);
}
public static function findByKey($_key)
{
return static::findOne(['_key' => $_key]);
}
public static function validateMail($mail)
{
if (static::findByMail($mail)) {
// Почта найдена в базе данных
return false;
}
return true;
}
public function validatePassword($pswd)
{
return Yii::$app->security->validatePassword($pswd, $this->pswd);
}
public function validateAuthKey($auth)
{
return $this->getAuthKey() === $auth;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class AccountEdgePurchase extends Edge
{
public static function collectionName()
{
return 'account_edge_purchase';
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class AccountEdgeSupply extends Edge
{
public static function collectionName()
{
return 'account_edge_supply';
}
}

View File

@@ -0,0 +1,172 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use app\models\Account;
/**
* AccountForm is the model behind the login form.
*
* @property-read Account|null $account This property is read-only.
*
*/
class AccountForm extends Model
{
const SCENARIO_REGISTRATION = 'registration';
const SCENARIO_REGISTRATION_END = 'registration_end';
const SCENARIO_AUTHENTICATION = 'authentication';
public $mail;
public $pswd;
public $auto = true;
private $account = false;
public function rules()
{
return [
// Обязательные поля
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
// Функция "Запомнить меня"
['auto', 'boolean', 'on' => self::SCENARIO_AUTHENTICATION],
// Проверка почты,
['mail', 'email', 'message' => 'Проверьте почту'],
['mail', 'validateMail', 'on' => self::SCENARIO_REGISTRATION],
// Проверка пароля
['pswd', 'validatePassword', 'on' => self::SCENARIO_AUTHENTICATION]
];
}
public function attributeLabels()
{
return [
'mail' => 'Почта',
'pswd' => 'Пароль',
'auto' => '<i class="fas fa-lock"></i>'
];
}
public function validateMail($attribute, $params)
{
if (!$this->hasErrors()) {
// Проблем нет, обрабатывается событие регистрации
$account = $this->getAccount();
if (!$account || !$account->validateMail($this->mail)) {
// Проверка не пройдена
$this->addError($attribute, 'Почта уже привязана');
}
}
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
// Проблем нет, обрабатывается событие аутентификации
$account = $this->getAccount();
if (!$account || !$account->validatePassword($this->pswd)) {
// Проверка не пройдена
$this->addError($attribute, 'Проверьте пароль');
}
}
}
/**
* Logs in a account using the provided accountname and password.
* @return bool whether the account is logged in successfully
*/
public function authentication(string $mail = null, string $pswd = null)
{
if (isset($mail, $pswd)) {
$this->mail = $mail;
$this->pswd = $pswd;
}
if (isset($this->mail, $this->pswd) && $this->validate()) {
// Проверка пройдена
// Аутентификация
return Yii::$app->user->login($this->getAccount(), $this->auto ? 3600 * 24 * 30 : 0);
}
return false;
}
/**
* @return bool
*/
public function registration()
{
// Инициализация нового аккаунта
$this->account = new Account();
if (isset($this->mail, $this->pswd) && $this->validate()) {
// Проверка пройдена
// Запись параметров
$this->account->mail = $this->mail;
$this->account->pswd = Yii::$app->security->generatePasswordHash($this->pswd);
// Регистрация
return $this->account->save();
}
return false;
}
/**
* Finds account by [[accountname]]
*
* @return Account|null
*/
public function getAccount()
{
if ($this->account === false) {
$this->account = Account::findByMail($this->mail);
}
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;
}
}

View File

@@ -1,65 +0,0 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* ContactForm is the model behind the contact form.
*/
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public $verifyCode;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// name, email, subject and body are required
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
// verifyCode needs to be entered correctly
['verifyCode', 'captcha'],
];
}
/**
* @return array customized attribute labels
*/
public function attributeLabels()
{
return [
'verifyCode' => 'Verification Code',
];
}
/**
* Sends an email to the specified email address using the information collected by this model.
* @param string $email the target email address
* @return bool whether the model passes validation
*/
public function contact($email)
{
if ($this->validate()) {
Yii::$app->mailer->compose()
->setTo($email)
->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']])
->setReplyTo([$this->email => $this->name])
->setSubject($this->subject)
->setTextBody($this->body)
->send();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace app\models;
use Yii;
use explosivebit\arangodb\ActiveRecord;
abstract class Document extends ActiveRecord
{
public function attributes()
{
return [
'_key',
'date',
'writer'
];
}
public function attributeLabels()
{
return [
'date' => 'Дата',
'writer' => 'Аккаунт записавшего'
];
}
public function rules()
{
return [
[
'writer',
'string'
// Надо добавить проверку существования аккаунта
]
];
}
public function beforeSave($data)
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
}
$this->date = time();
$this->writer = $this->writer ?? Yii::$app->user->identity->readId();
return true;
}
return false;
}
public function readId(): ?string
{
return isset($this->_key) ? static::collectionName() . '/' . $this->_key : null;
}
public static function readAmount(): int
{
return static::find()->count();
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace app\models;
use explosivebit\arangodb\ActiveRecord;
abstract class Edge extends Document
{
public function attributes()
{
return array_merge(
parent::attributes(),
[
'_from',
'_to',
'type'
]
);
}
public function attributeLabels()
{
return array_merge(
parent::attributeLabels(),
[
'date' => 'От кого',
'date' => 'К кому',
'type' => 'Тип'
]
);
}
public function rules()
{
return array_merge(
parent::rules(),
[
[
['_from', '_to'],
'required',
'message' => 'Заполните поле: {attribute}'
],
[
'type',
'string'
]
]
);
}
/**
* Записать
*/
public function write(string $_from, string $_to, string $type = '', array $data = []): ?static
{
// Инициализация
$edge = isset($this->_key) ? $this : new static;
// Настройка
$edge->_from = $_from;
$edge->_to = $_to;
$edge->type = $type;
foreach ($data as $key => $value) {
if(is_int($key)) {
// Если ключ задан автоматически
$edge->{$value} = true;
} else {
// Иначе ключ записан вручную
$edge->{$key} = $value;
}
}
// Запись
$edge->save();
return $edge;
}
public function beforeSave($data)
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
}
$this->type = $this->type ?? '';
return true;
}
return false;
}
}

View File

@@ -1,81 +0,0 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* LoginForm is the model behind the login form.
*
* @property-read User|null $user This property is read-only.
*
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
/**
* Logs in a user using the provided username and password.
* @return bool whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
}
return false;
}
/**
* Finds user by [[username]]
*
* @return User|null
*/
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace app\models;
use moonland\phpexcel\Excel;
class Product extends Document
{
const SCENARIO_IMPORT = 'import';
const SCENARIO_WRITE = 'write';
public $file;
public $group;
public static function collectionName(): string
{
return 'product';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'name',
'ocid',
'catn',
'oemn'
// 'data',
// 'cost',
// 'time'
]
);
}
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
['name', 'catn'],
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_WRITE,
'except' => self::SCENARIO_IMPORT
],
[
'file',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT
],
['catn', 'string', 'message' => '{attribute} должен быть строкой'],
// ['oemn', 'integer'], Нужна своя проверка на массив
[
'file',
'file',
'skipOnEmpty' => false,
'extensions' => 'xlsx',
'checkExtensionByMimeType' => false,
'maxFiles' => 5,
'maxSize' => 1024 * 1024 * 30,
'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"',
'message' => 'Проблема при чтении документа',
'on' => self::SCENARIO_IMPORT
]
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название (name)',
'ocid' => 'Идентификатор 1C (ocid)',
'catn' => 'Каталожный номер (catn)',
'oemn' => 'OEM номера (oemn)',
// 'data' => 'Данные товара (data)',
// 'cost' => 'Цены (cost)',
// 'time' => 'Сроки доставки (time)',
'file' => 'Документ',
'group' => 'Группа'
]
);
}
public function import()
{
// Инициализация массива данных
$data = [];
if ($this->validate()) {
foreach ($this->file as $file) {
// Перебор файлов
// Сохранение на диск
if (!file_exists('../assets/import/excel/')) {
mkdir('../assets/import/excel/', 0775, true);
}
$file->saveAs($path = '../assets/import/excel/' . $file->baseName . '.' . $file->extension);
// Проверка файла пройдена
$data[] = Excel::import($path, [
'setFirstRecordAsKeys' => true,
'setIndexSheetByName' => true,
]);
}
foreach ($data[0] as $doc) {
// Перебор полученных документов
// Сохранение в базе данных
$product = new static($doc);
if ($product->validate()) {
// Проверка пройдена
// Запись документа
$product->save();
// Запись группы
$group = static::class . 'Group';
(new $group())->writeMember($product, $this->group);
} else {
// Проверка не пройдена
foreach ($product->errors as $attribute => $error) {
$this->addError($attribute, $error);
}
}
}
return true;
}
return false;
}
private static function writeEdgeBetweenGroup(string $from, string $to): bool
{
// Инициализация
$edge = new SupplyEdgeSupplyGroup();
// Настройка
$edge->_from = $from;
$edge->_to = $to;
// Запись
return $edge->save();
}
private static function writeEdgeBetweenRequisite(string $from, string $to): bool
{
// Инициализация
$edge = new SupplyEdgeRequisite();
// Настройка
$edge->_from = $from;
$edge->_to = $to;
// Запись
return $edge->save();
}
public static function readById(string $_key): ?Product
{
return self::findOne(['_key' => $_key]);
}
/**
* Поиск по каталожному номеру
*/
public static function readByCatn(string $catn): ?Product
{
return self::findOne(['catn' => $catn]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class ProductEdgeProduct extends Edge
{
public static function collectionName()
{
return 'product_edge_product';
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class ProductEdgeProductGroup extends Edge
{
public static function collectionName()
{
return 'product_edge_product_group';
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace app\models;
use carono\exchange1c\interfaces\GroupInterface;
use Zenwalker\CommerceML\Model\Group;
class ProductGroup extends Document implements GroupInterface
{
public static function collectionName()
{
return 'product_group';
}
public function attributes()
{
return array_merge(
parent::attributes(),
['name', 'onec_id', 'onec_prnt_id']
);
}
public function rules()
{
return array_merge(
parent::rules(),
[
// [
// 'name',
// 'required',
// 'message' => 'Заполните поле: {attribute}'
// ]
]
);
}
public function attributeLabels()
{
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;
}
/**
* Создание дерева групп
* в параметр передаётся массив всех групп (import.xml > Классификатор > Группы)
* $groups[0]->parent - родительская группа
* $groups[0]->children - дочерние группы
*
* @param Group[] $groups
*/
public static function createTree1c($groups): Document|null
{
foreach ($groups as $group) {
// Перебор групп
// Создание
$parent = static::createByML($group);
if ($children = $group->getChildren()) {
// Найден потомок
// Вход в рекурсию
$children = static::createTree1c($children);
// Запись рёбер с родителем
static::writeEdgeBetweenGroup($parent::collectionName() . '/' . $parent->_key, $children::collectionName() . '/' . $children->_key);
return $children;
}
return $parent;
}
}
/**
* Создаём группу по модели группы CommerceML
* проверяем все дерево родителей группы, если родителя нет в базе - создаём
*
* @param Group $group
*/
public static function createByML(Group $group): static|array|null
{
if (!$model = static::readByOnecId($group->id)) {
// Группа не найдена
// Инициализация
$model = new static;
$model->onec_id = $group->id;
}
$model->name = $group->name;
if ($parent = $group->getParent()) {
// Найден родитель
// Инициализация (рекурсия)
$parentModel = static::createByML($parent);
$model->onec_prnt_id = $parentModel->id;
unset($parentModel);
} else {
$model->onec_prnt_id = null;
}
$model->save();
return $model;
}
private static function writeEdgeBetweenMember(string $from, string $to): bool
{
// Инициализация
$edge = new ProductEdgeProductGroup();
// Настройка
$edge->_from = $from;
$edge->_to = $to;
// Запись
return $edge->save();
}
private static function writeEdgeBetweenGroup(string $from, string $to): bool
{
// Инициализация
$edge = new ProductGroupEdgeProductGroup();
// Настройка
$edge->_from = $from;
$edge->_to = $to;
// Запись
return $edge->save();
}
public static function readAll()
{
return static::find()->all();
}
public static function readByName(string $name)
{
return static::findOne(['name' => $name]);
}
/**
* Название поля в котором хранится ID из 1C
*/
public static function getIdFieldName1c(): string
{
return 'onec_id';
}
public static function readByOnecId(string $onec_id): ?ProductGroup
{
return static::findOne(['onec_id' => $onec_id]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class ProductGroupEdgeProductGroup extends Edge
{
public static function collectionName()
{
return 'product_group_edge_product_group';
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace app\models;
use carono\exchange1c\interfaces\DocumentInterface;
class Purchase extends Document implements DocumentInterface
{
public static function collectionName(): string
{
return 'purchase';
}
/**
* @return DocumentInterface[]
*/
public static function findDocuments1c(): ?self
{
return self::find()->andWhere(['status_id' => 2])->all();
}
/**
* @return OfferInterface[]
*/
public function getOffers1c(): mixed
{
return true;
}
public function getRequisites1c(): mixed
{
return true;
}
/**
* Получаем контрагента у документа
*
* @return PartnerInterface
*/
public function getPartner1c(): Account
{
// !!!!!!!!!!!!!!!!!!!
return $this->user ?? new Account;
}
public function getExportFields1c($context = null)
{
return [];
}
/**
* Возвращаем имя поля в базе данных, в котором хранится ID из 1с
*
* @return string
*/
public static function getIdFieldName1c()
{
return 'onec["Ид"]';
}
public function setRaw1cData($cml, $object): void
{
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class PurchaseEdgeSupply extends Edge
{
public static function collectionName()
{
return 'purchase_edge_supply';
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace app\models;
class Requisite extends Document
{
public static function collectionName()
{
return 'requisite';
}
public function attributes()
{
return array_merge(
parent::attributes(),
['name', 'value']
);
}
public function rules()
{
return array_merge(
parent::rules(),
[
[
['name', 'value'],
'required',
'message' => 'Заполните поле: {attribute}'
]
]
);
}
public function attributeLabels()
{
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название',
'value' => 'Значение'
]
);
}
public static function readByName(string $name)
{
return static::findOne(['name' => $name]);
}
}

View File

@@ -0,0 +1,187 @@
<?php
namespace app\models;
use Yii;
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
class Supply extends Product implements ProductInterface
{
use Xml2Array;
public static function collectionName(): string
{
return 'supply';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'onec'
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'onec' => 'Данные 1C'
]
);
}
public function afterSave($data, $vars): void
{
// Запись ребра: АККАУНТ -> ПОСТАВКА
(new AccountEdgeSupply)->write(Yii::$app->user->identity->readId(), $this->readId(), 'import');
}
public function setRequisite1c($name, $value): mixed
{
return true;
}
/**
* Установка группы, где находится продукт
*/
public function setGroup1c($group): mixed
{
// Чтение группы
// if ($group = SupplyGroup::readByOnecId($group->id)) {
// // Запись ребра: ПОСТАВКА => ГРУППА ПОСТАВОК
// return static::writeEdgeBetweenGroup(static::collectionName() . '/' . $this->_key, $group->collectionName() . '/' . $group->_key);
// }
return true;
}
public static function createProperties1c($properties): mixed
{
return true;
}
public function setProperty1c($property): mixed
{
return true;
}
public function addImage1c($path, $caption): mixed
{
return true;
}
/**
* Запись ребра (предложения от поставки к продукту)
*/
public function getOffer1c($offer): SupplyEdgeProduct
{
if (empty($this->catn)) {
// Не передан каталожный номер
// Разработчику библеотеки надо дать по жопе
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)
]
);
}
/**
* Создать продукт
*/
public static function createModel1c($product): self
{
// Инициализация
$model = self::readByOnecId($id = (string) $product->Ид) ?? new self;
// Настройки
$model->ocid = $id ?? null;
$model->catn = (string) $product->Артикул;
$model->oemn = null;
$model->onec = self::xml2array($product->xml);
// Запись
$model->save();
return $model;
}
protected function initProduct(): bool
{
// Надо не забыть сделать выборку полей и ручное подключение
if (empty($this->catn)) {
// Не передан каталожный номер
return false;
} else if (Product::readByCatn($this->catn)) {
// Продукт уже был инициализирован
return true;
}
// Инициализация
$product = new Product();
// Настройки
$product->catn = $this->catn;
// Запись
return $product->save();
}
public function setPrice1c($price): void
{
}
public function setRaw1cData($cml, $object): bool
{
return false;
}
public static function readByOnecId(string $ocid): ?Supply
{
return self::findOne([self::getIdFieldName1c() => $ocid]);
}
public function getGroup1c(): SupplyGroup
{
return $this->group;
}
/**
* Название поля в котором хранится ID из 1C
*/
public static function getIdFieldName1c(): string
{
return 'ocid';
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace app\models;
use app\models\traits\Xml2Array;
use carono\exchange1c\interfaces\OfferInterface;
use Zenwalker\CommerceML\Model\Offer;
class SupplyEdgeProduct extends Edge implements OfferInterface
{
use Xml2Array;
public static function collectionName(): string
{
return 'supply_edge_product';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'ocid',
'onec'
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'ocid' => 'Идентификатор 1C (ocid)',
'onec' => 'Данные 1C'
]
);
}
public static function createPriceTypes1c($types): mixed
{
return true;
}
public function setPrice1c($price): mixed
{
return true;
}
public function setSpecification1c($specification): mixed
{
return true;
}
public function getExportFields1c($context = null): array
{
return [];
}
public function getGroup1c(): ProductGroup
{
return $this->group ?? new ProductGroup;
}
public static function readByOnecId(string $ocid): ?Supply
{
return self::findOne([self::getIdFieldName1c() => $ocid]);
}
/**
* Название поля в котором хранится ID из 1C
*/
public static function getIdFieldName1c(): string
{
return 'ocid';
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class SupplyEdgeRequisite extends Edge
{
public static function collectionName()
{
return 'supply_edge_requisite';
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class SupplyEdgeSupply extends ProductEdgeProduct
{
public static function collectionName()
{
return 'supply_edge_supply';
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class SupplyEdgeSupplyGroup extends ProductEdgeProductGroup
{
public static function collectionName()
{
return 'supply_edge_supply_group';
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\models;
class SupplyGroup extends ProductGroup
{
public static function collectionName()
{
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();
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\models;
class SupplyGroupEdgeSupplyGroup extends Edge
{
public static function collectionName()
{
return 'supply_group_edge_supply_group';
}
}

View File

@@ -1,104 +0,0 @@
<?php
namespace app\models;
class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface
{
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;
private static $users = [
'100' => [
'id' => '100',
'username' => 'admin',
'password' => 'admin',
'authKey' => 'test100key',
'accessToken' => '100-token',
],
'101' => [
'id' => '101',
'username' => 'demo',
'password' => 'demo',
'authKey' => 'test101key',
'accessToken' => '101-token',
],
];
/**
* {@inheritdoc}
*/
public static function findIdentity($id)
{
return isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
}
/**
* {@inheritdoc}
*/
public static function findIdentityByAccessToken($token, $type = null)
{
foreach (self::$users as $user) {
if ($user['accessToken'] === $token) {
return new static($user);
}
}
return null;
}
/**
* Finds user by username
*
* @param string $username
* @return static|null
*/
public static function findByUsername($username)
{
foreach (self::$users as $user) {
if (strcasecmp($user['username'], $username) === 0) {
return new static($user);
}
}
return null;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function getAuthKey()
{
return $this->authKey;
}
/**
* {@inheritdoc}
*/
public function validateAuthKey($authKey)
{
return $this->authKey === $authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @return bool if password provided is valid for current user
*/
public function validatePassword($password)
{
return $this->password === $password;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace app\models\traits;
trait Xml2Array {
protected static function xml2array($xmlObject, $out = [])
{
foreach ((array) $xmlObject as $index => $node)
$out[$index] = (is_object($node) || is_array($node)) ? self::xml2array($node) : $node;
return $out;
}
}

View File

@@ -0,0 +1,43 @@
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use app\models\AccountForm;
?>
<div class="container d-flex flex-column h-100">
<div class="row my-auto">
<div class="mx-auto">
<?php
$form = ActiveForm::begin([
'id' => 'form_account',
'action' => false,
'fieldConfig' => [
'template' => '{label}{input}{error}',
'options' => ['class' => '']
],
'options' => [
'class' => '',
'onsubmit' => 'return false;'
]
]);
$model = $model ?? new AccountForm;
?>
<?= $form->field($model, 'mail', ['enableLabel' => false])->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">
<?= Html::submitButton('Войти', ['name' => 'submitAuthentication', 'onclick' => 'authentication(this.parentElement.parentElement);', 'class' => 'flex-grow-1 mr-2 btn btn-primary button_clean']) ?>
<?= $form->field($model, 'auto', ['checkboxTemplate' => '<div class="checkbox button_clean">{beginLabel}' .
Html::submitButton('{labelTitle}', ['name' => 'submit', 'data-toggle' => 'button', 'class' => 'w-100 btn btn-primary button_clean', 'aria-pressed' => 'false']) .
'{endLabel}</div>'])->checkbox()->label($model->getAttributeLabel('auto'), ['class' => 'w-100 m-0']) ?>
</div>
<?= Html::submitButton('Регистрация', ['name' => 'submitRegistration', 'onclick' => 'registration(this.parentElement);', 'class' => 'col-12 ml-auto btn btn-success btn-sm button_clean']) ?>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,16 @@
<?php
use yii\helpers\Html;
$this->title = $name;
?>
<div id="page_error" class="container py-3">
<h1><?= Html::encode($this->title) ?></h1>
<div class="alert alert-danger">
<?= nl2br(Html::encode($message)) ?>
</div>
</div>

View File

@@ -0,0 +1,86 @@
<?php
/* @var $this yii\web\View */
$this->title = 'SkillParts';
?>
<link href="/css/ticker.css" rel="stylesheet">
<div id="page_index">
<div 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>
<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>
<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>
<div class="container mb-4">
<!-- <div class="row mb-3">
<h4 class="col gilroy categories_blocks_panel_title">Сопутствующие товары</h4>
</div> -->
<div class="row mb-5 mb-md-0 px-3 px-md-0">
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Масла, смазки</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Масла моторные</dd>
<dd>Масла трансмиссионные</dd>
<dd>Масла гидравлические</dd>
<dd>Смазки</dd>
</dl>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Электрооборудование</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Фары и свет</dd>
</dl>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Инструмент</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Шприцы для смазки </dd>
<dd>Ключи, съёмники</dd>
<dd>Наборы инструментов</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<script src="/js/ticker.js" defer></script>

View File

@@ -7,8 +7,11 @@ use yii\helpers\Html;
use app\assets\AppAsset;
AppAsset::register($this);
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
@@ -17,92 +20,77 @@ AppAsset::register($this);
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php $this->registerCsrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<title><?= Html::encode($this->title ?? 'SkillParts') ?></title>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<header>
<div class="container">
<div class="row mt-3 mt-sm-0 pt-3">
<div class="col-3 col-sm-4 col-md-2 d-flex flex-column justify-content-end logotype">
<img class="img-fluid" src="/img/logos/skillparts.png" alt="SkillParts">
</div>
<div class="col ml-auto text-right d-flex flex-column justify-content-end">
<div class="row">
<div class="col">
<p class="m-0"><i class="fas fa-shopping-cart mr-4"></i><i class="fas fa-list mr-4"></i><a>Личный кабинет</a></p>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col">
<div class="h-divider d-flex">
<div class="col-1 ml-auto p-0 h-divider-title-left"></div>
<div class="col-5 col-lg-4 h-divider-title d-flex flex-column justify-content-center">
<h6 class="text-center text-white my-0"><b>Запчасти для спецтехники</b></h6>
</div>
<div class="col-1 mr-auto p-0 h-divider-title-right"></div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 pr-0 pr-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">
<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">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
<div id="searchPanel" class="col">
<!-- <input id="catalog_search_panel_button_1" class="btn btn-sm button_clean" type="radio" name="catalog_search_panel_buttons" value="catalog_search_panel_button_1" checked>
<label class="mb-0 px-3 px-md-4 py-1" for="catalog_search_panel_button_1">Номер детали</label>
<input id="catalog_search_panel_button_2" class="btn btn-sm text-white button_clean" type="radio" name="catalog_search_panel_buttons" value="catalog_search_panel_button_2">
<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>
<header class="container pt-2 mt-1 mb-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 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>
</div>
<div class="h-divider"></div>
</header>
<main>
<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">
<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">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
<div id="searchPanel" class="col">
<!-- <input id="catalog_search_panel_button_1" class="btn btn-sm button_clean" type="radio" name="catalog_search_panel_buttons" value="catalog_search_panel_button_1" checked>
<label class="mb-0 px-3 px-md-4 py-1" for="catalog_search_panel_button_1">Номер детали</label>
<input id="catalog_search_panel_button_2" class="btn btn-sm text-white button_clean" type="radio" name="catalog_search_panel_buttons" value="catalog_search_panel_button_2">
<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>
</div>
</div>
</aside>
<main class="col p-0">
<?= $content ?>
</main>
<footer class="py-4">
<div class="container">
<div class="row px-3">
<div class="col-12 col-md-auto mr-md-5">
<h5 class="row mb-2"><b>Контакты</b></h5>
<small class="row mb-1"><b>Адрес:&nbsp;</b>Хабаровск, Промышленная 3, 105</small>
<small class="row mb-1"><b>Время работы:&nbsp;</b>пн-пт 09:00-18:00</small>
<small class="row mb-1"><b>Телефон:&nbsp;</b>+7 (4212) 35-85-34</small>
<small class="row mb-1"><b>Почта:&nbsp;</b>info@skillparts.ru</small>
</div>
<div class="col-md-auto mr-md-5 partnership">
<h5 class="row mb-2"><b>Партнёрство</b></h5>
<small class="row mb-1"><a>Оптовым покупателям</a></small>
<small class="row mb-1"><a>Поставщикам</a></small>
<small class="row mb-1"><a>Партнерская сеть</a></small>
</div>
<!-- <p class="pull-left">&copy; My Company <?= date('Y') ?></p>
<p class="pull-right"><?= Yii::powered() ?></p> -->
<footer class="container py-4">
<div class="row px-3">
<div class="col-12 col-md-auto mr-md-5">
<h5 class="row mb-2"><b>Контакты</b></h5>
<small class="row mb-1"><b>Адрес:&nbsp;</b>Хабаровск, Промышленная 3, 105</small>
<small class="row mb-1"><b>Время работы:&nbsp;</b>пн-пт 09:00-18:00</small>
<small class="row mb-1"><b>Телефон:&nbsp;</b>+7 (4212) 35-85-34</small>
<small class="row mb-1"><b>Почта:&nbsp;</b>info@skillparts.ru</small>
</div>
<div class="col-md-auto mr-md-5 partnership">
<h5 class="row mb-2"><b>Партнёрство</b></h5>
<small class="row mb-1"><a>Оптовым покупателям</a></small>
<small class="row mb-1"><a>Поставщикам</a></small>
<small class="row mb-1"><a>Партнерская сеть</a></small>
</div>
</div>
</footer>
@@ -111,4 +99,5 @@ AppAsset::register($this);
</body>
</html>
<?php $this->endPage() ?>

View File

@@ -0,0 +1,18 @@
<?php
?>
<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>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,61 @@
<?php
use yii\bootstrap\ActiveForm;
use app\controllers\ProfileController;
use app\models\Product;
use app\models\Supply;
?>
<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>
</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>
</div>
</div>
<script src="/js/profile.js" defer></script>

View File

@@ -1,18 +0,0 @@
<?php
/* @var $this yii\web\View */
use yii\helpers\Html;
$this->title = 'About';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-about">
<h1><?= Html::encode($this->title) ?></h1>
<p>
This is the About page. You may modify the following file to customize its content:
</p>
<code><?= __FILE__ ?></code>
</div>

View File

@@ -1,68 +0,0 @@
<?php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\ContactForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
<h1><?= Html::encode($this->title) ?></h1>
<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<div class="alert alert-success">
Thank you for contacting us. We will respond to you as soon as possible.
</div>
<p>
Note that if you turn on the Yii debugger, you should be able
to view the mail message on the mail panel of the debugger.
<?php if (Yii::$app->mailer->useFileTransport): ?>
Because the application is in development mode, the email is not sent but saved as
a file under <code><?= Yii::getAlias(Yii::$app->mailer->fileTransportPath) ?></code>.
Please configure the <code>useFileTransport</code> property of the <code>mail</code>
application component to be false to enable email sending.
<?php endif; ?>
</p>
<?php else: ?>
<p>
If you have business inquiries or other questions, please fill out the following form to contact us.
Thank you.
</p>
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
<?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'email') ?>
<?= $form->field($model, 'subject') ?>
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>
<div class="form-group">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php endif; ?>
</div>

View File

@@ -1,27 +0,0 @@
<?php
/* @var $this yii\web\View */
/* @var $name string */
/* @var $message string */
/* @var $exception Exception */
use yii\helpers\Html;
$this->title = $name;
?>
<div class="site-error">
<h1><?= Html::encode($this->title) ?></h1>
<div class="alert alert-danger">
<?= nl2br(Html::encode($message)) ?>
</div>
<p>
The above error occurred while the Web server was processing your request.
</p>
<p>
Please contact us if you think this is a server error. Thank you.
</p>
</div>

View File

@@ -1,77 +0,0 @@
<?php
/* @var $this yii\web\View */
$this->title = 'SkillParts';
?>
<div class="info_panel mb-4">
<div class="container h-100 d-flex flex-column justify-content-center">
<p class="col-lg-8 mb-4 gilroy">Проблема с подбором запчастей?</p>
<button class="text-white btn button_clean button_call">Связаться с менеджером</button>
</div>
<div class="h-100 d-flex flex-column justify-content-end">
<img class="img-fluid" src="/img/photos/963K_cutted.webp" alt="Связаться с менеджером">
</div>
</div>
<div class="h-100 d-flex ticker">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/cummins.png" alt="Cummins">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/iveco.png" alt="Iveco">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/komatsu.png" alt="Komatsu">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/case.png" alt="Case">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/isuzu.png" alt="Isuzu">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/new_holland.png" alt="New Holland">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/perkins.png" alt="Perkins">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/john_deere.png" alt="John Deere">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/caterpillar.png" alt="Caterpillar">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/shantui.png" alt="Shantui">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/xcmg.png" alt="XCMG">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/kobelco.png" alt="Kobelco">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/shehwa.png" alt="SHEHWA">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/bomag.png" alt="BOMAG">
<img class="w-auto h-75 mr-3 my-auto" src="/img/logos/hitachi.png" alt="Hitachi">
</div>
<div class="container mb-4">
<!-- <div class="row mb-3">
<h4 class="col gilroy categories_blocks_panel_title">Сопутствующие товары</h4>
</div> -->
<div class="row mb-5 mb-md-0 px-3 px-md-0">
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Масла, смазки</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Масла моторные</dd>
<dd>Масла трансмиссионные</dd>
<dd>Масла гидравлические</dd>
<dd>Смазки</dd>
</dl>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Электрооборудование</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Фары и свет</dd>
</dl>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4 mb-4 mb-lg-0 py-0 d-flex flex-column">
<div class="px-3 px-xl-4 pt-3 d-inline-block category_block_title">
<h4 class="m-0">Инструмент</h4>
</div>
<div class="p-3 px-md-4 category_block">
<dl class="mb-0">
<dd>Шприцы для смазки </dd>
<dd>Ключи, съёмники</dd>
<dd>Наборы инструментов</dd>
</dl>
</div>
</div>
</div>
</div>

View File

@@ -1,47 +0,0 @@
<?php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\LoginForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-login">
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'layout' => 'horizontal',
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
'labelOptions' => ['class' => 'col-lg-1 control-label'],
],
]); ?>
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'rememberMe')->checkbox([
'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>",
]) ?>
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
<div class="col-lg-offset-1" style="color:#999;">
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
To modify the username/password, please check out the code <code>app\models\User::$users</code>.
</div>
</div>

View File

@@ -18,15 +18,15 @@ ssl_certificate_key '%sprogdir%/userdata/config/cert_files/server.key'
# ssl_trusted_certificate '';
# Force HTTPS
# add_header Strict-Transport-Security 'max-age=2592000' always;
# if ($scheme ~* ^(?!https).*$) {
# return 301 https://$host$request_uri;
# }
add_header Strict-Transport-Security 'max-age=2592000' always;
if ($scheme ~* ^(?!https).*$) {
return 301 https://$host$request_uri;
}
# Force www.site.com => site.com
# if ($host ~* ^www\.(.+)$) {
# return 301 $scheme://$1$request_uri;
# }
if ($host ~* ^www\.(.+)$) {
return 301 $scheme://$1$request_uri;
}
# Disable access to backup/config/command/log files
# if ($uri ~* ^.+\.(?:bak|co?nf|in[ci]|log|orig|sh|sql|tar|sql|t?gz|cmd|bat)$) {
@@ -70,38 +70,6 @@ location / {
include '%sprogdir%/userdata/config/nginx_fastcgi_params.txt';
}
}
# Service configuration (do not edit!)
# ----------------------------
location /openserver/ {
root '%sprogdir%/modules/system/html';
autoindex off;
index index.php index.html index.htm;
%allow%allow all;
allow 127.0.0.0/8;
allow ::1/128;
allow %ips%;
deny all;
location ~* ^/openserver/.+\.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv|svgz?|ttf|ttc|otf|eot|woff2?)$ {
expires 1d;
access_log off;
}
location /openserver/server-status {
stub_status on;
}
location ~ ^/openserver/.*\.php$ {
try_files $fastcgi_script_name =404;
fastcgi_index index.php;
fastcgi_pass backend;
include '%sprogdir%/userdata/config/nginx_fastcgi_params.txt';
}
}
# End service configuration
# ----------------------------
}
# ----------------------------

View File

@@ -1,11 +1,21 @@
header {
height: 4rem;
background-color: #fff;
}
nav {
height: max-content;
}
#profile_button+.dropdown-menu-long {
width: 250px;
}
#profile_button+.dropdown-menu #form_account .help-block-error {
margin-bottom: .5rem;
}
.h-divider {
margin-top: -20px;
position: relative;
pointer-events: none;
border-bottom: 4px solid #123EAB;
}

View File

@@ -27,17 +27,14 @@
}
.info_panel .button_call {
width: 350px;
margin-left: 2vw;
background-color: #123EAB;
width: max-content;
border-radius: 0px 5px 5px 0px;
}
.info_panel .button_call:hover {
background-color: #1b4bc4;
}
.info_panel .button_call:active {
background-color: #402d82;
.info_panel .button_call_icon {
font-size: 1rem;
border-radius: 5px 0px 0px 5px;
background-color: #132f77;
}
@media (max-width: 400px) {}

View File

@@ -12,6 +12,10 @@ a {
cursor: pointer;
}
a:hover {
text-decoration: none;
}
button {
transition: 0s !important;
}
@@ -28,15 +32,51 @@ main {
background-color: #f0eefb;
}
.block_sidebar {
background-color: #fff;
}
.block_main {
background-color: #fff;
}
.button_clean, .button_clean:hover, .button_clean:focus, .button_clean:active {
outline: none !important;
box-shadow: none !important;
}
.button_blue {
background-color: #123EAB;
}
.button_blue:hover {
background-color: #1b4bc4;
}
.button_blue:active {
background-color: #402d82;
}
.button_white {
background-color: #fff;
}
.button_white:hover {
background-color: #eaebee;
}
.button_white:active {
background-color: #cfd3dd;
}
.d-inline-block {
display: inline-block;
}
.form-group:last-child {
padding: 0;
}
.gilroy {
font-family: 'Gilroy';
}

View File

@@ -1,3 +1,7 @@
.ticker img {
object-fit: contain;
}
.bx-wrapper {
margin-bottom: 1.5rem !important;
width: 100vw;
@@ -8,5 +12,5 @@
.bx-wrapper .bx-viewport {
width: 100vw !important;
height: 4rem !important;
height: 2rem !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Some files were not shown because too many files have changed in this diff Show More