From 4522e0233dbe288fbda42a955e0c308e6b737911 Mon Sep 17 00:00:00 2001 From: Alex Lushpai Date: Fri, 29 Jan 2016 17:59:53 +0300 Subject: [PATCH] vqmod, refactoring, bug fixes --- Changelog.md | 7 + README.md | 54 +- admin/controller/module/retailcrm.php | 57 +- admin/model/retailcrm/history.php | 17 +- admin/model/retailcrm/icml.php | 12 +- admin/model/retailcrm/order.php | 168 ++++- admin/model/retailcrm/references.php | 125 +--- catalog/model/retailcrm/order.php | 119 +++- system/library/retailcrm/CurlException.php | 5 + .../retailcrm/InvalidJsonException.php | 5 + .../RetailcrmApiClient.php} | 633 +++--------------- .../retailcrm/RetailcrmApiResponse.php | 122 ++++ .../library/retailcrm/RetailcrmHttpClient.php | 113 ++++ system/library/retailcrm/RetailcrmProxy.php | 43 ++ system/library/retailcrm/bootstrap.php | 99 +++ vqmod/xml/retailcrm_create_order.xml | 40 ++ 16 files changed, 866 insertions(+), 753 deletions(-) create mode 100644 system/library/retailcrm/CurlException.php create mode 100644 system/library/retailcrm/InvalidJsonException.php rename system/library/{retailcrm.php => retailcrm/RetailcrmApiClient.php} (51%) create mode 100644 system/library/retailcrm/RetailcrmApiResponse.php create mode 100644 system/library/retailcrm/RetailcrmHttpClient.php create mode 100644 system/library/retailcrm/RetailcrmProxy.php create mode 100644 system/library/retailcrm/bootstrap.php create mode 100644 vqmod/xml/retailcrm_create_order.xml diff --git a/Changelog.md b/Changelog.md index f30fb5a..47bf1b0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,13 @@ Changelog ========= +####v0.3.0 + +* Расширена библиотека клиента +* Добавлена возможность кастомизации моделей заказа через vqmod +* Устранены мелкие баги, проведен рефакторинг кода. + + ####v0.2.0 Общие изменения diff --git a/README.md b/README.md index 58f44da..97f3217 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,42 @@ Opencart module =============== -Модуль интеграции CMS Openacrt c [RetailCRM](http://retailcrm.ru) +Модуль интеграции CMS Openacrt c [RetailCRM](http://retailcrm.ru) -### Модуль позволяет: +#### Модуль позволяет: * Экспортировать в CRM данные о заказах и клиентах и получать обратно изменения по этим данным * Синхронизировать справочники (способы доставки и оплаты, статусы заказов и т.п.) * Выгружать каталог товаров в формате [ICML](http://retailcrm.ru/docs/Разработчики/ФорматICML) (IntaroCRM Markup Language) -### Установка +#### Установка -#### Скачайте модуль +Установите модуль скопировав необходимые файлы в корень сайта -``` -https://github.com/retailcrm/opencart-module/archive/master.zip -``` - - -#### Установите модуль скопировав необходимые файлы в корень сайта ``` unzip master.zip -cp -r opencart-module/* /path/to/opecart/instance +cp -r opencart-module/* /path/to/site/root ``` #### Активируйте модуль -В основном меню Extension -> Modules -> Intstall module. +В списке модулей нажмите "Установить" + +#### Настройте параметры интеграции + +На странице настроек модуля укажите 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_ + +##### Ручная установка + В файле: ``` @@ -38,9 +45,9 @@ cp -r opencart-module/* /path/to/opecart/instance Добавьте следующие строки в метод addOrder непосредственно перед языковой конструкцией return: -``` +```php $this->load->model('retailcrm/order'); -$this->model_retailcrm_order->send($data, $order_id); +$this->model_retailcrm_order->sendToCrm($data, $order_id); ``` В файле: @@ -49,12 +56,23 @@ $this->model_retailcrm_order->send($data, $order_id); /admin/model/sale/order.php ``` -Добавьте следующие строки в методы addOrder и editOrder непосредственно перед языковой конструкцией return: +Добавьте следующие строки в методы 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'); - $this->model_retailcrm_order->send($data, $order_id); + if (isset ($order_query)) { + $this->model_retailcrm_order->changeInCrm($data, $order_id); + } else { + $this->model_retailcrm_order->sendToCrm($data, $order_id); + } } ``` @@ -77,5 +95,5 @@ if (!isset($data['fromApi'])) { В настройках CRM установите путь к файлу выгрузки ``` -/download/retailcrm.xml +http://myopencartsite.ru/download/retailcrm.xml ``` diff --git a/admin/controller/module/retailcrm.php b/admin/controller/module/retailcrm.php index 036d25d..8bbe1b0 100644 --- a/admin/controller/module/retailcrm.php +++ b/admin/controller/module/retailcrm.php @@ -1,6 +1,6 @@ load->model('setting/setting'); - $this->model_setting_setting->editSetting( - 'retailcrm', - array('retailcrm_status' => 1) - ); + $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) - ); + $this->model_setting_setting->editSetting('retailcrm', array('retailcrm_status' => 0)); } public function index() @@ -36,22 +30,10 @@ class ControllerModuleRetailcrm extends Controller $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); - + 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( - 'extension/module', - 'token=' . $this->session->data['token'], 'SSL' - ) - ); + $this->redirect($this->url->link('module/retailcrm', 'token=' . $this->session->data['token'], 'SSL')); } $text_strings = array( @@ -75,23 +57,18 @@ class ControllerModuleRetailcrm extends Controller } $this->data['retailcrm_errors'] = array(); - $this->data['saved_settings'] = $this->model_setting_setting - ->getSetting('retailcrm'); + $this->data['saved_settings'] = $this->model_setting_setting->getSetting('retailcrm'); - if ( - !empty($this->data['saved_settings']['retailcrm_url']) - && - !empty($this->data['saved_settings']['retailcrm_apikey']) - ) { + $url = $this->data['saved_settings']['retailcrm_url']; + $key = $this->data['saved_settings']['retailcrm_apikey']; - $this->retailcrm = new ApiHelper($this->data['saved_settings']); + if (!empty($url) && !empty($key)) { - $this->data['delivery'] = $this->model_retailcrm_references - ->getDeliveryTypes(); - $this->data['statuses'] = $this->model_retailcrm_references - ->getOrderStatuses(); - $this->data['payments'] = $this->model_retailcrm_references - ->getPaymentTypes(); + $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(); } @@ -176,7 +153,7 @@ class ControllerModuleRetailcrm extends Controller public function history() { - if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/history')) { + if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/history.php')) { $this->load->model('retailcrm/custom/history'); $this->model_retailcrm_custom_history->request(); } else { @@ -187,7 +164,7 @@ class ControllerModuleRetailcrm extends Controller public function icml() { - if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/icml')) { + if (file_exists(DIR_APPLICATION . 'model/retailcrm/custom/icml.php')) { $this->load->model('retailcrm/custom/icml'); $this->model_retailcrm_custom_icml->generateICML(); } else { diff --git a/admin/model/retailcrm/history.php b/admin/model/retailcrm/history.php index 918eedb..b8a2afa 100644 --- a/admin/model/retailcrm/history.php +++ b/admin/model/retailcrm/history.php @@ -18,7 +18,12 @@ class ModelRetailcrmHistory extends Model $settings['domain'] = parse_url(HTTP_SERVER, PHP_URL_HOST); if (!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { - $crm = new ApiHelper($settings); + $crm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); + $orders = $crm->ordersHistory(); $ordersIdsFix = array(); $customersIdsFix = array(); @@ -288,19 +293,15 @@ class ModelRetailcrmHistory extends Model } 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 retailcrm module first.' - ); + $this->log->addNotice('You need to configure retailcrm module first.'); } } } diff --git a/admin/model/retailcrm/icml.php b/admin/model/retailcrm/icml.php index de6f648..e584614 100644 --- a/admin/model/retailcrm/icml.php +++ b/admin/model/retailcrm/icml.php @@ -69,7 +69,7 @@ class ModelRetailcrmIcml extends Model ) ); - $e->setAttribute('id', $category['id']); + $e->setAttribute('id', $category['category_id']); if ($category['parent_id'] > 0) { $e->setAttribute('parentId', $category['parent_id']); @@ -99,15 +99,15 @@ class ModelRetailcrmIcml extends Model $e->setAttribute('id', $offer['product_id']); $e->setAttribute('productId', $offer['product_id']); $e->setAttribute('quantity', $offer['quantity']); - + /** * Offer activity */ - $offer['status'] ? 'Y' : 'N'; + $activity = $offer['status'] == 1 ? 'Y' : 'N'; $e->appendChild( $this->dd->createElement('productActivity') )->appendChild( - $this->dd->createTextNode($offer['status']) + $this->dd->createTextNode($activity) ); /** @@ -115,7 +115,7 @@ class ModelRetailcrmIcml extends Model */ $categories = $this->model_catalog_product ->getProductCategories($offer['product_id']); - + if (!empty($categories)) { foreach ($categories as $category) { $e->appendChild($this->dd->createElement('category')) @@ -134,7 +134,7 @@ class ModelRetailcrmIcml extends Model ->appendChild($this->dd->createTextNode($offer['name'])); $e->appendChild($this->dd->createElement('price')) ->appendChild($this->dd->createTextNode($offer['price'])); - + /** * Vendor */ diff --git a/admin/model/retailcrm/order.php b/admin/model/retailcrm/order.php index dc74271..bad9338 100644 --- a/admin/model/retailcrm/order.php +++ b/admin/model/retailcrm/order.php @@ -1,17 +1,173 @@ load->model('setting/setting'); $settings = $this->model_setting_setting->getSetting('retailcrm'); - $order['order_status'] = $settings['retailcrm_status'][$order['order_status_id']]; if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { - require_once DIR_SYSTEM . 'library/retailcrm.php'; - $order['order_id'] = $order_id; - $crm = new ApiHelper($settings); - $crm->processOrder($order); + 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 index 49924e9..80f2854 100644 --- a/admin/model/retailcrm/references.php +++ b/admin/model/retailcrm/references.php @@ -1,6 +1,6 @@ model_setting_setting->getSetting('retailcrm'); if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { - $this->retailcrm = new ApiHelper($settings); + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); - try { - $response = $this->retailcrm->api->deliveryTypesList(); - if ($response->isSuccessful() && $response->getStatusCode() == 200) { - return $response->deliveryTypes; - } else { - $this->log->write( - sprintf( - "RestApi::deliveryTypesList::Errors: [HTTP-status %s] %s", - $response->getStatusCode(), - $response->getErrorMsg() - ) - ); + $response = $this->retailcrm->deliveryTypesList(); - if (isset($response['errors'])) { - foreach ($response['errors'] as $error) { - $this->log->write( - sprintf( - "RestApi::deliveryTypesList::Errors: %s", $error - ) - ); - } - } - - return array(); - } - } catch (CurlException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::deliveryTypesList::Curl:' . $e->getMessage()); - } catch (InvalidJsonException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::deliveryTypesList::JSON:' . $e->getMessage()); - } - } else { - return array(); + return ($response === false) ? array() : $response->deliveryTypes; } } @@ -133,42 +106,15 @@ class ModelRetailcrmReferences extends Model $settings = $this->model_setting_setting->getSetting('retailcrm'); if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { - $this->retailcrm = new ApiHelper($settings); + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); - try { - $response = $this->retailcrm->api->statusesList(); - if ($response->isSuccessful() && $response->getStatusCode() == 200) { - return $response->statuses; - } else { - $this->log->write( - sprintf( - "RestApi::statusesList::Errors: [HTTP-status %s] %s", - $response->getStatusCode(), - $response->getErrorMsg() - ) - ); + $response = $this->retailcrm->statusesList(); - if (isset($response['errors'])) { - foreach ($response['errors'] as $error) { - $this->log->write( - sprintf( - "RestApi::statusesList::Errors: %s", $error - ) - ); - } - } - - return array(); - } - } catch (CurlException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::orderStatusesList::Curl:' . $e->getMessage()); - } catch (InvalidJsonException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::orderStatusesList::JSON:' . $e->getMessage()); - } - } else { - return array(); + return ($response === false) ? array() : $response->statuses; } } @@ -178,42 +124,15 @@ class ModelRetailcrmReferences extends Model $settings = $this->model_setting_setting->getSetting('retailcrm'); if(!empty($settings['retailcrm_url']) && !empty($settings['retailcrm_apikey'])) { - $this->retailcrm = new ApiHelper($settings); + $this->retailcrm = new RetailcrmProxy( + $settings['retailcrm_url'], + $settings['retailcrm_apikey'], + DIR_SYSTEM . 'logs/retailcrm.log' + ); - try { - $response = $this->retailcrm->api->paymentTypesList(); - if ($response->isSuccessful() && $response->getStatusCode() == 200) { - return $response->paymentTypes; - } else { - $this->log->write( - sprintf( - "RestApi::paymentTypesList::Errors: [HTTP-status %s] %s", - $response->getStatusCode(), - $response->getErrorMsg() - ) - ); + $response = $this->retailcrm->paymentTypesList(); - if (isset($response['errors'])) { - foreach ($response['errors'] as $error) { - $this->log->write( - sprintf( - "RestApi::paymentTypesList::Errors: %s", $error - ) - ); - } - } - - return array(); - } - } catch (CurlException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::paymentTypesList::Curl:' . $e->getMessage()); - } catch (InvalidJsonException $e) { - $this->data['retailcrm_error'][] = $e->getMessage(); - $this->log->write('RestApi::paymentTypesList::JSON:' . $e->getMessage()); - } - } else { - return array(); + return ($response === false) ? array() : $response->paymentTypes; } } } diff --git a/catalog/model/retailcrm/order.php b/catalog/model/retailcrm/order.php index 3f101ef..62fa7c2 100644 --- a/catalog/model/retailcrm/order.php +++ b/catalog/model/retailcrm/order.php @@ -1,22 +1,97 @@ -load->model('setting/setting'); - $settings = $this->model_setting_setting->getSetting('retailcrm'); - $settings['domain'] = parse_url(HTTP_SERVER, PHP_URL_HOST); - - if( - !empty($settings['retailcrm_url']) - && - !empty($settings['retailcrm_apikey']) - ) { - require_once DIR_SYSTEM . 'library/retailcrm/Retailcrm.php'; - $order['order_id'] = $order_id; - $crm = new ApiHelper($settings); - $crm->processOrder($order); - } - } -} - +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/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 @@ +settings = $settings; - - $this->api = new ApiClient( - $settings['retailcrm_url'], - $settings['retailcrm_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->api->customers($data['telephone'], $data['email'], $data['lastname'], 200, 0); - } catch (CurlException $e) { - $this->log->write('RestApi::customers:' . $e->getMessage()); - $this->log->write('RestApi::customers:' . json_encode($data)); - } catch (InvalidJsonException $e) { - $this->log->write('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->api->customersEdit($customer); - } catch (CurlException $e) { - $this->customer = $e->getMessage(); - $this->log->write('RestApi::orderCreate:' . $e->getMessage()); - $this->log->write('RestApi::orderCreate:' . json_encode($order)); - } catch (InvalidJsonException $e) { - $this->customer = $e->getMessage(); - $this->log->write('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->api->ordersEdit($order); - } catch (CurlException $e) { - $this->log->write('RestApi::orderCreate:' . $e->getMessage()); - $this->log->write('RestApi::orderCreate:' . json_encode($order)); - } catch (InvalidJsonException $e) { - $this->log->write('RestApi::orderCreate::Curl:' . $e->getMessage()); - } - } - - public function ordersHistory() { - - $orders = array(); - - try { - $orders = $this->api->ordersHistory($this->getDate()); - $this->saveDate($this->api->getGeneratedAt()->format('Y-m-d H:i:s')); - } catch (CurlException $e) { - $this->log->write('RestApi::orderHistory:' . $e->getMessage()); - $this->log->write('RestApi::orderHistory:' . json_encode($orders)); - - return false; - } catch (InvalidJsonException $e) { - $this->log->write('RestApi::orderHistory::Curl:' . $e->getMessage()); - - return false; - } - - return $orders; - } - - public function orderFixExternalIds($data) - { - try { - return $this->api->orderFixExternalIds($data); - } catch (CurlException $e) { - $this->log->write('RestApi::orderFixExternalIds:' . $e->getMessage()); - $this->log->write('RestApi::orderFixExternalIds:' . json_encode($data)); - - return false; - } catch (InvalidJsonException $e) { - $this->log->write('RestApi::orderFixExternalIds::Curl:' . $e->getMessage()); - - return false; - } - } - - public function customerFixExternalIds($data) - { - try { - return $this->api->customerFixExternalIds($data); - } catch (CurlException $e) { - $this->log->write('RestApi::customerFixExternalIds:' . $e->getMessage()); - $this->log->write('RestApi::customerFixExternalIds:' . json_encode($data)); - - return false; - } catch (InvalidJsonException $e) { - $this->log->write('RestApi::customerFixExternalIds::Curl:' . $e->getMessage()); - - return false; - } - } - - public function getOrder($order_id) - { - try { - return $this->api->orderGet($order_id); - } catch (CurlException $e) { - $this->log->write('RestApi::orderFixExternalIds:' . $e->getMessage()); - return false; - } catch (InvalidJsonException $e) { - $this->log->write('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; - } - -} - - -/** - * HTTP client - */ -class Client -{ - const METHOD_GET = 'GET'; - const METHOD_POST = 'POST'; - - protected $url; - protected $defaultParameters; - protected $retry; - - public function __construct($url, array $defaultParameters = array()) - { - if (false === stripos($url, 'https://')) { - throw new InvalidArgumentException('API schema requires HTTPS protocol'); - } - - $this->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 ApiResponse - */ - 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 ApiResponse($statusCode, $responseBody); - } - - public function getRetry() - { - return $this->retry; - } -} - -/** - * Response from retailCRM API - */ -class ApiResponse implements ArrayAccess -{ - // HTTP response status code - protected $statusCode; - - // response assoc array - protected $response; - - public function __construct($statusCode, $responseBody = null) - { - $this->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 - * @param array $arguments - * @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]; - } -} - - /** * retailCRM API client class */ -class ApiClient +class RetailcrmApiClient { const VERSION = 'v3'; @@ -488,7 +29,7 @@ class ApiClient $url = $url . 'api/' . self::VERSION; - $this->client = new Client($url, array('apiKey' => $apiKey)); + $this->client = new RetailcrmHttpClient($url, array('apiKey' => $apiKey)); $this->siteCode = $site; } @@ -497,7 +38,7 @@ class ApiClient * * @param array $order * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersCreate(array $order, $site = null) { @@ -505,7 +46,7 @@ class ApiClient throw new InvalidArgumentException('Parameter `order` must contains a data'); } - return $this->client->makeRequest("/orders/create", Client::METHOD_POST, $this->fillSite($site, array( + return $this->client->makeRequest("/orders/create", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( 'order' => json_encode($order) ))); } @@ -513,10 +54,10 @@ class ApiClient /** * Edit a order * - * @param array $order - * @param string $by - * @param string $site (default: null) - * @return ApiResponse + * @param array $order + * @param string $by + * @param string $site (default: null) + * @return RetailcrmApiResponse */ public function ordersEdit(array $order, $by = 'externalId', $site = null) { @@ -532,7 +73,7 @@ class ApiClient return $this->client->makeRequest( "/orders/" . $order[$by] . "/edit", - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( 'order' => json_encode($order), 'by' => $by, @@ -545,7 +86,7 @@ class ApiClient * * @param array $orders * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersUpload(array $orders, $site = null) { @@ -553,7 +94,7 @@ class ApiClient throw new InvalidArgumentException('Parameter `orders` must contains array of the orders'); } - return $this->client->makeRequest("/orders/upload", Client::METHOD_POST, $this->fillSite($site, array( + return $this->client->makeRequest("/orders/upload", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( 'orders' => json_encode($orders), ))); } @@ -564,13 +105,13 @@ class ApiClient * @param string $id * @param string $by (default: 'externalId') * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersGet($id, $by = 'externalId', $site = null) { $this->checkIdParameter($by); - return $this->client->makeRequest("/orders/$id", Client::METHOD_GET, $this->fillSite($site, array( + return $this->client->makeRequest("/orders/$id", RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, array( 'by' => $by ))); } @@ -583,7 +124,7 @@ class ApiClient * @param int $limit (default: 100) * @param int $offset (default: 0) * @param bool $skipMyChanges (default: true) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersHistory( DateTime $startDate = null, @@ -610,7 +151,7 @@ class ApiClient $parameters['skipMyChanges'] = (bool) $skipMyChanges; } - return $this->client->makeRequest('/orders/history', Client::METHOD_GET, $parameters); + return $this->client->makeRequest('/orders/history', RetailcrmHttpClient::METHOD_GET, $parameters); } /** @@ -619,7 +160,7 @@ class ApiClient * @param array $filter (default: array()) * @param int $page (default: null) * @param int $limit (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersList(array $filter = array(), $page = null, $limit = null) { @@ -635,7 +176,7 @@ class ApiClient $parameters['limit'] = (int) $limit; } - return $this->client->makeRequest('/orders', Client::METHOD_GET, $parameters); + return $this->client->makeRequest('/orders', RetailcrmHttpClient::METHOD_GET, $parameters); } /** @@ -643,7 +184,7 @@ class ApiClient * * @param array $ids (default: array()) * @param array $externalIds (default: array()) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersStatuses(array $ids = array(), array $externalIds = array()) { @@ -656,14 +197,14 @@ class ApiClient $parameters['externalIds'] = $externalIds; } - return $this->client->makeRequest('/orders/statuses', Client::METHOD_GET, $parameters); + return $this->client->makeRequest('/orders/statuses', RetailcrmHttpClient::METHOD_GET, $parameters); } /** * Save order IDs' (id and externalId) association in the CRM * * @param array $ids - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersFixExternalIds(array $ids) { @@ -671,7 +212,7 @@ class ApiClient throw new InvalidArgumentException('Method parameter must contains at least one IDs pair'); } - return $this->client->makeRequest("/orders/fix-external-ids", Client::METHOD_POST, array( + return $this->client->makeRequest("/orders/fix-external-ids", RetailcrmHttpClient::METHOD_POST, array( 'orders' => json_encode($ids), )); } @@ -682,7 +223,7 @@ class ApiClient * @param array $filter (default: array()) * @param int $page (default: null) * @param int $limit (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function ordersPacksHistory(array $filter = array(), $page = null, $limit = null) { @@ -698,7 +239,7 @@ class ApiClient $parameters['limit'] = (int) $limit; } - return $this->client->makeRequest('/orders/packs/history', Client::METHOD_GET, $parameters); + return $this->client->makeRequest('/orders/packs/history', RetailcrmHttpClient::METHOD_GET, $parameters); } /** @@ -706,7 +247,7 @@ class ApiClient * * @param array $customer * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersCreate(array $customer, $site = null) { @@ -714,7 +255,7 @@ class ApiClient throw new InvalidArgumentException('Parameter `customer` must contains a data'); } - return $this->client->makeRequest("/customers/create", Client::METHOD_POST, $this->fillSite($site, array( + return $this->client->makeRequest("/customers/create", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( 'customer' => json_encode($customer) ))); } @@ -725,7 +266,7 @@ class ApiClient * @param array $customer * @param string $by (default: 'externalId') * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersEdit(array $customer, $by = 'externalId', $site = null) { @@ -741,7 +282,7 @@ class ApiClient return $this->client->makeRequest( "/customers/" . $customer[$by] . "/edit", - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, $this->fillSite( $site, array( @@ -757,7 +298,7 @@ class ApiClient * * @param array $customers * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersUpload(array $customers, $site = null) { @@ -765,7 +306,7 @@ class ApiClient throw new InvalidArgumentException('Parameter `customers` must contains array of the customers'); } - return $this->client->makeRequest("/customers/upload", Client::METHOD_POST, $this->fillSite($site, array( + return $this->client->makeRequest("/customers/upload", RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array( 'customers' => json_encode($customers), ))); } @@ -776,13 +317,13 @@ class ApiClient * @param string $id * @param string $by (default: 'externalId') * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersGet($id, $by = 'externalId', $site = null) { $this->checkIdParameter($by); - return $this->client->makeRequest("/customers/$id", Client::METHOD_GET, $this->fillSite($site, array( + return $this->client->makeRequest("/customers/$id", RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, array( 'by' => $by ))); } @@ -793,7 +334,7 @@ class ApiClient * @param array $filter (default: array()) * @param int $page (default: null) * @param int $limit (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersList(array $filter = array(), $page = null, $limit = null) { @@ -809,14 +350,14 @@ class ApiClient $parameters['limit'] = (int) $limit; } - return $this->client->makeRequest('/customers', Client::METHOD_GET, $parameters); + return $this->client->makeRequest('/customers', RetailcrmHttpClient::METHOD_GET, $parameters); } /** * Save customer IDs' (id and externalId) association in the CRM * * @param array $ids - * @return ApiResponse + * @return RetailcrmApiResponse */ public function customersFixExternalIds(array $ids) { @@ -824,7 +365,7 @@ class ApiClient throw new InvalidArgumentException('Method parameter must contains at least one IDs pair'); } - return $this->client->makeRequest("/customers/fix-external-ids", Client::METHOD_POST, array( + return $this->client->makeRequest("/customers/fix-external-ids", RetailcrmHttpClient::METHOD_POST, array( 'customers' => json_encode($ids), )); } @@ -836,7 +377,7 @@ class ApiClient * @param int $page (default: null) * @param int $limit (default: null) * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function storeInventories(array $filter = array(), $page = null, $limit = null, $site = null) { @@ -852,7 +393,7 @@ class ApiClient $parameters['limit'] = (int) $limit; } - return $this->client->makeRequest('/store/inventories', Client::METHOD_GET, $this->fillSite($site, $parameters)); + return $this->client->makeRequest('/store/inventories', RetailcrmHttpClient::METHOD_GET, $this->fillSite($site, $parameters)); } /** @@ -860,7 +401,7 @@ class ApiClient * * @param array $offers * @param string $site (default: null) - * @return ApiResponse + * @return RetailcrmApiResponse */ public function storeInventoriesUpload(array $offers, $site = null) { @@ -870,7 +411,7 @@ class ApiClient return $this->client->makeRequest( "/store/inventories/upload", - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, $this->fillSite($site, array('offers' => json_encode($offers))) ); } @@ -878,118 +419,118 @@ class ApiClient /** * Returns deliveryServices list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function deliveryServicesList() { - return $this->client->makeRequest('/reference/delivery-services', Client::METHOD_GET); + return $this->client->makeRequest('/reference/delivery-services', RetailcrmHttpClient::METHOD_GET); } /** * Returns deliveryTypes list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function deliveryTypesList() { - return $this->client->makeRequest('/reference/delivery-types', Client::METHOD_GET); + return $this->client->makeRequest('/reference/delivery-types', RetailcrmHttpClient::METHOD_GET); } /** * Returns orderMethods list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function orderMethodsList() { - return $this->client->makeRequest('/reference/order-methods', Client::METHOD_GET); + return $this->client->makeRequest('/reference/order-methods', RetailcrmHttpClient::METHOD_GET); } /** * Returns orderTypes list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function orderTypesList() { - return $this->client->makeRequest('/reference/order-types', Client::METHOD_GET); + return $this->client->makeRequest('/reference/order-types', RetailcrmHttpClient::METHOD_GET); } /** * Returns paymentStatuses list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function paymentStatusesList() { - return $this->client->makeRequest('/reference/payment-statuses', Client::METHOD_GET); + return $this->client->makeRequest('/reference/payment-statuses', RetailcrmHttpClient::METHOD_GET); } /** * Returns paymentTypes list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function paymentTypesList() { - return $this->client->makeRequest('/reference/payment-types', Client::METHOD_GET); + return $this->client->makeRequest('/reference/payment-types', RetailcrmHttpClient::METHOD_GET); } /** * Returns productStatuses list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function productStatusesList() { - return $this->client->makeRequest('/reference/product-statuses', Client::METHOD_GET); + return $this->client->makeRequest('/reference/product-statuses', RetailcrmHttpClient::METHOD_GET); } /** * Returns statusGroups list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function statusGroupsList() { - return $this->client->makeRequest('/reference/status-groups', Client::METHOD_GET); + return $this->client->makeRequest('/reference/status-groups', RetailcrmHttpClient::METHOD_GET); } /** * Returns statuses list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function statusesList() { - return $this->client->makeRequest('/reference/statuses', Client::METHOD_GET); + return $this->client->makeRequest('/reference/statuses', RetailcrmHttpClient::METHOD_GET); } /** * Returns sites list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function sitesList() { - return $this->client->makeRequest('/reference/sites', Client::METHOD_GET); + return $this->client->makeRequest('/reference/sites', RetailcrmHttpClient::METHOD_GET); } /** * Returns stores list * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function storesList() { - return $this->client->makeRequest('/reference/stores', Client::METHOD_GET); + return $this->client->makeRequest('/reference/stores', RetailcrmHttpClient::METHOD_GET); } /** * Edit deliveryService * * @param array $data delivery service data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function deliveryServicesEdit(array $data) { @@ -999,7 +540,7 @@ class ApiClient return $this->client->makeRequest( '/reference/delivery-services/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'deliveryService' => json_encode($data) ) @@ -1010,7 +551,7 @@ class ApiClient * Edit deliveryType * * @param array $data delivery type data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function deliveryTypesEdit(array $data) { @@ -1020,7 +561,7 @@ class ApiClient return $this->client->makeRequest( '/reference/delivery-types/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'deliveryType' => json_encode($data) ) @@ -1031,7 +572,7 @@ class ApiClient * Edit orderMethod * * @param array $data order method data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function orderMethodsEdit(array $data) { @@ -1041,7 +582,7 @@ class ApiClient return $this->client->makeRequest( '/reference/order-methods/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'orderMethod' => json_encode($data) ) @@ -1052,7 +593,7 @@ class ApiClient * Edit orderType * * @param array $data order type data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function orderTypesEdit(array $data) { @@ -1062,7 +603,7 @@ class ApiClient return $this->client->makeRequest( '/reference/order-types/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'orderType' => json_encode($data) ) @@ -1073,7 +614,7 @@ class ApiClient * Edit paymentStatus * * @param array $data payment status data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function paymentStatusesEdit(array $data) { @@ -1083,7 +624,7 @@ class ApiClient return $this->client->makeRequest( '/reference/payment-statuses/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'paymentStatus' => json_encode($data) ) @@ -1094,7 +635,7 @@ class ApiClient * Edit paymentType * * @param array $data payment type data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function paymentTypesEdit(array $data) { @@ -1104,7 +645,7 @@ class ApiClient return $this->client->makeRequest( '/reference/payment-types/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'paymentType' => json_encode($data) ) @@ -1115,7 +656,7 @@ class ApiClient * Edit productStatus * * @param array $data product status data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function productStatusesEdit(array $data) { @@ -1125,7 +666,7 @@ class ApiClient return $this->client->makeRequest( '/reference/product-statuses/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'productStatus' => json_encode($data) ) @@ -1136,7 +677,7 @@ class ApiClient * Edit order status * * @param array $data status data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function statusesEdit(array $data) { @@ -1146,7 +687,7 @@ class ApiClient return $this->client->makeRequest( '/reference/statuses/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'status' => json_encode($data) ) @@ -1157,7 +698,7 @@ class ApiClient * Edit site * * @param array $data site data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function sitesEdit(array $data) { @@ -1167,7 +708,7 @@ class ApiClient return $this->client->makeRequest( '/reference/sites/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'site' => json_encode($data) ) @@ -1178,7 +719,7 @@ class ApiClient * Edit store * * @param array $data site data - * @return ApiResponse + * @return RetailcrmApiResponse */ public function storesEdit(array $data) { @@ -1192,7 +733,7 @@ class ApiClient return $this->client->makeRequest( '/reference/stores/' . $data['code'] . '/edit', - Client::METHOD_POST, + RetailcrmHttpClient::METHOD_POST, array( 'store' => json_encode($data) ) @@ -1202,11 +743,11 @@ class ApiClient /** * Update CRM basic statistic * - * @return ApiResponse + * @return RetailcrmApiResponse */ public function statisticUpdate() { - return $this->client->makeRequest('/statistic/update', Client::METHOD_GET); + return $this->client->makeRequest('/statistic/update', RetailcrmHttpClient::METHOD_GET); } /** @@ -1268,11 +809,3 @@ class ApiClient return $params; } } - -class InvalidJsonException extends DomainException -{ -} - -class CurlException extends RuntimeException -{ -} 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); + } + } + ]]> + + +