From 1ad5a19793a00eb48e346294e73b28f23061c78c Mon Sep 17 00:00:00 2001
From: Dmitry Pankov <dmitry@enula.ru>
Date: Fri, 10 Jun 2022 18:53:08 +0300
Subject: [PATCH] Initial commit

---
 admin/controller/analytics/metrica.php        | 141 ++++++++++
 admin/language/en-gb/analytics/metrica.php    |  79 ++++++
 admin/language/ru-ru/analytics/metrica.php    |  75 ++++++
 admin/model/analytics/metrica.php             |  58 +++++
 .../view/image/analytics/metrica/logo-en.svg  |  14 +
 .../view/image/analytics/metrica/logo-ru.svg  |  14 +
 admin/view/template/analytics/metrica.twig    | 228 ++++++++++++++++
 catalog/controller/analytics/metrica.php      | 243 ++++++++++++++++++
 catalog/model/analytics/metrica.php           |  38 +++
 catalog/view/javascript/metrica.js            |   0
 catalog/view/template/analytics/metrica.twig  |  64 +++++
 install.json                                  |   6 +
 12 files changed, 960 insertions(+)
 create mode 100644 admin/controller/analytics/metrica.php
 create mode 100644 admin/language/en-gb/analytics/metrica.php
 create mode 100644 admin/language/ru-ru/analytics/metrica.php
 create mode 100644 admin/model/analytics/metrica.php
 create mode 100644 admin/view/image/analytics/metrica/logo-en.svg
 create mode 100644 admin/view/image/analytics/metrica/logo-ru.svg
 create mode 100644 admin/view/template/analytics/metrica.twig
 create mode 100644 catalog/controller/analytics/metrica.php
 create mode 100644 catalog/model/analytics/metrica.php
 create mode 100644 catalog/view/javascript/metrica.js
 create mode 100644 catalog/view/template/analytics/metrica.twig
 create mode 100644 install.json

diff --git a/admin/controller/analytics/metrica.php b/admin/controller/analytics/metrica.php
new file mode 100644
index 0000000..897f677
--- /dev/null
+++ b/admin/controller/analytics/metrica.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace Opencart\Admin\Controller\Extension\Yandex\Analytics;
+
+class Metrica extends \Opencart\System\Engine\Controller
+{
+	public function index(): void
+	{
+		$this->load->language('extension/yandex/analytics/metrica');
+
+		$this->document->setTitle($this->language->get('heading_title'));
+
+		$this->load->model('extension/yandex/analytics/metrica');
+
+		if ($this->model_extension_yandex_analytics_metrica->hasUpdates()) {
+			$data['notify_module_version'] = $this->language->get('text_notify_module_version');
+		}
+
+		$data['breadcrumbs'] = [];
+
+		$data['breadcrumbs'][] = [
+			'text' => $this->language->get('text_home'),
+			'href' => $this->url->link('common/dashboard', 'user_token' . '=' . $this->session->data['user_token'])
+		];
+
+		$data['breadcrumbs'][] = [
+			'text' => $this->language->get('text_extension'),
+			'href' => $this->url->link('marketplace/extension', 'user_token' . '=' . $this->session->data['user_token'] . '&type=analytics')
+		];
+
+		$data['breadcrumbs'][] = [
+			'text' => $this->language->get('heading_title'),
+			'href' => $this->url->link('extension/yandex/analytics/metrica', 'user_token=' . $this->session->data['user_token'])
+		];
+
+		$data['save'] = $this->url->link('extension/yandex/analytics/metrica|save', 'user_token=' . $this->session->data['user_token']);
+		$data['back'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics');
+
+		$data['logo'] = $this->url->link('extension/yandex/analytics/metrica|logo', 'user_token=' . $this->session->data['user_token']);
+		$data['find_ya_metrica'] =  $this->url->link('extension/yandex/analytics/metrica|find_ya_metrica', 'user_token=' . $this->session->data['user_token']);
+
+		$data['has_settings'] = !!$this->model_setting_setting->getSetting('analytics_metrica');
+		$data['analytics_metrica_status'] = $this->model_setting_setting->getValue('analytics_metrica_status');
+		$data['analytics_metrica_code'] = $this->model_setting_setting->getValue('analytics_metrica_code');
+		$data['analytics_metrica_codes'] = json_decode($this->model_setting_setting->getValue('analytics_metrica_codes'), true);
+		$data['analytics_metrica_log'] = $this->model_setting_setting->getValue('analytics_metrica_log');
+		$data['analytics_metrica_debug'] = $this->model_setting_setting->getValue('analytics_metrica_debug');
+
+		$data['log'] = '';
+
+		$file = DIR_LOGS . 'log_yandex_metrica.log';
+
+		if (file_exists($file)) {
+			$size = filesize($file);
+
+			if ($size >= 3145728) {
+				$suffix = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+
+				$i = 0;
+
+				while (($size / 1024) > 1) {
+					$size = $size / 1024;
+					$i++;
+				}
+
+				$data['error_warning'] = sprintf($this->language->get('error_warning'), basename($file), round(substr($size, 0, strpos($size, '.') + 4), 2) . $suffix[$i]);
+			} else {
+				$data['log'] = file_get_contents($file, FILE_USE_INCLUDE_PATH, null);
+			}
+		}
+
+		$data['user_token'] = $this->session->data['user_token'];
+
+		$data['header'] = $this->load->controller('common/header');
+		$data['column_left'] = $this->load->controller('common/column_left');
+		$data['footer'] = $this->load->controller('common/footer');
+
+		$this->response->setOutput($this->load->view('extension/yandex/analytics/metrica', $data));
+	}
+
+
+	public function save(): void
+	{
+		$this->load->language('extension/yandex/analytics/metrica');
+
+		$json = [];
+
+		if (!$this->user->hasPermission('modify', 'extension/yandex/analytics/metrica')) {
+			$json['error']['warning'] = $this->language->get('error_permission');
+		}
+
+		if (isset($this->request->post['analytics_metrica_codes'])) {
+			foreach ($this->request->post['analytics_metrica_codes'] as $row => $metrica) {
+				if (empty($metrica['code'])) {
+					$json['error']['metrica-' . $row . '-code'] = $this->language->get('error_metric_code');
+				}
+			}
+		}
+
+		if (!$json) {
+			$this->model_setting_setting->editSetting('analytics_metrica', $this->request->post);
+			$json['success'] = $this->language->get('text_success');
+		}
+
+		$this->response->addHeader('Content-Type: application/json');
+		$this->response->setOutput(json_encode($json));
+	}
+
+	public function logo(): void
+	{
+		$this->response->addHeader('Content-Type: image/svg+xml');
+
+		if ($this->config->get('config_admin_language') === 'ru-ru') {
+			$this->response->setOutput(file_get_contents(DIR_EXTENSION . 'yandex/admin/view/image/analytics/metrica/logo-ru.svg'));
+		} else {
+			$this->response->setOutput(file_get_contents(DIR_EXTENSION . 'yandex/admin/view/image/analytics/metrica/logo-en.svg'));
+		}
+	}
+
+	public function findMetrica()
+	{
+		$json = $this->model_extension_yandex_analytics_metrica->findMetrica();
+
+		$this->response->addHeader('Content-Type: application/json');
+		$this->response->setOutput(json_encode($json));
+	}
+
+	public function install()
+	{
+		$this->load->model('setting/event');
+
+		$this->model_setting_event->addEvent('yandex_metrica_checkout', '', 'catalog/controller/checkout/success/before', 'extension/yandex/analytics/metrica|checkout');
+	}
+
+	public function uninstall()
+	{
+		$this->load->model('setting/event');
+
+		$this->model_setting_event->deleteEventByCode('yandex_metrica_checkout');
+	}
+}
diff --git a/admin/language/en-gb/analytics/metrica.php b/admin/language/en-gb/analytics/metrica.php
new file mode 100644
index 0000000..254d788
--- /dev/null
+++ b/admin/language/en-gb/analytics/metrica.php
@@ -0,0 +1,79 @@
+<?php
+$_['heading_title']               = 'Yandex.Metrica';
+
+// Text
+$_['text_yes']                    = 'Yes';
+$_['text_no']                     = 'No';
+$_['text_enabled']                = 'Enabled';
+$_['text_disabled']               = 'Disabled';
+$_['text_loading']                = 'Loading...';
+$_['text_extension']              = 'Extensions';
+$_['text_success']	              = 'Settings changed';
+$_['text_edit']                   = 'Module settings';
+$_['text_signup']                 = 'To get started, enter the relevant Yandex.Metrica tag numbers and the company information. The tag number is specified on the page "<a href="https://metrika.yandex.ru/list">My tags</a>" in Yandex.Metrica next to the site address.
+You can create a new tag in the Yandex.Metrica interface at <a href="https://metrika.yandex.ru/">https://metrika.yandex.ru/</a>';
+$_['text_default']                = 'By default';
+$_['text_find_metric']            = 'Find metric';
+$_['text_write_logs']             = 'Record logs';
+$_['text_debug']                  = 'Debug mode';
+$_['text_logs']                   = 'Logs';
+$_['text_start_of_search_metric'] = 'Start of search';
+$_['text_end_of_search_metric']   = 'End of search';
+$_['text_metrics_found']          = 'The following metrics were found';
+$_['text_metrics_not_found']      = 'Metrics not found';
+$_['text_counter_number']         = 'Tag number';
+$_['text_notify_module_version']  = 'New module version detected';
+$_['text_instructions']           = <<<INSTRUCTIONS
+<h2>Instructions for configuring the module</h2>
+</br>
+<h3>"Main" tab.</h3> 
+<p>The main settings for the module are here.</p>
+<p><b>Status</b>: Enabling/Disabling the module.</p>
+<p><b>Metrics</b>: Metric codes are added here, as well as the status of enabling/disabling Session Replay for metrics.</p>
+</br>
+<p>To add a metric, click "+".</p>
+<p>Next, in the "Metric code" column, enter the metric code created on the site <a href="https://metrika.yandex.ru/">https://metrika.yandex.ru/</a></p>
+<p>Also on this tab, you can enable and disable Session Replay in the Settings column.</p>
+</br>
+<h3>"Metric search" tab</h3>
+<p>Here you can check whether a Yandex.Metrica tag is installed on your site. To do this, go to the "Metric search" tab and click "Find metric".</p>
+</br>
+<h3>"Logging" tab</h3>
+<p>On this tab, module error logging is enabled and disabled. When this option is enabled, entries about errors that occur will appear here.</p>
+</br>
+<p>To save the module settings, click the blue "Save" button in the upper-right corner.</p>
+INSTRUCTIONS;
+
+// Tabs
+$_['tab_metrics']                 = 'Main';
+$_['tab_metric_search']           = 'Metric search';
+$_['tab_logging']                 = 'Logging';
+$_['tab_info']                    = 'Instructions';
+
+// Column
+$_['column_metric_code']          = 'Metric code';
+$_['column_settings']             = 'Settings';
+$_['column_sort_order']           = 'Sort order';
+$_['column_name']                 = 'Name';
+$_['column_selector']             = 'Selector';
+$_['column_action']               = 'Action';
+
+
+// Entry
+$_['entry_code']                  = 'Yandex.Metrica code';
+$_['entry_metrics_list']          = 'Metrics';
+$_['entry_status']                = 'Status';
+$_['entry_webvisor']              = 'Session Replay';
+$_['entry_clickmap']              = 'Click Map';
+$_['entry_ecommerce']             = 'E-commerce';
+$_['entry_tracking_the_hash'] = 'Hash tracking in the address bar';
+
+// Error
+$_['error_permission']            = 'You do not have the rights to manage this module';
+$_['error_metric_code']           = 'Metric code missing';
+$_['error_selector']              = 'Selector missing';
+$_['error_selector_name']         = 'Name missing';
+$_['error_selector_defined']      = 'This selector has already been defined';
+
+// Button
+$_['button_remove']               = 'Remove';
diff --git a/admin/language/ru-ru/analytics/metrica.php b/admin/language/ru-ru/analytics/metrica.php
new file mode 100644
index 0000000..12202af
--- /dev/null
+++ b/admin/language/ru-ru/analytics/metrica.php
@@ -0,0 +1,75 @@
+<?php
+$_['heading_title']               = 'Яндекс.Метрика';
+
+// Text
+$_['text_yes']                    = 'Да';
+$_['text_no']                     = 'Нет';
+$_['text_enabled']                = 'Включено';
+$_['text_disabled']               = 'Отключено';
+$_['text_loading']                = 'Загрузка...';
+$_['text_extension']              = 'Расширения';
+$_['text_success']	              = 'Настройки успешно изменены!';
+$_['text_edit']                   = 'Настройки модуля';
+$_['text_signup']                 = 'Для начала работы укажите номер счётчика Яндекс.Метрики. Номер счётчика можно найти на странице <a href="https://metrika.yandex.ru/list/" target="_blank">"Мои счетчики"</a> в Метрике рядом с адресом сайта.<br>
+Если у вас еще нет счетчика Метрики, то создайте его на сайте <a href="https://metrika.yandex.ru/" target="_blank">Метрики</a>.<br>
+Убедитесь, что у вас в настройках счетчика в Метрике включена опция «Электронная коммерция». Подробнее о том, как включить электронную коммерцию можно почитать в <a href="https://yandex.ru/support/metrica/ecommerce/enabling.html" target="_blank">Справке</a>.';
+$_['text_default']                = 'По умолчанию';
+$_['text_find_metric']            = 'Найти метрику';
+$_['text_write_logs']             = 'Записывать логи';
+$_['text_debug']                  = 'Режим отладки';
+$_['text_logs']                   = 'Логи';
+$_['text_start_of_search_metric'] = 'Начало поиска';
+$_['text_end_of_search_metric']   = 'Конец поиска';
+$_['text_metrics_found']          = 'Найдены следующие метрики';
+$_['text_metrics_not_found']      = 'Метрики не найдены';
+$_['text_counter_number']         = 'Номер счетчика';
+$_['text_notify_module_version']  = 'Обнаружена новая версия модуля. Узнайте подробнее про установку обновления в <a href="https://yandex.ru/support/metrica/ecommerce/opencart.html#update" target="_blank">Справке Яндекс.Метрики</a>';
+$_['text_instructions']           = <<<INSTRUCTIONS
+<h3>Вкладка «Основное»</h3>
+<p>Этот раздел содержит основные настройки модуля.<br>
+В этом разделе вы сможете включить или выключить модуль, выбрав соответствующее значение в поле "Статус".<br>
+В поле "Номер счетчика" вам необходимо указать ваш номер счетчика Метрики.</p>
+<p>Модуль может помочь вам обнаружить уже установленный на сайте счетчик Метрики. Чтобы узнать его номер, перейдите на вкладку "Поиск метрики" и нажмите кнопку "Найти метрику"</p>
+<p><span style="color:#cf2015">Внимание!</span> Убедитесь, что в OpenCart не активирован другой аналогичный модуль, устанавливающий счетчик Метрики. Для этого перейдите на страницу Расширения → Модификации и поищите модуль с фрагментом в названии: «Metrica» или «Metrika». Если такой модуль установлен, отключите его.</p>
+<p>По умолчанию опция Вебвизор включена. Она позволяет записывать визиты посетителей, чтобы вы могли более полно анализировать статистику. Вы можете включить или отключить опцию Вебвизор для каждого из ваших счетчиков Метрики.</p>
+<br>
+<h3>Вкладка «Поиск Метрики»</h3>
+<p>На этой вкладке вы можете проверить установлен ли на вашем сайте счетчик Метрики. Чтобы узнать его номер, перейдите на вкладку "Поиск метрики" и нажмите кнопку "Найти метрику".</p>
+<br>
+<h3>Вкладка «Логирование»</h3>
+<p>Чтобы включить запись ошибок, которые происходят во время работы модуля, на вкладке "Логирование" установите переключатель в положение «Да».</p>
+<p>Подробнее о настройке модуля можно прочитать в <a href="https://yandex.ru/support/metrica/ecommerce/opencart.html" target="_blank">Справке</a>.</p>
+INSTRUCTIONS;
+
+// Tabs
+$_['tab_metrics']                 = 'Основное';
+$_['tab_metric_search']           = 'Поиск метрики';
+$_['tab_logging']                 = 'Логирование';
+$_['tab_info']                    = 'Инструкция';
+
+// Column
+$_['column_metric_code']          = 'Номер счетчика Метрики';
+$_['column_settings']             = 'Настройки';
+$_['column_sort_order']           = 'Порядок сортировки';
+$_['column_name']                 = 'Название';
+$_['column_selector']             = 'Селектор';
+$_['column_action']               = 'Действие';
+
+// Entry
+$_['entry_code']                  = 'Код Яндекс.Метрика';
+$_['entry_metrics_list']          = 'Метрики';
+$_['entry_status']                = 'Статус';
+$_['entry_webvisor']              = 'Вебвизор';
+$_['entry_clickmap']              = 'Карта кликов';
+$_['entry_ecommerce']             = 'Электронная коммерция';
+$_['entry_tracking_the_hash']     = 'Отслеживание хеша в адресной строке';
+
+// Error
+$_['error_permission']            = 'У Вас нет прав для управления данным модулем!';
+$_['error_metric_code']           = 'Отсутствует номер счетчика Метрики!';
+$_['error_selector']              = 'Отсутсвует селектор!';
+$_['error_selector_name']         = 'Отсутсвует название!';
+$_['error_selector_defined']      = 'Такой селектор уже задан!';
+
+// Button
+$_['button_remove']               = 'Удалить';
diff --git a/admin/model/analytics/metrica.php b/admin/model/analytics/metrica.php
new file mode 100644
index 0000000..1ead863
--- /dev/null
+++ b/admin/model/analytics/metrica.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Opencart\Admin\Model\Extension\Yandex\Analytics;
+
+class Metrica extends \Opencart\System\Engine\Model
+{
+    public function hasUpdates(): bool
+    {
+        $this->load->model('setting/extension');
+
+        if (!empty($extension = $this->model_setting_extension->getInstallByCode('yandex')) && $latestVersion = $this->getLatestVersion())
+            return version_compare($extension['version'], $latestVersion) < 0;
+        else
+            return false;
+    }
+
+    protected function getLatestVersion(): ?string
+    {
+        if ($json = json_decode($this->file_get_contents_curl('https://api.github.com/repos/dima424658/oc-yandex-metrica/releases/latest'), true))
+            return $json['tag_name'];
+        else
+            return null;
+    }
+
+    public function findMetrica(): array
+    {
+        $count_of_metrik = 0;
+        $codes_metrik = '';
+        $page_code = $this->file_get_contents_curl(HTTP_CATALOG);
+        $status = str_contains($page_code, 'https://mc.yandex.ru/metrika/tag.js');
+
+        if ($status) {
+            $regexp = '/ym\((.+?), \"init\"/';
+            $count_of_metrik = preg_match_all($regexp, $page_code, $matches);
+            $codes_metrik = $matches[1];
+        }
+
+        return [
+            'success' => $status,
+            'count_of_metrik' => $count_of_metrik,
+            'codes_metrik' => $codes_metrik,
+        ];
+    }
+
+    protected function file_get_contents_curl(string $url): string
+    {
+        if ($ch = curl_init()) {
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+            curl_setopt($ch, CURLOPT_URL, $url);
+            curl_setopt($ch, CURLOPT_USERAGENT, 'OpenCart');
+
+            if (($result = curl_exec($ch)) !== false)
+                return $result;
+        }
+
+        return '';
+    }
+}
diff --git a/admin/view/image/analytics/metrica/logo-en.svg b/admin/view/image/analytics/metrica/logo-en.svg
new file mode 100644
index 0000000..f32422a
--- /dev/null
+++ b/admin/view/image/analytics/metrica/logo-en.svg
@@ -0,0 +1,14 @@
+<svg width="400" height="272" viewBox="0 0 400 272" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="400" height="272" fill="white"/>
+<path d="M71.0149 210V204.377C71.0149 200.135 70.4907 198.896 68.8704 195.37L60.3875 176.879H54.3828L65.2485 200.517V210H71.0149ZM74.7798 192.891L81.8329 176.879H75.9712L68.918 192.891H74.7798ZM135.018 210.477C137.686 210.477 139.879 209.238 141.404 206.902L141.689 210H146.932V176.879H141.261V188.841C139.831 186.696 137.734 185.552 135.256 185.552C129.775 185.552 125.963 190.175 125.963 198.181C125.963 206.092 129.68 210.477 135.018 210.477ZM169.854 208.38V203.757C168.091 204.949 165.136 205.997 162.372 205.997C158.226 205.997 156.654 204.043 156.415 200.04H170.093V197.038C170.093 188.698 166.423 185.552 160.752 185.552C153.842 185.552 150.554 190.842 150.554 198.086C150.554 206.426 154.652 210.477 161.896 210.477C165.518 210.477 168.186 209.524 169.854 208.38ZM98.5127 193.654C98.5127 187.84 95.558 185.648 89.5533 185.648C85.8361 185.648 82.8337 187.22 81.1181 188.459V193.368C82.929 191.891 85.6931 190.175 88.8861 190.175C91.6025 190.175 92.8416 191.128 92.8416 193.702V195.227H91.9361C83.2626 195.227 79.4025 198.038 79.4025 203.042C79.4025 207.665 82.3572 210.334 86.7416 210.334C90.0775 210.334 91.5072 209.238 92.6033 208.094H92.8416C92.8892 208.713 93.0799 209.524 93.2705 210H98.7986C98.608 208.046 98.5127 206.092 98.5127 204.138V193.654ZM108.807 192.796C109.855 191.509 111.523 190.413 113.572 190.413C115.574 190.413 116.479 191.271 116.479 193.082V210H122.15V192.51C122.15 187.745 120.244 185.648 115.621 185.648C112.238 185.648 110.236 186.887 109.092 188.031H108.807L108.664 186.029H103.135V210H108.807V192.796ZM187.297 210H193.683L185.867 197.657L192.634 186.029H186.963L182.865 193.273L178.29 186.029H171.904L179.147 197.514L171.618 210H177.384L182.197 201.899L187.297 210ZM136.686 190.032C139.736 190.032 141.261 192.463 141.261 197.991C141.261 203.567 139.64 205.997 136.447 205.997C133.35 205.997 131.825 203.614 131.825 198.181C131.825 192.51 133.445 190.032 136.686 190.032ZM160.657 190.032C163.468 190.032 164.326 192.367 164.326 195.37V195.846H156.415C156.558 192.034 157.94 190.032 160.657 190.032ZM92.8416 204.281C92.1267 205.33 90.7924 206.188 88.7908 206.188C86.408 206.188 85.2165 204.806 85.2165 202.709C85.2165 199.945 87.1705 198.992 92.0314 198.992H92.8416V204.281Z" fill="black"/>
+<path d="M219.868 210H224.824L231.592 184.981V210H237.263V176.879H229.542L222.87 201.422L216.198 176.879H208.383V210H213.101V184.981L219.868 210Z" fill="black"/>
+<path d="M260.379 208.38V203.757C258.615 204.949 255.661 205.997 252.897 205.997C248.75 205.997 247.178 204.043 246.94 200.04H260.617V197.038C260.617 188.698 256.947 185.552 251.276 185.552C244.366 185.552 241.078 190.842 241.078 198.086C241.078 206.426 245.176 210.477 252.42 210.477C256.042 210.477 258.711 209.524 260.379 208.38ZM251.181 190.032C253.993 190.032 254.85 192.367 254.85 195.37V195.846H246.94C247.082 192.034 248.465 190.032 251.181 190.032Z" fill="black"/>
+<path d="M262.51 186.029V190.509H265.512V202.804C265.512 207.284 268.038 210.477 273.281 210.477C276.045 210.477 277.617 209.857 277.617 209.857V205.235C277.617 205.235 276.569 205.997 274.663 205.997C272.28 205.997 271.184 204.615 271.184 202.852V190.509H277.141V186.029H271.184V180.787H265.512V186.029H262.51Z" fill="black"/>
+<path d="M286.144 195.036C287.716 192.129 290.051 190.652 293.53 190.842V185.505C290.29 185.695 287.621 187.649 286.287 190.795H286.144L286.001 186.029H280.473V210H286.144V195.036Z" fill="black"/>
+<path d="M299.433 183.074C301.292 183.074 302.769 181.788 302.769 179.738C302.769 177.737 301.292 176.402 299.433 176.402C297.575 176.402 296.145 177.737 296.145 179.738C296.145 181.788 297.575 183.074 299.433 183.074ZM296.622 186.029V210H302.293V186.029H296.622Z" fill="black"/>
+<path d="M317.34 210.477C319.962 210.477 321.82 210 323.202 208.999V204.377C321.772 205.377 320.057 205.997 317.674 205.997C313.623 205.997 311.955 202.852 311.955 197.895C311.955 192.701 314.004 190.032 317.722 190.032C319.914 190.032 322.058 190.795 323.202 191.509V186.696C322.011 186.029 319.914 185.552 317.102 185.552C309.858 185.552 306.093 190.747 306.093 198.038C306.093 206.045 309.763 210.477 317.34 210.477Z" fill="black"/>
+<path d="M344.698 193.654C344.698 187.84 341.743 185.648 335.739 185.648C331.974 185.648 329.019 186.839 327.304 187.84V192.558C328.829 191.414 332.165 190.175 335.072 190.175C337.788 190.175 339.027 191.128 339.027 193.702V195.036H338.122C329.448 195.036 325.588 197.895 325.588 202.756C325.588 207.617 328.543 210.334 332.927 210.334C336.263 210.334 337.693 209.238 338.789 208.094H339.027C339.075 208.713 339.265 209.524 339.456 210H344.984C344.794 208.046 344.698 206.092 344.698 204.138V193.654ZM339.027 204.281C338.312 205.33 336.978 206.188 334.976 206.188C332.593 206.188 331.402 204.758 331.402 202.613C331.402 199.802 333.356 198.801 338.217 198.801H339.027V204.281Z" fill="black"/>
+<rect x="149" y="114.001" width="32" height="32" fill="#FF3333"/>
+<rect x="184" y="86.0007" width="32" height="60" fill="#0077FF"/>
+<rect x="219" y="50.0007" width="32" height="96" fill="#FFCC00"/>
+</svg>
diff --git a/admin/view/image/analytics/metrica/logo-ru.svg b/admin/view/image/analytics/metrica/logo-ru.svg
new file mode 100644
index 0000000..04b3e88
--- /dev/null
+++ b/admin/view/image/analytics/metrica/logo-ru.svg
@@ -0,0 +1,14 @@
+<svg width="400" height="272" viewBox="0 0 400 272" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="400" height="272" fill="white"/>
+<path d="M67.729 209.961H73.4954V176.84H65.1079C56.6727 176.84 52.2407 181.177 52.2407 187.563C52.2407 192.662 54.6711 195.664 59.0079 198.762L51.4781 209.961H57.7211L66.1087 197.427L63.2016 195.474C59.6751 193.091 57.9594 191.232 57.9594 187.229C57.9594 183.702 60.4376 181.32 65.1556 181.32H67.729V209.961ZM143.455 208.341V203.718C141.692 204.91 138.737 205.958 135.973 205.958C131.827 205.958 130.254 204.004 130.016 200.001H143.693V196.999C143.693 188.659 140.024 185.513 134.353 185.513C127.443 185.513 124.154 190.803 124.154 198.047C124.154 206.387 128.253 210.438 135.497 210.438C139.118 210.438 141.787 209.485 143.455 208.341ZM178.912 210.438C181.533 210.438 183.391 209.961 184.773 208.96V204.338C183.344 205.338 181.628 205.958 179.245 205.958C175.194 205.958 173.526 202.813 173.526 197.856C173.526 192.662 175.576 189.993 179.293 189.993C181.485 189.993 183.63 190.756 184.773 191.47V186.657C183.582 185.99 181.485 185.513 178.673 185.513C171.429 185.513 167.665 190.708 167.665 197.999C167.665 206.006 171.334 210.438 178.912 210.438ZM91.5096 185.99V195.474H83.9322V185.99H78.2611V209.961H83.9322V199.953H91.5096V209.961H97.1807V185.99H91.5096ZM122.772 205.481H120.246V185.99H103.71V188.039C103.71 193.901 103.328 201.478 101.327 205.481H99.5635V215.442H104.806V209.961H117.53V215.442H122.772V205.481ZM161.565 209.961H167.998L158.896 197.046L166.902 185.99H161.183L153.177 197.046V185.99H147.506V209.961H153.177V198.19L161.565 209.961ZM134.257 189.993C137.069 189.993 137.927 192.328 137.927 195.331V195.807H130.016C130.159 191.995 131.541 189.993 134.257 189.993ZM114.575 205.481H106.569C108.142 201.859 108.571 195.331 108.571 191.184V190.47H114.575V205.481Z" fill="black"/>
+<path d="M210.868 209.961H215.824L222.592 184.941V209.961H228.263V176.84H220.542L213.87 201.383L207.198 176.84H199.383V209.961H204.101V184.941L210.868 209.961Z" fill="black"/>
+<path d="M251.379 208.341V203.718C249.615 204.91 246.661 205.958 243.897 205.958C239.75 205.958 238.178 204.004 237.94 200.001H251.617V196.999C251.617 188.659 247.947 185.513 242.276 185.513C235.366 185.513 232.078 190.803 232.078 198.047C232.078 206.387 236.176 210.438 243.42 210.438C247.042 210.438 249.711 209.485 251.379 208.341ZM242.181 189.993C244.993 189.993 245.85 192.328 245.85 195.331V195.807H237.94C238.082 191.995 239.465 189.993 242.181 189.993Z" fill="black"/>
+<path d="M271.792 190.47V185.99H252.777V190.47H259.449V209.961H265.12V190.47H271.792Z" fill="black"/>
+<path d="M280.391 185.99H275.149V216.871H280.82V207.149C282.25 209.294 284.347 210.438 286.777 210.438C292.305 210.438 296.118 206.006 296.118 197.952C296.118 189.945 292.401 185.513 287.063 185.513C284.395 185.513 282.202 186.752 280.677 189.088L280.391 185.99ZM285.348 205.958C282.345 205.958 280.82 203.527 280.82 197.999C280.82 192.424 282.441 189.993 285.634 189.993C288.731 189.993 290.256 192.424 290.256 197.952C290.256 203.527 288.636 205.958 285.348 205.958Z" fill="black"/>
+<path d="M299.908 185.99V209.961H304.817L313.586 195.14V209.961H319.114V185.99H314.205L305.436 200.811V185.99H299.908Z" fill="black"/>
+<path d="M337.935 209.961H344.368L335.266 197.046L343.272 185.99H337.553L329.547 197.046V185.99H323.876V209.961H329.547V198.19L337.935 209.961Z" fill="black"/>
+<path d="M363.668 193.615C363.668 187.801 360.714 185.609 354.709 185.609C350.944 185.609 347.989 186.8 346.274 187.801V192.519C347.799 191.375 351.135 190.136 354.042 190.136C356.758 190.136 357.997 191.089 357.997 193.663V194.997H357.092C348.418 194.997 344.558 197.856 344.558 202.717C344.558 207.578 347.513 210.295 351.897 210.295C355.233 210.295 356.663 209.199 357.759 208.055H357.997C358.045 208.674 358.236 209.485 358.426 209.961H363.954C363.764 208.007 363.668 206.053 363.668 204.099V193.615ZM357.997 204.242C357.282 205.291 355.948 206.149 353.947 206.149C351.564 206.149 350.372 204.719 350.372 202.574C350.372 199.763 352.326 198.762 357.187 198.762H357.997V204.242Z" fill="black"/>
+<rect x="149" y="114" width="32" height="32" fill="#FF3333"/>
+<rect x="184" y="86.0005" width="32" height="60" fill="#0077FF"/>
+<rect x="219" y="50.0005" width="32" height="96" fill="#FFCC00"/>
+</svg>
diff --git a/admin/view/template/analytics/metrica.twig b/admin/view/template/analytics/metrica.twig
new file mode 100644
index 0000000..6b1c1b1
--- /dev/null
+++ b/admin/view/template/analytics/metrica.twig
@@ -0,0 +1,228 @@
+{{ header }}{{ column_left }}
+<div id="content">
+	<div class="page-header">
+		<div class="container-fluid">
+			<div class="float-end">
+				<button type="submit" form="form-analytics" data-bs-toggle="tooltip" title="{{ button_save }}" class="btn btn-primary">
+					<i class="fas fa-save"></i>
+				</button>
+				<a href="{{ back }}" data-bs-toggle="tooltip" title="{{ button_back }}" class="btn btn-light">
+					<i class="fas fa-reply"></i>
+				</a>
+			</div>
+			<h1>{{ heading_title }}</h1>
+			<ol class="breadcrumb">
+				{% for breadcrumb in breadcrumbs %}
+					<li class="breadcrumb-item">
+						<a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a>
+					</li>
+				{% endfor %}
+			</ol>
+		</div>
+	</div>
+	<div class="container-fluid">
+		{% if error_warning %}
+			<div class="alert alert-danger alert-dismissible">
+				<i class="fas fa-exclamation-circle"></i>
+				{{ error_warning }}
+				<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
+			</div>
+		{% endif %}
+		<div class="card">
+			<div class="card-header">
+				<i class="fas fa-pencil-alt"></i>
+				{{ text_edit }}</div>
+			<div class="card-body">
+				<a href="https://metrika.yandex.ru/" target="_blank">
+					<img src="{{ logo }}" alt="{{ heading_title }}" title="{{ heading_title }}" class="mx-auto d-block mb-3" style="width:150px;"/>
+				</a>
+				<form id="form-analytics" action="{{ save }}" method="post" data-oc-toggle="ajax">
+					<ul class="nav nav-tabs">
+						<li class="nav-item">
+							<a href="#tab-general" data-bs-toggle="tab" class="nav-link {% if has_settings %} active {% endif %}">{{ tab_metrics }}</a>
+						</li>
+						<li class="nav-item">
+							<a href="#tab-search" data-bs-toggle="tab" class="nav-link">{{ tab_metric_search }}</a>
+						</li>
+						<li class="nav-item">
+							<a href="#tab-log" data-bs-toggle="tab" class="nav-link">{{ tab_logging }}</a>
+						</li>
+						<li class="nav-item">
+							<a href="#tab-info" data-bs-toggle="tab" class="nav-link {% if not has_settings %} active {% endif %}">{{ tab_info }}</a>
+						</li>
+					</ul>
+					<div class="tab-content">
+						<div id="tab-general" class="tab-pane {% if has_settings %} active {% endif %}">
+							{% if notify_module_version %}
+								<div class="alert alert-warning alert-dismissible" role="alert">
+									<i class="fa fa-info-circle"></i>
+									{{ notify_module_version }}
+									<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+								</div>
+							{% endif %}
+							<div class="row mb-3">
+								<label for="input-status" class="col-sm-2 col-form-label">{{ entry_status }}</label>
+								<div class="col-sm-10">
+								<select name="analytics_metrica_status" id="input-status" class="form-select">
+									<option value="1"{% if analytics_metrica_status %} selected="selected"{% endif %}>{{ text_enabled }}</option>
+									<option value="0"{% if not analytics_metrica_status %} selected="selected"{% endif %}>{{ text_disabled }}</option>
+								</select>
+								</div>
+							</div>
+							<div class="row mb-3">
+								<label class="col-sm-2 col-form-label">{{ text_debug }}</label>
+								<div class="col-sm-10">
+									<div class="form-check form-switch form-switch-lg">
+										<input type="checkbox" name="analytics_metrica_debug" value="1" id="input-approval" class="form-check-input" {% if analytics_metrica_debug %} checked {% endif %}>
+									</div>
+								</div>
+							</div>
+							<fieldset id="option-value">
+								<legend>{{ entry_metrics_list }}</legend>
+								<div class="table-responsive">
+									<table id="metrics" class="table table-bordered table-hover">
+										<thead>
+											<tr>
+												<td class="text-start">{{ column_metric_code }}</td>
+												<td class="text-start">{{ column_settings }}</td>
+												<td class="text-end">{{ column_action }}</td>
+											</tr>
+										</thead>
+										<tbody>
+											{% for metrica in analytics_metrica_codes %}
+												<tr id="metrica-row{{ loop.index0 }}">
+													<td class="text-left">
+														<input type="text" name="analytics_metrica_codes[{{ loop.index0 }}][code]" value="{{ metrica.code }}" placeholder="{{ column_metric_code }}" class="form-control"/>
+                            							<div id="error-metrica-{{ loop.index0 }}-code" class="invalid-feedback"></div>
+													</td>
+													<td class="text-left" style="width: 30%;">
+														<div class="form-check">
+															<input class="form-check-input" type="checkbox" name="analytics_metrica_codes[{{ loop.index0 }}][ya_metrica_webvizor]" value="1" id="ya-metrica-webvizor-{{ loop.index0 }}" {% if metrica.ya_metrica_webvizor %}checked {% endif %}>
+															<label class="form-check-label" for="ya-metrica-webvizor-{{ loop.index0 }}">{{ entry_webvisor }}</label>
+														</div>
+													</td>
+													<td class="text-end">
+														<button type="button" onclick="$('#metrica-row{{ loop.index0 }}').remove();" data-bs-toggle="tooltip" title="{{ button_remove }}" class="btn btn-danger">
+															<i class="fas fa-minus-circle"></i>
+														</button>
+													</td>
+												</tr>
+											{% endfor %}
+										</tbody>
+										<tfoot>
+											<tr>
+												<td colspan="2"></td>
+												<td class="text-end">
+													<button type="button" onclick="addmetrica();" data-bs-toggle="tooltip" title="{{ button_banner_add }}" class="btn btn-primary">
+														<i class="fas fa-plus-circle"></i>
+													</button>
+												</td>
+											</tr>
+										</tfoot>
+									</table>
+								</div>
+							</fieldset>
+						</div>
+						<div id="tab-search" class="tab-pane">
+							<textarea id="search-metrica-status" wrap="off" rows="15" id="input-log" class="form-control" readonly></textarea>
+							<br>
+							<div class="row mb-3">
+								<div class="text-center">
+									<button type="button" id="button-search-metrica" data-bs-loading-text="{{ text_loading }}" class="btn btn-primary">
+										<i class="fa fa-arrow-search"></i>
+										{{ text_find_metric }}</button>
+								</div>
+							</div>
+						</div>
+						<div id="tab-log" class="tab-pane">
+							<div class="row mb-3">
+								<label class="col-sm-2 col-form-label">{{ text_write_logs }}</label>
+								<div class="col-sm-10">
+									<div class="form-check form-switch form-switch-lg">
+										<input type="checkbox" name="analytics_metrica_log" value="1" id="input-approval" class="form-check-input" {% if analytics_metrica_log %} checked {% endif %}>
+									</div>
+								</div>
+							</div>
+							<fieldset>
+								<legend>
+									<i class="fas fa-exclamation-triangle"></i>
+									{{ text_logs }}</legend>
+								<textarea wrap="off" rows="15" readonly class="form-control">{{ log }}</textarea>
+							</fieldset>
+						</div>
+						<div id="tab-info" class="tab-pane {% if not has_settings %} active {% endif %}">
+							<div class="text-left">
+								{{ text_instructions }}
+							</div>
+						</div>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</div>
+<script>
+var metrica_row = {{ analytics_metrica_codes| length }};
+
+function addmetrica() {
+	html = '<tr id="metrica-row' + metrica_row + '">';
+	html += '	<td class="text-left">';
+	html += '		<input type="text" name="analytics_metrica_codes[' + metrica_row + '][code]" value="{{ metrica.code }}" placeholder="{{ column_metric_code }}" class="form-control"/>';
+	html += '		<div id="error-metrica-' + metrica_row + '-code" class="invalid-feedback"></div>';
+	html += '	</td>';
+	html += '	<td class="text-left" style="width: 30%;">';
+	html += '		<div class="form-check">';
+	html += '			<input class="form-check-input" type="checkbox" name="analytics_metrica_codes[' + metrica_row + '][ya_metrica_webvizor]" value="" id="ya-metrica-webvizor-' + metrica_row + '" {% if metrica.ya_metrica_webvizor %}checked {% endif %}>';
+	html += '			<label class="form-check-label" for="ya-metrica-webvizor-' + metrica_row + '">{{ entry_webvisor }}</label>';
+	html += '		</div>';
+	html += '	</td>';
+	html += '	<td class="text-end">';
+	html += '		<button type="button" onclick="$(\'#metrica-row' + metrica_row + '\').remove();" data-bs-toggle="tooltip" title="{{ button_remove }}" class="btn btn-danger">';
+	html += '			<i class="fas fa-minus-circle"></i>';
+	html += '		</button>';
+	html += '	</td>';
+	html += '</tr>';
+
+    $('#metrics tbody').append(html);
+
+    ++metrica_row;
+}
+
+$('#button-search-metrica').on('click', function () {
+    $.ajax({
+        url: '{{ find_ya_metrica }}',
+        dataType: 'json',
+        beforeSend: function () {
+            $('#search-metrica-status').val('');
+            $('#search-metrica-status').val('{{ text_start_of_search_metric }}');
+            result = $('#search-metrica-status').val();
+        },
+        complete: function () {
+            result += "\n{{ text_end_of_search_metric }}";
+            $('#search-metrica-status').val(result);
+        },
+        success: function (json) {
+            console.log(json);
+            if (json.count_of_metrik == 0) {
+                result += "\n{{ text_metrics_not_found }}";
+            } else {
+                result += "\n{{ text_metrics_found }}:";
+                result += printMetrikCodes(json.codes_metrik);
+            }
+            $('#search-metrica-status').val(result);
+        },
+        error: function (xhr, ajaxOptions, thrownError) {
+            console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
+        }
+    });
+});
+
+function printMetrikCodes(codes) {
+    str = '';
+    for (var i = 0; i < codes.length; i++) {
+        str += "\n   {{ text_counter_number }}: " + codes[i] + ";";
+    }
+    return str;
+}
+</script>
+{{ footer }}
diff --git a/catalog/controller/analytics/metrica.php b/catalog/controller/analytics/metrica.php
new file mode 100644
index 0000000..3f78857
--- /dev/null
+++ b/catalog/controller/analytics/metrica.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace Opencart\Catalog\Controller\Extension\Yandex\Analytics;
+
+class Metrica extends \Opencart\System\Engine\Controller
+{
+	public function index($status)
+	{
+		$data['cms_version'] = mb_substr(VERSION, 0, 3);
+		$data['yandex_metrics'] = $this->config->get('analytics_metrica_codes');
+		$data['yandex_metrica_status'] = $status;
+
+		if (!empty($data['yandex_metrica_status']) && $data['yandex_metrica_status'] === '1') {
+			if (!empty($data['yandex_metrics'])) {
+				foreach ($data['yandex_metrics'] as $key => $value) {
+					if (!empty($value['ya_metrica_webvizor']) && $value['ya_metrica_webvizor'] == true) {
+						$data['yandex_metrics'][$key]['ya_metrica_webvizor'] = 'true';
+					} else {
+						$data['yandex_metrics'][$key]['ya_metrica_webvizor'] = 'false';
+					}
+				}
+			}
+		}
+
+		if (isset($this->request->get['route']) && $this->request->get['route'] == 'product/product' && $this->request->get['product_id']) {
+			$data['data_layer'] = json_encode($this->getProductDataLayer($this->request->get['product_id']), JSON_UNESCAPED_UNICODE);
+		}
+
+		if (isset($this->request->get['route']) && $this->request->get['route'] == 'checkout/success') {
+			$data['data_layer'] = json_encode($this->getCheckoutDataLayer(), JSON_UNESCAPED_UNICODE);
+		}
+
+
+		return $this->load->view('extension/yandex/analytics/metrica', $data);
+	}
+
+	public function getProductDataForYandexMetrica()
+	{
+		$this->load->model('catalog/category');
+		$this->load->model('catalog/product');
+		$this->load->model('extension/yandex/analytics/metrica');
+
+		if (isset($this->request->post['event'])) {
+			$event = $this->request->post['event'];
+		} else {
+			$event = 'add';
+		}
+
+		if (isset($this->request->post['quantity'])) {
+			$quantity = $this->request->post['quantity'];
+		} else {
+			$quantity = 0;
+		}
+
+		if (isset($this->request->post['id_type'])  && $this->request->post['id_type'] == 'key') {
+			$product_id = 0;
+			foreach ($this->cart->getProducts() as $product)
+				if ($product['cart_id'] == $this->request->post['id']) {
+					$product_id = $product['product_id'];
+					break;
+				}
+		} elseif (isset($this->request->post['id_type']) && $this->request->post['id_type'] == 'product_id') {
+			$product_id = $this->request->post['id'];
+		} else {
+			$product_id = 0;
+		}
+
+		if ($product_id === 0) {
+			$this->model_extension_yandex_analytics_metrica->writeLog('Required parameters not found (name and ID)');
+		}
+
+		$product_info = $this->model_catalog_product->getProduct($product_id);
+		$product_categories = $this->model_catalog_product->getCategories($product_id);
+
+		if (count($product_categories) > 0) {
+			$category = array_pop($product_categories);
+			$category_info = $this->model_catalog_category->getCategory($category['category_id']);
+			if ($category_info) {
+				$product_category = $category_info['name'];
+			} else {
+				$product_category = "";
+			}
+		} else {
+			$product_category = "";
+		}
+
+		$json = [];
+
+		if ($event == 'add') {
+			$json = [
+				'currency_code' => $this->session->data['currency'],
+				'product' => [
+					'id' => $product_info['product_id'],
+					'name' => $product_info['name'],
+					'price' => $this->currency->format($this->tax->calculate($product_info['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency'], false),
+					'brand' => $product_info['manufacturer'],
+					'category' => $product_category,
+					'quantity' => $quantity,
+				],
+			];
+		} elseif ($event == 'remove') {
+			$json = [
+				'currency_code' => $this->session->data['currency'],
+				'product' => [
+					'id' => $product_info['product_id'],
+					'name' => $product_info['name'],
+					'category' => $product_category,
+					'quantity' => $quantity,
+				],
+			];
+		}
+
+		$this->response->addHeader('Content-Type: application/json');
+		$this->response->setOutput(json_encode($json));
+	}
+
+	public function checkout(string &$route, array &$args): void
+	{
+		if ($route == 'checkout/success' && isset($this->session->data['order_id'])) {
+			$this->session->data['metrica_order_id'] = $this->session->data['order_id'];
+		}
+	}
+
+	public function getProductDataLayer(): array
+	{
+		$this->load->model('catalog/product');
+		$this->load->model('catalog/category');
+		$this->load->model('extension/yandex/analytics/metrica');
+
+		$product_id = 0;
+		$product_name = '';
+		$product_price = '';
+		$product_manufacturer = '';
+		$product_category = '';
+
+		if (isset($this->request->get['product_id']))
+			$product_id = $this->request->get['product_id'];
+
+		if ($product_info = $this->model_catalog_product->getProduct($product_id)) {
+			$product_name = $product_info['name'];
+
+			if ((float)$product_info['special']) {
+				$product_price = $this->currency->format($this->tax->calculate($product_info['special'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
+			} else {
+				$product_price = $this->currency->format($this->tax->calculate($product_info['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
+			}
+
+			$product_manufacturer = $product_info['manufacturer'];
+
+			$product_categories = $this->model_catalog_product->getCategories($product_info["product_id"]);
+			if (!empty($product_categories)) {
+				$category = array_pop($product_categories);
+				if ($category_info = $this->model_catalog_category->getCategory($category['category_id'])) {
+					$product_category = $category_info['name'];
+				}
+			}
+		} else {
+			$this->model_extension_yandex_analytics_metrica->writeLog('Required parameters not found (name and ID)');
+		}
+
+		$data = [
+			'ecommerce' => [
+				'currencyCode' => $this->session->data['currency'],
+				'detail' => [
+					'products' => [
+						[
+							'id' => $product_id,
+							'name' => $product_name,
+							'price' => $product_price,
+							'brand' => $product_manufacturer,
+							'category' => $product_category
+						]
+					]
+				]
+			]
+		];
+
+		return $data;
+	}
+
+
+	public function getCheckoutDataLayer(): array
+	{
+		if (!isset($this->session->data['metrica_order_id']))
+			return [];
+		else
+			$order_id = $this->session->data['metrica_order_id'];
+
+		$this->load->model('account/order');
+		$this->load->model('catalog/category');
+		$this->load->model('catalog/product');
+		$this->load->model('extension/yandex/analytics/metrica');
+
+		$data = [];
+		$products = [];
+		$order_info = $this->model_extension_yandex_analytics_metrica->getOrder($order_id);
+		$order_products = $this->model_account_order->getProducts($order_id);
+
+		foreach ($order_products as $order_product) {
+			$product_info = $this->model_catalog_product->getProduct($order_product['product_id']);
+			$categories_product = $this->model_catalog_product->getCategories($order_product['product_id']);
+			if ($product_info) {
+				$metrica_product_category = '';
+				if (count($categories_product) > 0) {
+					$category = array_pop($categories_product);
+					$category_info = $this->model_catalog_category->getCategory($category['category_id']);
+					if ($category_info) {
+						$metrica_product_category = $category_info['name'];
+					}
+				}
+
+				$price = $this->currency->format($this->tax->calculate($order_product['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
+
+				$products[] = [
+					'id' => $order_product['product_id'],
+					'name' => $order_product['name'],
+					'price' => $price,
+					'quantity' => $order_product['quantity'],
+					'manufacturer' => $product_info['manufacturer'],
+					'category' => $metrica_product_category,
+				];
+			}
+		}
+
+		$data = [
+			'ecommerce' => [
+				'currencyCode' => $this->session->data['currency'],
+				'purchase' => [
+					'actionField' => [
+						'id' => $order_info['order_id'],
+						'coupon' => isset($this->session->data['coupon']) ? $this->session->data['coupon'] : '',
+						'revenue' =>  $this->currency->format($order_info['total'], $this->session->data['currency'])
+					],
+					'products' => $products
+				]
+			]
+		];
+
+        unset($this->session->data['metrica_order_id']);
+
+		return $data;
+	}
+}
diff --git a/catalog/model/analytics/metrica.php b/catalog/model/analytics/metrica.php
new file mode 100644
index 0000000..0b40f25
--- /dev/null
+++ b/catalog/model/analytics/metrica.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Opencart\Catalog\Model\Extension\Yandex\Analytics;
+
+class Metrica extends \Opencart\System\Engine\Model
+{
+	public function getProductFromCart($cart_id)
+	{
+		$query = $this->db->query("SELECT DISTINCT product_id, quantity FROM " . DB_PREFIX . "cart WHERE cart_id = '" . (int)$cart_id . "'");
+
+		return $query->row;
+	}
+
+	public function getOrder($order_id)
+	{
+		$order_query = $this->db->query("SELECT `order_id`, `total`, `currency_code` FROM `" . DB_PREFIX . "order` WHERE order_id = '" . (int)$order_id . "' AND order_status_id > '0'");
+
+		if ($order_query->num_rows) {
+
+			return array(
+				'order_id'                => $order_query->row['order_id'],
+				'total'                   => $order_query->row['total'],
+				'currency_code'           => $order_query->row['currency_code']
+			);
+		} else {
+			return false;
+		}
+	}
+
+	public function writeLog(string $log)
+	{
+		if($this->config->get('analytics_yandex_metrica_log'))
+		{
+			$log_ym = new \Opencart\System\Library\Log('log_yandex_metrica.log');
+			$log_ym->write($log);
+		}
+	}
+}
diff --git a/catalog/view/javascript/metrica.js b/catalog/view/javascript/metrica.js
new file mode 100644
index 0000000..e69de29
diff --git a/catalog/view/template/analytics/metrica.twig b/catalog/view/template/analytics/metrica.twig
new file mode 100644
index 0000000..a8114be
--- /dev/null
+++ b/catalog/view/template/analytics/metrica.twig
@@ -0,0 +1,64 @@
+{% if (yandex_metrica_status is not empty) and (yandex_metrica_status == 1) %} 
+    <!-- Yandex.Metrica counters -->
+    {% if yandex_metrics is not empty %}
+        <script>
+        window.dataLayer = window.dataLayer || [];
+
+        {% if data_layer %}
+            dataLayer.push({{ data_layer }});
+        {% endif %}
+
+        (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
+        m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
+        (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
+
+        {% for metrica in yandex_metrics %}
+            ym({{ metrica.code }}, "init", {
+                clickmap:true,
+                trackLinks:true,
+                accurateTrackBounce:true,
+                webvisor:{{ metrica.ya_metrica_webvizor }},
+                ecommerce:"dataLayer",
+                params: { __ym: {"ymCmsPlugin": { "cms": "opencart", "cmsVersion":"{{ cms_version }}", "pluginVersion":"1.1.8"}}}
+            });
+        {% endfor %}
+        </script>
+        <noscript>
+            <div>
+                {% for metrica in yandex_metrics %}
+                <img src="https://mc.yandex.ru/watch/{{ metrica.code }}" style="position:absolute; left:-9999px;" alt="" />
+                {% endfor %}
+            </div>
+        </noscript>
+    {% endif %}
+
+    <script>
+    function sendDataForEcommerceYandexMetrica(options) {
+        let id = options.id; // required
+        let event = options.event; // required
+        let id_type = (typeof(options.id_type) != 'undefined' ? options.id_type : 'product_id');
+        let quantity = (typeof(options.quantity) != 'undefined' ? options.quantity : 'null');
+        let async = (typeof(options.async) != 'undefined' ? options.async : true);
+
+        $.ajax({
+        url: 'index.php?route=extension/yandex/analytics/metrica|getProductDataForYandexMetrica',
+        async: (typeof(async) != 'undefined' ? async : true),
+        type: 'post',
+        data: 'id=' + id + '&event=' + event + '&id_type=' + id_type + '&quantity=' + quantity,
+        //dataType: 'json',
+        success: function(json) {
+            console.log(json);
+            if (event == 'add') {
+            dataLayer.push({"ecommerce": {"currencyCode": json['currency_code'], "add": {"products": [json['product']]}}});
+            } else if (event == 'remove') {
+            dataLayer.push({"ecommerce": {"currencyCode": json['currency_code'], "remove": {"products": [json['product']]}}});
+            }
+        },
+        error: function(xhr, ajaxOptions, thrownError) {
+            alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
+        }
+        });
+    }
+    </script>
+    <!-- /Yandex.Metrica counters -->
+{% endif %}
\ No newline at end of file
diff --git a/install.json b/install.json
new file mode 100644
index 0000000..ad59677
--- /dev/null
+++ b/install.json
@@ -0,0 +1,6 @@
+{
+  "name": "Yandex.Metrica",
+  "version": "1.1.9",
+  "author": "Dmitry Pankov",
+  "link": "https://github.com/dima424658"
+}