From 7865cac5d473d7f7946d2d8558c5375b6a8d8a50 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Mon, 19 Apr 2021 07:11:38 +1000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=20=D0=94=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=B5=D0=9B=D0=B8=D0=BD=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 6 +- .../system/commands/DellinController.php | 20 ++ .../controllers/NotificationController.php | 2 +- .../system/controllers/ProductController.php | 51 +++ .../system/controllers/ProfileController.php | 25 +- .../system/controllers/SearchController.php | 5 + mirzaev/skillparts/system/models/Account.php | 101 +++++- mirzaev/skillparts/system/models/City.php | 18 +- mirzaev/skillparts/system/models/Document.php | 4 +- mirzaev/skillparts/system/models/Product.php | 4 +- mirzaev/skillparts/system/models/Supply.php | 51 ++- .../system/models/connection/Dellin.php | 291 ++++++++++++++---- .../system/models/traits/SearchByEdge.php | 42 ++- .../skillparts/system/views/product/index.php | 43 +-- .../skillparts/system/views/profile/index.php | 46 ++- .../skillparts/system/views/search/index.php | 64 ++-- .../skillparts/system/web/js/product_panel.js | 72 ++++- mirzaev/skillparts/system/yii | 1 + 18 files changed, 676 insertions(+), 170 deletions(-) create mode 100644 mirzaev/skillparts/system/commands/DellinController.php diff --git a/composer.lock b/composer.lock index 57392e1..7583990 100644 --- a/composer.lock +++ b/composer.lock @@ -32,7 +32,7 @@ "version": "3.3.11", "source": { "type": "git", - "url": "https://github.com/RobinHerbots/Inputmask.git", + "url": "git@github.com:RobinHerbots/Inputmask.git", "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b" }, "dist": { @@ -1016,7 +1016,7 @@ "source": { "type": "git", "url": "https://git.hood.su/mirzaev/yii2/arangodb", - "reference": "bb7b1b8f8e26c409f9c113a6bd459e3274e94599" + "reference": "cbc26916ea54bb767306e4c2750e710b08eecc21" }, "require": { "php": "^8.0.0", @@ -1058,7 +1058,7 @@ "ArangoDb", "yii2" ], - "time": "2021-04-10T16:39:51+00:00" + "time": "2021-04-11T19:27:43+00:00" }, { "name": "mirzaev/yii2-arangodb-sessions", diff --git a/mirzaev/skillparts/system/commands/DellinController.php b/mirzaev/skillparts/system/commands/DellinController.php new file mode 100644 index 0000000..228cbe7 --- /dev/null +++ b/mirzaev/skillparts/system/commands/DellinController.php @@ -0,0 +1,20 @@ + yii::$app->request->getCsrfToken() + ]; + + if (is_null($catn)) { + // Не получен артикул + + yii::$app->response->statusCode = 500; + + goto end; + } + + if ($product = Product::searchByCatn($catn)) { + // Товар найден + + // Инициализация + $text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0'; + $text or $text = '0'; + + $product->wght = $text; + + if ($product->save()) { + // Товар обновлён + + $return['wght'] = $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 { // Инициализация diff --git a/mirzaev/skillparts/system/controllers/ProfileController.php b/mirzaev/skillparts/system/controllers/ProfileController.php index 26287e2..698a627 100644 --- a/mirzaev/skillparts/system/controllers/ProfileController.php +++ b/mirzaev/skillparts/system/controllers/ProfileController.php @@ -91,10 +91,6 @@ class ProfileController extends Controller */ public function actionIndex(): string|array { - $browser = new Dellin(); - - $browser->syncCities(); - // Инициализация $model = yii::$app->user->identity; $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); @@ -122,8 +118,14 @@ class ProfileController extends Controller } // Инициализация - $account_sections_city_list = null; - $import_sections_oem_list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]')); + $delivery_from_city_list = $model->genListCitiesFrom(); + $delivery_to_city_list = $model->genListCitiesTo(); + $import_oem_list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]')); + + // Сортировка по алфавиту + asort($delivery_from_city_list); + asort($delivery_to_city_list); + asort($import_oem_list); if (yii::$app->request->isPost) { // POST-запрос @@ -134,8 +136,9 @@ class ProfileController extends Controller 'main' => $this->renderPartial('index', compact( 'model', 'sidebar', - 'account_sections_city_list', - 'import_sections_oem_list', + 'delivery_from_city_list', + 'delivery_to_city_list', + 'import_oem_list', 'panel' )), 'redirect' => '/profile', @@ -146,8 +149,9 @@ class ProfileController extends Controller return $this->render('index', compact( 'model', 'sidebar', - 'account_sections_city_list', - 'import_sections_oem_list', + 'delivery_from_city_list', + 'delivery_to_city_list', + 'import_oem_list', 'panel' )); } @@ -363,7 +367,6 @@ class ProfileController extends Controller */ public function actionImport() { - var_dump($_FILES); // Инициализация $model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); $model->scenario = $model::SCENARIO_IMPORT_EXCEL; diff --git a/mirzaev/skillparts/system/controllers/SearchController.php b/mirzaev/skillparts/system/controllers/SearchController.php index 3a000ea..2518f28 100644 --- a/mirzaev/skillparts/system/controllers/SearchController.php +++ b/mirzaev/skillparts/system/controllers/SearchController.php @@ -166,6 +166,11 @@ class SearchController extends Controller // Инициализация $row['overload'] = true; } + + // Поиск аккаунтов владельцев поставок + foreach ($row['supplies'] as &$edge) { + $edge['account'] = Supply::searchAccountById($edge['_from']); + } } // Запись ответа diff --git a/mirzaev/skillparts/system/models/Account.php b/mirzaev/skillparts/system/models/Account.php index 6f0398c..36ae052 100644 --- a/mirzaev/skillparts/system/models/Account.php +++ b/mirzaev/skillparts/system/models/Account.php @@ -273,44 +273,113 @@ class Account extends Document implements IdentityInterface, PartnerInterface // Инициализация $list = []; - // Перебор свойств поставок foreach ($supplies as $supply) { - // Инициализация - - $id = $supply['ЗначенияСвойства']['Ид']; - - if (in_array($id, $list, true)) { + // Перебор поставок + if (in_array($supply['ЗначенияСвойства']['Ид'], $list, true)) { // Если встретился дубликат (исполняется очень часто) + continue; } // Генерация - !isset($supply['ЗначенияСвойства']['Наименование']) or $list[$id] = $supply['ЗначенияСвойства']['Наименование']; + empty($supply['ЗначенияСвойства']['Наименование']) or $list[$supply['ЗначенияСвойства']['Ид']] = $supply['ЗначенияСвойства']['Наименование']; } - // Инициализация текущего значения параметра в начале массива - if (isset($this->opts['import_sections_oem'])) { - // Параметр 'import_sections_oem' найден в настройках аккаунта + return $this->syncListWithSettings($list, 'import_supplies_oem'); + } - if (isset($list[$this->opts['import_sections_oem']])) { + /** + * Генерация списка городов из ДеловыеЛинии для отправителя + * + * Актуальное (выбранное, активное) значение записывается первым + * + * @param array Необработанный список городов + */ + public function genListCitiesFrom(): array + { + // Инициализация + $list = []; + + $cities = City::readAll(); + + foreach ($cities as $city) { + // Перебор городов + + if (in_array($city->name, $list, true)) { + // Если встретился дубликат (исполняется очень часто) + + continue; + } + + // Запись + empty($city->name) or $list[$city->code] = $city->name; + } + + return $this->syncListWithSettings($list, 'delivery_from_city'); + } + + /** + * Генерация списка городов из ДеловыеЛинии для получателя + * + * Актуальное (выбранное, активное) значение записывается первым + * + * @param array Необработанный список городов + */ + public function genListCitiesTo(): array + { + // Инициализация + $list = []; + + $cities = City::readAll(); + + foreach ($cities as $city) { + // Перебор городов + + if (in_array($city->name, $list, true)) { + // Если встретился дубликат (исполняется очень часто) + + continue; + } + + // Запись + empty($city->name) or $list[$city->code] = $city->name; + } + + return $this->syncListWithSettings($list, 'delivery_to_city'); + } + + /** + * Синхронизация списка вариантов параметра с текущим значением из настроек + * + * @param array &$list Список + * @param string $var Название параметра + * + * @return array Сортированный список + */ + protected function syncListWithSettings(array &$list, string $var): array { + // Инициализация текущего значения параметра в начале массива + if (isset($this->opts[$var])) { + // Параметр найден в настройках аккаунта + + if (isset($list[$this->opts[$var]])) { // Найдено совпадение сохранённого параметра с полученным списком из поставок // Буфер для сохранения параметра - $buffer = $list[$this->opts['import_sections_oem']]; + $buffer = $list[$this->opts[$var]]; // Удаление параметра - unset($list[$this->opts['import_sections_oem']]); + unset($list[$this->opts[$var]]); // Сохранение параметра в начале массива - $list = array_merge([$this->opts['import_sections_oem'] => $buffer], $list); + $list = array_merge([$this->opts[$var] => $buffer], $list); } else { // Совпадение не найдено // Сохранение параметра из данных аккаунта в начале массива - $list = array_merge([$this->opts['import_sections_oem'] => $this->opts['import_sections_oem']], $list); + $list = array_merge([$this->opts[$var] => $this->opts[$var]], $list); } } else { - // Параметр 'import_sections_oem' не найден в настройках аккаунта + // Параметр $var не найден в настройках аккаунта // Сохранение параметра из данных аккаунта в начале массива $list = array_merge(['Выберите'], $list); diff --git a/mirzaev/skillparts/system/models/City.php b/mirzaev/skillparts/system/models/City.php index a35141d..4b5371b 100644 --- a/mirzaev/skillparts/system/models/City.php +++ b/mirzaev/skillparts/system/models/City.php @@ -16,6 +16,7 @@ class City extends Document return array_merge( parent::attributes(), [ + 'indx', 'name', 'code', 'term' @@ -28,10 +29,16 @@ class City extends Document return array_merge( parent::rules(), [ + [ + 'indx', + 'string' + ], [ [ + 'indx', 'name', - 'code' + 'code', + 'term' ], 'required', 'message' => 'Заполните поле: {attribute}' @@ -56,10 +63,19 @@ class City extends Document return array_merge( parent::attributeLabels(), [ + 'indx' => 'Идентификатор в ДеловыеЛинии', 'name' => 'Название', 'code' => 'Код КЛАДР', 'term' => 'Указатель наличия терминала в городе' ] ); } + + /** + * Поиск по идентификатору в ДеловыеЛинии + */ + public static function searchByDellinId(string $indx): ?static + { + return static::findOne(['indx' => $indx]); + } } diff --git a/mirzaev/skillparts/system/models/Document.php b/mirzaev/skillparts/system/models/Document.php index 2f75c2e..67435be 100644 --- a/mirzaev/skillparts/system/models/Document.php +++ b/mirzaev/skillparts/system/models/Document.php @@ -70,7 +70,7 @@ abstract class Document extends ActiveRecord $this->jrnl = array_merge( [[ 'date' => time(), - 'account' => yii::$app->user->id, + 'account' => yii::$app->user->id ?? 'system', 'action' => 'create' ]], $this->jrnl ?? [] @@ -106,7 +106,7 @@ abstract class Document extends ActiveRecord [array_merge( [ 'date' => $time = time(), - 'account' => yii::$app->user->id, + 'account' => yii::$app->user->id ?? 'system', 'action' => $action ], ...$data diff --git a/mirzaev/skillparts/system/models/Product.php b/mirzaev/skillparts/system/models/Product.php index 126a4dd..640c5ae 100644 --- a/mirzaev/skillparts/system/models/Product.php +++ b/mirzaev/skillparts/system/models/Product.php @@ -80,6 +80,7 @@ class Product extends Document 'dscr', 'prod', 'dmns', + 'wght', 'imgs', 'time' ] @@ -99,6 +100,7 @@ class Product extends Document 'dscr' => 'Описание (dscr)', 'prod' => 'Производитель (prod)', 'dmns' => 'Габариты (dmns)', + 'wght' => 'Вес (wght)', 'imgs' => 'Изображения (imgs)', 'time' => 'Срок доставки (time)', 'file_excel' => 'Документ (file_excel)', @@ -301,7 +303,7 @@ class Product extends Document // Перебор файлов // Инициализация - $dir = '../assets/import/' . date('Y_m_d#H-i', time()) . '/excel/'; + $dir = YII_PATH_PUBLIC . '../assets/import/' . date('Y-m-d', time()) . '/excel/' . (yii::$app->user->identity->_key ?? 'system') . '/' . time() . '/'; // Сохранение на диск if (!file_exists($dir)) { diff --git a/mirzaev/skillparts/system/models/Supply.php b/mirzaev/skillparts/system/models/Supply.php index 1c7e149..8d78c94 100644 --- a/mirzaev/skillparts/system/models/Supply.php +++ b/mirzaev/skillparts/system/models/Supply.php @@ -15,7 +15,7 @@ use carono\exchange1c\interfaces\OfferInterface; use carono\exchange1c\interfaces\ProductInterface; use carono\exchange1c\controllers\ApiController; -use exception; +use Exception; /** * Поставка (выгрузка товаров от поставщиков) @@ -416,7 +416,8 @@ class Supply extends Product implements ProductInterface, OfferInterface * @param mixed|null $context * @return array */ - public function getExportFields1c($context = null) { + public function getExportFields1c($context = null) + { return $this->onec; } @@ -498,7 +499,8 @@ class Supply extends Product implements ProductInterface, OfferInterface * @param $types * @return void */ - public static function createPriceTypes1c($types) { + public static function createPriceTypes1c($types) + { } /** @@ -511,8 +513,8 @@ class Supply extends Product implements ProductInterface, OfferInterface * @param \Zenwalker\CommerceML\Model\Simple $specification * @return void */ - public function setSpecification1c($specification) { - + public function setSpecification1c($specification) + { } /** @@ -572,4 +574,43 @@ class Supply extends Product implements ProductInterface, OfferInterface return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; } + + /** + * Найти аккаунт владельца + * + * @return Account|null Аккаунт владельца + */ + public function searchAccount(): ?array + { + return static::searchAccountById($this->_id); + } + + /** + * Найти аккаунт владельца + * + * @param string|null $_id Идентификатор + * + * @return Account|null Аккаунт владельца + */ + public static function searchAccountById(string $_id): ?array + { + return static::searchByEdge( + from: 'account', + to: 'supply', + edge: 'account_edge_supply', + direction: 'INBOUND', + subquery_where: [ + [ + 'account_edge_supply._from == account._id' + ], + [ + 'account_edge_supply._to == "' . $_id . '"' + ] + ], + subquery_select: 'account', + where: 'account_edge_supply[0]._id != null', + limit: 1, + select: 'account_edge_supply[0]' + )[0]; + } } diff --git a/mirzaev/skillparts/system/models/connection/Dellin.php b/mirzaev/skillparts/system/models/connection/Dellin.php index 6780a09..1d3f202 100644 --- a/mirzaev/skillparts/system/models/connection/Dellin.php +++ b/mirzaev/skillparts/system/models/connection/Dellin.php @@ -12,61 +12,108 @@ use app\models\City; use GuzzleHttp\Client as Guzzle; use Exception; +use phpDocumentor\Reflection\Types\Nullable; class Dellin extends Model { /** * Инстанция браузера */ - public Guzzle $browser; + public static Guzzle $browser; /** * Сессия аккаунта */ - public string $session; + public static string $session; public function __construct($config = []) { parent::__construct($config); - $this->browser = new Guzzle([ + self::$browser = new Guzzle([ 'base_uri' => 'https://api.dellin.ru/' ]); - $this->authorization(); + self::authorization(); } - /** - * Поиск городов - * - * @return array|null Найденные города - */ - public function searchCities(): ?array - { - // $this->onReady(function () { - // // Запрос городов - // $request = $this->browser->post('/v2/public/kladr.json', [ - // 'json' => [ - // 'appkey' => yii::$app->params['dellin']['key'], + // /** + // * Поиск городов + // * + // * @return array|null Найденные города + // */ + // public function searchCities(): ?array + // { + // $this->onReady(function () { + // // Запрос городов + // $request = $this->browser->post('/v2/public/kladr.json', [ + // 'json' => [ + // 'appkey' => yii::$app->params['dellin']['key'], - // ] - // ]) - // }); + // ] + // ]) + // }); + // } + + /** + * Рассчет доставки + * + * @param string $from Номер КЛАДР + * @param string $to Номер КЛАДР + * + * @return string + */ + public static function calcDelivery(string $from, string $to): array + { + return self::handle(function () use ($from, $to) { + // Всё готово к работе + + // Запрос + $request = self::$browser->post('/v1/micro_calc.json', [ + 'json' => [ + 'appkey' => yii::$app->params['dellin']['key'], + 'sessionID' => self::$session, + 'derival' => [ + 'city' => $from + ], + 'arrival' => [ + 'city' => $to + ] + ] + ]); + + + if ($request->getStatusCode() === 200) { + // Запрос прошел успешно + + // Инициализация + $response = json_decode((string) $request->getBody(), true); + + if ($response['metadata']['status'] === 200) { + // Со стороны ДеловыеЛинии ошибок нет + + return $response['data']; + } + + throw new Exception('На стороне сервера ДеловыеЛинии какие-то проблемы, либо отправлен неверный запрос', 500); + } + + throw new Exception('Не удалось запросить рассчёт доставки у ДеловыеЛинии', 500); + }); } /** * Синхронизация городов * * @return array|null Найденные города - * - * @todo Написать проверку по хешу */ - public function syncCities(): ?array + public static function importCities(): ?int { - return $this->ifReady(function () { - // Запрос ссылки на файл с городам - // Получаем hash и url () - $request = $this->browser->post('/v1/public/cities.json', [ + return self::handle(function () { + // Всё готово к работе + + // Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string] + $request = self::$browser->post('/v1/public/cities.json', [ 'json' => [ 'appkey' => yii::$app->params['dellin']['key'], ] @@ -77,79 +124,187 @@ class Dellin extends Model // Инициализация $response = json_decode((string) $request->getBody(), true); + $hash_target = $response['hash']; + $dir = YII_PATH_PUBLIC . '/../assets/import/' . date('Y-m-d', time()) . '/dellin/cities/' . (yii::$app->user->identity->_key ?? 'system') . '/'; + if (!file_exists($dir)) { + // Директории не существует - if ($response['metadata']['status'] === 200) { - // Аутентификация и авторизация пройдены успешно - - // Запись сессии - - return $this; + mkdir($dir, 0775, true); } - throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', $response['metadata']['status']); + $request = self::$browser->get($response['url'], [ + 'sink' => $file = $dir . time() . '.csv' + ]); + + // Проверка хеша + if ($hash_target !== $hash_received = md5_file($file)) { + // Удалось пройти проверку на хеши файлов + + // Инициализация (чтение файла) + $file = fopen($file, "r"); + $first_raw_block = true; + + while ($row = fgets($file, 4096)) { + // Перебор строк + + if ($first_raw_block) { + // Сработала защита от чтения первой строки файла (указываются названия колонок) + + // Отключение + $first_raw_block = false; + + // Пропуск цикла + continue; + } + + // Инициализация + $data = explode(',', $row, 4); + $amount = 0; + + // Очистка + array_walk($data, fn (&$value) => $value = trim($value, '"')); + + if ($city = City::searchByDellinId($data[0])) { + // Удалось найти город в базе данных + + $after_import_log = function () use ($city): void { + // Запись в журнал + $city->journal('update'); + + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo 'Удалось перезаписать город: ' . $city->name . PHP_EOL; + } + }; + } else { + // Не удалось найти город в базе данных + + $city = new City(); + + $after_import_log = function () use ($city): void { + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo 'Удалось записать город: ' . $city->name . PHP_EOL; + } + }; + } + + // Запись + $city->indx = $data[0]; + $city->name = $data[1]; + $city->code = $data[2]; + $city->term = (bool) $data[3]; + + // Отправка в базу данных + if ($city->save()) { + // Удалось сохранить в базе данных + + // Запись в журнал + $after_import_log(); + + // Постинкрементация счётчика + $amount++; + + continue; + } else { + // Не удалось сохранить в базе данных + + throw new Exception('Не удалось сохранить город ' . $data[1] . ' в базу данных', 500); + } + } + + // Деинициализация + fclose($file); + + return $amount; + } else { + // Не удалось пройти проверку на соответствие хешей файлов + + throw new Exception('Хеши файлов не совпадают. Должен быть: "' . $hash_target . '", получен: "' . $hash_received . '"', 500); + } } - - throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', $request->getStatusCode); - + throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500); }); } /** * Аутентификация и авторизация */ - protected function authorization(): ?self + protected static function authorization(): bool { - return $this->ifReady(function () { - // Аутентификация и авторизация - $request = $this->browser->post('/v3/auth/login.json', [ - 'json' => [ - 'appkey' => yii::$app->params['dellin']['key'], - 'login' => yii::$app->params['dellin']['nickname'], - 'password' => yii::$app->params['dellin']['password'] - ] - ]); + // Аутентификация и авторизация + $request = self::browser()->post('/v3/auth/login.json', [ + 'json' => [ + 'appkey' => yii::$app->params['dellin']['key'], + 'login' => yii::$app->params['dellin']['nickname'], + 'password' => yii::$app->params['dellin']['password'] + ] + ]); - if ($request->getStatusCode() === 200) { - // Запрос прошел успешно + if ($request->getStatusCode() === 200) { + // Запрос прошел успешно - // Инициализация - $response = json_decode((string) $request->getBody(), true); + // Инициализация + $response = json_decode((string) $request->getBody(), true); - if ($response['metadata']['status'] === 200) { - // Аутентификация и авторизация пройдены успешно + if ($response['metadata']['status'] === 200) { + // Аутентификация и авторизация пройдены успешно - // Запись сессии - $this->session = $response['data']['sessionID']; + // Запись сессии + self::$session = $response['data']['sessionID']; - return $this; - } - - throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $response['metadata']['status']); + return true; } + throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $response['metadata']['status']); + } - throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $request->getStatusCode); - }); + throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $request->getStatusCode); } /** - * Проверка на то, что браузер готов к работе + * Инициализация и выполнение * - * @param callable $function Код к выполнению - * @param [type] ...$vars Параметры к нему + * @param callable|null $function Код к выполнению + * @param [mixed] ...$vars Параметры к нему * * @return mixed Возврат из функции */ - protected function ifReady(callable $function, ...$vars): mixed + protected static function handle(callable $function = null, mixed ...$vars): mixed { - if (isset($this->browser)) { - // Браузер инициализирован + try { + if (self::browser() instanceof Guzzle) { + // Браузер инициализирован - return $function(...$vars); + if (self::authorization() && isset(self::$session)) { + // Аутентифицирован и авторизован + + return $function(...$vars); + } else { + throw new Exception('Аккаунт не авторизирован', 401); + } + } else { + throw new Exception('Браузер не инициализирован', 500); + } + } catch (Exception $e) { + throw new Exception($e->getMessage(), 500, $e->getPrevious()); + // throw new Exception('Не удалось инициализировать инстанцию для работы с API ДеловыеЛинии', 500, $e->getPrevious()); } + } - throw new Exception('Браузер не инициализирован', 500); + /** + * Чтение или инициализация браузера + * + * @return Guzzle Инстанция + */ + protected static function browser(): Guzzle + { + return self::$browser ?? self::$browser = new Guzzle([ + 'base_uri' => 'https://api.dellin.ru/' + ]); } } diff --git a/mirzaev/skillparts/system/models/traits/SearchByEdge.php b/mirzaev/skillparts/system/models/traits/SearchByEdge.php index 1065b9b..4caf238 100644 --- a/mirzaev/skillparts/system/models/traits/SearchByEdge.php +++ b/mirzaev/skillparts/system/models/traits/SearchByEdge.php @@ -6,13 +6,17 @@ namespace app\models\traits; use yii; -use exception; +use ArangoDBClient\Document; + +use Exception; trait SearchByEdge { /** * Поиск через связи рёбрами с аккаунтом * + * Аргумент $asArray и его реализацию пришлось добавить чтобы не переписывать кучу кода + * * @param string $id Идентификатор пользователя * @param int $limit Количество * @param int $offset Сдвиг @@ -27,12 +31,15 @@ trait SearchByEdge int|null $offset = 0, array $sort = ['ASC'], string|array $subquery_where = [], + string|array $subquery_select = null, array $foreach = [], string|array $where = [], array|null $let = [], string|array $select = null, callable|null $handle = null, - array $params = [] + array $params = [], + bool $asArray = true, + bool $debug = false ): mixed { $subquery = static::find() ->params($params) @@ -42,7 +49,7 @@ trait SearchByEdge ->where($subquery_where); $subquery = $subquery - ->select($edge ?? $from . '_edge_' . $to) + ->select($subquery_select ?? $edge ?? $from . '_edge_' . $to) ->createCommand(); $query = static::find() @@ -58,12 +65,20 @@ trait SearchByEdge $query->let(...$let); } - // Запрос + // Инициализация $request = $query ->foreach($foreach) ->where($where) ->select($select ?? $to); + // Режим проверки + if ($debug) { + // Запрошена проверка + + return (string) $request->createCommand(); + } + + // Запрос if (isset($handle)) { // Передана функция для постобработки @@ -71,14 +86,27 @@ trait SearchByEdge } else if (isset($select)) { $response = $request->createCommand()->execute()->getAll(); - foreach ($response as &$attribute) { - // Приведение всех свойств в массив и очистка от лишних данных + if ($asArray) { + // Передан параметр указывающий на необходимость возврата как объекта - $attribute = $attribute->getAll(); + // Очистка + foreach ($response as &$attribute) { + // Приведение всех свойств в массив и очистка от лишних данных + + if ($attribute instanceof Document) { + // Получена инстанция документа ArangoDB + + $attribute = $attribute->getAll(); + } + } } return $response; } else { + if ($limit === 1) { + return $request->one(); + } + return $request->all(); } } diff --git a/mirzaev/skillparts/system/views/product/index.php b/mirzaev/skillparts/system/views/product/index.php index a4ca323..a2e6d66 100644 --- a/mirzaev/skillparts/system/views/product/index.php +++ b/mirzaev/skillparts/system/views/product/index.php @@ -14,7 +14,7 @@ use app\models\Product;
-
+
+ + user->isGuest && (yii::$app->user->identity->type === 'administrator' || yii::$app->user->identity->type === 'moderator') ) : ?> -
-

- Габариты: - - см - x - - см - x - - см +

+ +

+ Габариты:смxсмxсм +

+ +

+ Вес:кг

-

- Габариты: - - x - - x - + +

+ Габариты:смxсмxсм +

+ +

+ Габариты:кг

+ + + +
user->isGuest @@ -260,4 +263,4 @@ use app\models\Product; || yii::$app->user->identity->type === 'moderator') ) : ?> - \ No newline at end of file + diff --git a/mirzaev/skillparts/system/views/profile/index.php b/mirzaev/skillparts/system/views/profile/index.php index 1ec2552..3cdb866 100644 --- a/mirzaev/skillparts/system/views/profile/index.php +++ b/mirzaev/skillparts/system/views/profile/index.php @@ -40,7 +40,8 @@ if ( user->isGuest) : ?> />
-
Основная информация
+
Доставка
+ 'form_profile_settings', 'action' => false, @@ -54,15 +55,15 @@ if ( // Инициализация $model ?? $model = yii::$app->user->identity; - $account_sections_city_list or $account_sections_city_list = ['Нет данных']; + $delivery_to_city_list or $delivery_to_city_list = ['Нет данных']; ?> - field($model, 'opts[account_sections_city]', ['options' => ['class' => "mb-1"]]) - ->dropDownList($account_sections_city_list, [ + field($model, 'opts[delivery_to_city]', ['options' => ['class' => "mb-1"]]) + ->dropDownList($delivery_to_city_list, [ 'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_account\')' ])->label('Город'); ?> - Выберите город для рассчёта доставки + Выберите город получателя для рассчёта доставки
@@ -70,7 +71,32 @@ if ( user->identity->agnt) : ?> />
- 2 +
Доставка
+ + 'form_profile_settings', + 'action' => false, + 'fieldConfig' => [ + 'template' => '{label}{input}', + ], + 'options' => [ + 'onsubmit' => 'return false;' + ] + ]); + + // Инициализация + $model ?? $model = yii::$app->user->identity; + $delivery_from_city_list or $delivery_from_city_list = ['Нет данных']; + ?> + + field($model, 'opts[delivery_from_city]', ['options' => ['class' => "mb-1"]]) + ->dropDownList($delivery_from_city_list, [ + 'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_company\')' + ])->label('Город'); ?> + + Выберите город отправителя для рассчёта доставки + +
/> @@ -90,13 +116,13 @@ if ( // Инициализация $model ?? $model = yii::$app->user->identity; - $import_sections_oem_list or $import_sections_oem_list = ['Нет данных']; + $import_oem_list or $import_oem_list = ['Нет данных']; ?> - field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]]) - ->dropDownList($import_sections_oem_list, [ + field($model, 'opts[import_supplies_oem]', ['options' => ['class' => "mb-1"]]) + ->dropDownList($import_oem_list, [ 'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_import\')', - 'disabled' => count($import_sections_oem_list) <= 1 + 'disabled' => count($import_oem_list) <= 1 ])->label('OEM-номера'); ?> Выберите поле в котором хранятся ОЕМ-номера и повторите импорт diff --git a/mirzaev/skillparts/system/views/search/index.php b/mirzaev/skillparts/system/views/search/index.php index 153216d..c02c0ba 100644 --- a/mirzaev/skillparts/system/views/search/index.php +++ b/mirzaev/skillparts/system/views/search/index.php @@ -1,3 +1,11 @@ + + HTML; } else { - $button_cart = << - - + // Есть в наличии + + $supplies_html .= << + + $amount + + + $delivery_max дней
+ $delivery_price рублей +
+ + $price + + + + +
HTML; } - - $supplies_html .= << - - $amount - - - Доставка - - - $price - - $button_cart -
- HTML; ?> @@ -171,4 +187,4 @@ || yii::$app->user->identity->type === 'moderator') ) : ?> - \ No newline at end of file + diff --git a/mirzaev/skillparts/system/web/js/product_panel.js b/mirzaev/skillparts/system/web/js/product_panel.js index 26530a3..57d3d8b 100644 --- a/mirzaev/skillparts/system/web/js/product_panel.js +++ b/mirzaev/skillparts/system/web/js/product_panel.js @@ -171,6 +171,76 @@ function product_panel_dimensions_save(catn, element, dimension) { return true; } + +function product_panel_weight_edit(catn, element) { + if (catn !== null && catn !== undefined && element !== null && element !== undefined) { + + let input = document.createElement('input'); + + input.setAttribute('id', element.id); + input.setAttribute('class', 'ml-1 text-center product_options_edit_small'); + input.setAttribute('type', 'number'); + input.setAttribute('value', element.innerText); + input.setAttribute('aria-invalid', 'false'); + + element.replaceWith(input); + + product_panel_handler_save(product_panel_weight_save, catn, input); + + return false; + } + + return true; +} + +function product_panel_weight_save(catn, element) { + if (catn !== null && catn !== undefined && element !== null && element !== undefined) { + // Инициализация + let text = element.value; + let span = document.createElement('span'); + + span.setAttribute('id', element.id); + span.setAttribute('class', 'ml-1 pointer-event'); + span.setAttribute('role', 'button'); + span.setAttribute('onclick', 'return product_panel_weight_edit(\'' + catn + '\', this);'); + + if (text.length === 0) { + text = '0'; + } + + span.innerText = text; + + element.replaceWith(span); + + $.ajax({ + url: '/product/' + catn + '/edit/wght', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + 'text': text + }, + success: function (data, status) { + // Заголовок + if (data.weight !== undefined && span !== null && span !== undefined) { + // Обновление заголовка + span.innerText = data.weight; + + // Запись аттрибута + span.setAttribute('onclick', 'return product_panel_weight_edit(\'' + catn + '\', this);'); + }; + + product_response_success(data, status); + }, + error: product_response_error + }); + + return false; + } + + return true; +} + function product_panel_description_edit(catn, element) { if (catn !== null && catn !== undefined && element !== null && element !== undefined) { element.innerHTML = ''; @@ -301,4 +371,4 @@ function product_panel_handler_save(save, catn, element, ...vars) { document.body.addEventListener('click', product_panel_handler_save_function, true); } -} \ No newline at end of file +} diff --git a/mirzaev/skillparts/system/yii b/mirzaev/skillparts/system/yii index f743758..94e11a2 100644 --- a/mirzaev/skillparts/system/yii +++ b/mirzaev/skillparts/system/yii @@ -10,6 +10,7 @@ defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); +defined('YII_PATH_PUBLIC') or define('YII_PATH_PUBLIC', __DIR__ . '/web'); require __DIR__ . '../../../../vendor/autoload.php'; require __DIR__ . '../../../../vendor/yiisoft/yii2/Yii.php';