[feature] send abandoned carts to retailCRM as orders

This commit is contained in:
Pavel 2019-10-23 17:52:23 +03:00
parent 5ca68822e7
commit ec34a62f9f
39 changed files with 731 additions and 22 deletions

0
retailcrm/bootstrap.php Normal file → Executable file
View file

13
retailcrm/config_ru.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<module>
<name>retailcrm</name>
<displayName><![CDATA[RetailCRM]]></displayName>
<version><![CDATA[2.3.4]]></version>
<description><![CDATA[Модуль интеграции с RetailCRM]]></description>
<author><![CDATA[Retail Driver LCC]]></author>
<tab><![CDATA[export]]></tab>
<confirmUninstall><![CDATA[Вы уверены, что хотите удалить модуль?]]></confirmUninstall>
<is_configurable>1</is_configurable>
<need_instance>1</need_instance>
<limited_countries></limited_countries>
</module>

0
retailcrm/index.php Normal file → Executable file
View file

View file

@ -0,0 +1,85 @@
<?php
/**
* @author Retail Driver LCC
* @copyright RetailCRM
* @license GPL
* @version 2.2.9
* @link https://retailcrm.ru
*
*/
$_SERVER['HTTPS'] = 1;
require_once(dirname(__FILE__) . '/../../../config/config.inc.php');
require_once(dirname(__FILE__) . '/../../../init.php');
require_once(dirname(__FILE__) . '/../bootstrap.php');
if (empty(Configuration::get('RETAILCRM_API_SYNCHRONIZE_CARTS'))) {
return;
}
$apiUrl = Configuration::get('RETAILCRM_ADDRESS');
$apiKey = Configuration::get('RETAILCRM_API_TOKEN');
$apiVersion = Configuration::get('RETAILCRM_API_VERSION');
$api = null;
if (!empty($apiUrl) && !empty($apiKey)) {
$api = new RetailcrmProxy($apiUrl, $apiKey, _PS_ROOT_DIR_ . '/retailcrm.log', $apiVersion);
} else {
error_log('abandonedCarts: set api key & url first', 3, _PS_ROOT_DIR_ . '/retailcrm.log');
return;
}
$rows = Db::getInstance()->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();
}
}
}

0
retailcrm/job/export.php Normal file → Executable file
View file

0
retailcrm/job/icml.php Normal file → Executable file
View file

0
retailcrm/job/index.php Normal file → Executable file
View file

0
retailcrm/job/inventories.php Normal file → Executable file
View file

18
retailcrm/job/jobs.php Normal file
View file

@ -0,0 +1,18 @@
<?php
/**
* @author Retail Driver LCC
* @copyright RetailCRM
* @license GPL
* @version 2.2.9
* @link https://retailcrm.ru
*/
require_once(dirname(__FILE__) . '/../../../config/config.inc.php');
require_once(dirname(__FILE__) . '/../../../init.php');
require_once(dirname(__FILE__) . '/../bootstrap.php');
JobManager::startJobs(
array(
'abandonedCarts' => DateInterval::createFromDateString('10 seconds')
),
true
);

0
retailcrm/job/missing.php Normal file → Executable file
View file

0
retailcrm/job/sync.php Normal file → Executable file
View file

0
retailcrm/lib/CurlException.php Normal file → Executable file
View file

0
retailcrm/lib/InvalidJsonException.php Normal file → Executable file
View file

View file

@ -0,0 +1,393 @@
<?php
/**
* @author Retail Driver LCC
* @copyright RetailCRM
* @license GPL
* @version 2.2.11
* @link https://retailcrm.ru
*
*/
if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) {
date_default_timezone_set(@date_default_timezone_get());
}
require_once(dirname(__FILE__) . '/../../../config/config.inc.php');
require_once(dirname(__FILE__) . '/../../../init.php');
require_once(dirname(__FILE__) . '/../bootstrap.php');
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Class JobManager
*
* @author Retail Driver LCC
* @license GPL
* @link https://retailcrm.ru
*/
class JobManager
{
const DATE_FORMAT = 'Y.m.d H:i:s';
const LAST_RUN_NAME = 'RETAILCRM_LAST_RUN';
const IN_PROGRESS_NAME = 'RETAILCRM_JOBS_IN_PROGRESS';
/**
* @var resource $lock
*/
static $lock;
public static function startJobs($jobs = array(), $runOnceInContext = false)
{
$inBackground = static::canExecInBackground();
if ($inBackground) {
$cmdline = sprintf('%s "%s" %b', __FILE__, static::serializeJobs($jobs), $runOnceInContext);
static::execPHP($cmdline, true, $runOnceInContext);
} else {
static::execJobs($jobs, $runOnceInContext);
}
}
/**
* Run scheduled jobs with request
*
* @param array $jobs
* @param bool $runOnceInContext
*/
public static function execJobs($jobs = array(), $runOnceInContext = false)
{
$current = date_create('now');
$lastRun = date_create_from_format(self::DATE_FORMAT, (string) Configuration::get(self::LAST_RUN_NAME));
if ($lastRun === false) {
$lastRun = $current;
}
if (!static::lock()) {
return;
}
Configuration::updateValue(self::LAST_RUN_NAME, date_format($current, self::DATE_FORMAT));
foreach ($jobs as $job => $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);
}

0
retailcrm/lib/RetailcrmApiClientV4.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmApiClientV5.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmApiErrors.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmApiResponse.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmCatalog.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmDaemonCollector.php Normal file → Executable file
View file

View file

@ -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;
}

0
retailcrm/lib/RetailcrmHistoryHelper.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmHttpClient.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmIcml.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmInventories.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmProxy.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmReferences.php Normal file → Executable file
View file

0
retailcrm/lib/RetailcrmService.php Normal file → Executable file
View file

0
retailcrm/lib/index.php Normal file → Executable file
View file

0
retailcrm/logo.gif Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 306 B

0
retailcrm/logo.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

0
retailcrm/objects.xml Normal file → Executable file
View file

0
retailcrm/public/css/retailcrm-upload.css Normal file → Executable file
View file

View file

@ -0,0 +1,6 @@
(function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/modules/retailcrm/job/jobs.php', true);
xhr.timeout = 0;
xhr.send(null);
})();

0
retailcrm/public/js/retailcrm-upload.js Normal file → Executable file
View file

View file

@ -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
"<a target=\"_blank\" href=\"$address/admin/settings#t-main\">$address/admin/settings#t-main</a>"
);
$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
*

6
retailcrm/translations/es.php Normal file → Executable file
View file

@ -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';
$_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';

0
retailcrm/translations/index.php Normal file → Executable file
View file

6
retailcrm/translations/ru.php Normal file → Executable file
View file

@ -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'] = 'Не все заказы загружены успешно';
$_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'] = 'Статус заказа для брошенных корзин не должен использоваться в других настройках';