diff --git a/mirzaev/skillparts/system/commands/AccountController.php b/mirzaev/skillparts/system/commands/AccountController.php new file mode 100644 index 0000000..d4d0a59 --- /dev/null +++ b/mirzaev/skillparts/system/commands/AccountController.php @@ -0,0 +1,36 @@ + 0) { + // Был успешно обработан минимум 1 аккаунт + + return ExitCode::OK; + } + + return ExitCode::UNSPECIFIED_ERROR; + } +} diff --git a/mirzaev/skillparts/system/commands/DellinController.php b/mirzaev/skillparts/system/commands/DellinController.php index 228cbe7..b5248c6 100644 --- a/mirzaev/skillparts/system/commands/DellinController.php +++ b/mirzaev/skillparts/system/commands/DellinController.php @@ -9,7 +9,10 @@ use app\models\connection\Dellin; class DellinController extends Controller { - public function actionCitiesImport() + /** + * Импортировать города из ДеловыеЛинии + */ + public function actionImportCities() { if (Dellin::importCities()) { return ExitCode::OK; @@ -17,4 +20,16 @@ class DellinController extends Controller return ExitCode::UNSPECIFIED_ERROR; } + + /** + * Импортировать терминалы из ДеловыеЛинии + */ + public function actionImportTerminals() + { + if (Dellin::importTerminals()) { + return ExitCode::OK; + } + + return ExitCode::UNSPECIFIED_ERROR; + } } diff --git a/mirzaev/skillparts/system/controllers/CartController.php b/mirzaev/skillparts/system/controllers/CartController.php index 68d8b7c..679be37 100644 --- a/mirzaev/skillparts/system/controllers/CartController.php +++ b/mirzaev/skillparts/system/controllers/CartController.php @@ -4,12 +4,14 @@ declare(strict_types=1); namespace app\controllers; +use app\models\Notification; use yii; use yii\filters\AccessControl; use yii\web\Controller; use yii\web\Response; use app\models\Order; +use app\models\OrderEdgeSupply; use Exception; @@ -24,7 +26,7 @@ class CartController extends Controller { // Инициализация $page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1; - $account = yii::$app->user; + $account = yii::$app->user->identity; // Поиск корзины (текущего заказа) $model = Order::search(); @@ -41,7 +43,7 @@ class CartController extends Controller } // Инициализация содержимого корзины - $supplies = $model->content(10, $page); + $connections = $model->content(10, $page); if (yii::$app->request->isPost) { // POST-запрос @@ -49,13 +51,88 @@ class CartController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'main' => $this->renderPartial('index', compact('model', 'supplies')), + 'main' => $this->renderPartial('index', compact('model', 'connections')), 'title' => 'Корзина', 'redirect' => '/cart', '_csrf' => yii::$app->request->getCsrfToken() ]; } - return $this->render('index', compact('model', 'supplies')); + return $this->render('index', compact('model', 'connections')); + } + + public function actionEditComm(string $catn): array|string|null + { + // Инициализация + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + + if (is_null($catn)) { + // Не получен артикул + + yii::$app->response->statusCode = 500; + + goto end; + } + + if ($edges = OrderEdgeSupply::searchBySupplyCatn($catn)) { + // Рёбра найдены (связи заказа с поставкой) + + // Инициализация + $amount = 0; + + foreach ($edges as $edge) { + // Перебор рёбер (связей заказа с поставкой) + + // Инициализация + $text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? 'Комментарий к заказу'; + + $comm = $edge->comm ?? null; + $edge->comm = $text; + + if ($edge->save()) { + // Ребро обновлено + + // Запись в журнал + $edge->journal('update', ['comm' => ['from' => $comm, 'to' => $edge->comm]]); + + // Обновление счётчика + ++$amount; + + // Запись в буфер ответа + $return['comm'] = $text; + } + } + + if ($amount > 0) { + // Удалось записать минимум 1 связь с поставкой + + Notification::_write("Обновлён комментарий к товару $catn ($amount шт)"); + } else { + // Не удалось записать минимум 1 связь с поставкой + + Notification::_write("Не удалось обновить комментарий к товару $catn"); + } + } + + /** + * Конец алгоритма + */ + 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('/'); + } } } diff --git a/mirzaev/skillparts/system/controllers/NotificationController.php b/mirzaev/skillparts/system/controllers/NotificationController.php index 4715c2a..55c3d67 100644 --- a/mirzaev/skillparts/system/controllers/NotificationController.php +++ b/mirzaev/skillparts/system/controllers/NotificationController.php @@ -92,7 +92,7 @@ class NotificationController extends Controller * @param bool $new Активация проверки на то, что уведомление не получено * @param bool $count Посчитать */ - $search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null { + $search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null|Notification { if ($count) { // Запрошен подсчёт непрочитанных уведомлений @@ -164,7 +164,7 @@ class NotificationController extends Controller goto end; } - foreach ($notifications as $notification) { + foreach (is_array($notifications) ? $notifications : [$notifications] as $notification) { // Перебор найденных уведомлений if ($preload) { @@ -174,7 +174,7 @@ class NotificationController extends Controller } // Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано) - AccountEdgeNotification::write(yii::$app->user->id, $notification->readId(), $type); + AccountEdgeNotification::write($account->readId(), $notification->readId(), $type); } if (yii::$app->request->post('last')) { diff --git a/mirzaev/skillparts/system/controllers/OrderController.php b/mirzaev/skillparts/system/controllers/OrderController.php index 47f25c1..0275fa7 100644 --- a/mirzaev/skillparts/system/controllers/OrderController.php +++ b/mirzaev/skillparts/system/controllers/OrderController.php @@ -96,7 +96,7 @@ class OrderController extends Controller // POST-запрос // Инициализация входных данных - $account = yii::$app->user; + $account = yii::$app->user->identity; $supplies = yii::$app->request->post('supplies'); yii::$app->response->format = Response::FORMAT_JSON; @@ -122,7 +122,7 @@ class OrderController extends Controller $model->save() or throw new Exception('Не удалось инициализировать заказ'); // Запись ребра: АККАУНТ -> ЗАКАЗ - AccountEdgeOrder::write($account->id, $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ'); + AccountEdgeOrder::write($account->readId(), $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ'); } // Проверка входных данных @@ -155,7 +155,7 @@ class OrderController extends Controller // Инициализация $targets = yii::$app->request->post('targets') ?? yii::$app->request->get('targets'); $page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1; - $account = yii::$app->user; + $account = yii::$app->user->identity; $order = Order::search(); if ($targets) { @@ -182,7 +182,7 @@ class OrderController extends Controller $order->journal('reserved'); // Поиск - $edge = AccountEdgeOrder::searchByVertex($account->id, $order->readId(), 'current'); + $edge = AccountEdgeOrder::searchByVertex($account->readId(), $order->readId(), 'current'); if (count($edge) > 1) { // Найден более чем 1 заказ @@ -214,7 +214,7 @@ class OrderController extends Controller } // Инициализация содержимого корзины - $supplies = $order->content(10, $page); + $connections = $order->content(10, $page); if (yii::$app->request->isPost) { // POST-запрос @@ -222,14 +222,14 @@ class OrderController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')), + 'main' => $this->renderPartial('/cart/index', compact('order', 'connections')), 'title' => 'Корзина', 'redirect' => '/cart', '_csrf' => yii::$app->request->getCsrfToken() ]; } - return $this->render('/cart/index', compact('order', 'supplies')); + return $this->render('/cart/index', compact('order', 'connections')); } /** @@ -242,7 +242,7 @@ class OrderController extends Controller $page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1; $account = yii::$app->user; $order = Order::search(); - $supplies = $order->content(10, $page); + $connections = $order->content(10, $page); foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) { // Унификация входных параметров @@ -250,22 +250,22 @@ class OrderController extends Controller foreach ($target as $catn => $amount) { // Перебор целей (переданных объектов в корзине) - foreach ($supplies as $supply) { + foreach ($connections as $connection) { // Перебор объектов в корзине - if ($supply->catn === $catn) { + if ($connection['supply']['catn'] === $catn) { // Цель найдена - if ($supply->amnt > $amount) { + if ($connection['amount'] > $amount) { // Запрошено уменьшение количества // Удаление - $order->deleteSupply([$catn => $supply->amnt - $amount]); - } else if ($supply->amnt < $amount) { + $order->deleteSupply([$catn => $connection['amount'] - $amount]); + } else if ($connection['amount'] < $amount) { // Запрошено увеличение количества // Запись - $order->writeSupply([$supply->catn => $amount - $supply->amnt]); + $order->writeSupply([$connection['supply']['catn'] => $amount - $connection['amount']]); } } } @@ -273,7 +273,7 @@ class OrderController extends Controller } // Ренициализация - $supplies = $order->content(10, $page); + $connections = $order->content(10, $page); if (yii::$app->request->isPost) { // POST-запрос @@ -281,14 +281,14 @@ class OrderController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')), + 'main' => $this->renderPartial('/cart/index', compact('order', 'connections')), 'title' => 'Корзина', 'redirect' => '/cart', '_csrf' => yii::$app->request->getCsrfToken() ]; } - return $this->render('/cart/index', compact('order', 'supplies')); + return $this->render('/cart/index', compact('order', 'connections')); } /** diff --git a/mirzaev/skillparts/system/controllers/ProductController.php b/mirzaev/skillparts/system/controllers/ProductController.php index b82ae2b..2f65a1d 100644 --- a/mirzaev/skillparts/system/controllers/ProductController.php +++ b/mirzaev/skillparts/system/controllers/ProductController.php @@ -208,7 +208,6 @@ class ProductController extends Controller // Инициализация $text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0'; - $text or $text = '0'; $dimension = yii::$app->request->post('dimension') ?? yii::$app->request->get('dimension') ?? 'x'; $product->dmns = array_merge( @@ -265,8 +264,6 @@ class ProductController extends Controller // Инициализация $text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0'; - $text or $text = '0'; - $product->wght = $text; if ($product->save()) { diff --git a/mirzaev/skillparts/system/controllers/SearchController.php b/mirzaev/skillparts/system/controllers/SearchController.php index 2518f28..83fbb8c 100644 --- a/mirzaev/skillparts/system/controllers/SearchController.php +++ b/mirzaev/skillparts/system/controllers/SearchController.php @@ -12,6 +12,8 @@ use app\models\Product; use app\models\Supply; use app\models\Search; +use app\models\connection\Dellin; + class SearchController extends Controller { /** @@ -167,9 +169,19 @@ class SearchController extends Controller $row['overload'] = true; } - // Поиск аккаунтов владельцев поставок foreach ($row['supplies'] as &$edge) { + // Перебор поставок + + // Инициализация аккаунта $edge['account'] = Supply::searchAccountById($edge['_from']); + + // Инициализация доставки + $edge['delivery'] = Dellin::calcDelivery($edge['account']['opts']['delivery_from_city'] ?? $settings['delivery_from_city_default'] ?? '2700000100000000000000000', yii::$app->user->identity->opts['delivery_to_city'] ?? $settings['delivery_to_city_default'] ?? '2700000100000000000000000')['terminals_standard']; + $edge['delivery']['max'] = $edge['delivery']['period_to']; + $edge['delivery']['price'] = $edge['delivery']['price']; + + // Инициализация цены (цена поставки + цена доставки + наша наценка) + $edge['prce'] = ($edge['prce'] ?? $edge['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($edge['delivery']['price'] ?? 0) + ($settings['increase'] ?? 0); } } diff --git a/mirzaev/skillparts/system/models/Account.php b/mirzaev/skillparts/system/models/Account.php index 36ae052..8894517 100644 --- a/mirzaev/skillparts/system/models/Account.php +++ b/mirzaev/skillparts/system/models/Account.php @@ -36,6 +36,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface [ 'auth', 'mail', + 'indx', 'pswd', 'name', 'simc', @@ -60,6 +61,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface [ 'auth' => 'Аутентификационный хеш', 'mail' => 'Почта', + 'indx' => 'Псевдоанонимный идентификатор', 'pswd' => 'Пароль', 'name' => 'Имя', 'simc' => 'Номер', @@ -82,9 +84,32 @@ class Account extends Document implements IdentityInterface, PartnerInterface return array_merge( parent::rules(), [ - [['mail', 'pswd'], 'required', 'message' => 'Заполните поле'], - ['mail', 'email'], - [['mail', 'comp', 'simc'], 'unique', 'message' => '2'] + [ + [ + 'mail', + 'pswd' + ], + 'required', + 'message' => 'Заполните поле' + ], + [ + 'mail', + 'email' + ], + [ + [ + 'mail', + 'indx', + 'comp', + 'simc' + ], + 'unique', + 'message' => 'Атрибут {attribute} должен иметь уникальное значение' + ], + [ + 'indx', + 'string' + ] ] ); } @@ -356,7 +381,8 @@ class Account extends Document implements IdentityInterface, PartnerInterface * * @return array Сортированный список */ - protected function syncListWithSettings(array &$list, string $var): array { + protected function syncListWithSettings(array &$list, string $var): array + { // Инициализация текущего значения параметра в начале массива if (isset($this->opts[$var])) { // Параметр найден в настройках аккаунта @@ -387,4 +413,65 @@ class Account extends Document implements IdentityInterface, PartnerInterface return $list; } + + /** + * Генерация псевдоанонимных индексов + * + * @param [int] $accounts Массив аккаунтов + * @param bool $init Параметр обозначающий изменение только для тех у кого ранее идентификатор задан не был (без перезаписи) + * + * @return int Количество успешно обработанных аккаунтов + */ + public static function generateIndexes(array $accounts, bool $init = true): int + { + // Инициализация + $amount = 0; + + // Функция для конвертации цифр в буквы + $int_to_string = function (int $target): string { + $alphabet = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я']; + + return $alphabet[$target]; + }; + + foreach ($accounts as $account) { + // Перебор запрошенных аккаунтов + + if (($init && empty($account->indx)) || !$init) { + // Запись только тех аккаунтов у кого не инициализирован индекс или если указано отсутствие проверки + + // Повтор генерации + regenerate: + + // Генерация + $account->indx = $int_to_string(random_int(0, 32)) . $int_to_string(random_int(0, 32)) . $int_to_string(random_int(0, 32)); + + // Запись + if ($account->save()) { + // Аккаунт сохранён (в базе данных это поле проверяется на уникальность) + + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo "Удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL; + } + + // Запись операции в счётчик + $amount++; + } else { + // Аккаунт не сохранен (подразумевается несовпадение идентификаторов) + + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo "Не удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL; + } + + goto regenerate; + } + } + } + + return $amount; + } } diff --git a/mirzaev/skillparts/system/models/Order.php b/mirzaev/skillparts/system/models/Order.php index 6949f5c..b570630 100644 --- a/mirzaev/skillparts/system/models/Order.php +++ b/mirzaev/skillparts/system/models/Order.php @@ -11,6 +11,8 @@ use app\models\traits\SearchByEdge; use carono\exchange1c\controllers\ApiController; use carono\exchange1c\interfaces\DocumentInterface; +use app\models\connection\Dellin; + use Exception; /** @@ -45,6 +47,7 @@ class Order extends Document implements DocumentInterface parent::attributes(), [ 'ocid', + 'stts', 'sync' ] ); @@ -59,6 +62,7 @@ class Order extends Document implements DocumentInterface parent::attributeLabels(), [ 'ocid' => 'Идентификатор 1C', + 'stts' => 'Статус', 'sync' => 'Статус синхронизации с 1C' ] ); @@ -92,7 +96,7 @@ class Order extends Document implements DocumentInterface public function connect(Account $account): ?AccountEdgeOrder { // Запись ребра: АККАУНТ -> ЗАКАЗ - return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ'); + return AccountEdgeOrder::write($account->readId(), $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ'); } /** @@ -110,7 +114,7 @@ class Order extends Document implements DocumentInterface public function writeSupply(Supply|string|array $supply, Account $trgt = null): int { // Инициализация - $trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика'); + $trgt ?? $trgt = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать заказчика'); if ($supply instanceof Supply) { // Передана инстанция класса поставки или второй элемент массива не является числом @@ -235,7 +239,7 @@ class Order extends Document implements DocumentInterface public static function search(Account $account = null, string $type = 'current', int $limit = 1, int $page = 1, string $select = null): self|array|null { // Инициализация - $account or $account = yii::$app->user ?? throw new Exception('Не удалось инициализировать пользователя'); + $account or $account = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать пользователя'); // Генерация сдвига по запрашиваемым данным (пагинация) $offset = $limit * ($page - 1); @@ -255,7 +259,7 @@ class Order extends Document implements DocumentInterface to: 'order', subquery_where: [ [ - 'account._id' => $account->id + 'account._id' => $account->readId() ], $where_type ], @@ -281,8 +285,8 @@ class Order extends Document implements DocumentInterface // Генерация сдвига по запрашиваемым данным (пагинация) $offset = $limit * ($page - 1); - // Поиск рёбер: ЗАКАЗ -> ПОСТАВКА - $supplies = Supply::searchByEdge( + // Поиск по рёбрам: ЗАКАЗ -> ПОСТАВКА + $connections = Supply::searchByEdge( from: 'order', to: 'supply', edge: 'order_edge_supply', @@ -295,21 +299,22 @@ class Order extends Document implements DocumentInterface where: 'edge._to == supply._id', limit: $limit, offset: $offset, - direction: 'INBOUND' + direction: 'INBOUND', + select: '{supply, order_edge_supply}' ); // Инициализация реестра дубликатов $registry = []; // Подсчёт и перестройка массива для очистки от дубликатов - foreach ($supplies as $key => &$supply) { + foreach ($connections as $key => &$connection) { // Перебор поставок - if (in_array($supply->catn, $registry)) { + if (in_array($connection['supply']['catn'], $registry)) { // Если данная поставка найдена в реестре // Удаление - unset($supplies[$key]); + unset($connections[$key]); // Пропуск итерации continue; @@ -319,47 +324,56 @@ class Order extends Document implements DocumentInterface $amount = 0; // Повторный перебор для поиска дубликатов - foreach ($supplies as &$supply4check) { - if ($supply == $supply4check) { + foreach ($connections as &$connection_for_check) { + if ($connection == $connection_for_check) { // Найден дубликат // Постинкрементация счётчика $amount++; // Запись в реестр - $registry[] = $supply4check->catn; + $registry[] = $connection_for_check['supply']['catn']; } } // Запись количества для заказа - $supply->amnt = $amount; + $connection['amount'] = $amount; } - // Поиск стоимости для каждой поставки - foreach ($supplies as $key => &$supply) { + // Инициализация дополнительных данных + foreach ($connections as $key => &$connection) { // Перебор поставок // Чтение стоимости - $cost = $supply->readCost(); + $cost = Supply::readCostById($connection['supply']['_id']); - if ($cost < 1) { - // Если стоимость равна нулю (явная ошибка) + if (empty($cost) || $cost['ЦенаЗаЕдиницу'] < 1) { + // Если стоимость не найдена или равна нулю (явная ошибка) // Удаление из базы данных - $this->deleteSupply($supply->readId()); + $this->deleteSupply($connection['supply']->readId()); // Удаление из списка - unset($supplies[$key]); + unset($connections[$key]); // Пропуск итерации continue; } - // Запись цены - $supply->cost = $cost['ЦенаЗаЕдиницу'] . ' ' . $cost['Валюта']; + // Поиск ребра до аккаунта + $connection['account'] = Supply::searchAccountById($connection['supply']['_id']); + + // Инициализация доставки + $connection['delivery'] = Dellin::calcDelivery($connection['account']['opts']['delivery_from_city'] ?? $settings['delivery_from_city_default'] ?? '2700000100000000000000000', yii::$app->user->identity->opts['delivery_to_city'] ?? $settings['delivery_to_city_default'] ?? '2700000100000000000000000')['terminals_standard']; + + // Запись цены (цена поставки + цена доставки + наша наценка) + $connection['cost'] = ($cost['ЦенаЗаЕдиницу'] ?? $connection['supply']->onec['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($connection['delivery']['price'] ?? 0) + ($settings['increase'] ?? 0); + + // Запись валюты + $connection['currency'] = $cost['Валюта']; } - return $supplies; + return $connections; } /** diff --git a/mirzaev/skillparts/system/models/OrderEdgeSupply.php b/mirzaev/skillparts/system/models/OrderEdgeSupply.php index a9b5020..0bd3b60 100644 --- a/mirzaev/skillparts/system/models/OrderEdgeSupply.php +++ b/mirzaev/skillparts/system/models/OrderEdgeSupply.php @@ -6,8 +6,57 @@ namespace app\models; class OrderEdgeSupply extends Edge { + /** + * Имя коллекции + */ public static function collectionName(): string { return 'order_edge_supply'; } + + + /** + * Свойства + */ + public function attributes(): array + { + return array_merge( + parent::attributes(), + [ + 'comm' + ] + ); + } + + /** + * Метки свойств + */ + public function attributeLabels(): array + { + return array_merge( + parent::attributeLabels(), + [ + 'comm' => 'Комментарий к заказу' + ] + ); + } + + /** + * Поиск поставки по артикулу + * + * @param string $catn Артикул + * @param int $limit Максимальное количество + * + * @return array Поставки + */ + public static function searchBySupplyCatn(string $catn, int $limit = 10): array + { + if ($supply = Supply::searchByCatn($catn, 1)) { + // Поставка найдена + + return self::find()->where(['_to' => $supply->readId()])->limit($limit)->all(); + } + + return []; + } } diff --git a/mirzaev/skillparts/system/models/Supply.php b/mirzaev/skillparts/system/models/Supply.php index 8d78c94..397a20e 100644 --- a/mirzaev/skillparts/system/models/Supply.php +++ b/mirzaev/skillparts/system/models/Supply.php @@ -565,14 +565,31 @@ class Supply extends Product implements ProductInterface, OfferInterface /** * Прочитать стоимость + * + * @param Product|null $product Товар для поиска по вершинам + * + * @return array|null Данные о ценах */ - public function readCost(Product $product = null): array + public function readCost(Product $product = null): ?array + { + return static::readCostById($this->readId(), $product); + } + + /** + * Прочитать стоимость по идентификатору поставки + * + * @param string $_id Идентификатор поставки + * @param Product|null $product Товар для поиска по вершинам + * + * @return array|null Данные о ценах + */ + public static function readCostById(string $_id, Product $product = null): ?array { if (isset($product)) { - return SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; + return SupplyEdgeProduct::searchByVertex($_id, $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; } - return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; + return SupplyEdgeProduct::search($_id, type: 'connect', limit: 1)['onec']['Цены']['Цена']; } /** @@ -582,7 +599,7 @@ class Supply extends Product implements ProductInterface, OfferInterface */ public function searchAccount(): ?array { - return static::searchAccountById($this->_id); + return static::searchAccountById($this->readId()); } /** diff --git a/mirzaev/skillparts/system/models/connection/Dellin.php b/mirzaev/skillparts/system/models/connection/Dellin.php index 1d3f202..43c7299 100644 --- a/mirzaev/skillparts/system/models/connection/Dellin.php +++ b/mirzaev/skillparts/system/models/connection/Dellin.php @@ -55,6 +55,76 @@ class Dellin extends Model // }); // } + /** + * Рассчет доставки (расширенный) + * + * @param string $from Номер КЛАДР + * @param string $to Номер КЛАДР + * @param int $weight Вес (кг) + * @param int $x Ширина (см) + * @param int $y Высота (см) + * @param int $z Длинна (см) + * + * @return string + */ + public static function calcDeliveryAdvanced(string $from, string $to, int $weight, int $x, int $y, int $z): array + { + return self::handle(function () use ($from, $to, $weight, $x, $y, $z) { + // Всё готово к работе + + // Рассчёт типа доставки + if ( + $weight <= 10 + && (($x <= 5.4 && $y <= 3.9 && $z <= 3.9) + || ($x <= 3.9 && $y <= 5.4 && $z <= 3.9) + || ($x <= 3.9 && $y <= 3.9 && $z <= 5.4)) + && $x * $y * $z <= 1000000 + ) { + // Доставка категории "small" + + $type = 'small'; + } else { + // Доставка категории "auto" + + $type = 'auto'; + } + + // Запрос + $request = self::$browser->post('/v1/micro_calc.json', [ + 'json' => [ + 'appkey' => yii::$app->params['dellin']['key'], + 'sessionID' => self::$session, + 'delivery' => [ + 'deliveryType' => [ + 'type' => $type + ], + 'arrival' => [ + 'variant' => 'terminal', + ] + ] + ] + ]); + + 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); + }); + } + + /** * Рассчет доставки * @@ -103,9 +173,9 @@ class Dellin extends Model } /** - * Синхронизация городов + * Импорт городов * - * @return array|null Найденные города + * @return array|null Сохранённые города */ public static function importCities(): ?int { @@ -137,94 +207,217 @@ class Dellin extends Model 'sink' => $file = $dir . time() . '.csv' ]); - // Проверка хеша - if ($hash_target !== $hash_received = md5_file($file)) { - // Удалось пройти проверку на хеши файлов + // Проверка хеша (оказалось это хеш запроса, бесполезный) + // if ($hash_target === $hash_received = md5_file($file)) { + // Удалось пройти проверку на хеши файлов - // Инициализация (чтение файла) - $file = fopen($file, "r"); - $first_raw_block = true; + // Инициализация (чтение файла) + $file = fopen($file, "r"); + $first_raw_block = true; - while ($row = fgets($file, 4096)) { - // Перебор строк + while ($row = fgets($file, 4096)) { + // Перебор строк - if ($first_raw_block) { - // Сработала защита от чтения первой строки файла (указываются названия колонок) + if ($first_raw_block) { + // Сработала защита от чтения первой строки файла (указываются названия колонок) - // Отключение - $first_raw_block = false; + // Отключение + $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); - } + // Пропуск цикла + continue; } - // Деинициализация - fclose($file); + // Инициализация + $data = explode(',', $row, 4); + $amount = 0; - return $amount; - } else { - // Не удалось пройти проверку на соответствие хешей файлов + // Очистка + array_walk($data, fn (&$value) => $value = trim($value, '"')); - throw new Exception('Хеши файлов не совпадают. Должен быть: "' . $hash_target . '", получен: "' . $hash_received . '"', 500); + 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('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500); + }); + } + + + /** + * Импорт терминалов + * + * @return array|null Сохранённые терминалы + */ + public static function importTerminals(): ?int + { + return self::handle(function () { + // Всё готово к работе + + // Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string] + $request = self::$browser->post('/v3/public/terminals.json', [ + 'json' => [ + 'appkey' => yii::$app->params['dellin']['key'], + ] + ]); + + if ($request->getStatusCode() === 200) { + // Запрос прошел успешно + + // Инициализация + $response = json_decode((string) $request->getBody(), true); + $dir = YII_PATH_PUBLIC . '/../assets/import/' . date('Y-m-d', time()) . '/dellin/terminals/' . (yii::$app->user->identity->_key ?? 'system') . '/'; + + if (!file_exists($dir)) { + // Директории не существует + + mkdir($dir, 0775, true); + } + + $request = self::$browser->get($response['url'], [ + 'sink' => $file = $dir . time() . '.json' + ]); + + die; + + + // Инициализация (чтение файла) + $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 = Terminal::searchByDellinId($data[0])) { + // Удалось найти город в базе данных + + $after_import_log = function () use ($city): void { + // Запись в журнал + $city->journal('update'); + + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo 'Удалось перезаписать город: ' . $city->name . PHP_EOL; + } + }; + } else { + // Не удалось найти город в базе данных + + $terminal = new Terminal(); + + $after_import_log = function () use ($terminal): void { + if (yii::$app->getRequest()->isConsoleRequest) { + // Вызов из терминала + + echo 'Удалось записать город: ' . $terminal->name . PHP_EOL; + } + }; + } + + // Запись + $terminal->indx = $data[0]; + $terminal->name = $data[1]; + $terminal->code = $data[2]; + $terminal->term = (bool) $data[3]; + + // Отправка в базу данных + if ($terminal->save()) { + // Удалось сохранить в базе данных + + // Запись в журнал + $after_import_log(); + + // Постинкрементация счётчика + $amount++; + + continue; + } else { + // Не удалось сохранить в базе данных + + throw new Exception('Не удалось сохранить город ' . $data[1] . ' в базу данных', 500); + } + } + + // Деинициализация + fclose($file); + + return $amount; } throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500); diff --git a/mirzaev/skillparts/system/models/traits/SearchByEdge.php b/mirzaev/skillparts/system/models/traits/SearchByEdge.php index 4caf238..a147725 100644 --- a/mirzaev/skillparts/system/models/traits/SearchByEdge.php +++ b/mirzaev/skillparts/system/models/traits/SearchByEdge.php @@ -69,6 +69,7 @@ trait SearchByEdge $request = $query ->foreach($foreach) ->where($where) + ->limit($limit) ->select($select ?? $to); // Режим проверки @@ -84,6 +85,8 @@ trait SearchByEdge return $handle($request); } else if (isset($select)) { + // Указан выбор свойств + $response = $request->createCommand()->execute()->getAll(); if ($asArray) { @@ -103,9 +106,7 @@ trait SearchByEdge return $response; } else { - if ($limit === 1) { - return $request->one(); - } + // Иначе просто запросить для ActiveQuery return $request->all(); } diff --git a/mirzaev/skillparts/system/views/cart/index.php b/mirzaev/skillparts/system/views/cart/index.php index 9a74f08..e6c09a5 100644 --- a/mirzaev/skillparts/system/views/cart/index.php +++ b/mirzaev/skillparts/system/views/cart/index.php @@ -1,3 +1,11 @@ + +
$comment
+