diff --git a/retailcrm/bootstrap.php b/retailcrm/bootstrap.php old mode 100644 new mode 100755 diff --git a/retailcrm/config_ru.xml b/retailcrm/config_ru.xml new file mode 100644 index 0000000..e87ab03 --- /dev/null +++ b/retailcrm/config_ru.xml @@ -0,0 +1,13 @@ + + + retailcrm + + + + + + + 1 + 1 + + \ No newline at end of file diff --git a/retailcrm/index.php b/retailcrm/index.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/abandonedCarts.php b/retailcrm/job/abandonedCarts.php new file mode 100644 index 0000000..3f84007 --- /dev/null +++ b/retailcrm/job/abandonedCarts.php @@ -0,0 +1,85 @@ +executeS( + 'SELECT c.id_cart, c.date_upd + FROM '._DB_PREFIX_.'cart AS c + WHERE id_customer != 0 + AND TIME_TO_SEC(TIMEDIFF(now(), date_upd)) >= 86400 + AND c.id_cart NOT IN(SELECT id_cart from '._DB_PREFIX_.'orders);' +); + +$status = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_STATUS'); +$paymentTypes = array_keys(json_decode(Configuration::get('RETAILCRM_API_PAYMENT'), true)); + +if (empty($rows) + || empty($status) + || !$api + || (count($paymentTypes) < 1) +) { + return; +} + +foreach ($rows as $cartId) { + $cart = new Cart($cartId['id_cart']); + $cartExternalId = RetailCRM::getCartOrderExternalId($cart); + + $response = $api->ordersGet($cartExternalId); + + if ($response === false) { + $api->customersCreate(RetailCRM::buildCrmCustomer(new Customer($cart->id_customer))); + $order = RetailCRM::buildCrmOrderFromCart($cart, $cartExternalId, $paymentTypes[0], $status); + + if (empty($order)) { + continue; + } + + if ($api->ordersCreate($order) !== false) { + $cart->date_upd = date('Y-m-d H:i:s'); + $cart->save(); + } + + continue; + } + + if (isset($response['order']) && !empty($response['order'])) { + $order = RetailCRM::buildCrmOrderFromCart($cart, $response['order']['externalId'], $paymentTypes[0], $status); + + if (empty($order)) { + continue; + } + + if ($api->ordersEdit($order) !== false) { + $cart->date_upd = date('Y-m-d H:i:s'); + $cart->save(); + } + } +} diff --git a/retailcrm/job/export.php b/retailcrm/job/export.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/icml.php b/retailcrm/job/icml.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/index.php b/retailcrm/job/index.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/inventories.php b/retailcrm/job/inventories.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/jobs.php b/retailcrm/job/jobs.php new file mode 100644 index 0000000..aeeedbc --- /dev/null +++ b/retailcrm/job/jobs.php @@ -0,0 +1,18 @@ + DateInterval::createFromDateString('10 seconds') + ), + true +); diff --git a/retailcrm/job/missing.php b/retailcrm/job/missing.php old mode 100644 new mode 100755 diff --git a/retailcrm/job/sync.php b/retailcrm/job/sync.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/CurlException.php b/retailcrm/lib/CurlException.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/InvalidJsonException.php b/retailcrm/lib/InvalidJsonException.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/JobManager.php b/retailcrm/lib/JobManager.php new file mode 100644 index 0000000..ea5db29 --- /dev/null +++ b/retailcrm/lib/JobManager.php @@ -0,0 +1,393 @@ + $diff) { + try { + if (static::compareIntervals(date_diff($lastRun, $current), $diff, $current) == 1) { + JobManager::runJob($job, $runOnceInContext); + } + } catch (\Exception $exception) { + static::handleError($exception->getFile(), $exception->getMessage()); + } catch (\Throwable $throwable) { + static::handleError($throwable->getFile(), $throwable->getMessage()); + } catch (\Error $error) { + static::handleError($error->getFile(), $error->getMessage()); + } + } + + static::unlock(); + } + + /** + * Compare two date intervals + * + * @param $first + * @param $second + * @param null $now + * + * @return int + * @throws \Exception + */ + private static function compareIntervals($first, $second, $now = null) + { + $dateFirst = is_null($now) ? new DateTime() : $now; + $dateSecond = clone $dateFirst; + + $dateFirst->add($first); + $dateSecond->add($second); + + if($dateFirst < $dateSecond) { + return -1; + } elseif($dateFirst == $dateSecond) { + return 0; + } + + return 1; + } + + /** + * Runs job + * + * @param string $job + * @param bool $once + */ + public static function runJob($job, $once = false) + { + $jobFile = implode(DIRECTORY_SEPARATOR, array(_PS_ROOT_DIR_, 'modules', 'retailcrm', 'job', $job . '.php')); + + if (!file_exists($jobFile)) { + throw new \InvalidArgumentException('Cannot find job'); + } + + static::execPHP($jobFile, false, $once); + } + + /** + * Runs PHP file + * + * @param $fileCommandLine + * @param bool $fork + * @param bool $once + */ + private static function execPHP($fileCommandLine, $fork = true, $once = false) + { + if ($fork) { + static::execInBackground(sprintf('%s %s', static::getPhpBinary(), $fileCommandLine)); + } else { + static::execHere($fileCommandLine, $once); + } + } + + /** + * Serializes jobs to JSON + * + * @param $jobs + * + * @return string + */ + public static function serializeJobs($jobs) + { + foreach ($jobs as $name => $interval) { + $jobs[$name] = serialize($interval); + } + + return (string) base64_encode(json_encode($jobs)); + } + + /** + * Unserializes jobs + * + * @param $jobsJson + * + * @return array + */ + public static function deserializeJobs($jobsJson) + { + $jobs = json_decode(base64_decode($jobsJson), true); + + if (json_last_error() != JSON_ERROR_NONE) { + throw new \InvalidArgumentException(sprintf('Invalid JSON: %s', json_last_error_msg())); + } + + if (!is_array($jobs) || count($jobs) == 0) { + throw new \InvalidArgumentException('Empty or invalid data'); + } + + foreach ($jobs as $name => $interval) { + if (!is_string($name) || !is_string($interval)) { + throw new \InvalidArgumentException('Invalid job in array'); + } + + $intervalObj = unserialize($interval); + + if (!($intervalObj instanceof DateInterval)) { + throw new \InvalidArgumentException('Invalid job interval in array'); + } + + $jobs[$name] = $intervalObj; + } + + return (array) $jobs; + } + + /** + * Writes error to log and returns 500 + * + * @param $file + * @param $msg + */ + private static function handleError($file, $msg) + { + error_log(sprintf('%s: %s', $file, $msg), 3, _PS_ROOT_DIR_ . '/retailcrm.log'); + http_response_code(500); + } + + /** + * Run process in background without waiting + * + * @param $cmd + * + * @return void + */ + private static function execInBackground($cmd) { + if (substr(php_uname(), 0, 7) == "Windows"){ + pclose(popen("start /B ". $cmd, "r")); + } else { + $outputPos = strpos($cmd, '>'); + + if ($outputPos !== false) { + $cmd = substr($cmd, 0, $outputPos); + } + + $command = $cmd . " > /dev/null &"; + + if (function_exists('exec')) { + exec($command); + } else if (function_exists('shell_exec')) { + shell_exec($command); + } else if (function_exists('passthru')) { + passthru($command); + } + } + } + + /** + * Executes php script in this context, without hanging up request + * + * @param string $phpScript + * @param bool $once + */ + private static function execHere($phpScript, $once = false) + { + ignore_user_abort( true); + set_time_limit(static::getTimeLimit()); + + if (version_compare(phpversion(), '7.0.16', '>=') && + function_exists('fastcgi_finish_request') + ) { + if (!headers_sent()) { + header('Expires: Thu, 19 Nov 1981 08:52:00 GMT'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + } + + fastcgi_finish_request(); + } + + if ($once) { + require_once($phpScript); + } else { + require($phpScript); + } + } + + /** + * Returns true if system support execution in background + * + * @return bool + */ + private static function canExecInBackground() + { + if (substr(php_uname(), 0, 7) == "Windows"){ + return function_exists('pclose') && function_exists('popen'); + } else { + return function_exists('exec') + || function_exists('shell_exec') + || function_exists('passthru'); + } + } + + /** + * Returns path to current PHP binary + * + * @return string + */ + private static function getPhpBinary() + { + if (defined('PHP_BINARY') && !empty(PHP_BINARY)) { + return PHP_BINARY; + } + + if (defined('PHP_BINDIR') && !empty(PHP_BINDIR)) { + $version = phpversion(); + $filePath = implode(DIRECTORY_SEPARATOR, array(PHP_BINDIR, 'php' . $version)); + + while (strlen($version) != 0 && !file_exists($filePath)) { + $dotPos = strrpos($version, '.'); + + if ($dotPos !== false) { + $version = substr($version, 0, strrpos($version, '.')); + } else { + $version = ''; + } + + $filePath = implode(DIRECTORY_SEPARATOR, array(PHP_BINDIR, 'php' . $version)); + } + + if (file_exists($filePath)) { + return $filePath; + } + } + + return 'php'; + } + + /** + * Returns script execution time limit + * + * @return int + */ + private static function getTimeLimit() + { + return 14400; + } + + /** + * Returns true if lock is present and it's not expired + * + * @return bool + */ + private static function isLocked() + { + $inProcess = (bool) Configuration::get(self::IN_PROGRESS_NAME); + $lastRan = date_create_from_format(self::DATE_FORMAT, (string) Configuration::get(self::LAST_RUN_NAME)); + $lastRanSeconds = $lastRan instanceof DateTime ? $lastRan->format('U') : time(); + + if (($lastRanSeconds + self::getTimeLimit()) < time()) { + static::unlock(); + + return false; + } + + return $inProcess; + } + + /** + * Installs lock + * + * @return bool + */ + private static function lock() + { + if (!static::isLocked()) { + Configuration::updateValue(self::IN_PROGRESS_NAME, true); + + return true; + } + + return false; + } + + /** + * Removes lock + * + * @return bool + */ + private static function unlock() + { + Configuration::updateValue(self::IN_PROGRESS_NAME, false); + + return false; + } +} + +if (PHP_SAPI == 'cli' && $argc == 3) { + try { + $jobs = JobManager::deserializeJobs($argv[1]); + $runOnce = (bool) $argv[2]; + } catch (InvalidArgumentException $exception) { + printf('Error: %s%s', $exception->getMessage(), PHP_EOL); + exit(0); + } + + JobManager::execJobs($jobs, $runOnce); +} \ No newline at end of file diff --git a/retailcrm/lib/RetailcrmApiClientV4.php b/retailcrm/lib/RetailcrmApiClientV4.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmApiClientV5.php b/retailcrm/lib/RetailcrmApiClientV5.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmApiErrors.php b/retailcrm/lib/RetailcrmApiErrors.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmApiResponse.php b/retailcrm/lib/RetailcrmApiResponse.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmCatalog.php b/retailcrm/lib/RetailcrmCatalog.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmDaemonCollector.php b/retailcrm/lib/RetailcrmDaemonCollector.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmHistory.php b/retailcrm/lib/RetailcrmHistory.php index 764388a..7aabe0c 100755 --- a/retailcrm/lib/RetailcrmHistory.php +++ b/retailcrm/lib/RetailcrmHistory.php @@ -173,6 +173,7 @@ class RetailcrmHistory $sinceId = $end['id']; $statuses = array_flip(array_filter(json_decode(Configuration::get('RETAILCRM_API_STATUS'), true))); + $cartStatus = (string)(Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_STATUS')); $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))); $deliveryDefault = json_decode(Configuration::get('RETAILCRM_API_DELIVERY_DEFAULT'), true); @@ -189,6 +190,10 @@ class RetailcrmHistory if ($responce) { $order = $responce['order']; + + if ($order['status'] == $cartStatus) { + continue; + } } else { continue; } diff --git a/retailcrm/lib/RetailcrmHistoryHelper.php b/retailcrm/lib/RetailcrmHistoryHelper.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmHttpClient.php b/retailcrm/lib/RetailcrmHttpClient.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmIcml.php b/retailcrm/lib/RetailcrmIcml.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmInventories.php b/retailcrm/lib/RetailcrmInventories.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmProxy.php b/retailcrm/lib/RetailcrmProxy.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmReferences.php b/retailcrm/lib/RetailcrmReferences.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/RetailcrmService.php b/retailcrm/lib/RetailcrmService.php old mode 100644 new mode 100755 diff --git a/retailcrm/lib/index.php b/retailcrm/lib/index.php old mode 100644 new mode 100755 diff --git a/retailcrm/logo.gif b/retailcrm/logo.gif old mode 100644 new mode 100755 diff --git a/retailcrm/logo.png b/retailcrm/logo.png old mode 100644 new mode 100755 diff --git a/retailcrm/objects.xml b/retailcrm/objects.xml old mode 100644 new mode 100755 diff --git a/retailcrm/public/css/retailcrm-upload.css b/retailcrm/public/css/retailcrm-upload.css old mode 100644 new mode 100755 diff --git a/retailcrm/public/js/exec-jobs.js b/retailcrm/public/js/exec-jobs.js new file mode 100644 index 0000000..cd2fc8d --- /dev/null +++ b/retailcrm/public/js/exec-jobs.js @@ -0,0 +1,6 @@ +(function () { + var xhr = new XMLHttpRequest(); + xhr.open('GET', '/modules/retailcrm/job/jobs.php', true); + xhr.timeout = 0; + xhr.send(null); +})(); \ No newline at end of file diff --git a/retailcrm/public/js/retailcrm-upload.js b/retailcrm/public/js/retailcrm-upload.js old mode 100644 new mode 100755 diff --git a/retailcrm/retailcrm.php b/retailcrm/retailcrm.php index b5758d5..727ad6d 100755 --- a/retailcrm/retailcrm.php +++ b/retailcrm/retailcrm.php @@ -31,6 +31,7 @@ class RetailCRM extends Module public $log; public $confirmUninstall; public $reference; + public $assetsBase; private $use_new_hooks = true; @@ -53,6 +54,12 @@ class RetailCRM extends Module $this->psVersion = Tools::substr(_PS_VERSION_, 0, 3); $this->log = _PS_ROOT_DIR_ . '/retailcrm.log'; $this->module_key = '149c765c6cddcf35e1f13ea6c71e9fa5'; + $this->assetsBase = + Tools::getShopDomainSsl(true, true) . + __PS_BASE_URI__ . + 'modules/' . + $this->name . + '/public'; if ($this->psVersion == '1.6') { $this->bootstrap = true; @@ -84,6 +91,8 @@ class RetailCRM extends Module public function hookHeader() { + $this->context->controller->addJS($this->assetsBase . '/js/exec-jobs.js'); + if (Configuration::get('RETAILCRM_DAEMON_COLLECTOR_ACTIVE') && Configuration::get('RETAILCRM_DAEMON_COLLECTOR_KEY') ) { @@ -116,6 +125,8 @@ class RetailCRM extends Module Configuration::deleteByName('RETAILCRM_LAST_SYNC') && Configuration::deleteByName('RETAILCRM_API_VERSION') && Configuration::deleteByName('RETAILCRM_LAST_CUSTOMERS_SYNC') && + Configuration::deleteByName('RETAILCRM_API_SYNCHRONIZE_CARTS') && + Configuration::deleteByName('RETAILCRM_API_SYNCHRONIZED_CART_STATUS') && Configuration::deleteByName('RETAILCRM_LAST_ORDERS_SYNC'); } @@ -144,12 +155,17 @@ class RetailCRM extends Module $collectorActive = (Tools::getValue('RETAILCRM_DAEMON_COLLECTOR_ACTIVE_1')); $collectorKey = (string)(Tools::getValue('RETAILCRM_DAEMON_COLLECTOR_KEY')); $clientId = Configuration::get('RETAILCRM_CLIENT_ID'); + $synchronizeCartsActive = (Tools::getValue('RETAILCRM_API_SYNCHRONIZE_CARTS_1')); + $synchronizedCartStatus = (string)(Tools::getValue('RETAILCRM_API_SYNCHRONIZED_CART_STATUS')); $settings = array( 'address' => $address, 'token' => $token, 'version' => $version, - 'clientId' => $clientId + 'clientId' => $clientId, + 'status' => $status, + 'statusExport' => $statusExport, + 'synchronizeCartStatus' => $synchronizedCartStatus ); $output .= $this->validateForm($settings, $output); @@ -166,6 +182,8 @@ class RetailCRM extends Module Configuration::updateValue('RETAILCRM_STATUS_EXPORT', $statusExport); Configuration::updateValue('RETAILCRM_DAEMON_COLLECTOR_ACTIVE', $collectorActive); Configuration::updateValue('RETAILCRM_DAEMON_COLLECTOR_KEY', $collectorKey); + Configuration::updateValue('RETAILCRM_API_SYNCHRONIZE_CARTS', $synchronizeCartsActive); + Configuration::updateValue('RETAILCRM_API_SYNCHRONIZED_CART_STATUS', $synchronizedCartStatus); $output .= $this->displayConfirmation($this->l('Settings updated')); } @@ -189,14 +207,9 @@ class RetailCRM extends Module "$address/admin/settings#t-main" ); - $assetsBase = - Tools::getShopDomainSsl(true, true) . - __PS_BASE_URI__ . - 'modules/' . - $this->name . - '/public'; - $this->context->controller->addCSS($assetsBase . '/css/retailcrm-upload.css'); - $this->context->controller->addJS($assetsBase . '/js/retailcrm-upload.js'); + $this->context->controller->addCSS($this->assetsBase . '/css/retailcrm-upload.css'); + $this->context->controller->addJS($this->assetsBase . '/js/retailcrm-upload.js'); + $this->context->controller->addJS($this->assetsBase . '/js/exec-jobs.js'); $this->display(__FILE__, 'retailcrm.tpl'); return $output . $this->displaySettingsForm() . $this->displayUploadOrdersForm(); @@ -278,7 +291,45 @@ class RetailCRM extends Module */ public static function verifyDate($date, $format = "Y-m-d") { - return (bool)date_create_from_format($format, $date); + return $date !== "0000-00-00" && (bool)date_create_from_format($format, $date); + } + + /** + * Build array with order data for retailCRM from PrestaShop cart data + * + * @param Cart $cart Cart with data + * @param string $externalId External ID for order + * @param string $paymentType Payment type (buildCrmOrder requires it) + * @param string $status Status for order + * + * @return array + */ + public static function buildCrmOrderFromCart(Cart $cart = null, $externalId = '', $paymentType = '', $status = '') + { + if (empty($cart) || empty($paymentType) || empty($status)) { + return array(); + } + + $order = new Order(); + $order->id_cart = $cart->id; + $order->id_customer = $cart->id_customer; + $order->total_discounts = 0; + $order->module = $paymentType; + $order->payment = $paymentType; + $orderData = static::buildCrmOrder( + $order, + new Customer($cart->id_customer), + $cart, + false, + true, + true + ); + $orderData['externalId'] = $externalId; + $orderData['status'] = $status; + + unset($orderData['payments']); + + return $orderData; } /** @@ -289,6 +340,7 @@ class RetailCRM extends Module * @param Cart $orderCart Cart for provided order. Optional * @param bool $isStatusExport Use status for export * @param bool $preferCustomerAddress Use customer address even if delivery address is provided + * @param bool $dataFromCart Prefer data from cart * * @return array retailCRM order data */ @@ -297,7 +349,8 @@ class RetailCRM extends Module Customer $customer = null, Cart $orderCart = null, $isStatusExport = false, - $preferCustomerAddress = false + $preferCustomerAddress = false, + $dataFromCart = false ) { $apiVersion = Configuration::get('RETAILCRM_API_VERSION'); $statusExport = Configuration::get('RETAILCRM_STATUS_EXPORT'); @@ -357,6 +410,10 @@ class RetailCRM extends Module return $v->id_customer == $customer->id; } ); + + if (is_array($address) && count($address) == 1) { + $address = reset($address); + } } $address = static::addressParse($address); @@ -388,16 +445,34 @@ class RetailCRM extends Module $crmOrder['payments'] = array(); } - if (array_key_exists($order->id_carrier, $delivery) && !empty($delivery[$order->id_carrier])) { - $crmOrder['delivery']['code'] = $delivery[$order->id_carrier]; + $idCarrier = $dataFromCart ? $cart->id_carrier : $order->id_carrier; + + if (empty($idCarrier)) { + $idCarrier = $order->id_carrier; + $totalShipping = $order->total_shipping; + $totalShippingWithoutTax = $order->total_shipping_tax_excl; + } else { + $totalShipping = $dataFromCart ? $cart->getCarrierCost($idCarrier) : $order->total_shipping; + + if (!empty($totalShipping) && $totalShipping != 0) { + $totalShippingWithoutTax = $dataFromCart + ? $totalShipping - $cart->getCarrierCost($idCarrier, false) + : $order->total_shipping_tax_excl; + } else { + $totalShippingWithoutTax = $order->total_shipping_tax_excl; + } } - if (isset($order->total_shipping) && ((int) $order->total_shipping) > 0) { - $crmOrder['delivery']['cost'] = round($order->total_shipping, 2); + if (array_key_exists($idCarrier, $delivery) && !empty($delivery[$idCarrier])) { + $crmOrder['delivery']['code'] = $delivery[$idCarrier]; } - if (isset($order->total_shipping_tax_excl) && $order->total_shipping_tax_excl > 0) { - $crmOrder['delivery']['netCost'] = round($order->total_shipping_tax_excl, 2); + if (isset($totalShipping) && ((int) $totalShipping) > 0) { + $crmOrder['delivery']['cost'] = round($totalShipping, 2); + } + + if (isset($totalShippingWithoutTax) && $totalShippingWithoutTax > 0) { + $crmOrder['delivery']['netCost'] = round($totalShippingWithoutTax, 2); } $comment = $order->getFirstMessage(); @@ -406,7 +481,30 @@ class RetailCRM extends Module $crmOrder['customerComment'] = $comment; } - foreach ($order->getProducts() as $product) { + if ($dataFromCart) { + $productStore = $cart; + $converter = function ($product) { + $product['product_attribute_id'] = $product['id_product_attribute']; + $product['product_quantity'] = $product['cart_quantity']; + $product['product_id'] = $product['id_product']; + $product['id_order_detail'] = $product['id_product']; + $product['product_name'] = $product['name']; + $product['product_price'] = $product['price']; + $product['purchase_supplier_price'] = $product['price']; + $product['product_price_wt'] = $product['price_wt']; + + return $product; + }; + } else { + $productStore = $order; + $converter = function ($product) { + return $product; + }; + } + + foreach ($productStore->getProducts() as $productData) { + $product = $converter($productData); + if (isset($product['product_attribute_id']) && $product['product_attribute_id'] > 0) { $productId = $product['product_id'] . '#' . $product['product_attribute_id']; } else { @@ -605,6 +703,43 @@ class RetailCRM extends Module ); if ($this->api) { + /* + * Synchronize carts form + */ + if ($this->use_new_hooks) { + $fields_form[]['form'] = array( + 'legend' => array( + 'title' => $this->l('Synchronization of buyer carts'), + ), + 'input' => array( + array( + 'type' => 'checkbox', + 'label' => $this->l('Create orders for abandoned carts of buyers'), + 'name' => 'RETAILCRM_API_SYNCHRONIZE_CARTS', + 'values' => array( + 'query' => array( + array( + 'id_option' => 1, + ) + ), + 'id' => 'id_option', + 'name' => 'name' + ) + ), + array( + 'type' => 'select', + 'name' => 'RETAILCRM_API_SYNCHRONIZED_CART_STATUS', + 'label' => $this->l('Order status for abandoned carts of buyers'), + 'options' => array( + 'query' => $this->reference->getStatuseDefaultExport(), + 'id' => 'id_option', + 'name' => 'name' + ) + ) + ) + ); + } + /* * Delivery */ @@ -698,6 +833,8 @@ class RetailCRM extends Module $helper->fields_value['RETAILCRM_API_VERSION'] = Configuration::get('RETAILCRM_API_VERSION'); $helper->fields_value['RETAILCRM_STATUS_EXPORT'] = Configuration::get('RETAILCRM_STATUS_EXPORT'); $helper->fields_value['RETAILCRM_DAEMON_COLLECTOR_ACTIVE_1'] = Configuration::get('RETAILCRM_DAEMON_COLLECTOR_ACTIVE'); + $helper->fields_value['RETAILCRM_API_SYNCHRONIZE_CARTS_1'] = Configuration::get('RETAILCRM_API_SYNCHRONIZE_CARTS'); + $helper->fields_value['RETAILCRM_API_SYNCHRONIZED_CART_STATUS'] = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_STATUS'); $helper->fields_value['RETAILCRM_DAEMON_COLLECTOR_KEY'] = Configuration::get('RETAILCRM_DAEMON_COLLECTOR_KEY'); $deliverySettings = Configuration::get('RETAILCRM_API_DELIVERY'); @@ -751,6 +888,17 @@ class RetailCRM extends Module } } + if ($this->use_new_hooks) { + $synchronizedCartsStatusDefault = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_STATUS'); + if (isset($synchronizedCartsStatusDefault) && $synchronizedCartsStatusDefault != '') { + $synchronizedCartsStatus = json_decode($synchronizedCartsStatusDefault); + if ($synchronizedCartsStatus) { + $name = 'RETAILCRM_API_SYNCHRONIZED_CART_STATUS'; + $helper->fields_value[$name] = $synchronizedCartsStatus; + } + } + } + return $helper->generateForm($fields_form); } @@ -1036,9 +1184,15 @@ class RetailCRM extends Module if (isset($params['orderStatus'])) { $cart = $params['cart']; - $order = static::buildCrmOrder($params['order'], $params['customer'], $params['cart'], false); + $response = $this->api->ordersGet(self::getCartOrderExternalId($cart)); + $order = static::buildCrmOrder($params['order'], $params['customer'], $cart, false); - $this->api->ordersCreate($order); + if (!empty($response) && isset($response['order'])) { + $order['id'] = $response['order']['id']; + $this->api->ordersEdit($order, 'id'); + } else { + $this->api->ordersCreate($order); + } return true; @@ -1159,6 +1313,15 @@ class RetailCRM extends Module return false; } + private function validateStatuses($statuses, $statusExport, $cartStatus) + { + if ($cartStatus == $statusExport || (stripos($statuses, $cartStatus) !== false)) { + return false; + } + + return true; + } + private function validateForm($settings, $output) { if (!$this->validateCrmAddress($settings['address']) || !Validate::isGenericName($settings['address'])) { @@ -1167,11 +1330,29 @@ class RetailCRM extends Module $output .= $this->displayError($this->l('Invalid or empty crm api token')); } elseif (!$this->validateApiVersion($settings)) { $output .= $this->displayError($this->l('The selected version of the API is unavailable')); + } elseif (!$this->validateStatuses( + $settings['status'], + $settings['statusExport'], + $settings['synchronizeCartStatus']) + ) { + $output .= $this->displayError($this->l('Order status for abandoned carts should not be used in other settings')); } return $output; } + /** + * Returns externalId for order + * + * @param Cart $cart + * + * @return string + */ + public static function getCartOrderExternalId(Cart $cart) + { + return sprintf('pscart_%d', $cart->id); + } + /** * Activate/deactivate module in marketplace retailCRM * diff --git a/retailcrm/translations/es.php b/retailcrm/translations/es.php old mode 100644 new mode 100755 index 6db2eca..43e037f --- a/retailcrm/translations/es.php +++ b/retailcrm/translations/es.php @@ -61,4 +61,8 @@ $_MODULE['<{retailcrm}prestashop>retailcrm_acfa058ec9e6e4745eddc0cae3f0f881'] = $_MODULE['<{retailcrm}prestashop>retailcrm_91412465ea9169dfd901dd5e7c96dd99'] = 'Exportar'; $_MODULE['<{retailcrm}prestashop>retailcrm_6bd461d1fc51b3294c6513cecc24758d'] = 'Los pedidos han sido cargados con éxito'; $_MODULE['<{retailcrm}prestashop>retailcrm_3518f7b8d79f91da4c91772b4c46db94'] = 'No se han podido cargar algunos pedidos'; -$_MODULE['<{retailcrm}prestashop>retailcrm_9a7fc06b4b2359f1f26f75fbbe27a3e8'] = 'No todos los pedidos se han cargado con existo'; \ No newline at end of file +$_MODULE['<{retailcrm}prestashop>retailcrm_9a7fc06b4b2359f1f26f75fbbe27a3e8'] = 'No todos los pedidos se han cargado con existo'; +$_MODULE['<{retailcrm}prestashop>retailcrm_917afe348e09163269225a89a825e634'] = 'Sincronización de carritos de compradores'; +$_MODULE['<{retailcrm}prestashop>retailcrm_d8e002d770b6f98af7b7ae9a0e5acfe9'] = 'Crear pedidos para carritos abandonados de compradores'; +$_MODULE['<{retailcrm}prestashop>retailcrm_35b5a9139a54caeb925556ceb2c38086'] = 'Estado del pedido para carritos abandonados de compradores'; +$_MODULE['<{retailcrm}prestashop>retailcrm_b9c4e8fe56eabcc4c7913ebb2f8eb388'] = 'Estado del pedido para carritos abandonados no debe ser utilizado en otros ajustes'; \ No newline at end of file diff --git a/retailcrm/translations/index.php b/retailcrm/translations/index.php old mode 100644 new mode 100755 diff --git a/retailcrm/translations/ru.php b/retailcrm/translations/ru.php old mode 100644 new mode 100755 index 1f0def2..a2136b1 --- a/retailcrm/translations/ru.php +++ b/retailcrm/translations/ru.php @@ -61,4 +61,8 @@ $_MODULE['<{retailcrm}prestashop>retailcrm_acfa058ec9e6e4745eddc0cae3f0f881'] = $_MODULE['<{retailcrm}prestashop>retailcrm_91412465ea9169dfd901dd5e7c96dd99'] = 'Выгрузить'; $_MODULE['<{retailcrm}prestashop>retailcrm_6bd461d1fc51b3294c6513cecc24758d'] = 'Все заказы успешно загружены'; $_MODULE['<{retailcrm}prestashop>retailcrm_3518f7b8d79f91da4c91772b4c46db94'] = 'Некоторые заказы не удалось загрузить'; -$_MODULE['<{retailcrm}prestashop>retailcrm_9a7fc06b4b2359f1f26f75fbbe27a3e8'] = 'Не все заказы загружены успешно'; \ No newline at end of file +$_MODULE['<{retailcrm}prestashop>retailcrm_9a7fc06b4b2359f1f26f75fbbe27a3e8'] = 'Не все заказы загружены успешно'; +$_MODULE['<{retailcrm}prestashop>retailcrm_917afe348e09163269225a89a825e634'] = 'Синхронизация корзин покупателей'; +$_MODULE['<{retailcrm}prestashop>retailcrm_d8e002d770b6f98af7b7ae9a0e5acfe9'] = 'Создавать заказы для брошенных корзин покупателей'; +$_MODULE['<{retailcrm}prestashop>retailcrm_35b5a9139a54caeb925556ceb2c38086'] = 'Статус заказа для брошенных корзин покупателей'; +$_MODULE['<{retailcrm}prestashop>retailcrm_b9c4e8fe56eabcc4c7913ebb2f8eb388'] = 'Статус заказа для брошенных корзин не должен использоваться в других настройках'; \ No newline at end of file