mirror of
https://github.com/retailcrm/prestashop-module.git
synced 2025-03-02 19:33:14 +03:00
Merge pull request #51 from Neur0toxine/master
[feature] send abandoned carts to retailCRM as orders
This commit is contained in:
commit
f6f8d1302b
@ -1,3 +1,6 @@
|
||||
## v2.4.0
|
||||
* Добавлена возможность выгрузки брошенных корзин
|
||||
|
||||
## v2.3.4
|
||||
* Добавлена поддержка передачи одинаковых товаров в заказе как разных товарных позиций
|
||||
|
||||
|
0
retailcrm/bootstrap.php
Normal file → Executable file
0
retailcrm/bootstrap.php
Normal file → Executable file
0
retailcrm/index.php
Normal file → Executable file
0
retailcrm/index.php
Normal file → Executable file
92
retailcrm/job/abandonedCarts.php
Executable file
92
retailcrm/job/abandonedCarts.php
Executable file
@ -0,0 +1,92 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
$time = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_DELAY');
|
||||
|
||||
if (is_numeric($time) && $time > 0) {
|
||||
$time = intval($time);
|
||||
} else {
|
||||
$time = 0;
|
||||
}
|
||||
|
||||
$now = new DateTime();
|
||||
$sql = 'SELECT c.id_cart, c.date_upd
|
||||
FROM '._DB_PREFIX_.'cart AS c
|
||||
WHERE id_customer != 0
|
||||
AND TIME_TO_SEC(TIMEDIFF(\''.pSQL($now->format('Y-m-d H:i:s')).'\', date_upd)) >= '.$time.'
|
||||
AND c.id_cart NOT IN(SELECT id_cart from '._DB_PREFIX_.'orders);';
|
||||
$rows = Db::getInstance()->executeS($sql);
|
||||
$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
0
retailcrm/job/export.php
Normal file → Executable file
0
retailcrm/job/icml.php
Normal file → Executable file
0
retailcrm/job/icml.php
Normal file → Executable file
0
retailcrm/job/index.php
Normal file → Executable file
0
retailcrm/job/index.php
Normal file → Executable file
0
retailcrm/job/inventories.php
Normal file → Executable file
0
retailcrm/job/inventories.php
Normal file → Executable file
18
retailcrm/job/jobs.php
Executable file
18
retailcrm/job/jobs.php
Executable 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' => null
|
||||
),
|
||||
true
|
||||
);
|
0
retailcrm/job/missing.php
Normal file → Executable file
0
retailcrm/job/missing.php
Normal file → Executable file
0
retailcrm/job/sync.php
Normal file → Executable file
0
retailcrm/job/sync.php
Normal file → Executable file
0
retailcrm/lib/CurlException.php
Normal file → Executable file
0
retailcrm/lib/CurlException.php
Normal file → Executable file
0
retailcrm/lib/InvalidJsonException.php
Normal file → Executable file
0
retailcrm/lib/InvalidJsonException.php
Normal file → Executable file
444
retailcrm/lib/JobManager.php
Executable file
444
retailcrm/lib/JobManager.php
Executable file
@ -0,0 +1,444 @@
|
||||
<?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 LAST_RUN_NAME = 'RETAILCRM_LAST_RUN';
|
||||
const IN_PROGRESS_NAME = 'RETAILCRM_JOBS_IN_PROGRESS';
|
||||
|
||||
/**
|
||||
* @var resource $lock
|
||||
*/
|
||||
static $lock;
|
||||
|
||||
/**
|
||||
* Entry point for all jobs.
|
||||
* Jobs must be passed in this format:
|
||||
* JobManager::startJobs(
|
||||
* array(
|
||||
* 'jobName' => DateInterval::createFromDateString('1 hour')
|
||||
* ),
|
||||
* true
|
||||
* );
|
||||
*
|
||||
* File `jobName.php` must exist in retailcrm/job and must contain everything to run job.
|
||||
* Throwed errors will be logged in <prestashop directory>/retailcrm.log
|
||||
* DateInterval must be positive. Pass `null` instead of DateInterval to remove
|
||||
* any delay - in other words, jobs without interval will be executed every time.
|
||||
*
|
||||
* @param array $jobs
|
||||
* @param bool $runOnceInContext
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function execJobs($jobs = array(), $runOnceInContext = false)
|
||||
{
|
||||
$current = date_create('now');
|
||||
$lastRuns = static::getLastRuns();
|
||||
|
||||
if (!static::lock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($jobs as $job => $diff) {
|
||||
try {
|
||||
if (isset($lastRuns[$job]) && $lastRuns[$job] instanceof DateTime) {
|
||||
$shouldRunAt = clone $lastRuns[$job];
|
||||
} else {
|
||||
$shouldRunAt = new DateTime();
|
||||
}
|
||||
|
||||
if ($diff instanceof DateInterval) {
|
||||
$shouldRunAt->add($diff);
|
||||
}
|
||||
|
||||
if (!isset($shouldRunAt) || $shouldRunAt <= $current) {
|
||||
JobManager::runJob($job, $runOnceInContext);
|
||||
$lastRuns[$job] = new DateTime();
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
static::handleError($exception->getFile(), $exception->getMessage());
|
||||
} catch (\Throwable $throwable) {
|
||||
static::handleError($throwable->getFile(), $throwable->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static::setLastRuns($lastRuns);
|
||||
static::unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts jobs last runs from db
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function getLastRuns()
|
||||
{
|
||||
$lastRuns = json_decode((string) Configuration::get(self::LAST_RUN_NAME), true);
|
||||
|
||||
if (json_last_error() != JSON_ERROR_NONE) {
|
||||
$lastRuns = [];
|
||||
} else {
|
||||
foreach ($lastRuns as $job => $ran) {
|
||||
$lastRan = DateTime::createFromFormat(DATE_RFC3339, $ran);
|
||||
|
||||
if ($lastRan instanceof DateTime) {
|
||||
$lastRuns[$job] = $lastRan;
|
||||
} else {
|
||||
$lastRuns[$job] = new DateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (array) $lastRuns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates jobs last runs in db
|
||||
*
|
||||
* @param array $lastRuns
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function setLastRuns($lastRuns = [])
|
||||
{
|
||||
$now = new DateTime();
|
||||
|
||||
if (!is_array($lastRuns)) {
|
||||
$lastRuns = [];
|
||||
}
|
||||
|
||||
foreach ($lastRuns as $job => $ran) {
|
||||
if ($ran instanceof DateTime) {
|
||||
$lastRuns[$job] = $ran->format(DATE_RFC3339);
|
||||
} else {
|
||||
$lastRuns[$job] = $now->format(DATE_RFC3339);
|
||||
}
|
||||
}
|
||||
|
||||
Configuration::updateValue(self::LAST_RUN_NAME, (string) json_encode($lastRuns));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(DATE_RFC3339, (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
0
retailcrm/lib/RetailcrmApiClientV4.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiClientV5.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiClientV5.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiErrors.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiErrors.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiResponse.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmApiResponse.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmCatalog.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmCatalog.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmDaemonCollector.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmDaemonCollector.php
Normal file → Executable 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
0
retailcrm/lib/RetailcrmHistoryHelper.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmHttpClient.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmHttpClient.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmIcml.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmIcml.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmInventories.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmInventories.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmProxy.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmProxy.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmReferences.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmReferences.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmService.php
Normal file → Executable file
0
retailcrm/lib/RetailcrmService.php
Normal file → Executable file
0
retailcrm/lib/index.php
Normal file → Executable file
0
retailcrm/lib/index.php
Normal file → Executable file
0
retailcrm/logo.gif
Normal file → Executable file
0
retailcrm/logo.gif
Normal file → Executable file
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 306 B |
0
retailcrm/logo.png
Normal file → Executable file
0
retailcrm/logo.png
Normal file → Executable file
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
0
retailcrm/objects.xml
Normal file → Executable file
0
retailcrm/objects.xml
Normal file → Executable file
1
retailcrm/public/css/.gitignore
vendored
Normal file
1
retailcrm/public/css/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.min.css
|
0
retailcrm/public/css/retailcrm-upload.css
Normal file → Executable file
0
retailcrm/public/css/retailcrm-upload.css
Normal file → Executable file
1
retailcrm/public/js/.gitignore
vendored
Normal file
1
retailcrm/public/js/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.min.js
|
6
retailcrm/public/js/exec-jobs.js
Executable file
6
retailcrm/public/js/exec-jobs.js
Executable 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
0
retailcrm/public/js/retailcrm-upload.js
Normal file → Executable file
@ -31,6 +31,7 @@ class RetailCRM extends Module
|
||||
public $log;
|
||||
public $confirmUninstall;
|
||||
public $reference;
|
||||
public $assetsBase;
|
||||
|
||||
private $use_new_hooks = true;
|
||||
|
||||
@ -38,7 +39,7 @@ class RetailCRM extends Module
|
||||
{
|
||||
$this->name = 'retailcrm';
|
||||
$this->tab = 'export';
|
||||
$this->version = '2.3.4';
|
||||
$this->version = '2.4.0';
|
||||
$this->author = 'Retail Driver LCC';
|
||||
$this->displayName = $this->l('RetailCRM');
|
||||
$this->description = $this->l('Integration module for RetailCRM');
|
||||
@ -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,9 @@ 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_API_SYNCHRONIZED_CART_DELAY') &&
|
||||
Configuration::deleteByName('RETAILCRM_LAST_ORDERS_SYNC');
|
||||
}
|
||||
|
||||
@ -144,12 +156,18 @@ 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'));
|
||||
$synchronizedCartDelay = (string)(Tools::getValue('RETAILCRM_API_SYNCHRONIZED_CART_DELAY'));
|
||||
|
||||
$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 +184,9 @@ 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);
|
||||
Configuration::updateValue('RETAILCRM_API_SYNCHRONIZED_CART_DELAY', $synchronizedCartDelay);
|
||||
|
||||
$output .= $this->displayConfirmation($this->l('Settings updated'));
|
||||
}
|
||||
@ -189,14 +210,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 +294,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 +343,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 +352,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 +413,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 +448,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 +484,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 +706,84 @@ class RetailCRM extends Module
|
||||
);
|
||||
|
||||
if ($this->api) {
|
||||
/*
|
||||
* Synchronize carts form
|
||||
*/
|
||||
$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'
|
||||
)
|
||||
),
|
||||
array(
|
||||
'type' => 'select',
|
||||
'name' => 'RETAILCRM_API_SYNCHRONIZED_CART_DELAY',
|
||||
'label' => $this->l('Upload abandoned carts'),
|
||||
'options' => array(
|
||||
'query' => array(
|
||||
array(
|
||||
'id_option' => '0',
|
||||
'name' => $this->l('Immediately')
|
||||
),
|
||||
array(
|
||||
'id_option' => '60',
|
||||
'name' => $this->l('After 1 minute')
|
||||
),
|
||||
array(
|
||||
'id_option' => '300',
|
||||
'name' => $this->l('After 5 minutes')
|
||||
),
|
||||
array(
|
||||
'id_option' => '600',
|
||||
'name' => $this->l('After 10 minutes')
|
||||
),
|
||||
array(
|
||||
'id_option' => '900',
|
||||
'name' => $this->l('After 15 minutes')
|
||||
),
|
||||
array(
|
||||
'id_option' => '1800',
|
||||
'name' => $this->l('After 30 minutes')
|
||||
),
|
||||
array(
|
||||
'id_option' => '2700',
|
||||
'name' => $this->l('After 45 minute')
|
||||
),
|
||||
array(
|
||||
'id_option' => '3600',
|
||||
'name' => $this->l('After 1 hour')
|
||||
),
|
||||
),
|
||||
'id' => 'id_option',
|
||||
'name' => 'name'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Delivery
|
||||
*/
|
||||
@ -698,6 +877,9 @@ 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_API_SYNCHRONIZED_CART_DELAY'] = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_DELAY');
|
||||
$helper->fields_value['RETAILCRM_DAEMON_COLLECTOR_KEY'] = Configuration::get('RETAILCRM_DAEMON_COLLECTOR_KEY');
|
||||
|
||||
$deliverySettings = Configuration::get('RETAILCRM_API_DELIVERY');
|
||||
@ -751,6 +933,24 @@ class RetailCRM extends Module
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
$synchronizedCartsDelayDefault = Configuration::get('RETAILCRM_API_SYNCHRONIZED_CART_DELAY');
|
||||
if (isset($synchronizedCartsDelayDefault) && $synchronizedCartsDelayDefault != '') {
|
||||
$synchronizedCartsDelay = json_decode($synchronizedCartsDelayDefault);
|
||||
if ($synchronizedCartsDelay) {
|
||||
$name = 'RETAILCRM_API_SYNCHRONIZED_CART_DELAY';
|
||||
$helper->fields_value[$name] = $synchronizedCartsDelay;
|
||||
}
|
||||
}
|
||||
|
||||
return $helper->generateForm($fields_form);
|
||||
}
|
||||
|
||||
@ -1036,9 +1236,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 +1365,15 @@ class RetailCRM extends Module
|
||||
return false;
|
||||
}
|
||||
|
||||
private function validateStatuses($statuses, $statusExport, $cartStatus)
|
||||
{
|
||||
if ($cartStatus != '' && ($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 +1382,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
|
||||
*
|
||||
|
15
retailcrm/translations/es.php
Normal file → Executable file
15
retailcrm/translations/es.php
Normal file → Executable file
@ -61,4 +61,17 @@ $_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';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_a0d135501a738c3c98de385dc28cda61'] = 'Cargar carritos abandonados';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_fd83e0ccb3e6312a62f888dd496dd0a5'] = 'Instantáneamente';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_6c70ac5cc9fe8dff4f3bfe765dfe8798'] = 'Tras 1 minuto';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_be76333ef50b23b6337d263fc87fe11a'] = 'Tras 5 minutos';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_6e74495fad332de9a8207b5416de3cce'] = 'Tras 10 minutos';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_d5bb7c2cb1565fb1568924b01847b330'] = 'Tras 15 minutos';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_9d3095e54f694bb41ef4a3e62ed90e7a'] = 'Tras 30 minutos';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_dfb403fd86851c7d9f97706dff5a2327'] = 'Tras 45 minutos';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_4b5e6470d5d85448fcd89c828352d25e'] = 'Tras 1 hora';
|
0
retailcrm/translations/index.php
Normal file → Executable file
0
retailcrm/translations/index.php
Normal file → Executable file
15
retailcrm/translations/ru.php
Normal file → Executable file
15
retailcrm/translations/ru.php
Normal file → Executable file
@ -61,4 +61,17 @@ $_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'] = 'Статус заказа для брошенных корзин не должен использоваться в других настройках';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_a0d135501a738c3c98de385dc28cda61'] = 'Выгружать брошенные корзины';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_fd83e0ccb3e6312a62f888dd496dd0a5'] = 'Мгновенно';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_6c70ac5cc9fe8dff4f3bfe765dfe8798'] = 'Через 1 минуту';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_be76333ef50b23b6337d263fc87fe11a'] = 'Через 5 минут';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_6e74495fad332de9a8207b5416de3cce'] = 'Через 10 минут';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_d5bb7c2cb1565fb1568924b01847b330'] = 'Через 15 минут';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_9d3095e54f694bb41ef4a3e62ed90e7a'] = 'Через 30 минут';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_dfb403fd86851c7d9f97706dff5a2327'] = 'Через 45 минут';
|
||||
$_MODULE['<{retailcrm}prestashop>retailcrm_4b5e6470d5d85448fcd89c828352d25e'] = 'Через 1 час';
|
Loading…
x
Reference in New Issue
Block a user