diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..47bf1b0 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,39 @@ +Changelog +========= + +####v0.3.0 + +* Расширена библиотека клиента +* Добавлена возможность кастомизации моделей заказа через vqmod +* Устранены мелкие баги, проведен рефакторинг кода. + + +####v0.2.0 + +Общие изменения + +* Код приведен в состоянии совместимости с PHP 5.2 +* Убрана необходимость собирать пакет через composer +* Библиотека api-client-php обновлена до последней версии и добавлена в стандартную поставку +* Переименованы методы, пути и значения ключей конфигурации в связи с ребрендингом +* Временая метка последнего запуска получения истории перенесена в таблицу конфигурации БД + +Выгрузка каталога (ICML) + +* Генерация обновлена в соответствии с последними измениями формата файла выгрузки +* Генерация вынесена в отдельный класс +* Добавлена возможность добавлять подкатегории +* Скорректировано указание активности офера +* Убрана генерация размера офера вследствие кастомизации этого параметра в разных магазинах + +####v0.1.1 +* Устранена ошибка редактирования, при которой терялась часть данных при получении истории из CRM +* Оптимизирован код получения и обработки истории заказов +* Актуализированы переводы + +####v.0.1 +* Реализован интерфейс настроек модуля +* Реализована отправка данных о заказе/клиенте в CRM +* Реализована выгрузка каталога (cron only) +* Реализовано получение данных о заказах, сделанных на стороне CRM (cron only) + diff --git a/README.md b/README.md index 58fbe97..97f3217 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,99 @@ Opencart module -============== +=============== -Opencart module for interaction with [IntaroCRM](http://www.intarocrm.com) through [REST API](http://docs.intarocrm.ru/rest-api/). +Модуль интеграции CMS Openacrt c [RetailCRM](http://retailcrm.ru) -Module allows: +#### Модуль позволяет: -* Send to IntaroCRM new orders -* Configure relations between dictionaries of IntaroCRM and Opencart (statuses, payments, delivery types and etc) -* Generate [ICML](http://docs.intarocrm.ru/index.php?n=Пользователи.ФорматICML) (IntaroCRM Markup Language) for catalog loading by IntaroCRM +* Экспортировать в CRM данные о заказах и клиентах и получать обратно изменения по этим данным +* Синхронизировать справочники (способы доставки и оплаты, статусы заказов и т.п.) +* Выгружать каталог товаров в формате [ICML](http://retailcrm.ru/docs/Разработчики/ФорматICML) (IntaroCRM Markup Language) -#### Documentation +#### Установка -* [Install](doc/Install.md) -* [Changelog](doc/Changelog.md) -* [TODO](doc/TODO.md) +Установите модуль скопировав необходимые файлы в корень сайта + +``` +unzip master.zip +cp -r opencart-module/* /path/to/site/root +``` + +#### Активируйте модуль + +В списке модулей нажмите "Установить" + +#### Настройте параметры интеграции + +На странице настроек модуля укажите URL Вашей CRM и ключ авторизации, после сохранения этих данных укажите соответствия справочников типов доставок, оплат и статусов заказа. + + +#### Выгрузка новых заказов в CRM (для версии opencart 1.5.x.x, для версии 2.0 и старше не требуется) + +##### VQmod + +Скопируйте xml-файл модифицирующий работу моделей _admin/model/sale/order.php_ и _catalog/model/checkout/order.php_ в _/path/to/site/root/vqmod/xml_. + +Для обновления кеша VQmod может потрбоваться удалить файлы _/path/to/site/root/vqmod/vqcache/vq2-admin_model_sale_order.php_ и _/path/to/site/root/vqmod/vqcache/vq2-catalog_model_checkout_order.php_ + +##### Ручная установка + +В файле: + +``` +/catalog/model/checkout/order.php +``` + +Добавьте следующие строки в метод addOrder непосредственно перед языковой конструкцией return: + +```php +$this->load->model('retailcrm/order'); +$this->model_retailcrm_order->sendToCrm($data, $order_id); +``` + +В файле: + +``` +/admin/model/sale/order.php +``` + +Добавьте следующие строки в методы addOrder и editOrder в самом конце каждого метода: + +```php +if (!isset($data['fromApi'])) { + $this->load->model('setting/setting'); + $status = $this->model_setting_setting->getSetting('retailcrm'); + + if (!empty($data['order_status_id'])) { + $data['order_status'] = $status['retailcrm_status'][$data['order_status_id']]; + } + + $this->load->model('retailcrm/order'); + if (isset ($order_query)) { + $this->model_retailcrm_order->changeInCrm($data, $order_id); + } else { + $this->model_retailcrm_order->sendToCrm($data, $order_id); + } +} +``` + +#### Получение измений из CRM + +Для получения изменений и новых данных добавьте в cron следующую запись: + +``` +*/5 * * * * /usr/bin/php /path/to/opencart/system/cron/retailcrm/history.php >> /path/to/opencart/system/logs/cronjob_history.log 2>&1 +``` + +#### Настройка экспорта каталога + +Для периодической выгрузки каталога добавьте в cron следующую запись: + +``` +* */4 * * * /usr/bin/php /path/to/opencart/system/cron/export.php >> /path/to/opencart/system/logs/cronjob_export.log 2>&1 +``` + +В настройках CRM установите путь к файлу выгрузки + +``` +http://myopencartsite.ru/download/retailcrm.xml +``` diff --git a/admin/controller/module/retailcrm.php b/admin/controller/module/retailcrm.php new file mode 100644 index 0000000..8bbe1b0 --- /dev/null +++ b/admin/controller/module/retailcrm.php @@ -0,0 +1,188 @@ +load->model('setting/setting'); + $this->model_setting_setting->editSetting('retailcrm', array('retailcrm_status' => 1)); + } + + public function uninstall() + { + $this->load->model('setting/setting'); + $this->model_setting_setting->editSetting('retailcrm', array('retailcrm_status' => 0)); + } + + public function index() + { + + $this->load->model('setting/setting'); + $this->load->model('setting/extension'); + $this->load->model('retailcrm/references'); + $this->load->language('module/retailcrm'); + $this->document->setTitle($this->language->get('heading_title')); + $this->document->addStyle('/admin/view/stylesheet/retailcrm.css'); + + if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validate()) { + $this->model_setting_setting->editSetting('retailcrm', $this->request->post); + $this->session->data['success'] = $this->language->get('text_success'); + $this->redirect($this->url->link('module/retailcrm', 'token=' . $this->session->data['token'], 'SSL')); + } + + $text_strings = array( + 'heading_title', + 'text_enabled', + 'text_disabled', + 'button_save', + 'button_cancel', + 'text_notice', + 'retailcrm_url', + 'retailcrm_apikey', + 'retailcrm_base_settings', + 'retailcrm_dict_settings', + 'retailcrm_dict_delivery', + 'retailcrm_dict_status', + 'retailcrm_dict_payment', + ); + + foreach ($text_strings as $text) { + $this->data[$text] = $this->language->get($text); + } + + $this->data['retailcrm_errors'] = array(); + $this->data['saved_settings'] = $this->model_setting_setting->getSetting('retailcrm'); + + $url = $this->data['saved_settings']['retailcrm_url']; + $key = $this->data['saved_settings']['retailcrm_apikey']; + + if (!empty($url) && !empty($key)) { + + $this->retailcrm = new RetailcrmProxy($url, $key, DIR_SYSTEM . 'logs/retailcrm.log'); + + $this->data['delivery'] = $this->model_retailcrm_references->getDeliveryTypes(); + $this->data['statuses'] = $this->model_retailcrm_references->getOrderStatuses(); + $this->data['payments'] = $this->model_retailcrm_references->getPaymentTypes(); + + } + + $config_data = array( + 'retailcrm_status' + ); + + foreach ($config_data as $conf) { + if (isset($this->request->post[$conf])) { + $this->data[$conf] = $this->request->post[$conf]; + } else { + $this->data[$conf] = $this->config->get($conf); + } + } + + if (isset($this->error['warning'])) { + $this->data['error_warning'] = $this->error['warning']; + } else { + $this->data['error_warning'] = ''; + } + + $this->data['breadcrumbs'] = array(); + + $this->data['breadcrumbs'][] = array( + 'text' => $this->language->get('text_home'), + 'href' => $this->url->link( + 'common/home', + 'token=' . $this->session->data['token'], 'SSL' + ), + 'separator' => false + ); + + $this->data['breadcrumbs'][] = array( + 'text' => $this->language->get('text_module'), + 'href' => $this->url->link( + 'extension/module', + 'token=' . $this->session->data['token'], 'SSL' + ), + 'separator' => ' :: ' + ); + + $this->data['breadcrumbs'][] = array( + 'text' => $this->language->get('heading_title'), + 'href' => $this->url->link( + 'module/retailcrm', + 'token=' . $this->session->data['token'], 'SSL' + ), + 'separator' => ' :: ' + ); + + $this->data['action'] = $this->url->link( + 'module/retailcrm', + 'token=' . $this->session->data['token'], 'SSL' + ); + + $this->data['cancel'] = $this->url->link( + 'extension/module', + 'token=' . $this->session->data['token'], 'SSL' + ); + + + $this->data['modules'] = array(); + + if (isset($this->request->post['retailcrm_module'])) { + $this->data['modules'] = $this->request->post['retailcrm_module']; + } elseif ($this->config->get('retailcrm_module')) { + $this->data['modules'] = $this->config->get('retailcrm_module'); + } + + $this->load->model('design/layout'); + + $this->data['layouts'] = $this->model_design_layout->getLayouts(); + + $this->template = 'module/retailcrm.tpl'; + $this->children = array( + 'common/header', + 'common/footer', + ); + + $this->response->setOutput($this->render()); + } + + public function history() + { + if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/history.php')) { + $this->load->model('retailcrm/custom/history'); + $this->model_retailcrm_custom_history->request(); + } else { + $this->load->model('retailcrm/history'); + $this->model_retailcrm_history->request(); + } + } + + public function icml() + { + if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/icml.php')) { + $this->load->model('retailcrm/custom/icml'); + $this->model_retailcrm_custom_icml->generateICML(); + } else { + $this->load->model('retailcrm/icml'); + $this->model_retailcrm_icml->generateICML(); + } + } + + private function validate() + { + if (!$this->user->hasPermission('modify', 'module/retailcrm')) { + $this->error['warning'] = $this->language->get('error_permission'); + } + + if (!$this->error) { + return TRUE; + } else { + return FALSE; + } + } +} diff --git a/admin/language/english/module/intarocrm.php b/admin/language/english/module/intarocrm.php deleted file mode 100644 index b95fcbc..0000000 --- a/admin/language/english/module/intarocrm.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/admin/language/english/module/retailcrm.php b/admin/language/english/module/retailcrm.php new file mode 100644 index 0000000..06385bc --- /dev/null +++ b/admin/language/english/module/retailcrm.php @@ -0,0 +1,30 @@ + + diff --git a/admin/model/intarocrm/order.php b/admin/model/intarocrm/order.php deleted file mode 100644 index 8779f66..0000000 --- a/admin/model/intarocrm/order.php +++ /dev/null @@ -1,19 +0,0 @@ -load->model('setting/setting'); - $settings = $this->model_setting_setting->getSetting('intarocrm'); - $settings['domain'] = parse_url(HTTP_SERVER, PHP_URL_HOST); - - if(isset($settings['intarocrm_url']) && $settings['intarocrm_url'] != '' && isset($settings['intarocrm_apikey']) && $settings['intarocrm_apikey'] != '') { - include_once DIR_SYSTEM . 'library/intarocrm/apihelper.php'; - $order['order_id'] = $order_id; - $crm = new ApiHelper($settings); - $crm->processOrder($order); - } - - } -} -?> \ No newline at end of file diff --git a/admin/model/intarocrm/tools.php b/admin/model/intarocrm/tools.php deleted file mode 100644 index b3d812a..0000000 --- a/admin/model/intarocrm/tools.php +++ /dev/null @@ -1,197 +0,0 @@ -load->language('shipping/' . $extension); - - if ($this->config->get($extension . '_status')) { - $deliveryMethods[$extension.'.'.$extension] = strip_tags($this->language->get('heading_title')); - } - } - } - - return $deliveryMethods; - } - - public function getOpercartOrderStatuses() - { - $this->load->model('localisation/order_status'); - return $this->model_localisation_order_status->getOrderStatuses(array()); - } - - public function getOpercartPaymentTypes() - { - $paymentTypes = array(); - $files = glob(DIR_APPLICATION . 'controller/payment/*.php'); - - if ($files) { - foreach ($files as $file) { - $extension = basename($file, '.php'); - - $this->load->language('payment/' . $extension); - - if ($this->config->get($extension . '_status')) { - $paymentTypes[$extension] = strip_tags($this->language->get('heading_title')); - } - } - } - - return $paymentTypes; - } - - public function generateICML() - { - $string = ' - - - '.$this->config->get('config_name').' - - - - - '; - - $xml = new SimpleXMLElement($string, LIBXML_NOENT |LIBXML_NOCDATA | LIBXML_COMPACT | LIBXML_PARSEHUGE); - - $this->dd = new DOMDocument(); - $this->dd->preserveWhiteSpace = false; - $this->dd->formatOutput = true; - $this->dd->loadXML($xml->asXML()); - - $this->eCategories = $this->dd->getElementsByTagName('categories')->item(0); - $this->eOffers = $this->dd->getElementsByTagName('offers')->item(0); - - $this->addCategories(); - $this->addOffers(); - - $this->dd->saveXML(); - - $downloadPath = DIR_SYSTEM . '/../download/'; - - if (!file_exists($downloadPath)) { - mkdir($downloadPath, 0755); - } - - $this->dd->save($downloadPath . 'intarocrm.xml'); - } - - private function addCategories() - { - $this->load->model('catalog/category'); - - foreach ($this->model_catalog_category->getCategories(array()) as $category) { - $e = $this->eCategories->appendChild($this->dd->createElement('category', $category['name'])); - $e->setAttribute('id',$category['category_id']); - } - - } - - private function addOffers() - { - $this->load->model('catalog/product'); - $this->load->model('catalog/manufacturer'); - $this->load->model('tool/image'); - - $offerManufacturers = array(); - - $manufacturers = $this->model_catalog_manufacturer->getManufacturers(array()); - - foreach ($manufacturers as $manufacturer) { - $offerManufacturers[$manufacturer['manufacturer_id']] = $manufacturer['name']; - } - - foreach ($this->model_catalog_product->getProducts(array()) as $offer) { - - $e = $this->eOffers->appendChild($this->dd->createElement('offer')); - $e->setAttribute('id', $offer['product_id']); - $e->setAttribute('productId', $offer['product_id']); - $e->setAttribute('quantity', $offer['quantity']); - $e->setAttribute('available', $offer['status'] ? 'true' : 'false'); - - /* - * DIRTY HACK, NEED TO REFACTOR - */ - - $sql = "SELECT * FROM `" . - DB_PREFIX . - "product_to_category` WHERE `product_id` = " .$offer['product_id']. ";" - ; - $result = $this->db->query($sql); - foreach ($result->rows as $row) { - $e->appendChild($this->dd->createElement('categoryId', $row['category_id'])); - } - - $e->appendChild($this->dd->createElement('name'))->appendChild($this->dd->createTextNode($offer['name'])); - $e->appendChild($this->dd->createElement('productName')) - ->appendChild($this->dd->createTextNode($offer['name'])); - $e->appendChild($this->dd->createElement('price', $offer['price'])); - - if ($offer['manufacturer_id'] != 0) { - $e->appendChild($this->dd->createElement('vendor')) - ->appendChild($this->dd->createTextNode($offerManufacturers[$offer['manufacturer_id']])); - } - - if ($offer['image']) { - $e->appendChild( - $this->dd->createElement( - 'picture', - $this->model_tool_image->resize( - $offer['image'], - $this->config->get('config_image_product_width'), - $this->config->get('config_image_product_height') - ) - ) - ); - } - - $this->url = new Url(HTTP_CATALOG, $this->config->get('config_secure') ? HTTP_CATALOG : HTTPS_CATALOG); - $e->appendChild($this->dd->createElement('url'))->appendChild( - $this->dd->createTextNode( - $this->url->link('product/product&product_id=' . $offer['product_id']) - ) - ); - - if ($offer['sku'] != '') { - $sku = $this->dd->createElement('param'); - $sku->setAttribute('name', 'article'); - $sku->appendChild($this->dd->createTextNode($offer['sku'])); - $e->appendChild($sku); - } - - if ($offer['weight'] != '') { - $weight = $this->dd->createElement('param'); - $weight->setAttribute('name', 'weight'); - $weightValue = (isset($offer['weight_class'])) - ? round($offer['weight'], 3) . ' ' . $offer['weight_class'] - : round($offer['weight'], 3) - ; - $weight->appendChild($this->dd->createTextNode($weightValue)); - $e->appendChild($weight); - } - - if ($offer['length'] != '' && $offer['width'] != '' && $offer['height'] != '') { - $size = $this->dd->createElement('param'); - $size->setAttribute('name', 'size'); - $size->appendChild( - $this->dd->createTextNode( - round($offer['length'], 2) .'x'. - round($offer['width'], 2) .'x'. - round($offer['height'], 2) - ) - ); - $e->appendChild($size); - } - } - } -} \ No newline at end of file diff --git a/admin/model/retailcrm/custom/.gitkeep b/admin/model/retailcrm/custom/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/admin/controller/module/intarocrm.php b/admin/model/retailcrm/history.php similarity index 55% rename from admin/controller/module/intarocrm.php rename to admin/model/retailcrm/history.php index 5ffa43e..b8a2afa 100644 --- a/admin/controller/module/intarocrm.php +++ b/admin/model/retailcrm/history.php @@ -1,283 +1,55 @@ load->model('setting/setting'); - $this->model_setting_setting->editSetting('intarocrm', array('intarocrm_status'=>1)); - } - - public function uninstall() { - $this->load->model('setting/setting'); - $this->model_setting_setting->editSetting('intarocrm', array('intarocrm_status'=>0)); - } - - public function index() { - - $this->log = new Monolog\Logger('opencart-module'); - $this->log->pushHandler( - new Monolog\Handler\StreamHandler(DIR_LOGS . 'intarocrm_module.log', Monolog\Logger::INFO) - ); - - $this->load->model('setting/setting'); - $this->load->model('setting/extension'); - $this->load->model('intarocrm/tools'); - $this->load->language('module/intarocrm'); - $this->document->setTitle($this->language->get('heading_title')); - $this->document->addStyle('/admin/view/stylesheet/intarocrm.css'); - - if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { - $this->model_setting_setting->editSetting('intarocrm', $this->request->post); - $this->session->data['success'] = $this->language->get('text_success'); - $this->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')); - } - - $text_strings = array( - 'heading_title', - 'text_enabled', - 'text_disabled', - 'button_save', - 'button_cancel', - 'text_notice', - 'intarocrm_url', - 'intarocrm_apikey', - 'intarocrm_base_settings', - 'intarocrm_dict_settings', - 'intarocrm_dict_delivery', - 'intarocrm_dict_status', - 'intarocrm_dict_payment', - ); - - foreach ($text_strings as $text) { - $this->data[$text] = $this->language->get($text); - } - - $this->data['intarocrm_errors'] = array(); - $this->data['saved_settings'] = $this->model_setting_setting->getSetting('intarocrm'); - - if ($this->data['saved_settings']['intarocrm_url'] != '' && - $this->data['saved_settings']['intarocrm_apikey'] != '' - ) { - - $this->intarocrm = new \IntaroCrm\RestApi( - $this->data['saved_settings']['intarocrm_url'], - $this->data['saved_settings']['intarocrm_apikey'] - ); - - /* - * Delivery - */ - - try { - $this->deliveryTypes = $this->intarocrm->deliveryTypesList(); - } - catch (IntaroCrm\Exception\ApiException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . - $this->config->get('store_name') . - '] RestApi::deliveryTypesList::Api:' . $e->getMessage() - ); - } - catch (IntaroCrm\Exception\CurlException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . $this->config->get('store_name') . - '] RestApi::deliveryTypesList::Curl:' . $e->getMessage() - ); - } - - $this->data['delivery'] = array( - 'opencart' => $this->model_intarocrm_tools->getOpercartDeliveryMethods(), - 'intarocrm' => $this->deliveryTypes - ); - - /* - * Statuses - */ - try { - $this->statuses = $this->intarocrm->orderStatusesList(); - } - catch (IntaroCrm\Exception\ApiException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . - $this->config->get('store_name') . - '] RestApi::orderStatusesList::Api:' . $e->getMessage() - ); - } - catch (IntaroCrm\Exception\CurlException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . - $this->config->get('store_name') . - '] RestApi::orderStatusesList::Curl:' . $e->getMessage() - ); - } - - $this->data['statuses'] = array( - 'opencart' => $this->model_intarocrm_tools->getOpercartOrderStatuses(), - 'intarocrm' => $this->statuses - ); - - /* - * Payment - */ - - try { - $this->payments = $this->intarocrm->paymentTypesList(); - } - catch (IntaroCrm\Exception\ApiException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . - $this->config->get('store_name') . - '] RestApi::paymentTypesList::Api:' . $e->getMessage() - ); - } - catch (IntaroCrm\Exception\CurlException $e) - { - $this->data['intarocrm_error'][] = $e->getMessage(); - $this->log->addError( - '[' . - $this->config->get('store_name') . - '] RestApi::paymentTypesList::Curl:' . $e->getMessage() - ); - } - - $this->data['payments'] = array( - 'opencart' => $this->model_intarocrm_tools->getOpercartPaymentTypes(), - 'intarocrm' => $this->payments - ); - - } - - $config_data = array( - 'intarocrm_status' - ); - - foreach ($config_data as $conf) { - if (isset($this->request->post[$conf])) { - $this->data[$conf] = $this->request->post[$conf]; - } else { - $this->data[$conf] = $this->config->get($conf); - } - } - - if (isset($this->error['warning'])) { - $this->data['error_warning'] = $this->error['warning']; - } else { - $this->data['error_warning'] = ''; - } - - $this->data['breadcrumbs'] = array(); - - $this->data['breadcrumbs'][] = array( - 'text' => $this->language->get('text_home'), - 'href' => $this->url->link('common/home', 'token=' . $this->session->data['token'], 'SSL'), - 'separator' => false - ); - - $this->data['breadcrumbs'][] = array( - 'text' => $this->language->get('text_module'), - 'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'), - 'separator' => ' :: ' - ); - - $this->data['breadcrumbs'][] = array( - 'text' => $this->language->get('heading_title'), - 'href' => $this->url->link('module/intarocrm', 'token=' . $this->session->data['token'], 'SSL'), - 'separator' => ' :: ' - ); - - $this->data['action'] = $this->url->link('module/intarocrm', 'token=' . $this->session->data['token'], 'SSL'); - - $this->data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'); - - - $this->data['modules'] = array(); - - if (isset($this->request->post['intarocrm_module'])) { - $this->data['modules'] = $this->request->post['intarocrm_module']; - } elseif ($this->config->get('intarocrm_module')) { - $this->data['modules'] = $this->config->get('intarocrm_module'); - } - - $this->load->model('design/layout'); - - $this->data['layouts'] = $this->model_design_layout->getLayouts(); - - $this->template = 'module/intarocrm.tpl'; - $this->children = array( - 'common/header', - 'common/footer', - ); - - $this->response->setOutput($this->render()); - } - - public function order_history() +class ModelRetailcrmHistory extends Model +{ + public function request() { - $this->log = new Monolog\Logger('opencart-module'); - $this->log->pushHandler( - new Monolog\Handler\StreamHandler(DIR_LOGS . 'intarocrm_module.log', Monolog\Logger::INFO) - ); - $this->load->model('setting/setting'); $this->load->model('setting/store'); $this->load->model('sale/order'); $this->load->model('sale/customer'); - $this->load->model('intarocrm/tools'); + $this->load->model('retailcrm/tools'); $this->load->model('catalog/product'); $this->load->model('localisation/zone'); - $this->load->language('module/intarocrm'); + $this->load->language('module/retailcrm'); - $settings = $this->model_setting_setting->getSetting('intarocrm'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); $settings['domain'] = parse_url(HTTP_SERVER, PHP_URL_HOST); - if (isset($settings['intarocrm_url']) && - $settings['intarocrm_url'] != '' && - isset($settings['intarocrm_apikey']) && - $settings['intarocrm_apikey'] != '' - ) { - include_once __DIR__ . '/../../../system/library/intarocrm/apihelper.php'; - $crm = new ApiHelper($settings); - $orders = $crm->orderHistory(); + if (!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + $crm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $orders = $crm->ordersHistory(); $ordersIdsFix = array(); $customersIdsFix = array(); $subtotalSettings = $this->model_setting_setting->getSetting('sub_total'); $totalSettings = $this->model_setting_setting->getSetting('total'); $shippingSettings = $this->model_setting_setting->getSetting('shipping'); - $delivery = array_flip($settings['intarocrm_delivery']); - $payment = array_flip($settings['intarocrm_payment']); - $status = array_flip($settings['intarocrm_status']); + $delivery = array_flip($settings['retailcrm_delivery']); + $payment = array_flip($settings['retailcrm_payment']); + $status = array_flip($settings['retailcrm_status']); - $ocPayment = $this->model_intarocrm_tools->getOpercartPaymentTypes(); - $ocDelivery = $this->model_intarocrm_tools->getOpercartDeliveryMethods(); + $ocPayment = $this->model_retailcrm_tools->getOpercartPaymentTypes(); + $ocDelivery = $this->model_retailcrm_tools->getOpercartDeliveryMethods(); $zones = $this->model_localisation_zone->getZones(); foreach ($orders as $order) { - if (!isset($order['deleted']) || !$order['deleted']) { + if (empty($order['deleted'])) { $data = array(); - $customer_id = (isset($order['customer']['externalId']) && $order['customer']['externalId'] != 0) + $customer_id = (!empty($order['customer']['externalId'])) ? $order['customer']['externalId'] : '' - ; + ; if (isset($order['externalId'])) { /* @@ -297,12 +69,12 @@ class ControllerModuleIntarocrm extends Controller { 'lastname' => (isset($order['customer']['lastName'])) ? $order['customer']['lastName'] : ' ' - , + , 'email' => $order['customer']['email'], 'telephone' => (isset($order['customer']['phones'][0]['number'])) ? $order['customer']['phones'][0]['number'] : ' ' - , + , 'newsletter' => 0, 'password' => 'tmppass', 'status' => 1, @@ -311,16 +83,16 @@ class ControllerModuleIntarocrm extends Controller { 'lastname' => (isset($order['customer']['lastName'])) ? $order['customer']['lastName'] : ' ' - , + , 'address_1' => $order['customer']['address']['text'], 'city' => isset($order['customer']['address']['city']) ? $order['customer']['address']['city'] : $order['delivery']['address']['city'] - , + , 'postcode' => isset($order['customer']['address']['index']) ? $order['customer']['address']['index'] : $order['delivery']['address']['index'] - , + , ), 'tax_id' => '', 'zone_id' => '', @@ -349,7 +121,7 @@ class ControllerModuleIntarocrm extends Controller { $data['store_id'] = ($this->config->get('config_store_id') == null) ? 0 : $this->config->get('config_store_id') - ; + ; $data['customer'] = $order['customer']['firstName']; $data['customer_id'] = $customer_id; $data['customer_group_id'] = 1; @@ -359,7 +131,7 @@ class ControllerModuleIntarocrm extends Controller { $data['telephone'] = (isset($order['customer']['phones'][0]['number'])) ? $order['customer']['phones'][0]['number'] : ' ' - ; + ; $data['comment'] = isset($order['customerComment']) ? $order['customerComment'] : ''; $data['fax'] = ''; @@ -374,11 +146,11 @@ class ControllerModuleIntarocrm extends Controller { $data['payment_city'] = isset($order['customer']['address']['city']) ? $order['customer']['address']['city'] : $order['delivery']['address']['city'] - ; + ; $data['payment_postcode'] = isset($order['customer']['address']['index']) ? $order['customer']['address']['index'] : $order['delivery']['address']['index'] - ; + ; /* * Country & zone id detection @@ -401,11 +173,11 @@ class ControllerModuleIntarocrm extends Controller { $data['payment_country_id'] = isset($order['customer']['address']['country']) ? $order['customer']['address']['country'] : $order['delivery']['address']['country'] - ; + ; $data['payment_zone_id'] = isset($order['customer']['address']['region']) ? $order['customer']['address']['region'] : $region - ; + ; $data['shipping_country_id'] = $order['delivery']['address']['country']; $data['shipping_zone_id'] = $region; @@ -415,7 +187,7 @@ class ControllerModuleIntarocrm extends Controller { $data['shipping_lastname'] = (isset($order['customer']['lastName'])) ? $order['customer']['lastName'] : ' ' - ; + ; $data['shipping_address_1'] = $order['delivery']['address']['text']; $data['shipping_address_2'] = ''; $data['shipping_company'] = ''; @@ -490,10 +262,10 @@ class ControllerModuleIntarocrm extends Controller { 'value' => isset($order['totalSumm']) ? $order['totalSumm'] : $order['summ'] + $deliveryCost - , + , 'text' => isset($order['totalSumm']) - ? $order['totalSumm'] - : $order['summ'] + $deliveryCost + ? $order['totalSumm'] + : $order['summ'] + $deliveryCost , 'sort_order' => $totalSettings['total_sort_order'] ) @@ -521,39 +293,15 @@ class ControllerModuleIntarocrm extends Controller { } if (!empty($customersIdsFix)) { - $crm->customerFixExternalIds($customersIdsFix); + $crm->customersFixExternalIds($customersIdsFix); } if (!empty($ordersIdsFix)) { - $crm->orderFixExternalIds($ordersIdsFix); + $crm->ordersFixExternalIds($ordersIdsFix); } } else { - $this->log->addNotice( - '['. - $this->config->get('store_name'). - '] RestApi::orderHistory: you need to configure Intarocrm module first.' - ); + $this->log->addNotice('You need to configure retailcrm module first.'); } } - - public function export_icml() - { - $this->load->model('intarocrm/tools'); - $this->model_intarocrm_tools->generateICML(); - } - - private function validate() { - if (!$this->user->hasPermission('modify', 'module/intarocrm')) { - $this->error['warning'] = $this->language->get('error_permission'); - } - - if (!$this->error) { - return TRUE; - } else { - return FALSE; - } - } - - } diff --git a/admin/model/retailcrm/icml.php b/admin/model/retailcrm/icml.php new file mode 100644 index 0000000..e584614 --- /dev/null +++ b/admin/model/retailcrm/icml.php @@ -0,0 +1,215 @@ +load->language('module/retailcrm'); + $this->load->model('catalog/category'); + $this->load->model('catalog/product'); + $this->load->model('catalog/manufacturer'); + + $string = ' + + + '.$this->config->get('config_name').' + + + + + '; + + $xml = new SimpleXMLElement( + $string, + LIBXML_NOENT |LIBXML_NOCDATA | LIBXML_COMPACT | LIBXML_PARSEHUGE + ); + + $this->dd = new DOMDocument(); + $this->dd->preserveWhiteSpace = false; + $this->dd->formatOutput = true; + $this->dd->loadXML($xml->asXML()); + + $this->eCategories = $this->dd + ->getElementsByTagName('categories')->item(0); + $this->eOffers = $this->dd + ->getElementsByTagName('offers')->item(0); + + $this->addCategories(); + $this->addOffers(); + + $this->dd->saveXML(); + + $downloadPath = DIR_SYSTEM . '/../download/'; + + if (!file_exists($downloadPath)) { + mkdir($downloadPath, 0755); + } + + $this->dd->save($downloadPath . 'retailcrm.xml'); + } + + /** + * + */ + private function addCategories() + { + $categories = $this->model_catalog_category->getCategories(array()); + foreach($categories as $category) { + $e = $this->eCategories->appendChild( + $this->dd->createElement( + 'category', $category['name'] + ) + ); + + $e->setAttribute('id', $category['category_id']); + + if ($category['parent_id'] > 0) { + $e->setAttribute('parentId', $category['parent_id']); + } + } + + } + + private function addOffers() + { + $offerManufacturers = array(); + + $manufacturers = $this->model_catalog_manufacturer + ->getManufacturers(array()); + + foreach ($manufacturers as $manufacturer) { + $offerManufacturers[ + $manufacturer['manufacturer_id'] + ] = $manufacturer['name']; + } + + $products = $this->model_catalog_product->getProducts(array()); + + foreach ($products as $offer) { + + $e = $this->eOffers->appendChild($this->dd->createElement('offer')); + $e->setAttribute('id', $offer['product_id']); + $e->setAttribute('productId', $offer['product_id']); + $e->setAttribute('quantity', $offer['quantity']); + + /** + * Offer activity + */ + $activity = $offer['status'] == 1 ? 'Y' : 'N'; + $e->appendChild( + $this->dd->createElement('productActivity') + )->appendChild( + $this->dd->createTextNode($activity) + ); + + /** + * Offer categories + */ + $categories = $this->model_catalog_product + ->getProductCategories($offer['product_id']); + + if (!empty($categories)) { + foreach ($categories as $category) { + $e->appendChild($this->dd->createElement('category')) + ->appendChild( + $this->dd->createTextNode($category) + ); + } + } + + /** + * Name & price + */ + $e->appendChild($this->dd->createElement('name')) + ->appendChild($this->dd->createTextNode($offer['name'])); + $e->appendChild($this->dd->createElement('productName')) + ->appendChild($this->dd->createTextNode($offer['name'])); + $e->appendChild($this->dd->createElement('price')) + ->appendChild($this->dd->createTextNode($offer['price'])); + + /** + * Vendor + */ + if ($offer['manufacturer_id'] != 0) { + $e->appendChild($this->dd->createElement('vendor')) + ->appendChild( + $this->dd->createTextNode( + $offerManufacturers[$offer['manufacturer_id']] + ) + ); + } + + /** + * Image + */ + if ($offer['image']) { + $image = $this->generateImage($offer['image']); + $e->appendChild($this->dd->createElement('picture')) + ->appendChild($this->dd->createTextNode($image)); + } + + /** + * Url + */ + $this->url = new Url( + HTTP_CATALOG, + $this->config->get('config_secure') + ? HTTP_CATALOG + : HTTPS_CATALOG + ); + + $e->appendChild($this->dd->createElement('url')) + ->appendChild( + $this->dd->createTextNode( + $this->url->link( + 'product/product&product_id=' . $offer['product_id'] + ) + ) + ); + + + if ($offer['sku']) { + $sku = $this->dd->createElement('param'); + $sku->setAttribute('code', 'article'); + $sku->setAttribute('name', $this->language->get('article')); + $sku->appendChild($this->dd->createTextNode($offer['sku'])); + $e->appendChild($sku); + } + + if ($offer['weight'] != '') { + $weight = $this->dd->createElement('param'); + $weight->setAttribute('color', 'weight'); + $weight->setAttribute('name', $this->language->get('weight')); + $weightValue = (isset($offer['weight_class'])) + ? round($offer['weight'], 3) . ' ' . $offer['weight_class'] + : round($offer['weight'], 3) + ; + $weight->appendChild($this->dd->createTextNode($weightValue)); + $e->appendChild($weight); + } + } + } + + /** + * @param $image + * @return mixed + */ + private function generateImage($image) + { + $this->load->model('tool/image'); + + return $this->model_tool_image->resize( + $image, + $this->config->get('config_image_product_width'), + $this->config->get('config_image_product_height') + ); + } +} diff --git a/admin/model/retailcrm/order.php b/admin/model/retailcrm/order.php new file mode 100644 index 0000000..5ef90c5 --- /dev/null +++ b/admin/model/retailcrm/order.php @@ -0,0 +1,174 @@ +load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + require_once DIR_SYSTEM . 'library/retailcrm/bootstrap.php'; + + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $order = array(); + + $payment_code = $order_data['payment_code']; + $delivery_code = $order_data['shipping_code']; + + $customers = $this->retailcrm->customersList( + array( + 'name' => $order_data['telephone'], + 'email' => $order_data['email'] + ), + 1, + 100 + ); + + foreach($customers['customers'] as $customer) { + $order['customer']['id'] = $customer['id']; + } + + unset($customers); + + $order['externalId'] = $order_id; + $order['firstName'] = $order_data['firstname']; + $order['lastName'] = $order_data['lastname']; + $order['email'] = $order_data['email']; + $order['phone'] = $order_data['telephone']; + $order['customerComment'] = $order_data['comment']; + + $deliveryCost = 0; + $orderTotals = isset($order_data['totals']) ? $order_data['totals'] : $order_data['order_total'] ; + + foreach ($orderTotals as $totals) { + if ($totals['code'] == 'shipping') { + $deliveryCost = $totals['value']; + } + } + + $order['createdAt'] = date('Y-m-d H:i:s'); + $order['paymentType'] = $settings['retailcrm_payment'][$payment_code]; + + $country = (isset($order_data['shipping_country'])) ? $order_data['shipping_country'] : '' ; + + $order['delivery'] = array( + 'code' => $settings['retailcrm_delivery'][$delivery_code], + 'cost' => $deliveryCost, + 'address' => array( + 'index' => $order_data['shipping_postcode'], + 'city' => $order_data['shipping_city'], + 'country' => $order_data['shipping_country_id'], + 'region' => $order_data['shipping_zone_id'], + 'text' => implode(', ', array( + $order_data['shipping_postcode'], + $country, + $order_data['shipping_city'], + $order_data['shipping_address_1'], + $order_data['shipping_address_2'] + )) + ) + ); + + $orderProducts = isset($order_data['products']) ? $order_data['products'] : $order_data['order_product']; + + foreach ($orderProducts as $product) { + $order['items'][] = array( + 'productId' => $product['product_id'], + 'productName' => $product['name'], + 'initialPrice' => $product['price'], + 'quantity' => $product['quantity'], + ); + } + + if (isset($order_data['order_status_id'])) { + $order['status'] = $settings['retailcrm_status'][$order_data['order_status_id']]; + } + + $this->retailcrm->ordersCreate($order); + } + } + + public function changeInCrm($order_data, $order_id) + { + $this->load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + require_once DIR_SYSTEM . 'library/retailcrm/bootstrap.php'; + + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $order = array(); + + $payment_code = $order_data['payment_code']; + $delivery_code = $order_data['shipping_code']; + + $order['externalId'] = $order_id; + $order['firstName'] = $order_data['firstname']; + $order['lastName'] = $order_data['lastname']; + $order['email'] = $order_data['email']; + $order['phone'] = $order_data['telephone']; + $order['customerComment'] = $order_data['comment']; + + $deliveryCost = 0; + $orderTotals = isset($order_data['totals']) ? $order_data['totals'] : $order_data['order_total'] ; + + foreach ($orderTotals as $totals) { + if ($totals['code'] == 'shipping') { + $deliveryCost = $totals['value']; + } + } + + $order['createdAt'] = date('Y-m-d H:i:s'); + $order['paymentType'] = $settings['retailcrm_payment'][$payment_code]; + + $country = (isset($order_data['shipping_country'])) ? $order_data['shipping_country'] : '' ; + + $order['delivery'] = array( + 'code' => $settings['retailcrm_delivery'][$delivery_code], + 'cost' => $deliveryCost, + 'address' => array( + 'index' => $order_data['shipping_postcode'], + 'city' => $order_data['shipping_city'], + 'country' => $order_data['shipping_country_id'], + 'region' => $order_data['shipping_zone_id'], + 'text' => implode(', ', array( + $order_data['shipping_postcode'], + $country, + $order_data['shipping_city'], + $order_data['shipping_address_1'], + $order_data['shipping_address_2'] + )) + ) + ); + + $orderProducts = isset($order_data['products']) ? $order_data['products'] : $order_data['order_product']; + + foreach ($orderProducts as $product) { + $order['items'][] = array( + 'productId' => $product['product_id'], + 'productName' => $product['name'], + 'initialPrice' => $product['price'], + 'quantity' => $product['quantity'], + ); + } + + if (isset($order_data['order_status_id'])) { + $order['status'] = $settings['retailcrm_status'][$order_data['order_status_id']]; + } + + $this->retailcrm->ordersEdit($order); + } + } +} + diff --git a/admin/model/retailcrm/references.php b/admin/model/retailcrm/references.php new file mode 100644 index 0000000..d031250 --- /dev/null +++ b/admin/model/retailcrm/references.php @@ -0,0 +1,138 @@ + $this->getOpercartDeliveryTypes(), + 'retailcrm' => $this->getApiDeliveryTypes() + ); + } + + public function getOrderStatuses() + { + return array( + 'opencart' => $this->getOpercartOrderStatuses(), + 'retailcrm' => $this->getApiOrderStatuses() + ); + } + + public function getPaymentTypes() + { + return array( + 'opencart' => $this->getOpercartPaymentTypes(), + 'retailcrm' => $this->getApiPaymentTypes() + ); + } + + protected function getOpercartDeliveryTypes() + { + $deliveryMethods = array(); + $files = glob(DIR_APPLICATION . 'controller/shipping/*.php'); + + if ($files) { + foreach ($files as $file) { + $extension = basename($file, '.php'); + + $this->load->language('shipping/' . $extension); + + if ($this->config->get($extension . '_status')) { + $deliveryMethods[$extension.'.'.$extension] = strip_tags( + $this->language->get('heading_title') + ); + } + } + } + + return $deliveryMethods; + } + + protected function getOpercartOrderStatuses() + { + $this->load->model('localisation/order_status'); + + return $this->model_localisation_order_status + ->getOrderStatuses(array()); + } + + protected function getOpercartPaymentTypes() + { + $paymentTypes = array(); + $files = glob(DIR_APPLICATION . 'controller/payment/*.php'); + + if ($files) { + foreach ($files as $file) { + $extension = basename($file, '.php'); + + $this->load->language('payment/' . $extension); + + if ($this->config->get($extension . '_status')) { + $paymentTypes[$extension] = strip_tags( + $this->language->get('heading_title') + ); + } + } + } + + return $paymentTypes; + } + + protected function getApiDeliveryTypes() + { + $this->load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $response = $this->retailcrm->deliveryTypesList(); + + return ($response === false) ? array() : $response->deliveryTypes; + } + } + + protected function getApiOrderStatuses() + { + $this->load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $response = $this->retailcrm->statusesList(); + + return ($response === false) ? array() : $response->statuses; + } + } + + protected function getApiPaymentTypes() + { + $this->load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $response = $this->retailcrm->paymentTypesList(); + + return ($response === false) ? array() : $response->paymentTypes; + } + } +} diff --git a/admin/view/stylesheet/intarocrm.css b/admin/view/stylesheet/intarocrm.css deleted file mode 100644 index 8bf0ceb..0000000 --- a/admin/view/stylesheet/intarocrm.css +++ /dev/null @@ -1,2 +0,0 @@ -.intarocrm_unit {margin-bottom: 10px;} -.intarocrm_unit input {width: 30%;} \ No newline at end of file diff --git a/admin/view/stylesheet/retailcrm.css b/admin/view/stylesheet/retailcrm.css new file mode 100644 index 0000000..43cbeb6 --- /dev/null +++ b/admin/view/stylesheet/retailcrm.css @@ -0,0 +1,2 @@ +.retailcrm_unit {margin-bottom: 10px;} +.retailcrm_unit input {width: 30%;} diff --git a/admin/view/template/module/intarocrm.tpl b/admin/view/template/module/retailcrm.tpl similarity index 50% rename from admin/view/template/module/intarocrm.tpl rename to admin/view/template/module/retailcrm.tpl index 6154a66..53e4841 100644 --- a/admin/view/template/module/intarocrm.tpl +++ b/admin/view/template/module/retailcrm.tpl @@ -9,10 +9,10 @@
- +
- /admin/settings#t-main + /admin/settings#t-main
@@ -23,67 +23,67 @@
- + -

-
-
- +

+
+
+
-
-
- +
+
+
- + - - -
+ + +
-

+

-

+

$value): ?> -
- + $v): ?> + - +
-

+

-
- + $v): ?> + - +
-

+

$value): ?> -
- + $v): ?> + - +
@@ -93,8 +93,7 @@
-
- \ No newline at end of file + diff --git a/catalog/model/intarocrm/order.php b/catalog/model/intarocrm/order.php deleted file mode 100644 index 8779f66..0000000 --- a/catalog/model/intarocrm/order.php +++ /dev/null @@ -1,19 +0,0 @@ -load->model('setting/setting'); - $settings = $this->model_setting_setting->getSetting('intarocrm'); - $settings['domain'] = parse_url(HTTP_SERVER, PHP_URL_HOST); - - if(isset($settings['intarocrm_url']) && $settings['intarocrm_url'] != '' && isset($settings['intarocrm_apikey']) && $settings['intarocrm_apikey'] != '') { - include_once DIR_SYSTEM . 'library/intarocrm/apihelper.php'; - $order['order_id'] = $order_id; - $crm = new ApiHelper($settings); - $crm->processOrder($order); - } - - } -} -?> \ No newline at end of file diff --git a/catalog/model/retailcrm/order.php b/catalog/model/retailcrm/order.php new file mode 100644 index 0000000..e021e30 --- /dev/null +++ b/catalog/model/retailcrm/order.php @@ -0,0 +1,97 @@ +load->model('setting/setting'); + $settings = $this->model_setting_setting->getSetting('retailcrm'); + + if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { + require_once DIR_SYSTEM . 'library/retailcrm/bootstrap.php'; + + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + + $order = array(); + + $payment_code = $order_data['payment_code']; + $delivery_code = $order_data['shipping_code']; + + $customers = $this->retailcrm->customersList( + array( + 'name' => $order_data['telephone'], + 'email' => $order_data['email'] + ), + 1, + 100 + ); + + foreach($customers['customers'] as $customer) { + $order['customer']['id'] = $customer['id']; + } + + unset($customers); + + $order['externalId'] = $order_id; + $order['firstName'] = $order_data['firstname']; + $order['lastName'] = $order_data['lastname']; + $order['email'] = $order_data['email']; + $order['phone'] = $order_data['telephone']; + $order['customerComment'] = $order_data['comment']; + + $deliveryCost = 0; + $orderTotals = isset($order_data['totals']) ? $order_data['totals'] : $order_data['order_total'] ; + + foreach ($orderTotals as $totals) { + if ($totals['code'] == 'shipping') { + $deliveryCost = $totals['value']; + } + } + + $order['createdAt'] = date('Y-m-d H:i:s'); + $order['paymentType'] = $settings['retailcrm_payment'][$payment_code]; + + $country = (isset($order_data['shipping_country'])) ? $order_data['shipping_country'] : '' ; + + $order['delivery'] = array( + 'code' => $settings['retailcrm_delivery'][$delivery_code], + 'cost' => $deliveryCost, + 'address' => array( + 'index' => $order_data['shipping_postcode'], + 'city' => $order_data['shipping_city'], + 'country' => $order_data['shipping_country_id'], + 'region' => $order_data['shipping_zone_id'], + 'text' => implode(', ', array( + $order_data['shipping_postcode'], + $country, + $order_data['shipping_city'], + $order_data['shipping_address_1'], + $order_data['shipping_address_2'] + )) + ) + ); + + $orderProducts = isset($order_data['products']) ? $order_data['products'] : $order_data['order_product']; + + foreach ($orderProducts as $product) { + $order['items'][] = array( + 'productId' => $product['product_id'], + 'productName' => $product['name'], + 'initialPrice' => $product['price'], + 'quantity' => $product['quantity'], + ); + } + + if (isset($order_data['order_status_id'])) { + $order['status'] = $settings['retailcrm_status'][$order_data['order_status_id']]; + } + + $this->retailcrm->ordersCreate($order); + } + } +} + diff --git a/cli/cli_export.php b/cli/cli_export.php deleted file mode 100644 index f07aa2e..0000000 --- a/cli/cli_export.php +++ /dev/null @@ -1,3 +0,0 @@ - Intstall module. Before running exchange you must configure module. - -### Export - -Setup cron job for periodically catalog export - -``` -* */12 * * * /usr/bin/php /path/to/opencart/cli/cli_export.php >> /path/to/opencart/system/logs/cronjob_export.log 2>&1 -``` - -Into your CRM settings set path to exported file - -``` -/download/intarocrm.xml -``` - -### Exchange setup - - -#### Export new order from shop to CRM - -``` -$this->load->model('intarocrm/order'); -$this->model_intarocrm_order->send($data, $order_id); -``` - -Add this lines into: -* /catalog/model/checkout/order.php script, into addOrder method before return statement - -``` -if (!isset($data['fromApi'])) { - $this->load->model('setting/setting'); - $status = $this->model_setting_setting->getSetting('intarocrm'); - $data['order_status'] = $status['intarocrm_status'][$data['order_status_id']]; - - $this->load->model('intarocrm/order'); - $this->model_intarocrm_order->send($data, $order_id); -} -``` - -Add this lines into: -* /admin/model/sale/order.php script, into addOrder & editOrder methods at the end of these methods - -#### Export new order from CRM to shop - -Setup cron job for exchange between CRM & your shop - -``` -*/5 * * * * /usr/bin/php /path/to/opencart/cli/cli_history.php >> /path/to/opencart/system/logs/cronjob_history.log 2>&1 -``` diff --git a/doc/TODO.md b/doc/TODO.md deleted file mode 100644 index 32dce71..0000000 --- a/doc/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -TODO -==== - -* Export old customers & orders -* New customers export -* Make sources PSR-2 compatible \ No newline at end of file diff --git a/cli/cli_dispatch.php b/system/cron/dispatch.php similarity index 94% rename from cli/cli_dispatch.php rename to system/cron/dispatch.php index 8baa5e4..1c756ed 100644 --- a/cli/cli_dispatch.php +++ b/system/cron/dispatch.php @@ -1,12 +1,5 @@ write("ERROR: cli $cli_action call attempted by non-cli."); - http_response_code(400); - exit; -} - // Ensure $cli_action is set if (!isset($cli_action)) { echo 'ERROR: $cli_action must be set in calling script.'; @@ -19,7 +12,7 @@ if (!isset($cli_action)) { define('VERSION', '1.5.6'); // Configuration (note we're using the admin config) -require_once(__DIR__ . '/../admin/config.php'); +require_once(realpath(dirname(__FILE__)) . '/../../admin/config.php'); // Configuration check if (!defined('DIR_APPLICATION')) { @@ -138,4 +131,3 @@ $controller->dispatch($action, new Action('error/not_found')); // Output $response->output(); -?> \ No newline at end of file diff --git a/system/cron/export.php b/system/cron/export.php new file mode 100644 index 0000000..85d7d82 --- /dev/null +++ b/system/cron/export.php @@ -0,0 +1,3 @@ +dir = __DIR__ . '/../../logs/'; - $this->fileDate = $this->dir . 'intarocrm_history.log'; - $this->settings = $settings; - $this->domain = $settings['domain']; - - $this->log = new Monolog\Logger('intarocrm'); - $this->log->pushHandler( - new Monolog\Handler\StreamHandler($this->dir . 'intarocrm_module.log', Monolog\Logger::INFO) - ); - - $this->intaroApi = new IntaroCrm\RestApi( - $settings['intarocrm_url'], - $settings['intarocrm_apikey'] - ); - } - - public function processOrder($data) { - - $order = array(); - $customer = array(); - $customers = array(); - - $payment_code = $data['payment_code']; - $delivery_code = $data['shipping_code']; - $settings = $this->settings; - - try { - $customers = $this->intaroApi->customers($data['telephone'], $data['email'], $data['lastname'], 200, 0); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::customers:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::customers:' . json_encode($data)); - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::customers::Curl:' . $e->getMessage()); - } - - - if(count($customers) > 0 && isset($customers[0]['externalId'])) { - $order['customerId'] = $customers[0]['externalId']; - } else { - $order['customerId'] = ($data['customer_id'] != '') ? $data['customer_id'] : (int) substr((microtime(true) * 10000) . mt_rand(1, 1000), 10, -1); - $customer['externalId'] = $order['customerId']; - $customer['firstName'] = $data['firstname']; - $customer['lastName'] = $data['lastname']; - $customer['email'] = $data['email']; - $customer['phones'] = array(array('number' => $data['telephone'])); - - $customer['address']['country'] = $data['payment_country_id']; - $customer['address']['region'] = $data['payment_zone_id']; - - $customer['address']['text'] = implode(', ', array( - $data['payment_postcode'], - $data['payment_country'], - $data['payment_city'], - $data['payment_address_1'], - $data['payment_address_2'] - )); - - try { - $this->customer = $this->intaroApi->customerEdit($customer); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->customer = $e->getMessage(); - $this->log->addError('['.$this->domain.'] RestApi::orderCreate:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::orderCreate:' . json_encode($order)); - } catch (IntaroCrm\Exception\CurlException $e) { - $this->customer = $e->getMessage(); - $this->log->addError('['.$this->domain.'] RestApi::orderCreate::Curl:' . $e->getMessage()); - } - } - - unset($customer); - unset($customers); - - $order['externalId'] = $data['order_id']; - $order['firstName'] = $data['firstname']; - $order['lastName'] = $data['lastname']; - $order['email'] = $data['email']; - $order['phone'] = $data['telephone']; - $order['customerComment'] = $data['comment']; - - $deliveryCost = 0; - $orderTotals = isset($data['totals']) ? $data['totals'] : $data['order_total'] ; - - foreach ($orderTotals as $totals) { - if ($totals['code'] == 'shipping') { - $deliveryCost = $totals['value']; - } - } - - $order['createdAt'] = date('Y-m-d H:i:s'); - $order['paymentType'] = $settings['intarocrm_payment'][$payment_code]; - - $country = (isset($data['shipping_country'])) ? $data['shipping_country'] : '' ; - - $order['delivery'] = array( - 'code' => $settings['intarocrm_delivery'][$delivery_code], - 'cost' => $deliveryCost, - 'address' => array( - 'index' => $data['shipping_postcode'], - 'city' => $data['shipping_city'], - 'country' => $data['shipping_country_id'], - 'region' => $data['shipping_zone_id'], - 'text' => implode(', ', array( - $data['shipping_postcode'], - $country, - $data['shipping_city'], - $data['shipping_address_1'], - $data['shipping_address_2'] - )) - ) - ); - - $orderProducts = isset($data['products']) ? $data['products'] : $data['order_product']; - - foreach ($orderProducts as $product) { - $order['items'][] = array( - 'productId' => $product['product_id'], - 'productName' => $product['name'], - 'initialPrice' => $product['price'], - 'quantity' => $product['quantity'], - ); - } - - if (isset($data['order_status_id'])) { - $order['status'] = $data['order_status']; - } - - try { - $this->intaroApi->orderEdit($order); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderCreate:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::orderCreate:' . json_encode($order)); - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderCreate::Curl:' . $e->getMessage()); - } - } - - public function orderHistory() { - - $orders = array(); - - try { - $orders = $this->intaroApi->orderHistory($this->getDate()); - $this->saveDate($this->intaroApi->getGeneratedAt()->format('Y-m-d H:i:s')); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderHistory:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::orderHistory:' . json_encode($orders)); - - return false; - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderHistory::Curl:' . $e->getMessage()); - - return false; - } - - return $orders; - } - - public function orderFixExternalIds($data) - { - try { - return $this->intaroApi->orderFixExternalIds($data); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds:' . json_encode($data)); - - return false; - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds::Curl:' . $e->getMessage()); - - return false; - } - } - - public function customerFixExternalIds($data) - { - try { - return $this->intaroApi->customerFixExternalIds($data); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::customerFixExternalIds:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::customerFixExternalIds:' . json_encode($data)); - - return false; - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::customerFixExternalIds::Curl:' . $e->getMessage()); - - return false; - } - } - - public function getOrder($order_id) - { - try { - return $this->intaroApi->orderGet($order_id); - } catch (IntaroCrm\Exception\ApiException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds:' . $e->getMessage()); - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds:' . json_encode($order)); - - return false; - } catch (IntaroCrm\Exception\CurlException $e) { - $this->log->addError('['.$this->domain.'] RestApi::orderFixExternalIds::Curl:' . $e->getMessage()); - - return false; - } - - } - - private function saveDate($date) { - file_put_contents($this->fileDate, $date, LOCK_EX); - } - - private function getDate() { - if (file_exists($this->fileDate)) { - $result = file_get_contents($this->fileDate); - } else { - $result = date('Y-m-d H:i:s', strtotime('-2 days', strtotime(date('Y-m-d H:i:s')))); - } - - return $result; - } - -} diff --git a/system/library/intarocrm/composer.json b/system/library/intarocrm/composer.json deleted file mode 100644 index 18ae32f..0000000 --- a/system/library/intarocrm/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "retailcrm/opencart-module", - "description": "Opencart integration for IntaroCRM", - "type": "library", - "keywords": ["api", "Intaro CRM", "rest"], - "homepage": "http://www.retailcrm.ru/", - "authors": [ - { - "name": "Alex Lushpai", - "email": "lushpai@intaro.ru", - "role": "Developer" - } - ], - "support": { - "email": "support@intarocrm.ru" - }, - "require": { - "php": ">=5.3", - "retailcrm/api-client-php": "1.3.*", - "symfony/console": "2.6.*", - "monolog/monolog": "1.12.*" - }, - "autoload": { - "psr-0": { - "": "/" - } - } -} diff --git a/system/library/intarocrm/composer.phar b/system/library/intarocrm/composer.phar deleted file mode 100755 index aa9c50d..0000000 Binary files a/system/library/intarocrm/composer.phar and /dev/null differ diff --git a/system/library/retailcrm/CurlException.php b/system/library/retailcrm/CurlException.php new file mode 100644 index 0000000..2ab00f5 --- /dev/null +++ b/system/library/retailcrm/CurlException.php @@ -0,0 +1,5 @@ +client = new RetailcrmHttpClient($url, array('apiKey' => $apiKey)); + $this->siteCode = $site; + } + + /** + * Create a order + * + * @param array $order + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function ordersCreate(array $order, $site = null) + { + if (!sizeof($order)) { + throw new InvalidArgumentException('Parameter `order` must contains a data'); + } + + return $this->client->makeRequest("/orders/create", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( + 'order' => json_encode($order) + ))); + } + + /** + * Edit a order + * + * @param array $order + * @param string $by + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function ordersEdit(array $order, $by = 'externalId', $site = null) + { + if (!sizeof($order)) { + throw new InvalidArgumentException('Parameter `order` must contains a data'); + } + + $this->checkIdParameter($by); + + if (!isset($order[$by])) { + throw new InvalidArgumentException(sprintf('Order array must contain the "%s" parameter.', $by)); + } + + return $this->client->makeRequest( + "/orders/" . $order[$by] . "/edit", + RetailcrmHttpClient::METHOD_POST, + $this->fillSite($site, array( + 'order' => json_encode($order), + 'by' => $by, + )) + ); + } + + /** + * Upload array of the orders + * + * @param array $orders + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function ordersUpload(array $orders, $site = null) + { + if (!sizeof($orders)) { + throw new InvalidArgumentException('Parameter `orders` must contains array of the orders'); + } + + return $this->client->makeRequest("/orders/upload", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( + 'orders' => json_encode($orders), + ))); + } + + /** + * Get order by id or externalId + * + * @param string $id + * @param string $by (default: 'externalId') + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function ordersGet($id, $by = 'externalId', $site = null) + { + $this->checkIdParameter($by); + + return $this->client->makeRequest("/orders/$id", RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, array( + 'by' => $by + ))); + } + + /** + * Returns a orders history + * + * @param DateTime $startDate (default: null) + * @param DateTime $endDate (default: null) + * @param int $limit (default: 100) + * @param int $offset (default: 0) + * @param bool $skipMyChanges (default: true) + * @return RetailcrmApiResponse + */ + public function ordersHistory( + DateTime $startDate = null, + DateTime $endDate = null, + $limit = 100, + $offset = 0, + $skipMyChanges = true + ) { + $parameters = array(); + + if ($startDate) { + $parameters['startDate'] = $startDate->format('Y-m-d H:i:s'); + } + if ($endDate) { + $parameters['endDate'] = $endDate->format('Y-m-d H:i:s'); + } + if ($limit) { + $parameters['limit'] = (int) $limit; + } + if ($offset) { + $parameters['offset'] = (int) $offset; + } + if ($skipMyChanges) { + $parameters['skipMyChanges'] = (bool) $skipMyChanges; + } + + return $this->client->makeRequest('/orders/history', RetailcrmHttpClient::METHOD_GET, $parameters); + } + + /** + * Returns filtered orders list + * + * @param array $filter (default: array()) + * @param int $page (default: null) + * @param int $limit (default: null) + * @return RetailcrmApiResponse + */ + public function ordersList(array $filter = array(), $page = null, $limit = null) + { + $parameters = array(); + + if (sizeof($filter)) { + $parameters['filter'] = $filter; + } + if (null !== $page) { + $parameters['page'] = (int) $page; + } + if (null !== $limit) { + $parameters['limit'] = (int) $limit; + } + + return $this->client->makeRequest('/orders', RetailcrmHttpClient::METHOD_GET, $parameters); + } + + /** + * Returns statuses of the orders + * + * @param array $ids (default: array()) + * @param array $externalIds (default: array()) + * @return RetailcrmApiResponse + */ + public function ordersStatuses(array $ids = array(), array $externalIds = array()) + { + $parameters = array(); + + if (sizeof($ids)) { + $parameters['ids'] = $ids; + } + if (sizeof($externalIds)) { + $parameters['externalIds'] = $externalIds; + } + + return $this->client->makeRequest('/orders/statuses', RetailcrmHttpClient::METHOD_GET, $parameters); + } + + /** + * Save order IDs' (id and externalId) association in the CRM + * + * @param array $ids + * @return RetailcrmApiResponse + */ + public function ordersFixExternalIds(array $ids) + { + if (!sizeof($ids)) { + throw new InvalidArgumentException('Method parameter must contains at least one IDs pair'); + } + + return $this->client->makeRequest("/orders/fix-external-ids", RetailcrmHttpClient::METHOD_POST, array( + 'orders' => json_encode($ids), + )); + } + + /** + * Get orders assembly history + * + * @param array $filter (default: array()) + * @param int $page (default: null) + * @param int $limit (default: null) + * @return RetailcrmApiResponse + */ + public function ordersPacksHistory(array $filter = array(), $page = null, $limit = null) + { + $parameters = array(); + + if (sizeof($filter)) { + $parameters['filter'] = $filter; + } + if (null !== $page) { + $parameters['page'] = (int) $page; + } + if (null !== $limit) { + $parameters['limit'] = (int) $limit; + } + + return $this->client->makeRequest('/orders/packs/history', RetailcrmHttpClient::METHOD_GET, $parameters); + } + + /** + * Create a customer + * + * @param array $customer + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function customersCreate(array $customer, $site = null) + { + if (!sizeof($customer)) { + throw new InvalidArgumentException('Parameter `customer` must contains a data'); + } + + return $this->client->makeRequest("/customers/create", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( + 'customer' => json_encode($customer) + ))); + } + + /** + * Edit a customer + * + * @param array $customer + * @param string $by (default: 'externalId') + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function customersEdit(array $customer, $by = 'externalId', $site = null) + { + if (!sizeof($customer)) { + throw new InvalidArgumentException('Parameter `customer` must contains a data'); + } + + $this->checkIdParameter($by); + + if (!isset($customer[$by])) { + throw new InvalidArgumentException(sprintf('Customer array must contain the "%s" parameter.', $by)); + } + + return $this->client->makeRequest( + "/customers/" . $customer[$by] . "/edit", + RetailcrmHttpClient::METHOD_POST, + $this->fillSite( + $site, + array( + 'customer' => json_encode($customer), + 'by' => $by + ) + ) + ); + } + + /** + * Upload array of the customers + * + * @param array $customers + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function customersUpload(array $customers, $site = null) + { + if (!sizeof($customers)) { + throw new InvalidArgumentException('Parameter `customers` must contains array of the customers'); + } + + return $this->client->makeRequest("/customers/upload", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( + 'customers' => json_encode($customers), + ))); + } + + /** + * Get customer by id or externalId + * + * @param string $id + * @param string $by (default: 'externalId') + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function customersGet($id, $by = 'externalId', $site = null) + { + $this->checkIdParameter($by); + + return $this->client->makeRequest("/customers/$id", RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, array( + 'by' => $by + ))); + } + + /** + * Returns filtered customers list + * + * @param array $filter (default: array()) + * @param int $page (default: null) + * @param int $limit (default: null) + * @return RetailcrmApiResponse + */ + public function customersList(array $filter = array(), $page = null, $limit = null) + { + $parameters = array(); + + if (sizeof($filter)) { + $parameters['filter'] = $filter; + } + if (null !== $page) { + $parameters['page'] = (int) $page; + } + if (null !== $limit) { + $parameters['limit'] = (int) $limit; + } + + return $this->client->makeRequest('/customers', RetailcrmHttpClient::METHOD_GET, $parameters); + } + + /** + * Save customer IDs' (id and externalId) association in the CRM + * + * @param array $ids + * @return RetailcrmApiResponse + */ + public function customersFixExternalIds(array $ids) + { + if (!sizeof($ids)) { + throw new InvalidArgumentException('Method parameter must contains at least one IDs pair'); + } + + return $this->client->makeRequest("/customers/fix-external-ids", RetailcrmHttpClient::METHOD_POST, array( + 'customers' => json_encode($ids), + )); + } + + /** + * Get purchace prices & stock balance + * + * @param array $filter (default: array()) + * @param int $page (default: null) + * @param int $limit (default: null) + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function storeInventories(array $filter = array(), $page = null, $limit = null, $site = null) + { + $parameters = array(); + + if (sizeof($filter)) { + $parameters['filter'] = $filter; + } + if (null !== $page) { + $parameters['page'] = (int) $page; + } + if (null !== $limit) { + $parameters['limit'] = (int) $limit; + } + + return $this->client->makeRequest('/store/inventories', RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, $parameters)); + } + + /** + * Upload store inventories + * + * @param array $offers + * @param string $site (default: null) + * @return RetailcrmApiResponse + */ + public function storeInventoriesUpload(array $offers, $site = null) + { + if (!sizeof($offers)) { + throw new InvalidArgumentException('Parameter `offers` must contains array of the customers'); + } + + return $this->client->makeRequest( + "/store/inventories/upload", + RetailcrmHttpClient::METHOD_POST, + $this->fillSite($site, array('offers' => json_encode($offers))) + ); + } + + /** + * Returns deliveryServices list + * + * @return RetailcrmApiResponse + */ + public function deliveryServicesList() + { + return $this->client->makeRequest('/reference/delivery-services', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns deliveryTypes list + * + * @return RetailcrmApiResponse + */ + public function deliveryTypesList() + { + return $this->client->makeRequest('/reference/delivery-types', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns orderMethods list + * + * @return RetailcrmApiResponse + */ + public function orderMethodsList() + { + return $this->client->makeRequest('/reference/order-methods', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns orderTypes list + * + * @return RetailcrmApiResponse + */ + public function orderTypesList() + { + return $this->client->makeRequest('/reference/order-types', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns paymentStatuses list + * + * @return RetailcrmApiResponse + */ + public function paymentStatusesList() + { + return $this->client->makeRequest('/reference/payment-statuses', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns paymentTypes list + * + * @return RetailcrmApiResponse + */ + public function paymentTypesList() + { + return $this->client->makeRequest('/reference/payment-types', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns productStatuses list + * + * @return RetailcrmApiResponse + */ + public function productStatusesList() + { + return $this->client->makeRequest('/reference/product-statuses', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns statusGroups list + * + * @return RetailcrmApiResponse + */ + public function statusGroupsList() + { + return $this->client->makeRequest('/reference/status-groups', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns statuses list + * + * @return RetailcrmApiResponse + */ + public function statusesList() + { + return $this->client->makeRequest('/reference/statuses', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns sites list + * + * @return RetailcrmApiResponse + */ + public function sitesList() + { + return $this->client->makeRequest('/reference/sites', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Returns stores list + * + * @return RetailcrmApiResponse + */ + public function storesList() + { + return $this->client->makeRequest('/reference/stores', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Edit deliveryService + * + * @param array $data delivery service data + * @return RetailcrmApiResponse + */ + public function deliveryServicesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/delivery-services/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'deliveryService' => json_encode($data) + ) + ); + } + + /** + * Edit deliveryType + * + * @param array $data delivery type data + * @return RetailcrmApiResponse + */ + public function deliveryTypesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/delivery-types/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'deliveryType' => json_encode($data) + ) + ); + } + + /** + * Edit orderMethod + * + * @param array $data order method data + * @return RetailcrmApiResponse + */ + public function orderMethodsEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/order-methods/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'orderMethod' => json_encode($data) + ) + ); + } + + /** + * Edit orderType + * + * @param array $data order type data + * @return RetailcrmApiResponse + */ + public function orderTypesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/order-types/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'orderType' => json_encode($data) + ) + ); + } + + /** + * Edit paymentStatus + * + * @param array $data payment status data + * @return RetailcrmApiResponse + */ + public function paymentStatusesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/payment-statuses/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'paymentStatus' => json_encode($data) + ) + ); + } + + /** + * Edit paymentType + * + * @param array $data payment type data + * @return RetailcrmApiResponse + */ + public function paymentTypesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/payment-types/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'paymentType' => json_encode($data) + ) + ); + } + + /** + * Edit productStatus + * + * @param array $data product status data + * @return RetailcrmApiResponse + */ + public function productStatusesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/product-statuses/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'productStatus' => json_encode($data) + ) + ); + } + + /** + * Edit order status + * + * @param array $data status data + * @return RetailcrmApiResponse + */ + public function statusesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/statuses/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'status' => json_encode($data) + ) + ); + } + + /** + * Edit site + * + * @param array $data site data + * @return RetailcrmApiResponse + */ + public function sitesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + return $this->client->makeRequest( + '/reference/sites/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'site' => json_encode($data) + ) + ); + } + + /** + * Edit store + * + * @param array $data site data + * @return RetailcrmApiResponse + */ + public function storesEdit(array $data) + { + if (!isset($data['code'])) { + throw new InvalidArgumentException('Data must contain "code" parameter.'); + } + + if (!isset($data['name'])) { + throw new InvalidArgumentException('Data must contain "name" parameter.'); + } + + return $this->client->makeRequest( + '/reference/stores/' . $data['code'] . '/edit', + RetailcrmHttpClient::METHOD_POST, + array( + 'store' => json_encode($data) + ) + ); + } + + /** + * Update CRM basic statistic + * + * @return RetailcrmApiResponse + */ + public function statisticUpdate() + { + return $this->client->makeRequest('/statistic/update', RetailcrmHttpClient::METHOD_GET); + } + + /** + * Return current site + * + * @return string + */ + public function getSite() + { + return $this->siteCode; + } + + /** + * Set site + * + * @param string $site + * @return void + */ + public function setSite($site) + { + $this->siteCode = $site; + } + + /** + * Check ID parameter + * + * @param string $by + * @return bool + */ + protected function checkIdParameter($by) + { + $allowedForBy = array('externalId', 'id'); + if (!in_array($by, $allowedForBy)) { + throw new InvalidArgumentException(sprintf( + 'Value "%s" for parameter "by" is not valid. Allowed values are %s.', + $by, + implode(', ', $allowedForBy) + )); + } + + return true; + } + + /** + * Fill params by site value + * + * @param string $site + * @param array $params + * @return array + */ + protected function fillSite($site, array $params) + { + if ($site) { + $params['site'] = $site; + } elseif ($this->siteCode) { + $params['site'] = $this->siteCode; + } + + return $params; + } +} diff --git a/system/library/retailcrm/RetailcrmApiResponse.php b/system/library/retailcrm/RetailcrmApiResponse.php new file mode 100644 index 0000000..bba91db --- /dev/null +++ b/system/library/retailcrm/RetailcrmApiResponse.php @@ -0,0 +1,122 @@ +statusCode = (int) $statusCode; + + if (!empty($responseBody)) { + $response = json_decode($responseBody, true); + + if (!$response && JSON_ERROR_NONE !== ($error = json_last_error())) { + throw new InvalidJsonException( + "Invalid JSON in the API response body. Error code #$error", + $error + ); + } + + $this->response = $response; + } + } + + /** + * Return HTTP response status code + * + * @return int + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * HTTP request was successful + * + * @return bool + */ + public function isSuccessful() + { + return $this->statusCode < 400; + } + + /** + * Allow to access for the property throw class method + * + * @param string $name + * @return mixed + */ + public function __call($name, $arguments) + { + // convert getSomeProperty to someProperty + $propertyName = strtolower(substr($name, 3, 1)) . substr($name, 4); + + if (!isset($this->response[$propertyName])) { + throw new InvalidArgumentException("Method \"$name\" not found"); + } + + return $this->response[$propertyName]; + } + + /** + * Allow to access for the property throw object property + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + if (!isset($this->response[$name])) { + throw new InvalidArgumentException("Property \"$name\" not found"); + } + + return $this->response[$name]; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + throw new BadMethodCallException('This activity not allowed'); + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + throw new BadMethodCallException('This call not allowed'); + } + + /** + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->response[$offset]); + } + + /** + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + if (!isset($this->response[$offset])) { + throw new InvalidArgumentException("Property \"$offset\" not found"); + } + + return $this->response[$offset]; + } +} diff --git a/system/library/retailcrm/RetailcrmHttpClient.php b/system/library/retailcrm/RetailcrmHttpClient.php new file mode 100644 index 0000000..6f74d16 --- /dev/null +++ b/system/library/retailcrm/RetailcrmHttpClient.php @@ -0,0 +1,113 @@ +url = $url; + $this->defaultParameters = $defaultParameters; + $this->retry = 0; + } + + /** + * Make HTTP request + * + * @param string $path + * @param string $method (default: 'GET') + * @param array $parameters (default: array()) + * @param int $timeout + * @param bool $verify + * @param bool $debug + * @return RetailcrmApiResponse + */ + public function makeRequest( + $path, + $method, + array $parameters = array(), + $timeout = 30, + $verify = false, + $debug = false + ) { + $allowedMethods = array(self::METHOD_GET, self::METHOD_POST); + if (!in_array($method, $allowedMethods)) { + throw new InvalidArgumentException(sprintf( + 'Method "%s" is not valid. Allowed methods are %s', + $method, + implode(', ', $allowedMethods) + )); + } + + $parameters = array_merge($this->defaultParameters, $parameters); + + $url = $this->url . $path; + + if (self::METHOD_GET === $method && sizeof($parameters)) { + $url .= '?' . http_build_query($parameters, '', '&'); + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify); + + if (!$debug) { + curl_setopt($ch, CURLOPT_TIMEOUT, (int) $timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); + } else { + curl_setopt($ch, CURLOPT_TIMEOUT_MS, (int) $timeout + ($this->retry * 2000)); + } + + if (self::METHOD_POST === $method) { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters); + } + + $responseBody = curl_exec($ch); + $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $errno = curl_errno($ch); + $error = curl_error($ch); + + curl_close($ch); + + if ($errno && in_array($errno, array(6, 7, 28, 34, 35)) && $this->retry < 3) { + $errno = null; + $error = null; + $this->retry += 1; + $this->makeRequest( + $path, + $method, + $parameters, + $timeout, + $verify, + $debug + ); + } + + if ($errno) { + throw new CurlException($error, $errno); + } + + return new RetailcrmApiResponse($statusCode, $responseBody); + } + + public function getRetry() + { + return $this->retry; + } +} diff --git a/system/library/retailcrm/RetailcrmProxy.php b/system/library/retailcrm/RetailcrmProxy.php new file mode 100644 index 0000000..f99f740 --- /dev/null +++ b/system/library/retailcrm/RetailcrmProxy.php @@ -0,0 +1,43 @@ +api = new RetailcrmApiClient($url, $key); + $this->log = $log; + } + + public function __call($method, $arguments) + { + try { + $response = call_user_func_array(array($this->api, $method), $arguments); + + if (!$response->isSuccessful()) { + error_log("[$method] " . $response->getErrorMsg() . "\n", 3, $this->log); + if (isset($response['errors'])) { + $error = implode("\n", $response['errors']); + error_log($error . "\n", 3, $this->log); + } + $response = false; + } + + return $response; + } catch (CurlException $e) { + error_log("[$method] " . $e->getMessage() . "\n", 3, $this->log); + return false; + } catch (InvalidJsonException $e) { + error_log("[$method] " . $e->getMessage() . "\n", 3, $this->log); + return false; + } + } + +} diff --git a/system/library/retailcrm/bootstrap.php b/system/library/retailcrm/bootstrap.php new file mode 100644 index 0000000..7c0cc34 --- /dev/null +++ b/system/library/retailcrm/bootstrap.php @@ -0,0 +1,99 @@ + + * @author Alex Lushpai + */ +class RetailcrmAutoloader +{ + /** + * File extension as a string. Defaults to ".php". + */ + protected static $fileExt = '.php'; + + /** + * The top level directory where recursion will begin. + * + */ + protected static $pathTop; + + /** + * Autoload function for registration with spl_autoload_register + * + * Looks recursively through project directory and loads class files based on + * filename match. + * + * @param string $className + */ + public static function loader($className) + { + $directory = new RecursiveDirectoryIterator(self::$pathTop); + $fileIterator = new RecursiveIteratorIterator($directory); + $filename = $className . self::$fileExt; + + foreach ($fileIterator as $file) { + if (strtolower($file->getFilename()) === strtolower($filename) && $file->isReadable()) { + include_once $file->getPathname(); + } + } + + } + + /** + * Sets the $fileExt property + * + * @param string $fileExt The file extension used for class files. Default is "php". + */ + public static function setFileExt($fileExt) + { + self::$fileExt = $fileExt; + } + + /** + * Sets the $path property + * + * @param string $path The path representing the top level where recursion should + * begin. Defaults to the current directory. + */ + public static function setPath($path) + { + self::$pathTop = $path; + } + +} + +RetailcrmAutoloader::setPath(realpath(dirname(__FILE__))); +RetailcrmAutoloader::setFileExt('.php'); +spl_autoload_register('RetailcrmAutoloader::loader'); diff --git a/vqmod/xml/retailcrm_create_order.xml b/vqmod/xml/retailcrm_create_order.xml new file mode 100644 index 0000000..eeb2704 --- /dev/null +++ b/vqmod/xml/retailcrm_create_order.xml @@ -0,0 +1,40 @@ + + + Send order to RetailCRM when it created + 1.5.x + 2.3.x + retailcrm.ru + + + + + load->model('retailcrm/order'); + $this->model_retailcrm_order->sendToCrm($data, $order_id); + ]]> + + + + + + db->query("UPDATE `" . DB_PREFIX . "order` SET total = '" . (float)$total . "', affiliate_id = '" . (int)$affiliate_id . "', commission = '" . (float)$commission . "' WHERE order_id = '" . (int)$order_id . "'");]]> + load->model('setting/setting'); + $status = $this->model_setting_setting->getSetting('retailcrm'); + + if (!empty($data['order_status_id'])) { + $data['order_status'] = $status['retailcrm_status'][$data['order_status_id']]; + } + + $this->load->model('retailcrm/order'); + if (isset ($order_query)) { + $this->model_retailcrm_order->changeInCrm($data, $order_id); + } else { + $this->model_retailcrm_order->sendToCrm($data, $order_id); + } + } + ]]> + + +