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;
                                 ?>
                                 
-                                    
+

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 });