diff --git a/README.md b/README.md index f1ae684d..5ba61cbf 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,54 @@ Prestashop module -============= +================= -Prestashop module for interaction with [IntaroCRM](http://www.intarocrm.com) through [REST API](http://docs.intarocrm.ru/rest-api/). +Модуль интеграции CMS Prestashop c [RetailCRM](http://www.retailcrm.com) -Module allows: +Модуль позволяет: -* Send to IntaroCRM new orders -* Configure relations between dictionaries of IntaroCRM and Prestashop (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) -Installation -------------- +###Установка -### 1. Manual installation +#####Скачайте модуль +[Cкачать](http://download.retailcrm.pro/modules/prestashop/retailcrm-2.0.zip) -#### Clone module. -``` -git clone git@github.com:/intarocrm/prestashop-module.git -``` +#####Установите через административный интерфейс управления модулями. + +![Установка модуля](/docs/images/add.png) + + +###Настройка + +#####Перейдите к настройкам + +![Настройка модуля](/docs/images/setup.png) + +#####Введите адрес и API ключ вашей CRM и задайте соответствие справочников -#### Install Rest API Client. +![Справочники](/docs/images/ref.png) -Install api-client-php via [composer](http://getcomposer.org) + +#####Регулярная генерация выгрузки каталога + +Добавьте в крон запись вида ``` -cd prestashop-module -/path/to/composer.phar install +* */4 * * * /usr/bin/php /path/to/your/site/modules/retailcrm/job/icml.php ``` -#### Create .zip file. +#####Регулярное получение изменение из RetailCRM + +Добавьте в крон запись вида + ``` -zip -r intarocrm.zip intarocrm +*/7 * * * * /usr/bin/php /path/to/your/site/modules/retailcrm/job/sync.php ``` -#### Install via Admin interface. +#####Единоразовая выгрузка архива клиентов и заказов в RetailCRM - -Go to Modules -> Add module. After that upload your zipped module and activate it. +``` +/usr/bin/php /path/to/your/site/modules/retailcrm/job/export.php +``` diff --git a/composer.json b/composer.json deleted file mode 100644 index 5b855e4b..00000000 --- a/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "intarocrm/prestashop-module", - "description": "Prestashop integration for IntaroCRM", - "type": "library", - "keywords": ["api", "Intaro CRM", "rest"], - "homepage": "http://www.intarocrm.ru/", - "config": { - "vendor-dir": "intarocrm/classes" - }, - "authors": [ - { - "name": "Alex Lushpai", - "email": "lushpai@intaro.ru", - "role": "Developer" - } - ], - "support": { - "email": "support@intarocrm.ru" - }, - "require": { - "php": ">=5.3", - "intarocrm/rest-api-client": "1.2.*" - }, - "autoload": { - "psr-0": { - "": "src/" - } - }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/intarocrm/rest-api-client" - } - ] -} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 239a7397..00000000 --- a/composer.lock +++ /dev/null @@ -1,67 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" - ], - "hash": "e4583dae732a6e5ebafff8cca46b1984", - "packages": [ - { - "name": "intarocrm/rest-api-client", - "version": "v1.2.5", - "source": { - "type": "git", - "url": "https://github.com/intarocrm/rest-api-client", - "reference": "b54350ff2f09d8202cf2931895bba8dced4dcf21" - }, - "require": { - "ext-curl": "*", - "php": ">=5.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "IntaroCrm\\": "lib/" - } - }, - "authors": [ - { - "name": "Kruglov Kirill", - "email": "kruglov@intaro.ru", - "role": "Developer" - } - ], - "description": "PHP Client for IntaroCRM REST API", - "homepage": "http://www.intarocrm.ru/", - "keywords": [ - "Intaro CRM", - "api", - "rest" - ], - "support": { - "email": "support@intarocrm.ru" - }, - "time": "2014-04-13 09:58:37" - } - ], - "packages-dev": [ - - ], - "aliases": [ - - ], - "minimum-stability": "stable", - "stability-flags": [ - - ], - "platform": { - "php": ">=5.3" - }, - "platform-dev": [ - - ] -} diff --git a/docs/images/add.png b/docs/images/add.png new file mode 100644 index 00000000..c518ef58 Binary files /dev/null and b/docs/images/add.png differ diff --git a/docs/images/ref.png b/docs/images/ref.png new file mode 100644 index 00000000..ac8b2294 Binary files /dev/null and b/docs/images/ref.png differ diff --git a/docs/images/setup.png b/docs/images/setup.png new file mode 100644 index 00000000..99c7fc91 Binary files /dev/null and b/docs/images/setup.png differ diff --git a/intarocrm/config.xml b/intarocrm/config.xml deleted file mode 100644 index dcb2d940..00000000 --- a/intarocrm/config.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - intarocrm - - - - - - Вы уверены, что хотите удалить модуль? - 1 - 1 - - \ No newline at end of file diff --git a/intarocrm/export.tpl b/intarocrm/export.tpl deleted file mode 100644 index 1ee8a3f6..00000000 --- a/intarocrm/export.tpl +++ /dev/null @@ -1,39 +0,0 @@ - - - - {$shop_name} - {$company} - {$shop_url} - PrestaShop - - {foreach from=$currencies item=cur name=currencies} - {if $cur.iso_code != 'GBP'} - - {/if} - {/foreach} - - - {foreach from=$categories item=cat name=categories} - {if $cat.id_category!=2} - {$cat.name|escape} - {/if} - {/foreach} - - - {foreach from=$products item=offer name=products} - - {$offer.url} - {$offer.price} - {$offer.purchase_price} - {$currency} - {$offer.id_category_default} - {$offer.picture} - {$offer.name|escape} - {if $offer.article} - {$offer.article|escape} - {/if} - - {/foreach} - - - diff --git a/intarocrm/index.php b/intarocrm/index.php deleted file mode 100644 index 5f95ef61..00000000 --- a/intarocrm/index.php +++ /dev/null @@ -1,9 +0,0 @@ -exportCatalog(); \ No newline at end of file diff --git a/intarocrm/intarocrm.php b/intarocrm/intarocrm.php deleted file mode 100644 index eb5940e7..00000000 --- a/intarocrm/intarocrm.php +++ /dev/null @@ -1,1267 +0,0 @@ -name = 'intarocrm'; - $this->tab = 'market_place'; - $this->version = '0.1'; - $this->author = 'Intaro Ltd.'; - - $this->displayName = $this->l('IntaroCRM'); - $this->description = $this->l('Integration module for IntaroCRM'); - $this->confirmUninstall = $this->l('Are you sure you want to uninstall?'); - - $this->intaroCRM = new \IntaroCrm\RestApi( - Configuration::get('INTAROCRM_ADDRESS'), - Configuration::get('INTAROCRM_API_TOKEN') - ); - - $this->default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); - $this->default_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT'); - $this->default_country = (int)Configuration::get('PS_COUNTRY_DEFAULT'); - - $this->response = array(); - $this->customerFix = array(); - $this->orderFix = array(); - - $this->address_id = null; - $this->customer_id = null; - - $this->customer = null; - - parent::__construct(); - } - - function install() - { - return ( - parent::install() && - $this->registerHook('newOrder') && - $this->registerHook('actionOrderStatusPostUpdate') && - $this->registerHook('actionPaymentConfirmation') - - ); - } - - function uninstall() - { - return parent::uninstall() && - Configuration::deleteByName('INTAROCRM_ADDRESS') && - Configuration::deleteByName('INTAROCRM_API_TOKEN') && - Configuration::deleteByName('INTAROCRM_API_STATUS') && - Configuration::deleteByName('INTAROCRM_API_DELIVERY') && - Configuration::deleteByName('INTAROCRM_LAST_SYNC') && - Configuration::deleteByName('INTAROCRM_API_ADDR') - ; - } - - public function getContent() - { - $output = null; - - $address = Configuration::get('INTAROCRM_ADDRESS'); - $token = Configuration::get('INTAROCRM_API_TOKEN'); - - if (!$address || $address == '') { - $output .= $this->displayError( $this->l('Invalid crm address') ); - } elseif (!$token || $token == '') { - $output .= $this->displayError( $this->l('Invalid crm api token') ); - } else { - $output .= $this->displayConfirmation( - $this->l('Timezone settings must be identical to both of your crm and shop') . - " $address/admin/settings#t-main" - ); - } - - if (Tools::isSubmit('submit'.$this->name)) - { - $address = strval(Tools::getValue('INTAROCRM_ADDRESS')); - $token = strval(Tools::getValue('INTAROCRM_API_TOKEN')); - $delivery = json_encode(Tools::getValue('INTAROCRM_API_DELIVERY')); - $status = json_encode(Tools::getValue('INTAROCRM_API_STATUS')); - $payment = json_encode(Tools::getValue('INTAROCRM_API_PAYMENT')); - $order_address = json_encode(Tools::getValue('INTAROCRM_API_ADDR')); - - if (!$address || empty($address) || !Validate::isGenericName($address)) { - $output .= $this->displayError( $this->l('Invalid crm address') ); - } elseif (!$token || empty($token) || !Validate::isGenericName($token)) { - $output .= $this->displayError( $this->l('Invalid crm api token') ); - } else { - Configuration::updateValue('INTAROCRM_ADDRESS', $address); - Configuration::updateValue('INTAROCRM_API_TOKEN', $token); - Configuration::updateValue('INTAROCRM_API_DELIVERY', $delivery); - Configuration::updateValue('INTAROCRM_API_STATUS', $status); - Configuration::updateValue('INTAROCRM_API_PAYMENT', $payment); - Configuration::updateValue('INTAROCRM_API_ADDR', $order_address); - $output .= $this->displayConfirmation($this->l('Settings updated')); - } - } - $this->display(__FILE__, 'intarocrm.tpl'); - - return $output.$this->displayForm(); - } - - public function displayForm() - { - - $this->displayConfirmation($this->l('Settings updated')); - - $default_lang = $this->default_lang; - $intaroCrm = $this->intaroCRM; - - /* - * Network connection form - */ - $fields_form[0]['form'] = array( - 'legend' => array( - 'title' => $this->l('Network connection'), - ), - 'input' => array( - array( - 'type' => 'text', - 'label' => $this->l('CRM address'), - 'name' => 'INTAROCRM_ADDRESS', - 'size' => 20, - 'required' => true - ), - array( - 'type' => 'text', - 'label' => $this->l('CRM token'), - 'name' => 'INTAROCRM_API_TOKEN', - 'size' => 20, - 'required' => true - ) - ), - 'submit' => array( - 'title' => $this->l('Save'), - 'class' => 'button' - ) - ); - - /* - * Delivery - */ - $fields_form[1]['form'] = array( - 'legend' => array( - 'title' => $this->l('Delivery'), - ), - 'input' => $this->getDeliveryTypes($default_lang, $intaroCrm), - ); - - /* - * Order status - */ - $fields_form[2]['form'] = array( - 'legend' => array( - 'title' => $this->l('Order statuses'), - ), - 'input' => $this->getStatusTypes($default_lang, $intaroCrm), - ); - - /* - * Payment - */ - $fields_form[3]['form'] = array( - 'legend' => array( - 'title' => $this->l('Payment types'), - ), - 'input' => $this->getPaymentTypes($intaroCrm), - ); - - /* - * Address fields - */ - $fields_form[4]['form'] = array( - 'legend' => array( - 'title' => $this->l('Address'), - ), - 'input' => $this->getAddressFields() - ); - - - /* - * Diplay forms - */ - - $helper = new HelperForm(); - - $helper->module = $this; - $helper->name_controller = $this->name; - $helper->token = Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; - - $helper->default_form_language = $default_lang; - $helper->allow_employee_form_lang = $default_lang; - - $helper->title = $this->displayName; - $helper->show_toolbar = true; - $helper->toolbar_scroll = true; - $helper->submit_action = 'submit'.$this->name; - $helper->toolbar_btn = array( - 'save' => - array( - 'desc' => $this->l('Save'), - 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name. - '&token='.Tools::getAdminTokenLite('AdminModules'), - ), - 'back' => array( - 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->l('Back to list') - ) - ); - - $helper->fields_value['INTAROCRM_ADDRESS'] = Configuration::get('INTAROCRM_ADDRESS'); - $helper->fields_value['INTAROCRM_API_TOKEN'] = Configuration::get('INTAROCRM_API_TOKEN'); - - $deliverySettings = Configuration::get('INTAROCRM_API_DELIVERY'); - if (isset($deliverySettings) && $deliverySettings != '') - { - $deliveryTypes = json_decode($deliverySettings); - foreach ($deliveryTypes as $idx => $delivery) { - $name = 'INTAROCRM_API_DELIVERY[' . $idx . ']'; - $helper->fields_value[$name] = $delivery; - } - } - - $statusSettings = Configuration::get('INTAROCRM_API_STATUS'); - if (isset($statusSettings) && $statusSettings != '') - { - $statusTypes = json_decode($statusSettings); - foreach ($statusTypes as $idx => $status) { - $name = 'INTAROCRM_API_STATUS[' . $idx . ']'; - $helper->fields_value[$name] = $status; - } - } - - $paymentSettings = Configuration::get('INTAROCRM_API_PAYMENT'); - if (isset($paymentSettings) && $paymentSettings != '') - { - $paymentTypes = json_decode($paymentSettings); - foreach ($paymentTypes as $idx => $payment) { - $name = 'INTAROCRM_API_PAYMENT[' . $idx . ']'; - $helper->fields_value[$name] = $payment; - } - } - - $addressSettings = Configuration::get('INTAROCRM_API_ADDR'); - if (isset($addressSettings) && $addressSettings != '') - { - $addressTypes = json_decode($addressSettings); - foreach ($addressTypes as $idx => $address) { - $name = 'INTAROCRM_API_ADDR[' . $idx . ']'; - $helper->fields_value[$name] = $address; - } - } - - return $helper->generateForm($fields_form); - } - - public function hookNewOrder($params) - { - return $this->hookActionOrderStatusPostUpdate($params); - } - - public function hookActionPaymentConfirmation($params) - { - $this->intaroCRM->orderEdit( - array( - 'externalId' => $params['id_order'], - 'paymentStatus' => 'paid', - 'createdAt' => $params['cart']->date_upd - ) - ); - return $this->hookActionOrderStatusPostUpdate($params); - } - - public function hookActionOrderStatusPostUpdate($params) - { - $address_id = Address::getFirstCustomerAddressId($params['cart']->id_customer); - $sql = 'SELECT * FROM '._DB_PREFIX_.'address WHERE id_address='.(int)$address_id; - $address = Db::getInstance()->ExecuteS($sql); - $address = $address[0]; - $delivery = json_decode(Configuration::get('INTAROCRM_API_DELIVERY')); - $payment = json_decode(Configuration::get('INTAROCRM_API_PAYMENT')); - $inCart = $params['cart']->getProducts(); - - if (isset($params['orderStatus'])) { - try { - $crmCustomerId = $this->intaroCRM->customerCreate( - array( - 'externalId' => $params['cart']->id_customer, - 'lastName' => $params['customer']->lastname, - 'firstName' => $params['customer']->firstname, - 'email' => $params['customer']->email, - 'phones' => array( - array( - 'number' => $address['phone'], - 'type' => 'mobile' - ), - array( - 'number' => $address['phone_mobile'], - 'type' => 'mobile' - ) - ), - 'createdAt' => $params['customer']->date_add - ) - ); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log("customerCreate: connection error", 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('customerCreate: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - try { - $items = array(); - foreach ($inCart as $item) { - $items[] = array( - 'initialPrice' => $item['price'], - 'quantity' => $item['quantity'], - 'productId' => $item['id_product'], - 'productName' => $item['name'], - 'createdAt' => $item['date_add'] - ); - } - - $dTypeKey = $params['cart']->id_carrier; - if (Module::getInstanceByName('advancedcheckout') === false) { - $pTypeKey = $params['order']->module; - } else { - $pTypeKey = $params['order']->payment; - } - $this->intaroCRM->orderCreate( - array( - 'externalId' => $params['order']->id, - 'orderType' => 'eshop-individual', - 'orderMethod' => 'shopping-cart', - 'customerId' => $params['cart']->id_customer, - 'firstName' => $params['customer']->firstname, - 'lastName' => $params['customer']->lastname, - 'phone' => $address['phone'], - 'email' => $params['customer']->email, - 'paymentStatus' => 'not-paid', - 'paymentType' => $payment->$pTypeKey, - 'deliveryType' => $delivery->$dTypeKey, - 'deliveryCost' => $params['order']->total_shipping, - 'status' => 'new', - 'deliveryAddress' => array( - 'city' => $address['city'], - 'index' => $address['postcode'], - 'text' => $address['address1'], - ), - 'discount' => $params['order']->total_discounts, - 'items' => $items, - 'createdAt' => $params['order']->date_add - ) - ); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('orderCreate: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('orderCreate: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - } - - if (isset($params['newOrderStatus']) && !empty($params['newOrderStatus'])) { - $statuses = OrderState::getOrderStates($this->default_lang); - $aStatuses = json_decode(Configuration::get('INTAROCRM_API_STATUS')); - foreach ($statuses as $status) { - if ($status['name'] == $params['newOrderStatus']->name) { - $currStatus = $status['id_order_state']; - try { - $this->intaroCRM->orderEdit( - array( - 'externalId' => $params['id_order'], - 'status' => $aStatuses->$currStatus, - 'createdAt' => $params['cart']->date_upd - ) - ); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('orderStatusUpdate: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('orderStatusUpdate: ' . $e->getMessage(), 3, "intarocrm.log"); - } - } - } - } - } - - protected function getApiDeliveryTypes($intaroCrm) - { - $crmDeliveryTypes = array(); - - try { - $deliveryTypes = $intaroCrm->deliveryTypesList(); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('deliveryTypesList: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('deliveryTypesList: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - if (!empty($deliveryTypes)) { - $crmDeliveryTypes[] = array(); - foreach ($deliveryTypes as $dType) { - $crmDeliveryTypes[] = array( - 'id_option' => $dType['code'], - 'name' => $dType['name'], - ); - } - } - - return $crmDeliveryTypes; - - } - - protected function getDeliveryTypes($default_lang, $intaroCrm) - { - $deliveryTypes = array(); - - $carriers = Carrier::getCarriers( - $default_lang, true, false, false, - null, PS_CARRIERS_AND_CARRIER_MODULES_NEED_RANGE - ); - - if (!empty($carriers)) { - foreach ($carriers as $carrier) { - $deliveryTypes[] = array( - 'type' => 'select', - 'label' => $carrier['name'], - 'name' => 'INTAROCRM_API_DELIVERY[' . $carrier['id_carrier'] . ']', - 'required' => false, - 'options' => array( - 'query' => $this->getApiDeliveryTypes($intaroCrm), - 'id' => 'id_option', - 'name' => 'name' - ) - ); - } - } - - return $deliveryTypes; - } - - protected function getApiStatuses($intaroCrm) - { - $crmStatusTypes = array(); - - try { - $statusTypes = $intaroCrm->orderStatusesList(); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('statusTypesList: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('statusTypesList: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - if (!empty($statusTypes)) { - $crmStatusTypes[] = array(); - foreach ($statusTypes as $sType) { - $crmStatusTypes[] = array( - 'id_option' => $sType['code'], - 'name' => $sType['name'] - ); - } - } - - return $crmStatusTypes; - } - - protected function getStatusTypes($default_lang, $intaroCrm) - { - $statusTypes = array(); - $states = OrderState::getOrderStates($default_lang, true); - - if (!empty($states)) { - foreach ($states as $state) { - if ($state['name'] != ' ') { - $statusTypes[] = array( - 'type' => 'select', - 'label' => $state['name'], - 'name' => 'INTAROCRM_API_STATUS[' . $state['id_order_state'] . ']', - 'required' => false, - 'options' => array( - 'query' => $this->getApiStatuses($intaroCrm), - 'id' => 'id_option', - 'name' => 'name' - ) - ); - } - } - } - - return $statusTypes; - } - - protected function getApiPaymentTypes($intaroCrm) - { - $crmPaymentTypes = array(); - - try { - $paymentTypes = $intaroCrm->paymentTypesList(); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('paymentTypesList: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('paymentTypesList: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - if (!empty($paymentTypes)) { - $crmPaymentTypes[] = array(); - foreach ($paymentTypes as $pType) { - $crmPaymentTypes[] = array( - 'id_option' => $pType['code'], - 'name' => $pType['name'] - ); - } - } - - return $crmPaymentTypes; - } - - protected function getPaymentTypes($intaroCrm) - { - $payments = $this->getSystemPaymentModules(); - $paymentTypes = array(); - - if (!empty($payments)) { - foreach ($payments as $payment) { - $paymentTypes[] = array( - 'type' => 'select', - 'label' => $payment['name'], - 'name' => 'INTAROCRM_API_PAYMENT[' . $payment['code'] . ']', - 'required' => false, - 'options' => array( - 'query' => $this->getApiPaymentTypes($intaroCrm), - 'id' => 'id_option', - 'name' => 'name' - ) - ); - } - } - - return $paymentTypes; - } - - protected function getSystemPaymentModules() - { - $shop_id = Context::getContext()->shop->id; - - /* Get all modules then select only payment ones */ - $modules = Module::getModulesOnDisk(true); - /* - * Hack for knivesland - */ - if (Module::getInstanceByName('advancedcheckout') === false) { - foreach ($modules as $module) { - if ($module->tab == 'payments_gateways') - { - if ($module->id) - { - if (!get_class($module) == 'SimpleXMLElement') - $module->country = array(); - $countries = DB::getInstance()->executeS(' - SELECT id_country - FROM '._DB_PREFIX_.'module_country - WHERE id_module = '.(int)$module->id.' AND `id_shop`='.(int)$shop_id - ); - foreach ($countries as $country) - $module->country[] = $country['id_country']; - - if (!get_class($module) == 'SimpleXMLElement') - $module->currency = array(); - $currencies = DB::getInstance()->executeS(' - SELECT id_currency - FROM '._DB_PREFIX_.'module_currency - WHERE id_module = '.(int)$module->id.' AND `id_shop`='.(int)$shop_id - ); - foreach ($currencies as $currency) - $module->currency[] = $currency['id_currency']; - - if (!get_class($module) == 'SimpleXMLElement') - $module->group = array(); - $groups = DB::getInstance()->executeS(' - SELECT id_group - FROM '._DB_PREFIX_.'module_group - WHERE id_module = '.(int)$module->id.' AND `id_shop`='.(int)$shop_id - ); - foreach ($groups as $group) - $module->group[] = $group['id_group']; - } - else - { - $module->country = null; - $module->currency = null; - $module->group = null; - } - - if ($module->active != 0) { - $this->payment_modules[] = array( - 'id' => $module->id, - 'code' => $module->name, - 'name' => $module->displayName - ); - } - - } - } - } else { - require_once(dirname(__FILE__) . '/../advancedcheckout/classes/Payment.php'); - $modules = Payment::getPaymentMethods(); - foreach ($modules as $module) { - $this->payment_modules[] = array( - 'id' => $module['id_payment'], - 'code' => $module['name'], - 'name' => $module['name'] - ); - } - } - - return $this->payment_modules; - } - - protected function getAddressFields() - { - $addressFields = array(); - $address = explode(' ', str_replace("\n", ' ', AddressFormat::getAddressCountryFormat($this->context->country->id))); - - if (!empty($address)) { - foreach ($address as $idx => $a) { - if (!strpos($a, ':')) { - $addressFields[] = array( - 'type' => 'select', - 'label' => $this->l((string)$a), - 'name' => 'INTAROCRM_API_ADDR[' . $idx . ']', - 'required' => false, - 'options' => array( - 'query' => array( - array( - 'name' => '', - 'id_option' => '' - ), - array( - 'name' => $this->l('FIRST_NAME'), - 'id_option' => 'first_name' - ), - array( - 'name' => $this->l('LAST_NAME'), - 'id_option' => 'last_name' - ), - array( - 'name' => $this->l('PHONE'), - 'id_option' => 'phone' - ), - array( - 'name' => $this->l('EMAIL'), - 'id_option' => 'email' - ), - array( - 'name' => $this->l('ADDRESS'), - 'id_option' => 'address' - ), - array( - 'name' => $this->l('COUNTRY'), - 'id_option' => 'country' - ), - array( - 'name' => $this->l('REGION'), - 'id_option' => 'region' - ), - array( - 'name' => $this->l('CITY'), - 'id_option' => 'city' - ), - array( - 'name' => $this->l('ZIP'), - 'id_option' => 'index' - ), - array( - 'name' => $this->l('STREET'), - 'id_option' => 'street' - ), - array( - 'name' => $this->l('BUILDING'), - 'id_option' => 'building' - ), - array( - 'name' => $this->l('FLAT'), - 'id_option' => 'flat' - ), - array( - 'name' => $this->l('INTERCOMCODE'), - 'id_option' => 'intercomcode' - ), - array( - 'name' => $this->l('FLOOR'), - 'id_option' => 'floor' - ), - array( - 'name' => $this->l('BLOCK'), - 'id_option' => 'block' - ), - array( - 'name' => $this->l('HOUSE'), - 'ID' => 'house' - ) - ), - 'id' => 'id_option', - 'name' => 'name' - ) - ); - } - } - } - - return $addressFields; - } - - public function exportCatalog() - { - global $smarty; - $shop_url = (Configuration::get('PS_SSL_ENABLED') ? _PS_BASE_URL_SSL_ : _PS_BASE_URL_); - $id_lang = (int)Configuration::get('PS_LANG_DEFAULT'); - $currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT')); - if ($currency->iso_code == 'RUB') { - $currency->iso_code = 'RUR'; - } - - // Get currencies - $currencies = Currency::getCurrencies(); - - // Get categories - $categories = Category::getCategories($id_lang, true, false); - - // Get products - $sql = 'SELECT count(*) as `total` FROM `'._DB_PREFIX_.'product`'; - $rq = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); - - $items = array(); - $total_products = $rq[0]['total']; - $limit = 100; - $start = 0; - $parts = (int)($total_products/$limit) + 1; - - for ($i = 0; $i < $parts; $i++) { - $products = Product::getProducts($id_lang, $start, $limit, 'name', 'asc'); - foreach ($products AS $product) { - // Check for home category - $category = $product['id_category_default']; - if ($category == Configuration::get('PS_HOME_CATEGORY')) { - $temp_categories = Product::getProductCategories($product['id_product']); - - foreach ($temp_categories AS $category) { - if ($category != Configuration::get('PS_HOME_CATEGORY')) - break; - } - - if ($category == Configuration::get('PS_HOME_CATEGORY')) { - continue; - } - - } - $link = new Link(); - $cover = Image::getCover($product['id_product']); - $picture = 'http://' . $link->getImageLink($product['link_rewrite'], $product['id_product'].'-'.$cover['id_image'], 'large_default'); - if (!(substr($picture, 0, strlen($shop_url)) === $shop_url)) { - $picture = rtrim($shop_url,"/") . $picture; - } - $crewrite = Category::getLinkRewrite($product['id_category_default'], $id_lang); - $url = $link->getProductLink($product['id_product'], $product['link_rewrite'], $crewrite); - $items[] = array( - 'id_product' => $product['id_product'], - 'price' => $product['price'], - 'purchase_price' => $product['wholesale_price'], - 'name' => htmlspecialchars(strip_tags($product['name'])), - 'article' => htmlspecialchars($product['reference']), - 'id_category_default' => $category, - 'picture' => $picture, - 'url' => $url - ); - } - - $start += 100; - $limit += 100; - unset($products); - } - - $smarty->assign('currencies', $currencies); - $smarty->assign('currency', $currency->iso_code); - $smarty->assign('categories', $categories); - $smarty->assign('products', $items); - $smarty->assign('shop_name', Configuration::get('PS_SHOP_NAME')); - $smarty->assign('company', Configuration::get('PS_SHOP_NAME')); - $smarty->assign('shop_url', $shop_url . __PS_BASE_URI__); - return $this->display(__FILE__, 'export.tpl'); - } - - public function orderHistory() - { - /* - * get last sync date - */ - $lastSync = Configuration::get('INTAROCRM_LAST_SYNC'); - - $startFrom = ($lastSync === false) ? null : $lastSync; - $endTime = date('Y-m-d H:i:s'); - - $data = array(); - $counter = 0; - - /* - * retrive orders from crm since last update - */ - do { - try { - $this->response = $this->intaroCRM->orderHistory( - $startDate = $startFrom, $endDate = $endTime, - $limit = 250, $offset = $counter - ); - $data = array_merge($data, $this->response); - $counter += 250; - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('orderHistory: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('orderHistory: ' . $e->getMessage(), 3, "intarocrm.log"); - } - } while (!empty($response)); - - /* - * store recieved data into shop database - */ - if (!empty($data)) { - $toUpdate = array(); - - /* - * Customer object. Will be used for further updates. - */ - $this->customer = new Customer(); - - $statuses = array_flip((array)json_decode(Configuration::get('INTAROCRM_API_STATUS'))); - $deliveries = array_flip((array)json_decode(Configuration::get('INTAROCRM_API_DELIVERY'))); - $payments = array_flip((array)json_decode(Configuration::get('INTAROCRM_API_PAYMENT'))); - - foreach ($data as $order) { - if (!array_key_exists('externalId', $order)) { - /* - * create customer if not exist - */ - $this->customer->getByEmail($order['customer']['email']); - - if (!array_key_exists('externalId', $order['customer'])) { - if (Validate::isEmail($order['customer']['email'])) { - - if (!$this->customer->id) - { - $this->customer->firstname = $order['customer']['firstName']; - $this->customer->lastname = $order['customer']['lastName']; - $this->customer->email = $order['customer']['email']; - $this->customer->passwd = substr(str_shuffle(strtolower(sha1(rand() . time()))),0, 5); - - if($this->customer->add()) { - - /* - * create customer address for delivery data - */ - - $this->customer->getByEmail($order['customer']['email']); - $this->customer_id = $this->customer->id; - - $address = new Address(); - $address->id_customer = $this->customer->id; - $address->id_country = $this->default_country; - $address->lastname = $this->customer->lastname; - $address->firstname = $this->customer->firstname; - $address->alias = 'default'; - $address->postcode = $order['deliveryAddress']['index']; - $address->city = $order['deliveryAddress']['city']; - $address->address1 = $order['deliveryAddress']['text']; - $address->phone = $order['phone']; - $address->phone_mobile = $order['phone']; - - $address->add(); - - /* - * store address record id for handle order data - */ - $addr = $this->customer->getAddresses($this->default_lang); - $this->address_id = $addr[0]['id_address']; - } - } else { - $addresses = $this->customer->getAddresses($this->default_lang); - $this->address_id = $addresses[0]['id_address']; - $this->customer_id = $this->customer->id; - } - - /* - * collect customer ids for single fix request - */ - array_push( - $this->customerFix, - array( - 'id' => $order['customer']['id'], - 'externalId' => $this->customer_id - ) - ); - } - } else { - $addresses = $this->customer->getAddresses($this->default_lang); - $this->address_id = $addresses[0]['id_address']; - $this->customer_id = $order['customer']['externalId']; - } - - $delivery = $order['deliveryType']; - $payment = $order['paymentType']; - $state = $order['status']; - - $cart = new Cart(); - $cart->id_currency = $this->default_currency; - $cart->id_lang = $this->default_lang; - $cart->id_customer = $this->customer_id; - $cart->id_address_delivery = (int)$this->address_id; - $cart->id_address_invoice = (int)$this->address_id; - $cart->id_carrier = (int)$deliveries[$delivery]; - - $cart->add(); - - $products = array(); - if(!empty($order['items'])) { - foreach ($order['items'] as $item) { - $product = array(); - $product['id_product'] = (int)$item['offer']['externalId']; - $product['quantity'] = $item['quantity']; - $product['id_address_delivery'] = (int)$this->address_id; - $products[] = $product; - } - } - - $cart->setWsCartRows($products); - $cart->update(); - - /* - * Create order - */ - - $newOrder = new Order(); - $newOrder->id_address_delivery = (int)$this->address_id; - $newOrder->id_address_invoice = (int)$this->address_id; - $newOrder->id_cart = (int)$cart->id; - $newOrder->id_currency = $this->default_currency; - $newOrder->id_lang = $this->default_lang; - $newOrder->id_customer = (int)$this->customer_id; - $newOrder->id_carrier = (int)$deliveries[$delivery]; - $newOrder->payment = $payments[$payment]; - $newOrder->module = (Module::getInstanceByName('advancedcheckout') === false) - ? $payments[$payment] - : 'advancedcheckout' - ; - $newOrder->total_paid = $order['summ'] + $order['deliveryCost']; - $newOrder->total_paid_tax_incl = $order['summ'] + $order['deliveryCost']; - $newOrder->total_paid_tax_excl = $order['summ'] + $order['deliveryCost']; - $newOrder->total_paid_real = $order['summ'] + $order['deliveryCost']; - $newOrder->total_products = $order['summ']; - $newOrder->total_products_wt = $order['summ']; - $newOrder->total_shipping = $order['deliveryCost']; - $newOrder->total_shipping_tax_incl = $order['deliveryCost']; - $newOrder->total_shipping_tax_excl = $order['deliveryCost']; - $newOrder->conversion_rate = 1.000000; - $newOrder->current_state = (int)$statuses[$state]; - $newOrder->delivery_date = $order['deliveryDate']; - $newOrder->date_add = $order['createdAt']; - $newOrder->date_upd = $order['createdAt']; - $newOrder->valid = 1; - $newOrder->secure_key = md5(time()); - - if (isset($order['discount'])) - { - $newOrder->total_discounts = $order['discount']; - } - - $newOrder->add(false, false); - - /* - * collect order ids for single fix request - */ - array_push( - $this->orderFix, - array( - 'id' => $order['id'], - 'externalId' => $newOrder->id - ) - ); - - /* - * Create order details - */ - $product_list = array(); - foreach ($order['items'] as $item) { - $product = new Product((int)$item['offer']['externalId'], false, $this->default_lang); - $qty = $item['quantity']; - $product_list[] = array('product' =>$product, 'quantity' => $qty); - } - - $query = 'INSERT `'._DB_PREFIX_.'order_detail` - ( - `id_order`, `id_order_invoice`, `id_shop`, `product_id`, `product_attribute_id`, - `product_name`, `product_quantity`, `product_quantity_in_stock`, `product_price`, - `product_reference`, `total_price_tax_excl`, `total_price_tax_incl`, - `unit_price_tax_excl`, `unit_price_tax_incl`, `original_product_price` - ) - - VALUES'; - - foreach ($product_list as $product) { - $query .= '(' - .(int)$newOrder->id.', - 0, - '. $this->context->shop->id.', - '.(int)$product['product']->id.', - 0, - '.implode('', array('\'', $product['product']->name, '\'')).', - '.(int)$product['quantity'].', - '.(int)$product['quantity'].', - '.$product['product']->price.', - '.implode('', array('\'', $product['product']->reference, '\'')).', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.' - ),'; - } - - Db::getInstance()->execute(rtrim($query, ',')); - - try { - $this->intaroCRM->customerFixExternalIds($this->customerFix); - $this->intaroCRM->orderFixExternalIds($this->orderFix); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('fixExternalId: connection error', 3, "intarocrm.log"); - continue; - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('fixExternalId: ' . $e->getMessage(), 3, "intarocrm.log"); - continue; - } - - } else { - if (!in_array($order['id'], $toUpdate)) - { - /* - * take last order update only - */ - $toUpdate[] = $order['id']; - if ($order['paymentType'] != null && $order['deliveryType'] != null && $order['status'] != null) { - $orderToUpdate = new Order((int)$order['externalId']); - - /* - * check status - */ - $stype = $order['status']; - if ($statuses[$stype] != null) { - if ($statuses[$stype] != $orderToUpdate->current_state) { - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'orders` - SET `current_state` = \''.$statuses[$stype].'\' - WHERE `id_order` = '.(int)$order['externalId']); - } - } - - /* - * check delivery type - */ - $dtype = $order['deliveryType']; - if ($deliveries[$dtype] != null) { - if ($deliveries[$dtype] != $orderToUpdate->id_carrier) { - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'orders` - SET `id_carrier` = \''.$deliveries[$dtype].'\' - WHERE `id_order` = '.(int)$order['externalId']); - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'order_carrier` - SET `id_carrier` = \''.$deliveries[$dtype].'\' - WHERE `id_order` = \''.$orderToUpdate->id.'\''); - } - } - - /* - * check payment type - */ - $ptype = $order['paymentType']; - if ($payments[$ptype] != null) { - if ($payments[$ptype] != $orderToUpdate->payment) { - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'orders` - SET `payment` = \''.$payments[$ptype].'\' - WHERE `id_order` = '.(int)$order['externalId']); - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'order_payment` - SET `payment_method` = \''.$payments[$ptype].'\' - WHERE `order_reference` = \''.$orderToUpdate->reference.'\''); - - } - } - - /* - * check items - */ - - /* - * Clean deleted - */ - foreach ($order['items'] as $key => $item) { - if (isset($item['deleted']) && $item['deleted'] == true) { - Db::getInstance()->execute(' - DELETE FROM `'._DB_PREFIX_.'order_detail` - WHERE `id_order` = '. $orderToUpdate->id .' - AND `product_id` = '.$item['id']); - - unset($order['items'][$key]); - } - } - - /* - * check quantity - */ - - foreach ($orderToUpdate->getProductsDetail() as $orderItem) { - foreach ($order['items'] as $key => $item) { - if ($item['offer']['externalId'] == $orderItem['product_id']) { - if (isset($item['quantity']) && $item['quantity'] != $orderItem['product_quantity']) { - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'order_detail` - SET `product_quantity` = '.$item['quantity'].', - `product_quantity_in_stock` = '.$item['quantity'].' - WHERE `id_order_detail` = '.$orderItem['id_order_detail']); - } - - unset($order['items'][$key]); - } - } - } - - /* - * check new items - */ - if (!empty($order['items'])) { - foreach ($order['items'] as $key => $newItem) { - $product = new Product((int)$newItem['offer']['externalId'], false, $this->default_lang); - $qty = $newItem['quantity']; - $product_list[] = array('product' =>$product, 'quantity' => $qty); - } - - - $query = 'INSERT `'._DB_PREFIX_.'order_detail` - ( - `id_order`, `id_order_invoice`, `id_shop`, `product_id`, `product_attribute_id`, - `product_name`, `product_quantity`, `product_quantity_in_stock`, `product_price`, - `product_reference`, `total_price_tax_excl`, `total_price_tax_incl`, - `unit_price_tax_excl`, `unit_price_tax_incl`, `original_product_price` - ) - - VALUES'; - - foreach ($product_list as $product) { - $query .= '(' - .(int)$orderToUpdate->id.', - 0, - '. $this->context->shop->id.', - '.(int)$product['product']->id.', - 0, - '.implode('', array('\'', $product['product']->name, '\'')).', - '.(int)$product['quantity'].', - '.(int)$product['quantity'].', - '.$product['product']->price.', - '.implode('', array('\'', $product['product']->reference, '\'')).', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.', - '.$product['product']->price.' - ),'; - } - - Db::getInstance()->execute(rtrim($query, ',')); - unset($order['items'][$key]); - } - - /* - * Fix prices & discounts - * Discounts only for whole order - */ - $orderDiscout = null; - $orderTotal = $order['summ']; - - if (isset($order['discount']) && $order['discount'] > 0) { - if ($order['discount'] != $orderToUpdate->total_discounts) { - $orderDiscout = ($orderDiscout == null) ? $order['discount'] : $order['discount'] + $orderDiscout; - } - } - - if (isset($order['discountPercent']) && $order['discountPercent'] > 0) { - $percent = ($order['summ'] * $order['discountPercent'])/100; - if ($percent != $orderToUpdate->total_discounts) { - $orderDiscout = ($orderDiscout == null) ? $percent : $percent + $orderDiscout; - } - } - - $totalDiscount = ($orderDiscout == null) ? $orderToUpdate->total_discounts : $orderDiscout; - - if ($totalDiscount != $orderToUpdate->total_discounts || $orderTotal != $orderToUpdate->total_paid) { - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'orders` - SET `total_discounts` = '.$totalDiscount.', - `total_discounts_tax_incl` = '.$totalDiscount.', - `total_discounts_tax_excl` = '.$totalDiscount.', - `total_paid` = '.$orderTotal.', - `total_paid_tax_incl` = '.$orderTotal.', - `total_paid_tax_excl` = '.$orderTotal.' - WHERE `id_order` = '.(int)$order['externalId']); - } - } - } - } - } - - /* - * Update last sync timestamp - */ - try { - Configuration::updateValue( - 'INTAROCRM_LAST_SYNC', - date_format($this->intaroCRM->getGeneratedAt(), 'Y-m-d H:i:s') - ); - } - catch (\IntaroCrm\Exception\CurlException $e) { - error_log('getLastSync: connection error', 3, "intarocrm.log"); - } - catch (\IntaroCrm\Exception\ApiException $e) { - error_log('getLastSync: ' . $e->getMessage(), 3, "intarocrm.log"); - } - - return count($data) . " records was synced"; - - } else { - return 'Nothing to sync'; - } - - } -} diff --git a/intarocrm/logo.gif b/intarocrm/logo.gif deleted file mode 100644 index c9abff4a..00000000 Binary files a/intarocrm/logo.gif and /dev/null differ diff --git a/intarocrm/logo.png b/intarocrm/logo.png deleted file mode 100644 index 5daa45ae..00000000 Binary files a/intarocrm/logo.png and /dev/null differ diff --git a/intarocrm/sync.php b/intarocrm/sync.php deleted file mode 100644 index bab4cc34..00000000 --- a/intarocrm/sync.php +++ /dev/null @@ -1,9 +0,0 @@ -orderHistory(); diff --git a/intarocrm/translations/ru.php b/intarocrm/translations/ru.php deleted file mode 100644 index bc44b2e2..00000000 --- a/intarocrm/translations/ru.php +++ /dev/null @@ -1,36 +0,0 @@ -intarocrm_03c4d9465b9b3a7533d18cacc79c7fe4'] = 'IntaroCRM'; -$_MODULE['<{intarocrm}prestashop>intarocrm_5e695dc9fe273b7bc074e608113f4662'] = 'Модуль интеграции с IntaroCRM'; -$_MODULE['<{intarocrm}prestashop>intarocrm_876f23178c29dc2552c0b48bf23cd9bd'] = 'Вы уверены, что хотите удалить модуль?'; -$_MODULE['<{intarocrm}prestashop>intarocrm_5effd5157947e8ba4a08883f198b2e31'] = 'Неверный адрес CRM'; -$_MODULE['<{intarocrm}prestashop>intarocrm_576300f5b6faeb746bb6d034d98e7afd'] = 'Неверный API ключ'; -$_MODULE['<{intarocrm}prestashop>intarocrm_fba05687b61bc936d1a9a92371ba8bcf'] = 'Внимание! Часовой пояс в CRM должен совпадать с часовым поясом в магазине, настроки часового пояса CRM можно задать по адресу:'; -$_MODULE['<{intarocrm}prestashop>intarocrm_c888438d14855d7d96a2724ee9c306bd'] = 'Настройки обновлены'; -$_MODULE['<{intarocrm}prestashop>intarocrm_51af428aa0dcceb5230acb267ecb91c5'] = 'Настройка соединения'; -$_MODULE['<{intarocrm}prestashop>intarocrm_4cbd5dbeeef7392e50358b1bc00dd592'] = 'Адрес CRM'; -$_MODULE['<{intarocrm}prestashop>intarocrm_7f775042e08eddee6bbfd8fbe0add4a3'] = 'Ключ авторизации'; -$_MODULE['<{intarocrm}prestashop>intarocrm_c9cc8cce247e49bae79f15173ce97354'] = 'Сохранить'; -$_MODULE['<{intarocrm}prestashop>intarocrm_065ab3a28ca4f16f55f103adc7d0226f'] = 'Способы доставки'; -$_MODULE['<{intarocrm}prestashop>intarocrm_33af8066d3c83110d4bd897f687cedd2'] = 'Статусы заказов'; -$_MODULE['<{intarocrm}prestashop>intarocrm_bab959acc06bb03897b294fbb892be6b'] = 'Способы оплаты'; -$_MODULE['<{intarocrm}prestashop>intarocrm_dd7bf230fde8d4836917806aff6a6b27'] = 'Адрес'; -$_MODULE['<{intarocrm}prestashop>intarocrm_630f6dc397fe74e52d5189e2c80f282b'] = 'Вернуться к списку'; -$_MODULE['<{intarocrm}prestashop>intarocrm_5c1cf6cfec2dad86c8ca5286a0294516'] = 'Имя'; -$_MODULE['<{intarocrm}prestashop>intarocrm_c695cfe527a6fcd680114851b86b7555'] = 'Фамилия'; -$_MODULE['<{intarocrm}prestashop>intarocrm_f9dd946cc89c1f3b41a0edbe0f36931d'] = 'Телефон'; -$_MODULE['<{intarocrm}prestashop>intarocrm_61a649a33f2869e5e35fbb7aff3a80d9'] = 'Email'; -$_MODULE['<{intarocrm}prestashop>intarocrm_2664f03ac6b8bb9eee4287720e407db3'] = 'Адрес'; -$_MODULE['<{intarocrm}prestashop>intarocrm_6ddc09dc456001d9854e9fe670374eb2'] = 'Страна'; -$_MODULE['<{intarocrm}prestashop>intarocrm_69aede266809f89b89fe70681f6a129f'] = 'Область/Край/Республика'; -$_MODULE['<{intarocrm}prestashop>intarocrm_859214628431995197c0558f7b5f8ffc'] = 'Город'; -$_MODULE['<{intarocrm}prestashop>intarocrm_4348f938bbddd8475e967ccb47ecb234'] = 'Почтовый индекс'; -$_MODULE['<{intarocrm}prestashop>intarocrm_78fce82336bbbdca7f6da7564b8f9325'] = 'Улица'; -$_MODULE['<{intarocrm}prestashop>intarocrm_71a6834884666147c0334f0c40bc7295'] = 'Дом/Строение'; -$_MODULE['<{intarocrm}prestashop>intarocrm_f88a77e3d68d251c3dc4008c327b5a0c'] = 'Квартира'; -$_MODULE['<{intarocrm}prestashop>intarocrm_d977f846d110fcb7f71c6f97330c9d10'] = 'Код домофона'; -$_MODULE['<{intarocrm}prestashop>intarocrm_56c1e354d36beb85b0d881c5b2e24cbe'] = 'Этаж'; -$_MODULE['<{intarocrm}prestashop>intarocrm_4d34f53389ed7f28ca91fc31ea360a66'] = 'Корпус'; -$_MODULE['<{intarocrm}prestashop>intarocrm_49354b452ec305136a56fe7731834156'] = 'Дом/Строение'; diff --git a/retailcrm/bootstrap.php b/retailcrm/bootstrap.php new file mode 100644 index 00000000..7c0cc34d --- /dev/null +++ b/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/retailcrm/config.xml b/retailcrm/config.xml new file mode 100644 index 00000000..52524199 --- /dev/null +++ b/retailcrm/config.xml @@ -0,0 +1,13 @@ + + + retailcrm + + + + + + + 1 + 1 + + diff --git a/retailcrm/config_ru.xml b/retailcrm/config_ru.xml new file mode 100644 index 00000000..7a939044 --- /dev/null +++ b/retailcrm/config_ru.xml @@ -0,0 +1,13 @@ + + + retailcrm + + + + + + + 1 + 1 + + diff --git a/retailcrm/job/export.php b/retailcrm/job/export.php new file mode 100644 index 00000000..d2ca9d5c --- /dev/null +++ b/retailcrm/job/export.php @@ -0,0 +1,147 @@ +getCustomers(); +$orderRecords = $orderInstance->getOrdersWithInformations(); + +$delivery = json_decode(Configuration::get('RETAILCRM_API_DELIVERY'), true); +$payment = json_decode(Configuration::get('RETAILCRM_API_PAYMENT'), true); +$status = json_decode(Configuration::get('RETAILCRM_API_STATUS'), true); + +foreach ($customerRecords as $record) { + $customers[$record['id_customer']] = array( + 'externalId' => $record['id_customer'], + 'firstName' => $record['firstname'], + 'lastname' => $record['lastname'], + 'email' => $record['email'] + ); +} + +unset($customerRecords); + +foreach ($orderRecords as $record) { + + $object = new Order($record['id_order']); + + if (Module::getInstanceByName('advancedcheckout') === false) { + $paymentType = $record['module']; + } else { + $paymentType = $record['payment']; + } + + if ($record['current_state'] == 0) { + $order_status = 'completed'; + } else { + $order_status = array_key_exists($record['current_state'], $status) + ? $status[$record['current_state']] + : 'completed' + ; + } + + $cart = new Cart($object->getCartIdStatic($record['id_order'])); + $addressCollection = $cart->getAddressCollection(); + $address = array_shift($addressCollection); + + if ($address instanceof Address) { + $phone = is_null($address->phone) + ? is_null($address->phone_mobile) ? '' : $address->phone_mobile + : $address->phone + ; + + $postcode = $address->postcode; + $city = $address->city; + $addres_line = sprintf("%s %s", $address->address1, $address->address2); + } + + $order = array( + 'externalId' => $record['id_order'], + 'createdAt' => $record['date_add'], + 'status' => $order_status, + 'firstName' => $record['firstname'], + 'lastName' => $record['lastname'], + 'email' => $record['email'], + ); + + if (isset($postcode)) { + $order['delivery']['address']['postcode'] = $postcode; + } + + if (isset($city)) { + $order['delivery']['address']['city'] = $city; + } + + if (isset($addres_line)) { + $order['delivery']['address']['text'] = $addres_line; + } + + if ($phone) { + $order['phone'] = $phone; + } + + if (array_key_exists($paymentType, $payment)) { + $order['paymentType'] = $payment[$paymentType]; + } + + if (array_key_exists($record['id_carrier'], $delivery)) { + $order['delivery']['code'] = $delivery[$record['id_carrier']]; + } + + if (isset($record['total_shipping_tax_incl']) && (int) $record['total_shipping_tax_incl'] > 0) { + $order['delivery']['cost'] = round($record['total_shipping_tax_incl'], 2); + } + + $products = $object->getProducts(); + + foreach($products as $product) { + $item = array( + 'productId' => $product['product_id'], + 'productName' => $product['product_name'], + 'quantity' => $product['product_quantity'], + 'initialPrice' => round($product['product_price'], 2), + 'purchasePrice' => round($product['purchase_supplier_price'], 2) + ); + + $order['items'][] = $item; + } + + if ($record['id_customer']) { + $order['customer']['externalId'] = $record['id_customer']; + } + + $orders[$record['id_order']] = $order; +} + +unset($orderRecords); + +$customers = array_chunk($customers, 50); + +foreach ($customers as $chunk) { + $api->customersUpload($chunk); + time_nanosleep(0, 200000000); +} + +$orders = array_chunk($orders, 50); + +foreach ($orders as $chunk) { + $api->ordersUpload($chunk); + time_nanosleep(0, 200000000); +} diff --git a/retailcrm/job/icml.php b/retailcrm/job/icml.php new file mode 100644 index 00000000..9cf6da53 --- /dev/null +++ b/retailcrm/job/icml.php @@ -0,0 +1,11 @@ +getData(); + +$icml = new RetailcrmIcml(Configuration::get('PS_SHOP_NAME'), _PS_ROOT_DIR_ . '/retailcrm.xml'); +$icml->generate($data[0], $data[1]); diff --git a/retailcrm/job/sync.php b/retailcrm/job/sync.php new file mode 100644 index 00000000..07e50c5e --- /dev/null +++ b/retailcrm/job/sync.php @@ -0,0 +1,398 @@ +ordersHistory(new DateTime($startFrom)); + +if ($history->isSuccess() && count($history->orders) > 0) { + + $statuses = array_flip(array_filter(json_decode(Configuration::get('RETAILCRM_API_STATUS'), true))); + $deliveries = array_flip(array_filter(json_decode(Configuration::get('RETAILCRM_API_DELIVERY'), true))); + $payments = array_flip(array_filter(json_decode(Configuration::get('RETAILCRM_API_PAYMENT'), true))); + + foreach ($history->orders as $order) { + + if (!array_key_exists('externalId', $order)) { + + $customer = new Customer(); + $customer->getByEmail($order['customer']['email']); + + if ( + !array_key_exists('externalId', $order['customer']) && + Validate::isEmail($order['customer']['email']) + ) { + if (!$customer->id) + { + $customer->firstname = $order['customer']['firstName']; + $customer->lastname = $order['customer']['lastName']; + $customer->email = $order['customer']['email']; + $customer->passwd = substr(str_shuffle(strtolower(sha1(rand() . time()))),0, 5); + + if($customer->add()) { + $customer->getByEmail($order['customer']['email']); + $customer_id = $customer->id; + + $address = new Address(); + $address->id_customer = $customer->id; + $address->id_country = $default_country; + $address->lastname = $customer->lastname; + $address->firstname = $customer->firstname; + $address->alias = 'default'; + $address->postcode = $customer['address']['index']; + $address->city = $customer['address']['city']; + $address->address1 = $customer['address']['text']; + $address->phone = $customer['phones'][0]['number']; + + $address->add(); + $addr = $customer->getAddresses($default_lang); + $address_id = $addr[0]['id_address']; + } + } else { + $addresses = $customer->getAddresses($default_lang); + $address_id = $addresses[0]['id_address']; + $customer_id = $customer->id; + } + + array_push( + $customerFix, + array( + 'id' => $order['customer']['id'], + 'externalId' => $customer_id + ) + ); + } else { + $addresses = $customer->getAddresses($default_lang); + $address_id = $addresses[0]['id_address']; + $customer_id = $order['customer']['externalId']; + } + + $delivery = $order['delivery']['code']; + $payment = $order['paymentType']; + $state = $order['status']; + + $cart = new Cart(); + $cart->id_currency = $default_currency; + $cart->id_lang = $default_lang; + $cart->id_customer = $customer_id; + $cart->id_address_delivery = (int) $address_id; + $cart->id_address_invoice = (int) $address_id; + $cart->id_carrier = (int) $deliveries[$delivery]; + + $cart->add(); + + $products = array(); + + if(!empty($order['items'])) { + foreach ($order['items'] as $item) { + $product = array(); + $product['id_product'] = (int) $item['offer']['externalId']; + $product['quantity'] = $item['quantity']; + $product['id_address_delivery'] = (int) $address_id; + $products[] = $product; + } + } + + $cart->setWsCartRows($products); + $cart->update(); + + /* + * Create order + */ + $newOrder = new Order(); + $newOrder->id_address_delivery = (int) $address_id; + $newOrder->id_address_invoice = (int) $address_id; + $newOrder->id_cart = (int) $cart->id; + $newOrder->id_currency = $default_currency; + $newOrder->id_lang = $default_lang; + $newOrder->id_customer = (int) $customer_id; + $newOrder->id_carrier = (int) $deliveries[$delivery]; + $newOrder->payment = $payments[$payment]; + $newOrder->module = (Module::getInstanceByName('advancedcheckout') === false) + ? $payments[$payment] + : 'advancedcheckout' + ; + $newOrder->total_paid = $order['summ'] + $order['delivery']['cost']; + $newOrder->total_paid_tax_incl = $order['summ'] + $order['delivery']['cost']; + $newOrder->total_paid_tax_excl = $order['summ'] + $order['delivery']['cost']; + $newOrder->total_paid_real = $order['summ'] + $order['delivery']['cost']; + $newOrder->total_products = $order['summ']; + $newOrder->total_products_wt = $order['summ']; + $newOrder->total_shipping = $order['delivery']['cost']; + $newOrder->total_shipping_tax_incl = $order['delivery']['cost']; + $newOrder->total_shipping_tax_excl = $order['delivery']['cost']; + $newOrder->conversion_rate = 1.000000; + $newOrder->current_state = (int) $statuses[$state]; + $newOrder->delivery_date = $order['delivery']['date']; + $newOrder->date_add = $order['createdAt']; + $newOrder->date_upd = $order['createdAt']; + $newOrder->valid = 1; + $newOrder->secure_key = md5(time()); + + if (isset($order['discount'])) + { + $newOrder->total_discounts = $order['discount']; + } + + $newOrder->add(false, false); + + /* + * collect order ids for single fix request + */ + array_push($orderFix, array('id' => $order['id'], 'externalId' => $newOrder->id)); + + /* + * Create order details + */ + $product_list = array(); + foreach ($order['items'] as $item) { + $product = new Product((int) $item['offer']['externalId'], false, $default_lang); + $qty = $item['quantity']; + $product_list[] = array('product' =>$product, 'quantity' => $qty); + } + + $query = 'INSERT `'._DB_PREFIX_.'order_detail` + ( + `id_order`, `id_order_invoice`, `id_shop`, `product_id`, `product_attribute_id`, + `product_name`, `product_quantity`, `product_quantity_in_stock`, `product_price`, + `product_reference`, `total_price_tax_excl`, `total_price_tax_incl`, + `unit_price_tax_excl`, `unit_price_tax_incl`, `original_product_price` + ) + + VALUES'; + + foreach ($product_list as $product) { + $query .= '(' + .(int) $newOrder->id.', + 0, + '. $this->context->shop->id.', + '.(int) $product['product']->id.', + 0, + '.implode('', array('\'', $product['product']->name, '\'')).', + '.(int) $product['quantity'].', + '.(int) $product['quantity'].', + '.$product['product']->price.', + '.implode('', array('\'', $product['product']->reference, '\'')).', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.' + ),'; + } + + Db::getInstance()->execute(rtrim($query, ',')); + + $this->api->customersFixExternalIds($customerFix); + $this->api->ordersFixExternalIds($orderFix); + } else { + /* + * take last order update only + */ + if ($order['paymentType'] != null && $order['deliveryType'] != null && $order['status'] != null) { + $orderToUpdate = new Order((int) $order['externalId']); + + /* + * check status + */ + $stype = $order['status']; + if ($statuses[$stype] != null) { + if ($statuses[$stype] != $orderToUpdate->current_state) { + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'orders` + SET `current_state` = \''.$statuses[$stype].'\' + WHERE `id_order` = '.(int) $order['externalId'] + ); + } + } + + /* + * check delivery type + */ + $dtype = $order['deliveryType']; + if ($deliveries[$dtype] != null) { + if ($deliveries[$dtype] != $orderToUpdate->id_carrier) { + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'orders` + SET `id_carrier` = \''.$deliveries[$dtype].'\' + WHERE `id_order` = '.(int) $order['externalId'] + ); + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'order_carrier` + SET `id_carrier` = \''.$deliveries[$dtype].'\' + WHERE `id_order` = \''.$orderToUpdate->id.'\'' + ); + } + } + + /* + * check payment type + */ + $ptype = $order['paymentType']; + if ($payments[$ptype] != null) { + if ($payments[$ptype] != $orderToUpdate->payment) { + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'orders` + SET `payment` = \''.$payments[$ptype].'\' + WHERE `id_order` = '.(int) $order['externalId'] + ); + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'order_payment` + SET `payment_method` = \''.$payments[$ptype].'\' + WHERE `order_reference` = \''.$orderToUpdate->reference.'\'' + ); + + } + } + + /* + * Clean deleted items + */ + foreach ($order['items'] as $key => $item) { + if (isset($item['deleted']) && $item['deleted'] == true) { + Db::getInstance()->execute(' + DELETE FROM `'._DB_PREFIX_.'order_detail` + WHERE `id_order` = '. $orderToUpdate->id .' + AND `product_id` = '.$item['id'] + ); + + unset($order['items'][$key]); + } + } + + /* + * Check items quantity + */ + foreach ($orderToUpdate->getProductsDetail() as $orderItem) { + foreach ($order['items'] as $key => $item) { + if ($item['offer']['externalId'] == $orderItem['product_id']) { + if (isset($item['quantity']) && $item['quantity'] != $orderItem['product_quantity']) { + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'order_detail` + SET `product_quantity` = '.$item['quantity'].', + `product_quantity_in_stock` = '.$item['quantity'].' + WHERE `id_order_detail` = '.$orderItem['id_order_detail'] + ); + } + + unset($order['items'][$key]); + } + } + } + + /* + * Check new items + */ + if (!empty($order['items'])) { + foreach ($order['items'] as $key => $newItem) { + $product = new Product((int) $newItem['offer']['externalId'], false, $default_lang); + $qty = $newItem['quantity']; + $product_list[] = array('product' =>$product, 'quantity' => $qty); + } + + + $query = 'INSERT `'._DB_PREFIX_.'order_detail` + ( + `id_order`, `id_order_invoice`, `id_shop`, `product_id`, `product_attribute_id`, + `product_name`, `product_quantity`, `product_quantity_in_stock`, `product_price`, + `product_reference`, `total_price_tax_excl`, `total_price_tax_incl`, + `unit_price_tax_excl`, `unit_price_tax_incl`, `original_product_price` + ) + + VALUES'; + + foreach ($product_list as $product) { + $query .= '(' + .(int) $orderToUpdate->id.', + 0, + '. $this->context->shop->id.', + '.(int) $product['product']->id.', + 0, + '.implode('', array('\'', $product['product']->name, '\'')).', + '.(int) $product['quantity'].', + '.(int) $product['quantity'].', + '.$product['product']->price.', + '.implode('', array('\'', $product['product']->reference, '\'')).', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.', + '.$product['product']->price.' + ),'; + } + + Db::getInstance()->execute(rtrim($query, ',')); + unset($order['items'][$key]); + } + + /* + * Fix prices & discounts + * Discounts only for whole order + */ + $orderDiscout = null; + $orderTotal = $order['summ']; + + if (isset($order['discount']) && $order['discount'] > 0) { + if ($order['discount'] != $orderToUpdate->total_discounts) { + $orderDiscout = ($orderDiscout == null) ? $order['discount'] : $order['discount'] + $orderDiscout; + } + } + + if (isset($order['discountPercent']) && $order['discountPercent'] > 0) { + $percent = ($order['summ'] * $order['discountPercent'])/100; + if ($percent != $orderToUpdate->total_discounts) { + $orderDiscout = ($orderDiscout == null) ? $percent : $percent + $orderDiscout; + } + } + + $totalDiscount = ($orderDiscout == null) ? $orderToUpdate->total_discounts : $orderDiscout; + + if ($totalDiscount != $orderToUpdate->total_discounts || $orderTotal != $orderToUpdate->total_paid) { + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'orders` + SET `total_discounts` = '.$totalDiscount.', + `total_discounts_tax_incl` = '.$totalDiscount.', + `total_discounts_tax_excl` = '.$totalDiscount.', + `total_paid` = '.$orderTotal.', + `total_paid_tax_incl` = '.$orderTotal.', + `total_paid_tax_excl` = '.$orderTotal.' + WHERE `id_order` = '.(int) $order['externalId'] + ); + } + } + } + } + + /* + * Update last sync timestamp + */ + Configuration::updateValue('RETAILCRM_LAST_SYNC', $history->generatedAt); +} else { + return 'Nothing to sync'; +} diff --git a/retailcrm/lib/CurlException.php b/retailcrm/lib/CurlException.php new file mode 100644 index 00000000..2ab00f57 --- /dev/null +++ b/retailcrm/lib/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/retailcrm/lib/RetailcrmApiResponse.php b/retailcrm/lib/RetailcrmApiResponse.php new file mode 100644 index 00000000..bba91db8 --- /dev/null +++ b/retailcrm/lib/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/retailcrm/lib/RetailcrmCatalog.php b/retailcrm/lib/RetailcrmCatalog.php new file mode 100644 index 00000000..a1808b1f --- /dev/null +++ b/retailcrm/lib/RetailcrmCatalog.php @@ -0,0 +1,133 @@ +default_lang = (int) Configuration::get('PS_LANG_DEFAULT'); + $this->default_currency = (int) Configuration::get('PS_CURRENCY_DEFAULT'); + $this->default_country = (int) Configuration::get('PS_COUNTRY_DEFAULT'); + } + + public function getData() + { + + $id_lang = (int) Configuration::get('PS_LANG_DEFAULT'); + $currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT')); + $shop_url = (Configuration::get('PS_SSL_ENABLED') ? _PS_BASE_URL_SSL_ : _PS_BASE_URL_); + + $items = array(); + $categories = array(); + + if ($currency->iso_code == 'RUB') { + $currency->iso_code = 'RUR'; + } + + $currencies = Currency::getCurrencies(); + + $types = Category::getCategories($id_lang, true, false); + foreach ($types AS $category) { + $categories[] = array( + 'id' => $category['id_category'], + 'parentId' => $category['id_parent'], + 'name' => $category['name'] + ); + } + + $products = Product::getProducts($id_lang, 0, 0, 'name', 'asc'); + + foreach ($products AS $product) { + $category = $product['id_category_default']; + + if ($category == Configuration::get('PS_HOME_CATEGORY')) { + $temp_categories = Product::getProductCategories($product['id_product']); + + foreach ($temp_categories AS $category) { + if ($category != Configuration::get('PS_HOME_CATEGORY')) + break; + } + + if ($category == Configuration::get('PS_HOME_CATEGORY')) { + continue; + } + } + + $link = new Link(); + $cover = Image::getCover($product['id_product']); + + $picture = 'http://' . $link->getImageLink($product['link_rewrite'], $product['id_product'] . '-' . $cover['id_image'], 'large_default'); + if (!(substr($picture, 0, strlen($shop_url)) === $shop_url)) { + $picture = rtrim($shop_url, "/") . $picture; + } + + $crewrite = Category::getLinkRewrite($product['id_category_default'], $id_lang); + $url = $link->getProductLink($product['id_product'], $product['link_rewrite'], $crewrite); + $version = substr(_PS_VERSION_, 0, 3); + + if ($version == "1.3") { + $available_for_order = $product['active'] && $product['quantity']; + $quantity = $product['quantity']; + } else { + $prod = new Product($product['id_product']); + $available_for_order = $product['active'] && $product['available_for_order'] && $prod->checkQty(1); + $quantity = (int) StockAvailable::getQuantityAvailableByProduct($prod->id); + } + + $item = array( + 'id' => $product['id_product'], + 'productId' => $product['id_product'], + 'productActivity' => ($available_for_order) ? 'Y' : 'N', + 'name' => htmlspecialchars(strip_tags($product['name'])), + 'productName' => htmlspecialchars(strip_tags($product['name'])), + 'categoryId' => array($category), + 'picture' => $picture, + 'url' => $url, + 'quantity' => $quantity > 0 ? $quantity : 0 + ); + + if (!empty($product['wholesale_price'])) { + $item['purchasePrice'] = round($product['wholesale_price'], 2); + } + + $item['price'] = !empty($product['rate']) + ? round($product['price'], 2) + (round($product['price'], 2) * $product['rate'] / 100) + : round($product['price'], 2) + ; + + + if (!empty($product['manufacturer_name'])) { + $item['vendor'] = $product['manufacturer_name']; + } + + if (!empty($product['reference'])) { + $item['article'] = htmlspecialchars($product['reference']); + } + + $weight = round($product['weight'], 2); + + if (!empty($weight)) { + $item['weight'] = $weight; + } + + $width = round($product['width'], 2); + $height = round($product['height'], 2); + $depth = round($product['depth'], 2); + + if (!empty($width)) { + if (!empty($height)) { + if (!empty($depth)) { + $item['size'] = implode('x', array($width, $height, $depth)); + } else { + $item['size'] = implode('x', array($width, $height)); + } + } + } + + $items[] = $item; + } + + return array($categories, $items); + } + +} diff --git a/retailcrm/lib/RetailcrmHttpClient.php b/retailcrm/lib/RetailcrmHttpClient.php new file mode 100644 index 00000000..6f74d16a --- /dev/null +++ b/retailcrm/lib/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/retailcrm/lib/RetailcrmIcml.php b/retailcrm/lib/RetailcrmIcml.php new file mode 100644 index 00000000..825d9c2d --- /dev/null +++ b/retailcrm/lib/RetailcrmIcml.php @@ -0,0 +1,137 @@ +shop = $shop; + $this->file = $file; + + $this->properties = array( + 'name', + 'productName', + 'price', + 'purchasePrice', + 'vendor', + 'picture', + 'url', + 'xmlId', + 'productActivity' + ); + + $this->params = array( + 'article' => 'Артикул', + 'color' => 'Цвет', + 'weight' => 'Вес', + 'size' => 'Размер', + 'tax' => 'Наценка' + ); + } + + public function generate($categories, $offers) + { + $string = ' + + + ' . $this->shop . ' + + + + + '; + + $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($categories); + $this->addOffers($offers); + + $this->dd->saveXML(); + $this->dd->save($this->file); + } + + private function addCategories($categories) + { + foreach ($categories as $category) { + $e = $this->eCategories->appendChild( + $this->dd->createElement( + 'category', $category['name'] + ) + ); + + $e->setAttribute('id', $category['id']); + + if ($category['parentId'] > 0) { + $e->setAttribute('parentId', $category['parentId']); + } + } + } + + private function addOffers($offers) + { + foreach ($offers as $offer) { + + $e = $this->eOffers->appendChild( + $this->dd->createElement('offer') + ); + + $e->setAttribute('id', $offer['id']); + $e->setAttribute('productId', $offer['productId']); + + if (!empty($offer['quantity'])) { + $e->setAttribute('quantity', (int) $offer['quantity']); + } else { + $e->setAttribute('quantity', 0); + } + + foreach ($offer['categoryId'] as $categoryId) { + $e->appendChild( + $this->dd->createElement('categoryId', $categoryId) + ); + } + + $offerKeys = array_keys($offer); + + foreach ($offerKeys as $key) { + if (in_array($key, $this->properties)) { + $e->appendChild( + $this->dd->createElement($key) + )->appendChild( + $this->dd->createTextNode(trim($offer[$key])) + ); + } + + if (in_array($key, array_keys($this->params))) { + $param = $this->dd->createElement('param'); + $param->setAttribute('code', $key); + $param->setAttribute('name', $this->params[$key]); + $param->appendChild( + $this->dd->createTextNode($offer[$key]) + ); + $e->appendChild($param); + } + } + } + } + +} diff --git a/retailcrm/lib/RetailcrmProxy.php b/retailcrm/lib/RetailcrmProxy.php new file mode 100644 index 00000000..f99f7407 --- /dev/null +++ b/retailcrm/lib/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/retailcrm/lib/RetailcrmReferences.php b/retailcrm/lib/RetailcrmReferences.php new file mode 100644 index 00000000..1c630484 --- /dev/null +++ b/retailcrm/lib/RetailcrmReferences.php @@ -0,0 +1,215 @@ +api = $client; + $this->default_lang = (int) Configuration::get('PS_LANG_DEFAULT'); + } + + public function getDeliveryTypes() + { + $deliveryTypes = array(); + + $carriers = Carrier::getCarriers( + $this->default_lang, + true, + false, + false, + null, + PS_CARRIERS_AND_CARRIER_MODULES_NEED_RANGE + ); + + if (!empty($carriers)) { + foreach ($carriers as $carrier) { + $deliveryTypes[] = array( + 'type' => 'select', + 'label' => $carrier['name'], + 'name' => 'RETAILCRM_API_DELIVERY[' . $carrier['id_carrier'] . ']', + 'required' => false, + 'options' => array( + 'query' => $this->getApiDeliveryTypes(), + 'id' => 'id_option', + 'name' => 'name' + ) + ); + } + } + + return $deliveryTypes; + } + + public function getStatuses() + { + $statusTypes = array(); + /** + * TODO: state ids duplicates between both arrays, temporary disable return states + * + $states = array_merge( + OrderState::getOrderStates($this->default_lang, true), + OrderReturnState::getOrderReturnStates($this->default_lang, true) + ); + */ + + $states = OrderState::getOrderStates($this->default_lang, true); + + if (!empty($states)) { + foreach ($states as $state) { + if ($state['name'] != ' ') { + /*$key = isset($state['id_order_state']) + ? $state['id_order_state'] + : $state['id_order_return_state'] + ;*/ + $key = $state['id_order_state']; + $statusTypes[] = array( + 'type' => 'select', + 'label' => $state['name'], + 'name' => "RETAILCRM_API_STATUS[$key]", + 'required' => false, + 'options' => array( + 'query' => $this->getApiStatuses(), + 'id' => 'id_option', + 'name' => 'name' + ) + ); + } + } + } + + return $statusTypes; + } + + public function getPaymentTypes() + { + $payments = $this->getSystemPaymentModules(); + $paymentTypes = array(); + + if (!empty($payments)) { + foreach ($payments as $payment) { + $paymentTypes[] = array( + 'type' => 'select', + 'label' => $payment['name'], + 'name' => 'RETAILCRM_API_PAYMENT[' . $payment['code'] . ']', + 'required' => false, + 'options' => array( + 'query' => $this->getApiPaymentTypes(), + 'id' => 'id_option', + 'name' => 'name' + ) + ); + } + } + + return $paymentTypes; + } + + protected function getSystemPaymentModules() + { + $shop_id = Context::getContext()->shop->id; + + /* Get all modules then select only payment ones */ + $modules = Module::getModulesOnDisk(true); + + foreach ($modules as $module) { + if ($module->tab == 'payments_gateways') { + if ($module->id) { + if (!get_class($module) == 'SimpleXMLElement') + $module->country = array(); + $countries = DB::getInstance()->executeS('SELECT id_country FROM ' . _DB_PREFIX_ . 'module_country WHERE id_module = ' . (int) $module->id . ' AND `id_shop`=' . (int) $shop_id); + foreach ($countries as $country) + $module->country[] = $country['id_country']; + if (!get_class($module) == 'SimpleXMLElement') + $module->currency = array(); + $currencies = DB::getInstance()->executeS('SELECT id_currency FROM ' . _DB_PREFIX_ . 'module_currency WHERE id_module = ' . (int) $module->id . ' AND `id_shop`=' . (int) $shop_id); + foreach ($currencies as $currency) + $module->currency[] = $currency['id_currency']; + if (!get_class($module) == 'SimpleXMLElement') + $module->group = array(); + $groups = DB::getInstance()->executeS('SELECT id_group FROM ' . _DB_PREFIX_ . 'module_group WHERE id_module = ' . (int) $module->id . ' AND `id_shop`=' . (int) $shop_id); + foreach ($groups as $group) + $module->group[] = $group['id_group']; + } else { + $module->country = null; + $module->currency = null; + $module->group = null; + } + + if ($module->active != 0) { + $this->payment_modules[] = array( + 'id' => $module->id, + 'code' => $module->name, + 'name' => $module->displayName + ); + } + } + } + + return $this->payment_modules; + } + + protected function getApiDeliveryTypes() + { + $crmDeliveryTypes = array(); + $request = $this->api->deliveryTypesList(); + + if ($request) { + $crmDeliveryTypes[] = array( + 'id_option' => '', + 'name' => '', + ); + foreach ($request->deliveryTypes as $dType) { + $crmDeliveryTypes[] = array( + 'id_option' => $dType['code'], + 'name' => $dType['name'], + ); + } + } + + return $crmDeliveryTypes; + } + + protected function getApiStatuses() + { + $crmStatusTypes = array(); + $request = $this->api->statusesList(); + + if ($request) { + $crmStatusTypes[] = array( + 'id_option' => '', + 'name' => '', + ); + foreach ($request->statuses as $sType) { + $crmStatusTypes[] = array( + 'id_option' => $sType['code'], + 'name' => $sType['name'] + ); + } + } + + return $crmStatusTypes; + } + + protected function getApiPaymentTypes() + { + $crmPaymentTypes = array(); + $request = $this->api->paymentTypesList(); + + if ($request) { + $crmPaymentTypes[] = array( + 'id_option' => '', + 'name' => '', + ); + foreach ($request->paymentTypes as $pType) { + $crmPaymentTypes[] = array( + 'id_option' => $pType['code'], + 'name' => $pType['name'] + ); + } + } + + return $crmPaymentTypes; + } + +} diff --git a/retailcrm/lib/RetailcrmService.php b/retailcrm/lib/RetailcrmService.php new file mode 100644 index 00000000..9a946946 --- /dev/null +++ b/retailcrm/lib/RetailcrmService.php @@ -0,0 +1,42 @@ +name = 'retailcrm'; + $this->tab = 'export'; + $this->version = '2.0'; + $this->author = 'Retail Driver LCC'; + $this->displayName = $this->l('RetailCRM'); + $this->description = $this->l('Integration module for RetailCRM'); + $this->confirmUninstall = $this->l('Are you sure you want to uninstall?'); + $this->default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); + $this->default_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT'); + $this->default_country = (int)Configuration::get('PS_COUNTRY_DEFAULT'); + $this->apiUrl = Configuration::get('RETAILCRM_ADDRESS'); + $this->apiKey = Configuration::get('RETAILCRM_API_TOKEN'); + $this->ps_versions_compliancy = array('min' => '1.5', 'max' => _PS_VERSION_); + $this->version = substr(_PS_VERSION_, 0, 3); + + if ($this->version == '1.6') { + $this->bootstrap = true; + } + + if (!empty($this->apiUrl) && !empty($this->apiKey)) { + $this->api = new RetailcrmProxy($this->apiUrl, $this->apiKey, _PS_ROOT_DIR_ . '/retailcrm.log'); + $this->reference = new RetailcrmReferences($this->api); + } + + parent::__construct(); + } + + function install() + { + return ( + parent::install() && + $this->registerHook('newOrder') && + $this->registerHook('actionOrderStatusPostUpdate') && + $this->registerHook('actionPaymentConfirmation') && + $this->registerHook('actionCustomerAccountAdd') + ); + } + + function uninstall() + { + return parent::uninstall() && + Configuration::deleteByName('RETAILCRM_ADDRESS') && + Configuration::deleteByName('RETAILCRM_API_TOKEN') && + Configuration::deleteByName('RETAILCRM_API_STATUS') && + Configuration::deleteByName('RETAILCRM_API_DELIVERY') && + Configuration::deleteByName('RETAILCRM_LAST_SYNC'); + } + + public function getContent() + { + $output = null; + + $address = Configuration::get('RETAILCRM_ADDRESS'); + $token = Configuration::get('RETAILCRM_API_TOKEN'); + + if (!$address || $address == '') { + $output .= $this->displayError($this->l('Invalid or empty crm address')); + } elseif (!$token || $token == '') { + $output .= $this->displayError($this->l('Invalid or empty crm api token')); + } else { + $output .= $this->displayConfirmation( + $this->l('Timezone settings must be identical to both of your crm and shop') . + " $address/admin/settings#t-main" + ); + } + + if (Tools::isSubmit('submit' . $this->name)) { + $address = strval(Tools::getValue('RETAILCRM_ADDRESS')); + $token = strval(Tools::getValue('RETAILCRM_API_TOKEN')); + $delivery = json_encode(Tools::getValue('RETAILCRM_API_DELIVERY')); + $status = json_encode(Tools::getValue('RETAILCRM_API_STATUS')); + $payment = json_encode(Tools::getValue('RETAILCRM_API_PAYMENT')); + + if (!$address || empty($address) || !Validate::isGenericName($address)) { + $output .= $this->displayError($this->l('Invalid crm address')); + } elseif (!$token || empty($token) || !Validate::isGenericName($token)) { + $output .= $this->displayError($this->l('Invalid crm api token')); + } else { + Configuration::updateValue('RETAILCRM_ADDRESS', $address); + Configuration::updateValue('RETAILCRM_API_TOKEN', $token); + Configuration::updateValue('RETAILCRM_API_DELIVERY', $delivery); + Configuration::updateValue('RETAILCRM_API_STATUS', $status); + Configuration::updateValue('RETAILCRM_API_PAYMENT', $payment); + $output .= $this->displayConfirmation($this->l('Settings updated')); + } + } + + $this->display(__FILE__, 'retailcrm.tpl'); + + return $output . $this->displayForm(); + } + + public function displayForm() + { + + $this->displayConfirmation($this->l('Settings updated')); + + $default_lang = $this->default_lang; + + /* + * Network connection form + */ + $fields_form[0]['form'] = array( + 'legend' => array( + 'title' => $this->l('Network connection'), + ), + 'input' => array( + array( + 'type' => 'text', + 'label' => $this->l('CRM address'), + 'name' => 'RETAILCRM_ADDRESS', + 'size' => 20, + 'required' => true + ), + array( + 'type' => 'text', + 'label' => $this->l('CRM token'), + 'name' => 'RETAILCRM_API_TOKEN', + 'size' => 20, + 'required' => true + ) + ), + 'submit' => array( + 'title' => $this->l('Save'), + 'class' => 'button' + ) + ); + + + if (!empty($this->apiUrl) && !empty($this->apiKey)) { + /* + * Delivery + */ + $fields_form[1]['form'] = array( + 'legend' => array('title' => $this->l('Delivery')), + 'input' => $this->reference->getDeliveryTypes(), + ); + + /* + * Order status + */ + $fields_form[2]['form'] = array( + 'legend' => array('title' => $this->l('Order statuses')), + 'input' => $this->reference->getStatuses(), + ); + + /* + * Payment + */ + $fields_form[3]['form'] = array( + 'legend' => array('title' => $this->l('Payment types')), + 'input' => $this->reference->getPaymentTypes(), + ); + } + + /* + * Diplay forms + */ + + $helper = new HelperForm(); + + $helper->module = $this; + $helper->name_controller = $this->name; + $helper->token = Tools::getAdminTokenLite('AdminModules'); + $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name; + + $helper->default_form_language = $default_lang; + $helper->allow_employee_form_lang = $default_lang; + + $helper->title = $this->displayName; + $helper->show_toolbar = true; + $helper->toolbar_scroll = true; + $helper->submit_action = 'submit' . $this->name; + $helper->toolbar_btn = array( + 'save' => + array( + 'desc' => $this->l('Save'), + 'href' => sprintf( + "%s&configure=%s&save%s&token=%s", + AdminController::$currentIndex, + $this->name, + $this->name, + Tools::getAdminTokenLite('AdminModules') + ) + ), + 'back' => array( + 'href' => AdminController::$currentIndex . '&token=' . Tools::getAdminTokenLite('AdminModules'), + 'desc' => $this->l('Back to list') + ) + ); + + $helper->fields_value['RETAILCRM_ADDRESS'] = Configuration::get('RETAILCRM_ADDRESS'); + $helper->fields_value['RETAILCRM_API_TOKEN'] = Configuration::get('RETAILCRM_API_TOKEN'); + + $deliverySettings = Configuration::get('RETAILCRM_API_DELIVERY'); + if (isset($deliverySettings) && $deliverySettings != '') { + $deliveryTypes = json_decode($deliverySettings); + if ($deliveryTypes) { + foreach ($deliveryTypes as $idx => $delivery) { + $name = 'RETAILCRM_API_DELIVERY[' . $idx . ']'; + $helper->fields_value[$name] = $delivery; + } + } + } + + $statusSettings = Configuration::get('RETAILCRM_API_STATUS'); + if (isset($statusSettings) && $statusSettings != '') { + $statusTypes = json_decode($statusSettings); + if ($statusTypes) { + foreach ($statusTypes as $idx => $status) { + $name = 'RETAILCRM_API_STATUS[' . $idx . ']'; + $helper->fields_value[$name] = $status; + } + } + } + + $paymentSettings = Configuration::get('RETAILCRM_API_PAYMENT'); + if (isset($paymentSettings) && $paymentSettings != '') { + $paymentTypes = json_decode($paymentSettings); + if ($paymentTypes) { + foreach ($paymentTypes as $idx => $payment) { + $name = 'RETAILCRM_API_PAYMENT[' . $idx . ']'; + $helper->fields_value[$name] = $payment; + } + } + } + + return $helper->generateForm($fields_form); + } + + public function hookActionCustomerAccountAdd($params) + { + $this->api->customersCreate( + array( + 'externalId' => $params['newCustomer']->id, + 'firstName' => $params['newCustomer']->firstname, + 'lastName' => $params['newCustomer']->lastname, + 'email' => $params['newCustomer']->email, + 'createdAt' => $params['newCustomer']->date_add + ) + ); + } + + public function hookNewOrder($params) + { + return $this->hookActionOrderStatusPostUpdate($params); + } + + public function hookActionPaymentConfirmation($params) + { + $this->api->ordersEdit( + array( + 'externalId' => $params['id_order'], + 'paymentStatus' => 'paid', + 'createdAt' => $params['cart']->date_upd + ) + ); + + return $this->hookActionOrderStatusPostUpdate($params); + } + + public function hookActionOrderStatusPostUpdate($params) + { + $address_id = Address::getFirstCustomerAddressId($params['cart']->id_customer); + $sql = 'SELECT * FROM ' . _DB_PREFIX_ . 'address WHERE id_address=' . (int)$address_id; + $dbaddress = Db::getInstance()->ExecuteS($sql); + $address = $dbaddress[0]; + $delivery = json_decode(Configuration::get('RETAILCRM_API_DELIVERY')); + $payment = json_decode(Configuration::get('RETAILCRM_API_PAYMENT')); + $inCart = $params['cart']->getProducts(); + + if (isset($params['orderStatus'])) { + $this->api->customersEdit( + array( + 'externalId' => $params['cart']->id_customer, + 'lastName' => $params['customer']->lastname, + 'firstName' => $params['customer']->firstname, + 'email' => $params['customer']->email, + 'phones' => array(array('number' => $address['phone'])), + 'createdAt' => $params['customer']->date_add + ) + ); + + $items = array(); + foreach ($inCart as $item) { + $items[] = array( + 'initialPrice' => (!empty($item['rate'])) ? $item['price'] + ($item['price'] * $item['rate'] / 100) : $item['price'], + 'quantity' => $item['quantity'], + 'productId' => $item['id_product'], + 'productName' => $item['name'], + 'createdAt' => $item['date_add'] + ); + } + + $dTypeKey = $params['cart']->id_carrier; + + if (Module::getInstanceByName('advancedcheckout') === false) { + $pTypeKey = $params['order']->module; + } else { + $pTypeKey = $params['order']->payment; + } + + $this->api->ordersCreate( + array( + 'externalId' => $params['order']->id, + 'orderType' => 'eshop-individual', + 'orderMethod' => 'shopping-cart', + 'status' => 'new', + 'customerId' => $params['cart']->id_customer, + 'firstName' => $params['customer']->firstname, + 'lastName' => $params['customer']->lastname, + 'phone' => $address['phone'], + 'email' => $params['customer']->email, + 'paymentType' => $payment->$pTypeKey, + 'delivery' => array( + 'code' => $delivery->$dTypeKey, + 'cost' => $params['order']->total_shipping, + 'address' => array( + 'city' => $address['city'], + 'index' => $address['postcode'], + 'text' => $address['address1'], + ) + ), + 'discount' => $params['order']->total_discounts, + 'items' => $items, + 'createdAt' => $params['order']->date_add + ) + ); + } + + if (!empty($params['newOrderStatus'])) { + $statuses = OrderState::getOrderStates($this->default_lang); + $aStatuses = json_decode(Configuration::get('RETAILCRM_API_STATUS')); + foreach ($statuses as $status) { + if ($status['name'] == $params['newOrderStatus']->name) { + $currStatus = $status['id_order_state']; + $this->api->ordersEdit( + array( + 'externalId' => $params['id_order'], + 'status' => $aStatuses->$currStatus + ) + ); + } + } + } + } +} diff --git a/retailcrm/translations/ru.php b/retailcrm/translations/ru.php new file mode 100644 index 00000000..901ffc1c --- /dev/null +++ b/retailcrm/translations/ru.php @@ -0,0 +1,49 @@ +retailcrm_463dc31aa1a0b6e871b1a9fed8e9860a'] = 'RetailCRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_30de6237576b9a24f6fc599c22a35a4b'] = 'Модуль интеграции с RetailCRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_876f23178c29dc2552c0b48bf23cd9bd'] = 'Вы уверены, что хотите удалить модуль?'; +$_MODULE['<{retailcrm}prestashop>retailcrm_b9b2d9f66d0112f3aae7dbdbd4e22a43'] = 'Некорректный или пустой адрес CRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_942010ef43f3fec28741f62a0d9ff29c'] = 'Некорректный или пустой ключ CRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_fba05687b61bc936d1a9a92371ba8bcf'] = 'Внимание! Часовой пояс в CRM должен совпадать с часовым поясом в магазине, настроки часового пояса CRM можно задать по адресу:'; +$_MODULE['<{retailcrm}prestashop>retailcrm_5effd5157947e8ba4a08883f198b2e31'] = 'Неверный или пустой адрес CRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_576300f5b6faeb746bb6d034d98e7afd'] = 'Неверный или пустой API ключ'; +$_MODULE['<{retailcrm}prestashop>retailcrm_c888438d14855d7d96a2724ee9c306bd'] = 'Настройки обновлены'; +$_MODULE['<{retailcrm}prestashop>retailcrm_51af428aa0dcceb5230acb267ecb91c5'] = 'Настройка соединения'; +$_MODULE['<{retailcrm}prestashop>retailcrm_4cbd5dbeeef7392e50358b1bc00dd592'] = 'URL адрес CRM'; +$_MODULE['<{retailcrm}prestashop>retailcrm_7f775042e08eddee6bbfd8fbe0add4a3'] = 'API ключ'; +$_MODULE['<{retailcrm}prestashop>retailcrm_c9cc8cce247e49bae79f15173ce97354'] = 'Сохранить'; +$_MODULE['<{retailcrm}prestashop>retailcrm_065ab3a28ca4f16f55f103adc7d0226f'] = 'Способы доставки'; +$_MODULE['<{retailcrm}prestashop>retailcrm_33af8066d3c83110d4bd897f687cedd2'] = 'Статусы заказов'; +$_MODULE['<{retailcrm}prestashop>retailcrm_bab959acc06bb03897b294fbb892be6b'] = 'Способы оплаты'; +$_MODULE['<{retailcrm}prestashop>retailcrm_dd7bf230fde8d4836917806aff6a6b27'] = 'Адрес'; +$_MODULE['<{retailcrm}prestashop>retailcrm_630f6dc397fe74e52d5189e2c80f282b'] = 'Вернуться к списку'; +$_MODULE['<{retailcrm}prestashop>retailcrm_5c1cf6cfec2dad86c8ca5286a0294516'] = 'Имя'; +$_MODULE['<{retailcrm}prestashop>retailcrm_c695cfe527a6fcd680114851b86b7555'] = 'Фамилия'; +$_MODULE['<{retailcrm}prestashop>retailcrm_f9dd946cc89c1f3b41a0edbe0f36931d'] = 'Телефон'; +$_MODULE['<{retailcrm}prestashop>retailcrm_61a649a33f2869e5e35fbb7aff3a80d9'] = 'Email'; +$_MODULE['<{retailcrm}prestashop>retailcrm_2664f03ac6b8bb9eee4287720e407db3'] = 'Адрес'; +$_MODULE['<{retailcrm}prestashop>retailcrm_6ddc09dc456001d9854e9fe670374eb2'] = 'Страна'; +$_MODULE['<{retailcrm}prestashop>retailcrm_69aede266809f89b89fe70681f6a129f'] = 'Область/Край/Республика'; +$_MODULE['<{retailcrm}prestashop>retailcrm_859214628431995197c0558f7b5f8ffc'] = 'Город'; +$_MODULE['<{retailcrm}prestashop>retailcrm_4348f938bbddd8475e967ccb47ecb234'] = 'Почтовый индекс'; +$_MODULE['<{retailcrm}prestashop>retailcrm_78fce82336bbbdca7f6da7564b8f9325'] = 'Улица'; +$_MODULE['<{retailcrm}prestashop>retailcrm_71a6834884666147c0334f0c40bc7295'] = 'Дом/Строение'; +$_MODULE['<{retailcrm}prestashop>retailcrm_f88a77e3d68d251c3dc4008c327b5a0c'] = 'Квартира'; +$_MODULE['<{retailcrm}prestashop>retailcrm_d977f846d110fcb7f71c6f97330c9d10'] = 'Код домофона'; +$_MODULE['<{retailcrm}prestashop>retailcrm_56c1e354d36beb85b0d881c5b2e24cbe'] = 'Этаж'; +$_MODULE['<{retailcrm}prestashop>retailcrm_4d34f53389ed7f28ca91fc31ea360a66'] = 'Корпус'; +$_MODULE['<{retailcrm}prestashop>retailcrm_49354b452ec305136a56fe7731834156'] = 'Дом/Строение'; +$_MODULE['<{retailcrm}prestashop>retailcrm_04176f095283bc729f1e3926967e7034'] = 'Имя'; +$_MODULE['<{retailcrm}prestashop>retailcrm_dff4bf10409100d989495c6d5486035e'] = 'Фамилия'; +$_MODULE['<{retailcrm}prestashop>retailcrm_1c76cbfe21c6f44c1d1e59d54f3e4420'] = 'Компания'; +$_MODULE['<{retailcrm}prestashop>retailcrm_1aadcc03a9dbba84a3c5a5cbfde8a162'] = 'ИНН'; +$_MODULE['<{retailcrm}prestashop>retailcrm_93d03fe37ab3c6abc2a19dd8e41543bd'] = 'Адрес строка 1'; +$_MODULE['<{retailcrm}prestashop>retailcrm_22fcffe02ab9eda5b769387122f2ddce'] = 'Адрес строка 2'; +$_MODULE['<{retailcrm}prestashop>retailcrm_8bcdc441379cbf584638b0589a3f9adb'] = 'Почтовый индекс'; +$_MODULE['<{retailcrm}prestashop>retailcrm_69aede266809f89b89fe70681f6a129f'] = 'Область/Край/Республика'; +$_MODULE['<{retailcrm}prestashop>retailcrm_57d056ed0984166336b7879c2af3657f'] = 'Город'; +$_MODULE['<{retailcrm}prestashop>retailcrm_bcc254b55c4a1babdf1dcb82c207506b'] = 'Телефон'; +$_MODULE['<{retailcrm}prestashop>retailcrm_f0e1fc6f97d36cb80f29196e2662ffde'] = 'Мобильный телефон';