diff --git a/retailcrm/lib/RetailcrmExport.php b/retailcrm/lib/RetailcrmExport.php
index 8d2d3b0..4745e7f 100644
--- a/retailcrm/lib/RetailcrmExport.php
+++ b/retailcrm/lib/RetailcrmExport.php
@@ -38,6 +38,35 @@
*/
class RetailcrmExport
{
+ const RETAILCRM_EXPORT_ORDERS_STEP_SIZE_CLI = 5000;
+ const RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE_CLI = 5000;
+ const RETAILCRM_EXPORT_ORDERS_STEP_SIZE_WEB = 50;
+ const RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE_WEB = 300;
+
+ /**
+ * @var \RetailcrmProxy|\RetailcrmApiClientV5
+ */
+ static $api;
+
+ /**
+ * @var integer
+ */
+ static $ordersOffset;
+
+ /**
+ * @var integer
+ */
+ static $customersOffset;
+
+ /**
+ * Initialize inner state
+ */
+ public static function init()
+ {
+ static::$api = null;
+ static::$ordersOffset = self::RETAILCRM_EXPORT_ORDERS_STEP_SIZE_CLI;
+ static::$customersOffset = self::RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE_CLI;
+ }
/**
* Get total count of orders for context shop
@@ -66,19 +95,23 @@ class RetailcrmExport
public static function getOrdersIds($start = 0, $count = null)
{
if (is_null($count)) {
- $count = static::getOrdersCount();
+ $to = static::getOrdersCount();
+ $count = $to - $start;
+ } else {
+ $to = $start + $count;
}
if ($count > 0) {
- $loadSize = 5000;
$predefinedSql = 'SELECT o.`id_order`
FROM `' . _DB_PREFIX_ . 'orders` o
WHERE 1
' . Shop::addSqlRestriction(false, 'o') . '
ORDER BY o.`id_order` ASC';
- while ($start <= $count) {
- $offset = ($start + $loadSize > $count) ? $count - $start : $loadSize;
+ while ($start < $to) {
+ $offset = ($start + static::$ordersOffset > $to) ? $to - $start : static::$ordersOffset;
+ if ($offset <= 0)
+ break;
$sql = $predefinedSql . '
LIMIT ' . (int)$start . ', ' . (int)$offset;
@@ -98,16 +131,82 @@ class RetailcrmExport
}
/**
- * Get total count of customers without orders for context shop
+ * @param int $from
+ * @param int|null $count
+ */
+ public static function exportOrders($from = 0, $count = null)
+ {
+ if (!static::validateState()) {
+ return;
+ }
+
+ $orders = array();
+ $orderRecords = static::getOrdersIds($from, $count);
+ $orderBuilder = new RetailcrmOrderBuilder();
+ $orderBuilder->defaultLangFromConfiguration()->setApi(static::$api);
+
+ foreach ($orderRecords as $record) {
+ $orderBuilder->reset();
+
+ $order = new Order($record['id_order']);
+
+ $orderCart = new Cart($order->id_cart);
+ $orderCustomer = new Customer($order->id_customer);
+
+ $orderBuilder->setCmsOrder($order);
+
+ if (!empty($orderCart->id)) {
+ $orderBuilder->setCmsCart($orderCart);
+ } else {
+ $orderBuilder->setCmsCart(null);
+ }
+
+ if (!empty($orderCustomer->id)) {
+ $orderBuilder->setCmsCustomer($orderCustomer);
+ } else {
+ //TODO
+ // Caused crash before because of empty RetailcrmOrderBuilder::cmsCustomer.
+ // Current version *shouldn't* do this, but I suggest more tests for guest customers.
+ $orderBuilder->setCmsCustomer(null);
+ }
+
+ try {
+ $orders[] = $orderBuilder->buildOrderWithPreparedCustomer();
+ } catch (\InvalidArgumentException $exception) {
+ RetailcrmLogger::writeCaller('export', sprintf('Error while building %s: %s', $record['id_order'], $exception->getMessage()));
+ RetailcrmLogger::writeNoCaller($exception->getTraceAsString());
+ RetailcrmLogger::output($exception->getMessage());
+ }
+
+ time_nanosleep(0, 250000000);
+
+ if (count($orders) == 50) {
+ static::$api->ordersUpload($orders);
+ $orders = array();
+ }
+ }
+
+ if (count($orders)) {
+ static::$api->ordersUpload($orders);
+ }
+ }
+
+ /**
+ * Get total count of customers for context shop
+ * @param bool $withOrders If set to true, then return total count of customers.
+ * If set to false, then return count of customers without orders
*
* @return int
*/
- public static function getCustomersCount()
+ public static function getCustomersCount($withOrders = true)
{
$sql = 'SELECT count(c.id_customer)
FROM `' . _DB_PREFIX_ . 'customer` c
WHERE 1
- ' . Shop::addSqlRestriction(false, 'c') . '
+ ' . Shop::addSqlRestriction(false, 'c');
+
+ if (!$withOrders) {
+ $sql .= '
AND c.id_customer not in (
select o.id_customer
from `' . _DB_PREFIX_ . 'orders` o
@@ -115,6 +214,7 @@ class RetailcrmExport
' . Shop::addSqlRestriction(false, 'o') . '
group by o.id_customer
)';
+ }
return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
}
@@ -124,23 +224,27 @@ class RetailcrmExport
*
* @param int $start Sets the LIMIT start parameter for sql query
* @param null $count Sets the count of customers to get from database
- * @param bool $withAddressId If set to true, then also return address id in `id_address`
+ * @param bool $withOrders If set to true, then return all customers ids.
+ * If set to false, then return ids only of customers without orders
+ * @param bool $returnAddressId If set to true, then also return address id in `id_address`
*
* @return Generator
* @throws PrestaShopDatabaseException
*/
- public static function getCustomersIds($start = 0, $count = null, $withAddressId = true)
+ public static function getCustomersIds($start = 0, $count = null, $withOrders = true, $returnAddressId = true)
{
if (is_null($count)) {
- $count = static::getCustomersCount();
+ $to = static::getCustomersCount($withOrders);
+ $count = $to - $start;
+ } else {
+ $to = $start + $count;
}
if ($count > 0) {
- $loadSize = 500;
$predefinedSql = 'SELECT c.`id_customer`
- ' . ($withAddressId ? ', a.`id_address`' : '') . '
+ ' . ($returnAddressId ? ', a.`id_address`' : '') . '
FROM `' . _DB_PREFIX_ . 'customer` c
- ' . ($withAddressId ? '
+ ' . ($returnAddressId ? '
LEFT JOIN
(
SELECT
@@ -167,19 +271,22 @@ class RetailcrmExport
a.`id_customer` = c.`id_customer`
' : '') . '
WHERE 1
- ' . Shop::addSqlRestriction(false, 'c') . '
+ ' . Shop::addSqlRestriction(false, 'c') .
+ ($withOrders ? '' : '
AND c.`id_customer` not in (
select o.`id_customer`
from `' . _DB_PREFIX_ . 'orders` o
WHERE 1
' . Shop::addSqlRestriction(false, 'o') . '
group by o.`id_customer`
- )
+ )') . '
ORDER BY c.`id_customer` ASC';
- while ($start <= $count) {
- $offset = ($start + $loadSize > $count) ? $count - $start : $loadSize;
+ while ($start < $to) {
+ $offset = ($start + static::$customersOffset > $to) ? $to - $start : static::$customersOffset;
+ if ($offset <= 0)
+ break;
$sql = $predefinedSql . '
LIMIT ' . (int)$start . ', ' . (int)$offset;
@@ -198,4 +305,74 @@ class RetailcrmExport
}
}
+ /**
+ * @param int $from
+ * @param int|null $count
+ */
+ public static function exportCustomers($from = 0, $count = null)
+ {
+ if (!static::validateState()) {
+ return;
+ }
+
+ $customers = array();
+ $customersRecords = RetailcrmExport::getCustomersIds($from, $count, false);
+
+ foreach ($customersRecords as $record) {
+ $customerId = $record['id_customer'];
+ $addressId = $record['id_address'];
+
+ $cmsCustomer = new Customer($customerId);
+
+ if (Validate::isLoadedObject($cmsCustomer)) {
+ if ($addressId) {
+ $cmsAddress = new Address($addressId);
+
+ $addressBuilder = new RetailcrmAddressBuilder();
+ $address = $addressBuilder
+ ->setAddress($cmsAddress)
+ ->build()
+ ->getDataArray();
+ } else {
+ $address = array();
+ }
+
+ try {
+ $customers[] = RetailcrmOrderBuilder::buildCrmCustomer($cmsCustomer, $address);
+ } catch (\Exception $exception) {
+ RetailcrmLogger::writeCaller('export', sprintf('Error while building %s: %s', $customerId, $exception->getMessage()));
+ RetailcrmLogger::writeNoCaller($exception->getTraceAsString());
+ RetailcrmLogger::output($exception->getMessage());
+ }
+
+ if (count($customers) == 50) {
+ static::$api->customersUpload($customers);
+ $customers = array();
+
+ time_nanosleep(0, 250000000);
+ }
+ }
+ }
+
+ if (count($customers)) {
+ static::$api->customersUpload($customers);
+ }
+ }
+
+ /**
+ * Returns false if inner state is not correct
+ *
+ * @return bool
+ */
+ private static function validateState()
+ {
+ if (!static::$api ||
+ !static::$ordersOffset ||
+ !static::$customersOffset
+ ) {
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/retailcrm/lib/events/RetailcrmExportEvent.php b/retailcrm/lib/events/RetailcrmExportEvent.php
index b4e90d1..7d2b540 100644
--- a/retailcrm/lib/events/RetailcrmExportEvent.php
+++ b/retailcrm/lib/events/RetailcrmExportEvent.php
@@ -64,98 +64,10 @@ class RetailcrmExportEvent extends RetailcrmAbstractEvent implements RetailcrmEv
continue;
}
- $orders = array();
- $orderRecords = RetailcrmExport::getOrdersIds();
- $orderBuilder = new RetailcrmOrderBuilder();
- $orderBuilder->defaultLangFromConfiguration()->setApi($api);
-
- foreach ($orderRecords as $record) {
- $orderBuilder->reset();
-
- $order = new Order($record['id_order']);
-
- $orderCart = new Cart($order->id_cart);
- $orderCustomer = new Customer($order->id_customer);
-
- $orderBuilder->setCmsOrder($order);
-
- if (!empty($orderCart->id)) {
- $orderBuilder->setCmsCart($orderCart);
- } else {
- $orderBuilder->setCmsCart(null);
- }
-
- if (!empty($orderCustomer->id)) {
- $orderBuilder->setCmsCustomer($orderCustomer);
- } else {
- //TODO
- // Caused crash before because of empty RetailcrmOrderBuilder::cmsCustomer.
- // Current version *shouldn't* do this, but I suggest more tests for guest customers.
- $orderBuilder->setCmsCustomer(null);
- }
-
- try {
- $orders[] = $orderBuilder->buildOrderWithPreparedCustomer();
- } catch (\InvalidArgumentException $exception) {
- RetailcrmLogger::writeCaller('export', sprintf('Error while building %s: %s', $record['id_order'], $exception->getMessage()));
- RetailcrmLogger::writeNoCaller($exception->getTraceAsString());
- RetailcrmLogger::output($exception->getMessage());
- }
-
- time_nanosleep(0, 250000000);
-
- if (count($orders) == 50) {
- $api->ordersUpload($orders);
- $orders = array();
- }
- }
-
- if (count($orders)) {
- $api->ordersUpload($orders);
- }
-
- $customers = array();
- $customersRecords = RetailcrmExport::getCustomersIds();
-
- foreach ($customersRecords as $record) {
- $customerId = $record['id_customer'];
- $addressId = $record['id_address'];
-
- $cmsCustomer = new Customer($customerId);
-
- if (Validate::isLoadedObject($cmsCustomer)) {
- if ($addressId) {
- $cmsAddress = new Address($addressId);
-
- $addressBuilder = new RetailcrmAddressBuilder();
- $address = $addressBuilder
- ->setAddress($cmsAddress)
- ->build()
- ->getDataArray();
- } else {
- $address = array();
- }
-
- try {
- $customers[] = RetailcrmOrderBuilder::buildCrmCustomer($cmsCustomer, $address);
- } catch (\Exception $exception) {
- RetailcrmLogger::writeCaller('export', sprintf('Error while building %s: %s', $customerId, $exception->getMessage()));
- RetailcrmLogger::writeNoCaller($exception->getTraceAsString());
- RetailcrmLogger::output($exception->getMessage());
- }
-
- if (count($customers) == 50) {
- $api->customersUpload($customers);
- $customers = array();
-
- time_nanosleep(0, 250000000);
- }
- }
- }
-
- if (count($customers)) {
- $api->customersUpload($customers);
- }
+ RetailcrmExport::init();
+ RetailcrmExport::$api = $api;
+ RetailcrmExport::exportOrders();
+ RetailcrmExport::exportCustomers();
}
return true;
diff --git a/retailcrm/lib/templates/RetailcrmSettingsTemplate.php b/retailcrm/lib/templates/RetailcrmSettingsTemplate.php
index fdc5893..168e6ad 100644
--- a/retailcrm/lib/templates/RetailcrmSettingsTemplate.php
+++ b/retailcrm/lib/templates/RetailcrmSettingsTemplate.php
@@ -74,6 +74,11 @@ class RetailcrmSettingsTemplate extends RetailcrmAbstractTemplate
$params['methodsForDefault'] = $this->module->reference->getPaymentAndDeliveryForDefault(
array($this->module->translate('Delivery method'), $this->module->translate('Payment type'))
);
+ $params['ordersCount'] = RetailcrmExport::getOrdersCount();
+ $params['customersCount'] = RetailcrmExport::getCustomersCount();
+ $params['exportCustomersCount'] = RetailcrmExport::getCustomersCount(false);
+ $params['exportOrdersStepSize'] = RetailcrmExport::RETAILCRM_EXPORT_ORDERS_STEP_SIZE_WEB;
+ $params['exportCustomersStepSize'] = RetailcrmExport::RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE_WEB;
}
return $params;
diff --git a/retailcrm/retailcrm.php b/retailcrm/retailcrm.php
index 3db54dd..a964e1f 100644
--- a/retailcrm/retailcrm.php
+++ b/retailcrm/retailcrm.php
@@ -63,6 +63,8 @@ class RetailCRM extends Module
const SYNC_CARTS_STATUS = 'RETAILCRM_API_SYNCHRONIZED_CART_STATUS';
const SYNC_CARTS_DELAY = 'RETAILCRM_API_SYNCHRONIZED_CART_DELAY';
const UPLOAD_ORDERS = 'RETAILCRM_UPLOAD_ORDERS_ID';
+ const EXPORT_ORDERS = 'RETAILCRM_EXPORT_ORDERS_STEP';
+ const EXPORT_CUSTOMERS = 'RETAILCRM_EXPORT_CUSTOMERS_STEP';
const MODULE_LIST_CACHE_CHECKSUM = 'RETAILCRM_MODULE_LIST_CACHE_CHECKSUM';
const ENABLE_CORPORATE_CLIENTS = 'RETAILCRM_ENABLE_CORPORATE_CLIENTS';
const ENABLE_HISTORY_UPLOADS = 'RETAILCRM_ENABLE_HISTORY_UPLOADS';
@@ -261,9 +263,15 @@ class RetailCRM extends Module
if (Tools::isSubmit('submit' . $this->name)) {
$ordersIds = (string)(Tools::getValue(static::UPLOAD_ORDERS));
+ $exportOrders = (int)(Tools::getValue(static::EXPORT_ORDERS));
+ $exportCustomers = (int)(Tools::getValue(static::EXPORT_CUSTOMERS));
if (!empty($ordersIds)) {
$output .= $this->uploadOrders(RetailcrmTools::partitionId($ordersIds));
+ } elseif (!empty($exportOrders)) {
+ return $this->export($exportOrders);
+ } elseif (!empty($exportCustomers)) {
+ return $this->export($exportCustomers, 'customer');
} else {
$output .= $this->saveSettings();
}
@@ -357,6 +365,45 @@ class RetailCRM extends Module
}
}
+ /**
+ * @param int $step
+ * @param string $entity
+ * @return bool
+ */
+ public function export($step, $entity = 'order')
+ {
+ $step--;
+ if ($step < 0)
+ return false;
+
+ $api = RetailcrmTools::getApiClient();
+
+ if (empty($api)) {
+ RetailcrmLogger::writeCaller(__METHOD__, 'Set API key & URL first');
+
+ return false;
+ }
+
+ RetailcrmExport::init();
+ RetailcrmExport::$api = $api;
+
+ if ($entity === 'order') {
+ $stepSize = RetailcrmExport::RETAILCRM_EXPORT_ORDERS_STEP_SIZE_WEB;
+
+ RetailcrmExport::$ordersOffset = $stepSize;
+ RetailcrmExport::exportOrders($step * $stepSize, $stepSize);
+ // todo maybe save current step to database
+ } elseif ($entity === 'customer') {
+ $stepSize = RetailcrmExport::RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE_WEB;
+
+ RetailcrmExport::$customersOffset = $stepSize;
+ RetailcrmExport::exportCustomers($step * $stepSize, $stepSize);
+ // todo maybe save current step to database
+ }
+
+ return true;
+ }
+
public function hookActionCustomerAccountAdd($params)
{
if ($this->api) {
diff --git a/retailcrm/translations/es.php b/retailcrm/translations/es.php
index 6d4979b..a9c13eb 100644
--- a/retailcrm/translations/es.php
+++ b/retailcrm/translations/es.php
@@ -47,10 +47,17 @@ $_MODULE['<{retailcrm}prestashop>settings_27096e1243f98e1b3300f57ff1c76456'] = '
$_MODULE['<{retailcrm}prestashop>settings_5b385947acf10ac0c5521161ce96aaa7'] = 'Elige la entrega';
$_MODULE['<{retailcrm}prestashop>settings_7dcc1208fa03381346955c6732d9ea85'] = 'Elige el tipo';
$_MODULE['<{retailcrm}prestashop>settings_acfa058ec9e6e4745eddc0cae3f0f881'] = 'Identificador del pedido';
-$_MODULE['<{retailcrm}prestashop>settings_91412465ea9169dfd901dd5e7c96dd99'] = 'Exportar';
+$_MODULE['<{retailcrm}prestashop>settings_91412465ea9169dfd901dd5e7c96dd99'] = 'Subir';
$_MODULE['<{retailcrm}prestashop>settings_4d3d769b812b6faa6b76e1a8abaece2d'] = 'Active';
$_MODULE['<{retailcrm}prestashop>settings_f75d8fa5c89351544d372cf90528ccf2'] = 'Clave de la página web';
$_MODULE['<{retailcrm}prestashop>settings_c9cc8cce247e49bae79f15173ce97354'] = 'Guardar';
+$_MODULE['<{retailcrm}prestashop>settings_30a8be02b0e6e45988617eca94301966'] = 'Ingrese los ID de los pedidos para cargar en RetailCRM, divididos por una coma. También puede especificar rangos, como "1-10". Se permite subir hasta 10 pedidos a la vez.';
+$_MODULE['<{retailcrm}prestashop>settings_f4af7f6987dfee28741ce77ff2d09d46'] = 'Exportar pedidos y clientes';
+$_MODULE['<{retailcrm}prestashop>settings_06d94a1c5dfe9d54ef201d2217ca477c'] = 'Puede exportar todos los pedidos y clientes de CMS a RetailCRM presionando el botón "Exportar". Este proceso puede llevar mucho tiempo y es necesario que mantenga la pestaña abierta hasta que termine.';
+$_MODULE['<{retailcrm}prestashop>settings_7442e29d7d53e549b78d93c46b8cdcfc'] = 'Pedidos';
+$_MODULE['<{retailcrm}prestashop>settings_e6d0e1c8fc6a4fcf47869df87e04cd88'] = 'Clientes';
+$_MODULE['<{retailcrm}prestashop>settings_f8f36c02fa6f370808135c66cfc788aa'] = 'Clientes sin pedidos';
+$_MODULE['<{retailcrm}prestashop>settings_0095a9fa74d1713e43e370a7d7846224'] = 'Exportar';
$_MODULE['<{retailcrm}prestashop>settings_4f18e3f1c9941a6ec5a38bc716c521b4'] = 'Código que necesita insertar en la web';
$_MODULE['<{retailcrm}prestashop>settings_9082f68bc90113d8950e4ed7fe8fa0a4'] = 'Administrador de tareas';
$_MODULE['<{retailcrm}prestashop>settings_9194de58ce560c095f02cefc1c1c61e6'] = 'Nombre de la tarea';
diff --git a/retailcrm/translations/ru.php b/retailcrm/translations/ru.php
index d20dc43..1ea2893 100644
--- a/retailcrm/translations/ru.php
+++ b/retailcrm/translations/ru.php
@@ -52,6 +52,13 @@ $_MODULE['<{retailcrm}prestashop>settings_4d3d769b812b6faa6b76e1a8abaece2d'] = '
$_MODULE['<{retailcrm}prestashop>settings_f75d8fa5c89351544d372cf90528ccf2'] = 'Ключ сайта';
$_MODULE['<{retailcrm}prestashop>settings_c9cc8cce247e49bae79f15173ce97354'] = 'Сохранить';
$_MODULE['<{retailcrm}prestashop>settings_4f18e3f1c9941a6ec5a38bc716c521b4'] = 'Код для вставки на сайт';
+$_MODULE['<{retailcrm}prestashop>settings_30a8be02b0e6e45988617eca94301966'] = 'Введите идентификаторы заказов для загрузки в RetailCRM, разделив их запятыми. Вы также можете указать диапазоны, например "1-10". Одновременно можно загружать до 10 заказов.';
+$_MODULE['<{retailcrm}prestashop>settings_f4af7f6987dfee28741ce77ff2d09d46'] = 'Экспортировать заказы и клиентов';
+$_MODULE['<{retailcrm}prestashop>settings_06d94a1c5dfe9d54ef201d2217ca477c'] = 'Вы можете экспортировать все заказы и клиентов из CMS в RetailCRM, нажав кнопку «Экспорт». Этот процесс может занять много времени, и до его завершения необходимо держать вкладку открытой.';
+$_MODULE['<{retailcrm}prestashop>settings_7442e29d7d53e549b78d93c46b8cdcfc'] = 'Заказы';
+$_MODULE['<{retailcrm}prestashop>settings_e6d0e1c8fc6a4fcf47869df87e04cd88'] = 'Клиенты';
+$_MODULE['<{retailcrm}prestashop>settings_f8f36c02fa6f370808135c66cfc788aa'] = 'Клиенты без заказов';
+$_MODULE['<{retailcrm}prestashop>settings_0095a9fa74d1713e43e370a7d7846224'] = 'Экспортировать';
$_MODULE['<{retailcrm}prestashop>settings_9082f68bc90113d8950e4ed7fe8fa0a4'] = 'Менеджер задач';
$_MODULE['<{retailcrm}prestashop>settings_9194de58ce560c095f02cefc1c1c61e6'] = 'Имя задачи';
$_MODULE['<{retailcrm}prestashop>settings_05a3a24340b7b9cc8d4e08f0ef4f4dd9'] = 'Последний запуск';
diff --git a/retailcrm/views/css/less/retailcrm-export.less b/retailcrm/views/css/less/retailcrm-export.less
new file mode 100644
index 0000000..39d63b8
--- /dev/null
+++ b/retailcrm/views/css/less/retailcrm-export.less
@@ -0,0 +1,45 @@
+.retail {
+ &-circle {
+ float: left;
+ width: 50%;
+ text-align: center;
+ padding: 20px;
+ margin: 20px 0;
+ &__title {
+ font-size: 18px;
+ margin-bottom: 20px;
+ }
+ input&__content {
+ width: 120px;
+ height: 120px;
+ border-radius: 120px;
+ text-align: center;
+ font-size: 20px;
+ line-height: 60px;
+ }
+ }
+ &-progress {
+ border-radius: 60px;
+ border: 1px solid rgba(122, 122, 122, 0.15);
+ width: 100%;
+ height: 60px;
+ overflow: hidden;
+ transition: height 0.25s ease;
+ &__loader {
+ width: 0;
+ border-radius: 60px;
+ background: #0068FF;
+ color: white;
+ text-align: center;
+ padding: 0 30px;
+ font-size: 18px;
+ font-weight: 600;
+ transition: width 0.4s ease-in;
+ line-height: 60px;
+ }
+ }
+ &-hidden {
+ visibility: hidden;
+ height: 0!important;
+ }
+}
diff --git a/retailcrm/views/css/retailcrm-export.min.css b/retailcrm/views/css/retailcrm-export.min.css
new file mode 100644
index 0000000..2a9c514
--- /dev/null
+++ b/retailcrm/views/css/retailcrm-export.min.css
@@ -0,0 +1 @@
+.retail-circle{float:left;width:50%;text-align:center;padding:20px;margin:20px 0}.retail-circle__title{font-size:18px;margin-bottom:20px}input.retail-circle__content{width:120px;height:120px;border-radius:120px;text-align:center;font-size:20px;line-height:60px}.retail-progress{border-radius:60px;border:1px solid rgba(122,122,122,0.15);width:100%;height:60px;overflow:hidden;transition:height .25s ease}.retail-progress__loader{width:0;border-radius:60px;background:#0068FF;color:white;text-align:center;padding:0 30px;font-size:18px;font-weight:600;transition:width .4s ease-in;line-height:60px}.retail-hidden{visibility:hidden;height:0!important}
\ No newline at end of file
diff --git a/retailcrm/views/css/styles.min.css b/retailcrm/views/css/styles.min.css
index 1ffc9e9..4820beb 100644
--- a/retailcrm/views/css/styles.min.css
+++ b/retailcrm/views/css/styles.min.css
@@ -1,611 +1 @@
-@font-face {
- font-family: 'OpenSans';
- src: url('../fonts/OpenSans/opensans-regular.eot');
- src: url('../fonts/OpenSans/opensans-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/OpenSans/opensans-regular.woff2') format('woff2'), url('../fonts/OpenSans/opensans-regular.woff') format('woff'), url('../fonts/OpenSans/opensans-regular.ttf') format('truetype'), url('../fonts/OpenSans/opensans-regular.svg#open_sansregular') format('svg');
- font-weight: normal;
- font-style: normal;
-}
-@font-face {
- font-family: 'OpenSans';
- src: url('../fonts/OpenSansBold/opensans-bold.eot');
- src: url('../fonts/OpenSansBold/opensans-bold.eot?#iefix') format('embedded-opentype'), url('../fonts/OpenSansBold/opensans-bold.woff2') format('woff2'), url('../fonts/OpenSansBold/opensans-bold.woff') format('woff'), url('../fonts/OpenSansBold/opensans-bold.ttf') format('truetype'), url('../fonts/OpenSansBold/opensans-bold.svg#open_sansbold') format('svg');
- font-weight: 600;
- font-style: normal;
-}
-body,
-html {
- margin: 0;
- padding: 0;
- height: 100%;
-}
-.hidden {
- visibility: hidden;
-}
-.retail-wrap {
- font-family: OpenSans, Arial, sans-serif;
- padding: 0 15px;
- height: 100%;
-}
-.retail-wrap *,
-.retail-wrap *::before,
-.retail-wrap *::after {
- box-sizing: border-box;
-}
-.retail-container {
- margin: 0 auto;
- width: 100%;
- max-width: 950px;
-}
-.retail-title {
- margin: 60px 0 0;
- font-weight: 400;
- text-align: center;
- font-size: 28px;
- line-height: 38px;
-}
-.retail-title_content {
- text-align: left;
- margin-top: 40px;
-}
-.retail-txt {
- color: #7A7A7A;
- font-size: 18px;
- line-height: 26px;
-}
-.retail-descript {
- margin-top: 45px;
- text-align: center;
-}
-.retail-tab__enabled {
- display: block;
-}
-.retail-tab__disabled {
- display: none !important;
-}
-.retail-video {
- margin: 57px auto 0;
- max-width: 442px;
- position: relative;
-}
-.retail-video-trigger {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 0;
- left: 0;
- width: 100%;
- height: 100%;
- cursor: pointer;
-}
-.retail-video iframe {
- pointer-events: none;
-}
-.retail-video__btn {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- margin: auto;
- width: 100px;
- height: 100px;
- cursor: pointer;
- opacity: 0.4;
- transition: 0.25s ease;
-}
-.retail-video__btn svg {
- width: 100%;
-}
-.retail-video__btn:hover {
- opacity: 0.6;
-}
-.retail-btns {
- margin: 56px auto 0;
- display: flex;
- justify-content: space-between;
- max-width: 815px;
- transition: 0.05s ease;
-}
-.retail-btns__separate {
- padding: 0 20px;
- display: flex;
- align-items: center;
- color: #7A7A7A;
- font-size: 16px;
-}
-.retail-btns_hide {
- opacity: 0;
-}
-.retail-form {
- margin-top: 60px;
-}
-.retail-form__title {
- font-size: 16px;
- font-weight: 600;
- line-height: 24px;
- margin-bottom: 22px;
-}
-.retail-form__label {
- width: 100% !important;
- text-align: left !important;
- margin: 15px 12px;
- font-size: 15px;
-}
-.retail-form__row {
- margin-top: 15px;
-}
-.retail-form__row_submit {
- margin-top: 23px;
-}
-.retail-form__message-warning {
- padding: 13px 18px;
- margin: 1px 13px;
- border-radius: 8px;
- border: 1px solid #fcf3b5;
- font-size: 1rem;
- box-shadow: 0px 0px 6px 0px #fdd0d0;
-}
-.retail-form__checkbox {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 4px 12px;
-}
-.retail-form__checkbox input[type=checkbox] {
- width: 24px;
- height: 24px;
-}
-.retail-form__checkbox label {
- width: auto;
- margin-left: 8px;
- font-size: 16px;
-}
-.retail-form__area {
- display: inline-block !important;
- vertical-align: top;
- width: 430px !important;
- height: 60px !important;
- border: 1px solid rgba(122, 122, 122, 0.15) !important;
- box-shadow: none !important;
- border-radius: 58px !important;
- padding: 0 28px !important;
- line-height: normal;
- color: #7A7A7A !important;
- font-size: 16px !important;
- appearance: none;
-}
-.retail-form__area:focus {
- color: #363A41;
-}
-.retail-form__area:focus::-webkit-input-placeholder {
- color: #363A41;
-}
-.retail-form__area:focus::-moz-placeholder {
- color: #363A41;
-}
-.retail-form__area:focus:-moz-placeholder {
- color: #363A41;
-}
-.retail-form__area:focus:-ms-input-placeholder {
- color: #363A41;
-}
-.retail-form__area_txt {
- padding: 20px 28px !important;
- line-height: 24px !important;
- height: 487px !important;
- border-radius: 20px !important;
- resize: none !important;
- font-family: OpenSans, Arial, sans-serif !important;
-}
-.retail-form input:focus,
-.retail-form textarea:focus {
- outline: none !important;
-}
-.retail-form_main {
- margin-top: 34px;
- max-width: 900px;
- width: 100%;
-}
-.retail-form_main .retail-form__area {
- width: 100% !important;
-}
-.retail-tabs {
- margin-top: 60px;
-}
-.retail-tabs__btn {
- display: inline-block;
- vertical-align: top;
- padding: 19px 30px;
- font-size: 16px;
- font-weight: 600;
- line-height: 22px;
- color: #7A7A7A;
- text-align: center;
- min-width: 152px;
- text-decoration: none !important;
- position: relative;
- transition: 0.25s ease;
-}
-.retail-tabs__btn:hover {
- color: #363A41;
-}
-.retail-tabs__btn::after {
- content: "";
- height: 3px;
- width: 100%;
- position: absolute;
- bottom: -1px;
- left: 0;
- right: 0;
- opacity: 0;
- visibility: hidden;
- background: #0068FF;
- transition: 0.25s ease;
-}
-.retail-tabs__btn_active {
- color: #363A41;
-}
-.retail-tabs__btn_active::after {
- opacity: 1;
- visibility: visible;
-}
-.retail-tabs__head {
- display: flex;
- justify-content: space-between;
- border-bottom: 1px solid #DFDFDF;
-}
-.retail-tabs__body {
- padding-top: 18px;
-}
-.retail-tabs__body p {
- margin-top: 23px;
- margin-bottom: 0;
- color: #7A7A7A;
- font-size: 16px;
- line-height: 24px;
-}
-.retail-tabs__item {
- display: none;
-}
-.retail-list {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-.retail-list__item {
- padding-left: 2px;
- position: relative;
- color: #7A7A7A;
- font-size: 16px;
- line-height: 24px;
-}
-.retail-list__item::before {
- content: "-";
- display: inline-block;
- vertical-align: top;
- color: #7A7A7A;
- font-size: 16px;
- line-height: 24px;
- margin-right: 3px;
-}
-.retail-tile {
- display: flex;
- flex-wrap: wrap;
-}
-.retail-tile__col {
- width: 48%;
- padding-right: 35px;
-}
-.retail-tile__col:nth-child(1) {
- width: 52%;
-}
-.retail-tile__col_contacts {
- padding-right: 0;
-}
-.retail-tile__row {
- display: flex;
- justify-content: center;
- margin-bottom: 30px;
-}
-.retail-tile__row:nth-last-child(1) {
- margin-bottom: 0;
-}
-.retail-tile__item {
- margin-top: 34px;
-}
-.retail-tile__item:nth-child(1) {
- margin-top: 20px;
-}
-.retail-tile__title {
- color: #363A41;
- font-size: 16px;
- font-weight: 600;
- line-height: 24px;
-}
-.retail-tile__descript {
- color: #7A7A7A;
- font-size: 16px;
- line-height: 24px;
- margin-top: 10px;
-}
-.retail-tile__link {
- color: #0068FF;
- font-size: 16px;
- font-weight: 600;
- line-height: 24px;
- transition: 0.25s ease;
-}
-.retail-tile__link:hover {
- color: #005add;
-}
-.retail-popup {
- position: absolute;
- width: 90%;
- height: 90%;
- background: white;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- margin: auto;
- transform: translate3d(0, -1000%, 0) scale(0.1);
- transition: 0.25s ease;
-}
-.retail-popup__close {
- position: absolute;
- top: -30px;
- right: -30px;
- width: 30px;
- height: 30px;
- cursor: pointer;
-}
-.retail-popup__close::before,
-.retail-popup__close::after {
- content: "";
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- margin: auto;
- height: 30px;
- width: 2px;
- background: white;
-}
-.retail-popup__close::before {
- transform: rotate(45deg);
-}
-.retail-popup__close::after {
- transform: rotate(-45deg);
-}
-.retail-popup.open {
- transform: translate3d(0, 0, 0) scale(1);
-}
-.retail-popup-wrap {
- position: fixed;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.6);
- z-index: 1000;
- display: none;
-}
-.retail-column {
- display: flex;
- max-width: 1389px;
- height: 100%;
-}
-.retail-column__aside {
- width: 253px;
- background: #EAEBEC;
- min-height: 300px;
-}
-.retail-column__content {
- flex: 1 0 auto;
- padding: 0 58px;
- min-height: 300px;
-}
-.retail-menu {
- padding: 8px 20px 8px 15px;
-}
-.retail-menu__btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- vertical-align: top;
- width: 100%;
- height: 60px;
- background: rgba(122, 122, 122, 0.1) !important;
- font-weight: bold;
- font-size: 16px;
- color: #363A41 !important;
- text-decoration: none !important;
- padding: 0 30px;
- margin-top: 20px;
- border-radius: 5px;
- text-align: center;
- transition: 0.25s ease;
-}
-.retail-menu__btn span {
- display: block;
-}
-.retail-menu__btn:hover {
- background: rgba(122, 122, 122, 0.15) !important;
-}
-.retail-menu__btn:nth-child(1) {
- margin-top: 0;
-}
-.retail-menu__btn_active {
- color: white !important;
- background: #0068FF !important;
-}
-.retail-menu__btn_active:hover {
- background: #005add !important;
-}
-.retail-menu__btn_big {
- font-size: 18px;
-}
-.retail-full-height {
- height: 100%;
-}
-.retail .btn {
- display: inline-block;
- vertical-align: top;
- background: rgba(122, 122, 122, 0.1);
- border-radius: 58px;
- height: 60px;
- line-height: 60px;
- padding: 0 30px;
- font-size: 18px;
- font-weight: 600;
- text-align: center;
- color: #0068FF;
- text-decoration: none;
- cursor: pointer;
- appearance: none;
- border: none;
- box-shadow: none;
- transition: 0.25s ease;
-}
-.retail .btn:hover {
- background: rgba(122, 122, 122, 0.15);
-}
-.retail .btn:active {
- background: rgba(122, 122, 122, 0.25);
-}
-.retail .btn_max {
- min-width: 356px;
-}
-.retail .btn_invert {
- background: #0068FF;
- color: white;
-}
-.retail .btn_invert:hover {
- background: #005add;
-}
-.retail .btn_invert:active {
- background: #0045aa;
-}
-.retail .btn_whatsapp {
- background: #33D16B;
- color: white;
- padding: 0 62px;
-}
-.retail .btn_whatsapp:hover {
- background: #22CA5D;
-}
-.retail .btn_submit {
- min-width: 218px;
-}
-.retail .toggle-box {
- display: none;
-}
-.retail-table {
- width: 100%;
- border: none;
- border-radius: 20px;
-}
-.retail-table thead th {
- background: rgba(122, 122, 122, 0.15);
- font-size: 16px;
-}
-.retail-table tbody tr td {
- border-bottom: 1px solid #ccc;
-}
-.retail-table td,
-.retail-table th {
- color: #333;
- font-size: 14px;
- line-height: 60px;
- padding: 4px 4px 4px 6px;
- max-width: 300px;
- vertical-align: middle;
-}
-.retail-table-no-wrap {
- white-space: nowrap;
-}
-.retail-table-center {
- text-align: center;
-}
-.retail-table-sort__btn,
-.retail-table-sort__switch {
- cursor: pointer;
-}
-.retail-table-sort__btn:hover,
-.retail-table-sort__switch:hover {
- color: #005add;
-}
-.retail-table-sort__btn {
- float: left;
- clear: both;
- line-height: 15px;
- font-size: 20px;
- padding-left: 10px;
- padding-right: 10px;
- opacity: 0;
- transition: opacity 0.2s ease-out;
-}
-.retail-table-sort__btn-wrap {
- position: absolute;
-}
-.retail-table-sort__asc {
- padding-top: 15px;
- padding-bottom: 0;
-}
-.retail-table-sort__desc {
- padding-top: 0;
- padding-bottom: 15px;
-}
-.retail-table-sort thead th:hover .retail-table-sort__btn,
-.retail-table-sort thead td:hover .retail-table-sort__btn {
- opacity: 1;
-}
-.retail-collapsible__input {
- display: none;
-}
-label.retail-collapsible__title {
- cursor: pointer;
- font-weight: normal;
- text-align: center;
- padding: 0;
- position: relative;
- line-height: 1;
- width: 100%;
-}
-label.retail-collapsible__title:before {
- content: '\25B6';
- opacity: 0;
- transition: opacity 0.2s ease-out;
-}
-label.retail-collapsible__title:hover:before {
- opacity: 1;
-}
-.retail-collapsible__content {
- text-align: left;
- font-size: 12px;
- line-height: 1.5;
- background: #fff;
- border: 1px solid rgba(122, 122, 122, 0.15);
- position: absolute;
- top: 20px;
- left: 0;
- padding: 18px;
- margin: 0;
- border-radius: 20px;
- overflow: hidden;
- max-height: 0;
- transition-duration: 0.2s;
- transition-timing-function: ease-out;
- transition-property: max-height, visibility;
- visibility: hidden;
-}
-.retail-collapsible__input:checked + .retail-collapsible__title .retail-collapsible__content {
- max-height: 100vh;
- visibility: visible;
-}
-.retail-error-msg,
-.retail-error-msg:after,
-.retail-error-msg:before {
- color: #dd2e44;
-}
+@font-face{font-family:'OpenSans';src:url('../fonts/OpenSans/opensans-regular.eot');src:url('../fonts/OpenSans/opensans-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/OpenSans/opensans-regular.woff2') format('woff2'),url('../fonts/OpenSans/opensans-regular.woff') format('woff'),url('../fonts/OpenSans/opensans-regular.ttf') format('truetype'),url('../fonts/OpenSans/opensans-regular.svg#open_sansregular') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'OpenSans';src:url('../fonts/OpenSansBold/opensans-bold.eot');src:url('../fonts/OpenSansBold/opensans-bold.eot?#iefix') format('embedded-opentype'),url('../fonts/OpenSansBold/opensans-bold.woff2') format('woff2'),url('../fonts/OpenSansBold/opensans-bold.woff') format('woff'),url('../fonts/OpenSansBold/opensans-bold.ttf') format('truetype'),url('../fonts/OpenSansBold/opensans-bold.svg#open_sansbold') format('svg');font-weight:600;font-style:normal}body,html{margin:0;padding:0;height:100%}.hidden{visibility:hidden}.retail-wrap{font-family:OpenSans,Arial,sans-serif;padding:0 15px;height:100%}.retail-wrap *,.retail-wrap *::before,.retail-wrap *::after{box-sizing:border-box}.retail-container{margin:0 auto;width:100%;max-width:950px}.retail-title{margin:60px 0 0;font-weight:400;text-align:center;font-size:28px;line-height:38px}.retail-title_content{text-align:left;margin-top:40px}.retail-txt{color:#7A7A7A;font-size:18px;line-height:26px}.retail-descript{margin-top:45px;text-align:center}.retail-tab__enabled{display:block}.retail-tab__disabled{display:none !important}.retail-video{margin:57px auto 0;max-width:442px;position:relative}.retail-video-trigger{position:absolute;top:0;bottom:0;right:0;left:0;width:100%;height:100%;cursor:pointer}.retail-video iframe{pointer-events:none}.retail-video__btn{position:absolute;left:0;right:0;top:0;bottom:0;margin:auto;width:100px;height:100px;cursor:pointer;opacity:.4;transition:.25s ease}.retail-video__btn svg{width:100%}.retail-video__btn:hover{opacity:.6}.retail-btns{margin:56px auto 0;display:flex;justify-content:space-between;max-width:815px;transition:.05s ease}.retail-btns__separate{padding:0 20px;display:flex;align-items:center;color:#7A7A7A;font-size:16px}.retail-btns_hide{opacity:0}.retail-form{margin-top:60px}.retail-form__title{font-size:16px;font-weight:600;line-height:24px;margin-bottom:22px}.retail-form__label{width:100% !important;text-align:left !important;margin:15px 12px;font-size:15px}.retail-form__row{margin-top:15px}.retail-form__row_submit{margin-top:23px}.retail-form__message-warning{padding:13px 18px;margin:1px 13px;border-radius:8px;border:1px solid #fcf3b5;font-size:1rem;box-shadow:0 0 6px 0 #fdd0d0}.retail-form__checkbox{display:flex;flex-direction:row;align-items:center;padding:4px 12px}.retail-form__checkbox input[type=checkbox]{width:24px;height:24px}.retail-form__checkbox label{width:auto;margin-left:8px;font-size:16px}.retail-form__area{display:inline-block !important;vertical-align:top;width:430px !important;height:60px !important;border:1px solid rgba(122,122,122,0.15) !important;box-shadow:none !important;border-radius:58px !important;padding:0 28px !important;line-height:normal;color:#7A7A7A !important;font-size:16px !important;appearance:none}.retail-form__area:focus{color:#363A41}.retail-form__area:focus::-webkit-input-placeholder{color:#363A41}.retail-form__area:focus::-moz-placeholder{color:#363A41}.retail-form__area:focus:-moz-placeholder{color:#363A41}.retail-form__area:focus:-ms-input-placeholder{color:#363A41}.retail-form__area_txt{padding:20px 28px !important;line-height:24px !important;height:487px !important;border-radius:20px !important;resize:none !important;font-family:OpenSans,Arial,sans-serif !important}.retail-form input:focus,.retail-form textarea:focus{outline:none !important}.retail-form_main{margin-top:34px;max-width:900px;width:100%}.retail-form_main .retail-form__area{width:100% !important}.retail-tabs{margin-top:60px}.retail-tabs__btn{display:inline-block;vertical-align:top;padding:19px 30px;font-size:16px;font-weight:600;line-height:22px;color:#7A7A7A;text-align:center;min-width:152px;text-decoration:none !important;position:relative;transition:.25s ease}.retail-tabs__btn:hover{color:#363A41}.retail-tabs__btn::after{content:"";height:3px;width:100%;position:absolute;bottom:-1px;left:0;right:0;opacity:0;visibility:hidden;background:#0068FF;transition:.25s ease}.retail-tabs__btn_active{color:#363A41}.retail-tabs__btn_active::after{opacity:1;visibility:visible}.retail-tabs__head{display:flex;justify-content:space-between;border-bottom:1px solid #DFDFDF}.retail-tabs__body{padding-top:18px}.retail-tabs__body p{margin-top:23px;margin-bottom:0;color:#7A7A7A;font-size:16px;line-height:24px}.retail-tabs__item{display:none}.retail-list{margin:0;padding:0;list-style:none}.retail-list__item{padding-left:2px;position:relative;color:#7A7A7A;font-size:16px;line-height:24px}.retail-list__item::before{content:"-";display:inline-block;vertical-align:top;color:#7A7A7A;font-size:16px;line-height:24px;margin-right:3px}.retail-tile{display:flex;flex-wrap:wrap}.retail-tile__col{width:48%;padding-right:35px}.retail-tile__col:nth-child(1){width:52%}.retail-tile__col_contacts{padding-right:0}.retail-tile__row{display:flex;justify-content:center;margin-bottom:30px}.retail-tile__row:nth-last-child(1){margin-bottom:0}.retail-tile__item{margin-top:34px}.retail-tile__item:nth-child(1){margin-top:20px}.retail-tile__title{color:#363A41;font-size:16px;font-weight:600;line-height:24px}.retail-tile__descript{color:#7A7A7A;font-size:16px;line-height:24px;margin-top:10px}.retail-tile__link{color:#0068FF;font-size:16px;font-weight:600;line-height:24px;transition:.25s ease}.retail-tile__link:hover{color:#005add}.retail-popup{position:absolute;width:90%;height:90%;background:white;left:0;right:0;top:0;bottom:0;margin:auto;transform:translate3d(0, -1000%, 0) scale(.1);transition:.25s ease}.retail-popup__close{position:absolute;top:-30px;right:-30px;width:30px;height:30px;cursor:pointer}.retail-popup__close::before,.retail-popup__close::after{content:"";position:absolute;left:0;right:0;top:0;bottom:0;margin:auto;height:30px;width:2px;background:white}.retail-popup__close::before{transform:rotate(45deg)}.retail-popup__close::after{transform:rotate(-45deg)}.retail-popup.open{transform:translate3d(0, 0, 0) scale(1)}.retail-popup-wrap{position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,0.6);z-index:1000;display:none}.retail-column{display:flex;max-width:1389px;height:100%}.retail-column__aside{width:253px;background:#EAEBEC;min-height:300px}.retail-column__content{flex:1 0 auto;padding:0 58px;min-height:300px}.retail-menu{padding:8px 20px 8px 15px}.retail-menu__btn{display:inline-flex;align-items:center;justify-content:center;vertical-align:top;width:100%;height:60px;background:rgba(122,122,122,0.1) !important;font-weight:bold;font-size:16px;color:#363A41 !important;text-decoration:none !important;padding:0 30px;margin-top:20px;border-radius:5px;text-align:center;transition:.25s ease}.retail-menu__btn span{display:block}.retail-menu__btn:hover{background:rgba(122,122,122,0.15) !important}.retail-menu__btn:nth-child(1){margin-top:0}.retail-menu__btn_active{color:white !important;background:#0068FF !important}.retail-menu__btn_active:hover{background:#005add !important}.retail-menu__btn_big{font-size:18px}.retail-full-height{height:100%}.retail .btn{display:inline-block;vertical-align:top;background:rgba(122,122,122,0.1);border-radius:58px;height:60px;line-height:60px;padding:0 30px;font-size:18px;font-weight:600;text-align:center;color:#0068FF;text-decoration:none;cursor:pointer;appearance:none;border:none;box-shadow:none;transition:.25s ease}.retail .btn:hover{background:rgba(122,122,122,0.15)}.retail .btn:active{background:rgba(122,122,122,0.25)}.retail .btn_max{min-width:356px}.retail .btn_invert{background:#0068FF;color:white}.retail .btn_invert:hover{background:#005add}.retail .btn_invert:active{background:#0045aa}.retail .btn_whatsapp{background:#33D16B;color:white;padding:0 62px}.retail .btn_whatsapp:hover{background:#22CA5D}.retail .btn_submit{min-width:218px}.retail .toggle-box{display:none}.retail-table{width:100%;border:none;border-radius:20px}.retail-table thead th{background:rgba(122,122,122,0.15);font-size:16px}.retail-table tbody tr td{border-bottom:1px solid #ccc}.retail-table td,.retail-table th{color:#333;font-size:14px;line-height:60px;padding:4px 4px 4px 6px;max-width:300px;vertical-align:middle}.retail-table-no-wrap{white-space:nowrap}.retail-table-center{text-align:center}.retail-table-sort__btn,.retail-table-sort__switch{cursor:pointer}.retail-table-sort__btn:hover,.retail-table-sort__switch:hover{color:#005add}.retail-table-sort__btn{float:left;clear:both;line-height:15px;font-size:20px;padding-left:10px;padding-right:10px;opacity:0;transition:opacity .2s ease-out}.retail-table-sort__btn-wrap{position:absolute}.retail-table-sort__asc{padding-top:15px;padding-bottom:0}.retail-table-sort__desc{padding-top:0;padding-bottom:15px}.retail-table-sort thead th:hover .retail-table-sort__btn,.retail-table-sort thead td:hover .retail-table-sort__btn{opacity:1}.retail-collapsible__input{display:none}label.retail-collapsible__title{cursor:pointer;font-weight:normal;text-align:center;padding:0;position:relative;line-height:1;width:100%}label.retail-collapsible__title:before{content:'\25B6';opacity:0;transition:opacity .2s ease-out}label.retail-collapsible__title:hover:before{opacity:1}.retail-collapsible__content{text-align:left;font-size:12px;line-height:1.5;background:#fff;border:1px solid rgba(122,122,122,0.15);position:absolute;top:20px;left:0;padding:18px;margin:0;border-radius:20px;overflow:hidden;max-height:0;transition-duration:.2s;transition-timing-function:ease-out;transition-property:max-height,visibility;visibility:hidden}.retail-collapsible__input:checked+.retail-collapsible__title .retail-collapsible__content{max-height:100vh;visibility:visible}.retail-error-msg,.retail-error-msg:after,.retail-error-msg:before{color:#dd2e44}
\ No newline at end of file
diff --git a/retailcrm/views/js/retailcrm-export.js b/retailcrm/views/js/retailcrm-export.js
new file mode 100644
index 0000000..f7d0da3
--- /dev/null
+++ b/retailcrm/views/js/retailcrm-export.js
@@ -0,0 +1,143 @@
+/**
+ * MIT License
+ *
+ * Copyright (c) 2020 DIGITAL RETAIL TECHNOLOGIES SL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
+ * versions in the future. If you wish to customize PrestaShop for your
+ * needs please refer to http://www.prestashop.com for more information.
+ *
+ * @author DIGITAL RETAIL TECHNOLOGIES SL
+ * @copyright 2020 DIGITAL RETAIL TECHNOLOGIES SL
+ * @license https://opensource.org/licenses/MIT The MIT License
+ *
+ * Don't forget to prefix your containers with your own identifier
+ * to avoid any conflicts with others containers.
+ */
+$(function () {
+ function RetailcrmExportForm() {
+ this.form = $('input[name=RETAILCRM_EXPORT_ORDERS_COUNT]').closest('form').get(0);
+
+ if (typeof this.form === 'undefined') {
+ return false;
+ }
+
+ this.ordersCount = parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_ORDERS_COUNT"]').val());
+ this.customersCount = parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_CUSTOMERS_COUNT"]').val());
+ this.ordersStepSize = parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_ORDERS_STEP_SIZE"]').val());
+ this.customersStepSize = parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE"]').val());
+ this.ordersStep = 0;
+ this.customersStep = 0;
+
+ this.submitButton = $(this.form).find('button[id="export-orders-submit"]').get(0);
+ this.progressBar = $(this.form).find('div[id="export-orders-progress"]').get(0);
+
+ this.submitAction = this.submitAction.bind(this);
+ this.exportAction = this.exportAction.bind(this);
+ this.exportDone = this.exportDone.bind(this);
+ this.initializeProgressBar = this.initializeProgressBar.bind(this);
+ this.updateProgressBar = this.updateProgressBar.bind(this);
+
+ $(this.submitButton).click(this.submitAction);
+ }
+
+ RetailcrmExportForm.prototype.submitAction = function (event) {
+ event.preventDefault();
+
+ this.initializeProgressBar();
+ this.exportAction();
+ };
+
+ RetailcrmExportForm.prototype.exportAction = function () {
+ let data = {};
+ if (this.ordersStep * this.ordersStepSize < this.ordersCount) {
+ this.ordersStep++;
+ data = {
+ submitretailcrm: 1,
+ RETAILCRM_EXPORT_ORDERS_STEP: this.ordersStep
+ }
+ } else {
+ if (this.customersStep * this.customersStepSize < this.customersCount) {
+ this.customersStep++;
+ data = {
+ submitretailcrm: 1,
+ RETAILCRM_EXPORT_CUSTOMERS_STEP: this.customersStep
+ }
+ } else {
+ return this.exportDone();
+ }
+ }
+
+ let _this = this;
+
+ $.ajax({
+ url: this.form.action,
+ method: this.form.method,
+ timeout: 0,
+ data: data
+ })
+ .done(function (response) {
+ _this.updateProgressBar();
+ _this.exportAction();
+ })
+ };
+
+ RetailcrmExportForm.prototype.initializeProgressBar = function () {
+ $(this.submitButton).addClass('retail-hidden');
+ $(this.progressBar)
+ .removeClass('retail-hidden')
+ .append($('', {class: 'retail-progress__loader', text: '0%'}))
+
+ window.addEventListener('beforeunload', this.confirmLeave);
+ };
+
+ RetailcrmExportForm.prototype.updateProgressBar = function () {
+ let processedOrders = this.ordersStep * this.ordersStepSize;
+ if (processedOrders > this.ordersCount)
+ processedOrders = this.ordersCount;
+
+ let processedCustomers = this.customersStep * this.customersStepSize;
+ if (processedCustomers > this.customersCount)
+ processedCustomers = this.customersCount;
+
+ const processed = processedOrders + processedCustomers;
+ const total = this.ordersCount + this.customersCount;
+ const percents = Math.round(100 * processed / total);
+
+ $(this.progressBar).find('.retail-progress__loader').text(percents + '%');
+ $(this.progressBar).find('.retail-progress__loader').css('width', percents + '%');
+ $(this.progressBar).find('.retail-progress__loader').attr('alt', processed + '/' + total);
+ };
+
+ RetailcrmExportForm.prototype.confirmLeave = function (event) {
+ event.preventDefault();
+ e.returnValue = 'Export process has been started';
+ }
+
+ RetailcrmExportForm.prototype.exportDone = function () {
+ window.removeEventListener('beforeunload', this.confirmLeave);
+ alert('Export is done')
+ }
+
+ window.RetailcrmExportForm = RetailcrmExportForm;
+});
diff --git a/retailcrm/views/js/retailcrm-export.min.js b/retailcrm/views/js/retailcrm-export.min.js
new file mode 100644
index 0000000..54c4d06
--- /dev/null
+++ b/retailcrm/views/js/retailcrm-export.min.js
@@ -0,0 +1,37 @@
+/**
+ * MIT License
+ *
+ * Copyright (c) 2020 DIGITAL RETAIL TECHNOLOGIES SL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
+ * versions in the future. If you wish to customize PrestaShop for your
+ * needs please refer to http://www.prestashop.com for more information.
+ *
+ * @author DIGITAL RETAIL TECHNOLOGIES SL
+ * @copyright 2020 DIGITAL RETAIL TECHNOLOGIES SL
+ * @license https://opensource.org/licenses/MIT The MIT License
+ *
+ * Don't forget to prefix your containers with your own identifier
+ * to avoid any conflicts with others containers.
+ */
+$(function(){function t(){if(this.form=$("input[name=RETAILCRM_EXPORT_ORDERS_COUNT]").closest("form").get(0),void 0===this.form)return!1;this.ordersCount=parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_ORDERS_COUNT"]').val()),this.customersCount=parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_CUSTOMERS_COUNT"]').val()),this.ordersStepSize=parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_ORDERS_STEP_SIZE"]').val()),this.customersStepSize=parseInt($(this.form).find('input[name="RETAILCRM_EXPORT_CUSTOMERS_STEP_SIZE"]').val()),this.ordersStep=0,this.customersStep=0,this.submitButton=$(this.form).find('button[id="export-orders-submit"]').get(0),this.progressBar=$(this.form).find('div[id="export-orders-progress"]').get(0),this.submitAction=this.submitAction.bind(this),this.exportAction=this.exportAction.bind(this),this.exportDone=this.exportDone.bind(this),this.initializeProgressBar=this.initializeProgressBar.bind(this),this.updateProgressBar=this.updateProgressBar.bind(this),$(this.submitButton).click(this.submitAction)}t.prototype.submitAction=function(t){t.preventDefault(),this.initializeProgressBar(),this.exportAction()},t.prototype.exportAction=function(){let t={};if(this.ordersStep*this.ordersStepSize",{class:"retail-progress__loader",text:"0%"})),window.addEventListener("beforeunload",this.confirmLeave)},t.prototype.updateProgressBar=function(){let t=this.ordersStep*this.ordersStepSize;t>this.ordersCount&&(t=this.ordersCount);let e=this.customersStep*this.customersStepSize;e>this.customersCount&&(e=this.customersCount);var s=t+e,i=this.ordersCount+this.customersCount,r=Math.round(100*s/i);$(this.progressBar).find(".retail-progress__loader").text(r+"%"),$(this.progressBar).find(".retail-progress__loader").css("width",r+"%"),$(this.progressBar).find(".retail-progress__loader").attr("alt",s+"/"+i)},t.prototype.confirmLeave=function(t){t.preventDefault(),e.returnValue="Export process has been started"},t.prototype.exportDone=function(){window.removeEventListener("beforeunload",this.confirmLeave),alert("Export is done")},window.RetailcrmExportForm=t});
\ No newline at end of file
diff --git a/retailcrm/views/js/retailcrm.js b/retailcrm/views/js/retailcrm.js
index a504d4b..640f7d1 100644
--- a/retailcrm/views/js/retailcrm.js
+++ b/retailcrm/views/js/retailcrm.js
@@ -43,6 +43,7 @@ $(function(){
this.player.init();
this.tabs.init();
this.uploadForm.init(this.settingsTabs.init());
+ this.exportForm.init()
this.popup.init();
this.toggleBox();
this.trimConsultant();
@@ -233,6 +234,13 @@ $(function(){
}
}
},
+ exportForm: {
+ init: function () {
+ if (!(typeof RetailcrmExportForm === 'undefined')) {
+ new RetailcrmExportForm();
+ }
+ }
+ },
tabs: {
init: function () {
$('.retail-tabs__btn').on('click', this.swithTab);
diff --git a/retailcrm/views/js/retailcrm.min.js b/retailcrm/views/js/retailcrm.min.js
index 7bb64a9..3fedc14 100644
--- a/retailcrm/views/js/retailcrm.min.js
+++ b/retailcrm/views/js/retailcrm.min.js
@@ -1 +1,37 @@
-$(function(){var Main={init:function(){this.selects.init();this.tableSort.init();this.player.init();this.tabs.init();this.uploadForm.init(this.settingsTabs.init());this.popup.init();this.toggleBox();this.trimConsultant();this.showSettings()},selects:{init:function(){var _this=this;try{$(".jq-select").SumoSelect();$("li.opt").each((_,el)=>{if($(el).find("label").html().length===0){let select=$(el).closest("ul").closest("div").parent().find("select");$(el).find("label").html(select.attr("placeholder"));$(el).addClass("disabled")}});_this.update();$(document).on("change",".jq-select",function(){_this.update()})}catch(e){console.warn("Cannot initialize select: "+e.message)}},update:function(){var selected={};let selects=$(".retail-tab__enabled").find("select:not(#RETAILCRM_API_DELIVERY_DEFAULT, #RETAILCRM_API_PAYMENT_DEFAULT)");selects.each((i,select)=>{var value=$(select).val();if(value&&value.length){selected[i]=$('option[value="'+$(select).val()+'"]',$(select)).index()}});let values=Object.values(selected);selects.each((i,select)=>{$("option",select).each((o,option)=>{if($.inArray(o,values)===-1||typeof selected[i]!=="undefined"&&selected[i]==o){select.sumo.enableItem(o)}else{select.sumo.disableItem(o)}})})}},tableSort:{init:function(){var _this=this;$(".retail-table-sort").each((i,table)=>{$(table).find(".retail-table-sort__switch").each((j,header)=>{const column=$(header).closest("th,td").index();$(header).click(e=>{e.preventDefault();_this.sort(table,column)})});$(table).find(".retail-table-sort__asc").each((j,header)=>{const column=$(header).closest("th,td").index();$(header).click(e=>{e.preventDefault();_this.sort(table,column,"asc")})});$(table).find(".retail-table-sort__desc").each((j,header)=>{const column=$(header).closest("th,td").index();$(header).click(e=>{e.preventDefault();_this.sort(table,column,"desc")})});$(table).find(".retail-table-sort__initial").click()})},sort:function(table,column,direction=undefined){let rows,switching,i,x,y,shouldSwitch,dir,switchcount=0;switching=true;dir=direction?direction:"asc";while(switching){switching=false;rows=table.rows;for(i=1;iy.innerHTML.toLowerCase()){shouldSwitch=true;break}}else if(dir==="desc"){if(x.innerHTML.toLowerCase()