diff --git a/mirzaev/skillparts/system/controllers/ProfileController.php b/mirzaev/skillparts/system/controllers/ProfileController.php index 99fb44a..42dd74a 100644 --- a/mirzaev/skillparts/system/controllers/ProfileController.php +++ b/mirzaev/skillparts/system/controllers/ProfileController.php @@ -56,7 +56,9 @@ class ProfileController extends Controller 'imports-delete', 'warehouses-write', 'warehouses-rename', - 'warehouses-delete' + 'warehouses-delete', + 'warehouses-close', + 'warehouses-open' ] ], [ @@ -1319,4 +1321,152 @@ class ProfileController extends Controller return $return; } } + + /** + * Открыть склад + * + * @return array|string|null + */ + public function actionWarehousesOpen(): array|string|null + { + // Инициализация аккаунта + $account = Account::initAccount(); + + // Инициализация остальных входных параметров + $supply = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); + $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); + $sidebar = $this->renderPartial('sidebar'); + $groups = self::readGroups(); + + if (yii::$app->request->isPost) { + // POST-запрос + + // Запись заголовка с типом ответа + yii::$app->response->format = Response::FORMAT_JSON; + + // Инициализация буфера вывода + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + + // Инициализация идентификатора + $_key = yii::$app->request->post('_key'); + + if (empty($_key)) { + // Не передан идентификатор + + // Запись кода ошибки + yii::$app->response->statusCode = 500; + + return $return; + } + + if ($warehouse = Warehouse::searchById(Warehouse::collectionName() . "/$_key")) { + // Найден склад + + // Запись статуса + $warehouse->open = true; + + if ($warehouse->update() > 0) { + // Открыт склад + + // Отправка уведомления + Notification::_write("Склад $_key был открыт", account: $account->_key); + + + // Запись в буфер вывода реинициализированного элемента + $return['opened'] = true; + } + } else { + // Не найден склад + + // Отправка уведомления + Notification::_write("Не найден склад $_key", account: $account->_key); + } + + // Запись в буфер вывода реинициализированного элемента + $return['main'] = $this->renderPartial('supplies', compact( + 'supply', + 'groups', + 'sidebar', + 'panel' + )); + + return $return; + } + } + + /** + * Закрыть склад + * + * @return array|string|null + */ + public function actionWarehousesClose(): array|string|null + { + // Инициализация аккаунта + $account = Account::initAccount(); + + // Инициализация остальных входных параметров + $supply = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); + $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); + $sidebar = $this->renderPartial('sidebar'); + $groups = self::readGroups(); + + if (yii::$app->request->isPost) { + // POST-запрос + + // Запись заголовка с типом ответа + yii::$app->response->format = Response::FORMAT_JSON; + + // Инициализация буфера вывода + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + + // Инициализация идентификатора + $_key = yii::$app->request->post('_key'); + + if (empty($_key)) { + // Не передан идентификатор + + // Запись кода ошибки + yii::$app->response->statusCode = 500; + + return $return; + } + + if ($warehouse = Warehouse::searchById(Warehouse::collectionName() . "/$_key")) { + // Найден склад + + // Запись статуса + $warehouse->open = false; + + if ($warehouse->update() > 0) { + // Закрыт склад + + // Отправка уведомления + Notification::_write("Склад $_key был закрыт", account: $account->_key); + + + // Запись в буфер вывода реинициализированного элемента + $return['closed'] = true; + } + } else { + // Не найден склад + + // Отправка уведомления + Notification::_write("Не найден склад $_key", account: $account->_key); + } + + // Запись в буфер вывода реинициализированного элемента + $return['main'] = $this->renderPartial('supplies', compact( + 'supply', + 'groups', + 'sidebar', + 'panel' + )); + + return $return; + } + } } diff --git a/mirzaev/skillparts/system/models/Import.php b/mirzaev/skillparts/system/models/Import.php index d7180e5..5386640 100644 --- a/mirzaev/skillparts/system/models/Import.php +++ b/mirzaev/skillparts/system/models/Import.php @@ -96,4 +96,28 @@ class Import extends Document limit: $limit ); } + + /** + * Поиск по поставке + * + * @param Supply $supply Поставка + * @param int $limit Ограничение по максимальному количеству + * + * @return array Инстанции испортов + */ + public static function searchBySupply(Supply $supply, int $limit = 10): array + { + return self::searchByEdge( + from: 'supply', + to: 'import', + edge: 'import_edge_supply', + direction: 'OUTBOUND', + subquery_where: [ + ['import_edge_supply._to' => $supply->readId()], + ['import_edge_supply.type' => 'imported'] + ], + where: 'import_edge_supply[0] != null', + limit: $limit + ); + } } diff --git a/mirzaev/skillparts/system/models/Search.php b/mirzaev/skillparts/system/models/Search.php index 585c5d6..155a1fd 100644 --- a/mirzaev/skillparts/system/models/Search.php +++ b/mirzaev/skillparts/system/models/Search.php @@ -176,9 +176,6 @@ class Search extends Document foreach ($connections as $key => &$connection) { // Перебор поставок - // if (($cost = $connection['cost'] ?? $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 - // || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 - // ) { if ($cost = $connection['supply']['cost'] < 1) { // Цена меньше единицы (подразумевается как ошибка) @@ -198,7 +195,7 @@ class Search extends Document // Инициализация данных геолокации try { - $from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default; + $from = (int) (Warehouse::searchBySupply(Supply::searchByCatn($connection['supply']['catn']))[0]->trmn ?? Settings::searchActive()?->delivery_from_default ?? 36); } catch (exception $e) { $from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default; } @@ -209,13 +206,20 @@ class Search extends Document $to = 36; } - if ( - ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false) - && time() < $buffer_connection['expires'] - ) { + // Инициализация буфера доставки + $buffer_connection = $connection['product']['bffr']["$from-$to"] ?? null; + + if (isset($buffer_connection) && !empty($buffer_connection['data']) && time() < $buffer_connection['expires'] ?? 0) { // Найдены данные доставки в буфере // и срок хранения не превышен, информация актуальна + // var_dump($buffer_connection); + // var_dump(isset($buffer_connection['data'])); + // var_dump(isset($buffer_connection)); + // var_dump(time() < $buffer_connection['expires'] ?? 0); + // var_dump(isset($buffer_connection) && time() < $buffer_connection['expires'] ?? 0); + // die; + // Запись в буфер вывода $connection['delivery'] = $buffer_connection['data']; $connection['delivery']['type'] = 'auto'; @@ -223,24 +227,30 @@ class Search extends Document // Инициализация инстанции продукта в базе данных $product = Product::searchByCatn($connection['product']['catn']); - // Инициализация доставки Dellin (автоматическая) - $product->bffr = [ - "$from-$to" => [ - 'data' => $connection['delivery'] = Dellin::calcDeliveryAdvanced( - $from, - $to, - (int) ($connection['product']['wght'] ?? 0), - (int) ($connection['product']['dmns']['x'] ?? 0), - (int) ($connection['product']['dmns']['y'] ?? 0), - (int) ($connection['product']['dmns']['z'] ?? 0) - ), - 'expires' => time() + 86400 - ] - ] + ($product->bffr ?? []); - $connection['delivery']['type'] = 'auto'; + if ($connection['delivery'] = Dellin::calcDeliveryAdvanced( + $from, + $to, + (int) ($connection['product']['wght'] ?? 0), + (int) ($connection['product']['dmns']['x'] ?? 0), + (int) ($connection['product']['dmns']['y'] ?? 0), + (int) ($connection['product']['dmns']['z'] ?? 0) + )) { + // Получены данные доставки - // Отправка в базу данных - $product->update(); + // Инициализация доставки Dellin (автоматическая) + $product->bffr = [ + "$from-$to" => [ + 'data' => $connection['delivery'], + 'expires' => time() + 86400 + ] + ] + ($product->bffr ?? []); + + // Отправка в базу данных + $product->update(); + } + + // Запись типа доставки + $connection['delivery']['type'] = 'auto'; } } catch (Exception $e) { $connection['delivery']['error'] = true; @@ -251,21 +261,20 @@ class Search extends Document // var_dump(json_decode($e->getMessage(), true)['errors']); // die; + } finally { + // echo $connection['delivery']['price']['all']; + // Инициализация цены (цена поставки + цена доставки + наша наценка) + $connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); } - // Инициализация цены (цена поставки + цена доставки + наша наценка) - $connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); - // Инициализация версии для рассчета доставки по воздуху $buffer_delivery_avia = $connection; try { // Инициализация данных геолокации - if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 - || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 - ) { - // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы + if ($cost = $connection['supply']['cost'] < 1) { + // Цена меньше единицы (подразумевается как ошибка) // Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть // Однако я это оставлю для возможных доработок в будущем @@ -276,7 +285,7 @@ class Search extends Document } try { - $from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default; + $from = (int) (Warehouse::searchBySupply(Supply::searchByCatn($connection['supply']['catn']))[0]->trmn ?? Settings::searchActive()?->delivery_from_default ?? 36); } catch (Exception $e) { $from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default; } @@ -287,10 +296,10 @@ class Search extends Document $to = 36; } - if ( - ($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false) - && time() < $buffer_connection['expires'] - ) { + // Инициализация буфера доставки + $buffer_connection = $connection['product']['bffr']["$from-$to-avia"] ?? null; + + if (isset($buffer_connection) && !empty($buffer_connection['data']) && time() < $buffer_connection['expires'] ?? 0) { // Найдены данные доставки в буфере // и срок хранения не превышен, информация актуальна @@ -301,46 +310,53 @@ class Search extends Document // Инициализация инстанции продукта в базе данных $product = Product::searchByCatn($buffer_delivery_avia['product']['catn']); - // Инициализация доставки Dellin (автоматическая) - $product->bffr = [ - "$from-$to-avia" => [ - 'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced( - $from, - $to, - (int) ($buffer_delivery_avia['product']['wght'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0), - avia: true - ), - 'expires' => time() + 86400 - ] - ] + ($product->bffr ?? []); - $buffer_delivery_avia['delivery']['type'] = 'avia'; + if ($buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced( + $from, + $to, + (int) ($buffer_delivery_avia['product']['wght'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0), + avia: true + )) { + // Получены данные доставки - // Отправка в базу данных - $product->update(); + // Инициализация доставки Dellin (автоматическая) + $product->bffr = [ + "$from-$to-avia" => [ + 'data' => $buffer_delivery_avia['delivery'], + 'expires' => time() + 86400 + ] + ] + ($product->bffr ?? []); + + // Отправка в базу данных + $product->update(); + } + + // Запись типа доставки + $buffer_delivery_avia['delivery']['type'] = 'avia'; } } catch (exception $e) { $buffer_delivery_avia['delivery']['error'] = true; - // echo '
'; // var_dump($e->getMessage()); // var_dump($e->getTrace()); // var_dump($e->getFile()); // var_dump(json_decode($e->getMessage(), true)['errors']); // die; - } + } finally { + if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) { + // Если рассчиталась доставка самолётом - if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) { - // Если рассчиталась доставка самолётом + // echo $buffer_delivery_avia['delivery']['price']['all']; die; - // Инициализация цены (цена поставки + цена доставки + наша наценка) - $buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); + // Инициализация цены (цена поставки + цена доставки + наша наценка) + $buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); - // Запись в буфер - $buffer_connections[] = $buffer_delivery_avia; + // Запись в буфер + $buffer_connections[] = $buffer_delivery_avia; + } } } @@ -426,7 +442,7 @@ class Search extends Document } // Инициализация цены - $price_raw = $supply['cost']; + $price_raw = ($supply['cost'] ?? 0) + ($cost ?? 0); // $price = $price_raw . ' ' . $supply_edge_product[0]['onec']['Цены']['Цена']['Валюта'] ?? 'руб'; $price = $price_raw . ' руб'; @@ -518,6 +534,7 @@ class Search extends Document $delivery_converted = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspReceiver'])->getTimestamp(); } + $delivery = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1; } diff --git a/mirzaev/skillparts/system/models/Warehouse.php b/mirzaev/skillparts/system/models/Warehouse.php index 546fc33..8a8b378 100644 --- a/mirzaev/skillparts/system/models/Warehouse.php +++ b/mirzaev/skillparts/system/models/Warehouse.php @@ -34,7 +34,8 @@ class Warehouse extends Document 'name', 'addr', 'trmn', - 'actv' + 'actv', + 'open' ] ); } @@ -50,7 +51,8 @@ class Warehouse extends Document 'name' => 'Название', 'addr' => 'Адрес', 'trmn' => 'Терминал', - 'actv' => 'Активность' + 'actv' => 'Активность', + 'open' => 'Доступность' ] ); } @@ -115,6 +117,8 @@ class Warehouse extends Document * @param int $limit Ограничение по максимальному количеству * * @return mixed Склады + * + * @deprecated */ public static function searchByAccount(Account|null $account = null, int $limit = 10): mixed { @@ -142,6 +146,43 @@ class Warehouse extends Document return null; } + /** + * Найти по поставке + * + * @param Supply $supply Поставка + * @param int $limit Ограничение по максимальному количеству + * + * @return mixed Склады + */ + public static function searchBySupply(Supply $supply, int $limit = 10): mixed + { + return static::searchByImport(Import::searchBySupply($supply, limit: 1)[0], $limit); + } + + /** + * Найти по инстанции поставки + * + * @param Import $import Инстанция поставки + * @param int $limit Ограничение по максимальному количеству + * + * @return mixed Склады + */ + public static function searchByImport(Import $import, int $limit = 10): mixed + { + return self::searchByEdge( + from: 'import', + to: 'warehouse', + edge: 'warehouse_edge_import', + direction: 'OUTBOUND', + subquery_where: [ + ['warehouse_edge_import._to' => $import->readId()], + ['warehouse_edge_import.type' => 'loaded'] + ], + where: 'warehouse_edge_import[0] != null', + limit: $limit + ); + } + /** * Инициализация с записью * @@ -196,7 +237,7 @@ class Warehouse extends Document } // Запись - $list[$termial['id']] = $city->data['name']; + $list[$termial['id']] = $city->data['name'] . ' (' . $termial['address'] . ')'; } } diff --git a/mirzaev/skillparts/system/models/connection/Dellin.php b/mirzaev/skillparts/system/models/connection/Dellin.php index 800ac66..7baba3d 100644 --- a/mirzaev/skillparts/system/models/connection/Dellin.php +++ b/mirzaev/skillparts/system/models/connection/Dellin.php @@ -84,41 +84,17 @@ class Dellin extends Model return self::handle(function () use ($from, $to, $weight, $x, $y, $z, $amount, $avia, $account) { // Всё готово к работе - if (is_null($account)) { - // Данные аккаунта не переданы - - if (yii::$app->user->isGuest) { - // Аккаунт не аутентифицирован - - return []; - } else { - // Аккаунт аутентифицирован - - // Инициализация - $account = yii::$app->user->identity; - } - } else { - if (is_int($account)) { - // Передан идентификатор (_key) аккаунта (подразумевается) - - // Инициализация (поиск в базе данных) - if (!$account = Account::searchById(Account::collectionName() . "/$account")) { - // Не удалось инициализировать аккаунт - - return []; - } - } - } + $account = Account::initAccount($account); // Инициализация $from = DellinModel::searchByTerminalId($from, terminal_data_only: true); $to = DellinModel::searchByTerminalId($to, terminal_data_only: true); // Значения по умолчанию, если указан 0 - $x === 0 and $x = 25; - $y === 0 and $y = 40; - $z === 0 and $z = 25; - $weight === 0 and $weight = 300; + if (empty($x) || $x === 0) $x = 25; + if (empty($y) || $y === 0) $y = 40; + if (empty($z) || $z === 0) $z = 25; + if (empty($weight) || $weight === 0) $weight = 300; // Конвертация из сантиметров в метры $x /= 100; @@ -220,11 +196,10 @@ class Dellin extends Model 'width' => $width, 'height' => $height, 'length' => $length, - 'weight' => $weight, - 'totalVolume' => $x * $y * $z, + 'totalVolume' => $width * $height * $length, 'totalWeight' => $weight, 'oversizedWeight' => $weight, - 'oversizedVolume' => $x * $y * $z + 'oversizedVolume' => $width * $height * $length ] ] ); diff --git a/mirzaev/skillparts/system/views/profile/supplies.php b/mirzaev/skillparts/system/views/profile/supplies.php index 5e4027b..bf43ec8 100644 --- a/mirzaev/skillparts/system/views/profile/supplies.php +++ b/mirzaev/skillparts/system/views/profile/supplies.php @@ -136,7 +136,7 @@ $panel ?? $panel = 'profile_panel_supplies_input_import'; $amount_warehouses = 0; ?> -+ = $warehouse->name ?? 'Без названия' ?> [ 'onsubmit' => 'return false;', - 'class' => 'ml-auto' + 'class' => 'ml-auto px-0 col-5' ] ]); @@ -161,8 +161,9 @@ $panel ?? $panel = 'profile_panel_supplies_input_import'; ])->label(false); ?> - - + + +
--> - +diff --git a/mirzaev/skillparts/system/web/js/profile.js b/mirzaev/skillparts/system/web/js/profile.js index f6cf613..18625ab 100644 --- a/mirzaev/skillparts/system/web/js/profile.js +++ b/mirzaev/skillparts/system/web/js/profile.js @@ -118,8 +118,90 @@ function page_profile_warehouses_write() { return false; }; -function page_profile_warehouses_delete(_key) { - if (confirm(`Удалить склад ${_key}?`)) { +function page_profile_warehouses_open(element, _key, name) { + if (confirm(`Открыть склад "${name}"?`)) { + $.ajax({ + url: '/profile/warehouses/open', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + _key + }, + success: function (data, status, xhr) { + if (data !== undefined) { + // Получены данные с сервера + + if (data.opened !== undefined && data.opened == true) { + // Получена информации об успешном открытии склала + + // Перезапись иконки + element.classList.remove('fa-lock'); + element.classList.add('fa-lock-open'); + + // Перезапись вызываемой функции + element.removeAttribute('onclick'); + element.setAttribute('onclick', 'return page_profile_warehouses_close(this, ' + _key + ', \'' + element.parentElement.parentElement.parentElement.children[0].children[0].textContent + '\');"'); + + // Перезапись всплывающей подсказки + element.title = 'Закрыть'; + } + } + + page_profile_response_success(data, status, xhr); + }, + error: page_profile_response_error + }); + + return false; + } + + return true; +}; + +function page_profile_warehouses_close(element, _key, name) { + if (confirm(`Закрыть склад "${name}"?`)) { + $.ajax({ + url: '/profile/warehouses/close', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + _key + }, + success: function (data, status, xhr) { + if (data !== undefined) { + // Получены данные с сервера + + if (data.closed !== undefined && data.closed == true) { + // Получена информации об успешном открытии склала + + // Перезапись иконки + element.classList.remove('fa-lock-open'); + element.classList.add('fa-lock'); + + // Перезапись вызываемой функции + element.removeAttribute('onclick'); + element.setAttribute('onclick', 'return page_profile_warehouses_open(this, ' + _key + ', \'' + element.parentElement.parentElement.parentElement.children[0].children[0].textContent + '\');"'); + + // Перезапись всплывающей подсказки + element.title = 'Открыть'; + } + } + + page_profile_response_success(data, status, xhr); + }, + error: page_profile_response_error + }); + + return false; + } + + return true; +}; + +function page_profile_warehouses_delete(_key, name) { + if (confirm(`Удалить склад "${name}"?`)) { $.ajax({ url: '/profile/warehouses/delete', type: 'post', @@ -262,9 +344,7 @@ function page_profile_monitoring(change = 1, panel) { 'search': search, 'panel': panel }, - success: function (data, status) { - page_profile_response_success(data, status); - + success: function (data, status, xhr) { // Ренициализация let url = new URL(document.location); @@ -273,6 +353,8 @@ function page_profile_monitoring(change = 1, panel) { // Запись в историю history.pushState('', document.title, url.toString()); + + page_profile_response_success(data, status, xhr); }, error: page_profile_response_error });