1с php mysql обмен данными
В данной статье я не буду подробно расписывать как писать обработку для 1С чтобы приконнектиться к MySQL. У меня уже была обработка и я столкнулся с проблемой именно из-за отсутствия драйвера.
Пункт 1. Проблема
При переезде на другой сервер столкнулся со следующей проблемой (Произошла исключительная ситуация Microsoft OLE DB Provider for ODBC Drivers): не работал обмен 1С с внешней базой данных MySQL. Оно то и правильно что не работал. Он не должен, ведь я не установил ODBC Connector MySQL и как следствие при попытке обмена видел ошибку (у вас наверняка что-то подобное выползает на экран, если вы сюда попали гугля аналогичную проблему 😉)
Пункт 2. Какая версия MySQL ODBC ANSI Driver нужна?
И так. У нас для решения проблемы с подключением 1С 8.2/8.3 к промежуточной базе MySQL нам нужен, как я уже писал выше, ODBC Connector MySQL! И не простой, а именно тот, что у нас прописан в модуле обмена. Идём в Конфигуратор 1С, находим там модуль в котором прописано подключение и смотрим какой же чудесный драйвер и какой версии используется для подключения к внешней базе данных! И о чудо - это MySQL ODBC 5.3 ANSI Driver! В моём случае, конечно у вас он может быть другой версии. И на всякий случай вот код (смотрите ниже), можете сравнить со своим или вообще написать заново. Как вам больше нравится 🙂! И да, я тут пару фишек сделал. Вы можете вписать свои данные в поля и они сразу же отобразятся в коде, после чего вам остаётся скопипастить его себе прямо с сайта. Удобно? Ну да ладно. Код:
Пункт 3. Установка MySQL ODBC ANSI Driver
Но всё это чудо не будет работать собственно без драйвера, так что идём по этой ссылке и скачиваем себе драйвер подходящей версии и разрядности. Копируем на сервер и устанавливаем его. Думаю с этим у вас проблем не возникнет, а если будут, то дальше можете не читать 🙂.
Пункт 4. Настройка соединения 1С 8.2/8.3 с внешней базой данных MySQL
Дальше нам понадобится программа odbcad32. Её устанавливать не нужно, она у вас уже есть! Ищите её в Администрирование или Ctrl + R odbcad32 Enter
Для 32 битных систем программа расположена:
А для 64 битных систем программа лежит тут:
Но и ту и другую вы можете открыть из Администрирование в панели управления.
Переходим во вкладку Системный DSN и жмем кнопку Добавить. выбираем MySQL ODBC 5.3 ANSI Driver или какой у вас там версии коннектор. Готово . После чего переходим в настройки, выделяем системный источник данных и кликаем Настройка. . Тут совсем всё просто, даже писать не буду, подставляете свои данные
Пункт 5. Пару слов в заключение
Действительно тут нет ничего сложного, однако решил записать, так как дело это нехитрое и если ты не занимаешься настройкаой соединения базы 1С с внешней MySQL базой данных каждый день, то через N месяцев ты успешно можешь забыть всю процедуру и будешь заново гуглить. Я записал для себя, но если это пригодится и тебе, то будет просто здорово! Спасибо что дочитал до конца. А то обычно пользователи только успешно копипастят куски кода и ничего не читают 😒 !
MySql - Система Управления Базами Данных (СУБД). Основное отличие от всех остальных СУБД это то, что она является бесплатной. В силу того, что mysql бесплатна, она поддерживается очень многими хостинг провайдерами.
Здесь я расскажу как связать php и mysql т.е. как получить данные из базы данных mysql в php скрипт.
Ну вообще-то это не так сложно. Всё, что нужно знать:
- Хост - адрес сервера баз данных mysql
- Имя базы данных
- Имя пользователя
- Пароль - пароль для доступа к БД
- Некоторые sql команды
Итак, алгоритм такой:
- Устанавливаем соединение с сервером
- Выбираем нужную базу данных
- Делаем запрос к серверу баз данных (sql запрос)
- Обрабатываем результат запроса, если это необходимо
- Закрываем соединение (отключаемся от сервера БД)
По части php, чтобы работать с БД надо знать несколько функций (полный список смотри в php мануале).
mysql_connect ( ) ;
mysql_select_db ( ) ;
mysql_query ( ) ;
mysql_fetch_array ( ) ;
mysql_close ( ) ;
Это тот минимум, который позволит вам устанавливать соединение с сервером mysql, выбирать базы данных и делать запросы к серверу баз данных.
Для справки!
Все php функции которые предназначены для работы с базой данных mysql имеют префикс mysql_
Допустим в нашей базе данных храниться одна таблица со следующей структурой:
Таблица имеет имя: customer, а наша база данных называется database.
Допустим, что ваши данные для подключения к серверу mysql Такие:
- Хост: localhost
- Имя пользователя: guest
- Пароль: mypassword
Для начала необходимо создать нашу таблицу customer. Напишем скрипт который создаст в базе данных database таблицу customer. Назовём файлик как install.php.
Файл install.php
// Данные для mysql сервера
$dbhost = "localhost" ; // Хост
$dbuser = "guest" ; // Имя пользователя
$dbpassword = "mypassword" ; // Пароль
$dbname = "database" ; // Имя базы данных
// Подключаемся к mysql серверу
$link = mysql_connect ( $dbhost , $dbuser , $dbpassword ) ;
// Выбираем нашу базу данных
mysql_select_db ( $dbname , $link ) ;
// Создаём таблицу customer
// т.е. делаем sql запрос
$query = "create table customer (id int(2) primary key
auto_increment, name varchar(100), tel varchar(20))" ;
mysql_query ( $query , $link ) ;
// Закрываем соединение
mysql_close ( $link )
Функция mysql_connect() возвращает идентификатор соединения. Этот идентификатор необходимо указывать во всех mysql функциях. Можно провести аналогию с указателем на файл (file pointer), который используется функциями по работе с файлами. В параметрах функции мы указали хост, имя пользователя и пароль к базе данных.
На сервере mysql храниться не только ваша база данных. Сервер может обслуживать тысячи таких баз данных.
Поэтому, чтобы получить доступ к своей базе данных, необходимо выбрать её.
Выбор базы данных осуществляется функцией mysql_select_db(). В параметрах указывается: имя требуемой базы данных $dbname и идентификатор соединения $link, который мы получили с помощью функции mysql_connect().
После того как мы подключились к серверу mysql и выбрали нашу базу данных, мы делаем sql запрос.
И, наконец, отключаемся от сервера (закрываем соединение) функцией mysql_close().
В параметрах указываем идентификатор того соединения, которое нужно закрыть.
Для справки!
На самом деле скрипт может устанавливать несколько соединений с сервером mysql.
Всё зависит от хостинга на котором вы сидите.
Если вы всё сделали правильно, то на сервере mysql в вашей базе данных, появится таблица customer.
После того как мы создали таблицу, её надо заполнить т.е. записать в нёё какие-нибудь данные (записи).
Напишем скрипт, который будет добавлять запись в нашу таблицу. Назовём файл скрипта как insert.php.
Файл insert.php
// Данные для mysql сервера
$dbhost = "localhost" ; // Хост
$dbuser = "guest" ; // Имя пользователя
$dbpassword = "mypassword" ; // Пароль
$dbname = "database" ; // Имя базы данных
// Подключаемся к mysql серверу
$link = mysql_connect ( $dbhost , $dbuser , $dbpassword ) ;
// Выбираем нашу базу данных
mysql_select_db ( $dbname , $link ) ;
// Добавляем запись в нашу таблицу customer
// т.е. делаем sql запрос
$query = "insert into customer values(0,'Иванов Иван Иванович',
'(095) 555-55-55')" ;
mysql_query ( $query , $link ) ;
// Закрываем соединение
mysql_close ( $link ) ;
Посмотрев этот скрипт вы заметете, что он отличается от предыдущего только строкой запроса $query.
Теперь когда мы умеем записывать данные в базу данных, перейдём к процедуре запроса данных из базы mysql.
Для этого напишем скрипт и назовём файлик select.php.
Файл select.php
// Данные для mysql сервера
$dbhost = "localhost" ; // Хост
$dbuser = "guest" ; // Имя пользователя
$dbpassword = "mypassword" ; // Пароль
$dbname = "database" ; // Имя базы данных
// Подключаемся к mysql серверу
$link = mysql_connect ( $dbhost , $dbuser , $dbpassword ) ;
// Выбираем нашу базу данных
mysql_select_db ( $dbname , $link ) ;
// Добавляем запись в нашу таблицу customer
// т.е. делаем sql запрос
$query = "select * from customer" ;
// Запрашиваем
$result = mysql_query ( $query , $link ) ;
// Закрываем соединение
mysql_close ( $link ) ;
Ну тут. всё понятно :) :) :) Естественно изменилась строка запроса $query, которая теперь содержит sql-оператор select. Так же добавился небольшой код while() < . >.
Скажу только, что функция mysql_fetch_array() обрабатывает результат запроса и возвращает массив полей текущей! Внимание! текущей строки результата.
Константа mysql_assoc указывает на то, что функция должна вернуть ассоциативный массив полей.
После того как функция mysql_fetch_array() обработает все строки результата, она вернёт значение false и тогда цикл while() - не выполниться.
Тут в цикле используем функцию printf(). Она очень похожа на эту же функцию в языке c.
Продолжаем настраивать рабочее окружение для курса по интеграции 1С с web сайтами, и в данном уроке установим MySQL. Для этого надо скачать MySQL с сайта. Скачиваем версию MySQL Community Edition (GPL) в виде zip архива, а не установщика. Ознакомится с полным курсом по интеграции вы можете здесь.
Базу данных мы будем использовать при разработке на PHP и урок по его установке и настройке вы можете посмотреть по ссылке.
Установка MySQL для интеграции с 1С
В папке нашего локального web сервера установленного в предыдущем уроке, создайте папку DB. В ней будет хранится наша база данных.
Разархивируйте скачанный файл и скопируйте его содержимое в папку MySQL в каталоге Bin нашего web сервера.
Настройка 1С и MySQL
Я подготовил файл с настройками my.ini. Его надо скопировать в папку MySQL. Ниже его содержание:
[ mysqld ]
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
datadir="c:/Server/DB/"
default_authentication_plugin=mysql_native_password
Немного опишу, что означают наши параметры:
STRICT_TRANS_TABLES
Включает «строгий режим» для всех таблиц, поддерживающих транзакции, т.е. на InnoDB и BDB. Этот режим возвращает ошибку, вместо предупреждения в следующих случаях:
1. Тип входных данных не соответствует заданному типу. Например, вставка строки в колонку c числовым типом
2. Число или дата находится вне допустимого диапазона. Диапазон определяется типом данных. Например, для типа unsigned tinyint допустимым диапазоном являются числа от 0 до 255
3. При вставке данных пропущено значение колонки, для которой не задано значение по умолчанию и имеет атрибут NOT NULL
4. Длина значения выходит за пределы заданного диапазона. Например, для колонки типа CHAR(5) вы не сможете вставить строку более 5 символов
5. Для типов ENUM и SET отсутствует вставляемое или обновляемое значение
Более подробно об особенностях работы данного режима будет рассказано отдельно в последующей ниже главе.
datadir
Каталог где расположена наша база данных, обратите внимание на слеши
default_authentication_plugin
Для того чтобы избежать появление ошибки «The server requested authentication method unknown to the client [caching_sha2_password]»
Установка базы данных
Теперь откроем командную строку от имени администратора и выполним поочередно 3 команды для инициализации и установки нашей СУБД MySQL.
C:\Server\bin\mysql\bin\mysqld --initialize-insecure --user=root
C:\Server\bin\mysql\bin\mysqld --install
net start mysql
На этом установка MySQL завершена и для управления базами данных нам необходимо установить phpmyadmin. Чем мы и займемся в следующем уроке.
В новой редакции платформы 1С 8.2.14 появилась возможность устанавливать связь с внешними источниками данных. У меня была идея написать программу для прямой работы с базой данных на нашем сайте из 1С:Предприятия 8
В новой редакции платформы 1С 8.2.14 появилась возможность устанавливать связь с внешними источниками данных. У меня была идея написать программу для прямой работы с базой данных на нашем сайте из 1С:Предприятия 8
По шагам надо сделать следующее:
1. Скачиваем ODBC коннектор с сайта поставщика mysql.
2. Устанавливаем его на компьютер.
3. Заходим в конфигуратор 1С и добавляем новый внешний источник данных.
4. Добавляем внешнюю таблицу:
5. Указываем режим автоматического формирования списка таблиц
6. Указываем параметры соединения. Пишем руками:
и вбиваем логин и пароль для доступа к базе mysql
7. Если все ок, то мы увидим список таблиц из базы данных сайта.
8. Выбираем нужную таблицу галочкой
9. И получаем ее в дереве метаданных.
10. Создаем обработку для тестирования с одной процедурой:
Тут необходимо понимать что в обработке надо обязательно заново прописывать параметры соединения с внешней базой данных, они не хранятся в конфигурации.
11. Ставим точку останова на конец процедуры и запускаем обработку в режиме предприятия.
p.s. мы получили данные из внешней таблицы mysql средствами языка 1С. Что самое интересное, внешние таблицы доступны в конструкторе запроса.
upd: как можно изменять данные через ODBC я написал в этом посте.
Специальные предложения
В новой редакции платформы 1С 8.2.14 появилась возможность устанавливать связь с внешними источниками данных. У меня была идея написать программу для прямой работы с базой данных на нашем сайте из 1С:Предприятия 8
Очень интересная возможность новой платформы, да все никак руки не доходят ее попробовать.
Хочется уточнить один вопрос: если я установлю драйвер MySQL, а потом настрою его в "Администратор источников данных ODBC" где-нибудь в "Пользовательский DSN" или "Системный DSN", соответственно прописав там параметры подключения - смогу ли я потом подключаться к этому источнику просто по имени этой настройки? И понадобится ли заново прописывать настройки подключения к источнику данных в каждой обработке при таком подключении?
(1) я экспериментировал, параметры подключения вводил дважды:
1) в режиме конфигуратора для автоматического создания структуры таблиц
2) в режиме предприятия для отображения динамического списка записей таблиц.
Параметры вводились только один раз, они запоминаются в каком-то менеджере внешних источников данных, который доступен через "все функции" -> Стандартные -> Управление внешними источниками данных
(3) Автор пишет "Тут необходимо понимать что в обработке надо обязательно заново прописывать параметры соединения с внешней базой данных, они не хранятся в конфигурации.". Получается, что хранятся? И как потом строка подключения из обработки выглядит? Можно пример?
(5) Спасибо за дополнительную информацию. Только Ваше подключение практически идентично авторскому. И парочка примечаний "Важно". Выходит к внешнему источнику данных через подключение, описанное в (1), встроенными средствами платформы обратиться нельзя.
Ладно, пока сам не попробую приставать больше не буду.
(1) V_V_V, насчёт подключения с использованием DSN: там просто строка подключения будет иметь вид "DSN=;".
Теоретически, так можно избавиться от необходимости указывать логин/пароль в коде.
очень полезная возможность новой версии платформы представлена наглядно в очень полезной публикации этого сообщества :) спасибо :)
Да, все это безусловно хорошая вещь - внешние источники данных.
Я вначале сильно обрадовался когда узнал что 1С сделала такой механизм.
Но потом был сильно огорчен когда узнал что с этими источниками можно работать только на чтение.:(
(9) Spacer,
Ну собственно не совсем понятно в чем беда. Изменять данные через ODBC вроде всегда можно было. А тут вся фишка в том что с таблицей через запросы можно работать. Вроде запросы всегда только на чтение в 1С использовались :)
Набросаю сегодня завтра пример как я на сайте в данные меняю. Дам ссылку тут.
upd. На инфостарт не в силах перепостить сейчас, потому кому интересно как менять данные через ODBC, смотрите тут .
Попозже оформлю на инфостарте статью.
Да, все это безусловно хорошая вещь - внешние источники данных.
Я вначале сильно обрадовался когда узнал что 1С сделала такой механизм.
Но потом был сильно огорчен когда узнал что с этими источниками можно работать только на чтение.:(
Обидно что только на чтение, я уже размечтался что базу данных своего сайта смогу прикрутить и из 1С грузить информацию на сайт
Как то еще на тестовом релизе пытался связать с базой данных под управлением СУБД LETODB.Так и не получилось победить грабли вида иррациаональных чисел, и если среди DBF файлов базы имелись "пароленные" dbfки их прочитать так и не удалось, пока dbf редактором не исправил заголовок файла. а была такая надежда :(
За статью безусловно плюс. Как только появился 14 релиз 8.2 я пыталась подключить через внешние таблицы екселевский файл, пока результат отрицательный. У кого-нибудь получилось?
мне бы было интересно как подключиться к файлу базы данных на сайте (например sqlite) - не задавались таким вопросом?
(15) aximo,
Я думаю что принцип соединения аналогичный.
Сначала качаем ODBC драйвер для sqlite.
Потом из 1С прописываем сотроку соединения по аналогии
Под рукой нет такой базы чтобы проверить, но суть примерно такая.
sqlite - это файл. допустим он лежит на запароленном фтп. мне кажеться, что подключение будет несколько иное. кто знает - отпишитесь
Если база на запороленном ftp то надо вероятно другими средствами делать доступ, например поднимать ssh тонель и через него самбой шарить файл базы данных. Ну и строка подключения будет какой то такой.
Очень интресная тема.И очень полезная,если параметры подключения действительно хранятся в конфигураторе
vec435 пишет:
Очень интресная тема.И очень полезная,если параметры подключения действительно хранятся в конфигураторе
А они там не хранятся :)
Потестировал на MySQL. Вывод, бестолковая приблуда, зачем промежуточный механизм? какие плюсы использования.
Парни что я делаю не так? поставил себе последнюю платформу(8.3.5.1146), подключил базу через внешний источник данных. Если в конструкторе запросов выбираю поле без нижнего подчеркивания - то все работает. Если выбираю поле с нижним подчеркиванием, то выдает ошибку:
: Ошибка при вызове метода контекста (Выполнить)
Таблица = Запрос.Выполнить().Выгрузить();
по причине:
Ошибка выполнения запроса
по причине:
Ошибка внешней базы данных:
ошибка при выполнении запроса
по причине:
Ошибка ODBC. SQLSTATE: 42000
Номер ошибки: 1064
Описание: [MySQL][ODBC 5.1 Driver][mysqld-5.5.25]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"section_id"
FROM lesson_article T1' at line 2
а вот как на 2003 сервере х64 получить доступ к xbase через ODBC . ни в какую не могу загрузить дрова.
Всем доброго дня! Начал разбираться с использованием внешних источников. Столкнулся со следующей проблемой. Создал форму списка для таблицы MySQL. Подключение к Базе MySQL происходит, в обработчике загрузки формы делаю подключение. Но вываливается ошибка и, соответственно таблица пустая. Прилагаю скрин ошибки.
(27) bssat, непонятно почему, но у меня в в рабочем коде выскочила точно такая же ошибка как у вас на скриншоте. Пока разбираюсь в чем дело.
у менея точно такая же ошибка, походу при трансляции запроса 1с в mysql есть какойто ограничение по символам, изза етого формируется неправельный запрос (посмотри свой скрин там видно что текст запроса обрезан) если выбрать только 1-3 поля и они уместятся в запросе тогда работает, пока не разобрался с проблемой, возможно глючный ODBC , возможноно и в самой платформе глюк
(30) roha, У меня с момента написания статьи запрос работал без проблем до 26 декабря в фоновом процессе. Что произошло я не понимаю.
Рядом с поломанным запросом лежит другой, к другому ресурсу и он нормально отрабатывает команды из 1С. Пробовал вручную через Mysql front написать запрос, все работает отлично.
Что то в механизме трансляции изменилось, по-моему после обновления на 8.2.15.289
Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.
В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "///// echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1.
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.
Собственно сам скрипт:
global $wpdb2;
global $failure;
global $file_hist;
///// echo '
Старт загрузки
';
$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
///// echo '
Файл обмена с сервисными книжками не существует.
';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
>
/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure) ///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
///// echo '
Попытка вставить запись в лог таблицу
';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
///// echo '
Возврат в начало.
';
return $failure;
>
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist); ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7]; ////получаем размер файла
$m_mtime_file=$masiv_data_file[9]; ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file
///// echo '
Размер файла: '.$m_size_file.'
';
///// echo '
Штамп времени файла: '.$m_mtime_file.'
';
///// echo '
Формирование запроса на выборку из лога
';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);
if ($results)
< foreach ( $results as $r)
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
///echo '
Возврат в начало, т.к. найдена запись в логе.
';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
>
>
>
////если данные новые, пишем в лог запись о начале загрузки
/////echo '
Попытка вставить запись о начале загрузки в лог таблицу
';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));
////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
///// echo '
Очистка таблицы сервисных книжек
';
if (empty($results))
///// echo '
Ошибка очистки таблицы книжек, завершение.
';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
>
////загружаем данные
$table='vin_history'; // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация // (путь от корня web-сервера)
$delim=';'; // Разделитель полей в CSV файле
$enclosed='"'; // Кавычки для содержимого полей
$escaped='\
Читайте также: