/** * Парсинг XML и импортв БД товаров. * @param $filename - путь к файлу архива с данными. */ public function processImportXml($filename) { $importOnlyNew = false; $sep = DIRECTORY_SEPARATOR; $dirname = dirname(__FILE__); $realDocumentRoot = str_replace($sep . 'mg-core' . $sep . 'controllers', '', $dirname); $lastPositionProduct = $_SESSION['lastCountProduct1cImport']; $lastPositionOffer = $_SESSION['lastCountOffer1cImport']; $xml = $this->getImportXml($filename); if ($xml && $filename == 'import.xml') { foreach ($xml->Каталог->attributes() as $key => $val) { if ($key == 'СодержитТолькоИзменения' && $val == "true") { $importOnlyNew = true; } } if (isset($xml->Каталог->СодержитТолькоИзменения)) { $importOnlyNew = $xml->Каталог->СодержитТолькоИзменения[0] == 'true' ? true : false; } if (empty($lastPositionProduct) && $importOnlyNew == false) { // если установлена директива CLEAR_CATALOG = 1 в config.ini, то удаляем товары перед синхронизацией с 1с if (CLEAR_1С_CATALOG != 'CLEAR_1С_CATALOG' && CLEAR_1С_CATALOG != 0) { DB::query('DELETE FROM `' . PREFIX . 'product` WHERE 1'); DB::query('DELETE FROM `' . PREFIX . 'category` WHERE 1'); } } $category = $this->groupsGreate($xml->Классификатор, $category, 0); $this->propertyСreate($xml->Классификатор->Свойства); $model = new Models_Product(); $currentPosition = 0; foreach ($xml->Каталог->Товары[0] as $item) { $currentPosition++; if ($currentPosition <= $lastPositionProduct) { continue; } // Добавляем изображение товара в папку uploads $imageUrl = array(); if (isset($item->Картинка)) { foreach ($item->Картинка as $img) { $path = $item->Картинка; $image = basename($item->Картинка); if (!empty($image) && is_file($path)) { copy($path, $realDocumentRoot . $sep . 'uploads' . $sep . $image); $widthPreview = MG::getSetting('widthPreview') ? MG::getSetting('widthPreview') : 200; $widthSmallPreview = MG::getSetting('widthSmallPreview') ? MG::getSetting('widthSmallPreview') : 50; $heightPreview = MG::getSetting('heightPreview') ? MG::getSetting('heightPreview') : 100; $heightSmallPreview = MG::getSetting('heightSmallPreview') ? MG::getSetting('heightSmallPreview') : 50; $upload = new Upload(false); $upload->_reSizeImage('70_' . $image, $realDocumentRoot . $sep . 'uploads' . $sep . $image, $widthPreview, $heightPreview); // миниатюра по размерам из БД (150*100) $upload->_reSizeImage('30_' . $image, $realDocumentRoot . $sep . 'uploads' . $sep . $image, $widthSmallPreview, $heightSmallPreview); } $imageUrl[] = $image; } } $imageUrl = implode($imageUrl, "|"); $id = (string) $item->Группы->Ид[0]; $name = (string) $item->Наименование[0]; $description = ''; if (isset($item->Описание)) { $description = nl2br((string) $item->Описание[0], true); } foreach ($item->ЗначенияРеквизитов->ЗначениеРеквизита as $row) { if ($row->Наименование == 'Полное наименование') { $name = $row->Значение; } } $code = !empty($item->Артикул[0]) ? $item->Артикул[0] : $item->ШтрихКод[0]; $id_1c = (string) $item->Ид[0]; $dataProd = array('title' => $name, 'url' => str_replace('\\', '-', URL::prepareUrl(MG::translitIt($name), true)), 'code' => $code, 'price' => 0, 'description' => $description, 'old_price' => '', 'image_url' => $imageUrl, 'count' => 0, 'cat_id' => $category[$id]['category_id'], 'meta_title' => $name, 'meta_keywords' => $name, 'meta_desc' => MG::textMore($description, 157), 'recommend' => 0, 'activity' => 1, 'new' => 0, 'related' => '', 'inside_cat' => '', '1c_id' => $id_1c, 'weight' => '0'); if ($importOnlyNew) { unset($dataProd['description']); unset($dataProd['image_url']); unset($dataProd['meta_title']); unset($dataProd['meta_keywords']); unset($dataProd['recommend']); unset($dataProd['activity']); unset($dataProd['new']); unset($dataProd['related']); unset($dataProd['inside_cat']); unset($dataProd['weight']); } //MG::loger(print_r($dataProd,1)); $res = DB::query('SELECT * FROM ' . PREFIX . 'product WHERE `1c_id`=' . DB::quote($id_1c)); if ($row = DB::fetchAssoc($res)) { DB::query(' UPDATE `' . PREFIX . 'product` SET ' . DB::buildPartQuery($dataProd) . ' WHERE `1c_id`=' . DB::quote($id_1c)); } else { $model->addProduct($dataProd); } // Привязываем свойства. if (isset($item->ЗначенияСвойств)) { foreach ($item->ЗначенияСвойств->ЗначенияСвойства as $prop) { $this->propertyConnect($id_1c, $prop->Ид, $prop->Значение, $category[$id]['category_id']); } } $execTime = microtime(true) - $this->startTime; if ($execTime + 1 >= $this->maxExecTime) { header("Content-type: text/xml; charset=utf-8"); echo ""; echo "progress\r\n"; echo "Выгружено товаров: {$currentPosition}"; $_SESSION['lastCountProduct1cImport'] = $currentPosition; exit; } } if ($this->unlinkFile) { unlink($filename); } $_SESSION['lastCountProduct1cImport'] = 0; } elseif ($xml && $filename == 'offers.xml') { $currentPosition = 0; foreach ($xml->ПакетПредложений[0]->Предложения[0] as $item) { $currentPosition++; if ($currentPosition <= $lastPositionOffer) { continue; } $id = (string) $item->Ид[0]; $price = (string) $item->Цены->Цена->ЦенаЗаЕдиницу[0]; $count = (string) $item->Количество[0]; $partProd = array('price' => $price, 'count' => $count, 'price_course' => $price); DB::query(' UPDATE `' . PREFIX . 'product` SET ' . DB::buildPartQuery($partProd) . ' WHERE 1c_id = ' . DB::quote($id) . ' '); $execTime = microtime(true) - $this->startTime; if ($execTime + 1 >= $this->maxExecTime) { header("Content-type: text/xml; charset=utf-8"); echo ""; echo "progress\r\n"; echo "Выгружено предложений: {$currentPosition}"; $_SESSION['lastCountOffer1cImport'] = $currentPosition; exit; } } if ($this->unlinkFile) { unlink($filename); } $_SESSION['lastCountOffer1cImport'] = 0; Storage::clear(); } else { echo "Ошибка загрузки XML\n"; foreach (libxml_get_errors() as $error) { echo "\t", $error->message; exit; } } }
/** * Создает продукт в БД если его не было. * @param type $product - массив с данными о продукте. * @param type $catId - категория к которой относится продукт. */ public function createProduct($product, $catId = null) { $model = new Models_Product(); $variant = $product['variant']; $img_var = $product['image']; $property = $product['property']; unset($product['cat_url']); unset($product['variant']); unset($product['image']); unset($product['property']); if ($catId === null) { // 1 находим ID категории по заданному пути $product['cat_id'] = MG::translitIt($product['cat_id'], 1); $product['cat_id'] = URL::prepareUrl($product['cat_id']); if ($product['cat_id']) { $url = URL::parsePageUrl($product['cat_id']); $parentUrl = URL::parseParentUrl($product['cat_id']); $parentUrl = $parentUrl != '/' ? $parentUrl : ''; $cat = MG::get('category')->getCategoryByUrl($url, $parentUrl); $product['cat_id'] = $cat['id']; } } else { $product['cat_id'] = $catId; } $product['cat_id'] = !empty($product['cat_id']) ? $product['cat_id'] : 0; if (!empty($product['id'])) { $dbRes = DB::query('SELECT `id`, `url`, `title` FROM `' . PREFIX . 'product` WHERE `id` = ' . DB::quote($product['id'], 1)); if ($res = DB::fetchArray($dbRes)) { if ($res['title'] == $product['title']) { $product['url'] = $res['url']; } unset($product['id']); } else { $arrProd = $model->addProduct($product); } } if (empty($arrProd)) { // 2 если URL не задан в файле, то транслитирируем его из названия товара $product['url'] = !empty($product['url']) ? $product['url'] : preg_replace('~-+~', '-', MG::translitIt($product['title'], 0)); $product['url'] = URL::prepareUrl($product['url'], true); if ($product['cat_id'] == 0) { $alreadyProduct = $model->getProductByUrl($product['url']); } else { $alreadyProduct = $model->getProductByUrl($product['url'], $product['cat_id']); } // если в базе найден этот продукт, то при обновлении будет сохранен ID и URL if (!empty($alreadyProduct['id'])) { $product['id'] = $alreadyProduct['id']; $product['url'] = $alreadyProduct['url']; } // парсим изображение, его alt и title if (strpos($product['image_url'], '[:param:]') !== false) { $images = $this->parseImgSeo($product['image_url']); $product['image_url'] = $images[0]; $product['image_alt'] = $images[1]; $product['image_title'] = $images[2]; } // обновляем товар, если его не было то метод вернет массив с параметрами вновь созданного товара, в том числе и ID. Иначе вернет true $arrProd = $model->updateProduct($product); } $product_id = $product['id'] ? $product['id'] : $arrProd['id']; $categoryId = $product['cat_id']; $productId = $product_id; $listProperty = $property; $arrProperty = $this->parseListProperty($listProperty); foreach ($arrProperty as $key => $value) { $this->createProperty($key, $value, $categoryId, $productId); } if (!$variant) { return true; } $var = $model->getVariants($product['id'], $variant); $varUpdate = null; if (!empty($var)) { foreach ($var as $k => $v) { if ($v['title_variant'] == $variant && $v['product_id'] == $product_id) { $varUpdate = $v['id']; } } } // Иначе обновляем существующую запись в таблице вариантов. $newVariant = array('product_id' => $product_id, 'title_variant' => $variant, 'sort' => $product['sort'], 'price' => $product['price'], 'old_price' => $product['old_price'], 'count' => $product['count'], 'code' => $product['code'], 'image' => $img_var, 'activity' => $product['activity'], 'currency_iso' => $product['currency_iso']); $model->importUpdateProductVariant($varUpdate, $newVariant, $product_id); // Обновляем продукт по первому варианту. $res = DB::query(' SELECT pv.* FROM `' . PREFIX . 'product_variant` pv WHERE pv.product_id = ' . DB::quote($product_id) . ' ORDER BY sort '); if ($row = DB::fetchAssoc($res)) { if (!empty($row)) { $row['title'] = $product['title']; $row['id'] = $row['product_id']; unset($row['image']); unset($row['title_variant']); unset($row['product_id']); $model->updateProduct($row); } } }
/** * Сохраняет и обновляет параметры товара. * @return type */ public function saveProduct() { $this->messageSucces = $this->lang['ACT_SAVE_PROD']; $this->messageError = $this->lang['ACT_NOT_SAVE_PROD']; //Перед сохранением удалим все помеченные картинки продукта физически с диска. $images = explode("|", $_POST['image_url']); $model = new Models_Product(); if (!is_numeric($_POST['count'])) { $_POST['count'] = "-1"; } // исключаем дублированные артикулы в строке связаных товаров if (!empty($_POST['related'])) { $_POST['related'] = implode(',', array_unique(explode(',', $_POST['related']))); } if (!empty($_POST['userProperty'])) { foreach ($_POST['userProperty'] as $k => $v) { $_POST['userProperty'][$k] = htmlspecialchars_decode($v); } } //Обновление if (!empty($_POST['id'])) { $_POST['updateFromModal'] = true; // флаг, чтобы отличить откуда было обновление товара $model->updateProduct($_POST); $_POST['image_url'] = $images[0]; $_POST['currency'] = MG::getSetting('currency'); $_POST['recommend'] = $_POST['recommend']; $tempProd = $model->getProduct($_POST['id']); $arrVar = $model->getVariants($_POST['id']); foreach ($arrVar as $key => $variant) { $tempProd['variants'][] = $variant; } // $tempProd['variants'] = array($arrVar); $tempProd['real_price'] = $tempProd['price']; $this->data = $tempProd; } else { // добавление unset($_POST['delete_image']); $newProd = $model->addProduct($_POST); $this->data['image_url'] = $images[0]; $this->data['currency'] = MG::getSetting('currency'); $this->data['recommend'] = $_POST['recommend']; $tempProd = $model->getProduct($newProd['id']); $arrVar = $model->getVariants($newProd['id']); foreach ($arrVar as $key => $variant) { $tempProd['variants'][] = $variant; } // $tempProd['variants'] = array($arrVar); $tempProd['real_price'] = $tempProd['price']; $this->data = $tempProd; } return true; }
/** * Парсинг XML и импортв БД товаров. * @param $filename - путь к файлу архива с данными. */ public function processImportXml($filename) { $importOnlyNew = false; $sep = DIRECTORY_SEPARATOR; $dirname = dirname(__FILE__); $realDocumentRoot = str_replace($sep . 'mg-core' . $sep . 'controllers', '', $dirname); $lastPositionProduct = $_SESSION['lastCountProduct1cImport']; $lastPositionOffer = $_SESSION['lastCountOffer1cImport']; $xml = $this->getImportXml($filename); if ($xml && $filename == 'import.xml') { foreach ($xml->Каталог->attributes() as $key => $val) { if ($key == 'СодержитТолькоИзменения' && $val == "true") { $importOnlyNew = true; } } if (isset($xml->Каталог->СодержитТолькоИзменения)) { $importOnlyNew = $xml->Каталог->СодержитТолькоИзменения[0] == 'true' ? true : false; } if (empty($lastPositionProduct) && $importOnlyNew == false) { // если установлена директива CLEAR_CATALOG = 1 в config.ini, то удаляем товары перед синхронизацией с 1с if (CLEAR_1С_CATALOG != 'CLEAR_1С_CATALOG' && CLEAR_1С_CATALOG != 0) { DB::query('DELETE FROM `' . PREFIX . 'product` WHERE 1'); DB::query('DELETE FROM `' . PREFIX . 'category` WHERE 1'); DB::query('DELETE FROM `' . PREFIX . 'product_variant` WHERE 1'); } } $category = $this->groupsGreate($xml->Классификатор, $category, 0); $this->propertyСreate($xml->Классификатор->Свойства); $model = new Models_Product(); $currentPosition = 0; $upload = new Upload(false); $widthPreview = MG::getSetting('widthPreview') ? MG::getSetting('widthPreview') : 200; $widthSmallPreview = MG::getSetting('widthSmallPreview') ? MG::getSetting('widthSmallPreview') : 50; $heightPreview = MG::getSetting('heightPreview') ? MG::getSetting('heightPreview') : 100; $heightSmallPreview = MG::getSetting('heightSmallPreview') ? MG::getSetting('heightSmallPreview') : 50; foreach ($xml->Каталог->Товары[0] as $item) { $currentPosition++; if ($currentPosition <= $lastPositionProduct) { continue; } // Добавляем изображение товара в папку uploads $imageUrl = array(); $realImgPath = array(); if (isset($item->Картинка)) { foreach ($item->Картинка as $img) { $path = 'tempcml' . $sep . $img; $realImgPath[] = $path; $image = basename($img); $imageUrl[] = $image; } } $imageUrl = implode($imageUrl, "|"); $id = (string) $item->Группы->Ид[0]; $name = (string) $item->Наименование[0]; $description = ''; $desExist = false; if (isset($item->Описание)) { $description = nl2br((string) $item->Описание[0], true); $desExist = true; } foreach ($item->ЗначенияРеквизитов->ЗначениеРеквизита as $row) { if ($row->Наименование == 'Полное наименование') { // если в файле нет специального тега с описанием, то берем из полного наименования if (!$desExist) { $description = (string) $row->Значение ? (string) $row->Значение : $description; $description = nl2br($description, true); } else { // иначе полное наименование подставляем в title товара $name = (string) $row->Значение ? (string) $row->Значение : $name; } } } $code = !empty($item->Артикул[0]) ? $item->Артикул[0] : $item->ШтрихКод[0]; $id_1c = (string) $item->Ид[0]; $dataProd = array('title' => $name, 'url' => str_replace('\\', '-', URL::prepareUrl(MG::translitIt($name), true)), 'code' => $code, 'price' => 0, 'description' => $description, 'old_price' => '', 'image_url' => $imageUrl, 'count' => 0, 'cat_id' => $category[$id]['category_id'], 'meta_title' => $name, 'meta_keywords' => $name, 'meta_desc' => MG::textMore($description, 157), 'recommend' => 0, 'activity' => 1, 'new' => 0, 'related' => '', 'inside_cat' => '', '1c_id' => $id_1c, 'weight' => '0'); if ($importOnlyNew) { unset($dataProd['description']); unset($dataProd['image_url']); unset($dataProd['meta_title']); unset($dataProd['meta_keywords']); unset($dataProd['recommend']); unset($dataProd['activity']); unset($dataProd['new']); unset($dataProd['related']); unset($dataProd['inside_cat']); unset($dataProd['weight']); } $res = DB::query('SELECT * FROM ' . PREFIX . 'product WHERE `1c_id`=' . DB::quote($id_1c)); if ($row = DB::fetchAssoc($res)) { DB::query(' UPDATE `' . PREFIX . 'product` SET ' . DB::buildPartQuery($dataProd) . ' WHERE `1c_id`=' . DB::quote($id_1c)); $productId = $row['id']; } else { $newProd = $model->addProduct($dataProd); $productId = $newProd['id']; } $arImgPath = explode('/', $realImgPath[0]); array_pop($arImgPath); $path = implode($sep, $arImgPath); $imageUrl = explode('|', $imageUrl); $dir = floor($productId / 100) . '00'; if (!empty($realImgPath)) { foreach ($realImgPath as $cell => $image) { if (!empty($image) && is_file($image)) { $upload->_reSizeImage('70_' . $imageUrl[$cell], $realDocumentRoot . $sep . $image, $widthPreview, $heightPreview, 'PROPORTIONAL', 'uploads' . $sep . $addPath . 'thumbs' . $sep); $upload->_reSizeImage('30_' . $imageUrl[$cell], $realDocumentRoot . $sep . $image, $widthSmallPreview, $heightSmallPreview, 'PROPORTIONAL', 'uploads/' . $addPath . 'thumbs/'); } } $model->movingProductImage($imageUrl, $productId, $path); } // Привязываем свойства. if (isset($item->ЗначенияСвойств)) { foreach ($item->ЗначенияСвойств->ЗначенияСвойства as $prop) { $propVal = ''; $tempProp = '' . $prop->Значение[0]; if (!empty($_SESSION['variant_value'][$tempProp])) { $propVal = $_SESSION['variant_value'][$tempProp]; } if (empty($propVal)) { $propVal = ''; $idVal = '' . $prop->ИдЗначения; if (!empty($_SESSION['variant_value'][$idVal])) { $propVal = $_SESSION['variant_value'][$idVal]; } } $this->propertyConnect($id_1c, $prop->Ид, $propVal, $category[$id]['category_id']); } } $execTime = microtime(true) - $this->startTime; if ($execTime + 1 >= $this->maxExecTime) { header("Content-type: text/xml; charset=utf-8"); echo ""; echo "progress\r\n"; echo "Выгружено товаров: {$currentPosition}"; $_SESSION['lastCountProduct1cImport'] = $currentPosition; exit; } } if ($this->unlinkFile) { unlink($realDocumentRoot . '/tempcml/' . $filename); } $_SESSION['lastCountProduct1cImport'] = 0; } elseif ($xml && $filename == 'offers.xml') { $currentPosition = 0; $model = new Models_Product(); $currencyRate = MG::getSetting('currencyRate'); $currencyShort = MG::getSetting('currencyShort'); foreach ($xml->ПакетПредложений[0]->Предложения[0] as $item) { $currentPosition++; if ($currentPosition <= $lastPositionOffer) { continue; } $id = (string) $item->Ид[0]; $price = (string) $item->Цены->Цена->ЦенаЗаЕдиницу[0]; $iso = $this->getIsoByCode((string) $item->Цены->Цена->Валюта[0]); if ($iso == 'NULL') { $iso = substr(MG::translitIt((string) $item->Цены->Цена->Валюта[0]), 0, 3); } $count = (string) $item->Количество[0]; // если валюта товара не задана ранее в магазине, то добавим ее. (Курс нужно будет установить вручную в настройках) $currency = array(); if (empty($currencyRate[$iso])) { $currency['iso'] = htmlspecialchars($iso); $currency['short'] = $currency['iso']; $currency['rate'] = 1; $currencyRate[$currency['iso']] = $currency['rate']; $currencyShort[$currency['iso']] = $currency['short']; MG::setOption(array('option' => 'currencyRate', 'value' => addslashes(serialize($currencyRate)))); MG::setOption(array('option' => 'currencyShort', 'value' => addslashes(serialize($currencyShort)))); } $partProd = array('price' => $price, 'count' => $count < 0 ? 0 : $count, 'currency_iso' => $iso); // проверяем, вдруг это предложение является вариантом для товара $ids1c = explode('#', (string) $item->Ид[0]); $variantId = ''; // если id варианта не найден if (empty($ids1c[1])) { // просто товар, не вариант DB::query(' UPDATE `' . PREFIX . 'product` SET ' . DB::buildPartQuery($partProd) . ' , `price_course` = ROUND(' . DB::quote($price * $currencyRate[$iso], TRUE) . ',2) WHERE 1c_id = ' . DB::quote($ids1c[0]) . ' '); } else { // если товарное предложение является вариантом для продукта $productId = ''; $variantId = $ids1c[1]; $variant = array(); $dbRes = DB::query(' SELECT id FROM `' . PREFIX . 'product` WHERE 1c_id = ' . DB::quote($ids1c[0]) . ' '); if ($row = DB::fetchArray($dbRes)) { $productId = $row['id']; $name = array(); foreach ($item->ХарактеристикиТовара->ХарактеристикаТовара as $prop) { $name[] = $prop->Значение; } $name = implode(', ', $name); $titleVariant = $name; $variant = array('title_variant' => $titleVariant, 'code' => $item->Артикул[0], 'price' => $price, 'old_price' => '', 'image' => '', 'count' => $count < 0 ? 0 : $count, '1c_id' => $variantId, 'weight' => '0', 'activity' => 1, 'currency_iso' => $iso); // ****** // ищем варианты для этого товара $dbRes = DB::query(' SELECT id FROM `' . PREFIX . 'product_variant` WHERE product_id = ' . DB::quote($productId) . ' '); // если еще ни одного небыло, то создаем и обновляем в таблице product значения по первому варианту if ($row != DB::fetchArray($dbRes)) { DB::query(' UPDATE `' . PREFIX . 'product` SET ' . DB::buildPartQuery($partProd) . ' , `price_course` = ROUND(' . DB::quote($price * $currencyRate[$iso], TRUE) . ',2) WHERE 1c_id = ' . DB::quote($ids1c[0]) . ' '); } // ****** // проверяем, импортирован ли ранее этот вариант $dbRes = DB::query(' SELECT id FROM `' . PREFIX . 'product_variant` WHERE 1c_id = ' . DB::quote($ids1c[1]) . ' '); // если еще нет, то получаем массив всех имеющихся вариантов по этому продукту, // добавляем к нему новый вариант и обновляем массив вариантов стандартными средствами if (!($row = DB::fetchArray($dbRes))) { $arrVariants = array(); $res = DB::query(' SELECT pv.* FROM `' . PREFIX . 'product_variant` pv WHERE pv.product_id = ' . DB::quote($productId) . ' ORDER BY sort '); if (!empty($res)) { while ($var = DB::fetchAssoc($res)) { $arrVariants[$var['id']] = $var; } } $variant['price_course'] = round($price * $currencyRate[$iso], 2); $arrVariants[] = $variant; $model->saveVariants($arrVariants, $productId); } else { // обновить вариант DB::query(' UPDATE `' . PREFIX . 'product_variant` SET ' . DB::buildPartQuery($variant) . ',`price_course` = ROUND(' . DB::quote($price * $currencyRate[$iso], TRUE) . ',2) WHERE 1c_id = ' . DB::quote($ids1c[0]) . ' '); } } } $execTime = microtime(true) - $this->startTime; if ($execTime + 1 >= $this->maxExecTime) { header("Content-type: text/xml; charset=utf-8"); echo ""; echo "progress\r\n"; echo "Выгружено предложений: {$currentPosition}"; $_SESSION['lastCountOffer1cImport'] = $currentPosition; exit; } } if ($this->unlinkFile) { unlink($realDocumentRoot . '/tempcml/' . $filename); } $_SESSION['lastCountOffer1cImport'] = 0; Storage::clear(); } else { echo "Ошибка загрузки XML\n"; foreach (libxml_get_errors() as $error) { echo "\t", $error->message; exit; } } }